PF Jails with NAT


New Member

Reaction score: 6
Messages: 15

I'm trying to set up an Ampache media server at home and am taking the opportunity to learn how to use jails on FreeBSD. I'm trying to set up jails on a separate loopback network on the host and use the NAT features of PF to direct the traffic where it should go. I've tried following multiple different guides for this, except everything I can find refers to using ezjail and I want to do it using just jail.conf and pf.conf, if possible.

The problem is that the jails cannot access the internet. I have set up their resolv.conf files and - I thought - set up the shared network and the NAT rules to make it work. Here's my current setup:

ifconfig_fxp0_ipv6="inet6 accept_rtadv"
# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
#ifconfig_lo1="inet netmask"
# Enabled packet forwarding between interfaces
exec.start="sh /etc/rc";
exec.stop="sh /etc/rc.shutdown";

nginx {

ampache {

nginx_ports="{ http, https }"

# Don't filter loopback
set skip on lo0
set loginterface lo1
set block-policy return
set fail-policy  return

# Sanitize incoming data
scrub in on $ext_if all

# Route HTTP/S to nginx jail
#nat pass on $ext_if inet from $jail_net to any -> ($ext_if)
nat on $ext_if from $jail_net to any -> ($ext_if)
rdr on $ext_if proto tcp from any to ($ext_if) port $nginx_ports -> $nginx_ip

# Allow blacklistd to block stuff
#anchor "blacklistd/*" in on $ext_if
# Block incoming by default
#block in
# Allow outgoing by default
pass out
pass in
# Prevent spoofing attacks
#antispoof for $ext_if

# Allow traffic to/from jails
#pass in on $ext_if proto tcp from $ext_net to $jail_net port $nginx_ports keep state
#pass out on $jail_if proto tcp from $ext_net to $jail_net port $nginx_ports keep state
# Allow SSH
#pass in on $ext_if proto tcp from any to ($ext_if) port ssh
# Allow ICMP
#pass inet proto icmp from $jail_net to any keep state
pass in on $ext_if inet proto icmp to ($ext_if) icmp-type { unreach, redir, timex, echoreq }
pass in on $jail_if inet proto icmp to ($jail_if) icmp-type { unreach, redir, timex, echoreq }
Running tcpdump on pflog0 shows nothing when I attempt to use the host's pkg to install packages in a jail. The host has internet access. The jails' rc.conf files only disable sendmail and prevents syslogd from communicating on the network. The jails properly receive their IP address and hang when they attempt to access the internet, followed eventually by "No address record" for requests with domain names or "Operation timed out" for requests using IP addresses.

I'm new to FreeBSD and PF, so I'm not sure where to go from here. It seems like PF is blocking part of the request, response, or both, but I'm not sure how to check that or how I would fix it. Any help is appreciated.


Staff member

Reaction score: 7,409
Messages: 29,985

Run tcpdump(1) on the $ext_if interface, then initiate some outgoing traffic from one of the jails. Check if there's actually anything going out and if the source address is correctly NAT'ed. Using tcpdump(1) allows you to look at the actual packets, invaluable tool when dealing with firewalls or connection problems.



Reaction score: 480
Messages: 2,150

If you want to monitor the pflog0 then you need to use[ FILE] log[/FILE] in your statements :

pass out log
pass in log


Well-Known Member

Reaction score: 225
Messages: 433

I'm usually using the default loopback interface (lo0) for jails on hosts with only one IP, but besides that my NAT/rdr rules look the same, so I'm not fully convinced it is a filter/rdr/NAT problem.

Usually you don't pre-set the jails IP in the rc.conf of the host, at least that's what I've done on several jailhosts with NAT and one IP.
I only set one IP on that interface via the rc.conf that isn't connected to any jail. This IP is used by the host to reach the associated subnet on that interface. This IP is also used as the default GW by the jails.
The jail IPs are added to the interface when the jails are started.

Make sure your jails have a default route (=the hosts IP on the same loopback IF; e.g. and resolver configured and reachable.

Then try to ping to/from the host and/or a separate host in the same network. If you still don't get any traffic from/to the jails, tcpdump on $ext_if as well as lo0 (and on the outside host you are pinging to/from), so you can find out where packets are either blocked or not properly routed and they are lost.
If ICMP gets the full way in one direction but no answers are generated, also look at ARP traffic and if requests/replies are transmitted the full way in both directions.


New Member

Reaction score: 6
Messages: 15

Again, I'm new to this sort of thing, so I couldn't really tell how to read the tcpdump output. It looks like the jail is successfully sending requests over $ext_if but isn't receiving any response. It queries the first DNS server for a domain, then the second server, then the first again for a different domain... I was attempting to install a package in the jail, so it was querying multiple pkg servers.

I was able to ping the host from the jail and the jail from the host. I was also able to ping from the host, though attempting to ping a domain caused the same problem as the above paragraph.

Looks like the DNS server is responding to the host, but the host isn't properly directing the response to the jail. I tried a naive attempt to fix this by adding the following line to pf.conf:

rdr on $ext_if proto { tcp, udp } from any to ($ext_if) port domain -> ($jail_if)
That didn't work. I'm pretty sure that's not the correct way to redirect DNS response packets, but I thought I'd try. I feel like I shouldn't need to redirect response packets though, since PF is a stateful firewall. If I'm understanding how that works, I shouldn't need to do anything about the response packets, right?



Reaction score: 480
Messages: 2,150

Try replacing this:

nat on $ext_if from $jail_net to any -> ($ext_if) to this:
nat on $ext_if from to any -> ($ext_if)

Assuming that is the IP address of the jail


New Member

Reaction score: 6
Messages: 15

I decided to try spinning up a Digital Ocean droplet with FreeBSD and set up jails there. It looks like being on a home network with a NAT-ing router is causing some issues, as I have functional jail networking on the droplet but not on the home server. Both are using nearly identical configurations, with the only difference being that I can't hardcode the ip address for the home server.

I think I have an ebook copy of the Book of PF somewhere, so I might try reading through that and see if I'm missing something...