O blog da AWS

Traga seu próprio certificado de cliente para mTLS de backend no Amazon API Gateway

Por Biswanath Mukherjee e Udit Parikh, na Amazon Web Services.

Empresas que usam o Amazon API Gateway na frente de backends internos ou de parceiros frequentemente desejam trazer seu próprio certificado de cliente para autenticação mútua TLS (mTLS) de backend. Durante o mTLS, o backend apresenta seu próprio certificado de servidor e também solicita que o chamador apresente um certificado de cliente para validá-lo em relação a uma autoridade certificadora (CA) confiável. Até agora, você só podia usar um certificado SSL autoassinado gerado pelo API Gateway para a conexão de saída, porque não havia uma CA por trás dele para o backend confiar. Backends que exigem uma CA corporativa ou de parceiro específica rejeitam esse certificado autoassinado, e o handshake mútuo TLS falha. Trazer seu próprio certificado assinado por uma CA é necessário para cenários como a migração de APIs de gateways legados ou o atendimento de mandatos internos de PKI que exigem certificados de uma CA aprovada.

Com o API Gateway, agora você pode trazer seu próprio certificado de cliente para autenticação mútua TLS (mTLS) de backend. Você pode usar um certificado de terceiros ou um certificado emitido pela AWS Private Certificate Authority. Se estiver usando um certificado de terceiros, você deve importar o certificado no AWS Certificate Manager (ACM). Em seguida, você configura o ARN do certificado ACM no estágio da sua API REST. O API Gateway apresenta esse certificado durante o handshake mTLS de backend.

Visão geral da solução

Nesta postagem, você criará uma API REST com uma conexão mTLS de saída usando este recurso recém-lançado do API Gateway. Esta solução demonstra uma conexão mTLS de saída entre o Amazon API Gateway e uma aplicação de backend em execução no Amazon Elastic Container Service (Amazon ECS).

O diagrama a seguir mostra a arquitetura da solução.

Diagrama de arquitetura mostrando o API Gateway apresentando um certificado de cliente ACM a um Network Load Balancer que encaminha o tráfego para um sidecar NGINX e uma aplicação validadora no Amazon ECS Fargate, com certificados emitidos pela AWS Private CA por meio do ACM

A solução usa:

  1. AWS Private Certificate Authority com uma hierarquia de CA raiz-subordinada para emitir tanto o certificado de cliente quanto o de servidor por meio do AWS Certificate Manager (ACM).
  2. Estágio da API REST do Amazon API Gateway configurado com o ARN do certificado de cliente ACM (ClientCertificateId), para que o API Gateway apresente o certificado durante o handshake TLS de saída.
  3. Amazon ECS no AWS Fargate executando um sidecar NGINX que mantém o certificado de servidor e valida o certificado de cliente recebido em relação a um pacote de CA (cadeia raiz e subordinada).

Uma solicitação passa pelas seguintes etapas:

  1. A aplicação cliente invoca a API REST exposta pelo API Gateway. O estágio do API Gateway é configurado com um ARN de certificado de cliente ACM.
  2. O API Gateway abre uma conexão de saída para o Network Load Balancer (NLB) para iniciar o handshake TLS. O API Gateway apresenta um certificado de cliente ACM configurado no nível do estágio quando o backend solicita um.
  3. O NLB escuta a solicitação TCP entrante na porta 443 e encaminha a chamada para o Amazon ECS Fargate. O NLB atua como passthrough e não termina a conexão TLS.
  4. O container sidecar NGINX em execução no Amazon ECS realiza o handshake mTLS entrante:
  • O NGINX apresenta o certificado de servidor de backend e verifica o certificado de cliente em relação a um pacote de CA montado (cadeia raiz e subordinada).
  • Após a verificação, o NGINX encaminha a solicitação e os detalhes do certificado analisado ao container da aplicação validadora por meio de HTTP local.
  • A aplicação validadora reverifica a janela de validade do certificado, verifica o nome comum em relação a uma lista de permissões e retorna uma resposta JSON estruturada.

