Build log · MikroTik RB5009 · DoH + stable RDNSS
Encrypted DNS with a stable resolver address on RouterOS
Cloudflare DoH upstream and a resolver address clients never have to relearn — a locally assigned ULA over RA RDNSS. No VLANs, no IPv6 uplink.
Build log · MikroTik RB5009 · DoH + stable RDNSS
Cloudflare DoH upstream and a resolver address clients never have to relearn — a locally assigned ULA over RA RDNSS. No VLANs, no IPv6 uplink.
This is a small, self-contained companion in the RB5009 CGNAT series. It does one thing: make a RouterOS v7 box resolve upstream over encrypted DNS while handing clients a resolver address that never changes.
It deliberately depends on almost nothing. There is no VLAN segmentation here, no WireGuard, and — the part worth stating plainly — no IPv6 uplink required. The stable resolver address is a Unique Local Address (RFC 4193): it is generated by you, on the LAN, and exists whether or not the ISP delegates a single bit of IPv6. The encrypted upstream is DoH over port 443, which leaves the house on whatever default route exists — plain IPv4 is fine. The whole thing works on a flat, single-subnet, IPv4-only-internet LAN; it simply also survives the prefix churn you get once real IPv6 shows up.
Every numbered section is paste-ready against a defconf RouterOS v7 box. The italic notes are the rationale — the trade-off being made and why.
A RouterOS v7 box with a working WAN and a LAN interface clients sit on. That is the whole list. Concretely it does not require:
The snippets below assume the defconf bridge on 192.168.88.0/24. Rename
the interface and subnet to match your box; nothing else changes.
| Placeholder | Meaning |
|---|---|
<ULA_PREFIX> | Your RFC 4193 ULA /48, e.g. fd7a:1b2c:3d4e. Generate one randomly; do not reuse the example. |
bridge | The interface your LAN clients are on (defconf bridge here). |
192.168.88.0/24 | The LAN's IPv4 subnet (defconf here). |
A ULA is fd00::/8 plus 40 random bits. Pick the 40 bits randomly once
and keep them — the whole value of a ULA is that it is stable and unique to
your network. fd7a:1b2c:3d4e::/48 is an illustrative value, not one to copy.
The router becomes the LAN resolver and forwards every query upstream over
Cloudflare DoH. The static records pin cloudflare-dns.com so the very first
query has somewhere to go before DoH itself can resolve anything.
# Apply this AFTER NTP has set the clock — `/tool/fetch` validates TLS
# against the CA bundle below, which fails on a fresh defconf box with a
# bad RTC. `/system/ntp/client set enabled=yes servers=time.cloudflare.com`
# is enough; verify with `/system/clock/print` before continuing.
/tool/fetch url=https://curl.se/ca/cacert.pem dst-path=cacert.pem
/certificate/import file-name=cacert.pem passphrase=""
/ip/dns set allow-remote-requests=yes max-concurrent-queries=200 \
use-doh-server=https://cloudflare-dns.com/dns-query verify-doh-cert=yes
/ip/dns/static
# A-only on purpose — see rationale below.
add address=104.16.248.249 name=cloudflare-dns.com comment="DoH bootstrap"
add address=104.16.249.249 name=cloudflare-dns.com comment="DoH bootstrap"Add a ULA /64 to the LAN interface and publish the router itself as a name on it. This address is reachable on-link with no IPv6 internet whatsoever — it is a locally assigned RFC 4193 prefix, not anything the ISP hands you.
/ipv6/address add interface=bridge address=<ULA_PREFIX>:1::1/64 \
advertise=yes comment="LAN ULA"
/ip/dns/static
# Reachable as the FQDN `router.lan` from any client whose resolver is the
# router (the default once §5/§6 are applied). No search-domain magic; type
# the dot-lan suffix.
add address=<ULA_PREFIX>:1::1 name=router.lan type=AAAA comment="LAN ULA"Router Advertisements carry the resolver to clients (RFC 8106). This needs IPv6 enabled on the LAN — it does not need IPv6 to the internet.
/ipv6/nd add interface=bridge advertise-dns=self \
managed-address-configuration=no other-configuration=noWith the resolver advertised over RDNSS, the DHCPv4 server should stop handing out a DNS server at all, so resolution is uniformly the ND RDNSS path.
# Defconf scope; repeat for every DHCPv4 network you serve.
/ip/dhcp-server/network set [find address=192.168.88.0/24] dns-none=yes# On the router: upstream really is DoH, not plain 53.
/ip/dns/cache/print # entries present
/log/print where message~"doh" # DoH server in use
# From a client: resolver is the ULA, and it answers.
dig @<ULA_PREFIX>:1::1 cloudflare.com
scutil --dns | grep -i 'nameserver\[' # macOS: expect the ULA
nslookup router.lan # resolves to the ULA
# No DHCPv4 resolver leaked.
ipconfig /all # Windows: no IPv4 DNS serverA client with the ULA as its only nameserver, resolving names while the WAN shows IPv4-only, is the whole proof: upstream is sealed and the resolver address is one that prefix churn can never move.
Comments