„Kontejnery jsou izolované, takže jsou bezpečné.” Tohle jsme si mysleli, než nám penetrační tester během půl hodiny eskaloval z kontejneru na root hosta. Kontejnery nejsou VM a bezpečnostní model je zásadně odlišný.
Kontejner není sandbox¶
Docker kontejner sdílí jádro s hostem. Namespace a cgroups poskytují izolaci procesů, síťového stacku a souborového systému — ale není to hardwarová virtualizace. Kernel exploit v kontejneru = kernel exploit na hostu. Tohle je fundamentální rozdíl oproti VM a musí se s ním počítat.
To neznamená, že kontejnery jsou nebezpečné. Znamená to, že bezpečnost musíte řešit na všech vrstvách — od base image přes build pipeline až po runtime konfiguraci.
Base image — minimalizujte útočnou plochu¶
Každý balíček v image je potenciální zranitelnost. Ubuntu base image má stovky balíčků, které vaše aplikace nepotřebuje — a každý z nich může mít CVE. Pravidlo číslo jedna: použijte minimální base image.
# ❌ Špatně — plný Ubuntu, 188 MB, stovky balíčků
FROM ubuntu:16.04
# ✅ Lépe — Alpine, 5 MB, minimální balíčky
FROM alpine:3.6
# ✅ Nejlépe — distroless, jen runtime
FROM gcr.io/distroless/java:latest
Alpine Linux je dobrý kompromis — 5 MB, musl libc, apk package manager.
Pro maximální bezpečnost zvažte distroless images od Googlu:
žádný shell, žádný package manager, žádné utility. Útočník, který se
dostane do kontejneru, nemá k dispozici ani ls.
Multi-stage builds — oddělte build a runtime¶
Build nástroje (gcc, npm, maven) nemají co dělat v produkčním image. Multi-stage build oddělí kompilaci od finálního image.
FROM golang:1.9 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o server .
FROM alpine:3.6
RUN adduser -D -u 1001 appuser
COPY --from=builder /app/server /server
USER appuser
EXPOSE 8080
CMD ["/server"]
Výsledný image obsahuje jen binárku a Alpine. Žádný Go toolchain, žádné zdrojové kódy, žádné build závislosti. Menší image = menší útočná plocha = rychlejší deploy.
Neběžte jako root¶
Překvapivě mnoho Docker images běží jako root. Pokud aplikace nepotřebuje privilegovaný port nebo přístup k systémovým resources, vždy přidejte USER directive. Root v kontejneru může za určitých okolností eskalovat na root hosta.
V Kubernetes použijte securityContext na úrovni podu:
securityContext:
runAsNonRoot: true
runAsUser: 1001
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem zabrání zápisu do souborového systému
kontejneru — útočník nemůže persistovat malware. drop ALL capabilities
odebere Linux capabilities, které aplikace nepotřebuje.
Image scanning — najděte CVE dřív než útočník¶
Každý image by měl projít vulnerability scannerem před nasazením do produkce. Nástroje jako Clair, Trivy nebo Anchore analyzují vrstvy image a porovnávají balíčky s CVE databázemi.
My skenujeme na dvou místech: v CI pipeline (build time) a v registry (continuous scan). Build time scan zastaví deploy s kritickými CVE. Continuous scan odhalí nově objevené zranitelnosti v images, které už běží v produkci.
Klíčové je nastavit policy: co je akceptovatelné a co ne. Nulová tolerance na kritické CVE v base image. Medium severity s plánem opravy do 30 dní. Low severity sledovat, ale neblokovat deploy.
Supply chain — důvěřuj, ale prověřuj¶
docker pull node:latest — víte, co přesně stahujete?
Kdo ten image vytvořil? Byl modifikován? Docker Content Trust
(Notary) umožňuje podepisování images. Povolte ho a akceptujte jen
podepsané images.
Používejte konkrétní tagy, ne :latest.
Ještě lépe: pinujte na digest. Tag může být přepsán,
digest je immutable hash. A provozujte vlastní registry (Harbor, GitLab
Registry) místo přímého stahování z Docker Hub.
Runtime ochrana¶
Build-time bezpečnost nestačí. V runtime potřebujete detekci anomálií.
Seccomp profily omezují systémová volání — kontejner,
který normálně dělá HTTP requesty, nemá důvod volat ptrace
nebo mount. AppArmor nebo SELinux
profily přidávají další vrstvu mandatory access control.
Network policies v Kubernetes fungují jako firewall mezi pody. Default deny — žádný pod nekomunikuje s jiným, pokud to explicitně nepovolíte. Mikroslužba pro platby nemá důvod komunikovat s CMS.
Secrets management¶
Nikdy nepečte secrets do image. Ani jako environment variable v Dockerfile, ani jako soubor v build kontextu. Secrets patří do Kubernetes Secrets (ideálně s externím backendem jako HashiCorp Vault), mountované jako volumes, rotované automaticky.
Skenujte historii image vrstev — docker history ukáže
všechny vrstvy včetně smazaných souborů. Secret přidaný a pak
smazaný v dalším RUN příkazu je stále v předchozí vrstvě.
Security jako continuous process¶
Container security není jednorázový audit. Je to kontinuální proces integrovaný do celého životního cyklu — od Dockerfile přes CI pipeline až po runtime monitoring. Začněte základy: non-root, minimální image, scanning v CI. Postupně přidávejte seccomp, network policies, runtime detekci. Každá vrstva ztěžuje útočníkovi práci.