├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── centralized_architecture ├── README.md ├── anfw-centralized-2az-template.yaml └── single_az_deployment │ ├── README.md │ └── anfw-centralized-1az-template.yaml ├── cloudwatch_dashboard ├── README.md ├── nfw-cloudwatch-dashboard-china.yaml ├── nfw-cloudwatch-dashboard-govcloud.yaml └── nfw-cloudwatch-dashboard.yaml ├── distributed_architecture ├── README.md ├── anfw-distributed-2az-template.yaml └── single_az_deployment │ ├── README.md │ └── anfw-distributed-1az-template.yaml ├── images ├── anfw-centralized-model-1az.jpg ├── anfw-centralized-model-1az.png ├── anfw-centralized-model-2az.jpg ├── anfw-distributed-model-1az.png ├── anfw-distributed-model-2az.jpg └── egress-inspection-aws-cloud-wan-base-architecture.png └── outbound_inspection_with_aws_cloud_wan ├── Makefile ├── README.md ├── core_network_base_policy.yaml ├── core_network_update_policy_edge_locations.yaml ├── region1_resources.yaml ├── region2_resources.yaml └── region3_resources.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | .ropeproject 2 | .DS_Store 3 | *.pyc 4 | __pycache__ 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS Network Firewall Templates 2 | 3 | ## Authors 4 | 5 | |Name | Email| 6 | |------|------| 7 | |Shakeel Ahmad, Sr. Solutions Architect | shkahma@amazon.com| 8 | |Pratik R. Mankad, Sr. Solutions Architect | pmankad@amazon.com| 9 | |Daniel Yu, Sr. Technical Account Manager | dyuamzn@amazon.com| 10 | 11 | 12 | ## License: 13 | 14 | This sample code is made available under the MIT-0 license. See the LICENSE file. 15 | 16 | ## Summary: 17 | 18 | AWS Network Firewall is a new AWS-managed service that makes it easy to deploy essential network protections for all of your Amazon Virtual Private Clouds (VPCs). The service can be set up with just a few clicks and scales automatically with your network traffic, so you don't have to worry about deploying and managing any infrastructure. AWS Network Firewall is for customers who want to inspect and filter traffic to, from, or between their Amazon VPCs. 19 | 20 | For more information, see the [AWS Network Firewall announcement](https://aws.amazon.com/blogs/aws/aws-network-firewall-new-managed-firewall-service-in-vpc) on the AWS News blog, [product overview page](https://aws.amazon.com/network-firewall/), and the [Deployment models for AWS Network Firewall blog](https://aws.amazon.com/blogs/networking-and-content-delivery/deployment-models-for-aws-network-firewall). 21 | 22 | This repository contains sample [AWS CloudFormation](https://aws.amazon.com/cloudformation/) templates to provision AWS Network Firewall for testing & demo purpose. 23 | 24 | For more samples, refer to [AWS CloudFormation Templates](https://aws.amazon.com/cloudformation/resources/templates/) in the [AWS CloudFormation User Guide](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-sample-templates.html) 25 | 26 | For more information about developing application using AWS CloudFormation, see the [AWS CloudFormation User Guide](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html) 27 | 28 | ---- 29 | 30 | ### ***AWS Network Firewall Deployment Architectures:*** 31 | * [Distributed Architecture](distributed_architecture) 32 | * [Centralized Architecture](centralized_architecture) 33 | * [Egress Inspection with AWS Cloud WAN and AWS Network Firewall Workshop](outbound_inspection_with_aws_cloud_wan) 34 | * [CloudWatch Dashboard](cloudwatch_dashboard) -------------------------------------------------------------------------------- /centralized_architecture/README.md: -------------------------------------------------------------------------------- 1 | # Centralized Architecture 2 | 3 | **Template File:** [anfw-centralized-2az-template.yaml](anfw-centralized-2az-template.yaml) 4 | 5 | For centralized deployment model, [AWS Transit Gateway](https://aws.amazon.com/transit-gateway/) is a prerequisite. AWS Transit Gateway acts as a network hub and simplifies the connectivity between VPCs as well as on-premises networks. AWS Transit Gateway also provides inter-region peering capabilities to other Transit Gateways to establish a global network using AWS backbone. 6 | 7 | ![anfw-centralized-model-2az](../images/anfw-centralized-model-2az.jpg) 8 | *Figure 1: Multi AZ Centralized Architecture* 9 | 10 | [Centralized multi AZ deployment template](anfw-centralized-2az-template.yaml), as described in Figure 1, creates dedicated 11 | 12 | * Inspection VPC for East-West (inter-vpc) traffic inspection. Inspection VPC consists of two subnets in each AZs: 13 | * Transit Gateway subnet for Transit Gateway attchment. 14 | * Firewall subnet for firewall endpoint. 15 | 16 | * Central Egress VPC for North-South (spoke VPCs to Internet) traffic inspection. Central Egress VPC consists of 3 subnets in each AZs: 17 | * Transit Gateway subnet for Transit Gateway attchment. 18 | * Firewall subnet for firewall endpoint. 19 | * Public subnet for NAT Gateway. 20 | 21 | * Two spoke VPCs. Spoke VPCs resources are configured only single AZ. 22 | 23 | Each Transit Gateway subnet in each dedicated VPC requires a dedicated VPC route table to ensure the traffic is forwarded to firewall endpoint within the same AZ. These route tables have a default route (0.0.0.0/0) pointing towards firewall endpoint in the same AZ. 24 | 25 | This is a Multi AZ configuration. Resources in Inpsection VPC and Centrall Egress VPC are provisioned across two AZs. You can also refer to [Single AZ Deployment](single_az_deployment) for testing/poc purpose only. 26 | 27 | For more details, refer to [Blog: Deployment models for AWS Network Firewall](https://aws.amazon.com/blogs/networking-and-content-delivery/deployment-models-for-aws-network-firewall/). -------------------------------------------------------------------------------- /centralized_architecture/anfw-centralized-2az-template.yaml: -------------------------------------------------------------------------------- 1 | #Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 5 | #FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 6 | #COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 7 | #IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 8 | #CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | 10 | AWSTemplateFormatVersion: "2010-09-09" 11 | Description: "AWS Network Firewall Demo using multiple VPCs" 12 | 13 | Metadata: 14 | "AWS::CloudFormation::Interface": 15 | ParameterGroups: 16 | - Label: 17 | default: "VPC Parameters" 18 | Parameters: 19 | - AvailabilityZone1Selection 20 | - AvailabilityZone2Selection 21 | - Label: 22 | default: "EC2 Parameters" 23 | Parameters: 24 | - LatestAmiId 25 | 26 | Parameters: 27 | AvailabilityZone1Selection: 28 | Description: Availability Zone 1 29 | Type: AWS::EC2::AvailabilityZone::Name 30 | Default: us-east-1a 31 | 32 | AvailabilityZone2Selection: 33 | Description: Availability Zone 2 34 | Type: AWS::EC2::AvailabilityZone::Name 35 | Default: us-east-1b 36 | 37 | LatestAmiId: 38 | Description: Latest EC2 AMI from Systems Manager Parameter Store 39 | Type: 'AWS::SSM::Parameter::Value' 40 | Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' 41 | 42 | Resources: 43 | # spoke-a VPC 44 | VPCA: 45 | Type: AWS::EC2::VPC 46 | Properties: 47 | CidrBlock: "10.1.0.0/16" 48 | EnableDnsSupport: "true" 49 | EnableDnsHostnames: "true" 50 | InstanceTenancy: default 51 | Tags: 52 | - Key: Name 53 | Value: !Sub "spoke-a-${AWS::StackName}" 54 | 55 | SubnetAWorkload: 56 | Type: AWS::EC2::Subnet 57 | Properties: 58 | VpcId: 59 | Ref: VPCA 60 | CidrBlock: "10.1.1.0/24" 61 | AvailabilityZone: 62 | Ref: AvailabilityZone1Selection 63 | MapPublicIpOnLaunch: false 64 | Tags: 65 | - Key: Name 66 | Value: !Sub "spoke-a-workload-${AWS::StackName}" 67 | 68 | SubnetATGW: 69 | Type: AWS::EC2::Subnet 70 | Properties: 71 | VpcId: 72 | Ref: VPCA 73 | CidrBlock: "10.1.0.0/28" 74 | AvailabilityZone: 75 | Ref: AvailabilityZone1Selection 76 | MapPublicIpOnLaunch: false 77 | Tags: 78 | - Key: Name 79 | Value: !Sub "spoke-a-tgw-${AWS::StackName}" 80 | 81 | VPCAEndpointSecurityGroup: 82 | Type: AWS::EC2::SecurityGroup 83 | Properties: 84 | GroupDescription: Allow instances to get to SSM Systems Manager 85 | VpcId: !Ref VPCA 86 | SecurityGroupIngress: 87 | - IpProtocol: tcp 88 | FromPort: 443 89 | ToPort: 443 90 | CidrIp: 10.1.0.0/16 91 | 92 | VPCASSMEndpoint: 93 | Type: AWS::EC2::VPCEndpoint 94 | Properties: 95 | PrivateDnsEnabled: true 96 | SecurityGroupIds: 97 | - !Ref VPCAEndpointSecurityGroup 98 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm" 99 | SubnetIds: 100 | - !Ref SubnetAWorkload 101 | VpcEndpointType: Interface 102 | VpcId: !Ref VPCA 103 | 104 | VPCAEC2MessagesEndpoint: 105 | Type: AWS::EC2::VPCEndpoint 106 | Properties: 107 | PrivateDnsEnabled: true 108 | SecurityGroupIds: 109 | - !Ref VPCAEndpointSecurityGroup 110 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2messages" 111 | SubnetIds: 112 | - !Ref SubnetAWorkload 113 | VpcEndpointType: Interface 114 | VpcId: !Ref VPCA 115 | 116 | VPCASSMMessagesEndpoint: 117 | Type: AWS::EC2::VPCEndpoint 118 | Properties: 119 | PrivateDnsEnabled: true 120 | SecurityGroupIds: 121 | - !Ref VPCAEndpointSecurityGroup 122 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages" 123 | SubnetIds: 124 | - !Ref SubnetAWorkload 125 | VpcEndpointType: Interface 126 | VpcId: !Ref VPCA 127 | 128 | SubnetARole: 129 | Type: AWS::IAM::Role 130 | Properties: 131 | RoleName: !Sub "subnet-a-role-${AWS::Region}-${AWS::StackName}" 132 | Path: "/" 133 | ManagedPolicyArns: 134 | - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM" 135 | AssumeRolePolicyDocument: 136 | Version: "2012-10-17" 137 | Statement: 138 | - Effect: Allow 139 | Principal: 140 | Service: 141 | - ec2.amazonaws.com 142 | Action: 143 | - sts:AssumeRole 144 | 145 | SubnetAInstanceProfile: 146 | Type: AWS::IAM::InstanceProfile 147 | Properties: 148 | Path: "/" 149 | Roles: 150 | - !Ref SubnetARole 151 | 152 | SubnetASecGroup: 153 | Type: AWS::EC2::SecurityGroup 154 | Properties: 155 | GroupDescription: "ICMP acess from 10.0.0.0/8" 156 | GroupName: !Sub "spoke-a-sec-group-${AWS::StackName}" 157 | VpcId: !Ref VPCA 158 | SecurityGroupIngress: 159 | - IpProtocol: icmp 160 | CidrIp: 10.0.0.0/8 161 | FromPort: "-1" 162 | ToPort: "-1" 163 | 164 | EC2SubnetA: 165 | Type: 'AWS::EC2::Instance' 166 | Properties: 167 | ImageId: !Ref LatestAmiId 168 | SubnetId: !Ref SubnetAWorkload 169 | InstanceType: t2.micro 170 | SecurityGroupIds: 171 | - !Ref SubnetASecGroup 172 | IamInstanceProfile: !Ref SubnetAInstanceProfile 173 | Tags: 174 | - Key: Name 175 | Value: !Sub "spoke-a-${AWS::StackName}" 176 | 177 | # spoke-b VPC 178 | VPCB: 179 | Type: AWS::EC2::VPC 180 | Properties: 181 | CidrBlock: "10.2.0.0/16" 182 | EnableDnsSupport: "true" 183 | EnableDnsHostnames: "true" 184 | InstanceTenancy: default 185 | Tags: 186 | - Key: Name 187 | Value: !Sub "spoke-b-${AWS::StackName}" 188 | 189 | SubnetBWorkload: 190 | Type: AWS::EC2::Subnet 191 | Properties: 192 | VpcId: 193 | Ref: VPCB 194 | CidrBlock: "10.2.1.0/24" 195 | AvailabilityZone: 196 | Ref: AvailabilityZone2Selection 197 | MapPublicIpOnLaunch: true 198 | Tags: 199 | - Key: Name 200 | Value: !Sub "spoke-b-workload-${AWS::StackName}" 201 | 202 | SubnetBTGW: 203 | Type: AWS::EC2::Subnet 204 | Properties: 205 | VpcId: 206 | Ref: VPCB 207 | CidrBlock: "10.2.0.0/28" 208 | AvailabilityZone: 209 | Ref: AvailabilityZone2Selection 210 | MapPublicIpOnLaunch: false 211 | Tags: 212 | - Key: Name 213 | Value: !Sub "spoke-b-tgw-${AWS::StackName}" 214 | 215 | VPCBEndpointSecurityGroup: 216 | Type: AWS::EC2::SecurityGroup 217 | Properties: 218 | GroupDescription: Allow instances to get to SSM Systems Manager 219 | VpcId: !Ref VPCB 220 | SecurityGroupIngress: 221 | - IpProtocol: tcp 222 | FromPort: 443 223 | ToPort: 443 224 | CidrIp: 10.2.0.0/16 225 | 226 | VPCBSSMEndpoint: 227 | Type: AWS::EC2::VPCEndpoint 228 | Properties: 229 | PrivateDnsEnabled: true 230 | SecurityGroupIds: 231 | - !Ref VPCBEndpointSecurityGroup 232 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm" 233 | SubnetIds: 234 | - !Ref SubnetBWorkload 235 | VpcEndpointType: Interface 236 | VpcId: !Ref VPCB 237 | 238 | VPCBEC2MessagesEndpoint: 239 | Type: AWS::EC2::VPCEndpoint 240 | Properties: 241 | PrivateDnsEnabled: true 242 | SecurityGroupIds: 243 | - !Ref VPCBEndpointSecurityGroup 244 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2messages" 245 | SubnetIds: 246 | - !Ref SubnetBWorkload 247 | VpcEndpointType: Interface 248 | VpcId: !Ref VPCB 249 | 250 | VPCBSSMMessagesEndpoint: 251 | Type: AWS::EC2::VPCEndpoint 252 | Properties: 253 | PrivateDnsEnabled: true 254 | SecurityGroupIds: 255 | - !Ref VPCBEndpointSecurityGroup 256 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages" 257 | SubnetIds: 258 | - !Ref SubnetBWorkload 259 | VpcEndpointType: Interface 260 | VpcId: !Ref VPCB 261 | 262 | SubnetBRole: 263 | Type: AWS::IAM::Role 264 | Properties: 265 | RoleName: !Sub "subnet-b-role-${AWS::Region}-${AWS::StackName}" 266 | Path: "/" 267 | ManagedPolicyArns: 268 | - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM" 269 | AssumeRolePolicyDocument: 270 | Version: "2012-10-17" 271 | Statement: 272 | - Effect: Allow 273 | Principal: 274 | Service: 275 | - ec2.amazonaws.com 276 | Action: 277 | - sts:AssumeRole 278 | 279 | SubnetBInstanceProfile: 280 | Type: AWS::IAM::InstanceProfile 281 | Properties: 282 | Path: "/" 283 | Roles: 284 | - !Ref SubnetBRole 285 | 286 | SubnetBSecGroup: 287 | Type: AWS::EC2::SecurityGroup 288 | Properties: 289 | GroupDescription: "ICMP acess from 10.0.0.0/8" 290 | GroupName: !Sub "spoke-b-sec-group-${AWS::StackName}" 291 | VpcId: !Ref VPCB 292 | SecurityGroupIngress: 293 | - IpProtocol: icmp 294 | CidrIp: 10.0.0.0/8 295 | FromPort: "-1" 296 | ToPort: "-1" 297 | 298 | EC2SubnetB: 299 | Type: 'AWS::EC2::Instance' 300 | Properties: 301 | ImageId: !Ref LatestAmiId 302 | SubnetId: !Ref SubnetBWorkload 303 | InstanceType: t2.micro 304 | SecurityGroupIds: 305 | - !Ref SubnetBSecGroup 306 | IamInstanceProfile: !Ref SubnetBInstanceProfile 307 | Tags: 308 | - Key: Name 309 | Value: !Sub "spoke-b-${AWS::StackName}" 310 | 311 | # inspection VPC 312 | VPCC: 313 | Type: AWS::EC2::VPC 314 | Properties: 315 | CidrBlock: "100.64.0.0/16" 316 | EnableDnsSupport: "true" 317 | EnableDnsHostnames: "true" 318 | InstanceTenancy: default 319 | Tags: 320 | - Key: Name 321 | Value: !Sub "inspection-vpc-${AWS::StackName}" 322 | 323 | SubnetCTGWA: 324 | Type: AWS::EC2::Subnet 325 | Properties: 326 | VpcId: 327 | Ref: VPCC 328 | CidrBlock: "100.64.0.0/28" 329 | AvailabilityZone: 330 | Ref: AvailabilityZone1Selection 331 | MapPublicIpOnLaunch: false 332 | Tags: 333 | - Key: Name 334 | Value: !Sub "inspections-tgw-a-${AWS::StackName}" 335 | 336 | SubnetCFirewallA: 337 | Type: AWS::EC2::Subnet 338 | Properties: 339 | VpcId: 340 | Ref: VPCC 341 | CidrBlock: "100.64.0.16/28" 342 | AvailabilityZone: 343 | Ref: AvailabilityZone1Selection 344 | MapPublicIpOnLaunch: false 345 | Tags: 346 | - Key: Name 347 | Value: !Sub "inspection-firewall-a-${AWS::StackName}" 348 | 349 | SubnetCTGWB: 350 | Type: AWS::EC2::Subnet 351 | Properties: 352 | VpcId: 353 | Ref: VPCC 354 | CidrBlock: "100.64.0.32/28" 355 | AvailabilityZone: 356 | Ref: AvailabilityZone2Selection 357 | MapPublicIpOnLaunch: false 358 | Tags: 359 | - Key: Name 360 | Value: !Sub "inspections-tgw-b-${AWS::StackName}" 361 | 362 | SubnetCFirewallB: 363 | Type: AWS::EC2::Subnet 364 | Properties: 365 | VpcId: 366 | Ref: VPCC 367 | CidrBlock: "100.64.0.48/28" 368 | AvailabilityZone: 369 | Ref: AvailabilityZone2Selection 370 | MapPublicIpOnLaunch: false 371 | Tags: 372 | - Key: Name 373 | Value: !Sub "inspection-firewall-b-${AWS::StackName}" 374 | 375 | # egress VPC 376 | VPCD: 377 | Type: AWS::EC2::VPC 378 | Properties: 379 | CidrBlock: "10.10.0.0/16" 380 | EnableDnsSupport: "true" 381 | EnableDnsHostnames: "true" 382 | InstanceTenancy: default 383 | Tags: 384 | - Key: Name 385 | Value: !Sub "egress-${AWS::StackName}" 386 | 387 | SubnetDFirewallA: 388 | Type: AWS::EC2::Subnet 389 | Properties: 390 | VpcId: 391 | Ref: VPCD 392 | CidrBlock: "10.10.16.0/28" 393 | AvailabilityZone: 394 | Ref: AvailabilityZone1Selection 395 | MapPublicIpOnLaunch: false 396 | Tags: 397 | - Key: Name 398 | Value: !Sub "egress-firewall-a-${AWS::StackName}" 399 | 400 | SubnetDTGWA: 401 | Type: AWS::EC2::Subnet 402 | Properties: 403 | VpcId: 404 | Ref: VPCD 405 | CidrBlock: "10.10.0.0/28" 406 | AvailabilityZone: 407 | Ref: AvailabilityZone1Selection 408 | MapPublicIpOnLaunch: false 409 | Tags: 410 | - Key: Name 411 | Value: !Sub "egress-tgw-a-${AWS::StackName}" 412 | 413 | SubnetDPublicA: 414 | Type: AWS::EC2::Subnet 415 | Properties: 416 | VpcId: 417 | Ref: VPCD 418 | CidrBlock: "10.10.1.0/24" 419 | AvailabilityZone: 420 | Ref: AvailabilityZone1Selection 421 | MapPublicIpOnLaunch: true 422 | Tags: 423 | - Key: Name 424 | Value: !Sub "egress-public-a-${AWS::StackName}" 425 | 426 | SubnetDFirewallB: 427 | Type: AWS::EC2::Subnet 428 | Properties: 429 | VpcId: 430 | Ref: VPCD 431 | CidrBlock: "10.10.16.16/28" 432 | AvailabilityZone: 433 | Ref: AvailabilityZone2Selection 434 | MapPublicIpOnLaunch: false 435 | Tags: 436 | - Key: Name 437 | Value: !Sub "egress-firewall-b-${AWS::StackName}" 438 | 439 | SubnetDTGWB: 440 | Type: AWS::EC2::Subnet 441 | Properties: 442 | VpcId: 443 | Ref: VPCD 444 | CidrBlock: "10.10.0.16/28" 445 | AvailabilityZone: 446 | Ref: AvailabilityZone2Selection 447 | MapPublicIpOnLaunch: false 448 | Tags: 449 | - Key: Name 450 | Value: !Sub "egress-tgw-b-${AWS::StackName}" 451 | 452 | SubnetDPublicB: 453 | Type: AWS::EC2::Subnet 454 | Properties: 455 | VpcId: 456 | Ref: VPCD 457 | CidrBlock: "10.10.2.0/24" 458 | AvailabilityZone: 459 | Ref: AvailabilityZone2Selection 460 | MapPublicIpOnLaunch: true 461 | Tags: 462 | - Key: Name 463 | Value: !Sub "egress-public-b-${AWS::StackName}" 464 | 465 | InternetGatewayVPCD: 466 | Type: AWS::EC2::InternetGateway 467 | Properties: 468 | Tags: 469 | - Key: Name 470 | Value: !Sub "egress-igw-${AWS::StackName}" 471 | 472 | AttachGatewayVPCD: 473 | Type: AWS::EC2::VPCGatewayAttachment 474 | Properties: 475 | VpcId: 476 | !Ref VPCD 477 | InternetGatewayId: 478 | !Ref InternetGatewayVPCD 479 | 480 | SubnetDNATEIPA: 481 | Type: "AWS::EC2::EIP" 482 | Properties: 483 | Domain: vpc 484 | 485 | SubnetDNATGatewayA: 486 | Type: "AWS::EC2::NatGateway" 487 | Properties: 488 | AllocationId: 489 | Fn::GetAtt: 490 | - SubnetDNATEIPA 491 | - AllocationId 492 | SubnetId: 493 | Ref: SubnetDPublicA 494 | Tags: 495 | - Key: Name 496 | Value: !Sub "egress-natgw-a-${AWS::StackName}" 497 | 498 | SubnetDNATEIPB: 499 | Type: "AWS::EC2::EIP" 500 | Properties: 501 | Domain: vpc 502 | 503 | SubnetDNATGatewayB: 504 | Type: "AWS::EC2::NatGateway" 505 | Properties: 506 | AllocationId: 507 | Fn::GetAtt: 508 | - SubnetDNATEIPB 509 | - AllocationId 510 | SubnetId: 511 | Ref: SubnetDPublicB 512 | Tags: 513 | - Key: Name 514 | Value: !Sub "egress-natgw-b-${AWS::StackName}" 515 | 516 | # Transit Gateway 517 | TransitGateway: 518 | Type: "AWS::EC2::TransitGateway" 519 | Properties: 520 | AmazonSideAsn: 65000 521 | Description: "TGW Network Firewall Demo" 522 | AutoAcceptSharedAttachments: "enable" 523 | DefaultRouteTableAssociation: "disable" 524 | DnsSupport: "enable" 525 | VpnEcmpSupport: "enable" 526 | Tags: 527 | - Key: Name 528 | Value: !Sub "tgw-${AWS::StackName}" 529 | 530 | AttachVPCA: 531 | Type: "AWS::EC2::TransitGatewayAttachment" 532 | Properties: 533 | SubnetIds: 534 | - !Ref SubnetATGW 535 | Tags: 536 | - Key: Name 537 | Value: !Sub "spoke-a-attach-${AWS::StackName}" 538 | TransitGatewayId: !Ref TransitGateway 539 | VpcId: !Ref VPCA 540 | 541 | AttachVPCB: 542 | Type: "AWS::EC2::TransitGatewayAttachment" 543 | Properties: 544 | SubnetIds: 545 | - !Ref SubnetBTGW 546 | Tags: 547 | - Key: Name 548 | Value: !Sub "spoke-b-attach-${AWS::StackName}" 549 | TransitGatewayId: !Ref TransitGateway 550 | VpcId: !Ref VPCB 551 | 552 | AttachVPCC: 553 | Type: "AWS::EC2::TransitGatewayAttachment" 554 | Properties: 555 | SubnetIds: 556 | - !Ref SubnetCTGWA 557 | - !Ref SubnetCTGWB 558 | Tags: 559 | - Key: Name 560 | Value: !Sub "inspection-attach-${AWS::StackName}" 561 | TransitGatewayId: !Ref TransitGateway 562 | VpcId: !Ref VPCC 563 | 564 | AttachVPCD: 565 | Type: "AWS::EC2::TransitGatewayAttachment" 566 | Properties: 567 | SubnetIds: 568 | - !Ref SubnetDTGWA 569 | - !Ref SubnetDTGWB 570 | Tags: 571 | - Key: Name 572 | Value: !Sub "egress-attach-${AWS::StackName}" 573 | TransitGatewayId: !Ref TransitGateway 574 | VpcId: !Ref VPCD 575 | 576 | # To ensure flow symmetry, Transit Gateway appliance mode is enabled on the Inspection VPC’s attachment. 577 | # For more details on Transit Gateway appliance mode, refer to: 578 | # https://aws.amazon.com/blogs/networking-and-content-delivery/centralized-inspection-architecture-with-aws-gateway-load-balancer-and-aws-transit-gateway/ 579 | # https://docs.aws.amazon.com/vpc/latest/tgw/transit-gateway-appliance-scenario.html 580 | # CloudFormation yet does not support enabling appliance mode. Hence need to enable it using Lambda custom resource: 581 | 582 | # Transit Gateway appliance mode Lambda Role: 583 | TgwLambdaExecutionRole: 584 | Type: AWS::IAM::Role 585 | Properties: 586 | RoleName: !Sub "${AWS::StackName}-${AWS::Region}-tgw-lambda-role" 587 | AssumeRolePolicyDocument: 588 | Version: "2012-10-17" 589 | Statement: 590 | - Effect: Allow 591 | Principal: 592 | Service: 593 | - lambda.amazonaws.com 594 | Action: 595 | - sts:AssumeRole 596 | Path: / 597 | Policies: 598 | - PolicyName: root 599 | PolicyDocument: 600 | Version: "2012-10-17" 601 | Statement: 602 | - Effect: Allow 603 | Action: 604 | - logs:CreateLogStream 605 | - logs:PutLogEvents 606 | Resource: !GetAtt TgwApplianceModeLogGroup.Arn 607 | - Effect: Allow 608 | Action: 609 | - ec2:ModifyTransitGatewayVpcAttachment 610 | - ec2:DescribeTransitGatewayVpcAttachments 611 | Resource: "*" 612 | 613 | # Enable Transit Gateway Appliance Mode Lambda Custom Resource: 614 | TgwApplianceModeLogGroup: 615 | Type: AWS::Logs::LogGroup 616 | Properties: 617 | LogGroupName: !Sub /aws/lambda/${AWS::StackName}-tgw-appliancemode 618 | RetentionInDays: 1 619 | 620 | TgwApplianceMode: 621 | Type: AWS::Lambda::Function 622 | DependsOn: TgwApplianceModeLogGroup 623 | Properties: 624 | FunctionName: !Sub ${AWS::StackName}-tgw-appliancemode 625 | Handler: "index.handler" 626 | Role: !GetAtt TgwLambdaExecutionRole.Arn 627 | Code: 628 | ZipFile: | 629 | import boto3 630 | import cfnresponse 631 | import json 632 | import logging 633 | 634 | def handler(event, context): 635 | logger = logging.getLogger() 636 | logger.setLevel(logging.INFO) 637 | responseData = {} 638 | responseStatus = cfnresponse.FAILED 639 | logger.info('Received event: {}'.format(json.dumps(event))) 640 | if event["RequestType"] == "Delete": 641 | responseStatus = cfnresponse.SUCCESS 642 | cfnresponse.send(event, context, responseStatus, responseData) 643 | if event["RequestType"] == "Create": 644 | try: 645 | TgwInspectionVpcAttachmentId = event["ResourceProperties"]["TgwInspectionVpcAttachmentId"] 646 | ApplianceMode = event["ResourceProperties"]["ApplianceMode"] 647 | except Exception as e: 648 | logger.info('Key retrieval failure: {}'.format(e)) 649 | try: 650 | ec2 = boto3.client('ec2') 651 | except Exception as e: 652 | logger.info('boto3.client failure: {}'.format(e)) 653 | try: 654 | ec2.modify_transit_gateway_vpc_attachment( 655 | TransitGatewayAttachmentId = TgwInspectionVpcAttachmentId, 656 | Options = {'ApplianceModeSupport': ApplianceMode} 657 | ) 658 | TgwResponse = ec2.describe_transit_gateway_vpc_attachments( 659 | TransitGatewayAttachmentIds=[TgwInspectionVpcAttachmentId] 660 | ) 661 | ApplianceModeStatus = TgwResponse['TransitGatewayVpcAttachments'][0]['Options']['ApplianceModeSupport'] 662 | except Exception as e: 663 | logger.info('ec2.modify/describe_transit_gateway_vpc_attachment: {}'.format(e)) 664 | 665 | responseData['ApplianceModeStatus'] = ApplianceModeStatus 666 | responseStatus = cfnresponse.SUCCESS 667 | cfnresponse.send(event, context, responseStatus, responseData) 668 | Runtime: python3.7 669 | Timeout: 30 670 | 671 | ApplianceModeEnabled: 672 | Type: Custom::ModifyTransitGatewayVpcAttachment 673 | Properties: 674 | ServiceToken: !GetAtt TgwApplianceMode.Arn 675 | TgwInspectionVpcAttachmentId: !Ref AttachVPCC 676 | ApplianceMode: enable 677 | 678 | # Firewalls 679 | 680 | ICMPAlertStatefulRuleGroup: 681 | Type: 'AWS::NetworkFirewall::RuleGroup' 682 | Properties: 683 | RuleGroupName: !Sub "icmp-alert-${AWS::StackName}" 684 | Type: STATEFUL 685 | Capacity: 100 686 | RuleGroup: 687 | RulesSource: 688 | StatefulRules: 689 | - Action: ALERT 690 | Header: 691 | Direction: ANY 692 | Protocol: ICMP 693 | Destination: ANY 694 | Source: ANY 695 | DestinationPort: ANY 696 | SourcePort: ANY 697 | RuleOptions: 698 | - Keyword: "sid:1" 699 | Tags: 700 | - Key: Name 701 | Value: !Sub "icmp-alert-${AWS::StackName}" 702 | 703 | DomainAllowStatefulRuleGroup: 704 | Type: 'AWS::NetworkFirewall::RuleGroup' 705 | Properties: 706 | RuleGroupName: !Sub "domain-allow-${AWS::StackName}" 707 | Type: STATEFUL 708 | Capacity: 100 709 | RuleGroup: 710 | RuleVariables: 711 | IPSets: 712 | HOME_NET: 713 | Definition: 714 | - "10.0.0.0/8" 715 | RulesSource: 716 | RulesSourceList: 717 | TargetTypes: 718 | - HTTP_HOST 719 | - TLS_SNI 720 | Targets: 721 | - ".amazon.com" 722 | GeneratedRulesType: "ALLOWLIST" 723 | Tags: 724 | - Key: Name 725 | Value: !Sub "domain-allow-${AWS::StackName}" 726 | 727 | # Inspection Firewall configuration: 728 | InspectionFirewallPolicy: 729 | Type: AWS::NetworkFirewall::FirewallPolicy 730 | Properties: 731 | FirewallPolicyName: !Sub "inspection-firewall-policy-${AWS::StackName}" 732 | FirewallPolicy: 733 | StatelessDefaultActions: 734 | - 'aws:forward_to_sfe' 735 | StatelessFragmentDefaultActions: 736 | - 'aws:forward_to_sfe' 737 | StatefulRuleGroupReferences: 738 | - ResourceArn: !Ref ICMPAlertStatefulRuleGroup 739 | Tags: 740 | - Key: Name 741 | Value: !Sub "inspection-firewall-policy-${AWS::StackName}" 742 | 743 | InspectionFirewall: 744 | Type: AWS::NetworkFirewall::Firewall 745 | Properties: 746 | FirewallName: !Sub "inspection-firewall-${AWS::StackName}" 747 | FirewallPolicyArn: !Ref InspectionFirewallPolicy 748 | VpcId: !Ref VPCC 749 | SubnetMappings: 750 | - SubnetId: !Ref SubnetCFirewallA 751 | - SubnetId: !Ref SubnetCFirewallB 752 | Tags: 753 | - Key: Name 754 | Value: !Sub "inspection-firewall-${AWS::StackName}" 755 | 756 | InspectionFirewallLogFlowGroup: 757 | Type: AWS::Logs::LogGroup 758 | Properties: 759 | LogGroupName: !Sub "/${AWS::StackName}/inspection-fw/flow" 760 | 761 | InspectionFirewallLogAlertGroup: 762 | Type: AWS::Logs::LogGroup 763 | Properties: 764 | LogGroupName: !Sub "/${AWS::StackName}/inspection-fw/alert" 765 | 766 | InspectionFirewallLog: 767 | Type: AWS::NetworkFirewall::LoggingConfiguration 768 | Properties: 769 | FirewallArn: !Ref InspectionFirewall 770 | LoggingConfiguration: 771 | LogDestinationConfigs: 772 | - LogType: FLOW 773 | LogDestinationType: CloudWatchLogs 774 | LogDestination: 775 | logGroup: !Sub "/${AWS::StackName}/inspection-fw/flow" 776 | - LogType: ALERT 777 | LogDestinationType: CloudWatchLogs 778 | LogDestination: 779 | logGroup: !Sub "/${AWS::StackName}/inspection-fw/alert" 780 | 781 | # Egress Firewall configuration: 782 | EgressFirewallPolicy: 783 | Type: AWS::NetworkFirewall::FirewallPolicy 784 | Properties: 785 | FirewallPolicyName: !Sub "egress-firewall-policy-${AWS::StackName}" 786 | FirewallPolicy: 787 | StatelessDefaultActions: 788 | - 'aws:forward_to_sfe' 789 | StatelessFragmentDefaultActions: 790 | - 'aws:forward_to_sfe' 791 | StatefulRuleGroupReferences: 792 | - ResourceArn: !Ref DomainAllowStatefulRuleGroup 793 | - ResourceArn: !Ref ICMPAlertStatefulRuleGroup 794 | Tags: 795 | - Key: Name 796 | Value: !Sub "egress-firewall-policy-${AWS::StackName}" 797 | 798 | EgressFirewall: 799 | Type: AWS::NetworkFirewall::Firewall 800 | Properties: 801 | FirewallName: !Sub "egress-firewall-${AWS::StackName}" 802 | FirewallPolicyArn: !Ref EgressFirewallPolicy 803 | VpcId: !Ref VPCD 804 | SubnetMappings: 805 | - SubnetId: !Ref SubnetDFirewallA 806 | - SubnetId: !Ref SubnetDFirewallB 807 | Tags: 808 | - Key: Name 809 | Value: !Sub "egress-firewall-${AWS::StackName}" 810 | 811 | EgressFirewallLogFlowGroup: 812 | Type: AWS::Logs::LogGroup 813 | Properties: 814 | LogGroupName: !Sub "/${AWS::StackName}/egress-fw/flow" 815 | 816 | EgressFirewallLogAlertGroup: 817 | Type: AWS::Logs::LogGroup 818 | Properties: 819 | LogGroupName: !Sub "/${AWS::StackName}/egress-fw/alert" 820 | 821 | EgressFirewallLog: 822 | Type: AWS::NetworkFirewall::LoggingConfiguration 823 | Properties: 824 | FirewallArn: !Ref EgressFirewall 825 | LoggingConfiguration: 826 | LogDestinationConfigs: 827 | - LogType: FLOW 828 | LogDestinationType: CloudWatchLogs 829 | LogDestination: 830 | logGroup: !Sub "/${AWS::StackName}/egress-fw/flow" 831 | - LogType: ALERT 832 | LogDestinationType: CloudWatchLogs 833 | LogDestination: 834 | logGroup: !Sub "/${AWS::StackName}/egress-fw/alert" 835 | 836 | # Fn::GetAtt for Firewall do not return VPCE Id in ordered format. 837 | # For more details refer to: https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-networkfirewall/issues/15 838 | # Until the bug is fixed we have to rely on custom resource to retrieve AZ specific VPCE Id. 839 | 840 | # Firewall Endpoint Id Retrieval Lambda Role: 841 | FwLambdaExecutionRole: 842 | Type: AWS::IAM::Role 843 | Properties: 844 | RoleName: !Sub "${AWS::StackName}-${AWS::Region}-nfw-lambda-role" 845 | AssumeRolePolicyDocument: 846 | Version: "2012-10-17" 847 | Statement: 848 | - Effect: Allow 849 | Principal: 850 | Service: 851 | - lambda.amazonaws.com 852 | Action: 853 | - sts:AssumeRole 854 | Path: / 855 | Policies: 856 | - PolicyName: root 857 | PolicyDocument: 858 | Version: "2012-10-17" 859 | Statement: 860 | - Effect: Allow 861 | Action: 862 | - logs:CreateLogStream 863 | - logs:PutLogEvents 864 | Resource: !GetAtt RetrieveVpcIdLogGroup.Arn 865 | - Effect: Allow 866 | Action: 867 | - network-firewall:DescribeFirewall 868 | Resource: "*" 869 | 870 | # Retrieve VpceId Lambda Custom Resource: 871 | RetrieveVpcIdLogGroup: 872 | Type: AWS::Logs::LogGroup 873 | Properties: 874 | LogGroupName: !Sub /aws/lambda/${AWS::StackName}-retrieve-vpceid 875 | RetentionInDays: 1 876 | 877 | RetrieveVpceId: 878 | Type: AWS::Lambda::Function 879 | DependsOn: RetrieveVpcIdLogGroup 880 | Properties: 881 | FunctionName: !Sub ${AWS::StackName}-retrieve-vpceid 882 | Handler: "index.handler" 883 | Role: !GetAtt 884 | - FwLambdaExecutionRole 885 | - Arn 886 | Code: 887 | ZipFile: | 888 | import boto3 889 | import cfnresponse 890 | import json 891 | import logging 892 | 893 | def handler(event, context): 894 | logger = logging.getLogger() 895 | logger.setLevel(logging.INFO) 896 | responseData = {} 897 | responseStatus = cfnresponse.FAILED 898 | logger.info('Received event: {}'.format(json.dumps(event))) 899 | if event["RequestType"] == "Delete": 900 | responseStatus = cfnresponse.SUCCESS 901 | cfnresponse.send(event, context, responseStatus, responseData) 902 | if event["RequestType"] == "Create": 903 | try: 904 | Az1 = event["ResourceProperties"]["Az1"] 905 | Az2 = event["ResourceProperties"]["Az2"] 906 | FwArn = event["ResourceProperties"]["FwArn"] 907 | except Exception as e: 908 | logger.info('AZ retrieval failure: {}'.format(e)) 909 | try: 910 | nfw = boto3.client('network-firewall') 911 | except Exception as e: 912 | logger.info('boto3.client failure: {}'.format(e)) 913 | try: 914 | NfwResponse=nfw.describe_firewall(FirewallArn=FwArn) 915 | VpceId1 = NfwResponse['FirewallStatus']['SyncStates'][Az1]['Attachment']['EndpointId'] 916 | VpceId2 = NfwResponse['FirewallStatus']['SyncStates'][Az2]['Attachment']['EndpointId'] 917 | 918 | except Exception as e: 919 | logger.info('ec2.describe_firewall failure: {}'.format(e)) 920 | 921 | responseData['FwVpceId1'] = VpceId1 922 | responseData['FwVpceId2'] = VpceId2 923 | responseStatus = cfnresponse.SUCCESS 924 | cfnresponse.send(event, context, responseStatus, responseData) 925 | Runtime: python3.7 926 | Timeout: 30 927 | 928 | InspectionFirewallVpceIds: 929 | Type: Custom::DescribeVpcEndpoints 930 | Properties: 931 | ServiceToken: !GetAtt RetrieveVpceId.Arn 932 | Az1: !Ref AvailabilityZone1Selection 933 | Az2: !Ref AvailabilityZone2Selection 934 | FwArn: !Ref InspectionFirewall 935 | 936 | EgressFirewallVpceIds: 937 | Type: Custom::DescribeVpcEndpoints 938 | Properties: 939 | ServiceToken: !GetAtt RetrieveVpceId.Arn 940 | Az1: !Ref AvailabilityZone1Selection 941 | Az2: !Ref AvailabilityZone2Selection 942 | FwArn: !Ref EgressFirewall 943 | 944 | # Route Tables: 945 | # Spoke A route table configuration: 946 | SubnetAWorkloadRouteTable: 947 | Type: AWS::EC2::RouteTable 948 | Properties: 949 | VpcId: !Ref VPCA 950 | Tags: 951 | - Key: Name 952 | Value: !Sub "subnet-a-workload-route-table-${AWS::StackName}" 953 | 954 | SubnetAWorkloadRouteTableAssociation: 955 | Type: AWS::EC2::SubnetRouteTableAssociation 956 | DependsOn: SubnetAWorkload 957 | Properties: 958 | RouteTableId: !Ref SubnetAWorkloadRouteTable 959 | SubnetId: !Ref SubnetAWorkload 960 | 961 | SubnetAWorkloadDefaultRoute: 962 | Type: AWS::EC2::Route 963 | DependsOn: AttachVPCA 964 | Properties: 965 | DestinationCidrBlock: "0.0.0.0/0" 966 | TransitGatewayId: !Ref TransitGateway 967 | RouteTableId: !Ref SubnetAWorkloadRouteTable 968 | 969 | # Spoke B route table configuration: 970 | SubnetBWorkloadRouteTable: 971 | Type: AWS::EC2::RouteTable 972 | Properties: 973 | VpcId: !Ref VPCB 974 | Tags: 975 | - Key: Name 976 | Value: !Sub "subnet-b-workload-route-table-${AWS::StackName}" 977 | 978 | SubnetBWorkloadRouteTableAssociation: 979 | Type: AWS::EC2::SubnetRouteTableAssociation 980 | DependsOn: SubnetBWorkload 981 | Properties: 982 | RouteTableId: !Ref SubnetBWorkloadRouteTable 983 | SubnetId: !Ref SubnetBWorkload 984 | 985 | SubnetBWorkloadInternalRoute: 986 | Type: AWS::EC2::Route 987 | DependsOn: AttachVPCB 988 | Properties: 989 | DestinationCidrBlock: "0.0.0.0/0" 990 | TransitGatewayId: !Ref TransitGateway 991 | RouteTableId: !Ref SubnetBWorkloadRouteTable 992 | 993 | # Inspection VPC route table configuration: AZ A 994 | SubnetCFirewallRouteTableA: 995 | Type: AWS::EC2::RouteTable 996 | Properties: 997 | VpcId: !Ref VPCC 998 | Tags: 999 | - Key: Name 1000 | Value: !Sub "subnet-c-firewall-route-table-a-${AWS::StackName}" 1001 | 1002 | SubnetCTGWRouteTableA: 1003 | Type: AWS::EC2::RouteTable 1004 | Properties: 1005 | VpcId: !Ref VPCC 1006 | Tags: 1007 | - Key: Name 1008 | Value: !Sub "subnet-c-tgw-route-table-a-${AWS::StackName}" 1009 | 1010 | SubnetCFirewallRouteTableAAssociation: 1011 | Type: AWS::EC2::SubnetRouteTableAssociation 1012 | DependsOn: SubnetCFirewallA 1013 | Properties: 1014 | RouteTableId: !Ref SubnetCFirewallRouteTableA 1015 | SubnetId: !Ref SubnetCFirewallA 1016 | 1017 | SubnetCTGWRouteTableAAssociation: 1018 | Type: AWS::EC2::SubnetRouteTableAssociation 1019 | DependsOn: SubnetCTGWA 1020 | Properties: 1021 | RouteTableId: !Ref SubnetCTGWRouteTableA 1022 | SubnetId: !Ref SubnetCTGWA 1023 | 1024 | SubnetCFirewallADefaultRoute: 1025 | Type: AWS::EC2::Route 1026 | DependsOn: AttachVPCC 1027 | Properties: 1028 | DestinationCidrBlock: "0.0.0.0/0" 1029 | TransitGatewayId: !Ref TransitGateway 1030 | RouteTableId: !Ref SubnetCFirewallRouteTableA 1031 | 1032 | SubnetCTGWADefaultRoute: 1033 | Type: AWS::EC2::Route 1034 | DependsOn: InspectionFirewall 1035 | Properties: 1036 | DestinationCidrBlock: "0.0.0.0/0" 1037 | VpcEndpointId: !GetAtt InspectionFirewallVpceIds.FwVpceId1 1038 | RouteTableId: !Ref SubnetCTGWRouteTableA 1039 | 1040 | # Inspection VPC route table configuration: AZ B 1041 | SubnetCFirewallRouteTableB: 1042 | Type: AWS::EC2::RouteTable 1043 | Properties: 1044 | VpcId: !Ref VPCC 1045 | Tags: 1046 | - Key: Name 1047 | Value: !Sub "subnet-c-firewall-route-table-b-${AWS::StackName}" 1048 | 1049 | SubnetCTGWRouteTableB: 1050 | Type: AWS::EC2::RouteTable 1051 | Properties: 1052 | VpcId: !Ref VPCC 1053 | Tags: 1054 | - Key: Name 1055 | Value: !Sub "subnet-c-tgw-route-table-b-${AWS::StackName}" 1056 | 1057 | SubnetCFirewallRouteTableBAssociation: 1058 | Type: AWS::EC2::SubnetRouteTableAssociation 1059 | DependsOn: SubnetCFirewallB 1060 | Properties: 1061 | RouteTableId: !Ref SubnetCFirewallRouteTableB 1062 | SubnetId: !Ref SubnetCFirewallB 1063 | 1064 | SubnetCTGWRouteTableBAssociation: 1065 | Type: AWS::EC2::SubnetRouteTableAssociation 1066 | DependsOn: SubnetCTGWB 1067 | Properties: 1068 | RouteTableId: !Ref SubnetCTGWRouteTableB 1069 | SubnetId: !Ref SubnetCTGWB 1070 | 1071 | SubnetCFirewallBDefaultRoute: 1072 | Type: AWS::EC2::Route 1073 | DependsOn: AttachVPCC 1074 | Properties: 1075 | DestinationCidrBlock: "0.0.0.0/0" 1076 | TransitGatewayId: !Ref TransitGateway 1077 | RouteTableId: !Ref SubnetCFirewallRouteTableB 1078 | 1079 | SubnetCTGWBDefaultRoute: 1080 | Type: AWS::EC2::Route 1081 | DependsOn: InspectionFirewall 1082 | Properties: 1083 | DestinationCidrBlock: "0.0.0.0/0" 1084 | VpcEndpointId: !GetAtt InspectionFirewallVpceIds.FwVpceId2 1085 | RouteTableId: !Ref SubnetCTGWRouteTableB 1086 | 1087 | # Egress VPC route table configuration: AZ A 1088 | SubnetDFirewallRouteTableA: 1089 | Type: AWS::EC2::RouteTable 1090 | Properties: 1091 | VpcId: !Ref VPCD 1092 | Tags: 1093 | - Key: Name 1094 | Value: !Sub "subnet-d-firewall-route-table-a-${AWS::StackName}" 1095 | 1096 | SubnetDFirewallRouteTableAAssociation: 1097 | Type: AWS::EC2::SubnetRouteTableAssociation 1098 | DependsOn: SubnetDFirewallA 1099 | Properties: 1100 | RouteTableId: !Ref SubnetDFirewallRouteTableA 1101 | SubnetId: !Ref SubnetDFirewallA 1102 | 1103 | SubnetDFirewallAInternalRoute: 1104 | Type: AWS::EC2::Route 1105 | DependsOn: AttachVPCD 1106 | Properties: 1107 | DestinationCidrBlock: "10.0.0.0/8" 1108 | TransitGatewayId: !Ref TransitGateway 1109 | RouteTableId: !Ref SubnetDFirewallRouteTableA 1110 | 1111 | SubnetDFirewallADefaultRoute: 1112 | Type: AWS::EC2::Route 1113 | DependsOn: SubnetDNATGatewayA 1114 | Properties: 1115 | DestinationCidrBlock: "0.0.0.0/0" 1116 | NatGatewayId: !Ref SubnetDNATGatewayA 1117 | RouteTableId: !Ref SubnetDFirewallRouteTableA 1118 | 1119 | SubnetDPublicRouteTableA: 1120 | Type: AWS::EC2::RouteTable 1121 | Properties: 1122 | VpcId: !Ref VPCD 1123 | Tags: 1124 | - Key: Name 1125 | Value: !Sub "subnet-d-public-route-table-a-${AWS::StackName}" 1126 | 1127 | SubnetDPublicRouteTableAAssociation: 1128 | Type: AWS::EC2::SubnetRouteTableAssociation 1129 | DependsOn: SubnetDPublicA 1130 | Properties: 1131 | RouteTableId: !Ref SubnetDPublicRouteTableA 1132 | SubnetId: !Ref SubnetDPublicA 1133 | 1134 | SubnetDPublicAInternalRoute: 1135 | Type: AWS::EC2::Route 1136 | DependsOn: EgressFirewall 1137 | Properties: 1138 | DestinationCidrBlock: "10.0.0.0/8" 1139 | VpcEndpointId: !GetAtt EgressFirewallVpceIds.FwVpceId1 1140 | RouteTableId: !Ref SubnetDPublicRouteTableA 1141 | 1142 | SubnetDPublicADefaultRoute: 1143 | Type: AWS::EC2::Route 1144 | DependsOn: InternetGatewayVPCD 1145 | Properties: 1146 | DestinationCidrBlock: "0.0.0.0/0" 1147 | GatewayId: !Ref InternetGatewayVPCD 1148 | RouteTableId: !Ref SubnetDPublicRouteTableA 1149 | 1150 | SubnetDTGWRouteTableA: 1151 | Type: AWS::EC2::RouteTable 1152 | Properties: 1153 | VpcId: !Ref VPCD 1154 | Tags: 1155 | - Key: Name 1156 | Value: !Sub "subnet-d-tgw-route-table-a-${AWS::StackName}" 1157 | 1158 | SubnetDTGWRouteTableAAssociation: 1159 | Type: AWS::EC2::SubnetRouteTableAssociation 1160 | DependsOn: SubnetDTGWA 1161 | Properties: 1162 | RouteTableId: !Ref SubnetDTGWRouteTableA 1163 | SubnetId: !Ref SubnetDTGWA 1164 | 1165 | SubnetDTGWADefaultRoute: 1166 | Type: AWS::EC2::Route 1167 | DependsOn: EgressFirewall 1168 | Properties: 1169 | DestinationCidrBlock: "0.0.0.0/0" 1170 | VpcEndpointId: !GetAtt EgressFirewallVpceIds.FwVpceId1 1171 | RouteTableId: !Ref SubnetDTGWRouteTableA 1172 | 1173 | # Egress VPC route table configuration: AZ B 1174 | SubnetDFirewallRouteTableB: 1175 | Type: AWS::EC2::RouteTable 1176 | Properties: 1177 | VpcId: !Ref VPCD 1178 | Tags: 1179 | - Key: Name 1180 | Value: !Sub "subnet-d-firewall-route-table-b-${AWS::StackName}" 1181 | 1182 | SubnetDFirewallRouteTableBAssociation: 1183 | Type: AWS::EC2::SubnetRouteTableAssociation 1184 | DependsOn: SubnetDFirewallB 1185 | Properties: 1186 | RouteTableId: !Ref SubnetDFirewallRouteTableB 1187 | SubnetId: !Ref SubnetDFirewallB 1188 | 1189 | SubnetDFirewallBInternalRoute: 1190 | Type: AWS::EC2::Route 1191 | DependsOn: AttachVPCD 1192 | Properties: 1193 | DestinationCidrBlock: "10.0.0.0/8" 1194 | TransitGatewayId: !Ref TransitGateway 1195 | RouteTableId: !Ref SubnetDFirewallRouteTableB 1196 | 1197 | SubnetDFirewallBDefaultRoute: 1198 | Type: AWS::EC2::Route 1199 | DependsOn: SubnetDNATGatewayB 1200 | Properties: 1201 | DestinationCidrBlock: "0.0.0.0/0" 1202 | NatGatewayId: !Ref SubnetDNATGatewayB 1203 | RouteTableId: !Ref SubnetDFirewallRouteTableB 1204 | 1205 | SubnetDPublicRouteTableB: 1206 | Type: AWS::EC2::RouteTable 1207 | Properties: 1208 | VpcId: !Ref VPCD 1209 | Tags: 1210 | - Key: Name 1211 | Value: !Sub "subnet-d-public-route-table-b-${AWS::StackName}" 1212 | 1213 | SubnetDPublicRouteTableBAssociation: 1214 | Type: AWS::EC2::SubnetRouteTableAssociation 1215 | DependsOn: SubnetDPublicB 1216 | Properties: 1217 | RouteTableId: !Ref SubnetDPublicRouteTableB 1218 | SubnetId: !Ref SubnetDPublicB 1219 | 1220 | SubnetDPublicBInternalRoute: 1221 | Type: AWS::EC2::Route 1222 | DependsOn: EgressFirewall 1223 | Properties: 1224 | DestinationCidrBlock: "10.0.0.0/8" 1225 | VpcEndpointId: !GetAtt EgressFirewallVpceIds.FwVpceId2 1226 | RouteTableId: !Ref SubnetDPublicRouteTableB 1227 | 1228 | SubnetDPublicBDefaultRoute: 1229 | Type: AWS::EC2::Route 1230 | DependsOn: InternetGatewayVPCD 1231 | Properties: 1232 | DestinationCidrBlock: "0.0.0.0/0" 1233 | GatewayId: !Ref InternetGatewayVPCD 1234 | RouteTableId: !Ref SubnetDPublicRouteTableB 1235 | 1236 | SubnetDTGWRouteTableB: 1237 | Type: AWS::EC2::RouteTable 1238 | Properties: 1239 | VpcId: !Ref VPCD 1240 | Tags: 1241 | - Key: Name 1242 | Value: !Sub "subnet-d-tgw-route-table-b-${AWS::StackName}" 1243 | 1244 | SubnetDTGWRouteTableBAssociation: 1245 | Type: AWS::EC2::SubnetRouteTableAssociation 1246 | DependsOn: SubnetDTGWB 1247 | Properties: 1248 | RouteTableId: !Ref SubnetDTGWRouteTableB 1249 | SubnetId: !Ref SubnetDTGWB 1250 | 1251 | SubnetDTGWBDefaultRoute: 1252 | Type: AWS::EC2::Route 1253 | DependsOn: EgressFirewall 1254 | Properties: 1255 | DestinationCidrBlock: "0.0.0.0/0" 1256 | VpcEndpointId: !GetAtt EgressFirewallVpceIds.FwVpceId2 1257 | RouteTableId: !Ref SubnetDTGWRouteTableB 1258 | 1259 | # TransitGateway route table configuration: 1260 | 1261 | SpokeRouteTable: 1262 | Type: "AWS::EC2::TransitGatewayRouteTable" 1263 | Properties: 1264 | Tags: 1265 | - Key: Name 1266 | Value: !Sub "spoke-route-table-${AWS::StackName}" 1267 | TransitGatewayId: !Ref TransitGateway 1268 | 1269 | FirewallRouteTable: 1270 | Type: "AWS::EC2::TransitGatewayRouteTable" 1271 | Properties: 1272 | Tags: 1273 | - Key: Name 1274 | Value: !Sub "firewall-route-table-${AWS::StackName}" 1275 | TransitGatewayId: !Ref TransitGateway 1276 | 1277 | AssociateVPCARouteTable: 1278 | Type: AWS::EC2::TransitGatewayRouteTableAssociation 1279 | Properties: 1280 | TransitGatewayAttachmentId: !Ref AttachVPCA 1281 | TransitGatewayRouteTableId: !Ref SpokeRouteTable 1282 | 1283 | AssociateVPCBRouteTable: 1284 | Type: AWS::EC2::TransitGatewayRouteTableAssociation 1285 | Properties: 1286 | TransitGatewayAttachmentId: !Ref AttachVPCB 1287 | TransitGatewayRouteTableId: !Ref SpokeRouteTable 1288 | 1289 | AssociateVPCCRouteTable: 1290 | Type: AWS::EC2::TransitGatewayRouteTableAssociation 1291 | Properties: 1292 | TransitGatewayAttachmentId: !Ref AttachVPCC 1293 | TransitGatewayRouteTableId: !Ref FirewallRouteTable 1294 | 1295 | AssociateVPCDRouteTable: 1296 | Type: AWS::EC2::TransitGatewayRouteTableAssociation 1297 | Properties: 1298 | TransitGatewayAttachmentId: !Ref AttachVPCD 1299 | TransitGatewayRouteTableId: !Ref FirewallRouteTable 1300 | 1301 | SpokeInspectionRoute: 1302 | Type: AWS::EC2::TransitGatewayRoute 1303 | Properties: 1304 | DestinationCidrBlock: "10.0.0.0/8" 1305 | TransitGatewayAttachmentId: !Ref AttachVPCC 1306 | TransitGatewayRouteTableId: !Ref SpokeRouteTable 1307 | 1308 | SpokeEgressRoute: 1309 | Type: AWS::EC2::TransitGatewayRoute 1310 | Properties: 1311 | DestinationCidrBlock: "0.0.0.0/0" 1312 | TransitGatewayAttachmentId: !Ref AttachVPCD 1313 | TransitGatewayRouteTableId: !Ref SpokeRouteTable 1314 | 1315 | FirewallSpokeARoute: 1316 | Type: AWS::EC2::TransitGatewayRoute 1317 | Properties: 1318 | DestinationCidrBlock: "10.1.0.0/16" 1319 | TransitGatewayAttachmentId: !Ref AttachVPCA 1320 | TransitGatewayRouteTableId: !Ref FirewallRouteTable 1321 | 1322 | FirewallSpokeBRoute: 1323 | Type: AWS::EC2::TransitGatewayRoute 1324 | Properties: 1325 | DestinationCidrBlock: "10.2.0.0/16" 1326 | TransitGatewayAttachmentId: !Ref AttachVPCB 1327 | TransitGatewayRouteTableId: !Ref FirewallRouteTable 1328 | -------------------------------------------------------------------------------- /centralized_architecture/single_az_deployment/README.md: -------------------------------------------------------------------------------- 1 | # Centralized Architecture - Single AZ Deployment: 2 | 3 | **Template File:** [anfw-centralized-single-1az-template.yaml](anfw-centralized-1az-template.yaml) 4 | 5 | For centralized deployment model, [AWS Transit Gateway](https://aws.amazon.com/transit-gateway/) is a prerequisite. AWS Transit Gateway acts as a network hub and simplifies the connectivity between VPCs as well as on-premises networks. AWS Transit Gateway also provides inter-region peering capabilities to other Transit Gateways to establish a global network using AWS backbone. 6 | 7 | ![anfw-centralized-model-1az](../../images/anfw-centralized-model-1az.jpg) 8 | *Figure 1: Single AZ Centralized Architecture* 9 | 10 | [Centralized single AZ deployment template](anfw-centralized-1az-template.yaml) template, as described in Figure 1, creates dedicated 11 | 12 | * Inspection VPC for East-West (inter-vpc) traffic inspection. Inspection VPC consists of two subnets in single AZ: 13 | * Transit Gateway subnet for Transit Gateway attchment. 14 | * Firewall subnet for firewall endpoint. 15 | 16 | * Central Egress VPC for North-South (spoke VPCs to Internet) traffic inspection. Central Egress VPC consists of 3 subnets in single AZ: 17 | * Transit Gateway subnet for Transit Gateway attchment. 18 | * Firewall subnet for firewall endpoint. 19 | * Public subnet for NAT Gateway. 20 | 21 | * Two Spoke VPCs. 22 | 23 | Each Transit Gateway subnet in each dedicated VPC requires a dedicated VPC route table to ensure the traffic is forwarded to firewall endpoint within the same AZ. These route tables have a default route (0.0.0.0/0) pointing towards firewall endpoint in the same AZ. 24 | 25 | This is a single AZ configuration. Use it for testing/POC. It is recommended to deploy resources in atleast 2 AZs. For multi AZ deployment refer to [Multi AZ Deployment](../README.md). 26 | 27 | For more details, refer to [Blog: Deployment models for AWS Network Firewall](https://aws.amazon.com/blogs/networking-and-content-delivery/deployment-models-for-aws-network-firewall/). -------------------------------------------------------------------------------- /centralized_architecture/single_az_deployment/anfw-centralized-1az-template.yaml: -------------------------------------------------------------------------------- 1 | #Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 5 | #FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 6 | #COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 7 | #IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 8 | #CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | 10 | AWSTemplateFormatVersion: "2010-09-09" 11 | Description: "AWS Network Firewall Demo using multiple VPCs" 12 | 13 | Metadata: 14 | "AWS::CloudFormation::Interface": 15 | ParameterGroups: 16 | - Label: 17 | default: "VPC Parameters" 18 | Parameters: 19 | - AvailabilityZoneSelection 20 | - Label: 21 | default: "EC2 Parameters" 22 | Parameters: 23 | - LatestAmiId 24 | 25 | Parameters: 26 | AvailabilityZoneSelection: 27 | Description: Availability Zone 28 | Type: AWS::EC2::AvailabilityZone::Name 29 | Default: us-east-1a 30 | 31 | LatestAmiId: 32 | Description: Latest EC2 AMI from Systems Manager Parameter Store 33 | Type: 'AWS::SSM::Parameter::Value' 34 | Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' 35 | 36 | Resources: 37 | # spoke-a VPC 38 | VPCA: 39 | Type: AWS::EC2::VPC 40 | Properties: 41 | CidrBlock: "10.1.0.0/16" 42 | EnableDnsSupport: "true" 43 | EnableDnsHostnames: "true" 44 | InstanceTenancy: default 45 | Tags: 46 | - Key: Name 47 | Value: !Sub "spoke-a-${AWS::StackName}" 48 | 49 | SubnetAWorkload: 50 | Type: AWS::EC2::Subnet 51 | Properties: 52 | VpcId: 53 | Ref: VPCA 54 | CidrBlock: "10.1.1.0/24" 55 | AvailabilityZone: 56 | Ref: AvailabilityZoneSelection 57 | MapPublicIpOnLaunch: false 58 | Tags: 59 | - Key: Name 60 | Value: !Sub "spoke-a-workload-${AWS::StackName}" 61 | 62 | SubnetATGW: 63 | Type: AWS::EC2::Subnet 64 | Properties: 65 | VpcId: 66 | Ref: VPCA 67 | CidrBlock: "10.1.0.0/28" 68 | AvailabilityZone: 69 | Ref: AvailabilityZoneSelection 70 | MapPublicIpOnLaunch: false 71 | Tags: 72 | - Key: Name 73 | Value: !Sub "spoke-a-tgw-${AWS::StackName}" 74 | 75 | VPCAEndpointSecurityGroup: 76 | Type: AWS::EC2::SecurityGroup 77 | Properties: 78 | GroupDescription: Allow instances to get to SSM Systems Manager 79 | VpcId: !Ref VPCA 80 | SecurityGroupIngress: 81 | - IpProtocol: tcp 82 | FromPort: 443 83 | ToPort: 443 84 | CidrIp: 10.1.0.0/16 85 | 86 | VPCASSMEndpoint: 87 | Type: AWS::EC2::VPCEndpoint 88 | Properties: 89 | PrivateDnsEnabled: true 90 | SecurityGroupIds: 91 | - !Ref VPCAEndpointSecurityGroup 92 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm" 93 | SubnetIds: 94 | - !Ref SubnetAWorkload 95 | VpcEndpointType: Interface 96 | VpcId: !Ref VPCA 97 | 98 | VPCAEC2MessagesEndpoint: 99 | Type: AWS::EC2::VPCEndpoint 100 | Properties: 101 | PrivateDnsEnabled: true 102 | SecurityGroupIds: 103 | - !Ref VPCAEndpointSecurityGroup 104 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2messages" 105 | SubnetIds: 106 | - !Ref SubnetAWorkload 107 | VpcEndpointType: Interface 108 | VpcId: !Ref VPCA 109 | 110 | VPCASSMMessagesEndpoint: 111 | Type: AWS::EC2::VPCEndpoint 112 | Properties: 113 | PrivateDnsEnabled: true 114 | SecurityGroupIds: 115 | - !Ref VPCAEndpointSecurityGroup 116 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages" 117 | SubnetIds: 118 | - !Ref SubnetAWorkload 119 | VpcEndpointType: Interface 120 | VpcId: !Ref VPCA 121 | 122 | SubnetARole: 123 | Type: AWS::IAM::Role 124 | Properties: 125 | RoleName: !Sub "subnet-a-role-${AWS::StackName}" 126 | Path: "/" 127 | ManagedPolicyArns: 128 | - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM" 129 | AssumeRolePolicyDocument: 130 | Version: "2012-10-17" 131 | Statement: 132 | - Effect: Allow 133 | Principal: 134 | Service: 135 | - ec2.amazonaws.com 136 | Action: 137 | - sts:AssumeRole 138 | 139 | SubnetAInstanceProfile: 140 | Type: AWS::IAM::InstanceProfile 141 | Properties: 142 | Path: "/" 143 | Roles: 144 | - !Ref SubnetARole 145 | 146 | SubnetASecGroup: 147 | Type: AWS::EC2::SecurityGroup 148 | Properties: 149 | GroupDescription: "ICMP acess from 10.0.0.0/8" 150 | GroupName: !Sub "spoke-a-sec-group-${AWS::StackName}" 151 | VpcId: !Ref VPCA 152 | SecurityGroupIngress: 153 | - IpProtocol: icmp 154 | CidrIp: 10.0.0.0/8 155 | FromPort: "-1" 156 | ToPort: "-1" 157 | 158 | EC2SubnetA: 159 | Type: 'AWS::EC2::Instance' 160 | Properties: 161 | ImageId: !Ref LatestAmiId 162 | SubnetId: !Ref SubnetAWorkload 163 | InstanceType: t2.micro 164 | SecurityGroupIds: 165 | - !Ref SubnetASecGroup 166 | IamInstanceProfile: !Ref SubnetAInstanceProfile 167 | Tags: 168 | - Key: Name 169 | Value: !Sub "spoke-a-${AWS::StackName}" 170 | 171 | # spoke-b VPC 172 | VPCB: 173 | Type: AWS::EC2::VPC 174 | Properties: 175 | CidrBlock: "10.2.0.0/16" 176 | EnableDnsSupport: "true" 177 | EnableDnsHostnames: "true" 178 | InstanceTenancy: default 179 | Tags: 180 | - Key: Name 181 | Value: !Sub "spoke-b-${AWS::StackName}" 182 | 183 | SubnetBWorkload: 184 | Type: AWS::EC2::Subnet 185 | Properties: 186 | VpcId: 187 | Ref: VPCB 188 | CidrBlock: "10.2.1.0/24" 189 | AvailabilityZone: 190 | Ref: AvailabilityZoneSelection 191 | MapPublicIpOnLaunch: true 192 | Tags: 193 | - Key: Name 194 | Value: !Sub "spoke-b-workload-${AWS::StackName}" 195 | 196 | SubnetBTGW: 197 | Type: AWS::EC2::Subnet 198 | Properties: 199 | VpcId: 200 | Ref: VPCB 201 | CidrBlock: "10.2.0.0/28" 202 | AvailabilityZone: 203 | Ref: AvailabilityZoneSelection 204 | MapPublicIpOnLaunch: false 205 | Tags: 206 | - Key: Name 207 | Value: !Sub "spoke-b-tgw-${AWS::StackName}" 208 | 209 | VPCBEndpointSecurityGroup: 210 | Type: AWS::EC2::SecurityGroup 211 | Properties: 212 | GroupDescription: Allow instances to get to SSM Systems Manager 213 | VpcId: !Ref VPCB 214 | SecurityGroupIngress: 215 | - IpProtocol: tcp 216 | FromPort: 443 217 | ToPort: 443 218 | CidrIp: 10.2.0.0/16 219 | 220 | VPCBSSMEndpoint: 221 | Type: AWS::EC2::VPCEndpoint 222 | Properties: 223 | PrivateDnsEnabled: true 224 | SecurityGroupIds: 225 | - !Ref VPCBEndpointSecurityGroup 226 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm" 227 | SubnetIds: 228 | - !Ref SubnetBWorkload 229 | VpcEndpointType: Interface 230 | VpcId: !Ref VPCB 231 | 232 | VPCBEC2MessagesEndpoint: 233 | Type: AWS::EC2::VPCEndpoint 234 | Properties: 235 | PrivateDnsEnabled: true 236 | SecurityGroupIds: 237 | - !Ref VPCBEndpointSecurityGroup 238 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2messages" 239 | SubnetIds: 240 | - !Ref SubnetBWorkload 241 | VpcEndpointType: Interface 242 | VpcId: !Ref VPCB 243 | 244 | VPCBSSMMessagesEndpoint: 245 | Type: AWS::EC2::VPCEndpoint 246 | Properties: 247 | PrivateDnsEnabled: true 248 | SecurityGroupIds: 249 | - !Ref VPCBEndpointSecurityGroup 250 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages" 251 | SubnetIds: 252 | - !Ref SubnetBWorkload 253 | VpcEndpointType: Interface 254 | VpcId: !Ref VPCB 255 | 256 | SubnetBRole: 257 | Type: AWS::IAM::Role 258 | Properties: 259 | RoleName: !Sub "subnet-b-role-${AWS::StackName}" 260 | Path: "/" 261 | ManagedPolicyArns: 262 | - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM" 263 | AssumeRolePolicyDocument: 264 | Version: "2012-10-17" 265 | Statement: 266 | - Effect: Allow 267 | Principal: 268 | Service: 269 | - ec2.amazonaws.com 270 | Action: 271 | - sts:AssumeRole 272 | 273 | SubnetBInstanceProfile: 274 | Type: AWS::IAM::InstanceProfile 275 | Properties: 276 | Path: "/" 277 | Roles: 278 | - !Ref SubnetBRole 279 | 280 | SubnetBSecGroup: 281 | Type: AWS::EC2::SecurityGroup 282 | Properties: 283 | GroupDescription: "ICMP acess from 10.0.0.0/8" 284 | GroupName: !Sub "spoke-b-sec-group-${AWS::StackName}" 285 | VpcId: !Ref VPCB 286 | SecurityGroupIngress: 287 | - IpProtocol: icmp 288 | CidrIp: 10.0.0.0/8 289 | FromPort: "-1" 290 | ToPort: "-1" 291 | 292 | EC2SubnetB: 293 | Type: 'AWS::EC2::Instance' 294 | Properties: 295 | ImageId: !Ref LatestAmiId 296 | SubnetId: !Ref SubnetBWorkload 297 | InstanceType: t2.micro 298 | SecurityGroupIds: 299 | - !Ref SubnetBSecGroup 300 | IamInstanceProfile: !Ref SubnetBInstanceProfile 301 | Tags: 302 | - Key: Name 303 | Value: !Sub "spoke-b-${AWS::StackName}" 304 | 305 | # inspection VPC 306 | VPCC: 307 | Type: AWS::EC2::VPC 308 | Properties: 309 | CidrBlock: "100.64.0.0/16" 310 | EnableDnsSupport: "true" 311 | EnableDnsHostnames: "true" 312 | InstanceTenancy: default 313 | Tags: 314 | - Key: Name 315 | Value: !Sub "inspection-${AWS::StackName}" 316 | 317 | SubnetCFirewall: 318 | Type: AWS::EC2::Subnet 319 | Properties: 320 | VpcId: 321 | Ref: VPCC 322 | CidrBlock: "100.64.0.16/28" 323 | AvailabilityZone: 324 | Ref: AvailabilityZoneSelection 325 | MapPublicIpOnLaunch: false 326 | Tags: 327 | - Key: Name 328 | Value: !Sub "inspection-firewall-${AWS::StackName}" 329 | 330 | SubnetCTGW: 331 | Type: AWS::EC2::Subnet 332 | Properties: 333 | VpcId: 334 | Ref: VPCC 335 | CidrBlock: "100.64.0.0/28" 336 | AvailabilityZone: 337 | Ref: AvailabilityZoneSelection 338 | MapPublicIpOnLaunch: false 339 | Tags: 340 | - Key: Name 341 | Value: !Sub "inspections-tgw-${AWS::StackName}" 342 | 343 | # egress VPC 344 | VPCD: 345 | Type: AWS::EC2::VPC 346 | Properties: 347 | CidrBlock: "10.10.0.0/16" 348 | EnableDnsSupport: "true" 349 | EnableDnsHostnames: "true" 350 | InstanceTenancy: default 351 | Tags: 352 | - Key: Name 353 | Value: !Sub "egress-${AWS::StackName}" 354 | 355 | SubnetDTGW: 356 | Type: AWS::EC2::Subnet 357 | Properties: 358 | VpcId: 359 | Ref: VPCD 360 | CidrBlock: "10.10.0.0/28" 361 | AvailabilityZone: 362 | Ref: AvailabilityZoneSelection 363 | MapPublicIpOnLaunch: false 364 | Tags: 365 | - Key: Name 366 | Value: !Sub "egress-tgw-${AWS::StackName}" 367 | 368 | SubnetDPublic: 369 | Type: AWS::EC2::Subnet 370 | Properties: 371 | VpcId: 372 | Ref: VPCD 373 | CidrBlock: "10.10.1.0/24" 374 | AvailabilityZone: 375 | Ref: AvailabilityZoneSelection 376 | MapPublicIpOnLaunch: true 377 | Tags: 378 | - Key: Name 379 | Value: !Sub "egress-public-${AWS::StackName}" 380 | 381 | InternetGatewayVPCD: 382 | Type: AWS::EC2::InternetGateway 383 | Properties: 384 | Tags: 385 | - Key: Name 386 | Value: !Sub "egress-igw-${AWS::StackName}" 387 | 388 | AttachGatewayVPCD: 389 | Type: AWS::EC2::VPCGatewayAttachment 390 | Properties: 391 | VpcId: 392 | !Ref VPCD 393 | InternetGatewayId: 394 | !Ref InternetGatewayVPCD 395 | 396 | SubnetDNATEIP: 397 | Type: "AWS::EC2::EIP" 398 | Properties: 399 | Domain: vpc 400 | 401 | SubnetDNATGateway: 402 | Type: "AWS::EC2::NatGateway" 403 | Properties: 404 | AllocationId: 405 | Fn::GetAtt: 406 | - SubnetDNATEIP 407 | - AllocationId 408 | SubnetId: 409 | Ref: SubnetDPublic 410 | Tags: 411 | - Key: Name 412 | Value: !Sub "egress-natgw-${AWS::StackName}" 413 | 414 | # Transit Gateway 415 | TransitGateway: 416 | Type: "AWS::EC2::TransitGateway" 417 | Properties: 418 | AmazonSideAsn: 65000 419 | Description: "TGW Network Firewall Demo" 420 | AutoAcceptSharedAttachments: "disable" 421 | DefaultRouteTableAssociation: "disable" 422 | DefaultRouteTablePropagation: "disable" 423 | DnsSupport: "enable" 424 | VpnEcmpSupport: "enable" 425 | Tags: 426 | - Key: Name 427 | Value: !Sub "tgw-${AWS::StackName}" 428 | 429 | AttachVPCA: 430 | Type: "AWS::EC2::TransitGatewayAttachment" 431 | Properties: 432 | SubnetIds: 433 | - !Ref SubnetATGW 434 | Tags: 435 | - Key: Name 436 | Value: !Sub "spoke-a-attach-${AWS::StackName}" 437 | TransitGatewayId: !Ref TransitGateway 438 | VpcId: !Ref VPCA 439 | 440 | AttachVPCB: 441 | Type: "AWS::EC2::TransitGatewayAttachment" 442 | Properties: 443 | SubnetIds: 444 | - !Ref SubnetBTGW 445 | Tags: 446 | - Key: Name 447 | Value: !Sub "spoke-b-attach-${AWS::StackName}" 448 | TransitGatewayId: !Ref TransitGateway 449 | VpcId: !Ref VPCB 450 | 451 | AttachVPCC: 452 | Type: "AWS::EC2::TransitGatewayAttachment" 453 | Properties: 454 | SubnetIds: 455 | - !Ref SubnetCTGW 456 | Tags: 457 | - Key: Name 458 | Value: !Sub "inspection-attach-${AWS::StackName}" 459 | TransitGatewayId: !Ref TransitGateway 460 | VpcId: !Ref VPCC 461 | 462 | AttachVPCD: 463 | Type: "AWS::EC2::TransitGatewayAttachment" 464 | Properties: 465 | SubnetIds: 466 | - !Ref SubnetDTGW 467 | Tags: 468 | - Key: Name 469 | Value: !Sub "egress-attach-${AWS::StackName}" 470 | TransitGatewayId: !Ref TransitGateway 471 | VpcId: !Ref VPCD 472 | 473 | # Firewalls 474 | 475 | ICMPAlertStatefulRuleGroup: 476 | Type: 'AWS::NetworkFirewall::RuleGroup' 477 | Properties: 478 | RuleGroupName: !Sub "icmp-alert-${AWS::StackName}" 479 | Type: STATEFUL 480 | Capacity: 100 481 | RuleGroup: 482 | RulesSource: 483 | StatefulRules: 484 | - Action: ALERT 485 | Header: 486 | Direction: ANY 487 | Protocol: ICMP 488 | Destination: ANY 489 | Source: ANY 490 | DestinationPort: ANY 491 | SourcePort: ANY 492 | RuleOptions: 493 | - Keyword: "sid:1" 494 | Tags: 495 | - Key: Name 496 | Value: !Sub "icmp-alert-${AWS::StackName}" 497 | 498 | DomainAllowStatefulRuleGroup: 499 | Type: 'AWS::NetworkFirewall::RuleGroup' 500 | Properties: 501 | RuleGroupName: !Sub "domain-allow-${AWS::StackName}" 502 | Type: STATEFUL 503 | Capacity: 100 504 | RuleGroup: 505 | RuleVariables: 506 | IPSets: 507 | HOME_NET: 508 | Definition: 509 | - "10.0.0.0/8" 510 | RulesSource: 511 | RulesSourceList: 512 | TargetTypes: 513 | - HTTP_HOST 514 | - TLS_SNI 515 | Targets: 516 | - ".amazon.com" 517 | GeneratedRulesType: "ALLOWLIST" 518 | Tags: 519 | - Key: Name 520 | Value: !Sub "domain-allow-${AWS::StackName}" 521 | 522 | InspectionFirewallPolicy: 523 | Type: AWS::NetworkFirewall::FirewallPolicy 524 | Properties: 525 | FirewallPolicyName: !Sub "inspection-firewall-policy-${AWS::StackName}" 526 | FirewallPolicy: 527 | StatelessDefaultActions: 528 | - 'aws:forward_to_sfe' 529 | StatelessFragmentDefaultActions: 530 | - 'aws:forward_to_sfe' 531 | StatefulRuleGroupReferences: 532 | - ResourceArn: !Ref ICMPAlertStatefulRuleGroup 533 | Tags: 534 | - Key: Name 535 | Value: !Sub "inspection-firewall-policy-${AWS::StackName}" 536 | 537 | InspectionFirewall: 538 | Type: AWS::NetworkFirewall::Firewall 539 | Properties: 540 | FirewallName: !Sub "inspection-firewall-${AWS::StackName}" 541 | FirewallPolicyArn: !Ref InspectionFirewallPolicy 542 | VpcId: !Ref VPCC 543 | SubnetMappings: 544 | - SubnetId: !Ref SubnetCFirewall 545 | Tags: 546 | - Key: Name 547 | Value: !Sub "inspection-firewall-${AWS::StackName}" 548 | 549 | InspectionFirewallLogFlowGroup: 550 | Type: AWS::Logs::LogGroup 551 | Properties: 552 | LogGroupName: !Sub "/${AWS::StackName}/inspection-fw/flow" 553 | 554 | InspectionFirewallLogAlertGroup: 555 | Type: AWS::Logs::LogGroup 556 | Properties: 557 | LogGroupName: !Sub "/${AWS::StackName}/inspection-fw/alert" 558 | 559 | InspectionFirewallLog: 560 | Type: AWS::NetworkFirewall::LoggingConfiguration 561 | Properties: 562 | FirewallArn: !Ref InspectionFirewall 563 | LoggingConfiguration: 564 | LogDestinationConfigs: 565 | - LogType: FLOW 566 | LogDestinationType: CloudWatchLogs 567 | LogDestination: 568 | logGroup: !Sub "/${AWS::StackName}/inspection-fw/flow" 569 | - LogType: ALERT 570 | LogDestinationType: CloudWatchLogs 571 | LogDestination: 572 | logGroup: !Sub "/${AWS::StackName}/inspection-fw/alert" 573 | 574 | 575 | 576 | # Route Tables 577 | SubnetAWorkloadRouteTable: 578 | Type: AWS::EC2::RouteTable 579 | Properties: 580 | VpcId: !Ref VPCA 581 | Tags: 582 | - Key: Name 583 | Value: !Sub "subnet-a-workload-route-table-${AWS::StackName}" 584 | 585 | SubnetAWorkloadRouteTableAssociation: 586 | Type: AWS::EC2::SubnetRouteTableAssociation 587 | DependsOn: SubnetAWorkload 588 | Properties: 589 | RouteTableId: !Ref SubnetAWorkloadRouteTable 590 | SubnetId: !Ref SubnetAWorkload 591 | 592 | SubnetAWorkloadDefaultRoute: 593 | Type: AWS::EC2::Route 594 | DependsOn: AttachVPCA 595 | Properties: 596 | DestinationCidrBlock: "0.0.0.0/0" 597 | TransitGatewayId: !Ref TransitGateway 598 | RouteTableId: !Ref SubnetAWorkloadRouteTable 599 | 600 | SubnetBWorkloadRouteTable: 601 | Type: AWS::EC2::RouteTable 602 | Properties: 603 | VpcId: !Ref VPCB 604 | Tags: 605 | - Key: Name 606 | Value: !Sub "subnet-b-workload-route-table-${AWS::StackName}" 607 | 608 | SubnetBWorkloadRouteTableAssociation: 609 | Type: AWS::EC2::SubnetRouteTableAssociation 610 | DependsOn: SubnetBWorkload 611 | Properties: 612 | RouteTableId: !Ref SubnetBWorkloadRouteTable 613 | SubnetId: !Ref SubnetBWorkload 614 | 615 | SubnetBWorkloadInternalRoute: 616 | Type: AWS::EC2::Route 617 | DependsOn: AttachVPCB 618 | Properties: 619 | DestinationCidrBlock: "0.0.0.0/0" 620 | TransitGatewayId: !Ref TransitGateway 621 | RouteTableId: !Ref SubnetBWorkloadRouteTable 622 | 623 | SubnetCFirewallRouteTable: 624 | Type: AWS::EC2::RouteTable 625 | Properties: 626 | VpcId: !Ref VPCC 627 | Tags: 628 | - Key: Name 629 | Value: !Sub "subnet-c-firewall-route-table-${AWS::StackName}" 630 | 631 | SubnetCTGWRouteTable: 632 | Type: AWS::EC2::RouteTable 633 | Properties: 634 | VpcId: !Ref VPCC 635 | Tags: 636 | - Key: Name 637 | Value: !Sub "subnet-c-tgw-route-table-${AWS::StackName}" 638 | 639 | SubnetCFirewallRouteTableAssociation: 640 | Type: AWS::EC2::SubnetRouteTableAssociation 641 | DependsOn: SubnetCFirewall 642 | Properties: 643 | RouteTableId: !Ref SubnetCFirewallRouteTable 644 | SubnetId: !Ref SubnetCFirewall 645 | 646 | SubnetCTGWRouteTableAssociation: 647 | Type: AWS::EC2::SubnetRouteTableAssociation 648 | DependsOn: SubnetCTGW 649 | Properties: 650 | RouteTableId: !Ref SubnetCTGWRouteTable 651 | SubnetId: !Ref SubnetCTGW 652 | 653 | SubnetCFirewallDefaultRoute: 654 | Type: AWS::EC2::Route 655 | DependsOn: AttachVPCC 656 | Properties: 657 | DestinationCidrBlock: "0.0.0.0/0" 658 | TransitGatewayId: !Ref TransitGateway 659 | RouteTableId: !Ref SubnetCFirewallRouteTable 660 | 661 | SubnetCTGWDefaultRoute: 662 | Type: AWS::EC2::Route 663 | DependsOn: InspectionFirewall 664 | Properties: 665 | DestinationCidrBlock: "0.0.0.0/0" 666 | VpcEndpointId: !Select [1, !Split [":", !Select [0, !GetAtt InspectionFirewall.EndpointIds]]] 667 | RouteTableId: !Ref SubnetCTGWRouteTable 668 | 669 | SubnetDTGWRouteTable: 670 | Type: AWS::EC2::RouteTable 671 | Properties: 672 | VpcId: !Ref VPCD 673 | Tags: 674 | - Key: Name 675 | Value: !Sub "subnet-d-tgw-route-table-${AWS::StackName}" 676 | 677 | SubnetDTGWCorpRoute: 678 | Type: AWS::EC2::Route 679 | DependsOn: AttachVPCC 680 | Properties: 681 | DestinationCidrBlock: "10.0.0.0/8" 682 | TransitGatewayId: !Ref TransitGateway 683 | RouteTableId: !Ref SubnetDTGWRouteTable 684 | 685 | SubnetDTGWDefaultRoute: 686 | Type: AWS::EC2::Route 687 | DependsOn: AttachVPCC 688 | Properties: 689 | DestinationCidrBlock: "0.0.0.0/0" 690 | NatGatewayId: !Ref SubnetDNATGateway 691 | RouteTableId: !Ref SubnetDTGWRouteTable 692 | 693 | SubnetDPublicRouteTable: 694 | Type: AWS::EC2::RouteTable 695 | Properties: 696 | VpcId: !Ref VPCD 697 | Tags: 698 | - Key: Name 699 | Value: !Sub "subnet-d-public-route-table-${AWS::StackName}" 700 | 701 | SubnetDPublicCorpRoute: 702 | Type: AWS::EC2::Route 703 | DependsOn: AttachVPCD 704 | Properties: 705 | DestinationCidrBlock: "10.0.0.0/8" 706 | TransitGatewayId: !Ref TransitGateway 707 | RouteTableId: !Ref SubnetDPublicRouteTable 708 | 709 | SubnetDPublicDefaultRoute: 710 | Type: AWS::EC2::Route 711 | DependsOn: AttachVPCD 712 | Properties: 713 | DestinationCidrBlock: "0.0.0.0/0" 714 | GatewayId: !Ref InternetGatewayVPCD 715 | RouteTableId: !Ref SubnetDPublicRouteTable 716 | 717 | SubnetDTGWRouteTableAssociation: 718 | Type: AWS::EC2::SubnetRouteTableAssociation 719 | DependsOn: SubnetDTGW 720 | Properties: 721 | RouteTableId: !Ref SubnetDTGWRouteTable 722 | SubnetId: !Ref SubnetDTGW 723 | 724 | SubnetDPublicRouteTableAssociation: 725 | Type: AWS::EC2::SubnetRouteTableAssociation 726 | DependsOn: SubnetDPublic 727 | Properties: 728 | RouteTableId: !Ref SubnetDPublicRouteTable 729 | SubnetId: !Ref SubnetDPublic 730 | 731 | SpokeRouteTable: 732 | Type: "AWS::EC2::TransitGatewayRouteTable" 733 | Properties: 734 | Tags: 735 | - Key: Name 736 | Value: !Sub "spoke-route-table-${AWS::StackName}" 737 | TransitGatewayId: !Ref TransitGateway 738 | 739 | FirewallRouteTable: 740 | Type: "AWS::EC2::TransitGatewayRouteTable" 741 | Properties: 742 | Tags: 743 | - Key: Name 744 | Value: !Sub "firewall-route-table-${AWS::StackName}" 745 | TransitGatewayId: !Ref TransitGateway 746 | 747 | AssociateVPCARouteTable: 748 | Type: AWS::EC2::TransitGatewayRouteTableAssociation 749 | Properties: 750 | TransitGatewayAttachmentId: !Ref AttachVPCA 751 | TransitGatewayRouteTableId: !Ref SpokeRouteTable 752 | 753 | AssociateVPCBRouteTable: 754 | Type: AWS::EC2::TransitGatewayRouteTableAssociation 755 | Properties: 756 | TransitGatewayAttachmentId: !Ref AttachVPCB 757 | TransitGatewayRouteTableId: !Ref SpokeRouteTable 758 | 759 | AssociateVPCCRouteTable: 760 | Type: AWS::EC2::TransitGatewayRouteTableAssociation 761 | Properties: 762 | TransitGatewayAttachmentId: !Ref AttachVPCC 763 | TransitGatewayRouteTableId: !Ref FirewallRouteTable 764 | 765 | AssociateVPCDRouteTable: 766 | Type: AWS::EC2::TransitGatewayRouteTableAssociation 767 | Properties: 768 | TransitGatewayAttachmentId: !Ref AttachVPCD 769 | TransitGatewayRouteTableId: !Ref SpokeRouteTable 770 | 771 | PropagateVPCARoute: 772 | Type: AWS::EC2::TransitGatewayRouteTablePropagation 773 | Properties: 774 | TransitGatewayAttachmentId: !Ref AttachVPCA 775 | TransitGatewayRouteTableId: !Ref FirewallRouteTable 776 | 777 | PropagateVPCBRoute: 778 | Type: AWS::EC2::TransitGatewayRouteTablePropagation 779 | Properties: 780 | TransitGatewayAttachmentId: !Ref AttachVPCB 781 | TransitGatewayRouteTableId: !Ref FirewallRouteTable 782 | 783 | PropagateVPDARoute: 784 | Type: AWS::EC2::TransitGatewayRouteTablePropagation 785 | Properties: 786 | TransitGatewayAttachmentId: !Ref AttachVPCD 787 | TransitGatewayRouteTableId: !Ref FirewallRouteTable 788 | 789 | 790 | SpokeInspectionRoute: 791 | Type: AWS::EC2::TransitGatewayRoute 792 | Properties: 793 | DestinationCidrBlock: "0.0.0.0/0" 794 | TransitGatewayAttachmentId: !Ref AttachVPCC 795 | TransitGatewayRouteTableId: !Ref SpokeRouteTable 796 | 797 | SpokeEgressRoute: 798 | Type: AWS::EC2::TransitGatewayRoute 799 | Properties: 800 | DestinationCidrBlock: "0.0.0.0/0" 801 | TransitGatewayAttachmentId: !Ref AttachVPCD 802 | TransitGatewayRouteTableId: !Ref FirewallRouteTable 803 | -------------------------------------------------------------------------------- /cloudwatch_dashboard/README.md: -------------------------------------------------------------------------------- 1 | # AWS Network Firewall CloudWatch Dashboard 2 | 3 | ## Authors 4 | 5 | |Name | Email| 6 | |------|------| 7 | |Todd Pula, Sr. Cloud Support Engineer | toddpul@amazon.com| 8 | 9 | ## License: 10 | 11 | This sample code is made available under the MIT-0 license. See the LICENSE file. 12 | 13 | ## Summary: 14 | 15 | AWS Network Firewall provides a number of tools that you can use to monitor the utilization, availability, and performance of your firewall. Firewall stateless and stateful engine metrics are published in near real-time to Amazon CloudWatch. CloudWatch alarms can monitor these metrics and send notifications or take actions when a defined threshold is breached. Configuring logging for the firewall's stateful engine is optional but highly recommended. Firewall flow and alert logs provide a wealth of information related to network traffic and stateful drop, reject, and alert rule matches. Publishing firewall flow and alert log events to CloudWatch log groups allows you to take advantage of CloudWatch log analytics tools such as Logs Insights and Contributor Insights. 16 | 17 | Included in this project is a Network Firewall CloudWatch dashboard CloudFormation template. This easy-to-use template creates a monitoring dashboard for a single AWS Network Firewall with just a few clicks. Through a single pane of glass, you will be able to visualize and analyze firewall metric and log data in a structured way. The included dashboard widgets provide insight into areas such as: 18 | 19 | * Gateway Load Balancer endpoint [metrics](https://docs.aws.amazon.com/vpc/latest/privatelink/privatelink-cloudwatch-metrics.html) 20 | * Firewall engine [metrics](https://docs.aws.amazon.com/network-firewall/latest/developerguide/monitoring-cloudwatch.html) 21 | * Top talkers 22 | * Top protocols 23 | * Alert log analysis 24 | * HTTP & TLS flow analysis 25 | 26 | ## Pricing Considerations: 27 | 28 | The AWS Network Firewall CloudWatch dashboard incorporates a number of CloudWatch features including basic monitoring metrics, [vended logs](https://aws.amazon.com/cloudwatch/pricing/#Vended_Logs), Logs Insights queries, Contributor Insights rules, and the dashboard itself. Please refer to the [Amazon CloudWatch Pricing](https://aws.amazon.com/cloudwatch/pricing/) guide for up-to-date free and paid tier pricing considerations. By default, the dashboard will query firewall flow and alert log events over a 3 hour time range. Increasing the time range will scan more log events at an increased cost while reducing the time range will scan fewer log events at a reduced cost. All of the Logs Insights and Contributor Insights widgets display the top 10 data points by default. You can choose to edit the Insights queries or change the Top Contributors to a larger value in order to display more results. The dashboard will not automatically refresh the widget data by default. You can choose to manually refresh the data within a single widget or all widgets or you can optionally configure the entire dashboard to automatically refresh at a configured time interval. 29 | 30 | ## Before You Begin 31 | 32 | The provided CloudFormation template makes the following two assumptions: 33 | 34 | 1. You have already created an AWS Network Firewall in your VPC 35 | 2. Your AWS Network Firewall has been configured to publish firewall flow and alert logs to two different CloudWatch log groups. For example, firewall flow logs are published to /my-firewall-flow-logs and alert logs are published to /my-firewall-alert-logs. 36 | 37 | If you have not deployed AWS Network Firewall in your VPC, you can use one of the available [AWS Network Firewall Deployment Architecture](https://github.com/aws-samples/aws-networkfirewall-cfn-templates) templates to create a firewall. Once created, configure [CloudWatch log groups](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html) for the firewall flow and alert logs and configure stateful [logging](https://docs.aws.amazon.com/network-firewall/latest/developerguide/firewall-logging.html) as described above. Fine tune your firewall policy and rule configuration and make sure you are routing traffic symmetrically through the firewall. With the firewall now in the routed path and publishing metrics and log events, you can now proceed with this AWS Network Firewall CloudWatch dashboard template. 38 | 39 | ## Installation 40 | 41 | The AWS Network Firewall dashboard CloudFormation template will create a monitoring dashboard for a single AWS Network Firewall. You will launch this CloudFormation stack in the same region as your firewall. 42 | 43 | 1. Navigate to the **CloudFormation > Stacks** console in the region where your firewall is located. Click the **Create stacks** button and select **With new resources (standard)**. 44 | 2. Make sure that **Prepare template** is set to **Template is ready**. For **Template source**, select **Upload a template file**. Click the **Choose file** button and select the **nfw-cloudwatch-dashboard.yaml** file downloaded from this project. Click Next. 45 | 46 | When launching the stack, you will need to input the following parameters: 47 | 48 | * **Stack name:** A descriptive name for this CloudFormation stack. For example, **my-firewall-dashboard**. 49 | * **Firewall name:** Firewall name as seen in the **VPC > Network Firewall > Firewalls** console 50 | * **Firewall subnets:** The firewall subnet Ids to which your firewall endpoints are attached. The firewall subnets can be found on the **Firewall details** tab of your firewall in the **VPC > Network Firewall > Firewalls** console. 51 | * **Flow log group name:** The name of the CloudWatch log group where your firewall flow logs are stored 52 | * **Alert log group name:** The name of the CloudWatch log group where your firewall alert logs are stored 53 | * **Contributor Insights rule state:** Choose to enable or disable the Contributor Insights rules. Disabling will prevent the rules from scanning log data and displaying results in the Contributor Insights widgets. Once created, you can change the state of one or more Contributor Insights rules from the **CloudWatch > Insights > Contributor Insights** console. The template defaults to ENABLED. 54 | 55 | Once the stack has reached the **CREATE_COMPLETE** status, click on the **Outputs** tab. Clicking the **FirewallDashboardURI** link will open the newly created dashboard in the CloudWatch Dashboards console. Note that it can take a few minutes for the Logs Insights and Contributor Insights widgets to display data. Some widgets may not display data points if you don't have log events that match the query parameters. 56 | 57 | ## Removal 58 | 59 | You can delete the AWS Network Firewall CloudWatch dashboard and all of the associated resources with just a few clicks. Deleting the dashboard will not impact the routing and network traffic inspection performed by the firewall. 60 | 61 | 1. Navigate to the **CloudFormation > Stacks** console in the region where you launched the stack 62 | 1. Select the radio button next to the Stack name you chose when launching the stack. For example, **my-firewall-dashboard**. 63 | 1. Click the **Delete** button 64 | 65 | 66 | ## Dashboard Widget Details: 67 | 68 | ### Firewall Endpoints: 69 | 70 | |Widget Name | Type | Usage| 71 | |------|------|------| 72 | |Firewall Endpoint ENI (GWLBe/VPCe) Metrics | Metrics | Per-endpoint packet, byte, and connections metrics| 73 | |Per Endpoint Utilization Gbps | Metrics | Per-endpoint utilization in Gbps during the most recent period| 74 | |ActiveConnections | Metrics | Active connections per-endpoint during the most recent period and time range| 75 | |BytesProcessed | Metrics | Bytes processed per-endpoint during the most recent period and time range| 76 | 77 | ### Firewall Engines: 78 | 79 | #### Stateless: 80 | 81 | |Widget Name | Type | Usage| 82 | |------|------|------| 83 | |Stateless Engine Metrics | Metrics | All available stateless engine metrics per availability zone| 84 | |Stateless Passed Packets | Metrics | Packets passed by stateless rules or default action during the most recent period and time range| 85 | |Stateless Dropped Packets - Rule Action | Metrics | Packets dropped by stateless rules or stateless default action during the most recent period and time range| 86 | |Stateless Dropped Packets - Other | Metrics | Packets dropped by stateless packet validation checks during the most recent period and time range| 87 | 88 | #### Stateful: 89 | 90 | |Widget Name | Type | Usage| 91 | |------|------|------| 92 | |Stateful Engine Metrics | Metrics | All available stateful engine metrics per availability zone| 93 | |Stateful Passed Packets | Metrics | Packets passed by stateful rules or default action during the most recent period and time range| 94 | |Stateful Dropped Packets | Metrics | Packets dropped by stateful rules or stateful default action during the most recent period and time range| 95 | |Stateful Rejected Packets | Metrics | Packets rejected due to Reject stateful rule action during the most recent period and time range| 96 | |TLS Inspection | Metrics | Displays all available SSL/TLS metrics related to [inbound/outbound TLS inspection configurations](https://docs.aws.amazon.com/network-firewall/latest/developerguide/tls-inspection-configurations.html)| 97 | |Stream Exception Policy Packets| Metrics | Displays count of packets related to long-lived established connections that the receiving firewall appliance is seeing mid-conversation. The chosen [stream exception policy](https://docs.aws.amazon.com/network-firewall/latest/developerguide/stream-exception-policy.html) action determines how the firewall handles these midstream packets.| 98 | |Top Long-Lived TCP Flows - Age > 350s | Contributor Insights | Count of TCP flows active for longer than the Gateway Load Balancer's 350 second idle timeout. This can be used to identify long-lived TCP flows that may be affected by the chosen stream exception policy action.| 99 | |Top TCP Flows - SYN Without SYN-ACK | Contributor Insights | Count of flows failing the TCP 3-way handshake. During initial firewall configuration, this might be a sign of misconfigured routing in the return path. Failing to received a SYN-ACK from the server can also be due to Network ACL, security group, or on-premises firewall rules.| 100 | 101 | ### Top Talkers 102 | 103 | |Widget Name | Type | Usage| 104 | |------|------|------| 105 | |Top Source IP by Packets | Contributor Insights | Sum of packets by Source IP| 106 | |Top Source IP by Bytes | Contributor Insights | Sum of bytes by Source IP| 107 | |Top Destination IP by Packets | Contributor Insights | Sum of packets by Destination IP| 108 | |Top Destination IP by Bytes | Contributor Insights | Sum of bytes by Destination IP| 109 | |Top Source and Destination IP by Packets | Contributor Insights | Sum of packets by Source and Destination IP| 110 | |Top Source and Destination IP by Bytes | Contributor Insights | Sum of bytes by Source and Destination IP| 111 | 112 | ### Top Protocols 113 | 114 | |Widget Name | Type | Usage| 115 | |------|------|------| 116 | |Top Protocols | Logs Insights | Count of top protocols| 117 | |Top Application Layer Protocols Detected | Logs Insights | Count of top application layer protocols automatically detected by Suricata| 118 | |Top Source Port| Contributor Insights | Count of top source ports| 119 | |Top Destination Port| Contributor Insights | Count of top destination ports| 120 | |Top TCP Flows| Contributor Insights | Count of TCP flows based on source IP, destination IP, and destination port| 121 | |Top TCP Flows by Packets| Contributor Insights | Sum of packets for TCP flows based on source IP, destination IP, and destination port| 122 | |Top TCP Flows by Bytes| Contributor Insights | Sum of bytes for TCP flows based on source IP, destination IP, and destination port| 123 | |Top TCP Flags| Logs Insights | Top TCP flag combinations from firewall flow logs represented in hexadecimal form. For example, an established and gracefully terminated connection is represented as 1b which equals 27 in decimal. This equates to FIN (1) + SYN (2) + PSH (8) + ACK (16) flag control bits being set. 124 | |Top UDP Flows| Contributor Insights | Count of UDP flows based on source IP, destination IP, and destination port| 125 | |Top UDP Flows by Packets| Contributor Insights | Sum of packets for UDP flows based on source IP, destination IP, and destination port| 126 | |Top UDP Flows by Bytes| Contributor Insights | Sum of bytes for UDP flows based on source IP, destination IP, and destination port| 127 | |Top ICMP Flows| Contributor Insights | Count of ICMP flows based on source and destination IP| 128 | 129 | ### Alert Log Analysis 130 | 131 | #### Rule Summary 132 | 133 | |Widget Name | Type | Usage| 134 | |------|------|------| 135 | |Top Drop/Reject Rules | Logs Insights | Count of drop/reject rule action matches by signature ID, message, and protocol| 136 | |Top Alert Rules | Logs Insights | Count of alert rule action matches by signature ID, message, and protocol. Note that alert action rule matches do not also equal pass.| 137 | |Recent Alert Log Events| Logs Insights | Recent alert log events by timestamp. A single alert log row can be expanded to view the entire log message.| 138 | |Top Blocked Source IPs | Logs Insights | Count of source IPs where the rule action is blocked.| 139 | |Top Blocked Destination IPs | Logs Insights | Count of Destination IPs where the rule action was blocked.| 140 | |Top Blocked Destination Ports | Logs Insights | Count of destination ports where the rule action was blocked.| 141 | |Top Blocked Remote Access Ports - Telnet, SSH, RDP | Contributor Insights | Count of flows to destination port 22, 23, or 3389 where the rule action was blocked.| 142 | |Top blocked TCP Flows | Contributor Insights | Count of TCP flows where the rule action is blocked.| 143 | |Top blocked UDP Flows | Contributor Insights | Count of UDP flows where the rule action is blocked.| 144 | 145 | #### HTTP & TLS 146 | 147 | |Widget Name | Type | Usage| 148 | |------|------|------| 149 | |Top HTTP Host Header | Contributor Insights | Count of top HTTP hostnames where rule action is not blocked. Using strict order, you can insert alert rules above pass rules in order to generate HTTP alert log events. You can use this information to help identify hostnames to include in domain list rule groups or custom Suricata rules.| 150 | |Top Blocked HTTP Host Header| Contributor Insights | Count of hostnames for blocked HTTP requests.| 151 | |Top HTTP URI Paths | Contributor Insights | Count of URI paths seen in HTTP requests.| 152 | |Top HTTP User-Agents | Contributor Insights | Count of user-agents seen in HTTP requests.| 153 | |Top TLS SNI | Contributor Insights | Count of top server names where rule action is not blocked. Using strict order, you can insert alert rules above pass rules in order to generate TLS alert log events. You can use this information to help identify server names to include in domain list rule groups or custom Suricata rules.| 154 | |Top Blocked SNI| Contributor Insights | Count of SNI for blocked TLS requests.| 155 | |Top PrivateLink Endpoint Candidates (S3, DynamoDB, & Backup) | Logs Insights | Highlights HTTP/TLS calls to high throughput AWS services such as S3. Implementing a Gateway or Interface endpoint for these services may be more cost effective than routing this traffic through AWS Network Firewall.| 156 | ---- 157 | 158 | ## Resources 159 | 160 | For more information about developing applications using AWS CloudFormation, see the [AWS CloudFormation User Guide](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html). 161 | 162 | 163 | -------------------------------------------------------------------------------- /distributed_architecture/README.md: -------------------------------------------------------------------------------- 1 | # Distributed Architecture 2 | 3 | **Template File:** [anfw-distributed-2az-template.yaml](anfw-distributed-2az-template.yaml) 4 | 5 | For the distributed deployment model, we deploy AWS Network Firewall into each VPC which requires protection. Each VPC is protected individually and blast radius is reduced through VPC isolation. Each VPC does not require connectivity to any other VPC or AWS Transit Gateway. Each AWS Network Firewall can have its own firewall policy or share a policy through common rule groups (reusable collections of rules) across multiple firewalls. This allows each AWS Network Firewall to be managed independently, which reduces the possibility of misconfiguration and limits the scope of impact. 6 | 7 | ![anfw-distributed-model-2az](../images/anfw-distributed-model-2az.jpg) 8 | *Figure 1: Multi AZ Distributed Architecture* 9 | 10 | [Distributed multi AZ deployment template](anfw-distributed-2az-template.yaml), as described in Figure 1, creates: 11 | 12 | * Spoke VPC. Spoke VPC consists of three subnets in each AZ: 13 | * Private subnet for application/client. 14 | * Public subnet for NAT Gateway. 15 | * Firewall subnet for firewall endpoint. 16 | 17 | For return traffic, Ingress Route Table is associated with Internet Gateway to ensure the traffic is forwarded to firewall endpoint within the same AZ. This route tables has public subnet route pointing towards firewall endpoint in the same AZ. 18 | 19 | This is a Multi AZ configuration. You can also refer to [Single AZ Deployment](single_az_deployment) for testing/poc purpose only. 20 | 21 | For more details, refer to [Blog: Deployment models for AWS Network Firewall](https://aws.amazon.com/blogs/networking-and-content-delivery/deployment-models-for-aws-network-firewall/). 22 | -------------------------------------------------------------------------------- /distributed_architecture/anfw-distributed-2az-template.yaml: -------------------------------------------------------------------------------- 1 | #Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 5 | #FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 6 | #COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 7 | #IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 8 | #CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | 10 | AWSTemplateFormatVersion: "2010-09-09" 11 | Description: "AWS Network Firewall Demo using distributed model." 12 | 13 | Metadata: 14 | "AWS::CloudFormation::Interface": 15 | ParameterGroups: 16 | - Label: 17 | default: "VPC Parameters" 18 | Parameters: 19 | - AvailabilityZone1Selection 20 | - AvailabilityZone2Selection 21 | - Label: 22 | default: "EC2 Parameters" 23 | Parameters: 24 | - LatestAmiId 25 | 26 | Parameters: 27 | AvailabilityZone1Selection: 28 | Description: Availability Zone 1 29 | Type: AWS::EC2::AvailabilityZone::Name 30 | Default: us-east-1a 31 | 32 | AvailabilityZone2Selection: 33 | Description: Availability Zone 2 34 | Type: AWS::EC2::AvailabilityZone::Name 35 | Default: us-east-1b 36 | 37 | LatestAmiId: 38 | Description: Latest EC2 AMI from Systems Manager Parameter Store 39 | Type: 'AWS::SSM::Parameter::Value' 40 | Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' 41 | 42 | Resources: 43 | 44 | # VPC: 45 | SpokeVpcA: 46 | Type: AWS::EC2::VPC 47 | Properties: 48 | CidrBlock: "10.1.0.0/16" 49 | EnableDnsSupport: "true" 50 | EnableDnsHostnames: "true" 51 | InstanceTenancy: default 52 | Tags: 53 | - Key: Name 54 | Value: !Sub "${AWS::StackName}-spokevpca" 55 | 56 | # Internet Gateway: 57 | InternetGateway: 58 | Type: AWS::EC2::InternetGateway 59 | Properties: 60 | Tags: 61 | - Key: Name 62 | Value: !Sub "${AWS::StackName}-spokevpca-igw" 63 | 64 | AttachInternetGateway: 65 | Type: AWS::EC2::VPCGatewayAttachment 66 | Properties: 67 | VpcId: 68 | !Ref SpokeVpcA 69 | InternetGatewayId: 70 | !Ref InternetGateway 71 | 72 | # NAT Gateway: 73 | NatGw1Eip: 74 | Type: AWS::EC2::EIP 75 | Properties: 76 | Domain: vpc 77 | 78 | NatGw2Eip: 79 | Type: AWS::EC2::EIP 80 | Properties: 81 | Domain: vpc 82 | 83 | NatGw1: 84 | Type: AWS::EC2::NatGateway 85 | DependsOn: 86 | - NatGw1Eip 87 | - PublicSubnet1 88 | Properties: 89 | AllocationId: !GetAtt 90 | - NatGw1Eip 91 | - AllocationId 92 | SubnetId: !Ref PublicSubnet1 93 | Tags: 94 | - Key: Name 95 | Value: !Sub "${AWS::StackName}-spokevpca-natgw-1" 96 | 97 | NatGw2: 98 | Type: AWS::EC2::NatGateway 99 | DependsOn: 100 | - NatGw2Eip 101 | - PublicSubnet2 102 | Properties: 103 | AllocationId: !GetAtt 104 | - NatGw2Eip 105 | - AllocationId 106 | SubnetId: !Ref PublicSubnet2 107 | Tags: 108 | - Key: Name 109 | Value: !Sub "${AWS::StackName}-spokevpca-natgw-2" 110 | 111 | # Private Subnets for Test Instances: 112 | PrivateSubnet1: 113 | Type: AWS::EC2::Subnet 114 | Properties: 115 | VpcId: 116 | Ref: SpokeVpcA 117 | CidrBlock: "10.1.0.0/24" 118 | AvailabilityZone: 119 | Ref: AvailabilityZone1Selection 120 | MapPublicIpOnLaunch: false 121 | Tags: 122 | - Key: Name 123 | Value: !Sub "${AWS::StackName}-private-subnet-1" 124 | 125 | PrivateSubnet2: 126 | Type: AWS::EC2::Subnet 127 | Properties: 128 | VpcId: 129 | Ref: SpokeVpcA 130 | CidrBlock: "10.1.2.0/24" 131 | AvailabilityZone: 132 | Ref: AvailabilityZone2Selection 133 | MapPublicIpOnLaunch: false 134 | Tags: 135 | - Key: Name 136 | Value: !Sub "${AWS::StackName}-private-subnet-2" 137 | 138 | # Public Subnets for NAT GWs: 139 | PublicSubnet1: 140 | Type: AWS::EC2::Subnet 141 | Properties: 142 | VpcId: 143 | Ref: SpokeVpcA 144 | CidrBlock: "10.1.1.0/24" 145 | AvailabilityZone: 146 | Ref: AvailabilityZone1Selection 147 | MapPublicIpOnLaunch: true 148 | Tags: 149 | - Key: Name 150 | Value: !Sub "${AWS::StackName}-public-subnet-1" 151 | 152 | PublicSubnet2: 153 | Type: AWS::EC2::Subnet 154 | Properties: 155 | VpcId: 156 | Ref: SpokeVpcA 157 | CidrBlock: "10.1.3.0/24" 158 | AvailabilityZone: 159 | Ref: AvailabilityZone2Selection 160 | MapPublicIpOnLaunch: true 161 | Tags: 162 | - Key: Name 163 | Value: !Sub "${AWS::StackName}-public-subnet-2" 164 | 165 | # Firewall Subnets for firewall endpoints: 166 | FirewallSubnet1: 167 | Type: AWS::EC2::Subnet 168 | Properties: 169 | VpcId: 170 | Ref: SpokeVpcA 171 | CidrBlock: "10.1.16.0/28" 172 | AvailabilityZone: 173 | Ref: AvailabilityZone1Selection 174 | MapPublicIpOnLaunch: false 175 | Tags: 176 | - Key: Name 177 | Value: !Sub "${AWS::StackName}-firewall-subnet-1" 178 | 179 | FirewallSubnet2: 180 | Type: AWS::EC2::Subnet 181 | Properties: 182 | VpcId: 183 | Ref: SpokeVpcA 184 | CidrBlock: "10.1.16.16/28" 185 | AvailabilityZone: 186 | Ref: AvailabilityZone2Selection 187 | MapPublicIpOnLaunch: false 188 | Tags: 189 | - Key: Name 190 | Value: !Sub "${AWS::StackName}-firewall-subnet-2" 191 | 192 | # AWS PrivateLink interface endpoint for services: 193 | SpokeVpcAEndpointSecurityGroup: 194 | Type: AWS::EC2::SecurityGroup 195 | Properties: 196 | GroupDescription: Allow instances to get to SSM Systems Manager 197 | VpcId: !Ref SpokeVpcA 198 | SecurityGroupIngress: 199 | - IpProtocol: tcp 200 | FromPort: 443 201 | ToPort: 443 202 | CidrIp: 10.1.0.0/16 203 | Tags: 204 | - Key: Name 205 | Value: !Sub "${AWS::StackName}-vpce-sg-1" 206 | 207 | SpokeVpcASSMEndpoint: 208 | Type: AWS::EC2::VPCEndpoint 209 | Properties: 210 | PrivateDnsEnabled: true 211 | SecurityGroupIds: 212 | - !Ref SpokeVpcAEndpointSecurityGroup 213 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm" 214 | SubnetIds: 215 | - !Ref PublicSubnet1 216 | - !Ref PublicSubnet2 217 | VpcEndpointType: Interface 218 | VpcId: !Ref SpokeVpcA 219 | 220 | SpokeVpcAEC2MessagesEndpoint: 221 | Type: AWS::EC2::VPCEndpoint 222 | Properties: 223 | PrivateDnsEnabled: true 224 | SecurityGroupIds: 225 | - !Ref SpokeVpcAEndpointSecurityGroup 226 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2messages" 227 | SubnetIds: 228 | - !Ref PublicSubnet1 229 | - !Ref PublicSubnet2 230 | VpcEndpointType: Interface 231 | VpcId: !Ref SpokeVpcA 232 | 233 | SpokeVpcASSMMessagesEndpoint: 234 | Type: AWS::EC2::VPCEndpoint 235 | Properties: 236 | PrivateDnsEnabled: true 237 | SecurityGroupIds: 238 | - !Ref SpokeVpcAEndpointSecurityGroup 239 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages" 240 | SubnetIds: 241 | - !Ref PublicSubnet1 242 | - !Ref PublicSubnet2 243 | VpcEndpointType: Interface 244 | VpcId: !Ref SpokeVpcA 245 | 246 | # SSM Role: 247 | SubnetRole: 248 | Type: AWS::IAM::Role 249 | Properties: 250 | RoleName: !Sub "${AWS::StackName}-subnet-role" 251 | Path: "/" 252 | ManagedPolicyArns: 253 | - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM" 254 | AssumeRolePolicyDocument: 255 | Version: "2012-10-17" 256 | Statement: 257 | - Effect: Allow 258 | Principal: 259 | Service: 260 | - ec2.amazonaws.com 261 | Action: 262 | - sts:AssumeRole 263 | 264 | SubnetInstanceProfile: 265 | Type: AWS::IAM::InstanceProfile 266 | Properties: 267 | Path: "/" 268 | Roles: 269 | - !Ref SubnetRole 270 | 271 | # Fn::GetAtt for Firewall do not return VPCE Id in ordered format. 272 | # For more details refer to: https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-networkfirewall/issues/15 273 | # Until the bug is fixed we have to rely on custom resource to retrieve AZ specific VPCE Id. 274 | 275 | # Lambda Role: 276 | LambdaExecutionRole: 277 | Type: AWS::IAM::Role 278 | Properties: 279 | RoleName: !Sub "${AWS::StackName}-lambda-role" 280 | AssumeRolePolicyDocument: 281 | Version: "2012-10-17" 282 | Statement: 283 | - Effect: Allow 284 | Principal: 285 | Service: 286 | - lambda.amazonaws.com 287 | Action: 288 | - sts:AssumeRole 289 | Path: / 290 | Policies: 291 | - PolicyName: root 292 | PolicyDocument: 293 | Version: "2012-10-17" 294 | Statement: 295 | - Effect: Allow 296 | Action: 297 | - logs:CreateLogStream 298 | - logs:PutLogEvents 299 | Resource: !GetAtt RetrieveVpcIdLogGroup.Arn 300 | - Effect: Allow 301 | Action: 302 | - network-firewall:DescribeFirewall 303 | Resource: "*" 304 | 305 | # Retrieve VpceId Lambda Custom Resource: 306 | RetrieveVpcIdLogGroup: 307 | Type: AWS::Logs::LogGroup 308 | Properties: 309 | LogGroupName: !Sub /aws/lambda/${AWS::StackName}-retrieve-vpceid 310 | RetentionInDays: 1 311 | 312 | RetrieveVpceId: 313 | Type: AWS::Lambda::Function 314 | DependsOn: RetrieveVpcIdLogGroup 315 | Properties: 316 | FunctionName: !Sub ${AWS::StackName}-retrieve-vpceid 317 | Handler: "index.handler" 318 | Role: !GetAtt 319 | - LambdaExecutionRole 320 | - Arn 321 | Code: 322 | ZipFile: | 323 | import boto3 324 | import cfnresponse 325 | import json 326 | import logging 327 | 328 | def handler(event, context): 329 | logger = logging.getLogger() 330 | logger.setLevel(logging.INFO) 331 | responseData = {} 332 | responseStatus = cfnresponse.FAILED 333 | logger.info('Received event: {}'.format(json.dumps(event))) 334 | if event["RequestType"] == "Delete": 335 | responseStatus = cfnresponse.SUCCESS 336 | cfnresponse.send(event, context, responseStatus, responseData) 337 | if event["RequestType"] == "Create": 338 | try: 339 | Az1 = event["ResourceProperties"]["Az1"] 340 | Az2 = event["ResourceProperties"]["Az2"] 341 | FwArn = event["ResourceProperties"]["FwArn"] 342 | except Exception as e: 343 | logger.info('AZ retrieval failure: {}'.format(e)) 344 | try: 345 | nfw = boto3.client('network-firewall') 346 | except Exception as e: 347 | logger.info('boto3.client failure: {}'.format(e)) 348 | try: 349 | NfwResponse=nfw.describe_firewall(FirewallArn=FwArn) 350 | VpceId1 = NfwResponse['FirewallStatus']['SyncStates'][Az1]['Attachment']['EndpointId'] 351 | VpceId2 = NfwResponse['FirewallStatus']['SyncStates'][Az2]['Attachment']['EndpointId'] 352 | 353 | except Exception as e: 354 | logger.info('ec2.describe_firewall failure: {}'.format(e)) 355 | 356 | responseData['FwVpceId1'] = VpceId1 357 | responseData['FwVpceId2'] = VpceId2 358 | responseStatus = cfnresponse.SUCCESS 359 | cfnresponse.send(event, context, responseStatus, responseData) 360 | Runtime: python3.7 361 | Timeout: 30 362 | 363 | FirewallVpceIds: 364 | Type: Custom::DescribeVpcEndpoints 365 | Properties: 366 | ServiceToken: !GetAtt RetrieveVpceId.Arn 367 | Az1: !Ref AvailabilityZone1Selection 368 | Az2: !Ref AvailabilityZone2Selection 369 | FwArn: !Ref SpokeVpcAFirewall 370 | 371 | # Testing Security Group: 372 | SubnetSecurityGroup: 373 | Type: AWS::EC2::SecurityGroup 374 | Properties: 375 | GroupDescription: "ICMP acess from 10.0.0.0/8" 376 | GroupName: !Sub "${AWS::StackName}-test-instance-sec-group" 377 | VpcId: !Ref SpokeVpcA 378 | SecurityGroupIngress: 379 | - IpProtocol: icmp 380 | CidrIp: 10.0.0.0/8 381 | FromPort: "-1" 382 | ToPort: "-1" 383 | Tags: 384 | - Key: Name 385 | Value: !Sub "${AWS::StackName}-test-sg-1" 386 | 387 | # Test Instances: 388 | TestInstance1: 389 | Type: 'AWS::EC2::Instance' 390 | Properties: 391 | ImageId: !Ref LatestAmiId 392 | SubnetId: !Ref PrivateSubnet1 393 | InstanceType: t2.micro 394 | SecurityGroupIds: 395 | - !Ref SubnetSecurityGroup 396 | IamInstanceProfile: !Ref SubnetInstanceProfile 397 | Tags: 398 | - Key: Name 399 | Value: !Sub "${AWS::StackName}-test-instance-1" 400 | 401 | TestInstance2: 402 | Type: 'AWS::EC2::Instance' 403 | Properties: 404 | ImageId: !Ref LatestAmiId 405 | SubnetId: !Ref PrivateSubnet2 406 | InstanceType: t2.micro 407 | SecurityGroupIds: 408 | - !Ref SubnetSecurityGroup 409 | IamInstanceProfile: !Ref SubnetInstanceProfile 410 | Tags: 411 | - Key: Name 412 | Value: !Sub "${AWS::StackName}-test-instance-2" 413 | 414 | # AWS Network Firewall: 415 | SpokeVpcAFirewall: 416 | Type: AWS::NetworkFirewall::Firewall 417 | Properties: 418 | FirewallName: !Sub "${AWS::StackName}-firewall" 419 | FirewallPolicyArn: !Ref EgressFirewallPolicy 420 | VpcId: !Ref SpokeVpcA 421 | SubnetMappings: 422 | - SubnetId: !Ref FirewallSubnet1 423 | - SubnetId: !Ref FirewallSubnet2 424 | Tags: 425 | - Key: Name 426 | Value: !Sub "${AWS::StackName}-firewall" 427 | 428 | ICMPAlertStatefulRuleGroup: 429 | Type: 'AWS::NetworkFirewall::RuleGroup' 430 | Properties: 431 | RuleGroupName: !Sub "${AWS::StackName}-icmp-alert" 432 | Type: STATEFUL 433 | Capacity: 100 434 | RuleGroup: 435 | RulesSource: 436 | StatefulRules: 437 | - Action: ALERT 438 | Header: 439 | Direction: ANY 440 | Protocol: ICMP 441 | Destination: ANY 442 | Source: ANY 443 | DestinationPort: ANY 444 | SourcePort: ANY 445 | RuleOptions: 446 | - Keyword: "sid:1" 447 | Tags: 448 | - Key: Name 449 | Value: !Sub "${AWS::StackName}-icmp-alert" 450 | 451 | DomainAllowStatefulRuleGroup: 452 | Type: 'AWS::NetworkFirewall::RuleGroup' 453 | Properties: 454 | RuleGroupName: !Sub "${AWS::StackName}-domain-allow" 455 | Type: STATEFUL 456 | Capacity: 100 457 | RuleGroup: 458 | RuleVariables: 459 | IPSets: 460 | HOME_NET: 461 | Definition: 462 | - "10.0.0.0/8" 463 | RulesSource: 464 | RulesSourceList: 465 | TargetTypes: 466 | - HTTP_HOST 467 | - TLS_SNI 468 | Targets: 469 | - ".amazon.com" 470 | GeneratedRulesType: "ALLOWLIST" 471 | Tags: 472 | - Key: Name 473 | Value: !Sub "${AWS::StackName}-domain-allow" 474 | 475 | EgressFirewallPolicy: 476 | Type: AWS::NetworkFirewall::FirewallPolicy 477 | Properties: 478 | FirewallPolicyName: !Sub "${AWS::StackName}-firewall-policy" 479 | FirewallPolicy: 480 | StatelessDefaultActions: 481 | - 'aws:forward_to_sfe' 482 | StatelessFragmentDefaultActions: 483 | - 'aws:forward_to_sfe' 484 | StatefulRuleGroupReferences: 485 | - ResourceArn: !Ref DomainAllowStatefulRuleGroup 486 | - ResourceArn: !Ref ICMPAlertStatefulRuleGroup 487 | 488 | Tags: 489 | - Key: Name 490 | Value: !Sub "${AWS::StackName}-firewall-policy" 491 | 492 | SpokeVpcAFirewallLogFlowGroup: 493 | Type: AWS::Logs::LogGroup 494 | Properties: 495 | LogGroupName: !Sub "/${AWS::StackName}/anfw/flow" 496 | 497 | SpokeVpcAFirewallLogAlertGroup: 498 | Type: AWS::Logs::LogGroup 499 | Properties: 500 | LogGroupName: !Sub "/${AWS::StackName}/anfw/alert" 501 | 502 | SpokeVpcAFirewallLog: 503 | Type: AWS::NetworkFirewall::LoggingConfiguration 504 | Properties: 505 | FirewallArn: !Ref SpokeVpcAFirewall 506 | LoggingConfiguration: 507 | LogDestinationConfigs: 508 | - LogType: FLOW 509 | LogDestinationType: CloudWatchLogs 510 | LogDestination: 511 | logGroup: !Sub "/${AWS::StackName}/anfw/flow" 512 | - LogType: ALERT 513 | LogDestinationType: CloudWatchLogs 514 | LogDestination: 515 | logGroup: !Sub "/${AWS::StackName}/anfw/alert" 516 | 517 | # Private Route Tables: 518 | PrivateRtb1: 519 | Type: AWS::EC2::RouteTable 520 | Properties: 521 | VpcId: !Ref SpokeVpcA 522 | Tags: 523 | - Key: Name 524 | Value: !Sub "${AWS::StackName}-private-route-table-1" 525 | 526 | PrivateRtb1Association: 527 | Type: AWS::EC2::SubnetRouteTableAssociation 528 | DependsOn: PrivateSubnet1 529 | Properties: 530 | RouteTableId: !Ref PrivateRtb1 531 | SubnetId: !Ref PrivateSubnet1 532 | 533 | PrivateRtb1DefaultRoute: 534 | Type: AWS::EC2::Route 535 | DependsOn: NatGw1 536 | Properties: 537 | DestinationCidrBlock: "0.0.0.0/0" 538 | NatGatewayId: !Ref NatGw1 539 | RouteTableId: !Ref PrivateRtb1 540 | 541 | PrivateRtb2: 542 | Type: AWS::EC2::RouteTable 543 | Properties: 544 | VpcId: !Ref SpokeVpcA 545 | Tags: 546 | - Key: Name 547 | Value: !Sub "${AWS::StackName}-private-route-table-2" 548 | 549 | PrivateRtb2Association: 550 | Type: AWS::EC2::SubnetRouteTableAssociation 551 | DependsOn: PrivateSubnet2 552 | Properties: 553 | RouteTableId: !Ref PrivateRtb2 554 | SubnetId: !Ref PrivateSubnet2 555 | 556 | PrivateRtb2DefaultRoute: 557 | Type: AWS::EC2::Route 558 | DependsOn: NatGw2 559 | Properties: 560 | DestinationCidrBlock: "0.0.0.0/0" 561 | NatGatewayId: !Ref NatGw2 562 | RouteTableId: !Ref PrivateRtb2 563 | 564 | # Public Route Tables: 565 | PublicRtb1: 566 | Type: AWS::EC2::RouteTable 567 | Properties: 568 | VpcId: !Ref SpokeVpcA 569 | Tags: 570 | - Key: Name 571 | Value: !Sub "${AWS::StackName}-public-route-table-1" 572 | 573 | PublicRtb1Association: 574 | Type: AWS::EC2::SubnetRouteTableAssociation 575 | DependsOn: PublicSubnet1 576 | Properties: 577 | RouteTableId: !Ref PublicRtb1 578 | SubnetId: !Ref PublicSubnet1 579 | 580 | PublicRtb1DefaultRoute: 581 | Type: AWS::EC2::Route 582 | DependsOn: SpokeVpcAFirewall 583 | Properties: 584 | DestinationCidrBlock: "0.0.0.0/0" 585 | VpcEndpointId: !GetAtt FirewallVpceIds.FwVpceId1 586 | RouteTableId: !Ref PublicRtb1 587 | 588 | PublicRtb2: 589 | Type: AWS::EC2::RouteTable 590 | Properties: 591 | VpcId: !Ref SpokeVpcA 592 | Tags: 593 | - Key: Name 594 | Value: !Sub "${AWS::StackName}-public-route-table-2" 595 | 596 | PublicRtb2Association: 597 | Type: AWS::EC2::SubnetRouteTableAssociation 598 | DependsOn: PublicSubnet2 599 | Properties: 600 | RouteTableId: !Ref PublicRtb2 601 | SubnetId: !Ref PublicSubnet2 602 | 603 | PublicRtb2DefaultRoute: 604 | Type: AWS::EC2::Route 605 | DependsOn: SpokeVpcAFirewall 606 | Properties: 607 | DestinationCidrBlock: "0.0.0.0/0" 608 | VpcEndpointId: !GetAtt FirewallVpceIds.FwVpceId2 609 | RouteTableId: !Ref PublicRtb2 610 | 611 | # Firewall Route Tables: 612 | FirewallRtb1: 613 | Type: AWS::EC2::RouteTable 614 | Properties: 615 | VpcId: !Ref SpokeVpcA 616 | Tags: 617 | - Key: Name 618 | Value: !Sub "${AWS::StackName}-firewall-route-table-1" 619 | 620 | FirewallRtb1Association: 621 | Type: AWS::EC2::SubnetRouteTableAssociation 622 | DependsOn: FirewallSubnet1 623 | Properties: 624 | RouteTableId: !Ref FirewallRtb1 625 | SubnetId: !Ref FirewallSubnet1 626 | 627 | FirewallRtb1DefaultRoute: 628 | Type: AWS::EC2::Route 629 | DependsOn: InternetGateway 630 | Properties: 631 | DestinationCidrBlock: "0.0.0.0/0" 632 | GatewayId: !Ref InternetGateway 633 | RouteTableId: !Ref FirewallRtb1 634 | 635 | FirewallRtb2: 636 | Type: AWS::EC2::RouteTable 637 | Properties: 638 | VpcId: !Ref SpokeVpcA 639 | Tags: 640 | - Key: Name 641 | Value: !Sub "${AWS::StackName}-firewall-route-table-2" 642 | 643 | FirewallRtb2Association: 644 | Type: AWS::EC2::SubnetRouteTableAssociation 645 | DependsOn: FirewallSubnet2 646 | Properties: 647 | RouteTableId: !Ref FirewallRtb2 648 | SubnetId: !Ref FirewallSubnet2 649 | 650 | FirewallRtb2DefaultRoute: 651 | Type: AWS::EC2::Route 652 | DependsOn: InternetGateway 653 | Properties: 654 | DestinationCidrBlock: "0.0.0.0/0" 655 | GatewayId: !Ref InternetGateway 656 | RouteTableId: !Ref FirewallRtb2 657 | 658 | # Ingress Route Table: 659 | IngressRtb: 660 | Type: AWS::EC2::RouteTable 661 | Properties: 662 | VpcId: !Ref SpokeVpcA 663 | Tags: 664 | - Key: Name 665 | Value: !Sub "${AWS::StackName}-ingress-route-table" 666 | 667 | IngressRtbAssociation: 668 | Type: AWS::EC2::GatewayRouteTableAssociation 669 | DependsOn: InternetGateway 670 | Properties: 671 | RouteTableId: !Ref IngressRtb 672 | GatewayId: !Ref InternetGateway 673 | 674 | IngressRtbPublicSubnet1Route: 675 | Type: AWS::EC2::Route 676 | DependsOn: SpokeVpcAFirewall 677 | Properties: 678 | DestinationCidrBlock: "10.1.1.0/24" 679 | VpcEndpointId: !GetAtt FirewallVpceIds.FwVpceId1 680 | RouteTableId: !Ref IngressRtb 681 | 682 | IngressRtbPublicSubnet2Route: 683 | Type: AWS::EC2::Route 684 | DependsOn: SpokeVpcAFirewall 685 | Properties: 686 | DestinationCidrBlock: "10.1.3.0/24" 687 | VpcEndpointId: !GetAtt FirewallVpceIds.FwVpceId2 688 | RouteTableId: !Ref IngressRtb -------------------------------------------------------------------------------- /distributed_architecture/single_az_deployment/README.md: -------------------------------------------------------------------------------- 1 | # Distributed Architecture - Single AZ Deployment: 2 | 3 | **Template File:** [anfw-distributed-1az-template.yaml](anfw-distributed-1az-template.yaml) 4 | 5 | For the distributed deployment model, we deploy AWS Network Firewall into each VPC which requires protection. Each VPC is protected individually and blast radius is reduced through VPC isolation. Each VPC does not require connectivity to any other VPC or AWS Transit Gateway. Each AWS Network Firewall can have its own firewall policy or share a policy through common rule groups (reusable collections of rules) across multiple firewalls. This allows each AWS Network Firewall to be managed independently, which reduces the possibility of misconfiguration and limits the scope of impact. 6 | 7 | ![anfw-distributed-model-1az](../../images/anfw-distributed-model-1az.png) 8 | *Figure 1: Single AZ Distrbuted Architecture* 9 | 10 | [Distributed single AZ deployment template](anfw-distributed-1az-template.yaml), as described in Figure 1, creates: 11 | 12 | * Spoke VPC. Spoke VPC consists of three subnets in each AZ:. 13 | * Public subnet for client with public ip. 14 | * Firewall subnet for firewall endpoint. 15 | 16 | For return traffic, Ingress Route Table is associated with Internet Gateway to ensure the traffic is forwarded to firewall endpoint within the same AZ. This route tables has public subnet route pointing towards firewall endpoint in the same AZ. 17 | 18 | This is a single AZ configuration. Use it for testing/POC. It is recommended to deploy resources in atleast 2 AZs. For multi AZ deployment refer to [Multi AZ Deployment](../README.md). 19 | 20 | For more details, refer to [Blog: Deployment models for AWS Network Firewall](https://aws.amazon.com/blogs/networking-and-content-delivery/deployment-models-for-aws-network-firewall/). 21 | -------------------------------------------------------------------------------- /distributed_architecture/single_az_deployment/anfw-distributed-1az-template.yaml: -------------------------------------------------------------------------------- 1 | #Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4 | #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 5 | #FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 6 | #COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 7 | #IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 8 | #CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | 10 | AWSTemplateFormatVersion: "2010-09-09" 11 | Description: "AWS Network Firewall Demo using distributed model." 12 | 13 | Metadata: 14 | "AWS::CloudFormation::Interface": 15 | ParameterGroups: 16 | - Label: 17 | default: "VPC Parameters" 18 | Parameters: 19 | - AvailabilityZoneSelection 20 | - Label: 21 | default: "EC2 Parameters" 22 | Parameters: 23 | - LatestAmiId 24 | 25 | Parameters: 26 | AvailabilityZoneSelection: 27 | Description: Availability Zone 28 | Type: AWS::EC2::AvailabilityZone::Name 29 | Default: us-east-1a 30 | 31 | LatestAmiId: 32 | Description: Latest EC2 AMI from Systems Manager Parameter Store 33 | Type: 'AWS::SSM::Parameter::Value' 34 | Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2' 35 | 36 | Resources: 37 | 38 | 39 | VPCB: 40 | Type: AWS::EC2::VPC 41 | Properties: 42 | CidrBlock: "10.2.0.0/16" 43 | EnableDnsSupport: "true" 44 | EnableDnsHostnames: "true" 45 | InstanceTenancy: default 46 | Tags: 47 | - Key: Name 48 | Value: !Sub "${AWS::StackName}-vpc" 49 | 50 | SubnetBWorkload: 51 | Type: AWS::EC2::Subnet 52 | Properties: 53 | VpcId: 54 | Ref: VPCB 55 | CidrBlock: "10.2.1.0/24" 56 | AvailabilityZone: 57 | Ref: AvailabilityZoneSelection 58 | MapPublicIpOnLaunch: true 59 | Tags: 60 | - Key: Name 61 | Value: !Sub "${AWS::StackName}-public-subnet" 62 | 63 | SubnetBTGW: 64 | Type: AWS::EC2::Subnet 65 | Properties: 66 | VpcId: 67 | Ref: VPCB 68 | CidrBlock: "10.2.0.0/28" 69 | AvailabilityZone: 70 | Ref: AvailabilityZoneSelection 71 | MapPublicIpOnLaunch: false 72 | Tags: 73 | - Key: Name 74 | Value: !Sub "${AWS::StackName}-private-subnet" 75 | 76 | SubnetBFirewall: 77 | Type: AWS::EC2::Subnet 78 | Properties: 79 | VpcId: 80 | Ref: VPCB 81 | CidrBlock: "10.2.16.0/28" 82 | AvailabilityZone: 83 | Ref: AvailabilityZoneSelection 84 | MapPublicIpOnLaunch: false 85 | Tags: 86 | - Key: Name 87 | Value: !Sub "${AWS::StackName}-firewall-subnet" 88 | 89 | InternetGatewayVPCB: 90 | Type: AWS::EC2::InternetGateway 91 | Properties: 92 | Tags: 93 | - Key: Name 94 | Value: !Sub "vpcb-igw-${AWS::StackName}" 95 | 96 | AttachGatewayVPCB: 97 | Type: AWS::EC2::VPCGatewayAttachment 98 | Properties: 99 | VpcId: 100 | !Ref VPCB 101 | InternetGatewayId: 102 | !Ref InternetGatewayVPCB 103 | 104 | VPCBEndpointSecurityGroup: 105 | Type: AWS::EC2::SecurityGroup 106 | Properties: 107 | GroupDescription: Allow instances to get to SSM Systems Manager 108 | VpcId: !Ref VPCB 109 | SecurityGroupIngress: 110 | - IpProtocol: tcp 111 | FromPort: 443 112 | ToPort: 443 113 | CidrIp: 10.2.0.0/16 114 | VPCBSSMEndpoint: 115 | Type: AWS::EC2::VPCEndpoint 116 | Properties: 117 | PrivateDnsEnabled: true 118 | SecurityGroupIds: 119 | - !Ref VPCBEndpointSecurityGroup 120 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm" 121 | SubnetIds: 122 | - !Ref SubnetBWorkload 123 | VpcEndpointType: Interface 124 | VpcId: !Ref VPCB 125 | 126 | VPCBEC2MessagesEndpoint: 127 | Type: AWS::EC2::VPCEndpoint 128 | Properties: 129 | PrivateDnsEnabled: true 130 | SecurityGroupIds: 131 | - !Ref VPCBEndpointSecurityGroup 132 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2messages" 133 | SubnetIds: 134 | - !Ref SubnetBWorkload 135 | VpcEndpointType: Interface 136 | VpcId: !Ref VPCB 137 | 138 | VPCBSSMMessagesEndpoint: 139 | Type: AWS::EC2::VPCEndpoint 140 | Properties: 141 | PrivateDnsEnabled: true 142 | SecurityGroupIds: 143 | - !Ref VPCBEndpointSecurityGroup 144 | ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages" 145 | SubnetIds: 146 | - !Ref SubnetBWorkload 147 | VpcEndpointType: Interface 148 | VpcId: !Ref VPCB 149 | 150 | SubnetBRole: 151 | Type: AWS::IAM::Role 152 | Properties: 153 | RoleName: !Sub "subnet-b-role-${AWS::StackName}" 154 | Path: "/" 155 | ManagedPolicyArns: 156 | - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM" 157 | AssumeRolePolicyDocument: 158 | Version: "2012-10-17" 159 | Statement: 160 | - Effect: Allow 161 | Principal: 162 | Service: 163 | - ec2.amazonaws.com 164 | Action: 165 | - sts:AssumeRole 166 | 167 | SubnetBInstanceProfile: 168 | Type: AWS::IAM::InstanceProfile 169 | Properties: 170 | Path: "/" 171 | Roles: 172 | - !Ref SubnetBRole 173 | 174 | SubnetBSecGroup: 175 | Type: AWS::EC2::SecurityGroup 176 | Properties: 177 | GroupDescription: "ICMP acess from 10.0.0.0/8" 178 | GroupName: !Sub "${AWS::StackName}-test-instance-sec-group" 179 | VpcId: !Ref VPCB 180 | SecurityGroupIngress: 181 | - IpProtocol: icmp 182 | CidrIp: 10.0.0.0/8 183 | FromPort: "-1" 184 | ToPort: "-1" 185 | 186 | EC2SubnetB: 187 | Type: 'AWS::EC2::Instance' 188 | Properties: 189 | ImageId: !Ref LatestAmiId 190 | SubnetId: !Ref SubnetBWorkload 191 | InstanceType: t2.micro 192 | SecurityGroupIds: 193 | - !Ref SubnetBSecGroup 194 | IamInstanceProfile: !Ref SubnetBInstanceProfile 195 | Tags: 196 | - Key: Name 197 | Value: !Sub "${AWS::StackName}-test-instance" 198 | 199 | VPCBFirewall: 200 | Type: AWS::NetworkFirewall::Firewall 201 | Properties: 202 | FirewallName: !Sub "${AWS::StackName}-firewall" 203 | FirewallPolicyArn: !Ref EgressFirewallPolicy 204 | VpcId: !Ref VPCB 205 | SubnetMappings: 206 | - SubnetId: !Ref SubnetBFirewall 207 | Tags: 208 | - Key: Name 209 | Value: !Sub "${AWS::StackName}-firewall" 210 | 211 | ICMPAlertStatefulRuleGroup: 212 | Type: 'AWS::NetworkFirewall::RuleGroup' 213 | Properties: 214 | RuleGroupName: !Sub "icmp-alert-${AWS::StackName}" 215 | Type: STATEFUL 216 | Capacity: 100 217 | RuleGroup: 218 | RulesSource: 219 | StatefulRules: 220 | - Action: ALERT 221 | Header: 222 | Direction: ANY 223 | Protocol: ICMP 224 | Destination: ANY 225 | Source: ANY 226 | DestinationPort: ANY 227 | SourcePort: ANY 228 | RuleOptions: 229 | - Keyword: "sid:1" 230 | Tags: 231 | - Key: Name 232 | Value: !Sub "icmp-alert-${AWS::StackName}" 233 | 234 | DomainAllowStatefulRuleGroup: 235 | Type: 'AWS::NetworkFirewall::RuleGroup' 236 | Properties: 237 | RuleGroupName: !Sub "domain-allow-${AWS::StackName}" 238 | Type: STATEFUL 239 | Capacity: 100 240 | RuleGroup: 241 | RuleVariables: 242 | IPSets: 243 | HOME_NET: 244 | Definition: 245 | - "10.0.0.0/8" 246 | RulesSource: 247 | RulesSourceList: 248 | TargetTypes: 249 | - HTTP_HOST 250 | - TLS_SNI 251 | Targets: 252 | - ".amazon.com" 253 | GeneratedRulesType: "ALLOWLIST" 254 | Tags: 255 | - Key: Name 256 | Value: !Sub "domain-allow-${AWS::StackName}" 257 | 258 | EgressFirewallPolicy: 259 | Type: AWS::NetworkFirewall::FirewallPolicy 260 | Properties: 261 | FirewallPolicyName: !Sub "${AWS::StackName}-firewall-policy" 262 | FirewallPolicy: 263 | StatelessDefaultActions: 264 | - 'aws:forward_to_sfe' 265 | StatelessFragmentDefaultActions: 266 | - 'aws:forward_to_sfe' 267 | StatefulRuleGroupReferences: 268 | - ResourceArn: !Ref DomainAllowStatefulRuleGroup 269 | - ResourceArn: !Ref ICMPAlertStatefulRuleGroup 270 | 271 | Tags: 272 | - Key: Name 273 | Value: !Sub "${AWS::StackName}-firewall-policy" 274 | 275 | 276 | VPCBFirewallLogFlowGroup: 277 | Type: AWS::Logs::LogGroup 278 | Properties: 279 | LogGroupName: !Sub "/${AWS::StackName}/anfw/flow" 280 | 281 | VPCBFirewallLogAlertGroup: 282 | Type: AWS::Logs::LogGroup 283 | Properties: 284 | LogGroupName: !Sub "/${AWS::StackName}/anfw/alert" 285 | 286 | VPCBFirewallLog: 287 | Type: AWS::NetworkFirewall::LoggingConfiguration 288 | Properties: 289 | FirewallArn: !Ref VPCBFirewall 290 | LoggingConfiguration: 291 | LogDestinationConfigs: 292 | - LogType: FLOW 293 | LogDestinationType: CloudWatchLogs 294 | LogDestination: 295 | logGroup: !Sub "/${AWS::StackName}/anfw/flow" 296 | - LogType: ALERT 297 | LogDestinationType: CloudWatchLogs 298 | LogDestination: 299 | logGroup: !Sub "/${AWS::StackName}/anfw/alert" 300 | 301 | SubnetBWorkloadRouteTable: 302 | Type: AWS::EC2::RouteTable 303 | Properties: 304 | VpcId: !Ref VPCB 305 | Tags: 306 | - Key: Name 307 | Value: !Sub "subnet-b-workload-route-table-${AWS::StackName}" 308 | 309 | SubnetBWorkloadRouteTableAssociation: 310 | Type: AWS::EC2::SubnetRouteTableAssociation 311 | DependsOn: SubnetBWorkload 312 | Properties: 313 | RouteTableId: !Ref SubnetBWorkloadRouteTable 314 | SubnetId: !Ref SubnetBWorkload 315 | 316 | 317 | 318 | SubnetBWorkloadDefaultRoute: 319 | Type: AWS::EC2::Route 320 | DependsOn: VPCBFirewall 321 | Properties: 322 | DestinationCidrBlock: "0.0.0.0/0" 323 | VpcEndpointId: !Select [1, !Split [":", !Select [0, !GetAtt VPCBFirewall.EndpointIds]]] 324 | RouteTableId: !Ref SubnetBWorkloadRouteTable 325 | 326 | SubnetBFirewallRouteTable: 327 | Type: AWS::EC2::RouteTable 328 | Properties: 329 | VpcId: !Ref VPCB 330 | Tags: 331 | - Key: Name 332 | Value: !Sub "subnet-b-firewall-route-table-${AWS::StackName}" 333 | 334 | SubnetBFirewallRouteTableAssociation: 335 | Type: AWS::EC2::SubnetRouteTableAssociation 336 | DependsOn: SubnetBFirewall 337 | Properties: 338 | RouteTableId: !Ref SubnetBFirewallRouteTable 339 | SubnetId: !Ref SubnetBFirewall 340 | 341 | SubnetBFirewallDefaultRoute: 342 | Type: AWS::EC2::Route 343 | DependsOn: InternetGatewayVPCB 344 | Properties: 345 | DestinationCidrBlock: "0.0.0.0/0" 346 | GatewayId: !Ref InternetGatewayVPCB 347 | RouteTableId: !Ref SubnetBFirewallRouteTable 348 | 349 | SubnetBIngressRouteTable: 350 | Type: AWS::EC2::RouteTable 351 | Properties: 352 | VpcId: !Ref VPCB 353 | Tags: 354 | - Key: Name 355 | Value: !Sub "subnet-b-ingress-route-table-${AWS::StackName}" 356 | SubnetBIngressRouteTableAssociation: 357 | Type: AWS::EC2::GatewayRouteTableAssociation 358 | DependsOn: InternetGatewayVPCB 359 | Properties: 360 | RouteTableId: !Ref SubnetBIngressRouteTable 361 | GatewayId: !Ref InternetGatewayVPCB 362 | 363 | SubnetBIngressRoute: 364 | Type: AWS::EC2::Route 365 | DependsOn: VPCBFirewall 366 | Properties: 367 | DestinationCidrBlock: "10.2.1.0/24" 368 | VpcEndpointId: !Select [1, !Split [":", !Select [0, !GetAtt VPCBFirewall.EndpointIds]]] 369 | RouteTableId: !Ref SubnetBIngressRouteTable 370 | 371 | -------------------------------------------------------------------------------- /images/anfw-centralized-model-1az.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-networkfirewall-cfn-templates/e08a0bb4411c3ef638e891bfd866eb8cb3dc982b/images/anfw-centralized-model-1az.jpg -------------------------------------------------------------------------------- /images/anfw-centralized-model-1az.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-networkfirewall-cfn-templates/e08a0bb4411c3ef638e891bfd866eb8cb3dc982b/images/anfw-centralized-model-1az.png -------------------------------------------------------------------------------- /images/anfw-centralized-model-2az.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-networkfirewall-cfn-templates/e08a0bb4411c3ef638e891bfd866eb8cb3dc982b/images/anfw-centralized-model-2az.jpg -------------------------------------------------------------------------------- /images/anfw-distributed-model-1az.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-networkfirewall-cfn-templates/e08a0bb4411c3ef638e891bfd866eb8cb3dc982b/images/anfw-distributed-model-1az.png -------------------------------------------------------------------------------- /images/anfw-distributed-model-2az.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-networkfirewall-cfn-templates/e08a0bb4411c3ef638e891bfd866eb8cb3dc982b/images/anfw-distributed-model-2az.jpg -------------------------------------------------------------------------------- /images/egress-inspection-aws-cloud-wan-base-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-networkfirewall-cfn-templates/e08a0bb4411c3ef638e891bfd866eb8cb3dc982b/images/egress-inspection-aws-cloud-wan-base-architecture.png -------------------------------------------------------------------------------- /outbound_inspection_with_aws_cloud_wan/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: deploy deploy-base-policy update-cloudwan deploy-workloads undeploy undeploy-workloads undeploy-cloudwan 2 | 3 | deploy: deploy-base-policy update-cloudwan deploy-workloads 4 | 5 | deploy-base-policy: 6 | aws cloudformation deploy --stack-name egress-inspection-core-network --template-file core_network_base_policy.yaml --no-fail-on-empty-changeset --region us-east-1 7 | 8 | update-cloudwan: 9 | aws cloudformation update-stack --stack-name egress-inspection-core-network --template-body file://core_network_update_policy_edge_locations.yaml --region us-east-1 10 | aws cloudformation wait stack-update-complete --stack-name egress-inspection-core-network --region us-east-1 11 | 12 | deploy-workloads: CORENETWORK_ID = $(shell aws cloudformation describe-stacks --stack-name "egress-inspection-core-network" --query 'Stacks[0].Outputs[?OutputKey == `CoreNetworkId`].OutputValue' --output text --region us-east-1 ) 13 | deploy-workloads: CORENETWORK_ARN = $(shell aws cloudformation describe-stacks --stack-name "egress-inspection-core-network" --query 'Stacks[0].Outputs[?OutputKey == `CoreNetworkArn`].OutputValue' --output text --region us-east-1 ) 14 | deploy-workloads: 15 | aws cloudformation deploy --stack-name cwan-nvirginia --template-file region1_resources.yaml --parameter-overrides CoreNetworkId="$(CORENETWORK_ID)" CoreNetworkArn="$(CORENETWORK_ARN)" --capabilities CAPABILITY_NAMED_IAM --no-fail-on-empty-changeset --region us-east-1 & \ 16 | aws cloudformation deploy --stack-name cwan-ohio --template-file region2_resources.yaml --parameter-overrides CoreNetworkId="$(CORENETWORK_ID)" CoreNetworkArn="$(CORENETWORK_ARN)" --capabilities CAPABILITY_NAMED_IAM --no-fail-on-empty-changeset --region us-east-2 & \ 17 | aws cloudformation deploy --stack-name cwan-oregon --template-file region3_resources.yaml --parameter-overrides CoreNetworkId="$(CORENETWORK_ID)" CoreNetworkArn="$(CORENETWORK_ARN)" --capabilities CAPABILITY_NAMED_IAM --no-fail-on-empty-changeset --region us-west-2 18 | 19 | 20 | 21 | undeploy: undeploy-workloads undeploy-cloudwan 22 | 23 | undeploy-workloads: 24 | aws cloudformation delete-stack --stack-name cwan-nvirginia --region us-east-1 & \ 25 | aws cloudformation delete-stack --stack-name cwan-ohio --region us-east-2 & \ 26 | aws cloudformation delete-stack --stack-name cwan-oregon --region us-west-2 27 | aws cloudformation wait stack-delete-complete --stack-name cwan-nvirginia --region us-east-1 & \ 28 | aws cloudformation wait stack-delete-complete --stack-name cwan-ohio --region us-east-2 & \ 29 | aws cloudformation wait stack-delete-complete --stack-name cwan-oregon --region us-west-2 30 | 31 | undeploy-cloudwan: 32 | aws cloudformation delete-stack --stack-name egress-inspection-core-network --region us-east-1 33 | aws cloudformation wait stack-delete-complete --stack-name egress-inspection-core-network --region us-east-1 -------------------------------------------------------------------------------- /outbound_inspection_with_aws_cloud_wan/README.md: -------------------------------------------------------------------------------- 1 | ## Prerequisites 2 | 3 | An AWS account with an IAM user with the appropriate permissions 4 | 5 | ## Description 6 | 7 | The repository contains AWS CloudFormation sample code for the following workshop: 8 | 9 | * [Egress Inspection with AWS Cloud WAN and AWS Network Firewall](https://catalog.us-east-1.prod.workshops.aws/workshops/547dc923-8c8f-45b2-a772-f1c233e6864c/en-US) 10 | 11 | ![Base Architecture](../images/egress-inspection-aws-cloud-wan-base-architecture.png) 12 | 13 | * To run the workshop in your environment, visit [workshop](https://catalog.us-east-1.prod.workshops.aws/workshops/547dc923-8c8f-45b2-a772-f1c233e6864c/en-US) guide. 14 | 15 | ## Usage 16 | * Clone the repository. 17 | * Change the directory to (cd) to `/path_to/aws-networkfirewall-cfn-templates/outbound_inspection_with_aws_cloud_wan`. 18 | * (Optional) Edit the VPC and subnet CIDRs in the following region specific resource files if you want to test with other values: 19 | * region1_resources.yaml 20 | * region2_resources.yaml 21 | * region3_resources.yaml 22 | * (Optional). The repository uses us-east-1, us-east-2 and us-west-2 as the deployment regions. If you want to use other regions: 23 | * In the `Makefile`, change --region and --stack-name to to the desired values. 24 | * In the `core_network_base_policy.yaml` and `core_network_update_policy_edge_locations.yaml`, change the edge-locations to the desired values. 25 | * You will find two files creating Core Network policy documents. 26 | * `core_network_base_policy.yaml` is used to create with two edge location first. This is to mimic randomly deterministic behavior. 27 | * `core_network_update_policy_edge_locations.yaml` contains the final format for the policy document with all three edge locaiton. 28 | * Deploy the resources using `make deploy`. 29 | * Remember to clean up resoures once you are done by using `make undeploy`. 30 | * When cleaing up resources, CloudWatch LogGroups created by AWS CloudFormation might not get deleted and you will have to manually detele it. For more details refer to [Stack deletion deletes log group but re-creates it on lambda invocation](https://repost.aws/questions/QUzZH7Nz2DT_-PagH_godYYA/stack-deletion-deletes-log-group-but-re-creates-it-on-lambda-invocation) 31 | 32 | **Note:** 33 | Keep the following in mind when testing this environment from a cost perspective - for production environments, we recommend the use of at least 2 AZs for high-availability: 34 | * AWS Cloud WAN core network edge (CNE) gets created in each edge location. 35 | * EC2 instances will be deployed in all the Workload Subnets configured for each Prod VPC. 36 | * EC2 Instance Connect Endpoint will be deloyed in one Endpoint Subnet configured for each Prod VPC 1, 2 and 3 and in one Firewall Subnet configured for Prod VPC 4. 37 | * AWS Network Firewall Endpoints will be deployed in all the Firewall Subnets configured for each inspection VPC and Prod VPC 4. 38 | * NAT Gateway will be deployed in all the Public Subnets configured for each inspection VPC and Prod VPC 4. 39 | -------------------------------------------------------------------------------- /outbound_inspection_with_aws_cloud_wan/core_network_base_policy.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Description: >- 3 | Egress Inspection using AWS Cloud WAN and AWS Network Firewall. Templates creates 4 | AWS Cloud WAN global and core Network with core network base policy. 5 | 6 | Resources: 7 | GlobalNetwork: 8 | Type: AWS::NetworkManager::GlobalNetwork 9 | Properties: 10 | Description: Global Network - Egress Inspection Scenarios 11 | Tags: 12 | - Key: Name 13 | Value: cwanegress-inspection-global-network 14 | 15 | CoreNetwork: 16 | Type: AWS::NetworkManager::CoreNetwork 17 | Properties: 18 | Description: Core Network - Egress Inspection Scenarios 19 | GlobalNetworkId: !Ref GlobalNetwork 20 | Tags: 21 | - Key: Name 22 | Value: cwanegress-inspection-global-network 23 | PolicyDocument: 24 | version: "2021.12" 25 | core-network-configuration: 26 | vpn-ecmp-support: true 27 | asn-ranges: 28 | - 64520-65525 29 | edge-locations: 30 | - location: us-east-2 31 | - location: us-west-2 32 | segments: 33 | - name: Production 34 | require-attachment-acceptance: false 35 | - name: EgressInspection 36 | require-attachment-acceptance: false 37 | network-function-groups: 38 | - name: EgressInspectionNFG 39 | require-attachment-acceptance: false 40 | attachment-policies: 41 | - rule-number: 100 42 | condition-logic: or 43 | conditions: 44 | - type: tag-exists 45 | key: domain 46 | action: 47 | association-method: tag 48 | tag-value-of-key: domain 49 | 50 | Outputs: 51 | CoreNetworkId: 52 | Description: Core Network ID. 53 | Value: !GetAtt CoreNetwork.CoreNetworkId 54 | CoreNetworkArn: 55 | Description: Core Network ARN. 56 | Value: !GetAtt CoreNetwork.CoreNetworkArn -------------------------------------------------------------------------------- /outbound_inspection_with_aws_cloud_wan/core_network_update_policy_edge_locations.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Description: >- 3 | Egress Inspection using AWS Cloud WAN and AWS Network Firewall. Templates creates 4 | AWS Cloud WAN global and core Network with core network base policy. 5 | 6 | Resources: 7 | GlobalNetwork: 8 | Type: AWS::NetworkManager::GlobalNetwork 9 | Properties: 10 | Description: Global Network - Egress Inspection Scenarios 11 | Tags: 12 | - Key: Name 13 | Value: cwanegress-inspection-global-network 14 | 15 | CoreNetwork: 16 | Type: AWS::NetworkManager::CoreNetwork 17 | Properties: 18 | Description: Core Network - Egress Inspection Scenarios 19 | GlobalNetworkId: !Ref GlobalNetwork 20 | Tags: 21 | - Key: Name 22 | Value: cwanegress-inspection-global-network 23 | PolicyDocument: 24 | version: "2021.12" 25 | core-network-configuration: 26 | vpn-ecmp-support: true 27 | asn-ranges: 28 | - 64520-65525 29 | edge-locations: 30 | - location: us-east-1 31 | - location: us-east-2 32 | - location: us-west-2 33 | segments: 34 | - name: Production 35 | require-attachment-acceptance: false 36 | - name: EgressInspection 37 | require-attachment-acceptance: false 38 | network-function-groups: 39 | - name: EgressInspectionNFG 40 | require-attachment-acceptance: false 41 | attachment-policies: 42 | - rule-number: 100 43 | condition-logic: or 44 | conditions: 45 | - type: tag-exists 46 | key: domain 47 | action: 48 | association-method: tag 49 | tag-value-of-key: domain 50 | 51 | Outputs: 52 | CoreNetworkId: 53 | Description: Core Network ID. 54 | Value: !GetAtt CoreNetwork.CoreNetworkId 55 | CoreNetworkArn: 56 | Description: Core Network ARN. 57 | Value: !GetAtt CoreNetwork.CoreNetworkArn -------------------------------------------------------------------------------- /outbound_inspection_with_aws_cloud_wan/region1_resources.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Description: >- 3 | AWS Cloud WAN Egress Inspection Architecture - Region 1 resources 4 | Transform: 'AWS::LanguageExtensions' 5 | 6 | Metadata: 7 | AWS::CloudFormation::Interface: 8 | ParameterGroups: 9 | - Label: 10 | default: Cloud WAN Parameters 11 | Parameters: 12 | - CoreNetworkArn 13 | - CoreNetworkId 14 | - Label: 15 | default: EC2 Parameters 16 | Parameters: 17 | - InstanceType 18 | - InstanceDiskSize 19 | - LatestAmiId 20 | 21 | Parameters: 22 | CoreNetworkArn: 23 | Description: Cloud WAN Core Network ARN 24 | Type: String 25 | ConstraintDescription: Cloud WAN Core Network ARN of type string required 26 | CoreNetworkId: 27 | Description: Cloud WAN Core Network ID 28 | Type: String 29 | ConstraintDescription: Cloud WAN Core Network ID of type string required 30 | InstanceType: 31 | Description: >- 32 | EC2 instance type for the workload instance. Default is set to t2.micro 33 | Default: t2.micro 34 | Type: String 35 | ConstraintDescription: Should be a valid EC2 instance type 36 | InstanceDiskSize: 37 | Description: EC2 instance disk size in GB. Default is set to 8GB 38 | Default: 8 39 | AllowedValues: [8] 40 | Type: Number 41 | ConstraintDescription: Should be a valid instance size in GB 42 | LatestAmiId: 43 | Description: Latest EC2 AMI from Systems Manager Parameter Store 44 | Type: "AWS::SSM::Parameter::Value" 45 | Default: "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64" 46 | ConstraintDescription: Must be a valid EC2 AMI from Systems Manager 47 | 48 | Resources: 49 | # ---------- Lambda Role ---------- 50 | IamRoleLambdaCFn: 51 | Type: AWS::IAM::Role 52 | Properties: 53 | AssumeRolePolicyDocument: 54 | Version: 2012-10-17 55 | Statement: 56 | - Effect: Allow 57 | Principal: 58 | Service: 59 | - lambda.amazonaws.com 60 | Action: 61 | - sts:AssumeRole 62 | Description: Provides permissions for Lambda functions. 63 | ManagedPolicyArns: 64 | - arn:aws:iam::aws:policy/AdministratorAccess 65 | # ---------- End of Lambda Role ---------- 66 | 67 | # ---------- Prod VPC 1 Resources ---------- 68 | # ---------- Prod VPC 1 ---------- 69 | ProdVPC1: 70 | Type: AWS::EC2::VPC 71 | Properties: 72 | CidrBlock: 10.1.0.0/16 73 | Tags: 74 | - Key: Name 75 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1" 76 | 77 | # ---------- Prod VPC 1 Subnets ---------- 78 | # CWAN Subnets: 79 | ProdVPC1CWANSubnet1: 80 | Type: AWS::EC2::Subnet 81 | Properties: 82 | VpcId: 83 | Ref: ProdVPC1 84 | CidrBlock: 10.1.0.0/28 85 | AvailabilityZone: !Select 86 | - 0 87 | - Fn::GetAZs: !Ref AWS::Region 88 | MapPublicIpOnLaunch: false 89 | Tags: 90 | - Key: Name 91 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-cwan-subnet1" 92 | 93 | ProdVPC1CWANSubnet2: 94 | Type: AWS::EC2::Subnet 95 | Properties: 96 | VpcId: !Ref ProdVPC1 97 | CidrBlock: 10.1.0.16/28 98 | AvailabilityZone: !Select [1, Fn::GetAZs: !Ref AWS::Region] 99 | MapPublicIpOnLaunch: false 100 | Tags: 101 | - Key: Name 102 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-cwan-subnet2" 103 | 104 | # Endpoint Subnets: 105 | ProdVPC1EndpointSubnet1: 106 | Type: AWS::EC2::Subnet 107 | Properties: 108 | VpcId: !Ref ProdVPC1 109 | CidrBlock: 10.1.0.32/28 110 | AvailabilityZone: !Select [0, Fn::GetAZs: !Ref AWS::Region] 111 | MapPublicIpOnLaunch: false 112 | Tags: 113 | - Key: Name 114 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-endpoint-subnet1" 115 | 116 | ProdVPC1EndpointSubnet2: 117 | Type: AWS::EC2::Subnet 118 | Properties: 119 | VpcId: !Ref ProdVPC1 120 | CidrBlock: 10.1.0.48/28 121 | AvailabilityZone: !Select [1, Fn::GetAZs: !Ref AWS::Region] 122 | MapPublicIpOnLaunch: false 123 | Tags: 124 | - Key: Name 125 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-endpoint-subnet2" 126 | 127 | # Workload Subnets: 128 | ProdVPC1WorkloadSubnet1: 129 | Type: AWS::EC2::Subnet 130 | Properties: 131 | VpcId: !Ref ProdVPC1 132 | CidrBlock: 10.1.1.0/24 133 | AvailabilityZone: !Select [0, Fn::GetAZs: !Ref AWS::Region] 134 | MapPublicIpOnLaunch: false 135 | Tags: 136 | - Key: Name 137 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-workload-subnet1" 138 | 139 | ProdVPC1WorkloadSubnet2: 140 | Type: AWS::EC2::Subnet 141 | Properties: 142 | VpcId: !Ref ProdVPC1 143 | CidrBlock: 10.1.2.0/24 144 | AvailabilityZone: !Select [1, Fn::GetAZs: !Ref AWS::Region] 145 | MapPublicIpOnLaunch: false 146 | Tags: 147 | - Key: Name 148 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-workload-subnet2" 149 | 150 | # ---------- Prod VPC 1 Route Tables and Subnet Associations ---------- 151 | # CWAN Route Tables: 152 | ProdVPC1CWANRTB1: 153 | Type: AWS::EC2::RouteTable 154 | Properties: 155 | VpcId: !Ref ProdVPC1 156 | Tags: 157 | - Key: Name 158 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-cwan-rtb1" 159 | 160 | ProdVPC1CWANRTB2: 161 | Type: AWS::EC2::RouteTable 162 | Properties: 163 | VpcId: !Ref ProdVPC1 164 | Tags: 165 | - Key: Name 166 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-cwan-rtb2" 167 | 168 | # CWAN Route Tables Subnet Associations: 169 | ProdVPC1CWANRTB1Association: 170 | Type: AWS::EC2::SubnetRouteTableAssociation 171 | Properties: 172 | RouteTableId: !Ref ProdVPC1CWANRTB1 173 | SubnetId: !Ref ProdVPC1CWANSubnet1 174 | 175 | ProdVPC1CWANRTB2Association: 176 | Type: AWS::EC2::SubnetRouteTableAssociation 177 | Properties: 178 | RouteTableId: !Ref ProdVPC1CWANRTB2 179 | SubnetId: !Ref ProdVPC1CWANSubnet2 180 | 181 | # Endpoint Route Tables: 182 | ProdVPC1EndpointRTB1: 183 | Type: AWS::EC2::RouteTable 184 | Properties: 185 | VpcId: !Ref ProdVPC1 186 | Tags: 187 | - Key: Name 188 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-endpoint-rtb1" 189 | 190 | ProdVPC1EndpointRTB2: 191 | Type: AWS::EC2::RouteTable 192 | Properties: 193 | VpcId: !Ref ProdVPC1 194 | Tags: 195 | - Key: Name 196 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-endpoint-rtb2" 197 | 198 | # Endpoint Route Tables Subnet Associations: 199 | ProdVPC1EndpointRTB1Association: 200 | Type: AWS::EC2::SubnetRouteTableAssociation 201 | Properties: 202 | RouteTableId: !Ref ProdVPC1EndpointRTB1 203 | SubnetId: !Ref ProdVPC1EndpointSubnet1 204 | 205 | ProdVPC1EndpointRTB2Association: 206 | Type: AWS::EC2::SubnetRouteTableAssociation 207 | Properties: 208 | RouteTableId: !Ref ProdVPC1EndpointRTB2 209 | SubnetId: !Ref ProdVPC1EndpointSubnet2 210 | 211 | # Workload Route Tables: 212 | ProdVPC1WorkloadRTB1: 213 | Type: AWS::EC2::RouteTable 214 | Properties: 215 | VpcId: !Ref ProdVPC1 216 | Tags: 217 | - Key: Name 218 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-workload-rtb1" 219 | 220 | ProdVPC1WorkloadRTB2: 221 | Type: AWS::EC2::RouteTable 222 | Properties: 223 | VpcId: !Ref ProdVPC1 224 | Tags: 225 | - Key: Name 226 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-workload-rtb2" 227 | 228 | # Workload Route Tables Subnet Associations: 229 | ProdVPC1WorkloadRTB1Association: 230 | Type: AWS::EC2::SubnetRouteTableAssociation 231 | Properties: 232 | RouteTableId: !Ref ProdVPC1WorkloadRTB1 233 | SubnetId: !Ref ProdVPC1WorkloadSubnet1 234 | 235 | ProdVPC1WorkloadRTB2Association: 236 | Type: AWS::EC2::SubnetRouteTableAssociation 237 | Properties: 238 | RouteTableId: !Ref ProdVPC1WorkloadRTB2 239 | SubnetId: !Ref ProdVPC1WorkloadSubnet2 240 | 241 | # ---------- AWS Cloud WAN attachment and default route---------- 242 | # Create Prod VPC 1 Cloud WAN Attachment: 243 | ProdVPC1CWANAttachment: 244 | Type: AWS::NetworkManager::VpcAttachment 245 | Properties: 246 | CoreNetworkId: !Ref CoreNetworkId 247 | VpcArn: !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/${ProdVPC1}" 248 | SubnetArns: 249 | - !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${ProdVPC1CWANSubnet1}" 250 | - !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${ProdVPC1CWANSubnet2}" 251 | Tags: 252 | - Key: Name 253 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-attachment" 254 | - Key: domain 255 | Value: Production 256 | 257 | # Create default route in Workload route tables with Core Network ARN has the next hop: 258 | ProdVPC1WorkloadRTB1DefaultRoute: 259 | DependsOn: 260 | - ProdVPC1CWANAttachment 261 | Type: AWS::EC2::Route 262 | Properties: 263 | RouteTableId: !Ref ProdVPC1WorkloadRTB1 264 | DestinationCidrBlock: 0.0.0.0/0 265 | CoreNetworkArn: !Ref CoreNetworkArn 266 | 267 | ProdVPC1WorkloadRTB2DefaultRoute: 268 | DependsOn: [ProdVPC1CWANAttachment] 269 | Type: AWS::EC2::Route 270 | Properties: 271 | RouteTableId: !Ref ProdVPC1WorkloadRTB2 272 | DestinationCidrBlock: 0.0.0.0/0 273 | CoreNetworkArn: !Ref CoreNetworkArn 274 | 275 | # ---------- Create Security Groups ---------- 276 | # Create Workload SG: 277 | ProdVPC1WorkloadSecurityGroup: 278 | Type: AWS::EC2::SecurityGroup 279 | Properties: 280 | GroupDescription: Prod VPC 1 Workload EC2 Instance Security Group 281 | GroupName: !Sub "cwanegress-${AWS::Region}-prod-vpc1-workload-sg" 282 | VpcId: !Ref ProdVPC1 283 | SecurityGroupIngress: 284 | - CidrIp: 10.0.0.0/8 285 | Description: Allowing inbound connection from 10.0.0.0/8 CIDR. 286 | IpProtocol: "-1" 287 | FromPort: -1 288 | ToPort: -1 289 | SecurityGroupEgress: 290 | - CidrIp: 0.0.0.0/0 291 | Description: Allowing outbound connection to 0.0.0.0/0 CIDR. 292 | IpProtocol: "-1" 293 | FromPort: -1 294 | ToPort: -1 295 | Tags: 296 | - Key: Name 297 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-workload-sg" 298 | 299 | # Create EC2 instance connect endpoint SG: 300 | ProdVPC1EndpointSecurityGroup: 301 | Type: AWS::EC2::SecurityGroup 302 | Properties: 303 | GroupDescription: Prod VPC 1 Endpoint Security Group 304 | GroupName: !Sub "cwanegress-${AWS::Region}-prod-vpc1-endpoint-sg" 305 | VpcId: !Ref ProdVPC1 306 | Tags: 307 | - Key: Name 308 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-endpoint-sg" 309 | 310 | ProdVPC1WorkloadSecurityGroupIngressEIC: 311 | Type: AWS::EC2::SecurityGroupIngress 312 | Properties: 313 | GroupId: !Ref ProdVPC1WorkloadSecurityGroup 314 | Description: Allowing inbound connenciton from EC2 Instance Connect (EIC). 315 | IpProtocol: tcp 316 | FromPort: 22 317 | ToPort: 22 318 | SourceSecurityGroupId: !Ref ProdVPC1EndpointSecurityGroup 319 | 320 | ProdVPC1EndpointSecurityGroupEgressEIC: 321 | Type: AWS::EC2::SecurityGroupEgress 322 | Properties: 323 | GroupId: !Ref ProdVPC1EndpointSecurityGroup 324 | Description: Allowing outbound connection to EC2 Instance Connect (EIC). 325 | IpProtocol: tcp 326 | FromPort: 22 327 | ToPort: 22 328 | DestinationSecurityGroupId: !Ref ProdVPC1WorkloadSecurityGroup 329 | 330 | # ---------- EC2 Instance Connect Endpoint ---------- 331 | ProdVPC1EICEndpoint: 332 | Type: AWS::EC2::InstanceConnectEndpoint 333 | Properties: 334 | PreserveClientIp: false 335 | SecurityGroupIds: 336 | - !Ref ProdVPC1EndpointSecurityGroup 337 | SubnetId: !Ref ProdVPC1EndpointSubnet1 338 | Tags: 339 | - Key: Name 340 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-eic" 341 | 342 | # ---------- Create Worload EC2 Intances ---------- 343 | # Workoad Instance 1: 344 | ProdVPC1WorkloadInstance1: 345 | Type: AWS::EC2::Instance 346 | Properties: 347 | ImageId: !Ref LatestAmiId 348 | InstanceType: !Ref InstanceType 349 | SubnetId: !Ref ProdVPC1WorkloadSubnet1 350 | SecurityGroupIds: 351 | - !Ref ProdVPC1WorkloadSecurityGroup 352 | Tags: 353 | - Key: Name 354 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-workload-instance1" 355 | UserData: 356 | Fn::Base64: | 357 | cat <> /home/ec2-user/usedatascript.sh 358 | #!/bin/bash -ex 359 | 360 | # Install packages: 361 | sudo yum update -y; 362 | sudo yum install jq -y; 363 | sudo yum install httpd -y; 364 | sudo yum install htop -y; 365 | 366 | # Enable and start httpd 367 | sudo systemctl enable httpd; 368 | sudo systemctl start httpd; 369 | 370 | # Configure hostname: 371 | sudo hostnamectl set-hostname prod-vpc1-workload-${AvailabilityZone}; 372 | 373 | # Configure SSH client alive interval for ssh session timeout: 374 | echo 'ClientAliveInterval 60' | sudo tee --append /etc/ssh/sshd_config; 375 | service sshd restart; 376 | 377 | # Set dark background for vim: 378 | touch /home/ec2-user/.vimrc; 379 | echo "set background=dark" >> /home/ec2-user/.vimrc; 380 | 381 | # Define variables: 382 | curl --silent http://169.254.169.254/latest/dynamic/instance-identity/document > /home/ec2-user/iid; 383 | export instance_interface=$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/); 384 | export instance_vpcid=$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/$instance_interface/vpc-id); 385 | export instance_az=$(cat /home/ec2-user/iid |grep 'availability' | awk -F': ' '{print $2}' | awk -F',' '{print $1}'); 386 | export instance_ip=$(cat /home/ec2-user/iid |grep 'privateIp' | awk -F': ' '{print $2}' | awk -F',' '{print $1}' | awk -F'"' '{print$2}'); 387 | export instance_region=$(cat /home/ec2-user/iid |grep 'region' | awk -F': ' '{print $2}' | awk -F',' '{print $1}' | awk -F'"' '{print$2}'); 388 | EOT 389 | 390 | # Add index.html 391 | touch /var/www/html/index.html; 392 | cat <> /var/www/html/index.html 393 | 394 | 395 | Prod VPC 1 Workload Instance 1 396 | 397 | 398 | 399 |

