-
Notifications
You must be signed in to change notification settings - Fork 25
8.2
Review and ensure that you have setup your development environment before going through the steps below.
In this sublesson, you will walk through an exercise to deploy a full-stack and fully-automated Continuous Encryption solution on AWS using Amazon CloudWatch Event Rules, AWS Config and Config Rules, IAM, AWS CodePipeline, AWS CodeBuild, AWS Lambda, Amazon SNS, Amazon S3, cfn_nag, and other tools and services.
Replace ceoa-***, CONFIGRECORDERNAME, and DELIVERYCHANNELNAME with the actual name of the resources.
aws configservice delete-config-rule --config-rule-name s3-bucket-server-side-encryption-enabled
aws configservice describe-configuration-recorders
aws configservice delete-configuration-recorder --configuration-recorder-name default
aws configservice describe-delivery-channels
aws configservice delete-delivery-channel --delivery-channel-name default
In this sublesson, I will highlight a few code snippets from the CloudFormation template that fully automates the end-to-end Continuous Encryption solution on AWS.
In the CloudFormation snippet below, you see how I am provisioning a CodeBuild project using the AWS::CodeBuild::Project resource. It refers to the name of the CodeCommit repository and the name of the buildspec file in the CodeCommit repo (buildspec-lambda.yml). The purpose of this resource definition is to provision CodeBuild so that it can run commands in its container to build a Lambda function that automatically remediates a noncompliant resource (in this case, an S3 Bucket).
CodeBuildLambdaTrigger:
Type: AWS::CodeBuild::Project
DependsOn: CodeBuildRole
Properties:
Name:
Fn::Join:
- ''
- - Run
- CodePipeline
- Ref: AWS::StackName
Description: Build application
ServiceRole:
Fn::GetAtt:
- CodeBuildRole
- Arn
Artifacts:
Type: no_artifacts
Environment:
EnvironmentVariables:
- Name: S3_BUCKET
Value:
Ref: ArtifactBucket
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/eb-nodejs-4.4.6-amazonlinux-64:2.1.3
Source:
BuildSpec: buildspec-lambda.yml
Location:
Fn::Join:
- ''
- - https://git-codecommit.
- Ref: AWS::Region
- ".amazonaws.com/v1/repos/"
- Ref: AWS::StackName
Type: CODECOMMIT
TimeoutInMinutes: 10
Tags:
- Key: Owner
Value: MyCodeBuildProject
In the CloudFormation snippet below, you see how I am provisioning a deployment pipeline using the AWS::CodePipeline::Pipeline resource. The purpose of this resource definition is to provision all of the stages and actions for the CodePipeline workflow which, in turn, gets its source files from CodeCommit, and builds and deploys the code run by Lambda.
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
RoleArn:
Fn::Join:
- ''
- - 'arn:aws:iam::'
- Ref: AWS::AccountId
- ":role/"
- Ref: CodePipelineRole
Stages:
- Name: Source
Actions:
- InputArtifacts: []
Name: Source
ActionTypeId:
Category: Source
Owner: AWS
Version: '1'
Provider: CodeCommit
OutputArtifacts:
- Name: MyApp
Configuration:
BranchName:
Ref: RepositoryBranch
RepositoryName:
Ref: AWS::StackName
RunOrder: 1
- Name: Build
Actions:
- InputArtifacts:
- Name: MyApp
Name: BuildLambdaFunctions
ActionTypeId:
Category: Build
Owner: AWS
Version: '1'
Provider: CodeBuild
OutputArtifacts:
- Name: lambdatrigger-BuildArtifact
Configuration:
ProjectName:
Ref: CodeBuildLambdaTrigger
RunOrder: 1
In the CloudFormation snippet below, you see how I am provisioning an AWS::CodeCommit::Repository resource, which creates a new CodeCommit repository to store my source files that run the remediation. For ease in remembering, the name of the repository is the same as the CloudFormation stack name. CodeCommitS3Bucket is a CloudFormation parameter that refers to the name of the S3 bucket that you will create to store the source files. CodeCommitS3Key is the S3 key/folder that refers to the name of the zip file you will be creating.
CodeCommitRepo:
Type: AWS::CodeCommit::Repository
Properties:
RepositoryName:
Ref: AWS::StackName
RepositoryDescription: CodeCommit Repository for remediation solution
Code:
S3:
Bucket: !Ref CodeCommitS3Bucket
Key: !Ref CodeCommitS3Key
Triggers:
- Name: MasterTrigger
CustomData:
Ref: AWS::StackName
DestinationArn:
Ref: MySNSTopic
Events:
- all
In the CloudFormation snippet below, you see that I am using CloudFormation as a CodePipeline deploy provider. Here are key components of this snippet:
- The name of the CodePipeline stage is Deploy (it can be any valid CodePipeline name).
- It takes in lambdatrigger-BuildArtifact as its input artifact (which is the OutputArtifacts of the Build stage).
- It uses CloudFormation as its Deploy provider to deploy the Lambda function to AWS.
- CHANGE_SET_REPLACE creates the CloudFormation change set if it does not exist based on the stack name and template that you declare. If the change set exists, AWS CloudFormation deletes it, and then creates a new one.
- TemplatePath refers to the generated file (template-export.json) from the SAM template that was stored in the OutputArtifacts. (lambdatrigger-BuildArtifact) generated in the Build stage of this pipeline. The full reference is lambdatrigger-BuildArtifact::template-export.json.
- CHANGE_SET_EXECUTE executes a CloudFormation change set.
- Name: Deploy
Actions:
- InputArtifacts:
- Name: lambdatrigger-BuildArtifact
Name: GenerateChangeSetLambdaFunction
ActionTypeId:
Category: Deploy
Owner: AWS
Version: '1'
Provider: CloudFormation
OutputArtifacts: []
Configuration:
ActionMode: CHANGE_SET_REPLACE
ChangeSetName: pipeline-changeset
RoleArn:
Fn::GetAtt:
- CloudFormationTrustRole
- Arn
Capabilities: CAPABILITY_IAM
StackName:
Fn::Join:
- ''
- - ''
- Ref: AWS::StackName
- "-"
- Ref: AWS::Region
- ''
TemplatePath: lambdatrigger-BuildArtifact::template-export.json
RunOrder: 1
- ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
ActionMode: CHANGE_SET_EXECUTE
ChangeSetName: pipeline-changeset
StackName:
Fn::Join:
- ''
- - ''
- Ref: AWS::StackName
- "-"
- Ref: AWS::Region
- ''
InputArtifacts: []
Name: ExecuteChangeSetFunction
OutputArtifacts: []
RunOrder: 2
In the CloudFormation snippet below, you see how I am provisioning an AWS Config Rule using the AWS::Config::ConfigRule resource. The purpose of this resource definition is to provision the S3_BUCKET_PUBLIC_WRITE_PROHIBITED managed config rule, which detects when S3 buckets allow the ability to write to them. This resource definition ensures that the Config DeliveryChannel and ConfigRecorder resources have already been provisioned.
AWSConfigRule:
DependsOn:
- DeliveryChannel
- ConfigRecorder
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName:
Ref: ConfigRuleName
Description: Checks that your Amazon S3 buckets do not allow public write access.
The rule checks the Block Public Access settings, the bucket policy, and the
bucket access control list (ACL).
InputParameters: {}
Scope:
ComplianceResourceTypes:
- AWS::S3::Bucket
Source:
Owner: AWS
SourceIdentifier: S3_BUCKET_PUBLIC_WRITE_PROHIBITED
MaximumExecutionFrequency:
Ref: MaximumExecutionFrequency
In the CloudFormation snippet below, you see that I am defining two Outputs: PipelineUrl and LambdaTrustRole. LambdaTrustRole is used by the SAM template definition so that we can define the IAM role in one location and refer to its output as an input when defining the Lambda function in the SAM template. Therefore, the name you use as the Output in this template must be used in the SAM template when defining the role.
Outputs:
PipelineUrl:
Value: https://console.aws.amazon.com/codepipeline/home?region=${AWS::Region}#/view/${Pipeline}
Description: CodePipeline URL
LambdaTrustRole:
Description: IAM role for AWS Lambda used for passRole to Lambda functions.
Export:
Name:
Fn::Join:
- ''
- - ''
- Ref: AWS::StackName
- "-"
- Ref: AWS::Region
- "-LambdaTrustRole"
Value:
Fn::GetAtt:
- MyLambdaTrustRole
- Arn
The AWS Serverless Application Model (SAM) uses the CloudFormation Transform to specify CloudFormation macros to convert its serverless domain-specific language (DSL) into CloudFormation. You can mix the SAM DSL with CloudFormation resource definitions in the same file.
The SAM components of this solution are described in more detail below:
AWS Lambda – There are two resources defined in the SAM template. The first is the AWS::Lambda::Permission and the second is AWS::Serverless::Function. AWS::Serverless::Function also defines a CloudWatch Event which triggers the Lambda function that is defined as part of its resource definition. "Under the hood", this calls AWS::Events::Rule to define a CloudWatch Events Rule.
The contents of the sam-s3-remediation.yml file (which is a SAM template) is listed below. It provisions the Lambda function by defining the name of its handler (which is associated with the name of its file: index.js), the runtime environment and version (Node.js 10.x), and a CloudWatch Event Rule with the pattern that needs to match in order to trigger the event which runs the Lambda function as its target. For more information, see Event Patterns in CloudWatch Events.
S3F:
Type: 'AWS::Serverless::Function'
Properties:
Handler: index.handler
Runtime: nodejs10.x
Events:
S3CWE:
Type: CloudWatchEvent
Properties:
Pattern:
source:
- aws.config
detail:
requestParameters:
evaluations:
complianceType:
- NON_COMPLIANT
additionalEventData:
managedRuleIdentifier:
- S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED
Role:
'Fn::ImportValue':
'Fn::Join':
- '-'
- - Ref: 'AWS::StackName'
- LambdaTrustRole
There are four main steps in launching this solution: preparing an AWS account, create and store source files, launching the CloudFormation stack, and testing the deployment. Each is described in more detail in this section. Please note that you are responsible for any fees incurred while creating and launching your solution.
Here are the prerequisites and assumptions for this solution:
- AWS Account – Follow these instructions to create an AWS Account: Creating an AWS Account. Be sure you have signed up for the CloudFormation service and have the proper permissions.
- AWS CloudTrail – A CloudTrail log should already be enabled for the region in which you are running this example. See Creating a Trail for more information.
- AWS Region – Use the region selector in the navigation bar of the console to choose your preferred AWS region.
- AWS Cloud9 – All of the CLI examples assume that you are using the AWS Cloud9 IDE. To learn how to install and configure Cloud9, go to Automating AWS Cloud9. You can easily adapt the commands to perform the same actions in a different IDE, but the instructions are outside the scope of this sublesson.
- AWS Config – This solution assumes you have not enabled AWS Config. If you have, you will receive an error when launching the CloudFormation stack as it provisions a Config Recorder and Delivery Channel.
In this section, you will create six source files that will be stored in S3 and then uploaded to AWS CodeCommit when launching the CloudFormation stack. The names are listed below.
From your AWS Cloud9 terminal, type the following to setup your directory structure:
cd ~/environment/ceoa
aws s3 mb s3://ceoa-8-$(aws sts get-caller-identity --output text --query 'Account')
mkdir codecommit-files
cd ~/environment/ceoa/codecommit-files
Create empty source files:
touch buildspec.yml
touch buildspec-lambda.yml
touch ceoa-8-pipeline.yml
touch index.js
touch package.json
touch README.md
touch sam-s3-remediation.yml
touch volume.yml
Save the files.
Copy the contents below into the buildspec-lambda.yml and save the file. AWS CodeBuild will use this buildspec to build the Lambda function that runs the automatic encryption remediation to fix the non-encrypted S3 Bucket.
CodeBuild runs an aws cloudformation CLI command to package a SAM template and then exports the contents as a zip file into S3 so that Lambda can run the code.
version: 0.2
phases:
build:
commands:
- npm install
- npm install aws-cli-js
- >-
aws cloudformation package --template sam-s3-remediation.yml --s3-bucket
$S3_BUCKET --output-template template-export.json
artifacts:
type: zip
files:
- template-export.json
Copy the source contents from the ceoa-8-pipeline.yml file and save it to your local file in your Cloud9 environment called ceoa-8-pipeline.yml. The file is a 500-line CloudFormation template.
Copy the contents below into the index.js file and save the file. This is the Node.js function that Lambda runs to remove the S3 bucket policy for S3 buckets that allow writes to their buckets.
var AWS = require('aws-sdk');
exports.handler = function(event) {
console.log("request:", JSON.stringify(event, undefined, 2));
var s3 = new AWS.S3({apiVersion: '2006-03-01'});
var resource = event['detail']['requestParameters']['evaluations'];
console.log("evaluations:", JSON.stringify(resource, null, 2));
for (var i = 0, len = resource.length; i < len; i++) {
if (resource[i]["complianceType"] == "NON_COMPLIANT")
{
console.log(resource[i]["complianceResourceId"]);
var params = {
Bucket: resource[i]["complianceResourceId"],
ServerSideEncryptionConfiguration: { /* required */
Rules: [ /* required */
{
ApplyServerSideEncryptionByDefault: {
SSEAlgorithm: "AES256"
}
},
]
},
};
s3.putBucketEncryption(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
}
}
};
Copy the contents below into the package.json file and save the file. This is a metadata file that all Node.js apps need to operate.
{
"name": "s3-bucket-server-side-encryption-enabled-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"run some tests here\""
},
"author": "",
"license": "ABC"
}
Copy the contents below into the README.md file and save the file.
# s3-bucket-server-side-encryption-enable-app
Copy the contents below into the sam-s3-remediation.yml file and save the file. This is the SAM template that defines the Lambda function and the CloudWatch Event Rule that triggers the Lambda function. The purpose of this CloudWatch Event Rule is to detect noncompliant S3 buckets and then automatically run a Lambda function that remediates these noncompliant buckets.
AWSTemplateFormatVersion: 2010-09-09
Transform:
- 'AWS::Serverless-2016-10-31'
Resources:
PermissionForEventsToInvokeLambda:
Type: 'AWS::Lambda::Permission'
Properties:
FunctionName:
Ref: S3F
Action: 'lambda:InvokeFunction'
Principal: events.amazonaws.com
SourceArn:
'Fn::GetAtt':
- S3F
- Arn
S3F:
Type: 'AWS::Serverless::Function'
Properties:
Handler: index.handler
Runtime: nodejs10.x
Events:
S3CWE:
Type: CloudWatchEvent
Properties:
Pattern:
source:
- aws.config
detail:
requestParameters:
evaluations:
complianceType:
- NON_COMPLIANT
additionalEventData:
managedRuleIdentifier:
- S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED
Role:
'Fn::ImportValue':
'Fn::Join':
- '-'
- - Ref: 'AWS::StackName'
- LambdaTrustRole
Copy the source contents from the buildspec.yml file and save it to your local file in your Cloud9 environment called buildspec.yml.
Copy the source contents from the volume.yml file and save it to your local file in your Cloud9 environment called volume.yml.
In this section, you will zip and upload all of the source files to S3 so that they can be committed to the CodeCommit repository that is automatically provisioned by the stack generated by the ceoa-8-pipeline.yml template.
From your AWS Cloud9 environment, type the following:
cd ~/environment/ceoa/codecommit-files
zip ceoa-8-examples.zip *.*
aws s3 sync ~/environment/ceoa/codecommit-files s3://ceoa-8-$(aws sts get-caller-identity --output text --query 'Account')
From your AWS Cloud9 environment, type the following (replacing you@example.com and ComplianceResourceId with the appropriate values):
aws cloudformation create-stack --stack-name ceoa-8-ar --template-body file:///home/ec2-user/environment/ceoa/codecommit-files/ceoa-8-pipeline.yml --parameters ParameterKey=EmailAddress,ParameterValue=you@example.com ParameterKey=CodeCommitS3Bucket,ParameterValue=ceoa-8-$(aws sts get-caller-identity --output text --query 'Account') ParameterKey=CodeCommitS3Key,ParameterValue=ceoa-8-examples.zip ParameterKey=ComplianceResourceId,ParameterValue=ceoa-82-s3-$(aws sts get-caller-identity --output text --query 'Account') --capabilities CAPABILITY_NAMED_IAM --disable-rollback
First, verify that the CloudFormation stack you just launched (called ceoa-8-ar) was successfully created. Click on the PipelineUrl Output to launch deployment pipeline in CodePipeline to see it running. Verify that the pipeline successfully went through all stages (as shown below).
Next, you will create an unencrypted S3 bucket that allows people to store files to the bucket. Here are the steps:
- Go to the S3 console.
- Click the Create bucket button.
- Enter
ceoa-82-s3-ACCOUNTIDin the Bucket name field (replacingACCOUNTIDwith your account id). - Click Next on the Configure Options pane.
- Click Next on the Set Permissions pane.
- Click Create bucket on the Review pane.
- Select the
ceoa-82-s3-ACCOUNTIDbucket and choose the Permissions tab. - Click Next.
- Click the Save button.
In this section, you will verify that the Config Rule has been triggered and that the S3 bucket resource has been automatically remediated.
- Go to the Config console.
- Click on Rules.
- Select the s3-bucket-server-side-encryption-enabled rule.
- Click the Re-evaluate button.
- Go back to Rules in the Config console.
- Go to the S3 console and choose the
ceoa-82-s3-ACCOUNTIDbucket - Click on the Properties pane and verify Default Encryption is enabled.
- Go back to Rules in the Config console and confirm that the s3-bucket-server-side-encryption-enabled rule is Compliant.
In this sublesson, you learned how to setup a robust automated encryption and remediation infrastructure for noncompliant AWS resources using services such as S3, AWS Config & Config Rules, Amazon CloudWatch Event Rules, AWS Lambda, IAM, and others. You did this by automating all of the provisioning by using tools like AWS CloudFormation, AWS CodePipeline, AWS CodeCommit, and AWS CodeBuild.
By leveraging this approach, your AWS infrastructure is capable of rapidly scaling resources while ensuring these resources are always in compliance with encryption requirements without humans needing to manually intervene.
Consider the possibilities when adding hundreds if not thousands of rules and remediations to your AWS infrastructure. Below is just an example of some of the different types of Managed Config Rules you can run. What if you took each of these and developed custom remediations for them and ensured they were running across all of your AWS accounts? Or, what if you wrote your own Config Rules and triggered CloudWatch Events to execute remediations you developed in Lambda? This way your compliance remains in lockstep with the rest of your AWS infrastructure.
As a result, engineers can focus their efforts on automating the prevention, detection, and remediation of their AWS infrastructure rather than manually hunting down every noncompliant resource, creating a ticket, and manually fixing the issue. This is Continuous Encryption – at scale!
- Introduction
- Labs
- The Current State of Encryption
- Setup Development Environment
- Lesson 1: Automating AWS Resources
- Lesson 2: Key Management
- Lesson 3: Developing with Encryption
- Lesson 4: Encryption in Transit
- Lesson 5: Encryption at Rest
- Lesson 6: Detecting Encrypted Resources
- Lesson 7: Logging and Searching KMS Keys
- Lesson 8: Continuous Encryption
- Summary