Times no NKP com RBAC nativo e quotas: cada dev no seu namespace

Publicado
Leitura
12 min

Testado emNKP 2.19Kubernetes 1.36.2Microsoft Entra ID FreeLab em Em outra versão, confira a matriz de compatibilidade e as release notes antes de aplicar.

Read in English

Capa do post: Times no NKP com RBAC nativo e quotas, cada dev no seu namespace

Resumo: no artigo anterior, o Entra ID passou a dizer quem entra no NKP. Este mostra o que cada time pode fazer e quanto pode consumir, só com objetos nativos do Kubernetes: um namespace por time, um RoleBinding que liga o grupo do Entra ao papel edit e uma ResourceQuota com LimitRange. A Carla, do grupo de devs, foi barrada antes do binding, passou a criar no namespace dela e em nenhum outro lugar, e a quota segurou um deploy de 8 réplicas em 4. Antes de liberar, o admin testa o acesso se passando pelo grupo, em segundos. Para quem não deve usar o painel, o próprio cluster emite um certificado de 24 horas preso a um namespace. A receita não depende de workspaces nem de projetos, e por isso serve também a quem tem NKP Pro ou Starter.

O resultado em uma tabela

Pergunta ao clusterAntes do RoleBindingDepois do RoleBinding
A Carla pode criar deployments no namespace do time?noyes
E no default?nono
Pode criar RoleBindings (se dar mais poder)?nono
Pode listar os nós?ForbiddenForbidden
Deploy com 8 réplicas—4 de 8 no ar: o resto barrado pela quota
Pod sem resources declarados—Recebe 50m/64Mi de request e 200m/128Mi de limite do LimitRange
Acesso sem o painel (Davi)Resultado
Certificado emitido pelo clusterCN=davi.local, válido por 24 h
No namespace deleyes: cria e publica
default, Secrets do kube-system, nós, RoleBindingsno em todos

Do login ao acesso por time

O login pelo Entra ID resolve a primeira pergunta: quem é você? O token que chega ao cluster traz o usuário e os grupos dele. A segunda pergunta é do Kubernetes: o que esse grupo pode fazer, e onde?

No artigo do Entra ID, a Carla recebeu acesso por um projeto do NKP, que cria o namespace e os bindings pelo painel. Workspaces e projetos são do NKP Ultimate, a licença do lab. Aqui o mesmo resultado sai com RBAC nativo do Kubernetes: três ou quatro objetos em YAML, que funcionam em qualquer edição, inclusive nos clusters onde o time prefere manter os acessos no Git.

Workspaces e projetos (Ultimate)RBAC nativo (todas as edições)
Onde se configuraPainel do NKP, uma vezYAML com kubectl apply, em cada cluster
Cluster novo herda os acessosSim, o NKP replicaNão: aplica-se o mesmo YAML (ou GitOps)
Namespace, binding e quotaO projeto criaUm arquivo por time
Como manter em ordemPainelYAMLs no Git, com revisão

Como o RBAC funciona: quem, onde e o quê

Toda permissão no Kubernetes é uma frase com três partes:

O RBAC só soma permissões: não existe regra de negar. Se nenhum binding deu, a resposta é Forbidden. Por isso começar sem acesso e liberar o mínimo é o caminho natural.

O Kubernetes já traz quatro papéis prontos, que cobrem a maior parte dos times:

PapelPodeNão podePara quem
viewLer pods, deployments, services e logsLer Secrets; alterar qualquer coisaSuporte, auditoria, devs em produção
editCriar, alterar e apagar quase tudo no namespaceMexer em permissões (Roles e RoleBindings)Devs em homologação
adminTudo do edit + RoleBindings no namespaceAlterar a quota; recursos do clusterLíder técnico do time
cluster-adminTudo, em todos os namespaces—Equipe de plataforma, pouca gente

O papel edit lê Secrets

O papel edit permite ler os Secrets do namespace. Para um time que só deve ver o que roda, view é o ponto de partida; para quem publica, edit no namespace de homologação e view no de produção.

A combinação mais usada para times é uma ClusterRole reutilizável (edit) ligada por um RoleBinding em cada namespace. Um ClusterRoleBinding vale para o cluster inteiro, inclusive para o que for criado depois: fica para a equipe de plataforma.

O que preparar antes