Welcome to AWS Cloud WAN Egress Inspection Architecture POC:

400 |

This is a simple web server running in $instance_az in $instance_region. Happy Testing!

401 | 402 | 403 | EOT 404 | 405 | # Workoad Instance 2: 406 | ProdVPC1WorkloadInstance2: 407 | Type: AWS::EC2::Instance 408 | Properties: 409 | ImageId: !Ref LatestAmiId 410 | InstanceType: !Ref InstanceType 411 | SubnetId: !Ref ProdVPC1WorkloadSubnet2 412 | SecurityGroupIds: 413 | - !Ref ProdVPC1WorkloadSecurityGroup 414 | Tags: 415 | - Key: Name 416 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc1-workload-instance2" 417 | UserData: 418 | Fn::Base64: | 419 | cat <> /home/ec2-user/usedatascript.sh 420 | #!/bin/bash -ex 421 | 422 | # Install packages: 423 | sudo yum update -y; 424 | sudo yum install jq -y; 425 | sudo yum install httpd -y; 426 | sudo yum install htop -y; 427 | 428 | # Enable and start httpd 429 | sudo systemctl enable httpd; 430 | sudo systemctl start httpd; 431 | 432 | # Configure hostname: 433 | sudo hostnamectl set-hostname prod-vpc1-workload-${AvailabilityZone}; 434 | 435 | # Configure SSH client alive interval for ssh session timeout: 436 | echo 'ClientAliveInterval 60' | sudo tee --append /etc/ssh/sshd_config; 437 | service sshd restart; 438 | 439 | # Set dark background for vim: 440 | touch /home/ec2-user/.vimrc; 441 | echo "set background=dark" >> /home/ec2-user/.vimrc; 442 | 443 | # Define variables: 444 | curl --silent http://169.254.169.254/latest/dynamic/instance-identity/document > /home/ec2-user/iid; 445 | export instance_interface=$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/); 446 | export instance_vpcid=$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/$instance_interface/vpc-id); 447 | export instance_az=$(cat /home/ec2-user/iid |grep 'availability' | awk -F': ' '{print $2}' | awk -F',' '{print $1}'); 448 | export instance_ip=$(cat /home/ec2-user/iid |grep 'privateIp' | awk -F': ' '{print $2}' | awk -F',' '{print $1}' | awk -F'"' '{print$2}'); 449 | export instance_region=$(cat /home/ec2-user/iid |grep 'region' | awk -F': ' '{print $2}' | awk -F',' '{print $1}' | awk -F'"' '{print$2}'); 450 | EOT 451 | 452 | # Add index.html 453 | touch /var/www/html/index.html; 454 | cat <> /var/www/html/index.html 455 | 456 | 457 | Prod VPC 1 Workload Instance 2 458 | 459 | 460 | 461 |

