Um aspecto crítico dos serviços de nuvem é a comunicação de serviço para serviço, e é essencial fazê-lo com segurança. Como um arquiteto que projetou serviços centralizados de autenticação e autorização no CyberArk , um provedor de SaaS de segurança cibernética, compartilharei minha opinião sobre o desenvolvimento de um mecanismo de autorização seguro entre serviços baseados em nuvem da AWS, sejam eles sem servidor ou contêineres.
Quando terminar de ler este post, você não só entenderá a importância da autenticação e autorização de serviço, mas também estará equipado com o conhecimento prático para implementá-lo. Isso inclui habilitar com segurança o acesso entre contas a recursos para padrões de comunicação síncronos e assíncronos usando o AWS IAM.
Esta postagem inclui políticas JSON IAM e exemplos de código Python.
Esta postagem é o primeiro de muitos tópicos de segurança de uma próxima série.
Índice
Conceitos de autenticação e autorização
A autenticação é:
Autenticação é o ato de provar uma afirmação , como a identidade de um usuário de sistema de computador - Wikipédia
e a autorização por outro lado é:
a função de especificar direitos/privilégios de acesso a recursos - Wikipédia
Em um serviço seguro, autenticação e autorização andam de mãos dadas.
Como desenvolvedor de serviços que expõe uma API REST, é crucial entender que deixar sua API aberta ao mundo sem medidas adequadas de autenticação e autorização pode levar ao acesso não autorizado e à manipulação de dados.
Você quer que seu serviço primeiro aceite solicitações de comunicação de serviços autenticados ( principais ), aqueles cujas identidades são comprovadamente confiáveis e conhecidas. Uma vez identificados, você quer garantir que somente serviços autorizados possam acionar sua API ou se comunicar com seu serviço.
Ou em outras palavras:
A autenticação tem como objetivo validar e identificar que um principal é quem ele afirma ser.
A autorização visa definir o relacionamento entre princípios, ações e recursos.
Podemos visualizar a autorização como o relacionamento entre os principais (serviços), as ações que eles desejam realizar (por exemplo, executar uma API) e os recursos (o ponto de extremidade da API REST real e o tipo de ação HTTP) nos quais suas ações são chamadas.
Portanto, a segurança da comunicação entre serviços se divide em duas partes:
Primeiro, certifique-se de que o serviço de atendimento ao cliente é quem diz ser.
Em seguida, afirme que ele tem permissão para agir sobre o recurso.
Para entender melhor a importância da autenticação e autorização, vamos considerar alguns exemplos reais de comunicação de serviço para serviço. Esses exemplos destacarão o papel crítico que essas medidas desempenham para garantir a segurança e a integridade dos seus serviços de API REST.
Padrões de comunicação de serviço para serviço
Os desenvolvedores gastam muito tempo projetando APIs REST voltadas para o cliente. Essas APIs introduzem o conceito de usuários ao sistema (humanos ou não humanos). A AWS oferece várias opções de autenticação de usuário e serviço, como Amazon Cognito (Cognito) e AWS Identity and Access Management (IAM), com o IAM sendo a principal opção para gerenciamento interno de usuários, enquanto o Cognito pode se conectar a provedores de identidade externos, como CyberArk e outros.
Nesta postagem, gostaria de me concentrar no AWS IAM e seu papel em um aspecto importante dos serviços de nuvem: autenticação e autorização de serviço para serviço.
Padrão de Comunicação Síncrona
Neste caso de uso, há três serviços: A, B e C, cada um em sua própria conta da AWS.
O serviço A é serverless, B é baseado em EC2 e C serve uma API REST com API Gateway. Ambos os serviços A e B usam a API do serviço C.
*Esta parte discute API Gateways públicas. Para API Gateway privada, visite o apêndice abaixo.
No entanto, o serviço C quer garantir que apenas esses serviços específicos possam chamar sua API e talvez até mesmo ajustá-la para ser a menos privilegiada, de modo que apenas uma função Lambda específica ou uma máquina EC2 possa acioná-la.
Vamos continuar com o segundo padrão.
Padrão de comunicação assíncrona
Neste caso de uso, temos nossos três serviços como antes:
A, B e C, cada um em sua própria conta AWS, como antes.
C contém um tópico SNS usado como um serviço de comunicação assíncrona centralizado entre publicador e assinante na organização.
O Serviço A é o publicador, e o Serviço B assina o tópico SNS por meio de uma fila SQS.
O Serviço C quer garantir que somente o Serviço A possa publicar mensagens no tópico do SNS e que somente o SQS do Serviço B possa assinar o tópico, pois o tópico contém dados que você deve manter privados.
Este é um padrão. Você pode trocar o tópico SNS para o barramento EventBridge ou qualquer serviço de mensagens que você tenha, como o Amazon MSK.
Contas únicas ou múltiplas
Como nota lateral, os serviços A, B e C podem compartilhar a mesma conta, simplificando a solução IAM. No entanto, seguir as práticas IAM neste post é essencial, mesmo que todos os serviços compartilhem a mesma conta, não pegue atalhos. Você pode acabar movendo-os para contas diferentes no futuro. Nesse caso, se você não seguir as melhores práticas, terá dificuldades e muitas mudanças drásticas pela frente (por experiência!). É melhor sempre seguir as melhores práticas, especialmente quando a segurança está envolvida.
Autenticação e autorização baseadas em IAM
Agora que abordamos os conceitos básicos de autenticação e autorização de serviço para serviço, vamos discutir a solução e começar a usar o AWS IAM.
Esta solução abrangerá autenticação e autorização entre contas e na mesma conta.
A autenticação IAM significa que um principal, por exemplo, o serviço A, deve ser autenticado (conectado à AWS) usando suas credenciais para enviar uma solicitação a outros serviços e recursos da AWS, ou ao serviço C em nosso exemplo. Uma vez autenticado, podemos aproveitar o IAM novamente para garantir que o serviço A esteja autorizado a acessar o serviço C.
Combinaremos dois aspectos do IAM, políticas baseadas em identidade e baseadas em recursos , e forneceremos soluções de autorização para comunicações síncronas e assíncronas. Também forneceremos acesso entre contas, ou seja, serviços de autorização de diferentes contas da AWS usando o mecanismo de "assumir função" ou delegação .
Políticas baseadas em recursos
De acordo com a documentação da AWS:
As políticas baseadas em recursos concedem ao principal especificado permissão para executar ações específicas naquele recurso e definem em quais condições isso se aplica
Políticas baseadas em recursos são documentos de política JSON que você anexa a um recurso, como API Gateway, SNS, S3 ou outro recurso. Com políticas baseadas em recursos, você pode especificar quem tem acesso ao recurso e quais ações eles podem executar nele. Além disso, elas podem ser usadas para permitir acesso entre contas. Parece perfeito, certo? Bem, ele tem limitações, e nem todos os tipos de recursos o suportam.
Vamos rever os prós e contras desse mecanismo de IAM.
Prós:
Simples de definir
Habilite o acesso entre contas.
Contras:
Nem todos os recursos da AWS oferecem suporte a políticas baseadas em recursos.
Políticas baseadas em recursos têm um limite de tamanho como todas as políticas do IAM. Quando você define mais recursos e atinge o tamanho máximo, não há como superá-lo, a não ser provisionando um novo recurso (duplicando parte do serviço C, basicamente).
Assumir Mecanismo de Função
O mecanismo de delegação de acesso do IAM, ou "assumir função", como eu o chamo, é crucial na comunicação entre contas. No entanto, ele também pode ser usado em um cenário de conta única da AWS.
O mecanismo de delegação de acesso do IAM é sustentado por políticas baseadas em identidade anexadas a uma função do IAM. Em nosso exemplo, a função C está localizada na conta do serviço C e concede acesso ao recurso em questão, que pode ser o API Gateway do serviço C ou o tópico SNS.
Quem tiver permissão para assumir a função C pode obter um conjunto de credenciais de segurança temporárias do IAM que podem ser usadas para autenticação e autorização para se comunicar com o serviço C, seja para executar uma chamada de API REST ou publicar uma mensagem em um tópico do SNS.
Supondo que uma função envolva fazer uma chamada do AWS SDK para o Amazon STS (AWS Security Token Service) e utilizar as credenciais temporárias da resposta do SDK para iniciar uma sessão de comunicação com o serviço com o qual você pretende se comunicar, exploraremos exemplos de código mais adiante nesta postagem para garantir que você tenha uma compreensão abrangente do processo.
No entanto, precisamos definir quem pode assumir essa função e obter acesso ao serviço C. Precisaremos de políticas de recursos e definir que apenas as funções dos serviços A e B podem assumir a função C e obter acesso ao serviço C.
Vamos rever os prós e contras desse mecanismo de IAM.
Prós:
Suporta todos os tipos de recursos, desde que você possa definir uma política de IAM.
Abstrai o recurso e seu ARN. Você obtém uma função que fornece acesso a você. O recurso pode mudar amanhã, mas tudo o que você sabe é a função do ARN e qual chamada de API usar. Suponha que a chamada seja abstraída em um SDK organizacional que encapsula o recurso. Nesse caso, você pode alterar os recursos — tópico SNS para barramento EventBridge — e manter as alterações nas políticas de função e níveis de implementação do SDK, mas os ARNs de função permanecem os mesmos.
A política baseada em recursos da função C, que define quem pode assumir a função, tem um limite de tamanho. No entanto, se quisermos adicionar mais serviços, podemos provisionar uma nova função para novos serviços assumirem. Diferentemente do mecanismo anterior, não há problema em provisionar um novo recurso, pois o recurso protegido permanece inalterado (API Gateway ou tópico SNS).
Contras:
É mais complicado de definir e requer uma função extra no serviço C.
Assumir uma função é outra chamada do AWS SDK que estende o tempo de execução geral dos serviços A e B e torna o código mais propenso a erros de manutenção.
Vamos ver como podemos resolver nossos problemas de autenticação e autorização em padrões de comunicação síncronos e assíncronos com políticas concretas de IAM e exemplos de código Python.
Solução IAM de comunicação síncrona
Vamos começar aumentando a segurança do serviço C, o API Gateway. Primeiro, adicionaremos um autorizador IAM a todos os seus endpoints de API. Ao fazer isso, por padrão, todas as solicitações não autenticadas e não autorizadas são negadas.
Quando a autorização do IAM está habilitada, os clientes devem usar o Signature Version 4 para assinar suas solicitações com credenciais da AWS. O API Gateway invoca sua rota de API somente se o cliente tiver permissão execute-api para a rota. - Documentação da AWS
O AWS IAM garante que apenas solicitações com credenciais IAM autenticadas e válidas que sejam autorizadas executarão a API C do serviço. Tudo o que resta é que o serviço C defina quais serviços (identidades) e contas AWS podem executar suas APIs.
Há duas maneiras de conseguir isso:
Política baseada em recursos
Políticas baseadas em identidade assumem mecanismos de função.
Vamos começar com uma política baseada em recursos.
Política baseada em recursos
Neste caso de uso, precisamos alterar a política baseada em recursos do API Gateway do serviço C para permitir que os serviços A e B (sua função Lambda, por exemplo, ou sua conta inteira ou endpoint VPC etc.) executem a API e seus endpoints. Podemos definir uma definição refinada e decidir exatamente com qual endpoint cada serviço pode se comunicar.
Aqui está um exemplo de uma política de recursos para o serviço C API Gateway:
Nas linhas 8-9, podemos permitir uma conta inteira (principal), como a conta do serviço A ou a conta do B ou um ARN de função específico em uma conta diferente (uma opção melhor, menos privilegiada), a ação (linha 12) de executar um ponto de extremidade de API. Podemos definir o ponto de extremidade exato e o comando HTTP na linha 14.
Esteja ciente de que, no momento, você não pode usar esse mecanismo em um HTTP API Gateway, apenas a variante REST.
Nos serviços A e B, eles devem definir suas funções (função Lambda para o serviço A) com permissões de identidade para a mesma ação especificada na política de recursos. Para acesso entre contas, você deve definir a política dessa maneira bilateral. O serviço A define a função de sua função Lambda com permissões para chamar o serviço C API Gateway, e o serviço C permite que o serviço A o chame da outra conta.
Além disso, os serviços A e B devem enviar suas credenciais do IAM ( Sig v4 ) no cabeçalho de autorização HTTP ao chamar o API Gateway do serviço C.
Aqui está um exemplo em Python para esse processo:
Neste exemplo, usamos a função do nosso serviço para autenticar com o IAM, criar os cabeçalhos de autenticação na linha 7 e enviá-los na linha 15.
Assumir papel
Neste caso de uso, precisamos criar uma função na conta do serviço C com permissões para executar o API Gateway e deixar que um principal nos serviços A e B assuma isso.
Podemos começar definindo a política de confiança da função — nessa política, o recurso é a própria função.
Deixamos que funções específicas (principais) dos serviços A e B assumam essa função (ação). Podemos dar um escopo mais amplo para os serviços A ou B, seja para a conta inteira ou para uma função com um prefixo predefinido. No entanto, geralmente é melhor minimizar o escopo, então um prefixo de função ou uma função específica é melhor e mais seguro.
Em seguida, precisamos adicionar as permissões da função para executar a API do serviço C à função:
Por fim, o código no serviço A é muito semelhante ao anterior, com a adição do código de função assumido.
Você pode encontrar exemplos de código para vários serviços aqui ou consultar o código abaixo.
Para 'RoleArn', você precisa fornecer a função que o serviço C cria e compartilha seu ARN com você. Normalmente, as equipes de serviço trocam ARNs manualmente. Eu recomendaria salvar esse ARN como uma variável de ambiente na função Lambda. Além disso, garanta que sua função Lambda tenha as permissões necessárias para assumir funções.
Como você pode ver abaixo, o código é muito semelhante ao exemplo de código anterior, apenas com a adição da chamada da API STS nas linhas 5 a 10 e o uso dos valores de resposta nas linhas 11 a 18.
Se você deseja aprender sobre casos de uso do API Gateway privado, consulte o apêndice .
Solução de Comunicação Assíncrona
Nosso objetivo é permitir que o serviço A publique mensagens SNS no tópico SNS do serviço C e permitir que o serviço B assine via SQS as mensagens SNS.
Vamos revisar as duas opções de implementação de IAM que temos.
Política baseada em recursos
Neste caso, definiremos uma política de acesso SNS que permita que o serviço A (principal) publique mensagens (ação) no tópico SNS do serviço C (recurso) e o serviço B assine as mensagens. Esteja ciente de que é melhor ajustar essas permissões para a função que pode publicar e para o SQS específico que pode assinar.
No lado do serviço A, a função Lambda definirá suas permissões para publicar no tópico SNS. Ter os dois lados definindo as permissões permite que eles trabalhem ao lidar com acesso entre contas.
Ao publicar uma mensagem no tópico, a função Lambda do serviço A utilizará o AWS SDK para fazer a chamada da API. O SDK usa as credenciais da função role para cuidar do lado da autenticação e autorização do IAM.
No lado do serviço B, precisamos definir uma assinatura SQS para o tópico SNS seguindo a documentação aqui .
Assumir papel
Neste caso, precisamos criar uma função na conta do serviço C com permissão para publicar mensagens no tópico SNS. Então, deixamos uma função específica do serviço A assumir essa função.
Podemos fazer isso definindo a política de confiança da função.
Podemos dar um escopo mais amplo para o serviço A para toda a conta ou uma função com um prefixo predefinido. No entanto, geralmente é melhor minimizar o escopo. Portanto, um prefixo de função ou uma função específica é melhor e mais seguro.
Em seguida, precisamos adicionar à função as permissões para publicar mensagens no tópico SNS do serviço C:
Agora, no lado do serviço A, precisamos assumir a função e usar o AWS SDK para enviar mensagens SNS. Também é importante garantir que a função Lambda role tenha permissões 'sns:Publish' e 'sts:AssumeRole'; caso contrário, as chamadas do AWS SDK não funcionarão.
As equipes de serviço precisam trocar os ARNs de recursos e números de conta para que as políticas funcionem.
O serviço A assume a função de chamada do SDK e usa as credenciais temporárias do IAM para criar um cliente boto para a chamada do SDK da mensagem de publicação do SNS.
O serviço B permanece como no exemplo de política baseada em recurso; ele pode funcionar apenas como uma política de recurso que permite que seu SQS assine. No entanto, o serviço A requer algumas alterações de código.
Escolhendo entre políticas baseadas em funções e recursos
Sua comunicação de serviço será segura com autenticação e autorização, não importa se você escolheu políticas baseadas em recursos ou solução de função assumida. No entanto, cada implementação tem seus prós e contras que podem tornar sua vida mais difícil no futuro se você ignorá-los.
Dividirei minha recomendação por diferentes parâmetros.
Suponha que eu tivesse que escolher apenas uma implementação. Nesse caso, eu seguiria o caminho 'assumir função', pois ele permite que meu serviço suporte vários serviços no futuro facilmente. Posso criar mais funções para oferecer suporte a mais serviços que as assumem e não estou limitado pelo tamanho da política do IAM.
No entanto, políticas baseadas em recursos são melhores se você só se importa com o desempenho, então não adicione outra chamada de SDK para assumir a função. Tenha em mente que essas políticas têm um tamanho máximo. Suponha que você espera conectar muitos serviços e diferentes contas da AWS (pense em uma conta central de pub-sub ou API central). Nesse caso, você encontrará essas limitações em algum momento no futuro. Como não faz sentido duplicar o tópico SNS ou o gateway de API para integração com novos serviços, é melhor escolher o caminho 'assumir função'. É mais fácil fornecer novas funções do que duplicar um tópico SNS ou um gateway de API, o que não faz sentido.
Outro fator decisivo é se os serviços estão na mesma conta e quem os mantém - a mesma equipe ou não. A política baseada em recursos é excelente para comunicação interna de serviços ou microsserviços quando a mesma equipe os mantém, pois introduz algum grau de acoplamento. Ainda assim, é aceitável, pois "permanece" na família. No entanto, suponha que diferentes contas e equipes estejam envolvidas. Nesse caso, a rota "assumir função" é melhor, pois desacopla os recursos e equipes em questão e suporta infinitas extensões futuras.
Por fim, você sempre pode alterar a implementação, então não tenha medo de escolher; apenas certifique-se de selecionar uma dessas duas opções.
Resumo
Neste post, aprendemos sobre autenticação e autorização. Também aprendemos sobre dois padrões de comunicação de serviço para serviço: assíncrono e síncrono.
Implementamos autenticação e autorização para esses padrões usando o AWS IAM. Vimos quatro implementações diferentes e discutimos seus prós e contras, seja a rota de políticas baseadas em recursos ou a de 'assumir função'.
Nas postagens a seguir, discutiremos os desafios que esses padrões trazem, como resolvê-los e como levar a autorização um passo adiante em direção ao domínio refinado.
Apêndice: Gateways de API privados e públicos
A AWS recomenda a criação de gateways de API privados para comunicação entre serviços para melhorar o desempenho, reduzir custos de rede (você não sai da rede AWS) e melhorar a segurança.
...o tráfego para sua API privada usa conexões seguras e não sai da rede da Amazon — ele é isolado da internet pública - documentação da AWS.
Na realidade, é possível e mais fácil construir APIs como API Gateways públicas. Nesse caso, autenticação e autorização se tornam mais críticas e você deve seguir as diretrizes deste post.
Private API Gateways exigem VPCs. Eles trazem complexidade extra, pois os serviços de conexão também precisam usar VPC e endpoints de VPC.
A AWS recomenda conectar as redes VPC dos serviços por meio de políticas de recursos para endpoints VPC ou peering VPC . Você pode ler mais sobre isso aqui .
Quando você usa funções serverless e Lambda e deseja se comunicar com um API Gateway privado, você precisa colocar suas funções Lambda dentro de VPCs. Isso não é o ideal, pois VPCs têm efeitos indesejados nas funções Lambda, como inicializações a frio mais longas e custos maiores.
VPCs não substituem autenticação e autorização
Quero deixar esse ponto claro.
Configurar uma conexão de rede entre diferentes endpoints VPC não significa que você implementou autenticação ou autorização de serviço ou que está 100% seguro.
Sim, ele traz uma camada extra de segurança, mas não substitui a autenticação e autorização baseadas em IAM.
Primeiro, não é o menor privilégio; qualquer serviço dentro dessas redes VPC pode acessar seu serviço. É uma camada extra de segurança, mas não substitui a autorização. Além disso, não é escalável. Quanto mais serviços você adiciona, mais "violado" seu serviço se torna, com mais VPCs e serviços que ganham acesso.
Além disso, se os invasores obtiverem acesso a uma das VPCs, eles poderão se comunicar livremente com seu serviço, pois ele aceita qualquer chamada recebida.
No entanto, combinar a autenticação e autorização do IAM com o VPC fornece a melhor e mais abrangente segurança para seu API Gateway.
Você pode aprender mais sobre esses padrões com API Gateways e VPC no vídeo abaixo:
Comments