O blog da AWS
Criação de um encaminhador de webhook seguro usando uma extensão do AWS Lambda e o Tailscale
Os webhooks podem ajudar os desenvolvedores a se integrarem com sistemas ou dispositivos de terceiros ao criar arquiteturas baseadas em eventos.
No entanto, há momentos em que o controle sobre o ambiente de rede é restrito ou os serviços alteram os endereços IP. Além disso, alguns endpoints não têm proteção de segurança suficiente, exigindo um proxy reverso e verificações de segurança adicionais para o tráfego de entrada da Internet.
Normalmente é complexo configurar e manter proxies reversos seguros altamente disponíveis para inspecionar e enviar eventos a esses sistemas de back-end para vários endpoints. Este blog mostra como usar as extensões do AWS Lambda para criar um encaminhador (forwarder) de webhook serverless nativo na nuvem para atender a essa necessidade com custos mínimos de manutenção e operação.
A extensão personalizada do Lambda forma uma conexão VPN WireGuard segura com um serviço em uma sub-rede privada por trás de um firewall com estado e um NAT gateway. Este exemplo configura um endpoint HTTPS público para receber eventos, filtros seletivos e solicitações de proxy pela conexão WireGuard. Este exemplo usa uma arquitetura serverless para minimizar a sobrecarga de manutenção e os custos operacionais.
Visão geral do exemplo
O exemplo para criar a arquitetura a seguir está disponível no GitHub. Este exemplo usa o AWS CodePipeline e o AWS CodeBuild para criar os artefatos de código e os implanta usando o AWS CloudFormation por meio do AWS Cloud Development Kit (CDK). Ele usa o Amazon API Gateway para gerenciar o endpoint HTTPS e o serviço Lambda para executar as funções do aplicativo. O AWS Secrets Manager armazena as credenciais do Tailscale.
Para orquestrar as conexões WireGuard, você pode usar uma conta gratuita no serviço Tailscale. Como alternativa, configure sua própria camada de coordenação usando o exemplo de Headscale de código aberto.
- O produtor do evento envia uma solicitação HTTP para o URL do API Gateway.
- O API Gateway envia por proxy a solicitação para a função autorizadora do Lambda. Ele retorna uma decisão de autorização com base no IP de origem da solicitação.
- O API Gateway envia por proxy a solicitação para a função Lambda do Secure Webhook Forwarder que executa a extensão Tailscale.
- Na invocação inicial, a extensão Lambda recupera a chave Tailscale Auth do Secrets Manager e a usa para estabelecer uma conexão com a rede Tailscale apropriada. A extensão então expõe a conexão como uma porta SOCKS5 local para a função Lambda.
- A extensão Lambda mantém uma conexão com a rede Tailscale por meio do servidor do Tailscale. Por meio desse servidor de coordenação, todos os outros dispositivos na rede podem ser informados sobre a função Lambda em execução e vice-versa. A função Lambda está configurada para recusar conexões WireGuard de entrada — leia mais sobre o comando –shields-up aqui.
- Depois que a conexão com a rede Tailscale é estabelecida, a função Secure Webhook Forwarder Lambda envia por proxy a solicitação pela Internet para o destino usando uma conexão WireGuard. A conexão é estabelecida por meio do servidor Tailscale Coordination, através do NAT Gateway para alcançar a instância do Amazon EC2 dentro de uma sub-rede privada. A instância EC2 responde com uma resposta HTML de um servidor web Python local.
- Na implantação e a cada 60 dias, o Secrets Manager rotaciona a chave de autenticação Tailscale automaticamente. Ele usa a função Lambda de rotação de credenciais, que recupera as credenciais OAuth do Secrets Manager e as usa para criar uma nova chave de autenticação Tailscale usando a API Tailscale e armazena a nova chave no Secrets Manager.
Como a extensão Lambda funciona
A extensão Lambda cria o túnel de rede e o expõe à função Lambda como um servidor SOCKS5 executado na porta 1055. Há três estágios do ciclo de vida do Lambda: init, invoke e shutdown.
Com a extensão Tailscale Lambda, a maior parte do trabalho é realizada na fase inicial. A função Lambda do webhook forwarder tem o seguinte ciclo de vida:
- Fase inicial:
- Extension Init — A extensão se conecta à rede Tailscale e expõe o túnel WireGuard por meio da porta SOCKS5 local.
- Runtime Init — Inicializa o tempo de execução do Node.js.
- Function Init — Importa os módulos Node.js necessários.
- Fase de invocação:
- A extensão intencionalmente não se registra para receber nenhum evento de invocação. A rede Tailscale é mantida on-line até que a função seja instruída a ser desligada.
- A função handler do Node.js recebe a solicitação do API Gateway no formato 2.0, que então envia por proxy para a porta SOCKS5 para redirecionar a solicitação pela conexão WireGuard até o destino. A fase de invocação termina quando a função recebe uma resposta da instância EC2 de destino e, opcionalmente, a retorna ao API Gateway para posterior encaminhamento para a fonte original do evento.
- Fase de desligamento:
- A extensão se desconecta da rede Tailscale e registra o recebimento do evento de desligamento.
- O ambiente de execução da função é encerrado junto com o ambiente de execução da função Lambda.
Estrutura do arquivo de extensão
O código da extensão existe como um arquivo zip junto com alguns metadados definidos no momento em que a extensão é publicada como uma camada do AWS Lambda. O arquivo zip contém três pastas:
/extensions
– contém o código da extensão e é o diretório em que o serviço Lambda procura o arquivo a ser executado quando a extensão Lambda é inicializada./bin
–inclui as dependências executáveis. Por exemplo, no script tsextension.sh, ele executa os binários tailscale, tailscaled, curl, jq e OpenSS/ssl
– armazena o repositório confiável da autoridade de certificação (CA) (contendo os certificados da CA raiz aos quais se pode se conectar). O OpenSSL os usa para verificar os certificados SSL e TLS.
O arquivo tsextension.sh é o núcleo da extensão. A maior parte do código é executada na fase inicial da função Lambda. O código da extensão é dividido em três estágios. Os dois primeiros estágios estão relacionados à fase do ciclo de vida inicial da função Lambda, com o terceiro estágio abrangendo as fases do ciclo de vida de invocação e desligamento.
Fase de extensão 1: Inicialização
Nessa fase, a extensão inicializa a conexão Tailscale e espera que a conexão fique disponível.
A primeira etapa recupera a chave de autenticação Tailscale do Secrets Manager. Para manter o tamanho da extensão pequeno, a extensão usa uma série de comandos Bash em vez de empacotar o AWS CLI para fazer as solicitações Sigv4 ao Secrets Manager.
As credenciais temporárias da função Lambda são disponibilizadas como variáveis de ambiente pelo ambiente de execução do Lambda, que a extensão usa para autenticar a solicitação Sigv4. As permissões do IAM para recuperar o segredo são adicionadas à função de execução do Lambda pelo código CDK. Para otimizar a segurança, a política do segredo restringe as permissões de leitura para (1) essa função Lambda e (2) função Lambda que a alterna a cada 60 dias.
O agente Tailscale começa a usar a chave Tailscale Auth. Os binários tailscaled e tailscale começam no modo de rede, pois cada função do Lambda é executada em seu próprio contêiner em sua própria máquina virtual. Mais informações sobre o modo de rede do espaço do usuário podem ser encontradas na documentação do Tailscale.
Com os processos Tailscale em execução, o processo deve esperar que a conexão com a Tailnet (o nome de uma rede Tailscale) seja estabelecida e que a porta SOCKS5 esteja disponível para aceitar conexões. Para fazer isso, a extensão simplesmente espera que o comando ‘tailscale status’ não retorne uma mensagem com ‘stop’ e depois passa para a fase 2.
Fase de extensão 2: Registro
A extensão agora se registra como “inicializada” com o serviço Lambda. Isso é feito enviando uma solicitação POST para a API de extensão de serviço do Lambda com os eventos que devem ser encaminhados para a extensão.
O runtime init começa em seguida (isso inicializa o runtime Node.js da própria função Lambda), seguido pela função init (o código fora do manipulador de eventos). No caso da extensão Tailscale Lambda, ela só registra a extensão para receber eventos ‘SHUTDOWN’. Depois que o serviço SOCKS5 estiver ativo e disponível, não há nenhuma ação a ser executada pela extensão em cada invocação subsequente da função.
Fase de extensão 3: processamento de eventos
Para sinalizar que a extensão está pronta para receber um evento, uma solicitação GET é feita para o “próximo” endpoint da API de tempo de execução do Lambda. Isso bloqueia a execução do script de extensão até que um evento SHUTDOWN seja enviado (já que esse é o único evento registrado para essa extensão do Lambda).
Quando isso é enviado, a extensão se desconecta do serviço Tailscale e a função Lambda é encerrada. Se os eventos INVOKE também forem registrados, a extensão processará o evento. Em seguida, ele sinaliza para a API de tempo de execução do Lambda que a extensão está pronta para receber outro evento enviando uma solicitação GET para o “próximo” endpoint.
Controle de acesso
Um exemplo de autorizador Lambda está incluído neste exemplo. Observe que é recomendável usar o serviço AWS Web Application Firewall para adicionar proteção adicional ao seu endpoint público de API, bem como fortalecer o código para uso em produção.
Para fins desta demonstração, a implementação demonstra uma restrição básica do intervalo CIDR de IP de origem, embora você possa usar qualquer propriedade da solicitação para basear as decisões de autorização. Leia mais sobre os autorizadores do Lambda para APIs HTTP aqui. Para usar a restrição de IP de origem, atualize o intervalo CIDR dos IPs que você deseja aceitar na variável de ambiente AUTHD_SOURCE_CIDR da função autorizadora do Lambda.
Custos
Você será cobrado por todos os recursos usados por este projeto. O NAT Gateway e a instância EC2 são destruídos pelo pipeline quando a etapa final do pipeline é liberada manualmente para minimizar os custos. A ferramenta AWS Lambda Power Tuning pode ajudar a encontrar o equilíbrio entre desempenho e custo enquanto pesquisa a instância de demonstração do EC2 por meio da rede Tailscale.
O resultado a seguir mostra que 256 MB de memória é o ideal para o menor custo de execução. O custo é estimado em menos de $3 para 1 milhão de solicitações por mês, uma vez que a pilha de demonstração é destruída.
Conclusão
O uso de extensões Lambda pode abrir uma ampla variedade de opções para ampliar a capacidade das arquiteturas serverless. Este blog mostra uma extensão do Lambda que cria um túnel VPN seguro usando o protocolo WireGuard e o serviço Tailscale para transferir eventos para uma instância do EC2 inacessível pela Internet.
Isso é configurado para minimizar a sobrecarga operacional com um pipeline de implantação automatizado. Um autorizador Lambda protege o endpoint, fornecendo a capacidade de implementar uma lógica personalizada com base no conteúdo e no contexto da solicitação.
Para obter mais recursos de aprendizado serverless, visite Serverless Land.
Este artigo foi traduzido do Blog da AWS em Inglês.
Sobre os autores
Duncan Parsons é Enterprise Solution Architect na AWS
Simon Kok é Sr. Consultant, AppDev na AWS
Tradutor
Daniel Abib é Enterprise Solution Architect 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 segurança. Ele trabalha apoiando clientes corporativos, ajudando-os em sua jornada para a nuvem.