Observação: o sidecar NGINX não é obrigatório para este fluxo. Ele demonstra a separação de responsabilidades: o NGINX lida com o handshake mTLS, e a aplicação validadora contém a lógica de negócio.

Pré-requisitos para a demonstração

Para acompanhar, você precisa do seguinte:

Configuração do ambiente

Execute os comandos a seguir para configurar o ambiente de demonstração:

  1. Crie uma nova pasta e clone o repositório do GitHub:
    git clone https://github.com/aws-samples/sample-api-backend-mtls
    cd sample-api-backend-mtls
  2. Defina as variáveis de ambiente após substituir os espaços reservados:
    ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
    REGION=<Sua Região da AWS, por exemplo, us-east-1>
    STACK_NAME=<Nome da sua stack, por exemplo, outbound-mtls-backend>

Compile as imagens de container

Execute os comandos a seguir para criar imagens de container do container sidecar NGINX e dos containers da aplicação validadora:

  1. Crie dois repositórios do Amazon Elastic Container Registry (Amazon ECR), um para os containers NGINX e outro para os containers da aplicação validadora, respectivamente:
    NGINX_REPO_URI=$(aws ecr create-repository \
      --repository-name $STACK_NAME-nginx-sidecar \
      --image-tag-mutability IMMUTABLE \
      --image-scanning-configuration scanOnPush=true \
      --region "$REGION" \
      --query "repository.repositoryUri" --output text)
    
    VALIDATOR_REPO_URI=$(aws ecr create-repository \
      --repository-name $STACK_NAME-validator-app \
      --image-tag-mutability IMMUTABLE \
      --image-scanning-configuration scanOnPush=true \
      --region "$REGION" \
      --query "repository.repositoryUri" --output text)
    
    aws ecr get-login-password --region "$REGION" \
      | docker login --username AWS --password-stdin "${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com"
  2. Compile e envie os containers NGINX e da aplicação validadora:
    docker build --platform linux/amd64 -t $STACK_NAME-nginx-sidecar nginx/
    docker tag $STACK_NAME-nginx-sidecar:latest "${NGINX_REPO_URI}:latest"
    docker push "${NGINX_REPO_URI}:latest"
    docker build --platform linux/amd64 -t $STACK_NAME-validator-app validator_app/
    docker tag $STACK_NAME-validator-app:latest "${VALIDATOR_REPO_URI}:latest"
    docker push "${VALIDATOR_REPO_URI}:latest"

Implemente e teste a solução

