DNS – An underlying primitive that is simple yet powerful, all for a highly functional and frictionless internet. Many of us are aware of how authoritative DNS is; that which controls DNS can control nearly all experiences.
Shortly after adding Pi-Hole, a DNS based ad-blocking system, to my home network, I noticed that specific devices/functions didn’t seem to be leveraging the expected DNS settings. After looking at the Pi-Hole logs and a few captures of mirrored traffic across the network, I realized in some cases, that both the obtained [DHCP] or configured [Static] name server settings, were being ignored by offending devices. I believe the intent of this behavior by the device is for a few potential reasons.
- An attempt to have absolute “control” over record resolution
- A modest attempt to avoid downtime as a result of a “down” DNS endpoint
- An attempt at prevention of “selective resolution” by upstream DNS [Pi-Hole’s primary capability]
- Sheer ignorance and/or poor development practices
Notice the theme in the above points – They are all attempts, rudimentary ones at that. Nearly all home network users will never notice when a device is ignoring the intended configuration state relating to DNS settings. But for those who are cognizant about their privacy and time in front of devices, these attempts are easy to overcome, for now at least. DNS, in its traditional form, is ripe for manipulation. Being based mainly on UDP and without security baked in, interception and manipulation of connectionless and insecure protocols is relatively easy. ISPs have been guilty of intercepting and rewriting DNS requests for decades, being monetarily incentivized or politically pressured to do so.
In this post, I’ll specifically cover how to prevent these elementary attempts at non-compliance by leveraging NAT on a Ubiquiti EdgeRouter device. In doing so, all DNS requests which anticipate passing through the default gateway, are redirected to the sole provider of DNS on the network. For example, if a Roku device is attempting to reach out to proprietary DNS servers at 126.96.36.199, since this will naturally pass through the default gateway, Ubiquiti EdgeOS will redirect the insecure and connectionless request to the desired endpoint, 10.10.1.15 in my example. Also, as a catch-all safeguard, I prohibit outbound traffic traversing over UDP/53 to have one more ounce of prevention.
This type of redirection is configured with two separate NAT rules. One rule for modifying the destination address from a public IP, such as Google’s public DNS [188.8.131.52], to a desired internal endpoint [10.10.1.15], capable of fulfilling the same protocol promises [DNS resolution]. The second rule is for modifying the source address from that of the original client’s address [e.g., 10.10.1.4] to that of the gateway that would have been responsible for routing the packet to its original destination [10.10.1.1]
The first rule [4053 shown below] is responsible for:
- Classify any packet ingressing interface eth1.10 leveraging either TCP or UDP port 53 that is NOT originating from an address in the group ‘DNS_Servers’
- Modify the Layer 3 destination address in said packet to be 10.10.1.15 [The IP of a Pi-Hole interface]
set service nat rule 4053 description 'DNAT_PIHOLE'
set service nat rule 4053 destination port 53
set service nat rule 4053 inbound-interface eth1.10
set service nat rule 4053 inside-address address 10.10.1.15
set service nat rule 4053 inside-address port 53
set service nat rule 4053 protocol tcp_udp
set service nat rule 4053 source group address-group '!DNS_Servers'
set service nat rule 4053 type destination
The second rule [5053 shown below] is responsible for:
- See the note at the bottom of the post [Edited 12/28/2019]
- Classify any packet egressing interface eth1.10, leveraging either TCP or UDP port 53 and destined for 10.10.1.15 [Pi-Hole] that is NOT originating from an address in the group ‘NAT_DNS_REDIRECT’
- Modify the Layer 3 source address in said packet to be that of interface eth1.10 [e.g. 10.10.1.1]
set service nat rule 5053 description 'MASQ_PIHOLE'
set service nat rule 5053 destination address 10.10.1.15
set service nat rule 5053 destination port 53
set service nat rule 5053 outbound-interface eth1.10
set service nat rule 5053 protocol tcp_udp
set service nat rule 5053 source group address-group '!NAT_DNS_REDIRECT'
set service nat rule 5053 type masquerade
In the WebGUI, this is what the configuration looks like. Note: There are subtle differences in naming conventions between the configuration commands and what is displayed in the below images.
Once implemented, any endpoint that is attempting to access a DNS resource which does not reside on its local network, will go through those two NAT rules and answered by Pi-Hole. One downside to this exact implementation is the loss of query fidelity from a logging perspective. The Pi-Hole logs will show the IP of the gateway performing the NAT translation and not that of the original client, as depicted below.
One consequence of this setup is the loss of the originating client IP address.
For the clients that are attempting to utilize an “unapproved” DNS resolver, the NAT rules + Pi-Hole fill the void without the client ever knowing that the request was intercepted and manipulated. As shown below, the client sets the DNS server to be 184.108.40.206, and the request is seemingly fulfilled without issue.
If you enable logging for these specific NAT rules, you can tail the messages log on the Edge Router. Tail the log using: tail -f /var/log/messages – Continuing with the example above, here are two log entries that are generated from a single DNS lookup, which is subject to this NAT redirection. If the log is too noisy, be sure to disable the logging of other services such as the firewall or other NAT rules where large amounts of traffic are subject to a logging rule.
Dec 27 16:03:43 ER-LITE kernel: [NAT-4053-DNAT] IN=eth1.10 OUT= MAC=REDACTED SRC=10.10.1.4 DST=220.127.116.11 LEN=54 TOS=0x00 PREC=0x00 TTL=128 ID=11067 PROTO=UDP SPT=57439 DPT=53 LEN=34
Dec 27 16:03:43 ER-LITE kernel: [NAT-5053-MASQ] IN= OUT=eth1.10 SRC=10.10.1.4 DST=10.10.1.15 LEN=54 TOS=0x00 PREC=0x00 TTL=127 ID=11067 PROTO=UDP SPT=57439 DPT=53 LEN=34
Update: 12/28/2019 – NAT Rule 5053 Explained
The second rule, rule 5053, could be optional in your environment, depending on your setup. I will attempt to formulate an explanation, and if any clarity is required, please do reach out.
In my case, the Client + Pi-Hole + Gateway are all in the same subnet. Without the second rule, the Pi-Hole would respond back to the original client in a seemingly unsolicited fashion. To elaborate, the Pi-Hole [10.10.1.15] would send a DNS answer directly back to the client [10.10.1.4] even though the client was expecting a response from 18.104.22.168 and will consequently drop the packet as it’s not in the operating system’s state table with regard to expected network communications. In other terms, if your Pi-Hole is in a separate subnet from the clients attempting to access unauthorized DNS servers, the second rule is unnecessary.
Let’s take a look at a packet capture of my example setup with the second NAT rule  disabled. In the packets shown below, you’ll note that there appear to be two DNS queries from the client, 10.10.1.4.
If you look closely, you’ll notice the Layer 2 address [MAC Address] is different between the two queries. The first packet being that of the actual source and the second packet is forged due to NAT rule 4043 and has the MAC address of the Ubiquiti Edge Router. From the Pi-Hole perspective, it receives a packet with a source IP [10.10.1.4] on the same subnet as itself, 10.10.1.0/24. This will cause the OS that Pi-Hole is running on to look at it’s ARP table for an IP to MAC address mapping. If one does not exist, a broadcast would be sent throughout the subnet and the real client [Asus::email@example.com] would respond to the ARP. The Pi-Hole would then proceed to send the DNS answer to 10.10.1.4 but the client is expecting a response from 22.214.171.124 and therefore drops the packet, being unsolicited.
Note: It is very uncommon to use NAT in the same subnet; in effect, we are translating 10.10.1.x to 10.10.1.1.