Bevezetés – Miért fontosabb a konténerbiztonság, mint valaha?
A konténerizáció már rég nem újdonság – a legtöbb vállalat éles környezetben használja a Docker-t és a Kubernetes-t. Ami viszont 2026-ban is változatlanul aktuális (és őszintén szólva, egyre sürgetőbb), az a biztonsági kérdések komolyan vétele. A konténerek nem mágikus védelmi buborékok. Ugyanazt a Linux kernelt használják, mint a gazdarendszer, és egyetlen félrekonfigurált paraméter elég ahhoz, hogy a támadó kitörjön a konténerből.
Hogy ez nem elméleti fenyegetés, azt a 2025-ös év sebezhetőségei fényesen bizonyítják.
2025 novemberében három súlyos runc sebezhetőséget publikáltak: a CVE-2025-31133, CVE-2025-52565 és CVE-2025-52881 mindegyike a /proc fájlrendszeren keresztüli írási műveletek kihasználásával tette lehetővé a konténerből való kitörést. A CVE-2025-31133 konkrétan a maskedPaths mechanizmus TOCTOU (Time-of-Check to Time-of-Use) versenyhelyét használta ki – a támadó a /proc/sysrq-trigger vagy a /proc/sys/kernel/core_pattern fájlba írva tudták összeomlasztani a gazdarendszert, vagy tetszőleges kódot futtatni rajta.
2025 augusztusában pedig a CVE-2025-9074 mutatta meg, hogy a Docker Desktop sem sérthetetlen: ez a 9.3-as CVSS pontszámú kritikus sebezhetőség lehetővé tette, hogy egy lokálisan futó konténer hitelesítés nélkül elérje a Docker Engine API-t a 192.168.65.7:2375 címen, és azon keresztül további konténereket indítson – akár a teljes gazda fájlrendszert felcsatolva. Elég ijesztő, nem?
Na, vágjunk bele. Ebben a cikkben végigvesszük azokat a védelmi rétegeket és gyakorlati technikákat, amelyekkel a konténerizált környezeteinket valóban biztonságossá tehetjük. Az izolációs alapoktól a futásidejű védelemig – mindent lefedve, gyakorlati példákkal.
A konténerizolációs alapok megértése
Mielőtt bármilyen „hardening" technikát alkalmaznánk, fontos megérteni, hogyan működik a konténerizoláció a Linux kernel szintjén. Két alapvető mechanizmusra épül: a namespaces (névterek) és a cgroups (vezérlőcsoportok). Ezek nélkül igazából nem beszélhetünk konténerekről.
Linux Namespaces – Az izoláció pillérei
A namespace-ek lényegében a rendszer erőforrásainak virtuális nézeteit hozzák létre. Minden konténer a saját „világát" látja, anélkül, hogy tudna a többi konténerről vagy a gazdarendszerről. A Linux jelenleg hét namespace típust támogat:
- PID namespace – Folyamatazonosítók izolációja. A konténerben a főfolyamat PID 1-ként jelenik meg, és nem látja a gazdarendszer folyamatait.
- NET namespace – Hálózati verem izolációja. Minden konténer saját hálózati interfészekkel, routing táblával és tűzfalszabályokkal rendelkezik.
- MNT namespace – Fájlrendszer-felcsatolások izolációja. A konténer saját mount pontokkal rendelkezik.
- UTS namespace – Hostname és domain név izolációja.
- IPC namespace – Folyamatok közötti kommunikáció izolációja (shared memory, szemaforok).
- USER namespace – Felhasználói és csoport azonosítók leképezése. Lehetővé teszi, hogy a konténerben root-ként futó folyamat a gazdarendszeren nem-privált felhasználóként fusson.
- CGROUP namespace – A cgroup hierarchia nézetét izolálja, így a konténer csak a saját erőforrás-korlátait látja.
A namespace-ek állapotát bármikor ellenőrizhetjük:
# Egy futó konténer namespace-einek listázása
docker inspect --format '{{.State.Pid}}' my_container
# Tegyük fel, a PID 12345
ls -la /proc/12345/ns/
# Kimenet:
# lrwxrwxrwx 1 root root 0 ... cgroup -> 'cgroup:[4026532516]'
# lrwxrwxrwx 1 root root 0 ... ipc -> 'ipc:[4026532446]'
# lrwxrwxrwx 1 root root 0 ... mnt -> 'mnt:[4026532444]'
# lrwxrwxrwx 1 root root 0 ... net -> 'net:[4026532449]'
# lrwxrwxrwx 1 root root 0 ... pid -> 'pid:[4026532447]'
# lrwxrwxrwx 1 root root 0 ... user -> 'user:[4026531837]'
# lrwxrwxrwx 1 root root 0 ... uts -> 'uts:[4026532445]'
Cgroups v2 – Egységes erőforrás-kezelés
A cgroups v2 (unified hierarchy) már az alapvető a modern Linux disztribúciókban. Szemben a v1-es verzióval, ahol minden erőforrástípus (CPU, memória, I/O) külön hierarchiában élt, a v2 egyetlen egységes fában kezel mindent. Ez nemcsak egyszerűbb, de biztonsági szempontból is előnyös, mert megakadályozza az inkonzisztens erőforrás-korlátok kialakítását.
# Cgroups v2 ellenőrzése
mount | grep cgroup2
# Kimenet: cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime)
# Docker konténer erőforrás-korlátainak beállítása cgroups v2-vel
docker run -d \
--memory="256m" \
--memory-swap="512m" \
--cpus="0.5" \
--pids-limit=100 \
--name secure_app \
my_app:latest
Docker biztonsági keményítés
A Docker alapértelmezett konfigurációja a használhatóságra optimalizált, nem a maximális biztonságra. Ez teljesen érthető – senki sem akar egy vadonatúj telepítéssel órákig küzdeni. Viszont éles környezetben ez nem jó ötlet. Nézzük át azokat a beállításokat, amelyeket minden produkciós környezetben alkalmazni kellene.
Docker daemon biztonsági beállítások
A Docker daemon konfigurációja a /etc/docker/daemon.json fájlban történik. Íme egy biztonságilag keményített konfiguráció:
{
"icc": false,
"log-driver": "journald",
"log-opts": {
"tag": "{{.Name}}"
},
"no-new-privileges": true,
"userland-proxy": false,
"live-restore": true,
"userns-remap": "default",
"seccomp-profile": "/etc/docker/seccomp-strict.json",
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 64000,
"Soft": 64000
}
}
}
A legfontosabb beállítások magyarázata:
- icc: false – Letiltja a konténerek közötti közvetlen kommunikációt. Csak az explicit
--linkvagy hálózati kapcsolatokkal engedélyezett forgalom működik. - no-new-privileges: true – Megakadályozza, hogy a konténerben futó folyamatok új jogosultságokat szerezzenek (pl. setuid biten keresztül).
- userns-remap: default – Bekapcsolja a user namespace leképezést, amiről részletesen beszélünk a következőkben.
Rootless Docker mód
A rootless Docker az egyik leghatékonyabb védelmi réteg, és személyes tapasztalatom szerint az egyik legegyszerűbben bevezethető is. A lényeg: a teljes Docker daemon nem-root felhasználóként fut. Ez azt jelenti, hogy még ha a támadó kitör is a konténerből, nem root jogosultságot kap a gazdarendszeren.
# Rootless Docker telepítése
# Előfeltételek: uidmap, dbus-user-session csomagok
sudo apt-get install -y uidmap dbus-user-session fuse-overlayfs
# Telepítés az aktuális felhasználónak
dockerd-rootless-setuptool.sh install
# Környezeti változók beállítása
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
# Beállítás a .bashrc-be
echo 'export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock' >> ~/.bashrc
# Ellenőrzés
docker context use rootless
docker info | grep -i "rootless"
# Kimenet: rootless: true
Capabilities csökkentése
A Linux capabilities rendszere a root jogosultságokat kisebb, külön kezelhető egységekre bontja. A Docker alapértelmezetten 14 capability-t ad a konténereknek – ez jóval több, mint amennyire a legtöbb alkalmazásnak szüksége van. A legjobb gyakorlat egyszerű: mindent elveszünk, és csak a ténylegesen szükségeseket adjuk vissza.
# Minden capability eldobása, majd csak a szükségesek visszaadása
docker run -d \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--cap-add CHOWN \
--cap-add SETUID \
--cap-add SETGID \
--name secure_nginx \
nginx:alpine
# Futó konténer capability-jeinek ellenőrzése
docker exec secure_nginx cat /proc/1/status | grep Cap
# CapPrm: 00000000a80c25fb (alapértelmezett - sok jogosultság)
# vs.
# CapPrm: 00000000000c0400 (korlátozott - csak a szükségesek)
Csak olvasható fájlrendszer és no-new-privileges
A konténer fájlrendszerének csak olvashatóvá tétele megakadályozza, hogy a támadó módosítsa a fájlokat a konténerben. Gondoljunk bele: ha a fájlrendszer read-only, a támadó nem tud rosszindulatú binárist elhelyezni, nem tud konfigurációs fájlokat átírni. Persze az alkalmazásnak szüksége lehet írható könyvtárakra – erre valók a tmpfs mount-ok.
# Csak olvasható fájlrendszer + tmpfs az ideiglenes fájloknak
docker run -d \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=64m \
--tmpfs /var/run:rw,noexec,nosuid,size=16m \
--tmpfs /var/cache/nginx:rw,noexec,nosuid,size=32m \
--security-opt no-new-privileges:true \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--name hardened_nginx \
nginx:alpine
User namespace remapping
A user namespace remapping leképezi a konténer root felhasználóját egy magas UID-vel rendelkező, nem-privilegizált felhasználóra a gazdarendszeren. Ezt az egyik legfontosabb védelmi rétegnek tartom a konténer-kitörés ellen.
# User namespace remapping konfigurálása
# 1. Felhasználó létrehozása a leképezéshez
sudo useradd -r -s /bin/false dockremap
# 2. UID/GID leképezés beállítása
echo "dockremap:100000:65536" | sudo tee /etc/subuid
echo "dockremap:100000:65536" | sudo tee /etc/subgid
# 3. Docker daemon konfigurálása (/etc/docker/daemon.json)
# "userns-remap": "dockremap"
# 4. Docker daemon újraindítása
sudo systemctl restart docker
# 5. Ellenőrzés
docker run --rm alpine id
# uid=0(root) gid=0(root) -- a konténerben root-nak tűnik
# De a gazdagépen:
ps aux | grep "alpine"
# A folyamat UID 100000-ként fut a gazdagépen!
Seccomp profilok használata
A Seccomp (Secure Computing Mode) a Linux kernel egy olyan funkciója, amely lehetővé teszi a rendszerhívások (syscalls) szűrését. Miért olyan fontos ez? Azért, mert a kernel sebezhetőségek túlnyomó többségét rendszerhívásokon keresztül lehet kihasználni. Ha egy rendszerhívás el van tiltva, a rá épülő exploit egyszerűen nem működik.
Docker alapértelmezett seccomp profil
A Docker alapértelmezetten használ seccomp profilt, amely körülbelül 44–51 rendszerhívást blokkol a 300+-ból. Ez magában foglalja a veszélyes hívásokat, mint az unshare (namespace létrehozás), mount, reboot, kexec_load és az io_uring családot, amelyek számos kernel sebezhetőség forrásai. Az alapértelmezett profil allowlist alapú: a SCMP_ACT_ERRNO az alapértelmezett akció, és csak a külön engedélyezett hívások hajthatók végre.
Egyedi seccomp profil létrehozása
A legtöbb alkalmazásnak jóval kevesebb rendszerhívásra van szüksége, mint amennyit az alapértelmezett profil engedélyez. Érdemes tehát egy szorított profilt készíteni – ez egy kicsit munkaigényes, de megéri.
{
"defaultAction": "SCMP_ACT_ERRNO",
"archMap": [
{
"architecture": "SCMP_ARCH_X86_64",
"subArchitectures": [
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
]
}
],
"syscalls": [
{
"names": [
"accept", "accept4", "access", "bind", "brk",
"clock_getres", "clock_gettime", "clone", "close",
"connect", "dup", "dup2", "epoll_create", "epoll_ctl",
"epoll_wait", "execve", "exit", "exit_group",
"fchmod", "fchown", "fcntl", "fstat", "futex",
"getcwd", "getdents64", "getegid", "geteuid",
"getgid", "getpid", "getppid", "getsockname",
"getsockopt", "getuid", "ioctl", "listen",
"lseek", "madvise", "mmap", "mprotect", "munmap",
"nanosleep", "newfstatat", "open", "openat",
"pipe", "poll", "prctl", "pread64", "pwrite64",
"read", "readlink", "recvfrom", "recvmsg",
"rt_sigaction", "rt_sigprocmask", "rt_sigreturn",
"sendmsg", "sendto", "set_robust_list",
"set_tid_address", "setsockopt", "shutdown",
"sigaltstack", "socket", "stat", "statfs",
"sysinfo", "uname", "unlink", "wait4", "write",
"writev"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
Mentés után alkalmazzuk a profilt:
# Egyedi seccomp profil alkalmazása Docker konténerre
docker run -d \
--security-opt seccomp=/etc/docker/seccomp-nginx-strict.json \
--name nginx_strict \
nginx:alpine
# Seccomp profil ellenőrzése futó konténeren
docker inspect nginx_strict | jq '.[0].HostConfig.SecurityOpt'
# Tipp: Az OCI seccomp-bpf-hook segítségével automatikusan
# generálhatunk profilt az alkalmazás tényleges syscall használata alapján
sudo dnf install oci-seccomp-bpf-hook # RHEL/Fedora
docker run --annotation io.containers.trace-syscall="of:/tmp/nginx.json" \
nginx:alpine
Seccomp Kubernetes-ben
A Kubernetes-ben a seccomp profilt a Pod Security Context-ben adjuk meg:
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: profiles/nginx-strict.json
containers:
- name: nginx
image: nginx:alpine
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
runAsUser: 101
A profil fájlnak a kubelet --seccomp-profile-root könyvtárában kell lennie (alapértelmezetten /var/lib/kubelet/seccomp). Alternatív megoldásként a RuntimeDefault típus a konténer-runtime alapértelmezett profilját használja – ez az egyszerűbb út, ha nem akarsz egyedi profilt karbantartani.
AppArmor profilok konténerekhez
Az AppArmor (Application Armor) egy Linux kernel biztonsági modul, amely a Mandatory Access Control (MAC) elvén működik. A seccomp-pal ellentétben, amely rendszerhívás szinten szűr, az AppArmor fájlrendszer-hozzáféréseket, hálózati műveleteket és capability-ket szabályoz finomabban. A kettő együtt igazán hatékony párost alkot.
Docker alapértelmezett AppArmor profil
A Docker automatikusan alkalmazza a docker-default AppArmor profilt minden konténerre (feltéve, hogy az AppArmor elérhető a rendszeren). Ez a profil a következőket teszi:
- Megakadályozza a
/procés/sysérzékeny részeinek írását - Tiltja a mount rendszerhívást
- Korlátozza a signal küldést más folyamatoknak
- Megakadályozza a ptrace használatát
# Aktuális AppArmor állapot ellenőrzése
sudo aa-status
# Docker konténer AppArmor profiljának ellenőrzése
docker inspect --format='{{.AppArmorProfile}}' my_container
# Kimenet: docker-default
Egyedi AppArmor profil nginx konténerhez
Hozzunk létre egy szorított AppArmor profilt kifejezetten nginx konténerekhez. Ez a profil sokkal szigorúbb, mint az alapértelmezett, és pontosan az nginx igényeire van szabva:
# /etc/apparmor.d/docker-nginx
#include <tunables/global>
profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
#include <abstractions/nameservice>
# Hálózati hozzáférés
network inet tcp,
network inet udp,
network inet6 tcp,
network inet6 udp,
# Nginx binárisok
/usr/sbin/nginx ix,
/usr/lib/nginx/modules/*.so mr,
# Konfigurációs fájlok (csak olvasás)
/etc/nginx/** r,
# Naplófájlok
/var/log/nginx/** rw,
# PID fájl
/var/run/nginx.pid rw,
# Webszerver tartalom (csak olvasás)
/usr/share/nginx/html/** r,
# Ideiglenes fájlok
/var/cache/nginx/** rw,
/tmp/** rw,
# Proc fájlrendszer (korlátozott)
/proc/*/status r,
/proc/sys/net/** r,
# Mindent más tiltunk
deny /proc/sysrq-trigger rw,
deny /proc/sys/kernel/** w,
deny /proc/kcore r,
deny /sys/** w,
deny /etc/shadow r,
deny /etc/passwd w,
# Capability-k
capability net_bind_service,
capability setuid,
capability setgid,
capability dac_override,
# Signal kezelés
signal (send,receive) peer=docker-nginx,
}
A profil betöltése és alkalmazása:
# Profil betöltése
sudo apparmor_parser -r /etc/apparmor.d/docker-nginx
# Konténer indítása az egyedi profillal
docker run -d \
--security-opt apparmor=docker-nginx \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--read-only \
--tmpfs /var/cache/nginx:rw,noexec \
--tmpfs /var/run:rw,noexec \
--tmpfs /tmp:rw,noexec \
--name nginx_hardened \
nginx:alpine
# Ellenőrzés: próbáljunk meg tiltott műveletet
docker exec nginx_hardened cat /etc/shadow
# Kimenet: cat: can't open '/etc/shadow': Permission denied
Bane – Automatikus AppArmor profil generálás
Ha nem akarsz kézzel írni AppArmor profilokat (és tegyük hozzá, ki akar?), a Bane egy nyílt forráskódú eszköz, amely leegyszerűsíti az egész folyamatot. TOML formátumú konfigurációból generál AppArmor profilt:
# bane-nginx.toml
Name = "docker-nginx-bane"
[Filesystem]
# Engedélyezett olvasási útvonalak
AllowExec = [
"/usr/sbin/nginx"
]
ReadOnlyPaths = [
"/etc/nginx/",
"/usr/share/nginx/html/"
]
WritablePaths = [
"/var/log/nginx/",
"/var/cache/nginx/",
"/var/run/"
]
DenyPaths = [
"/etc/shadow",
"/proc/kcore"
]
[Network]
Raw = false
Packet = false
Protocols = [
"tcp",
"udp"
]
[Capabilities]
Allow = [
"net_bind_service"
]
Deny = [
"sys_admin",
"sys_ptrace",
"sys_rawio"
]
# Bane telepítése és profil generálása
go install github.com/genuinetools/bane@latest
sudo bane bane-nginx.toml
# A generált profil automatikusan betöltődik
docker run -d \
--security-opt apparmor=docker-nginx-bane \
--name nginx_bane \
nginx:alpine
Kubernetes Pod Security Standards
A Kubernetes 1.25-től kezdve a Pod Security Admission (PSA) váltotta fel az előzőleg használt (és már deprecated) PodSecurityPolicy-t. Ha még mindig PSP-t használsz, itt az ideje váltani. A PSA három előre definiált biztonsági szintet kínál, amelyeket névterekre (namespace) alkalmazhatunk.
Három biztonsági szint
- Privileged – Teljesen korlátozásmentes. Csak olyan namespace-ekre használjuk, ahol tényleg szükség van rá (pl. rendszerszintű konténerek, monitoring ágensek).
- Baseline – Minimális korlátozások, amelyek megakadályozzák az ismert jogosultság-eszkalációs technikákat. A legtöbb alkalmazás működik vele módosítás nélkül.
- Restricted – Erősen korlátozó, a jelenlegi Pod hardening legjobb gyakorlatait érvényesíti. Nem root futtatás, minden capability elvétele, seccomp profil kötelező. Ezt ajánlom produkciós környezetbe.
Minden szinthez három módot rendelhetünk:
- enforce – A szabályt sértő Pod-ok létrehozását elutasítja
- audit – A sértéseket naplózza, de engedi a Pod-ot
- warn – Figyelmeztetést küld a felhasználónak, de engedi a Pod-ot
# Namespace létrehozása Restricted biztonsági szinttel
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: latest
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: latest
# Alkalmazás kubectl-lel
kubectl apply -f namespace-restricted.yaml
# Tesztelés: privilegizált Pod létrehozási kísérlet
kubectl run test-priv --image=nginx --privileged -n production
# Kimenet: Error from server (Forbidden): pods "test-priv" is forbidden:
# violates PodSecurity "restricted:latest"
Security Context konfigurálása
A restricted szintnek megfelelő Pod definíció így néz ki. Érdemes ezt sablonként kezelni az új alkalmazásoknál:
apiVersion: v1
kind: Pod
metadata:
name: secure-app
namespace: production
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: my-registry.io/app:v2.1.0@sha256:abc123...
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
limits:
memory: "256Mi"
cpu: "500m"
requests:
memory: "128Mi"
cpu: "250m"
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache
volumes:
- name: tmp
emptyDir:
sizeLimit: "64Mi"
- name: cache
emptyDir:
sizeLimit: "128Mi"
Nézzük a legfontosabb elemeket:
- runAsNonRoot: true – Biztosítja, hogy a konténer ne root-ként fusson
- readOnlyRootFilesystem: true – Csak olvasható root fájlrendszer
- allowPrivilegeEscalation: false – Tiltja a jogosultság-növelést
- capabilities.drop: ALL – Minden Linux capability elvétele
- seccompProfile: RuntimeDefault – Alkalmazza a futási környezet alapértelmezett seccomp profilját
- automountServiceAccountToken: false – Nem csatolja automatikusan a ServiceAccount tokent, csökkentve a támadási felületet
- Image digest (@sha256:...) – Az image tag helyett digest-et használunk a supply chain támadások megelőzésére
Fokozatos bevezetés stratégiája
Ne próbáld meg egy csapásra minden namespace-re ráerőltetni a restricted módot – az katasztrófa receptje. Inkább fokozatosan vezesd be:
# 1. lépés: Audit és warn módban indulunk
kubectl label namespace staging \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/warn=restricted
# 2. lépés: Ellenőrizzük a meglévő workload-okat
kubectl get pods -n staging -o json | \
kubectl auth can-i --list --namespace=staging
# 3. lépés: Javítjuk a nem megfelelő Pod-okat
# 4. lépés: Enforce módra váltás
kubectl label namespace staging \
pod-security.kubernetes.io/enforce=restricted \
--overwrite
Konténerképek biztonsági vizsgálata
A konténerbiztonság nem ér véget a futási környezet keményítésével. Magát a konténerképet is alaposan meg kell vizsgálni – különben az egész eddig felépített védelmi rendszer egy lyukas csónakba épített széf lesz. Egy sebezhetó alap-image vagy egy ismert CVE-t tartalmazó függőség pont olyan veszélyes, mint egy félrekonfigurált futási környezet.
Trivy – Átfogó sebezhetőség-vizsgáló
A Trivy (az Aqua Security nyílt forráskódú projektje) az egyik legnépszerűbb konténerkép-vizsgáló eszköz. Sebezhetőségeket, félrekonfigurációkat és titkos kulcsokat keres képekben, fájlrendszerekben és IaC konfigurációkban.
# Trivy telepítése
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
# Konténerkép vizsgálata
trivy image nginx:alpine
# Csak CRITICAL és HIGH sebezhetőségek
trivy image --severity CRITICAL,HIGH nginx:alpine
# Vizsgálat és kilépés hibakóddal, ha van CRITICAL sebezhetőség
trivy image --exit-code 1 --severity CRITICAL nginx:alpine
# JSON kimenet további feldolgozáshoz
trivy image --format json --output results.json nginx:alpine
# SBOM generálása CycloneDX formátumban
trivy image --format cyclonedx --output sbom.json nginx:alpine
Grype – Gyors és pontos sebezhetőség-vizsgálat
A Grype (az Anchore nyílt forráskódú projektje) egy másik kiváló választás, különösen ha a Syft által generált SBOM-okat szeretnénk vizsgálni. Tapasztalatom szerint a Grype néha más sebezhetőségeket talál, mint a Trivy – ezért érdemes mindkettőt futtatni.
# Grype telepítése
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
# Konténerkép vizsgálata
grype nginx:alpine
# SBOM generálása Syft-tel és vizsgálata Grype-pal
syft nginx:alpine -o spdx-json > nginx-sbom.spdx.json
grype sbom:nginx-sbom.spdx.json
# Csak fixelhető sebezhetőségek mutatása
grype nginx:alpine --only-fixed
SBOM generálás Syft-tel
Az SBOM (Software Bill of Materials) a konténerkép összes összetevőjének listája – tulajdonképpen egy „alkatrészjegyzék". A Syft az Anchore eszköze, amely részletes SBOM-ot generál:
# Syft telepítése
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# SBOM generálása különböző formátumokban
syft nginx:alpine -o syft-json > nginx-sbom.json
syft nginx:alpine -o cyclonedx-json > nginx-cyclonedx.json
syft nginx:alpine -o spdx-json > nginx-spdx.json
CI/CD integráció
Az igazi erő a képvizsgálat automatizálásában rejlik. Íme egy GitHub Actions workflow példa, amely a képépítés során automatikusan vizsgálja a konténerképet:
# .github/workflows/container-security.yml
name: Container Security Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t my-app:${{ github.sha }} .
- name: Run Trivy vulnerability scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'my-app:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1'
- name: Upload scan results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: 'my-app:${{ github.sha }}'
format: 'cyclonedx-json'
output-file: 'sbom.cyclonedx.json'
- name: Scan SBOM with Grype
uses: anchore/scan-action@v4
with:
sbom: 'sbom.cyclonedx.json'
fail-build: true
severity-cutoff: high
Minimális támadási felület multi-stage build-ekkel
A konténerkép méretének csökkentése közvetlenül csökkenti a támadási felületet is. Kevesebb bináris = kevesebb potenciális sebezhetőség. A multi-stage build-ek csak a futáshoz ténylegesen szükséges fájlokat tartalmazó végső képet hoznak létre:
# Multi-stage Dockerfile - Go alkalmazás példa
# --- Build fázis ---
FROM golang:1.23-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app ./cmd/server
# --- Futtatási fázis ---
FROM scratch
# Nem-root felhasználó beállítása
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app /app
# Nem-root felhasználóként futtatás
USER 10001:10001
EXPOSE 8080
ENTRYPOINT ["/app"]
A scratch alap-image használata a minimális támadási felület – nincs benne shell, nincs csomagkezelő, nincsenek felesleges binárisok. Ha kell egy kicsivel több (pl. debug célokra), alternatívaként használhatjuk a distroless képeket (pl. gcr.io/distroless/static-debian12), amelyek minimális futási környezetet biztosítanak.
Futásidejű védekezés Falco-val
Eddig a megelőzésről beszéltünk – de mi történik, ha a megelőzés nem elég? Mert sajnos, bármennyire is keményítjük a rendszert, 100%-os biztonság nem létezik. Itt jön képbe a Falco, a CNCF (Cloud Native Computing Foundation) graduált projektje, amely valós idejű futásidejű biztonsági monitorozást biztosít eBPF technológiával.
Hogyan működik a Falco?
A Falco az eBPF (extended Berkeley Packet Filter) technológiát használja a rendszerhívások kernel szintű megfigyelésére. Az eBPF programok közvetlenül a kernelben futnak, így minimális teljesítményveszteséggel (jellemzően 5% CPU alá) figyel meg másodpercenként akár 10 000+ eseményt. A hagyományos kernel modul helyett az eBPF biztonságosabb, mert a kernel verifier ellenőrzi a kódot betöltés előtt.
Falco telepítése Kubernetes-be
# Falco telepítése Helm chart-tal
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update
# Telepítés eBPF driver-rel
helm install falco falcosecurity/falco \
--namespace falco \
--create-namespace \
--set driver.kind=modern_ebpf \
--set falcosidekick.enabled=true \
--set falcosidekick.config.slack.webhookurl="https://hooks.slack.com/services/XXX" \
--set tty=true
# Telepítés ellenőrzése
kubectl get pods -n falco
kubectl logs -n falco -l app.kubernetes.io/name=falco
Egyedi Falco szabályok írása
A Falco igazi ereje az egyedi szabályokban rejlik. YAML formátumban íródnak és feltételeken (conditions) alapulnak. Íme néhány gyakorlati példa, amelyeket érdemes beépíteni:
# /etc/falco/rules.d/custom-rules.yaml
# 1. szabály: Shell indítás konténerben
- rule: Shell Spawned in Container
desc: Detektálja, ha shell indul egy konténerben
condition: >
spawned_process and
container and
proc.name in (bash, sh, ash, zsh, ksh, dash) and
not container.image.repository in (allowed_shell_images)
output: >
Shell indítva konténerben
(felhasználó=%user.name konténer=%container.name
kép=%container.image.repository parancs=%proc.cmdline
pid=%proc.pid)
priority: WARNING
tags: [container, shell, mitre_execution]
# 2. szabály: Érzékeny fájl olvasása konténerben
- rule: Sensitive File Read in Container
desc: Érzékeny fájlok olvasásának detektálása
condition: >
open_read and
container and
fd.name in (/etc/shadow, /etc/sudoers, /root/.ssh/authorized_keys,
/root/.bash_history, /etc/kubernetes/admin.conf) and
not proc.name in (sshd, sudo)
output: >
Érzékeny fájl olvasása konténerben
(fájl=%fd.name felhasználó=%user.name konténer=%container.name
kép=%container.image.repository)
priority: CRITICAL
tags: [container, filesystem, mitre_credential_access]
# 3. szabály: Kriptobányász tevékenység detektálása
- rule: Crypto Mining Activity Detected
desc: Ismert kriptobányász pool-okhoz való csatlakozás
condition: >
container and
((evt.type = connect and
fd.sip.name in (pool.minergate.com, xmr.pool.minergate.com,
stratum.antpool.com, xmr-eu1.nanopool.org)) or
(spawned_process and
proc.name in (xmrig, minerd, minergate-cli, cpuminer)))
output: >
Kriptobányász tevékenység detektálva
(konténer=%container.name kép=%container.image.repository
folyamat=%proc.cmdline kapcsolat=%fd.name)
priority: CRITICAL
tags: [container, cryptomining, mitre_resource_hijacking]
# 4. szabály: Kubernetes API titkos hozzáférés
- rule: K8s ServiceAccount Token Access
desc: Konténerből a ServiceAccount token olvasásának detektálása
condition: >
open_read and
container and
fd.name startswith /var/run/secrets/kubernetes.io/
output: >
Kubernetes ServiceAccount token hozzáférés
(fájl=%fd.name konténer=%container.name felhasználó=%user.name
parancs=%proc.cmdline)
priority: WARNING
tags: [container, kubernetes, mitre_credential_access]
# Segéd lista az engedélyezett képekhez
- list: allowed_shell_images
items: [debug-container, maintenance-tools]
Integráció riasztási rendszerekkel
A Falcosidekick az események továbbításáért felel. Támogatja többek között a Slack-et, a PagerDuty-t, az Elasticsearch-öt és a Prometheus-t. Íme egy konfiguráció több kimenettel:
# Falcosidekick konfigurálása több kimenettel
# values.yaml a Helm telepítéshez
falcosidekick:
enabled: true
config:
slack:
webhookurl: "https://hooks.slack.com/services/T00/B00/XXX"
minimumpriority: "warning"
messageformat: "Falco Alert: {{ .Rule }} - {{ .Output }}"
elasticsearch:
hostport: "https://elasticsearch.monitoring:9200"
index: "falco-alerts"
minimumpriority: "notice"
prometheus:
extralabels: "cluster:production,env:prod"
alertmanager:
hostport: "http://alertmanager.monitoring:9093"
minimumpriority: "critical"
Hálózati biztonság konténerizált környezetben
A hálózati réteg az egyik leggyakrabban elhanyagolt biztonsági terület konténerizált környezetben – és őszintén szólva, ez érthetetlen számomra. Alapértelmezetten a legtöbb konténer-orchesztrátor (beleértve a Kubernetes-t is) minden Pod számára engedélyezi a kommunikációt minden más Pod-dal. Ez támadás esetén katasztrofális: egyetlen kompromittált konténer szabad mozgást kap a teljes klaszterben (lateral movement).
Docker hálózati szegmentáció
# Izolált hálózatok létrehozása
docker network create --driver bridge frontend_net
docker network create --driver bridge backend_net
docker network create --driver bridge db_net --internal
# Konténerek indítása elkülönített hálózatokon
docker run -d --name nginx --network frontend_net nginx:alpine
docker run -d --name api --network backend_net my-api:latest
docker run -d --name postgres --network db_net postgres:16-alpine
# Az API konténer mindkét hálózathoz csatlakozik (a frontend és backend között közvetít)
docker network connect frontend_net api
# A --internal flag biztosítja, hogy a db_net-ről nincs kimenő forgalom
# Az adatbázis csak a backend_net-en érhető el:
docker network connect db_net api
Kubernetes NetworkPolicy
A Kubernetes NetworkPolicy erőforrás lehetővé teszi a Pod-ok közötti forgalom szabályozását. Egy fontos megjegyzés: a NetworkPolicy működéséhez a CNI plugin-nak (pl. Calico, Cilium, Weave Net) támogatnia kell azt. Ha alapértelmezett CNI-t használsz, lehet, hogy a NetworkPolicy-k egyszerűen nem lépnek életbe.
# 1. Alapértelmezett tiltás (deny-all) az egész namespace-re
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
# 2. Specifikus forgalom engedélyezése a webalkalmazásnak
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web-traffic
namespace: production
spec:
podSelector:
matchLabels:
app: web-frontend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: api-backend
ports:
- protocol: TCP
port: 3000
- to: # DNS engedélyezése
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# 3. Adatbázis elérés korlátozása
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-access-policy
namespace: production
spec:
podSelector:
matchLabels:
app: postgresql
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: api-backend
ports:
- protocol: TCP
port: 5432
Service Mesh – Istio mTLS
A Service Mesh (különösen az Istio) egy magasabb szintű hálózati biztonsági réteget ad hozzá a mTLS (mutual TLS) megvalósításával. Ez biztosítja, hogy a szolgáltatások közötti kommunikáció minden esetben titkosított és hitelesített legyen – mindezt az alkalmazás kódjának módosítása nélkül.
# Istio mTLS szigorú módba állítása az egész mesh-re
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT
# Finom hangolás: Authorization Policy
# Csak a frontend szolgáltatás érheti el az API-t
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: api-access-policy
namespace: production
spec:
selector:
matchLabels:
app: api-backend
action: ALLOW
rules:
- from:
- source:
principals:
- "cluster.local/ns/production/sa/web-frontend"
to:
- operation:
methods: ["GET", "POST"]
paths: ["/api/v1/*"]
Az Istio használatával a mTLS automatikusan megoldott. Nem kell az alkalmazás kódjában TLS-t implementálni – a sidecar proxy (Envoy) átlátszóan kezeli a titkosítást és a tanúsítványkezelést. Ez hatalmas könnyebbség a fejlesztőknek.
Gyakorlati ellenőrzőlista és összefoglalás
Végül foglaljuk össze az összes tárgyalt védelmi réteget egy áttekinthető ellenőrzőlistában. Személyes tapasztalatból mondom: nyomtasd ki és tedd a falra. Használjuk ezt referenciaként minden új konténerizált alkalmazás üzembeállításakor:
Docker / Konténer-runtime szint
- ☐ Rootless Docker mód – A Docker daemon fut nem-root felhasználóként, vagy legalább user namespace remapping be van kapcsolva
- ☐ Capabilities csökkentése –
--cap-drop ALLés csak a szükséges capability-k visszaadása - ☐ Csak olvasható fájlrendszer –
--read-onlyflag használatatmpfsmount-okkal az írható könyvtárakhoz - ☐ No-new-privileges –
--security-opt no-new-privileges:truebeállítás - ☐ Seccomp profil – Legalább az alapértelmezett profil aktív, ideálisan egyedi szorított profillal
- ☐ AppArmor profil – Egyedi AppArmor profil az alkalmazáshoz igazítva
- ☐ Erőforrás-korlátok – Memória, CPU és PID limitek beállítva (cgroups v2)
- ☐ Inter-container communication tiltva –
"icc": falsea daemon konfigurációban - ☐ Docker socket védelme – Soha ne csatoljuk a Docker socketet konténerekbe, kivéve ha feltétlenül szükséges
Konténerkép szint
- ☐ Minimális alap-image –
scratch,distrolessvagyalpinehasználata - ☐ Multi-stage build – Csak a futáshoz szükséges fájlok a végső képben
- ☐ Nem-root felhasználó –
USERutasítás a Dockerfile-ban nem-root felhasználóval - ☐ Image digest – Tag helyett
@sha256:...digest használata éles környezetben - ☐ Sebezhetőség-vizsgálat – Trivy vagy Grype integrálva a CI/CD pipeline-ba
- ☐ SBOM generálás – Syft-tel generált SBOM minden kiadásnál
- ☐ Megbízható registry – Privát registry használata image aláírással (Cosign/Notary)
Kubernetes szint
- ☐ Pod Security Admission –
restrictedszint enforce módban a termelési namespace-eken - ☐ Security Context –
runAsNonRoot,readOnlyRootFilesystem,allowPrivilegeEscalation: false - ☐ ServiceAccount token –
automountServiceAccountToken: falseahol nem szükséges - ☐ NetworkPolicy – Default deny-all szabály, majd explicit engedélyezések
- ☐ RBAC – Minimális jogosultságú ServiceAccount-ok és Role-ok
- ☐ Secrets kezelés – External Secrets Operator vagy Vault használata a natív Secrets helyett
Monitoring és reagálás
- ☐ Falco – eBPF alapú futásidejű monitorozás telepítve és konfigurálva
- ☐ Egyedi szabályok – Az alkalmazás-specifikus viselkedési szabályok megírva
- ☐ Riasztási integráció – Falcosidekick összekötve a riasztási rendszerrel (Slack, PagerDuty)
- ☐ Audit naplózás – Kubernetes audit logging bekapcsolva és centrálisan gyűjtve
- ☐ Rendszeres ellenőrzés – Docker Bench for Security és kube-bench rendszeres futtatása
Hálózati biztonság
- ☐ Hálózati szegmentáció – Külön hálózatok a frontend, backend és adatbázis rétegnek
- ☐ mTLS – Service mesh (Istio/Linkerd) a szolgáltatások közötti titkosított kommunikációhoz
- ☐ Egress korlátozás – Kimenő forgalom explicit engedélyezése, alapértelmezett tiltás
A konténerbiztonság nem egyszeri feladat, hanem folyamatos folyamat. A fenyegetések fejlődnek – ahogy a 2025-ös runc és Docker Desktop sebezhetőségek is mutatják –, és a védelmi stratégiáinknak is lépést kell tartaniuk velük. A fent bemutatott rétegzett védelem (defense in depth) megközelítés biztosítja, hogy egyetlen meghiúsult védelmi réteg ne jelentsen azonnali kompromittálódást.
Tartsuk naprakészen a konténer-runtime-okat (a runc sebezhetőségeket az 1.2.8, 1.3.3 és 1.4.0-rc.3 verziók javították), a Docker Desktopot (minimum 4.44.3), és alkalmazzuk a fenti ellenőrzőlista minden pontját. A biztonság nem opció – alapkövetelmény. És ha ez a cikk segített eligazodni a konténerbiztonság világában, máris közelebb vagy a biztonságosabb infrastruktúrához.
# Végső ellenőrzés: Docker Bench for Security futtatása
docker run --rm --net host --pid host \
--userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /var/lib:/var/lib:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /usr/lib/systemd:/usr/lib/systemd:ro \
-v /etc:/etc:ro \
docker/docker-bench-security
# Kubernetes: kube-bench futtatása CIS Benchmark ellenőrzéshez
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
kubectl logs job/kube-bench