IPFW in FreeBSD has built-in support for NATing and the configuration syntax is same as that of natd. It took me quite some time to figure out how to NAT for jails while ensuring that certain jails can have public IPs.
Configure the nat on one of the IP addresses:
ipfw nat 123 config ip a.b.c.d
When using stateful firewall, the NAT rule for incoming traffic must appear before check-state:
ipfw add 100 nat 123 from any to a.b.c.d in
ipfw add 101 check-state
Other rules (service ports) can be placed below this:
index=300
for port in $tcp_service_ports; do
ipfw add $index allow tcp from any to me $port in
ipfw add $index allow tcp from me $port to any out
index=$((index+1))
done
index=400
for port in $udp_service_ports; do
ipfw add $index allow udp from any to me $port in
ipfw add $index allow udp from me $port to any out
index=$((index+1))
done
Then the NAT rule for outgoing traffic:
ipfw add 800 nat 123 ip4 from 10.0.0.0/8 to any out
Notice above, I am NATing only traffic that comes from 10.0.0.0/8 . I allocate jails an IP on that subnet (unless I need a public IP for the jail). If the source is not mentioned in the rule, it will NAT even public IPs!
And finally, the outgoing ports:
index=500
for port in $out_tcp_ports; do
ipfw add $index skipto 800 tcp from 10.0.0.0/8 to any $port out setup keep-state
ipfw add $index allow tcp from me to any $port out setup keep-state
index=$((index+1))
done
index=600
for port in $out_udp_ports; do
ipfw add $index skipto 800 udp from 10.0.0.0/8 to any $port out keep-state
ipfw add $index allow udp from me to any $port out keep-state
index=$((index+1))
done
The catch here is that we jump to the NAT rule only if the traffic comes from 10.0.0.0/8 . If the traffic is coming from somewhere else (for example, a public IP allocated to one of the jails), it will hit the second rule and directly allow it.
Make sure you have the rule to allow loX traffic if you have separate clone interfaces for each jail.
Final touches:
echo firewall_enable=YES >> /etc/rc.conf
echo firewall_nat_enable=YES >> /etc/rc.conf
echo firewall_script=/etc/ipfw.rules >> /etc/rc.conf
echo gateway_enable=YES >> /etc.rc.conf
service routing restart
service ipfw restart
The firewall script ipfw.rules must to contain other rules for services, icmp, etc not mentioned here.
Everything working smoothly now – ip4 from private jails, ip4 and ip6 from others 😀
Leave a comment