Pinniped: Autenticação Federada para Clusters Kubernetes

Gerir quem acede aos clusters Kubernetes é um dos maiores desafios de segurança em infraestruturas modernas. O Pinniped, projecto open source apoiado pela VMware, resolve este problema ao federar a autenticação com Fornecedores de Identidade existentes — Entra ID, Okta, Kanidm ou LDAP — eliminando certificados de cliente longevos e trazendo SSO com códigos de acesso de curta duração ao kubectl.

ℹ Para quem é este artigo: Administradores Kubernetes e equipas de segurança em PMEs que precisam de integrar clusters com identidade corporativa (Entra ID, Okta, Kanidm) sem gerir certificados de cliente manualmente.

Neste artigo

1. Introdução — O problema de acesso ao Kubernetes

Os DN, filtros e atributos LDAP dependem da configuração concreta de cada directório — os do exemplo seguem o Kanidm, mas valide sempre com ldapsearch contra o vosso servidor antes de aplicar os manifestos (o Kanidm documenta os filtros recomendados e os atributos expostos na interface LDAP).

O modelo tradicional de autenticação humana no Kubernetes baseia-se em certificados de cliente X.509 emitidos pelo cluster. Cada utilizador ou serviço que precise de aceder ao API server recebe um certificado assinado pela CA do cluster (o Kubernetes suporta outros mecanismos — ServiceAccount tokens, OIDC no API server, webhooks de autenticação — mas em PMEs é o modelo de certificados que domina na prática). Este modelo tem três problemas fundamentais:

Certificados longevos sem revogação. A autenticação por certificados de cliente não tem revogação prática no Kubernetes (sem CRL/OCSP para certificados de utilizador). Um certificado de cliente válido até 2028 continua a funcionar mesmo depois de o utilizador deixar a empresa, e as saídas limitam-se a recriar a CA ou recorrer a alternativas de autenticação — ambas operações disruptivas.

Gestão manual descentralizada. Cada cluster mantém a sua própria lista de certificados. Numa PME com 3 clusters de desenvolvimento, pré-produção e produção, um administrador que saia da empresa exige revogação manual em cada cluster — frequentemente esquecida.

Ausência de SSO. O kubectl não fala directamente com Entra ID, Okta ou qualquer IdP OIDC — suporta plugins de credenciais (exec), mas nenhum mecanismo nativo de federação. As equipas acabam por partilhar kubeconfig files com credenciais embutidas, violando princípios básicos de segregação de acesso.

O Pinniped resolve estes três problemas ao funcionar como intermediário entre o kubectl e o IdP corporativo, emitindo credenciais de curta duração (o login inicial consome-se em cerca de 19 minutos e as sessões continuam via refresh token de 9 horas, conforme as durações fixas documentadas) que se renovam automaticamente a partir de uma sessão SSO válida.

2. O que é o Pinniped

O Pinniped é um serviço de autenticação federada de código aberto para Kubernetes, um projecto apoiado pela VMware, licenciado sob Apache 2.0 e alojado na organização vmware no GitHub. O nome é o termo inglês para pinípede (focas e leões-marinhos).

Característica Descrição
Manutenção e licença VMware-backed · Apache 2.0
Licença Apache 2.0
IdPs suportados Entra ID (Azure AD), Okta, GitHub, LDAP, Active Directory (via LDAP), qualquer IdP OIDC
Modelo de códigos Durações fixas: auth code 10 min, ID/access token 2 min, credencial de cluster 5 min, refresh 9 h
Instalação kubectl apply dos YAMLs oficiais (get.pinniped.dev), sem dependências externas
Cliente Plugin kubectl (pinniped-cli), sem alterações ao kubectl

A vantagem principal para PMEs é a reutilização da identidade corporativa. Em vez de criar utilizadores Kubernetes independentes, o Pinniped delega a autenticação no IdP que a empresa já usa. Quando um colaborador é desactivado no Entra ID ou Kanidm, a mudança propaga-se para os clusters federados no ciclo seguinte de renovação — tipicamente em poucos minutos (a doc oficial indica cerca de 5 minutos, com um pior caso de 19 num login inicial) — sem revogar certificados manualmente.

O Pinniped suporta dois tipos de IdP: OIDC (Entra ID, Okta, qualquer fornecedor OAuth 2.0/OIDC) e LDAP (directório tradicional, Kanidm em modo LDAP). A configuração é feita via Custom Resource Definitions (CRDs) no cluster, sem necessidade de recompilar ou modificar o Kubernetes.

3. Arquitectura e Componentes

