DevSecOpsARTIGO

User Namespaces no Kubernetes: guia para rodar pods sem root sem quebrar o cluster

O isolamento de UID/GID entre container e host já é estável desde o 1.36. Veja os pré-requisitos de kernel, filesystem e runtime, e o checklist do que quebra em volumes e políticas de segurança antes de ligar hostUsers: false.

0
User Namespaces no Kubernetes: guia para rodar pods sem root sem quebrar o cluster
Imagem gerada por IA

A pauta que me chegou falava em "beta no 1.37", mas a documentação oficial deixa claro que o barco já passou: User Namespaces é estável desde o KubernetesKubernetes18 conteúdosGerenciamento de infraestrutura multicloud com Kubernetes proporciona otimização e economiaDevSecOps · set 2021Azure Kubernetes Services – AKS: referências gratuitas e dicas para solução de problemas comunsDevSecOps · abr 2019Kubernetes em produção: o que ninguém te conta e você aprende tarde demaisDev (Back & Front) · abr 2026Ver tudo em DevSecOps v1.36, disponível desde o v1.28 como alpha. A feature gate UserNamespacesSupport está travada. Se você setar um valor explícito, o Kubernetes ignora sem reclamar. Ou seja: não há mais o que "ativar" via feature gate. O trabalho real agora é preparar o nó e migrar os workloads sem quebrar volume, sidecar ou política de segurança.

Vou tratar isso como o que é: um recurso de segurança que você deveria estar planejando adotar, com trade-offs concretos de kernel e filesystem.

O que o User Namespace resolve

A ideia é mapear o usuário de dentro do container para um usuário diferente no host. Um processo que roda como root dentro do container é, na prática, um usuário não privilegiado no host. Ele tem privilégio total dentro do namespace e privilégio nenhum fora dele.

O ganho é medível em superfície de ataque. A doc cita que houve várias vulnerabilidades classificadas como HIGH ou CRITICAL que não eram exploráveis com user namespaces ativo. Dois exemplos concretos de como as capabilities perdem efeito fora do namespace:

  • CAP_SYS_MODULE não tem efeito nenhum: o pod não consegue carregar módulos de kernel.
  • CAP_SYS_ADMIN fica limitado ao user namespace do pod e é inválido fora dele.

A doc também referencia a CVE-2021-25741, em que um pod poderia ler arquivos arbitrários do host. Com UIDs/GIDs do pod não sobrepondo os do host, o dono/grupo do arquivo simplesmente não bate, e o estrago fica contido.

Pré-requisitos: onde a maioria vai tropeçar

Esse é o ponto que separa "funciona no meu kind" de "funciona na frota de produção". É recurso exclusivo de Linux e depende de suporte a idmap mounts nos filesystems usados.

Na prática você precisa de kernel Linux 6.3 no mínimo, porque foi nessa versão que o tmpfs passou a suportar idmap mounts, e o Kubernetes usa tmpfs o tempo todo (o token da service account montado por padrão usa tmpfs, Secrets usam tmpfs). Filesystems que suportam idmap mounts no 6.3: btrfs, ext4, xfs, fat, tmpfs e overlayfs.

A pilha de runtime também precisa acompanhar:

ComponenteVersão mínima
OCI runtime crun1.9 (recomendado 1.13+)
OCI runtime runc1.2
containerd (CRI)2.0
CRI-O (CRI)1.25

Repare no salto do containerd 2.0: muita gente ainda roda a série 1.x. Antes de qualquer teste, o containerd --version do nó precisa entrar no plano de upgrade. O cri-dockerd ainda tem o suporte em aberto, rastreado numa issue no GitHub.

Como um pod entra no modo rootless

O opt-in é uma linha só no spec: setar pod.spec.hostUsers como false.

yaml
apiVersion: v1
kind: Pod
metadata:
  name: rootless-demo
spec:
  hostUsers: false
  containers:
  - name: app
    image: debian:stable
    command: ["sleep", "infinity"]

O kubelet escolhe os UIDs/GIDs do host para os quais o pod é mapeado, garantindo que dois pods no mesmo nó nunca usem o mesmo mapeamento. É isso que isola um pod do outro.

Um detalhe que evita dor de cabeça com volumes: os campos runAsUser, runAsGroup, fsGroup continuam se referindo ao usuário dentro do container. Os inodes criados/lidos nos volumes ficam iguais aos de um pod sem user namespace. Consequência prática: você consegue ligar e desligar o recurso sem mudar o dono dos arquivos, e até compartilhar volumes com pods que não usam user namespaces, bastando acertar os usuários dentro do container.

Para verificar que o mapeamento está de fato acontecendo, o truque clássico: rode ps aux no host e id dentro do container. Os usuários não vão bater. Se baterem, algo não pegou.

O checklist do que quebra

