Boas práticas de autenticação service-to-service com mTLS

1. Fundamentos do mTLS para comunicação entre serviços

O Mutual Transport Layer Security (mTLS) é uma extensão do protocolo TLS que estabelece autenticação bidirecional entre serviços. Diferentemente do TLS tradicional, onde apenas o servidor apresenta um certificado para validação pelo cliente, no mTLS ambos os lados da conexão devem apresentar e validar certificados mutuamente.

A cadeia de confiança no mTLS é composta por três elementos principais: uma Autoridade Certificadora (CA) raiz que emite certificados intermediários, certificados de servidor que identificam o serviço receptor, e certificados de cliente que autenticam o serviço originador. Durante o handshake mTLS, ocorre a seguinte sequência:

Cliente                          Servidor
   |                                |
   |--- ClientHello --------------->|
   |<--- ServerHello + Cert -------|
   |<--- CertificateRequest -------|
   |--- ClientCertificate -------->|
   |--- ClientVerify -------------->|
   |--- ChangeCipherSpec --------->|
   |--- Finished ----------------->|
   |<--- ChangeCipherSpec ---------|
   |<--- Finished -----------------|
   |                                |
   |<====== Canal Criptografado ===>|

Cada serviço verifica se o certificado recebido foi assinado por uma CA confiável, se não está expirado e se o CN (Common Name) ou SAN (Subject Alternative Name) corresponde à identidade esperada.

2. Planejamento da infraestrutura de PKI

