Permissions Error Attempting To Read From stdout

Dockerfile:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-bullseye AS base

RUN apt-get update && apt-get -y upgrade && apt-get -y install git

FROM base AS compile

WORKDIR /app

RUN pip install -U build

COPY .git/ ./.git/
COPY LICENSE.md pyproject.toml supervisord.conf ./
COPY src/ ./src/

RUN python -m build -w

FROM base AS deploy

EXPOSE 5000

RUN useradd -m -G tty fakeuser && mkdir /data && chown fakeuser:fakeuser /data
VOLUME ["/data"]

WORKDIR /home/fakeuser
USER fakeuser

ENV PATH="/home/fakeuser/.local/bin:$PATH"
RUN pip install gunicorn supervisor

COPY --from=compile /app/dist/*.whl /app/supervisord.conf ./
RUN pip install *.whl

ENTRYPOINT (k1-create-db ${K1_DATA_DB} || true) && k1-start-all

supervisor conf:

[supervisord]
logfile=/dev/stdout
logfile_maxbytes=0
loglevel=info
pidfile=/tmp/supervisord.pid
nodaemon=true

[unix_http_server]
file=/tmp/supervisor.sock

[program:frontend]
command=gunicorn -w 4 -b 0.0.0.0:5000 k1insights.frontend:app
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0

[program:backend]
command=k1-start-backend
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0

Logs:

2022-05-14T16:44:25Z   [info]Preparing to run: `/bin/sh -c (k1-create-db ${K1_DATA_DB} || true) && k1-start-all` as fakeuser
2022-05-14T16:44:25Z   [info]2022/05/14 16:44:25 listening on [fdaa:0:640f:a7b:21e0:0:f4ee:2]:22 (DNS: [fdaa::3]:53)
2022-05-14T16:44:26Z   [info]Not overwriting /data/k1.db; delete the file or use '-f'
2022-05-14T16:44:26Z   [info]Traceback (most recent call last):
2022-05-14T16:44:26Z   [info]  File "/home/fakeuser/.local/bin/k1-start-all", line 8, in <module>
2022-05-14T16:44:26Z   [info]    sys.exit(main())
2022-05-14T16:44:26Z   [info]  File "/home/fakeuser/.local/lib/python3.8/site-packages/supervisor/supervisord.py", line 359, in main
2022-05-14T16:44:26Z   [info]    go(options)
2022-05-14T16:44:26Z   [info]  File "/home/fakeuser/.local/lib/python3.8/site-packages/supervisor/supervisord.py", line 369, in go
2022-05-14T16:44:26Z   [info]    d.main()
2022-05-14T16:44:26Z   [info]  File "/home/fakeuser/.local/lib/python3.8/site-packages/supervisor/supervisord.py", line 72, in main
2022-05-14T16:44:26Z   [info]    self.options.make_logger()
2022-05-14T16:44:26Z   [info]  File "/home/fakeuser/.local/lib/python3.8/site-packages/supervisor/options.py", line 1492, in make_logger
2022-05-14T16:44:26Z   [info]    loggers.handle_file(
2022-05-14T16:44:26Z   [info]  File "/home/fakeuser/.local/lib/python3.8/site-packages/supervisor/loggers.py", line 417, in handle_file
2022-05-14T16:44:26Z   [info]    handler = FileHandler(filename)
2022-05-14T16:44:26Z   [info]  File "/home/fakeuser/.local/lib/python3.8/site-packages/supervisor/loggers.py", line 160, in __init__
2022-05-14T16:44:26Z   [info]    self.stream = open(filename, mode)
2022-05-14T16:44:26Z   [info]PermissionError: [Errno 13] Permission denied: '/dev/stdout'
2022-05-14T16:44:27Z   [info]Main child exited normally with code: 1

Running as root doesn’t help, I get an illegal seek error(?); it Runs On My Machine ™ so I can’t tell what the problem is.

Ah, yes. I had this too. Take a look at Fly’s example:

[supervisord]
logfile=/dev/stdout 
logfile_maxbytes=0  
loglevel=info
pidfile=/tmp/supervisord.pid
nodaemon=true
user=root

...

I believe the magic line was logfile_maxbytes=0 . Else yep, you get a seek error.

I copied my conf file from that post; logfile_maxbytes=0 is already there and doesn’t explain why I cannot access /dev/stdout as a non-root user.

Ah, their example includes user=root yet oddly you say that running as root doesn’t help. Strange. I used supervisor to run nginx and php-fpm for a demo app, and it worked. I needed all these lines to avoid the seek error.

Since supervisor is a background process keeping everything else running I’m not sure running as root is a problem. But perhaps there is a good reason you don’t want to. In which case, not sure.

You shouldn’t need root for writing to stdout.

Nothing but the parent program (our VM init) can read from /dev/stdout though.

logfile_maxbytes=0 is definitely needed so supervisord doesn’t attempt to read from /dev/stdout.

Sample output from an example app:

/ $ ls -lah /proc/514/fd
total 0
dr-x------    2 appuser  appuser        0 May 14 18:39 .
dr-xr-xr-x    9 appuser  appuser        0 May 14 18:39 ..
lr-x------    1 appuser  appuser       64 May 14 18:39 0 -> /dev/null # stdin
l-wx------    1 appuser  appuser       64 May 14 18:39 1 -> pipe:[4358] # stdout
l-wx------    1 appuser  appuser       64 May 14 18:39 2 -> pipe:[4359] # stderr
# ...

(note there’s no r bit on either stdout and stderr)

Where should I look to make sure nothing is trying to read from stdout? Is there anything in my Dockerfile/supervisord file that looks out of place?

Changed my Dockerfile to this:

# syntax=docker/dockerfile:1

FROM python:3.8-slim-bullseye AS base

RUN apt-get update && apt-get -y upgrade && apt-get -y install git

FROM base AS compile

WORKDIR /app

RUN pip install -U build

COPY .git/ ./.git/
COPY LICENSE.md pyproject.toml supervisord.conf ./
COPY src/ ./src/

RUN python -m build -w

FROM base AS deploy

EXPOSE 5000
RUN useradd -m fakeuser && pip install gunicorn supervisor
VOLUME ["/home/fakeuser/data"]
COPY --from=compile /app/dist/*.whl /app/supervisord.conf ./
RUN pip install *.whl

ENTRYPOINT (k1-create-db ${K1_DATA_DB} || true) && k1-start-all

My deploy fails when my supervisord conf looks like this:

[supervisord]
user=fakeuser
logfile=/dev/stdout
logfile_maxbytes=0
loglevel=info
pidfile=/tmp/supervisord.pid
nodaemon=true

[unix_http_server]
file=/tmp/supervisor.sock

[program:frontend]
command=gunicorn -w 4 -b 0.0.0.0:5000 k1insights.frontend:app
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0

[program:backend]
command=k1-start-backend
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0

But changing the user to root makes it pass. I don’t know why I was getting the illegal seek error before and not now, but I think the core issue is that I need to do more than useradd to create a working user and nobody seems to know what that “more stuff” is.