NAT Instance on Amazon EC2/VPC

Hi,
Currently, I'm in the process of switching from Linux to FreeBSD in our Amazon EC2 (VPC) infrastructure. We use an instance that serves as the "NAT box" (it's really PAT) using iptables that sets up the masquerading. It works great, and this is all over a single interface that uses an internal RFC 1918 IP (Amazon EC2 then does its own translation using an Elastic IP at the gateway of the NAT box -- nothing needed on the box to make that work).

See this page <http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_NAT_Instance.html> for details. The commands to allow the NAT instance to serve as the NAT box is:

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o eth0 -s 10.100.20.0/24 -j MASQUERADE


How can this be done using FreeBSD v10? Everything I've read says you need to have two interfaces to do NAT, but I must be missing something.

Thanks!
 
With pf(4):

Code:
ext_if = vtnet0 # or something else depending on the type of adapter used
...
nat on $ext_if inet from ! $ext_if to any -> $ext_if
...
 
And to /etc/rc.conf:
Code:
pf_enable="YES"
gateway_enable="YES"
 
Thanks. Can this only be done with PF, or is IPFW able to do the same (just curious, mostly).

SirDice: Does your method automatically put the rule into the ruleset, or do I still need to do that in pf.conf?
 
With pf(4):

Code:
ext_if = vtnet0 # or something else depending on the type of adapter used
...
nat on $ext_if inet from ! $ext_if to any -> $ext_if
...

kpa: The nat rule seems like it wouldn't work, would it? If I'm reading this correctly it says to nat any traffic that does NOT come in over $ext_if. However, there is only one interface on the instance, so the traffic would in fact be coming in over $ext_if (just from an RFC1918 address, destined to a non-RFC1918 address). Or am I reading this wrong?
 
kpa: The nat rule seems like it wouldn't work, would it? If I'm reading this correctly it says to nat any traffic that does NOT come in over $ext_if. However, there is only one interface on the instance, so the traffic would in fact be coming in over $ext_if (just from an RFC1918 address, destined to a non-RFC1918 address). Or am I reading this wrong?

You have to know how PF and routing play together to understand what it does. The rule applies only to outgoing traffic (via a routing decision by the host) on ext_if and only if the source address in the IP packet is something else that an address bound to ext_if. This hides any other addresses used on the host to appear from an address that is directly on ext_if. If this is not what you want please elaborate.
 
SirDice: Does your method automatically put the rule into the ruleset, or do I still need to do that in pf.conf?
No, you'd still need to create a /etc/pf.conf. But kpa already posted it. I just added some necessary settings to make it load at boot and enable routing.

As for your other question, it can be done with IPFW too. A lot of us simply prefer PF due to its easy syntax.
 
You have to know how PF and routing play together to understand what it does. The rule applies only to outgoing traffic (via a routing decision by the host) on ext_if and only if the source address in the IP packet is something else that an address bound to ext_if. This hides any other addresses used on the host to appear from an address that is directly on ext_if. If this is not what you want please elaborate.

So the setup I'm looking at would be like this:

Private subnet (10.0.1.0/24):
web1 (xn0=10.0.1.2)
web2 (xn0=10.0.1.3)

DMZ subnet (10.0.0.0/24):
nat1 (xn0=10.0.0.2)
nginx1 (xn0=10.0.0.3)

The hosts in the private subnet cannot reach the internet without going through the NAT box, which is in the DMZ. Those hosts have a route table which sends their traffic to the gateway of their subnet, which will then send the traffic to the NAT box (this is done via Amazon VPC configuration, not on any of the instances). As an example, traffic from web1 destined to the internet would hit the nat1 box from a source IP of 10.0.1.2 and a dest IP of some internet address, coming into nat1 via xn0. nat1 would then nat the traffic and send it to the gateway over the same interface, xn0. The gateway (part of Amazon VPC) does its own 1:1 NAT for 10.0.0.2 to an Elastic IP (as they call it) which is a public IP. Traffic then comes back in via the gateway, back to nat1, which should then send the traffic back to web1.
 
Please draw a picture of your infrastructure with interfaces and connections between the different hosts.

There's a nice tool that I've used occasionally to draw such diagrams, Gliffy. Also available as a browser app for at least Chrome:

https://www.gliffy.com/
 
Please draw a picture of your infrastructure with interfaces and connections between the different hosts.

There's a nice tool that I've used occasionally to draw such diagrams, Gliffy. Also available as a browser app for at least Chrome:

https://www.gliffy.com/

It is setup similarly to this:

upload_2016-5-13_10-4-27.png


In this diagram, the database servers have no Elastic IP associated with them, so they must have their traffic NAT'd to reach the internet. This gets accomplished by sending their traffic to the router, which sends the traffic to the NAT instance (10.0.0.8 in the diagram). The NAT instance nats the traffic and sends it to its default gateway ("Router"), which translates to an Elastic IP of 198.51.100.4 in the diagram.

All instances have one interface named xn0, and all instances only have an RFC1918 address bound to them.
 
I don't see why it wouldn't work if configured with what we already gave you. The fact that the traffic arrives in the same interface it would be later NAT'ed out via shouldn't be a problem at all.
 
Yep, just got around to testing this out and it does in fact work just fine. The final pf.conf that worked was:

ext_if = xn0

# NAT the traffic coming in via $ext_if, only if the source isn't the IP bound
# to $ext_if.
nat on $ext_if inet from ! $ext_if to any -> $ext_if

pass all


Thanks for all the help, kpa and SirDice!
 
Have you been able to do port forwarding? For some reason this rule works:

Code:
rdr pass proto tcp from any to $ext_if port 80 -> 10.22.1.157 port 80

and this no:

Code:
rdr pass proto tcp from any to $ext_if port 8080 -> 10.22.1.157 port 80

With tcpdump I can see the data coming to 10.22.157 on port 80, but the client doesn't get anything back.
 
It works. My mistake. Here's the complete config if anybody wants to have Firewall and NAT on AWS.
Don't forget to disable source/destination check on the instance.
Code:
ext_if = xn0                   
                              
set loginterface $ext_if       
scrub in all                   
                              
# NAT the traffic coming in via $ext_if, only if the source isn't the IP bound
# to $ext_if.                 
nat on $ext_if inet from ! $ext_if to any -> $ext_if
                              
# Redirection must be declared before firewall rules
rdr pass proto tcp from any to $ext_if port 8080 -> 10.22.1.157 port 80
                              
# FTP thing                   
nat-anchor "ftp-proxy/*"       
rdr-anchor "ftp-proxy/*"       
rdr pass proto tcp from any to any port ftp -> $ext_if port 8021
                              
antispoof for $ext_if         
                              
# First rule for lo0 interface 
pass quick on lo0 all         
                              
# Firewall rules               
pass in all                   
pass out all
 
Don't use this:
Code:
# First rule for lo0 interface 
pass quick on lo0 all
Instead use:
Code:
set skip on lo0
 
You have to know how PF and routing play together to understand what it does. The rule applies only to outgoing traffic (via a routing decision by the host) on ext_if and only if the source address in the IP packet is something else that an address bound to ext_if. This hides any other addresses used on the host to appear from an address that is directly on ext_if. If this is not what you want please elaborate.

Indeed. Type pfctl -vnf /etc/pf.conf to see how this rule is interpreted by pf(4).

BTW, for more security (recommended in AWS NAT AMI /usr/local/sbin/configure-pat.sh) you can use something like:

Code:
nat log on $ext_if inet from { 10.0.1.0/24 } to any -> $ext_if

...to limit the incoming IPs to the NAT to your private subnet and log those connections.
 
Back
Top