Skip to content

container-nesting

Version 2026.185.1940
Repo superproject

Rootless nested podman/buildah/skopeo inside a rootless outer container – zero added capabilities, no –privileged. Ports quay.io/podman/stable’s canonical rootless-in-rootless configuration into the candy system. Installs podman + crun + buildah + skopeo + fuse-overlayfs, writes containers.conf with every namespace=host (the userns=host line is what bypasses the kernel mount_too_revealing /proc check), a two-range subuid layout that fits the keep-id window, newuidmap/newgidmap file caps, policy.json and storage.conf – so an inner podman run of a fresh container succeeds with no cap_add and no –privileged. Every artifact below is observable in the built image; the end-to-end nested run is provable on a live deployment.

Installed on every distro:

  • buildah
  • fuse-overlayfs
  • skopeo

This candy’s plan: — the runnable spec charly check executes against a live deployment. check: steps are idempotent probes; run: steps change state.

Intent Step
run command=touch /etc/subuid /etc/subgid sed -i “/^root:/d” /etc/subuid /etc/subgid echo “root:1:65535” >> /etc/subuid echo “root:1:65535” >> /etc/subgid uid1000=$(getent passwd 1000 2>/dev/null | cut -d: -f1 || true) if [ -n “${uid1000}” ] && [ “${uid1000}” != “root” ]; then sed -i “/^${uid1000}:/d” /etc/subuid /etc/subgid { echo “${uid1000}:1:999” echo “${uid1000}:1001:64535” } >> /etc/subuid { echo “${uid1000}:1:999” echo “${uid1000}:1001:64535” } >> /etc/subgid fi
run grant cap_setuid/cap_setgid to newuidmap/newgidmap AND strip the setuid-root bit. Debian/Ubuntu’s uidmap package ships these binaries setuid-root (mode 4755); a newuidmap that is BOTH setuid-root AND file-capability’d fails inside a nested rootless user namespace with “newuidmap: open of uid_map failed: Permission denied”. Fedora/Arch ship them cap-only (no setuid bit), so chmod u-s makes every distro match that working posture and is a no-op where the bit is already absent. RDD-confirmed 2026-06-16.
run mkdir=/etc/containers
run mkdir=${HOME}/.config/containers
run write=/etc/containers/containers.conf
run write=${HOME}/.config/containers/containers.conf
run write=/etc/containers/policy.json
run write=${HOME}/.config/containers/policy.json
run write=/etc/containers/storage.conf
run write=${HOME}/.config/containers/storage.conf
check the podman container engine is installed for in-container use
check the buildah image-build tool is installed
check the skopeo image-copy tool is installed
check containers.conf pins userns=host so the inner /proc mount is not rejected by the kernel mount_too_revealing check
check policy.json is present so podman pull/run accepts an image signature policy
check newuidmap carries the cap_setuid file capability so a nested user namespace can be delegated
check newuidmap is NOT setuid-root — only the cap_setuid file capability remains; a binary that is BOTH setuid-root (Debian/Ubuntu’s uidmap default) AND cap-bearing fails to write uid_map inside a nested rootless user namespace
check /etc/subuid declares the username:start:count delegation ranges the inner podman maps from
agent-check a fresh rootless podman run of an inner container succeeds end-to-end with no added capabilities and no –privileged
check command=/usr/bin/podman –version
check command=/usr/bin/buildah –version
check command=/usr/bin/skopeo –version
check file=/usr/bin/newuidmap
check file=/usr/bin/newgidmap
check file=/usr/bin/tailscale
check command=/usr/bin/tailscale version
check the uid-1000 user has exactly two subuid delegation ranges (cross-distro — the user is user on fedora/arch but ubuntu/debian under adopt user-policy, so resolve the name via getent rather than hardcoding user:)
check command=grep -E ‘^userns\s*=\s*“host”’ ${HOME}/.config/containers/containers.conf
check command=sh -c ‘env | grep -q “^_CONTAINERS_USERNS_CONFIGURED=”’
check command=sh -c ‘env | grep -qx “BUILDAH_ISOLATION=chroot”’
check command=podman run –rm quay.io/libpod/alpine:latest /bin/true