WAF-SEC-050 – Network Segmentation & Security Group Hardening

Description

All compute resources MUST be placed in private subnets unless they explicitly serve public traffic. Security groups MUST NOT allow unrestricted ingress from 0.0.0.0/0 or ::/0 on administrative ports (SSH port 22, RDP port 3389). Egress MUST be restricted to required destinations; unrestricted egress (0.0.0.0/0 on all ports) is prohibited for workloads handling sensitive data. Network ACLs (NACLs) and security groups MUST be used in combination for defense-in-depth. VPC resources MUST be logically segmented by workload tier (public, private, data).

Rationale

Risk Description

Risk

Internet-facing resources or open management ports become direct attack vectors.

Risk

Lateral movement is easier when network segmentation and access rules are missing.

Requirements

  • All compute resources MUST be placed in private subnets unless they explicitly serve public traffic.

  • Security groups MUST NOT allow unrestricted ingress from 0.0.0.0/0 or ::/0 on administrative ports (SSH port 22, RDP port 3389).

  • Egress MUST be restricted to required destinations; unrestricted egress (0.0.0.0/0 on all ports) is prohibited for workloads handling sensitive data.

  • Network ACLs (NACLs) and security groups MUST be used in combination for defense-in-depth.

  • VPC resources MUST be logically segmented by workload tier (public, private, data).

Implementation Guidance

  1. Implement the required configuration as code in Terraform or equivalent IaC.

  2. Add an automated check to CI/CD so deviations are caught before deployment.

Maturity Levels

Level Name Criteria

L1

Ad hoc – control not consistently applied

* Manual evidence exists for some workloads, but coverage is unknown.

L2

Standardised – policy defined and communicated

* A version-controlled policy, ADR, or standard describes the requirement.

L3

Integrated – automated validation in CI/CD

* Terraform checks run automatically on pull requests and report results.

L4

Auditable – metrics and exceptions tracked

* Control coverage, failures, and approved exceptions are reported quarterly.

L5

Optimised – continuous improvement based on data

* Control effectiveness is measured and iteratively improved.

Checks

waf-sec-050.tf.aws.no-public-ip-internal-compute

Engine: terraform | Provider: aws | Automated: Yes

Internal compute resources must not be assigned public IP addresses

EC2 instances and launch templates used for application or internal workloads must have associate_public_ip_address = false to ensure they are not directly reachable from the internet.

Scope: resource β€” aws_instance, aws_launch_template

Assertions: * associate_public_ip_address is false β€” Internal compute resources MUST NOT have public IP addresses assigned. Use private subnets and NAT Gateway for outbound access.

Remediation: Set associate_public_ip_address = false on all aws_instance and aws_launch_template resources used for internal workloads. Place these instances in private subnets. Use a NAT Gateway for outbound internet access and an Application Load Balancer in a public subnet for inbound traffic if required.

Compliant
resource "aws_instance" "app" {
  ami                         = data.aws_ami.ubuntu.id
  instance_type               = "t3.medium"
  subnet_id                   = aws_subnet.private.id
  associate_public_ip_address = false
  vpc_security_group_ids      = [aws_security_group.app.id]
  tags = { Name = "app-server" }
}
Non-Compliant
resource "aws_instance" "app" {
  ami                         = data.aws_ami.ubuntu.id
  instance_type               = "t3.medium"
  subnet_id                   = aws_subnet.public.id
  associate_public_ip_address = true   # WAF-SEC-050 violation
  vpc_security_group_ids      = [aws_security_group.app.id]
}

waf-sec-050.tf.aws.no-unrestricted-ssh

Engine: terraform | Provider: aws | Automated: Yes

Security groups must not allow unrestricted SSH or RDP ingress from the internet

Security group ingress rules must not allow port 22 (SSH) or port 3389 (RDP) from 0.0.0.0/0 (IPv4) or ::/0 (IPv6). Administrative access must be provided via AWS Systems Manager Session Manager or a bastion host with restricted CIDR access.

Scope: resource β€” aws_security_group, aws_vpc_security_group_ingress_rule