O Pinniped tem uma arquitectura de três componentes que funcionam em conjunto para fornecer autenticação federada:

3.1 Supervisor

O Pinniped Supervisor é o componente central. Corre dentro do cluster Kubernetes como Deployment e expõe um endpoint de autenticação. É responsável por:

  • Comunicar com o IdP externo (Entra ID, Okta, LDAP) via OIDC ou LDAP bind
  • Validar as credenciais do utilizador contra o IdP
  • Emitir tokens OIDC de curta duração: um ID token (JWT), um access token opaco e um refresh token opaco
  • Trocar o access token por um ID token limitado a um cluster (RFC 8693) e renovar a sessão via refresh, sem nova autenticação do utilizador

3.2 Concierge

O Pinniped Concierge é o componente que corre em cada cluster alvo (onde os utilizadores querem executar kubectl). Funciona como API de troca de credenciais (TokenCredentialRequest): o CLI envia o ID token limitado ao cluster, o Concierge valida-o (via JWTAuthenticator) e devolve um certificado cliente mTLS de curta duração — é esse certificado que o kubectl usa nos pedidos ao API server.

3.3 Pinniped CLI

O pinniped-cli é um plugin de kubectl que o utilizador instala na sua máquina. Gere o fluxo de autenticação: abre o navegador para iniciar sessão no IdP, recebe o código de autorização, troca-o por tokens do Supervisor, pede ao Supervisor um ID token limitado ao cluster (RFC 8693) e obtém do Concierge o certificado cliente mTLS que o kubeconfig usa via TokenCredentialRequest.

ℹ O fluxo de autenticação é transparente para o utilizador: após executar pinniped login uma vez, o kubectl funciona normalmente. Os códigos de acesso são renovados em background sem intervenção do utilizador, enquanto a sessão SSO for válida.

O diagrama seguinte ilustra o fluxo de autenticação completo:

Utilizador       Pinniped CLI      Supervisor        IdP (Entra ID)     Concierge      K8s API
    |                |                  |                  |                 |              |
    | kubectl get pods|                  |                  |                 |              |
    |--------------->|                  |                  |                 |              |
    |                | sem código válido |                  |                 |              |
    |                |----------------->|                  |                 |              |
    |                |                  | redirect OIDC    |                 |              |
    |                |<-----------------|                  |                 |              |
    | abrir navegador|                  |                  |                 |              |
    |<---------------|                  |                  |                 |              |
    | login (user+MFA)                  |                  |                 |              |
    |----------------------------------------------------->|                 |              |
    | código autorização                |                  |                 |              |
    |<-----------------------------------------------------|                 |              |
    |                | exchange code     |                  |                 |              |
    |                |----------------->|                  |                 |              |
    |                |                  | tokens OIDC      |                 |              |
    |                |                  |----------------->|                 |              |
    |                |                  |<-----------------|                 |              |
    |                | ID + access token |                  |                 |              |
    |                |<-----------------|                  |                 |              |
    |                | ID token cluster-scoped +         |                  |                 |              |
    |                | TokenCredentialRequest            |                  |                 |              |
    |                |------------------------------------------------->|              |
    |                |       certificado cliente mTLS (5 min)           |              |
    |                |<-------------------------------------------------|              |
    |                | pedidos API com cert mTLS                         |              |
    |                |------------------------------------------------->|------------>|
    |                |<-------------------------------------------------|              |
    | pods list      |                  |                  |                 |              |
    |<---------------|                  |                  |                 |              |

4. Configuração com Entra ID

A configuração com Microsoft Entra ID (antigo Azure AD) é o cenário mais comum em PMEs que já usam Microsoft 365. O processo envolve três passos: registar uma aplicação no Entra ID, instalar o Pinniped no cluster, e configurar o Supervisor com as credenciais da aplicação.

4.1 Registo da aplicação no Entra ID

No portal do Entra ID (entra.microsoft.com), criar um novo registo de aplicação com:

  • Nome: Pinniped Kubernetes Auth
  • Tipo de conta: Contas neste directório organizacional apenas
  • URI de redireccionamento: https://<supervisor-endpoint>/callback

Anotar o Application (client) ID e o Directory (tenant) ID. Criar um secret de cliente e anotar o valor. Estes três valores são necessários para configurar o Supervisor.

4.2 Instalação do Pinniped no cluster

Instalar o Supervisor e o Concierge via YAML oficial (método único documentado):

# Instalar o Supervisor (YAML oficial da release; substituir pela versão desejada)
kubectl apply -f https://get.pinniped.dev/v0.47.0/install-pinniped-supervisor.yaml

