Uma CDN (CloudFront) com wildcard domain apontando dinamicamente para vários endpoints usando Lambda@Edge

Imagine o cenário de uma startup onde cada cliente possui sua própria infraestrutura dedicada. Atualmente, isso é facilmente viável com serviços como AWS Lambda (para processamento), AWS DynamoDB (para banco de dados) e uma solução de Infraestrutura como Código (como o CDK, por exemplo). Essa abordagem permite isolar cada inquilino (a.k.a tenant) em sua própria infraestrutura, proporcionando uma série de benefícios. No entanto, o foco do artigo de hoje não é esse; prometo abordar esse tema em um próximo post. 🤞🏻

💡
Esse post não é sobre REDIRECTs como muitos outros blog posts que tem por aí e sim Request Origin, ou seja, ao acessar o domínio com wildcard, por exemplo, img-dogs.euquero.cafe o usuário não será redirecionado, o que irá acontecer é que o CloudFront irá repassar o request internamente para a origin informada pela Lambda@Edge como um Proxy e irá retornar o resultado para o client na mesma url que foi solicitada.

Se você quer pular direto para o código clica aqui

O cenário

Bom, voltando ao assunto, em nosso cenário, cada cliente possui sua própria infraestrutura e conjuntos de AWS Lambdas para responder a chamadas HTTP. Suponhamos que estamos utilizando Lambda Function Urls para isso, onde cada tenant tem uma série de URLs exclusivas que podem ser chamadas. Essas URLs podem se parecer com algo como:

https://<url-id>.lambda-url.<region>.on.aws

Neste mesmo cenário imagine que temos 3 serviços para cada tenant e cada um tem sua própria Lambda Function Url.

  • Users -> https://abc123.lambda-url.sa-east-1.on.aws
  • Dogs -> https://def456.lambda-url.sa-east-1.on.aws
  • Cats -> https://ghi789.lambda-url.sa-east-1.on.aws

E eu gostaria que as URLs fossem mais amigáveis, sendo elas respectivamente:

  • Users -> https://users-<tenant>.euquero.cafe
  • Dogs -> https://dogs-<tenant>.euquero.cafe
  • Cats -> https://cats-<tenant>.euquero.cafe

Como resolver isso?

Como já estamos utilizando CDK para criar nossa infraestrutura eu poderia então para cada tenant criar uma 3 Distribuições CloudFront (uma para cada serviço) e fazer os mapeamentos de Origins apontando para minhas Lambda Function Urls e também registrar 3 domínios no Route 53 apontando para os respectivos CloudFronts... Mas vocês já conseguem imaginar que adicionar novos serviços aqui significa adicionar mais CDNs, mais registros de DNS...

Neste exemplo tenho somente 3 serviços mas imagine agora +10 serviços, seriam muitos CloudFronts a serem criados. Sem falar que para criação de ambientes de desenvolvimento toda vez que você cria um CloudFront ele leva muito tempo para ser publicado o que faria o tempo de deploy dos ambientes aumentar significativamente.

Certo... que outra opção temos?

Sabemos que o Route 53 (DNS), o ACM (certificados) e o CloudFront (CDN) aceitam domínios wildcard (*.euquero.cafe).

Formato de nome de domínio DNS - Amazon Route 53
Descreve o formato de nomes de domínio DNS que você especifica para o Amazon Route 53.
Amazon CloudFront Announces Wildcard CNAME Support - AWS
Discover more about what’s new at AWS with Amazon CloudFront Announces Wildcard CNAME Support

Logo, podemos fazer uso disso para rotear baseado nos subdomínios dinamicamente, certo?

Sim, mas como?

Isso pode ser alcançado configurando uma única distribuição CloudFront que atua como um ponto de entrada para todas as solicitações. Em seguida, podemos usar Lambda@Edge para examinar o cabeçalho de host da solicitação e encaminhar a solicitação para o endpoint adequado com base no subdomínio da requisição.

Show me the code

Agora vem a parte divertida.

Vamos dividir nossa implementação em duas partes distintas: a infraestrutura, construída utilizando o AWS CDK, que será responsável por provisionar todos os componentes necessários, incluindo CloudFront, certificados SSL, configurações DNS, definições de permissões, funções Lambda e suas associações (toda parafernalha necessária para soluções cloud modernas 😆)

E a lambda de Origin Request que é responsável por instruir o CloudFront onde está o endpoint que ele deve fazer o request baseado no subdomínio recebido no request.

Neste exemplo, implementei um roteamento estático usando um mapa DE -> PARA. No entanto, em um cenário real, é provável que você precise ler as informações de roteamento de uma fonte de dados dinâmica, como o Amazon DynamoDB ou outro Key Store. É importante lembrar que as funções Lambda@Edge não podem ter variáveis de ambiente (sim, também acho isso estranho!). E também, para garantir a melhor performance possível, sua implementação deve ser altamente eficiente, pois será executada em cada solicitação recebida. Escolha sabiamente o seu banco de dados e faça uso de caches locais (a.k.a. new Map()) para otimizar o desempenho.

O repositório completo se encontra aqui https://github.com/euquero-cafe/aws-cloudfront-wildcard-dynamic-routing

Aproveite para se inscrever e receber atualizações por e-mail sobre novos posts! Além disso, sinta-se à vontade para deixar sugestões nos comentários sobre qualquer tópico específico relacionado a computação em nuvem que você gostaria que fosse abordado em futuros posts.

🤘 keep rockin.