Kaique Mitsuo Silva Yamamoto
Arquitetura softwareSeguranca

Zero Trust: autenticação e acesso moderno

Zero Trust é um modelo de segurança que assume que nenhuma entidade — interna ou externa — é confiável por padrão. Cada acesso é verificado, independentemente da origem.


Princípios Fundamentais

"Never trust, always verify."

PrincípioDescrição
Verificação explícitaAutenticar e autorizar sempre, usando todos os dados disponíveis (identidade, localização, dispositivo, serviço)
Menor privilégioAcesso mínimo necessário, just-in-time e just-enough-access (JIT/JEA)
Assume breachMinimizar blast radius, segmentar acesso, verificar criptografia end-to-end

Perímetro Tradicional vs Zero Trust

AspectoPerímetro TradicionalZero Trust
Modelo de confiançaConfiar na rede internaVerificar toda identidade e contexto
Controle de acessoBaseado em IP/redeBaseado em identidade + atributos
Movimento lateralPossível após entrada na redeBloqueado por microssegmentação
VPNAcesso amplo à rede internaAcesso granular por aplicação
AutenticaçãoNa borda (firewall)Em cada serviço / recurso
Criptografia internaOpcional (rede "segura")Obrigatória (mTLS)

Os 5 Pilares do Zero Trust

┌──────────────────────────────────────────────────────────┐
│                      ZERO TRUST                           │
├──────────────┬──────────────┬──────────────┬─────────────┤
│  Identidade  │  Dispositivo  │    Rede      │  Workload   │
│  (quem?)     │  (de onde?)   │  (como?)     │  (o quê?)   │
├──────────────┴──────────────┴──────────────┴─────────────┤
│                       Dados                               │
│             (o que está sendo acessado?)                  │
└──────────────────────────────────────────────────────────┘

1. Identidade

MFA obrigatório, SSO via Keycloak/OIDC, Conditional Access (bloquear login de localização suspeita).

2. Dispositivo

Device compliance (MDM), certificate-based auth, posture check antes de conceder acesso.

3. Rede

Microssegmentação, mTLS entre serviços, nenhum trust implícito em tráfego leste-oeste.

4. Workload

Identidade de serviço via SPIFFE/SPIRE, verificação de integridade de container (image signing).

5. Dados

Classificação de dados, DLP, criptografia em repouso e em trânsito.


mTLS: Certificados de Serviço

mTLS (mutual TLS) exige que tanto o cliente quanto o servidor apresentem certificados. Isso garante identidade em ambas as pontas — eliminando dependência exclusiva de tokens.

cert-manager no Kubernetes

# Instalar cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml

# ClusterIssuer com Let's Encrypt
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
      - http01:
          ingress:
            class: nginx
# Certificate para serviço interno (CA interna)
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: order-service-cert
  namespace: default
spec:
  secretName: order-service-tls
  issuerRef:
    name: internal-ca
    kind: ClusterIssuer
  dnsNames:
    - order-service
    - order-service.default.svc.cluster.local
  usages:
    - digital signature
    - key encipherment
    - client auth
    - server auth

Configurar mTLS em serviço Node.js

import https from 'https'
import fs from 'fs'
import express from 'express'

const app = express()

const server = https.createServer({
  key: fs.readFileSync('/certs/tls.key'),
  cert: fs.readFileSync('/certs/tls.crt'),
  ca: fs.readFileSync('/certs/ca.crt'),
  requestCert: true,     // exigir certificado do cliente
  rejectUnauthorized: true,  // rejeitar se inválido
}, app)

app.use((req, res, next) => {
  const cert = (req as any).socket.getPeerCertificate()
  if (!cert || !cert.subject) {
    return res.status(401).json({ error: 'Certificado de cliente ausente' })
  }

  // Verificar CN ou SPIFFE URI do certificado
  const spiffeUri = cert.subjectaltname?.split(', ')
    .find(s => s.startsWith('URI:spiffe://'))
  if (!spiffeUri?.includes('order-service')) {
    return res.status(403).json({ error: 'Serviço não autorizado' })
  }

  next()
})

Service Mesh: Istio + Keycloak

# PeerAuthentication — mTLS obrigatório em todo o mesh
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: default
spec:
  mtls:
    mode: STRICT
# RequestAuthentication — validar JWT do Keycloak
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
  name: keycloak-jwt
  namespace: default
spec:
  jwtRules:
    - issuer: "https://auth.example.com/realms/myrealm"
      jwksUri: "https://auth.example.com/realms/myrealm/protocol/openid-connect/certs"
      audiences:
        - my-api
      forwardOriginalToken: true
# AuthorizationPolicy — controle granular por serviço
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: order-service-policy
  namespace: default