Welcome to AWS Cloud WAN Egress Inspection Architecture POC:

462 |

This is a simple web server running in $instance_az in $instance_region. Happy Testing!

463 | 464 | 465 | EOT 466 | 467 | # ---------- End of Prod VPC 1 Resources ---------- 468 | 469 | # ---------- Inspection VPC 1 Resources ---------- 470 | # ---------- Inspection VPC 1 ---------- 471 | InspectionVPC1: 472 | Type: AWS::EC2::VPC 473 | Properties: 474 | CidrBlock: 100.64.1.0/24 475 | Tags: 476 | - Key: Name 477 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1" 478 | 479 | # ---------- Inspection VPC 1 IGW and IGW Attachment---------- 480 | InspectionVPC1IGW: 481 | Type: AWS::EC2::InternetGateway 482 | Properties: 483 | Tags: 484 | - Key: Name 485 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-igw" 486 | 487 | InspectionVPC1IGWAttachment: 488 | Type: AWS::EC2::VPCGatewayAttachment 489 | Properties: 490 | InternetGatewayId: !Ref InspectionVPC1IGW 491 | VpcId: !Ref InspectionVPC1 492 | 493 | # ---------- Inspection VPC 1 Subnets ---------- 494 | # CWAN Subnets: 495 | InspectionVPC1CWANSubnet1: 496 | Type: AWS::EC2::Subnet 497 | Properties: 498 | VpcId: !Ref InspectionVPC1 499 | CidrBlock: 100.64.1.0/28 500 | AvailabilityZone: !Select 501 | - 0 502 | - Fn::GetAZs: !Ref AWS::Region 503 | MapPublicIpOnLaunch: false 504 | Tags: 505 | - Key: Name 506 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-cwan-subnet1" 507 | 508 | InspectionVPC1CWANSubnet2: 509 | Type: AWS::EC2::Subnet 510 | Properties: 511 | VpcId: !Ref InspectionVPC1 512 | CidrBlock: 100.64.1.16/28 513 | AvailabilityZone: !Select [1, Fn::GetAZs: !Ref AWS::Region] 514 | MapPublicIpOnLaunch: false 515 | Tags: 516 | - Key: Name 517 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-cwan-subnet2" 518 | 519 | # Firewall Endpoint Subnets: 520 | InspectionVPC1FirewallSubnet1: 521 | Type: AWS::EC2::Subnet 522 | Properties: 523 | VpcId: !Ref InspectionVPC1 524 | CidrBlock: 100.64.1.32/28 525 | AvailabilityZone: !Select [0, Fn::GetAZs: !Ref AWS::Region] 526 | MapPublicIpOnLaunch: false 527 | Tags: 528 | - Key: Name 529 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-fwe-subnet1" 530 | 531 | InspectionVPC1FirewallSubnet2: 532 | Type: AWS::EC2::Subnet 533 | Properties: 534 | VpcId: !Ref InspectionVPC1 535 | CidrBlock: 100.64.1.48/28 536 | AvailabilityZone: !Select [1, Fn::GetAZs: !Ref AWS::Region] 537 | MapPublicIpOnLaunch: false 538 | Tags: 539 | - Key: Name 540 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-fwe-subnet2" 541 | 542 | # Public Subnets: 543 | InspectionVPC1PublicSubnet1: 544 | Type: AWS::EC2::Subnet 545 | Properties: 546 | VpcId: !Ref InspectionVPC1 547 | CidrBlock: 100.64.1.64/28 548 | AvailabilityZone: !Select [0, Fn::GetAZs: !Ref AWS::Region] 549 | MapPublicIpOnLaunch: true 550 | Tags: 551 | - Key: Name 552 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-public-subnet1" 553 | 554 | InspectionVPC1PublicSubnet2: 555 | Type: AWS::EC2::Subnet 556 | Properties: 557 | VpcId: !Ref InspectionVPC1 558 | CidrBlock: 100.64.1.80/28 559 | AvailabilityZone: !Select [1, Fn::GetAZs: !Ref AWS::Region] 560 | MapPublicIpOnLaunch: true 561 | Tags: 562 | - Key: Name 563 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-public-subnet2" 564 | 565 | # ---------- Inspection VPC 1 Route Tables and Subnet Associations ---------- 566 | # CWAN Route Tables: 567 | InspectionVPC1CWANRTB1: 568 | Type: AWS::EC2::RouteTable 569 | Properties: 570 | VpcId: !Ref InspectionVPC1 571 | Tags: 572 | - Key: Name 573 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-cwan-rtb1" 574 | 575 | InspectionVPC1CWANRTB2: 576 | Type: AWS::EC2::RouteTable 577 | Properties: 578 | VpcId: !Ref InspectionVPC1 579 | Tags: 580 | - Key: Name 581 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-cwan-rtb2" 582 | 583 | # CWAN Route Tables Subnet Associations: 584 | InspectionVPC1CWANRTB1Association: 585 | Type: AWS::EC2::SubnetRouteTableAssociation 586 | Properties: 587 | RouteTableId: !Ref InspectionVPC1CWANRTB1 588 | SubnetId: !Ref InspectionVPC1CWANSubnet1 589 | 590 | InspectionVPC1CWANRTB2Association: 591 | Type: AWS::EC2::SubnetRouteTableAssociation 592 | Properties: 593 | RouteTableId: !Ref InspectionVPC1CWANRTB2 594 | SubnetId: !Ref InspectionVPC1CWANSubnet2 595 | 596 | # Firewall Endpoint Route Tables: 597 | InspectionVPC1FirewallRTB1: 598 | Type: AWS::EC2::RouteTable 599 | Properties: 600 | VpcId: !Ref InspectionVPC1 601 | Tags: 602 | - Key: Name 603 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-fwe-rtb1" 604 | 605 | InspectionVPC1FirewallRTB2: 606 | Type: AWS::EC2::RouteTable 607 | Properties: 608 | VpcId: !Ref InspectionVPC1 609 | Tags: 610 | - Key: Name 611 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-fwe-rtb2" 612 | 613 | # Firewall Endpoint Route Tables Subnet Associations: 614 | InspectionVPC1FirewallRTB1Association: 615 | Type: AWS::EC2::SubnetRouteTableAssociation 616 | Properties: 617 | RouteTableId: !Ref InspectionVPC1FirewallRTB1 618 | SubnetId: !Ref InspectionVPC1FirewallSubnet1 619 | 620 | InspectionVPC1FirewallRTB2Association: 621 | Type: AWS::EC2::SubnetRouteTableAssociation 622 | Properties: 623 | RouteTableId: !Ref InspectionVPC1FirewallRTB2 624 | SubnetId: !Ref InspectionVPC1FirewallSubnet2 625 | 626 | # Public Route Tables: 627 | InspectionVPC1PublicRTB1: 628 | Type: AWS::EC2::RouteTable 629 | Properties: 630 | VpcId: !Ref InspectionVPC1 631 | Tags: 632 | - Key: Name 633 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-public-rtb1" 634 | 635 | InspectionVPC1PublicRTB2: 636 | Type: AWS::EC2::RouteTable 637 | Properties: 638 | VpcId: !Ref InspectionVPC1 639 | Tags: 640 | - Key: Name 641 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-public-rtb2" 642 | 643 | # Public Route Tables Subnet Associations: 644 | InspectionVPC1PublicRTB1Association: 645 | Type: AWS::EC2::SubnetRouteTableAssociation 646 | Properties: 647 | RouteTableId: !Ref InspectionVPC1PublicRTB1 648 | SubnetId: !Ref InspectionVPC1PublicSubnet1 649 | 650 | InspectionVPC1PublicRTB2Association: 651 | Type: AWS::EC2::SubnetRouteTableAssociation 652 | Properties: 653 | RouteTableId: !Ref InspectionVPC1PublicRTB2 654 | SubnetId: !Ref InspectionVPC1PublicSubnet2 655 | 656 | # ---------- Inspection VPC 1 NAT Gateways and EIPs ---------- 657 | # NAT Gateway 1: 658 | InspectionVPC1NATGW1EIP1: 659 | Type: AWS::EC2::EIP 660 | Properties: 661 | Domain: vpc 662 | 663 | InspectionVPC1NATGW1: 664 | Type: AWS::EC2::NatGateway 665 | Properties: 666 | AllocationId: !GetAtt InspectionVPC1NATGW1EIP1.AllocationId 667 | SubnetId: !Ref InspectionVPC1PublicSubnet1 668 | Tags: 669 | - Key: Name 670 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-natgw1" 671 | 672 | # NAT Gateway 2: 673 | InspectionVPC1NATGW1EIP2: 674 | Type: AWS::EC2::EIP 675 | Properties: 676 | Domain: vpc 677 | 678 | InspectionVPC1NATGW2: 679 | Type: AWS::EC2::NatGateway 680 | Properties: 681 | AllocationId: !GetAtt InspectionVPC1NATGW1EIP2.AllocationId 682 | SubnetId: !Ref InspectionVPC1PublicSubnet2 683 | Tags: 684 | - Key: Name 685 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-natgw2" 686 | 687 | # ---------- AWS Network Firewall (ANFW) and related resources ---------- 688 | # Network Firewall resource 689 | InspectionVPC1Anfw1: 690 | DependsOn: [InspectionVPC1Anfw1RulesPolicy1] 691 | Type: AWS::NetworkFirewall::Firewall 692 | Properties: 693 | FirewallName: !Sub "cwanegress-${AWS::Region}-insp-vpc1-anfw1" 694 | FirewallPolicyArn: !GetAtt InspectionVPC1Anfw1RulesPolicy1.FirewallPolicyArn 695 | VpcId: !Ref InspectionVPC1 696 | SubnetMappings: 697 | - SubnetId: !Ref InspectionVPC1FirewallSubnet1 698 | - SubnetId: !Ref InspectionVPC1FirewallSubnet2 699 | 700 | # Firewall Policy 701 | InspectionVPC1Anfw1RulesPolicy1: 702 | Type: AWS::NetworkFirewall::FirewallPolicy 703 | Properties: 704 | FirewallPolicy: 705 | StatelessDefaultActions: 706 | - aws:forward_to_sfe 707 | StatelessFragmentDefaultActions: 708 | - aws:forward_to_sfe 709 | StatelessRuleGroupReferences: 710 | - Priority: 10 711 | ResourceArn: !GetAtt InspectionVPC1Anfw1StatelessDropRemoteRuleGroup.RuleGroupArn 712 | StatefulEngineOptions: 713 | RuleOrder: STRICT_ORDER 714 | StatefulDefaultActions: 715 | - aws:drop_established 716 | - aws:alert_established 717 | StatefulRuleGroupReferences: 718 | - ResourceArn: !GetAtt InspectionVPC1Anfw1DomainListStatefulAllowRuleGroup.RuleGroupArn 719 | Priority: 100 720 | - ResourceArn: !GetAtt InspectionVPC1Anfw1StandardStatefulRuleGroup.RuleGroupArn 721 | Priority: 200 722 | 723 | FirewallPolicyName: !Sub "cwanegress-${AWS::Region}-insp-vpc1-anfw1-policy1" 724 | Tags: 725 | - Key: Name 726 | Value: !Sub "cwanegress-${AWS::Region}-firewall-policy-v1" 727 | 728 | # Stateless Rule Group - Dropping any SSH connection 729 | InspectionVPC1Anfw1StatelessDropRemoteRuleGroup: 730 | Type: AWS::NetworkFirewall::RuleGroup 731 | Properties: 732 | Capacity: 100 733 | RuleGroupName: !Sub "cwanegress-${AWS::Region}-insp-vpc1-anfw1-dropremote-rg" 734 | Description: Drop remote SSH connections 735 | Type: STATELESS 736 | RuleGroup: 737 | RulesSource: 738 | StatelessRulesAndCustomActions: 739 | StatelessRules: 740 | - Priority: 1 741 | RuleDefinition: 742 | MatchAttributes: 743 | Protocols: 744 | - 6 745 | Sources: 746 | - AddressDefinition: 0.0.0.0/0 747 | SourcePorts: 748 | - FromPort: 22 749 | ToPort: 22 750 | Destinations: 751 | - AddressDefinition: 0.0.0.0/0 752 | DestinationPorts: 753 | - FromPort: 22 754 | ToPort: 22 755 | Actions: 756 | - "aws:drop" 757 | 758 | # Domain list Stateful Rule Group: 759 | InspectionVPC1Anfw1DomainListStatefulAllowRuleGroup: 760 | Type: AWS::NetworkFirewall::RuleGroup 761 | Properties: 762 | Capacity: 100 763 | RuleGroupName: !Sub "cwanegress-${AWS::Region}-insp-vpc1-anfw1-domain-allow-rg" 764 | Description: Allowing access to desired domains 765 | Type: STATEFUL 766 | RuleGroup: 767 | StatefulRuleOptions: 768 | RuleOrder: STRICT_ORDER 769 | RuleVariables: 770 | IPSets: 771 | HOME_NET: 772 | Definition: 773 | - "10.0.0.0/8" 774 | RulesSource: 775 | RulesSourceList: 776 | TargetTypes: 777 | - HTTP_HOST 778 | - TLS_SNI 779 | Targets: 780 | - ".amazon.com" 781 | - ".amazonaws.com" 782 | - ".google.com" 783 | GeneratedRulesType: "ALLOWLIST" 784 | Tags: 785 | - Key: Name 786 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-anfw1-domain-allow-rg" 787 | 788 | # Standard Stateful Rule Group: 789 | InspectionVPC1Anfw1StandardStatefulRuleGroup: 790 | Type: AWS::NetworkFirewall::RuleGroup 791 | Properties: 792 | RuleGroupName: !Sub "cwanegress-${AWS::Region}-insp-vpc1-anfw1-standard-stateful-rg" 793 | Type: STATEFUL 794 | Capacity: 100 795 | RuleGroup: 796 | StatefulRuleOptions: 797 | RuleOrder: STRICT_ORDER 798 | RuleVariables: 799 | IPSets: 800 | HOME_NET: 801 | Definition: 802 | - "10.0.0.0/8" 803 | RulesSource: 804 | StatefulRules: 805 | - Action: ALERT 806 | Header: 807 | Direction: ANY 808 | Protocol: ICMP 809 | Destination: ANY 810 | Source: $HOME_NET 811 | DestinationPort: ANY 812 | SourcePort: ANY 813 | RuleOptions: 814 | - Keyword: "sid:10001" 815 | - Action: PASS 816 | Header: 817 | Direction: ANY 818 | Protocol: ICMP 819 | Destination: ANY 820 | Source: $HOME_NET 821 | DestinationPort: ANY 822 | SourcePort: ANY 823 | RuleOptions: 824 | - Keyword: "sid:10002" 825 | - Action: ALERT 826 | Header: 827 | Direction: ANY 828 | Protocol: UDP 829 | Destination: ANY 830 | Source: $HOME_NET 831 | DestinationPort: ANY 832 | SourcePort: ANY 833 | RuleOptions: 834 | - Keyword: "sid:20001" 835 | - Action: PASS 836 | Header: 837 | Direction: ANY 838 | Protocol: UDP 839 | Destination: ANY 840 | Source: $HOME_NET 841 | DestinationPort: ANY 842 | SourcePort: ANY 843 | RuleOptions: 844 | - Keyword: "sid:20002" 845 | Tags: 846 | - Key: Name 847 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-anfw1-standard-stateful-rg" 848 | 849 | # Firewall Logs - Flow 850 | InspectionVPC1Anfw1LogFlowGroup: 851 | Type: AWS::Logs::LogGroup 852 | Properties: 853 | LogGroupName: !Sub "/anfw1/flow/cwanegress/${AWS::Region}" 854 | 855 | # Firewall Logs - Aler 856 | InspectionVPC1Anfw1LogAlertGroup: 857 | Type: AWS::Logs::LogGroup 858 | Properties: 859 | LogGroupName: !Sub "/anfw1/alert/cwanegress/${AWS::Region}" 860 | 861 | # Firewall Log - Policy reference 862 | InspectionVPC1Anfw1Log: 863 | Type: AWS::NetworkFirewall::LoggingConfiguration 864 | Properties: 865 | FirewallArn: !Ref InspectionVPC1Anfw1 866 | LoggingConfiguration: 867 | LogDestinationConfigs: 868 | - LogType: FLOW 869 | LogDestinationType: CloudWatchLogs 870 | LogDestination: 871 | logGroup: !Sub "/anfw1/flow/cwanegress/${AWS::Region}" 872 | - LogType: ALERT 873 | LogDestinationType: CloudWatchLogs 874 | LogDestination: 875 | logGroup: !Sub "/anfw1/alert/cwanegress/${AWS::Region}" 876 | 877 | # ---------- Lambda and Custom Resource to retrieve ANFW Endpoint IDs ---------- 878 | # CloudWatch Log Group for DescribeVpceIdLambda Function: 879 | DescribeVpceIdLambdaLogGroup: 880 | Type: AWS::Logs::LogGroup 881 | Properties: 882 | LogGroupName: !Sub "/aws/lambda/cwanegress-${AWS::Region}-DescribeVpceId" 883 | 884 | # Lambda Function to retrieve ANFW Endpoint IDs: 885 | DescribeVpceIdLambda: 886 | Type: AWS::Lambda::Function 887 | DependsOn: DescribeVpceIdLambdaLogGroup 888 | Properties: 889 | FunctionName: !Sub "cwanegress-${AWS::Region}-DescribeVpceId" 890 | Handler: "index.handler" 891 | Role: !GetAtt IamRoleLambdaCFn.Arn 892 | Code: 893 | ZipFile: | 894 | import boto3 895 | import cfnresponse 896 | import json 897 | import logging 898 | 899 | def handler(event, context): 900 | logger = logging.getLogger() 901 | logger.setLevel(logging.INFO) 902 | 903 | responseData = {} 904 | responseStatus = cfnresponse.FAILED 905 | 906 | eventinfo = json.dumps(event) 907 | logger.info(f"Received event: {eventinfo}") 908 | 909 | if event["RequestType"] == "Delete": 910 | responseStatus = cfnresponse.SUCCESS 911 | cfnresponse.send(event, context, responseStatus, responseData, "CustomResourcePhysicalID") 912 | if event["RequestType"] == "Create": 913 | try: 914 | Az1 = event["ResourceProperties"]["Az1"] 915 | Az2 = event["ResourceProperties"]["Az2"] 916 | FwArn = event["ResourceProperties"]["FwArn"] 917 | except Exception as e: 918 | logger.info(f"AZ retrieval failure: {e}") 919 | 920 | try: 921 | nfw = boto3.client('network-firewall') 922 | except Exception as e: 923 | logger.info(f"boto3.client failure: {e}") 924 | 925 | try: 926 | NfwResponse=nfw.describe_firewall(FirewallArn=FwArn) 927 | VpceId1 = NfwResponse['FirewallStatus']['SyncStates'][Az1]['Attachment']['EndpointId'] 928 | VpceId2 = NfwResponse['FirewallStatus']['SyncStates'][Az2]['Attachment']['EndpointId'] 929 | except Exception as e: 930 | logger.info(f"ec2.describe_firewall failure: {e}") 931 | 932 | responseData['FwVpceId1'] = VpceId1 933 | responseData['FwVpceId2'] = VpceId2 934 | responseStatus = cfnresponse.SUCCESS 935 | print(f"VPCE ID1: {VpceId1}") 936 | print(f"VPCE ID2: {VpceId2}") 937 | print(f"response data: {responseData}") 938 | cfnresponse.send(event, context, responseStatus, responseData) 939 | Runtime: python3.12 940 | Timeout: 900 941 | 942 | # Custom resource to retrieve ANFW Endpoint IDs using Lambda function created above: 943 | RetrieveInspectionVPC1AnfwVpceIds: 944 | Type: Custom::DescribeVpcEndpoints 945 | Properties: 946 | ServiceToken: !GetAtt DescribeVpceIdLambda.Arn 947 | Az1: !Select [0, Fn::GetAZs: !Ref AWS::Region] 948 | Az2: !Select [1, Fn::GetAZs: !Ref AWS::Region] 949 | FwArn: !Ref InspectionVPC1Anfw1 950 | 951 | # ---------- AWS Cloud WAN attachment ---------- 952 | # Create Inspection VPC 1 Cloud WAN Attachment: 953 | InspectionVPC1CWANAttachment: 954 | Type: AWS::NetworkManager::VpcAttachment 955 | Properties: 956 | CoreNetworkId: !Ref CoreNetworkId 957 | VpcArn: !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/${InspectionVPC1}" 958 | SubnetArns: 959 | - !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${InspectionVPC1CWANSubnet1}" 960 | - !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${InspectionVPC1CWANSubnet2}" 961 | Tags: 962 | - Key: Name 963 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc1-attachment" 964 | - Key: domain 965 | Value: EgressInspection 966 | 967 | # ---------- Add Routes ---------- 968 | # Create default route in CWAN route tables with ANFW endpoints as the next hop: 969 | InspectionVPC1CWANRTB1DefaultRoute: 970 | DependsOn: [InspectionVPC1Anfw1] 971 | Type: AWS::EC2::Route 972 | Properties: 973 | RouteTableId: !Ref InspectionVPC1CWANRTB1 974 | DestinationCidrBlock: 0.0.0.0/0 975 | VpcEndpointId: !GetAtt RetrieveInspectionVPC1AnfwVpceIds.FwVpceId1 976 | 977 | InspectionVPC1CWANRTB2DefaultRoute: 978 | DependsOn: [InspectionVPC1Anfw1] 979 | Type: AWS::EC2::Route 980 | Properties: 981 | RouteTableId: !Ref InspectionVPC1CWANRTB2 982 | DestinationCidrBlock: 0.0.0.0/0 983 | VpcEndpointId: !GetAtt RetrieveInspectionVPC1AnfwVpceIds.FwVpceId2 984 | 985 | # Create default route in ANFW route tables with NAT Gateway as the next hop: 986 | InspectionVPC1FirewallRTB1DefaultRoute: 987 | Type: AWS::EC2::Route 988 | Properties: 989 | RouteTableId: !Ref InspectionVPC1FirewallRTB1 990 | DestinationCidrBlock: 0.0.0.0/0 991 | NatGatewayId: !Ref InspectionVPC1NATGW1 992 | 993 | InspectionVPC1FirewallRTB2DefaultRoute: 994 | Type: AWS::EC2::Route 995 | Properties: 996 | RouteTableId: !Ref InspectionVPC1FirewallRTB2 997 | DestinationCidrBlock: 0.0.0.0/0 998 | NatGatewayId: !Ref InspectionVPC1NATGW2 999 | 1000 | # Create summary route in ANFW route tables with Core Network ARN as the next hop: 1001 | InspectionVPC1FirewallRTB1SummaryRoute: 1002 | DependsOn: [InspectionVPC1CWANAttachment] 1003 | Type: AWS::EC2::Route 1004 | Properties: 1005 | RouteTableId: !Ref InspectionVPC1FirewallRTB1 1006 | DestinationCidrBlock: 10.0.0.0/8 1007 | CoreNetworkArn: !Ref CoreNetworkArn 1008 | 1009 | InspectionVPC1FirewallRTB2SummaryRoute: 1010 | DependsOn: [InspectionVPC1CWANAttachment] 1011 | Type: AWS::EC2::Route 1012 | Properties: 1013 | RouteTableId: !Ref InspectionVPC1FirewallRTB2 1014 | DestinationCidrBlock: 10.0.0.0/8 1015 | CoreNetworkArn: !Ref CoreNetworkArn 1016 | 1017 | # Create default route in Public route tables with Internet Gateway as the next hop: 1018 | InspectionVPC1PublicRTB1DefaultRoute: 1019 | DependsOn: [InspectionVPC1IGWAttachment] 1020 | Type: AWS::EC2::Route 1021 | Properties: 1022 | RouteTableId: !Ref InspectionVPC1PublicRTB1 1023 | DestinationCidrBlock: 0.0.0.0/0 1024 | GatewayId: !Ref InspectionVPC1IGW 1025 | 1026 | InspectionVPC1PublicRTB2DefaultRoute: 1027 | DependsOn: [InspectionVPC1IGWAttachment] 1028 | Type: AWS::EC2::Route 1029 | Properties: 1030 | RouteTableId: !Ref InspectionVPC1PublicRTB2 1031 | DestinationCidrBlock: 0.0.0.0/0 1032 | GatewayId: !Ref InspectionVPC1IGW 1033 | 1034 | # Create summary route in Public route tables with firewall endpoint as the next hop: 1035 | InspectionVPC1PublicRTB1SummaryRoute: 1036 | Type: AWS::EC2::Route 1037 | Properties: 1038 | RouteTableId: !Ref InspectionVPC1PublicRTB1 1039 | DestinationCidrBlock: 10.0.0.0/8 1040 | VpcEndpointId: !GetAtt RetrieveInspectionVPC1AnfwVpceIds.FwVpceId1 1041 | 1042 | InspectionVPC1PublicRTB2SummaryRoute: 1043 | Type: AWS::EC2::Route 1044 | Properties: 1045 | RouteTableId: !Ref InspectionVPC1PublicRTB2 1046 | DestinationCidrBlock: 10.0.0.0/8 1047 | VpcEndpointId: !GetAtt RetrieveInspectionVPC1AnfwVpceIds.FwVpceId2 1048 | 1049 | # ---------- End of Inspection VPC 1 Resources ---------- -------------------------------------------------------------------------------- /outbound_inspection_with_aws_cloud_wan/region3_resources.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Description: >- 3 | AWS Cloud WAN Egress Inspection Architecture - Region 1 resources 4 | Transform: 'AWS::LanguageExtensions' 5 | 6 | Metadata: 7 | AWS::CloudFormation::Interface: 8 | ParameterGroups: 9 | - Label: 10 | default: Cloud WAN Parameters 11 | Parameters: 12 | - CoreNetworkArn 13 | - CoreNetworkId 14 | - Label: 15 | default: EC2 Parameters 16 | Parameters: 17 | - InstanceType 18 | - InstanceDiskSize 19 | - LatestAmiId 20 | 21 | Parameters: 22 | CoreNetworkArn: 23 | Description: Cloud WAN Core Network ARN 24 | Type: String 25 | ConstraintDescription: Cloud WAN Core Network ARN of type string required 26 | CoreNetworkId: 27 | Description: Cloud WAN Core Network ID 28 | Type: String 29 | ConstraintDescription: Cloud WAN Core Network ID of type string required 30 | InstanceType: 31 | Description: >- 32 | EC2 instance type for the workload instance. Default is set to t2.micro 33 | Default: t2.micro 34 | Type: String 35 | ConstraintDescription: Should be a valid EC2 instance type 36 | InstanceDiskSize: 37 | Description: EC2 instance disk size in GB. Default is set to 8GB 38 | Default: 8 39 | AllowedValues: [8] 40 | Type: Number 41 | ConstraintDescription: Should be a valid instance size in GB 42 | LatestAmiId: 43 | Description: Latest EC2 AMI from Systems Manager Parameter Store 44 | Type: "AWS::SSM::Parameter::Value" 45 | Default: "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64" 46 | ConstraintDescription: Must be a valid EC2 AMI from Systems Manager 47 | 48 | Resources: 49 | # ---------- Lambda Role ---------- 50 | IamRoleLambdaCFn: 51 | Type: AWS::IAM::Role 52 | Properties: 53 | AssumeRolePolicyDocument: 54 | Version: 2012-10-17 55 | Statement: 56 | - Effect: Allow 57 | Principal: 58 | Service: 59 | - lambda.amazonaws.com 60 | Action: 61 | - sts:AssumeRole 62 | Description: Provides permissions for Lambda functions. 63 | ManagedPolicyArns: 64 | - arn:aws:iam::aws:policy/AdministratorAccess 65 | # ---------- End of Lambda Role ---------- 66 | 67 | # ---------- Prod VPC 3 Resources ---------- 68 | # ---------- Prod VPC 3 ---------- 69 | ProdVPC3: 70 | Type: AWS::EC2::VPC 71 | Properties: 72 | CidrBlock: 10.3.0.0/16 73 | Tags: 74 | - Key: Name 75 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3" 76 | 77 | # ---------- Prod VPC 3 Subnets ---------- 78 | # CWAN Subnets: 79 | ProdVPC3CWANSubnet1: 80 | Type: AWS::EC2::Subnet 81 | Properties: 82 | VpcId: 83 | Ref: ProdVPC3 84 | CidrBlock: 10.3.0.0/28 85 | AvailabilityZone: !Select 86 | - 0 87 | - Fn::GetAZs: !Ref AWS::Region 88 | MapPublicIpOnLaunch: false 89 | Tags: 90 | - Key: Name 91 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-cwan-subnet1" 92 | 93 | ProdVPC3CWANSubnet2: 94 | Type: AWS::EC2::Subnet 95 | Properties: 96 | VpcId: !Ref ProdVPC3 97 | CidrBlock: 10.3.0.16/28 98 | AvailabilityZone: !Select [1, Fn::GetAZs: !Ref AWS::Region] 99 | MapPublicIpOnLaunch: false 100 | Tags: 101 | - Key: Name 102 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-cwan-subnet2" 103 | 104 | # Endpoint Subnets: 105 | ProdVPC3EndpointSubnet1: 106 | Type: AWS::EC2::Subnet 107 | Properties: 108 | VpcId: !Ref ProdVPC3 109 | CidrBlock: 10.3.0.32/28 110 | AvailabilityZone: !Select [0, Fn::GetAZs: !Ref AWS::Region] 111 | MapPublicIpOnLaunch: false 112 | Tags: 113 | - Key: Name 114 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-endpoint-subnet1" 115 | 116 | ProdVPC3EndpointSubnet2: 117 | Type: AWS::EC2::Subnet 118 | Properties: 119 | VpcId: !Ref ProdVPC3 120 | CidrBlock: 10.3.0.48/28 121 | AvailabilityZone: !Select [1, Fn::GetAZs: !Ref AWS::Region] 122 | MapPublicIpOnLaunch: false 123 | Tags: 124 | - Key: Name 125 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-endpoint-subnet2" 126 | 127 | # Workload Subnets: 128 | ProdVPC3WorkloadSubnet1: 129 | Type: AWS::EC2::Subnet 130 | Properties: 131 | VpcId: !Ref ProdVPC3 132 | CidrBlock: 10.3.1.0/24 133 | AvailabilityZone: !Select [0, Fn::GetAZs: !Ref AWS::Region] 134 | MapPublicIpOnLaunch: false 135 | Tags: 136 | - Key: Name 137 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-workload-subnet1" 138 | 139 | ProdVPC3WorkloadSubnet2: 140 | Type: AWS::EC2::Subnet 141 | Properties: 142 | VpcId: !Ref ProdVPC3 143 | CidrBlock: 10.3.2.0/24 144 | AvailabilityZone: !Select [1, Fn::GetAZs: !Ref AWS::Region] 145 | MapPublicIpOnLaunch: false 146 | Tags: 147 | - Key: Name 148 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-workload-subnet2" 149 | 150 | # ---------- Prod VPC 3 Route Tables and Subnet Associations ---------- 151 | # CWAN Route Tables: 152 | ProdVPC3CWANRTB1: 153 | Type: AWS::EC2::RouteTable 154 | Properties: 155 | VpcId: !Ref ProdVPC3 156 | Tags: 157 | - Key: Name 158 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-cwan-rtb1" 159 | 160 | ProdVPC3CWANRTB2: 161 | Type: AWS::EC2::RouteTable 162 | Properties: 163 | VpcId: !Ref ProdVPC3 164 | Tags: 165 | - Key: Name 166 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-cwan-rtb2" 167 | 168 | # CWAN Route Tables Subnet Associations: 169 | ProdVPC3CWANRTB1Association: 170 | Type: AWS::EC2::SubnetRouteTableAssociation 171 | Properties: 172 | RouteTableId: !Ref ProdVPC3CWANRTB1 173 | SubnetId: !Ref ProdVPC3CWANSubnet1 174 | 175 | ProdVPC3CWANRTB2Association: 176 | Type: AWS::EC2::SubnetRouteTableAssociation 177 | Properties: 178 | RouteTableId: !Ref ProdVPC3CWANRTB2 179 | SubnetId: !Ref ProdVPC3CWANSubnet2 180 | 181 | # Endpoint Route Tables: 182 | ProdVPC3EndpointRTB1: 183 | Type: AWS::EC2::RouteTable 184 | Properties: 185 | VpcId: !Ref ProdVPC3 186 | Tags: 187 | - Key: Name 188 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-endpoint-rtb1" 189 | 190 | ProdVPC3EndpointRTB2: 191 | Type: AWS::EC2::RouteTable 192 | Properties: 193 | VpcId: !Ref ProdVPC3 194 | Tags: 195 | - Key: Name 196 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-endpoint-rtb2" 197 | 198 | # Endpoint Route Tables Subnet Associations: 199 | ProdVPC3EndpointRTB1Association: 200 | Type: AWS::EC2::SubnetRouteTableAssociation 201 | Properties: 202 | RouteTableId: !Ref ProdVPC3EndpointRTB1 203 | SubnetId: !Ref ProdVPC3EndpointSubnet1 204 | 205 | ProdVPC3EndpointRTB2Association: 206 | Type: AWS::EC2::SubnetRouteTableAssociation 207 | Properties: 208 | RouteTableId: !Ref ProdVPC3EndpointRTB2 209 | SubnetId: !Ref ProdVPC3EndpointSubnet2 210 | 211 | # Workload Route Tables: 212 | ProdVPC3WorkloadRTB1: 213 | Type: AWS::EC2::RouteTable 214 | Properties: 215 | VpcId: !Ref ProdVPC3 216 | Tags: 217 | - Key: Name 218 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-workload-rtb1" 219 | 220 | ProdVPC3WorkloadRTB2: 221 | Type: AWS::EC2::RouteTable 222 | Properties: 223 | VpcId: !Ref ProdVPC3 224 | Tags: 225 | - Key: Name 226 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-workload-rtb2" 227 | 228 | # Workload Route Tables Subnet Associations: 229 | ProdVPC3WorkloadRTB1Association: 230 | Type: AWS::EC2::SubnetRouteTableAssociation 231 | Properties: 232 | RouteTableId: !Ref ProdVPC3WorkloadRTB1 233 | SubnetId: !Ref ProdVPC3WorkloadSubnet1 234 | 235 | ProdVPC3WorkloadRTB2Association: 236 | Type: AWS::EC2::SubnetRouteTableAssociation 237 | Properties: 238 | RouteTableId: !Ref ProdVPC3WorkloadRTB2 239 | SubnetId: !Ref ProdVPC3WorkloadSubnet2 240 | 241 | # ---------- AWS Cloud WAN attachment and default route---------- 242 | # Create Prod VPC 3 Cloud WAN Attachment: 243 | ProdVPC3CWANAttachment: 244 | Type: AWS::NetworkManager::VpcAttachment 245 | Properties: 246 | CoreNetworkId: !Ref CoreNetworkId 247 | VpcArn: !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/${ProdVPC3}" 248 | SubnetArns: 249 | - !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${ProdVPC3CWANSubnet1}" 250 | - !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${ProdVPC3CWANSubnet2}" 251 | Tags: 252 | - Key: Name 253 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-attachment" 254 | - Key: domain 255 | Value: Production 256 | 257 | # Create default route in Workload route tables with Core Network ARN has the next hop: 258 | ProdVPC3WorkloadRTB1DefaultRoute: 259 | DependsOn: 260 | - ProdVPC3CWANAttachment 261 | Type: AWS::EC2::Route 262 | Properties: 263 | RouteTableId: !Ref ProdVPC3WorkloadRTB1 264 | DestinationCidrBlock: 0.0.0.0/0 265 | CoreNetworkArn: !Ref CoreNetworkArn 266 | 267 | ProdVPC3WorkloadRTB2DefaultRoute: 268 | DependsOn: [ProdVPC3CWANAttachment] 269 | Type: AWS::EC2::Route 270 | Properties: 271 | RouteTableId: !Ref ProdVPC3WorkloadRTB2 272 | DestinationCidrBlock: 0.0.0.0/0 273 | CoreNetworkArn: !Ref CoreNetworkArn 274 | 275 | # ---------- Create Security Groups ---------- 276 | # Create Workload SG: 277 | ProdVPC3WorkloadSecurityGroup: 278 | Type: AWS::EC2::SecurityGroup 279 | Properties: 280 | GroupDescription: Prod VPC 3 Workload EC2 Instance Security Group 281 | GroupName: !Sub "cwanegress-${AWS::Region}-prod-vpc3-workload-sg" 282 | VpcId: !Ref ProdVPC3 283 | SecurityGroupIngress: 284 | - CidrIp: 10.0.0.0/8 285 | Description: Allowing inbound connection from 10.0.0.0/8 CIDR. 286 | IpProtocol: "-1" 287 | FromPort: -1 288 | ToPort: -1 289 | SecurityGroupEgress: 290 | - CidrIp: 0.0.0.0/0 291 | Description: Allowing outbound connection to 0.0.0.0/0 CIDR. 292 | IpProtocol: "-1" 293 | FromPort: -1 294 | ToPort: -1 295 | Tags: 296 | - Key: Name 297 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-workload-sg" 298 | 299 | # Create EC2 instance connect endpoint SG: 300 | ProdVPC3EndpointSecurityGroup: 301 | Type: AWS::EC2::SecurityGroup 302 | Properties: 303 | GroupDescription: Prod VPC 3 Endpoint Security Group 304 | GroupName: !Sub "cwanegress-${AWS::Region}-prod-vpc3-endpoint-sg" 305 | VpcId: !Ref ProdVPC3 306 | Tags: 307 | - Key: Name 308 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-endpoint-sg" 309 | 310 | ProdVPC3WorkloadSecurityGroupIngressEIC: 311 | Type: AWS::EC2::SecurityGroupIngress 312 | Properties: 313 | GroupId: !Ref ProdVPC3WorkloadSecurityGroup 314 | Description: Allowing inbound connenciton from EC2 Instance Connect (EIC). 315 | IpProtocol: tcp 316 | FromPort: 22 317 | ToPort: 22 318 | SourceSecurityGroupId: !Ref ProdVPC3EndpointSecurityGroup 319 | 320 | ProdVPC3EndpointSecurityGroupEgressEIC: 321 | Type: AWS::EC2::SecurityGroupEgress 322 | Properties: 323 | GroupId: !Ref ProdVPC3EndpointSecurityGroup 324 | Description: Allowing outbound connection to EC2 Instance Connect (EIC). 325 | IpProtocol: tcp 326 | FromPort: 22 327 | ToPort: 22 328 | DestinationSecurityGroupId: !Ref ProdVPC3WorkloadSecurityGroup 329 | 330 | # ---------- EC2 Instance Connect Endpoint ---------- 331 | ProdVPC3EICEndpoint: 332 | Type: AWS::EC2::InstanceConnectEndpoint 333 | Properties: 334 | PreserveClientIp: false 335 | SecurityGroupIds: 336 | - !Ref ProdVPC3EndpointSecurityGroup 337 | SubnetId: !Ref ProdVPC3EndpointSubnet1 338 | Tags: 339 | - Key: Name 340 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-eic" 341 | 342 | # ---------- Create Worload EC2 Intances ---------- 343 | # Workoad Instance 1: 344 | ProdVPC3WorkloadInstance1: 345 | Type: AWS::EC2::Instance 346 | Properties: 347 | ImageId: !Ref LatestAmiId 348 | InstanceType: !Ref InstanceType 349 | SubnetId: !Ref ProdVPC3WorkloadSubnet1 350 | SecurityGroupIds: 351 | - !Ref ProdVPC3WorkloadSecurityGroup 352 | Tags: 353 | - Key: Name 354 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-workload-instance1" 355 | UserData: 356 | Fn::Base64: | 357 | cat <> /home/ec2-user/usedatascript.sh 358 | #!/bin/bash -ex 359 | 360 | # Install packages: 361 | sudo yum update -y; 362 | sudo yum install jq -y; 363 | sudo yum install httpd -y; 364 | sudo yum install htop -y; 365 | 366 | # Enable and start httpd 367 | sudo systemctl enable httpd; 368 | sudo systemctl start httpd; 369 | 370 | # Configure hostname: 371 | sudo hostnamectl set-hostname prod-vpc3-workload-${AvailabilityZone}; 372 | 373 | # Configure SSH client alive interval for ssh session timeout: 374 | echo 'ClientAliveInterval 60' | sudo tee --append /etc/ssh/sshd_config; 375 | service sshd restart; 376 | 377 | # Set dark background for vim: 378 | touch /home/ec2-user/.vimrc; 379 | echo "set background=dark" >> /home/ec2-user/.vimrc; 380 | 381 | # Define variables: 382 | curl --silent http://169.254.169.254/latest/dynamic/instance-identity/document > /home/ec2-user/iid; 383 | export instance_interface=$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/); 384 | export instance_vpcid=$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/$instance_interface/vpc-id); 385 | export instance_az=$(cat /home/ec2-user/iid |grep 'availability' | awk -F': ' '{print $2}' | awk -F',' '{print $1}'); 386 | export instance_ip=$(cat /home/ec2-user/iid |grep 'privateIp' | awk -F': ' '{print $2}' | awk -F',' '{print $1}' | awk -F'"' '{print$2}'); 387 | export instance_region=$(cat /home/ec2-user/iid |grep 'region' | awk -F': ' '{print $2}' | awk -F',' '{print $1}' | awk -F'"' '{print$2}'); 388 | EOT 389 | 390 | # Add index.html 391 | touch /var/www/html/index.html; 392 | cat <> /var/www/html/index.html 393 | 394 | 395 | Prod VPC 3 Workload Instance 1 396 | 397 | 398 | 399 |

