O blog da AWS

Restrinja acesso via IP para seu AWS Amplify

Por Marcelo Oliveira, Arquiteto de Soluções, AWS Brasil Setor Público; Ernesto dos Santos (Tito), Arquiteto de Soluções Senior, AWS Brasil e Leticia Dornelas, Arquiteta de Soluções, AWS Brasil.

Em determinados cenários, organizações que utilizam o AWS Amplify para suas aplicações necessitam restringir o acesso dos usuários a determinados intervalos de IP, a fim de manterem controle sobre quem está autorizado a acessa-las.

AWS Amplify é uma coleção de serviços de nuvem e bibliotecas para o desenvolvimento de aplicativos fullstack. O Amplify fornece: bibliotecas frontend, componentes de UI, construção de backend e hospedagem frontend para a construção de aplicativos fullstack na AWS.

Por meio da funcionalidade CloudFront Functions do serviço Amazon CloudFront, é possível realizar este controle de acesso de maneira eficiente.

Neste blog post você aprenderá a bloquear origens (IPs) indesejados utilizando o CloudFront Functions para restringir o acesso às suas aplicações no AWS Amplify.

Introdução

Para o cenário que detalharemos em nossas explicações, iremos considerar como ponto de partida que você já possui uma aplicação provisionada e em funcionamento através do AWS Amplify, conforme o exemplo da Figura 1:

Figura 1: Console do AWS Amplify, contendo uma aplicação web exemplo já provisionada.

Tome nota do identificador “App ID”, e do nome da branch de sua aplicação. Na Figura 2 há um exemplo de tela, onde as informações mencionadas podem ser localizadas.

Figura 2: Detalhes da aplicação no console do AWS Amplify, onde você encontrará o ID da aplicação ao lado do texto “App ID:”, e o nome da branch em uma caixa de textos abaixo do texto “Production branch”.

Obs: Estas duas informações serão necessárias para a configuração do “Origin domain” da distribuição Amazon Cloudfront que você irá criar mais adiante.

Arquitetura Macro

Na Figura 3, temos um diagrama simplificado de como a integração do Amazon CloudfrontCloudfront Functions, e AWS Amplify acontecerá.

Figura 3: Diagrama de arquitetura, demonstrando o fluxo de acessos partindo dos usuários, chegando ao Amazon CloudFront e CloudFront Function, posteriormente chegando ao AWS Amplify.

Configurando o “CloudFront Functions”

Iremos iniciar pela criação de uma nova função no Cloudfront Functions. Para isso, você deve selecionar a opção “Functions” no console do serviço Amazon Cloudfront, conforme indicado no exemplo da Figura 4:

Figura 4: Console do Amazon CloudFront, com opção “Functions” destacada.

Na tela da opção “Functions”, selecione o botão “Create function”, conforme indicado no exemplo da Figura 5:

Figura 5: Tela da funcionalidade “Functions” no console do serviço Amazon CloudFront, indicando a localização do botão “Create function”.

Na tela de criação de uma nova “CloudFront Functions”, escolha um nome para a sua função (como por exemplo: “restrictip”). Opcionalmente, adicione uma descrição para a função. Por fim escolha o “runtime” a ser utilizado, neste caso utilize o “cloudfront-js-2.0”. Na Figura 6 temos um exemplo desta configuração.

Figura 6: Tela “Create functions”, contendo um exemplo das informações necessárias, onde temos o nome da função “restrictip”, uma descrição “Restrict source IP”, e a seleção do runtime a ser utilizado, neste caso selecionamos “cloudfront-js-2.0”.

Após criada a função, você deve adicionar o código. Para isso utilize a sessão “Function code” para adicionar o código abaixo na caixa de edição da aba “Development”. Substitua o “xx.xx.xx.xx”, e/ou o “yy.yy.yy.yy” pelo(s) IP(s) que você deseja que tenham acesso à sua aplicação.

function handler(event) {
    const request = event.request;
    const clientIP = event.viewer.ip;
    // This will contain a list of all IPs we want to allow
    const ALLOWLISTED_IP = [
        'xx.xx.xx.xx',
        'yy.yy.yy.yy'
    ];

    const shouldAllowIP = ALLOWLISTED_IP.includes(clientIP);

    if (shouldAllowIP) {
        // Allow the original request to pass through
        return request;
    } else {
        var response = {
            statusCode: 403,
            message: 'FORBIDDEN',
            body: 'IP address not authorized'
        }
        // Send error message
        return response;
    }
}

Adicione o código à função recém criada, salvando a configuração selecionando o botão “Save changes”, conforme o exemplo da Figura 7.

Figura 7: Tela do CloudFront Function, contendo o código da função, onde podemos realizar a gravação das modificações através do botão “Save changes”, situado na sessão “Function Code”.

Após gravar a função recém criada, a mesma estará pronta para ser publicada, situando-se no status de “Unpublished changes”, conforme você pode verificar no exemplo da Figura 8.

