#Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS #FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR #COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER #IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN #CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. AWSTemplateFormatVersion: "2010-09-09" Description: "AWS Network Firewall Demo using multiple VPCs" Metadata: "AWS::CloudFormation::Interface": ParameterGroups: - Label: default: "VPC Parameters" Parameters: - AvailabilityZoneSelection - Label: default: "EC2 Parameters" Parameters: - LatestAmiId Parameters: AvailabilityZoneSelection: Description: Availability Zone Type: AWS::EC2::AvailabilityZone::Name Default: us-east-1a LatestAmiId: Description: Latest EC2 AMI from Systems Manager Parameter Store Type: 'AWS::SSM::Parameter::Value' Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' Resources: # spoke-a VPC VPCA: Type: AWS::EC2::VPC Properties: CidrBlock: "10.1.0.0/16" EnableDnsSupport: "true" EnableDnsHostnames: "true" InstanceTenancy: default Tags: - Key: Name Value: !Sub "spoke-a-${AWS::StackName}" SubnetAWorkload: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPCA CidrBlock: "10.1.1.0/24" AvailabilityZone: Ref: AvailabilityZoneSelection MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub "spoke-a-workload-${AWS::StackName}" SubnetATGW: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPCA CidrBlock: "10.1.0.0/28" AvailabilityZone: Ref: AvailabilityZoneSelection MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub "spoke-a-tgw-${AWS::StackName}" VPCAEndpointSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Allow instances to get to SSM Systems Manager VpcId: !Ref VPCA SecurityGroupIngress: - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 10.1.0.0/16 VPCASSMEndpoint: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref VPCAEndpointSecurityGroup ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm" SubnetIds: - !Ref SubnetAWorkload VpcEndpointType: Interface VpcId: !Ref VPCA VPCAEC2MessagesEndpoint: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref VPCAEndpointSecurityGroup ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2messages" SubnetIds: - !Ref SubnetAWorkload VpcEndpointType: Interface VpcId: !Ref VPCA VPCASSMMessagesEndpoint: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref VPCAEndpointSecurityGroup ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages" SubnetIds: - !Ref SubnetAWorkload VpcEndpointType: Interface VpcId: !Ref VPCA SubnetARole: Type: AWS::IAM::Role Properties: RoleName: !Sub "subnet-a-role-${AWS::StackName}" Path: "/" ManagedPolicyArns: - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM" AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole SubnetAInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: "/" Roles: - !Ref SubnetARole SubnetASecGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: "ICMP acess from 10.0.0.0/8" GroupName: !Sub "spoke-a-sec-group-${AWS::StackName}" VpcId: !Ref VPCA SecurityGroupIngress: - IpProtocol: icmp CidrIp: 10.0.0.0/8 FromPort: "-1" ToPort: "-1" EC2SubnetA: Type: 'AWS::EC2::Instance' Properties: ImageId: !Ref LatestAmiId SubnetId: !Ref SubnetAWorkload InstanceType: t2.micro SecurityGroupIds: - !Ref SubnetASecGroup IamInstanceProfile: !Ref SubnetAInstanceProfile Tags: - Key: Name Value: !Sub "spoke-a-${AWS::StackName}" # spoke-b VPC VPCB: Type: AWS::EC2::VPC Properties: CidrBlock: "10.2.0.0/16" EnableDnsSupport: "true" EnableDnsHostnames: "true" InstanceTenancy: default Tags: - Key: Name Value: !Sub "spoke-b-${AWS::StackName}" SubnetBWorkload: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPCB CidrBlock: "10.2.1.0/24" AvailabilityZone: Ref: AvailabilityZoneSelection MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub "spoke-b-workload-${AWS::StackName}" SubnetBTGW: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPCB CidrBlock: "10.2.0.0/28" AvailabilityZone: Ref: AvailabilityZoneSelection MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub "spoke-b-tgw-${AWS::StackName}" VPCBEndpointSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Allow instances to get to SSM Systems Manager VpcId: !Ref VPCB SecurityGroupIngress: - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 10.2.0.0/16 VPCBSSMEndpoint: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref VPCBEndpointSecurityGroup ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm" SubnetIds: - !Ref SubnetBWorkload VpcEndpointType: Interface VpcId: !Ref VPCB VPCBEC2MessagesEndpoint: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref VPCBEndpointSecurityGroup ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2messages" SubnetIds: - !Ref SubnetBWorkload VpcEndpointType: Interface VpcId: !Ref VPCB VPCBSSMMessagesEndpoint: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref VPCBEndpointSecurityGroup ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages" SubnetIds: - !Ref SubnetBWorkload VpcEndpointType: Interface VpcId: !Ref VPCB SubnetBRole: Type: AWS::IAM::Role Properties: RoleName: !Sub "subnet-b-role-${AWS::StackName}" Path: "/" ManagedPolicyArns: - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM" AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole SubnetBInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: "/" Roles: - !Ref SubnetBRole SubnetBSecGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: "ICMP acess from 10.0.0.0/8" GroupName: !Sub "spoke-b-sec-group-${AWS::StackName}" VpcId: !Ref VPCB SecurityGroupIngress: - IpProtocol: icmp CidrIp: 10.0.0.0/8 FromPort: "-1" ToPort: "-1" EC2SubnetB: Type: 'AWS::EC2::Instance' Properties: ImageId: !Ref LatestAmiId SubnetId: !Ref SubnetBWorkload InstanceType: t2.micro SecurityGroupIds: - !Ref SubnetBSecGroup IamInstanceProfile: !Ref SubnetBInstanceProfile Tags: - Key: Name Value: !Sub "spoke-b-${AWS::StackName}" # inspection VPC VPCC: Type: AWS::EC2::VPC Properties: CidrBlock: "100.64.0.0/16" EnableDnsSupport: "true" EnableDnsHostnames: "true" InstanceTenancy: default Tags: - Key: Name Value: !Sub "inspection-${AWS::StackName}" SubnetCFirewall: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPCC CidrBlock: "100.64.0.16/28" AvailabilityZone: Ref: AvailabilityZoneSelection MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub "inspection-firewall-${AWS::StackName}" SubnetCTGW: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPCC CidrBlock: "100.64.0.0/28" AvailabilityZone: Ref: AvailabilityZoneSelection MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub "inspections-tgw-${AWS::StackName}" # egress VPC VPCD: Type: AWS::EC2::VPC Properties: CidrBlock: "10.10.0.0/16" EnableDnsSupport: "true" EnableDnsHostnames: "true" InstanceTenancy: default Tags: - Key: Name Value: !Sub "egress-${AWS::StackName}" SubnetDTGW: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPCD CidrBlock: "10.10.0.0/28" AvailabilityZone: Ref: AvailabilityZoneSelection MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub "egress-tgw-${AWS::StackName}" SubnetDPublic: Type: AWS::EC2::Subnet Properties: VpcId: Ref: VPCD CidrBlock: "10.10.1.0/24" AvailabilityZone: Ref: AvailabilityZoneSelection MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub "egress-public-${AWS::StackName}" InternetGatewayVPCD: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub "egress-igw-${AWS::StackName}" AttachGatewayVPCD: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref VPCD InternetGatewayId: !Ref InternetGatewayVPCD SubnetDNATEIP: Type: "AWS::EC2::EIP" Properties: Domain: vpc SubnetDNATGateway: Type: "AWS::EC2::NatGateway" Properties: AllocationId: Fn::GetAtt: - SubnetDNATEIP - AllocationId SubnetId: Ref: SubnetDPublic Tags: - Key: Name Value: !Sub "egress-natgw-${AWS::StackName}" # Transit Gateway TransitGateway: Type: "AWS::EC2::TransitGateway" Properties: AmazonSideAsn: 65000 Description: "TGW Network Firewall Demo" AutoAcceptSharedAttachments: "disable" DefaultRouteTableAssociation: "disable" DefaultRouteTablePropagation: "disable" DnsSupport: "enable" VpnEcmpSupport: "enable" Tags: - Key: Name Value: !Sub "tgw-${AWS::StackName}" AttachVPCA: Type: "AWS::EC2::TransitGatewayAttachment" Properties: SubnetIds: - !Ref SubnetATGW Tags: - Key: Name Value: !Sub "spoke-a-attach-${AWS::StackName}" TransitGatewayId: !Ref TransitGateway VpcId: !Ref VPCA AttachVPCB: Type: "AWS::EC2::TransitGatewayAttachment" Properties: SubnetIds: - !Ref SubnetBTGW Tags: - Key: Name Value: !Sub "spoke-b-attach-${AWS::StackName}" TransitGatewayId: !Ref TransitGateway VpcId: !Ref VPCB AttachVPCC: Type: "AWS::EC2::TransitGatewayAttachment" Properties: SubnetIds: - !Ref SubnetCTGW Tags: - Key: Name Value: !Sub "inspection-attach-${AWS::StackName}" TransitGatewayId: !Ref TransitGateway VpcId: !Ref VPCC AttachVPCD: Type: "AWS::EC2::TransitGatewayAttachment" Properties: SubnetIds: - !Ref SubnetDTGW Tags: - Key: Name Value: !Sub "egress-attach-${AWS::StackName}" TransitGatewayId: !Ref TransitGateway VpcId: !Ref VPCD # Firewalls ICMPAlertStatefulRuleGroup: Type: 'AWS::NetworkFirewall::RuleGroup' Properties: RuleGroupName: !Sub "icmp-alert-${AWS::StackName}" Type: STATEFUL Capacity: 100 RuleGroup: RulesSource: StatefulRules: - Action: ALERT Header: Direction: ANY Protocol: ICMP Destination: ANY Source: ANY DestinationPort: ANY SourcePort: ANY RuleOptions: - Keyword: "sid:1" Tags: - Key: Name Value: !Sub "icmp-alert-${AWS::StackName}" DomainAllowStatefulRuleGroup: Type: 'AWS::NetworkFirewall::RuleGroup' Properties: RuleGroupName: !Sub "domain-allow-${AWS::StackName}" Type: STATEFUL Capacity: 100 RuleGroup: RuleVariables: IPSets: HOME_NET: Definition: - "10.0.0.0/8" RulesSource: RulesSourceList: TargetTypes: - HTTP_HOST - TLS_SNI Targets: - ".amazon.com" GeneratedRulesType: "ALLOWLIST" Tags: - Key: Name Value: !Sub "domain-allow-${AWS::StackName}" InspectionFirewallPolicy: Type: AWS::NetworkFirewall::FirewallPolicy Properties: FirewallPolicyName: !Sub "inspection-firewall-policy-${AWS::StackName}" FirewallPolicy: StatelessDefaultActions: - 'aws:forward_to_sfe' StatelessFragmentDefaultActions: - 'aws:forward_to_sfe' StatefulRuleGroupReferences: - ResourceArn: !Ref ICMPAlertStatefulRuleGroup Tags: - Key: Name Value: !Sub "inspection-firewall-policy-${AWS::StackName}" InspectionFirewall: Type: AWS::NetworkFirewall::Firewall Properties: FirewallName: !Sub "inspection-firewall-${AWS::StackName}" FirewallPolicyArn: !Ref InspectionFirewallPolicy VpcId: !Ref VPCC SubnetMappings: - SubnetId: !Ref SubnetCFirewall Tags: - Key: Name Value: !Sub "inspection-firewall-${AWS::StackName}" InspectionFirewallLogFlowGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/${AWS::StackName}/inspection-fw/flow" InspectionFirewallLogAlertGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/${AWS::StackName}/inspection-fw/alert" InspectionFirewallLog: Type: AWS::NetworkFirewall::LoggingConfiguration Properties: FirewallArn: !Ref InspectionFirewall LoggingConfiguration: LogDestinationConfigs: - LogType: FLOW LogDestinationType: CloudWatchLogs LogDestination: logGroup: !Sub "/${AWS::StackName}/inspection-fw/flow" - LogType: ALERT LogDestinationType: CloudWatchLogs LogDestination: logGroup: !Sub "/${AWS::StackName}/inspection-fw/alert" # Route Tables SubnetAWorkloadRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCA Tags: - Key: Name Value: !Sub "subnet-a-workload-route-table-${AWS::StackName}" SubnetAWorkloadRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation DependsOn: SubnetAWorkload Properties: RouteTableId: !Ref SubnetAWorkloadRouteTable SubnetId: !Ref SubnetAWorkload SubnetAWorkloadDefaultRoute: Type: AWS::EC2::Route DependsOn: AttachVPCA Properties: DestinationCidrBlock: "0.0.0.0/0" TransitGatewayId: !Ref TransitGateway RouteTableId: !Ref SubnetAWorkloadRouteTable SubnetBWorkloadRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCB Tags: - Key: Name Value: !Sub "subnet-b-workload-route-table-${AWS::StackName}" SubnetBWorkloadRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation DependsOn: SubnetBWorkload Properties: RouteTableId: !Ref SubnetBWorkloadRouteTable SubnetId: !Ref SubnetBWorkload SubnetBWorkloadInternalRoute: Type: AWS::EC2::Route DependsOn: AttachVPCB Properties: DestinationCidrBlock: "0.0.0.0/0" TransitGatewayId: !Ref TransitGateway RouteTableId: !Ref SubnetBWorkloadRouteTable SubnetCFirewallRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCC Tags: - Key: Name Value: !Sub "subnet-c-firewall-route-table-${AWS::StackName}" SubnetCTGWRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCC Tags: - Key: Name Value: !Sub "subnet-c-tgw-route-table-${AWS::StackName}" SubnetCFirewallRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation DependsOn: SubnetCFirewall Properties: RouteTableId: !Ref SubnetCFirewallRouteTable SubnetId: !Ref SubnetCFirewall SubnetCTGWRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation DependsOn: SubnetCTGW Properties: RouteTableId: !Ref SubnetCTGWRouteTable SubnetId: !Ref SubnetCTGW SubnetCFirewallDefaultRoute: Type: AWS::EC2::Route DependsOn: AttachVPCC Properties: DestinationCidrBlock: "0.0.0.0/0" TransitGatewayId: !Ref TransitGateway RouteTableId: !Ref SubnetCFirewallRouteTable SubnetCTGWDefaultRoute: Type: AWS::EC2::Route DependsOn: InspectionFirewall Properties: DestinationCidrBlock: "0.0.0.0/0" VpcEndpointId: !Select [1, !Split [":", !Select [0, !GetAtt InspectionFirewall.EndpointIds]]] RouteTableId: !Ref SubnetCTGWRouteTable SubnetDTGWRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCD Tags: - Key: Name Value: !Sub "subnet-d-tgw-route-table-${AWS::StackName}" SubnetDTGWCorpRoute: Type: AWS::EC2::Route DependsOn: AttachVPCC Properties: DestinationCidrBlock: "10.0.0.0/8" TransitGatewayId: !Ref TransitGateway RouteTableId: !Ref SubnetDTGWRouteTable SubnetDTGWDefaultRoute: Type: AWS::EC2::Route DependsOn: AttachVPCC Properties: DestinationCidrBlock: "0.0.0.0/0" NatGatewayId: !Ref SubnetDNATGateway RouteTableId: !Ref SubnetDTGWRouteTable SubnetDPublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCD Tags: - Key: Name Value: !Sub "subnet-d-public-route-table-${AWS::StackName}" SubnetDPublicCorpRoute: Type: AWS::EC2::Route DependsOn: AttachVPCD Properties: DestinationCidrBlock: "10.0.0.0/8" TransitGatewayId: !Ref TransitGateway RouteTableId: !Ref SubnetDPublicRouteTable SubnetDPublicDefaultRoute: Type: AWS::EC2::Route DependsOn: AttachVPCD Properties: DestinationCidrBlock: "0.0.0.0/0" GatewayId: !Ref InternetGatewayVPCD RouteTableId: !Ref SubnetDPublicRouteTable SubnetDTGWRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation DependsOn: SubnetDTGW Properties: RouteTableId: !Ref SubnetDTGWRouteTable SubnetId: !Ref SubnetDTGW SubnetDPublicRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation DependsOn: SubnetDPublic Properties: RouteTableId: !Ref SubnetDPublicRouteTable SubnetId: !Ref SubnetDPublic SpokeRouteTable: Type: "AWS::EC2::TransitGatewayRouteTable" Properties: Tags: - Key: Name Value: !Sub "spoke-route-table-${AWS::StackName}" TransitGatewayId: !Ref TransitGateway FirewallRouteTable: Type: "AWS::EC2::TransitGatewayRouteTable" Properties: Tags: - Key: Name Value: !Sub "firewall-route-table-${AWS::StackName}" TransitGatewayId: !Ref TransitGateway AssociateVPCARouteTable: Type: AWS::EC2::TransitGatewayRouteTableAssociation Properties: TransitGatewayAttachmentId: !Ref AttachVPCA TransitGatewayRouteTableId: !Ref SpokeRouteTable AssociateVPCBRouteTable: Type: AWS::EC2::TransitGatewayRouteTableAssociation Properties: TransitGatewayAttachmentId: !Ref AttachVPCB TransitGatewayRouteTableId: !Ref SpokeRouteTable AssociateVPCCRouteTable: Type: AWS::EC2::TransitGatewayRouteTableAssociation Properties: TransitGatewayAttachmentId: !Ref AttachVPCC TransitGatewayRouteTableId: !Ref FirewallRouteTable AssociateVPCDRouteTable: Type: AWS::EC2::TransitGatewayRouteTableAssociation Properties: TransitGatewayAttachmentId: !Ref AttachVPCD TransitGatewayRouteTableId: !Ref SpokeRouteTable PropagateVPCARoute: Type: AWS::EC2::TransitGatewayRouteTablePropagation Properties: TransitGatewayAttachmentId: !Ref AttachVPCA TransitGatewayRouteTableId: !Ref FirewallRouteTable PropagateVPCBRoute: Type: AWS::EC2::TransitGatewayRouteTablePropagation Properties: TransitGatewayAttachmentId: !Ref AttachVPCB TransitGatewayRouteTableId: !Ref FirewallRouteTable PropagateVPDARoute: Type: AWS::EC2::TransitGatewayRouteTablePropagation Properties: TransitGatewayAttachmentId: !Ref AttachVPCD TransitGatewayRouteTableId: !Ref FirewallRouteTable SpokeInspectionRoute: Type: AWS::EC2::TransitGatewayRoute Properties: DestinationCidrBlock: "0.0.0.0/0" TransitGatewayAttachmentId: !Ref AttachVPCC TransitGatewayRouteTableId: !Ref SpokeRouteTable SpokeEgressRoute: Type: AWS::EC2::TransitGatewayRoute Properties: DestinationCidrBlock: "0.0.0.0/0" TransitGatewayAttachmentId: !Ref AttachVPCD TransitGatewayRouteTableId: !Ref FirewallRouteTable