Skip to content

Conversation

@jameslaneovermind
Copy link
Contributor

This PR implements a complete Policy-as-Code solution using Open Policy Agent (OPA) with Conftest to automatically enforce security, compliance, and cost controls on Terraform infrastructure changes. Policy violations are automatically detected and reported as custom signals in Overmind with direct links to Terraform Cloud runs.

🎯 What This Implements

1. Security Policies (security-groups.rego)

  • SSH Access Control: Prevents security groups from allowing SSH (port 22) access from 0.0.0.0/0
  • RDP Access Control: Blocks RDP (port 3389) from anywhere on the internet
  • Database Port Protection: Warns about database ports (3306, 5432, 1433) exposed to wide networks
  • Port Range Validation: Prevents overly broad port ranges (>100 ports) in security group rules

2. S3 Security Policies (s3-security.rego)

  • Encryption Enforcement: Requires server-side encryption on all S3 buckets
  • Tagging Standards: Enforces required tags (Environment, Owner, Project, Name)
  • Public Access Controls: Validates proper public access block configurations

3. Cost Control Policies (cost-control.rego)

  • Expensive Instance Detection: Flags high-cost EC2 instance types (24xlarge, 16xlarge, etc.)
  • RDS Cost Controls: Monitors expensive RDS instance classes
  • Resource Tagging: Enforces cost tracking tags for budget allocation

🔄 How It Works

CI/CD Integration

  1. Parallel Execution: Policy checks run alongside existing Terraform validation
  2. Plan Analysis: Uses the same Terraform plan JSON that gets submitted to Overmind
  3. Signal Creation: Policy violations become custom signals in Overmind with severity -3
  4. Terraform Cloud Links: Each signal links directly to the specific Terraform Cloud run

Workflow Structure

graph LR
    A[PR Created] --> B[fmt Job]
    A --> C[execute Job]
    C --> D[Terraform Plan]
    D --> E[Submit to Overmind]
    E --> F[policy-checks Job]
    F --> G[Run OPA Policies]
    G --> H[Submit Violations as Signals]
Loading

Example Violations Detected

The current terraform-example infrastructure will trigger these policy violations:

  • 21 security violations including SSH access from 0.0.0.0/0
  • Missing encryption on multiple S3 buckets
  • Missing required tags (Environment, Owner, Project) on resources
  • ⚠️ Database ports exposed to wide network ranges

Demo Talking Points

  1. "Policy-as-Code in Action" - Show how infrastructure changes are automatically validated
  2. "Shift-Left Security" - Catch issues before deployment, not after
  3. "Overmind Integration" - Demonstrate how policy violations become actionable signals
  4. "Compliance Automation" - No manual security reviews needed

🚫 Does It Block Anything?

Non-Blocking by Default

  • Pull requests will NOT be blocked by policy violations
  • Terraform deployments continue normally
  • Existing workflows remain unchanged

Configurable Enforcement

The action supports a fail-on-violations parameter (currently set to false) that could be enabled to:

  • Block PRs with security violations
  • Prevent deployments of non-compliant infrastructure
  • Require policy fixes before merge

🔧 Technical Implementation

Components Added

  • 3 OPA Policy Files: Comprehensive security, compliance, and cost policies
  • GitHub Actions Integration: New policy-checks job in existing workflow
  • Overmind Signals: Automatic submission of violations with custom ticket links
  • Legacy Syntax Compatibility: Works with Conftest v0.46.0 in GitHub Actions

🧪 Testing

This implementation has been tested with:

  • ✅ Real Terraform plans from this repository
  • ✅ Multiple violation scenarios (SSH, encryption, tagging)
  • ✅ Overmind signal creation and linking
  • ✅ GitHub Actions workflow integration

This commit adds comprehensive Open Policy Agent (OPA) policies to enforce
security best practices and cost control for AWS infrastructure:

Policy files added:
- policies/security-groups.rego: Prevents dangerous security group configurations
  * Blocks SSH/RDP access from 0.0.0.0/0
  * Prevents overly permissive rules
  * Warns about exposed database and application ports

