Wednesday, July 27, 2016

How to stop DNS leaks on pfsense when using VPN client and dnsmasq forwarding

Situation

I configured a privateinternetaccess.com VPN client on pfsense and created a firewall alias group for clients that will be forced through the VPN tunnel. DNS lookups leak because the dnsmasq forwarder doesn't know who is making the requests and is configured with only the Internet DNS servers instead of PIA's DNS servers. The "Internet DNS Servers" are your normal DNS servers; either the ones your ISP assigned you, or maybe you're using Google or OpenDNS.

I first did this on pfsense 2.2.5-RELEASE but it still works fine on 2.3.2.

Solution summary

There are two solutions to this. Either you get dnsmasq to use PIA's DNS servers for everything, or you set up a second dnsmasq just for forwarding to PIA's DNS servers and port forward to it for the PIA VPN clients. I have used both and they each have their drawbacks. 

Use the PIA DNS servers for everything

This solution has you setting the DNS servers in the General Settings to use PIA's DNS servers in the first two slots, and the Internet DNS servers in the last two slots. Force PIA-bound DNS requests through the VPN interface, and force the Internet DNS requests through the WAN interface. Now, jump over to Services -> DNS Forwarder and check "Query DNS servers sequentially". This will cause dnsmasq to query top down in your list. You need to have the Internet DNS servers defined otherwise pfsense won't come up properly, or very slowly, and you wouldn't be able to restart your VPN tunnel because you can't do a DNS lookup to find the remote VPN server. As it is, your pfsense box will boot a little slower as it times out through the first two DNS servers. If your VPN is down, or the PIA DNS servers are down, your PIA DNS requests will go out the WAN interface which is a leak, but at least it's not everything all the time. Another downside of this configuration is that you will experience a latency in DNS requests because it has to go out the VPN interface for everything. It's not much, but it is noticeable. This is mitigated somewhat by dnsmasq's caching however many web pages are full of third party content like images, ads and scripts and will load more slowly because of the DNS requests through the VPN.

Set up a second dnsmasq server

Here's the flow.

LAN client -> firewall LAN interface port 53 -> NAT to firewall LAN interface port 54 -> dnsmasq -> VPN -> PIA DNS servers

You'll need to shell in to the firewall. You can only do this at the command line. First, find the dnsmasq process that's already running. We'll use it to create another one just for the VPN. 

# pgrep -lf dnsmasq

You'll see it and the dhcpleases process. Copy the text for the dnsmasq process and paste it into a text editor. With all the options I selected in the DNS Forwarder page of pfsense, mine looks like this: 

/usr/local/sbin/dnsmasq --all-servers --rebind-localhost-ok --stop-dns-rebind --dhcp-hostsfile=/var/etc/hosts --server=/10.in-addr.arpa/ --server=/168.192.in-addr.arpa/ --server=/16.172.in-addr.arpa/ --server=/17.172.in-addr.arpa/ --server=/18.172.in-addr.arpa/ --server=/19.172.in-addr.arpa/ --server=/20.172.in-addr.arpa/ --server=/21.172.in-addr.arpa/ --server=/22.172.in-addr.arpa/ --server=/23.172.in-addr.arpa/ --server=/24.172.in-addr.arpa/ --server=/25.172.in-addr.arpa/ --server=/26.172.in-addr.arpa/ --server=/27.172.in-addr.arpa/ --server=/28.172.in-addr.arpa/ --server=/29.172.in-addr.arpa/ --server=/30.172.in-addr.arpa/ --server=/31.172.in-addr.arpa/ --domain-needed --dns-forward-max=5000 --cache-size=10000 --local-ttl=1

Now update this line so that we bind to port 54 instead. We're also removing the dhcphostfile entry because we don't want anything to do with dhcp. We're going to ignore the /etc/resolv.conf file and specify the PIA DNS servers on the command line, and we're going to change the pid file so that we don't overwrite the one already there. Paste it into the shell and see if it starts without error. All good? Great, let's put it into an rc script to start on boot. 

# cd /usr/local/etc/rc.d
# vi dnsmasq-pia.sh

and put this into it: 

