When I was delivering the Architecting on AWS class, customers often asked me how to configure an Amazon Virtual Private Cloud to enforce the same network security policies in the cloud as they have on-premises. For example, to scan all ingress traffic with an Intrusion Detection System (IDS) appliance or to use the same firewall in the cloud as on-premises. Until today, the only answer I could provide was to route all traffic back from their VPC to an on-premises appliance or firewall in order to inspect the traffic with their usual networking gear before routing it back to the cloud. This is obviously not an ideal configuration, it adds latency and complexity.

Today, we announce new VPC networking routing primitives to allow to route all incoming and outgoing traffic to/from an Internet Gateway (IGW) or Virtual Private Gateway (VGW) to a specific EC2 instance’s Elastic Network Interface. It means you can now configure your Virtual Private Cloud to send all traffic to an EC2 instance before the traffic reaches your business workloads. The instance typically runs network security tools to inspect or to block suspicious network traffic (such as IDS/IPS or Firewall) or to perform any other network traffic inspection before relaying the traffic to other EC2 instances.

How Does it Work?

To learn how it works, I wrote this CDK script to create a VPC with two public subnets: one subnet for the appliance and one subnet for a business application. The script launches two EC2 instances with public IP address, one in each subnet. The script creates the below architecture:

This is a regular VPC, the subnets have routing tables to the Internet Gateway and the traffic flows in and out as expected. The application instance hosts a static web site, it is accessible from any browser. You can retrieve the application public DNS name from the EC2 Console (for your convenience, I also included the CLI version in the comments of the CDK script).

AWS_REGION=us-west-2 APPLICATION_IP=$(aws ec2 describe-instances \ --region $AWS_REGION \ --query "Reservations[].Instances[] | [?Tags[?Key=='Name' && Value=='application']].NetworkInterfaces[].Association.PublicDnsName" \ --output text) curl -I $APPLICATION_IP

Configure Routing

To configure routing, you need to know the VPC ID, the ENI ID of the ENI attached to the appliance instance, and the Internet Gateway ID. Assuming you created the infrastructure using the CDK script I provided, here are the commands I use to find these three IDs (be sure to adjust to the AWS region you use):

AWS_REGION=us-west-2 VPC_ID=$(aws cloudformation describe-stacks \ --region $AWS_REGION \ --stack-name VpcIngressRoutingStack \ --query "Stacks[].Outputs[?OutputKey=='VPCID'].OutputValue" \ --output text) ENI_ID=$(aws ec2 describe-instances \ --region $AWS_REGION \ --query "Reservations[].Instances[] | [?Tags[?Key=='Name' && Value=='appliance']].NetworkInterfaces[].NetworkInterfaceId" \ --output text) IGW_ID=$(aws ec2 describe-internet-gateways \ --region $AWS_REGION \ --query "InternetGateways[] | [?Attachments[?VpcId=='${VPC_ID}']].InternetGatewayId" \ --output text)

To route all incoming traffic through my appliance, I create a routing table for the Internet Gateway and I attach a rule to direct all traffic to the EC2 instance Elastic Network Interface (ENI):

# create a new routing table for the Internet Gateway ROUTE_TABLE_ID=$(aws ec2 create-route-table \ --region $AWS_REGION \ --vpc-id $VPC_ID \ --query "RouteTable.RouteTableId" \ --output text) # create a route for 10.0.1.0/24 pointing to the appliance ENI aws ec2 create-route \ --region $AWS_REGION \ --route-table-id $ROUTE_TABLE_ID \ --destination-cidr-block 10.0.1.0/24 \ --network-interface-id $ENI_ID # associate the routing table to the Internet Gateway aws ec2 associate-route-table \ --region $AWS_REGION \ --route-table-id $ROUTE_TABLE_ID \ --gateway-id $IGW_ID

Alternatively, I can use the VPC Console under the new Edge Associations tab.

To route all application outgoing traffic through the appliance, I replace the default route for the application subnet to point to the appliance’s ENI:

