PF 'rdr' to 127.0.0.1 not working

I'm in the process of reworking my pf(4) rules and am tightening up blocking of LAN clients making their own DNS queries directly to Internet DNS servers, instead forcing them to use the resolver on my firewall.

I'm blocking all outgoing traffic from the LAN to port 853 (except from the firewall itself) and to known DNS-over-HTTPS servers on port 443, but instead of blocking outgoing plain-text DNS, I'm trying to redirect it to the resolver on the firewall.

I have these redirect rules:
Code:
set skip on lo

...

# Don't redirect unencrypted DNS sent directly to local network resolver, but ...
no rdr on pf_internal inet proto { tcp, udp } to pf_internal port 53
no rdr on pf_internal inet6 proto { tcp, udp } to fe80::1 port 53
# ... do redirect queries sent to Internet DNS servers
rdr pass log on pf_internal inet proto { tcp, udp } to any port 53 -> 127.0.0.1
rdr pass log on pf_internal inet6 proto { tcp, udp } to any port 53 -> ::1
(pf_internal is an interface group containing the interfaces connected to my internal networks, bridge0 and bridge1.)

The pf logs do show the redirect rules being triggered, but the DNS queries just time out and tcpdump shows no traffic on lo0.

If I change the redirect destination host from 127.0.0.1 to the IP addresses of the pf_internal member interfaces, as follows, then it works fine.

Code:
# ... do redirect unencrypted DNS queries sent to Internet DNS servers to local resolver
rdr pass log on bridge0 inet proto { tcp, udp } to any port 53 -> 192.168.0.1
rdr pass log on bridge1 inet proto { tcp, udp } to any port 53 -> 10.0.0.1
rdr pass log on pf_internal inet6 proto { tcp, udp } to any port 53 -> fe80::1

I've seen numerous examples of pf redirect rules redirecting to 127.0.0.1, so I'm puzzled why it's not working for me.

Anyone have any suggestions?
 
Yup. Unbound bound to all interfaces and answering queries in both TCP and UDP over IPv4 and IPv6:

Code:
[@router ~]$ sudo sockstat -46 -p 53
USER     COMMAND    PID   FD  PROTO  LOCAL ADDRESS         FOREIGN ADDRESS      
unbound  unbound     1561 3   udp4   *:53                  *:*
unbound  unbound     1561 4   tcp4   *:53                  *:*
unbound  unbound     1561 5   udp6   *:53                  *:*
unbound  unbound     1561 6   tcp6   *:53                  *:*


[@router ~]$ drill -Q @127.0.0.1 www.google.com
142.250.179.228
[@router ~]$ drill -Qt @127.0.0.1 www.google.com
142.250.179.228
[@router ~]$ drill -Q @::1 www.google.com
142.250.179.228
[@router ~]$ drill -Qt @::1 www.google.com
142.250.179.228
 
This didn't work.

I commented out the 'set skip on lo' and added a 'pass quick on lo0' rule, but still don't see any redirected DNS traffic on the loopback interface and queries still time out.
 
Time to do some debugging then.

You'll want to gather some basic information first: your full ruleset, the exact FreeBSD version you're running and such.
Then gather the other obvious things: tcpdump a dns request on the incoming interface and on lo0 and state table entries.

You may also want to experiment to see if things work if your traffic doesn't come in on a bridge interface. If that's the case check net.link.bridge.pfil_local_phys and net.link.bridge.pfil_member. Setting those to not-zero has complex interactions with the firewall and it basically never does what you expect.
 
(pf_internal is an interface group containing the interfaces connected to my internal networks, bridge0 and bridge1.)
From the host's point of view those are outgoing interfaces, a rdr only applies to incoming traffic of the interface.

How are those bridges connected to the LAN? Have you tried setting the rdr on the incoming (physical) interface? Or are all those "internal" networks essentially "virtual" networks (they only live on the host)?

A rdr is basically a destination NAT and only applies to incoming traffic on that interface. A nat rule is a source NAT and only applies to outgoing traffic of that interface.

It is somewhat unclear where the traffic is incoming and where it is outgoing.
 
Back
Top