Skip to content
Home / Skills / Aws / Networking
AW

Networking

Aws core v1.0.0

AWS Networking

Overview

This skill covers AWS networking fundamentals including VPC design, subnets, security groups, load balancers, and connectivity options. Proper network architecture is critical for security, performance, and cost optimization.


Key Concepts

VPC Architecture

┌─────────────────────────────────────────────────────────────┐
│                     VPC (10.0.0.0/16)                       │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌────────────────────┐    ┌────────────────────┐          │
│  │  Availability Zone A│    │  Availability Zone B│          │
│  │                    │    │                    │          │
│  │  ┌──────────────┐  │    │  ┌──────────────┐  │          │
│  │  │Public Subnet │  │    │  │Public Subnet │  │          │
│  │  │ 10.0.1.0/24  │  │    │  │ 10.0.2.0/24  │  │          │
│  │  │  ┌───┐ ┌───┐ │  │    │  │  ┌───┐ ┌───┐ │  │          │
│  │  │  │ALB│ │NAT│ │  │    │  │  │ALB│ │NAT│ │  │          │
│  │  │  └───┘ └───┘ │  │    │  │  └───┘ └───┘ │  │          │
│  │  └──────────────┘  │    │  └──────────────┘  │          │
│  │                    │    │                    │          │
│  │  ┌──────────────┐  │    │  ┌──────────────┐  │          │
│  │  │Private Subnet│  │    │  │Private Subnet│  │          │
│  │  │ 10.0.11.0/24 │  │    │  │ 10.0.12.0/24 │  │          │
│  │  │  ┌───┐ ┌───┐ │  │    │  │  ┌───┐ ┌───┐ │  │          │
│  │  │  │ECS│ │ECS│ │  │    │  │  │ECS│ │ECS│ │  │          │
│  │  │  └───┘ └───┘ │  │    │  │  └───┘ └───┘ │  │          │
│  │  └──────────────┘  │    │  └──────────────┘  │          │
│  │                    │    │                    │          │
│  │  ┌──────────────┐  │    │  ┌──────────────┐  │          │
│  │  │Isolated Subnet│  │    │  │Isolated Subnet│  │          │
│  │  │ 10.0.21.0/24 │  │    │  │ 10.0.22.0/24 │  │          │
│  │  │  ┌───┐       │  │    │  │  ┌───┐       │  │          │
│  │  │  │RDS│       │  │    │  │  │RDS│       │  │          │
│  │  │  └───┘       │  │    │  │  └───┘       │  │          │
│  │  └──────────────┘  │    │  └──────────────┘  │          │
│  └────────────────────┘    └────────────────────┘          │
│                                                              │
│  Internet ←→ IGW ←→ Public Subnets                         │
│  Private Subnets ←→ NAT Gateway ←→ Internet                │
│  VPC Endpoints ←→ AWS Services (S3, DynamoDB, etc.)        │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Best Practices

1. Use Multiple Availability Zones

Deploy across at least 2 AZs for high availability.

2. Implement Least Privilege Security Groups

Only allow required traffic, deny by default.

3. Use VPC Endpoints

Access AWS services without internet gateway.

4. Enable VPC Flow Logs

Monitor network traffic for security and debugging.

5. Plan IP Address Space

Leave room for growth and avoid overlapping CIDR blocks.


Code Examples

Example 1: VPC with Best Practices

import * as ec2 from 'aws-cdk-lib/aws-ec2';

export class NetworkStack extends cdk.Stack {
  public readonly vpc: ec2.Vpc;

  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // VPC with 3-tier architecture
    this.vpc = new ec2.Vpc(this, 'MainVpc', {
      vpcName: 'main-vpc',
      ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'),
      maxAzs: 3,
      
      // Subnet configuration
      subnetConfiguration: [
        {
          name: 'Public',
          subnetType: ec2.SubnetType.PUBLIC,
          cidrMask: 24,
          mapPublicIpOnLaunch: false,
        },
        {
          name: 'Private',
          subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
          cidrMask: 24,
        },
        {
          name: 'Isolated',
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
          cidrMask: 24,
        },
      ],
      
      // NAT Gateway configuration
      natGateways: 1, // Cost optimization: 1 for non-prod, 3 for prod
      natGatewaySubnets: {
        subnetType: ec2.SubnetType.PUBLIC,
      },
      
      // DNS settings
      enableDnsHostnames: true,
      enableDnsSupport: true,
    });