SUBNET_ID=$(aws ec2 describe-instances \ --region $AWS_REGION \ --query "Reservations[].Instances[] | [?Tags[?Key=='Name' && Value=='application']].NetworkInterfaces[].SubnetId" \ --output text) ROUTING_TABLE=$(aws ec2 describe-route-tables \ --region $AWS_REGION \ --query "RouteTables[?VpcId=='${VPC_ID}'] | [?Associations[?SubnetId=='${SUBNET_ID}']].RouteTableId" \ --output text) # delete the existing default route (the one pointing to the internet gateway) aws ec2 delete-route \ --region $AWS_REGION \ --route-table-id $ROUTING_TABLE \ --destination-cidr-block 0.0.0.0/0 # create a default route pointing to the appliance's ENI aws ec2 create-route \ --region $AWS_REGION \ --route-table-id $ROUTING_TABLE \ --destination-cidr-block 0.0.0.0/0 \ --network-interface-id $ENI_ID aws ec2 associate-route-table \ --region $AWS_REGION \ --route-table-id $ROUTING_TABLE \ --subnet-id $SUBNET_ID

Alternatively, I can use the VPC Console. Within the correct routing table, I select the Routes tab and click Edit routes to replace the default route (the one pointing to 0.0.0.0/0 ) to target the appliance’s ENI.

Now I have the routing configuration in place. The new routing looks like:

Configure the Appliance Instance

Finally, I configure the appliance instance to forward all traffic it receives. Your software appliance usually does that for you, no extra step is required when you use AWS Marketplace appliances. When using a plain Linux instance, two extra steps are required:

1. Connect to the EC2 appliance instance and configure IP traffic forwarding in the kernel:

APPLIANCE_ID=$(aws ec2 describe-instances \ --region $AWS_REGION \ --query "Reservations[].Instances[] | [?Tags[?Key=='Name' && Value=='appliance']].InstanceId" \ --output text) aws ssm start-session --region $AWS_REGION --target $APPLIANCE_ID ## ## once connected (you see the 'sh-4.2$' prompt), type: ## sudo sysctl -w net.ipv4.ip_forward=1 sudo sysctl -w net.ipv6.conf.all.forwarding=1 exit

2. Configure the EC2 instance to accept traffic for different destinations than itself (known as Dest/Source check) :

aws ec2 modify-instance-attribute --region $AWS_REGION \ --no-source-dest-check \ --instance-id $APPLIANCE_ID

Now, the appliance is ready to forward traffic to the other EC2 instances. You can test this by pointing your browser (or using cURL ) to the application instance.

APPLICATION_IP=$(aws ec2 describe-instances --region $AWS_REGION \ --query "Reservations[].Instances[] | [?Tags[?Key=='Name' && Value=='application']].NetworkInterfaces[].Association.PublicDnsName" \ --output text) curl -I $APPLICATION_IP

To verify the traffic is really flowing through the appliance, you can enable source/destination check on the instance again (use --source-dest-check parameter with the modify-instance-attribute CLI command above). The traffic is blocked when Source/Destination check is enabled.

Cleanup

Should you use the CDK script I provided for this article, be sure to run cdk destroy when finished. This ensures you are not billed for the two EC2 instances I use for this demo. As I modified routing tables behind the back of AWS CloudFormation, I need to manually delete the routing tables, the subnet, and the VPC. The easiest is to navigate to the VPC Console, select the VPC and click Actions => Delete VPC. The console deletes all components in the correct order. You might need to wait 5-10 minutes after the end of cdk destroy before the console is able to delete the VPC.

From our Partners

During the beta test of these new routing capabilities, we granted early access to a collection of AWS partners. They provided us with tons of helpful feedback. Here are some of the blog posts that they wrote in order to share their experiences (I am updating this article with links as they are published):

Availability

There are no additional costs to use Virtual Private Cloud ingress routing. It is available in all AWS Regions (including AWS GovCloud (US-West)) and you can start to use it today.

You can learn more about gateway routing tables in the updated VPC documentation.

What are the appliances you are going to use with this new VPC routing capability?