Protegendo o Amazon Bedrock AgentCore Runtime com AWS WAF

O problema: como proteger agentes de IA em produção com AWS WAF

Quando você expõe um agente de IA generativa construído com o Amazon Bedrock AgentCore como endpoint de API em produção, surgem requisitos legítimos de segurança: limitação de taxa, proteção contra ameaças web comuns, controles de auditoria e políticas de firewall de aplicação web. O serviço responsável por isso na AWS é o AWS WAF — Firewall de Aplicação Web (WAF).

O AWS WAF se integra com três serviços: Application Load Balancers (ALBs), distribuições do Amazon CloudFront e APIs REST do Amazon API Gateway. O CloudFront é voltado para cache e entrega de conteúdo estático — invocações de agentes são dinâmicas e em tempo real, então cache não se aplica. O API Gateway introduz sua própria camada de autenticação e transformação de requisições, o que cria um problema de dupla autenticação com o SigV4 e OAuth já embutidos no AgentCore.

Sobra o ALB voltado para a internet como ponto de integração: ele repassa cabeçalhos de forma transparente, suporta roteamento interno à VPC e se conecta diretamente a uma WebACL do AWS WAF. A partir do ALB, o tráfego é roteado para o AgentCore por meio de um VPC Interface Endpoint — Endpoint de Interface de Nuvem Privada Virtual (VPC) — para o serviço de plano de dados do AgentCore.

É aqui que aparece o desafio técnico central. ALBs exigem health checks para verificar se os backends estão respondendo. Mas o AgentCore Runtime exige autenticação — SigV4 ou OAuth — em todas as chamadas de API, inclusive nas de health check. Os health checks padrão do ALB enviam requisições sem credenciais, e por isso falham por padrão. É preciso uma forma de fazer os health checks funcionarem sem credenciais, enquanto o tráfego de produção autenticado passa normalmente para o AgentCore.

Dois padrões de arquitetura para resolver o problema

A AWS documentou dois padrões que resolvem esse problema. Ambos usam um ALB voltado para a internet com AWS WAF e roteiam o tráfego por um VPC Interface Endpoint até o AgentCore Runtime. O Padrão 1 coloca um proxy AWS Lambda entre o ALB e o VPC Endpoint, dando controle total sobre a transformação de requisições. O Padrão 2 aponta o ALB diretamente para os endereços IP das Interfaces de Rede Elástica (ENIs) do VPC Endpoint, eliminando o salto pelo Lambda. Os dois padrões foram testados de ponta a ponta com autenticação SigV4 e OAuth (JWT do Amazon Cognito).

A base comum entre os dois: o cliente envia uma requisição autenticada (assinatura SigV4 ou Bearer token OAuth) para o ALB voltado para a internet. O AWS WAF inspeciona a requisição antes de o ALB encaminhá-la para as ENIs do VPC Endpoint na porta HTTPS 443. O AgentCore valida a autenticação e roteia a requisição para o container de runtime na porta interna 8080.

Os quatro componentes compartilhados são: o AWS WAF acoplado ao ALB (com limitação de taxa, proteção contra injeção de SQL, filtragem de XSS e controle de bots via grupos de regras gerenciadas como AWSManagedRulesCommonRuleSet); o ALB voltado para a internet com listener HTTPS na porta 443; o VPC Interface Endpoint (com.amazonaws.<region>.bedrock-agentcore) para conectividade privada via PrivateLink; e o AgentCore Runtime como container gerenciado que executa o código do agente.

Atenção: use o endpoint com.amazonaws.<region>.bedrock-agentcore (plano de dados) para invocações de Runtime. Existem três endpoints distintos: bedrock-agentcore (plano de dados para Runtime, Memory e Tools), bedrock-agentcore-control (plano de controle) e bedrock-agentcore.gateway (exclusivo para o Gateway). Usar o endpoint errado não roteará para o seu Runtime.

Padrão 1: ALB com proxy Lambda

Neste padrão, uma função Lambda fica atrás do ALB e encaminha as requisições — incluindo o cabeçalho de autenticação — para o AgentCore pelo VPC Endpoint. A função Lambda oferece uma camada de computação onde é possível transformar requisições, traduzir entre métodos de autenticação ou adicionar logging personalizado antes de a requisição chegar ao AgentCore.

Imagem original — fonte: Aws

Escolha o Padrão 1 se você precisar de: transformação de requisições (manipulação de cabeçalhos, alterações de payload ou tradução de protocolo); logs personalizados ou trilhas de auditoria na camada de proxy; tradução entre múltiplos métodos de autenticação; ou uma camada de computação explícita entre o ALB e o backend (algumas políticas de segurança exigem isso).

