HOWTO - Redirect all DNS Requests to Opnsense

Started by Cypher100, July 26, 2018, 03:16:37 AM

Previous topic - Next topic

I am having some trouble with this and a pi hole.  I set the firewall rule to send all traffic from port 53 to the pi-hole.  The pi-hole is set and the dns server in the opnsense settings.  When the firewall rule is enabled there is too much traffic to the pihole.  I think what is happening is the pihole is also sending queries out on port 53 which is getting bounced back to itself causing a never ending loop.

I came across this thread while looking for a way to perform DNS NAT'ing.  However it seems that if, like I am, using unBound, the DNSBL's don't get adhered to when performing these instructions with a NAT rule.

Has anyone else found a solution to that?

Quote from: p1n0ck10 on November 28, 2018, 06:15:14 AM
Quote from: GDixon on November 26, 2018, 05:20:07 AM
  from chris42
QuoteHow would this work on ipv6? I tried to mimic the NAT rules for ipv6, however then the DNS queries fail completely

Excellent question what would be the destination for IPv6 or what is the equivalent to 127.0.0.1 for IPv6?

would it be ::1 for the loopback like 127.0.0.1 is for IPv4 loopback?


Normally ::1 is the IPv6-localhost-Address. I must configure the IPv6-Address of the Interface (created an Alias) instead of ::1 in the NAT Rule and then it works. The clients resolves DNS-Records even if using his own IPv6-DNS-Servers.

I tested this with my Android Phone. This has the App DNSChanger installed
https://play.google.com/store/apps/details?id=com.frostnerd.dnschanger
with this App you can use other DNS-Server. With the IPv6 DNS NAT Rule you can farther resolve your own DNS-Records in the Override Tab from Unbound DNS. Normally when using a external DNS-Server you can't resolve internal DNS-Records.

Quote from: dave on June 25, 2020, 06:11:53 PM
QuoteNormally ::1 is the IPv6-localhost-Address. I must configure the IPv6-Address of the Interface (created an Alias) instead of ::1 in the NAT Rule and then it works. The clients resolves DNS-Records even if using his own IPv6-DNS-Servers.

Hey p1n0ck10, could you go in to a little more detail regarding this?

NAT redirects now use floating rules when the rule's running across multiply interfaces.

You saying I'm going to have to create individual rules & aliases for each interfaces ipv6 address?

Currently I've got a floating ipv6 NAT rule redirecting to ::1, and it's clearly not working.

Did either of you find a solution for IPv6. I was having the exact same issue as dave.

Quote from: gspannu on February 16, 2023, 01:47:10 AM

Figured out a way to solve this issue.
You can now safely use 127.0.0.1 in your NAT rule, instead of specifying the interface address (192.168.1.1 and other VLAN addresses).

The culprit here is AdGuardHome; as the 127.0.0.1 setting works fine when using Unbound instead of AGH.

Steps to FIX:
S1) Goto AdGuardHome webpage, navigate to 'Setup Guide' page
S2) Note down all the IP addresses (you can ignore the https, tls, quic addresses if any)

S3) Stop AdGuardHome service
S4) Edit the AdGuardHome.yaml file manually (make a backup !)
- file should be here... /usr/local/AdGuardHome/AdGuardHome.yaml
S5) Find the bind_hosts: line in the file
S6) Remove the 0.0.0.0 and replace with all the IP addresses you wish to listen
e.g.
bind_hosts:
- aaa.xxx.yyy.zzz
- 127.0.0.1
- ::1
- fe80::1%lo0
- 192.168.1.1
- 192.168.10.1
- 192.168.60.1
- 10.0.0.1

S7) Save the file
S8) Enable AdGuardHome service again.
S9) Goto AdGuardHome webpage, navigate to 'Setup Guide' page
S10) Compare these with Step2 - They should be the same as before...

All set...

You can now change your NAT rules to 127.0.0.1 instead of 192.168.1.1
:)

@Aida, AdGuardHome does not start any more when I add all these hosts under "bind_hosts":


    - 192.168.1.1
    - 127.0.0.1
    - ::1
    - fd00:1::
    - fe80::f690:edff:fe00:b3a1

So I fixed the issue with AdGuard not starting: it seems it has issues parsing specific IPv6. This works:


  bind_hosts:
    - 192.168.1.1
    - 127.0.0.1
    - ::1
    - 'fd00:1::'


