macOS 26 破坏了自定义 DNS 设置,包括 .internal
macOS 26 breaks custom DNS settings including .internal

原始链接: https://gist.github.com/adamamyl/81b78eced40feae50eae7c4f3bec1f5a

## macOS 26.3.1 DNS 漏洞总结 macOS 26.3.1 (Darwin 25.3.0) 中最近发现的一个漏洞破坏了标准 `/etc/resolver/` 针对自定义或私有顶级域名 (TLD) – 例如 `.internal`、`.test` 或 `.home.arpa` – 的域名 DNS 配置。 以前,macOS 可靠地使用这些文件将特定域名的 DNS 查询路由到指定名称服务器(例如本地 dnsmasq 实例)。 现在,`mDNSResponder` 错误地拦截了这些未在 IANA 注册的 TLD 的查询,将其视为多播 DNS (mDNS) 请求,而不是将其转发到指定的单播名称服务器。 这导致依赖这些自定义域名的应用程序无法解析域名,影响本地开发工作流程、Docker 容器 DNS 以及 Vagrant 和 Kubernetes 等工具。 虽然像 `google.com` 这样的标准 TLD 仍然不受影响,但此漏洞会导致解析静默失败,`scutil --dns` 显示配置的解析器,但实际 DNS 流量并未发送到目标服务器。 一种解决方法是手动将条目添加到 `/etc/hosts`,但这对于动态环境来说是不切实际的。 已经向 Apple 提交了错误报告:[https://feedbackassistant.apple.com/feedback/22280434](https://feedbackassistant.apple.com/feedback/22280434)。

最近的macOS更新正在破坏一些用户的自定义DNS设置,特别是那些使用`dnsmasq`等工具在Docker本地开发环境中的用户。该更新会静默地禁用解析内部域名(例如`.internal`)的能力,导致用户无法访问他们的容器。 这个问题似乎源于苹果弃用了较旧的DNS配置方法。虽然使用`scutil`的更复杂解决方案*可能*仍然有效,但许多用户对小版本更新中的意外故障感到沮丧。 用户表达了对苹果控制操作系统的更广泛担忧,一些人呼吁拆分硬件和软件部门。其他报告的问题包括ScreenTime阻止端口8080以及Keychain Access出现问题。最初的发布者已向苹果提交了错误报告,并建议如果依赖自定义DNS,则推迟更新。
相关文章

