Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't run docker containers as root #400

Open
wants to merge 81 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
93e99d5
Make packages easier to read
jeremyestein May 8, 2024
4b1f8fa
Remove unneeded packages
jeremyestein May 8, 2024
d087520
Merge three pixl python docker images into one multi-stage dockerfile to
jeremyestein May 8, 2024
1e1c18e
Specify healthcheck command in only one place
jeremyestein May 8, 2024
63d6edb
De-dupe the pre-reqs installation code using a build arg
jeremyestein May 8, 2024
46a08e0
De-dupe the actual install as well
jeremyestein May 8, 2024
64fcbc5
Add (failing) test for the condition we are aiming for: containers to
jeremyestein May 8, 2024
0d5cb39
Run as user pixl, which we have to create inside the image with a
jeremyestein May 9, 2024
397cb68
The system test already builds the required docker images, and passes
jeremyestein May 9, 2024
d7fa46e
Semi-WIP: Create pixl user+group on the GHA test runner so that we ca…
jeremyestein May 9, 2024
d8525b1
Fix variable usage and exports dir location
jeremyestein May 9, 2024
6facef9
More debugging
jeremyestein May 9, 2024
55db93c
debug
jeremyestein May 9, 2024
6f7c8dd
debug
jeremyestein May 9, 2024
0ff9148
split build and up
jeremyestein May 9, 2024
cce26a5
Create user home dir in the GHA script to avoid error from failed
jeremyestein May 9, 2024
1e3ed88
Why is pytest not found? Some kind of env change caused by sudo?
jeremyestein May 9, 2024
8abcb5f
Preserve path when running sudo
jeremyestein May 9, 2024
de4e1e9
Run tests as normal runner user
jeremyestein May 9, 2024
5736f30
Don't see why this needs to have docker group
jeremyestein May 9, 2024
09f37ea
Debugging for perm failure on file deletes
jeremyestein May 9, 2024
0edcf17
Fix scope error
jeremyestein May 9, 2024
d0201d8
OK maybe this was needed after all
jeremyestein May 9, 2024
077a304
Since CLI creates everything in exports dir these days, it makes more
jeremyestein May 9, 2024
cd93adc
Try not creating home dir for pixl - what is even using this?
jeremyestein May 9, 2024
c0c0d96
and therefore I can't do this any more
jeremyestein May 9, 2024
9507816
Remove debugging
jeremyestein May 10, 2024
bd5cf84
Document the reason for the permissions setup better. Don't need to
jeremyestein May 10, 2024
de2ea9d
If Export API is believed to only read from the export dir, then let's
jeremyestein May 10, 2024
9aaf2c7
First attempt at documentation on docker permissions.
jeremyestein May 10, 2024
0bd75fb
CLI user needs to be able to write to export dir.
jeremyestein May 10, 2024
db201d9
Clarify docs
jeremyestein May 10, 2024
928a092
Go with the assumption that the host machine will have a "pixl" user and
jeremyestein May 15, 2024
e13fe01
oops left in empty arg
jeremyestein May 15, 2024
017a0a9
Set the setgid bit so that subdirs/files in exports will be owned by the
jeremyestein May 15, 2024
9eb831b
debugging
jeremyestein May 15, 2024
8e9771b
fix debugging
jeremyestein May 15, 2024
15b98c8
doesn't exist?
jeremyestein May 15, 2024
572144d
Make debugging more useful and permanent. More docs
jeremyestein May 15, 2024
2d7c902
more debugging
jeremyestein May 15, 2024
cf1900e
Set the FACL
jeremyestein May 15, 2024
eb5da97
usermod doesn't change groups of existing shells. Need to run the system
jeremyestein May 15, 2024
995b35b
Invoking a new shell with "bash" doesn't re-read groups, but using sudo
jeremyestein May 15, 2024
303374c
Need to preserve envs
jeremyestein May 15, 2024
edde878
more debugging
jeremyestein May 15, 2024
ef1b113
Unlikely to work because su will prompt
jeremyestein May 15, 2024
806449d
Will just passing PATH be enough?
jeremyestein May 15, 2024
629a5e4
Pass through all env as well as PATH
jeremyestein May 15, 2024
8bbe273
Remove debugging
jeremyestein May 15, 2024
b9edf54
Merge branch 'main' into jeremy/docker-uid-new
jeremyestein May 20, 2024
d167c13
Merge branch 'main' into jeremy/docker-uid-new
jeremyestein May 21, 2024
4c6f6d1
Unite orthanc Dockerfiles
jeremyestein May 21, 2024
1b46c83
Add orthanc containers to test
jeremyestein May 21, 2024
468d908
Make orthanc run as orthanc user instead of root
jeremyestein May 22, 2024
1004681
Can also use non-root for fakes in system test
jeremyestein May 22, 2024
668ebc0
Non-root orthanc works for core and imaging tests too
jeremyestein May 22, 2024
695f64f
Add fakes to system test container user check
jeremyestein May 22, 2024
b34abbc
Remove debugging
jeremyestein May 22, 2024
02e3b79
Use same orthanc version everywhere
jeremyestein May 22, 2024
420fb7f
Use existing YAML anchors
jeremyestein May 22, 2024
b4d2526
Don't think this is needed since we moved this code to the export-api
jeremyestein May 22, 2024
cca7b93
Try adding orthanc to PIXL group
stefpiatek Jun 14, 2024
5925520
Try adding orthanc to PIXL group
stefpiatek Jun 14, 2024
3437005
Try adding orthanc to PIXL group
stefpiatek Jun 14, 2024
905e741
Try adding orthanc to PIXL group
stefpiatek Jun 14, 2024
19be6c7
Change owner of pixl file
stefpiatek Jun 14, 2024
4c39072
Try setting pixl user before copying
stefpiatek Jun 14, 2024
32ca6bb
Change owner of python projects on copy
stefpiatek Jun 14, 2024
81eabb7
Change owner of python projects on copy
stefpiatek Jun 14, 2024
21ccb13
Change owner of python projects on copy
stefpiatek Jun 14, 2024
9ef77e7
Merge branch 'main' into jeremy/docker-uid-new
stefpiatek Jun 14, 2024
ee1b97c
Define wait for images to be exported in conftest
stefpiatek Jun 14, 2024
47822a9
Add group ID to test build args
stefpiatek Jun 14, 2024
242aacf
Merge branch 'main' into jeremy/docker-uid-new
milanmlft Jul 4, 2024
6e945c3
Update README.md
stefpiatek Jul 18, 2024
c7b2711
Merge branch 'main' into jeremy/docker-uid-new
milanmlft Jul 30, 2024
521b1cd
Consolidate `RUN` commands
milanmlft Jul 30, 2024
653a921
Merge branch 'main' into jeremy/docker-uid-new
stefpiatek Jul 30, 2024
5d1937d
Add @dram1964's instructions for setting up a PIXL user on GAE
milanmlft Jul 31, 2024
b8a6de5
Merge remote-tracking branch 'origin/main' into jeremy/docker-uid-new
stefpiatek Oct 7, 2024
75af526
Merge remote-tracking branch 'origin/main' into jeremy/docker-uid-new
stefpiatek Oct 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,7 @@ HASHER_API_AZ_CLIENT_ID=
HASHER_API_AZ_CLIENT_PASSWORD=
HASHER_API_AZ_TENANT_ID=
HASHER_API_AZ_KEY_VAULT_NAME=