But, the problem remains, a DNS request to 1.1.1.1 for example is properly redirected to AdGuard, but the answer does not make it to the client initiating the request.

In the packet capture done on the client (laptop) I can see what the reason is: the DNS request goes to 1.1.1.1, as expected, but the answer is coming from 192.168.1.1, which of course is then ignored.

Does someone know how I can get the Source IP "faked" on the way back? Or is that not really possible?

It's not really that important, it's in case I have a device on my home network with a hardcoded DNS server, but it would be useful in this case.

June 03, 2023, 12:40:22 PM #66 Last Edit: June 03, 2023, 01:03:58 PM by Vexz
Quote from: aida on April 26, 2023, 07:28:14 PM
Did either of you find a solution for IPv6. I was having the exact same issue as dave.
Sadly no. Wish I knew the answer but using an Alias for ::1 and/or the link local IPv6 of my OPNsense's LAN interface does not work. I really don't know how to make this work.

Hopefully giving this topic a push with this post raises some attention.

Edit:
I read that you should use an Alais with the type "Dynamic IPv6 Host" and did as told in the documentation but still no luck.

Hi! Nice write up. Thank you!

I was toying around with this before finding this HOWTO-resource.

So here is what I did and wanted to check that I haven't created any holes or stupid things.

Got 3 networks. LAN, KIDS and GUEST. KIDS and GUEST are VLANs.
Got a alias net_RFC1918 which holds all private network addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)

Port Forward:
Checked all three networks above.
Proto: TCP/UDP
Destination: ! net_RFC1918
Ports: 53
Redirect IP: 127.0.0.1
This would catch any DNS request going outside private addresses and forward them to localhost DNS.

Floating rule:
It was automatically created from the Port Forward and looks like this.
Proto: TCP/UDP
Source: *
Destination: *
Port: 53
And it indicates that 3 interfaces are involved in this rule.

Allow rule for KIDS and GUEST are as follows, example here is for KIDS:
Proto: IPv4*
Source: KIDS_net
Destination: ! net_RFC1918