    // VPC Flow Logs
    this.vpc.addFlowLog('FlowLog', {
      destination: ec2.FlowLogDestination.toCloudWatchLogs(flowLogGroup, flowLogRole),
      trafficType: ec2.FlowLogTrafficType.ALL,
      maxAggregationInterval: ec2.FlowLogMaxAggregationInterval.ONE_MINUTE,
    });

    // Gateway Endpoints (free)
    this.vpc.addGatewayEndpoint('S3Endpoint', {
      service: ec2.GatewayVpcEndpointAwsService.S3,
    });

    this.vpc.addGatewayEndpoint('DynamoDBEndpoint', {
      service: ec2.GatewayVpcEndpointAwsService.DYNAMODB,
    });

    // Interface Endpoints (cost per hour + data)
    this.vpc.addInterfaceEndpoint('SecretsManagerEndpoint', {
      service: ec2.InterfaceVpcEndpointAwsService.SECRETS_MANAGER,
      subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
      privateDnsEnabled: true,
    });

    this.vpc.addInterfaceEndpoint('EcrEndpoint', {
      service: ec2.InterfaceVpcEndpointAwsService.ECR,
      subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
    });

    this.vpc.addInterfaceEndpoint('EcrDockerEndpoint', {
      service: ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER,
      subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
    });

    this.vpc.addInterfaceEndpoint('LogsEndpoint', {
      service: ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS,
      subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
    });
  }
}

Example 2: Security Groups

export class SecurityGroupsStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: SecurityGroupsStackProps) {
    super(scope, id, props);

    const { vpc } = props;

    // ALB Security Group
    const albSecurityGroup = new ec2.SecurityGroup(this, 'AlbSG', {
      vpc,
      securityGroupName: 'alb-sg',
      description: 'Security group for Application Load Balancer',
      allowAllOutbound: false,
    });

    albSecurityGroup.addIngressRule(
      ec2.Peer.anyIpv4(),
      ec2.Port.tcp(443),
      'Allow HTTPS from internet'
    );

    albSecurityGroup.addEgressRule(
      ec2.Peer.ipv4(vpc.vpcCidrBlock),
      ec2.Port.tcp(8080),
      'Allow to application port'
    );

    // Application Security Group
    const appSecurityGroup = new ec2.SecurityGroup(this, 'AppSG', {
      vpc,
      securityGroupName: 'app-sg',
      description: 'Security group for application containers',
      allowAllOutbound: false,
    });

    // Only allow from ALB
    appSecurityGroup.addIngressRule(
      albSecurityGroup,
      ec2.Port.tcp(8080),
      'Allow from ALB'
    );

    // Allow outbound to AWS services via VPC endpoints
    appSecurityGroup.addEgressRule(
      ec2.Peer.prefixList(s3PrefixListId),
      ec2.Port.tcp(443),
      'Allow to S3'
    );

    appSecurityGroup.addEgressRule(
      ec2.Peer.ipv4(vpc.vpcCidrBlock),
      ec2.Port.tcp(443),
      'Allow to VPC endpoints'
    );

    // Database Security Group
    const dbSecurityGroup = new ec2.SecurityGroup(this, 'DbSG', {
      vpc,
      securityGroupName: 'db-sg',
      description: 'Security group for database',
      allowAllOutbound: false, // DB shouldn't initiate outbound
    });

    dbSecurityGroup.addIngressRule(
      appSecurityGroup,
      ec2.Port.tcp(5432),
      'Allow PostgreSQL from app'
    );

    // Bastion Security Group (for emergency access)
    const bastionSecurityGroup = new ec2.SecurityGroup(this, 'BastionSG', {
      vpc,
      securityGroupName: 'bastion-sg',
      description: 'Security group for bastion host',
    });

    bastionSecurityGroup.addIngressRule(
      ec2.Peer.ipv4('10.0.0.0/8'), // Corporate network only
      ec2.Port.tcp(22),
      'Allow SSH from corporate network'
    );