# User and group IDs for pixl:pixl. Only needed at build time.
PIXL_USER_UID=7001
PIXL_USER_GID=7001
34 changes: 24 additions & 10 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,6 @@ jobs:
- name: Create .secrets.env
run: touch test/.secrets.env

- name: Build test services
working-directory: test
run: |
docker compose build

- name: Build services
run: |
docker compose build

- name: Run tests
working-directory: test
env:
Expand All @@ -128,7 +119,24 @@ jobs:
HASHER_API_AZ_TENANT_ID: ${{ secrets.EXPORT_AZ_TENANT_ID }}
HASHER_API_AZ_KEY_VAULT_NAME: ${{ secrets.EXPORT_AZ_KEY_VAULT_NAME }}
run: |
./run-system-test.sh coverage
# We need PIXL_USER_UID and PIXL_USER_GID from the test env file.
source .env
# Set up as per docker_perms.md
# Most importantly, the pixl user is *not* in the docker group, and
# the default "runner" user is added to the pixl group.
sudo --non-interactive groupadd --gid $PIXL_USER_GID pixl
sudo --non-interactive useradd --uid $PIXL_USER_UID --gid pixl pixl
sudo --non-interactive usermod --append --groups pixl runner
host_export_root_dir=../projects/exports
sudo --non-interactive chown -R pixl:pixl "$host_export_root_dir"
sudo --non-interactive chmod -R u=rwx,g=rwxs,o= "$host_export_root_dir"
sudo --non-interactive setfacl -R -m d:g::rwX "$host_export_root_dir"
# The top-level commands are still run as the GHA "runner" user, which
# is analogous to a human user on the GAE bringing up a PIXL instance.
# The sudo command is needed to make it run in a new shell
# so that the new group (see usermod above) is picked up.
# PATH needs to be explicitly mentioned to be passed through.
sudo --non-interactive --preserve-env --preserve-env=PATH -u runner ./run-system-test.sh coverage
echo FINISHED SYSTEM TEST SCRIPT