Assertions: * ingress json not contains pattern pattern "cidr_blocks":\\s*\\["0\\.0\\.0\\.0/0"\\] β€” Security group must not allow ingress from 0.0.0.0/0 on administrative ports. Restrict to known CIDR ranges or use SSM Session Manager.

Remediation: Remove any ingress rule allowing port 22 or 3389 from 0.0.0.0/0. For SSH access, use AWS Systems Manager Session Manager (requires SSM agent and IAM role with AmazonSSMManagedInstanceCore policy). For legacy SSH requirements, restrict the source CIDR to your organization’s VPN or office IP range.

Compliant
resource "aws_security_group" "app" {
  name   = "app-server-sg"
  vpc_id = aws_vpc.main.id
  # No SSH ingress rule – use SSM Session Manager instead
  egress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
    description = "HTTPS outbound for AWS API calls"
  }
}
Non-Compliant
resource "aws_security_group" "bad" {
  name   = "app-server-sg"
  vpc_id = aws_vpc.main.id
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]   # WAF-SEC-050 violation: open SSH to internet
  }
}

waf-sec-050.tf.azurerm.nic-no-public-ip

Engine: terraform | Provider: azurerm | Automated: Yes

Azure network interfaces for internal workloads must not have a public IP address

All azurerm_network_interface resources used for internal application workloads must leave ip_configuration.public_ip_address_id empty or unset.

Scope: resource β€” azurerm_network_interface

Assertions: * ip_configuration.public_ip_address_id equals `` β€” Network interface for internal workloads must not have a public IP address assigned.

Remediation: Remove public_ip_address_id from the ip_configuration block of internal azurerm_network_interface resources. Place VMs behind an Azure Load Balancer or Application Gateway for inbound traffic if required.

Compliant
resource "azurerm_network_interface" "app" {
  name                = "app-nic"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.private.id
    private_ip_address_allocation = "Dynamic"
  }
}
Non-Compliant
resource "azurerm_network_interface" "app" {
  name                = "app-nic"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.private.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.app.id
  }
}

waf-sec-050.tf.azurerm.nsg-no-admin-internet

Engine: terraform | Provider: azurerm | Automated: Yes

Azure network security groups must not allow unrestricted SSH or RDP from the internet

All azurerm_network_security_group resources must not contain a security_rule that allows destination port 22 or 3389 from source_address_prefix "*" or "Internet".

Scope: resource β€” azurerm_network_security_group, azurerm_network_security_rule

Assertions: * security_rule json not contains pattern pattern "destination_port_range":\s*"22" β€” NSG must not allow unrestricted SSH (port 22) from the internet. * security_rule json not contains pattern pattern "destination_port_range":\s*"3389" β€” NSG must not allow unrestricted RDP (port 3389) from the internet.

Remediation: Remove or restrict administrative ingress rules in azurerm_network_security_group and azurerm_network_security_rule resources. Source CIDRs should be limited to known ranges; prefer Azure Bastion or JIT access.

Compliant
resource "azurerm_network_security_group" "app" {
  name                = "app-nsg"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  # No SSH/RDP ingress from the internet
}
Non-Compliant
resource "azurerm_network_security_group" "bad" {
  name                = "bad-nsg"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  security_rule {
    name                       = "AllowSSH"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "22"
    source_address_prefix      = "Internet"
    destination_address_prefix = "*"
  }
}

waf-sec-050.tf.google.vm-no-access-config

Engine: terraform | Provider: google | Automated: Yes

Google Compute Engine instances for internal workloads must not have external IPs

All google_compute_instance resources used for internal application workloads must not contain a network_interface.access_config block (no external IP).

Scope: resource β€” google_compute_instance

Assertions: * network_interface.access_config equals `` β€” Internal compute instances must not have an access_config block assigning an external IP.

Remediation: Remove the access_config block from network_interface on internal google_compute_instance resources. Use Cloud NAT for outbound internet access and a load balancer for inbound traffic if required.

