[Feature] Adding an option to prevent tailscale clients adding ~. to its DNS search domains for systemd-resolved. #862

Closed
opened 2025-12-29 02:24:59 +01:00 by adam · 6 comments
Owner

Originally created by @AlynxZhou on GitHub (Nov 25, 2024).

Use case

systemd-resolved works as a multicast DNS server, which means by default when it queries domains, it will try all available providers, and use the fastest result, unless the domain matches one of DNS search domains of one provider, then it will only use this provider to query.

Tailscale by default registers itself as one of providers of systemd-resolved, but it adds ~. to its DNS search domains, this means all domains will match, and makes systemd-resolved's multicast invalid, all DNS queries go to tailscale's DNS. User may only wants to query tailscale domains via tailscale.

Description

Setting tailcfg.DNSConfig.FallbackResolvers instead of tailcfg.DNSConfig.Resolvers can prevent tailscale clients from adding ~.. In previous version this behavior is controlled by override_local_dns, but this option is removed because it does not work as intended, and then we lose a way to control this behavior.

Is there any chance to add an option to control setting tailcfg.DNSConfig.FallbackResolvers instead of tailcfg.DNSConfig.Resolvers?

Contribution

  • I can write the design doc for this feature
  • I can contribute this feature

How can it be implemented?

Just bring back https://github.com/juanfont/headscale/pull/905/files#diff-0e426a43248661127a0c0ee115aef7a1093b635f8993b3f7ebb1dd9f05b8f249R406-R410, but with a better option to describe what it does?

Originally created by @AlynxZhou on GitHub (Nov 25, 2024). ### Use case systemd-resolved works as a multicast DNS server, which means by default when it queries domains, it will try all available providers, and use the fastest result, unless the domain matches one of DNS search domains of one provider, then it will only use this provider to query. Tailscale by default registers itself as one of providers of systemd-resolved, but it adds `~.` to its DNS search domains, this means all domains will match, and makes systemd-resolved's multicast invalid, all DNS queries go to tailscale's DNS. User may only wants to query tailscale domains via tailscale. ### Description Setting `tailcfg.DNSConfig.FallbackResolvers` instead of `tailcfg.DNSConfig.Resolvers` can prevent tailscale clients from adding `~.`. In previous version this behavior is controlled by `override_local_dns`, but this option is removed because it does not work as intended, and then we lose a way to control this behavior. Is there any chance to add an option to control setting `tailcfg.DNSConfig.FallbackResolvers` instead of `tailcfg.DNSConfig.Resolvers`? ### Contribution - [ ] I can write the design doc for this feature - [X] I can contribute this feature ### How can it be implemented? Just bring back <https://github.com/juanfont/headscale/pull/905/files#diff-0e426a43248661127a0c0ee115aef7a1093b635f8993b3f7ebb1dd9f05b8f249R406-R410>, but with a better option to describe what it does?
adam added the enhancement label 2025-12-29 02:24:59 +01:00
adam closed this issue 2025-12-29 02:24:59 +01:00
Author
Owner

@nblock commented on GitHub (Dec 20, 2024):

The Tailscale SaaS behaves as follows when changing the setting Override local DNS:

When enabled in the control plane, Tailscale configures systemd-resolved as default route (+DefaultRoute) and also
configures a route-only domain ~. so all domains will match and Tailscale will receive all DNS requests.

$ resolvectl status tailscale0
Link 4 (tailscale0)
    Current Scopes: DNS
         Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 100.100.100.100
       DNS Servers: 100.100.100.100
        DNS Domain: taile8db11.ts.net ~.
     Default Route: yes

When disabled in the control plane, Tailscale will not set the default route (-DefaultRoute) and the route-only domain
is not present in the list of DNS domains. It will only handle requests that match the list of domains.

$ resolvectl status tailscale0
Link 4 (tailscale0)
    Current Scopes: DNS
         Protocols: -DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 100.100.100.100
       DNS Servers: 100.100.100.100
        DNS Domain: taile8db11.ts.net ~0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa ~100.100.in-addr.arpa ~101.100.in-addr.arpa ~102.100.in-addr.arpa ~103.100.in-addr.arpa ~104.100.in-addr.arpa ~105.100.in-addr.arpa ~106.100.in-addr.arpa
                    ~107.100.in-addr.arpa ~108.100.in-addr.arpa ~109.100.in-addr.arpa ~110.100.in-addr.arpa ~111.100.in-addr.arpa ~112.100.in-addr.arpa ~113.100.in-addr.arpa ~114.100.in-addr.arpa ~115.100.in-addr.arpa ~116.100.in-addr.arpa
                    ~117.100.in-addr.arpa ~118.100.in-addr.arpa ~119.100.in-addr.arpa ~120.100.in-addr.arpa ~121.100.in-addr.arpa ~122.100.in-addr.arpa ~123.100.in-addr.arpa ~124.100.in-addr.arpa ~125.100.in-addr.arpa ~126.100.in-addr.arpa
                    ~127.100.in-addr.arpa ~64.100.in-addr.arpa ~65.100.in-addr.arpa ~66.100.in-addr.arpa ~67.100.in-addr.arpa ~68.100.in-addr.arpa ~69.100.in-addr.arpa ~70.100.in-addr.arpa ~71.100.in-addr.arpa ~72.100.in-addr.arpa
                    ~73.100.in-addr.arpa ~74.100.in-addr.arpa ~75.100.in-addr.arpa ~76.100.in-addr.arpa ~77.100.in-addr.arpa ~78.100.in-addr.arpa ~79.100.in-addr.arpa ~80.100.in-addr.arpa ~81.100.in-addr.arpa ~82.100.in-addr.arpa
                    ~83.100.in-addr.arpa ~84.100.in-addr.arpa ~85.100.in-addr.arpa ~86.100.in-addr.arpa ~87.100.in-addr.arpa ~88.100.in-addr.arpa ~89.100.in-addr.arpa ~90.100.in-addr.arpa ~91.100.in-addr.arpa ~92.100.in-addr.arpa
                    ~93.100.in-addr.arpa ~94.100.in-addr.arpa ~95.100.in-addr.arpa ~96.100.in-addr.arpa ~97.100.in-addr.arpa ~98.100.in-addr.arpa ~99.100.in-addr.arpa ~ts.net
     Default Route: no

