blocking local service on external IP via local loopback

I'm having trouble with accessing my DNS caching resolver from within the machine it runs on. I have dnscache(8) set up listening for network queries from clients
Code:
dnscache dnscache   31267 3  udp4   192.0.2.53:53      *:*
dnscache dnscache   31267 4  tcp4   192.0.2.53:53      *:*

To allow clients to reach the service I set up the firewall rules
Code:
EXT_NIC=em0
DNSCACHE_PORT=53

# incoming TCP querries to DNS cache from clients
   pass in quick on $EXT_NIC inet proto tcp from $DNSCACHE_CLIENT_NETS to $EXT_IP port $DNSCACHE_PORT flags $SYN_ONLY modulate state  \\
      (max-src-states 5, max-src-conn 5, max-src-conn-rate 15/5)
   
# stateless incoming UDP querries to DNS cache from clients
   # querries
   pass in quick on $EXT_NIC inet proto udp from $DNSCACHE_CLIENT_NETS to $EXT_IP port $DNSCACHE_PORT no state
   # responses
   pass out quick on $EXT_NIC inet proto udp from $EXT_IP port $DNSCACHE_PORT to $DNSCACHE_CLIENT_NETS no state

This works fine for queries from the outside clients on the network. The problem is I cannot reach the service from the machine it self, which is configured to use it's own DNS service
Code:
# cat /etc/resolv.conf
nameserver 192.0.2.53

It turns out pf() blocks these queries since they are not passed via em0 interface but via lo0.
Code:
# /usr/sbin/tcpdump -nn -e -l -tttt -i pflog0 -s 0
tcpdump: WARNING: pflog0: no IPv4 address assigned
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on pflog0, link-type PFLOG (OpenBSD pflog file), capture size 65535 bytes
2013-08-29 16:46:32.538047 rule 0..16777216/0(match): block in on lo0: 192.0.2.53.49179 > 192.0.2.53.53: 38019+ A? xxxx.yyy.zzz. (30)

To fix this, I tried to add explicit rules to allow this kind of traffic, but pf() keeps blocking it.
Code:
   # querries
   pass in quick on lo0 inet proto udp from $EXT_IP to $EXT_IP port $DNSCACHE_PORT no state
   # responses
   pass out quick on lo0 inet proto udp from $EXT_IP port $DNSCACHE_PORT to $EXT_IP no state

I'd appreciate any input helping me to find out what I'm doing wrong. Thanks.
 
Add to your pf.conf
Code:
set skip on lo0

And bind dnscache to 127.0.0.1.

The problem is that packets never pass through the interface, even if you connect to the IP address of the interface.
 
Thank you for the quick response.

Code:
set skip on lo0
This would allow much more traffic than needed. All I really want to allow is UDP/53 traffic. Anything else should be blocked and logged so I know what is going on with the machine.

And bind dnscache to 127.0.0.1.
In order to listen on the network, dnscache(8) has to listen on an external IP address. There are some third party patches that make it possible to bind it to more adresses, but I prefer to use as few modifications to the djb's code as possible.
 
marwis said:
In order to listen on the network, dnscache(8) has to listen on an external IP address. There are some third party patches that make it possible to bind it to more adresses, but I prefer to use as few modifications to the djb's code as possible.
You should be able to bind it to both 127.0.0.1 and the external IP address.
 
The official dnscache-conf configuration utility defaults to 127.0.0.1 or accepts an explicitly specified IP address
[font="Courier New"]dnscache-conf acct logacct D ip[/font]

[font="Courier New"]dnscache-conf[/font] arranges for [font="Courier New"]dnscache[/font] to listen for UDP packets and TCP connections on port 53 of [font="Courier New"]ip[/font]. [font="Courier New"]ip[/font] is optional; if it is not supplied, [font="Courier New"]dnscache-conf[/font] arranges for [font="Courier New"]dnscache[/font] to listen on 127.0.0.1. [font="Courier New"]dnscache-conf[/font] creates [font="Courier New"]D/root/ip/127.0.0.1[/font] so that [font="Courier New"]dnscache[/font] will accept queries from 127.0.0.1.
In any case, it's a single address.

