Linux-Container verstehen: Namespaces, cgroups v2 und Rootless Podman
EdwardMoon
Linux-Container sind Prozesse, die den Hostkernel teilen. Prozess-, Mount-, Netzwerk- und User-Namespaces begrenzen ihre Sicht auf das System; cgroups beschränken Ressourcen wie CPU, Arbeitsspeicher und PIDs. Dieses Modell unterscheidet sich von kleinen virtuellen Maschinen. Wer es versteht, kann Rechte, Images, Volumes, Netzwerke und Ausfallbereiche passend entwerfen.
Die Übung verwendet cgroup v2 und Rootless Podman auf Rocky Linux 9. SELinux und firewalld bleiben aktiv. Entfernte Installationsskripte werden nicht direkt an eine Shell weitergereicht; neben Image-Tags werden Digest und Herkunft dokumentiert. Ausgaben können von Podman- und Kernelversion abhängen. Deshalb in jedem Schritt den tatsächlichen Zustand prüfen.

Container und virtuelle Maschinen vergleichen
| Merkmal | Container | Virtuelle Maschine |
|---|---|---|
| Kernel | Gemeinsamer Hostkernel | Eigener Kernel pro Gastbetriebssystem |
| Isolation | namespaces·LSM·seccomp·capabilities | Hypervisor und virtuelle Hardware |
| Image | OCI-Layer und Metadaten | Vollständiges Datenträgerabbild |
| Start | Isolierten Prozess erzeugen | Gastbetriebssystem booten |
| Ressourcen | cgroups und Laufzeitlimits | Zuweisung von vCPUs, RAM und virtuellen Geräten |
| Risikogrenze | Gemeinsame Betroffenheit durch Kernel-Schwachstellen | Getrennte Kernelgrenze zwischen Gast und Host |
Bestandteile von Linux-Containern
| Komponente | Aufgabe | Typische Prüfung |
|---|---|---|
| OCI image | Root-Dateisystem-Layer und Ausführungsmetadaten | podman image inspect·history |
| Container engine | pull·build·network·storage·lifecycle | podman info |
| OCI runtime | Namespaces und cgroups erstellen und Prozess ausführen | crun –version oder runc –version |
| conmon | Containerprozess überwachen, Standardein-/ausgabe und Beendigung behandeln | podman info –debug |
| namespaces | Sichtbare IDs, Mounts und Netzwerke eines Prozesses isolieren | lsns·/proc/PID/ns |
| cgroups v2 | CPU-, Speicher-, I/O- und PID-Nutzung erfassen und begrenzen | podman stats·/sys/fs/cgroup |
| SELinux/seccomp | Zulässige Dateien und Systemaufrufe begrenzen | getenforce·podman inspect |
Übungsumgebung prüfen
Podman- und Kernelfunktionen prüfen
uname -r
cat /etc/os-release
podman --version
podman info --debug
podman info --format '{{.Host.CgroupsVersion}}'
stat -fc %T /sys/fs/cgroup
getenforce
systemctl is-active firewalld
Prüfen, dass cgroup2fs und Podmans Meldung zu cgroup v2 übereinstimmen. Quadlet benötigt cgroup v2. Bei SELinux Enforcing müssen Volume-Labels stimmen. Nicht sämtliche Sicherheitsfunktionen abschalten, wenn ein Zugriff zunächst scheitert.
Rootless-UID- und GID-Bereiche prüfen
id
grep -E "^${USER}:" /etc/subuid /etc/subgid
command -v newuidmap newgidmap
command -v pasta
podman unshare cat /proc/self/uid_map
podman unshare cat /proc/self/gid_map
Rootless Podman verwendet zusätzliche ID-Bereiche aus /etc/subuid und /etc/subgid, um Container-UIDs auf unprivilegierte Host-UIDs abzubilden. Fehlen Bereiche, muss die Administration mit usermod –add-subuids und –add-subgids nicht überlappende Bereiche zuweisen. Gemeinsam genutzte NFS-Home-Verzeichnisse verstehen User-Namespaces nicht. Deshalb Rootless-graphroot auf einem lokalen Dateisystem vorsehen.
Namespaces praktisch untersuchen
Namespaces trennen die Sicht eines Prozesses auf globale Ressourcen. Im PID-Namespace erscheinen andere Prozessnummern. Mount-Namespaces trennen Mount-Tabellen, Netzwerk-Namespaces Schnittstellen, Routen und Ports; User-Namespaces trennen UID-/GID-Zuordnung und den Geltungsbereich von Capabilities.
Aktuelle Namespaces auflisten
lsns
readlink /proc/self/ns/user
readlink /proc/self/ns/pid
readlink /proc/self/ns/mnt
readlink /proc/self/ns/net
Unprivilegierten User- und PID-Namespace erstellen
unshare --user --map-root-user --pid --fork sh -c '
id
echo "namespace PID: $$"
readlink /proc/self/ns/user
readlink /proc/self/ns/pid
'
uid=0 in der Ausgabe bedeutet root innerhalb des neuen User-Namespace, nicht Host-root. Host-UID-Zuordnung und Capabilities bleiben eingeschränkt, sodass nicht sämtliche Hostdateien oder Geräte zugänglich sind. Wegen des gemeinsamen Kernels bleiben Kernel-Schwachstellen sowie falsch eingebundene Geräte oder Sockets jedoch Risiken.
OCI-Images und Digests
Images bestehen aus unveränderlichen Layern und Metadaten wie Konfiguration, Entrypoint und Umgebungsvariablen. Ein Registry-Tag kann später auf einen anderen Digest zeigen. Für produktive Bereitstellungen daher den geprüften Manifest-Digest dokumentieren und Richtlinien für Signaturen, SBOM und Schwachstellen anwenden.
Image mit vollständig qualifiziertem Namen laden
IMAGE='docker.io/library/busybox:1.36.1'
podman pull "$IMAGE"
podman image inspect "$IMAGE" --format 'ID={{.Id}} Digest={{.Digest}} Created={{.Created}}'
podman history --no-trunc "$IMAGE"
podman images --digests
Kurznamen können je nach registries.conf auf unterschiedliche Registries verweisen. Deshalb den vollständigen Namen mit Registry und Namespace verwenden. Wurde während der Entwicklung ein Tag genutzt, den von inspect gelieferten Digest im Freigabeprotokoll festhalten und in Quadlet oder Deployment-Manifest auf image@sha256 festlegen.
OCI-Manifest prüfen
skopeo inspect docker://docker.io/library/busybox:1.36.1 | jq '{Name,Digest,Created,Architecture,Os}'
skopeo inspect --raw docker://docker.io/library/busybox:1.36.1 | jq .
Rootless-Container mit Podman starten
BusyBox httpd nur über Loopback bereitstellen, das Root-Dateisystem schreibgeschützt halten und Standard-Capabilities, Rechteausweitung sowie übermäßige PID-, Speicher- und CPU-Nutzung begrenzen. Nur tatsächlich benötigte Schreibpfade als tmpfs oder ausdrückliche Volumes anbieten.
IMAGE='docker.io/library/busybox:1.36.1'
install -d -m 0750 "$HOME/container-data"
printf 'hello from rootless Podman\n' > "$HOME/container-data/index.html"
podman run --detach --rm --name web-demo \
--read-only --cap-drop=all --security-opt=no-new-privileges \
--pids-limit=128 --memory=256m --cpus=0.50 \
--publish 127.0.0.1:8080:8080 \
--volume "$HOME/container-data:/www:ro,Z" \
--tmpfs /tmp:rw,noexec,nosuid,nodev,size=32m \
"$IMAGE" httpd -f -p 8080 -h /www
Laufzustand und Limits prüfen
podman ps
podman port web-demo
curl --fail --silent --show-error http://127.0.0.1:8080/
podman stats --no-stream web-demo
podman top web-demo user hpid pid args
podman inspect web-demo > web-demo.inspect.json
jq '.[0].HostConfig | {ReadonlyRootfs,Memory,NanoCpus,PidsLimit}' web-demo.inspect.json
Fehlt bei –publish die Host-IP, kann der Port auf allen Schnittstellen erreichbar werden. Dienste hinter einem lokalen Reverse-Proxy an 127.0.0.1 binden. Bei erforderlicher externer Freigabe firewalld-Zone, Quellnetze, TLS und Authentifizierung gesondert prüfen.
Prozesse und Namespaces verfolgen
HOST_PID=$(podman inspect --format '{{.State.Pid}}' web-demo)
printf 'host PID=%s
' "$HOST_PID"
ps -o user,pid,ppid,cmd -p "$HOST_PID"
sudo lsns -p "$HOST_PID"
sudo readlink "/proc/${HOST_PID}/ns/user"
sudo readlink "/proc/${HOST_PID}/ns/net"
sudo cat "/proc/${HOST_PID}/cgroup"
Auch PID 1 im Container erscheint auf dem Host als gewöhnliche PID. nsenter erlaubt mächtige Eingriffe zur Diagnose innerhalb der Isolationsgrenze. Den Zugriff darauf im Betrieb beschränken und zunächst Engine-Schnittstellen wie podman exec, logs und inspect verwenden.
podman exec web-demo sh -c '
echo "container PID=$$"
id
cat /proc/self/status | grep -E "^(Name|Pid|NSpid|CapEff|NoNewPrivs):"
'
podman logs web-demo
podman events --since 10m --filter container=web-demo
cgroups v2 praktisch prüfen
cgroups v2 erfassen und begrenzen Ressourcen. Speicherlimits verringern Risiken unkontrollierter Speichernutzung, PID-Limits die Ausbreitung von Fork-Bombs und CPU-Quoten die Auswirkungen störender Nachbarlasten. Im Rootless-Betrieb können manche Controller-Limits durch systemd-Benutzerdelegation oder Hostrichtlinien eingeschränkt sein.
podman stats --no-stream web-demo
podman inspect web-demo --format '{{.State.CgroupPath}}'
systemd-cgls "/user.slice/user-${UID}.slice"
systemctl --user status
podman update --memory=192m --pids-limit=96 web-demo
podman stats --no-stream web-demo
Zu niedrige Limits können bereits bei normaler Last OOM-Kills oder fehlgeschlagene Anfragen auslösen. Speichermodell der Anwendung, JVM- und Worker-Anzahl, Healthchecks und Neustartstrategie gemeinsam abstimmen. Auch freie Hostressourcen und Pressure Stall Information überwachen.
Volumes und SELinux
Eigenes Hostverzeichnis und privates Label
install -d -m 0750 "$HOME/volume-label-demo"
printf 'hello from a labeled volume\n' > "$HOME/volume-label-demo/index.html"
podman run --rm --read-only --cap-drop=all \
--security-opt=no-new-privileges \
--volume "$HOME/volume-label-demo:/data:ro,Z" \
docker.io/library/busybox:1.36.1 cat /data/index.html
ls -Zd "$HOME/volume-label-demo"
:Z versieht einen Pfad mit einem privaten SELinux-Label für einen einzelnen Container. Für gemeinsam genutzte Pfade kann :z geeignet sein. Großflächiges Relabeling von Systemverzeichnissen oder sensiblen Home-Verzeichnissen kann jedoch Hostdienste beschädigen. Nur eigene Verzeichnisse einbinden und Sicherung, Eigentümer sowie UID-Zuordnung vorab planen.
Unsichere Vorgehensweisen bei Containern
| Vorgehensweise | Risiko | Empfohlene Alternative |
|---|---|---|
| Vollständig privilegierter Modus | Hebt zahlreiche Geräte-, Capability- und LSM-Grenzen auf | Nur benötigte Capabilities und Geräte einzeln erlauben |
| host network/PID | Gemeinsame Host-Namespaces und weitreichende Sicht auf den Host | Eigener Netzwerk-Namespace und ausdrücklich veröffentlichte Ports |
| Podman/Docker socket mount | Beliebige Container und Mounts auf dem Host ausführbar | Eingeschränkter API-Proxy oder separates Automatisierungskonto |
| latest tag | Ergebnis erneuter Bereitstellung verändert sich | Geprüfter Digest und Signaturrichtlinie |
| Sämtliche Sicherheitsfunktionen deaktivieren | Verlust der SELinux- und Firewall-Schutzschichten | Labels, Ports und Richtlinien nur im erforderlichen Umfang anpassen |
| curl-Ausgabe direkt an die Shell übergeben | Entfernten Code ohne Prüfung und Integritätskontrolle ausführen | Offizielle Pakete sowie Signatur- und Prüfsummenprüfung |
Rechte und Mounts prüfen
podman inspect web-demo --format '{{json .HostConfig.SecurityOpt}} {{json .HostConfig.CapDrop}}'
podman inspect web-demo --format '{{range .Mounts}}{{.Type}} {{.Source}} -> {{.Destination}} rw={{.RW}}{{println}}{{end}}'
podman diff web-demo
podman top web-demo capeff label
Container mit Quadlet betreiben
Eine Quadlet-.container-Datei im Suchpfad der Rootless-Benutzer-Units ermöglicht systemd die Verwaltung von Dienstlebenszyklus und Logs. Das ist für den Betrieb geeigneter als ein einmaliger podman-run-Aufruf in einem Shellskript. Den folgenden Image-Wert produktiv durch einen geprüften SHA-256-Digest ersetzen.
Einmaligen Übungscontainer beenden
podman stop web-demo
podman ps --all --filter name=web-demo
# Da zuvor --rm verwendet wurde, darf nach regulärem Ende kein Container zurückbleiben.
podman container exists web-demo; printf 'exit=%s
' "$?"
mkdir -p "$HOME/.config/containers/systemd"
${EDITOR:-vi} "$HOME/.config/containers/systemd/web-demo.container"
~/.config/containers/systemd/web-demo.container
[Unit]
Description=Rootless read-only web demo
[Container]
Image=docker.io/library/busybox:1.36.1
ContainerName=web-demo
Exec=httpd -f -p 8080 -h /www
Volume=%h/container-data:/www:ro,Z
PublishPort=127.0.0.1:8080:8080
ReadOnly=true
NoNewPrivileges=true
DropCapability=all
PidsLimit=128
[Service]
MemoryMax=256M
Restart=on-failure
TimeoutStartSec=120
[Install]
WantedBy=default.target
Erzeugte Quadlet-Unit und Dienst prüfen
mkdir -p "$HOME/.config/containers/systemd"
chmod 0700 "$HOME/.config/containers/systemd"
chmod 0644 "$HOME/.config/containers/systemd/web-demo.container"
systemctl --user daemon-reload
systemctl --user start web-demo.service
systemctl --user status web-demo.service
journalctl --user -u web-demo.service --since '-10 min'
podman info --format '{{.Host.CgroupsVersion}}'
curl --fail --silent --show-error http://127.0.0.1:8080/
Die generierte Quadlet-Service-Unit nicht direkt aktivieren. Stattdessen WantedBy im [Install]-Abschnitt der .container-Datei durch den Generator berücksichtigen lassen. Soll der Rootless-Dienst nach Abmeldung weiterlaufen, kann die Administration loginctl enable-linger verwenden. Vorher die betrieblichen und sicherheitsbezogenen Auswirkungen dauerhaft ohne Anmeldung laufender Benutzerprozesse freigeben lassen.
Diagnosereihenfolge
- Mit podman ps –all und Dienststatus Exitcodes sowie Neustartschleifen prüfen.
- Zeitpunkte von Anwendungs- und Engine-Ereignissen mit podman logs und events abgleichen.
- Image-ID, Digest, Befehl, Umgebung, Mounts und Sicherheitsoptionen mit inspect prüfen.
- Portbindungen, Rootless-Netzwerk, DNS und Host-Firewall prüfen.
- SELinux-AVCs, Volume-Labels, UID-Zuordnung und Dateirechte prüfen.
- cgroup-Limits für Speicher, PIDs und CPU sowie OOM- und Druckmetriken prüfen.
- Mit demselben Digest und minimalen Eingaben auf einem isolierten Host reproduzieren.
podman ps --all --size
podman inspect web-demo
podman logs --timestamps web-demo
podman events --since 30m
podman stats --no-stream web-demo
journalctl --user -u web-demo.service --since '-30 min'
sudo ausearch -m AVC,USER_AVC -ts recent
ss -lntp | grep ':8080'
Checkliste für den Betrieb
- Gemeinsamer Kernel von Containern gegenüber VMs sowie Aufgaben von Namespaces und cgroups sind verstanden.
- Rootless-subuid und -subgid, cgroup v2, Laufzeit, lokaler Speicher und Netzwerkwerkzeuge sind geprüft.
- Vollständiger Registry-Name, geprüfter Image-Digest sowie Signatur-, SBOM- und Schwachstellenergebnisse sind dokumentiert.
- Schreibgeschütztes Root-Dateisystem, no-new-privileges, Capability-Entzug sowie PID-, Speicher- und CPU-Limits sind angewendet.
- Ports sind nur an benötigte Host-IPs gebunden; privilegierter Modus, Host-Namespaces und Engine-Socket werden vermieden.
- Eigene Volumes und passende SELinux-Labels werden bei aktiven Sicherheitsfunktionen verwendet.
- Quadlet-Dienst, Logs, Healthchecks, Sicherung, Wiederherstellung und Update-Rollback sind geprüft.
Offizielle Dokumentation und weiterführende Artikel
- Offizielle Podman-Dokumentation zum Rootless-Betrieb
- Sicherheits- und Ressourcenoptionen von podman run
- Offizielle Podman-Quadlet-Dokumentation
- Offizielle Linux-Kernel-Dokumentation zu cgroup v2
- Linux-Namespaces-Handbuch
- Linux-Kernel: Architektur und Diagnose
- NetBox mit Docker sicher bereitstellen und sichern
Zusammenfassung
Sicherer Containerbetrieb berücksichtigt die Risiken des gemeinsamen Kernels und verwaltet Namespaces, cgroups v2, Benutzerzuordnung, SELinux und die OCI-Lieferkette gemeinsam. Rootless als Ausgangspunkt wählen, Digests festlegen, Capabilities, Mounts und Ports begrenzen sowie schreibgeschützte Dateisysteme und Ressourcenlimits verwenden. Quadlet, systemd-Logs, Healthchecks, Sicherung und Rollback vervollständigen einen reproduzierbaren Dienst.