[Bug] DNS not working when using exit nodes #851

Open
opened 2025-12-29 02:24:49 +01:00 by adam · 15 comments
Owner

Originally created by @myOmikron on GitHub (Nov 12, 2024).

Is this a support request?

  • This is not a support request

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

When setting an exit-node on any client, DNS to external addresses isn't working anymore.

Steps tried:

  • Ping public IP: Working
  • Ping internal IP: Working
  • Resolve public domain using 100.100.100.100: Not working
  • Resolve internal domain using 100.100.100.100: Working
  • Manually setting DNS to public DNS: Working

When using public DNS, using tools like https://whatismyipaddress.com/, I can verify that I'm using the exit node as intended. I guess this is also the case when using MagicDNS, but as no public address is resolved, I can't check this right now.

Expected Behavior

DNS working regardless of usage of a exit-node

Steps To Reproduce

tailscale set --exit-node <exit-node>

Environment

- OS: Arch Linux
- Headscale version: v0.23.0
- Tailscale version: 1.76.6
  tailscale commit: 439305eeeda64a1851e8b775724ff1fbbd713207-dirty
  go version: go1.23.2

Also verified on Android with the tailscale App from google play store.
- Tailscale version: 1.76.2 (Oct. 17 2024)

Runtime environment

  • Headscale is behind a (reverse) proxy
  • Headscale runs in a container

Anything else?

No response

Originally created by @myOmikron on GitHub (Nov 12, 2024). ### Is this a support request? - [X] This is not a support request ### Is there an existing issue for this? - [X] I have searched the existing issues ### Current Behavior When setting an exit-node on any client, DNS to external addresses isn't working anymore. Steps tried: - Ping public IP: Working - Ping internal IP: Working - Resolve public domain using 100.100.100.100: Not working - Resolve internal domain using 100.100.100.100: Working - Manually setting DNS to public DNS: Working When using public DNS, using tools like https://whatismyipaddress.com/, I can verify that I'm using the exit node as intended. I guess this is also the case when using MagicDNS, but as no public address is resolved, I can't check this right now. ### Expected Behavior DNS working regardless of usage of a exit-node ### Steps To Reproduce `tailscale set --exit-node <exit-node>` ### Environment ```markdown - OS: Arch Linux - Headscale version: v0.23.0 - Tailscale version: 1.76.6 tailscale commit: 439305eeeda64a1851e8b775724ff1fbbd713207-dirty go version: go1.23.2 Also verified on Android with the tailscale App from google play store. - Tailscale version: 1.76.2 (Oct. 17 2024) ``` ### Runtime environment - [X] Headscale is behind a (reverse) proxy - [ ] Headscale runs in a container ### Anything else? _No response_
adam added the bugDNS labels 2025-12-29 02:24:49 +01:00
Author
Owner

@myOmikron commented on GitHub (Nov 16, 2024):

Additional information:

Got it to working after installing systemd-resolved and linking its stub-resolve to /etc/resolve.conf like proposed in tailwinds article about Linux DNS systems on the exit-node.

@myOmikron commented on GitHub (Nov 16, 2024): Additional information: Got it to working after installing `systemd-resolved` and linking its `stub-resolve` to `/etc/resolve.conf` like proposed in tailwinds article about Linux DNS systems on the exit-node.
Author
Owner

@nblock commented on GitHub (Nov 16, 2024):

Got it to working after installing systemd-resolved and linking its stub-resolve to /etc/resolve.conf like proposed in tailwinds article about Linux DNS systems on the exit-node.

Thanks for the update, is your issue now fully resolved?

For others with DNS related issues: recent tailscale versions have a command to print the DNS status which might be helpful for debugging: tailscale dns status

@nblock commented on GitHub (Nov 16, 2024): > Got it to working after installing `systemd-resolved` and linking its `stub-resolve` to `/etc/resolve.conf` like proposed in tailwinds article about Linux DNS systems on the exit-node. Thanks for the update, is your issue now fully resolved? For others with DNS related issues: recent tailscale versions have a command to print the DNS status which might be helpful for debugging: `tailscale dns status`
Author
Owner