Para OAuth, a função Lambda repassa o cabeçalho Authorization (o Bearer token) sem alteração, junto com o caminho e o corpo da requisição original, de modo que o JWT chegue ao AgentCore intacto. Para SigV4, a assinatura está vinculada ao host original da requisição e se torna inválida após o encaminhamento para o VPC Endpoint — por isso o proxy refaz a assinatura usando as credenciais da sua própria execution role.

O trecho Python abaixo mostra a lógica central de encaminhamento. A função lê o cabeçalho Authorization do evento do ALB, constrói a URL de destino usando o nome DNS do VPC Endpoint e encaminha a requisição com os cabeçalhos e o corpo originais. Esse caminho funciona diretamente para Bearer tokens OAuth. Requisições SigV4 exigem a etapa adicional de reassinatura, disponível no repositório:

import json
import urllib3
import os

http = urllib3.PoolManager()
VPCE_DNS = os.environ["VPCE_DNS_NAME"]

def handler(event, context):
    path = event.get("path", "/")
    headers = event.get("headers", {})
    body = event.get("body", "")
    method = event.get("httpMethod", "POST")

    target_url = f"https://{VPCE_DNS}{path}"

    resp = http.request(
        method,
        target_url,
        headers={
            "Authorization": headers.get("authorization", ""),
            "Content-Type": headers.get("content-type", "application/json"),
        },
        body=body,
    )

    return {
        "statusCode": resp.status,
        "headers": {"Content-Type": "application/json"},
        "body": resp.data.decode("utf-8"),
    }

A implementação completa e pronta para produção — incluindo assinatura SigV4, tratamento de query strings, respostas de health check do ALB e tratamento estruturado de erros — está disponível no repositório GitHub de acompanhamento.

Os grupos de destino Lambda não usam health checks baseados em caminho. O ALB verifica se a função é invocável chamando-a diretamente, então nenhuma configuração especial de health check é necessária.

Trade-offs do Padrão 1

  • Latência: adiciona 50–200 ms por requisição (cold start do Lambda). Pode ser mitigado com concorrência provisionada.
  • Custo: custo de invocação Lambda por requisição. Consulte os preços do AWS Lambda.
  • Complexidade: componente adicional para implantar, monitorar e manter.
  • Flexibilidade: controle total sobre transformação de requisição/resposta e logging personalizado.

Padrão 2: ALB direto para o VPC Endpoint

Neste padrão, o ALB aponta diretamente para os endereços IP das ENIs do VPC Endpoint na porta HTTPS 443. Não há função Lambda nem Network Load Balancer. O ALB repassa os cabeçalhos de autenticação para o AgentCore de forma transparente. Essa arquitetura tem menos componentes e elimina o salto pelo proxy Lambda.

Imagem original — fonte: Aws

Escolha o Padrão 2 se quiser: uma arquitetura mais simples com menos componentes; sem transformação de requisições entre o ALB e o AgentCore; latência mínima sem saltos adicionais; e que a aplicação cliente lide diretamente com a assinatura SigV4 ou OAuth.

A etapa-chave que faz o Padrão 2 funcionar é a criação de um target group do tipo IP na porta HTTPS 443 com um health check que não exige autenticação:

aws elbv2 create-target-group \
  --name agentcore-vpce-tg \
  --target-type ip \
  --protocol HTTPS \
  --port 443 \
  --vpc-id vpc-xxxxxxxxx \
  --health-check-protocol HTTPS \
  --health-check-port 443 \
  --health-check-path / \
  --matcher HttpCode=200-499

O health check usa o caminho / com um matcher de 200–499. O AgentCore retorna 404 em / porque não é um caminho de API válido, mas o ALB aceita respostas nesse intervalo como saudáveis. O objetivo é verificar que o VPC Endpoint está respondendo — o código de status específico não importa. Essa abordagem não exige autenticação para os health checks.

Para SigV4 (AWS SDK), basta apontar o SDK para a URL do endpoint do ALB — o SDK cuida da assinatura automaticamente:

import boto3

client = boto3.client(
    "bedrock-agentcore",
    endpoint_url="https://<alb-dns>"
)

response = client.invoke_agent_runtime(
    agentRuntimeArn="arn:aws:bedrock-agentcore:...",
    payload='{"input": "hello"}'
)

Para OAuth (navegador ou curl), passe o JWT como Bearer token:

curl -X POST https://<alb-dns>/runtimes/<runtime-arn>/invocations \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{"input": "hello"}'

Observação importante: ao usar o grant client_credentials no Amazon Cognito, o JWT contém a claim client_id, mas não a claim aud. Configure o autorizador OAuth do AgentCore com allowedClients (que corresponde ao client_id) em vez de allowedAudience.

Trade-offs do Padrão 2

  • Latência: sem overhead de Lambda, passagem direta.
  • Custo: sem custo adicional de computação.
  • Complexidade: mínima — apenas ALB + VPC Endpoint.
  • Flexibilidade: sem capacidade de transformação de requisições.

