# Security Module
# Creates security groups, NACLs, and IAM roles
# Web Server Security Group
resource "aws_security_group" "web" {
name = "${var.project_name}-${var.environment}-web-sg"
description = "Security group for web servers"
vpc_id = var.vpc_id
# Allow HTTP from ALB
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
description = "Allow HTTP from ALB"
}
# Allow HTTPS from ALB
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
description = "Allow HTTPS from ALB"
}
# Allow SSH from bastion or VPN (in production, restrict this further)
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # In production, restrict to specific IPs
description = "Allow SSH access"
}
# Allow all outbound traffic
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow all outbound traffic"
}
tags = merge(
var.tags,
{
Name = "${var.project_name}-${var.environment}-web-sg"
}
)
}
# ALB Security Group
resource "aws_security_group" "alb" {
name = "${var.project_name}-${var.environment}-alb-sg"
description = "Security group for application load balancer"
vpc_id = var.vpc_id
# Allow HTTP from anywhere
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow HTTP from anywhere"
}
# Allow HTTPS from anywhere
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow HTTPS from anywhere"
}
# Allow all outbound traffic
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow all outbound traffic"
}
tags = merge(
var.tags,
{
Name = "${var.project_name}-${var.environment}-alb-sg"
}
)
}
# Database Security Group
resource "aws_security_group" "db" {
name = "${var.project_name}-${var.environment}-db-sg"
description = "Security group for database"
vpc_id = var.vpc_id
# Allow MySQL from web servers
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.web.id]
description = "Allow MySQL from web servers"
}
# Allow all outbound traffic
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow all outbound traffic"
}
tags = merge(
var.tags,
{
Name = "${var.project_name}-${var.environment}-db-sg"
}
)
}
# EFS Security Group
resource "aws_security_group" "efs" {
name = "${var.project_name}-${var.environment}-efs-sg"
description = "Security group for EFS"
vpc_id = var.vpc_id
# Allow NFS from web servers
ingress {
from_port = 2049
to_port = 2049
protocol = "tcp"
security_groups = [aws_security_group.web.id]
description = "Allow NFS from web servers"
}
# Allow all outbound traffic
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "Allow all outbound traffic"
}
tags = merge(
var.tags,
{
Name = "${var.project_name}-${var.environment}-efs-sg"
}
)
}
# Network ACLs for public subnets
resource "aws_network_acl" "public" {
vpc_id = var.vpc_id
subnet_ids = var.public_subnet_ids
# Allow all inbound traffic
ingress {
protocol = "-1"
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
# Allow all outbound traffic
egress {
protocol = "-1"
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
tags = merge(
var.tags,
{
Name = "${var.project_name}-${var.environment}-public-nacl"
}
)
}
# Network ACLs for private subnets
resource "aws_network_acl" "private" {
vpc_id = var.vpc_id
subnet_ids = var.private_subnet_ids
# Allow all inbound traffic
ingress {
protocol = "-1"
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
# Allow all outbound traffic
egress {
protocol = "-1"
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
tags = merge(
var.tags,
{
Name = "${var.project_name}-${var.environment}-private-nacl"
}
)
}
# Network ACLs for database subnets
resource "aws_network_acl" "database" {
vpc_id = var.vpc_id
subnet_ids = var.database_subnet_ids
# Allow MySQL from private subnets
ingress {
protocol = "tcp"
rule_no = 100
action = "allow"
cidr_block = var.vpc_cidr
from_port = 3306
to_port = 3306
}
# Allow all return traffic
ingress {
protocol = "tcp"
rule_no = 200
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 1024
to_port = 65535
}
# Allow all outbound traffic
egress {
protocol = "-1"
rule_no = 100
action = "allow"
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
tags = merge(
var.tags,
{
Name = "${var.project_name}-${var.environment}-database-nacl"
}
)
}
# IAM Role for EC2 instances
resource "aws_iam_role" "web_server" {
name = "${var.project_name}-${var.environment}-web-server-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
tags = var.tags
}
# IAM Policy for EC2 instances to access EFS
resource "aws_iam_policy" "efs_access" {
name = "${var.project_name}-${var.environment}-efs-access-policy"
description = "Policy for EC2 instances to access EFS"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"elasticfilesystem:ClientMount",
"elasticfilesystem:ClientWrite"
]
Effect = "Allow"
Resource = "*"
}
]
})
}
# IAM Policy for EC2 instances to access CloudWatch
resource "aws_iam_policy" "cloudwatch_access" {
name = "${var.project_name}-${var.environment}-cloudwatch-access-policy"
description = "Policy for EC2 instances to access CloudWatch"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
]
Effect = "Allow"
Resource = "*"
}
]
})
}
# IAM Policy for EC2 instances to access SSM
resource "aws_iam_policy" "ssm_access" {
name = "${var.project_name}-${var.environment}-ssm-access-policy"
description = "Policy for EC2 instances to access SSM"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"ssm:DescribeAssociation",
"ssm:GetDeployablePatchSnapshotForInstance",
"ssm:GetDocument",
"ssm:DescribeDocument",
"ssm:GetManifest",
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:ListAssociations",
"ssm:ListInstanceAssociations",
"ssm:PutInventory",
"ssm:PutComplianceItems",
"ssm:PutConfigurePackageResult",
"ssm:UpdateAssociationStatus",
"ssm:UpdateInstanceAssociationStatus",
"ssm:UpdateInstanceInformation"
]
Effect = "Allow"
Resource = "*"
}
]
})
}
# Attach policies to the EC2 role
resource "aws_iam_role_policy_attachment" "efs_access" {
role = aws_iam_role.web_server.name
policy_arn = aws_iam_policy.efs_access.arn
}
resource "aws_iam_role_policy_attachment" "cloudwatch_access" {
role = aws_iam_role.web_server.name
policy_arn = aws_iam_policy.cloudwatch_access.arn
}
resource "aws_iam_role_policy_attachment" "ssm_access" {
role = aws_iam_role.web_server.name
policy_arn = aws_iam_policy.ssm_access.arn
}
resource "aws_iam_role_policy_attachment" "ssm_managed" {
role = aws_iam_role.web_server.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
# IAM Instance Profile for EC2 instances
resource "aws_iam_instance_profile" "web_server" {
name = "${var.project_name}-${var.environment}-web-server-profile"
role = aws_iam_role.web_server.name
}