Skip to main content
โšก Calmops

Infrastructure Compliance: Automated Auditing, Policy Enforcement

Introduction

Compliance isn’t a checkboxโ€”it’s a continuous process. With average non-compliance costs reaching $14.8 million per incident, automated compliance is essential. This guide covers building infrastructure that enforces compliance policies automatically.

Key Statistics:

  • 95% of compliance failures are due to misconfiguration
  • Automated compliance reduces audit costs by 60%
  • SOC2 compliance takes 12-18 months on average
  • Companies with automated compliance detect issues 85% faster

Compliance Frameworks

Framework Comparison

Framework Focus Industry Certification
SOC2 Security, Availability SaaS Audit Required
CIS Benchmarks System Hardening All Self-Assessment
PCI-DSS Payment Data Financial Annual Audit
HIPAA Health Data Healthcare Certification
ISO 27001 Info Security All Certification
NIST 800-53 Federal Systems Government Required

CIS Benchmarks Implementation

AWS CIS Benchmark

# AWS Config Rules for CIS Compliance
resources:
  - resource_type: AWS::EC2::Instance
    properties:
      imageId: ami-*  # Not CIS compliant - use hardened AMI
    mode: periodic
    type: AWSManagedRulesCommon
    parameters:
      - name: DaysToExpiration
        value: 90

  - resource_type: AWS::S3::Bucket
    properties:
      bucketEncryption:
        serverSideEncryptionConfiguration:
          - serverSideEncryptionByDefault:
              sseAlgorithm: AES256
    mode: periodic

# CloudGuard CSPM Rules
rules:
  - id: "CIS-2.1.1"
    description: "Ensure MFA is enabled for the root account"
    severity: critical
    remediation:
      - action: "Enable MFA on root account"
        link: "https://console.aws.amazon.com/iam"

  - id: "CIS-2.1.2"
    description: "Ensure MFA is enabled for all IAM users"
    severity: high
    remediation:
      - action: "Enable MFA for IAM users"
        link: "https://console.aws.amazon.com/iam"

  - id: "CIS-2.3.1"
    description: "Ensure credentials unused for 90 days are disabled"
    severity: medium

Prowler Security Assessment

# Install Prowler
pip install prowler
prowler aws --profile default

# Run specific checks
prowler aws -f cis -r us-east-1

# Export to multiple formats
prowler aws -M csv,json,html -o ./reports/

# prowler-config.yaml
output:
  format: json
  filename: prowler-report
  
checks:
  exclude:
    - extra7123  # Exclude specific check
    
compliance:
  frameworks:
    - cis
    - soc2
    - hipaa
    - pci

AWS Config

Configuration Recorder

# Terraform AWS Config
resource "aws_config_configuration_recorder" "main" {
  name     = "main-recorder"
  role_arn = aws_iam_role.config_role.arn
  
  recording_group {
    all_supported                 = true
    include_global_resource_types = true
    resource_types_to_include    = []
  }
}

resource "aws_iam_role" "config_role" {
  name = "aws-config-role"
  
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = {
        Service = "config.amazonaws.com"
      }
    }]
  })
}

resource "aws_iam_role_policy_attachment" "config_policy" {
  role       = aws_iam_role.config_role.name
  policy_arn = "arn:aws:iam::aws:policy/service_role/AWSConfigRole"
}

Config Rules

# Custom Config Rule - Require Tags
resource "aws_config_config_rule" "require_tags" {
  name = "require-tags"
  
  source {
    owner             = "CUSTOM_LAMBDA"
    source_identifier = "arn:aws:lambda:us-east-1:123456789:function:require-tags"
  }
  
  scope {
    resources = ["AWS::EC2::Instance", "AWS::S3::Bucket"]
  }
}
#!/usr/bin/env python3
"""AWS Config Rule - Require Tags."""

import boto3
import json

config = boto3.client('config')

def evaluate_configuration_change(configuration_item):
    """Evaluate if resource has required tags."""
    
    required_tags = ['Environment', 'Owner', 'CostCenter']
    resource_tags = configuration_item.get('tags', {})
    
    missing_tags = [tag for tag in required_tags if tag not in resource_tags]
    
    if missing_tags:
        return {
            'complianceType': 'NON_COMPLIANT',
            'annotation': f"Missing required tags: {', '.join(missing_tags)}"
        }
    
    return {'complianceType': 'COMPLIANT'}

def handler(event, context):
    """Handle AWS Config evaluation."""
    
    invoking_event = json.loads(event['invokingEvent'])
    configuration_item = invoking_event['configurationItem']
    
    result = evaluate_configuration_item(configuration_item)
    
    config.put_evaluations(
        Evaluations=[
            {
                'ComplianceResourceType': configuration_item['resourceType'],
                'ComplianceResourceId': configuration_item['resourceId'],
                'ComplianceType': result['complianceType'],
                'Annotation': result.get('annotation'),
                'OrderingTimestamp': configuration_item['configurationItemCaptureTime']
            }
        ],
        ResultToken=event['resultToken']
    )

Azure Policy

{
  "properties": {
    "displayName": "Require tags on resource groups",
    "description": "Enforces required tags on resource groups",
    "mode": "All",
    "parameters": {
      "tagName": {
        "type": "String",
        "displayName": "Tag Name",
        "description": "The name of the tag"
      }
    },
    "policyRule": {
      "if": {
        "allOf": [
          {
            "field": "type",
            "equals": "Microsoft.Resources/subscriptions/resourceGroups"
          },
          {
            "not": {
              "field": "[concat('tags[', parameters('tagName'), ']')]",
              "exists": "true"
            }
          }
        ]
      },
      "then": {
        "effect": "deny"
      }
    }
  }
}
# Assign policy
az policy assignment create \
  --name "require-tags" \
  --policy /path/to/policy.json \
  --params '{"tagName":"Environment"}' \
  --scope "/subscriptions/xxx"

