As organizations increasingly adopt cloud architectures and Infrastructure as Code (IaC), the management of policies required to maintain security, compliance, and operational efficiency is becoming more complex. Managing these policies manually can be time-consuming and error-prone, not to mention difficult to scale. Policy as Code (PaC) offers a solution to these challenges by automating policy creation, enforcement, and maintenance.
This article provides an overview of how PaC works and how we used Checkov to automate the detection of misconfigurations and security risks in IaC files.
Policy as Code: An Overview
Policies are first defined in code, making them easy to version and update. These policies are then integrated into the Continuous Integration (CI) pipeline. Tools like Checkov automatically scan IaC files for security vulnerabilities, compliance issues, and configuration errors.
As developers push code changes, the CI pipeline runs these checks in real time, providing instant feedback through Merge Request (MR) comments. This allows issues to be addressed before merging. PaC also serves as a pre-merge gate, blocking non-compliant code from reaching production. By automating these checks, PaC ensures consistent compliance without manual intervention. This streamlined approach reduces errors, strengthens security and compliance, and optimizes workflows.
Why Did We Choose Checkov for Policy as Code Implementation?
Our organization hosts multiple in-house products. Ensuring consistent compliance and security is a critical requirement for us as our infrastructure grows. We wanted to implement Policy as Code to address the challenge and evaluated several tools, including Open Policy Agent, Regula, Sentinel, and Checkov.
We chose Checkov for its various advantages. It supports widely used languages like YAML, Python, and JSON, while Regula requires learning a custom language. Checkov also offers broader IaC coverage, including Terraform, Helm, Kubernetes, Serverless, Docker, ARM, and Bicep, whereas Regula is limited to Kubernetes, Terraform, and CloudFormation.
In addition, Checkov has a larger, widely adopted open-source community (Apache 2.0 license) and a comprehensive library of over 750 predefined policies for detecting common IaC misconfigurations. Its flexibility also allows us to customize policies to meet our organization’s specific requirements, making it the ideal choice for our PaC implementation.
Implementing Policy as Code Using Chekov
Our CI pipeline is organized into distinct stages via a Jenkins shared library, each stage performing a specific task. The outcome of each stage determines whether the pipeline moves to the next stage or fails. Additionally, a stage within the pipeline is dedicated to posting comments about the pipeline's outcome to the Merge Request.
We added a new stage to the existing CI pipeline. This stage runs a Checkov policy check, which scans the infrastructure code for security and compliance issues.
We can write policies in YAML or Python, defining security and compliance guidelines for infrastructure as code (IaC). These policies are then committed to a version control system like Git for versioning and collaboration.
When the CI/CD pipeline is triggered, Checkov scans the IaC files, such as Terraform, during the build phase, checking them against the defined policies. The results of this check, along with the Tfplan and Tflint outputs, are posted as comments on the Merge Request. We then review the report, fix any issues, and update the IaC configurations to comply with the policies—this could involve adjusting resource attributes or adding necessary security controls. Once the issues are addressed, the code changes are merged into the main branch.
Checkov scans the code for potential security and compliance violations, and if no violations are detected, the pipeline proceeds to deploy the infrastructure changes.
Note: Checkov comes with predefined policies to scan for common misconfigurations, security risks, and best practice violations, which you can find on checkov.io.
Based on the policy check result, the CI pipeline is configured to succeed or fail, ensuring that only compliant code is merged.
Below is an example of a custom policy written in YAML format.
- ID: CUSTOM_AWS_SCG_002
- Policy: Ensure VPC security group inbound rules do not allow 0.0.0.0/0 for all ports and protocols.
metadata: name: "Ensure VPC security group inbound rules do not allow 0.0.0.0/0 for all ports and protocols." category: "CONVENTION" id: "CUSTOM_AWS_SCG_002" definition: or: - and: - resource_types: - "aws_security_group" cond_type: attribute attribute : "ingress.*.cidr_blocks" operator: "not_contains" value: "0.0.0.0/0"
Below is an example of a custom policy written in Python format.
- ID: CUSTOM_AWS_SCG_002
- Policy: Ensure VPC security group inbound rules do not allow 0.0.0.0/0 for all ports and protocols.
from checkov.common.models.enums import CheckCategories, CheckResult from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck class VPCSecurityGroupInboundCheck(BaseResourceCheck): def __init__(self): name = "Ensure VPC security group inbound rules do not allow 0.0.0.0/0 for all ports and protocols." id = "CUSTOM_AWS_SCG_002" supported_resources = ['aws_security_group'] categories = [CheckCategories.CONVENTION] super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) def scan_resource_conf(self, conf) -> CheckResult: # Check if any ingress rule permits 0.0.0.0/0 if 'ingress' in conf: for rule in conf['ingress']: if 'cidr_blocks' in rule: if '0.0.0.0/0' in rule['cidr_blocks']: return CheckResult.FAILED return CheckResult.PASSED check = VPCSecurityGroupInboundCheck()
Default and Custom Policies in Checkov
A list of default and custom policies in Checkov as implemented in one of our in-house projects is provided below.
Policy ID | Name/Description | Resource Checked |
CUSTOM_AWS_SCG_002 | Ensure VPC security group inbound rules do not allow 0.0.0.0/0 for all ports and protocols. | Security Group |
CUSTOM_AWS_ECS_001 | Ensure the DNS record type for AWS Service Discovery Service is set to SRV. | Elastic Container Service |
CUSTOM_AWS_EIP_001 | Ensure Elastic IPs are not created via Terraform to avoid deletion | Elastic IP Address |
CUSTOM_AWS_RDS_005 | Ensure DB instance classes are db.t2.micro, db.t2.small, or db.t2.medium | Relational Database Service |
CUSTOM_AWS_VPC_001 | Ensure a CIDR is defined for the VPC | Virtual Private Cloud |
CUSTOM_AWS_GEN_001 | Ensure all resources have common tags applied | All |
CKV_AWS_8 | Ensure all data stored in the Launch Configuration or instance Elastic Block Store (EBS) is securely encrypted | Elastic Block Store |
CKV_AWS_19 | Ensure all data stored in S3 buckets is securely encrypted at rest | Simple Storage Service |
CKV_AWS_130 | Ensure VPC subnets do not assign public IP addresses by default. | Virtual Private Cloud |
Conclusion
Embedding policies directly into code and integrating automated checks into our CI/CD pipelines has helped us achieve greater consistency, reduced risks, and streamlined our development processes. The ease of customization and Checkov's extensive library of predefined policies ensured a smooth transition to this new approach. The active community and open-source model provided the support and flexibility needed to scale. Whether starting with Infrastructure as Code or looking to enhance your existing workflows, Checkov offers a robust, cost-effective solution to bring your policy management to the next level.