appsody / stacks

Appsody application stacks. This repo will be archived soon.
https://appsody.dev
Apache License 2.0
89 stars 121 forks source link

python-flask: Avoid permission errors when building dependencies #834

Closed henrynash closed 4 years ago

henrynash commented 4 years ago

Avoid clashing UIDs in the attempt to ensure the application does not run as root. Since the Dockerfiles already create a non-root user, do not also set APPSODY_USER_RUN_AS_LOCAL to true.

Attempting to only use APPSODY_USER_RUN_AS_LOCAL=true and not create a user in the Dockerfile, leads to different issues regarding clashing UIDs

Fixes #833

henrynash commented 4 years ago

So this is super puzzling. The stack validate fails when running our ci tests (with a permissions error when the docker file is running)....but if I run stack validate locally it works fine. I think this was the original reason why an attempt was done to add RUN_AS_LOCAL (which causes the other problems describe in the issue being fixed here).

But for the life of me, I cannot work our what is different in our ci environment vs running locally.

henrynash commented 4 years ago

So the issue is that when we run under Travis, the volume mount of the user app directory shows up in the container as UID 2000 (the Travis UID), which is different to the local user defined in the Dockerfile...and so when the local user tries to run the PREP command, it doesn't have permission to write file into the mounted volume in the container. This does not happen when running locally. Mulling a fix....

henrynash commented 4 years ago

So the underlying reason appears to be a difference between how docker bind mounting works on macOS (at least when using docker desktop for Mac) and Linux . On macOS, the new osxfs work in Docker desktop appears to change how the permissions of volume mounts are handled. In particular, a volume bind mount appears to honour the user currently running in the container (e.g. set by the USER commands). This is actually contrary to what the Docker standard says, where a volume bind mount has the UID of the original source point (usually the local file system). You can show this difference by doing the following:

Create a simple Dockefile like this and build an image:

$ cat Dockerfile << EOF
FROM ubuntu
RUN useradd -m worker -u 1001
USER worker
EOF
$ docker build -t myimage .

Build and and run on macOS and Linux gives you different results:

# macOS
docker run -v `pwd`/.:/test1 myimage ls -aln test1
total 8
drwxr-xr-x 3 1001 1001   96 Jun 15 08:05 .
drwxr-xr-x 1    0    0 4096 Jun 15 08:06 ..
-rw-r--r-- 1 1001 1001   54 Jun 15 08:01 Dockerfile

# Linux (fyi UID 1000 is the UID of the current user on the Linux host)
$ docker run -v `pwd`/.:/test1 myimage ls -aln test1
total 12
drwxr-xr-x 2 1000 1000 4096 Jun 15 08:04 .
drwxr-xr-x 1    0    0 4096 Jun 15 08:05 ..
-rw-r--r-- 1 1000 1000   54 Jun 15 08:04 Dockerfile

This is why under travis, using Linux, the mounted dir is owned by the travis user (UID 2000), while testing locally the mounted dir was owned by the user created in the Docker container (worker, UID 1000).

I'll work up a fix, that should work on all platforms.

henrynash commented 4 years ago

I have tested the proposed fix on macOS and Linux, as well as confirmed that this does work against https://github.ibm.com/vaccine-cold-chain/reeferiotsimulator, which was where this problem was found.

The essence of the fix is to not assume you can write to the APPSODY_MOUNTS directory (which in this case is the user application). An intermediate file was being written there, ahead of being processed to update the dependencies stored in APPSODY_DEPS. This intermediate file is now stored in /project. In addition, some tidying up in the application Dockerfile was done to ensure the permissions of the directory structure in the final image matches that of the one used in local development.