From 816986139ff2c40b89a3837eb6f40048f2129ea8 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 22 Aug 2026 03:45:06 -0700 Subject: [PATCH] observability: run as container root 0:0, not 1000:1000 Measured the rootless mapping: container root IS the invoking user (dank, owner of the /opt data dirs); the images' default users (loki 10001, prometheus nobody, grafana 472) map to subuids that own nothing. 1000:1000 was the wrong direction. --- dankclaw/compose.yaml | 21 ++++++++++++--------- tests/caddyfile.bats | 13 +++++++------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/dankclaw/compose.yaml b/dankclaw/compose.yaml index c9992d5..27d7f54 100644 --- a/dankclaw/compose.yaml +++ b/dankclaw/compose.yaml @@ -103,9 +103,10 @@ services: image: docker.io/prom/prometheus:latest container_name: prometheus restart: unless-stopped - # Rootless: container uid 1000 maps to the invoking user, so the - # container can write its /opt/prometheus/data bind mount. - user: "1000:1000" + # Rootless: container root maps to the invoking user (dank), which + # owns /opt/prometheus/data. The image's default user (nobody) maps to + # a subuid that cannot write it. + user: "0:0" command: - --config.file=/etc/prometheus/prometheus.yml - --storage.tsdb.path=/prometheus @@ -126,9 +127,10 @@ services: image: docker.io/grafana/grafana:latest container_name: grafana restart: unless-stopped - # Rootless: container uid 1000 maps to the invoking user, so the - # container can write its /opt/grafana/data bind mount. - user: "1000:1000" + # Rootless: container root maps to the invoking user (dank), which + # owns /opt/grafana/data. The image's default user (472) maps to a + # subuid that cannot write it. + user: "0:0" environment: GF_SERVER_ROOT_URL: "https://grafana.franksdank.cloud" GF_SECURITY_ADMIN_USER: "${GRAFANA_ADMIN_USER}" @@ -150,9 +152,10 @@ services: image: docker.io/grafana/loki:latest container_name: loki restart: unless-stopped - # Rootless: container uid 1000 maps to the invoking user, so the - # container can write its /opt/loki/data bind mount. - user: "1000:1000" + # Rootless: container root maps to the invoking user (dank), which + # owns /opt/loki/data. The image's default user (10001) maps to a + # subuid that cannot write it. + user: "0:0" command: ["-config.file=/etc/loki/loki-config.yaml"] ports: - "3100:3100" diff --git a/tests/caddyfile.bats b/tests/caddyfile.bats index 0aec966..8998f88 100644 --- a/tests/caddyfile.bats +++ b/tests/caddyfile.bats @@ -119,10 +119,11 @@ CADDYFILE="$REPO_ROOT/dankclaw/Caddyfile" ! grep -nE '^\s*-\s*"?http' "$REPO_ROOT/shared/prometheus/prometheus.yml" } -@test "observability containers run as the host user (rootless uid mapping)" { - # Rootless podman maps container uid 1000 to the invoking user, so - # 1000:1000 lets these containers write their /opt data dirs. Container - # root would map to a subuid that owns nothing under /opt. +@test "observability containers run as container root (rootless = host user)" { + # In rootless podman, container root maps to the invoking user (dank), + # which owns the /opt data dirs. The images' default users (loki 10001, + # prometheus nobody, grafana 472) map to subuids that own nothing under + # /opt and cannot write the data dirs. local compose="$REPO_ROOT/dankclaw/compose.yaml" local svc block for svc in prometheus loki grafana; do @@ -130,8 +131,8 @@ CADDYFILE="$REPO_ROOT/dankclaw/Caddyfile" $0 ~ s { inblk = 1; next } inblk && /^ [a-z]/ { inblk = 0 } inblk' "$compose") - echo "$block" | grep -q 'user: "1000:1000"' || { - echo "service $svc does not run as user 1000:1000"; return 1; + echo "$block" | grep -q 'user: "0:0"' || { + echo "service $svc does not run as user 0:0"; return 1; } done }