dunglas / symfony-docker

A Docker-based installer and runtime for Symfony. Install: download and `docker compose up`.
https://dunglas.dev/2021/12/symfonys-new-native-docker-support-symfony-world/
2.62k stars 779 forks source link

Run container as an unprivileged user #679

Open damienfern opened 1 month ago

damienfern commented 1 month ago

Hi,

Thx for this template, very useful ! :pray:

Many resources suggest using an unprivileged user in container in order to prevent privilege escalation attacks(e.g. OWASP https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-2-set-a-user or Docker docs https://docs.docker.com/build/building/best-practices/#user).

It seems it's not the case on this template with FrankenPHP and based on its doc, FrankenPHP can be used with an unprivileged user. Is it on purpose, or is it a feature that can be added to this template ?

7-zete-7 commented 1 month ago

Hi, @damienfern

I'm also interested in this question!

For now I'm using the following edits to run FrankenPHP as the built-in user www-data.

frankenphp/docker-entrypoint.sh

@@ -53,8 +53,9 @@
        fi
    fi

-   setfacl -R -m u:www-data:rwX -m u:"$(whoami)":rwX var
-   setfacl -dR -m u:www-data:rwX -m u:"$(whoami)":rwX var
+   chgrp -R www-data var /data /config
+   setfacl -R -m u:www-data:rwX -m u:"$(whoami)":rwX var /data /config
+   setfacl -dR -m u:www-data:rwX -m u:"$(whoami)":rwX var /data /config
 fi

-exec docker-php-entrypoint "$@"
+su -c "docker-php-entrypoint $*" -s '/bin/sh' 'www-data'

With this edit the container still runs as root, but FrankenPHP runs as www-data.

I'm not sure if this is the correct way, but in my case it works so far.

I would also be glad if this template contained information about a more correct way to run the container not as an privileged user.

dunglas commented 1 month ago

Modifying the Dockerfile like this should help: https://frankenphp.dev/docs/docker/#running-as-a-non-root-user

A doc PR explaining how to change this template to run as a non-root user is very welcome, however, we'll not do that by default, because this causes many issues.

7-zete-7 commented 5 days ago

Found a problem when using the method I described above (using su ... instead of exec ...): when using su ... a chain of subprocesses is created (entrypoint→su→sh→frankenphp), while when using exec ... there is no chain (frankenphp).

Due to the chain, signals sent by Docker do not reach the target process. This primarily negatively affects the Symfony Messenger consumer.

Processes when using exec ...: PID USER Command
1 root frankenphp run --config /etc/caddy/Caddyfile
Processes when using su ...: PID USER Command
1 root /bin/sh /usr/local/bin/docker-entrypoint frankenphp run --config /etc/caddy/Caddyfile
33 root `- su -c docker-php-entrypoint frankenphp run --config /etc/caddy/Caddyfile -s /usr/bin/sh www-data
34 www‑data `- sh -c frankenphp run --config /etc/caddy/Caddyfile
35 www‑data `- frankenphp run --config /etc/caddy/Caddyfile
damienfern commented 5 days ago

Hi @7-zete-7,

You should give a try to https://github.com/tianon/gosu as it uses the same methods to "impersonate" user as Docker, but it uses an exec instead of a subprocess (which, as you pointed, handles the signal better).

7-zete-7 commented 5 days ago

Thanks for such a quick response, @damienfern!

I'll try using gosu for this task.

7-zete-7 commented 5 days ago

gosu actually changes the user and does not create any subprocesses. Thanks, @damienfern!

7-zete-7 commented 5 days ago

Some update of https://github.com/dunglas/symfony-docker/issues/679#issuecomment-2434587889 to work via gosu.

Dockerfile

@@ -27,8 +27,9 @@
 RUN set -eux; \
    install-php-extensions \
        @composer \
        apcu \
+       gosu \
        intl \
        opcache \
        zip \
    ;

frankenphp/docker-entrypoint.sh

@@ -53,8 +53,9 @@
        fi
    fi

-   setfacl -R -m u:www-data:rwX -m u:"$(whoami)":rwX var
-   setfacl -dR -m u:www-data:rwX -m u:"$(whoami)":rwX var
+   chgrp -R www-data var /data /config
+   setfacl -R -m u:www-data:rwX -m u:"$(whoami)":rwX var /data /config
+   setfacl -dR -m u:www-data:rwX -m u:"$(whoami)":rwX var /data /config
 fi

-exec docker-php-entrypoint "$@"
+exec /usr/sbin/gosu www-data "$@"

Still not sure if this is the correct way, but it works for now.

damienfern commented 3 days ago

Have you tried to use gosu directly ? Based on the gosu's README, it already does an exec.

-exec /usr/sbin/gosu www-data "$@"
+/usr/sbin/gosu www-data "$@"
7-zete-7 commented 3 days ago

@damienfern, yes, I did this first. I was surprised that there was a chain of processes and tried with exec. With exec the chain was gone.

Using exec /usr/sbin/gosu www-data "$@"
PIDUSERCommand
1www-datafrankenphp run --config /etc/caddy/Caddyfile
Using /usr/sbin/gosu www-data "$@" or gosu www-data "$@"
PIDUSERCommand
1root/bin/sh /usr/local/bin/docker-entrypoint frankenphp run --config /etc/caddy/Caddyfile
20www-data`- frankenphp run --config /etc/caddy/Caddyfile