Úvod: Prečo je bezpečnosť kontajnerov v roku 2026 kritická
Kontajnery sú dnes všade. Podľa prieskumu Cloud Native Computing Foundation z roku 2025 používa kontajnery v produkcii 92 % organizácií — oproti 76 % v roku 2023. Kubernetes beží na viac než 7,5 milióna klastrov po celom svete a denne sa spúšťajú miliardy kontajnerových inštancií.
Kontajnerizácia jednoducho vyhrala.
No ruku na srdce — s masívnym rozšírením prišli aj poriadne bezpečnostné problémy. V novembri 2025 boli odhalené tri kritické zraniteľnosti v runc, základnom runtime pre Docker aj Podman: CVE-2025-31133 (symlink útok cez maskované cesty), CVE-2025-52565 (symlink útok cez /dev/console) a CVE-2025-52881 (obchádzanie LSM bezpečnostných značiek). Tieto chyby umožňovali únik z kontajnera — teda presne ten scenár, z ktorého sa každému adminovi dvíha žalúdok.
A to nie je ani zďaleka všetko. Podľa správy Sysdig z roku 2025 obsahuje 87 % kontajnerových obrazov vysoké alebo kritické zraniteľnosti. Viac ako 65 % organizácií otvorene prizná, že nemá adekvátne zabezpečenie kontajnerového prostredia. Úprimne? To sú dosť desivé čísla.
Tak poďme na to. V tomto článku vás prevediem kompletným zabezpečením kontajnerov na Linuxe — od rootless režimu cez Seccomp profily, AppArmor a SELinux, až po runtime monitoring a obranu proti novým zraniteľnostiam. Všetko s praktickými príkladmi, ktoré môžete nasadiť hneď dnes.
Ako funguje izolácia kontajnerov: Základy, ktoré musíte poznať
Menné priestory (namespaces)
Kontajner nie je virtuálny stroj. Toto je zásadný koncept, ktorý prekvapivo veľa ľudí prehliadne. Kontajnery zdieľajú jadro hostiteľského systému a ich izolácia je zabezpečená pomocou menných priestorov (namespaces) jadra Linuxu. Existuje niekoľko typov:
- PID namespace — izoluje procesy, kontajner vidí len svoje vlastné
- Network namespace — izoluje sieťové rozhrania, routing tabuľky, firewall pravidlá
- Mount namespace — izoluje súborové systémy a pripojenia
- UTS namespace — izoluje hostname a doménové meno
- IPC namespace — izoluje medziprocesovú komunikáciu
- User namespace — mapuje UID/GID kontajnera na odlišné UID/GID na hostiteľovi
- Cgroup namespace — izoluje pohľad na hierarchiu cgroups
- Time namespace — umožňuje rôznym kontajnerom mať odlišný systémový čas (od Linuxu 5.6)
Kontrolné skupiny (cgroups)
Zatiaľ čo menné priestory poskytujú izoláciu (čo kontajner vidí), cgroups zabezpečujú obmedzenie zdrojov (koľko toho kontajner môže spotrebovať). Cgroups v2, predvolené od RHEL 9, Ubuntu 22.04 a Fedory 31, umožňujú presné riadenie CPU, pamäte, I/O a sieťových zdrojov.
# Kontrola, či systém používa cgroups v2
stat -fc %T /sys/fs/cgroup/
# Výstup: cgroup2fs = cgroups v2
# Zobrazenie cgroup obmedzení pre bežiaci kontajner
podman inspect --format '{{.HostConfig.Memory}}' moj-kontajner
podman inspect --format '{{.HostConfig.CpuQuota}}' moj-kontajner
Útočná plocha: Zdieľané jadro
Najväčší bezpečnostný rozdiel medzi kontajnerom a VM-kom je práve to zdieľané jadro. Každý kontajner volá systémové volania priamo do jadra hostiteľa. Jadro Linuxu ponúka viac ako 450 systémových volaní a každé z nich je potenciálnym útočným vektorom. Zraniteľnosť v jadre môže znamenať únik z kontajnera — a presne preto potrebujeme viacvrstvovú obranu.
Predstavte si to ako bezpečnostné dvere v bytovom dome. Menné priestory sú steny medzi bytmi, cgroups sú merače spotreby, ale jadro je spoločný základ celej budovy. Ak niekto prerazí podlahu, dostane sa k susedom. A to nechcete.
Rootless kontajnery s Podmanom: Prečo a ako
Prečo je rootless režim kritický
V klasickom nastavení Docker démon beží ako root. To znamená, že ak útočník unikne z kontajnera, získa plné root oprávnenia na hostiteľovi. Katastrofa.
Rootless kontajnery tento problém riešia zásadne — celý kontajnerový runtime beží pod bežným neprivilegovaným používateľom. Aj keby útočník z kontajnera unikol, získa len oprávnenia bežného usera. Nie roota.
Podman bol od začiatku navrhnutý s rootless režimom ako prioritou. Na rozdiel od Dockeru nepotrebuje démona bežiaceho s root oprávneniami — je to daemonless architektúra. Každý kontajner beží ako priamy potomok používateľského procesu. Osobne si myslím, že práve toto je najväčšia výhoda Podmanu oproti Dockeru z pohľadu bezpečnosti.
Nastavenie rootless Podmanu
Na väčšine moderných distribúcií je Podman už predpripravený na rootless režim. Stačí pár krokov:
# Inštalácia Podmanu
sudo dnf install -y podman # RHEL/Fedora
sudo apt install -y podman # Debian/Ubuntu
# Overenie verzie (Podman 5.x v roku 2026)
podman --version
# Overenie, že bežíte ako bežný používateľ (nie root)
whoami
# admin
# Spustenie prvého rootless kontajnera
podman run --rm -it alpine:latest /bin/sh
# Vo vnútri kontajnera ste "root", ale na hostiteľovi ste bežný user
# Vo vnútri kontajnera:
id
# uid=0(root) gid=0(root)
# Na hostiteľovi (v inom termináli):
ps aux | grep conmon
# admin 12345 ... conmon --api-version 1 ...
Konfigurácia subuid a subgid
Rootless kontajnery využívajú user namespaces, kde root (UID 0) vo vnútri kontajnera je namapovaný na neprivilegovaného používateľa na hostiteľovi. Na to potrebujete správne nastaviť rozsahy subordinovaných UID a GID:
# Zobrazenie aktuálnych rozsahov
cat /etc/subuid
# admin:100000:65536
cat /etc/subgid
# admin:100000:65536
# Formát: používateľ:začiatočný_UID:počet
# admin dostáva UID 100000 až 165535 pre kontajnerové procesy
# Ak záznamy neexistujú, pridajte ich:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 admin
# Po zmene je potrebné migrovať existujúce kontajnery:
podman system migrate
Na hostiteľskom systéme sa potom procesy v kontajneri zobrazujú pod UID v rozsahu 100000+. To znamená, že nemajú absolútne žiadne oprávnenia k súborom hostiteľa. Toto je kľúčový bezpečnostný mechanizmus a podľa mňa jeden z najelegantnejších prístupov k izolácii.
Sieťovanie s pasta (predvolené od Podman 5.3)
Od verzie Podman 5.3 je predvoleným sieťovým backendom pre rootless kontajnery pasta (výslovnosť: "pa-sta" — áno, ako cestovina, nie som si to vymyslel). Nahradil starší slirp4netns a prináša výrazne lepší výkon:
# Overenie sieťového backendu
podman info | grep -i pasta
# networkBackend: pasta
# Spustenie kontajnera s mapovaním portov
podman run -d --name webserver -p 8080:80 nginx:alpine
# Pasta podporuje aj pokročilé mapovanie
podman run -d --name webserver \
-p 8080:80/tcp \
-p 8443:443/tcp \
--network pasta:--map-gw \
nginx:alpine
# Zobrazenie sieťových nastavení kontajnera
podman inspect webserver --format '{{.NetworkSettings}}'
Pasta funguje na princípe kopírovania paketov medzi mennými priestormi pomocou TAP/TUN rozhraní. Oproti slirp4netns ponúka 3-5x vyšší priepustnosť a výrazne nižšiu latenciu. Podporuje IPv6, DNS preposielanie a pokročilé sieťové funkcie — všetko bez nutnosti root oprávnení.
Porovnanie s Docker rootless režimom
Docker tiež ponúka rootless režim, ale s niekoľkými rozdielmi:
- Podman — rootless je predvolený režim, daemonless architektúra, natívna integrácia s user namespaces
- Docker rootless — vyžaduje špeciálnu inštaláciu (
dockerd-rootless-setuptool.sh), stále beží démon (len pod bežným používateľom), niektoré funkcie sú obmedzené - Podman — generuje Kubernetes YAML natívne (
podman generate kube) - Docker — má väčší ekosystém, širšiu komunitu a lepšiu podporu Docker Compose
Z bezpečnostného hľadiska je Podman v rootless režime mierne v predstihu, hlavne vďaka absencii démona a natívnej podpore cgroups v2 delegácie pre neprivilegovaných používateľov.
Seccomp profily: Obmedzte, čo kontajner môže robiť
Čo je Seccomp
Seccomp (Secure Computing Mode) je mechanizmus jadra Linuxu, ktorý filtruje systémové volania, ktoré proces smie vykonať. Funguje ako biely alebo čierny zoznam syscallov — ak sa proces pokúsi zavolať zakázané systémové volanie, jadro ho okamžite ukončí alebo vráti chybu.
Prečo je to dôležité? Predvolený Docker Seccomp profil povoľuje viac ako 300 zo 450+ systémových volaní. Väčšina bežných aplikácií však reálne potrebuje len 40 až 70. To znamená, že predvolený profil necháva otvorené stovky potenciálnych útočných vektorov. Trochu zbytočne, nemyslíte?
Predvolený profil vs. vlastný profil
# Spustenie kontajnera bez Seccomp profilu (NIKDY v produkcii!)
podman run --security-opt seccomp=unconfined alpine sh
# Spustenie s predvoleným profilom (automatické)
podman run alpine sh
# Zobrazenie predvoleného profilu
podman info --format '{{.Host.Security.SECCOMPProfilePath}}'
# Stiahnutie a prehliadanie predvoleného Docker profilu
curl -O https://raw.githubusercontent.com/moby/moby/master/profiles/seccomp/default.json
python3 -m json.tool default.json | head -50
Vytvorenie vlastného Seccomp profilu pomocou strace
Najlepší prístup k vytváraniu Seccomp profilov je profilovanie aplikácie — zistíte, aké systémové volania vaša aplikácia skutočne potrebuje, a povolíte len tie:
# Krok 1: Spustite aplikáciu pod strace a zaznamenajte syscally
strace -cf -o /tmp/syscalls.log podman run --rm nginx:alpine \
sh -c "nginx -g 'daemon off;' & sleep 10; kill %1"
# Krok 2: Extrahovanie zoznamu syscallov
awk 'NR>2 {print $NF}' /tmp/syscalls.log | sort -u > /tmp/syscall-list.txt
# Krok 3: Zobrazenie zoznamu
cat /tmp/syscall-list.txt
# accept4, bind, brk, close, connect, epoll_create1, ...
# Typicky 40-70 syscallov pre bežnú aplikáciu
Automatické generovanie profilu pomocou oci-seccomp-bpf-hook
Existuje aj elegantnejšie riešenie — oci-seccomp-bpf-hook. Tento OCI hook používa eBPF na sledovanie syscallov za behu kontajnera a automaticky vygeneruje Seccomp profil. Namiesto ručného strace-ovania to celé zvládne za vás:
# Inštalácia OCI hook
sudo dnf install -y oci-seccomp-bpf-hook # Fedora/RHEL
sudo apt install -y oci-seccomp-bpf-hook # Debian/Ubuntu
# Generovanie profilu pre Nginx kontajner
sudo podman run --annotation io.containers.trace-syscall="of:/tmp/nginx-seccomp.json" \
-d --name nginx-trace -p 8080:80 nginx:alpine
# Počkajte, kým kontajner spracuje nejaké požiadavky
curl http://localhost:8080
curl http://localhost:8080/nonexistent
# Zastavte kontajner
sudo podman stop nginx-trace
sudo podman rm nginx-trace
# Skontrolujte vygenerovaný profil
python3 -m json.tool /tmp/nginx-seccomp.json
Aplikovanie vlastného Seccomp profilu
Keď máte profil pripravený, aplikujete ho pri spustení kontajnera:
# Aplikovanie vlastného profilu
podman run -d --name webserver \
--security-opt seccomp=/tmp/nginx-seccomp.json \
-p 8080:80 \
nginx:alpine
# Overenie, že profil je aktívny
podman inspect webserver --format '{{.HostConfig.SecurityOpt}}'
# Príklad minimálneho Seccomp profilu pre statický webový server
cat <<'EOF' > /tmp/minimal-web-seccomp.json
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 1,
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_AARCH64"
],
"syscalls": [
{
"names": [
"accept4", "bind", "brk", "clone", "close",
"connect", "epoll_create1", "epoll_ctl", "epoll_wait",
"eventfd2", "exit", "exit_group", "fcntl", "fstat",
"futex", "getdents64", "getpid", "getuid", "ioctl",
"listen", "lseek", "mmap", "mprotect", "munmap",
"nanosleep", "newfstatat", "openat", "pread64",
"read", "recvfrom", "rt_sigaction", "rt_sigprocmask",
"sendfile", "setsockopt", "socket", "write", "writev"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
EOF
# Spustenie s minimálnym profilom
podman run -d --name secure-web \
--security-opt seccomp=/tmp/minimal-web-seccomp.json \
-p 8080:80 \
nginx:alpine
Tento prístup dramaticky zredukuje útočnú plochu. Namiesto 300+ povolených syscallov máte len tie, ktoré vaša aplikácia naozaj potrebuje. Ak by sa útočník dostal do kontajnera, jeho možnosti sú extrémne obmedzené — v podstate mu odstrihnete väčšinu nástrojov, s ktorými by mohol niečo spôsobiť.
AppArmor a SELinux: Povinná kontrola prístupu pre kontajnery
AppArmor profily pre Docker/Podman
AppArmor je systém povinnej kontroly prístupu (MAC) predvolene používaný na Debian/Ubuntu distribúciách. Docker aj Podman automaticky aplikujú predvolený AppArmor profil na kontajnery:
# Overenie, že AppArmor je aktívny
sudo aa-status
# Zobrazenie profilu aplikovaného na kontajner
podman inspect webserver --format '{{.AppArmorProfile}}'
# Výstup: containers-default-0.X.Y
# Vytvorenie vlastného AppArmor profilu pre kontajner
cat <<'EOF' | sudo tee /etc/apparmor.d/kontajner-nginx
#include <tunables/global>
profile kontajner-nginx flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
#include <abstractions/nameservice>
# Povolenie čítania webového obsahu
/usr/share/nginx/html/** r,
/etc/nginx/** r,
# Povolenie zápisu do logov
/var/log/nginx/** w,
# Povolenie sieťového prístupu
network inet stream,
network inet6 stream,
# Zakázanie zápisu do citlivých súborov
deny /etc/shadow rw,
deny /etc/passwd w,
deny /proc/sys/** w,
deny /sys/** w,
# Zakázanie mount operácií
deny mount,
deny umount,
deny pivot_root,
}
EOF
# Načítanie profilu
sudo apparmor_parser -r /etc/apparmor.d/kontajner-nginx
# Aplikovanie na kontajner
podman run -d --name secure-nginx \
--security-opt apparmor=kontajner-nginx \
-p 8080:80 \
nginx:alpine
SELinux značky pre Podman (automatické)
Na distribúciách s SELinuxom (RHEL, Fedora, CentOS) je situácia ešte príjemnejšia — Podman automaticky aplikuje SELinux kontextové značky na kontajnery bez akejkoľvek konfigurácie z vašej strany:
# Overenie SELinux režimu
getenforce
# Enforcing
# Spustenie kontajnera - SELinux značky sa nastavia automaticky
podman run -d --name webserver -p 8080:80 nginx:alpine
# Zobrazenie SELinux kontextu kontajnerových procesov
ps -eZ | grep nginx
# system_u:system_r:container_t:s0:c123,c456 ... nginx
# Kontajnerové súbory majú značku container_file_t
podman exec webserver ls -Z /etc/nginx/nginx.conf
# system_u:object_r:container_file_t:s0 nginx.conf
# Montovanie hostiteľského adresára s SELinux relabeling
podman run -d --name webserver \
-v /srv/web:/usr/share/nginx/html:Z \
-p 8080:80 \
nginx:alpine
# :Z automaticky nastaví správne SELinux značky (privátne pre kontajner)
# :z nastaví značky zdieľané medzi kontajnermi
Vytvorenie vlastných SELinux politík
Pre pokročilé scenáre môžete vytvoriť vlastné SELinux politiky:
# Inštalácia nástrojov na tvorbu politík
sudo dnf install -y selinux-policy-devel udica
# Generovanie SELinux politiky z bežiaceho kontajnera pomocou udica
podman inspect webserver | sudo udica moj-webserver
# Výstup vám poskytne inštrukcie na aplikovanie politiky:
# sudo semodule -i moj-webserver.cil /usr/share/udica/templates/{base_container.cil,net_container.cil}
# Aplikovanie na kontajner
podman run -d --name secure-webserver \
--security-opt label=type:moj-webserver.process \
-p 8080:80 \
nginx:alpine
# Analýza SELinux zamietnutí
sudo ausearch -m AVC -ts recent | audit2allow -w
Udica je fantastický nástroj od Red Hatu, ktorý analyzuje bežiaci kontajner a automaticky vygeneruje optimálnu SELinux politiku. Povedzme si to na rovinu — ručné písanie SELinux politík je niečo, čo dobrovoľne nechce robiť asi nikto. Udica tento proces dramaticky zjednodušuje a za tie roky, čo ho používam, mi ušetril nespočetné hodiny.
Linux capabilities: Princíp najmenších oprávnení
Čo sú capabilities
Tradične mal root na Linuxe neobmedzené oprávnenia. Linux capabilities rozdeľujú tieto oprávnenia na menšie, granulárne jednotky. Kontajner nepotrebuje plné root oprávnenia — potrebuje len konkrétne schopnosti.
Predvolene Docker a Podman prideľujú kontajnerom tieto capabilities:
# Zobrazenie predvolených capabilities
podman run --rm alpine grep Cap /proc/1/status
# CapPrm: 00000000800425fb
# CapEff: 00000000800425fb
# Dekódovanie pomocou capsh
podman run --rm alpine sh -c 'apk add -q libcap && capsh --decode=00000000800425fb'
# Predvolené: CHOWN, DAC_OVERRIDE, FOWNER, FSETID, KILL,
# NET_BIND_SERVICE, SETFCAP, SETGID, SETPCAP, SETUID,
# SYS_CHROOT, NET_RAW, AUDIT_WRITE
Odstránenie všetkých a pridanie len potrebných
Najlepšia prax je jednoduchá — začnite s nulovými oprávneniami a pridajte len tie, ktoré aplikácia skutočne vyžaduje:
# Odstránenie VŠETKÝCH capabilities a pridanie len potrebných
# Pre Nginx webový server:
podman run -d --name hardened-nginx \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--cap-add=CHOWN \
--cap-add=SETGID \
--cap-add=SETUID \
-p 8080:80 \
nginx:alpine
# Pre statickú aplikáciu, ktorá nepotrebuje nič špeciálne:
podman run -d --name static-app \
--cap-drop=ALL \
-p 8080:8080 \
moja-staticka-app:latest
# Overenie capabilities bežiaceho kontajnera
podman exec hardened-nginx grep Cap /proc/1/status
Nebezpečné capabilities, ktoré nikdy nepridávajte
Niektoré capabilities sú obzvlášť nebezpečné. Ich pridanie efektívne zruší izoláciu kontajnera:
- SYS_ADMIN — umožňuje mount operácie, manipuláciu s mennými priestormi, prístup k /proc a /sys. Toto je v podstate "root na hostiteľovi". Nikdy nepridávajte túto capability.
- NET_RAW — umožňuje vytváranie raw socketov, čo útočníkovi otvorí dvere k ARP spoofingu, DNS poisoningu a odpočúvaniu sieťovej prevádzky
- SYS_PTRACE — umožňuje debugging iných procesov, čo sa dá zneužiť na čítanie pamäte iných kontajnerov
- SYS_MODULE — umožňuje načítavanie modulov jadra. S touto capability môže útočník načítať rootkit priamo do jadra
- DAC_READ_SEARCH — obchádza kontroly oprávnení pri čítaní súborov, umožňuje čítať akýkoľvek súbor na hostiteľovi
- NET_ADMIN — umožňuje konfiguráciu sieťových rozhraní, firewallu a routingu, čo môže viesť k úniku dát
# NIKDY toto nerobte v produkcii:
# podman run --privileged ... # Všetky capabilities!
# podman run --cap-add=SYS_ADMIN ... # Takmer root
# podman run --cap-add=ALL ... # Rovnaké ako --privileged
# Namiesto toho vždy:
podman run --cap-drop=ALL --cap-add=NEEDED_CAP ...
Zabudnite na --privileged. Ak vám niekto tvrdí, že kontajner potrebuje --privileged, znamená to, že sa treba zamyslieť nad architektúrou — nie nad bezpečnostnými výnimkami. V 99 % prípadov existuje lepšie riešenie. Zvyšné 1 %? No, aj tam sa zvyčajne nájde.
Zabezpečenie kontajnerových obrazov
Distroless obrazy
Tradičné kontajnerové obrazy obsahujú celý operačný systém — shell, balíčkový manažér, ladiacie nástroje. Pre útočníka je to dar z nebies. Distroless obrazy od Google obsahujú len vašu aplikáciu a jej runtime závislosti — žiadny shell, žiadny apt, žiadny curl:
# Tradičný obraz (nebezpečný):
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y python3
COPY app.py /app/
CMD ["python3", "/app/app.py"]
# Výsledok: 200+ MB, stovky balíčkov, shell prístupný
# Distroless obraz (bezpečný):
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
FROM gcr.io/distroless/python3-debian12:nonroot
COPY --from=builder /app /app
WORKDIR /app
CMD ["app.py"]
# Výsledok: ~50 MB, žiadny shell, žiadne nástroje pre útočníka
Multi-stage buildy
Multi-stage buildy sú kľúčová technika na minimalizáciu veľkosti a útočnej plochy finálneho obrazu. Princíp je jednoduchý — buildujte v jednom kontajneri, bežte v druhom (minimálnom):
# Multi-stage build pre Go aplikáciu
FROM golang:1.23-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app
# Finálny minimálny obraz
FROM scratch
COPY --from=builder /app /app
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
USER 65534:65534
ENTRYPOINT ["/app"]
# Výsledok: ~5 MB, statický binárny súbor, žiadne zraniteľné závislosti
Skenovanie obrazov pomocou Trivy
Pred nasadením akéhokoľvek obrazu do produkcie ho musíte preskenovať na známe zraniteľnosti. Nie je to voliteľné — je to nevyhnutnosť:
# Inštalácia Trivy
sudo dnf install -y trivy # Fedora/RHEL (cez RPM repo)
# Alebo:
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# Skenovanie obrazu na zraniteľnosti
trivy image nginx:alpine
# Skenovanie len kritických a vysokých zraniteľností
trivy image --severity HIGH,CRITICAL nginx:alpine
# Skenovanie Dockerfile na chyby v konfigurácii
trivy config ./Dockerfile
# Skenovanie s výstupom v JSON pre automatizáciu
trivy image --format json --output report.json nginx:alpine
# Integrácia do CI/CD — zlyhanie ak existujú kritické zraniteľnosti
trivy image --exit-code 1 --severity CRITICAL moja-app:latest
# Skenovanie lokálneho Podman obrazu
trivy image --input $(podman save moja-app:latest -o /tmp/image.tar && echo /tmp/image.tar)
Podpisovanie obrazov s Cosign/Sigstore
Podpisovanie obrazov zaručuje, že obraz nebol zmanipulovaný a pochádza z dôveryhodného zdroja. V dnešnej dobe supply chain útokov je to absolútna nutnosť:
# Inštalácia Cosign
go install github.com/sigstore/cosign/v2/cmd/cosign@latest
# Generovanie podpisového kľúča
cosign generate-key-pair
# Vytvorí cosign.key (privátny) a cosign.pub (verejný)
# Podpísanie obrazu
cosign sign --key cosign.key registry.example.com/moja-app:v1.0
# Keyless podpísanie cez Sigstore (Fulcio + Rekor)
cosign sign registry.example.com/moja-app:v1.0
# Autentifikuje vás cez OIDC (GitHub, Google) a uloží podpis v Rekor transparentnom loge
# Overenie podpisu
cosign verify --key cosign.pub registry.example.com/moja-app:v1.0
# Keyless overenie
cosign verify \
--certificate-identity [email protected] \
--certificate-oidc-issuer https://accounts.google.com \
registry.example.com/moja-app:v1.0
# Vynútenie podpisov v Kubernetes cez Kyverno alebo Sigstore Policy Controller
# Toto zaručí, že sa do klastra dostanú len podpísané obrazy
Zraniteľnosti runc z novembra 2025: Detailná analýza
CVE-2025-31133: Symlink útok cez maskované cesty
Táto zraniteľnosť sa týka mechanizmu maskovaných ciest (masked paths) v OCI konfigurácii. Kontajnery štandardne maskujú určité cesty v /proc a /sys, aby zabránili prístupu k citlivým informáciám hostiteľa. Problém? Zraniteľnosť umožňovala útočníkovi vytvoriť symbolický odkaz v jednej z maskovaných ciest pred tým, než runc aplikoval maskovanie — čím efektívne obišiel túto ochranu a získal prístup k súborovému systému hostiteľa.
# Problém: runc kontroloval maskované cesty, ale neoveroval symlinky
# Útočník s prístupom do kontajnera mohol:
# 1. Vytvoriť symlink: /proc/acpi -> /host/etc/shadow
# 2. runc maskoval /proc/acpi (namontoval tmpfs), ale symlink bol už nastavený
# 3. Prístup k hostiteľovým súborom cez symlink
# Opravené v runc 1.2.8, 1.3.3 a 1.4.0-rc.3
# Overenie verzie runc:
runc --version
# runc version 1.2.8 alebo novšia
CVE-2025-52565: Symlink útok cez /dev/console
Druhá zraniteľnosť bola v spracovaní /dev/console zariadenia. Runc nastavoval /dev/console vo vnútri kontajnera, ale nedostatočne overoval, či cesta nie je symbolickým odkazom. Útočník mohol nahradiť /dev/console symlinkom na ľubovoľný súbor na hostiteľovi a následne doňho zapisovať. Výsledok? Potenciálne prepísanie konfiguračných súborov hostiteľa alebo eskalácia oprávnení. Nie je to presne ten typ chyby, pri ktorom by ste chceli byť nechránený.
CVE-2025-52881: Obchádzanie LSM bezpečnostných značiek
Tretia zraniteľnosť umožňovala kontajnerovým procesom obísť LSM (Linux Security Module) značky — konkrétne SELinux a AppArmor. Chyba bola v tom, ako runc nastavoval bezpečnostné značky pre procesy vo vnútri kontajnera. Za určitých podmienok mohol proces v kontajneri bežať bez správnych SELinux/AppArmor obmedzení, čím sa stal v podstate neviditeľným pre systém povinnej kontroly prístupu.
To je ako mať bezpečnostné kamery v budove, ale jedna miestnosť nemá pokrytie. Útočník presne vie, kam ísť.
Mitigácia a opravy
# Krok 1: Aktualizácia runc na opravenú verziu
sudo dnf update -y runc # RHEL/Fedora
sudo apt update && sudo apt install -y runc # Debian/Ubuntu
# Overenie verzie (minimálne 1.2.8, 1.3.3 alebo 1.4.0-rc.3)
runc --version
# Krok 2: Mitigácia pomocou user namespaces (ak nemôžete okamžite aktualizovať)
# User namespaces zmierňujú všetky tri zraniteľnosti, pretože
# aj pri úniku z kontajnera je útočník neprivilegovaným používateľom
# Pre Podman - rootless režim automaticky používa user namespaces
podman run --rm alpine id
# uid=0(root) ale na hostiteľovi je to UID 100000+
# Pre Docker - explicitné povolenie user namespaces
# V /etc/docker/daemon.json:
{
"userns-remap": "default"
}
# Reštart Docker:
sudo systemctl restart docker
# Krok 3: Overenie, že kontajnery bežia v user namespace
podman top moj-kontajner huser hgroup
# Zobraží skutočné UID/GID na hostiteľovi
# Krok 4: Kontrola, či sú maskované cesty správne nastavené
podman run --rm alpine ls -la /proc/acpi
# Malo by zobraziť prázdny obsah (maskované)
Tieto tri zraniteľnosti jasne ukazujú, prečo je viacvrstvová obrana (defense in depth) taká dôležitá. Aj keď runc mal chyby, kombinácia user namespaces, Seccomp profilov a SELinux/AppArmor výrazne zmierňuje dopad akéhokoľvek zlyhania. Žiadna jednotlivá vrstva nie je dokonalá — ale spolu tvoria solídnu obranu.
Runtime monitoring: Detekcia anomálií v reálnom čase
Falco: Open-source runtime bezpečnosť
Falco od Sysdig je de-facto štandard pre runtime bezpečnosť kontajnerov. Monitoruje systémové volania jadra a porovnáva ich s pravidlami, ktoré definujú podozrivé správanie. Keď som ho prvýkrát nasadil do produkcie, bol som prekvapený, koľko podozrivej aktivity odhalil aj v zdanlivo bezpečnom prostredí:
# Inštalácia Falco
curl -fsSL https://falco.org/repo/falcosecurity-packages.asc | \
sudo gpg --dearmor -o /usr/share/keyrings/falco-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] \
https://download.falco.org/packages/deb stable main" | \
sudo tee /etc/apt/sources.list.d/falcosecurity.list
sudo apt update && sudo apt install -y falco
# Spustenie Falco
sudo systemctl enable --now falco
# Príklad vlastného Falco pravidla pre kontajnery
cat <<'EOF' | sudo tee /etc/falco/rules.d/kontajner-rules.yaml
- rule: Shell spustený v kontajneri
desc: Detekcia spustenia shellu v produkcii
condition: >
container.id != host and
proc.name in (bash, sh, zsh, ash, dash) and
container.image.repository != "debug-tools"
output: >
Shell spustený v kontajneri
(user=%user.name container=%container.name
shell=%proc.name parent=%proc.pname
image=%container.image.repository:%container.image.tag)
priority: WARNING
tags: [container, shell]
- rule: Citlivý súbor otvorený v kontajneri
desc: Detekcia prístupu k citlivým súborom
condition: >
container.id != host and
open_read and
fd.name in (/etc/shadow, /etc/sudoers, /etc/pam.conf)
output: >
Citlivý súbor otvorený v kontajneri
(user=%user.name file=%fd.name container=%container.name
image=%container.image.repository)
priority: CRITICAL
tags: [container, filesystem]
- rule: Neočakávaný odchádzajúci prenos
desc: Kontajner komunikuje s neznámymi externými IP
condition: >
container.id != host and
(evt.type in (connect, sendto, sendmsg)) and
fd.sip.name != "" and
not fd.sip.name in (trusted-dns.example.com, api.example.com)
output: >
Neočakávané odchádzajúce spojenie z kontajnera
(container=%container.name dest=%fd.sip command=%proc.cmdline)
priority: NOTICE
tags: [container, network]
EOF
# Reštart Falco na načítanie nových pravidiel
sudo systemctl restart falco
# Sledovanie výstupov v reálnom čase
sudo journalctl -fu falco
Tetragon: eBPF-based runtime bezpečnosť
Cilium Tetragon je modernejšia alternatíva, ktorá využíva eBPF priamo v jadre. Na rozdiel od Falco, ktorý pôvodne používal modul jadra, Tetragon je plne založený na eBPF — čo znamená nižšiu réžiu a menšie bezpečnostné riziko samotného monitorovacieho nástroja:
# Inštalácia Tetragonu (ako DaemonSet v Kubernetes)
helm repo add cilium https://helm.cilium.io
helm install tetragon cilium/tetragon -n kube-system
# Alebo lokálne na standalone Linuxe
curl -LO https://github.com/cilium/tetragon/releases/latest/download/tetragon-linux-amd64.tar.gz
tar xzf tetragon-linux-amd64.tar.gz
sudo cp tetragon /usr/local/bin/
# Príklad TracingPolicy pre detekciu úniku z kontajnera
cat <<'EOF' > container-escape-policy.yaml
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: container-escape-detection
spec:
kprobes:
- call: "__x64_sys_setns"
syscall: true
args:
- index: 0
type: "int"
selectors:
- matchActions:
- action: Sigkill
matchNamespaces:
- namespace: Pid
operator: NotIn
values:
- "host_ns"
- call: "__x64_sys_unshare"
syscall: true
args:
- index: 0
type: "int"
selectors:
- matchActions:
- action: Post
matchNamespaces:
- namespace: Pid
operator: NotIn
values:
- "host_ns"
EOF
# Aplikovanie politiky
kubectl apply -f container-escape-policy.yaml
# Sledovanie udalostí pomocou tetra CLI
tetra getevents -o compact
Tetragon má oproti Falcu jednu zásadnú výhodu — dokáže nielen detekovať, ale aj aktívne zabrániť podozrivej aktivite v reálnom čase priamo v jadre. Pomocou akcií ako Sigkill môže okamžite ukončiť proces, ktorý sa pokúša o únik z kontajnera. Ešte predtým, než stihne čokoľvek dokončiť. To je dosť veľký rozdiel.
Kompletný checklist na zabezpečenie kontajnerov
Na záver tu máte súhrnný kontrolný zoznam, ktorý pokrýva všetko podstatné, o čom sme hovorili. Odporúčam si ho uložiť (alebo rovno vytlačiť) a prejsť pri každom nasadení:
Runtime a izolácia
- Používajte rootless kontajnery (Podman rootless alebo Docker rootless) — nikdy nespúšťajte kontajnery pod rootom v produkcii
- Udržujte runc aktualizovaný na najnovšiu verziu (minimálne 1.2.8/1.3.3/1.4.0-rc.3 kvôli novembrovým CVE z roku 2025)
- Povoľte user namespaces aj pri Dockeri (userns-remap) pre dodatočnú izoláciu
- Nikdy nepoužívajte
--privilegedflag - Nastavte limity zdrojov (--memory, --cpus) na prevenciu DoS útokov
- Používajte read-only root filesystem kde je to možné:
--read-only
Systémové volania a capabilities
- Aplikujte vlastný Seccomp profil na každý kontajner — predvolený je príliš permisívny
- Používajte
--cap-drop=ALLa pridávajte len potrebné capabilities - Nikdy nepridávajte SYS_ADMIN, SYS_PTRACE, SYS_MODULE ani NET_RAW do produkcie
- Pravidelne auditujte capabilities bežiacich kontajnerov
Povinná kontrola prístupu (MAC)
- Uistite sa, že SELinux beží v enforcing režime (RHEL/Fedora) alebo AppArmor je aktívny (Debian/Ubuntu)
- Vytvorte vlastné SELinux politiky pomocou udica pre špecializované kontajnery
- Vytvorte vlastné AppArmor profily pre kontajnery s netypickými požiadavkami
- Nikdy nespúšťajte kontajnery s
--security-opt label=disablealebo--security-opt apparmor=unconfined
Obrazy a supply chain
- Používajte distroless alebo scratch obrazy kde sa len dá
- Implementujte multi-stage buildy na minimalizáciu finálneho obrazu
- Skenujte obrazy pomocou Trivy pred každým nasadením
- Podpisujte obrazy pomocou Cosign/Sigstore a vynucujte overenie podpisov
- Používajte konkrétne tagy (alebo SHA digest), nikdy nie
:latest - Pravidelne aktualizujte base obrazy a rebildujte kontajnery
Sieťová bezpečnosť
- Implementujte sieťové politiky (NetworkPolicy v Kubernetes, Podman network isolation)
- Obmedzte odchádzajúcu komunikáciu — kontajner by mal komunikovať len s definovanými službami
- Šifrujte komunikáciu medzi kontajnermi pomocou mTLS (Istio, Linkerd)
- Neexponujte porty, ktoré nie sú potrebné
Monitoring a reakcia na incidenty
- Nasaďte Falco alebo Tetragon na runtime detekciu anomálií
- Monitorujte systémové volania, sieťové spojenia a súborové operácie
- Nastavte alerty na spustenie shellu v kontajneri, prístup k citlivým súborom a neočakávané sieťové spojenia
- Centralizujte logy kontajnerov (ELK stack, Loki + Grafana)
- Pravidelne testujte váš incident response plán pre kontajnerové prostredie
Ukážka kompletne zabezpečeného kontajnera
Takto vyzerá spustenie kontajnera so všetkými bezpečnostnými vrstvami naraz:
# Kompletne zabezpečený kontajner s Podmanom (rootless)
podman run -d \
--name production-app \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--security-opt seccomp=/etc/containers/seccomp/app-profile.json \
--security-opt label=type:moja_app.process \
--security-opt no-new-privileges:true \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=64m \
--memory=512m \
--cpus=1.0 \
--pids-limit=100 \
--network pasta:--map-gw \
-p 8080:8080 \
--health-cmd="curl -f http://localhost:8080/health || exit 1" \
--health-interval=30s \
--user 65534:65534 \
registry.example.com/moja-app@sha256:abc123...
# Vysvetlenie parametrov:
# --cap-drop=ALL --cap-add=NET_BIND_SERVICE : minimálne capabilities
# --security-opt seccomp=... : vlastný Seccomp profil
# --security-opt label=type:... : vlastná SELinux politika
# --security-opt no-new-privileges:true : zákaz eskalácie oprávnení
# --read-only : súborový systém len na čítanie
# --tmpfs /tmp:... : dočasný adresár s obmedzeniami
# --memory, --cpus, --pids-limit : limity zdrojov
# --user 65534:65534 : beh ako nobody (nie root ani v kontajneri)
# @sha256:... : pinning na konkrétny digest obrazu
Áno, je to dlhý príkaz. Ale každý parameter má svoj dôvod a spolu vytvárajú viacvrstvovú obranu, kde zlyhanie jednej vrstvy neznamená kompromitáciu celého systému. Skopírujte si ho, upravte pre vašu aplikáciu a používajte ako šablónu.
Záver: Bezpečnosť kontajnerov je proces, nie stav
Zabezpečenie kontajnerov nie je niečo, čo nastavíte raz a zabudnete na to. Je to kontinuálny proces — nové zraniteľnosti sa objavujú pravidelne (ako sme videli s runc CVE v novembri 2025), útočné techniky sa vyvíjajú a vaše aplikácie sa menia.
Kľúčové princípy, ktoré si odneste:
- Rootless kontajnery by mali byť predvolené nastavenie, nie výnimka. Podman to robí jednoducho.
- Princíp najmenších oprávnení — odstraňte všetky capabilities, syscally a prístupy, ktoré aplikácia nepotrebuje. Čo neexistuje, to sa nedá zneužiť.
- Viacvrstvová obrana — kombinujte user namespaces, Seccomp, SELinux/AppArmor a runtime monitoring. Ak jedna vrstva zlyhá, ďalšie vás ochránia.
- Supply chain bezpečnosť — skenujte a podpisujte obrazy, používajte minimálne base obrazy.
- Aktívny monitoring — bez runtime detekcie ste slepí voči útokom, ktoré prekonajú preventívne opatrenia.
Nástroje ako Podman 5.x s pasta networkingom, Trivy, Cosign a Tetragon robia zabezpečenie kontajnerov dostupnejším než kedykoľvek predtým. Nemáte výhovorku ho ignorovať. Začnite dnes — vaša infraštruktúra (a vaše pokojné noci) vám poďakujú.