Primeiro, você implementa a stack sem o certificado de cliente configurado no API Gateway e realiza testes negativos. O handshake mTLS falhará devido à ausência de um certificado de cliente na solicitação. Em seguida, você atualiza a stack para configurar o certificado de cliente no estágio do API Gateway e testa novamente o mTLS.

  1. Execute o comando a seguir para compilar e implementar a stack completa sem o certificado de cliente configurado no estágio do API Gateway:
    sam build
    sam deploy \
      --stack-name $STACK_NAME \
      --resolve-s3 \
      --capabilities CAPABILITY_IAM \
      --region "$REGION" \
      --parameter-overrides \
      NginxRepositoryUri="$NGINX_REPO_URI" \
      ValidatorRepositoryUri="$VALIDATOR_REPO_URI" \
      EnableOutboundMtls=false
  2. Aguarde até que a tarefa atinja o status RUNNING e passe pela verificação de saúde do grupo de destino:
    EcsClusterName=$(aws cloudformation describe-stacks \
      --stack-name $STACK_NAME --region "$REGION" \
      --query "Stacks[0].Outputs[?OutputKey=='EcsClusterName'].OutputValue" \
      --output text)
    
    TargetGroupArn=$(aws cloudformation describe-stacks \
      --stack-name $STACK_NAME --region "$REGION" \
      --query "Stacks[0].Outputs[?OutputKey=='TargetGroupArn'].OutputValue" \
      --output text)
    
    aws ecs list-tasks --cluster "$EcsClusterName" --region "$REGION"
    
    aws elbv2 describe-target-health \
      --target-group-arn "$TargetGroupArn" --region "$REGION"
  3. Capture a URL de invocação da API frontal a partir das saídas da stack:
    FRONT_API_URL=$(aws cloudformation describe-stacks \
      --stack-name $STACK_NAME --region "$REGION" \
      --query "Stacks[0].Outputs[?OutputKey=='FrontApiUrl'].OutputValue" \
      --output text)
    
    NLB_DNS_NAME=$(aws cloudformation describe-stacks \
      --stack-name $STACK_NAME --region "$REGION" \
      --query "Stacks[0].Outputs[?OutputKey=='NlbDnsName'].OutputValue" \
      --output text)
    
    FRONT_CLIENT_CERT_ARN=$(aws cloudformation describe-stacks \
      --stack-name $STACK_NAME --region "$REGION" \
      --query "Stacks[0].Outputs[?OutputKey=='FrontClientCertArn'].OutputValue" \
      --output text)
    
    FRONT_API_ID=$(aws cloudformation describe-stacks \
      --stack-name $STACK_NAME --region "$REGION" \
      --query "Stacks[0].Outputs[?OutputKey=='FrontApiId'].OutputValue" \
      --output text)
  4. Aguarde um ou dois minutos após a conclusão da stack e, em seguida, invoque a API frontal:
    curl -v "$FRONT_API_URL"

    A seguir está a configuração do NGINX para mTLS:

    ...
    server {
        listen 443 ssl;
        # Server identity (issued by the private CA).
        ssl_certificate /etc/nginx/certs/server.crt;
        ssl_certificate_key /etc/nginx/certs/server.key;
        # Inbound mutual TLS: require and validate the client certificate
        # against the CA bundle (root + subordinate CA chain).
        ssl_client_certificate /etc/nginx/certs/ca_bundle.pem;
        ssl_verify_client on;
        ssl_verify_depth 2;
        ssl_protocols TLSv1.2;
    ...}

    O comando curl retorna HTTP/2 400, com um corpo de resposta contendo 400 No required SSL certificate was sent. Como o API Gateway não está apresentando um certificado de cliente no handshake de saída, o container sidecar NGINX no Amazon ECS rejeita a conexão mTLS. A captura de tela a seguir mostra a resposta:

    Resposta no terminal mostrando um erro HTTP/2 400 com a mensagem No required SSL certificate was sent
  5. Agora, reimplemente a solução com o mTLS de saída ativado:
    sam deploy \
      --stack-name $STACK_NAME \
      --resolve-s3 \
      --capabilities CAPABILITY_IAM \
      --region "$REGION" \
      --parameter-overrides \
      NginxRepositoryUri="$NGINX_REPO_URI" \
      ValidatorRepositoryUri="$VALIDATOR_REPO_URI" \
      EnableOutboundMtls=true
  6. Aguarde um ou dois minutos após a conclusão da stack e, em seguida, invoque a API frontal novamente:
    curl -v "$FRONT_API_URL"

    Como o certificado de cliente agora é apresentado durante o handshake mTLS, o handshake é concluído com sucesso, conforme mostrado no trecho de resposta a seguir:

    Resposta no terminal mostrando um handshake mTLS bem-sucedido e uma resposta HTTP 200 do backend

Renovação automática de certificados

Quando um certificado é alterado no ACM, o API Gateway detecta a atualização e propaga o novo certificado automaticamente. Você não precisa reimplementar o estágio, e a API não sofre tempo de inatividade durante a rotação. A propagação do certificado é eventualmente consistente. Durante uma atualização, o backend pode receber brevemente o certificado antigo ou o novo. O ACM também emite notificações de expiração de certificado por meio do Amazon EventBridge, que você pode usar para configurar alarmes antes que um certificado expire.

