Met Kubernetes deploy je snel workloads, maar je attack surface groeit ook: meer services, meer afhankelijkheden en meer plekken waar verkeer en data langs komen. In deze handleiding lees je hoe je jouw eigen workloads beveiligt, van container tot gateway tot opslag.
De control plane valt hierbij buiten scope, omdat die door TransIP wordt beheerd. Je richt je dus op jouw namespaces, RBAC, pods, images, netwerktoegang en volumes.
- Werk volgens het least-privilege-principe: geef alleen toegang die een workload echt nodig heeft.
- Publiceer verkeer via een centrale gateway met TLS, niet rechtstreeks vanuit elke service.
- Gebruik alleen vertrouwde images en update ze actief wanneer er CVE's of dependency-updates beschikbaar zijn.
- Behandel storage, Secrets en netwerkregels als onderdeel van je beveiliging, niet als losse nazorg.
Scheid workloads logisch van elkaar
Beveiliging begint met scheiding. Een test-app, productie-app en CI-workload horen niet zomaar dezelfde rechten, hetzelfde netwerkbereik en dezelfde storage te delen.
- Namespace: geef elke applicatie, omgeving of tenant een eigen namespace zodat policies, quota en toegang voorspelbaar blijven.
- Label en selector: gebruik een consistente label- en selector-strategie om policies, deployments en services alleen op de juiste workloads te laten landen.
- Taints en tolerations: reserveer gevoelige nodes voor specifieke workloads met taints en een gerichte toleration.
- RBAC: gebruik Roles en ClusterRoles alleen wanneer nodig, en koppel die via RoleBindings of ClusterRoleBindings aan aparte ServiceAccounts. Vermijd `cluster-admin` voor applicaties.
- ServiceAccounts: gebruik per applicatie een eigen ServiceAccount. Zo blijft audit logging duidelijk en kun je rechten per workload beperken.
Door deze grenzen vroeg in te richten voorkom je dat een kwetsbare workload automatisch toegang krijgt tot andere workloads, configuratie of storage.
Harden pods en containers
Een workload die als root draait, een schrijfbaar root-filesystem heeft en alle Linux-capabilities meekrijgt, is onnodig riskant. Maak hardening onderdeel van je standaard manifesten.
- Container image: gebruik een kleine, onderhouden container image, pin versies en haal images alleen uit vertrouwde registries of je eigen registry.
- Non-root: draai containers standaard als niet-root met een vaste `runAsUser` en `runAsGroup`.
- Capabilities: verwijder standaard alle capabilities en voeg alleen terug wat aantoonbaar nodig is.
- Bestandssysteem: zet `readOnlyRootFilesystem: true` aan waar mogelijk. Voor tijdelijke schrijfdata gebruik je een apart volume zoals `emptyDir`.
- Privilege escalation: zet `allowPrivilegeEscalation: false` en vermijd privileged containers tenzij een component dat echt vereist.
- Seccomp en aanvullende policy's: gebruik `RuntimeDefault` voor seccomp en vul dat waar nodig aan met AppArmor of SELinux.
- ServiceAccount-token: zet `automountServiceAccountToken: false` als een workload de Kubernetes API niet hoeft te gebruiken.
- Probes: configureer readiness-, liveness- en startup-probes zodat kapotte pods geen verkeer of jobs blijven verwerken.
Voorbeeld van een hardening-profiel
apiVersion: v1
kind: Pod
metadata:
name: hardened
spec:
automountServiceAccountToken: false
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "sleep 3600"]
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefaultPas dit profiel aan op basis van de applicatie, maar maak uitzonderingen expliciet. Zodra je afwijkt van deze basis, moet duidelijk zijn waarom dat nodig is.
Beperk verkeer tussen pods en publiceer alleen via de gateway
Standaard mag verkeer in veel clusters breder lopen dan nodig is. Maak daarom expliciet welke workload met welke andere workload mag praten, en laat publiek verkeer via één gecontroleerde ingang lopen.
- Gateway of ingress: publiceer HTTP(S)-verkeer via een centrale gateway of ingress, bijvoorbeeld met Traefik.
- TLS: gebruik TLS op je publieke listeners en automatiseer certificaten met Cert-manager.
- NetworkPolicies: ga uit van deny-by-default en open alleen de poorten en namespaces die een workload echt nodig heeft.
- East-west encryptie: verkeer tussen pods is niet standaard versleuteld. Als je ook intern TLS of mTLS wilt afdwingen, gebruik dan een service mesh of CNI-oplossing die dat ondersteunt.
- Publieke services: vermijd `NodePort` en losse `LoadBalancer`-services voor interne componenten. Hoe minder directe ingangen, hoe kleiner je attack surface.
Voorbeeld van een beperkte NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-only-same-namespace
namespace: example
spec:
podSelector:
matchLabels:
app: echo
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: example
ports:
- protocol: TCP
port: 5678Met deze policy accepteert de geselecteerde workload alleen verkeer uit de eigen namespace op poort `5678`. Zo voorkom je dat willekeurige andere workloads in het cluster verbinding maken.
Bescherm Secrets en opslag alsof het productiegegevens zijn
Gevoelige data hoort niet in images, Git-repositories of algemene configuratiebestanden thuis. Datzelfde geldt voor storage: een verkeerd gedeeld volume kan net zo goed een beveiligingsprobleem zijn als een open poort.
- Secrets: zet wachtwoorden, tokens en sleutels in Kubernetes Secrets of in een externe secret manager. Gebruik geen ConfigMaps of omgevingsvariabelen (environment variables) in voorbeeldbestanden wanneer de waarden gevoelig zijn.
- Toegang tot volumes: mount volumes alleen read-write wanneer dat nodig is. Veel configuratie- of certificaatvolumes kunnen read-only gemount worden.
- Persistent storage: geef iedere applicatie een eigen volume waar mogelijk. Gebruik gedeelde file storage alleen wanneer de applicatie gelijktijdige toegang en file locking ondersteunt.
- Persistent volume claim: maak voor stateful workloads een aparte persistent volume claim per datastore, zodat rechten en lifecycle per workload beheersbaar blijven.
- Back-ups en snapshots: reken niet alleen op replicatie. Maak daarnaast back-ups of snapshots zodat je ook beschermd bent tegen menselijke fouten, corrupte data of ransomware in de workload zelf.
Beveiliging van storage gaat dus niet alleen over encryptie, maar ook over scheiding, lifecycle, toegangsrechten en herstelmogelijkheden.
Blijf scannen, loggen en updaten
Een cluster is nooit 'af'. Nieuwe CVE's, nieuwe containerversies en nieuwe Helm Charts maken periodieke controle noodzakelijk.
- Image- en manifestscans: scan images en manifesten periodiek met tooling zoals Trivy, kubeaudit, kube-bench of kube-hunter, afhankelijk van wat je wilt controleren.
- Third-party software: installeer alleen Helm Charts, operators en YAML van bronnen die je vertrouwt en onderhoudt. Controleer changelogs en permissions voordat je ze toepast.
- Monitoring: verzamel metrics en logs met Prometheus en Grafana, en gebruik Lens of vergelijkbare tooling om afwijkingen snel te zien.
- Updates: houd base images, dependencies en cluster-add-ons bij. Oude versies van images, charts of controllers blijven een van de grootste oorzaken van vermijdbare incidenten.
- Review regelmatig rechten: controleer periodiek welke ServiceAccounts, Roles en bindings nog echt nodig zijn. Oude rechten blijven anders onzichtbaar bestaan.
Door scannen, loggen en updates als routine in te richten, verklein je de kans dat een bekende kwetsbaarheid weken of maanden in je cluster blijft staan.
Met een goede scheiding tussen workloads, strakke pod-hardening, beperkte netwerktoegang en bewust gebruik van storage maak je jouw Kubernetes-workloads merkbaar veiliger. Combineer dat met actief patchbeheer en monitoring, en je cluster blijft ook op langere termijn beheersbaar en verdedigbaar.