spec:
  selector:
    matchLabels:
      app: order-service
  action: ALLOW
  rules:
    # Permitir apenas se tiver JWT válido com role correta
    - from:
        - source:
            requestPrincipals:
              - "https://auth.example.com/realms/myrealm/*"
      when:
        - key: request.auth.claims[realm_access.roles]
          values: ["user", "admin"]
    # Permitir tráfego de serviços internos via mTLS
    - from:
        - source:
            principals:
              - "cluster.local/ns/default/sa/payment-service"
              - "cluster.local/ns/default/sa/api-gateway"

SPIFFE/SPIRE para Identidade de Workload

SPIFFE (Secure Production Identity Framework for Everyone) fornece identidade criptográfica para workloads, independente de IP ou nome de host.

SPIFFE URI: spiffe://trust-domain/ns/default/sa/order-service
# SPIRE Server (simplificado)
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: spire-server
  namespace: spire
spec:
  selector:
    matchLabels:
      app: spire-server
  template:
    spec:
      containers:
        - name: spire-server
          image: ghcr.io/spiffe/spire-server:1.8.0
          args:
            - -config
            - /run/spire/config/server.conf
          volumeMounts:
            - name: spire-config
              mountPath: /run/spire/config
            - name: spire-data
              mountPath: /run/spire/data
# spire-server.conf
server {
  bind_address = "0.0.0.0"
  bind_port = "8081"
  trust_domain = "example.org"
  data_dir = "/run/spire/data"
  log_level = "DEBUG"

  ca_key_type = "ec-p256"
  ca_ttl = "168h"
  default_x509_svid_ttl = "1h"
}

plugins {
  DataStore "sql" {
    plugin_data {
      database_type = "sqlite3"
      connection_string = "/run/spire/data/datastore.sqlite3"
    }
  }
  NodeAttestor "k8s_psat" {
    plugin_data {
      clusters = {
        "my-cluster" = {
          service_account_allow_list = ["spire:spire-agent"]
        }
      }
    }
  }
}

Policy Enforcement: OPA + Keycloak

Open Policy Agent (OPA) complementa o Keycloak com políticas de autorização complexas definidas em Rego.

# policy/authz.rego
package authz

default allow = false

# Permitir acesso se usuário tem a role necessária E o recurso é do próprio usuário
allow if {
  has_role(input.token.realm_access.roles, required_role)
  input.resource.owner_id == input.token.sub
}

# Admins têm acesso total
allow if {
  has_role(input.token.realm_access.roles, "admin")
}

has_role(roles, role) if {
  roles[_] == role
}

required_role := r if {
  r := data.resource_roles[input.resource.type]
}
// Consultar OPA antes de processar requisição
async function authorizeRequest(
  token: Record<string, unknown>,
  resource: { type: string; owner_id: string }
): Promise<boolean> {
  const response = await fetch('http://opa:8181/v1/data/authz/allow', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      input: { token, resource },
    }),
  })

  const { result } = await response.json()
  return result === true
}

Istio AuthorizationPolicy com JWT Keycloak

# Exemplo completo: proteger /api/orders apenas para role "orders:read"
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: orders-api-policy
  namespace: production
spec:
  selector:
    matchLabels:
      app: orders-api
  action: ALLOW
  rules:
    - to:
        - operation:
            methods: ["GET"]
            paths: ["/api/orders*"]
      when:
        - key: request.auth.claims[realm_access]
          values: ["*orders:read*"]
    - to:
        - operation:
            methods: ["POST", "PUT", "DELETE"]
            paths: ["/api/orders*"]
      when:
        - key: request.auth.claims[realm_access]
          values: ["*orders:write*"]
    # Health check sem autenticação
    - to:
        - operation:
            methods: ["GET"]
            paths: ["/health", "/metrics"]

Checklist Zero Trust

Identidade
  [ ] MFA habilitado para todos os usuários
  [ ] SSO via OIDC/SAML (Keycloak como IdP)
  [ ] Conditional Access (localização, risco)
  [ ] Service accounts com Client Credentials

Rede
  [ ] mTLS entre todos os serviços
  [ ] Network Policies no Kubernetes (ingress/egress)
  [ ] Microssegmentação por namespace/label

Workload
  [ ] SPIFFE/SPIRE para identidade de workload
  [ ] Image signing (Cosign / Notary)
  [ ] Runtime security (Falco)

Dados
  [ ] Criptografia em repouso (Sealed Secrets / Vault)
  [ ] Criptografia em trânsito (TLS 1.3)
  [ ] Auditoria de acesso a dados sensíveis

Policy
  [ ] OPA para políticas complexas
  [ ] Istio AuthorizationPolicy por serviço
  [ ] Revisão periódica de permissões

Recursos

On this page