Skip to content

Commit abc2570

Browse files
tofupupyermulnikMaxymVlasovantonbabenko
authored
feat: Allow running container as non-root UID/GID for ownership issues (docker) (antonbabenko#433)
Co-authored-by: George L. Yermulnik <[email protected]> Co-authored-by: MaxymVlasov <[email protected]> Co-authored-by: Anton Babenko <[email protected]>
1 parent 005134b commit abc2570

File tree

7 files changed

+143
-6
lines changed

7 files changed

+143
-6
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
*
22
!.dockerignore
33
!Dockerfile
4+
!tools/entrypoint.sh

.github/.container-structure-test-config.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,19 @@ commandTests:
6060
args: [ "version" ]
6161
expectedOutput: [ "([0-9]+\\.){2}[0-9]+\\n$" ]
6262

63+
- name: "entrypoint.sh"
64+
envVars:
65+
- key: "USERID"
66+
value: "1000:1000"
67+
command: "/entrypoint.sh"
68+
args: [ "-V" ]
69+
expectedError: ["^ERROR: uid:gid 1000:1000 lacks permissions to //\\n$"]
70+
exitCode: 1
71+
72+
- name: "su-exec"
73+
command: "su-exec"
74+
expectedOutput: ["^Usage: su-exec user-spec command \\[args\\]\\n$"]
75+
6376
fileExistenceTests:
6477
- name: 'terrascan init'
6578
path: '/root/.terrascan/pkg/policies/opa/rego/github/github_repository/privateRepoEnabled.rego'

.github/workflows/build-image-test.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ jobs:
1919
with:
2020
files: |
2121
Dockerfile
22+
.dockerignore
23+
tools/entrypoint.sh
2224
2325
- name: Build if Dockerfile changed
2426
if: steps.changed-files-specific.outputs.any_changed == 'true'

Dockerfile

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ WORKDIR /bin_dir
55

66
RUN apk add --no-cache \
77
# Builder deps
8-
curl=~7 \
9-
unzip=~6 && \
8+
curl=~7 && \
109
# Upgrade pip for be able get latest Checkov
1110
python3 -m pip install --no-cache-dir --upgrade pip
1211

@@ -177,7 +176,9 @@ RUN apk add --no-cache \
177176
bash=~5 \
178177
# pre-commit-hooks deps: https://github.com/pre-commit/pre-commit-hooks
179178
musl-dev=~1 \
180-
gcc=~10
179+
gcc=~10 \
180+
# entrypoint wrapper deps
181+
su-exec=~0.2
181182

182183
# Copy tools
183184
COPY --from=builder \
@@ -203,9 +204,11 @@ RUN if [ "$(grep -o '^terraform-docs SKIPPED$' /usr/bin/tools_versions_info)" =
203204
# unsafe repository ('/lint' is owned by someone else)
204205
git config --global --add safe.directory /lint
205206

207+
COPY tools/entrypoint.sh /entrypoint.sh
208+
206209
ENV PRE_COMMIT_COLOR=${PRE_COMMIT_COLOR:-always}
207210

208211
ENV INFRACOST_API_KEY=${INFRACOST_API_KEY:-}
209212
ENV INFRACOST_SKIP_UPDATE_CHECK=${INFRACOST_SKIP_UPDATE_CHECK:-false}
210213

211-
ENTRYPOINT [ "pre-commit" ]
214+
ENTRYPOINT [ "/entrypoint.sh" ]

README.md

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ If you are using `pre-commit-terraform` already or want to support its developme
5151
* [terraform_wrapper_module_for_each](#terraform_wrapper_module_for_each)
5252
* [terrascan](#terrascan)
5353
* [tfupdate](#tfupdate)
54+
* [Docker Usage: File Permissions](#docker-usage-file-permissions)
5455
* [Authors](#authors)
5556
* [License](#license)
5657
* [Additional information for users from Russia and Belarus](#additional-information-for-users-from-russia-and-belarus)
@@ -227,16 +228,18 @@ pre-commit run -a
227228

228229
Or, using Docker ([available tags](https://github.com/antonbabenko/pre-commit-terraform/pkgs/container/pre-commit-terraform/versions)):
229230

231+
**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.
232+
230233
```bash
231234
TAG=latest
232-
docker run -v $(pwd):/lint -w /lint ghcr.io/antonbabenko/pre-commit-terraform:$TAG run -a
235+
docker run -e "USERID=$(id -u):$(id -g)" -v $(pwd):/lint -w /lint ghcr.io/antonbabenko/pre-commit-terraform:$TAG run -a
233236
```
234237

235238
Execute this command to list the versions of the tools in Docker:
236239

237240
```bash
238241
TAG=latest
239-
docker run --entrypoint cat ghcr.io/antonbabenko/pre-commit-terraform:$TAG /usr/bin/tools_versions_info
242+
docker run --rm --entrypoint cat ghcr.io/antonbabenko/pre-commit-terraform:$TAG /usr/bin/tools_versions_info
240243
```
241244

242245
## Available Hooks
@@ -735,6 +738,16 @@ Sample configuration:
735738
- --args=--verbose # Verbose output
736739
```
737740

741+
**If you use hook inside Docker:**
742+
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.
743+
If the generated name is incorrect, set them by providing the `module-repo-shortname` option to the hook:
744+
745+
```yaml
746+
- id: terraform_wrapper_module_for_each
747+
args:
748+
- '--args=--module-repo-shortname=ec2-instance'
749+
```
750+
738751
### terrascan
739752

740753
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:
779792
Check [`tfupdate` usage instructions](https://github.com/minamijoyo/tfupdate#usage) for other available options and usage examples.
780793
No need to pass `--recursive .` as it is added automatically.
781794

795+
## Docker Usage: File Permissions
796+
797+
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`.
798+
799+
The [recommended command](#4-run) to run the Docker container is:
800+
801+
```bash
802+
TAG=latest
803+
docker run -e "USERID=$(id -u):$(id -g)" -v $(pwd):/lint -w /lint ghcr.io/antonbabenko/pre-commit-terraform:$TAG run -a
804+
```
805+
806+
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.
807+
808+
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:
809+
810+
```bash
811+
$ ls -aldn .
812+
drwxr-xr-x 9 1000 1000 4096 Sep 1 16:23 .
813+
```
814+
782815
## Authors
783816

784817
This repository is managed by [Anton Babenko](https://github.com/antonbabenko) with help from these awesome contributors:

hooks/terraform_wrapper_module_for_each.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,9 @@ function create_tmp_file_tf {
414414
mv "$tmp_file" "$tmp_file.tf"
415415
tmp_file_tf="$tmp_file.tf"
416416

417+
# mktemp creates with no group/other read permissions
418+
chmod a+r "$tmp_file_tf"
419+
417420
echo "$CONTENT_MAIN_TF" > "$tmp_file_tf"
418421
}
419422

tools/entrypoint.sh

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/usr/bin/env bash
2+
#exit on error
3+
set -e
4+
5+
readonly USERBASE="run"
6+
readonly BASHPATH="/bin/bash"
7+
readonly HOMEPATH="/home"
8+
9+
function echo_error_and_exit {
10+
echo -e "ERROR: " "$@" >&2
11+
exit 1
12+
}
13+
14+
# make sure entrypoint is running as root
15+
if [[ $(id -u) -ne 0 ]]; then
16+
echo_error_and_exit "Container must run as root. Use environment variable USERID to set user.\n" \
17+
"Example: \"TAG=latest && " \
18+
"docker run -e USERID=$(id -u):$(id -g) -v $(pwd):/lint -w /lint ghcr.io/antonbabenko/pre-commit-terraform:$TAG run -a\""
19+
fi
20+
21+
# make sure USERID makes sense as UID:GID
22+
# it looks like the alpine distro limits UID and GID to 256000, but
23+
# could be more, so we accept any valid integers
24+
USERID=${USERID:-"0:0"}
25+
if [[ ! $USERID =~ ^[0-9]+:[0-9]+$ ]]; then
26+
echo_error_and_exit "USERID environment variable invalid, format is userid:groupid. Received: \"$USERID\""
27+
fi
28+
29+
# separate uid and gid
30+
uid=${USERID%%:*}
31+
gid=${USERID##*:}
32+
33+
# if requested UID:GID is root, go ahead and run without other processing
34+
[[ $USERID == "0:0" ]] && exec su-exec "$USERID" pre-commit "$@"
35+
36+
# make sure workdir and some files are readable/writable by the provided UID/GID
37+
# combo, otherwise will have errors when processing hooks
38+
wdir="$(pwd)"
39+
if ! su-exec "$USERID" "$BASHPATH" -c "test -w $wdir && test -r $wdir"; then
40+
echo_error_and_exit "uid:gid $USERID lacks permissions to $wdir/"
41+
fi
42+
wdirgitindex="$wdir/.git/index"
43+
if ! su-exec "$USERID" "$BASHPATH" -c "test -w $wdirgitindex && test -r $wdirgitindex"; then
44+
echo_error_and_exit "uid:gid $USERID cannot write to $wdirgitindex"
45+
fi
46+
47+
# check if group by this GID already exists, if so get the name since adduser
48+
# only accepts names
49+
if groupinfo="$(getent group "$gid")"; then
50+
groupname="${groupinfo%%:*}"
51+
else
52+
# create group in advance in case GID is different than UID
53+
groupname="$USERBASE$gid"
54+
if ! err="$(addgroup -g "$gid" "$groupname" 2>&1)"; then
55+
echo_error_and_exit "failed to create gid \"$gid\" with name \"$groupname\"\ncommand output: \"$err\""
56+
fi
57+
fi
58+
59+
# check if user by this UID already exists, if so get the name since id
60+
# only accepts names
61+
if userinfo="$(getent passwd "$uid")"; then
62+
username="${userinfo%%:*}"
63+
else
64+
username="$USERBASE$uid"
65+
if ! err="$(adduser -h "$HOMEPATH$username" -s "$BASHPATH" -G "$groupname" -D -u "$uid" -k "$HOME" "$username" 2>&1)"; then
66+
echo_error_and_exit "failed to create uid \"$uid\" with name \"$username\" and group \"$groupname\"\ncommand output: \"$err\""
67+
fi
68+
fi
69+
70+
# it's possible it was not in the group specified, add it
71+
if ! idgroupinfo="$(id -G "$username" 2>&1)"; then
72+
echo_error_and_exit "failed to get group list for username \"$username\"\ncommand output: \"$idgroupinfo\""
73+
fi
74+
if [[ ! " $idgroupinfo " =~ [[:blank:]]${gid}[[:blank:]] ]]; then
75+
if ! err="$(addgroup "$username" "$groupname" 2>&1)"; then
76+
echo_error_and_exit "failed to add user \"$username\" to group \"$groupname\"\ncommand output: \"$err\""
77+
fi
78+
fi
79+
80+
# user and group of specified UID/GID should exist now, and user should be
81+
# a member of group, so execute pre-commit
82+
exec su-exec "$USERID" pre-commit "$@"

0 commit comments

Comments
 (0)