Kubernetes Security: RBAC, Pod Security Standards a OPA Gatekeeper
Výchozí a překvapivě nezabezpečený cluster Kubernetes: kontejnery mohou běžet jako root, připojit souborový systém hostitelského uzlu, API pro přístup k serveru s tokenem ServiceAccount s širokými oprávněními. V roce 2025 68 % bezpečnostních incidentů v prostředích kontejnerizovaná byla způsobena spíše chybami konfigurace než zranitelností software (zdroj: Sysdig Container Security Report 2025). Dobrá zpráva: Kubernetes nabízí výkonné nástroje k odstranění těchto rizikových nastavení.
Tento článek popisuje kompletní klastrové zpevnění: RBAC zkontrolovat kdo co může dělat s Kubernetes API, Bezpečnostní standardy pod zabránit privilegované kontejnery a rizikové konfigurace, např Strážce brány OPA pro implementovat vlastní firemní zásady jako „všechny obrázky musí pocházet z interní registr“ nebo „žádné nasazení bez omezení zdrojů“.
Co se naučíte
- Autorizační model Kubernetes: RBAC, slovesa, prostředky, rozsah
- Princip nejmenšího privilegia: Role, ClusterRole, RoleBinding použita
- ServiceAccount: Jak omezit automaticky připojené tokeny a používat IRSA/Workload Identity
- Bezpečnostní standardy pod: Privilegované, Základní, Omezené a prosazování podle jmenného prostoru
- OPA Gatekeeper: Instalace, ConstraintTemplate v Rego, Constraint
- Společné zásady: schválené obrazy registru, povinné limity zdrojů, žádný root
- Protokolování auditu: Sledujte, kdo co v clusteru dělá
- Kompletní kontrolní seznam kalení
RBAC: Role Based Access Control
RBAC (Role-Based Access Control) je základní autorizační mechanismus Kubernetes. Definuje SZO (Předmět: Uživatel, Skupina, Servisní účet) lze spustit jaká akce (sloveso: získat, vypsat, sledovat, vytvořit, aktualizovat, opravit, odstranit) nahoru jaký zdroj (Zdroj: moduly, nasazení, tajemství) v jaký rozsah (jmenný prostor s Role/RoleBinding nebo celý cluster s ClusterRole/ClusterRoleBinding).
Role a RoleBinding pro vývojový tým
# rbac-developer-role.yaml
# Un ruolo per i developer: possono vedere e modificare
# deployment/pod/service nel loro namespace, ma non secrets
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: team-alpha
rules:
# Deployment: lettura + scaling
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
# Pod: lettura + exec + logs
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
# Service e ConfigMap: accesso completo
- apiGroups: [""]
resources: ["services", "configmaps", "endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# HPA
- apiGroups: ["autoscaling"]
resources: ["horizontalpodautoscalers"]
verbs: ["get", "list", "watch"]
# Ingress
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: team-alpha-developers
namespace: team-alpha
subjects:
# Gruppo di utenti (gestito da OIDC/SSO)
- kind: Group
name: "team-alpha-devs"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.io
ClusterRole pro SRE/Admin s omezeným rozsahem
# rbac-sre-clusterrole.yaml
# SRE: accesso in lettura a tutto il cluster, write solo su namespace specifici
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-reader
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["get", "list", "watch"]
# Nega esplicita: non puo leggere i secrets (sovrascritta da DENY)
# NOTA: in RBAC Kubernetes non esiste DENY esplicita - usa Gatekeeper per questo
---
# SRE: read su tutto + write limitato ai namespace di produzione
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: sre-cluster-reader
subjects:
- kind: Group
name: "platform-sre"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-reader
apiGroup: rbac.authorization.k8s.io
# Verifica i permessi di un utente/serviceaccount
kubectl auth can-i create pods --as=system:serviceaccount:production:api-server-sa
kubectl auth can-i delete secrets --as=developer-user -n production
kubectl auth can-i '*' '*' --as=alice # lista tutto quello che alice puo fare
Servisní účet: Omezte automatické tokeny
Ve výchozím nastavení každý modul automaticky připojí platný token ServiceAccount. V Pods že nevolají Kubernetes API, tento token je k ničemu, ale zvyšuje plochu útoku:
# serviceaccount-minimal.yaml
# ServiceAccount dedicata per ogni applicazione (mai usare default)
apiVersion: v1
kind: ServiceAccount
metadata:
name: api-server-sa
namespace: production
annotations:
# Su AWS EKS: delega i permessi IAM tramite IRSA
eks.amazonaws.com/role-arn: "arn:aws:iam::123456789:role/ApiServerRole"
automountServiceAccountToken: false # non montare il token automaticamente
---
# Deployment che usa la ServiceAccount
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
namespace: production
spec:
template:
spec:
serviceAccountName: api-server-sa
automountServiceAccountToken: false # ridondante ma esplicito
containers:
- name: api-server
image: my-registry/api-server:1.2.0
---
# Se il Pod deve chiamare l'API K8s, usa un token proiettato con scadenza
# invece del token auto-mounted (che non scade mai)
apiVersion: v1
kind: Pod
spec:
serviceAccountName: api-server-sa
volumes:
- name: api-token
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600 # scade ogni ora
audience: kubernetes.default.svc
containers:
- name: api-server
volumeMounts:
- name: api-token
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true
Bezpečnostní standardy pod (PSS)
Bezpečnostní standardy pod nahrazují zastaralé zásady PodSecurityPolicies (odstraněno v K8s 1.25). Definují tři úrovně zabezpečení použitelné na úrovni jmenného prostoru prostřednictvím štítků:
- Výsadní: žádná omezení (pouze pro systémové jmenné prostory, jako je kube-system)
- Základní linie: zabraňuje nejznámějším a nejrizikovějším konfiguracím (privilegované kontejnery, hostPath, hostNetwork)
- Omezený: Aktuální osvědčené postupy pro maximální zabezpečení (non-root, seccomp, pokles schopností)
Každá úroveň má tři režimy: enforce (blokuje modul), audit
(přihlásí se, ale neblokuje), warn (ukáže uživateli varování).
Použijte bezpečnostní standardy pod na jmenné prostory
# Applica PSS al namespace tramite label
kubectl label namespace production \
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
# Per namespace di sistema che richiedono pod privilegiati
kubectl label namespace kube-system \
pod-security.kubernetes.io/enforce=privileged
# Testa cosa succederebbe se applicassi restricted a un namespace esistente
kubectl label --dry-run=server --overwrite namespace production \
pod-security.kubernetes.io/enforce=restricted
Pod v souladu s omezenou úrovní
# pod-restricted-compliant.yaml
# Un Pod che rispetta tutte le regole del livello Restricted
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
namespace: production
spec:
securityContext:
runAsNonRoot: true # non girare come root
runAsUser: 1000 # UID non-root
runAsGroup: 3000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault # profilo seccomp di default
supplementalGroups: [1000]
containers:
- name: app
image: my-registry/api-server:1.2.0
securityContext:
allowPrivilegeEscalation: false # fondamentale: previene sudo
readOnlyRootFilesystem: true # filesystem immutabile
capabilities:
drop:
- ALL # rimuovi tutte le Linux capabilities
# add: [] - non aggiungere nessuna capability
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
volumeMounts:
- name: tmp
mountPath: /tmp # se l'app scrive su /tmp, monta un volume
- name: cache
mountPath: /app/cache
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
automountServiceAccountToken: false
OPA Gatekeeper: Policy Engine pro Kubernetes
Bezpečnostní standardy pod pokrývají pevnou sadu ovládacích prvků. OPA Gatekeeper umožňuje k definování vlastních zásad pomocí Rego, jazyk OPA. Je implementován jako Admission Webhook, který zachycuje každý požadavek na server API a platí proti definovaným zásadám.
Instalace OPA Gatekeeper
# Installa Gatekeeper con Helm
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update
helm install gatekeeper gatekeeper/gatekeeper \
--namespace gatekeeper-system \
--create-namespace \
--version 3.17.0 \
--set auditInterval=30 \
--set constraintViolationsLimit=100 \
--set logLevel=INFO
# Verifica installazione
kubectl get pods -n gatekeeper-system
ConstraintTemplate: Obrázky pouze ze schváleného registru
# constraint-template-registry.yaml
# Il template definisce lo schema e la logica in Rego
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sallowedrepos
annotations:
description: "Richiede che le immagini provengano solo da registry approvati"
spec:
crd:
spec:
names:
kind: K8sAllowedRepos
validation:
openAPIV3Schema:
type: object
properties:
repos:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sallowedrepos
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not starts_with_allowed(container.image)
msg := sprintf("Il container '%v' usa l'immagine '%v' che non proviene da un registry approvato", [container.name, container.image])
}
violation[{"msg": msg}] {
container := input.review.object.spec.initContainers[_]
not starts_with_allowed(container.image)
msg := sprintf("L'initContainer '%v' usa l'immagine '%v' non approvata", [container.name, container.image])
}
starts_with_allowed(image) {
repo := input.parameters.repos[_]
startswith(image, repo)
}
---
# Constraint: applica la policy con i parametri specifici
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
name: require-approved-registry
spec:
enforcementAction: deny # deny|warn|dryrun
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
excludedNamespaces:
- kube-system
- gatekeeper-system
- monitoring
parameters:
repos:
- "registry.company.internal/"
- "gcr.io/company-project/"
- "public.ecr.aws/company/"
ConstraintTemplate: Povinné limity zdrojů
# constraint-template-resource-limits.yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredresources
spec:
crd:
spec:
names:
kind: K8sRequiredResources
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredresources
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.resources.limits.cpu
msg := sprintf("Container '%v': cpu limit obbligatorio ma mancante", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.resources.limits.memory
msg := sprintf("Container '%v': memory limit obbligatorio ma mancante", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.resources.requests.cpu
msg := sprintf("Container '%v': cpu request obbligatoria ma mancante", [container.name])
}
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredResources
metadata:
name: require-resource-limits
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
excludedNamespaces:
- kube-system
- gatekeeper-system
ConstraintTemplate: Žádný kořenový adresář kontejneru
# constraint-template-no-root.yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8spsphostnamespace
spec:
crd:
spec:
names:
kind: K8sPSPHostNamespace
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8spsphostnamespace
violation[{"msg": msg}] {
input.review.object.spec.hostPID == true
msg := "hostPID non e consentito"
}
violation[{"msg": msg}] {
input.review.object.spec.hostIPC == true
msg := "hostIPC non e consentito"
}
violation[{"msg": msg}] {
input.review.object.spec.hostNetwork == true
not input.review.object.metadata.annotations["policy.company.internal/exempt-hostnetwork"]
msg := "hostNetwork non e consentito senza annotation di esenzione"
}
# Verifica violazioni esistenti nel cluster
kubectl get constraints
kubectl describe k8srequiredresources require-resource-limits
# Nella sezione Status.Violations vedrai tutti i Pod non conformi
Protokolování auditu
Kubernetes může protokolovat každý požadavek na server API do strukturovaného protokolu auditu. Je to nezbytné pro dodržování předpisů a vyšetřování incidentů.
# audit-policy.yaml - configurazione dell'audit log
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Registra tutto sui Secrets a livello RequestResponse (incluso il contenuto)
- level: RequestResponse
resources:
- group: ""
resources: ["secrets"]
# Registra le modifiche a RBAC
- level: RequestResponse
resources:
- group: "rbac.authorization.k8s.io"
resources: ["roles", "clusterroles", "rolebindings", "clusterrolebindings"]
# Registra exec nei Pod (potenziale accesso malevolo)
- level: Request
resources:
- group: ""
resources: ["pods/exec", "pods/portforward", "pods/proxy"]
# Per tutto il resto: registra solo i metadata (chi ha fatto cosa, quando)
- level: Metadata
omitStages:
- RequestReceived
# Kube-apiserver config (aggiungi all'avvio di kube-apiserver):
# --audit-log-path=/var/log/kubernetes/audit.log
# --audit-log-maxage=30
# --audit-log-maxbackup=10
# --audit-log-maxsize=100
# --audit-policy-file=/etc/kubernetes/audit-policy.yaml
Kontrolní seznam pro kalení Kubernetes
Kontrolní seznam pro posílení zabezpečení
- RBAC: Princip nejmenšího oprávnění pro každý ServiceAccount a skupinu uživatelů
- Servisní účet:
automountServiceAccountToken: falsena všech podech, které nevolají serverové API - Vypršení platnosti tokenu: použít tokeny promítnuté s
expirationSecondsmísto trvalých tokenů - Bezpečnostní standardy pod:
restrictedve všech produkčních jmenných prostorech,baselinena inscenaci - Žádný kořen:
runAsNonRoot: trueeallowPrivilegeEscalation: falsena všech nádobách - Neměnný souborový systém:
readOnlyRootFilesystem: true+ volume emptyDir pro /tmp a /cache - Limity zdrojů: Limity CPU a paměti na všech kontejnerech (Gatekeeper pro vynucení)
- Schválené registry: Omezení správce brány, které blokuje obrázky z neautorizovaných registrů
- Zásady sítě: default-deny ve všech produkčních jmenných prostorech (viz článek 1)
- tajemství: použijte externího správce tajných klíčů (AWS Secrets Manager, Vault) místo prostého tajného Kubernetes
- Protokoly auditu: povoleno a centralizováno v SIEM nebo v agregátoru protokolů
- atdd: šifrované v klidu s konfigurací šifrování
- Server API: anonymní přístup zakázán, řadiče přístupu povoleny
Běžné bezpečnostní chyby
- Výchozí jmenný prostor pro úlohy: výchozí jmenný prostor má ve výchozím nastavení povolený RBAC; Vždy používejte vyhrazené jmenné prostory pro své úlohy
- ClusterAdmin pro kanály CI/CD: Pipelines GitOps nepotřebují cluster-admin; Vytvořte ServiceAccount s minimálními oprávněními potřebnými pro cílový obor názvů
- Tajné jako proměnné prostředí: proměnné prostředí jsou viditelné v
kubectl describe poda v protokolech; používejte soubory připojené tajnými klíči nebo externími správci tajných klíčů - Nejnovější značky v obrázcích: nejnovější značka se může změnit; Vždy používejte SHA256 digest nebo neměnné značky, aby byla zajištěna reprodukovatelnost a bezpečnost
- Ignorovat CVE základního obrázku: skenujte obrázky pravidelně pomocí Trivy, Snyk nebo Grype; základní obraz Ubuntu s kritickými CVE ruší práci na posílení clusteru
Závěry a další kroky
Zabezpečení clusteru Kubernetes je vrstvený proces: RBAC řídí přístup k API brání bezpečnostní standardy podu riskantním konfiguracím na úrovni podu, a OPA Gatekeeper vám umožňuje kódovat firemní zásady jako verzovaný kód a auditovatelné. Žádný z těchto nástrojů sám o sobě nestačí; dohromady tvoří jedno hloubková obrana, která drasticky snižuje útočnou plochu.
Dalším krokem k dalšímu posílení bezpečnosti a zavedení systému runtime bezpečnostní nástroje jako Falco, které v reálném čase monitorují volání kontejnerového systému a upozornění na anomální chování (otevřená skořápka v kontejneru ve výrobě, čtení soubory pověření, neočekávaná síťová připojení).
Připravované články v sérii Kubernetes at Scale
Předchozí články
Související série
- DevSecOps — policy-as-code, bezpečnostní skenování v CI/CD kanálech
- Síť Kubernetes — Zásady sítě pro izolaci zátěže
- Platform Engineering — zabezpečení jako automatické zábradlí pro týmy







