Controles proativos para Lambda com AWS CloudFormation Guard
O AWS CloudFormation Guard é uma ferramenta de avaliação de política como código de código aberto e de uso geral. Ele pode ser usado para governança e conformidade preventivas, ao validar modelos de Infraestrutura como Código (IaC) e composições de serviços em relação às regras de política. Essas regras podem ser personalizadas com base nos requisitos da equipe ou da organização. Para funções do Lambda, as regras do Guard podem ser usadas para controlar a criação de recursos e as atualizações de configuração, ao definir as configurações de propriedade necessárias durante a criação ou a atualização de uma função do Lambda.
Os administradores de conformidade definem a lista de controles e políticas de governança que são necessários para implantar e atualizar as funções do Lambda. Os administradores da plataforma implementam os controles em pipelines de CI/CD, como webhooks de validação pré-confirmação com repositórios de código, e fornecem aos desenvolvedores ferramentas de linha de comando para validar modelos e códigos em estações de trabalho locais. Os desenvolvedores criam código, validam modelos com ferramentas de linha de comando e, em seguida, confirmam o código nos repositórios, que são validados automaticamente por meio dos pipelines de CI/CD antes da implantação em um ambiente da AWS.
O Guard permite que você crie suas regras e implemente seus controles com uma linguagem específica de domínio como a seguir.
Por exemplo, suponha que você deseje garantir que os desenvolvedores escolham somente os runtimes mais recentes. Você pode especificar duas políticas diferentes, uma para identificar os runtimes que já estão descontinuados e outra para identificar os runtimes que serão descontinuados em breve. Para fazer isso, você pode criar o seguinte arquivo etc/rules.guard
:
let lambda_functions = Resources.*[ Type == "AWS::Lambda::Function" ] rule lambda_already_deprecated_runtime when %lambda_functions !empty { %lambda_functions { Properties { when Runtime exists { Runtime !in ["dotnetcore3.1", "nodejs12.x", "python3.6", "python2.7", "dotnet5.0", "dotnetcore2.1", "ruby2.5", "nodejs10.x", "nodejs8.10", "nodejs4.3", "nodejs6.10", "dotnetcore1.0", "dotnetcore2.0", "nodejs4.3-edge", "nodejs"] <<Lambda function is using a deprecated runtime.>> } } } } rule lambda_soon_to_be_deprecated_runtime when %lambda_functions !empty { %lambda_functions { Properties { when Runtime exists { Runtime !in ["nodejs16.x", "nodejs14.x", "python3.7", "java8", "dotnet7", "go1.x", "ruby2.7", "provided"] <<Lambda function is using a runtime that is targeted for deprecation.>> } } } }
Agora, suponha que você crie o seguinte modelo iac/lambda.yaml
do CloudFormation que define uma função do Lambda:
Fn: Type: AWS::Lambda::Function Properties: Runtime: python3.7 CodeUri: src Handler: fn.handler Role: !GetAtt FnRole.Arn Layers: - arn:aws:lambda:us-east-1:111122223333:layer:LambdaInsightsExtension:35
Depois de instalar o utilitário Guard, valide o modelo:
cfn-guard validate --rules etc/rules.guard --data iac/lambda.yaml
O resultado se parece com:
lambda.yaml Status = FAIL FAILED rules rules.guard/lambda_soon_to_be_deprecated_runtime --- Evaluating data lambda.yaml against rules rules.guard Number of non-compliant resources 1 Resource = Fn { Type = AWS::Lambda::Function Rule = lambda_soon_to_be_deprecated_runtime { ALL { Check = Runtime not IN ["nodejs16.x","nodejs14.x","python3.7","java8","dotnet7","go1.x","ruby2.7","provided"] { ComparisonError { Message = Lambda function is using a runtime that is targeted for deprecation. Error = Check was not compliant as property [/Resources/Fn/Properties/Runtime[L:88,C:15]] was not present in [(resolved, Path=[L:0,C:0] Value=["nodejs16.x","nodejs14.x","python3.7","java8","dotnet7","go1.x","ruby2.7","provided"])] } PropertyPath = /Resources/Fn/Properties/Runtime[L:88,C:15] Operator = NOT IN Value = "python3.7" ComparedWith = [["nodejs16.x","nodejs14.x","python3.7","java8","dotnet7","go1.x","ruby2.7","provided"]] Code: 86. Fn: 87. Type: AWS::Lambda::Function 88. Properties: 89. Runtime: python3.7 90. CodeUri: src 91. Handler: fn.handler } } } }
O Guard permite que os desenvolvedores vejam em suas estações de trabalho locais que precisam atualizar o modelo para usar um runtime permitido pela organização. Isso acontece antes da confirmação em um repositório de código e, posteriormente, apresentar falha nas verificações em um pipeline de CI/CD. Como resultado, os desenvolvedores recebem esse feedback sobre como desenvolver modelos em conformidade e dedicar o tempo à criação de códigos que agreguem valor comercial. Esse controle pode ser aplicado na estação de trabalho local do desenvolvedor, em um webhook de validação pré-confirmação e/ou no pipeline de CI/CD antes da implantação.
Advertências
Se você estiver usando modelos do AWS Serverless Application Model (AWS SAM) para definir funções do Lambda, saiba que precisa atualizar a regra do Guard para pesquisar o tipo de recurso AWS::Serverless::Function
como a seguir.
let lambda_functions = Resources.*[ Type == "AWS::Serverless::Function" ]
O Guard também espera que as propriedades sejam incluídas na definição do recurso. Enquanto isso, os modelos do AWS SAM permitem que as propriedades sejam especificadas em uma seção Globals separada. As propriedades definidas na seção Globals não são validadas com suas regras do Guard.
Conforme descrito na documentação de solução de problemas do Guard, saiba que o Guard não é compatível com funções intrínsecas de forma abreviada, como !GetAtt
ou !Sub
. Em vez disso, ele requer o uso de formas expandidas: Fn::GetAtt
e Fn::Sub
. (O exemplo anterior não avalia a propriedade Role. Portanto, a função intrínseca de forma abreviada foi usada para simplificar.)