# List non-compliant resources
az policy state list \
  --filter "complianceState eq 'NonCompliant'" \
  --query "[].{resource:resourceId, policy:policyAssignmentName}"

SOC2 Compliance Automation

Evidence Collection

#!/usr/bin/env python3
"""SOC2 evidence collection automation."""

import boto3
import json
from datetime import datetime, timedelta

class SOC2EvidenceCollector:
    """Collect evidence for SOC2 compliance."""
    
    def __init__(self):
        self.iam = boto3.client('iam')
        self.cloudtrail = boto3.client('cloudtrail')
        self.config = boto3.client('config')
        
    def collect_access_review(self):
        """CC6.1: Access Control - Access Review"""
        
        # Get all IAM users
        users = self.iam.list_users()['Users']
        
        evidence = {
            'timestamp': datetime.now().isoformat(),
            'total_users': len(users),
            'users': []
        }
        
        for user in users:
            # Check MFA status
            mfa = self.iam.list_mfa_devices(UserName=user['UserName'])
            
            # Get attached policies
            policies = self.iam.list_attached_user_policies(
                UserName=user['UserName']
            )['AttachedPolicies']
            
            evidence['users'].append({
                'username': user['UserName'],
                'arn': user['Arn'],
                'mfa_enabled': len(mfa['MFADevices']) > 0,
                'policies': [p['PolicyName'] for p in policies],
                'created': user['CreateDate'].isoformat()
            })
        
        return evidence
    
    def collect_logging_evidence(self):
        """CC7.2: Logging - CloudTrail"""
        
        trails = self.cloudtrail.lookup_events(
            LookupAttributes=[
                {'AttributeKey': 'EventSource', 'AttributeValue': 's3.amazonaws.com'}
            ],
            MaxResults=100
        )
        
        return {
            'timestamp': datetime.now().isoformat(),
            'total_events': len(trails['Events']),
            'trail_status': 'Enabled'
        }
    
    def collect_encryption_evidence(self):
        """CC6.7: Encryption at Rest"""
        
        s3 = boto3.client('s3')
        buckets = s3.list_buckets()['Buckets']
        
        evidence = {
            'timestamp': datetime.now().isoformat(),
            'buckets': []
        }
        
        for bucket in buckets:
            encryption = s3.get_bucket_encryption(Bucket=bucket['Name'])
            evidence['buckets'].append({
                'name': bucket['Name'],
                'encrypted': 'ServerSideEncryptionConfiguration' in encryption
            })
        
        return evidence
    
    def generate_report(self):
        """Generate comprehensive evidence report."""
        
        report = {
            'generated_at': datetime.now().isoformat(),
            'access_review': self.collect_access_review(),
            'logging_evidence': self.collect_logging_evidence(),
            'encryption_evidence': self.collect_encryption_evidence()
        }
        
        # Save to S3
        s3 = boto3.client('s3')
        s3.put_object(
            Bucket='soc2-evidence-bucket',
            Key=f"evidence/{datetime.now().strftime('%Y%m%d')}.json",
            Body=json.dumps(report, indent=2)
        )
        
        return report

if __name__ == '__main__':
    collector = SOC2EvidenceCollector()
    report = collector.generate_report()
    print(f"Evidence collected: {json.dumps(report, indent=2)}")

Continuous Monitoring

# CloudWatch Dashboard for SOC2
{
  "widgets": [
    {
      "type": "metric",
      "properties": {
        "title": "Failed Login Attempts",
        "metrics": [
          ["AWS/CloudTrail", "ConsoleLogin", "ErrorCode", "FailedConsoleLogin"],
          [".", "AwsServiceEvent"]
        ],
        "period": 300,
        "stat": "Sum"
      }
    },
    {
      "type": "metric",
      "properties": {
        "title": "IAM Policy Changes",
        "metrics": [
          ["AWS/Config", "ResourceCompliance", "ComplianceType", "NON_COMPLIANT"],
          [".", "ComplianceType", "COMPLIANT"]
        ]
      }
    }
  ]
}

Kubernetes Policy Enforcement

OPA Gatekeeper

# Require resource limits
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8srequiredresources
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredResources
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredresources
        
        violation[{"msg": msg}] {
          container := input.request.object.spec.containers[_]
          not container.resources
          msg := sprintf("Container %s must have resources defined", [container.name])
        }
        
        violation[{"msg": msg}] {
          container := input.request.object.spec.containers[_]
          not container.resources.limits
          msg := sprintf("Container %s must have resource limits", [container.name])
        }

Audit Reporting

#!/usr/bin/env python3
"""Generate compliance audit reports."""

import boto3
from datetime import datetime
import json

def generate_audit_report():
    """Generate automated audit report."""
    
    config = boto3.client('config')
    
    # Get compliance summary
    compliance = config.get_compliance_summary_by_config_rule()
    
    # Generate report
    report = {
        'report_date': datetime.now().isoformat(),
        'period': 'Q1 2026',
        'summary': {
            'compliant_rules': compliance.get('CompliantRules', 0),
            'non_compliant_rules': compliance.get('NonCompliantRules', 0),
            'total_rules': compliance.get('CompliantRules', 0) + compliance.get('NonCompliantRules', 0)
        },
        'findings': [],
        'recommendations': []
    }
    
    return report

if __name__ == '__main__':
    report = generate_audit_report()
    print(json.dumps(report, indent=2))

External Resources


Comments