Network & Infrastructure Security

Security Groups & Network ACLs

4 min read

Security groups and Network ACLs (NACLs) provide layered network security. Understanding their differences and proper configuration is critical for defense in depth.

Security Groups vs NACLs

Feature Security Groups Network ACLs
Level Instance/Resource Subnet
State Stateful Stateless
Rules Allow only Allow and Deny
Evaluation All rules evaluated Rules processed in order
Default Deny all inbound Allow all
Best for Application-level filtering Subnet-level blocking

AWS Security Groups

Stateful Behavior

Security groups are stateful—if you allow inbound traffic, the response is automatically allowed:

Inbound Rule: Allow TCP 443 from 0.0.0.0/0
→ Response traffic automatically allowed (no outbound rule needed)

Secure Security Group Patterns

Web server security group:

resource "aws_security_group" "web" {
  name        = "web-server-sg"
  description = "Security group for web servers"
  vpc_id      = aws_vpc.main.id

  # HTTPS from load balancer only
  ingress {
    description     = "HTTPS from ALB"
    from_port       = 443
    to_port         = 443
    protocol        = "tcp"
    security_groups = [aws_security_group.alb.id]
  }

  # No direct SSH - use Session Manager
  # ingress { ... port 22 } # AVOID

  egress {
    description     = "HTTPS to app tier"
    from_port       = 8080
    to_port         = 8080
    protocol        = "tcp"
    security_groups = [aws_security_group.app.id]
  }

  tags = {
    Name = "web-server-sg"
  }
}

Database security group:

resource "aws_security_group" "database" {
  name        = "database-sg"
  description = "Security group for databases"
  vpc_id      = aws_vpc.main.id

  # PostgreSQL from app tier only
  ingress {
    description     = "PostgreSQL from app tier"
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [aws_security_group.app.id]
  }

  # No outbound internet access
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["10.0.0.0/16"]  # VPC only
  }
}

Common Security Group Mistakes

Mistake Risk Solution
0.0.0.0/0 on SSH (22) Brute force attacks Use Session Manager or bastion
0.0.0.0/0 on RDP (3389) Ransomware vector Use VPN or bastion
0.0.0.0/0 on databases Data breach Application tier only
Outbound 0.0.0.0/0 all Data exfiltration Limit to required

Network ACLs (NACLs)

Stateless Behavior

NACLs are stateless—you must explicitly allow both inbound and outbound:

Inbound Rule: Allow TCP 443 from 0.0.0.0/0
Outbound Rule: Allow TCP 1024-65535 to 0.0.0.0/0 (ephemeral ports for response)

NACL for Defense in Depth

Use NACLs to block known bad actors at the subnet level:

resource "aws_network_acl" "private" {
  vpc_id     = aws_vpc.main.id
  subnet_ids = [aws_subnet.private.id]

  # Block known malicious IPs
  ingress {
    protocol   = "-1"
    rule_no    = 100
    action     = "deny"
    cidr_block = "192.0.2.0/24"  # Example blocked range
    from_port  = 0
    to_port    = 0
  }

  # Allow VPC traffic
  ingress {
    protocol   = "-1"
    rule_no    = 200
    action     = "allow"
    cidr_block = "10.0.0.0/16"
    from_port  = 0
    to_port    = 0
  }

  # Deny all other inbound
  ingress {
    protocol   = "-1"
    rule_no    = 32766
    action     = "deny"
    cidr_block = "0.0.0.0/0"
    from_port  = 0
    to_port    = 0
  }

  # Allow all outbound to VPC
  egress {
    protocol   = "-1"
    rule_no    = 100
    action     = "allow"
    cidr_block = "10.0.0.0/16"
    from_port  = 0
    to_port    = 0
  }
}

Azure Network Security Groups

NSG Rules

# Create NSG
az network nsg create \
    --name web-nsg \
    --resource-group myRG

# Allow HTTPS from load balancer
az network nsg rule create \
    --nsg-name web-nsg \
    --resource-group myRG \
    --name AllowHTTPS \
    --priority 100 \
    --access Allow \
    --direction Inbound \
    --protocol Tcp \
    --destination-port-ranges 443 \
    --source-address-prefixes AzureLoadBalancer

# Deny direct SSH
az network nsg rule create \
    --nsg-name web-nsg \
    --resource-group myRG \
    --name DenySSH \
    --priority 200 \
    --access Deny \
    --direction Inbound \
    --protocol Tcp \
    --destination-port-ranges 22 \
    --source-address-prefixes Internet

Application Security Groups (ASG)

Group VMs by function instead of IP:

# Create ASGs
az network asg create --name web-asg --resource-group myRG
az network asg create --name db-asg --resource-group myRG

# NSG rule using ASGs
az network nsg rule create \
    --nsg-name app-nsg \
    --resource-group myRG \
    --name AllowWebToDb \
    --priority 100 \
    --access Allow \
    --direction Inbound \
    --protocol Tcp \
    --destination-port-ranges 5432 \
    --source-asgs web-asg \
    --destination-asgs db-asg

GCP Firewall Rules

VPC Firewall Rules

# Allow HTTPS from load balancer
gcloud compute firewall-rules create allow-https-lb \
    --network=production-vpc \
    --allow=tcp:443 \
    --source-ranges=130.211.0.0/22,35.191.0.0/16 \
    --target-tags=web-server

# Allow internal traffic
gcloud compute firewall-rules create allow-internal \
    --network=production-vpc \
    --allow=tcp,udp,icmp \
    --source-ranges=10.0.0.0/16

# Deny all ingress (implicit, but explicit is better)
gcloud compute firewall-rules create deny-all-ingress \
    --network=production-vpc \
    --action=DENY \
    --rules=all \
    --source-ranges=0.0.0.0/0 \
    --priority=65534

Security Group Audit Checklist

Check Action Priority
No 0.0.0.0/0 on admin ports Remove or restrict Critical
Database not internet-facing Application tier only Critical
Outbound restricted Limit egress rules High
Unused rules removed Audit and clean Medium
Flow logs enabled Monitor traffic High
Documentation current Review quarterly Medium

Next, we'll explore WAF, DDoS protection, and edge security services. :::

Quiz

Module 4: Network & Infrastructure Security

Take Quiz