# Instalar o Concierge no cluster alvo
kubectl apply -f https://get.pinniped.dev/v0.47.0/install-pinniped-concierge.yaml

# Instalar o CLI localmente (tap oficial)
brew install vmware/pinniped/pinniped-cli   # macOS/Linux
# ou: binários em https://github.com/vmware/pinniped/releases

4.3 Configuração do Supervisor com Entra ID

Criar um Secret com as credenciais do Entra ID e uma FederationDomain que referencia o IdP OIDC:

4.3.1 Criar a aplicação (App Registration) no Entra ID

Antes dos YAMLs, criar a aplicação no portal do Entra ID (os passos seguintes seguem a documentação oficial do Pinniped para Azure AD):

1. No portal do Entra ID (portal.azure.com), navegar para Identidade > Aplicações > Registos de aplicações (App registrations) e clicar em Novo registo (New registration).

2. Em Tipos de contas suportadas, escolher Contas apenas nesta diretoria organizacional (inquélinos únicos) e dar um nome à aplicação, por exemplo Pinniped Supervisor.

3. Em URI de redirecionamento, selecionar a plataforma Web e colar https://pinniped-supervisor.kbase.pt/callback — é o spec.issuer da FederationDomain com /callback no fim.

4. Clicar em Registar. Na página de descrição geral (Overview), copiar o ID da aplicação (cliente) (Application (client) ID) — vai ser usado no Secret clientID abaixo.

5. Em Certificados e segredos (Certificates & secrets), criar um Segredo do cliente (New client secret) e copiar o Valor imediatamente (só é visível nesse momento) — vai ser usado no Secret clientSecret abaixo.

6. Em Pontos de extremidade (Endpoints), copiar o URL do Documento de metadados OpenID Connect e consultar curl "https://login.microsoftonline.com/<tenant>/v2.0/.well-known/openid-configuration" | jq .issuer — o valor devolvido é o issuer do OIDCIdentityProvider abaixo.

No final, a aplicação tem de ter o redirect URI apontado ao endpoint do Supervisor; é lá que o Pinniped devolve o utilizador depois da autenticação no Entra ID.

# Secret com client ID e client secret do Entra ID
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: entra-id-client-credentials
  namespace: pinniped-supervisor
type: secrets.pinniped.dev/oidc-client
stringData:
  clientID: "11111111-2222-3333-4444-555555555555"
  clientSecret: "client-secret-value-here"
EOF

# Configurar o IdP OIDC no Supervisor
cat <<EOF | kubectl apply -f -
apiVersion: idp.supervisor.pinniped.dev/v1alpha1
kind: OIDCIdentityProvider
metadata:
  name: entra-id
  namespace: pinniped-supervisor
spec:
  issuer: "https://login.microsoftonline.com/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/v2.0"
  authorizationConfig:
    additionalScopes: ["offline_access", "groups"]
  claims:
    groups: "groups"
    username: "preferred_username"
  client:
    secretName: entra-id-client-credentials
EOF

# Criar FederationDomain (expõe o endpoint do Supervisor)
cat <<EOF | kubectl apply -f -
apiVersion: config.supervisor.pinniped.dev/v1alpha1
kind: FederationDomain
metadata:
  name: kbase-federation
  namespace: pinniped-supervisor
spec:
  issuer: "https://pinniped-supervisor.kbase.pt"
  tls:
    secretName: pinniped-supervisor-tls   # Secret tipo kubernetes.io/tls (tls.crt/tls.key)
  identityProviders:
    - displayName: "Entra ID Corporativo"
      objectRef:
        apiGroup: idp.supervisor.pinniped.dev
        kind: OIDCIdentityProvider
        name: entra-id
EOF

⚠ A claim groups não é automática no Entra ID. A emissão de grupos tem de estar configurada na App Registration (Token configuration / group claims) e há um limite de 200 grupos por JWT (150 em SAML). Utilizadores com muitas memberships recebem um indicador de overage (hasgroups ou _claim_names) em vez da lista — e o Supervisor não segue o Graph API. Em PME raramente se atinge o limite, mas vale validar com um utilizador real e, em organizações maiores, restringir os grupos emitidos aos atribuídos à aplicação.

⚠ O issuer do FederationDomain deve corresponder ao URL onde o Supervisor está acessível. Se o cluster estiver atrás de um ingress ou load balancer, garantir que o TLS está configurado correctamente — o Pinniped exige HTTPS para o endpoint do Supervisor. O Secret pinniped-supervisor-tls referenciado acima tem de existir no namespace com as chaves tls.crt e tls.key (o demo oficial usa cert-manager para o criar).

