While Amazon provides different ways to route traffic within and out of your subnets by means of internet gateways and NAT gateways, it’s not always the case that you it will suite your needs. If you want full control with lots of possibilities for customisation, you might consider building your own firewall instance and push all traffic via it.
Amazon provides NAT instances, but they also have some limitations, so to gain full features, its is possible to build a custom EC2 instance with whatever AMI and settings you like, attach two network interfaces to it where one is in a private and one in a public subnet and do classic iptables NAT on it.
For all instances in the private subnet to be routed via your custom firewall, you need to adjust the routing table for that subnet and point default route to the network interface of the firewall instance that is in that same private subnet.
All of the above works pretty good, but looks a bit weird: lets assume the following:
- We have a VPC 10.10.0.0/16
- We have a private subnet 10.10.10.0/24 within our VPC
- We have a public subnet 10.10.20.0/24 within our VPC
- We have a firewall instance with:
- public subnet IP: 10.10.10.10
- private subnet IP: 10.10.20.20
- We have a host in private subnet with IP 10.10.10.50
Now, the first question is why we don’t use 10.10.10.1 on firewall instance in a private subnet? Easy: it is used by amazon gateway and even though we have created routing table to through all to 10.10.10.10, on an actual host in a private subnet, the routing table will be:
default via 10.10.20.1 dev eth0 10.10.20.0/24 dev eth0 proto kernel scope link src 10.10.20.50
This means that host will send traffic to AWS gateway, and that one will pass it over to our firewall. Probably the idea behind such configuration is that AWS still needs to check security groups and so on, before it handles traffic to us.
Cool, and this work fine, but there is a small issue: if you will try to ping or access any services at firewalls public IP (10.10.10.10) from the host in your private subnet – you will fail! Moreover, if you fire up tcpdump on a firewall server listening for any packets from host in private subnet via interface of private subnet (10.10.20.20) and try to ping 10.10.10.10 from the private host – you will see completely nothing related to this. Nor you will see any other activity from your private host towards public IP address of your firewall.
Wanna go even more weird? If you will try to access any other hosts in the public subnet from your private host (for a sake of example assume you have another host with IP 10.10.10.99 and you try to ping it from 10.10.20.20): this will work as expected and traffic will flow via firewall as configured.
Not sure why and how, but Amazon seems to block access on 10.10.20.1 from any host in 10.10.20.0/24 network to 10.10.10.10, because that IP belongs to a firewall that is a default gateway for any host on 10.10.20.0/24 (even though the IP is another subnet).
The solution for this problem (if that’s a problem for your case) is either to put a direct route to 10.10.10.10 via 10.10.20.20 on the private host to make sure private host avoids using amazon 10.10.20.1 for this route:
default via 10.10.20.1 dev eth0 10.10.10.10/32 via 10.10.20.20 dev eth0 10.10.20.0/24 dev eth0 proto kernel scope link src 10.10.20.50
or even completely ignore 10.10.20.1 and set default gateway via 10.10.20.20:
default via 10.10.20.20 dev eth0 10.10.20.0/24 dev eth0 proto kernel scope link src 10.10.20.50
In either way you won’t be able to do it via AWS routing tables, but will have to configure routing right on the private host via ip route tool or route-ifcfgN file for persistence.
Keep in mind that if you will divert all traffic via 10.10.20.20, you might lose some security group checks within a subnet, so make sure to implement whatever security you need on the actual firewall.