Docker i Kubernetes: poradnik bezpieczeństwa kontenerów
Kontenery zmieniły sposób wdrażania oprogramowania — ale wiele zespołów uruchamia je z uprawnieniami roota, przestarzałymi obrazami i ujawnionymi sekretami. Oto pełny poradnik wzmacniania zabezpieczeń.
Spis treści
- Błędne przekonanie o bezpieczeństwie kontenerów
- Wzmacnianie obrazów Docker
- Używaj minimalnych obrazów bazowych
- Budowanie wieloetapowe
- Nigdy nie uruchamiaj jako root
- Skanuj obrazy pod kątem podatności
- Bezpieczeństwo środowiska uruchomieniowego Docker
- Odbieraj uprawnienia (capabilities)
- Seccomp i AppArmor
- Nigdy nie używaj --privileged
- Wzmacnianie bezpieczeństwa Kubernetes
- Pod Security Standards
- RBAC: najmniejsze uprawnienia dla kont usług
- Zarządzanie sekretami
- Polityki sieciowe
- Ciągła kontrola stanu bezpieczeństwa
Błędne przekonanie o bezpieczeństwie kontenerów
Częstym błędnym przekonaniem jest to, że kontenery zapewniają silną izolację podobną do maszyn wirtualnych. Tak nie jest. Kontenery współdzielą jądro hosta — luka umożliwiająca ucieczkę z kontenera i wykonanie kodu w jądrze hosta daje dostęp do każdego kontenera na tym hoście oraz do samego hosta. Bezpieczeństwo kontenerów polega na ograniczaniu zasięgu szkód po przejęciu kontenera, a nie na założeniu, że granica jest nie do przebicia.
Powierzchnia ataku obejmuje obraz kontenera, konfigurację uruchomieniową, platformę orkiestracji, sieć i warstwę zarządzania sekretami. Wzmacnianie zabezpieczeń musi objąć je wszystkie.
Wzmacnianie obrazów Docker
Używaj minimalnych obrazów bazowych
Każdy pakiet w obrazie bazowym to potencjalna podatność. Wybieraj obrazy distroless (obrazy Google bez powłoki, menedżera pakietów i narzędzi systemowych) lub Alpine Linux (musl libc, apk, ok. 5 MB):
# Instead of ubuntu:22.04
FROM gcr.io/distroless/java21-debian12
Do obrazów distroless nie da się wejść interaktywną powłoką — to znacznie ogranicza możliwości atakującego, jeśli kontener zostanie przejęty.
Budowanie wieloetapowe
Zależności do budowania (kompilatory, narzędzia buildowe, frameworki testowe) nigdy nie powinny trafiać do obrazu produkcyjnego:
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN go build -o server .
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]
Obraz końcowy zawiera tylko skompilowany plik binarny i jego zależności uruchomieniowe.
Nigdy nie uruchamiaj jako root
Domyślnie procesy w kontenerach działają jako root (UID 0). Jeśli dojdzie do ucieczki z kontenera, atakujący ma uprawnienia roota na hoście. Zawsze twórz użytkownika bez uprawnień roota:
RUN useradd -u 1001 -r appuser
USER appuser
Połącz to z systemem plików tylko do odczytu (--read-only) i --tmpfs /tmp dla miejsc wymagających zapisu.
Skanuj obrazy pod kątem podatności
Włącz skanowanie obrazów do potoku CI/CD:
# Trivy — fast, comprehensive vulnerability scanner
trivy image myapp:latest
# Grype — alternative with SBOM support
grype myapp:latest
Trivy to szybki, wszechstronny skaner podatności, a Grype — alternatywa z obsługą SBOM. Przerywaj build, jeśli wykryto podatności o wysokim lub krytycznym poziomie. Przebudowuj i aktualizuj obrazy bazowe regularnie — nie tylko przy zmianie kodu aplikacji.
Bezpieczeństwo środowiska uruchomieniowego Docker
Odbieraj uprawnienia (capabilities)
Mechanizm capabilities w Linuksie dzieli uprawnienia roota typu „wszystko albo nic” na osobne jednostki. Odbierz wszystkie i przywróć tylko te, których aplikacja potrzebuje:
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp
Seccomp i AppArmor
Domyślny profil seccomp w Dockerze blokuje ok. 44 niebezpieczne wywołania systemowe. Stosuj go jawnie i rozważ własny profil dla wrażliwych obciążeń. Profile AppArmor lub SELinux dodają obowiązkową kontrolę dostępu ponad jądrem:
docker run --security-opt seccomp=profile.json myapp
docker run --security-opt apparmor=docker-default myapp
Nigdy nie używaj --privileged
Flaga --privileged daje kontenerowi niemal pełny dostęp do hosta, wyłączając całą izolację bezpieczeństwa. Bywa wymagana w specyficznych przypadkach (np. Docker-in-Docker), ale na produkcji należy jej całkowicie unikać, a podczas przeglądów bezpieczeństwa traktować jako natychmiastowy sygnał alarmowy.
Wzmacnianie bezpieczeństwa Kubernetes
Pod Security Standards
Kubernetes egzekwuje zasady bezpieczeństwa przez Pod Security Standards (zastępujące wycofane PodSecurityPolicy):
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
Profil restricted wymusza: brak kontenerów uprzywilejowanych, brak użytkownika root, brak eskalacji uprawnień, wymagany profil seccomp i odebranie wszystkich capabilities.
RBAC: najmniejsze uprawnienia dla kont usług
Każdy pod działa z kontem usługi (service account). Domyślnie to konto ma dostęp do API Kubernetes. Stosuj zasadę najmniejszych uprawnień:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-role
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get"]
Audytuj poleceniem kubectl auth can-i --list --as=system:serviceaccount:default:myapp. Wyłącz automatyczne montowanie tokenów kont usług, gdy nie są potrzebne: automountServiceAccountToken: false.
Zarządzanie sekretami
Sekrety Kubernetes są domyślnie tylko zakodowane w Base64, a nie zaszyfrowane. Stosuj:
- Szyfrowanie w spoczynku: włącz
EncryptionConfigurationw serwerze API, aby szyfrować sekrety w etcd - Zewnętrzne menedżery sekretów: AWS Secrets Manager, HashiCorp Vault lub GCP Secret Manager przez External Secrets Operator
- Nigdy nie umieszczaj sekretów w ConfigMapach, zmiennych środowiskowych w specyfikacjach Deploymentów (widocznych przez
kubectl describe) ani w obrazach kontenerów
Polityki sieciowe
Domyślnie wszystkie pody w klastrze Kubernetes mogą się ze sobą komunikować. Wdróż NetworkPolicy, aby wymusić mikrosegmentację:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
spec:
podSelector: {}
policyTypes: [Ingress]
Następnie dodaj jawne reguły zezwalające tylko na niezbędne ścieżki komunikacji. Używaj wtyczki CNI, która egzekwuje NetworkPolicy (Calico, Cilium, Weave).
Ciągła kontrola stanu bezpieczeństwa
- Używaj Falco (narzędzia CNCF do bezpieczeństwa w czasie działania), aby wykrywać podejrzane zachowania: nieoczekiwane uruchomienia powłoki, połączenia wychodzące do nowych adresów IP, próby eskalacji uprawnień
- Skanuj konfigurację klastra za pomocą kube-bench (CIS Kubernetes Benchmark) i operatora Trivy
- Włącz logowanie audytowe w serwerze API Kubernetes i przesyłaj logi do SIEM
- Regularnie odświeżaj skróty (digesty) obrazów i automatyzuj aktualizacje zależności za pomocą Renovate lub Dependabot