Pré-requisitoPor quêComo conferir
Login com grupos funcionando (artigo do Entra ID)O binding aponta para o grupo que chega no tokenA Carla entra no painel e gera o kubeconfig (Generate Token)
O nome do grupo como chega no tokenNo NKP com Entra, é oidc: + Object ID do grupo, não o nome amigávelkubectl auth whoami com o kubeconfig da Carla
Kubeconfig de admin do clusterQuem cria o namespace, a quota e o bindingkubectl auth can-i '*' '*' = yes
Um kubeconfig por pessoa no terminalO Generate Token grava no kubeconfig ativoexport KUBECONFIG=~/.kube/carla.conf antes de colar os comandos

Boa prática: guarde o grupo numa variável

O Object ID do grupo não precisa aparecer na tela nem no Git em texto puro. No lab, o YAML tem um marcador (__GRUPO_DEVS__) trocado na hora do apply pelo valor guardado num arquivo da bastion. Para conferir que é o mesmo grupo do token: kubectl auth whoami -o jsonpath='{.status.userInfo.groups}' | grep -qF "$GRUPO_DEVS" && echo OK.

1. O espaço do time: namespace, quota e padrão de recursos

Um arquivo por time. A quota é o teto do namespace; o LimitRange dá valores padrão a quem não declarar resources, o que evita que um pod seja recusado pela quota por falta de requests.

apiVersion: v1
kind: Namespace
metadata:
name: time-dev-demo
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: quota-time-dev
namespace: time-dev-demo
spec:
hard:
pods: "4"
requests.cpu: "1"
requests.memory: 1Gi
limits.cpu: "2"
limits.memory: 2Gi
services.loadbalancers: "0" # o time publica por Ingress, sem consumir IP do load balancer
---
apiVersion: v1
kind: LimitRange
metadata:
name: padrao-time-dev
namespace: time-dev-demo
spec:
limits:
- type: Container
defaultRequest: {cpu: 50m, memory: 64Mi} # para quem não declarar requests
default: {cpu: 200m, memory: 128Mi} # para quem não declarar limits
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: devs-edit
namespace: time-dev-demo
roleRef: {apiGroup: rbac.authorization.k8s.io, kind: ClusterRole, name: edit}
subjects:
- {apiGroup: rbac.authorization.k8s.io, kind: Group, name: "__GRUPO_DEVS__"}

Para mostrar o antes e o depois, o admin aplica primeiro tudo menos o RoleBinding:

Terminal window
sed "s|__GRUPO_DEVS__|$GRUPO_DEVS|" time-dev.yaml | yq 'select(.kind != "RoleBinding")' | kubectl apply -f -
kubectl describe resourcequota -n time-dev-demo
namespace/time-dev-demo created
resourcequota/quota-time-dev created
limitrange/padrao-time-dev created
Resource Used Hard
-------- ---- ----
limits.cpu 0 2
limits.memory 0 2Gi
pods 0 4
requests.cpu 0 1
requests.memory 0 1Gi
services.loadbalancers 0 0

2. Autenticada, mas sem acesso

Com o kubeconfig dela, a Carla pergunta ao cluster quem ela é e o que pode fazer:

$ kubectl auth whoami
ATTRIBUTE VALUE
Groups [oidc:<id> system:authenticated]
$ kubectl auth can-i create deployments -n time-dev-demo
no
$ kubectl get pods -n time-dev-demo
Error from server (Forbidden): pods is forbidden: User "[email protected]" cannot list resource "pods" in API group "" in the namespace "time-dev-demo"

O Entra autenticou a Carla, com MFA. O cluster sabe quem ela é e a que grupo pertence, mas nenhum binding deu permissão a esse grupo neste namespace.

3. O acesso para o grupo, testado antes de liberar

O admin aplica o arquivo inteiro, agora com o RoleBinding, e se passa pelo grupo para perguntar ao cluster antes de avisar o time:

Terminal window
sed "s|__GRUPO_DEVS__|$GRUPO_DEVS|" time-dev.yaml | kubectl apply -f -
kubectl auth can-i create deployments -n time-dev-demo --as=teste --as-group="$GRUPO_DEVS"
kubectl auth can-i create deployments -n default --as=teste --as-group="$GRUPO_DEVS"
namespace/time-dev-demo unchanged
resourcequota/quota-time-dev unchanged
limitrange/padrao-time-dev unchanged
rolebinding.rbac.authorization.k8s.io/devs-edit created
yes
no

Boa prática: teste por impersonação antes de entregar

A impersonação (--as e --as-group) responde em segundos se o grupo vai conseguir o que precisa, e só ali. É o mesmo teste que validou os perfis do artigo do Entra ID, e evita descobrir um binding errado pelo chamado do time.

4. A Carla só no namespace dela