- policies/s3-security.rego: Enforces S3 security standards
  * Requires standard tags (Environment, Name, Owner, Project)
  * Mandates server-side encryption
  * Warns about public access configurations

- policies/cost-control.rego: Manages infrastructure costs
  * Flags expensive EC2/RDS instance types
  * Enforces cost tracking tags
  * Warns about high-cost configurations

These policies use modern OPA syntax (rego.v1) and are compatible with
the overmindtech/policy-signals-action for automated enforcement in CI/CD.

Testing:
- Policies tested with Conftest 0.62.0 (OPA 1.6.0)
- Sample violations properly detected and reported
- Ready for GitHub Actions integration
Integrates OPA policy enforcement into the CI/CD pipeline:

- Adds policy-checks job that runs in parallel with fmt and execute jobs
- Uses overmindtech/policy-signals-action@v1 for automated policy enforcement
- Generates Terraform plan JSON specifically for policy evaluation
- Runs on all pull request events (opened, synchronize, reopened)
- Posts policy violation results as PR comments

The policy checks will now automatically validate:
- Security group configurations (SSH/RDP exposure)
- S3 security settings (encryption, tags, public access)
- Cost control measures (expensive instances, required tags)

Policy violations will be reported as failures in the GitHub Actions run
and detailed results will be posted as pull request comments.
Update all policy files to use older OPA syntax compatible with
Conftest v0.46.0 (OPA 0.57.0) used in GitHub Actions:

- Remove 'import rego.v1' statements
- Change 'contains X if' to 'X[Y]' syntax
- Change 'X in Y' to 'X == Y[_]' syntax
- Fix duplicate package declarations

Tested with Docker using openpolicyagent/conftest:v0.46.0
to match the GitHub Action environment exactly.

Results: 36 tests, 27 passed, 2 warnings, 7 failures
All expected policy violations detected correctly.
- Restructure workflow to run policy checks after plan submission
- Add outputs to capture Terraform Cloud run URL from submit-plan
- Create separate policy-checks job that depends on execute job
- Pass custom ticket-link to policy-signals-action for proper Terraform Cloud integration
- Policy violations now appear in Overmind UI linked to correct Terraform runs
@github-actions
Copy link

Overmind

Open in Overmind ↗


✨ami_update

🟢 Change Signals

Routine 🟢 ▁▂▃▅ AWS instances and launch templates showing regular updates at 1 event/week for the last 2 weeks, indicating routine maintenance.

View signals ↗


🔥 Risks

Low Risk of Impact from Hibernation Attribute and AMI Change Low Open Risk ↗
No significant risk identified based on the change in hibernation attribute and AMI replacement. The application does not appear to rely on hibernation, and the AMI should be compatible with the t3.micro instance type.


🟣 Expected Changes

