feat: Allow running container as non-root UID/GID for ownership issues (docker) (#433)

Co-authored-by: George L. Yermulnik <yz@yz.kiev.ua>
Co-authored-by: MaxymVlasov <MaxymVlasov@users.noreply.github.com>
Co-authored-by: Anton Babenko <anton@antonbabenko.com>
This commit is contained in:
John Schutz 2022-09-07 07:19:52 -05:00 committed by GitHub
commit abc2570e42
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 143 additions and 6 deletions

View file

@ -1,3 +1,4 @@
*
!.dockerignore
!Dockerfile
!tools/entrypoint.sh

View file

@ -60,6 +60,19 @@ commandTests:
args: [ "version" ]
expectedOutput: [ "([0-9]+\\.){2}[0-9]+\\n$" ]
- name: "entrypoint.sh"
envVars:
- key: "USERID"
value: "1000:1000"
command: "/entrypoint.sh"
args: [ "-V" ]
expectedError: ["^ERROR: uid:gid 1000:1000 lacks permissions to //\\n$"]
exitCode: 1
- name: "su-exec"
command: "su-exec"
expectedOutput: ["^Usage: su-exec user-spec command \\[args\\]\\n$"]
fileExistenceTests:
- name: 'terrascan init'
path: '/root/.terrascan/pkg/policies/opa/rego/github/github_repository/privateRepoEnabled.rego'

View file

@ -19,6 +19,8 @@ jobs:
with:
files: |
Dockerfile
.dockerignore
tools/entrypoint.sh
- name: Build if Dockerfile changed
if: steps.changed-files-specific.outputs.any_changed == 'true'

View file

@ -5,8 +5,7 @@ WORKDIR /bin_dir
RUN apk add --no-cache \
# Builder deps
curl=~7 \
unzip=~6 && \
curl=~7 && \
# Upgrade pip for be able get latest Checkov
python3 -m pip install --no-cache-dir --upgrade pip
@ -177,7 +176,9 @@ RUN apk add --no-cache \
bash=~5 \
# pre-commit-hooks deps: https://github.com/pre-commit/pre-commit-hooks
musl-dev=~1 \
gcc=~10
gcc=~10 \
# entrypoint wrapper deps
su-exec=~0.2
# Copy tools
COPY --from=builder \
@ -203,9 +204,11 @@ RUN if [ "$(grep -o '^terraform-docs SKIPPED$' /usr/bin/tools_versions_info)" =
# unsafe repository ('/lint' is owned by someone else)
git config --global --add safe.directory /lint
COPY tools/entrypoint.sh /entrypoint.sh
ENV PRE_COMMIT_COLOR=${PRE_COMMIT_COLOR:-always}
ENV INFRACOST_API_KEY=${INFRACOST_API_KEY:-}
ENV INFRACOST_SKIP_UPDATE_CHECK=${INFRACOST_SKIP_UPDATE_CHECK:-false}
ENTRYPOINT [ "pre-commit" ]
ENTRYPOINT [ "/entrypoint.sh" ]

View file

@ -51,6 +51,7 @@ If you are using `pre-commit-terraform` already or want to support its developme
* [terraform_wrapper_module_for_each](#terraform_wrapper_module_for_each)
* [terrascan](#terrascan)
* [tfupdate](#tfupdate)
* [Docker Usage: File Permissions](#docker-usage-file-permissions)
* [Authors](#authors)
* [License](#license)
* [Additional information for users from Russia and Belarus](#additional-information-for-users-from-russia-and-belarus)
@ -227,16 +228,18 @@ pre-commit run -a
Or, using Docker ([available tags](https://github.com/antonbabenko/pre-commit-terraform/pkgs/container/pre-commit-terraform/versions)):
**NOTE:** This command uses your user id and group id for the docker container to use to access the local files. If the files are owned by another user, update the `USERID` environment variable. See [File Permissions section](#docker-usage-file-permissions) for more information.
```bash
TAG=latest
docker run -v $(pwd):/lint -w /lint ghcr.io/antonbabenko/pre-commit-terraform:$TAG run -a
docker run -e "USERID=$(id -u):$(id -g)" -v $(pwd):/lint -w /lint ghcr.io/antonbabenko/pre-commit-terraform:$TAG run -a
```
Execute this command to list the versions of the tools in Docker:
```bash
TAG=latest
docker run --entrypoint cat ghcr.io/antonbabenko/pre-commit-terraform:$TAG /usr/bin/tools_versions_info
docker run --rm --entrypoint cat ghcr.io/antonbabenko/pre-commit-terraform:$TAG /usr/bin/tools_versions_info
```
## Available Hooks
@ -735,6 +738,16 @@ Sample configuration:
- --args=--verbose # Verbose output
```
**If you use hook inside Docker:**
The `terraform_wrapper_module_for_each` hook attempts to determine the module's short name to be inserted into the generated `README.md` files for the `source` URLs. Since the container uses a bind mount at a static location, it can cause this short name to be incorrect.
If the generated name is incorrect, set them by providing the `module-repo-shortname` option to the hook:
```yaml
- id: terraform_wrapper_module_for_each
args:
- '--args=--module-repo-shortname=ec2-instance'
```
### terrascan
1. `terrascan` supports custom arguments so you can pass supported flags like `--non-recursive` and `--policy-type` to disable recursive inspection and set the policy type respectively:
@ -779,6 +792,26 @@ Sample configuration:
Check [`tfupdate` usage instructions](https://github.com/minamijoyo/tfupdate#usage) for other available options and usage examples.
No need to pass `--recursive .` as it is added automatically.
## Docker Usage: File Permissions
A mismatch between the Docker container's user and the local repository file ownership can cause permission issues in the repository where `pre-commit` is run. The container runs as the `root` user by default, and uses a `tools/entrypoint.sh` script to assume a user ID and group ID if specified by the environment variable `USERID`.
The [recommended command](#4-run) to run the Docker container is:
```bash
TAG=latest
docker run -e "USERID=$(id -u):$(id -g)" -v $(pwd):/lint -w /lint ghcr.io/antonbabenko/pre-commit-terraform:$TAG run -a
```
which uses your current session's user ID and group ID to set the variable in the run command. Without this setting, you may find files and directories owned by `root` in your local repository.
If the local repository is using a different user or group for permissions, you can modify the `USERID` to the user ID and group ID needed. **Do not use the username or groupname in the environment variable, as it has no meaning in the container.** You can get the current directory's owner user ID and group ID from the 3rd (user) and 4th (group) columns in `ls` output:
```bash
$ ls -aldn .
drwxr-xr-x 9 1000 1000 4096 Sep 1 16:23 .
```
## Authors
This repository is managed by [Anton Babenko](https://github.com/antonbabenko) with help from these awesome contributors:

View file

@ -414,6 +414,9 @@ function create_tmp_file_tf {
mv "$tmp_file" "$tmp_file.tf"
tmp_file_tf="$tmp_file.tf"
# mktemp creates with no group/other read permissions
chmod a+r "$tmp_file_tf"
echo "$CONTENT_MAIN_TF" > "$tmp_file_tf"
}

82
tools/entrypoint.sh Executable file
View file

@ -0,0 +1,82 @@
#!/usr/bin/env bash
#exit on error
set -e
readonly USERBASE="run"
readonly BASHPATH="/bin/bash"
readonly HOMEPATH="/home"
function echo_error_and_exit {
echo -e "ERROR: " "$@" >&2
exit 1
}
# make sure entrypoint is running as root
if [[ $(id -u) -ne 0 ]]; then
echo_error_and_exit "Container must run as root. Use environment variable USERID to set user.\n" \
"Example: \"TAG=latest && " \
"docker run -e USERID=$(id -u):$(id -g) -v $(pwd):/lint -w /lint ghcr.io/antonbabenko/pre-commit-terraform:$TAG run -a\""
fi
# make sure USERID makes sense as UID:GID
# it looks like the alpine distro limits UID and GID to 256000, but
# could be more, so we accept any valid integers
USERID=${USERID:-"0:0"}
if [[ ! $USERID =~ ^[0-9]+:[0-9]+$ ]]; then
echo_error_and_exit "USERID environment variable invalid, format is userid:groupid. Received: \"$USERID\""
fi
# separate uid and gid
uid=${USERID%%:*}
gid=${USERID##*:}
# if requested UID:GID is root, go ahead and run without other processing
[[ $USERID == "0:0" ]] && exec su-exec "$USERID" pre-commit "$@"
# make sure workdir and some files are readable/writable by the provided UID/GID
# combo, otherwise will have errors when processing hooks
wdir="$(pwd)"
if ! su-exec "$USERID" "$BASHPATH" -c "test -w $wdir && test -r $wdir"; then
echo_error_and_exit "uid:gid $USERID lacks permissions to $wdir/"
fi
wdirgitindex="$wdir/.git/index"
if ! su-exec "$USERID" "$BASHPATH" -c "test -w $wdirgitindex && test -r $wdirgitindex"; then
echo_error_and_exit "uid:gid $USERID cannot write to $wdirgitindex"
fi
# check if group by this GID already exists, if so get the name since adduser
# only accepts names
if groupinfo="$(getent group "$gid")"; then
groupname="${groupinfo%%:*}"
else
# create group in advance in case GID is different than UID
groupname="$USERBASE$gid"
if ! err="$(addgroup -g "$gid" "$groupname" 2>&1)"; then
echo_error_and_exit "failed to create gid \"$gid\" with name \"$groupname\"\ncommand output: \"$err\""
fi
fi
# check if user by this UID already exists, if so get the name since id
# only accepts names
if userinfo="$(getent passwd "$uid")"; then
username="${userinfo%%:*}"
else
username="$USERBASE$uid"
if ! err="$(adduser -h "$HOMEPATH$username" -s "$BASHPATH" -G "$groupname" -D -u "$uid" -k "$HOME" "$username" 2>&1)"; then
echo_error_and_exit "failed to create uid \"$uid\" with name \"$username\" and group \"$groupname\"\ncommand output: \"$err\""
fi
fi
# it's possible it was not in the group specified, add it
if ! idgroupinfo="$(id -G "$username" 2>&1)"; then
echo_error_and_exit "failed to get group list for username \"$username\"\ncommand output: \"$idgroupinfo\""
fi
if [[ ! " $idgroupinfo " =~ [[:blank:]]${gid}[[:blank:]] ]]; then
if ! err="$(addgroup "$username" "$groupname" 2>&1)"; then
echo_error_and_exit "failed to add user \"$username\" to group \"$groupname\"\ncommand output: \"$err\""
fi
fi
# user and group of specified UID/GID should exist now, and user should be
# a member of group, so execute pre-commit
exec su-exec "$USERID" pre-commit "$@"