{
  "title": "Step-by-Step AWS VPC Architecture to Meet FAR 52.204-21 / CMMC 2.0 Level 1 - Control - SC.L1-B.1.XI: Isolate Public Subnetworks with Sample CloudFormation",
  "date": "2026-04-07",
  "author": "Lakeridge Technologies",
  "featured_image": "/assets/images/blog/2026/4/step-by-step-aws-vpc-architecture-to-meet-far-52204-21-cmmc-20-level-1-control-scl1-b1xi-isolate-public-subnetworks-with-sample-cloudformation.jpg",
  "content": {
    "full_html": "<p>This post explains how to design and deploy an AWS VPC architecture that satisfies FAR 52.204-21 and CMMC 2.0 Level 1 control SC.L1-B.1.XI — isolating public subnetworks from internal/private subnetworks — with a focused, practical CloudFormation example you can adapt for a small business environment.</p>\n\n<h2>What SC.L1-B.1.XI Requires and Key Objectives</h2>\n<p>At Level 1, SC.L1-B.1.XI requires organizations to isolate public subnetworks (subnets which allow inbound Internet access) from internal/private subnetworks that host Controlled Unclassified Information (CUI) or internal services; the key objectives are preventing direct Internet access to internal hosts, limiting attack surface, and ensuring that public-facing components (ALBs, NAT gateways, bastions) are the only resources exposed to 0.0.0.0/0. For implementation this means segregated route tables, tightly-scoped security groups, optional Network ACLs, and monitoring such as VPC Flow Logs and AWS Config rules to detect drift.</p>\n\n<h2>High-level VPC Architecture Pattern</h2>\n<p>A recommended pattern for small businesses is a 4-subnet VPC per AZ: two public subnets (one per AZ) that host ALBs, NAT Gateways, or managed services that must be Internet-facing, and two private subnets (one per AZ) that host application servers and databases with no direct route to the Internet. Public subnets get a route to an Internet Gateway (IGW); private subnets route outbound via NAT Gateway in the public subnet. Security groups should only allow 0.0.0.0/0 to reach the ALB SG and should not permit direct inbound to private instance SGs. Use SSM Session Manager for admin access instead of an Internet-accessible bastion host where possible.</p>\n\n<h2>Step-by-step Implementation Notes (practical)</h2>\n<p>1) Choose a VPC CIDR (e.g., 10.0.0.0/16) and split into public (10.0.0.0/24, 10.0.1.0/24) and private (10.0.2.0/24, 10.0.3.0/24) subnets across AZs. 2) Create an Internet Gateway and attach it to the VPC; associate a public route table with public subnets only. 3) Provision one Elastic IP + NAT Gateway per AZ (or use 1 NAT GW for cost savings but note HA tradeoffs) and create a private route table where 0.0.0.0/0 points to the NAT. 4) Define security groups: ALB-SG (HTTP/HTTPS from 0.0.0.0/0), App-SG (only allow ingress from ALB-SG on app ports), DB-SG (only allow ingress from App-SG). 5) Enable VPC Flow Logs to a CloudWatch Log Group or S3 and enable AWS Config rules like \"restricted-common-ports\" and \"vpc-default-security-group-closed\" to continuously evaluate compliance.</p>\n\n<h3>Sample CloudFormation (YAML) — deploys the pattern</h3>\n<p>The following minimal CloudFormation template creates a VPC with two public subnets, two private subnets, an IGW, a NAT Gateway, route tables and example security groups. Adapt AZ selection, CIDRs and resource counts to your environment and add outputs, tags, logging, and monitoring as needed.</p>\n\n<pre><code>AWSTemplateFormatVersion: '2010-09-09'\nDescription: VPC with isolated public and private subnets (sample for SC.L1-B.1.XI)\nResources:\n  VPC:\n    Type: AWS::EC2::VPC\n    Properties:\n      CidrBlock: 10.0.0.0/16\n      EnableDnsHostnames: true\n      EnableDnsSupport: true\n      Tags: [{Key: Name, Value: vpc-isolated-sample}]\n\n  InternetGateway:\n    Type: AWS::EC2::InternetGateway\n    Properties:\n      Tags: [{Key: Name, Value: vpc-igw}]\n\n  VPCGatewayAttachment:\n    Type: AWS::EC2::VPCGatewayAttachment\n    Properties:\n      VpcId: !Ref VPC\n      InternetGatewayId: !Ref InternetGateway\n\n  PublicSubnetA:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      CidrBlock: 10.0.0.0/24\n      AvailabilityZone: !Select [0, !GetAZs '']\n      MapPublicIpOnLaunch: true\n      Tags: [{Key: Name, Value: public-subnet-a}]\n\n  PublicSubnetB:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      CidrBlock: 10.0.1.0/24\n      AvailabilityZone: !Select [1, !GetAZs '']\n      MapPublicIpOnLaunch: true\n      Tags: [{Key: Name, Value: public-subnet-b}]\n\n  PrivateSubnetA:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      CidrBlock: 10.0.2.0/24\n      AvailabilityZone: !Select [0, !GetAZs '']\n      MapPublicIpOnLaunch: false\n      Tags: [{Key: Name, Value: private-subnet-a}]\n\n  PrivateSubnetB:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      CidrBlock: 10.0.3.0/24\n      AvailabilityZone: !Select [1, !GetAZs '']\n      MapPublicIpOnLaunch: false\n      Tags: [{Key: Name, Value: private-subnet-b}]\n\n  PublicRouteTable:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n      Tags: [{Key: Name, Value: public-rt}]\n\n  PublicRoute:\n    Type: AWS::EC2::Route\n    DependsOn: VPCGatewayAttachment\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      DestinationCidrBlock: 0.0.0.0/0\n      GatewayId: !Ref InternetGateway\n\n  PublicRTAssocA:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      SubnetId: !Ref PublicSubnetA\n      RouteTableId: !Ref PublicRouteTable\n\n  PublicRTAssocB:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      SubnetId: !Ref PublicSubnetB\n      RouteTableId: !Ref PublicRouteTable\n\n  NatEIP:\n    Type: AWS::EC2::EIP\n    Properties:\n      Domain: vpc\n\n  NatGateway:\n    Type: AWS::EC2::NatGateway\n    Properties:\n      AllocationId: !GetAtt NatEIP.AllocationId\n      SubnetId: !Ref PublicSubnetA\n      Tags: [{Key: Name, Value: nat-gw}]\n\n  PrivateRouteTable:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n      Tags: [{Key: Name, Value: private-rt}]\n\n  PrivateRoute:\n    Type: AWS::EC2::Route\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable\n      DestinationCidrBlock: 0.0.0.0/0\n      NatGatewayId: !Ref NatGateway\n\n  PrivateRTAssocA:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      SubnetId: !Ref PrivateSubnetA\n      RouteTableId: !Ref PrivateRouteTable\n\n  PrivateRTAssocB:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      SubnetId: !Ref PrivateSubnetB\n      RouteTableId: !Ref PrivateRouteTable\n\n  ALBSecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n      GroupDescription: ALB SG - allows HTTP/HTTPS from Internet\n      VpcId: !Ref VPC\n      SecurityGroupIngress:\n        - IpProtocol: tcp\n          FromPort: 80\n          ToPort: 80\n          CidrIp: 0.0.0.0/0\n      Tags: [{Key: Name, Value: alb-sg}]\n\n  AppSecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n      GroupDescription: App SG - only allows traffic from ALB SG\n      VpcId: !Ref VPC\n      SecurityGroupIngress:\n        - IpProtocol: tcp\n          FromPort: 8080\n          ToPort: 8080\n          SourceSecurityGroupId: !Ref ALBSecurityGroup\n      Tags: [{Key: Name, Value: app-sg}]\n\nOutputs:\n  VpcId:\n    Description: VPC ID\n    Value: !Ref VPC\n  PublicSubnetA:\n    Value: !Ref PublicSubnetA\n  PrivateSubnetA:\n    Value: !Ref PrivateSubnetA\n</code></pre>\n\n<h2>How a Small Business Might Use This Pattern</h2>\n<p>Example: a small government contractor runs a public-facing marketing site and an internal proposal management app. Place the public web ALB in public subnets with the ALB-SG open to the Internet; app servers and the database run in private subnets with App-SG and DB-SG allowing traffic only from the ALB and other internal SGs. Admin access uses SSM Session Manager (no public SSH). This set up meets the isolation requirement: internal subnets have no direct IGW route and only limited inbound flows from explicitly allowed sources.</p>\n\n<h2>Compliance Tips and Best Practices</h2>\n<p>1) Tag every subnet and resource with purpose and compliance_tier so auditors can filter resources quickly. 2) Enable VPC Flow Logs and retain logs for the required retention period — stream to S3 with lifecycle rules and encryption. 3) Use AWS Config managed rules (e.g., vpc-default-security-group-closed, restricted-common-ports) and custom Config rules to detect public subnets hosting non-approved instances. 4) Prefer SSM over bastion hosts and use least-privilege IAM for engineers. 5) Regularly scan security groups for 0.0.0.0/0 openings using automated scripts or AWS Trusted Advisor/third-party tooling.</p>\n\n<h2>Risk of Not Isolating Public Subnetworks</h2>\n<p>Failing to isolate public subnets increases risk of direct compromise and lateral movement: if an attacker reaches an Internet-exposed instance in the same subnet as internal servers, they can pivot to databases and CUI, causing data leaks and regulatory noncompliance. For contractors under FAR 52.204-21, this could lead to audit findings, contract penalties, or loss of eligibility for sensitive work. Operationally you also risk undetected exfiltration if you lack flow logs and config tracking.</p>\n\n<p>In summary, isolating public subnetworks is straightforward but essential: use separate route tables, NAT gateways for outbound from private subnets, strict security groups, VPC Flow Logs, and AWS Config to enforce and monitor the state. The provided CloudFormation template gives a practical starting point; adapt CIDRs, HA choices (multiple NATs per AZ), and logging/retention policies to match your small business's risk posture and contractual obligations under FAR and CMMC.</p>",
    "plain_text": "This post explains how to design and deploy an AWS VPC architecture that satisfies FAR 52.204-21 and CMMC 2.0 Level 1 control SC.L1-B.1.XI — isolating public subnetworks from internal/private subnetworks — with a focused, practical CloudFormation example you can adapt for a small business environment.\n\nWhat SC.L1-B.1.XI Requires and Key Objectives\nAt Level 1, SC.L1-B.1.XI requires organizations to isolate public subnetworks (subnets which allow inbound Internet access) from internal/private subnetworks that host Controlled Unclassified Information (CUI) or internal services; the key objectives are preventing direct Internet access to internal hosts, limiting attack surface, and ensuring that public-facing components (ALBs, NAT gateways, bastions) are the only resources exposed to 0.0.0.0/0. For implementation this means segregated route tables, tightly-scoped security groups, optional Network ACLs, and monitoring such as VPC Flow Logs and AWS Config rules to detect drift.\n\nHigh-level VPC Architecture Pattern\nA recommended pattern for small businesses is a 4-subnet VPC per AZ: two public subnets (one per AZ) that host ALBs, NAT Gateways, or managed services that must be Internet-facing, and two private subnets (one per AZ) that host application servers and databases with no direct route to the Internet. Public subnets get a route to an Internet Gateway (IGW); private subnets route outbound via NAT Gateway in the public subnet. Security groups should only allow 0.0.0.0/0 to reach the ALB SG and should not permit direct inbound to private instance SGs. Use SSM Session Manager for admin access instead of an Internet-accessible bastion host where possible.\n\nStep-by-step Implementation Notes (practical)\n1) Choose a VPC CIDR (e.g., 10.0.0.0/16) and split into public (10.0.0.0/24, 10.0.1.0/24) and private (10.0.2.0/24, 10.0.3.0/24) subnets across AZs. 2) Create an Internet Gateway and attach it to the VPC; associate a public route table with public subnets only. 3) Provision one Elastic IP + NAT Gateway per AZ (or use 1 NAT GW for cost savings but note HA tradeoffs) and create a private route table where 0.0.0.0/0 points to the NAT. 4) Define security groups: ALB-SG (HTTP/HTTPS from 0.0.0.0/0), App-SG (only allow ingress from ALB-SG on app ports), DB-SG (only allow ingress from App-SG). 5) Enable VPC Flow Logs to a CloudWatch Log Group or S3 and enable AWS Config rules like \"restricted-common-ports\" and \"vpc-default-security-group-closed\" to continuously evaluate compliance.\n\nSample CloudFormation (YAML) — deploys the pattern\nThe following minimal CloudFormation template creates a VPC with two public subnets, two private subnets, an IGW, a NAT Gateway, route tables and example security groups. Adapt AZ selection, CIDRs and resource counts to your environment and add outputs, tags, logging, and monitoring as needed.\n\nAWSTemplateFormatVersion: '2010-09-09'\nDescription: VPC with isolated public and private subnets (sample for SC.L1-B.1.XI)\nResources:\n  VPC:\n    Type: AWS::EC2::VPC\n    Properties:\n      CidrBlock: 10.0.0.0/16\n      EnableDnsHostnames: true\n      EnableDnsSupport: true\n      Tags: [{Key: Name, Value: vpc-isolated-sample}]\n\n  InternetGateway:\n    Type: AWS::EC2::InternetGateway\n    Properties:\n      Tags: [{Key: Name, Value: vpc-igw}]\n\n  VPCGatewayAttachment:\n    Type: AWS::EC2::VPCGatewayAttachment\n    Properties:\n      VpcId: !Ref VPC\n      InternetGatewayId: !Ref InternetGateway\n\n  PublicSubnetA:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      CidrBlock: 10.0.0.0/24\n      AvailabilityZone: !Select [0, !GetAZs '']\n      MapPublicIpOnLaunch: true\n      Tags: [{Key: Name, Value: public-subnet-a}]\n\n  PublicSubnetB:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      CidrBlock: 10.0.1.0/24\n      AvailabilityZone: !Select [1, !GetAZs '']\n      MapPublicIpOnLaunch: true\n      Tags: [{Key: Name, Value: public-subnet-b}]\n\n  PrivateSubnetA:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      CidrBlock: 10.0.2.0/24\n      AvailabilityZone: !Select [0, !GetAZs '']\n      MapPublicIpOnLaunch: false\n      Tags: [{Key: Name, Value: private-subnet-a}]\n\n  PrivateSubnetB:\n    Type: AWS::EC2::Subnet\n    Properties:\n      VpcId: !Ref VPC\n      CidrBlock: 10.0.3.0/24\n      AvailabilityZone: !Select [1, !GetAZs '']\n      MapPublicIpOnLaunch: false\n      Tags: [{Key: Name, Value: private-subnet-b}]\n\n  PublicRouteTable:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n      Tags: [{Key: Name, Value: public-rt}]\n\n  PublicRoute:\n    Type: AWS::EC2::Route\n    DependsOn: VPCGatewayAttachment\n    Properties:\n      RouteTableId: !Ref PublicRouteTable\n      DestinationCidrBlock: 0.0.0.0/0\n      GatewayId: !Ref InternetGateway\n\n  PublicRTAssocA:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      SubnetId: !Ref PublicSubnetA\n      RouteTableId: !Ref PublicRouteTable\n\n  PublicRTAssocB:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      SubnetId: !Ref PublicSubnetB\n      RouteTableId: !Ref PublicRouteTable\n\n  NatEIP:\n    Type: AWS::EC2::EIP\n    Properties:\n      Domain: vpc\n\n  NatGateway:\n    Type: AWS::EC2::NatGateway\n    Properties:\n      AllocationId: !GetAtt NatEIP.AllocationId\n      SubnetId: !Ref PublicSubnetA\n      Tags: [{Key: Name, Value: nat-gw}]\n\n  PrivateRouteTable:\n    Type: AWS::EC2::RouteTable\n    Properties:\n      VpcId: !Ref VPC\n      Tags: [{Key: Name, Value: private-rt}]\n\n  PrivateRoute:\n    Type: AWS::EC2::Route\n    Properties:\n      RouteTableId: !Ref PrivateRouteTable\n      DestinationCidrBlock: 0.0.0.0/0\n      NatGatewayId: !Ref NatGateway\n\n  PrivateRTAssocA:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      SubnetId: !Ref PrivateSubnetA\n      RouteTableId: !Ref PrivateRouteTable\n\n  PrivateRTAssocB:\n    Type: AWS::EC2::SubnetRouteTableAssociation\n    Properties:\n      SubnetId: !Ref PrivateSubnetB\n      RouteTableId: !Ref PrivateRouteTable\n\n  ALBSecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n      GroupDescription: ALB SG - allows HTTP/HTTPS from Internet\n      VpcId: !Ref VPC\n      SecurityGroupIngress:\n        - IpProtocol: tcp\n          FromPort: 80\n          ToPort: 80\n          CidrIp: 0.0.0.0/0\n      Tags: [{Key: Name, Value: alb-sg}]\n\n  AppSecurityGroup:\n    Type: AWS::EC2::SecurityGroup\n    Properties:\n      GroupDescription: App SG - only allows traffic from ALB SG\n      VpcId: !Ref VPC\n      SecurityGroupIngress:\n        - IpProtocol: tcp\n          FromPort: 8080\n          ToPort: 8080\n          SourceSecurityGroupId: !Ref ALBSecurityGroup\n      Tags: [{Key: Name, Value: app-sg}]\n\nOutputs:\n  VpcId:\n    Description: VPC ID\n    Value: !Ref VPC\n  PublicSubnetA:\n    Value: !Ref PublicSubnetA\n  PrivateSubnetA:\n    Value: !Ref PrivateSubnetA\n\n\nHow a Small Business Might Use This Pattern\nExample: a small government contractor runs a public-facing marketing site and an internal proposal management app. Place the public web ALB in public subnets with the ALB-SG open to the Internet; app servers and the database run in private subnets with App-SG and DB-SG allowing traffic only from the ALB and other internal SGs. Admin access uses SSM Session Manager (no public SSH). This set up meets the isolation requirement: internal subnets have no direct IGW route and only limited inbound flows from explicitly allowed sources.\n\nCompliance Tips and Best Practices\n1) Tag every subnet and resource with purpose and compliance_tier so auditors can filter resources quickly. 2) Enable VPC Flow Logs and retain logs for the required retention period — stream to S3 with lifecycle rules and encryption. 3) Use AWS Config managed rules (e.g., vpc-default-security-group-closed, restricted-common-ports) and custom Config rules to detect public subnets hosting non-approved instances. 4) Prefer SSM over bastion hosts and use least-privilege IAM for engineers. 5) Regularly scan security groups for 0.0.0.0/0 openings using automated scripts or AWS Trusted Advisor/third-party tooling.\n\nRisk of Not Isolating Public Subnetworks\nFailing to isolate public subnets increases risk of direct compromise and lateral movement: if an attacker reaches an Internet-exposed instance in the same subnet as internal servers, they can pivot to databases and CUI, causing data leaks and regulatory noncompliance. For contractors under FAR 52.204-21, this could lead to audit findings, contract penalties, or loss of eligibility for sensitive work. Operationally you also risk undetected exfiltration if you lack flow logs and config tracking.\n\nIn summary, isolating public subnetworks is straightforward but essential: use separate route tables, NAT gateways for outbound from private subnets, strict security groups, VPC Flow Logs, and AWS Config to enforce and monitor the state. The provided CloudFormation template gives a practical starting point; adapt CIDRs, HA choices (multiple NATs per AZ), and logging/retention policies to match your small business's risk posture and contractual obligations under FAR and CMMC."
  },
  "metadata": {
    "description": "Practical, step-by-step AWS VPC design and a reusable CloudFormation template to isolate public subnetworks so small businesses can meet FAR 52.204-21 and CMMC 2.0 Level 1 SC.L1-B.1.XI.",
    "permalink": "/step-by-step-aws-vpc-architecture-to-meet-far-52204-21-cmmc-20-level-1-control-scl1-b1xi-isolate-public-subnetworks-with-sample-cloudformation.json",
    "categories": [],
    "tags": []
  }
}