    // Allow bastion to access DB (for debugging)
    dbSecurityGroup.addIngressRule(
      bastionSecurityGroup,
      ec2.Port.tcp(5432),
      'Allow PostgreSQL from bastion'
    );
  }
}

Example 3: Application Load Balancer

import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';

export class LoadBalancerStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: LoadBalancerStackProps) {
    super(scope, id, props);

    const { vpc, service, certificate } = props;

    // Application Load Balancer
    const alb = new elbv2.ApplicationLoadBalancer(this, 'ALB', {
      loadBalancerName: 'order-service-alb',
      vpc,
      internetFacing: true,
      vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
      
      // Security
      securityGroup: albSecurityGroup,
      
      // Access logs
      http2Enabled: true,
      idleTimeout: cdk.Duration.seconds(60),
    });

    // Enable access logging
    alb.logAccessLogs(accessLogsBucket, 'alb-logs');

    // HTTPS Listener
    const httpsListener = alb.addListener('HttpsListener', {
      port: 443,
      certificates: [certificate],
      sslPolicy: elbv2.SslPolicy.TLS13_13,
    });

    // Target Group with health check
    const targetGroup = httpsListener.addTargets('AppTargets', {
      port: 8080,
      protocol: elbv2.ApplicationProtocol.HTTP,
      targets: [service],
      
      healthCheck: {
        path: '/actuator/health',
        interval: cdk.Duration.seconds(30),
        timeout: cdk.Duration.seconds(5),
        healthyThresholdCount: 2,
        unhealthyThresholdCount: 3,
        healthyHttpCodes: '200',
      },
      
      deregistrationDelay: cdk.Duration.seconds(30),
      
      stickinessCookieDuration: cdk.Duration.hours(1),
    });

    // HTTP to HTTPS redirect
    alb.addListener('HttpListener', {
      port: 80,
      defaultAction: elbv2.ListenerAction.redirect({
        port: '443',
        protocol: 'HTTPS',
        permanent: true,
      }),
    });

    // Path-based routing
    httpsListener.addAction('ApiAction', {
      priority: 10,
      conditions: [
        elbv2.ListenerCondition.pathPatterns(['/api/*']),
      ],
      action: elbv2.ListenerAction.forward([apiTargetGroup]),
    });

    // Host-based routing
    httpsListener.addAction('AdminAction', {
      priority: 5,
      conditions: [
        elbv2.ListenerCondition.hostHeaders(['admin.example.com']),
      ],
      action: elbv2.ListenerAction.forward([adminTargetGroup]),
    });

    // Default action
    httpsListener.addAction('DefaultAction', {
      action: elbv2.ListenerAction.forward([targetGroup]),
    });
  }
}

Example 4: VPC Peering and Transit Gateway

export class ConnectivityStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // VPC Peering (for simple 2 VPC connections)
    const peeringConnection = new ec2.CfnVPCPeeringConnection(this, 'VpcPeering', {
      vpcId: productionVpc.vpcId,
      peerVpcId: managementVpc.vpcId,
      peerOwnerId: this.account,
      peerRegion: this.region,
    });

    // Add routes for peering
    productionVpc.privateSubnets.forEach((subnet, index) => {
      new ec2.CfnRoute(this, `ProdToMgmtRoute${index}`, {
        routeTableId: subnet.routeTable.routeTableId,
        destinationCidrBlock: managementVpc.vpcCidrBlock,
        vpcPeeringConnectionId: peeringConnection.ref,
      });
    });

    // Transit Gateway (for complex multi-VPC/on-premises)
    const transitGateway = new ec2.CfnTransitGateway(this, 'TransitGateway', {
      amazonSideAsn: 64512,
      autoAcceptSharedAttachments: 'enable',
      defaultRouteTableAssociation: 'enable',
      defaultRouteTablePropagation: 'enable',
      dnsSupport: 'enable',
      vpnEcmpSupport: 'enable',
      tags: [{ key: 'Name', value: 'main-tgw' }],
    });

    // Attach VPCs to Transit Gateway
    const prodAttachment = new ec2.CfnTransitGatewayAttachment(this, 'ProdAttachment', {
      transitGatewayId: transitGateway.ref,
      vpcId: productionVpc.vpcId,
      subnetIds: productionVpc.privateSubnets.map(s => s.subnetId),
    });

    // Site-to-Site VPN for on-premises
    const customerGateway = new ec2.CfnCustomerGateway(this, 'CustomerGateway', {
      bgpAsn: 65000,
      ipAddress: '203.0.113.1', // On-premises router IP
      type: 'ipsec.1',
    });

    const vpnConnection = new ec2.CfnVPNConnection(this, 'VpnConnection', {
      customerGatewayId: customerGateway.ref,
      transitGatewayId: transitGateway.ref,
      type: 'ipsec.1',
      staticRoutesOnly: false, // Use BGP
    });
  }
}

