The container sets its data directory permissions itself before startup #340

Closed
opened 2026-09-29 11:07:46 +02:00 by clawbot · 2 comments
Collaborator

sneak, 2026-09-29 in chat, on the README instruction that the host data directory must be created and chowned to the app's uid before the first deploy (verbatim):

this is wrong. always make sure that the container sets appropriate permissions on the data dir before startup.

A standing rule for every app image: the container itself makes its data directory usable before the app starts. No operator step (mkdir, chown, chmod on the host) is needed or documented.

Definition of done:

  • The image starts correctly with an empty, root-owned host directory bind-mounted as its data directory, on first start and on every later start: an entrypoint running as root creates the directory if needed, sets its owner and mode for the app's user, then drops to that user (for example with su-exec or setpriv) and execs the app. The app process never runs as root.
  • It also works when the directory already holds data owned by another uid (for example from an earlier run under a different uid).
  • A test or a recorded run shows both cases: container healthy, app running as its unprivileged user, data written.
  • README: every instruction to pre-create or chown the host directory is removed; the upaas section says only which path to mount.
  • No change to where data lives or to the app's uid is needed; if one is, say why on this issue first.

Model: opus-5-5

sneak, 2026-09-29 in chat, on the README instruction that the host data directory must be created and chowned to the app's uid before the first deploy (verbatim): > this is wrong. always make sure that the container sets appropriate permissions on the data dir before startup. A standing rule for every app image: the container itself makes its data directory usable before the app starts. No operator step (mkdir, chown, chmod on the host) is needed or documented. Definition of done: - The image starts correctly with an empty, root-owned host directory bind-mounted as its data directory, on first start and on every later start: an entrypoint running as root creates the directory if needed, sets its owner and mode for the app's user, then drops to that user (for example with `su-exec` or `setpriv`) and execs the app. The app process never runs as root. - It also works when the directory already holds data owned by another uid (for example from an earlier run under a different uid). - A test or a recorded run shows both cases: container healthy, app running as its unprivileged user, data written. - README: every instruction to pre-create or chown the host directory is removed; the upaas section says only which path to mount. - No change to where data lives or to the app's uid is needed; if one is, say why on this issue first. Model: opus-5-5
clawbot self-assigned this 2026-09-29 11:07:46 +02:00
Author
Collaborator

Plan. The image today switches to USER webhooker (UID 1000) in the Dockerfile and runs /app/webhooker directly, so a root-owned bind mount stops it at its data directory lock.

  • Entrypoint: a small POSIX sh entrypoint, copied into the image, becomes ENTRYPOINT. CMD stays ["/app/webhooker"], so docker run IMAGE /app/webhooker resetpw ... still works. Drop USER webhooker. As root, the entrypoint:

    1. creates ${DATA_DIR:-/var/lib/webhooker} if it is missing;
    2. makes it owned by webhooker:webhooker with mode 0750, and makes its contents owned by that user too, but only when something inside is owned by someone else, so a large directory is not walked on every start;
    3. drops to webhooker and execs "$@".

    If the container was started as a non-root user already, the entrypoint only execs. The app process never runs as root.

  • Dropping privileges: use su-exec from Alpine (apk add, pinned to a version the way the Dockerfile pins its other packages) or setpriv. Pick one and say why on the PR in one line. File modes stay as the app sets them (0600 files).

  • Checks (a recorded run is enough; publish no logs), each with --mount type=bind, the way upaas mounts:

    1. an empty root-owned host directory: healthy, the app running as UID 1000 (ps inside the container), data written;
    2. a directory already holding data owned by another UID: the same, with that data intact and readable;
    3. a second start on the same directory: no first-boot banner;
    4. resetpw with the section's command.
  • README: remove every instruction to pre-create or chown the host directory:

    • "Running with Docker", including the "must be owned by UID 1000" block and its mkdir/chown/chmod commands;
    • "Running under upaas", whose volume bullet now says only which path to mount;
    • Restore, whose chown -R step goes, replaced by a sentence that the container fixes ownership at start.

    Say in one sentence where the image is described that the container sets its data directory's owner and mode itself before the app starts.

  • Unchanged: where data lives and the UID. If either must change, stop and say why on this issue first.

  • Keep it within the standing RAM cap; this needs no data fill.

Model: opus-5-5

Plan. The image today switches to `USER webhooker` (UID 1000) in the `Dockerfile` and runs `/app/webhooker` directly, so a root-owned bind mount stops it at its data directory lock. - **Entrypoint:** a small POSIX `sh` entrypoint, copied into the image, becomes `ENTRYPOINT`. `CMD` stays `["/app/webhooker"]`, so `docker run IMAGE /app/webhooker resetpw ...` still works. Drop `USER webhooker`. As root, the entrypoint: 1. creates `${DATA_DIR:-/var/lib/webhooker}` if it is missing; 2. makes it owned by `webhooker:webhooker` with mode `0750`, and makes its contents owned by that user too, but only when something inside is owned by someone else, so a large directory is not walked on every start; 3. drops to `webhooker` and `exec`s `"$@"`. If the container was started as a non-root user already, the entrypoint only `exec`s. The app process never runs as root. - **Dropping privileges:** use `su-exec` from Alpine (`apk add`, pinned to a version the way the `Dockerfile` pins its other packages) or `setpriv`. Pick one and say why on the PR in one line. File modes stay as the app sets them (`0600` files). - **Checks** (a recorded run is enough; publish no logs), each with `--mount type=bind`, the way upaas mounts: 1. an empty root-owned host directory: healthy, the app running as UID 1000 (`ps` inside the container), data written; 2. a directory already holding data owned by another UID: the same, with that data intact and readable; 3. a second start on the same directory: no first-boot banner; 4. `resetpw` with the section's command. - **README:** remove every instruction to pre-create or `chown` the host directory: - "Running with Docker", including the "must be owned by UID 1000" block and its `mkdir`/`chown`/`chmod` commands; - "Running under upaas", whose volume bullet now says only which path to mount; - Restore, whose `chown -R` step goes, replaced by a sentence that the container fixes ownership at start. Say in one sentence where the image is described that the container sets its data directory's owner and mode itself before the app starts. - **Unchanged:** where data lives and the UID. If either must change, stop and say why on this issue first. - Keep it within the standing RAM cap; this needs no data fill. Model: opus-5-5
sneak referenced this issue from a commit 2026-09-29 11:46:51 +02:00
sneak referenced this issue from a commit 2026-09-29 11:48:16 +02:00
Author
Collaborator

Built in #353: the image now starts as root through deploy/docker-entrypoint.sh, which makes DATA_DIR belong to webhooker and then runs the app as webhooker; the README no longer asks for any host-side step.

Model: opus-5-5

Built in https://git.eeqj.de/sneak/webhooker/pulls/353: the image now starts as root through `deploy/docker-entrypoint.sh`, which makes `DATA_DIR` belong to `webhooker` and then runs the app as `webhooker`; the README no longer asks for any host-side step. Model: opus-5-5
sneak closed this issue 2026-09-29 13:05:17 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#340