In recent times the software development and deployment lifecycle has had more demand with respect to the integration of security testing and protection. As much as this requirement is about technological tools, it is also about the culture and shared responsibility, just like DevOps philosophy. This seamless integration of security and protection along with culture and shared responsibility has come to be known as DevSecOps.
In this blog, we are going to try to demonstrate using the CI/CD pipeline to automate security operations and auditing, for example, failing the pipeline when an instance is launched with a specific AMI. So, we use the same resources mentioned here.
Services used
- Two AWS CloudFormation
- One to create a Demo Pipeline.
- One to create an EC2 instance.
- 2 Lambda functions
- For static code analysis of the CloudFormation template.
- For dynamic stack validation of the EC2 instance launched.
- S3 bucker as the sample code repository.
- VPCs to deploy the resources. (You can use the default VPC too)
CodePipeline Stages
The CodePipeline has 4 stages: (Find the source code here.)
- Commit: In this stage, the pipeline gets the CloudFormation codes from the S3 bucket.
- StaticCodeAnalysis: This stage passes the CF template and pipeline name to the lambda function called CFNValidateLambda.
- TestDeployment: This stage has the test deployment where the stack creates various resources, and validation is done.
The StackValidationLambda is triggered, and the stack name and pipeline name are passed to this lambda function from the event parameters. If there is any violation detected, the lambda deletes the stack, stops the pipeline, and returns an error message.
The below code detects if the mentioned AMIs are present and fails if they are found in the stack: (note that this is for a single region and for only AMIs mentioned here)
client = boto3.client(\'ec2\') response = client.describe_images(Filters=[{\'Name\': \'image-id\', \'Values\': }]) des_inst = client.describe_instances(Filters=[{\'Name\': \'tag:aws:cloudformation:stack-name\', \'Values\': }]) print(\"dest : %s\" % des_inst) for image_id in des_inst: image_id = des_inst[0][0] instanceID = des_inst[0][0] if image_id in (\'ami-7a11e211\',\'ami-08111162\',\'ami-0022f774911c1d690\'): result = False failReason = \"Found an unapproved AMI\" offenders.append(str(instanceID)) return {\'Result\': result, \'failReason\': failReason, \'Offenders\': offenders, \'ScoredControl\': scored, \'Description\': description, \'ControlId\': control}
Next, there is a manual step to approve the step for review purposes, and thus it can be omitted.
After the above steps are completed, the test stack is deleted.
- ProductionDeployment: In the above steps, there is a security check that has run, which we will call as the testing phase in a testing VPC (which is evident from the fact that there was testing and security check run). Now, we create a change set and executes it in another VPC which will be for production.
Every time you make changes to the CloudFormation template, upload them to the S3 bucket and click the Release Change button. One can expand on these checks like I did: