Use external DNS
This page shows how to make an external resolver, AdGuardHome in the examples, answer every DNS query that dae intercepts, while the resolver's own upstream queries go out through dae and its proxies. The resolver can run on the dae host or on another machine in the LAN. How dae intercepts DNS and how the sniffed domain feeds back into routing is described in DNS and how it works.
External DNS on the dae host
AdGuardHome runs on the dae host and resolves China mainland domains directly and everything else through dns.google:
Listen on: the same machine with dae, port 53.
China mainland: udp://223.5.5.5:53
Others: https://dns.google/dns-queryConfigure dae as follows:
Set
wan_interfacein theglobalsection, so that AdGuardHome's own upstream queries leave the host through dae and can be proxied.Insert the following rule at the start of the
routingsection. Without it, dae would intercept AdGuardHome's plain UDP queries to223.5.5.5and hand them back to AdGuardHome, which loops:pythonpname(AdGuardHome) && l4proto(udp) && dport(53) -> must_directKeep a routing rule that sends
dns.googlethrough a proxy, so the DoH upstream is proxied.Make AdGuardHome the upstream for every intercepted query in the
dnssection:dns { upstream { adguardhome: 'udp://127.0.0.1:53' } routing { request { fallback: adguardhome } } }If you bind to WAN, point
/etc/resolv.confat a public resolver, for examplenameserver 119.29.29.29, not at the local AdGuardHome. Queries to127.0.0.1stay on the loopback interface and never reach dae; queries to a public address leave through the NIC, where dae intercepts them and sends them to AdGuardHome through thednssection, so dae sees the answers.DNS services such as dnsmasq often overwrite
/etc/resolv.confafter a reboot. If this happens, uninstall the service or runsudo chattr +i /etc/resolv.conf.shellsudo chattr +i /etc/resolv.confshellchattr +i /etc/resolv.confIf you bind to LAN, a UDP query from a LAN client to the dae host's own port 53 is routed like any other packet and then handed to the resolver configured in the
dnssection, so dae sees the answer and itsdomain()rules match that client's traffic too. Advertising the dae host as the DNS server in DHCP therefore works. If you want the host's own resolver to answer those queries instead of dae, express it as a routing rule, for examplel4proto(udp) && dport(53) && dip(<address of the dae host>) -> must_direct.If DNS still fails without warning or error logs, move AdGuardHome off port 53. Another program on port 53 breaks interception on NICs that cannot disable checksum verification; see #31.
If you use PVE, refer to #37.
External DNS on another LAN machine
AdGuardHome runs on another machine in the LAN:
Listen on: 192.168.30.3:53 (mac address: 8c:16:45:36:1c:5a)
China mainland: udp://223.5.5.5:53
Others: https://dns.google/dns-queryConfigure dae as follows:
Set
lan_interfacein theglobalsection, so that AdGuardHome's own upstream queries pass through dae and can be proxied.Insert the following rule at the start of the
routingsection, for the same reason as on the dae host:pythonsip(192.168.30.3) && l4proto(udp) && dport(53) -> must_direct # Or use MAC address if in the same link: # mac('8c:16:45:36:1c:5a') && l4proto(udp) && dport(53) -> must_directKeep a routing rule that sends
dns.googlethrough a proxy.Make AdGuardHome the upstream for every intercepted query in the
dnssection:dns { upstream { adguardhome: 'udp://192.168.30.3:53' } routing { request { fallback: adguardhome } } }Have the DHCP server advertise a public resolver, not the AdGuardHome machine, as the DNS server. A query sent straight to
192.168.30.3normally travels over the LAN without passing dae, so dae does not see the answer; dae intercepts a query to any other address, answers it through AdGuardHome and sees the answer.If DNS still fails without warning or error logs, move AdGuardHome off port 53; see #31.
If you use PVE, refer to #37.
Source: dae upstream · AGPL-3.0 license.
