Open Maxwellfire opened 1 year ago
Reading more about this, it seems like this will need to be configured per system, since docker container uids must match a suitable service user on the host. In light of this, maybe the documentation could be updated to advise how to run as a non-root host.
On my system I created a user and group pigallery2:pigallery2
for the service, gave that user access to the required bind mount locations, and then also had to chown
the files under the docker pigallery2_db-data
volume to the correct user.
I then added the uid:guid
pair for pigallery2:pigallery2
to the docker compose file as:
version: '3'
services:
pigallery2:
image: bpatrik/pigallery2:latest
container_name: pigallery2
environment:
- NODE_ENV=production # set to 'debug' for full debug logging
user: <uid>:<guid>
volumes:
- "<config>:/app/data/config"
- "db-data:/app/data/db"
- "<images>:/app/data/images:ro"
- "<temp>:/app/data/tmp"
ports:
- 44771:80
restart: always
volumes:
db-data:
It's more than that to it. I think you need root bind.to.port 80 and 443. If you use nginx, then I guess it only applies to the reverse proxy.
Anyway as you mentioned it depends on the host. The docker-compose is only a simple way to run the app. I think the simplest is to do some write up here, that you did above and add a comment to the docker compose files.
Clould you send a PR for that?
I don't believe you need root for that, since all ports are allowed to be bound inside the docker container without root, and the docker daemon running as root handles the binding outside the container.
Yes I can submit a pull request. I believe that in order to make this easy, the docker image should be changed so that the files in db-data are owned by node
(and node
changed to have a uid less likely to collide). It does seem like this is one major deficiency of docker. See here.
I mean that the one container that exposes the final port to the host. That should bind to 80 and or 443. And binding to those port on the host needs special rights if I understand correctly.
I don't think that the user within the container matters much. To my understanding if the container runs as a non-root user, within the container the user can be root, it won't be able to do more on the host machine than the non-root user, that is used for running the container. But correct me if im wrong, I'm not a docker expert. I only provide the app with some minimal docker setup.
I recall a few months ago, I had to make an nginx container run as non-root because our kubernetes cluster had a security check in place to not allow containers running as root.
In that nginx container, I had to change the port it was listening on to achieve that. Revisiting that now, I see that ports < 1024 have to run as root. So inside an nginx container, if you have listen 80
in the nginx.conf, it's going to complain if you try to set up a non-privileged user. For the purpose of these security checks, the container itself doesn't need port 80/443, it can listen on 8080. That way, we can have the container run as non-root user and be security compliant. Then the final port mapping from host->container will need to be ran by a privileged user.
In my personal setup of pigallery, I have a wrapping Dockerfile to build the container using a non-root user. You can see my setup in this repo, with the pigallery Dockerfile here, and nginx (technically openresty) Dockerfile here.
I don't think this is true. Inside docker containers there are no privileged ports (assuming you're using docker networking and not host
networking). See https://github.com/moby/moby/pull/41030/commits. Essentially net.ipv4.ip_unprivileged_port_start
is set to 0
so all ports are considered unprivileged.
The container itself needs to bind port 80 on the host machine, but that is done by the docker daemon, not the software inside the container. If you need the docker daemon to run rootless, that's a whole other thing.
You're right, maybe I'm misremembering something from a few months ago, because I can run a non-root nginx listening on port 80.
Editing since I don't want to derail this thread too much: You're right that my kubernetes cluster is running on containerd instead of docker; that explains it. Thanks.
If you run Kubernetes with the containerd
runtime instead of the docker runtime, it looks like you'd still need the privileges as that runtime doesn't change net.ipv4.ip_unprivileged_port_start
by default. This should still be pretty easy to manage though, since like you said you just change the port that nginx is binding inside and then map that port to 80
through the docker daemon's networking.
I had no issue on debian setting the user uid/gid in docker compose. You need to choose an external port in the user range, but the internal port of 80 is fine. If you already ran as root you have chown all the files, include the ones on the db data volume.
There is no sane explanation here why inside 80 port is by default and not 8000. I mean the gallery should be rootless, that's the problem of reverse proxy to host on 80 port and have minimal attack surface.
I dont mind chaning the def port to 8080 or similar. but a very easy workaround for now is:
pigallery2:
image: bpatrik/pigallery2:latest
container_name: pigallery2
environment:
- NODE_ENV=production # set to 'debug' for full debug logging
- PORT=8080 # <------------
# - NODE_OPTIONS=--enable-source-maps # enable source map support on the backend for development
volumes:
- "./pigallery2/config:/app/data/config" # CHANGE ME
- "db-data:/app/data/db"
- "./pigallery2/images:/app/data/images:ro" # CHANGE ME, ':ro' means read-only
- "./pigallery2/tmp:/app/data/tmp" # CHANGE ME
expose:
- "8080:80" # <------------
restart: always
I did not double check the code about but should be similar. My point is that the used port can be set through an env variable.
I believe currently the dockerfile is written so that node is run with the root user. This goes against best security practices. I don't believe there is anything that pigallery does that needs a root user in the container. I believe the node image you are using provides user
node
which is recommended here