Fechando a porta dos fundos com resource policies

Ambos os padrões exigem uma resource policy para impedir acesso direto ao AgentCore que contorne o AWS WAF. Sem essa política, usuários com credenciais válidas podem chamar o endpoint público do AgentCore diretamente, ignorando todas as proteções do WAF.

Imagem original — fonte: Aws

A resource policy usa dois statements. O primeiro permite InvokeAgentRuntime apenas quando a requisição origina do seu VPC Endpoint específico (condição aws:SourceVpce). O segundo nega InvokeAgentRuntime para requisições que não originem desse VPC Endpoint, com aws:ViaAWSService definido como false para evitar o bloqueio de chamadas internas do próprio serviço AgentCore.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowVPCEOnly",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "bedrock-agentcore:InvokeAgentRuntime",
      "Resource": "<runtime-arn>",
      "Condition": {
        "StringEquals": {
          "aws:SourceVpce": "<vpce-id>"
        }
      }
    },
    {
      "Sid": "DenyDirectAccess",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "bedrock-agentcore:InvokeAgentRuntime",
      "Resource": "<runtime-arn>",
      "Condition": {
        "StringNotEquals": {
          "aws:SourceVpce": "<vpce-id>"
        },
        "Bool": {
          "aws:ViaAWSService": "false"
        }
      }
    }
  ]
}

Após aplicar a policy, o resultado esperado é: OAuth via ALB → VPC Endpoint retorna 200 OK; OAuth direto ao endpoint público do AgentCore retorna 403 (não autorizado); SigV4 para um runtime configurado com OAuth retorna 403 (incompatibilidade de método de autenticação); sem autenticação via ALB retorna 401.

Defesa em profundidade: camadas de segurança

Ambos os padrões fornecem múltiplas camadas de segurança. Cada camada endereça um vetor de ameaça diferente e, juntas, formam uma postura de defesa em profundidade para o seu runtime de agentes. Uma requisição precisa passar por todas as camadas antes de chegar ao seu agente.

Imagem original — fonte: Aws
  • AWS WAF: limitação de taxa, injeção de SQL, XSS, controle de bots e geo-bloqueio.
  • Security Group do ALB: controle de acesso em nível de rede, HTTPS 443 apenas de fontes permitidas.
  • Security Group do VPC Endpoint: entrada restrita ao security group do ALB.
  • Autenticação do AgentCore: SigV4 ou OAuth obrigatório em cada invocação.
  • Resource Policy: nega acesso que não origine do VPC Endpoint.
  • VPC Endpoint Policy (opcional): restringe a principals IAM ou ações específicas.

Como escolher entre os padrões

  • Padrão 1 (proxy Lambda): componentes WAF + ALB + Lambda + VPC Endpoint; latência adicional de 50–200 ms; custo de invocações Lambda; transformação de requisições disponível; health check gerenciado pelo Lambda. Indicado quando você precisa de lógica personalizada ou tradução entre múltiplos métodos de autenticação.
  • Padrão 2 (direto ao VPC Endpoint): componentes WAF + ALB + VPC Endpoint; sem latência adicional de Lambda; sem custo extra de computação; sem transformação de requisições; health check via caminho / com matcher 200–499. Indicado quando você quer a opção mais simples e de menor latência.

Considerações importantes para ambos os padrões

  • Apenas porta 443. O VPC Endpoint expõe somente HTTPS 443. A porta 8080 (porta interna do container) não é acessível pelo VPC Endpoint — o AgentCore faz o roteamento 443 → 8080 internamente.
  • DNS privado não é obrigatório. O ALB aponta para os IPs das ENIs do VPC Endpoint diretamente, então DNS privado no VPC Endpoint não é necessário em nenhum dos padrões.
  • OAuth bloqueia SigV4. Quando um AgentCore Runtime está configurado com um autorizador OAuth, requisições SigV4 são rejeitadas com “Authorization method mismatch”. Escolha um método de autenticação por runtime.
  • Seleção do serviço de endpoint. Use bedrock-agentcore para o plano de dados do Runtime. O endpoint bedrock-agentcore.gateway é exclusivo para o AgentCore Gateway e não roteará para o seu Runtime.
  • Estabilidade dos IPs das ENIs. Os IPs das ENIs do VPC Endpoint são estáveis, mas podem mudar se o endpoint for recriado. Monitore a saúde dos targets e atualize os IPs se necessário.

Recursos adicionais

Fonte

Securing Amazon Bedrock AgentCore Runtime with AWS WAF (https://aws.amazon.com/blogs/machine-learning/securing-amazon-bedrock-agentcore-runtime-with-aws-waf/)

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *