Sobre este conteúdo
Guia prático para sair de PodSecurityPolicy (PSP) — removida no Kubernetes 1.25 — e adotar Pod Security Standards (PSS) nativos complementados por Kyverno para regras custom da organização. Pressupõe familiaridade com manifests Kubernetes e RBAC.
Neste artigo
Por que PSP foi removida
PodSecurityPolicy foi a primeira tentativa do Kubernetes de impor controles de segurança em pods (privilégios, hostPath, capabilities). Foi deprecada em 1.21 (abril/2021) e removida em 1.25 (agosto/2022). Por quê? PSP tinha três problemas operacionais sérios: (1) ordem de avaliação imprevisível quando havia várias políticas, (2) RBAC complexo — você precisava conceder use da PSP ao service account, com efeitos colaterais perversos, (3) sem mutação fácil para corrigir manifests, só rejeitar.
A substituição nativa é Pod Security Standards (PSS), que padroniza três perfis simples aplicados por namespace, sem RBAC custom. Para tudo que PSS não cobre — regras específicas da organização, mutações, validações ricas — entra um policy engine como Kyverno (ou OPA Gatekeeper). Esta é a combinação dominante em 2026.
Pod Security Standards: 3 perfis
PSS define três níveis cumulativos, em ordem de restrição crescente:
| Perfil | O que permite/exige | Quando usar |
|---|---|---|
| privileged | Sem restrições. Acesso a hostPath, hostNetwork, capabilities, runAsRoot. | Componentes do cluster (CSI, ingress, agentes de segurança) em namespaces isolados. |
| baseline | Bloqueia escalações conhecidas: hostNetwork, hostPID, hostIPC, hostPath, privileged, capabilities perigosas. Exige seccomp default. | Mínimo aceitável em produção. Workloads legados que ainda não rodam restricted. |
| restricted | Tudo de baseline + runAsNonRoot, runAsUser != 0, allowPrivilegeEscalation false, readOnlyRootFilesystem, seccomp RuntimeDefault, drop ALL capabilities. | Aplicações novas e workloads modernos. Meta para todo desenvolvimento greenfield. |
Modos audit, warn e enforce
Cada perfil pode ser aplicado em três modos via labels no namespace. Eles podem coexistir com versões diferentes do perfil:
apiVersion: v1
kind: Namespace
metadata:
name: payments-api
labels:
# Bloqueia pods que violam baseline
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: v1.29
# Avisa no kubectl apply se viola restricted (não bloqueia)
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: v1.29
# Registra violações de restricted no audit log
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: v1.29
Padrão de adoção:
- Fase 1: audit + warn em todos os namespaces. Observe o audit log por 1-2 semanas.
- Fase 2: enforce baseline. Pods que violam baseline são rejeitados.
- Fase 3: enforce restricted onde for possível. Workloads legados ficam em baseline.
Exemplo baseline na prática
Considere um Pod que tenta rodar com privilégios elevados:
apiVersion: v1
kind: Pod
metadata:
name: legacy-app
namespace: payments-api
spec:
hostNetwork: true # <- bloqueado em baseline
containers:
- name: app
image: corp/app:1.2.3
securityContext:
privileged: true # <- bloqueado em baseline
capabilities:
add: [ "SYS_ADMIN" ] # <- bloqueado em baseline
Com enforce=baseline no namespace, o API server rejeita imediatamente:
Error from server (Forbidden): error when creating "legacy-app.yaml":
pods "legacy-app" is forbidden: violates PodSecurity "baseline:v1.29":
host namespaces (hostNetwork=true),
privileged (container "app" must not set securityContext.privileged=true),
non-default capabilities (container "app" must not include "SYS_ADMIN" in securityContext.capabilities.add)
Exemplo restricted
Um Pod que passa em restricted típico:
apiVersion: v1
kind: Pod
metadata:
name: api
namespace: payments-api
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: api
image: corp/api:2.0.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ "ALL" ]
volumeMounts:
- name: cache
mountPath: /tmp
volumes:
- name: cache
emptyDir: {}
Note que readOnlyRootFilesystem: true exige montar volumes emptyDir/PVC para qualquer escrita (tmp, cache, logs).
Quando entra o Kyverno
PSS resolve o piso de segurança comum. Kyverno entra em três cenários:
Regras custom da organização
Exigir labels (centro de custo, dono), forçar registry corporativo, exigir limits de CPU/memória, verificar assinatura Cosign de imagens.
Mutação automática
PSS só valida — não consegue corrigir manifests. Kyverno injeta defaults: securityContext mínimo, label de team faltando, sidecars padrão.
Validações ricas
Políticas que cruzam estado de outros recursos (ClusterPolicy referencia ConfigMap), verificação de assinatura via Cosign, exigir SBOM anexado, etc.
Exemplo de ClusterPolicy Kyverno que exige imagens vindas apenas do registry corporativo:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-corp-registry
spec:
validationFailureAction: Enforce
background: true
rules:
- name: validate-image-registry
match:
any:
- resources:
kinds: [Pod]
namespaces: ["payments-*", "billing-*"]
validate:
message: "Imagens devem vir de registry.corp.example.com"
pattern:
spec:
containers:
- image: "registry.corp.example.com/*"
Para combinar com SLSA/Cosign (ver artigo dedicado):
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: Enforce
rules:
- name: check-signature
match:
any:
- resources:
kinds: [Pod]
verifyImages:
- imageReferences:
- "registry.corp.example.com/*"
attestors:
- entries:
- keyless:
subject: "https://github.com/corp/*/.github/workflows/release.yml@refs/heads/main"
issuer: "https://token.actions.githubusercontent.com"
Troubleshooting comum
| Sintoma | Causa provável | Resolução |
|---|---|---|
Pod rejeitado por restricted, mas o app precisa escrever em /var/log | readOnlyRootFilesystem bloqueia escrita | Montar emptyDir ou PVC em /var/log |
| "Cannot run as root" mas a imagem oficial roda como root | restricted exige runAsNonRoot | Usar imagem distroless/non-root ou fazer rebuild com USER 10001 |
| Workload precisa de CAP_NET_BIND para porta <1024 | restricted dropa ALL capabilities | Mover para porta >=1024 ou expor via Service NodePort/Ingress |
| Agente de segurança (Falco, NewRelic, Datadog) rejeitado | Precisa de hostPath, hostPID ou capabilities | Namespace dedicado com enforce=privileged; isolar RBAC |
| kubectl apply sem erro mas pod não inicia | warn não bloqueia, mas violação real bloqueia | Verifique kubectl get events -n <ns> e ajuste para o nível efetivo |
Roadmap de migração
Para clusters que ainda dependem de PSP ou estão sem qualquer admission de segurança:
Inventário (1 semana)
Liste todas PSPs em uso (se ainda em <1.25), e workloads por namespace. Decida quais namespaces vão para baseline e quais para restricted.
Audit + warn (1-2 semanas)
Habilite PSS em modo audit (restricted) + warn (baseline) em todos os namespaces. Colete violações sem bloquear.
Enforce baseline (2-4 semanas)
Corrija manifests dos workloads que violavam baseline. Suba para enforce=baseline namespace a namespace.
Enforce restricted (gradual)
Em namespaces de aplicações novas, vá direto para enforce=restricted. Para legados, plano por workload — algumas migrações exigem rebuild de imagens base.
Kyverno para regras custom
Instale Kyverno e implemente políticas específicas: registry corporativo, labels obrigatórios, verificação Cosign, mutação de defaults.
Perguntas frequentes
PSP foi removida em qual versão do Kubernetes?
Deprecada em 1.21 (abril/2021) e removida em 1.25 (agosto/2022). Quem está em 1.25+ precisou migrar para Pod Security Standards (built-in) ou policy engines como Kyverno e OPA Gatekeeper.
Qual a diferença entre baseline e restricted?
Baseline previne escalações conhecidas (hostNetwork, privileged, capabilities perigosas) — é o mínimo em produção. Restricted vai além: runAsNonRoot, readOnlyRootFilesystem, drop ALL capabilities, seccomp RuntimeDefault. Use baseline em legados e restricted em tudo novo.
Qual o papel dos modos audit, warn e enforce?
Enforce bloqueia. Warn avisa no kubectl mas permite. Audit registra no log do API server sem bloquear. Estratégia: audit+warn primeiro, depois enforce baseline, depois enforce restricted onde possível.
PSS substitui Kyverno?
Não. PSS cobre as restrições padronizadas. Kyverno entra em (1) regras custom da organização, (2) mutação, (3) validações ricas (Cosign, cross-resource). A combinação é o padrão moderno.
Como descobrir o que vai quebrar antes do enforce?
Use modo audit nos namespaces antes de enforce. Os eventos vão para o audit log do API server. Polaris, kubescape, kube-score rodam validação offline contra manifests. Integre no CI/CD para quebrar PR antes de chegar em prod.
O que fazer com workloads que precisam de privilégio?
CSI drivers, ingress controllers, agentes de segurança precisam genuinamente de capabilities elevadas. Isole em namespaces dedicados privileged, audite uso, e prefira providers que rodam restricted. Documente exceções com responsável e revisão semestral.
Referências
- Kubernetes — Pod Security Standards — kubernetes.io/docs/concepts/security/pod-security-standards/
- Kubernetes — Pod Security Admission — kubernetes.io/docs/concepts/security/pod-security-admission/
- Kyverno — Documentação oficial — kyverno.io/docs/
- CNCF — Cloud Native Security Whitepaper v2 — cncf.io
- NSA/CISA — Kubernetes Hardening Guide