Para ambientes de produção, a escolha entre CA interna e CA pública depende do contexto. CAs internas como HashiCorp Vault ou cert-manager no Kubernetes oferecem controle total sobre o ciclo de vida dos certificados, enquanto CAs públicas (Let's Encrypt, DigiCert) são mais adequadas para serviços expostos externamente.

O ciclo de vida do certificado deve ser gerenciado com automação:

# Exemplo de política de ciclo de vida
- Emissão: Automática via cert-manager ou Vault PKI
- Renovação: 30 dias antes da expiração (para certificados de 90 dias)
- Revogação: Imediata em caso de comprometimento
- Rotação: Chaves regeneradas a cada renovação

Para identificação de serviços, adote uma estrutura de nomes padronizada:

CN: service-name.namespace.svc.cluster.local
SANs:
  - DNS: service-name.namespace.svc.cluster.local
  - DNS: service-name.namespace.svc
  - DNS: service-name

3. Distribuição segura e armazenamento de certificados

A distribuição de certificados deve seguir o princípio do menor privilégio. No Kubernetes, utilize Secrets montados como volumes ou injetados via CSI drivers:

apiVersion: v1
kind: Secret
metadata:
  name: mTLS-certificates
  namespace: production
type: kubernetes.io/tls
data:
  tls.crt: <certificado-base64>
  tls.key: <chave-privada-base64>
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
spec:
  template:
    spec:
      containers:
      - name: app
        volumeMounts:
        - name: mTLS-certs
          mountPath: /etc/mTLS/certs
          readOnly: true
      volumes:
      - name: mTLS-certs
        secret:
          secretName: mTLS-certificates
          defaultMode: 0400

Para proteção de chaves privadas, implemente:
- Permissões de arquivo 0400 (apenas leitura para o proprietário)
- Criptografia em repouso usando KMS ou Vault Transit
- Rotação automática a cada 30-90 dias
- Armazenamento em HSM (Hardware Security Module) para ambientes críticos

4. Validação e controle de acesso no mTLS

A validação estrita de identidade deve verificar se o CN/SAN corresponde exatamente ao serviço esperado:

# Exemplo de validação em Go
func validatePeerCertificate(conn *tls.Conn, expectedService string) error {
    certs := conn.ConnectionState().PeerCertificates
    if len(certs) == 0 {
        return fmt.Errorf("nenhum certificado apresentado")
    }

    // Verificar CN
    if certs[0].Subject.CommonName != expectedService {
        return fmt.Errorf("CN inválido: esperado %s, recebido %s", 
            expectedService, certs[0].Subject.CommonName)
    }

    // Verificar SANs
    for _, san := range certs[0].DNSNames {
        if san == expectedService {
            return nil
        }
    }

    return fmt.Errorf("SAN não encontrada para %s", expectedService)
}

Para revogação, prefira OCSP (Online Certificate Status Protocol) sobre CRLs (Certificate Revocation Lists), pois oferece verificação em tempo real:

# Configuração de OCSP stapling no NGINX
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/mTLS/ca-chain.crt;
resolver 8.8.8.8 valid=300s;

Combine mTLS com RBAC baseado em claims do certificado:

# Mapeamento de certificado para permissões
{
  "service-a.production.svc": ["read:payments", "write:logs"],
  "service-b.production.svc": ["read:payments"],
  "monitoring.production.svc": ["read:*"]
}

5. Performance e resiliência da autenticação mTLS

O handshake mTLS adiciona latência devido à troca de certificados e verificação criptográfica. Para mitigar esse impacto:

# Configuração de keep-alive e reutilização de sessão
TLSConfig:
  MinVersion: TLSv1.3
  SessionTicketsDisabled: false
  SessionCache:
    Capacity: 10000
    Expiration: 3600  # 1 hora
  HandshakeTimeout: 5s
  KeepAlive:
    Enabled: true
    Interval: 30s
    Timeout: 10s
    MaxIdleConnections: 100

Implemente renovação proativa para evitar falhas de expiração:

# Algoritmo de renovação antecipada
if daysUntilExpiration <= 30:
    iniciarRenovacao()
    if renovacaoFalhou:
        manterCertificadoAtual
        agendarRetry(5 minutos)
    elif diasRestantes == 0:
        emitirAlertaCritico()
        usarFallbackSeguro()

6. Monitoramento e operação contínua

Adote métricas RED (Rate, Errors, Duration) específicas para mTLS:

# Métricas essenciais
mtls_handshake_total{status="success"}  # Taxa de handshakes bem-sucedidos
mtls_handshake_errors_total{reason="expired"}  # Erros por certificado expirado
mtls_handshake_duration_seconds  # Latência do handshake
mtls_certificate_expiry_days  # Dias até expiração
mtls_revocation_checks_total  # Verificações de revogação

Configure alertas para:
- Certificados com menos de 14 dias para expiração (warning)
- Certificados com menos de 7 dias para expiração (critical)
- Taxa de erros de handshake > 1% em 5 minutos
- Falhas de verificação OCSP/CRL

Logs de auditoria devem registrar:

2025-01-15T10:30:00Z [AUDIT] mTLS connection established
  Source: payment-service.production.svc
  Target: database-service.production.svc
  Certificate Serial: 0xABCD1234
  Expiry: 2025-04-15T10:30:00Z
  CA: vault-ca.production.internal

2025-01-15T10:30:01Z [AUDIT] mTLS connection rejected
  Source: unknown-service.staging.svc
  Reason: Certificate not trusted by CA
  Certificate Serial: 0xDEADBEEF

7. Integração com malhas de serviço e gateways

Malhas de serviço como Istio e Linkerd simplificam a implementação de mTLS:

# Istio PeerAuthentication para mTLS estrito
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: production
spec:
  mtls:
    mode: STRICT  # STRICT, PERMISSIVE, DISABLE
  portLevelMtls:
    8080:
      mode: STRICT

Para gateways de API, configure mTLS no Envoy:

# Configuração Envoy para mTLS
static_resources:
  listeners:
  - name: external_api
    address:
      socket_address:
        address: 0.0.0.0
        port_value: 443
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        config:
          stat_prefix: ingress_http
          route_config:
            virtual_hosts:
            - name: backend
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                route:
                  cluster: backend_service
      transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
          common_tls_context:
            tls_certificates:
            - certificate_chain:
                filename: /etc/mTLS/server.crt
              private_key:
                filename: /etc/mTLS/server.key
            validation_context:
              trusted_ca:
                filename: /etc/mTLS/ca.crt
              match_subject_alt_names:
              - exact: "allowed-client.service.consul"

Para adoção gradual, utilize o modo permissivo:

# Transição gradual para mTLS
Fase 1: PERMISSIVE (aceita conexões com e sem TLS)
  - Monitorar tráfego não criptografado
  - Identificar serviços que precisam de atualização

Fase 2: PERMISSIVE com alertas
  - Rejeitar conexões não TLS em ambiente de staging
  - Gerar alertas para tráfego inseguro

Fase 3: STRICT
  - Exigir mTLS para todas as conexões
  - Validar identidades de todos os serviços

O mTLS bem implementado oferece autenticação forte, confidencialidade e integridade para comunicação entre serviços, sendo um pilar fundamental para arquiteturas de microsserviços seguras e resilientes.

Referências