Example 5: Network ACLs

export class NaclStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: NaclStackProps) {
    super(scope, id, props);

    const { vpc } = props;

    // NACL for public subnets
    const publicNacl = new ec2.NetworkAcl(this, 'PublicNacl', {
      vpc,
      networkAclName: 'public-nacl',
    });

    // Inbound rules
    publicNacl.addEntry('InboundHttps', {
      ruleNumber: 100,
      cidr: ec2.AclCidr.anyIpv4(),
      traffic: ec2.AclTraffic.tcpPort(443),
      direction: ec2.TrafficDirection.INGRESS,
      ruleAction: ec2.Action.ALLOW,
    });

    publicNacl.addEntry('InboundHttp', {
      ruleNumber: 110,
      cidr: ec2.AclCidr.anyIpv4(),
      traffic: ec2.AclTraffic.tcpPort(80),
      direction: ec2.TrafficDirection.INGRESS,
      ruleAction: ec2.Action.ALLOW,
    });

    // Ephemeral ports for return traffic
    publicNacl.addEntry('InboundEphemeral', {
      ruleNumber: 200,
      cidr: ec2.AclCidr.anyIpv4(),
      traffic: ec2.AclTraffic.tcpPortRange(1024, 65535),
      direction: ec2.TrafficDirection.INGRESS,
      ruleAction: ec2.Action.ALLOW,
    });

    // Outbound rules
    publicNacl.addEntry('OutboundAll', {
      ruleNumber: 100,
      cidr: ec2.AclCidr.anyIpv4(),
      traffic: ec2.AclTraffic.allTraffic(),
      direction: ec2.TrafficDirection.EGRESS,
      ruleAction: ec2.Action.ALLOW,
    });

    // Associate with public subnets
    vpc.publicSubnets.forEach((subnet, index) => {
      new ec2.SubnetNetworkAclAssociation(this, `PublicNaclAssoc${index}`, {
        subnet,
        networkAcl: publicNacl,
      });
    });

    // NACL for private subnets (more restrictive)
    const privateNacl = new ec2.NetworkAcl(this, 'PrivateNacl', {
      vpc,
      networkAclName: 'private-nacl',
    });

    // Only allow from VPC
    privateNacl.addEntry('InboundFromVpc', {
      ruleNumber: 100,
      cidr: ec2.AclCidr.ipv4(vpc.vpcCidrBlock),
      traffic: ec2.AclTraffic.allTraffic(),
      direction: ec2.TrafficDirection.INGRESS,
      ruleAction: ec2.Action.ALLOW,
    });

    // Allow return traffic from internet (for NAT)
    privateNacl.addEntry('InboundEphemeral', {
      ruleNumber: 200,
      cidr: ec2.AclCidr.anyIpv4(),
      traffic: ec2.AclTraffic.tcpPortRange(1024, 65535),
      direction: ec2.TrafficDirection.INGRESS,
      ruleAction: ec2.Action.ALLOW,
    });

    privateNacl.addEntry('OutboundAll', {
      ruleNumber: 100,
      cidr: ec2.AclCidr.anyIpv4(),
      traffic: ec2.AclTraffic.allTraffic(),
      direction: ec2.TrafficDirection.EGRESS,
      ruleAction: ec2.Action.ALLOW,
    });
  }
}

Anti-Patterns

❌ Over-permissive Security Groups

// WRONG - allows all traffic
securityGroup.addIngressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.allTraffic(),
  'Allow all'
);

// ✅ CORRECT - least privilege
securityGroup.addIngressRule(
  albSecurityGroup,
  ec2.Port.tcp(8080),
  'Allow from ALB only'
);

References