fix(install): grant the reaper traverse on the worlds root (#6)

Live drill found this: the reaper pod runs as uid 1000, k3s creates its
storage root /var/lib/rancher/k3s/storage 0700 root:root, so enabling
retention on a stock install made every archive fail
'lstat /worlds/<pvc>: permission denied' and skip the world (fail-closed,
but a silent no-op). bootstrap now grants traverse (setfacl u:1000:x,
else chmod o+x) when FELIS_WORLDS_HOST_PATH is set, the renderer's
precondition note names the requirement, and troubleshooting documents
both it and the multi-node nodeSelector fact.

Verified on the VM after granting the ACL: a 20d-idle world with a marker
file was archived into felis-backups (marker intact), its PVC and host
directory were reclaimed, world_backups got an inactive_15d row, and the
servers row/CR were retained.
This commit is contained in:
Lemon-miaow committed 2026-09-22 21:03:17 +08:00
1 parent fd33fd05e1
commit 2b87a5a13b
4 files changed
+59 -10

No files matched your search

+17 -1
View File
@@ -71,7 +71,9 @@
# FELIS_WORLDS_HOST_PATH node directory holding the world volumes (on the k3s this
# installer provisions: /var/lib/rancher/k3s/storage). Setting it
# enables the daily retention reaper, which archives and then deletes
# worlds idle beyond the retention window (default: unset = no reaper)
# worlds idle beyond the retention window, and grants the reaper's
# uid (1000) traverse access to that root — k3s ships it 0700
# root:root (default: unset = no reaper)
# PKG_LOCK_TIMEOUT seconds to wait for package-manager locks (default: 900)
# APT_LOCK_TIMEOUT legacy alias for PKG_LOCK_TIMEOUT
set -Eeuo pipefail
@@ -2175,6 +2177,20 @@ deploy_bundle() {
# always travels with it because it must equal the [archive] local_path written above.
if [ -n "$FELIS_WORLDS_HOST_PATH" ]; then
log "retention enabled: the daily reaper will read worlds from ${FELIS_WORLDS_HOST_PATH}"
# The reaper pod runs as the tree's non-root uid (1000, platform.workloads.nonRootUID)
# and must traverse into the per-volume directories under this root. k3s's own storage
# root ships 0700 root:root, so grant traverse — an ACL entry when the host has setfacl,
# otherwise the equivalent o+x. Traverse only: no listing either way, and the per-volume
# directories themselves are world-accessible (local-path creates them 0777).
if [ -d "$FELIS_WORLDS_HOST_PATH" ]; then
if command -v setfacl >/dev/null 2>&1; then
setfacl -m u:1000:x "$FELIS_WORLDS_HOST_PATH" || chmod o+x "$FELIS_WORLDS_HOST_PATH"
else
chmod o+x "$FELIS_WORLDS_HOST_PATH"
fi
else
warn "worlds root ${FELIS_WORLDS_HOST_PATH} does not exist yet; the reaper CronJob cannot start until it does (hostPath type Directory)"
fi
manifest_args+=(--worlds-host-path "$FELIS_WORLDS_HOST_PATH" --archive-local-path "$FELIS_ARCHIVE_LOCAL_PATH")
fi
"$HOST_BIN" manifests "${manifest_args[@]}" | kube apply -f -