4.4 Configuração do Concierge e kubeconfig

Após o Supervisor estar configurado, ligar o Concierge ao Supervisor e gerar o kubeconfig para os utilizadores:

# Configurar o Concierge para confiar no Supervisor
cat <<EOF | kubectl apply -f -
apiVersion: authentication.concierge.pinniped.dev/v1alpha1
kind: JWTAuthenticator
metadata:
  name: pinniped-supervisor
spec:
  issuer: "https://pinniped-supervisor.kbase.pt"
  audience: "kbase-k8s"
  tls:
    certificateAuthorityData: "$(kubectl get secret -n pinniped-supervisor \
      pinniped-supervisor-tls -o jsonpath='{.data.ca\.crt}' | base64 -d | base64 -w0)"
EOF

# Gerar kubeconfig para os utilizadores utilizarem
pinniped get kubeconfig \\
  --kubeconfig ~/.kube/config-prod \\
  > pinniped-kubeconfig.yaml

# Distribuir pinniped-kubeconfig.yaml aos utilizadores.
# Cada utilizador executa:
KUBECONFIG=pinniped-kubeconfig.yaml kubectl get pods
# Isto abre o navegador para login no Entra ID.

A partir deste momento, o kubectl usa o Pinniped para autenticar. O utilizador inicia sessão uma vez no navegador, recebe tokens de curta duração que o Pinniped renova automaticamente enquanto a sessão for válida.

5. Configuração com Kanidm/LDAP

Para PMEs que não usam Entra ID — tipicamente infraestruturas auto-alojadas com Kanidm ou LDAP tradicional — o Pinniped suporta autenticação via LDAP bind. Esta secção cobre a configuração com Kanidm em modo LDAP, que é o cenário mais comum em PMEs que preferem soluções de código aberto auto-alojadas.

5.1 Configuração LDAP com Kanidm

O Kanidm expõe uma interface LDAP compatível — a porta é configurável no server.toml (3636 é o padrão da documentação) e o LDAP reutiliza o material TLS do servidor web. Criar um LDAPIdentityProvider no Supervisor:

# Secret com bind credentials para pesquisa LDAP no Kanidm
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: kanidm-bind-credentials
  namespace: pinniped-supervisor
type: kubernetes.io/basic-auth
stringData:
  # bind DN Kanidm: o atributo único serve de DN (name=, spn=, mail=…)
  username: "name=pinniped-bind,dc=kbase,dc=pt"
  password: "bind-password-here"
EOF

# Configurar LDAPIdentityProvider
cat <<EOF | kubectl apply -f -
apiVersion: idp.supervisor.pinniped.dev/v1alpha1
kind: LDAPIdentityProvider
metadata:
  name: kanidm-ldap
  namespace: pinniped-supervisor
spec:
  host: "kanidm.kbase.pt:3636"
  tls:
    caData: "$(base64 -w0 < kanidm-ca.crt)"
  bind:
    secretName: kanidm-bind-credentials
  # Atenção: Kanidm não usa árvores ou=people/ou=groups — as entradas
  # vivem directamente no base DN (ex.: spn=user@domain,dc=dom,dc=pt).
  # Os filtros seguem a recomendação oficial do Kanidm.
  userSearch:
    base: "dc=kbase,dc=pt"
    filter: "(&(class=account)(name={}))"
    attributes:
      username: "name"
      uid: "entryuuid"
  groupSearch:
    base: "dc=kbase,dc=pt"
    filter: "(&(class=group)(member={}))"
    attributes:
      groupName: "name"
EOF

