Skip to content

Commit

Permalink
Restore support for "Override local DNS"
Browse files Browse the repository at this point in the history
Tailscale allows to override the local DNS settings of a node via
"Override local DNS" [1]. Restore this flag with the same config setting
name `dns.override_local_dns` but disable it by default to align it with
Tailscale's default behaviour.

Tested with Tailscale 1.80.2 and systemd-resolved on Debian 12.

With `dns.override_local_dns: false`:

```
Link 12 (tailscale0)
Current Scopes: DNS
     Protocols: -DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
   DNS Servers: 100.100.100.100
    DNS Domain: tn.example.com ~0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa [snip]
```

With `dns.override_local_dns: true`:

```
Link 12 (tailscale0)
Current Scopes: DNS
     Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
   DNS Servers: 100.100.100.100
    DNS Domain: tn.example.com ~.
```

[1] https://tailscale.com/kb/1054/dns#override-local-dns

Fixes: juanfont#2256
  • Loading branch information
nblock committed Mar 1, 2025
1 parent 7891378 commit 3367193
Show file tree
Hide file tree
Showing 7 changed files with 24 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Note that if an exit route is approved (0.0.0.0/0 or ::/0), both IPv4 and IPv6 w
- It is now possible to inspect running goroutines and take profiles
- View of config, policy, filter, ssh policy per node, connected nodes and
DERPmap
- Restore support for "Override local DNS"
[#2438](https://github.com/juanfont/headscale/pull/2438)

## 0.25.1 (2025-02-25)

Expand Down
4 changes: 4 additions & 0 deletions config-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,10 @@ dns:
# `hostname.base_domain` (e.g., _myhost.example.com_).
base_domain: example.com

# Whether to use the local DNS settings of a node (default) or override the
# local DNS settings and force the use of Headscale's DNS configuration.
override_local_dns: false

# List of DNS servers to expose to clients.
nameservers:
global:
Expand Down
11 changes: 9 additions & 2 deletions hscontrol/types/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ type Config struct {
type DNSConfig struct {
MagicDNS bool `mapstructure:"magic_dns"`
BaseDomain string `mapstructure:"base_domain"`
OverrideLocalDNS bool `mapstructure:"override_local_dns"`
Nameservers Nameservers
SearchDomains []string `mapstructure:"search_domains"`
ExtraRecords []tailcfg.DNSRecord `mapstructure:"extra_records"`
Expand Down Expand Up @@ -287,6 +288,7 @@ func LoadConfig(path string, isFile bool) error {

viper.SetDefault("dns.magic_dns", true)
viper.SetDefault("dns.base_domain", "")
viper.SetDefault("dns.override_local_dns", false)
viper.SetDefault("dns.nameservers.global", []string{})
viper.SetDefault("dns.nameservers.split", map[string]string{})
viper.SetDefault("dns.search_domains", []string{})
Expand Down Expand Up @@ -351,9 +353,9 @@ func validateServerConfig() error {
depr.fatalIfNewKeyIsNotUsed("policy.path", "acl_policy_path")

// Move dns_config -> dns
depr.warn("dns_config.override_local_dns")
depr.fatalIfNewKeyIsNotUsed("dns.magic_dns", "dns_config.magic_dns")
depr.fatalIfNewKeyIsNotUsed("dns.base_domain", "dns_config.base_domain")
depr.fatalIfNewKeyIsNotUsed("dns.override_local_dns", "dns_config.override_local_dns")
depr.fatalIfNewKeyIsNotUsed("dns.nameservers.global", "dns_config.nameservers")
depr.fatalIfNewKeyIsNotUsed("dns.nameservers.split", "dns_config.restricted_nameservers")
depr.fatalIfNewKeyIsNotUsed("dns.search_domains", "dns_config.domains")
Expand Down Expand Up @@ -616,6 +618,7 @@ func dns() (DNSConfig, error) {

dns.MagicDNS = viper.GetBool("dns.magic_dns")
dns.BaseDomain = viper.GetString("dns.base_domain")
dns.OverrideLocalDNS = viper.GetBool("dns.override_local_dns")
dns.Nameservers.Global = viper.GetStringSlice("dns.nameservers.global")
dns.Nameservers.Split = viper.GetStringMapStringSlice("dns.nameservers.split")
dns.SearchDomains = viper.GetStringSlice("dns.search_domains")
Expand Down Expand Up @@ -721,7 +724,11 @@ func dnsToTailcfgDNS(dns DNSConfig) *tailcfg.DNSConfig {

cfg.Proxied = dns.MagicDNS
cfg.ExtraRecords = dns.ExtraRecords
cfg.Resolvers = dns.globalResolvers()
if dns.OverrideLocalDNS {
cfg.Resolvers = dns.globalResolvers()
} else {
cfg.FallbackResolvers = dns.globalResolvers()
}

routes := dns.splitResolvers()
cfg.Routes = routes
Expand Down
8 changes: 6 additions & 2 deletions hscontrol/types/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func TestReadConfig(t *testing.T) {
want: DNSConfig{
MagicDNS: true,
BaseDomain: "example.com",
OverrideLocalDNS: false,
Nameservers: Nameservers{
Global: []string{
"1.1.1.1",
Expand Down Expand Up @@ -70,7 +71,7 @@ func TestReadConfig(t *testing.T) {
want: &tailcfg.DNSConfig{
Proxied: true,
Domains: []string{"example.com", "test.com", "bar.com"},
Resolvers: []*dnstype.Resolver{
FallbackResolvers: []*dnstype.Resolver{
{Addr: "1.1.1.1"},
{Addr: "1.0.0.1"},
{Addr: "2606:4700:4700::1111"},
Expand Down Expand Up @@ -101,6 +102,7 @@ func TestReadConfig(t *testing.T) {
want: DNSConfig{
MagicDNS: false,
BaseDomain: "example.com",
OverrideLocalDNS: false,
Nameservers: Nameservers{
Global: []string{
"1.1.1.1",
Expand Down Expand Up @@ -135,7 +137,7 @@ func TestReadConfig(t *testing.T) {
want: &tailcfg.DNSConfig{
Proxied: false,
Domains: []string{"example.com", "test.com", "bar.com"},
Resolvers: []*dnstype.Resolver{
FallbackResolvers: []*dnstype.Resolver{
{Addr: "1.1.1.1"},
{Addr: "1.0.0.1"},
{Addr: "2606:4700:4700::1111"},
Expand Down Expand Up @@ -254,6 +256,7 @@ func TestReadConfigFromEnv(t *testing.T) {
configEnv: map[string]string{
"HEADSCALE_DNS_MAGIC_DNS": "true",
"HEADSCALE_DNS_BASE_DOMAIN": "example.com",
"HEADSCALE_DNS_OVERRIDE_LOCAL_DNS": "false",
"HEADSCALE_DNS_NAMESERVERS_GLOBAL": `1.1.1.1 8.8.8.8`,
"HEADSCALE_DNS_SEARCH_DOMAINS": "test.com bar.com",

Expand All @@ -274,6 +277,7 @@ func TestReadConfigFromEnv(t *testing.T) {
want: DNSConfig{
MagicDNS: true,
BaseDomain: "example.com",
OverrideLocalDNS: false,
Nameservers: Nameservers{
Global: []string{"1.1.1.1", "8.8.8.8"},
Split: map[string][]string{
Expand Down
1 change: 1 addition & 0 deletions hscontrol/types/testdata/dns_full.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dns:
magic_dns: true
base_domain: example.com

override_local_dns: false
nameservers:
global:
- 1.1.1.1
Expand Down
1 change: 1 addition & 0 deletions hscontrol/types/testdata/dns_full_no_magic.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ dns:
magic_dns: false
base_domain: example.com

override_local_dns: false
nameservers:
global:
- 1.1.1.1
Expand Down
1 change: 1 addition & 0 deletions integration/hsic/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func DefaultConfigEnv() map[string]string {
"HEADSCALE_PREFIXES_V6": "fd7a:115c:a1e0::/48",
"HEADSCALE_DNS_BASE_DOMAIN": "headscale.net",
"HEADSCALE_DNS_MAGIC_DNS": "true",
"HEADSCALE_DNS_OVERRIDE_LOCAL_DNS": "false",
"HEADSCALE_DNS_NAMESERVERS_GLOBAL": "127.0.0.11 1.1.1.1",
"HEADSCALE_PRIVATE_KEY_PATH": "/tmp/private.key",
"HEADSCALE_NOISE_PRIVATE_KEY_PATH": "/tmp/noise_private.key",
Expand Down

0 comments on commit 3367193

Please sign in to comment.