Principes van Linux-containers: namespaces, cgroups v2 en rootless Podman
AI_Manager
Linux-containers: Een container is geen kleine virtuele machine, maar een proces dat de Linux-kernel van de host deelt, waarbij het zichtbare bereik wordt verdeeld via process-, mount-, network- en user-namespaces, en resources zoals CPU, geheugen en PID worden beperkt door cgroups. Het begrijpen van dit verschil is essentieel voor het correct ontwerpen van rechten, images, volumes, netwerken en foutbereiken.
Deze praktijkoefening is gebaseerd op cgroup v2 en rootless Podman op Rocky Linux 9. We behouden SELinux en firewalld, voeren geen externe installatiescripts uit via pipes, en vertrouwen niet alleen op image-tags, maar leggen ook digests en provenance vast. De resultaten van opdrachten kunnen variëren afhankelijk van de geïnstalleerde Podman- en kernelversies, dus controleer in elke stap de werkelijke status.

Verschillen tussen Linux-containers en virtuele machines
| Item | Container | Virtuele machine |
|---|---|---|
| Kernel | Deelt de host-kernel | Kernel per gast-OS |
| Isolatie | namespaces, LSM, seccomp, capabilities | Hypervisor en virtuele hardware |
| Image | OCI-lagen en metadata | Volledige schijf-image |
| Opstarten | Creatie van een geïsoleerd proces | Opstarten van het gast-OS |
| Resources | cgroups en runtime-beperkingen | Toewijzing van vCPU, RAM en virtuele apparaten |
| Risicogrens | Deelt de impact van kernel-kwetsbaarheden | Aparte kernelgrens tussen gast en host |
Rootless brengt de container-root in kaart naar een niet-geprivilegieerd UID-bereik op de host om schade te beperken, maar het is geen volledige beveiligingsgrens. Blijf kwetsbaarheden in de host-kernel, container-engine, OCI-runtime en images patchen en verleen geen onnodige capabilities, apparaten, sockets of host-namespaces.
Componenten van Linux-containers
| Component | Rol | Belangrijkste verificatiemethode |
|---|---|---|
| OCI-image | root-bestandssysteemlaag en uitvoeringsmetadata | podman image inspect·history |
| Container-engine | pull·build·netwerk·opslag·levenscyclus | podman info |
| OCI-runtime | creëren van namespace·cgroup en uitvoeren van processen | crun –version of runc –version |
| conmon | bewaking van containerprocessen·stdio·afhandeling van exit | podman info –debug |
| namespaces | isolatie van ID·mount·netwerk zoals gezien door het proces | lsns·/proc/PID/ns |
| cgroups v2 | CPU·geheugen·I/O·PID-accounting en beperkingen | podman stats·/sys/fs/cgroup |
| SELinux/seccomp | beperking van toegestane bestanden·bereik van systeemaanroepen | getenforce·podman inspect |
controle van de Linux-containeromgeving
controle van Podman en kernel-functionaliteit
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
Controleer of cgroup2fs en de cgroup v2-rapportage van Podman overeenkomen. Quadlet vereist cgroup v2. Wanneer SELinux op Enforcing staat, moeten volumelabels correct worden ingesteld; schakel beveiligingsfuncties niet volledig uit alleen omdat iets niet werkt.
controle van rootless UID·GID-bereik
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 gebruikt extra ID-bereiken uit /etc/subuid en /etc/subgid om de container-UID te mappen naar een niet-geprivilegieerde UID op de host. Als er geen bereik is, moet een beheerder een niet-conflicterend bereik toewijzen met usermod –add-subuids en –add-subgids. Omdat gedeelde NFS-homes geen user namespaces begrijpen, is het raadzaam om een ontwerp te overwegen waarbij de rootless graphroot op een lokaal bestandssysteem wordt geplaatst.
oefening met Linux-container namespaces
Namespaces scheiden de weergave van globale bronnen die een proces ziet. Binnen een PID-namespace zien procesnummers er anders uit, de mount-namespace scheidt de mount-tabel, de netwerk-namespace scheidt interfaces·routes·poorten, en de user-namespace scheidt UID·GID en capability-bereiken.
huidige lijst met namespaces
lsns
readlink /proc/self/ns/user
readlink /proc/self/ns/pid
readlink /proc/self/ns/mnt
readlink /proc/self/ns/net
maken van een niet-geprivilegieerde user·PID-namespace
unshare --user --map-root-user --pid --fork sh -c '
id
echo "namespace PID: $$"
readlink /proc/self/ns/user
readlink /proc/self/ns/pid
'
De uid=0 in de uitvoer is slechts root binnen de nieuwe user-namespace, niet de host-root. Omdat de host-UID-mapping en de toegestane capabilities beperkt zijn, kunnen niet alle bestanden op de host worden gelezen of apparaten worden bestuurd. Omdat de kernel echter wordt gedeeld, blijven kwetsbaarheden in de kernel en onjuiste device·socket-mounts een risico.
Linux-container OCI-images en digest
Images bestaan uit onveranderlijke lagen en metadata zoals configuratie, entrypoint en omgevingsvariabelen. Omdat een tag in een registry naar een andere digest kan verwijzen, moet u voor productie-implementaties de geverifieerde manifest-digest vastleggen en deze combineren met ondertekening, SBOM en kwetsbaarheidsbeleid.
pullen met een volledig gekwalificeerde imagenaam
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
Omdat korte namen afhankelijk van de registries.conf-configuratie naar verschillende registries kunnen worden vertaald, gebruikt u de volledige naam inclusief registry en namespace. Als u tags voor ontwikkelgemak hebt gebruikt, noteer dan de digest die door inspect wordt geretourneerd in het goedkeuringslogboek en zet deze vast in Quadlet·implementatiemanifesten met de image@sha256-indeling.
OCI-manifest controleren
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 Podman-container uitvoeren op Linux
Stel BusyBox httpd alleen beschikbaar via loopback, houd het root-bestandssysteem read-only en beperk alle standaard capabilities, privilege escalation en overmatig gebruik van PID’s, geheugen en CPU. Bied alleen de schrijfpaden die de applicatie nodig heeft aan via tmpfs of expliciete volumes.
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
Status en beperkingen van uitvoering controleren
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
Als het host-IP wordt weggelaten bij –publish, kan de service worden blootgesteld aan alle interfaces. Bind services die achter een lokale reverse proxy staan aan 127.0.0.1, en als externe toegang vereist is, controleer dan afzonderlijk het firewalld-beleid voor zones, bronnen, TLS en authenticatie.
Processen en namespaces van Linux-containers traceren
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"
Zelfs PID 1 binnen een container verschijnt als een gewoon proces op de host. Omdat nsenter krachtige rechten vereist om de isolatiegrenzen te diagnosticeren, moet de toegang voor beheer worden beperkt en moeten eerst engine-interfaces zoals podman exec, logs en inspect worden gebruikt.
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
Oefening met cgroups v2 voor Linux-containers
cgroups v2 is niet verantwoordelijk voor isolatie, maar voor resource-accounting en -beperking. Geheugenlimieten verminderen het risico op OOM, PID-limieten voorkomen de verspreiding van fork-bombs en CPU-quota verminderen de impact van ‘noisy neighbors’. In een rootless-omgeving kunnen sommige controller-beperkingen mogelijk niet worden toegepast, afhankelijk van systemd user delegation en het host-beleid.
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
Als limieten te laag worden ingesteld, kunnen OOM-kills of verzoekfouten optreden, zelfs bij normale belasting. Stem het applicatiegeheugenmodel, het aantal JVM- of worker-processen, health checks en herstartbeleid op elkaar af, en monitor ook de beschikbare resources van de gehele host en de ‘pressure stall information’.
Volumes en SELinux in Linux-containers
Toegewezen host-directory en privélabels
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 labelt het betreffende pad opnieuw met een privaat SELinux-label dat specifiek is voor één container. Voor paden die door meerdere containers moeten worden gedeeld, kan :z worden overwogen, maar het op grote schaal opnieuw labelen van systeemmappen of gevoelige home-directories kan host-services verstoren. Mount alleen toegewezen mappen en ontwerp eerst het beleid voor back-ups, eigendom en UID-mapping.
Anti-patronen voor Linux-containerbeveiliging
| Anti-patroon | Risico | Aanbevolen alternatief |
|---|---|---|
| Volledige privileged-modus | De meeste grenzen voor apparaten, capabilities en LSM worden opgeheven | Sta alleen de noodzakelijke capabilities en apparaten individueel toe |
| Host-netwerk/PID | Deelt de host-namespace en de observatieomvang | Toegewezen netwerk-namespace en expliciete poorten |
| Podman/Docker socket mount | Willekeurige containers en mounts kunnen vanaf de host worden uitgevoerd | Beperkte API-proxy of afzonderlijk automatiseringsaccount |
| latest-tag | Het resultaat van herdistributie verandert | Geverifieerde digests en ondertekeningsbeleid |
| Beveiligingsfuncties volledig uitschakelen | Verlies van verdedigingslagen zoals SELinux en firewalls | Pas labels, poorten en beleid alleen aan waar nodig |
| curl-resultaten direct naar shell pipe | Remote code execution zonder controle of integriteitsverificatie | Officiële pakketten en verificatie van handtekeningen en checksums |
Rechten en mount-audit
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
Linux-container Quadlet-beheer
In plaats van een eenmalige podman run in een shellscript te verpakken, kunt u een Quadlet .container-bestand in het rootless user unit-zoekpad plaatsen, zodat systemd de servicelevenscyclus en logs kan beheren. Vervang in productie de Image-waarde door een geverifieerde sha256-digest.
Eenmalige testcontainer beëindigen
podman stop web-demo
podman ps --all --filter name=web-demo
# Aangezien dit hierboven is uitgevoerd met --rm, mag de container na een normale afsluiting niet achterblijven.
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
Resultaten van Quadlet-generatie en servicecontrole
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/
In plaats van de door Quadlet gegenereerde service direct in te schakelen, zorgt u ervoor dat de [Install] WantedBy van het .container-bestand wordt overgenomen door de generator. Om rootless services ook na het uitloggen actief te houden, kan een beheerder loginctl enable-linger gebruiken, maar pas dit pas toe nadat de operationele en beveiligingsgevolgen van processen die zonder actieve sessie blijven draaien, zijn goedgekeurd.
Diagnostische volgorde voor Linux-containers
- Controleer de exit-code en restart-loop via podman ps –all en de servicestatus.
- Synchroniseer de tijdstippen van applicatie- en engine-events met podman logs en events.
- Inspecteer het image ID, de digest, het commando, de omgeving, mounts en beveiligingsopties.
- Controleer poortbindingen, rootless netwerken, DNS en de grenzen van de host-firewall.
- Controleer SELinux AVC, volume-labels, UID-mapping en bestandsrechten.
- Controleer cgroup-geheugen, PID, CPU-limieten en OOM- of druk-indicatoren.
- Reproduceer het probleem op een geïsoleerde host met dezelfde digest en minimale invoer.
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'
Checklist voor Linux-containerbeheer
- Het verschil in shared-kernel tussen containers en VM’s, en de verantwoordelijkheden van namespaces en cgroups zijn vastgesteld.
- Rootless subuid/subgid, cgroup v2, runtime, lokale opslag en netwerktools zijn gecontroleerd.
- De volledige registry-naam, geverifieerde image-digest, handtekening, SBOM en kwetsbaarheidsresultaten zijn vastgelegd.
- Read-only rootfs, no-new-privileges, capability-drop en limieten voor PID, geheugen en CPU zijn toegepast.
- Poorten zijn alleen gebonden aan de benodigde host-IP’s en het gebruik van privileged, host-namespaces of engine-sockets is vermeden.
- Specifieke volumes en SELinux-labels zijn gebruikt en beveiligingsfuncties zijn niet uitgeschakeld.
- Quadlet-service, logs, health, back-up/restore en update-rollbacks zijn geverifieerd.
Officiële documentatie en gerelateerde artikelen over Linux-containers
- Officiële Podman rootless-documentatie
- Beveiligings- en resource-opties voor podman run
- Officiële Podman Quadlet-documentatie
- Officiële Linux kernel cgroup v2-documentatie
- Linux namespaces-handleiding
- Gids voor Linux kernel-structuur en diagnose
- Gids voor veilige implementatie, back-up en upgrade van NetBox Docker
Samenvatting
Linux-containers: Veilig beheer is meer dan alleen een commando om een image uit te voeren; het gaat om het begrijpen van de risico’s van een gedeelde kernel en het gezamenlijk beheren van namespaces, cgroups v2, user mapping, SELinux en de OCI-supply chain. Gebruik rootless als standaard, fixeer digests, pas minimale capabilities, mounts en poorten toe, en gebruik read-only bestandssystemen en resource-limieten. Tot slot moeten Quadlet, systemd-logs, health-checks, back-ups en rollbacks worden geverifieerd om een reproduceerbare service te garanderen.