FreeBSD IPFW NAT and Jails
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.
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 😀