Secure Configuration Management


Introduction

Configuration drift — when actual system configuration diverges from the intended secure baseline — is a leading cause of security incidents. Secure configuration management ensures that systems remain in a known, compliant state throughout their lifecycle. This requires automation at every stage: validation at build time, enforcement at deploy time, and detection at runtime.

Infrastructure as Code Scanning

IaC scanning catches misconfigurations before they reach production.




# Checkov: scan Terraform for security issues


checkov -d terraform/environments/production




# tfsec: Terraform security scanner


tfsec terraform/environments/production --config-file tfsec.yaml




# kics: Keep Infrastructure as Code Secure


kics scan -p kubernetes/deployments --output-path kics-report.json








# checkov policy: S3 bucket must have encryption enabled


resource "aws_s3_bucket" "data" {


bucket = "my-data-bucket"


# This will fail checkov check CKV_AWS_21


# Missing: server_side_encryption_configuration


}








# Custom Checkov policy


from checkov.common.models.enums import CheckResult


from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck




class S3EncryptionCheck(BaseResourceCheck):


def __init__(self):


name = "Ensure S3 bucket has encryption enabled"


id = "CKV_CUSTOM_001"


supported_resources = ['aws_s3_bucket']


super().__init__(name=name, id=id, supported_resources=supported_resources)




def scan_resource_conf(self, conf):


if 'server_side_encryption_configuration' in conf:


return CheckResult.PASSED


return CheckResult.FAILED





Drift Detection

Drift detection identifies when live infrastructure differs from the declared configuration.




# Terraform plan detects drift


terraform plan -refresh-only # Check for manual changes




# Terraform drift detection with AWS Config


resource "aws_config_config_rule" "s3_bucket_ssl" {


name = "s3-bucket-ssl-requests-only"




source {


owner = "AWS"


source_identifier = "S3_BUCKET_SSL_REQUESTS_ONLY"


}


}








# Continuous drift monitoring


# 1. Schedule terraform plan to run daily


# 2. Compare output with baseline


# 3. Alert on unexpected changes




#!/bin/bash


# drift_detection.sh


BASELINE_DIR="/config/baselines"


REPORT_DIR="/config/reports"




for env in production staging; do


cd terraform/environments/$env


terraform plan -refresh-only -out=plan.tfplan 2>&1 | \


grep -E "changed|destroyed|added" > /tmp/drift.txt




if [ -s /tmp/drift.txt ]; then


# Send alert


./send_alert.sh "Drift detected in $env environment"


cp /tmp/drift.txt "$REPORT_DIR/${env}_drift_$(date +%Y%m%d).txt"


fi


done





Configuration Validation Pipeline




# CI/CD configuration validation stage


stages:


- validate


- scan


- deploy




validate:


stage: validate


script:


- terraform fmt -check


- terraform validate




scan:


stage: scan


script:


- checkov -d . -o json > checkov-report.json


- tfsec . --format sarif > tfsec-report.sarif


- conftest test . --policy policies/




artifacts:


reports:


checkov: checkov-report.json


tfsec: tfsec-report.sarif




deploy:


stage: deploy


script:


- terraform apply -auto-approve


only:


- main


when: manual





Configuration Validation with Conftest

Conftest applies policy-as-code to configuration files using Open Policy Agent (OPA).




# conftest policy for Kubernetes


package main




deny[msg] {


input.kind == "Deployment"


not input.spec.template.spec.containers[_].securityContext.runAsNonRoot


msg = "Containers must run as non-root"


}




deny[msg] {


input.kind == "Deployment"


input.spec.template.spec.containers[_].securityContext.privileged == true


msg = "Privileged containers are not allowed"


}




deny[msg] {


input.kind == "Service"


input.spec.type == "LoadBalancer"


msg = "LoadBalancer services are not allowed"


}




deny[msg] {


input.kind == "Pod"


not input.spec.containers[_].resources.limits.memory


msg = "Memory limits are required"


}








# Run conftest policies


conftest test deployment.yaml --policy conftest/policies/





Policy as Code with OPA




# OPA policy for Terraform


package terraform




deny[msg] {


resource := input.resource.aws_security_group[_]


rule := resource.ingress[_]


rule.from_port == 22


rule.cidr_blocks[_] == "0.0.0.0/0"


msg = sprintf("Security group %v allows SSH from anywhere", [resource.name])


}




deny[msg] {


resource := input.resource.aws_s3_bucket[_]


resource.acl == "public-read" || resource.acl == "public-read-write"


msg = sprintf("S3 bucket %v has public ACL", [resource.name])


}





Secrets in Configuration

Never hardcode secrets in configuration. Use a secrets manager.




# BAD: Hardcoded secret


resource "aws_db_instance" "database" {


username = "admin"


password = "P@ssw0rd123!" # NEVER hardcode


}




# GOOD: Secrets Manager reference


data "aws_secretsmanager_secret_version" "db_creds" {


secret_id = "production/database/credentials"


}




resource "aws_db_instance" "database" {


username = jsondecode(


data.aws_secretsmanager_secret_version.db_creds.secret_string


)["username"]


password = jsondecode(


data.aws_secretsmanager_secret_version.db_creds.secret_string


)["password"]


}





Conclusion

Secure configuration management requires automation at every stage: scan IaC before deployment, validate against policy as code, detect drift continuously, and never embed secrets in configuration. Shift security left by validating configuration at build time rather than discovering issues in production. Use tools like Checkov, tfsec, conftest, and OPA to enforce your security baseline automatically.