@myOmikron commented on GitHub (Nov 18, 2024):

I don't think so.

The behavior I described was broken with a resolv.conf generated by tailscale, which pointed DNS to 100.100.100.100. As DNS should happen on my client machine asking 100.100.100.100 directly, I think the exit-node shouldn't have anything to do with resolving.

@myOmikron commented on GitHub (Nov 18, 2024): I don't think so. The behavior I described was broken with a `resolv.conf` generated by tailscale, which pointed DNS to `100.100.100.100`. As DNS should happen on my client machine asking `100.100.100.100` directly, I think the exit-node shouldn't have anything to do with resolving.
Author
Owner

@nblock commented on GitHub (Nov 22, 2024):

I don't think so.

I assume that /etc/resolv.conf was statically configured and tailscale overwrites the file on startup. Can you provide some logs during startup and then when you enable/disable the exit node? Given that you want to go back from systemd-resolved to a statically managed /etc/resolv.conf. DNS related configuration of your headscale might also be interesting.

@nblock commented on GitHub (Nov 22, 2024): > I don't think so. I assume that `/etc/resolv.conf` was statically configured and tailscale overwrites the file on startup. Can you provide some logs during startup and then when you enable/disable the exit node? Given that you want to go back from `systemd-resolved` to a statically managed `/etc/resolv.conf`. DNS related configuration of your headscale might also be interesting.
Author
Owner

@tho22 commented on GitHub (Nov 22, 2024):

Same problem here when --accept-dns=true
internal names are resolved. external ones are not :

$ dig nb-715.example.com @100.100.100.100 +short
10.197.4.3     ###<-- same with the 100.64 Net
$ dig google.com @100.100.100.100
;; communications error to 100.100.100.100#53: timed out
$ dig google.com @1.1.1.1 +short
142.251.36.238

config.yaml snippet:

dns:
  magic_dns: true
  #  magic_dns: false
  base_domain: example.com
  override_local_dns: true
  #override_local_dns: false
  nameservers:
    global:
      - 1.1.1.1

status on the client:

$ tailscale version
1.76.6
  tailscale commit: 1edcf9d466ceafedd2816db1a24d5ba4b0b18a5b
  other commit: d0a6cd8b27eb46f6dec31425499159f7949be7f9
  go version: go1.23.1
  
$ tailscale dns status

=== 'Use Tailscale DNS' status ===

Tailscale DNS: enabled.

Tailscale is configured to handle DNS queries on this device.
Run 'tailscale set --accept-dns=false' to revert to your system default DNS resolver.

=== MagicDNS configuration ===

This is the DNS configuration provided by the coordination server to this device.

MagicDNS: enabled tailnet-wide (suffix = example.com)

Other devices in your tailnet can reach this device at nb-715.example.com

Resolvers (in preference order):
  - 1.1.1.1

Split DNS Routes:

Search Domains:
  - example.com

=== System DNS configuration ===

This is the DNS configuration that Tailscale believes your operating system is using.
Tailscale may use this configuration if 'Override Local DNS' is disabled in the admin console,
or if no resolvers are provided by the coordination server.

  (reading the system DNS configuration is not supported on this platform)

[this is a preliminary version of this command; the output format may change in the future]

Server run on docker headscale/headscale:0.23.0
any ideas?