Welcome to AWS Cloud WAN Egress Inspection Architecture POC:

400 |

This is a simple web server running in $instance_az in $instance_region. Happy Testing!

401 | 402 | 403 | EOT 404 | 405 | # Workoad Instance 2: 406 | ProdVPC3WorkloadInstance2: 407 | Type: AWS::EC2::Instance 408 | Properties: 409 | ImageId: !Ref LatestAmiId 410 | InstanceType: !Ref InstanceType 411 | SubnetId: !Ref ProdVPC3WorkloadSubnet2 412 | SecurityGroupIds: 413 | - !Ref ProdVPC3WorkloadSecurityGroup 414 | Tags: 415 | - Key: Name 416 | Value: !Sub "cwanegress-${AWS::Region}-prod-vpc3-workload-instance2" 417 | UserData: 418 | Fn::Base64: | 419 | cat <> /home/ec2-user/usedatascript.sh 420 | #!/bin/bash -ex 421 | 422 | # Install packages: 423 | sudo yum update -y; 424 | sudo yum install jq -y; 425 | sudo yum install httpd -y; 426 | sudo yum install htop -y; 427 | 428 | # Enable and start httpd 429 | sudo systemctl enable httpd; 430 | sudo systemctl start httpd; 431 | 432 | # Configure hostname: 433 | sudo hostnamectl set-hostname prod-vpc3-workload-${AvailabilityZone}; 434 | 435 | # Configure SSH client alive interval for ssh session timeout: 436 | echo 'ClientAliveInterval 60' | sudo tee --append /etc/ssh/sshd_config; 437 | service sshd restart; 438 | 439 | # Set dark background for vim: 440 | touch /home/ec2-user/.vimrc; 441 | echo "set background=dark" >> /home/ec2-user/.vimrc; 442 | 443 | # Define variables: 444 | curl --silent http://169.254.169.254/latest/dynamic/instance-identity/document > /home/ec2-user/iid; 445 | export instance_interface=$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/); 446 | export instance_vpcid=$(curl --silent http://169.254.169.254/latest/meta-data/network/interfaces/macs/$instance_interface/vpc-id); 447 | export instance_az=$(cat /home/ec2-user/iid |grep 'availability' | awk -F': ' '{print $2}' | awk -F',' '{print $1}'); 448 | export instance_ip=$(cat /home/ec2-user/iid |grep 'privateIp' | awk -F': ' '{print $2}' | awk -F',' '{print $1}' | awk -F'"' '{print$2}'); 449 | export instance_region=$(cat /home/ec2-user/iid |grep 'region' | awk -F': ' '{print $2}' | awk -F',' '{print $1}' | awk -F'"' '{print$2}'); 450 | EOT 451 | 452 | # Add index.html 453 | touch /var/www/html/index.html; 454 | cat <> /var/www/html/index.html 455 | 456 | 457 | Prod VPC 3 Workload Instance 2 458 | 459 | 460 | 461 |

