diff --git a/.dockerignore b/.dockerignore index 0424638..63c6451 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,6 +5,7 @@ # Build tools may add their own copy of the Dockerfile (Arcane writes # .arcane.external.Dockerfile), which has to reach the builder. !*Dockerfile* +!docker-entrypoint.sh !go.mod !go.sum !cmd/ diff --git a/.env.example b/.env.example index cc7c2b6..3c2c69a 100644 --- a/.env.example +++ b/.env.example @@ -31,6 +31,11 @@ CANON_IGDB_CLIENT_SECRET= # CANON_DATA_PATH=/srv/canon # Where backups are written. Default: the canon_canon-backups volume. # CANON_BACKUP_PATH=/mnt/nas/canon-backups +# Owner of the files Canon writes (database, images, backups). The Compose +# file defaults to Unraid's 99:100 (nobody:users). +# PUID=99 +# PGID=100 +# UMASK=022 # Host address and port to publish on. # CANON_BIND=0.0.0.0 # CANON_PUBLISH_PORT=4242 diff --git a/Dockerfile b/Dockerfile index ec5b0d0..34f80d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,15 +7,20 @@ ARG VERSION=dev RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" -o /out/canon ./cmd/canon FROM alpine:3 -RUN apk add --no-cache ca-certificates tzdata \ - && adduser -D -u 1000 canon \ - && mkdir -p /data /backups && chown canon:canon /data /backups +RUN apk add --no-cache ca-certificates tzdata su-exec \ + && mkdir -p /data /backups COPY --from=build /out/canon /usr/local/bin/canon -USER canon +COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh +RUN chmod 755 /usr/local/bin/docker-entrypoint.sh +# The entrypoint starts as root only to set ownership, then runs Canon as +# PUID:PGID. ENV CANON_DATA_DIR=/data \ - CANON_PORT=4242 + CANON_PORT=4242 \ + PUID=1000 \ + PGID=1000 \ + UMASK=022 VOLUME /data /backups EXPOSE 4242 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s \ CMD wget -qO- http://127.0.0.1:${CANON_PORT}/healthz >/dev/null || exit 1 -ENTRYPOINT ["canon"] +ENTRYPOINT ["docker-entrypoint.sh"] diff --git a/README.md b/README.md index 512b457..ca55fda 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,8 @@ Keys entered in the UI are stored in the SQLite database on your server. Keys se | `CANON_HOST` | `0.0.0.0` | Interface to listen on. `0.0.0.0` makes it reachable from other devices on your network; `127.0.0.1` keeps it on this machine | | `CANON_PORT` | `4242` | Listen port | | `CANON_DATA_DIR` | `/data` (Docker), `./data` | Database + cached images | +| `PUID` / `PGID` | `1000` (image), `99`/`100` (Compose) | Docker only: the user and group that own everything Canon writes. The Compose file defaults to Unraid's `nobody:users` | +| `UMASK` | `022` | Docker only: permissions for new files (`002` makes them group-writable) | | `CANON_ADMIN_USERNAME` / `_PASSWORD` | | Create the owner account on first start | | `CANON_TMDB_API_KEY` | | | | `CANON_TVDB_API_KEY` / `CANON_TVDB_PIN` | | The PIN is only for user-supported keys | diff --git a/docker-compose.yml b/docker-compose.yml index 0e31e51..247a861 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,6 +28,11 @@ services: - ${CANON_BACKUP_PATH:-canon-backups}:/backups environment: CANON_BACKUP_DIR: /backups + # Who owns the files Canon writes. 99:100 is Unraid's nobody:users; + # use 1000:1000 (or your own `id -u`:`id -g`) elsewhere. + PUID: ${PUID:-99} + PGID: ${PGID:-100} + UMASK: ${UMASK:-022} env_file: - path: .env required: false diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..301e25f --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# Runs Canon as PUID:PGID (default 1000:1000) so files it creates on mounted +# folders belong to that user, e.g. 99:100 (nobody:users) on Unraid. +set -eu + +PUID="${PUID:-1000}" +PGID="${PGID:-1000}" +UMASK="${UMASK:-022}" + +case "$PUID$PGID" in + *[!0-9]*) echo "canon: PUID and PGID must be numbers (got PUID=$PUID PGID=$PGID)" >&2; exit 1 ;; +esac +case "$UMASK" in + *[!0-7]*) echo "canon: UMASK must be octal, like 022 or 002 (got $UMASK)" >&2; exit 1 ;; +esac +umask "$UMASK" + +# Started with `user:` set in Compose or `--user`: nothing to switch. +if [ "$(id -u)" != 0 ]; then + exec canon "$@" +fi + +data="${CANON_DATA_DIR:-/data}" +backups="${CANON_BACKUP_DIR:-$data/backups}" +for dir in "$data" "$backups"; do + mkdir -p "$dir" + # Only touch what's owned by someone else, so restarts stay fast. + if ! find "$dir" \( ! -user "$PUID" -o ! -group "$PGID" \) -exec chown "$PUID:$PGID" {} + 2>/dev/null; then + echo "canon: couldn't give $PUID:$PGID ownership of everything in $dir (a network share?); continuing" >&2 + fi +done + +export HOME="$data" +exec su-exec "$PUID:$PGID" canon "$@"