@tho22 commented on GitHub (Nov 22, 2024): Same problem here when --accept-dns=true internal names are resolved. external ones are not : ``` $ dig nb-715.example.com @100.100.100.100 +short 10.197.4.3 ###<-- same with the 100.64 Net $ dig google.com @100.100.100.100 ;; communications error to 100.100.100.100#53: timed out $ dig google.com @1.1.1.1 +short 142.251.36.238 ``` config.yaml snippet: ``` dns: magic_dns: true # magic_dns: false base_domain: example.com override_local_dns: true #override_local_dns: false nameservers: global: - 1.1.1.1 ``` status on the client: ``` $ tailscale version 1.76.6 tailscale commit: 1edcf9d466ceafedd2816db1a24d5ba4b0b18a5b other commit: d0a6cd8b27eb46f6dec31425499159f7949be7f9 go version: go1.23.1 $ tailscale dns status === 'Use Tailscale DNS' status === Tailscale DNS: enabled. Tailscale is configured to handle DNS queries on this device. Run 'tailscale set --accept-dns=false' to revert to your system default DNS resolver. === MagicDNS configuration === This is the DNS configuration provided by the coordination server to this device. MagicDNS: enabled tailnet-wide (suffix = example.com) Other devices in your tailnet can reach this device at nb-715.example.com Resolvers (in preference order): - 1.1.1.1 Split DNS Routes: Search Domains: - example.com === System DNS configuration === This is the DNS configuration that Tailscale believes your operating system is using. Tailscale may use this configuration if 'Override Local DNS' is disabled in the admin console, or if no resolvers are provided by the coordination server. (reading the system DNS configuration is not supported on this platform) [this is a preliminary version of this command; the output format may change in the future] ``` Server run on docker headscale/headscale:0.23.0 any ideas?
Author
Owner

@nblock commented on GitHub (Nov 22, 2024):

Same problem here when --accept-dns=true internal names are resolved. external ones are not :

Do you use an exit node?

@nblock commented on GitHub (Nov 22, 2024): > Same problem here when --accept-dns=true internal names are resolved. external ones are not : Do you use an exit node?
Author
Owner

@tho22 commented on GitHub (Nov 22, 2024):

Same problem here when --accept-dns=true internal names are resolved. external ones are not :

Do you use an exit node?

Yes, this problem only exists with exit node constellations.
For me, I have a workaround.
Instead of using "--advertise-exit-node", I use the routing for all "--advertise-routes=0.0.0.0/1,128.0.0.0/1,::/1,8000::/1".

@tho22 commented on GitHub (Nov 22, 2024): > > Same problem here when --accept-dns=true internal names are resolved. external ones are not : > > Do you use an exit node? Yes, this problem only exists with exit node constellations. For me, I have a workaround. Instead of using "--advertise-exit-node", I use the routing for all "--advertise-routes=0.0.0.0/1,128.0.0.0/1,::/1,8000::/1".
Author
Owner

@baiyz0825 commented on GitHub (Dec 7, 2024):

i have the same problem , i check the tailscale outpot ,in my client use exit node A ,controler config the router

ID | Node          | Prefix         | Advertised | Enabled | Primary
5  | vm-0-8-ubuntu | ::/0           | true       | true    | -
6  | vm-0-8-ubuntu | 0.0.0.0/0      | true       | true    | -

tailscale dns status outpput below:

=== 'Use Tailscale DNS' status ===

Tailscale DNS: enabled.

Tailscale is configured to handle DNS queries on this device.
Run 'tailscale set --accept-dns=false' to revert to your system default DNS resolver.

=== MagicDNS configuration ===

This is the DNS configuration provided by the coordination server to this device.

MagicDNS: enabled tailnet-wide (suffix = headscale.vitrul)

Other devices in your tailnet can reach this device at baiyz-laptop.headscale.vitrul

Resolvers (in preference order):
  - 223.5.5.5
  - 8.8.8.8
  - 1.1.1.1
  - 2400:3200::1
  - 2001:4860:4860::8888
  - 2606:4700:4700::1111
  - 2606:4700:4700::1001

Split DNS Routes:

Search Domains:
  - headscale.vitrul

=== System DNS configuration ===

This is the DNS configuration that Tailscale believes your operating system is using.
Tailscale may use this configuration if 'Override Local DNS' is disabled in the admin console,
or if no resolvers are provided by the coordination server.

Nameservers:
  - 192.168.94.232

