# LOM policy evaluations ======================================================= # Controls: LOM-001 through LOM-009 package controls.aws.lom import data.common.exemptions as exemptions import data.common.outputs as outputs # Scope guard ------------------------------------------------------------------ _config_in_scope if { some _, rc in input.resource_changes rc.type in {"aws_config_configuration_recorder", "aws_config_delivery_channel"} } # LOM-001 — CloudTrail must be multi-region and capture all management events -- deny contains msg if { policy_id := "ICP-TF-AWS-LOM-001" some _, rc in input.resource_changes rc.type == "aws_cloudtrail" rc.change.actions[_] in ["create", "update"] not _cloudtrail_compliant(rc.change.after) meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, sprintf("CloudTrail trail '%s' is not multi-region or does not capture all management events", [rc.name]), ) } _cloudtrail_compliant(after) if { after.is_multi_region_trail == true after.include_global_service_events == true some sel in after.event_selector sel.read_write_type == "All" sel.include_management_events == true } # LOM-002 — CloudTrail log file integrity validation must be enabled ----------- deny contains msg if { policy_id := "ICP-TF-AWS-LOM-002" some _, rc in input.resource_changes rc.type == "aws_cloudtrail" rc.change.actions[_] in ["create", "update"] rc.change.after.enable_log_file_validation != true meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, sprintf("CloudTrail trail '%s' does not have log file validation enabled", [rc.name]), ) } # LOM-003 — CloudTrail logs must be encrypted with a CMK ---------------------- deny contains msg if { policy_id := "ICP-TF-AWS-LOM-003" some _, rc in input.resource_changes rc.type == "aws_cloudtrail" rc.change.actions[_] in ["create", "update"] not _has_kms_key(rc.change.after.kms_key_id) meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, sprintf("CloudTrail trail '%s' is not encrypted with a KMS CMK", [rc.name]), ) } # LOM-004 — CloudWatch Log Groups must be encrypted with a CMK ---------------- deny contains msg if { policy_id := "ICP-TF-AWS-LOM-004" some _, rc in input.resource_changes rc.type == "aws_cloudwatch_log_group" rc.change.actions[_] in ["create", "update"] not _has_kms_key(rc.change.after.kms_key_id) meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, sprintf("CloudWatch Log Group '%s' is not encrypted with a KMS CMK", [rc.name]), ) } # LOM-005 — CloudWatch Log Groups must define an explicit retention period ----- # HCA minimum retention: 365 days deny contains msg if { policy_id := "ICP-TF-AWS-LOM-005" some _, rc in input.resource_changes rc.type == "aws_cloudwatch_log_group" rc.change.actions[_] in ["create", "update"] not _retention_compliant(rc.change.after.retention_in_days) meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, sprintf("CloudWatch Log Group '%s' does not have a retention period of at least 365 days", [rc.name]), ) } _retention_compliant(days) if { days != null days >= 365 } # LOM-006 — VPC Flow Logs must be enabled for every VPC ----------------------- deny contains msg if { policy_id := "ICP-TF-AWS-LOM-006" some _, rc in input.resource_changes rc.type == "aws_vpc" rc.change.actions[_] in ["create", "update"] not _flow_log_exists_by_name(rc.name) meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, sprintf("VPC '%s' has no flow log configured", [rc.name]), ) } _flow_log_exists_by_name(vpc_name) if { some r in _all_configuration_resources r.type == "aws_flow_log" some ref in r.expressions.vpc_id.references ref == concat(".", ["aws_vpc", vpc_name]) } _all_configuration_resources contains r if { some r in input.configuration.root_module.resources } _all_configuration_resources contains r if { some _, mc in input.configuration.root_module.module_calls some r in mc.module.resources } # LOM-007 — ALB/NLB must have access logging enabled -------------------------- deny contains msg if { policy_id := "ICP-TF-AWS-LOM-007" some _, rc in input.resource_changes rc.type == "aws_lb" rc.change.actions[_] in ["create", "update"] not _access_logging_enabled(rc.change.after) meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, sprintf("Load balancer '%s' does not have access logging enabled", [rc.name]), ) } _access_logging_enabled(after) if { some log in after.access_logs log.enabled == true log.bucket != null log.bucket != "" } # LOM-008 — RDS DB instances must export engine logs to CloudWatch ------------- deny contains msg if { policy_id := "ICP-TF-AWS-LOM-008" some _, rc in input.resource_changes rc.type == "aws_db_instance" rc.change.actions[_] in ["create", "update"] not _has_log_exports(rc.change.after) meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, sprintf("RDS instance '%s' has no CloudWatch log exports configured", [rc.name]), ) } _has_log_exports(after) if { count(after.enabled_cloudwatch_logs_exports) > 0 } # LOM-009 — AWS Config recorder and delivery channel must be defined ---------- deny contains msg if { policy_id := "ICP-TF-AWS-LOM-009" _config_in_scope not _config_recorder_exists not _config_delivery_channel_exists meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, "No aws_config_configuration_recorder or aws_config_delivery_channel found in plan", ) } deny contains msg if { policy_id := "ICP-TF-AWS-LOM-009" _config_in_scope _config_recorder_exists not _config_delivery_channel_exists meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, "aws_config_configuration_recorder exists but aws_config_delivery_channel is missing", ) } deny contains msg if { policy_id := "ICP-TF-AWS-LOM-009" _config_in_scope not _config_recorder_exists _config_delivery_channel_exists meta := data.policies[policy_id] meta.status == "active" not exemptions.policy_exemption(policy_id) not exemptions.severity_exemption(meta.severity) msg := outputs.violation( policy_id, meta, "aws_config_delivery_channel exists but aws_config_configuration_recorder is missing", ) } _config_recorder_exists if { some _, rc in input.resource_changes rc.type == "aws_config_configuration_recorder" rc.change.actions[_] in ["create", "update"] } _config_delivery_channel_exists if { some _, rc in input.resource_changes rc.type == "aws_config_delivery_channel" rc.change.actions[_] in ["create", "update"] } # Helpers ---------------------------------------------------------------------- _has_kms_key(key) if { key != null key != "" }