Autorização por rota no Linkerd: mTLS ligado não é tráfego autorizado
Injetar o proxy do Linkerd num Deployment te dá mTLS automático entre todos os pods do mesh. Isso costuma ser interpretado como “já está seguro” — mas mTLS resolve só uma pergunta: quem está falando comigo é quem diz que é. Não resolve a pergunta seguinte, que é esse alguém tem permissão pra falar comigo nessa rota. Essa segunda pergunta é trabalho de outro conjunto de CRDs: Server, MeshTLSAuthentication e AuthorizationPolicy.
O padrão abaixo é o que a própria documentação do projeto recomenda para restringir acesso — não é relato de “rodei isso em produção”, é a forma oficial de compor essas três CRDs (mais HTTPRoute, quando a granularidade precisa ser por caminho, não só por porta). Testei o schema direto no chart linkerd-crds do repositório do projeto pra garantir que os campos abaixo batem com a versão atual — sem inventar enum ou flag que eu não vi na fonte.
As três peças
Server (policy.linkerd.io/v1beta3) declara uma porta de um Deployment como alvo de política. O campo que muda o jogo é accessPolicy, que hoje tem deny como valor default: se o tráfego não casar com nenhuma regra de AuthorizationPolicy, ele é recusado — não fica liberado por omissão.
apiVersion: policy.linkerd.io/v1beta3
kind: Server
metadata:
name: billing-api
namespace: billing
spec:
podSelector:
matchLabels:
app: billing-api
port: http
# sem regra correspondente, a requisição é negada
accessPolicy: deny
MeshTLSAuthentication (policy.linkerd.io/v1alpha1) descreve quem está autorizado, usando a identidade mTLS do proxy de origem — não IP, não header, a identidade criptográfica que o Linkerd já verificou no handshake. Você referencia por string de identidade completa ou por ServiceAccount:
apiVersion: policy.linkerd.io/v1alpha1
kind: MeshTLSAuthentication
metadata:
name: checkout-identity
namespace: billing
spec:
identityRefs:
- kind: ServiceAccount
name: checkout
namespace: storefront
AuthorizationPolicy (policy.linkerd.io/v1alpha1) é quem liga as duas pontas: um targetRef (o que está protegido) e um ou mais requiredAuthenticationRefs (quem pode acessar). Se houver mais de uma referência de autenticação, todas precisam ser satisfeitas — não é “qualquer uma serve”.
apiVersion: policy.linkerd.io/v1alpha1
kind: AuthorizationPolicy
metadata:
name: billing-api-checkout-only
namespace: billing
spec:
targetRef:
kind: Server
name: billing-api
requiredAuthenticationRefs:
- kind: MeshTLSAuthentication
name: checkout-identity
Com essas três peças, só o pod rodando com a ServiceAccount checkout no namespace storefront consegue falar com billing-api, em qualquer rota daquela porta. Qualquer outro pod do mesh — mesmo com mTLS válido, mesmo no mesmo namespace — recebe conexão recusada.
Granularidade por rota, não só por porta
O ponto acima protege a porta inteira. Na prática, é comum precisar de granularidade menor: checkout pode chamar POST /orders, mas /admin/refund só pode ser chamado por um serviço interno de operações. Pra isso, o targetRef do AuthorizationPolicy não precisa apontar pro Server — pode apontar pra um HTTPRoute.
O Linkerd aceita tanto o HTTPRoute próprio (policy.linkerd.io) quanto o HTTPRoute padrão da Gateway API (gateway.networking.k8s.io). Se você já usa Gateway API para roteamento (como a maioria dos clusters recentes), reaproveita o mesmo recurso como alvo de autorização — não precisa manter dois objetos de rota:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: billing-admin-route
namespace: billing
spec:
parentRefs:
- name: billing-api
kind: Server
group: policy.linkerd.io
rules:
- matches:
- path:
type: PathPrefix
value: /admin
apiVersion: policy.linkerd.io/v1alpha1
kind: AuthorizationPolicy
metadata:
name: billing-admin-restricted
namespace: billing
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: billing-admin-route
requiredAuthenticationRefs:
- kind: MeshTLSAuthentication
name: ops-identity
Com isso, /admin/* fica restrito à identidade ops-identity, enquanto o resto das rotas de billing-api segue liberado pra quem satisfizer o AuthorizationPolicy de nível de Server. As duas políticas convivem: a mais específica (rota) governa o que ela cobre, a mais geral (porta) cobre o restante.
O que isso não substitui
AuthorizationPolicy opera em L7, com identidade criptográfica — é um controle diferente (e complementar) de NetworkPolicy, que opera em L3/L4 por IP/CIDR e não depende do Linkerd estar rodando. Um ataque que comprometa o nó ou contorne o proxy sidecar não é coberto pela política de autorização do mesh. Continue usando NetworkPolicy como camada de isolamento de rede; o AuthorizationPolicy entra por cima, como controle de identidade de serviço para serviço, inclusive por rota.
Também vale registrar: a mudança de default para accessPolicy: deny é comportamento atual do Server v1beta3 (a versão storage: true no CRD hoje). Se você tem Server de uma versão anterior do cluster com outro default, confira o accessPolicy explicitamente antes de assumir que “sem AuthorizationPolicy” significa “bloqueado” — não assuma, leia o manifesto aplicado.
Quando aplicar esse padrão
Faz sentido priorizar isso em: serviços com endpoint administrativo/destrutivo que não deveria estar acessível por qualquer coisa autenticada no mesh; ambientes multi-tenant onde “estar no cluster” não deveria implicar “poder chamar qualquer serviço”; e qualquer rota que hoje só está protegida por “ninguém mais sabe que ela existe” — segurança por obscuridade que uma varredura de service discovery derruba em minutos.
Não é o primeiro passo se o cluster ainda não tem mTLS habilitado de forma consistente (a injeção do proxy precisa estar em todo pod relevante) nem se você ainda não mapeou quem-chama-quem — aplicar accessPolicy: deny sem esse mapa quebra tráfego legítimo. Comece com Server + AuthorizationPolicy nos serviços de maior risco, valide em um ambiente de homologação observando erros de conexão recusada, e só depois expanda pra granularidade de rota.
Fontes
- linkerd/linkerd2 — CRD
AuthorizationPolicy(charts/linkerd-crds/templates/policy/authorization-policy.yaml) - linkerd/linkerd2 — CRD
Server(charts/linkerd-crds/templates/policy/server.yaml) - linkerd/linkerd2 — CRD
MeshTLSAuthentication(charts/linkerd-crds/templates/policy/meshtls-authentication.yaml) - linkerd/linkerd2 — CRD
HTTPRouteprópria do Linkerd (charts/linkerd-crds/templates/policy/httproute.yaml) - linkerd/linkerd2 — CHANGES.md, suporte a HTTPRoute da Gateway API como alvo de política
- Gateway API — especificação HTTPRoute