Limpeza

Se você acompanhou apenas para fins de demonstração, para evitar cobranças futuras, execute os comandos a seguir para excluir os recursos criados nesta demonstração:

  1. Limpe os buckets do S3:
    NLB_LOGS_BUCKET=$(aws cloudformation describe-stacks \
      --stack-name $STACK_NAME --region $REGION \
      --query "Stacks[0].Outputs[?OutputKey=='NlbAccessLogsBucketName'].OutputValue" \
      --output text)
    
    aws s3api list-object-versions --bucket "$NLB_LOGS_BUCKET" \
      --query '{Objects: Versions[].{Key:Key,VersionId:VersionId}}' \
      --output json | \
      jq -c '.Objects[]? // empty' | \
      while read -r obj; do
        aws s3api delete-object --bucket "$NLB_LOGS_BUCKET" \
          --key "$(echo "$obj" | jq -r .Key)" \
          --version-id "$(echo "$obj" | jq -r .VersionId)" \
          --region $REGION
      done
    
    aws s3api list-object-versions --bucket "$NLB_LOGS_BUCKET" \
      --query '{Objects: DeleteMarkers[].{Key:Key,VersionId:VersionId}}' \
      --output json | \
      jq -c '.Objects[]? // empty' | \
      while read -r obj; do
        aws s3api delete-object --bucket "$NLB_LOGS_BUCKET" \
          --key "$(echo "$obj" | jq -r .Key)" \
          --version-id "$(echo "$obj" | jq -r .VersionId)" \
          --region $REGION
      done
  2. Exclua a stack:
    sam delete --stack-name $STACK_NAME --region $REGION --no-prompts
  3. Exclua o repositório ECR:
    aws ecr delete-repository --repository-name $STACK_NAME-nginx-sidecar --force --region $REGION
    aws ecr delete-repository --repository-name $STACK_NAME-validator-app --force --region $REGION

Conclusão

Nesta postagem, você configurou uma API REST com uma conexão mTLS de saída usando o Amazon API Gateway e um backend ECS Fargate. Com este novo lançamento de recurso no API Gateway, agora você pode trazer seu próprio certificado de cliente para o handshake mTLS de saída de suas APIs REST. Agora você pode atender aos seus mandatos internos de PKI para autenticar backends que fixam um emissor de certificado específico.

Para começar, importe um certificado da sua própria PKI para o ACM e configure o estágio da sua API REST do API Gateway para autenticação mTLS de saída. Para mais informações, consulte Present client certificates to backend services with mutual TLS in API Gateway. Se você tiver feedback sobre esta postagem, deixe-o na seção de comentários. Para dúvidas técnicas, você pode iniciar um tópico no AWS re:Post.

Leitura complementar


Este conteúdo foi traduzido da publicação original do blog em inglês (disponível aqui).

Autores

Biswanath Mukherjee é Solutions Architect Sênior na AWS.
Udit Parikh é Engenheiro de Suporte em Cloud Sênior na AWS.

 

Tradutores

Nicolas Tarzia é Senior Technical Account Manager na AWS, com mais de 13 anos de experiência, com ampla experiência em arquitetura cloud, engenharia e design de software. Sua área de interesse são tecnologias serverless.
https://www.linkedin.com/in/nicolastarzia
Daniel Abib é Arquiteto de Soluções Sênior e Especialista em Amazon Bedrock na AWS, com mais de 25 anos trabalhando com gerenciamento de projetos, arquiteturas de soluções escaláveis, desenvolvimento de sistemas e CI/CD, microsserviços, arquitetura Serverless & Containers e especialização em Machine Learning. Ele trabalha apoiando Startups, ajudando-os em sua jornada para a nuvem.
https://www.linkedin.com/in/danielabib/