DEV Community

Dr. Malte Polley
Dr. Malte Polley

Posted on

Exploring CDK and Policy as Code with CDK-Nag and Python

Infrastructure as Code (IaC) has become a standard in cloud development, allowing for quick environment setups and compliance through versioning. Tools like Terraform and the Cloud Development Kit (CDK) simplify the process compared to traditional CloudFormation.

In a previous blog post, I discussed using a custom solution for pull request reporting with cfn-lint and cfn_nag. While cfn-lint is still valuable for creating your own compliance rules, keeping up with CDK and AWS recommendations can be challenging. Fortunately, cdk-nag can serve as a substitute for cfn-lint.

CDK-Nag: Making Infrastructure Decisions Visible

Most tools I've used have a common issue: they can check your infrastructure but often fall short on providing actionable insights. cdk-nag enhances visibility into the compliant aspects of your IaC deployment. It comes with prebuilt rule packs for various standards:

  1. AWS Solutions
  2. HIPAA Security
  3. NIST 800-53 Rev 4
  4. NIST 800-53 Rev 5
  5. PCI DSS 3.2.1

These rules are mapped to specific controls, making compliance evaluations straightforward, even for non-AWS exployees.

Sample Implementation of CDK-Nag in Python

For demonstration, we'll use a sample CDK application created with the command cdk init app --language python. If you're new to CDK, check out the official tutorial.

Here's a minimal example of creating an S3 bucket in your CDK stack:

from aws_cdk import Stack, aws_s3 as s3
from constructs import Construct
from src.load_env.config import CDKConfig

class CdkSampleRepoStack(Stack):
    """Create the actual deployment in each AWS account."""

    def __init__(self, scope: Construct, construct_id: str, stage: str, config: CDKConfig, **kwargs) -> None:
        """Initialize CDK stack class."""
        super().__init__(scope, construct_id, **kwargs)

        # Create the S3 bucket
        bucket = s3.Bucket(self, id="MyBucket")
Enter fullscreen mode Exit fullscreen mode

After running cdk synth, you'll generate a CloudFormation template in the cdk.out folder. However, this bucket configuration may have implicit or missing settings, such as encryption or TLS policies.

To leverage AWS best practices, add cdk-nag to your app.py:

#!/usr/bin/env python3
import os
import cdk_nag
import aws_cdk as cdk
from hello_cdk.hello_cdk_stack import CdkSampleRepoStack

app = cdk.App()
CdkSampleRepoStack(app, "HelloCdkStack", env=cdk.Environment(account='123456789012', region='us-east-1'))
cdk_nag.Aspects.of(app).add(cdk_nag.AwsSolutionsChecks(verbose=True))
app.synth()
Enter fullscreen mode Exit fullscreen mode

Running cdk synth now will display error messages in your terminal, such as:

[Error at /CdkSampleRepoStack/MyBucket/Resource] AwsSolutions-S1: The S3 Bucket has server access logs disabled.

[Error at /CdkSampleRepoStack/MyBucket/Resource] AwsSolutions-S10: The S3 Bucket or bucket policy does not require requests to use SSL.
Enter fullscreen mode Exit fullscreen mode

Handling Best Practice Violations

Sometimes, you may need to explicitly violate a best practice, like disabling access logging for a non-critical bucket. You can suppress specific rules as follows:

# hello_cdk.hello_cdk_stack.py
bucket = s3.Bucket(
    self,
    id="MyBucket",
    block_public_access=s3.BlockPublicAccess(
        block_public_acls=True,
        block_public_policy=True,
        ignore_public_acls=True,
        restrict_public_buckets=True,
    ),
    versioned=True,
    enforce_ssl=True,
    encryption=s3.BucketEncryption.KMS_MANAGED,
)

nag_suppression.add_resource_suppressions(
    construct=bucket,
    suppressions=[
        {
            "id": "AwsSolutions-S1",
            "reason": "This bucket does not hold customer data",
        }
    ],
)
Enter fullscreen mode Exit fullscreen mode

With this suppression, the CSV report will now indicate that the rule has been suppressed. But, cdk-nag not supports suppressions on constructs but also on resource paths and many more

Integrating Pull Request Reporting with cdk-nag

Integrating cdk-nag into your CI/CD pipeline can enhance your deployment process. For instance, in Azure DevOps, you can automate pull request comments based on the cdk synth results.

Here's a brief overview of how to set this up:

  1. Create a message class to handle comments.
  2. Use pandas to read the generated CSV files.
  3. Translate the CSV table to Markdown.
  4. Post comments to your pull requests.
import pull_request_comment
import logging
import pandas as pd
import os

logging.basicConfig(
    level=logging.INFO, format="[%(levelname)s] - %(message)s", force=True
)

def main():
    """Create a Pull Request comment within azure-pipelines-pr.yml"."""
    for filename in os.listdir("./synth/templates/"):
        if filename.endswith(".csv"):
            try:
                df = pd.read_csv(f"./cdk.out/templates/{filename}")
            except FileNotFoundError as e:
                logging.exception(e)
                raise
            if df.empty is False:
                logging.info("Adding CDK Validation report")
                result = msg.add(comment=df.to_markdown())

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

In your Azure DevOps pipeline, add a task to run cdk synth and then execute the script to post comments:

- task: AWSShellScript@1
  inputs:
    awsCredentials: ${{ parameters.ServiceConnection }}
    regionName: ${{ parameters.AwsRegion }}
    scriptType: 'inline'
    inlineScript: |
      cdk synth
      python3 src/pull_requests/comment.py
Enter fullscreen mode Exit fullscreen mode

Results of cdk-nag reports within a Pull Request pipeline

Final Thoughts

Implementing cdk-nag in your workflow may require some effort, but it provides an easier way to ensure compliance before deploying to AWS. Shifting left in your development process is always a good idea, especially for beginners looking to ensure correct deployments.

Happy and save coding! :-)

Top comments (2)

Collapse
 
mauricebrg profile image
Maurice Borgmeier

I haven't gotten around to actually integrating cdk-nag yet, it seems like the barrier to entry is actually quite low.

I should really give it a try!

Thanks, Malte!

Collapse
 
zirkonium88 profile image
Dr. Malte Polley

Yes, the integration effort is quite low. Taking care of the rule treatment can be tricky; especially with "hidden" CDK ressources like Inline Policies.