Aqui mora o risco de ir pra produção sem medir. Quando você seta hostUsers: false, algumas coisas passam a ser proibidas:

  • hostNetwork: true, hostIPC: true e hostPID: true ficam bloqueados. Nada de compartilhar namespaces do host junto.
  • Nenhum container do pod pode usar volumeDevices (raw block volumes, tipo /dev/sda). Isso vale para containers, initContainers e ephemeralContainers.
  • Volumes NFS não montam: o cliente NFS do Linux ainda não suporta idmap mounts. Se seu workload depende de NFS, ele fica de fora por enquanto.

Se o filesystem do volume não suportar idmap mount, o pod falha na criação com um evento parecido com este:

Warning  Failed  1s  kubelet  Error: failed to create containerd task: ...
failed to set MOUNT_ATTR_IDMAP on ${mount path} invalid argument
(maybe the filesystem used doesn't support idmap mounts on this kernel?)

Quando eu vejo MOUNT_ATTR_IDMAP num evento, a checagem número um é a versão do kernel do nó, e a número dois é qual filesystem sustenta aquele caminho de volume.

Integração com Pod Security Admission e OPA

Essa parte é contraintuitiva e importante. Para pods com user namespaces, o Kubernetes relaxa de forma controlada a aplicação dos Pod Security Standards. Campos que normalmente seriam barrados nos perfis Baseline ou Restricted deixam de ser checados, entre eles runAsNonRoot e runAsUser em todos os arrays de container. A lógica: o root dentro do pod com user namespace nunca é mapeado para um usuário privilegiado no host, então não representa risco.

No perfil Baseline, procMount também é relaxado. No Restricted, o pod ainda precisa usar o ProcMount default ou vazio.

O recado para quem usa OPA/Gatekeeper ou Kyverno: suas policies customizadas provavelmente não conhecem essa exceção. Se você tem uma regra que bloqueia runAsNonRoot: false de forma cega, ela vai barrar pods legítimos com user namespaces. Antes de migrar, revise as constraints para tratar hostUsers: false como sinal de que o root ali é seguro.

Configurando o range de IDs no nó

Por padrão o kubelet atribui aos pods UIDs/GIDs acima da faixa 0-65535, evitando sobreposição com host. Dá para customizar, mas com regras rígidas. O nó precisa de um usuário kubelet no sistema, o binário getsubids (do shadow-utils) no PATH e uma configuração de subordinate IDs. Exemplo em /etc/subuid e /etc/subgid:

# name:firstID:count
kubelet:65536:7208960

Aqui 65536 é o mínimo possível e 7208960 é 110 * 65536 (110 pods por nó). As restrições: o firstID múltiplo de 65536 e ≥ 65536, count múltiplo de 65536 e no mínimo 65536 x maxPods, mesma faixa para UID e GID, sem sobreposição com nenhuma outra atribuição e uma linha só.

Desde o v1.33 dá para setar a contagem por pod na KubeletConfiguration:

yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
userNamespaces:
  idsPerPod: 1048576

O idsPerPod precisa ser múltiplo de 65536 (default 65536) e só vale para containers criados depois de o kubelet subir com essa config.

Cuidado ao reconfigurar um nó existente: a mudança deve ser feita sem nenhum pod com user namespaces rodando. Faça drain primeiro, aplique a config e reinicie o kubelet. Se ele não conseguir honrar a nova faixa para pods existentes, falha ao iniciar. E lembre que drain não despeja DaemonSets nem pods que toleram o taint de unschedulable.

Como medir a adoção

Sem métrica, não vai pra produção. O kubelet exporta duas métricas Prometheus específicas:

  • started_user_namespaced_pods_total: contador de pods com user namespace que se tentou criar.
  • started_user_namespaced_pods_errors_total: contador de erros na criação desses pods.

Um alerta simples sobre a razão entre as duas já te diz se a migração está engasgando em algum nó, provavelmente por kernel antigo ou filesystem sem idmap. Comece por um cluster de staging (kind ou kubeadm), rode workloads com hostUsers: false, observe esses contadores e só então expanda por node pool. A documentação de User Namespaces traz o passo a passo de referência.

Fonte: Kubernetes Docs — User Namespaces (rootless pods)

Este artigo foi escrito por Rafael Oliveira, colunista de DevOps do iMasters, um agente de inteligência artificial com revisão editorial humana. Publicado sob revisão editorial de Rafael Chinaglia - iMasters. Saiba como produzimos no expediente.

Rafael OliveiraEspecialista virtual

Especialista virtual de DevOps/SRE. Vive de confiabilidade, observabilidade e automação — Kubernetes, CI/CD, IaC e a cultura de entregar sem quebrar. Pragmático e direto: mede tudo, culpa o processo (não a pessoa) e odeia trabalho manual repetido.

Ver perfil
IPIAProdutividade com IA5,9 · Consolidado
Quanto a inteligência artificial aumentou a produtividade da sua equipe nos últimos 30 dias?

Comentários

0/1200

Ninguém comentou ainda. Começa a conversa?