+/- ec2-instance › i-0e56e0f8eb58a4cf0
- ami: ami-006aa762ec87a7afb
+ ami: ami-0174327c2510b4f8b
- arn: arn:aws:ec2:eu-west-2:540044833068:instance/i-0e56e0f8eb58a4cf0
+ arn: (known after apply)
- availability_zone: eu-west-2b
+ availability_zone: (known after apply)
- capacity_reservation_specification: [map[capacity_reservation_preference:open capacity_reservation_target:[]]]
+ capacity_reservation_specification: (known after apply)
- cpu_core_count: 1
+ cpu_core_count: (known after apply)
- cpu_options: [map[amd_sev_snp: core_count:1 threads_per_core:2]]
+ cpu_options: (known after apply)
- cpu_threads_per_core: 2
+ cpu_threads_per_core: (known after apply)
- credit_specification: [map[cpu_credits:unlimited]]
+ credit_specification: []
- disable_api_stop: false
+ disable_api_stop: (known after apply)
- disable_api_termination: false
+ disable_api_termination: (known after apply)
- ebs_block_device: []
+ ebs_block_device: (known after apply)
- ebs_optimized: false
+ ebs_optimized: (known after apply)
- enable_primary_ipv6: <nil>
+ enable_primary_ipv6: (known after apply)
- enclave_options: [map[enabled:false]]
+ enclave_options: (known after apply)
- ephemeral_block_device: []
+ ephemeral_block_device: (known after apply)
- hibernation: false
+ hibernation: <nil>
- host_id: 
+ host_id: (known after apply)
- host_resource_group_arn: <nil>
+ host_resource_group_arn: (known after apply)
- iam_instance_profile: 
+ iam_instance_profile: (known after apply)
- id: i-0e56e0f8eb58a4cf0
+ id: (known after apply)
- instance_initiated_shutdown_behavior: stop
+ instance_initiated_shutdown_behavior: (known after apply)
- instance_lifecycle: 
+ instance_lifecycle: (known after apply)
- instance_market_options: []
+ instance_market_options: (known after apply)
- instance_state: running
+ instance_state: (known after apply)
- ipv6_address_count: 0
+ ipv6_address_count: (known after apply)
- ipv6_addresses: []
+ ipv6_addresses: (known after apply)
- maintenance_options: [map[auto_recovery:default]]
+ maintenance_options: (known after apply)
- metadata_options: [map[http_endpoint:enabled http_protocol_ipv6:disabled http_put_response_hop_limit:1 http_tokens:optional instance_metadata_tags:disabled]]
+ metadata_options: (known after apply)
- monitoring: false
+ monitoring: (known after apply)
- network_interface: []
+ network_interface: (known after apply)
- outpost_arn: 
+ outpost_arn: (known after apply)
- password_data: 
+ password_data: (known after apply)
- placement_group: 
+ placement_group: (known after apply)
- placement_partition_number: 0
+ placement_partition_number: (known after apply)
- primary_network_interface_id: eni-03d8d79766b0341a3
+ primary_network_interface_id: (known after apply)
- private_dns: ip-10-0-10-44.eu-west-2.compute.internal
+ private_dns: (known after apply)
- private_dns_name_options: [map[enable_resource_name_dns_a_record:false enable_resource_name_dns_aaaa_record:false hostname_type:ip-name]]
+ private_dns_name_options: (known after apply)
- private_ip: 10.0.10.44
+ private_ip: (known after apply)
- public_dns: ec2-18-130-236-8.eu-west-2.compute.amazonaws.com
+ public_dns: (known after apply)
- public_ip: 18.130.236.8
+ public_ip: (known after apply)
- root_block_device: [map[delete_on_termination:true device_name:/dev/xvda encrypted:false iops:0 kms_key_id: tags:map[] tags_all:map[] throughput:0 volume_id:vol-0200e8799ecd1e004 volume_size:8 volume_type:standard]]
+ root_block_device: (known after apply)
- secondary_private_ips: []
+ secondary_private_ips: (known after apply)
- security_groups: []
+ security_groups: (known after apply)
- spot_instance_request_id: 
+ spot_instance_request_id: (known after apply)
- tenancy: default
+ tenancy: (known after apply)
- user_data: <nil>
+ user_data: (known after apply)
- user_data_base64: <nil>
+ user_data_base64: (known after apply)
+/- ec2-instance › i-0a427830cc9151c53
- ami: ami-006aa762ec87a7afb
+ ami: ami-0174327c2510b4f8b
- arn: arn:aws:ec2:eu-west-2:540044833068:instance/i-0a427830cc9151c53
+ arn: (known after apply)
- availability_zone: eu-west-2a
+ availability_zone: (known after apply)
- capacity_reservation_specification: [map[capacity_reservation_preference:open capacity_reservation_target:[]]]
+ capacity_reservation_specification: (known after apply)
- cpu_core_count: 1
+ cpu_core_count: (known after apply)
- cpu_options: [map[amd_sev_snp: core_count:1 threads_per_core:2]]
+ cpu_options: (known after apply)
- cpu_threads_per_core: 2
+ cpu_threads_per_core: (known after apply)
- credit_specification: [map[cpu_credits:unlimited]]
+ credit_specification: []
- disable_api_stop: false
+ disable_api_stop: (known after apply)
- disable_api_termination: false
+ disable_api_termination: (known after apply)
- ebs_block_device: []
+ ebs_block_device: (known after apply)
- ebs_optimized: false
+ ebs_optimized: (known after apply)
- enable_primary_ipv6: <nil>
+ enable_primary_ipv6: (known after apply)
- enclave_options: [map[enabled:false]]
+ enclave_options: (known after apply)
- ephemeral_block_device: []
+ ephemeral_block_device: (known after apply)
- hibernation: false
+ hibernation: <nil>
- host_id: 
+ host_id: (known after apply)
- host_resource_group_arn: <nil>
+ host_resource_group_arn: (known after apply)
- iam_instance_profile: 
+ iam_instance_profile: (known after apply)
- id: i-0a427830cc9151c53
+ id: (known after apply)
- instance_initiated_shutdown_behavior: stop
+ instance_initiated_shutdown_behavior: (known after apply)
- instance_lifecycle: 
+ instance_lifecycle: (known after apply)
- instance_market_options: []
+ instance_market_options: (known after apply)
- instance_state: running
+ instance_state: (known after apply)
- ipv6_address_count: 0
+ ipv6_address_count: (known after apply)
- ipv6_addresses: []
+ ipv6_addresses: (known after apply)
- maintenance_options: [map[auto_recovery:default]]
+ maintenance_options: (known after apply)
- metadata_options: [map[http_endpoint:enabled http_protocol_ipv6:disabled http_put_response_hop_limit:1 http_tokens:optional instance_metadata_tags:disabled]]
+ metadata_options: (known after apply)
- monitoring: false
+ monitoring: (known after apply)
- network_interface: []
+ network_interface: (known after apply)
- outpost_arn: 
+ outpost_arn: (known after apply)
- password_data: 
+ password_data: (known after apply)
- placement_group: 
+ placement_group: (known after apply)
- placement_partition_number: 0
+ placement_partition_number: (known after apply)
- primary_network_interface_id: eni-060632729be4b5c61
+ primary_network_interface_id: (known after apply)
- private_dns: ip-10-0-9-171.eu-west-2.compute.internal
+ private_dns: (known after apply)
- private_dns_name_options: [map[enable_resource_name_dns_a_record:false enable_resource_name_dns_aaaa_record:false hostname_type:ip-name]]
+ private_dns_name_options: (known after apply)
- private_ip: 10.0.9.171
+ private_ip: (known after apply)
- public_dns: ec2-35-178-235-99.eu-west-2.compute.amazonaws.com
+ public_dns: (known after apply)
- public_ip: 35.178.235.99
+ public_ip: (known after apply)
- root_block_device: [map[delete_on_termination:true device_name:/dev/xvda encrypted:false iops:0 kms_key_id: tags:map[] tags_all:map[] throughput:0 volume_id:vol-01da4063d55002995 volume_size:8 volume_type:standard]]
+ root_block_device: (known after apply)
- secondary_private_ips: []
+ secondary_private_ips: (known after apply)
- security_groups: []
+ security_groups: (known after apply)
- spot_instance_request_id: 
+ spot_instance_request_id: (known after apply)
- tenancy: default
+ tenancy: (known after apply)
- user_data: <nil>
+ user_data: (known after apply)
- user_data_base64: <nil>
+ user_data_base64: (known after apply)
~ ec2-launch-template › lt-0731f767e6be2ab94
- image_id: ami-006aa762ec87a7afb
+ image_id: ami-0174327c2510b4f8b
- latest_version: 17
+ latest_version: (known after apply)


💥 Blast Radius

Items 17

Edges 20

@jameslaneovermind
Copy link
Contributor Author

The action repo is here

Copy link
Member

@dylanratcliffe dylanratcliffe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing

@jameslaneovermind jameslaneovermind merged commit facc8bb into main Sep 18, 2025
4 checks passed
@jameslaneovermind jameslaneovermind deleted the implement-opa-policy-enforcement branch September 18, 2025 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants