Controlli proattivi per Lambda con AWS CloudFormation Guard
AWS CloudFormation Guard è uno strumento di valutazione policy as code open source per uso generico. Può essere utilizzato per la governance preventiva e la conformità attraverso la convalida dei modelli di infrastructure as code (IaC) e le composizioni dei servizi rispetto alle regole delle policy. Queste regole possono essere personalizzate in base ai requisiti del team o dell'organizzazione. Per le funzioni Lambda, è possibile utilizzare le regole Guard per controllare la creazione di risorse e gli aggiornamenti della configurazione definendo le impostazioni di proprietà richieste necessarie durante la creazione o l'aggiornamento di una funzione Lambda.
Gli amministratori addetti alla conformità definiscono l'elenco dei controlli e delle policy di governance necessari per l'implementazione e l'aggiornamento delle funzioni Lambda. Gli amministratori della piattaforma implementano i controlli nelle pipeline CI/CD, come webhook di convalida pre-commit con repository di codice, e forniscono agli sviluppatori strumenti a riga di comando per la convalida di modelli e codice nelle postazioni di lavoro locali. Gli sviluppatori creano codice, convalidano i modelli con strumenti a riga di comando e quindi eseguono il commit del codice nei repository, che vengono poi convalidati in automatico attraverso le pipeline CI/CD prima dell'implementazione in un ambiente AWS.
Guard ti consente di scrivere le regole e di implementare i controlli con un linguaggio specifico per il dominio come riportato di seguito.
Ad esempio, supponi di volerti assicurare che gli sviluppatori scelgano solo i runtime più recenti. Potresti specificare due policy diverse, una per identificare i runtime già ritirati e l'altra per identificare i runtime che verranno ritirati a breve. A tale scopo, potresti scrivere il file etc/rules.guard seguente:
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.>> } } } }
Supponiamo ora di scrivere il seguente modello CloudFormation iac/lambda.yaml, che definisce una funzione 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
Dopo aver eseguito l'installazione della funzionalità Guard, convalida il modello:
cfn-guard validate --rules etc/rules.guard --data iac/lambda.yaml
L'output sarà il seguente:
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 consente agli sviluppatori di vedere dalle loro postazioni di lavoro locali che devono aggiornare il modello per utilizzare un runtime consentito dall'organizzazione. Ciò avviene prima di effettuare il commit in un repository di codice e quindi di non superare i controlli all'interno di una pipeline CI/CD. Di conseguenza, gli sviluppatori ricevono questo feedback su come sviluppare modelli conformi e dedicare più tempo alla scrittura di codice che offra valore aziendale. Questo controllo può essere applicato alla postazione di lavoro locale degli sviluppatori, a un webhook di convalida pre-commit e/o alla pipeline CI/CD prima dell'implementazione.
Avvertenze
Se utilizzi modelli AWS Serverless Application Model (AWS SAM) per definire le funzioni Lambda, tieni presente che devi aggiornare la regola Guard per cercare il tipo di risorsa AWS::Serverless::Function come riportato di seguito.
let lambda_functions = Resources.*[ Type == "AWS::Serverless::Function" ]
Guard si aspetta inoltre che le proprietà vengano incluse nella definizione della risorsa. Nel frattempo, i modelli AWS SAM consentono di specificare le proprietà in una sezione Globali separata. Le proprietà definite nella sezione Globali non vengono convalidate con le regole Guard.
Come indicato nella documentazione per la risoluzione dei problemi di Guard, tieni presente che Guard non supporta funzioni intrinseche in formato breve come !GetAtt o !Sub e richiede invece l'utilizzo dei formati espansi: Fn::GetAtt e Fn::Sub. (L'esempio precedente non valuta la proprietà Ruolo, quindi per semplicità è stata utilizzata la funzione intrinseca in formato breve.)