Container (non-root) user can't write to /dev/stdout or /dev/stderr

I’ve been wondering how it’s supposed to behave. We can likely make it work any way we want it to!

What’s the correct way this should work for our users you think?

1 Like

I’d like to find a way to get nginx to run as a non-root user and have its logs be visible via fly logs or the monitoring tab on the dashboard. Here’s a Dockerfile that doesn’t quite work today:

# syntax = docker/dockerfile:1

FROM debian:bullseye-slim

# Install nginx
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y nginx && \
    rm -rf /var/lib/apt/lists /var/cache/apt/archives

# configure nginx
RUN chown www-data:www-data /var/lib/nginx && \
    sed -i 's/80/8080/' /etc/nginx/sites-available/default && \
    sed -i 's/^user/#user/' /etc/nginx/nginx.conf && \
    sed -i 's/access_log\s.*;/access_log \/dev\/stdout;/' /etc/nginx/nginx.conf && \
    sed -i 's/error_log\s.*;/error_log \/dev\/stderr info;/' /etc/nginx/nginx.conf

# run without root privs
USER www-data:www-data

# Start nginx
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]

The error produced is:

[info]nginx: [emerg] open() "/dev/stdout" failed (13: Permission denied)
1 Like

Hi Jerome,

My expectation (and I believe I’m speaking for everyone) is that the anonymous pipes should be owned by the app’s user (same user as the container’s main process).

This allows the app to log by writing to /dev/stdout (which is a symlink to /proc/self/fd/1, which is a pseudo-symlink to the anonymous pipe). It is very useful, and some standard images rely on it (see the original post).

The code in the public init-snapshot repo already tries to do this, so I am curious why it’s not currently the case (code change? bug?). The ownership of the anonymous pipe can be verified by running stat -L /proc/self/fd/1.


The workaround I posted above should work, just add this to the Dockerfile:

ENTRYPOINT ["/bin/sh", "-c", "\"$@\" 2>&1 | cat", "/bin/sh"]

Or change CMD to

CMD nginx -g "daemon off;" 2>&1 | cat
1 Like

Ok, I think I have a fix for this. Here’s the output I get for my process:

$ ls -lah /proc/233/fd
total 0
dr-x------    2 appuser  appuser        0 Feb 20 17:09 .
dr-xr-xr-x    9 appuser  appuser        0 Feb 20 17:08 ..
lr-x------    1 appuser  appuser       64 Feb 20 17:09 0 -> /dev/null
l-wx------    1 appuser  appuser       64 Feb 20 17:09 1 -> pipe:[4328]
lrwx------    1 appuser  appuser       64 Feb 20 17:09 10 -> socket:[4404]
l-wx------    1 appuser  appuser       64 Feb 20 17:09 2 -> pipe:[4329]
# ...

Which looks right!

I su - appuser and then echoed into /proc/233/fd/1 and there were no errors and it did appear in my app logs.

I’ve pushed this change to all hosts now. You’ll have to restart your app instances to get the new init version (08b4c2b). If you’re using machines, you’ll need to update your machine to purge the rootfs cache for it.

4 Likes

Hurray, it’s working for me now – my humble nobody can log errors now. Thank you very much, this has been a pain for a loooooooooooooooooooooong time. Much appreciated.

2 Likes

Thanks @tom93 and Jerome (that was quick!)

Publish the new snapshot… :wink: