Skip to content

This blog post was updated on December 15, 2021, to reflect version 2.20 of the AWS CDK.

You may already know that Regula, Fugue's open-source policy engine that uses Open Policy Agent (OPA) for checking infrastructure as code (IaC), can evaluate Terraform and AWS CloudFormation templates for security issues. But did you know that you can use Regula to secure your AWS Cloud Development Kit (CDK) apps, too?

In this blog post, we'll walk you through how to use Regula to check an AWS CDK project for security and compliance violations. We'll define a vulnerable S3 bucket, synthesize the stack (create a CloudFormation template from it), evaluate the CloudFormation with Regula, and then bring the bucket into compliance with the CIS AWS Foundations Benchmark v1.3.0, a compliance standard designed to help organizations secure their AWS infrastructure.

Our example app is written in TypeScript due to its widespread popularity. But the great thing about using Regula with the AWS CDK is that Regula can be used with CDK apps written in any language AWS supports. That's because no matter what language the code is written in, it is synthesized into CloudFormation. And Regula evaluates CloudFormation!

Prerequisites

This guide assumes you have the following set up already:

  • An AWS account, configured with an access key. See the AWS docs for instructions. (We won't be creating any infrastructure in your account, but an access key is a prerequisite for using the AWS CDK. Using the AWS CLI to execute aws configure is an easy way to set this up.)
  • Node.js 10.13 or later. However, be aware that versions 13.0.0 through 13.6.0 aren't compatible with the AWS CDK.

Follow the steps below to install Regula, TypeScript, and the AWS CDK:

1. Install Regula. Homebrew users can execute the following commands:

brew tap fugue/regula
brew install regula

You can alternatively install a prebuilt binary for your platform or run Regula with Docker.

2. Install TypeScript:
npm install -g typescript
3. Install the AWS CDK:
npm install -g aws-cdk

Initialize the project

Create a folder called
cdk-regula
and
cd
into it:
mkdir cdk-regula
cd cdk-regula

Initialize the project:

cdk init --language typescript

This command creates all of the structure needed for your AWS CDK app and also initializes the folder as a git repository, should you decide to version control the source code. You can ignore it for the purposes of this walkthrough.

Install modules

The AWS CDK provides a library of constructs, each of which defines one or more AWS resources. To declare an S3 bucket, for instance, we'd instantiate the s3.Bucket construct. These constructs live in a single module called aws-cdk-lib, so let's install it:

npm install aws-cdk-lib

We now have a shiny, new (empty) CDK app! Let's write some TypeScript.

Write the code

Open the file

lib/cdk-regula-stack.ts
and replace the contents with the code below. We'll use the commented-out lines later on to demonstrate how Regula works:

 

import * as cdk from 'aws-cdk-lib';
import { aws_s3 as s3 } from 'aws-cdk-lib';
import { aws_iam as iam } from 'aws-cdk-lib';

export class CdkRegulaStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // S3 bucket
    const myBucket = new s3.Bucket(this, 'MyFirstBucket', {
      versioned: true,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
      accessControl: s3.BucketAccessControl.PRIVATE,
    //   encryption: s3.BucketEncryption.KMS_MANAGED,
    //   blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
    });

    // // Bucket policy to deny access to HTTP requests
    // const myBucketPolicy = new iam.PolicyStatement({
    //   effect: iam.Effect.DENY,
    //   actions: ["s3:*"],
    //   resources: [myBucket.bucketArn, myBucket.arnForObjects("*")],
    //   principals: [new iam.AnyPrincipal()],
    //   conditions: { "Bool": { "aws:SecureTransport": false }}
    // });

    // // Add policy to bucket
    // myBucket.addToResourcePolicy(myBucketPolicy);
  }
}

We based this code on the excellent Your First AWS CDK app tutorial in the AWS documentation. It declares a CloudFormation stack containing an S3 bucket with a private ACL. In case you want to cdk deploy the bucket later, we've instructed the CDK upon cdk destroy to automatically delete any objects in the bucket and then delete the bucket itself for a clean teardown.

Let's synthesize our stack to produce a CloudFormation template:

cdk synth

You'll see YAML output like this:

Resources:
  MyFirstBucketB8884501:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: Private
      VersioningConfiguration:
        Status: Enabled
    UpdateReplacePolicy: Delete
    DeletionPolicy: Delete
    Metadata:
      aws:cdk:path: CdkRegulaStack/MyFirstBucket/Resource
... cut for length ...

As you can see, this CloudFormation template defines a stack containing our bucket. Keep scrolling and you'll see it also contains a lambda function and IAM role, which allow AWS to delete the objects in the bucket if you deploy and tear down the stack later. (You can comment out lines 12-13 if you don't want those resources.)

Seems secure enough, right? Let's evaluate it with Regula to see if we're missing anything.

Run Regula on the synthesized CloudFormation

We'll pipe the synthesized CloudFormation output directly into Regula:

cdk synth | regula run

Uh oh! Looks like our bucket is not as secure as it could be. Regula gives us this report:

