Controles proactivos para Lambda con AWS CloudFormation Guard - AWS Lambda

Controles proactivos para Lambda con AWS CloudFormation Guard

AWS CloudFormation Guard es una herramienta de evaluación de políticas como código, de uso general y de código abierto. Se puede utilizar para la gobernanza y el cumplimiento preventivos mediante la validación del cumplimiento con las políticas de las plantillas de infraestructura como código (IaC) y las composiciones de los servicios. Estas reglas se pueden personalizar en función de los requisitos de su equipo o de la organización. En el caso de las funciones Lambda, las reglas de Guard se pueden utilizar para controlar la creación de recursos y las actualizaciones de configuración mediante la definición de los ajustes de propiedades necesarios al crear o actualizar una función de Lambda.

Los administradores de conformidad definen la lista de controles y políticas de gobernanza que se requieren para implementar y actualizar las funciones de Lambda. Los administradores de la plataforma implementan los controles en los canales de CI/CD, como webhooks de validación previa a la confirmación con repositorios de código, y proporcionan a los desarrolladores herramientas de línea de comandos para validar las plantillas y el código en las estaciones de trabajo locales. Los desarrolladores crean el código, validan las plantillas con herramientas de línea de comandos y, a continuación, envían el código a los repositorios, que luego se validan automáticamente mediante los canales de CI/CD antes de la implementación en un entorno de AWS.

Guard le permite escribir sus reglas e implementar sus controles con un lenguaje específico del dominio, como se muestra a continuación.

Guard rules include resource type, property name, operator, expression value, and optional comment

Por ejemplo, imagine que quiere asegurarse de que los desarrolladores elijan solo los tiempos de ejecución más recientes. Puede especificar dos políticas diferentes, una para identificar los tiempos de ejecución que ya están en desuso y otra para identificar los tiempos de ejecución que caducarán pronto. Para ello, puede escribir el siguiente archivo de 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.>> } } } }

Ahora imagine que escribe la siguiente plantilla de CloudFormation de iac/lambda.yaml que define una función de 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

Tras instalar la utilidad Guard, valide la plantilla:

cfn-guard validate --rules etc/rules.guard --data iac/lambda.yaml

El resultado tendrá este aspecto:

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 } } } }

Guard les permite a los desarrolladores comprobar desde sus estaciones de trabajo locales que necesitan actualizar la plantilla para utilizar un tiempo de ejecución permitido por la organización. Esto ocurre antes de iniciar sesión en un repositorio de código y no pasar las comprobaciones realizadas en los canales de CI/CD. Como resultado, los desarrolladores reciben esta información sobre cómo desarrollar plantillas compatibles y dedicar su tiempo a escribir código que aporte valor empresarial. Este control se puede aplicar en la estación de trabajo del desarrollador local, en un webhook de validación previo a la confirmación, o en el canal de CI/CD antes de la implementación.

Advertencias

Si utiliza plantillas de AWS Serverless Application Model (AWS SAM) para definir funciones de Lambda, tenga en cuenta que debe actualizar la regla de Guard para buscar el tipo de recurso AWS::Serverless::Function de la siguiente manera.

let lambda_functions = Resources.*[ Type == "AWS::Serverless::Function" ]

Guard también espera que las propiedades se incluyan en la definición del recurso. Mientras tanto, las plantillas de AWS SAM permiten especificar las propiedades en una sección Global independiente. Las propiedades que se definen en la sección Global no se validan con sus reglas de Guard.

Como se indica en la documentación de solución de problemas de Guard, tenga en cuenta que Guard no admite tipos intrínsecos abreviados, como !GetAtt o !Sub. Por el contrario, requiere el uso de formas expandidas, como Fn::GetAtt y Fn::Sub. (El ejemplo anterior no evalúa la propiedad Role, por lo que se utilizó el tipo intrínseco abreviado por simplicidad).