frankenphp
frankenphp copied to clipboard
Running as a Non-Root User not working on SF 7
What happened?
HERE THIS BUG WITH TRY Running as a Non-Root User
I try to change a user root on my docker frankenphp but isn't working. Let's see :
Building frankenphp Step 1/5 : FROM dunglas/frankenphp ---> f57655f0d144 Step 2/5 : ARG USER=www-data ---> Running in 375301e09d4f ---> Removed intermediate container 375301e09d4f ---> 55ddb50b9599 Step 3/5 : USER ${USER} ---> Running in eb7d0b3f58e6 ---> Removed intermediate container eb7d0b3f58e6 ---> ecb86f83c6f0 Step 4/5 : RUN adduser -D ${USER} setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp ---> Running in 5df2cbde730b Option d is ambiguous (debug, disabled-login, disabled-password) adduser [--uid id] [--firstuid id] [--lastuid id] [--gid id] [--firstgid id] [--lastgid id] [--ingroup group] [--add-extra-groups] [--shell shell] [--comment comment] [--home dir] [--no-create-home] [--allow-all-names] [--allow-bad-names] [--disabled-password] [--disabled-login] [--conf file] [--quiet] [--verbose] [--debug] user Add a normal user
adduser --system [--uid id] [--group] [--ingroup group] [--gid id] [--shell shell] [--comment comment] [--home dir] [--no-create-home] [--conf file] [--quiet] [--verbose] [--debug] user Add a system user
adduser --group [--gid ID] [--firstgid id] [--lastgid id] [--conf file] [--quiet] [--verbose] [--debug] group addgroup [--gid ID] [--firstgid id] [--lastgid id] [--conf file] [--quiet] [--verbose] [--debug] group Add a user group
addgroup --system [--gid id] [--conf file] [--quiet] [--verbose] [--debug] group Add a system group
adduser USER GROUP Add an existing user to an existing group ERROR: Service 'frankenphp' failed to build : The command '/bin/sh -c adduser -D ${USER} setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp' returned a non-zero code: 1
This is my dockerfile :
FROM dunglas/frankenphp
ARG USER=www-data USER ${USER}
RUN adduser -D ${USER}
# Caddy requires an additional capability to bind to port 80 and 443
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp
# Caddy requires write access to /data/caddy and /config/caddy
RUN chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy
And my docker-compose.yaml : version: '3.9'
services: ###> frankenphp ### frankenphp: container_name: bff-iauto-frankenphp build: context: . dockerfile: dockerfile ports: - 80:80 - 443:443 - 443:443/udp volumes: - $PWD:/app - caddy_data:/data - caddy_config:/config ###< frankenphp ###
volumes: caddy_data: caddy_config:
Build Type
Official static build
Worker Mode
No
Operating System
GNU/Linux
CPU Architecture
x86_64
Relevant log output
No response
Can you try to remove the -D
, it doesn't look necessary on Alpine. If it works, could you update the docs, please?
Ok but now i have this :
docker logs bff-iauto-frankenphp kev@kev {"level":"info","ts":1709142955.9294713,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"} {"level":"warn","ts":1709142955.93021,"msg":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"/etc/caddy/Caddyfile","line":16} {"level":"info","ts":1709142955.93076,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]} {"level":"info","ts":1709142955.9309363,"logger":"http.auto_https","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443} {"level":"info","ts":1709142955.9309967,"logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"} {"level":"info","ts":1709142955.931001,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc000525600"} {"level":"info","ts":1709142955.931198,"logger":"tls.cache.maintenance","msg":"stopped background certificate maintenance","cache":"0xc000525600"} Error: loading initial config: loading new config: loading http app module: provision http: loading pki app module: provision pki: provisioning CA 'local': loading root cert: open /data/caddy/pki/authorities/local/root.crt: permission denied
Its ok only with docker exec -it <name_container_franken> bash . We have www-data user , but after if i request i have this
You have to give whatever user you are using permission to read/write/access /data
and /config
here's what I've got so far
FROM dunglas/frankenphp
# add additional extensions here:
RUN install-php-extensions \
pdo_mysql \
pdo_pgsql \
gd \
intl \
zip \
bcmath \
xdebug
# opcache # disable for local development
# Install git
RUN apt-get update && apt-get install -y git
# Install symfony cli
RUN curl -1sLf 'https://dl.cloudsmith.io/public/symfony/stable/setup.deb.sh' | bash
RUN apt-get update && apt-get install -y symfony-cli
# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
ARG USER=www-data
RUN \
# Use "adduser -D ${USER}" for alpine based distros
useradd ${USER}; \
# Add additional capability to bind to port 80 and 443
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
# Give write access to /data/caddy and /config/caddy
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy;
USER ${USER}
COPY . /app
here's what I've got so far
COPY . /app
How are you solving the UID problem? if your host user has UID 1000 and you run the docker container, using volume, the files inside the container belongs to a user/group 1000, in my case. But the www-data user has UID 33
To solve this problem i'm creating a new user and group with the host UID 1000 (application:application)
@dunglas thanks for all your effort. In FrankenPHP can we use any environment variable to set the UID of the www-data user or may i create a new user/group with my host UID?
can you share your code ? @sneycampos plz
Isn't much different of yours:
FROM dunglas/frankenphp
ARG USER=application
ARG UID=1000
RUN \
# Add user and group
groupadd -g ${UID} ${USER} && \
useradd -u ${UID} -g ${USER} -m ${USER};
RUN \
# Use "adduser -D ${USER}" for alpine based distros
# useradd -D ${USER}; \
# Add additional capability to bind to port 80 and 443
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
# Give write access to /data/caddy and /config/caddy
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy;
USER ${USER}
How are you solving the UID problem? if your host user has UID 1000 and you run the docker container, using volume, the files inside the container belongs to a user/group 1000, in my case. But the www-data user has UID 33
Yes, volumes are always owned by whatever user created the files. This is how linux filesystems work, in general (it's literally a bind mount). So, if you mount a volume to a directory owned by your local user, then the files in the container will be owned by your local user because it was mounted there.
USER root # all files will be owned by root
COPY . .
USER www-data # all files will be owned by www-data
COPY . .
but mounting different files there and replacing them with files from your computer means they are whatever they are on your computer. There's no getting around this without fancy stuff.
If you are using pure docker, you can just change the process inside the container to match your computer (assuming it isn't privileged):
docker run --user $UID:$GID
if you are using docker compose:
service:
image: my-image
user: $UID:$GID
assuming $GID
and $UID
environment variables are set (they are set automatically in linux ... no idea if they are set on macs).
Starting docker (compose) this way ensures the process owns the files and is running as the same user as the host. It's no required, but it saves quite a bit of shenanigans.
if you are using docker compose:
service: image: my-image user: $UID:$GID
assuming
$GID
and$UID
environment variables are set (they are set automatically in linux ... no idea if they are set on macs).Starting docker (compose) this way ensures the process owns the files and is running as the same user as the host. It's no required, but it saves quite a bit of shenanigans.
I think it works only if the UID/GID in the host matches with the UID/GID in the container, for example: in OSX my UID/GID is 501:20 but inside the container doesn't exists any user with this UID, so it fails. I'll try in Ubuntu as soon as i can and give any feedback using an user with a UID different from 1000 (1001 for example in Hetzner's ARM instances)
So my question was if is possible to, using env vars, set the UID/GID of the www-data user or if is possible to have a non root user, like "application"
Error: loading initial config: loading new config: loading http app module: provision http: loading pki app module: provision pki: provisioning CA 'local': loading root cert: open /data/caddy/pki/authorities/local/root.crt: permission denied
I had a similar issue to this and although the permissions all looked good on that folder when manually logging into the starting it up as a as a non root would always fail.
Turns out the volume on the host had incorrect permissions, so i dropped the container volume and rebuilt it and all is working now.
You can check on your system
docker volume ls | grep caddy
Then inspect the volume
docker volume inspect <volume>
That should give you the host location and you can update permissions directly on that (or nuke it and start again with the correct docker user setup)
even if I change with it:
FROM dunglas/frankenphp
ARG USER=www-data
ARG UID=1000
# add additional extensions here:
RUN install-php-extensions \
@composer \
pdo_mysql \
pdo_pgsql \
gd \
intl \
zip \
bcmath \
xdebug
# opcache # disable for local development
# WORKDIR /app
# Install git
RUN apt-get update && apt-get install -y git
RUN apt-get install -y libnss3-tools
# Install symfony cli
RUN curl -1sLf 'https://dl.cloudsmith.io/public/symfony/stable/setup.deb.sh' | bash
RUN apt-get update && apt-get install -y symfony-cli
# Install composer
# RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
ENV COMPOSER_ALLOW_SUPERUSER=1
# Copy the current directory contents into the container at /app
COPY . /app
RUN composer install
# RUN \
# # Add user and group
# groupadd -g ${UID} ${USER} && \
# useradd -u ${UID} -g ${USER} -m ${USER};
RUN \
# Use "adduser -D ${USER}" for alpine based distros
# useradd -D ${USER}; \
# Add additional capability to bind to port 80 and 443
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
# Give write access to /data/caddy and /config/caddy
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy;
USER ${USER}
RUN echo "END 🐘🐳🐧🚀"
`
I have this as an error that tells me I'm not root, which is logical but annoying.
Could you type in your terminal echo $UID
to confirm your UID is 1000? Then enter in your container and type echo $UID too. The www-data user isn't with UID 1000 probably
Hi, I try to get it running in a open shift environment. There is this concept of arbitrary user id's. Therefore I adjusted the folders recursively with the following commands:
RUN chgrp -R 0 /app/var/cache && chmod -R g=u /app/var/cache && chmod -R a+rw /app/var/cache
RUN chgrp -R 0 /app/var/log && chmod -R g=u /app/var/log && chmod -R a+rw /app/var/log
RUN chgrp -R 0 /data && chmod -R g=u /data && chmod -R a+rw /data
RUN chgrp -R 0 /config && chmod -R g=u /config && chmod -R a+rw /config
Additionally I added the following command:
RUN setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp;
I opened a debug shell and I can create, write, read files within the above mentioned folders. The command getcap returns the following result:
1000700000@frontend-76866bd7df-d9x68-debug-lhff9:/usr/local/bin$ getcap frankenphp
frankenphp cap_net_bind_service=eip
Unfortunately I always get the result "Operation not permitted" whenever I try to execute the frankenphp binary.
Has anyone some hint which resources the binary is also needing in order to work as a non-root user?
EDIT: Perhaps I found the answer .... "In addition, the processes running in the container must not listen on privileged ports (ports below 1024), since they are not running as a privileged user." https://docs.openshift.com/container-platform/4.2/openshift_images/create-images.html#use-uid_create-images
Unfortunately I always get the result "Operation not permitted" whenever I try to execute the frankenphp binary.
Openshift and K8s simply do not care what capabilities the process has inside the container. Instead, you have to use the provided tools (from k8s/open shift) to grant the privilege to the associated kernel namespace (pod security policies in k8s).
Has anyone some hint which resources the binary is also needing in order to work as a non-root user?
Maybe a workaround could be change the http/https ports to a non provileged ports, like 8080 and 8443 and using a reverse proxy in front of, or (if possible to use docker port mappings, like 80:8080 and 443:8443) so you don't need to give root permissins, capacities, etc or anything else to frankenphp
Has anyone some hint which resources the binary is also needing in order to work as a non-root user?
Maybe a workaround could be change the http/https ports to a non provileged ports, like 8080 and 8443 and using a reverse proxy in front of, or (if possible to use docker port mappings, like 80:8080 and 443:8443) so you don't need to give root permissins, capacities, etc or anything else to frankenphp
Hi, yes I already thought about that and regarding the open shift doc it is not possible to bind something to privileged ports. So I definetely have to use other ports than 80, 443.
On the other hand it is even not possible to get the command frankenphp -v
working. And at this point I assume no port binding is executed.
So therefore I'm afraid I have to use a classic nginx/php-fpm approach here.
@interose how could we reproduce the issue? Is deploying a non-root image in a Kubernetes cluster is enough to trigger the problem? If that's the case, I can take a look.
@interose how could we reproduce the issue? Is deploying a non-root image in a Kubernetes cluster is enough to trigger the problem? If that's the case, I can take a look.
@dunglas I have created a bare minimum Dockerfile
FROM dunglas/frankenphp
RUN chgrp -R 0 /data && chmod -R g=u /data && chmod -R a+rw /data
RUN chgrp -R 0 /config && chmod -R g=u /config && chmod -R a+rw /config
RUN chgrp -R 0 /etc/caddy && chmod -R g=u /etc/caddy && chmod -R a+rw /etc/caddy
ENTRYPOINT ["tail", "-f", "/dev/null"]
created a image via docker build -t frankenphp_test .
and pushed it into the open shift registry. If I create the container locally everything is working fine. It's this special environment (Openshift / Kubernetes) where it is not working, to be more precise, a simple call to frankenphp -v
gives me the following result: (tried also a php -v)
1000700000@frontend-654fdccc96-rm28j:/usr/local/bin$ ./php -v
PHP 8.3.6 (cli) (built: Apr 11 2024 18:05:35) (ZTS)
Copyright (c) The PHP Group
Zend Engine v4.3.6, Copyright (c) Zend Technologies
1000700000@frontend-654fdccc96-rm28j:/usr/local/bin$ ./frankenphp -v
bash: ./frankenphp: Operation not permitted
1000700000@frontend-654fdccc96-rm28j:/usr/local/bin$
So I guess without access to this environment it would be hard to reproduce the issue and to be honest - the more I dig into this (Openshift / Kubernetes) the more I get the feeling this is an edge case here. I also used gdb, strace, ... in order to maybe get some information why it gives me "Operation not permitted" but with no luck. So I definitely could investigate a little bit further if there are some things which I could test here.
@dunglas I am able to also reproduce in regular k8s even though it isn't binding to a lower port (afaik).
Could you do a ls -l ./frankenphp
, it looks like the binary itself doesn't have the good permissions.
It looks like the issue is here: https://github.com/dunglas/frankenphp/blob/main/Dockerfile#L93
This requires that the associated kernel namespace also have the requisite permissions. Removing this line means you can use high ports in k8s (and probably openshift) without also needing NET_BIND.
Could you do a
ls -l ./frankenphp
, it looks like the binary itself doesn't have the good permissions.
It was 755 (like the other executables within /usr/local/bin). To rule that out I changed it to 777 -> same result.
OMG - I noticed I had the wrong dir within one statement - sbin instead of bin. With this
FROM dunglas/frankenphp
RUN chgrp -R 0 /usr/local/bin && chmod -R g=u /usr/local/bin
ENTRYPOINT ["tail", "-f", "/dev/null"]
at least the command frankenphp -v
and frankenphp -h
works as expected. Sorry, sorry for that.
Now I'm going to test the whole story.
Ok but now i have this :
docker logs bff-iauto-frankenphp kev@kev {"level":"info","ts":1709142955.9294713,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"} {"level":"warn","ts":1709142955.93021,"msg":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"/etc/caddy/Caddyfile","line":16} {"level":"info","ts":1709142955.93076,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]} {"level":"info","ts":1709142955.9309363,"logger":"http.auto_https","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443} {"level":"info","ts":1709142955.9309967,"logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"} {"level":"info","ts":1709142955.931001,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc000525600"} {"level":"info","ts":1709142955.931198,"logger":"tls.cache.maintenance","msg":"stopped background certificate maintenance","cache":"0xc000525600"} Error: loading initial config: loading new config: loading http app module: provision http: loading pki app module: provision pki: provisioning CA 'local': loading root cert: open /data/caddy/pki/authorities/local/root.crt: permission denied
Its ok only with docker exec -it <name_container_franken> bash . We have www-data user , but after if i request i have this
@kilwir76 I'm not sure if this helps you but I was facing a similar issue and deleting the volumes caddy_data
and caddy_config
fixed my problem. I deleted them with docker volume rm
and restarted the services.