Skip to content

fix(startup): keep nginx temp dirs owned by the nginx worker user - #952

Open
conorbronsdon wants to merge 1 commit into
madeofpendletonwool:mainfrom
conorbronsdon:fix/nginx-temp-ownership-940
Open

fix(startup): keep nginx temp dirs owned by the nginx worker user#952
conorbronsdon wants to merge 1 commit into
madeofpendletonwool:mainfrom
conorbronsdon:fix/nginx-temp-ownership-940

Conversation

@conorbronsdon

Copy link
Copy Markdown
Contributor

The PUID/PGID chown added in 123ae00 included /var/lib/nginx and
/var/lib/nginx/tmp. These images replace the packaged nginx.conf
(dockerfile:174, dockerfile-arm:165) and ours carries no 'user'
directive, so nginx uses its compile-time default and a master started as
root drops its workers to the 'nginx' user (uid 100). With PUID=0 the
temp tree therefore ends up root:root mode 0700 and those workers cannot
create proxy temp files:

[crit] open() "/var/lib/nginx/tmp/proxy/0/10/0000000100" failed
       (13: Permission denied) while reading upstream

When that happens nginx serves what it already has instead of failing, so
the client gets a short body with HTTP 200. Reported on
/api/data/podcast_episodes, where the mobile app parses the truncated
JSON, throws, and renders an empty list.

Response size alone does not trigger it, which is why it looks
intermittent: nginx only needs a temp file when it has to buffer ahead of
the client, so the same endpoint can return intact for a client that
drains as fast as the upstream delivers and truncate for a slower one.
The one log line it does produce goes to /var/log/nginx/error.log, which
nothing surfaces because horust captures only nginx's stdout/stderr
(startup/services/nginx.toml).

Only a root master can setuid. When privileges are actually dropped the
workers inherit PUID and the original chown was right, so the owner is
chosen rather than hard-coded:

privileges dropped (PUID and PGID set, PUID non-zero) -> PUID:PGID
otherwise (master stays root) -> nginx:nginx

The condition mirrors the privilege-drop guard below it, including the
PGID check: PUID set without PGID does NOT drop privileges, so the master
stays root and its workers are still 'nginx'. Testing PUID alone would
hand the dirs to PUID while nginx ran as 'nginx', reproducing the bug in
a configuration that works on main today. The zero test matches any
all-zero spelling for the same reason, since chown and su-exec both read
"00" as uid 0.

nginx's directories are removed from the PUID chown list so the two
cannot disagree.

Fixes #940


Verification

Rebuilt the relevant slice of the image (alpine + nginx, an nginx.conf with no user directive, a 20MB upstream body) and ran the branch logic directly, then forced nginx to spill to a proxy temp file with a rate-limited client.

Negative control, ownership broken with a root master: 7,008,182 of 20,263,158 bytes, HTTP 200, with the [crit] ... 13: Permission denied line quoted above.

Every PUID/PGID combination after the fix:

PUID PGID owner picked master worker result
unset unset nginx:nginx root nginx OK
0 0 nginx:nginx root nginx OK
1000 1000 1000:1000 pinepods pinepods OK
911 911 911:911 pinepods pinepods OK (compose default)
1000 unset nginx:nginx root nginx OK
unset 1000 nginx:nginx root nginx OK
0 1000 nginx:nginx root nginx OK
1000 0 1000:0 pinepods pinepods OK
100 101 100:101 nginx nginx OK
00 / 000 0 nginx:nginx root nginx OK

Also checked: chown -R does not traverse the package's symlinks (/var/lib/nginx/logs, modules, run) since busybox chown does not dereference them, so no system files are touched; the recursion is load-bearing across restarts with a named volume on /var/lib/nginx, correcting stale ownership in both directions; and nothing else in the repo references these paths.

The PUID/PGID chown added in 123ae00 included /var/lib/nginx and
/var/lib/nginx/tmp. These images replace the packaged nginx.conf
(dockerfile:174, dockerfile-arm:165) and ours carries no 'user'
directive, so nginx uses its compile-time default and a master started as
root drops its workers to the 'nginx' user (uid 100). With PUID=0 the
temp tree therefore ends up root:root mode 0700 and those workers cannot
create proxy temp files:

    [crit] open() "/var/lib/nginx/tmp/proxy/0/10/0000000100" failed
           (13: Permission denied) while reading upstream

When that happens nginx serves what it already has instead of failing, so
the client gets a short body with HTTP 200. Reported on
/api/data/podcast_episodes, where the mobile app parses the truncated
JSON, throws, and renders an empty list.

Response size alone does not trigger it, which is why it looks
intermittent: nginx only needs a temp file when it has to buffer ahead of
the client, so the same endpoint can return intact for a client that
drains as fast as the upstream delivers and truncate for a slower one.
The one log line it does produce goes to /var/log/nginx/error.log, which
nothing surfaces because horust captures only nginx's stdout/stderr
(startup/services/nginx.toml).

Only a root master can setuid. When privileges are actually dropped the
workers inherit PUID and the original chown was right, so the owner is
chosen rather than hard-coded:

  privileges dropped (PUID and PGID set, PUID non-zero) -> PUID:PGID
  otherwise (master stays root)                         -> nginx:nginx

The condition mirrors the privilege-drop guard below it, including the
PGID check: PUID set without PGID does NOT drop privileges, so the master
stays root and its workers are still 'nginx'. Testing PUID alone would
hand the dirs to PUID while nginx ran as 'nginx', reproducing the bug in
a configuration that works on main today. The zero test matches any
all-zero spelling for the same reason, since chown and su-exec both read
"00" as uid 0.

nginx's directories are removed from the PUID chown list so the two
cannot disagree.

Fixes madeofpendletonwool#940
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nginx proxy temp directory permissions broken by startup.sh PUID/PGID chown — truncates large API responses

1 participant