Search domains:
  (no search domains found)

[this is a preliminary version of this command; the output format may change in the future]
@baiyz0825 commented on GitHub (Dec 7, 2024): i have the same problem , i check the tailscale outpot ,in my client use exit node A ,controler config the router ```txt ID | Node | Prefix | Advertised | Enabled | Primary 5 | vm-0-8-ubuntu | ::/0 | true | true | - 6 | vm-0-8-ubuntu | 0.0.0.0/0 | true | true | - ``` tailscale dns status outpput below: ```txt === 'Use Tailscale DNS' status === Tailscale DNS: enabled. Tailscale is configured to handle DNS queries on this device. Run 'tailscale set --accept-dns=false' to revert to your system default DNS resolver. === MagicDNS configuration === This is the DNS configuration provided by the coordination server to this device. MagicDNS: enabled tailnet-wide (suffix = headscale.vitrul) Other devices in your tailnet can reach this device at baiyz-laptop.headscale.vitrul Resolvers (in preference order): - 223.5.5.5 - 8.8.8.8 - 1.1.1.1 - 2400:3200::1 - 2001:4860:4860::8888 - 2606:4700:4700::1111 - 2606:4700:4700::1001 Split DNS Routes: Search Domains: - headscale.vitrul === System DNS configuration === This is the DNS configuration that Tailscale believes your operating system is using. Tailscale may use this configuration if 'Override Local DNS' is disabled in the admin console, or if no resolvers are provided by the coordination server. Nameservers: - 192.168.94.232 Search domains: (no search domains found) [this is a preliminary version of this command; the output format may change in the future] ```
Author
Owner

@bentemple commented on GitHub (Feb 13, 2025):

Having this same problem as well. Was trying to setup an exit-node using a tailscale docker client, but DNS resolving is broken when trying to use the exit-node.

I think I got it working, I had to add the following to my exit node:

 sysctls:
   - net.ipv4.ip_forward=1
   - net.ipv6.conf.all.forwarding=1

if hosting the tailscale exit node on the host you can also add these directly to sysctls.conf

They mention this network configuration here: https://tailscale.com/kb/1408/quick-guide-exit-nodes

@bentemple commented on GitHub (Feb 13, 2025): Having this same problem as well. Was trying to setup an exit-node using a tailscale docker client, but DNS resolving is broken when trying to use the exit-node. I think I got it working, I had to add the following to my exit node: sysctls: - net.ipv4.ip_forward=1 - net.ipv6.conf.all.forwarding=1 if hosting the tailscale exit node on the host you can also add these directly to sysctls.conf They mention this network configuration here: https://tailscale.com/kb/1408/quick-guide-exit-nodes
Author
Owner

@kiawizard commented on GitHub (Feb 26, 2025):

I confirm this issue exists, Windows Exit Nodes used by Windows or Android clients do not answer to DNS queries to 100.100.100.100 for external domains while exit node is used

@kiawizard commented on GitHub (Feb 26, 2025): I confirm this issue exists, Windows Exit Nodes used by Windows or Android clients do not answer to DNS queries to 100.100.100.100 for external domains while exit node is used
Author
Owner

@clr1107 commented on GitHub (May 11, 2025):

Same issue here. I am not using magic dns, but have two dns resolvers set. I am using the mac tailscale client, and I am running my own dnsmasq instances to handle dns. It would be good to get this working, as I currently have to stop using an exit node to access internal resources (as they resolve via internal dns servers) and then turn it back on.

If I use dig it flags this warning about recursion, if that helps:

clr@clr-laptop ~ % dig google.com

; <<>> DiG 9.10.6 <<>> google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 18615
;; flags: qr aa rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;google.com.			IN	A

;; Query time: 27 msec
;; SERVER: 100.100.100.100#53(100.100.100.100)
;; WHEN: Sun May 11 13:22:41 BST 2025
;; MSG SIZE  rcvd: 28

If I am not using an exit node:

clr@clr-laptop ~ % dig google.com

; <<>> DiG 9.10.6 <<>> google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11453
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;google.com.			IN	A

;; ANSWER SECTION:
google.com.		65	IN	A	216.58.201.110

;; Query time: 13 msec
;; SERVER: 100.100.100.100#53(100.100.100.100)
;; WHEN: Sun May 11 13:22:46 BST 2025
;; MSG SIZE  rcvd: 55
@clr1107 commented on GitHub (May 11, 2025): Same issue here. I am not using magic dns, but have two dns resolvers set. I am using the mac tailscale client, and I am running my own dnsmasq instances to handle dns. It would be good to get this working, as I currently have to stop using an exit node to access internal resources (as they resolve via internal dns servers) and then turn it back on. If I use `dig` it flags this warning about recursion, if that helps: ``` clr@clr-laptop ~ % dig google.com ; <<>> DiG 9.10.6 <<>> google.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 18615 ;; flags: qr aa rd ad; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 ;; WARNING: recursion requested but not available ;; QUESTION SECTION: ;google.com. IN A ;; Query time: 27 msec ;; SERVER: 100.100.100.100#53(100.100.100.100) ;; WHEN: Sun May 11 13:22:41 BST 2025 ;; MSG SIZE rcvd: 28 ``` If I am not using an exit node: ``` clr@clr-laptop ~ % dig google.com ; <<>> DiG 9.10.6 <<>> google.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11453 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ;; QUESTION SECTION: ;google.com. IN A ;; ANSWER SECTION: google.com. 65 IN A 216.58.201.110 ;; Query time: 13 msec ;; SERVER: 100.100.100.100#53(100.100.100.100) ;; WHEN: Sun May 11 13:22:46 BST 2025 ;; MSG SIZE rcvd: 55 ```
Author
Owner

@kiawizard commented on GitHub (Jul 20, 2025):

Please consider raising the priority of this bug, this is critical, MagicDNS (which is on by default) does not work together with Exit Node

@kiawizard commented on GitHub (Jul 20, 2025): Please consider raising the priority of this bug, this is critical, MagicDNS (which is on by default) does not work together with Exit Node
Author
Owner

@djeclemen commented on GitHub (Oct 8, 2025):

Any update?

@djeclemen commented on GitHub (Oct 8, 2025): Any update?
Author
Owner

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

As DNS should happen on my client machine asking 100.100.100.100 directly, I think the exit-node shouldn't have anything to do with resolving.

As stated in this comment about DNS and exit nodes DNS queries are sent to the exit node and resolved on the exit node using its local DNS configuration.


FWIW, here's the gist of my working exit node setup:

  • No global DNS override: dns.override_local_dns: false
  • Exit node is Ubuntu 24.04 with systemd-resolved. It resolves all tailscale specific queries using Tailscale's DNS at 100.100.100.100 and everything else via the DNS resolver configured for the wired network interface.
  • The exit node is allowed to reach the configured DNS servers for split DNS domains: dns.split: .... This might be confusing, see https://github.com/tailscale/tailscale/issues/17401
  • ACLv2 with autogroup:internet:* to allow some clients to use said exit node
  • Those clients are either Linux (running systemd-resolved) or Android. Both work fine and can resolve both internal and external domains
  • Headscale 0.26.1
  • Tailscale 1.88.x on all mentioned nodes
@nblock commented on GitHub (Oct 13, 2025): > As DNS should happen on my client machine asking 100.100.100.100 directly, I think the exit-node shouldn't have anything to do with resolving. As stated in this [comment about DNS and exit nodes](https://github.com/tailscale/tailscale/issues/8237#issuecomment-1570581495) DNS queries are sent to the exit node and resolved on the exit node using its local DNS configuration. --- FWIW, here's the gist of my working exit node setup: - No global DNS override: `dns.override_local_dns: false` - Exit node is Ubuntu 24.04 with systemd-resolved. It resolves all tailscale specific queries using Tailscale's DNS at 100.100.100.100 and everything else via the DNS resolver configured for the wired network interface. - The exit node is allowed to reach the configured DNS servers for split DNS domains: `dns.split: ...`. This might be confusing, see https://github.com/tailscale/tailscale/issues/17401 - ACLv2 with `autogroup:internet:*` to allow some clients to use said exit node - Those clients are either Linux (running systemd-resolved) or Android. Both work fine and can resolve both internal and external domains - Headscale 0.26.1 - Tailscale 1.88.x on all mentioned nodes
Author
Owner

