mirror of
https://github.com/ghndrx/terraform-foundation.git
synced 2026-02-10 06:45:06 +00:00
Enterprise-grade multi-tenant AWS cloud foundation. Modules: - GitHub OIDC for keyless CI/CD authentication - IAM account settings and security baseline - AWS Config Rules for compliance - ABAC (Attribute-Based Access Control) - SCPs (Service Control Policies) Features: - Multi-account architecture - Cost optimization patterns - Security best practices - Comprehensive documentation Tech: Terraform, AWS Organizations, IAM Identity Center
335 lines
8.6 KiB
HCL
335 lines
8.6 KiB
HCL
################################################################################
|
|
# Security Baseline Module
|
|
#
|
|
# Enables core AWS security services:
|
|
# - GuardDuty (threat detection)
|
|
# - Security Hub (security posture)
|
|
# - AWS Config (configuration compliance)
|
|
# - IAM Access Analyzer
|
|
#
|
|
# For multi-account: Deploy in management account, then enable delegated admin
|
|
################################################################################
|
|
|
|
terraform {
|
|
required_providers {
|
|
aws = {
|
|
source = "hashicorp/aws"
|
|
version = ">= 5.0"
|
|
}
|
|
}
|
|
}
|
|
|
|
variable "name" {
|
|
type = string
|
|
description = "Name prefix for resources"
|
|
}
|
|
|
|
variable "enable_guardduty" {
|
|
type = bool
|
|
default = true
|
|
}
|
|
|
|
variable "enable_securityhub" {
|
|
type = bool
|
|
default = true
|
|
}
|
|
|
|
variable "enable_config" {
|
|
type = bool
|
|
default = true
|
|
}
|
|
|
|
variable "enable_access_analyzer" {
|
|
type = bool
|
|
default = true
|
|
}
|
|
|
|
variable "enable_macie" {
|
|
type = bool
|
|
default = false
|
|
description = "Macie for S3 data classification (additional cost)"
|
|
}
|
|
|
|
variable "config_bucket_name" {
|
|
type = string
|
|
description = "S3 bucket for AWS Config recordings"
|
|
}
|
|
|
|
variable "guardduty_finding_publishing_frequency" {
|
|
type = string
|
|
default = "FIFTEEN_MINUTES"
|
|
validation {
|
|
condition = contains(["FIFTEEN_MINUTES", "ONE_HOUR", "SIX_HOURS"], var.guardduty_finding_publishing_frequency)
|
|
error_message = "Must be FIFTEEN_MINUTES, ONE_HOUR, or SIX_HOURS"
|
|
}
|
|
}
|
|
|
|
variable "securityhub_standards" {
|
|
type = list(string)
|
|
default = [
|
|
"aws-foundational-security-best-practices/v/1.0.0",
|
|
"cis-aws-foundations-benchmark/v/1.4.0",
|
|
]
|
|
description = "Security Hub standards to enable"
|
|
}
|
|
|
|
variable "config_rules" {
|
|
type = list(string)
|
|
default = []
|
|
description = "Additional AWS Config managed rule identifiers to enable"
|
|
}
|
|
|
|
variable "tags" {
|
|
type = map(string)
|
|
default = {}
|
|
}
|
|
|
|
data "aws_caller_identity" "current" {}
|
|
data "aws_region" "current" {}
|
|
|
|
################################################################################
|
|
# GuardDuty
|
|
################################################################################
|
|
|
|
resource "aws_guardduty_detector" "main" {
|
|
count = var.enable_guardduty ? 1 : 0
|
|
|
|
enable = true
|
|
finding_publishing_frequency = var.guardduty_finding_publishing_frequency
|
|
|
|
datasources {
|
|
s3_logs {
|
|
enable = true
|
|
}
|
|
kubernetes {
|
|
audit_logs {
|
|
enable = true
|
|
}
|
|
}
|
|
malware_protection {
|
|
scan_ec2_instance_with_findings {
|
|
ebs_volumes {
|
|
enable = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tags = merge(var.tags, { Name = "${var.name}-guardduty" })
|
|
}
|
|
|
|
################################################################################
|
|
# Security Hub
|
|
################################################################################
|
|
|
|
resource "aws_securityhub_account" "main" {
|
|
count = var.enable_securityhub ? 1 : 0
|
|
|
|
enable_default_standards = false
|
|
auto_enable_controls = true
|
|
|
|
depends_on = [aws_guardduty_detector.main]
|
|
}
|
|
|
|
resource "aws_securityhub_standards_subscription" "standards" {
|
|
for_each = var.enable_securityhub ? toset(var.securityhub_standards) : []
|
|
|
|
standards_arn = "arn:aws:securityhub:${data.aws_region.current.name}::standards/${each.value}"
|
|
|
|
depends_on = [aws_securityhub_account.main]
|
|
}
|
|
|
|
################################################################################
|
|
# AWS Config
|
|
################################################################################
|
|
|
|
resource "aws_config_configuration_recorder" "main" {
|
|
count = var.enable_config ? 1 : 0
|
|
|
|
name = var.name
|
|
role_arn = aws_iam_role.config[0].arn
|
|
|
|
recording_group {
|
|
all_supported = true
|
|
include_global_resource_types = true
|
|
}
|
|
|
|
recording_mode {
|
|
recording_frequency = "CONTINUOUS"
|
|
}
|
|
}
|
|
|
|
resource "aws_config_delivery_channel" "main" {
|
|
count = var.enable_config ? 1 : 0
|
|
|
|
name = var.name
|
|
s3_bucket_name = var.config_bucket_name
|
|
s3_key_prefix = "config"
|
|
|
|
snapshot_delivery_properties {
|
|
delivery_frequency = "TwentyFour_Hours"
|
|
}
|
|
|
|
depends_on = [aws_config_configuration_recorder.main]
|
|
}
|
|
|
|
resource "aws_config_configuration_recorder_status" "main" {
|
|
count = var.enable_config ? 1 : 0
|
|
|
|
name = aws_config_configuration_recorder.main[0].name
|
|
is_enabled = true
|
|
|
|
depends_on = [aws_config_delivery_channel.main]
|
|
}
|
|
|
|
# IAM Role for Config
|
|
resource "aws_iam_role" "config" {
|
|
count = var.enable_config ? 1 : 0
|
|
name = "${var.name}-config"
|
|
|
|
assume_role_policy = jsonencode({
|
|
Version = "2012-10-17"
|
|
Statement = [{
|
|
Effect = "Allow"
|
|
Action = "sts:AssumeRole"
|
|
Principal = { Service = "config.amazonaws.com" }
|
|
}]
|
|
})
|
|
|
|
tags = merge(var.tags, { Name = "${var.name}-config" })
|
|
}
|
|
|
|
resource "aws_iam_role_policy_attachment" "config" {
|
|
count = var.enable_config ? 1 : 0
|
|
role = aws_iam_role.config[0].name
|
|
policy_arn = "arn:aws:iam::aws:policy/service-role/AWS_ConfigRole"
|
|
}
|
|
|
|
resource "aws_iam_role_policy" "config_s3" {
|
|
count = var.enable_config ? 1 : 0
|
|
name = "s3-delivery"
|
|
role = aws_iam_role.config[0].id
|
|
|
|
policy = jsonencode({
|
|
Version = "2012-10-17"
|
|
Statement = [{
|
|
Effect = "Allow"
|
|
Action = ["s3:PutObject", "s3:PutObjectAcl"]
|
|
Resource = "arn:aws:s3:::${var.config_bucket_name}/config/*"
|
|
Condition = {
|
|
StringEquals = {
|
|
"s3:x-amz-acl" = "bucket-owner-full-control"
|
|
}
|
|
}
|
|
}, {
|
|
Effect = "Allow"
|
|
Action = ["s3:GetBucketAcl"]
|
|
Resource = "arn:aws:s3:::${var.config_bucket_name}"
|
|
}]
|
|
})
|
|
}
|
|
|
|
################################################################################
|
|
# AWS Config Rules - Security Best Practices
|
|
################################################################################
|
|
|
|
locals {
|
|
default_config_rules = [
|
|
"ENCRYPTED_VOLUMES",
|
|
"RDS_STORAGE_ENCRYPTED",
|
|
"S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED",
|
|
"S3_BUCKET_SSL_REQUESTS_ONLY",
|
|
"S3_BUCKET_PUBLIC_READ_PROHIBITED",
|
|
"S3_BUCKET_PUBLIC_WRITE_PROHIBITED",
|
|
"RESTRICTED_SSH",
|
|
"VPC_DEFAULT_SECURITY_GROUP_CLOSED",
|
|
"VPC_FLOW_LOGS_ENABLED",
|
|
"CLOUD_TRAIL_ENABLED",
|
|
"CLOUD_TRAIL_ENCRYPTION_ENABLED",
|
|
"CLOUD_TRAIL_LOG_FILE_VALIDATION_ENABLED",
|
|
"IAM_ROOT_ACCESS_KEY_CHECK",
|
|
"IAM_USER_MFA_ENABLED",
|
|
"MFA_ENABLED_FOR_IAM_CONSOLE_ACCESS",
|
|
"ROOT_ACCOUNT_MFA_ENABLED",
|
|
"RDS_INSTANCE_PUBLIC_ACCESS_CHECK",
|
|
"GUARDDUTY_ENABLED_CENTRALIZED",
|
|
"SECURITYHUB_ENABLED",
|
|
"EBS_OPTIMIZED_INSTANCE",
|
|
"EC2_IMDSV2_CHECK",
|
|
"EKS_SECRETS_ENCRYPTED",
|
|
"LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED",
|
|
"LAMBDA_INSIDE_VPC",
|
|
]
|
|
|
|
all_config_rules = distinct(concat(local.default_config_rules, var.config_rules))
|
|
}
|
|
|
|
resource "aws_config_config_rule" "rules" {
|
|
for_each = var.enable_config ? toset(local.all_config_rules) : []
|
|
|
|
name = lower(replace(each.value, "_", "-"))
|
|
|
|
source {
|
|
owner = "AWS"
|
|
source_identifier = each.value
|
|
}
|
|
|
|
depends_on = [aws_config_configuration_recorder_status.main]
|
|
|
|
tags = merge(var.tags, { Name = lower(replace(each.value, "_", "-")) })
|
|
}
|
|
|
|
################################################################################
|
|
# IAM Access Analyzer
|
|
################################################################################
|
|
|
|
resource "aws_accessanalyzer_analyzer" "main" {
|
|
count = var.enable_access_analyzer ? 1 : 0
|
|
|
|
analyzer_name = var.name
|
|
type = "ACCOUNT"
|
|
|
|
tags = merge(var.tags, { Name = "${var.name}-access-analyzer" })
|
|
}
|
|
|
|
################################################################################
|
|
# Macie (Optional)
|
|
################################################################################
|
|
|
|
resource "aws_macie2_account" "main" {
|
|
count = var.enable_macie ? 1 : 0
|
|
|
|
finding_publishing_frequency = "FIFTEEN_MINUTES"
|
|
status = "ENABLED"
|
|
}
|
|
|
|
################################################################################
|
|
# Outputs
|
|
################################################################################
|
|
|
|
output "guardduty_detector_id" {
|
|
value = var.enable_guardduty ? aws_guardduty_detector.main[0].id : null
|
|
}
|
|
|
|
output "securityhub_account_id" {
|
|
value = var.enable_securityhub ? aws_securityhub_account.main[0].id : null
|
|
}
|
|
|
|
output "config_recorder_id" {
|
|
value = var.enable_config ? aws_config_configuration_recorder.main[0].id : null
|
|
}
|
|
|
|
output "access_analyzer_arn" {
|
|
value = var.enable_access_analyzer ? aws_accessanalyzer_analyzer.main[0].arn : null
|
|
}
|
|
|
|
output "enabled_services" {
|
|
value = {
|
|
guardduty = var.enable_guardduty
|
|
securityhub = var.enable_securityhub
|
|
config = var.enable_config
|
|
access_analyzer = var.enable_access_analyzer
|
|
macie = var.enable_macie
|
|
}
|
|
}
|