Securing CDK pipelines
Best practices for securing CDK pipelines in enterprise organizations, including constructs, aspects, and preventive measures.

On the 3rd of October, I did a meetup style presentation on the AWS Community day in The Netherlands. The topic was how to secure CDK pipelines in an enterprise organization. This blog will describe the slides I presented.
Intro
Working in an enterprise organization will bring extra security requirements to the table. Talks with CISO, adhering security standards, extra network requirements. All these things are not out of the box with CDK pipelines. So this talk will go over my experiences working in an enterprise organization to implement security best practices for CDK pipelines.
Enterprise vs Start-Ups

Enterprises often consist of departments, think of the Cloud Center of Excellence (a Platform team), CISO, Networking, firewall, Linux and IAM team. Where a Start-Up often consists of a single team.
The Enterprise also has processes in place. For example a company in the financial sector needs to adhere to regulations and the bankers oath. There are also rules to follow, ServiceNOW tickets need to be created, waiting time for execution, work needs to fit in the sprint.
Stakeholders

Because you often work in sprints there are stakeholders in place: the developer, a product owner who decides on what gets prioritized, the Cloud Center of Excellence who are responsible for the landing zone and set the boundaries for developers, and the CISO - the team you want to keep happy all the time.
Security Posture

Within an enterprise they often make use of the CIA (confidentiality, integrity and availability) rating to approve services. Every service is checked and depending on the depth of the CIA rating, adhere to certain rules. Think of a bucket that needs to be encrypted with KMS CMK. This all is written down in a so-called service catalogue.
All the services deployed by developers need to adhere to the rules in the service catalogue. The compliance status is checked via Security Hub (standards and controls) and extra custom config rules.
DevOps rules

Besides the security posture there are also DevOps rules to follow:
- Everything should be deployed via code
- Code should always be tested (TDD approach)
- Code needs to be secure
- Never push to main, use branching and pull requests
- The 4 eye-principle for pull requests
The Cloud Development Kit

With the use of CDK, the DevOps rule to deploy everything via code is checked.
Problems with CDK pipelines

CDK pipelines is not compliant out of the box according to all the CISO requirements mentioned in the service catalogue:
- CodeBuild projects should run in VPC
- Everything needs to be encrypted with KMS CMK
- Packages must come from internal repository
- 4 eye principle (approval process on repository)
- Testing code (run pytests)
- Manual approvals are missing between UAT and production
Solutions
CodeCommit

The code is stored in CodeCommit. We want to implement the process of 4 eyes principle and make sure that code is tested before release.
Basically a developer needs to create a pull request on the main or develop branch. For the main branch two approvals are needed, where the develop branch only needs one. When a pull request is created, an automated process will start to check the code via a CodeBuild project and run all your defined tests. If the test passes, the codechecker will update the pull request with an automated approval.
CodeCommit construct and aspects

We use the cdk-pull-request-check construct from CloudComponents. This construct will create all the approval templates, CodeBuild project, Lambda functions and notifications on your behalf. We create aspects for CodeBuild and Lambda functions to run inside your VPC.
This will checkbox the "4 eyes principle" and "Code is tested".
CDK pipelines construct

Let us create a construct for the secure CDK pipeline. As per service catalogue we need to have the CodeBuild projects running inside the VPC. We need to use the remote package management system, therefore we need to have a partial buildspec.yml and credentials in place.
One of the other service catalogue items is that a bucket needs to have bucket logging and versioning enabled. So we need to build an aspect for that. Also we want to have key rotation on KMS enabled.
DevOps rules checked

So summarized, we are using CDK to deploy our application, so everything is code. With the approval process (codechecker) of CloudComponents from the Construct Hub in place we meet the requirements that code is tested and we need that extra pair of eyes for pushing to the main branch.
Code is secure

Another cool construct from the Construct Hub is cdk-nag. With cdk-nag you can embed linting for AWS best practices on security right in your code. The nicest thing of all is that you are aware of security issues before deployment.
Never push to main

When I start a new project with a customer I always start with discussing rules and guidelines on how we are going to work. When you want to make sure everyone commits to the rules, you can also lock down the repositories in the IAM role which is being assumed.
Security posture achieved

Looking at the security posture of the enterprise, with all the measurements, constructs and aspects in place, we are creating code which adheres to the security standards written down in the service catalogue. And following this will result in a 95%+ Security Hub compliant score.
All DevOps rules checked

Also all the DevOps checks are checked and marked complete. This will make the CISO happy for sure!
Extra: pre-commit
As extra you can also implement pre-commit. A super handy tool which does checks before committing code to the repository.
The hooks we are using:
- check-merge-conflict
- check-json
- check-yaml
- detect-aws-credentials
- end-of-file-fixer
- trailing-whitespace
- black (python linter)
- pytest
Example .pre-commit-config.yaml:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
hooks:
- id: check-merge-conflict
- id: check-json
- id: check-yaml
- id: detect-aws-credentials
args: [--allow-missing-credentials]
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
- repo: local
hooks:
- id: pytest
name: Check pytest unit tests pass
entry: pytest --cov --cov-config=setup.cfg
pass_filenames: false
language: system
types: [python]Have questions about securing CDK pipelines? Find me on Twitter or LinkedIn.