Figura 8: Tela com os dados relacionados à função recém salva no “CloudFront Functions”, com a mensagem de status “Unplublished changes”, ao lado da aba “Publish”.

A partir deste momento, você poderá validar previamente o funcionamento da função, antes de publicá-la. Para isso você deve selecionar a aba “Test”, e com base no formulário apresentado, fornecer um IP que faça parte dos IPs pertencentes a lista de IPs previamente cadastrada no código da função. Selecione então o botão “Test function”. Nas Figuras 9a e 9b temos exemplos de uma função sendo testada.

Figura 9a: Aba “Test” e botão “Test function”, na tela do “CloudFront function”. Na caixa de texto “IP address” será fornecido o IP de origem a ser testado.

Figura 9b: Exemplo de teste realizado com sucesso, onde em uma caixa verde verifica-se o termo “Status Succeeded”, abaixo do termo “Execution result”.

Com a função validada, chega o momento de publicá-la. Para isso, selecione a aba “Publish”, e na sequencia selecione o botão “Publish function”, conforme podemos verificar no exemplo da Figura 10.

Figura 10: Exemplo de tela de uma “CloudFront Functions”, onde ao lado da aba “Publish” temos o texto “Unplublished changes”, o que indica que a função encontra-se pronta para ser publicada.

Configurando o “CloudFront Distribution”

Após você ter criado e publicado a “CloudFront Functions”, chega o momento de cria uma nova “CloudFront Distribution”. Para criar a distribuição de Amazon Cloudfront, acesse a console do serviço e selecione o botão “Create a CloudFront distribution”, o que pode ser verificado no exemplo da Figura 11.

Figura 11: Console inicial do serviço “Amazon CloudFront”, onde a partir do botão “Create a CloudFront distribution”, podemos criar uma nova distribuição.

Ao selecionar o botão “Create a CloudFront distribution”, você será levado a um formulário onde informará as propriedades da nova “CloudFront Distribution” a ser criada.

No início do formulário você encontrará a sessão “Origin”.
Para a propriedade “Origin domain” você precisará informar a URL de sua aplicação AWS Amplify, baseado-se nas duas informações que você tomou nota no início do blogpost: “App ID”, e “Branch name”.

O formato da URL informada deve seguir o seguinte padrão: “<<Branch name>>.<<AppID>>.amplifyapp.com”, onde você deve substituir o termo << Branch name>> pelo nome da branch anotado, e substituir o termo <<AppID>> pelo o ID da aplicação Amplify também anotado. Você pode conferir um exemplo de URL na Figura 12.

Figura 12: Tela de criação de uma “CloudFront distribution”, com um exemplo de URL usada na propriedade “Origin domain”, seguindo neste caso o padrão de URLs de aplicações do Amplify (“<>.<>.amplifyapp.com”).

Ainda na sessão “Origin”, deixe a propriedade “Origin path” em branco.
Para a propriedade “Name”, adicione um nome para esta origem.
Deixe a propriedade “Add custom header” como está, ou seja, sem a adição de cabeçalhos adicionais.
Deixe a propriedade “Enable Origin Shield” com a seleção padrão, ou seja, “No”.
Também não há necessidade de modificar as informação em “Additional settings”.

Passe para a próxima sessão de configurações “Default cache behavior”.
Deixe as duas primeiras propriedades “Path pattern” e “Compress objects automatically” sem mudanças.
Ajuste a propriedade “Viewer protocol policy” para ao valor “Redirect HTTP to HTTPS”.
Ajuste a propriedade “Allowed HTTP methods” para o valor “GET, HEAD”.
Deixe as próximas propriedades desta sessão sem mudanças.
Você pode verificar um exemplo das mudanças indicadas na sessão “Default cache behavior” na Figura 13.

Figura 13: Sessão “Default cache behavior”, onde pode-se verificar a mudança na propriedades “Viewer protocol policy” para o valor “Redirect HTTP to HTTPS , e “Allowed HTTP methods” para o valor “GET, HEAD”.

Na sessão “Function associations – optional”, altere a propriedade “Function type” da linha “Viewer request” para o valor “CloudFront Functions”, o que irá permitir a você que selecione através da propriedade “Function ARN / Name” a função recém criada (em nosso exemplo “restrictip”).
Deixe as 3 outras propriedades “Viewer response”, “Origin request”, e “Origin response” com os valores default, ou seja, “No association”.
Você pode verificar um exemplo desta ação de ajustes de propriedades na Figura 14.

Figura 14: Sessão “Function associations – optional”, onde na linha “Viewer request” ajusta-se a propriedade “Function type” para o valor “CloudFront Function”, assim permitindo selecionar a função recém criada na coluna “Function ARN / Name”.

