diff --git a/api.go b/api.go index e2a56185..c7fbf8a1 100644 --- a/api.go +++ b/api.go @@ -218,7 +218,7 @@ func (h *Headscale) getMapResponse(mKey wgkey.Key, req tailcfg.MapRequest, m Mac Str("func", "getMapResponse"). Str("machine", req.Hostinfo.Hostname). Msg("Creating Map response") - node, err := m.toNode(true) + node, err := h.toNode(m, true) if err != nil { log.Error(). Str("func", "getMapResponse"). @@ -242,17 +242,11 @@ func (h *Headscale) getMapResponse(mKey wgkey.Key, req tailcfg.MapRequest, m Mac } resp := tailcfg.MapResponse{ - KeepAlive: false, - Node: node, - Peers: *peers, - //TODO(kradalby): As per tailscale docs, if DNSConfig is nil, - // it means its not updated, maybe we can have some logic - // to check and only pass updates when its updates. - // This is probably more relevant if we try to implement - // "MagicDNS" + KeepAlive: false, + Node: node, + Peers: *peers, DNSConfig: h.cfg.DNSConfig, - SearchPaths: []string{}, - Domain: "headscale.net", + Domain: h.cfg.BaseDomain, PacketFilter: *h.aclRules, DERPMap: h.cfg.DerpMap, UserProfiles: []tailcfg.UserProfile{profile}, diff --git a/app.go b/app.go index dc398eb7..9e29640f 100644 --- a/app.go +++ b/app.go @@ -27,6 +27,7 @@ type Config struct { DerpMap *tailcfg.DERPMap EphemeralNodeInactivityTimeout time.Duration IPPrefix netaddr.IPPrefix + BaseDomain string DBtype string DBpath string diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index f5c2a6db..bff7693d 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -76,7 +76,7 @@ func LoadConfig(path string) error { } -func GetDNSConfig() *tailcfg.DNSConfig { +func GetDNSConfig() (*tailcfg.DNSConfig, string) { if viper.IsSet("dns_config") { dnsConfig := &tailcfg.DNSConfig{} @@ -112,11 +112,16 @@ func GetDNSConfig() *tailcfg.DNSConfig { dnsConfig.Proxied = viper.GetBool("dns_config.magic_dns") } - return dnsConfig - + var baseDomain string + if viper.IsSet("dns_config.base_domain") { + baseDomain = viper.GetString("dns_config.base_domain") + } else { + baseDomain = "headscale.net" // does not really matter when MagicDNS is not enabled + } + return dnsConfig, baseDomain } - return nil + return nil, "" } func absPath(path string) string { @@ -149,12 +154,15 @@ func getHeadscaleApp() (*headscale.Headscale, error) { return nil, err } + dnsConfig, baseDomain := GetDNSConfig() + cfg := headscale.Config{ ServerURL: viper.GetString("server_url"), Addr: viper.GetString("listen_addr"), PrivateKeyPath: absPath(viper.GetString("private_key_path")), DerpMap: derpMap, IPPrefix: netaddr.MustParseIPPrefix(viper.GetString("ip_prefix")), + BaseDomain: baseDomain, EphemeralNodeInactivityTimeout: viper.GetDuration("ephemeral_node_inactivity_timeout"), @@ -174,7 +182,7 @@ func getHeadscaleApp() (*headscale.Headscale, error) { TLSCertPath: absPath(viper.GetString("tls_cert_path")), TLSKeyPath: absPath(viper.GetString("tls_key_path")), - DNSConfig: GetDNSConfig(), + DNSConfig: dnsConfig, } h, err := headscale.NewHeadscale(cfg) diff --git a/integration_test.go b/integration_test.go index f62ca1da..e80ff46b 100644 --- a/integration_test.go +++ b/integration_test.go @@ -642,6 +642,19 @@ func (s *IntegrationTestSuite) TestTailDrop() { } } +// func (s *IntegrationTestSuite) TestMagicDNS() { +// for _, scales := range s.namespaces { +// ips, err := getIPs(scales.tailscales) +// assert.Nil(s.T(), err) +// apiURLs, err := getAPIURLs(scales.tailscales) +// assert.Nil(s.T(), err) + +// for hostname, tailscale := range scales.tailscales { + +// } +// } +// } + func getIPs(tailscales map[string]dockertest.Resource) (map[string]netaddr.IP, error) { ips := make(map[string]netaddr.IP) for hostname, tailscale := range tailscales { diff --git a/machine.go b/machine.go index 1d4939c1..d963d35f 100644 --- a/machine.go +++ b/machine.go @@ -52,7 +52,7 @@ func (m Machine) isAlreadyRegistered() bool { // toNode converts a Machine into a Tailscale Node. includeRoutes is false for shared nodes // as per the expected behaviour in the official SaaS -func (m Machine) toNode(includeRoutes bool) (*tailcfg.Node, error) { +func (h *Headscale) toNode(m Machine, includeRoutes bool) (*tailcfg.Node, error) { nKey, err := wgkey.ParseHex(m.NodeKey) if err != nil { return nil, err @@ -147,10 +147,12 @@ func (m Machine) toNode(includeRoutes bool) (*tailcfg.Node, error) { keyExpiry = time.Time{} } + hostname := fmt.Sprintf("%s.%s.%s", m.Name, m.Namespace.Name, h.cfg.BaseDomain) + n := tailcfg.Node{ ID: tailcfg.NodeID(m.ID), // this is the actual ID StableID: tailcfg.StableNodeID(strconv.FormatUint(m.ID, 10)), // in headscale, unlike tailcontrol server, IDs are permanent - Name: hostinfo.Hostname, + Name: hostname, User: tailcfg.UserID(m.NamespaceID), Key: tailcfg.NodeKey(nKey), KeyExpiry: keyExpiry, @@ -169,6 +171,8 @@ func (m Machine) toNode(includeRoutes bool) (*tailcfg.Node, error) { MachineAuthorized: m.Registered, Capabilities: []string{tailcfg.CapabilityFileSharing}, } + // TODO(juanfont): Node also has Sharer when is a shared node with info on the profile + return &n, nil } @@ -179,7 +183,7 @@ func (h *Headscale) getPeers(m Machine) (*[]*tailcfg.Node, error) { Msg("Finding peers") machines := []Machine{} - if err := h.db.Where("namespace_id = ? AND machine_key <> ? AND registered", + if err := h.db.Preload("Namespace").Where("namespace_id = ? AND machine_key <> ? AND registered", m.NamespaceID, m.MachineKey).Find(&machines).Error; err != nil { log.Error().Err(err).Msg("Error accessing db") return nil, err @@ -194,14 +198,14 @@ func (h *Headscale) getPeers(m Machine) (*[]*tailcfg.Node, error) { peers := []*tailcfg.Node{} for _, mn := range machines { - peer, err := mn.toNode(true) + peer, err := h.toNode(mn, true) if err != nil { return nil, err } peers = append(peers, peer) } for _, sharedMachine := range sharedMachines { - peer, err := sharedMachine.Machine.toNode(false) // shared nodes do not expose their routes + peer, err := h.toNode(sharedMachine.Machine, false) // shared nodes do not expose their routes if err != nil { return nil, err } diff --git a/poll.go b/poll.go index 60bfa9ea..40b3e28a 100644 --- a/poll.go +++ b/poll.go @@ -440,7 +440,7 @@ func (h *Headscale) scheduledPollWorker( case <-updateCheckerTicker.C: // Send an update request regardless of outdated or not, if data is sent // to the node is determined in the updateChan consumer block - n, _ := m.toNode(true) + n, _ := h.toNode(m, true) err := h.sendRequestOnUpdateChannel(n) if err != nil { log.Error().