Add support for NextDNS resolver
This commit is contained in:
parent
c0884f94b8
commit
6d3ede1367
4 changed files with 63 additions and 8 deletions
|
@ -27,6 +27,7 @@
|
||||||
- Fix some DNS config issues [#660](https://github.com/juanfont/headscale/issues/660)
|
- Fix some DNS config issues [#660](https://github.com/juanfont/headscale/issues/660)
|
||||||
- Make it possible to disable TS2019 with build flag [#928](https://github.com/juanfont/headscale/pull/928)
|
- Make it possible to disable TS2019 with build flag [#928](https://github.com/juanfont/headscale/pull/928)
|
||||||
- Fix OIDC registration issues [#960](https://github.com/juanfont/headscale/pull/960) and [#971](https://github.com/juanfont/headscale/pull/971)
|
- Fix OIDC registration issues [#960](https://github.com/juanfont/headscale/pull/960) and [#971](https://github.com/juanfont/headscale/pull/971)
|
||||||
|
- Add support for specifying NextDNS DNS-over-HTTPS resolver [#940](https://github.com/juanfont/headscale/pull/940)
|
||||||
|
|
||||||
## 0.16.4 (2022-08-21)
|
## 0.16.4 (2022-08-21)
|
||||||
|
|
||||||
|
|
|
@ -214,6 +214,18 @@ dns_config:
|
||||||
nameservers:
|
nameservers:
|
||||||
- 1.1.1.1
|
- 1.1.1.1
|
||||||
|
|
||||||
|
# NextDNS (see https://tailscale.com/kb/1218/nextdns/).
|
||||||
|
# "abc123" is example NextDNS ID, replace with yours.
|
||||||
|
#
|
||||||
|
# With metadata sharing:
|
||||||
|
# nameservers:
|
||||||
|
# - https://dns.nextdns.io/abc123
|
||||||
|
#
|
||||||
|
# Without metadata sharing:
|
||||||
|
# nameservers:
|
||||||
|
# - 2a07:a8c0::ab:c123
|
||||||
|
# - 2a07:a8c1::ab:c123
|
||||||
|
|
||||||
# Split DNS (see https://tailscale.com/kb/1054/dns/),
|
# Split DNS (see https://tailscale.com/kb/1054/dns/),
|
||||||
# list of search domains and the DNS to query for each one.
|
# list of search domains and the DNS to query for each one.
|
||||||
#
|
#
|
||||||
|
|
23
config.go
23
config.go
|
@ -383,10 +383,21 @@ func GetDNSConfig() (*tailcfg.DNSConfig, string) {
|
||||||
if viper.IsSet("dns_config.nameservers") {
|
if viper.IsSet("dns_config.nameservers") {
|
||||||
nameserversStr := viper.GetStringSlice("dns_config.nameservers")
|
nameserversStr := viper.GetStringSlice("dns_config.nameservers")
|
||||||
|
|
||||||
nameservers := make([]netip.Addr, len(nameserversStr))
|
nameservers := []netip.Addr{}
|
||||||
resolvers := make([]*dnstype.Resolver, len(nameserversStr))
|
resolvers := []*dnstype.Resolver{}
|
||||||
|
|
||||||
for index, nameserverStr := range nameserversStr {
|
for _, nameserverStr := range nameserversStr {
|
||||||
|
// Search for explicit DNS-over-HTTPS resolvers
|
||||||
|
if strings.HasPrefix(nameserverStr, "https://") {
|
||||||
|
resolvers = append(resolvers, &dnstype.Resolver{
|
||||||
|
Addr: nameserverStr,
|
||||||
|
})
|
||||||
|
|
||||||
|
// This nameserver can not be parsed as an IP address
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse nameserver as a regular IP
|
||||||
nameserver, err := netip.ParseAddr(nameserverStr)
|
nameserver, err := netip.ParseAddr(nameserverStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
|
@ -395,10 +406,10 @@ func GetDNSConfig() (*tailcfg.DNSConfig, string) {
|
||||||
Msgf("Could not parse nameserver IP: %s", nameserverStr)
|
Msgf("Could not parse nameserver IP: %s", nameserverStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
nameservers[index] = nameserver
|
nameservers = append(nameservers, nameserver)
|
||||||
resolvers[index] = &dnstype.Resolver{
|
resolvers = append(resolvers, &dnstype.Resolver{
|
||||||
Addr: nameserver.String(),
|
Addr: nameserver.String(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsConfig.Nameservers = nameservers
|
dnsConfig.Nameservers = nameservers
|
||||||
|
|
35
dns.go
35
dns.go
|
@ -3,11 +3,13 @@ package headscale
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
mapset "github.com/deckarep/golang-set/v2"
|
mapset "github.com/deckarep/golang-set/v2"
|
||||||
"go4.org/netipx"
|
"go4.org/netipx"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
"tailscale.com/types/dnstype"
|
||||||
"tailscale.com/util/dnsname"
|
"tailscale.com/util/dnsname"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,6 +22,10 @@ const (
|
||||||
ipv6AddressLength = 128
|
ipv6AddressLength = 128
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
nextDNSDoHPrefix = "https://dns.nextdns.io"
|
||||||
|
)
|
||||||
|
|
||||||
// generateMagicDNSRootDomains generates a list of DNS entries to be included in `Routes` in `MapResponse`.
|
// generateMagicDNSRootDomains generates a list of DNS entries to be included in `Routes` in `MapResponse`.
|
||||||
// This list of reverse DNS entries instructs the OS on what subnets and domains the Tailscale embedded DNS
|
// This list of reverse DNS entries instructs the OS on what subnets and domains the Tailscale embedded DNS
|
||||||
// server (listening in 100.100.100.100 udp/53) should be used for.
|
// server (listening in 100.100.100.100 udp/53) should be used for.
|
||||||
|
@ -152,16 +158,39 @@ func generateIPv6DNSRootDomain(ipPrefix netip.Prefix) []dnsname.FQDN {
|
||||||
return fqdns
|
return fqdns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If any nextdns DoH resolvers are present in the list of resolvers it will
|
||||||
|
// take metadata from the machine metadata and instruct tailscale to add it
|
||||||
|
// to the requests. This makes it possible to identify from which device the
|
||||||
|
// requests come in the NextDNS dashboard.
|
||||||
|
//
|
||||||
|
// This will produce a resolver like:
|
||||||
|
// `https://dns.nextdns.io/<nextdns-id>?device_name=node-name&device_model=linux&device_ip=100.64.0.1`
|
||||||
|
func addNextDNSMetadata(resolvers []*dnstype.Resolver, machine Machine) {
|
||||||
|
for _, resolver := range resolvers {
|
||||||
|
if strings.HasPrefix(resolver.Addr, nextDNSDoHPrefix) {
|
||||||
|
attrs := url.Values{
|
||||||
|
"device_name": []string{machine.Hostname},
|
||||||
|
"device_model": []string{machine.HostInfo.OS},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(machine.IPAddresses) > 0 {
|
||||||
|
attrs.Add("device_ip", machine.IPAddresses[0].String())
|
||||||
|
}
|
||||||
|
|
||||||
|
resolver.Addr = fmt.Sprintf("%s?%s", resolver.Addr, attrs.Encode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getMapResponseDNSConfig(
|
func getMapResponseDNSConfig(
|
||||||
dnsConfigOrig *tailcfg.DNSConfig,
|
dnsConfigOrig *tailcfg.DNSConfig,
|
||||||
baseDomain string,
|
baseDomain string,
|
||||||
machine Machine,
|
machine Machine,
|
||||||
peers Machines,
|
peers Machines,
|
||||||
) *tailcfg.DNSConfig {
|
) *tailcfg.DNSConfig {
|
||||||
var dnsConfig *tailcfg.DNSConfig
|
var dnsConfig *tailcfg.DNSConfig = dnsConfigOrig.Clone()
|
||||||
if dnsConfigOrig != nil && dnsConfigOrig.Proxied { // if MagicDNS is enabled
|
if dnsConfigOrig != nil && dnsConfigOrig.Proxied { // if MagicDNS is enabled
|
||||||
// Only inject the Search Domain of the current namespace - shared nodes should use their full FQDN
|
// Only inject the Search Domain of the current namespace - shared nodes should use their full FQDN
|
||||||
dnsConfig = dnsConfigOrig.Clone()
|
|
||||||
dnsConfig.Domains = append(
|
dnsConfig.Domains = append(
|
||||||
dnsConfig.Domains,
|
dnsConfig.Domains,
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
|
@ -184,5 +213,7 @@ func getMapResponseDNSConfig(
|
||||||
dnsConfig = dnsConfigOrig
|
dnsConfig = dnsConfigOrig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addNextDNSMetadata(dnsConfig.Resolvers, machine)
|
||||||
|
|
||||||
return dnsConfig
|
return dnsConfig
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue