blakeblackshear / frigate

NVR with realtime local object detection for IP cameras
https://frigate.video
MIT License
18.08k stars 1.65k forks source link

Should processes inside Docker container be run as a non-root user? #3108

Open felixc opened 2 years ago

felixc commented 2 years ago

I'm not an expert in Docker stuff, but I believe I've read that it's a good practice to run processes inside the container as a non-root user (defence in depth, and all that).

Might this be as simple as adding something like this to the Dockerfile?

RUN groupadd --system frigate && useradd --system -g frigate frigate
USER frigate

If some of the processes do in fact need to run as root, perhaps instead of using USER to change the settings across the board, the processes that do not need root could be launched as a dedicated user?

I did see https://github.com/blakeblackshear/frigate/issues/1233 but the discussion seems to be about --privileged rather than the root user, despite the title, so I'm not sure if it applies here? Apologies if I misunderstood and this is a duplicate. For what it's worth, I haven't been able to get hold of any Coral hardware, so although I am running everything just fine without --privileged, I can't confirm whether any of this would work with an actual Coral accelerator present.

blakeblackshear commented 2 years ago

I am well aware that running as root isn't a best security practice, but t isn't that simple since frigate requires access to Coral hardware and GPU for decoding video.

bhubbb commented 2 years ago

@blakeblackshear it should just be adding the group permission to the user and mounting/adding the device to the container. Probably video or dialout.

Are you able to do a ls -l on the coral device under /dev?

EDIT: Having a look at the TensorFlow Docker page it might even just be adding --gpus all.

blakeblackshear commented 2 years ago

Yep. I know how to do it.

bhubbb commented 2 years ago

OK I must be misunderstanding or missing some information. What is the bit that makes it not simple?

blakeblackshear commented 2 years ago

Different users run different hardware. Each user will need to know which groups to pass to the container. If you don't install the driver on the host, the groups aren't created for the USB device. Some users will need to modify udev rules to make things sure the group is applied to the device.

bhubbb commented 2 years ago
bhubbb commented 2 years ago

I miss read your reply, sorry about that. I can also see you have done a lot of work in the documentation around this.

I think there is wriggle room here though:

For that second image it should just be what @felixc said above and --gpus all or am I still missing something here?

blakeblackshear commented 2 years ago

You can set the user and groups at runtime with docker to override the default in the container. There isn't really a need to build a separate container. I would still like to eventually use non-root users by default, but it's just not a priority.

stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

patrickli commented 1 year ago

You can set the user and groups at runtime with docker to override the default in the container. There isn't really a need to build a separate container. I would still like to eventually use non-root users by default, but it's just not a priority.

This doesn't work. Container start up fails for the following:

frigate  | chown: changing ownership of '/dev/shm/logs/frigate': Operation not permitted
frigate  | chown: changing ownership of '/dev/shm/logs/go2rtc': Operation not permitted
frigate  | chown: changing ownership of '/dev/shm/logs/nginx': Operation not permitted

So we have to run frigate as root for now.

adriel commented 1 year ago

Yeah, I get the same thing as @patrickli when setting user: in docker compose to my local (non-root) user. Is there any way around this?

I really want the recordings/snapshots frigate makes to save as my user, so I don't need root (or sudo) to move them to my backup setup. (frigate doesn't notice the move as it's behind mergerfs)

patrickli commented 1 year ago

I haven't tested this but in the start up script, just don't do chown if the running user is not root. The culprit is this line https://github.com/blakeblackshear/frigate/blob/dev/docker/rootfs/etc/s6-overlay/s6-rc.d/log-prepare/run#L10. It basically tries to make these directories unprivileged. But we are already running unprivileged.

adriel commented 1 year ago

Good point. I tried commenting out the chown there, but seems to cause other issues.

frigate  | s6-rc: info: service go2rtc: starting
frigate  | s6-rc: info: service go2rtc successfully started
frigate  | s6-rc: info: service go2rtc-healthcheck: starting
frigate  | s6-rc: info: service frigate: starting
frigate  | s6-applyuidgid: fatal: unable to set supplementary group list: Operation not permitted
frigate  | s6-rc: info: service frigate successfully started
frigate  | s6-rc: info: service nginx: starting
frigate  | s6-applyuidgid: fatal: unable to set supplementary group list: Operation not permitted
frigate  | s6-rc: info: service go2rtc-healthcheck successfully started
frigate  | s6-rc: info: service nginx successfully started
frigate  | s6-rc: info: service legacy-services: starting
frigate  | s6-applyuidgid: fatal: unable to set supplementary group list: Operation not permitted
frigate  | s6-rc: info: service legacy-services successfully started
frigate  | s6-rc: info: service legacy-services: stopping
frigate  | s6-rc: info: service legacy-services successfully stopped
frigate  | s6-rc: info: service nginx: stopping
frigate  | s6-rc: info: service go2rtc-healthcheck: stopping
frigate  | s6-rc: info: service nginx successfully stopped
frigate  | s6-rc: info: service nginx-log: stopping
patrickli commented 1 year ago

Yeah just need to take some time and find them all I guess.

maxirus commented 1 year ago

Has anyone figured this out? I'm running mine in K8s and can't run as root.

rotilho commented 1 year ago

@maxirus depending how you configured your k8s, you can run it as privilleged

Unfortunately it does work without it.

PterX commented 1 year ago

has any one get it work

AnnoyingTechnology commented 9 months ago

Also interested in this, if anyone has managed to get it working.

marleyjaffe commented 8 months ago

Any progress on allowing frigate to run as a user and not root in docker? I ran as user: $PUID:$PGID but am getting the following errors:

chown: changing ownership of '/dev/shm/logs/frigate': Operation not permitted
chown: changing ownership of '/dev/shm/logs/go2rtc': Operation not permitted
chown: changing ownership of '/dev/shm/logs/nginx': Operation not permitted
anjanms commented 6 months ago

I’ve managed to get this working to some satisfaction with a USB Coral & Intel iGPU, but it isn’t pretty. For anyone wanting to dive down the non-root rabbit hole, this may serve as a starting point. However bear in mind that this customization may break or need modification with newer versions.

The following script needs to be executable and mounted within the container, ideally as read-only. Then configure the container’s environment;

This script disables Go2RTC simply because I run it outside of Frigate. But with some s6-rc.d modifications similar to what I’ve done for Frigate, theoretically it could also run without root.

#!/bin/bash
# {{ ansible_managed }}

# Disable Go2RTC

rm -r /etc/s6-overlay/s6-rc.d/{go2rtc,go2rtc-healthcheck,go2rtc-log}
rm /etc/s6-overlay/s6-rc.d/user/contents.d/go2rtc-pipeline

rm /etc/s6-overlay/s6-rc.d/frigate/dependencies.d/go2rtc
touch /etc/s6-overlay/s6-rc.d/frigate/dependencies.d/base

# Nginx Drop Privileges

sed -i 's/^user root;$/user www-data;/' /usr/local/nginx/conf/nginx.conf

# Frigate Drop Privileges

mv /etc/s6-overlay/s6-rc.d/frigate/run /etc/s6-overlay/s6-rc.d/frigate/run.upstream
sed -i '1c #!/bin/bash' /etc/s6-overlay/s6-rc.d/frigate/run.upstream
sed -i 's/^\(s6-svc -O \.\)$/#\1/' /etc/s6-overlay/s6-rc.d/frigate/run.upstream

cat << EOF > /etc/s6-overlay/s6-rc.d/frigate/run
#!/command/with-contenv /bin/bash
# Migrated from upstream, needs to be in the run file
s6-svc -O .

# User friendly reminder, suppress file not found errors
echo 'Go2RTC is disabled within the container' > /dev/shm/logs/go2rtc/current

# Handle warning about inability to use directory in /root
export MPLCONFIGDIR=/tmp/matplotlib

# https://skarnet.org/software/s6/s6-applyuidgid.html
chown "\${UID}:\${GID}" /config /media/frigate
exec s6-applyuidgid -z -U /etc/s6-overlay/s6-rc.d/frigate/run.upstream
EOF
chmod +x /etc/s6-overlay/s6-rc.d/frigate/run

# Frigate Disable PID 1 Kill

sed -i 's/proc\.name() == "s6-svscan"/False/' /opt/frigate/frigate/util/services.py

# Frigate Exit Halts Container

cat << EOF > /etc/s6-overlay/s6-rc.d/frigate/finish
#!/bin/bash
/run/s6/basedir/bin/halt
EOF
chmod +x /etc/s6-overlay/s6-rc.d/frigate/finish

For additional reference, my (ansible) container configuration & host udev rules.

- name: Create Frigate Container
  containers.podman.podman_container:
    name: frigate
    image: ghcr.io/blakeblackshear/frigate:stable
    cap_drop: [ALL]
    capabilities: [SETUID, SETGID, CHOWN, FOWNER, DAC_OVERRIDE, KILL]
    env:
      UID: "{{ getent_passwd['frigate'][1] }}"
      GID: "{{ getent_passwd['frigate'][2] }}"
      GIDLIST: "{{ getent_group['render'][1] }},{{ getent_group['plugdev'][1] }}"
      S6_STAGE2_HOOK: '/frigate-s6-stage2-hook'
    env_file: /root/.secrets/pzznr38yyqzcyv2wwa6iua6cj0rfjpwasx1hq6bltxipqw0af8b0nlhdvcutqml
    device:
      - /dev/bus/usb:/dev/bus/usb
      - /dev/dri/renderD128:/dev/dri/renderD128
    ports:
      - 127.0.0.1:5000:5000/tcp
    tmpfs:
      /dev/shm: rw,noexec,nosuid,size=256m
      /tmp/cache: rw,noexec,nosuid,size=1024m
    volumes:
      - /usr/local/lib/frigate-s6-stage2-hook:/frigate-s6-stage2-hook:ro
      - /usr/local/etc/frigate.yml:/config/config.yml:ro
      - /data/srv/frigate:/data/srv/frigate
      - /srv/frigate:/media/frigate
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1a6e", ATTRS{idProduct}=="089a", MODE="0664", GROUP="plugdev"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0664", GROUP="plugdev"
a10kiloham commented 2 months ago

Has anyone come around to a simpler workaround yet?