Please, let's get back to the original problem: how to allow traffic on em0 interface using the external IP addresses.
 
Not allowing any traffic over lo0 will confront you with very interesting errors that may not even be recognizable as stemming from that decision.
 
I'm a little confused now, as I believe I do have a rule allowing loopback traffic.
Code:
pass on lo0 all no state
I also have default rules blocking IPv4 UDP traffic on the em0 interface
Code:
# Default UDP policy - incoming and "sessions"
block in log on $EXT_NIC inet proto udp all

# Default UDP policy - outgoing
block out log on $EXT_NIC inet proto udp all

I am now unsure why traffic from 192.0.2.53 port UDP/49179 to 192.0.2.53 port UDP/53 via lo0 got blocked.

Once I have access to the machine I'll try
  1. adding quick keywork to pass on lo0 all no state rule, or
  2. replacing the loopback rule by set skip on lo0 as suggested by @SirDice.

Anyway, thanks to all for the suggestions!
 
Last edited by a moderator:
RE: your usage of no state

From pf.conf(5):
Code:
     pf(4) will also create state for other protocols which are effectively
     stateless by nature.  UDP packets are matched to states using only host
     addresses and ports, and other protocols are matched to states using only
     the host addresses.
From http://www.openbsd.org/faq/pf/filter.html#udpstate:
Keeping State for UDP

One will sometimes hear it said that, "One can not create state with UDP as UDP is a stateless protocol!" While it is true that a UDP communication session does not have any concept of state (an explicit start and stop of communications), this does not have any impact on PF's ability to create state for a UDP session. In the case of protocols without "start" and "end" packets, PF simply keeps track of how long it has been since a matching packet has gone through. If the timeout is reached, the state is cleared. The timeout values can be set in the options section of the pf.conf file.
Because all rules are stateful by default, your incoming UDP queries rule can be simplified to one single rule:
Code:
# incoming UDP queries to DNS cache from clients
pass in quick on $EXT_NIC inet proto udp from $DNSCACHE_CLIENT_NETS to $EXT_IP port $DNSCACHE_PORT

RE: block on lo0

On my OpenBSD firewall dnscache runs on the internal interface and tinydns, the authoritative name server, providing DNS for of my private domain, is running on an alias IP.
Code:
[cmd=#] netstat -an -f inet | grep '.53 '[/cmd]
tcp          0      0  192.168.222.10.53      *.*                    LISTEN
udp          0      0  192.168.222.10.53      *.*                   
udp          0      0  192.168.222.11.53      *.*
So just like in your situation no port 53 usage of 127.0.0.1.

However, when I disable my skip on lo0 rule and reload my ruleset, I cannot do name lookups on the firewall itself. I get similar results in pflog0:
Code:
rule 33/(match) block out on lo0: 188.x.x.x.59632 > 192.168.222.10.53: 47189+ A? freebsd.org. (29)

After enabling skip on lo0 everything is normal again. As suggested, just use the skip rule on loopback and be done with it ;)
 
Thanks. I know pf() can keep state for UDP. However, since UDP itself is stateless, pf() is not really able to tell when a "connection" is finished. Thus, the state has to expire (by default in 30 seconds). My dnscache(8) is fairly busy and would generate many states. This would give me one more thing to worry about.

I'll have a closer look at the exact difference between
  1. skip on lo0,
  2. pass on lo0 all no state, and
  3. pass quick on lo0 all no state.
 
Contrary to what I've writted previously, I didn't want to filter traffic on lo0 interface. (I had to dig deeper through my own documentation to realize this. :) ) I followed this howto which suggested pass on lo0 all no state. This however didn't allow the traffic to pass.

Reading the official pf documentation from OpenBSD here, I see it is also suggested to use set skip on lo0 as some of people here did. After allowing this, the local DNS lookups work.

I'm still not sure what exactly I'm doing as I've had pass on lo0 all no state rule set on several machines and didn't encounter any problem. Thus, I assumed it passed all traffic on lo0. Failing local DNS lookups were the first to point out the problem.

I'll use set skip on lo0 as it seems to do its job properly.
 
Back
Top