Fullmoon System

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.

Structuur van Linux-containers: rootless-gebruiker, OCI-imagelagen, namespaces, gedeelde kernel en cgroups v2
Een structuur waarin meerdere isolatieomgevingen, gecreëerd vanuit OCI-imagelagen, één Linux-kernel delen en resources beperken via cgroups

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

  1. Controleer de exit-code en restart-loop via podman ps –all en de servicestatus.
  2. Synchroniseer de tijdstippen van applicatie- en engine-events met podman logs en events.
  3. Inspecteer het image ID, de digest, het commando, de omgeving, mounts en beveiligingsopties.
  4. Controleer poortbindingen, rootless netwerken, DNS en de grenzen van de host-firewall.
  5. Controleer SELinux AVC, volume-labels, UID-mapping en bestandsrechten.
  6. Controleer cgroup-geheugen, PID, CPU-limieten en OOM- of druk-indicatoren.
  7. 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

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.