The thought here is that everything not targeting any private address is going through. Not allowing KIDS or GUEST to access the LAN 😁 (can't have my creative kids in the LAN as they install all sort of things... Yes, we clean them out periodically and learns from the cyber-not-to-do lesson...)

Allow rule for LAN is default. Any allow.

So, good or bad take on the DNS redirect thingy?

Brgs!

October 10, 2023, 04:45:50 PM #68 Last Edit: January 04, 2024, 06:33:03 PM by Lakkiada
Hey All  ;D,

Hopefully, this can be helpful as I saw several posts above in regards to NTP and PiHoles specifically, both of which I have running on my network and across a VLAN for IOT devices. I have been running OPNSense with a PiHole for several years- I've been able to NAT things to it without issue.

Physical setup is pretty simple - Modem -> OPNSense -> Managed Switch -> PiHole -> End Users

First suggestion: If you don't already, start using Aliases! They make the rule tables much easier to read and understand.

Let's address the PiHole or local DNS server other than OPNSense itself. The reason it breaks when this (the generic tutorial rule) is applied to the PiHole address is that the NAT rule will also catch the requests coming out of the PiHole, UNLESS you also change the source piece of the rule. We actually need to use both inverse matches (the !) on source and destination for this to work properly. See attachment 1. It reads as follows;

On LAN interface - if the source address is NOT the PiHole on any port (end devices randomize source ports) AND the destination address is NOT the PiHole on port 53, then NAT the request to the PiHole on port 53.

What this double negative rule does, is allow the PiHole to send traffic to the router/firewall whilst catching every other request.

For the VLAN or IOT interface - the source inverse is not needed as the PiHole resides on a different subnet, so you can simply say- If the destination is NOT the PiHole, NAT it to the PiHole. See attachment 2. I have a firewall rule in place that allows/passes DNS traffic to the PiHole (can be seen in attachment 3).

Furthermore, I block all other DNS requests and keep the IOT devices from talking to each other. See attachment 3. You can now extrapolate this idea across however many VLANs or subnets are in use.

Next, let's address the NTP service. I use the OPNSense machine to host NTP, which makes the NAT rules quite simple. The service is also applied on the VLAN/IOT interface. Unfortunately, within the OPNSense UI we can't select multiple destinations, so separate NAT rules are required for each interface (no biggie, just clone it and modify the interface). Additionally, within the UI, I could not select from known addresses/interfaces for the NAT IP as I could for any other NAT rule (appears to be a UI bug). I opted not using an alias here as the addresses are those of the interfaces themselves i.e. X.X.X.1 - NTP only uses UDP on port 123. See attachment 4.

The NTP rule(s) read as follows:
On selected Interface- any source on UDP that the destination is NOT the interface address on port 123, then NAT to the interface address on port 123.

Finally, don't forget to adjust the rule order across all interfaces to get traffic flowing as you intend. Remember, they process top down. Hopefully, this can save someone from banging their head too many times or giving up on the PiHole altogether! Happy computing!

Quote from: Lakkiada on October 10, 2023, 04:45:50 PM
Also, DNS does not use TCP (DoT uses TCP, but uses port 853 not 53). You can simplify and clean up the rules by applying it to UDP only or adjusting to your use case.

This is factually incorrect, do NOT do this. You will cause yourself a lot of trouble with large DNS responses and things like DNSSEC. Even worse when EDNS0 does not work for some reason.

"Traditional" DNS queries only use UDP but are limited, AFAIK. eDNS and DNSSec are extensions to the DNS system - also AFAIK. However, after reading further about DNS extensions @ https://en.wikipedia.org/wiki/Domain_Name_System and https://en.wikipedia.org/wiki/Extension_Mechanisms_for_DNS It appears that these extensions run on TCP across port 53, thus I decided to enable the rules for both protocols (TCP/UDP) across port 53, as per your suggestion.

If I am incorrect please point me to resources. I am all in for continued learning and ongoing education. Hopefully, with this change we can see further performance improvements! To clarify, I changed the protocols in my firewall allow rules and also the NAT rules to be sure that traffic may flow as intended.

Thanks for the tip! @ doktornotor


January 04, 2024, 06:53:09 PM #72 Last Edit: January 05, 2024, 01:48:18 AM by Lakkiada
Edited my original post to remove the inaccuracy. Dug up the old Unix networking book to clarify in my own brain:
"DNS hostname lookups are typically performed over UDP, but DNS also uses TCP for some operations."

Further in the hardening section, I was able to locate:
"DNS communicates over both UDP and TCP. Because UDP is a quick, packet-based protocol that allows for limited data transfer, it is typically used for the actual process of hostname resolution. TCP, meanwhile, is most commonly used for transactions that require large, reliable, and sustained data transfer- that is, zone transfers. However, individual queries can be made over TCP as well."

I must have confused the information in the zone transfer section as it goes onto discuss blocking TCP on 53 specifically in business settings and does warn: "in rare cases, this may block DNS queries, which are also permitted to use TCP. So use this approach with caution."

Thanks again for pointing me in the right direction @doktornotor and apologies for any confusion caused.

Quote from: 9axqe on May 14, 2023, 11:42:03 AM
So I fixed the issue with AdGuard not starting: it seems it has issues parsing specific IPv6. This works:


  bind_hosts:
    - 192.168.1.1
    - 127.0.0.1
    - ::1
    - 'fd00:1::'


But, the problem remains, a DNS request to 1.1.1.1 for example is properly redirected to AdGuard, but the answer does not make it to the client initiating the request.

In the packet capture done on the client (laptop) I can see what the reason is: the DNS request goes to 1.1.1.1, as expected, but the answer is coming from 192.168.1.1, which of course is then ignored.

Does someone know how I can get the Source IP "faked" on the way back? Or is that not really possible?

It's not really that important, it's in case I have a device on my home network with a hardcoded DNS server, but it would be useful in this case.

I had the same problem and the reason is that the DNS request from the client with be answered by the AdGuard server directly to the client, because the systems are in the same network. The IP of the answer will be rewrite by the firewall, because the OPNsense redirected the traffic. There are currently two solutions, which I found out.

  • Put the AdGuard server to a new subnet to use OPNsense for network traffic.
  • Change the netmask of the AdGuard server to 255.255.255.255

But keep in mind that for both options all the traffic will be route via the OPNsense firewall and not anymore directly at the same network!

It works now for me, I'm not sure I remember what I changed.

I have a NAT rule that redirects TCP/UDP 53 to 127.0.0.1 and one for ::1 and now the DNS lookups are corrected redirected and correctly answered, with source IP "faked".