原文
Ah, the joys of waking up to find the Mac's done an overnight upgrade… and erm, suddenly things stop working. Thankfully, me and Claude managed to work out what the fuck is going on… I'm sharing here, as well as having raised in on https://feedbackassistant.apple.com/feedback/22280434 (that seems to need a login?). # Bug Report: macOS 26 breaks /etc/resolver/ supplemental DNS for custom TLDs **Product:** macOS 26.3.1 (Darwin 25.3.0, Build 25D771280a) **Component:** Networking → DNS / mDNSResponder **Regression from:** macOS 25.x (working immediately prior to overnight update) --- ## Summary The `/etc/resolver/` per-domain DNS resolver mechanism — an Apple-documented, long-standing macOS feature — is silently broken in macOS 26 for any TLD that is not present in the IANA root zone. `mDNSResponder` intercepts queries for custom/private TLDs and handles them as mDNS (multicast DNS), never consulting the unicast nameserver specified in the resolver file. This breaks an entire class of local development and private network DNS workflows that previously worked correctly on macOS 25 and earlier. --- ## Background macOS supports per-domain DNS resolver configuration via files placed in `/etc/resolver/`. A file named `/etc/resolver/internal` containing `nameserver 127.0.0.1` instructs the DNS stack to send all `*.internal` queries to the local nameserver at 127.0.0.1. This mechanism is documented in `man 5 resolver` and has worked reliably since at least macOS 10.6. It is widely used by developers running local DNS servers (dnsmasq, bind, unbound) to resolve private domain suffixes. This machine runs `dnsmasq` (via Homebrew) as a local DNS resolver, configured to answer queries for `*.internal` domains (static entries for a local web application) and forward everything else upstream. The `/etc/resolver/internal` file routes these queries to dnsmasq. This setup worked correctly on macOS 25.x. --- ## Steps to Reproduce 1. Install dnsmasq and configure it to answer a custom domain: ``` # /opt/homebrew/etc/dnsmasq.d/test.conf address=/probe.example-private/127.0.0.1 ``` 2. Start dnsmasq: `brew services start dnsmasq` 3. Verify dnsmasq answers directly: ``` dig @127.0.0.1 probe.example-private A +short # Returns: 127.0.0.1 ✓ ``` 4. Create a resolver file: ``` sudo sh -c 'echo "nameserver 127.0.0.1" > /etc/resolver/example-private' ``` 5. Flush DNS cache and restart mDNSResponder: ``` sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder ``` 6. Verify `scutil --dns` shows the resolver is registered: ``` scutil --dns | grep -A4 "example-private" # Shows: domain: example-private, nameserver: 127.0.0.1 ✓ ``` 7. Attempt to resolve via the system resolver: ``` ping -c1 probe.example-private # ping: cannot resolve probe.example-private: Unknown host ✗ python3 -c "import socket; print(socket.getaddrinfo('probe.example-private', 80))" # socket.gaierror: [Errno 8] nodename nor servname provided, or not known ✗ ``` --- ## Expected Behaviour `ping`, `curl`, and any application using `getaddrinfo()` should resolve `probe.example-private` to `127.0.0.1`, as specified by the dnsmasq `address=` directive, reached via the `/etc/resolver/example-private` unicast nameserver entry. This is exactly what happened on macOS 25.x. --- ## Actual Behaviour All resolution via `getaddrinfo()` (i.e. every real application — browsers, curl, ping) fails with "Unknown host". No DNS traffic reaches dnsmasq. Instead, `mDNSResponder` intercepts the query and immediately returns a cached "No Such Record" mDNS response with an anomalously large TTL (~108002 seconds). Evidence from `dns-sd -G v4 probe.example-private`: ``` Timestamp A/R Flags IF Hostname Address TTL 11:42:03.617 Add 40000002 0 probe.example-private. 0.0.0.0 108002 No Such Record ``` Evidence from `tcpdump -i lo0 -n port 53` captured during a `getaddrinfo()` call: ``` 0 packets captured ``` No packets reach dnsmasq on 127.0.0.1:53 at all. mDNSResponder handles the query entirely internally via mDNS and never consults the unicast nameserver. --- ## Scope Tested TLDs that fail: | TLD | Status | Notes | |-----|--------|-------| | `.internal` | Broken | IETF draft special-use TLD; worked on macOS 25 | | `.test` | Broken | RFC 6761 §6.2 — explicitly reserved for local testing | | `.home.arpa` | Broken | RFC 8375 — IANA reserved for residential private networks | | `.lan` | Broken | Widely used convention (not IANA reserved, but irrelevant) | | Arbitrary (e.g. `.emflocal`) | Broken | Any TLD not in the IANA root zone | **`.test` is particularly egregious**: RFC 6761 Section 6.2 explicitly reserves `.test` for exactly this use case — local/private DNS testing — and specifies that resolvers SHOULD resolve it via normal DNS mechanisms. macOS 26 silently overrides this by treating it as mDNS-only. `google.com`, `bbc.co.uk` and other standard IANA-registered TLDs continue to work correctly via the default unicast resolver. Only custom/unregistered/special-use TLDs are affected. --- ## Workaround The only reliable workaround is to add entries manually to `/etc/hosts`, which bypasses mDNSResponder entirely. This is impractical for dynamic use cases (e.g. Docker container DNS, where host entries change frequently) and requires sudo for every change. --- ## Impact This breaks the standard local development DNS workflow that has been documented and recommended by the macOS developer community for over a decade: - Any developer using dnsmasq + `/etc/resolver/` for `*.test`, `*.local`, `*.internal`, or other private TLDs - Docker Desktop's (and similar tools') container name resolution via custom TLDs - Any tool that generates `/etc/resolver/` entries as part of its macOS integration (e.g. Vagrant, Tailscale, various VPN clients) - Kubernetes local development tools (minikube, kind, k3d) that use `*.cluster.local` or similar The failure is silent: `scutil --dns` correctly shows the resolver configuration is registered, leading users to believe the setup is correct while resolution silently fails. There is no log output, no error, and no indication that mDNS interception is occurring. --- ## Environment - **macOS version:** 26.3.1 (ProductVersionExtra: (a)) - **Build:** 25D771280a - **Hardware:** Apple Silicon (arm64) - **Regression:** Working on macOS 25.x immediately before overnight system update - **dnsmasq version:** Homebrew, listening on 127.0.0.1:53 - **Verified via:** `dig @127.0.0.1` (works), `host` (works — uses own resolver), `ping`/`curl`/`python3 socket.getaddrinfo` (all fail) --- ## References - `man 5 resolver` (macOS) — documents the `/etc/resolver/` mechanism - RFC 6761 — Special-Use Domain Names (reserves `.test`, `.localhost`, `.invalid`, `.example`) - RFC 8375 — Special-Use Domain `home.arpa` - IETF draft-ietf-dnsop-interneti-mdn — `.internal` special-use proposal
联系我们 contact @ memedata.com