The Debian-shipped passt AppArmor profile (/etc/apparmor.d/usr.bin.passt)
denies the accesses pasta needs to plumb rootlesskit's netns:
- ptrace_read on the rootlesskit child to enter its user namespace
- read /run/user/<uid>/dockerd-rootless/netns (the netns file)
- read /proc/<pid>/net/{tcp,tcp6,udp,udp6} for implicit port forwarding
Without these the rootless docker daemon fails with:
pasta failed with exit code 1:
Couldn't open user namespace /proc/<pid>/ns/user: Permission denied
scripts/docker/install/rootless/rootless_apparmor.sh:
New installRootlessApparmorForPasta() — idempotent fixup.
1. Adds `include if exists <local/usr.bin.passt>` to the main profile
(one line; re-adding is a no-op via grep).
2. Writes /etc/apparmor.d/local/usr.bin.passt with the four rules
pasta needs. The /local/ pattern is the standard Debian AppArmor
hook for site-managed overrides — survives `apt upgrade passt`
because it's outside the package's managed paths.
3. Reloads via apparmor_parser -r.
Called from installDockerRootless after the override.conf write, gated
on $rootless_net == pasta. slirp4netns installs skip it.
This box was already manually patched while debugging the pasta swap —
the installer-side change makes it idempotent across reinstalls and
applies the same fix on any other host that installs rootless docker
with pasta as the net driver.
Signed-off-by: librelad <librelad@digitalangels.vip>
91 lines
4.2 KiB
Bash
91 lines
4.2 KiB
Bash
#!/bin/bash
|
|
|
|
# AppArmor profile shim for the rootless Docker + pasta combo.
|
|
#
|
|
# Debian's stock `passt` profile (shipped by the `passt` package, used for the
|
|
# pasta network driver) is enforce-mode and denies the accesses pasta needs to
|
|
# wire up rootlesskit's net namespace:
|
|
#
|
|
# - ptrace(read) into the rootlesskit child to enter its user namespace
|
|
# - read /run/user/<uid>/dockerd-rootless/netns
|
|
# - read /proc/<pid>/net/{tcp,tcp6,udp,udp6} for implicit port-forwarding
|
|
#
|
|
# Without these the daemon fails to start with
|
|
# error: failed to setup network: pasta failed with exit code 1:
|
|
# Couldn't open user namespace /proc/<pid>/ns/user: Permission denied
|
|
#
|
|
# We extend the profile via the standard local-override pattern:
|
|
# /etc/apparmor.d/local/usr.bin.passt — our rules
|
|
# /etc/apparmor.d/usr.bin.passt — include if exists <local/...>
|
|
#
|
|
# Idempotent — safe to call on every rootless install. The local file survives
|
|
# `apt upgrade passt` (it's outside the package's managed paths). The one-line
|
|
# include statement could theoretically get reverted by an apt upgrade; we
|
|
# re-add it on every run as a belt-and-braces guard.
|
|
|
|
installRootlessApparmorForPasta()
|
|
{
|
|
# Bail silently when apparmor isn't on this system at all — Fedora/RHEL,
|
|
# ungrouped Debian minimals, etc. Nothing to patch.
|
|
if [[ ! -d /etc/apparmor.d ]]; then
|
|
return 0
|
|
fi
|
|
if [[ ! -f /etc/apparmor.d/usr.bin.passt ]]; then
|
|
isNotice "AppArmor present but no /etc/apparmor.d/usr.bin.passt — passt may be too old or not installed under apparmor."
|
|
return 0
|
|
fi
|
|
|
|
isHeader "AppArmor: enabling pasta access for rootless Docker"
|
|
|
|
# 1. Add `include if exists <local/usr.bin.passt>` to the main profile if
|
|
# it isn't already there. The line lives just before the closing `}` of
|
|
# the profile block.
|
|
if ! sudo grep -q "include if exists <local/usr.bin.passt>" /etc/apparmor.d/usr.bin.passt; then
|
|
# Insert right before the final `}` of the file. We don't try to be
|
|
# surgical about which `}` — passt's profile has exactly one top-level
|
|
# close, and any nested `{` `}` (e.g. inside abstractions) are
|
|
# behind `include` lines, not literal in this file.
|
|
sudo awk '
|
|
/^}/ && !done { print " include if exists <local/usr.bin.passt>"; done=1 }
|
|
{ print }
|
|
' /etc/apparmor.d/usr.bin.passt | sudo tee /etc/apparmor.d/usr.bin.passt.new > /dev/null
|
|
sudo mv /etc/apparmor.d/usr.bin.passt.new /etc/apparmor.d/usr.bin.passt
|
|
checkSuccess "Added include-local line to /etc/apparmor.d/usr.bin.passt"
|
|
else
|
|
isSuccessful "Main passt profile already sources /etc/apparmor.d/local/usr.bin.passt"
|
|
fi
|
|
|
|
# 2. Write (or overwrite — same content every time) the local rules file.
|
|
sudo mkdir -p /etc/apparmor.d/local
|
|
sudo tee /etc/apparmor.d/local/usr.bin.passt > /dev/null <<'EOF'
|
|
# Managed by LibrePortal (installRootlessApparmorForPasta). Edits here are
|
|
# fine but will be overwritten on the next rootless reinstall — put your
|
|
# customisations in a sibling file (e.g. local/usr.bin.passt.local) and
|
|
# include them yourself if you need to extend this further.
|
|
#
|
|
# These rules permit rootless Docker's pasta network driver to:
|
|
# - ptrace_read the rootlesskit child to enter its user namespace
|
|
# - read the netns file rootlesskit writes
|
|
# - read per-process socket tables for implicit port-forwarding
|
|
|
|
ptrace (read) peer=unconfined,
|
|
/run/user/[0-9]*/dockerd-rootless/netns r,
|
|
/proc/*/net/tcp r,
|
|
/proc/*/net/tcp6 r,
|
|
/proc/*/net/udp r,
|
|
/proc/*/net/udp6 r,
|
|
EOF
|
|
checkSuccess "Wrote /etc/apparmor.d/local/usr.bin.passt"
|
|
|
|
# 3. Reload the profile. apparmor_parser -r updates an in-kernel profile
|
|
# in place, so already-running pasta processes see the new rules on the
|
|
# next syscall — no daemon restart strictly needed, but the caller
|
|
# restarts docker anyway when override.conf changes.
|
|
if sudo apparmor_parser -r /etc/apparmor.d/usr.bin.passt 2>/dev/null; then
|
|
isSuccessful "Reloaded passt AppArmor profile"
|
|
else
|
|
isError "apparmor_parser -r failed — passt may still deny pasta. Check /var/log/syslog or journalctl for the error."
|
|
return 1
|
|
fi
|
|
}
|