@vborioni-onit commented on GitHub (Dec 21, 2025):

Ouch, just found the issue, lost two days on this one :(
I may add that this is the error i get with tailscail dns query, 100.64.0.1 is the ip of the exit node :

DNS query for "www.google.com" (A) using internal resolver:

failed to query DNS: 500 Internal Server Error: resolving using "http://100.64.0.1:58436/dns-query": Post "http://100.64.0.1:58436/dns-query": read tcp 100.64.0.2:59612->100.64.0.1:58436: wsarecv: An existing connection was forcibly closed by the remote host.

And here is the output of tailscale dns status :


=== 'Use Tailscale DNS' status ===

Tailscale DNS: enabled.

Tailscale is configured to handle DNS queries on this device.
Run 'tailscale set --accept-dns=false' to revert to your system default DNS resolver.

=== MagicDNS configuration ===

This is the DNS configuration provided by the coordination server to this device.

MagicDNS: enabled tailnet-wide (suffix = vpn.mydomain.it)

Other devices in your tailnet can reach this device at device-1.vpn.mydomain.it.

Resolvers (in preference order):
  - 1.1.1.1
  - 8.8.8.8
  - 1.0.0.1

Split DNS Routes:

Search Domains:
  - vpn.mydomain.it

=== System DNS configuration ===

This is the DNS configuration that Tailscale believes your operating system is using.
Tailscale may use this configuration if 'Override Local DNS' is disabled in the admin console,
or if no resolvers are provided by the coordination server.

Nameservers:
  - fde6:a348:bdb::1
  - 2a05:9d42:407::1
  - 192.168.10.1
  - fde6:a348:bdb::1
  - 2a05:9d42:407::1

Search domains:
  (no search domains found)
@vborioni-onit commented on GitHub (Dec 21, 2025): Ouch, just found the issue, lost two days on this one :( I may add that this is the error i get with tailscail dns query, 100.64.0.1 is the ip of the exit node : ``` DNS query for "www.google.com" (A) using internal resolver: failed to query DNS: 500 Internal Server Error: resolving using "http://100.64.0.1:58436/dns-query": Post "http://100.64.0.1:58436/dns-query": read tcp 100.64.0.2:59612->100.64.0.1:58436: wsarecv: An existing connection was forcibly closed by the remote host. ``` And here is the output of tailscale dns status : ``` === 'Use Tailscale DNS' status === Tailscale DNS: enabled. Tailscale is configured to handle DNS queries on this device. Run 'tailscale set --accept-dns=false' to revert to your system default DNS resolver. === MagicDNS configuration === This is the DNS configuration provided by the coordination server to this device. MagicDNS: enabled tailnet-wide (suffix = vpn.mydomain.it) Other devices in your tailnet can reach this device at device-1.vpn.mydomain.it. Resolvers (in preference order): - 1.1.1.1 - 8.8.8.8 - 1.0.0.1 Split DNS Routes: Search Domains: - vpn.mydomain.it === System DNS configuration === This is the DNS configuration that Tailscale believes your operating system is using. Tailscale may use this configuration if 'Override Local DNS' is disabled in the admin console, or if no resolvers are provided by the coordination server. Nameservers: - fde6:a348:bdb::1 - 2a05:9d42:407::1 - 192.168.10.1 - fde6:a348:bdb::1 - 2a05:9d42:407::1 Search domains: (no search domains found) ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/headscale#851