Pular para o conteúdo

Hardening

Avançado18 min de leituradevsecops

Hardening é reduzir o que existe: menos pacote instalado, menos porta aberta, menos permissão concedida, menos processo com privilégio. Cada coisa removida é uma coisa que não pode ser explorada, não precisa ser corrigida e não aparece no relatório de vulnerabilidade do mês que vem.

O erro comum é tratar hardening como uma varredura anual que gera trezentos itens. O que funciona é escolher poucos controles de alto impacto, aplicá-los por padrão no template que todo mundo usa, e verificar continuamente.

CIS Benchmarks e o STIG existem e cobrem host, Docker e Kubernetes. Adote uma delas como referência, documente os desvios com justificativa e pare de discutir item por item.

Janela do terminal
kube-bench run --targets node,policies # CIS para Kubernetes
docker run --rm --net host --pid host --cap-add audit_control \
-v /var/lib:/var/lib:ro -v /var/run/docker.sock:/var/run/docker.sock:ro \
docker/docker-bench-security

Trate o resultado como backlog priorizado, não como lista a zerar. Alguns itens não fazem sentido no seu contexto — registre a decisão em vez de deixá-los reaparecendo.

Se você ainda administra máquina, o essencial cabe em poucos pontos:

  • SSH: só chave (PasswordAuthentication no), sem login direto de root (PermitRootLogin no), acesso restrito por rede e, de preferência, via bastion ou acesso por identidade.
  • Pacotes: instale o mínimo; imagem de nó gerenciada e descartável é melhor que máquina de estimação corrigida à mão.
  • Atualização: automática para correção de segurança, com reinício planejado.
  • Firewall: negue por padrão na entrada, permita o necessário; e sim, também dentro da VPC.
  • Auditoria: auditd ou equivalente, com log enviado para fora da máquina — log que fica só no host comprometido não serve de evidência.
Janela do terminal
ss -tulpn # o que está escutando, e quem
systemctl list-units --type=service --state=running | wc -l # menos é melhor
sudo lastb | head # tentativas de login que falharam

Container: usuário, capabilities e sistema de arquivos

Seção intitulada “Container: usuário, capabilities e sistema de arquivos”

O securityContext abaixo é o padrão que deveria vir no template de todo serviço:

securityContext: # no Pod
runAsNonRoot: true
runAsUser: 10001
fsGroup: 10001
seccompProfile: { type: RuntimeDefault }
containers:
- name: loja
securityContext: # no container
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
volumeMounts:
- { name: tmp, mountPath: /tmp } # com root read-only, dê um /tmp gravável

Some a isso o que vem da imagem: base mínima (distroless ou Alpine), sem shell e sem gerenciador de pacotes, build multi-stage para não carregar compilador para produção, e nenhum segredo em camada intermediária.

Nunca use privileged: true para resolver permissão negada — ele desliga praticamente todo o isolamento. Descubra a capability específica que falta e conceda só ela.

Em ordem de retorno pelo esforço:

  1. Pod Security Admission em restricted para todo namespace de aplicação.
  2. RBAC mínimo: nada de cluster-admin para pipeline ou operador; audite com kubectl auth can-i --list --as=….
  3. NetworkPolicy padrão negando entrada, com liberação explícita — sem isso, qualquer Pod fala com qualquer Pod.
  4. API server sem exposição pública, ou restrita por origem.
  5. Criptografia do etcd e backup criptografado.
  6. Sem token de ServiceAccount montado onde não é usado (automountServiceAccountToken: false).
apiVersion: v1
kind: Namespace
metadata:
name: loja
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/warn: restricted
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: negar-entrada-padrao, namespace: loja }
spec:
podSelector: {} # todos os Pods do namespace
policyTypes: [Ingress] # nenhuma regra de allow: nega tudo que entra

Configuração segura sem verificação vira configuração insegura em três meses — alguém precisa “só para testar” e o teste fica. Automatize três coisas:

  • Política de admissão bloqueando o que você acabou de padronizar (Pod Security e Kyverno).
  • Varredura periódica do que já está rodando, não só do que vai entrar.
  • Alerta de mudança sensível: ClusterRoleBinding novo, Pod privilegiado, porta exposta.
Janela do terminal
kubectl get pods -A -o json | jq -r '.items[]
| select(.spec.containers[].securityContext.privileged == true)
| "\(.metadata.namespace)/\(.metadata.name)"'

Mudança de baseline entra por pull request, como qualquer código, e vale para o template que gera novos serviços — hardening que não está no template se perde no próximo projeto.

Próximo passo: para os requisitos que vêm de lei e não de ameaça técnica, siga para Compliance e LGPD para infraestrutura.