#!/bin/sh
# Another dnsmasq to do only DNS forwarding for PIA VPN hosts.
# Mario Stargard Nov 14 2015
/usr/local/sbin/dnsmasq --all-servers --port=54 --rebind-localhost-ok --stop-dns-rebind --server=/10.in-addr.arpa/ --server=/168.192.in-addr.arpa/ --server=/16.172.in-addr.arpa/ --server=/17.172.in-addr.arpa/ --server=/18.172.in-addr.arpa/ --server=/19.172.in-addr.arpa/ --server=/20.172.in-addr.arpa/ --server=/21.172.in-addr.arpa/ --server=/22.172.in-addr.arpa/ --server=/23.172.in-addr.arpa/ --server=/24.172.in-addr.arpa/ --server=/25.172.in-addr.arpa/ --server=/26.172.in-addr.arpa/ --server=/27.172.in-addr.arpa/ --server=/28.172.in-addr.arpa/ --server=/29.172.in-addr.arpa/ --server=/30.172.in-addr.arpa/ --server=/31.172.in-addr.arpa/ --domain-needed --dns-forward-max=5000 --cache-size=10000 --local-ttl=1 --pid-file=/var/run/dnsmasq-pia.pid --no-resolv --server=209.222.18.222 --server=209.222.18.218

Save the file and make it executable.

# chmod 755 dnsmasq-pia.sh 

If you want to kill it, you have to find and kill the pid,

# pgrep -lf dnsmasq
# kill <pid> 

To hook up your PIA VPN clients to this new instance of dnsmasq, create a port forward rule in Firewall -> NAT -> Port Forward 

I put mine at the top. 

Interface: LAN
Protocol: TCP/UDP
Source: PIAVPN_clients (This is firewall aliases group)
Destination: This Firewall (itself)
Destination Port Range: DNS/DNS
Redirect Target IP: IP address of your LAN interface where dnsmasq is listening.
Redirect Target Port: 54
Filter Rule Association: None

You'll need to create a route on the firewall so that the DNS queries to your VPN providers servers go out the VPN, otherwise they'll just go out the default route. 

Don't add a static route in your system. Rather, add it when openvpn comes up. Add this to the Custom Options of the openvpn client configuration. Remember, these options are semicolon delimited.

route 209.222.18.0 255.255.255.0

Testing 

Go to Diagnostics -> DNS Lookup and try you main resolver. You should see the resolver times for each of your Internet DNS servers defined in System -> General Setup. Go to a LAN host that is not in the PIAVPN_client list and try it out: 

$ dig www.eff.org @192.168.2.1

; <<>> DiG 9.9.5-3ubuntu0.5-Ubuntu <<>> www.eff.org @192.168.2.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 25000
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.eff.org. IN A

;; ANSWER SECTION:
www.eff.org. 2705 IN A 69.50.225.155

;; Query time: 42 msec
;; SERVER: 192.168.2.1#53(192.168.2.1)
;; WHEN: Sat Nov 14 19:34:28 EST 2015
;; MSG SIZE  rcvd: 56

Go to a LAN host this is in the PIAVPN_client list and try it out:

$ dig www.eff.org @192.168.2.1

; <<>> DiG 9.9.5-3ubuntu0.5-Ubuntu <<>> www.eff.org @192.168.2.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 36601
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;www.eff.org. IN A

;; ANSWER SECTION:
www.eff.org. 5265 IN A 69.50.225.155

;; Query time: 33 msec
;; SERVER: 192.168.2.1#53(192.168.2.1)
;; WHEN: Sat Nov 14 19:30:12 EST 2015
;; MSG SIZE  rcvd: 45

How do you know if it went out the correct interface? You can run a packet capture on the PIAVPN interface and on the WAN interface looking for port 53 traffic to convince yourself. Finally, check it out on one of the DNS leak test sites: http://dnsleaktest.com 

Downsides and Caveats 

One major downside is that this is not part of the GUI, so you have to remember it's there. Also, the dnsmasq processes don't shares caches and if a dhcp host joins and updates DNS, the second copy of dnsmasq won't know about it because it only reads /etc/hosts on startup. But, now you have a configuration that assigns only your VPN LAN clients to their own DNS servers through the tunnel.

Note 1: I tried a variation on this where I only just NAT'd to a DNS server through the tunnel for my VPN clients, but it made other LAN hosts unresolvable. 

Note 2: If you want the firewall to use the default route instead of the VPN tunnel, go into OpenVPN Client settings and check the box next to "Don't pull routes". You're forcing clients through the tunnel via Firewall rules anyway.

No comments:

Post a Comment