Welcome to AWS Cloud WAN Egress Inspection Architecture POC:

462 |

This is a simple web server running in $instance_az in $instance_region. Happy Testing!

463 | 464 | 465 | EOT 466 | 467 | # ---------- End of Prod VPC 3 Resources ---------- 468 | 469 | # ---------- Inspection VPC 3 Resources ---------- 470 | # ---------- Inspection VPC 3 ---------- 471 | InspectionVPC3: 472 | Type: AWS::EC2::VPC 473 | Properties: 474 | CidrBlock: 100.64.3.0/24 475 | Tags: 476 | - Key: Name 477 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3" 478 | 479 | # ---------- Inspection VPC 3 IGW and IGW Attachment---------- 480 | InspectionVPC3IGW: 481 | Type: AWS::EC2::InternetGateway 482 | Properties: 483 | Tags: 484 | - Key: Name 485 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-igw" 486 | 487 | InspectionVPC3IGWAttachment: 488 | Type: AWS::EC2::VPCGatewayAttachment 489 | Properties: 490 | InternetGatewayId: !Ref InspectionVPC3IGW 491 | VpcId: !Ref InspectionVPC3 492 | 493 | # ---------- Inspection VPC 3 Subnets ---------- 494 | # CWAN Subnets: 495 | InspectionVPC3CWANSubnet1: 496 | Type: AWS::EC2::Subnet 497 | Properties: 498 | VpcId: !Ref InspectionVPC3 499 | CidrBlock: 100.64.3.0/28 500 | AvailabilityZone: !Select 501 | - 0 502 | - Fn::GetAZs: !Ref AWS::Region 503 | MapPublicIpOnLaunch: false 504 | Tags: 505 | - Key: Name 506 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-cwan-subnet1" 507 | 508 | InspectionVPC3CWANSubnet2: 509 | Type: AWS::EC2::Subnet 510 | Properties: 511 | VpcId: !Ref InspectionVPC3 512 | CidrBlock: 100.64.3.16/28 513 | AvailabilityZone: !Select [1, Fn::GetAZs: !Ref AWS::Region] 514 | MapPublicIpOnLaunch: false 515 | Tags: 516 | - Key: Name 517 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-cwan-subnet2" 518 | 519 | # Firewall Endpoint Subnets: 520 | InspectionVPC3FirewallSubnet1: 521 | Type: AWS::EC2::Subnet 522 | Properties: 523 | VpcId: !Ref InspectionVPC3 524 | CidrBlock: 100.64.3.32/28 525 | AvailabilityZone: !Select [0, Fn::GetAZs: !Ref AWS::Region] 526 | MapPublicIpOnLaunch: false 527 | Tags: 528 | - Key: Name 529 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-fwe-subnet1" 530 | 531 | InspectionVPC3FirewallSubnet2: 532 | Type: AWS::EC2::Subnet 533 | Properties: 534 | VpcId: !Ref InspectionVPC3 535 | CidrBlock: 100.64.3.48/28 536 | AvailabilityZone: !Select [1, Fn::GetAZs: !Ref AWS::Region] 537 | MapPublicIpOnLaunch: false 538 | Tags: 539 | - Key: Name 540 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-fwe-subnet2" 541 | 542 | # Public Subnets: 543 | InspectionVPC3PublicSubnet1: 544 | Type: AWS::EC2::Subnet 545 | Properties: 546 | VpcId: !Ref InspectionVPC3 547 | CidrBlock: 100.64.3.64/28 548 | AvailabilityZone: !Select [0, Fn::GetAZs: !Ref AWS::Region] 549 | MapPublicIpOnLaunch: true 550 | Tags: 551 | - Key: Name 552 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-public-subnet1" 553 | 554 | InspectionVPC3PublicSubnet2: 555 | Type: AWS::EC2::Subnet 556 | Properties: 557 | VpcId: !Ref InspectionVPC3 558 | CidrBlock: 100.64.3.80/28 559 | AvailabilityZone: !Select [1, Fn::GetAZs: !Ref AWS::Region] 560 | MapPublicIpOnLaunch: true 561 | Tags: 562 | - Key: Name 563 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-public-subnet2" 564 | 565 | # ---------- Inspection VPC 3 Route Tables and Subnet Associations ---------- 566 | # CWAN Route Tables: 567 | InspectionVPC3CWANRTB1: 568 | Type: AWS::EC2::RouteTable 569 | Properties: 570 | VpcId: !Ref InspectionVPC3 571 | Tags: 572 | - Key: Name 573 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-cwan-rtb1" 574 | 575 | InspectionVPC3CWANRTB2: 576 | Type: AWS::EC2::RouteTable 577 | Properties: 578 | VpcId: !Ref InspectionVPC3 579 | Tags: 580 | - Key: Name 581 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-cwan-rtb2" 582 | 583 | # CWAN Route Tables Subnet Associations: 584 | InspectionVPC3CWANRTB1Association: 585 | Type: AWS::EC2::SubnetRouteTableAssociation 586 | Properties: 587 | RouteTableId: !Ref InspectionVPC3CWANRTB1 588 | SubnetId: !Ref InspectionVPC3CWANSubnet1 589 | 590 | InspectionVPC3CWANRTB2Association: 591 | Type: AWS::EC2::SubnetRouteTableAssociation 592 | Properties: 593 | RouteTableId: !Ref InspectionVPC3CWANRTB2 594 | SubnetId: !Ref InspectionVPC3CWANSubnet2 595 | 596 | # Firewall Endpoint Route Tables: 597 | InspectionVPC3FirewallRTB1: 598 | Type: AWS::EC2::RouteTable 599 | Properties: 600 | VpcId: !Ref InspectionVPC3 601 | Tags: 602 | - Key: Name 603 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-fwe-rtb1" 604 | 605 | InspectionVPC3FirewallRTB2: 606 | Type: AWS::EC2::RouteTable 607 | Properties: 608 | VpcId: !Ref InspectionVPC3 609 | Tags: 610 | - Key: Name 611 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-fwe-rtb2" 612 | 613 | # Firewall Endpoint Route Tables Subnet Associations: 614 | InspectionVPC3FirewallRTB1Association: 615 | Type: AWS::EC2::SubnetRouteTableAssociation 616 | Properties: 617 | RouteTableId: !Ref InspectionVPC3FirewallRTB1 618 | SubnetId: !Ref InspectionVPC3FirewallSubnet1 619 | 620 | InspectionVPC3FirewallRTB2Association: 621 | Type: AWS::EC2::SubnetRouteTableAssociation 622 | Properties: 623 | RouteTableId: !Ref InspectionVPC3FirewallRTB2 624 | SubnetId: !Ref InspectionVPC3FirewallSubnet2 625 | 626 | # Public Route Tables: 627 | InspectionVPC3PublicRTB1: 628 | Type: AWS::EC2::RouteTable 629 | Properties: 630 | VpcId: !Ref InspectionVPC3 631 | Tags: 632 | - Key: Name 633 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-public-rtb1" 634 | 635 | InspectionVPC3PublicRTB2: 636 | Type: AWS::EC2::RouteTable 637 | Properties: 638 | VpcId: !Ref InspectionVPC3 639 | Tags: 640 | - Key: Name 641 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-public-rtb2" 642 | 643 | # Public Route Tables Subnet Associations: 644 | InspectionVPC3PublicRTB1Association: 645 | Type: AWS::EC2::SubnetRouteTableAssociation 646 | Properties: 647 | RouteTableId: !Ref InspectionVPC3PublicRTB1 648 | SubnetId: !Ref InspectionVPC3PublicSubnet1 649 | 650 | InspectionVPC3PublicRTB2Association: 651 | Type: AWS::EC2::SubnetRouteTableAssociation 652 | Properties: 653 | RouteTableId: !Ref InspectionVPC3PublicRTB2 654 | SubnetId: !Ref InspectionVPC3PublicSubnet2 655 | 656 | # ---------- Inspection VPC 3 NAT Gateways and EIPs ---------- 657 | # NAT Gateway 1: 658 | InspectionVPC3NATGW1EIP1: 659 | Type: AWS::EC2::EIP 660 | Properties: 661 | Domain: vpc 662 | 663 | InspectionVPC3NATGW1: 664 | Type: AWS::EC2::NatGateway 665 | Properties: 666 | AllocationId: !GetAtt InspectionVPC3NATGW1EIP1.AllocationId 667 | SubnetId: !Ref InspectionVPC3PublicSubnet1 668 | Tags: 669 | - Key: Name 670 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-natgw1" 671 | 672 | # NAT Gateway 2: 673 | InspectionVPC3NATGW1EIP2: 674 | Type: AWS::EC2::EIP 675 | Properties: 676 | Domain: vpc 677 | 678 | InspectionVPC3NATGW2: 679 | Type: AWS::EC2::NatGateway 680 | Properties: 681 | AllocationId: !GetAtt InspectionVPC3NATGW1EIP2.AllocationId 682 | SubnetId: !Ref InspectionVPC3PublicSubnet2 683 | Tags: 684 | - Key: Name 685 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-natgw2" 686 | 687 | # ---------- AWS Network Firewall (ANFW) and related resources ---------- 688 | # Network Firewall resource 689 | InspectionVPC3Anfw1: 690 | Type: AWS::NetworkFirewall::Firewall 691 | DependsOn: [InspectionVPC3Anfw1RulesPolicy1] 692 | Properties: 693 | FirewallName: !Sub "cwanegress-${AWS::Region}-insp-vpc3-anfw1" 694 | FirewallPolicyArn: !GetAtt InspectionVPC3Anfw1RulesPolicy1.FirewallPolicyArn 695 | VpcId: !Ref InspectionVPC3 696 | SubnetMappings: 697 | - SubnetId: !Ref InspectionVPC3FirewallSubnet1 698 | - SubnetId: !Ref InspectionVPC3FirewallSubnet2 699 | 700 | # Firewall Policy 701 | InspectionVPC3Anfw1RulesPolicy1: 702 | Type: AWS::NetworkFirewall::FirewallPolicy 703 | Properties: 704 | FirewallPolicy: 705 | StatelessDefaultActions: 706 | - aws:forward_to_sfe 707 | StatelessFragmentDefaultActions: 708 | - aws:forward_to_sfe 709 | StatelessRuleGroupReferences: 710 | - Priority: 10 711 | ResourceArn: !GetAtt InspectionVPC3Anfw1StatelessDropRemoteRuleGroup.RuleGroupArn 712 | StatefulEngineOptions: 713 | RuleOrder: STRICT_ORDER 714 | StatefulDefaultActions: 715 | - aws:drop_established 716 | - aws:alert_established 717 | StatefulRuleGroupReferences: 718 | - ResourceArn: !GetAtt InspectionVPC3Anfw1DomainListStatefulAllowRuleGroup.RuleGroupArn 719 | Priority: 100 720 | - ResourceArn: !GetAtt InspectionVPC3Anfw1StandardStatefulRuleGroup.RuleGroupArn 721 | Priority: 200 722 | 723 | FirewallPolicyName: !Sub "cwanegress-${AWS::Region}-insp-vpc3-anfw1-policy1" 724 | Tags: 725 | - Key: Name 726 | Value: !Sub "cwanegress-${AWS::Region}-firewall-policy-v1" 727 | 728 | # Stateless Rule Group - Dropping any SSH connection 729 | InspectionVPC3Anfw1StatelessDropRemoteRuleGroup: 730 | Type: AWS::NetworkFirewall::RuleGroup 731 | Properties: 732 | Capacity: 100 733 | RuleGroupName: !Sub "cwanegress-${AWS::Region}-insp-vpc3-anfw1-dropremote-rg" 734 | Description: Drop remote SSH connections 735 | Type: STATELESS 736 | RuleGroup: 737 | RulesSource: 738 | StatelessRulesAndCustomActions: 739 | StatelessRules: 740 | - Priority: 1 741 | RuleDefinition: 742 | MatchAttributes: 743 | Protocols: 744 | - 6 745 | Sources: 746 | - AddressDefinition: 0.0.0.0/0 747 | SourcePorts: 748 | - FromPort: 22 749 | ToPort: 22 750 | Destinations: 751 | - AddressDefinition: 0.0.0.0/0 752 | DestinationPorts: 753 | - FromPort: 22 754 | ToPort: 22 755 | Actions: 756 | - "aws:drop" 757 | 758 | # Domain list Stateful Rule Group: 759 | InspectionVPC3Anfw1DomainListStatefulAllowRuleGroup: 760 | Type: AWS::NetworkFirewall::RuleGroup 761 | Properties: 762 | Capacity: 100 763 | RuleGroupName: !Sub "cwanegress-${AWS::Region}-insp-vpc3-anfw1-domain-allow-rg" 764 | Description: Allowing access to desired domains 765 | Type: STATEFUL 766 | RuleGroup: 767 | StatefulRuleOptions: 768 | RuleOrder: STRICT_ORDER 769 | RuleVariables: 770 | IPSets: 771 | HOME_NET: 772 | Definition: 773 | - "10.0.0.0/8" 774 | RulesSource: 775 | RulesSourceList: 776 | TargetTypes: 777 | - HTTP_HOST 778 | - TLS_SNI 779 | Targets: 780 | - ".amazon.com" 781 | - ".amazonaws.com" 782 | - ".google.com" 783 | GeneratedRulesType: "ALLOWLIST" 784 | Tags: 785 | - Key: Name 786 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-anfw1-domain-allow-rg" 787 | 788 | # Standard Stateful Rule Group: 789 | InspectionVPC3Anfw1StandardStatefulRuleGroup: 790 | Type: AWS::NetworkFirewall::RuleGroup 791 | Properties: 792 | RuleGroupName: !Sub "cwanegress-${AWS::Region}-insp-vpc3-anfw1-standard-stateful-rg" 793 | Type: STATEFUL 794 | Capacity: 100 795 | RuleGroup: 796 | StatefulRuleOptions: 797 | RuleOrder: STRICT_ORDER 798 | RuleVariables: 799 | IPSets: 800 | HOME_NET: 801 | Definition: 802 | - "10.0.0.0/8" 803 | RulesSource: 804 | StatefulRules: 805 | - Action: ALERT 806 | Header: 807 | Direction: ANY 808 | Protocol: ICMP 809 | Destination: ANY 810 | Source: $HOME_NET 811 | DestinationPort: ANY 812 | SourcePort: ANY 813 | RuleOptions: 814 | - Keyword: "sid:10001" 815 | - Action: PASS 816 | Header: 817 | Direction: ANY 818 | Protocol: ICMP 819 | Destination: ANY 820 | Source: $HOME_NET 821 | DestinationPort: ANY 822 | SourcePort: ANY 823 | RuleOptions: 824 | - Keyword: "sid:10002" 825 | - Action: ALERT 826 | Header: 827 | Direction: ANY 828 | Protocol: UDP 829 | Destination: ANY 830 | Source: $HOME_NET 831 | DestinationPort: ANY 832 | SourcePort: ANY 833 | RuleOptions: 834 | - Keyword: "sid:20001" 835 | - Action: PASS 836 | Header: 837 | Direction: ANY 838 | Protocol: UDP 839 | Destination: ANY 840 | Source: $HOME_NET 841 | DestinationPort: ANY 842 | SourcePort: ANY 843 | RuleOptions: 844 | - Keyword: "sid:20002" 845 | Tags: 846 | - Key: Name 847 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-anfw1-standard-stateful-rg" 848 | 849 | # Firewall Logs - Flow 850 | InspectionVPC3Anfw1LogFlowGroup: 851 | Type: AWS::Logs::LogGroup 852 | Properties: 853 | LogGroupName: !Sub "/anfw1/flow/cwanegress/${AWS::Region}" 854 | 855 | # Firewall Logs - Aler 856 | InspectionVPC3Anfw1LogAlertGroup: 857 | Type: AWS::Logs::LogGroup 858 | Properties: 859 | LogGroupName: !Sub "/anfw1/alert/cwanegress/${AWS::Region}" 860 | 861 | # Firewall Log - Policy reference 862 | InspectionVPC3Anfw1Log: 863 | Type: AWS::NetworkFirewall::LoggingConfiguration 864 | Properties: 865 | FirewallArn: !Ref InspectionVPC3Anfw1 866 | LoggingConfiguration: 867 | LogDestinationConfigs: 868 | - LogType: FLOW 869 | LogDestinationType: CloudWatchLogs 870 | LogDestination: 871 | logGroup: !Sub "/anfw1/flow/cwanegress/${AWS::Region}" 872 | - LogType: ALERT 873 | LogDestinationType: CloudWatchLogs 874 | LogDestination: 875 | logGroup: !Sub "/anfw1/alert/cwanegress/${AWS::Region}" 876 | 877 | # ---------- Lambda and Custom Resource to retrieve ANFW Endpoint IDs ---------- 878 | # CloudWatch Log Group for DescribeVpceIdLambda Function: 879 | DescribeVpceIdLambdaLogGroup: 880 | Type: AWS::Logs::LogGroup 881 | Properties: 882 | LogGroupName: !Sub "/aws/lambda/cwanegress-${AWS::Region}-DescribeVpceId" 883 | 884 | # Lambda Function to retrieve ANFW Endpoint IDs: 885 | DescribeVpceIdLambda: 886 | Type: AWS::Lambda::Function 887 | DependsOn: DescribeVpceIdLambdaLogGroup 888 | Properties: 889 | FunctionName: !Sub "cwanegress-${AWS::Region}-DescribeVpceId" 890 | Handler: "index.handler" 891 | Role: !GetAtt IamRoleLambdaCFn.Arn 892 | Code: 893 | ZipFile: | 894 | import boto3 895 | import cfnresponse 896 | import json 897 | import logging 898 | 899 | def handler(event, context): 900 | logger = logging.getLogger() 901 | logger.setLevel(logging.INFO) 902 | 903 | responseData = {} 904 | responseStatus = cfnresponse.FAILED 905 | 906 | eventinfo = json.dumps(event) 907 | logger.info(f"Received event: {eventinfo}") 908 | 909 | if event["RequestType"] == "Delete": 910 | responseStatus = cfnresponse.SUCCESS 911 | cfnresponse.send(event, context, responseStatus, responseData, "CustomResourcePhysicalID") 912 | if event["RequestType"] == "Create": 913 | try: 914 | Az1 = event["ResourceProperties"]["Az1"] 915 | Az2 = event["ResourceProperties"]["Az2"] 916 | FwArn = event["ResourceProperties"]["FwArn"] 917 | except Exception as e: 918 | logger.info(f"AZ retrieval failure: {e}") 919 | 920 | try: 921 | nfw = boto3.client('network-firewall') 922 | except Exception as e: 923 | logger.info(f"boto3.client failure: {e}") 924 | 925 | try: 926 | NfwResponse=nfw.describe_firewall(FirewallArn=FwArn) 927 | VpceId1 = NfwResponse['FirewallStatus']['SyncStates'][Az1]['Attachment']['EndpointId'] 928 | VpceId2 = NfwResponse['FirewallStatus']['SyncStates'][Az2]['Attachment']['EndpointId'] 929 | except Exception as e: 930 | logger.info(f"ec2.describe_firewall failure: {e}") 931 | 932 | responseData['FwVpceId1'] = VpceId1 933 | responseData['FwVpceId2'] = VpceId2 934 | responseStatus = cfnresponse.SUCCESS 935 | print(f"VPCE ID1: {VpceId1}") 936 | print(f"VPCE ID2: {VpceId2}") 937 | print(f"response data: {responseData}") 938 | cfnresponse.send(event, context, responseStatus, responseData) 939 | Runtime: python3.12 940 | Timeout: 900 941 | 942 | # Custom resource to retrieve ANFW Endpoint IDs using Lambda function created above: 943 | RetrieveInspectionVPC3AnfwVpceIds: 944 | Type: Custom::DescribeVpcEndpoints 945 | Properties: 946 | ServiceToken: !GetAtt DescribeVpceIdLambda.Arn 947 | Az1: !Select [0, Fn::GetAZs: !Ref AWS::Region] 948 | Az2: !Select [1, Fn::GetAZs: !Ref AWS::Region] 949 | FwArn: !Ref InspectionVPC3Anfw1 950 | 951 | # ---------- AWS Cloud WAN attachment ---------- 952 | # Create Inspection VPC 3 Cloud WAN Attachment: 953 | InspectionVPC3CWANAttachment: 954 | Type: AWS::NetworkManager::VpcAttachment 955 | Properties: 956 | CoreNetworkId: !Ref CoreNetworkId 957 | VpcArn: !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/${InspectionVPC3}" 958 | SubnetArns: 959 | - !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${InspectionVPC3CWANSubnet1}" 960 | - !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${InspectionVPC3CWANSubnet2}" 961 | Tags: 962 | - Key: Name 963 | Value: !Sub "cwanegress-${AWS::Region}-insp-vpc3-attachment" 964 | - Key: domain 965 | Value: EgressInspection 966 | 967 | # ---------- Add Routes ---------- 968 | # Create default route in CWAN route tables with ANFW endpoints as the next hop: 969 | InspectionVPC3CWANRTB1DefaultRoute: 970 | DependsOn: [InspectionVPC3Anfw1] 971 | Type: AWS::EC2::Route 972 | Properties: 973 | RouteTableId: !Ref InspectionVPC3CWANRTB1 974 | DestinationCidrBlock: 0.0.0.0/0 975 | VpcEndpointId: !GetAtt RetrieveInspectionVPC3AnfwVpceIds.FwVpceId1 976 | 977 | InspectionVPC3CWANRTB2DefaultRoute: 978 | DependsOn: [InspectionVPC3Anfw1] 979 | Type: AWS::EC2::Route 980 | Properties: 981 | RouteTableId: !Ref InspectionVPC3CWANRTB2 982 | DestinationCidrBlock: 0.0.0.0/0 983 | VpcEndpointId: !GetAtt RetrieveInspectionVPC3AnfwVpceIds.FwVpceId2 984 | 985 | # Create default route in ANFW route tables with NAT Gateway as the next hop: 986 | InspectionVPC3FirewallRTB1DefaultRoute: 987 | Type: AWS::EC2::Route 988 | Properties: 989 | RouteTableId: !Ref InspectionVPC3FirewallRTB1 990 | DestinationCidrBlock: 0.0.0.0/0 991 | NatGatewayId: !Ref InspectionVPC3NATGW1 992 | 993 | InspectionVPC3FirewallRTB2DefaultRoute: 994 | Type: AWS::EC2::Route 995 | Properties: 996 | RouteTableId: !Ref InspectionVPC3FirewallRTB2 997 | DestinationCidrBlock: 0.0.0.0/0 998 | NatGatewayId: !Ref InspectionVPC3NATGW2 999 | 1000 | # Create summary route in ANFW route tables with Core Network ARN as the next hop: 1001 | InspectionVPC3FirewallRTB1SummaryRoute: 1002 | DependsOn: [InspectionVPC3CWANAttachment] 1003 | Type: AWS::EC2::Route 1004 | Properties: 1005 | RouteTableId: !Ref InspectionVPC3FirewallRTB1 1006 | DestinationCidrBlock: 10.0.0.0/8 1007 | CoreNetworkArn: !Ref CoreNetworkArn 1008 | 1009 | InspectionVPC3FirewallRTB2SummaryRoute: 1010 | DependsOn: [InspectionVPC3CWANAttachment] 1011 | Type: AWS::EC2::Route 1012 | Properties: 1013 | RouteTableId: !Ref InspectionVPC3FirewallRTB2 1014 | DestinationCidrBlock: 10.0.0.0/8 1015 | CoreNetworkArn: !Ref CoreNetworkArn 1016 | 1017 | # Create default route in Public route tables with Internet Gateway as the next hop: 1018 | InspectionVPC3PublicRTB1DefaultRoute: 1019 | DependsOn: [InspectionVPC3IGWAttachment] 1020 | Type: AWS::EC2::Route 1021 | Properties: 1022 | RouteTableId: !Ref InspectionVPC3PublicRTB1 1023 | DestinationCidrBlock: 0.0.0.0/0 1024 | GatewayId: !Ref InspectionVPC3IGW 1025 | 1026 | InspectionVPC3PublicRTB2DefaultRoute: 1027 | DependsOn: [InspectionVPC3IGWAttachment] 1028 | Type: AWS::EC2::Route 1029 | Properties: 1030 | RouteTableId: !Ref InspectionVPC3PublicRTB2 1031 | DestinationCidrBlock: 0.0.0.0/0 1032 | GatewayId: !Ref InspectionVPC3IGW 1033 | 1034 | # Create summary route in Public route tables with firewall endpoint as the next hop: 1035 | InspectionVPC3PublicRTB1SummaryRoute: 1036 | Type: AWS::EC2::Route 1037 | Properties: 1038 | RouteTableId: !Ref InspectionVPC3PublicRTB1 1039 | DestinationCidrBlock: 10.0.0.0/8 1040 | VpcEndpointId: !GetAtt RetrieveInspectionVPC3AnfwVpceIds.FwVpceId1 1041 | 1042 | InspectionVPC3PublicRTB2SummaryRoute: 1043 | Type: AWS::EC2::Route 1044 | Properties: 1045 | RouteTableId: !Ref InspectionVPC3PublicRTB2 1046 | DestinationCidrBlock: 10.0.0.0/8 1047 | VpcEndpointId: !GetAtt RetrieveInspectionVPC3AnfwVpceIds.FwVpceId2 1048 | 1049 | # ---------- End of Inspection VPC 3 Resources ---------- --------------------------------------------------------------------------------