Public subnets route to IGW, private do not
Place databases and application servers in private subnets. Place load balancers and NAT gateways in public subnets.
Cloud deployment and service models define what you manage. The VPC is where you build the virtual network that hosts your workloads.
A VPC (Virtual Private Cloud) is a logically isolated network within a public cloud. It behaves like a private network: you define the IP address range, create subnets, configure route tables, and apply security controls.
A VPC contains one or more subnets. Each subnet maps to a single Availability Zone (AZ) and has its own route table.
A public subnet has a route table entry that sends internet-bound traffic (0.0.0.0/0) to an Internet Gateway (IGW). Instances in a public subnet can have public IP addresses and are directly reachable from the internet.
A private subnet has no route to the IGW. Instances in a private subnet are not directly reachable from the internet. To allow outbound internet access (for software updates, for example), route traffic through a NAT Gateway in the public subnet.
| Component | Purpose |
|---|---|
| Internet Gateway | Connects VPC to the internet. Stateless, horizontally scaled. |
| NAT Gateway | Allows private instances to reach the internet. Blocks inbound initiation. |
| Route Table | Determines where traffic goes. Each subnet has exactly one route table. |
| VPC Peering | Direct connection between two VPCs. Traffic stays on the provider backbone. |
| Transit Gateway | Hub connecting multiple VPCs and on-premises networks. |
| Destination | Target | Purpose |
|---|---|---|
| 10.0.0.0/16 | local | Traffic within the VPC stays local |
| 0.0.0.0/0 | igw-abc123 | Internet-bound traffic (public subnet) |
| 0.0.0.0/0 | nat-xyz789 | Internet-bound traffic (private subnet) |
| 172.16.0.0/12 | vgw-def456 | On-premises traffic via VPN gateway |
A security group acts as a stateful virtual firewall around an individual instance (VM). Every instance belongs to one or more security groups.
Security groups are stateful: if you allow inbound traffic on port 443, the response traffic is automatically allowed outbound. You do not need a separate outbound rule for return traffic. This is the same concept as a stateful firewall on-premises.
Each security group has separate inbound and outbound rule sets. The default security group denies all inbound traffic and allows all outbound traffic.
| Direction | Default Behavior | Typical Configuration |
|---|---|---|
| Inbound | Deny all | Allow specific ports from specific sources |
| Outbound | Allow all | Restrict to specific destinations in high-security environments |
Each rule specifies a protocol (TCP, UDP, ICMP), a port range, and a source (inbound) or destination (outbound). The source can be a CIDR block, another security group, or a prefix list.
| Rule | Protocol | Port | Source | Purpose |
|---|---|---|---|---|
| Allow HTTPS | TCP | 443 | 0.0.0.0/0 | Public web access |
| Allow SSH | TCP | 22 | 10.0.50.0/24 | Admin access from management subnet |
| Allow Modbus | TCP | 502 | sg-scada | SCADA servers (referenced by security group) |
Referencing another security group as the source (instead of an IP range) means “allow traffic from any instance in that security group.” This decouples rules from specific IP addresses, which change when instances are replaced.
| Feature | Security Group | NACL |
|---|---|---|
| Applied to | Instance (VM) | Subnet |
| Stateful | Yes (return traffic allowed automatically) | No (explicit rules for both directions) |
| Rule evaluation | All rules evaluated (union of all allows) | Top-to-bottom, first match |
| Default | Deny all inbound, allow all outbound | Allow all inbound and outbound |
| Use case | Instance-level control | Subnet-level defense in depth |
Use security groups as the primary control. Use NACLs as a second layer for subnet-wide policies (blocking entire IP ranges, for example).
The following script uses the AWS SDK for Python (boto3) to list all security group rules in a VPC. Run it to audit whether any security group allows overly permissive access to OT protocol ports.
import boto3
OT_PORTS = {502, 44818, 2222, 4840, 20000, 102}
def audit_security_groups(vpc_id: str): ec2 = boto3.client("ec2") response = ec2.describe_security_groups( Filters=[{"Name": "vpc-id", "Values": [vpc_id]}] ) findings = [] for sg in response["SecurityGroups"]: for rule in sg.get("IpPermissions", []): from_port = rule.get("FromPort", 0) to_port = rule.get("ToPort", 0) for ip_range in rule.get("IpRanges", []): cidr = ip_range.get("CidrIp", "") # Flag rules that allow OT ports from broad ranges if cidr == "0.0.0.0/0" and any( from_port <= p <= to_port for p in OT_PORTS ): findings.append({ "sg": sg["GroupId"], "name": sg["GroupName"], "port_range": f"{from_port}-{to_port}", "source": cidr, }) return findings
findings = audit_security_groups("vpc-0123456789abcdef0")for f in findings: print(f"WARNING: {f['sg']} ({f['name']}) allows ports {f['port_range']} " f"from {f['source']}")if not findings: print("No overly permissive OT port rules found.")A security group that allows Modbus TCP (port 502) from 0.0.0.0/0 is a critical finding. Modbus has no authentication. Exposing it to the internet gives any attacker full read/write access to PLC registers.
Public subnets route to IGW, private do not
Place databases and application servers in private subnets. Place load balancers and NAT gateways in public subnets.
Security groups are stateful, NACLs are not
Security groups allow return traffic automatically. NACLs require explicit rules for both directions.
Audit OT ports in security groups
Never expose Modbus, EtherNet/IP, or S7comm ports to 0.0.0.0/0. Audit security groups regularly.
VPCs and security groups protect cloud workloads. The next page covers how to connect the cloud to on-premises networks using Direct Connect and VPN, plus NFV concepts.