From 7e8bf4bfe5390b2732021fb82368b870a4193ae7 Mon Sep 17 00:00:00 2001 From: Alexander Halbarth Date: Tue, 16 Jan 2024 16:04:03 +0100 Subject: [PATCH] Add Customization Options to DERP Map entry of integrated DERP server (#1565) Co-authored-by: Alexander Halbarth Co-authored-by: Bela Lemle Co-authored-by: Kristoffer Dalby --- CHANGELOG.md | 1 + config-example.yaml | 10 +++++ hscontrol/app.go | 6 ++- hscontrol/db/routes.go | 2 +- hscontrol/derp/server/derp_server.go | 4 ++ hscontrol/types/config.go | 62 ++++++++++++++++++---------- hscontrol/types/node.go | 2 +- integration/general_test.go | 1 - 8 files changed, 61 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6460d7e4..a5441a5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ Fix hang on SIGTERM [#1492](https://github.com/juanfont/headscale/pull/1492) tak Send logs to stderr by default [#1524](https://github.com/juanfont/headscale/pull/1524) Fix [TS-2023-006](https://tailscale.com/security-bulletins/#ts-2023-006) security UPnP issue [#1563](https://github.com/juanfont/headscale/pull/1563) Turn off gRPC logging [#1640](https://github.com/juanfont/headscale/pull/1640) fixes [#1259](https://github.com/juanfont/headscale/issues/1259) +Added the possibility to manually create a DERP-map entry which can be customized, instead of automatically creating it. [#1565](https://github.com/juanfont/headscale/pull/1565) ## 0.22.3 (2023-05-12) diff --git a/config-example.yaml b/config-example.yaml index 5105dcd8..96a654a4 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -94,6 +94,16 @@ derp: # private_key_path: /var/lib/headscale/derp_server_private.key + # This flag can be used, so the DERP map entry for the embedded DERP server is not written automatically, + # it enables the creation of your very own DERP map entry using a locally available file with the parameter DERP.paths + # If you enable the DERP server and set this to false, it is required to add the DERP server to the DERP map using DERP.paths + automatically_add_embedded_derp_region: true + + # For better connection stability (especially when using an Exit-Node and DNS is not working), + # it is possible to optionall add the public IPv4 and IPv6 address to the Derp-Map using: + ipv4: 1.2.3.4 + ipv6: 2001:db8::1 + # List of externally available DERP maps encoded in JSON urls: - https://controlplane.tailscale.com/derpmap/default diff --git a/hscontrol/app.go b/hscontrol/app.go index 5327d6f8..75dfddee 100644 --- a/hscontrol/app.go +++ b/hscontrol/app.go @@ -268,7 +268,7 @@ func (h *Headscale) scheduledDERPMapUpdateWorker(cancelChan <-chan struct{}) { case <-ticker.C: log.Info().Msg("Fetching DERPMap updates") h.DERPMap = derp.GetDERPMap(h.cfg.DERP) - if h.cfg.DERP.ServerEnabled { + if h.cfg.DERP.ServerEnabled && h.cfg.DERP.AutomaticallyAddEmbeddedDerpRegion { region, _ := h.DERPServer.GenerateRegion() h.DERPMap.Regions[region.RegionID] = ®ion } @@ -501,7 +501,9 @@ func (h *Headscale) Serve() error { return err } - h.DERPMap.Regions[region.RegionID] = ®ion + if h.cfg.DERP.AutomaticallyAddEmbeddedDerpRegion { + h.DERPMap.Regions[region.RegionID] = ®ion + } go h.DERPServer.ServeSTUN() } diff --git a/hscontrol/db/routes.go b/hscontrol/db/routes.go index 51c7f3bc..aed9776d 100644 --- a/hscontrol/db/routes.go +++ b/hscontrol/db/routes.go @@ -349,7 +349,7 @@ func (hsdb *HSDatabase) GetNodePrimaryRoutes(node *types.Node) (types.Routes, er // SaveNodeRoutes takes a node and updates the database with // the new routes. -// It returns a bool wheter an update should be sent as the +// It returns a bool whether an update should be sent as the // saved route impacts nodes. func (hsdb *HSDatabase) SaveNodeRoutes(node *types.Node) (bool, error) { hsdb.mu.Lock() diff --git a/hscontrol/derp/server/derp_server.go b/hscontrol/derp/server/derp_server.go index 59e40285..ad325c7a 100644 --- a/hscontrol/derp/server/derp_server.go +++ b/hscontrol/derp/server/derp_server.go @@ -84,6 +84,8 @@ func (d *DERPServer) GenerateRegion() (tailcfg.DERPRegion, error) { RegionID: d.cfg.ServerRegionID, HostName: host, DERPPort: port, + IPv4: d.cfg.IPv4, + IPv6: d.cfg.IPv6, }, }, } @@ -99,6 +101,7 @@ func (d *DERPServer) GenerateRegion() (tailcfg.DERPRegion, error) { localDERPregion.Nodes[0].STUNPort = portSTUN log.Info().Caller().Msgf("DERP region: %+v", localDERPregion) + log.Info().Caller().Msgf("DERP Nodes[0]: %+v", localDERPregion.Nodes[0]) return localDERPregion, nil } @@ -208,6 +211,7 @@ func DERPProbeHandler( // The initial implementation is here https://github.com/tailscale/tailscale/pull/1406 // They have a cache, but not clear if that is really necessary at Headscale, uh, scale. // An example implementation is found here https://derp.tailscale.com/bootstrap-dns +// Coordination server is included automatically, since local DERP is using the same DNS Name in d.serverURL func DERPBootstrapDNSHandler( derpMap *tailcfg.DERPMap, ) func(http.ResponseWriter, *http.Request) { diff --git a/hscontrol/types/config.go b/hscontrol/types/config.go index 4b29c4b7..8e619733 100644 --- a/hscontrol/types/config.go +++ b/hscontrol/types/config.go @@ -107,16 +107,19 @@ type OIDCConfig struct { } type DERPConfig struct { - ServerEnabled bool - ServerRegionID int - ServerRegionCode string - ServerRegionName string - ServerPrivateKeyPath string - STUNAddr string - URLs []url.URL - Paths []string - AutoUpdate bool - UpdateFrequency time.Duration + ServerEnabled bool + AutomaticallyAddEmbeddedDerpRegion bool + ServerRegionID int + ServerRegionCode string + ServerRegionName string + ServerPrivateKeyPath string + STUNAddr string + URLs []url.URL + Paths []string + AutoUpdate bool + UpdateFrequency time.Duration + IPv4 string + IPv6 string } type LogTailConfig struct { @@ -169,6 +172,7 @@ func LoadConfig(path string, isFile bool) error { viper.SetDefault("derp.server.enabled", false) viper.SetDefault("derp.server.stun.enabled", true) + viper.SetDefault("derp.server.automatically_add_embedded_derp_region", true) viper.SetDefault("unix_socket", "/var/run/headscale/headscale.sock") viper.SetDefault("unix_socket_permission", "0o770") @@ -286,8 +290,14 @@ func GetDERPConfig() DERPConfig { serverRegionCode := viper.GetString("derp.server.region_code") serverRegionName := viper.GetString("derp.server.region_name") stunAddr := viper.GetString("derp.server.stun_listen_addr") - privateKeyPath := util.AbsolutePathFromConfigPath(viper.GetString("derp.server.private_key_path")) - + privateKeyPath := util.AbsolutePathFromConfigPath( + viper.GetString("derp.server.private_key_path"), + ) + ipv4 := viper.GetString("derp.server.ipv4") + ipv6 := viper.GetString("derp.server.ipv6") + automaticallyAddEmbeddedDerpRegion := viper.GetBool( + "derp.server.automatically_add_embedded_derp_region", + ) if serverEnabled && stunAddr == "" { log.Fatal(). Msg("derp.server.stun_listen_addr must be set if derp.server.enabled is true") @@ -310,20 +320,28 @@ func GetDERPConfig() DERPConfig { paths := viper.GetStringSlice("derp.paths") + if serverEnabled && !automaticallyAddEmbeddedDerpRegion && len(paths) == 0 { + log.Fatal(). + Msg("Disabling derp.server.automatically_add_embedded_derp_region requires to configure the derp server in derp.paths") + } + autoUpdate := viper.GetBool("derp.auto_update_enabled") updateFrequency := viper.GetDuration("derp.update_frequency") return DERPConfig{ - ServerEnabled: serverEnabled, - ServerRegionID: serverRegionID, - ServerRegionCode: serverRegionCode, - ServerRegionName: serverRegionName, - ServerPrivateKeyPath: privateKeyPath, - STUNAddr: stunAddr, - URLs: urls, - Paths: paths, - AutoUpdate: autoUpdate, - UpdateFrequency: updateFrequency, + ServerEnabled: serverEnabled, + ServerRegionID: serverRegionID, + ServerRegionCode: serverRegionCode, + ServerRegionName: serverRegionName, + ServerPrivateKeyPath: privateKeyPath, + STUNAddr: stunAddr, + URLs: urls, + Paths: paths, + AutoUpdate: autoUpdate, + UpdateFrequency: updateFrequency, + IPv4: ipv4, + IPv6: ipv6, + AutomaticallyAddEmbeddedDerpRegion: automaticallyAddEmbeddedDerpRegion, } } diff --git a/hscontrol/types/node.go b/hscontrol/types/node.go index 9b2ba769..44342646 100644 --- a/hscontrol/types/node.go +++ b/hscontrol/types/node.go @@ -383,7 +383,7 @@ func (node *Node) GetFQDN(dnsConfig *tailcfg.DNSConfig, baseDomain string) (stri // inform peers about smaller changes to the node. // When a field is added to this function, remember to also add it to: // - node.ApplyPeerChange -// - logTracePeerChange in poll.go +// - logTracePeerChange in poll.go. func (node *Node) PeerChangeFromMapRequest(req tailcfg.MapRequest) tailcfg.PeerChange { ret := tailcfg.PeerChange{ NodeID: tailcfg.NodeID(node.ID), diff --git a/integration/general_test.go b/integration/general_test.go index c0928445..15c3a72c 100644 --- a/integration/general_test.go +++ b/integration/general_test.go @@ -320,7 +320,6 @@ func TestTaildrop(t *testing.T) { if err != nil { t.Fatalf("failed to install curl on %s, err: %s", client.Hostname(), err) } - } curlCommand := []string{"curl", "--unix-socket", "/var/run/tailscale/tailscaled.sock", "http://local-tailscaled.sock/localapi/v0/file-targets"} err = retry(10, 1*time.Second, func() error {