Compliant
resource "google_compute_instance" "app" {
  name         = "app-vm"
  machine_type = "e2-medium"
  zone         = "europe-west3-a"
  network_interface {
    subnetwork = google_compute_subnetwork.private.id
  }
}
Non-Compliant
resource "google_compute_instance" "app" {
  name         = "app-vm"
  machine_type = "e2-medium"
  zone         = "europe-west3-a"
  network_interface {
    subnetwork = google_compute_subnetwork.private.id
    access_config {
      # external IP assigned
    }
  }
}

waf-sec-050.tf.google.firewall-no-admin-internet

Engine: terraform | Provider: google | Automated: Yes

Google Cloud firewalls must not allow unrestricted SSH or RDP from the internet

All google_compute_firewall resources allowing ingress must not allow ports 22 or 3389 from source_ranges containing "0.0.0.0/0".

Scope: resource β€” google_compute_firewall

Assertions: * allow json not contains pattern pattern "ports":\s*\[\s*"22" β€” Firewall must not allow unrestricted SSH (port 22) from the internet. * allow json not contains pattern pattern "ports":\s*\[\s*"3389" β€” Firewall must not allow unrestricted RDP (port 3389) from the internet.

Remediation: Restrict source_ranges on google_compute_firewall rules for management ports to trusted CIDRs. Prefer Identity-Aware Proxy (IAP) for administrative access.

Compliant
resource "google_compute_firewall" "internal" {
  name    = "allow-internal"
  network = google_compute_network.main.id
  allow {
    protocol = "tcp"
    ports    = ["443"]
  }
  source_ranges = ["10.0.0.0/8"]
}
Non-Compliant
resource "google_compute_firewall" "bad" {
  name    = "allow-ssh-internet"
  network = google_compute_network.main.id
  allow {
    protocol = "tcp"
    ports    = ["22"]
  }
  source_ranges = ["0.0.0.0/0"]
}

waf-sec-050.tf.stackit.network-no-public-ssh

Engine: terraform | Provider: stackit | Automated: Yes

STACKIT network resources must not expose administrative ports to the internet

stackit_network and stackit_security_group rules must not allow unrestricted ingress from 0.0.0.0/0 to administrative ports (SSH/RDP/3389) or database ports. Management access must be restricted to trusted sources.

Scope: resource β€” stackit_network, stackit_security_group

Assertions: * name attribute exists β€” STACKIT network resources must be defined and reviewed for unrestricted administrative access.

Remediation: Restrict ingress rules in STACKIT security groups to specific source CIDRs or VPN ranges. Do not expose SSH/RDP/3389 to the public internet.

Compliant
resource "stackit_network" "app" {
  project_id = var.stackit_project_id
  name       = "app-network"
}

# Ingress restricted to trusted office/VPN CIDR via STACKIT console or API
Non-Compliant
resource "stackit_network" "app" {
  project_id = var.stackit_project_id
  name       = "app-network"
  # Security group allows 0.0.0.0/0:22 – WAF-SEC-050 violation
}

waf-sec-050.tf.ovh.vps-no-public-admin

Engine: terraform | Provider: ovh | Automated: Yes

OVHcloud instances must not expose administrative ports to the internet

OVHcloud instances (ovh_cloud_project_instance, ovh_vps, ovh_dedicated_server) must not be reachable from 0.0.0.0/0 on administrative ports. Access must be restricted using OVHcloud firewall or customer-managed network ACLs.

Scope: resource β€” ovh_cloud_project_instance, ovh_vps, ovh_dedicated_server

Assertions: * name attribute exists β€” OVHcloud instance must be defined and reviewed for unrestricted administrative access.

Remediation: Configure OVHcloud firewall rules or host-based firewalls to restrict SSH/RDP/WinRM to trusted sources. Document allowed source ranges.

Compliant
resource "ovh_cloud_project_instance" "app" {
  project_id = var.ovh_project_id
  name       = "app-vm"
  flavor     = "b2-7"
  image      = "Ubuntu 22.04"
  # OVHcloud firewall restricts SSH to trusted CIDR
}
Non-Compliant
resource "ovh_cloud_project_instance" "app" {
  project_id = var.ovh_project_id
  name       = "app-vm"
  # SSH open to 0.0.0.0/0 – WAF-SEC-050 violation
}

waf-sec-050.tf.hcloud.firewall-no-public-admin

Engine: terraform | Provider: hcloud | Automated: Yes

Hetzner Cloud firewalls must block public administrative access

hcloud_firewall rules must not allow 0.0.0.0/0 ingress on administrative ports (SSH/RDP/3389). Management access must be limited to trusted source IPs or a bastion network.

Scope: resource β€” hcloud_firewall

Assertions: * rule json not contains pattern pattern "source_ips":\s*\[\s*"0\\.0\\.0\\.0/0"\s*\] β€” Hetzner Cloud firewall must not allow 0.0.0.0/0 on administrative ports.

Remediation: Create hcloud_firewall rules that restrict SSH and other admin ports to trusted source IPs. Attach the firewall to all production servers.

Compliant
resource "hcloud_firewall" "app" {
  name = "app-firewall"
  rule {
    direction  = "in"
    protocol   = "tcp"
    port       = "22"
    source_ips = ["203.0.113.0/24"]
  }
}
Non-Compliant
resource "hcloud_firewall" "app" {
  name = "app-firewall"
  rule {
    direction  = "in"
    protocol   = "tcp"
    port       = "22"
    source_ips = ["0.0.0.0/0"]  # WAF-SEC-050 violation
  }
}

Evidence

Kind Required Description

iac

Required

Terraform configuration implementing the control.

governance

Required

Policy, ADR, or standard documenting the requirement.

config

Optional

Cloud console or API output confirming runtime state.

Framework Controls

ISO 27001:2022

* A.8.20 – Networks security * A.8.21 – Security of network services * A.8.22 – Segregation of networks

ISO 27017

* CLD.13.1 – Network security management

CIS Controls v8

* CIS 4 – Secure Configuration of Enterprise Assets * CIS 12 – Network Infrastructure Management * CIS 13 – Network Monitoring and Defense

CSA STAR

* CCM IVS-03 – Network Security * CCM IVS-09 – Segmentation and Segregation

PCI DSS v4.0

* Req 1 – Install and maintain network security controls * Req 1.2 – Network segmentation

SOC 2 Type II

* CC6.6 – Network access boundaries

GDPR

* Art. 32 – Security of processing

NIS2

* Art. 21(2)(c) – Network and information system security

DORA

* Art. 9 – Protection and prevention

EUCS (ENISA)

* CSC-01 – Network security controls * CSC-04 – Network segmentation

BSI C5:2020

* COM-01 – Network architecture and segmentation * COM-02 – Network access controls

IT-Grundschutz

* NET.1.1 – Netzarchitektur und -design * NET.3.2 – Firewall

TISAX

* Information security – Network segmentation

ANSSI SecNumCloud

* Domain – Network security and segmentation

BIO

* BIO – Netwerksegmentatie

ENS High

* mp.com.1 – PerΓ­metro seguro * mp.com.4 – SegregaciΓ³n de redes

UK Cyber Essentials

* CE.FW – Firewalls

UK NCSC CAF

* B4 – System security – network and protective monitoring

UK GDPR

* Art. 32 – Security of processing

NIST SP 800-53

* SC-7 – Boundary Protection * AC-4 – Information Flow Enforcement

NIST CSF 2.0

* PR.IR-01 – Networks are protected from unauthorized access

FedRAMP

* SC-7, AC-4 (Moderate/High baseline)

CMMC 2.0

* SC.L2-3.13.1 – Monitor, control and protect communications * SC.L2-3.13.5 – Implement subnetworks for publicly accessible components

ASD Essential 8

* E8.4 – User application hardening (browser/network)

IRAP

* ISM – Network security

CCCS PBMM

* SC-7 – Boundary protection

MAS TRM

* Ch.9 – IT resilience – Network security

ISMAP

* Network access controls and segmentation

FISC

* Technical measures – Network access control