$ kubectl auth can-i create deployments -n time-dev-demo
yes
$ kubectl auth can-i create deployments -n default
no
$ kubectl auth can-i create rolebindings -n time-dev-demo
no
$ kubectl get nodes
Error from server (Forbidden): nodes is forbidden: User "[email protected]" cannot list resource "nodes" in API group "" at the cluster scope

Com edit, ela cria no namespace do time, não mexe nos outros, não vê os nós e não consegue se dar mais permissão: o papel não inclui Roles nem RoleBindings. É o menor privilégio funcionando.

5. A quota segurando o consumo

A Carla publica uma aplicação sem declarar resources, e o LimitRange preenche:

Terminal window
kubectl create deployment podinfo --image=ghcr.io/stefanprodan/podinfo:6.7.1 -n time-dev-demo
kubectl -n time-dev-demo rollout status deploy/podinfo
kubectl get pod -n time-dev-demo -o jsonpath='{.items[0].spec.containers[0].resources}'
deployment.apps/podinfo created
Waiting for deployment "podinfo" rollout to finish: 0 of 1 updated replicas are available...
deployment "podinfo" successfully rolled out
{"limits":{"cpu":"200m","memory":"128Mi"},"requests":{"cpu":"50m","memory":"64Mi"}}

Depois pede 8 réplicas, o dobro do que a quota permite:

Terminal window
kubectl scale deployment podinfo --replicas=8 -n time-dev-demo
kubectl get deploy -n time-dev-demo
kubectl get events -n time-dev-demo --field-selector reason=FailedCreate | tail -1
NAME READY UP-TO-DATE AVAILABLE AGE
podinfo 4/8 4 4 17s
15s Warning FailedCreate replicaset/podinfo-7f5848f5f6 Error creating: pods "podinfo-7f5848f5f6-xqgr5" is forbidden: exceeded quota: quota-time-dev, requested: pods=1, used: pods=4, limited: pods=4

O deployment não falha: fica em 4/8, e o motivo está nos eventos, não no deployment. Do lado do admin, a quota mostra o consumo:

Resource Used Hard
-------- ---- ----
limits.cpu 800m 2
limits.memory 512Mi 2Gi
pods 4 4
requests.cpu 200m 1
requests.memory 256Mi 1Gi
services.loadbalancers 0 0

Boa prática: deixe folga na quota para o rollout

Uma atualização do deployment (rolling update) sobe pods novos antes de desligar os antigos. Com a quota de pods no limite, o pod a mais não sobe e a atualização não avança. Dimensione a quota com uma ou duas réplicas de folga, ou ajuste o maxSurge do deployment.

6. Acesso só pelo kubectl, sem o painel

Algumas pessoas não devem entrar no painel do NKP: uma automação, um fornecedor por tempo limitado, um acesso de exceção. Para elas, o próprio Kubernetes emite um certificado de cliente, pela API de CertificateSigningRequest, e o RBAC prende esse usuário a um namespace. Funciona em qualquer edição do NKP, porque é Kubernetes padrão.

O fluxo, em seis passos (o Davi é um usuário local, davi.local):

  1. A chave e o pedido de certificado são gerados com CN=davi.local (o CN vira o nome do usuário).
  2. O admin cria a CertificateSigningRequest com validade de 24 horas e aprova.
  3. O kubeconfig do Davi aponta para o mesmo API server, com o certificado dele.
  4. O Davi autentica, mas ainda sem permissão: can-i = no.
  5. O admin cria o namespace acesso-direto-demo e um RoleBinding edit só para davi.local.
  6. O Davi trabalha no namespace dele e em nenhum outro lugar.
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: davi-local-demo
spec:
request: <CSR em base64>
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 86400 # 24 horas
usages: [client auth]
Terminal window
kubectl certificate approve davi-local-demo
kubectl create rolebinding davi-edit --clusterrole=edit --user=davi.local -n acesso-direto-demo
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
davi-local-demo 1s kubernetes.io/kube-apiserver-client kubernetes-admin 24h Approved,Issued
subject=CN=davi.local
notAfter=Nov 11 18:31:31 2026 GMT
$ kubectl auth whoami # com o kubeconfig do Davi, antes do RoleBinding
ATTRIBUTE VALUE
Username davi.local
Groups [system:authenticated]
can-i create deployments -n acesso-direto-demo: yes
can-i create deployments -n default: no
can-i get secrets -n kube-system: no
can-i list nodes: no
can-i create rolebindings -n acesso-direto-demo: no

Certificado não se revoga

