Search code examples
dnsopnsense

How do I work around forced DNS with routing?


I have a router running OPNsense, an internal DNS server (AdGuard Home), and a web server at 10.1.2.3:443 serving content via test.example.com. I'm using split DNS like this:

  • Internal users: My LAN's DNS server says test.example.com IN A 10.1.2.3, traffic goes directly to internal server
  • External users: My external DNS server says test.example.com IN A [my WAN IP], traffic gets port forwarded by the router to the internal server

However, there's a problem: some platforms, Android being the biggest offender, force their own DNS settings, either as Google's own DNS like 8.8.8.8 or DNS over HTTP (DoH). That means internal users get resolved to the external IP and traffic doesn't reach the server.

Is there a way of solving this at the router level? How can I tell OPNsense "if a LAN user requests WAN IP port 443, send that to 10.1.2.3 instead? And would doing so break my OPNsense web UI which is accessible (only internally) via HTTPS? I understand what's happening but I'm really bad at translating that into the correct firewall or routing rules I should create.

Things I've tried, or that are infeasible:

  • My DHCP settings advertise only my internal DNS server with no fallback. That doesn't matter though if a client chooses their own DNS server.
  • On the router I tried to block outbound port 53 traffic and used masquerading to redirect all outbound port 53 traffic to my own DNS server. But that's a game of whack-a-mole, especially as more and more devices are using DoH and it's harder to intercept. Also, some less-behaved devices get really mad if they can't reach their expected DNS server.
  • Some client devices let you control DNS, either indirectly or directly. However, it's not reasonable for me to configure every client device because that puts the burden on my users to have properly configured devices.

Solution

  • How can I tell OPNsense "if a LAN user requests WAN IP port 443, send that to 10.1.2.3 instead?"

    This is done by OPNsense's existing DNAT (port-forward) function. Most likely it can be handled by the same rule that you're using for access from WAN. It's not where the problem lies.

    The actual problem is that traffic in the other direction will not go through OPNsense. Your server knows that the client is in the same subnet, and will send responses directly to that client's MAC address, not giving OPNsense the opportunity to apply SNAT to the reply packets.

    As a result, even though the client sent a packet to WAN_IP, it will receive a response from 10.1.2.3 – and will ignore it, because it's expecting to receive a response from WAN_IP instead.

    There are two ways to solve this: a) Put your server in a separate subnet from clients, so that all traffic would naturally flow through the router; or b) Enable the "NAT hairpin" option in OPNsense, so that it would use SNAT trickery to force the server to send its replies towards the router.

    And would doing so break my OPNsense web UI which is accessible (only internally) via HTTPS?

    Not necessarily; if the rule is set to only match packets with WAN_IP as their destination, then it would have no effect on packets to OPNsense's internal address.