marfillaster · notes

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.

Overview

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.

1. What you need first

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:

  • VLAN segmentation. One flat LAN bridge is fine. If you do have VLANs, the same three steps apply per RA interface.
  • An IPv6 uplink. The ULA is internal and needs no delegation; DoH egresses over IPv4. A house with zero IPv6 internet still gets encrypted upstream resolution and a stable resolver address.
  • WireGuard, a VPS, or the rest of the CGNAT build. Those recover global IPv6; none of them are involved in resolving names or in advertising a local resolver.

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.

2. Conventions and placeholders

PlaceholderMeaning
<ULA_PREFIX>Your RFC 4193 ULA /48, e.g. fd7a:1b2c:3d4e. Generate one randomly; do not reuse the example.
bridgeThe interface your LAN clients are on (defconf bridge here).
192.168.88.0/24The 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.

3. Encrypted upstream — DoH with bootstrap pins

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.

bash

bash

# 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"

4. A ULA on the LAN — the stable resolver address

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.

bash

bash

/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"

5. Advertise the resolver — RA RDNSS

Router Advertisements carry the resolver to clients (RFC 8106). This needs IPv6 enabled on the LAN — it does not need IPv6 to the internet.

bash

bash

/ipv6/nd add interface=bridge advertise-dns=self \
  managed-address-configuration=no other-configuration=no

6. Stop handing out a DHCPv4 resolver

With 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.

bash

bash

# Defconf scope; repeat for every DHCPv4 network you serve.
/ip/dhcp-server/network set [find address=192.168.88.0/24] dns-none=yes

7. Verification

bash

bash

# 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 server

A 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.

References

Next in the series

Standards

Share

Comments

View or add comments