Agora você passará para a sessão “Web Application Firewall (WAF)”.
Como neste blogpost não nos aprofundaremos nas configurações de proteção de perímetro, siga por não utilizar uma configuração de AWS WAF associada a “CloudFront distribution”. Para isso selecione “Do not enable security protection”.
Caso você deseje habilitar o uso do AWS WAF em sua distribuição de CloudFront, você pode utilizar-se desta referência para isso.
Você pode verificar um exemplo da sessão “Web Application Firewall (WAF)” na Figura 15.

Figura 15: Sessão “Web Application Firewall (WAF)”, onde seleciomos a opção “Do not enable security protection”.

Na sessão “Settings”, não modifique as propriedades, deixando-as com os valores default.

Para concluir a configuração da “CloudFront distribution”, selecione o botão “Create distribution”.

A partir deste ponto você já poderá testar a configuração realizada, utilizando-se da URL provisionada pela “CloudFront distribution” recém criada.
Para ter acesso à esta URL a partir da console do Amazon CloudFront, selecione a opção “Distributions” no menu à esquerda da tela. Na lista de distruições apresentada, encontre a linha com as informações da distribuição que você acabou de criar. Anote a URL presente na coluna “Domain name”, utilizando-a em um browser de sua preferência.

Você poderá verificar um exemplo de como obter a URL de sua “CloudFront distribution” na Figura 16.

Figura 16: Tela contendo lista de “CloudFront distributions”, onde podemos verificar a URL de cada uma das distribuições a partir da coluna “Domain name”.

Cleaning-up

Caso você tenha realizado o passo-a-passo compartilhado neste blogpost, mas decida não utilizar esta abordagem no futuro, afim de evitar a geração de custos relacionados, basta que você efetue a deleção dos recursos “CloudFront distribution” e “CloudFront function” recém criados.

Para isso, o primeiro passo será desabilitar a “CloudFront distribution” que você criou.
A partir da console do Amazon CloudFront, selecione no menu à esquerda a opção “Distributions”, localize a distribuição a ser desabilitada, selecione o “check-box” ao lado do ID referente a linha da distribuição que você deseja desabilitar, selecionando na sequencia o botão “Disable”. Você pode verificar um exemplo deste passo na Figura 17.

Figura 17: Tela do Amazon CloudFront, onde através da opção “Distributions”, escolhe-se uma distribuição específica para ser desabilitada, via seleção do botão “Disable”.

Após alguns segundos, a operação de “Disable” estará concluída. Com isso você poderá seguir para a ação de deleção da “CloudFront distribution”. Na console do “Amazon CloudFront”, selecione no menu à esquerda a opção “Distributions”, localize a distribuição a ser deletada, selecione o “check-box” ao lado do ID referente a linha da distribuição que você deseja deletar, selecionando na sequencia o botão “Delete”. Você pode verificar um exemplo deste passo na Figura 18.

Figura 18: Tela do Amazon CloudFront, onde através da opção “Distributions”, escolhe-se uma distribuição específica já desabilitada, para ser deletada via seleção do botão “Delete”.

Após o passo de deleção da “CloudFront distribution” ter sido concluído, siga para a deleção da “CloudFront Function”. Para isso, a partir da console do Amazon CloudFront, selecione no menu à esquerda a opção “Functions”, localize a função a ser deletada, selecione o “check-box” ao lado do nome referente a linha da função que você deseja deletar, selecionando na sequencia o botão “Delete”. Você pode verificar um exemplo deste passo na Figura 19.

Figura 19: Tela do Amazon CloudFront, onde através da opção “Functions”, escolhe-se uma “CloudFront Function” específica para ser deletada, via seleção do botão “Delete”.

Conclusão

Neste blogpost tivemos a oportunidade de verificar que com o uso conjunto de serviços AWS, como o AWS Amplify e o Amazon Cloudfront, valendo-se de funcionalidades como o Cloudfront Functions, podemos restringir o acesso originados de IPs indesejados à aplicações criadas no AWS Amplify, cumprindo assim com possíveis requisitos de conformidade.

A abordagem apresentada é completamente serverless, o que a torna eficiente sob o ponto de vista de custos, além de prover capacidade de escalabilidade.

Sobre os Autores

Marcelo Oliveira é Arquiteto de Soluções na AWS para clientes do setor público de saúde. Tem foco em projetos que envolvam arquiteturas distribuídas e escaláveis, além de grande interesse nas áreas de Infraestrutura, Networking e Segurança da Informação.
Ernesto dos Santos (Tito) é Arquiteto de Soluções na AWS para clientes ISV, e está a 6 anos na AWS. Tito possui interesse em projetos que envolvam arquiteturas distribuídas e escaláveis, Cyber Security, Infraestrutura, Networking, e Serverless.

Letícia Dornelas é Arquiteta de Soluções na AWS para clientes do setor Financeiro. Trabalha com desenvolvimento de sistemas há mais de 8 anos, atuando em projetos com arquiteturas distribuídas, escaláveis e resilientes. Formada pelo IFSP, tem um interesse especial em Machine Learning, Databases, Serverless, e por livros.