FG_R00099: S3 bucket server side encryption should be enabled [High]

  [1]: MyFirstBucketB8884501
   	in :2:3

FG_R00229: S3 buckets should have all `block public access` options enabled [High]

  [1]: MyFirstBucketB8884501
   	in :2:3

FG_R00100: S3 bucket policies should only allow requests that use HTTPS [Medium]

  [1]: MyFirstBucketB8884501
   	in :2:3

Found 3 problems.

Our bucket has three vulnerabilities. Let's update the code to make the bucket compliant and secure!

Remediate vulnerabilities

We'll start with rule FG_R00099: S3 bucket server side encryption should be enabled. This rule maps to recommendation 2.1.1 of the CIS AWS Foundations Benchmark v1.3.0, which requires users to "Ensure all S3 buckets employ encryption-at-rest." Tip: You can see all the other compliance controls that each rule maps to in the JSON output:

cdk synth | regula run --format json

Why is this important? Enabling server-side encryption (SSE) on S3 buckets at the object level protects data at rest and helps prevent the breach of sensitive information assets. Objects can be encrypted with S3-Managed Keys (SSE-S3), KMS-Managed Keys (SSE-KMS), or Customer-Provided Keys (SSE-C).

Now, uncomment line 15 to enable KMS-managed encryption:

encryption: s3.BucketEncryption.KMS_MANAGED,

Next, we'll remediate FG_R00229: S3 buckets should have all "block public access" options enabled. This rule maps to recommendation 1.20 of CIS AWS v1.3.0, "Ensure that S3 Buckets are configured with 'Block public access (bucket settings).'"

Why is this important? AWS’s S3 Block Public Access feature has four settings:

BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy
,

and

RestrictPublicBuckets.

All four settings should be enabled to help prevent the risk of a data breach.

Uncomment line 16 to enable all "block public access" settings:

blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,

Finally, we'll address FG_R00100: S3 bucket policies should only allow requests that use HTTPS. This rule maps to recommendation 2.1.2 of CIS AWS v1.3.0, "Ensure S3 Bucket Policy allows HTTPS requests."

Why is this important? To protect data in transit, an S3 bucket policy should deny all HTTP requests to its objects and allow only HTTPS requests. HTTPS uses Transport Layer Security (TLS) to encrypt data, which preserves integrity and prevents tampering.

Uncomment lines 20-26:

const myBucketPolicy = new iam.PolicyStatement({
      effect: iam.Effect.DENY,
      actions: ["s3:*"],
      resources: [myBucket.bucketArn, myBucket.arnForObjects("*")],
      principals: [new iam.AnyPrincipal()],
      conditions: { "Bool": { "aws:SecureTransport": false }}
    });

Lines 20-26 create a bucket policy that denies all S3 actions on the bucket and its objects if the condition SecureTransport is set to false (i.e., if the request is not sent through HTTPS).

Finally, uncomment line 29:

myBucket.addToResourcePolicy(myBucketPolicy);

Line 29 simply adds the policy to our bucket.

Run Regula again

Great, we've addressed all of the vulnerabilities Regula found! Let's synthesize the CloudFormation one more time and confirm that our bucket is more secure:

cdk synth | regula run

And we see this output:

No problems found.

Congratulations! In this example, we wrote an AWS CDK app declaring a vulnerable S3 bucket, checked the synthesized CloudFormation with Regula, remediated the vulnerabilities, and confirmed it with Regula.

Our bucket is now more secure – and it's compliant with CIS AWS v1.3.0!

Here's the finished

lib/cdk-regula-stack.ts 

file, for reference:

import * as cdk from 'aws-cdk-lib';
import { aws_s3 as s3 } from 'aws-cdk-lib';
import { aws_iam as iam } from 'aws-cdk-lib';

export class CdkRegulaStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // S3 bucket
    const myBucket = new s3.Bucket(this, 'MyFirstBucket', {
      versioned: true,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
      accessControl: s3.BucketAccessControl.PRIVATE,
      encryption: s3.BucketEncryption.KMS_MANAGED,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
    });

    // // Bucket policy to deny access to HTTP requests
    const myBucketPolicy = new iam.PolicyStatement({
      effect: iam.Effect.DENY,
      actions: ["s3:*"],
      resources: [myBucket.bucketArn, myBucket.arnForObjects("*")],
      principals: [new iam.AnyPrincipal()],
      conditions: { "Bool": { "aws:SecureTransport": false }}
    });

    // // Add policy to bucket
    myBucket.addToResourcePolicy(myBucketPolicy);
  }
}

What's Next?

In this blog post we demonstrated how to use Regula with CloudFormation output from an AWS CDK app, but you can also use Regula to evaluate Terraform HCL and JSON plans. For more information about Regula – including examples to get you up and running, fast – see https://regula.dev.

Regula is maintained by engineers here at Fugue. For more information about Fugue, see www.fugue.co. (Oh – and we're hiring!)

Categorized Under