- name: Dump export-api docker logs for debugging
Expand All @@ -146,6 +154,12 @@ jobs:
run: |
docker logs -t system-test-imaging-api-1 2>&1

- name: View filesystem for debugging
if: ${{ failure() }}
run: |
# just in case the error is caused by permissions, make sure all files are seen
sudo --non-interactive ls -laR

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4.2.0
with:
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,14 @@ These files will subsequently be uploaded to the `parquet` destination specified
[project config](#3-configure-a-new-project).

```
EXPORT_ROOT/PROJECT_SLUG/all_extracts/EXTRACT_DATETIME/radiology/radiology.parquet
EXPORT_ROOT/PROJECT_SLUG/all_extracts/EXTRACT_DATETIME/radiology/IMAGE_LINKER.parquet
....................................................../omop/public/*.parquet
```

This directory is current located in `projects/exports` relative to the source code root.
stefpiatek marked this conversation as resolved.
Show resolved Hide resolved

[Further discussion about docker permissions and the exports directory is found here](docs/setup/docker_perms.md)

### Destination

#### FTP server
Expand Down
22 changes: 15 additions & 7 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ x-proxy-common: &proxy-common
NO_PROXY: *no-proxy
no_proxy: *no-proxy

x-pixl-uid-gid: &pixl-uid-gid
PIXL_USER_UID: ${PIXL_USER_UID}
PIXL_USER_GID: ${PIXL_USER_GID}

x-build-args-common: &build-args-common
<<: [*proxy-common]
<<: [*proxy-common, *pixl-uid-gid]

x-pixl-common-env: &pixl-common-env
DEBUG: ${DEBUG}
Expand Down Expand Up @@ -81,8 +85,10 @@ services:
hasher-api:
build:
context: .
dockerfile: ./docker/hasher-api/Dockerfile
dockerfile: ./docker/pixl-python/Dockerfile
target: hasher_api
args:
PIXL_PACKAGE_DIR: hasher
<<: *build-args-common
environment:
<<: [*proxy-common, *pixl-common-env]
Expand All @@ -100,7 +106,6 @@ services:
networks:
- pixl-net
healthcheck:
test: ["CMD", "curl", "-f", "http://hasher-api:8000/heart-beat"]
interval: 10s
timeout: 30s
retries: 5
Expand Down Expand Up @@ -253,8 +258,10 @@ services:
export-api:
build:
context: .
dockerfile: ./docker/export-api/Dockerfile
dockerfile: ./docker/pixl-python/Dockerfile
target: export_api
args:
PIXL_PACKAGE_DIR: pixl_export
<<: *build-args-common
environment:
<<:
Expand Down Expand Up @@ -293,22 +300,23 @@ services:
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ${PWD}/projects/exports:/run/projects/exports
- ${PWD}/projects/exports:/run/projects/exports:ro
- ${PWD}/projects/configs:/${PROJECT_CONFIGS_DIR:-/projects/configs}:ro

imaging-api:
build:
context: .
dockerfile: ./docker/imaging-api/Dockerfile
dockerfile: ./docker/pixl-python/Dockerfile
target: imaging_api
args:
PIXL_PACKAGE_DIR: pixl_imaging
<<: *build-args-common
depends_on:
queue:
condition: service_healthy
orthanc-raw:
condition: service_healthy
healthcheck:
test: curl -f http://0.0.0.0:8000/heart-beat
interval: 10s
timeout: 30s
retries: 5
Expand Down
46 changes: 0 additions & 46 deletions docker/hasher-api/Dockerfile
milanmlft marked this conversation as resolved.
Show resolved Hide resolved

This file was deleted.

49 changes: 0 additions & 49 deletions docker/imaging-api/Dockerfile

This file was deleted.

26 changes: 21 additions & 5 deletions docker/export-api/Dockerfile → docker/pixl-python/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
FROM python:3.11.7-slim-bullseye
FROM python:3.11.7-slim-bullseye AS pixl_python_base
SHELL ["/bin/bash", "-o", "pipefail", "-e", "-u", "-x", "-c"]

ARG TEST="false"
Expand All @@ -29,20 +29,36 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
RUN sed -i '/en_GB.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
RUN apt-get autoremove && apt-get clean && rm -rf /var/lib/apt/lists/*

HEALTHCHECK CMD /usr/bin/curl -f http://0.0.0.0:8000/heart-beat || exit 1

WORKDIR /app
# specify which of our packages we're installing using build time arg
ARG PIXL_PACKAGE_DIR

# Install requirements before copying modules
COPY ./pixl_core/pyproject.toml ./pixl_core/pyproject.toml
COPY ./pixl_export/pyproject.toml ./pixl_export/pyproject.toml
COPY ./$PIXL_PACKAGE_DIR/pyproject.toml ./$PIXL_PACKAGE_DIR/pyproject.toml
RUN pip3 install --no-cache-dir pixl_core/ \
&& pip3 install --no-cache-dir pixl_export/
&& pip3 install --no-cache-dir $PIXL_PACKAGE_DIR/

# Install our code
COPY ./pixl_core/ pixl_core/
COPY ./pixl_export/ .
COPY ./$PIXL_PACKAGE_DIR/ .
RUN pip install --no-cache-dir --force-reinstall --no-deps pixl_core/ \
--no-cache-dir --force-reinstall --no-deps . && \
if [ "$TEST" = "true" ]; then pip install --no-cache-dir pixl_core/[test] .[test]; fi

HEALTHCHECK CMD /usr/bin/curl -f http://0.0.0.0:8000/heart-beat || exit 1
ARG PIXL_USER_UID
ARG PIXL_USER_GID
RUN groupadd --gid $PIXL_USER_GID pixl && useradd --uid $PIXL_USER_UID --gid $PIXL_USER_GID pixl
USER pixl

# Each container should be run with a different entry point
FROM pixl_python_base AS export_api
ENTRYPOINT ["uvicorn", "pixl_export.main:app", "--host", "0.0.0.0", "--port", "8000"]
jeremyestein marked this conversation as resolved.
Show resolved Hide resolved

FROM pixl_python_base AS hasher_api
ENTRYPOINT ["uvicorn", "hasher.main:app", "--host", "0.0.0.0", "--port", "8000"]

FROM pixl_python_base AS imaging_api
ENTRYPOINT ["/app/scripts/migrate_and_run.sh"]
36 changes: 36 additions & 0 deletions docs/setup/docker_perms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Docker setup

Setup of user IDs, file permissions, ACLs, and other related things in docker.

## Requirements and design considerations

### The aim - Docker containers should not run as root

Generally it's considered a bad idea to run anything as root that doesn't need it.
See "Principle of Least Privilege".

Bear in mind that if you're running as a user in the `docker` group, you might as well
be running as root, because you can create docker containers that run as root.

### How to run as non-root

During the docker image build, a user called `pixl` with UID `PIXL_USER_UID` and
a group called `pixl` with GID `PIXL_USER_GID` are created *on the container*.
When the container is started, it runs as this user.

The numerical IDs need to be configurable because on the GAEs,
docker is set up so that there is no remapping between user IDs on containers and
user IDs on the host.
If you're UID 7001 on the container, then
you're 7001 on the host!


# System testing

The build args are also used by the GHA system test workflow to set up this user and group on
the GHA test runner, simulating how a user should set this up in production.

If you're running the system test on linux, you will probably have better luck if Docker
is installed in rootless mode.


23 changes: 23 additions & 0 deletions docs/setup/uclh-infrastructure-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,26 @@ chmod -R g+rwxs /gae/pixl_dev # inherit group when new directories or files are
setfacl -R -m d:g::rwX /gae/pixl_dev
# now clone the repository or copy an existing deployment
```

Following clone, the exports directory needs to be permissioned appropriately:

Change these values in your production `.env` file:
```
PIXL_USER_UID=???
PIXL_USER_GID=???
```
so that they match the UID and GID of the pixl user/group on
your host.

Set the ownership of the "exports" directory as follows:

```
MY_EXPORT_DIR=/gae/pixl_dev/PIXL/projects/exports
chgrp -R pixl "$MY_EXPORT_DIR"
```

See Issue https://github.com/UCLH-Foundry/PIXL/issues/401 where I suggest setting
the whole PIXL repo as group `pixl` rather than `docker`, for simplicity.

See [docker permissions setup](./docker_perms.md) for further discussion about
why the docker export directory needs to be set up this way.
4 changes: 4 additions & 0 deletions test/.env
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,7 @@ FTP_USER_PASSWORD=longpassword

# Project configs directory
PROJECT_CONFIGS_DIR=projects/configs

# User and group IDs for pixl:pixl. Only needed at build time.
PIXL_USER_UID=7001
PIXL_USER_GID=7001
Loading