ℹ O Kanidm suporta também OIDC nativo. Se preferir usar OIDC em vez de LDAP, configure um OIDCIdentityProvider com o issuer do Kanidm (https://kanidm.kbase.pt/oauth2/openid/pinniped). O OIDC oferece melhor rastreabilidade de sessões e suporte a MFA nativo.

5.2 Mapeamento de grupos

O Pinniped mapeia automaticamente os grupos do IdP para grupos Kubernetes. Isto permite usar RoleBindings e ClusterRoleBindings baseados em grupos do directório, em vez de utilizadores individuais:

# RoleBinding baseado em grupo do Kanidm/Entra ID
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: k8s-admins
subjects:
- kind: Group
  name: "k8s-admins"      # grupo no Kanidm ou Entra ID
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io
EOF

Quando um utilizador do grupo k8s-admins no Kanidm inicia sessão via Pinniped, o ID token federado inclui os grupos, e o Kubernetes aplica as permissões definidas no ClusterRoleBinding via RBAC.

6. Boas Práticas de Segurança

A implementação do Pinniped deve seguir boas práticas de segurança para garantir que a federação de identidade não introduz novos vectores de ataque:

Prática Recomendação Risco se ignorada
Duração dos códigos Fixas no Supervisor (10 min auth code, 2 min tokens, 9 h refresh) — planear à volta delas Sessões mal dimensionadas se assumir valores configuráveis
MFA no IdP Activar MFA obrigatório no Entra ID/Kanidm Password comprometida dá acesso ao cluster
TLS no Supervisor Usar certificados válidos, não self-signed MITM intercepta códigos de acesso
RBAC por grupos Usar grupos do IdP, não utilizadores Permissões não acompanham saída da empresa
Isolamento do Supervisor Namespace dedicado, NetworkPolicy restrictiva Comprometimento do Supervisor afecta todos os clusters
Registos de auditoria Activar audit logging no Concierge e Supervisor Sem rastreabilidade de quem acedeu ao cluster

6.1 Duração dos tokens e credenciais (fixa, não configurável)

As durações de todos os tokens e credenciais do Pinniped são fixas no código do Supervisor e não são configuráveis — nenhum campo do FederationDomain as altera. Para um login inicial, a cadeia é: código de autorização válido 10 minutos, ID token e access token 2 minutos cada, e a credencial de cluster (certificado cliente mTLS emitido pelo Concierge) 5 minutos — cerca de 19 minutos no total para consumir o login. O refresh token tem 9 horas de validade a partir da autenticação inicial e é ele que sustenta as sessões contínuas do kubectl.

A única excepção à regra: clientes OIDC personalizados (CRD OIDCClient) podem definir spec.tokenLifetimes.idTokenSeconds para ajustar a duração dos ID tokens emitidos a esse cliente — disponível desde a v0.30.0, e não aplicável ao pinniped-cli, que usa sempre o cliente pinniped-cli fixo. Verificar a duração corrente nos logs do Supervisor ou nos eventos do cluster; planejar o sizing de sessões à volta dos valores fixos acima (documentação oficial de tokens e credenciais).

6.2 NetworkPolicy para isolamento

O Supervisor tem de ser alcançável pelo exterior — é o endpoint OIDC onde os navegadores e o CLI terminam o login — por isso a política de rede correcta não o isola dos utilizadores: recebe tráfego externo via Ingress/Load Balancer e liga-se ao IdP externo. Uma NetworkPolicy Kubernetes padrão não filtra por FQDN (ex.: login.microsoftonline.com); se o CNI suportar políticas FQDN (Cilium), restringe-se o egress 443 ao endpoint real do IdP; sem esse suporte, o egress 443 fica aberto e o controlo faz-se na firewall de saída. O que a NetworkPolicy padrão garante é o ingress de gestão:

cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: pinniped-supervisor-protect
  namespace: pinniped-supervisor
spec:
  podSelector:
    matchLabels:
      app: pinniped-supervisor
  policyTypes:
  - Egress
  egress:
  # DNS para resolver o endpoint do IdP
  - to:
    - namespaceSelector: {}
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53
  # HTTPS de saída (IdP externo) — sem CNI com FQDN policies,
  # o destino não se restringe a um domínio ao nível da NetworkPolicy
  - to: []
    ports:
    - protocol: TCP
      port: 443
EOF
# O ingress do Supervisor fica exposto via Ingress/LoadBalancer —
# não aplicar ingress policy que bloqueie os utilizadores.
# O Concierge não precisa de ligar ao Supervisor (o fluxo é CLI→Concierge).

7. Conclusão

O Pinniped resolve um problema real e doloroso na gestão de clusters Kubernetes: a autenticação de utilizadores com certificados de cliente longevos e sem revogação. Ao federar a autenticação com IdPs corporativos (Entra ID, Okta, Kanidm, LDAP), as PMEs obtêm três benefícios concretos: códigos de acesso de curta duração que expiram automaticamente, SSO que elimina credenciais redundantes, e revogação centralizada — quando um utilizador é desactivado no IdP, perde acesso a todos os clusters federados.

A implementação é relativamente simples — três componentes (Supervisor, Concierge, CLI) configurados via CRDs — e não requer alterações ao código do Kubernetes ou ao kubectl. Para PMEs que já usam Entra ID ou Kanidm, o Pinniped é a forma mais directa de aplicar políticas de identidade corporativa ao acesso Kubernetes, alinhando-se com princípios Zero Trust sem custo de licenciamento.

Artigos relacionados:

Fontes oficiais: pinniped.dev · GitHub: vmware/pinniped · Documentação Pinniped