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

FeatureSecurity GroupsNetwork ACLs
LevelInstance/ResourceSubnet
StateStatefulStateless
RulesAllow onlyAllow and Deny
EvaluationAll rules evaluatedRules processed in order
DefaultDeny all inboundAllow all
Best forApplication-level filteringSubnet-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

MistakeRiskSolution
0.0.0.0/0 on SSH (22)Brute force attacksUse Session Manager or bastion
0.0.0.0/0 on RDP (3389)Ransomware vectorUse VPN or bastion
0.0.0.0/0 on databasesData breachApplication tier only
Outbound 0.0.0.0/0 allData exfiltrationLimit 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

CheckActionPriority
No 0.0.0.0/0 on admin portsRemove or restrictCritical
Database not internet-facingApplication tier onlyCritical
Outbound restrictedLimit egress rulesHigh
Unused rules removedAudit and cleanMedium
Flow logs enabledMonitor trafficHigh
Documentation currentReview quarterlyMedium

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

Quick check: how does this lesson land for you?

Quiz

Module 4: Network & Infrastructure Security

Take Quiz
FREE WEEKLY NEWSLETTER

Stay on the Nerd Track

One email per week — courses, deep dives, tools, and AI experiments.

No spam. Unsubscribe anytime.