DEV Community

Dr. Malte Polley
Dr. Malte Polley

Posted on

1

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! :-)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

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.

Billboard image

Try REST API Generation for Snowflake

DevOps for Private APIs. Automate the building, securing, and documenting of internal/private REST APIs with built-in enterprise security on bare-metal, VMs, or containers.

  • Auto-generated live APIs mapped from Snowflake database schema
  • Interactive Swagger API documentation
  • Scripting engine to customize your API
  • Built-in role-based access control

Learn more