O Kubernetes não tem lista de revogação para certificados de cliente. Para tirar o acesso do Davi antes do vencimento, apaga-se o RoleBinding: o certificado continua autenticando, mas sem permissão nenhuma. Por isso a validade curta (24 horas) e o uso só para exceções. Nunca coloque system:masters no campo de organização (O=) do certificado: esse grupo tem acesso total e não passa pelo RBAC. Em ambiente de produção, aja com cautela.

Para pessoas, no dia a dia, o caminho continua sendo o login do Entra: MFA, desligamento e troca de time ficam num lugar só.

Um modelo de times para começar

Grupos do Entra de um lado, namespaces do outro, papel no meio. Nomes de exemplo:

Grupo no EntraOndePapelVínculo
nkp-plataformaCluster inteirocluster-adminClusterRoleBinding
nkp-dev-sistemaxsistemax-hmleditRoleBinding
nkp-dev-sistemaxsistemax-prdviewRoleBinding
nkp-lider-sistemaxsistemax-hmladminRoleBinding
nkp-suporteNamespaces dos sistemasviewRoleBinding em cada um

Regra de ouro: permissão para grupo, nunca para pessoa. Quem entra ou sai do time muda no Entra, não no cluster.


Os números

MedidaValor
Objetos por time (namespace, quota, LimitRange, RoleBinding)4, num arquivo só
Do namespace criado à Carla trabalhando só no namespace dela2 s
Teste do binding por impersonação2 comandos, resposta no mesmo segundo
Ensaio completo, do namespace à limpeza24 s (com 15 s de espera proposital)
Quota do time de exemplo4 pods · 1 vCPU / 1 GiB de requests · 2 vCPU / 2 GiB de limites
Padrão do LimitRange por container50m / 64Mi de request · 200m / 128Mi de limite
Deploy pedindo 8 réplicas4 no ar, 4 barradas pela quota
Consumo com 4 réplicas200m de CPU e 256Mi de memória em requests (20% e 25% da quota)
Do pedido de certificado ao pod do Davi rodando5 s
Validade do certificado do acesso sem painel24 h
Validade do kubeconfig do Generate Token24 h

O que aprendi

  1. Login e permissão são camadas separadas. O Entra autentica; o RoleBinding autoriza. A Carla autenticada e barrada no passo 2 é o comportamento esperado.
  2. O grupo do binding tem de ser o grupo do token. No NKP com Entra, é oidc:<Object ID>. Confira com kubectl auth whoami antes de escrever o YAML.
  3. Teste por impersonação antes de liberar. --as-group responde em segundos e mostra também o que o time não pode.
  4. edit é o papel certo para devs em homologação, e não deixa o time ampliar o próprio acesso. Em produção, view.
  5. Quota sem LimitRange recusa pods sem resources. O LimitRange dá o padrão e deixa a quota fazer o trabalho dela.
  6. O erro de quota aparece nos eventos, não no deployment. kubectl get events --field-selector reason=FailedCreate é o primeiro lugar a olhar.
  7. Acesso por certificado é exceção, com validade curta. Tirar o acesso é apagar o binding; o certificado só para de autenticar quando vence.

Checklist: um namespace por time

  • Login com grupos funcionando e o nome do grupo conferido no token (kubectl auth whoami)
  • Um namespace por sistema e por ambiente (-hml, -prd)
  • RoleBinding para o grupo, nunca para a pessoa
  • edit em homologação, view em produção, admin só para o líder técnico
  • ResourceQuota em todo namespace de time, com folga para o rollout
  • LimitRange com requests e limits padrão
  • Binding testado com kubectl auth can-i --as-group antes de avisar o time
  • Um YAML por time no Git, com revisão
  • Acesso por certificado só para exceção: 24 h, nunca system:masters, binding apagado ao fim
  • Revisão periódica: quem tem cluster-admin hoje?

O que ainda não testei

  • Usuário do Entra só pelo kubectl, com login pelo navegador direto do terminal (kubelogin pelo Dex), sem passar pelo painel do NKP.
  • Os mesmos YAMLs aplicados por GitOps em vários clusters de uma vez.
  • Quotas calibradas pelo consumo real depois de 30 dias de uso de um time.
  • Um cluster com licença Pro ou Starter. A receita usa só objetos nativos do Kubernetes, que não dependem da edição, mas foi executada no lab, com licença Ultimate.

Referências

Nutanix

Projetos e comunidade

Comentários

Os comentários usam o GitHub Discussions; é preciso ter uma conta no GitHub.