As a workaround, one may configure domains and the default route explicitly on the tailscale0 link and "undo" the DNS override:

resolvectl domain tailscale0 tn.example.com
resolvectl default-route tailscale0 off

Likely this will only last until the next network change where tailscale gets a chance to reconfigure DNS.

@nblock commented on GitHub (Dec 20, 2024): The Tailscale SaaS behaves as follows when changing the setting [Override local DNS](https://tailscale.com/kb/1054/dns#override-local-dns): When enabled in the control plane, Tailscale configures systemd-resolved as default route (`+DefaultRoute`) and also configures a route-only domain `~.` so all domains will match and Tailscale will receive all DNS requests. ```shell $ resolvectl status tailscale0 Link 4 (tailscale0) Current Scopes: DNS Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported Current DNS Server: 100.100.100.100 DNS Servers: 100.100.100.100 DNS Domain: taile8db11.ts.net ~. Default Route: yes ``` When disabled in the control plane, Tailscale will not set the default route (`-DefaultRoute`) and the route-only domain is not present in the list of DNS domains. It will only handle requests that match the list of domains. ```shell $ resolvectl status tailscale0 Link 4 (tailscale0) Current Scopes: DNS Protocols: -DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported Current DNS Server: 100.100.100.100 DNS Servers: 100.100.100.100 DNS Domain: taile8db11.ts.net ~0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa ~100.100.in-addr.arpa ~101.100.in-addr.arpa ~102.100.in-addr.arpa ~103.100.in-addr.arpa ~104.100.in-addr.arpa ~105.100.in-addr.arpa ~106.100.in-addr.arpa ~107.100.in-addr.arpa ~108.100.in-addr.arpa ~109.100.in-addr.arpa ~110.100.in-addr.arpa ~111.100.in-addr.arpa ~112.100.in-addr.arpa ~113.100.in-addr.arpa ~114.100.in-addr.arpa ~115.100.in-addr.arpa ~116.100.in-addr.arpa ~117.100.in-addr.arpa ~118.100.in-addr.arpa ~119.100.in-addr.arpa ~120.100.in-addr.arpa ~121.100.in-addr.arpa ~122.100.in-addr.arpa ~123.100.in-addr.arpa ~124.100.in-addr.arpa ~125.100.in-addr.arpa ~126.100.in-addr.arpa ~127.100.in-addr.arpa ~64.100.in-addr.arpa ~65.100.in-addr.arpa ~66.100.in-addr.arpa ~67.100.in-addr.arpa ~68.100.in-addr.arpa ~69.100.in-addr.arpa ~70.100.in-addr.arpa ~71.100.in-addr.arpa ~72.100.in-addr.arpa ~73.100.in-addr.arpa ~74.100.in-addr.arpa ~75.100.in-addr.arpa ~76.100.in-addr.arpa ~77.100.in-addr.arpa ~78.100.in-addr.arpa ~79.100.in-addr.arpa ~80.100.in-addr.arpa ~81.100.in-addr.arpa ~82.100.in-addr.arpa ~83.100.in-addr.arpa ~84.100.in-addr.arpa ~85.100.in-addr.arpa ~86.100.in-addr.arpa ~87.100.in-addr.arpa ~88.100.in-addr.arpa ~89.100.in-addr.arpa ~90.100.in-addr.arpa ~91.100.in-addr.arpa ~92.100.in-addr.arpa ~93.100.in-addr.arpa ~94.100.in-addr.arpa ~95.100.in-addr.arpa ~96.100.in-addr.arpa ~97.100.in-addr.arpa ~98.100.in-addr.arpa ~99.100.in-addr.arpa ~ts.net Default Route: no ``` --- As a workaround, one may configure domains and the default route explicitly on the `tailscale0` link and "undo" the DNS override: ``` resolvectl domain tailscale0 tn.example.com resolvectl default-route tailscale0 off ``` Likely this will only last until the next network change where tailscale gets a chance to reconfigure DNS.
Author
Owner

@st0nie commented on GitHub (Dec 30, 2024):

Another workaround is to set headscale's global dns to empty, in which case the system will not use tailscale's dns to resolve other domains even if tailscale --accept-dns. But the disadvantage of this workaround is that if the linux system does not use systemd-resolved but uses 100.100.100.100 as the dns, the system won't be able to resolve domains except headscale domain (because the upstream dns is not set).

@st0nie commented on GitHub (Dec 30, 2024): Another workaround is to set headscale's global dns to empty, in which case the system will not use tailscale's dns to resolve other domains even if `tailscale --accept-dns`. But the disadvantage of this workaround is that if the linux system does not use `systemd-resolved` but uses `100.100.100.100` as the dns, the system won't be able to resolve domains except headscale domain (because the upstream dns is not set).
Author
Owner

@Toomoch commented on GitHub (Feb 3, 2025):

This is a big problem, as I can't resolve local domains served by my router's DNS server (.lan) and tailscale names at the same time. It's one or the other with --accept-dns. Interestingly, in Android everything works correctly.

@Toomoch commented on GitHub (Feb 3, 2025): This is a big problem, as I can't resolve local domains served by my router's DNS server (.lan) and tailscale names at the same time. It's one or the other with `--accept-dns`. Interestingly, in Android everything works correctly.
Author
Owner

@Devristo commented on GitHub (Mar 24, 2025):

This has hit me in the head a few times as well. I think it also breaks DNS resolution inside my docker containers / k3s containers from time to time, but have not been able to create a clear reproducer. However, sude tailscale set --accept-dns=false fixes everything.

@Devristo commented on GitHub (Mar 24, 2025): This has hit me in the head a few times as well. I think it also breaks DNS resolution inside my docker containers / k3s containers from time to time, but have not been able to create a clear reproducer. However, `sude tailscale set --accept-dns=false` fixes everything.
Author
Owner

@VolatileDream commented on GitHub (Apr 12, 2025):

I recently ran into this issue as a result of noticing that mDNS was broken only on Headscale connected Windows machines (which might be a bug, and the behaviour is tracked in https://github.com/tailscale/tailscale/issues/15093). And then noticed that DNS resolution was subtly wrong across a bunch of other devices that connected to Headscale.

I'll lend a perhaps contradictory opinion to this feature request: I don't mind if there's not a setting for this, I think the default should be to use tailcfg.DNSConfig.FallbackResolvers (equivalent to override_local_dns=false) and would be fine if Headscale folks decided that for simplicity sake they wouldn't add the feature.

I have worked around the current behaviour by using the config that follows, since my machines are configured to use something other than 100.100.100.100 for DNS.

dns:
  // Setting is needed to enable Tailscale to do DNS resolution.
  magic_dns: true
  // Since Tailscale uses two lists instead of an override setting,
  // passing an empty tailcfg.DNSConfig.Resolvers causes clients
  // to not override system DNS.
  nameservers:
    global: []

edit: I don't mind if there's no setting. 🤦 I dropped the "don't" during editing.

@VolatileDream commented on GitHub (Apr 12, 2025): I recently ran into this issue as a result of noticing that mDNS was broken only on Headscale connected Windows machines (which might be a bug, and the behaviour is tracked in https://github.com/tailscale/tailscale/issues/15093). And then noticed that DNS resolution was subtly wrong across a bunch of other devices that connected to Headscale. I'll lend a perhaps contradictory opinion to this feature request: I **don't** mind if there's not a setting for this, I think the default should be to use `tailcfg.DNSConfig.FallbackResolvers` (equivalent to `override_local_dns=false`) and would be fine if Headscale folks decided that for simplicity sake they wouldn't add the feature. I have worked around the current behaviour by using the config that follows, since my machines are configured to use something other than `100.100.100.100` for DNS. ``` dns: // Setting is needed to enable Tailscale to do DNS resolution. magic_dns: true // Since Tailscale uses two lists instead of an override setting, // passing an empty tailcfg.DNSConfig.Resolvers causes clients // to not override system DNS. nameservers: global: [] ``` edit: I **don't** mind if there's no setting. 🤦 I dropped the "don't" during editing.
Author
Owner

@nblock commented on GitHub (Apr 13, 2025):

I'll lend a perhaps contradictory opinion to this feature request: I mind if there's not a setting for this, I think the default should be to use tailcfg.DNSConfig.FallbackResolvers (equivalent to override_local_dns=false) and would be fine if Headscale folks decided that for simplicity sake they wouldn't add the feature.

I agree, PR https://github.com/juanfont/headscale/pull/2438 also changes the default to align with Tailscale's default behavior.

@nblock commented on GitHub (Apr 13, 2025): > I'll lend a perhaps contradictory opinion to this feature request: I mind if there's not a setting for this, I think the default should be to use `tailcfg.DNSConfig.FallbackResolvers` (equivalent to `override_local_dns=false`) and would be fine if Headscale folks decided that for simplicity sake they wouldn't add the feature. I agree, PR https://github.com/juanfont/headscale/pull/2438 also changes the default to align with Tailscale's default behavior.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/headscale#862