Skip to content

Instantly share code, notes, and snippets.

@vladak
Last active July 11, 2026 14:22
Show Gist options
  • Select an option

  • Save vladak/f12f1eb664fe26a4ec6763a41b7189be to your computer and use it in GitHub Desktop.

Select an option

Save vladak/f12f1eb664fe26a4ec6763a41b7189be to your computer and use it in GitHub Desktop.
Tailscale DNS resolution might actually be affected by ACLs

Tailscale: Quad100 vs. ACLs/grants

Consider the following Tailscale grant:

{
	"grants": [
		{
			"src": ["tag:foo"],
			"dst": ["tag:bar"],
			"ip":  ["*"],
		},
		{
			"src": ["tag:bar"],
			"dst": ["tag:foo"],
			"ip":  ["*"],
		},
		{
			"src": ["tag:bar"],
			"dst": ["autogroup:member"],
			"ip":  ["*"],
		},
		{
			"src": ["autogroup:member"],
			"dst": ["tag:bar"],
			"ip":  ["*"],
		},
		{
			"src": ["autogroup:member"],
			"dst": ["autogroup:member"],
			"ip":  ["*"],
		},
	],

	"tagOwners": {
		"tag:bar": ["autogroup:admin"],
		"tag:foo": ["autogroup:admin"],
	},
}

The purpose is to isolate the foo node in the Tailscale network so that it can communicate only with the bar node and the bar node should be unconstrained.

In my case after the grant rules were applied, foo was no longer able to resolve DNS names on the internet. Resolving names of other nodes in the Tailnet worked fine.

After chasing the wrong track of [https://tailscale.com/docs/reference/faq/dns-resolv-conf](Tailscale resolv.conf interaction), it turned out in the Tailscale DNS settings, Global nameservers were set to a particular IP of another Tailnet node and the 'Override DNS Servers' setting was enabled.

Thus, after the policy was applied, the Quad100 local resolver running on the foo node was not able to forward non-Tailnet DNS queries to the DNS server designated in the Tailnet configuration.

Asking the AI on the Tailscale pages about that (How does Quad100 interact with ACLs?) resulted in a reply which contained the following:

DNS resolution via Quad100 is unaffected by ACLs — even if --accept-dns=false is set, the resolver at 100.100.100.100 still responds to DNS queries locally. You can manually point *.ts.net queries at it if needed. [DNS resolv.conf FAQ]

In short: Quad100 is intentionally kept outside the scope of ACL enforcement because it is a local service, not a network-reachable node.

Clearly, this is not always the case. After creating DNS server specific tag (so that in the future there can be more than one), applying it to the DNS server and adding a rule to allow the foo node to communicate with it, the DNS issue at foo was fixed.

{
	"grants": [
		{
			"src": ["tag:foo"],
			"dst": ["tag:bar"],
			"ip":  ["*"],
		},
		{
			"src": ["tag:bar"],
			"dst": ["tag:foo"],
			"ip":  ["*"],
		},
		{
			"src": ["tag:foo"],
			"dst": ["tag:dns-servers"],
			"ip":  ["*"],
		},
		{
			"src": ["tag:bar"],
			"dst": ["autogroup:member"],
			"ip":  ["*"],
		},
		{
			"src": ["autogroup:member"],
			"dst": ["tag:bar"],
			"ip":  ["*"],
		},
		{
			"src": ["autogroup:member"],
			"dst": ["autogroup:member"],
			"ip":  ["*"],
		},
	],

	"tagOwners": {
		"tag:bar": ["autogroup:admin"],
		"tag:foo": ["autogroup:admin"],
		
		// DNS servers
		"tag:dns-servers": ["autogroup:admin"],
	},
}

After persuading the AI chatbot, it actually gave the correct answer (too late):

When a Tailscale node (e.g., a Pi-hole instance) is configured as a Global Nameserver in the Admin Console with Override DNS servers enabled, Quad100 acts as a forwarding stub resolver — it receives DNS queries locally and then forwards them over the tailnet to that designated node. At that point, ACLs absolutely come into play:

"If an ACL or grant prevents a device from accessing the global nameserver for the tailnet, the device won't be able to resolve DNS queries."

[Override DNS servers]

This is confirmed in practice by community experience — when a Pi-hole running as a Tailscale sidecar was set as the tailnet DNS server, devices without an explicit ACL grant to reach it failed to resolve DNS: [ACL grants for DNS]

"Setting a Tailscale IP as your DNS server doesn't implicitly give devices access to that server:port. If you're not using the default allow-all policy, you will need to add a grant giving access to the DNS server."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment