PF + if_bridge + NAT anomaly

Jay L. T. Cornwall jay at jcornwall.me.uk
Sun Apr 20 23:53:00 UTC 2008


Max Laier wrote:

> I think what happend in your scenario is that a state was created for
> the flow on *IN* bridge0 which would then prevent NAT from happening.
> Would you be up to share your complete working setup for future
> reference?

Sure. Here are my modified sysctls:

net.inet.ip.fw.enable=0
net.link.bridge.pfil_bridge=0
net.inet.ip.fw.dyn_keepalive=0

The last one seemed to be necessary to keep persistent connections
stable. Even with a very rudimentary PF setup I had SSH and IMAPS
sessions dropping like flies until I disabled dyn_keepalive. tcpdump
showed the keepalive packets going out, apparently with no reply, then
the connection would (rightly) die.

Here's the PF script. It's a half-firewall, in that I trust outbound
traffic, but I don't foresee any problems modifying it to be completely
exclusive. The public address block is masked as XXX.XXX.XXX.XXX.

# === Macros ===

int_if = "vr0"
ext_if = "vr1"
bridge_if = "bridge0"

lan_ips = "{192.168.1.0/24 XXX.XXX.XXX.16/29}"
nat_from_ips = "192.168.1.0/24"
nat_to_ip = "XXX.XXX.XXX.21"

bittorrent_ips = "XXX.XXX.XXX.19"
ident_ips = "XXX.XXX.XXX.19"
ssh_ips = "{XXX.XXX.XXX.17 XXX.XXX.XXX.18 XXX.XXX.XXX.20}"

bittorrent_ports = "6881:6889"

# === Tables ===

table <ssh-bruteforce> persist

# === Options ===

# Don't filter on loopback. (Not necessary and would collide with
# antispoof.)
set skip on lo0

# === Scrub ===

# Clean incoming packets on all interfaces. Scrubbing outbound packets
# would be redundant, save for those originating from the firewall
# itself. We assume the firewall machine is secure.
scrub in all

# === Queueing ===

# === Translation ===

# NAT through the external interface from a private subnet to a specific
# IP bound to the bridge interface. This IP may be an alias.

nat on $ext_if from $nat_from_ips to any -> $nat_to_ip

# === Filter rules ===

# Deny inbound traffic only. Assume all outbound traffic is legimitate.
block in all

# Deny hosts that have been banned for connection overloading.
block in quick on $ext_if from <ssh-bruteforce>

# Protect the loopback interface from spoofing. We cannot protect the
# bridge interface or it would block NAT.
antispoof quick for { lo0 }

# Allow free inbound traffic on the LAN interface. We will do all
# external-to-LAN filtering on the vr1 interface.
pass in quick on $int_if

# Maintain outbound state on all interfaces.
pass out quick on $int_if
pass out quick on $bridge_if
pass out quick on $ext_if

# Open holes for packets destined for LAN services. This does *not*
# cover the bridge itself.
pass in quick on $ext_if proto tcp from any to $bittorrent_ips port \
   $bittorrent_ports
pass in quick on $ext_if proto tcp from any to $ident_ips port auth
pass in quick on $ext_if proto tcp from any to $ssh_ips port ssh \
   flags S/SA synproxy state \
   (max-src-conn-rate 5/20, overload <ssh-bruteforce> flush global)

# The bridge needs its own set of service holes, applying to both
# internal and external hosts.
pass in quick on $bridge_if proto udp from $lan_ips to any port domain
pass in quick on $bridge_if proto tcp from any to any port ssh \
   flags S/SA synproxy state \
   (max-src-conn-rate 5/20, overload <ssh-bruteforce> flush global)

-- 
Jay L. T. Cornwall
http://www.jcornwall.me.uk/


More information about the freebsd-pf mailing list