Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix search domains and remove username from magicdns #1987

Merged
merged 2 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ after improving the test harness as part of adopting [#1460](https://github.com/
- Prefixes are now defined per v4 and v6 range. [#1756](https://github.com/juanfont/headscale/pull/1756)
- `ip_prefixes` option is now `prefixes.v4` and `prefixes.v6`
- `prefixes.allocation` can be set to assign IPs at `sequential` or `random`. [#1869](https://github.com/juanfont/headscale/pull/1869)
- MagicDNS domains no longer contain usernames []()
- This is in preperation to fix Headscales implementation of tags which currently does not correctly remove the link between a tagged device and a user. As tagged devices will not have a user, this will require a change to the DNS generation, removing the username, see [#1369](https://github.com/juanfont/headscale/issues/1369) for more information.
- `use_username_in_magic_dns` can be used to turn this behaviour on again, but note that this option _will be removed_ when tags are fixed.
- This option brings Headscales behaviour in line with Tailscale.

### Changes

Expand Down
9 changes: 9 additions & 0 deletions config-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,15 @@ dns_config:
# Only works if there is at least a nameserver defined.
magic_dns: true

# DEPRECATED
# Use the username as part of the DNS name for nodes, with this option enabled:
# node1.username.example.com
# while when this is disabled:
# node1.example.com
# This is a legacy option as Headscale has have this wrongly implemented
# while in upstream Tailscale, the username is not included.
use_username_in_magic_dns: false

# Defines the base domain to create the hostnames for MagicDNS.
# `base_domain` must be a FQDNs, without the trailing dot.
# The FQDN of the hosts will be
Expand Down
54 changes: 29 additions & 25 deletions hscontrol/mapper/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,37 +122,41 @@ func generateUserProfiles(
}

func generateDNSConfig(
base *tailcfg.DNSConfig,
cfg *types.Config,
baseDomain string,
node *types.Node,
peers types.Nodes,
) *tailcfg.DNSConfig {
dnsConfig := base.Clone()
if cfg.DNSConfig == nil {
return nil
}

// if MagicDNS is enabled
if base != nil && base.Proxied {
// Only inject the Search Domain of the current user
// shared nodes should use their full FQDN
dnsConfig.Domains = append(
dnsConfig.Domains,
fmt.Sprintf(
"%s.%s",
node.User.Name,
baseDomain,
),
)
dnsConfig := cfg.DNSConfig.Clone()

userSet := mapset.NewSet[types.User]()
userSet.Add(node.User)
for _, p := range peers {
userSet.Add(p.User)
}
for _, user := range userSet.ToSlice() {
dnsRoute := fmt.Sprintf("%v.%v", user.Name, baseDomain)
dnsConfig.Routes[dnsRoute] = nil
// if MagicDNS is enabled
if dnsConfig.Proxied {
if cfg.DNSUserNameInMagicDNS {
// Only inject the Search Domain of the current user
// shared nodes should use their full FQDN
dnsConfig.Domains = append(
dnsConfig.Domains,
fmt.Sprintf(
"%s.%s",
node.User.Name,
baseDomain,
),
)

userSet := mapset.NewSet[types.User]()
userSet.Add(node.User)
for _, p := range peers {
userSet.Add(p.User)
}
for _, user := range userSet.ToSlice() {
dnsRoute := fmt.Sprintf("%v.%v", user.Name, baseDomain)
dnsConfig.Routes[dnsRoute] = nil
}
}
} else {
dnsConfig = base
}

addNextDNSMetadata(dnsConfig.Resolvers, node)
Expand Down Expand Up @@ -568,7 +572,7 @@ func appendPeerChanges(
profiles := generateUserProfiles(node, changed, cfg.BaseDomain)

dnsConfig := generateDNSConfig(
cfg.DNSConfig,
cfg,
cfg.BaseDomain,
node,
peers,
Expand Down
11 changes: 7 additions & 4 deletions hscontrol/mapper/mapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,10 @@ func TestDNSConfigMapResponse(t *testing.T) {
}

got := generateDNSConfig(
&dnsConfigOrig,
&types.Config{
DNSConfig: &dnsConfigOrig,
DNSUserNameInMagicDNS: true,
},
baseDomain,
nodeInShared1,
peersOfNodeInShared1,
Expand Down Expand Up @@ -187,9 +190,9 @@ func Test_fullMapResponse(t *testing.T) {
UserID: 0,
User: types.User{Name: "mini"},
ForcedTags: []string{},
AuthKey: &types.PreAuthKey{},
LastSeen: &lastSeen,
Expiry: &expire,
AuthKey: &types.PreAuthKey{},
LastSeen: &lastSeen,
Expiry: &expire,
Hostinfo: &tailcfg.Hostinfo{},
Routes: []types.Route{
{
Expand Down
2 changes: 1 addition & 1 deletion hscontrol/mapper/tail.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func tailNode(
keyExpiry = time.Time{}
}

hostname, err := node.GetFQDN(cfg.DNSConfig, cfg.BaseDomain)
hostname, err := node.GetFQDN(cfg, cfg.BaseDomain)
if err != nil {
return nil, fmt.Errorf("tailNode, failed to create FQDN: %s", err)
}
Expand Down
29 changes: 16 additions & 13 deletions hscontrol/types/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ type Config struct {
ACMEURL string
ACMEEmail string

DNSConfig *tailcfg.DNSConfig
DNSConfig *tailcfg.DNSConfig
DNSUserNameInMagicDNS bool

UnixSocket string
UnixSocketPermission fs.FileMode
Expand Down Expand Up @@ -203,6 +204,7 @@ func LoadConfig(path string, isFile bool) error {

viper.SetDefault("dns_config", nil)
viper.SetDefault("dns_config.override_local_dns", true)
viper.SetDefault("dns_config.use_username_in_magic_dns", false)

viper.SetDefault("derp.server.enabled", false)
viper.SetDefault("derp.server.stun.enabled", true)
Expand Down Expand Up @@ -536,16 +538,6 @@ func GetDNSConfig() (*tailcfg.DNSConfig, string) {
dnsConfig.Domains = domains
}

if viper.IsSet("dns_config.domains") {
domains := viper.GetStringSlice("dns_config.domains")
if len(dnsConfig.Resolvers) > 0 {
dnsConfig.Domains = domains
} else if domains != nil {
log.Warn().
Msg("Warning: dns_config.domains is set, but no nameservers are configured. Ignoring domains.")
}
}

if viper.IsSet("dns_config.extra_records") {
var extraRecords []tailcfg.DNSRecord

Expand All @@ -571,8 +563,18 @@ func GetDNSConfig() (*tailcfg.DNSConfig, string) {
baseDomain = "headscale.net" // does not really matter when MagicDNS is not enabled
}

log.Trace().Interface("dns_config", dnsConfig).Msg("DNS configuration loaded")
if !viper.GetBool("dns_config.use_username_in_magic_dns") {
dnsConfig.Domains = []string{baseDomain}
} else {
log.Warn().Msg("DNS: Usernames in DNS has been deprecated, this option will be remove in future versions")
log.Warn().Msg("DNS: see 0.23.0 changelog for more information.")
}

if domains := viper.GetStringSlice("dns_config.domains"); len(domains) > 0 {
dnsConfig.Domains = append(dnsConfig.Domains, domains...)
}

log.Trace().Interface("dns_config", dnsConfig).Msg("DNS configuration loaded")
return dnsConfig, baseDomain
}

Expand Down Expand Up @@ -715,7 +717,8 @@ func GetHeadscaleConfig() (*Config, error) {

TLS: GetTLSConfig(),

DNSConfig: dnsConfig,
DNSConfig: dnsConfig,
DNSUserNameInMagicDNS: viper.GetBool("dns_config.use_username_in_magic_dns"),

ACMEEmail: viper.GetString("acme_email"),
ACMEURL: viper.GetString("acme_url"),
Expand Down
25 changes: 17 additions & 8 deletions hscontrol/types/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,23 +394,32 @@ func (node *Node) Proto() *v1.Node {
return nodeProto
}

func (node *Node) GetFQDN(dnsConfig *tailcfg.DNSConfig, baseDomain string) (string, error) {
func (node *Node) GetFQDN(cfg *Config, baseDomain string) (string, error) {
var hostname string
if dnsConfig != nil && dnsConfig.Proxied { // MagicDNS
if cfg.DNSConfig != nil && cfg.DNSConfig.Proxied { // MagicDNS
if node.GivenName == "" {
return "", fmt.Errorf("failed to create valid FQDN: %w", ErrNodeHasNoGivenName)
}

if node.User.Name == "" {
return "", fmt.Errorf("failed to create valid FQDN: %w", ErrNodeUserHasNoName)
}

hostname = fmt.Sprintf(
"%s.%s.%s",
"%s.%s",
node.GivenName,
node.User.Name,
baseDomain,
)

if cfg.DNSUserNameInMagicDNS {
if node.User.Name == "" {
return "", fmt.Errorf("failed to create valid FQDN: %w", ErrNodeUserHasNoName)
}

hostname = fmt.Sprintf(
"%s.%s.%s",
node.GivenName,
node.User.Name,
baseDomain,
)
}

if len(hostname) > MaxHostnameLength {
return "", fmt.Errorf(
"failed to create valid FQDN (%s): %w",
Expand Down
Loading
Loading