From b8aad5451daa24c8eeac9ab52c92b42542078675 Mon Sep 17 00:00:00 2001
From: Juan Font Alonso <juanfontalonso@gmail.com>
Date: Tue, 15 Mar 2022 13:22:25 +0100
Subject: [PATCH 1/6] Make STUN run by default when embedded DERP is enabled

This commit also allows to set an external STUN server, while running the embedded DERP server (without embedded STUN)
---
 app.go                     |  5 +++++
 cmd/headscale/cli/utils.go |  3 +++
 config-example.yaml        |  7 +++++--
 derp_server.go             | 18 ++++++++----------
 4 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/app.go b/app.go
index f1426bbb..177c3ee1 100644
--- a/app.go
+++ b/app.go
@@ -62,6 +62,7 @@ const (
 	errUnsupportedLetsEncryptChallengeType = Error(
 		"unknown value for Lets Encrypt challenge type",
 	)
+	errSTUNAddressNotSet = Error("STUN address not set")
 
 	DisabledClientAuth = "disabled"
 	RelaxedClientAuth  = "relaxed"
@@ -502,6 +503,10 @@ func (h *Headscale) Serve() error {
 	h.DERPMap = GetDERPMap(h.cfg.DERP)
 
 	if h.cfg.DERP.ServerEnabled {
+		if h.cfg.DERP.STUNAddr == "" { // When embedded DERP is enabled we always need a STUN server address, embedded or external
+			return errSTUNAddressNotSet
+		}
+
 		h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region
 		if h.cfg.DERP.STUNEnabled {
 			go h.ServeSTUN()
diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go
index dc7a4e9f..eb26a835 100644
--- a/cmd/headscale/cli/utils.go
+++ b/cmd/headscale/cli/utils.go
@@ -55,6 +55,9 @@ func LoadConfig(path string) error {
 
 	viper.SetDefault("dns_config", nil)
 
+	viper.SetDefault("derp.server.enabled", false)
+	viper.SetDefault("derp.server.stun.enabled", true)
+
 	viper.SetDefault("unix_socket", "/var/run/headscale.sock")
 	viper.SetDefault("unix_socket_permission", "0o770")
 
diff --git a/config-example.yaml b/config-example.yaml
index 2075e69a..31d7a8aa 100644
--- a/config-example.yaml
+++ b/config-example.yaml
@@ -69,10 +69,13 @@ derp:
     region_code: "headscale"
     region_name: "Headscale Embedded DERP"
 
-    # If enabled, also listens in UDP at the configured address for STUN connections to help on NAT traversal
+    # Enabled by default when embedded DERP is enabled. Listens in UDP at the configured address for STUN connections
+    # to help on NAT traversal.
+    # If DERP is enabled, but STUN is disabled you still need to input an external STUN server in the listen_addr field.
+    #
     # For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
     stun:
-      enabled: false
+      enabled: true
       listen_addr: "0.0.0.0:3478"
 
   # List of externally available DERP maps encoded in JSON
diff --git a/derp_server.go b/derp_server.go
index 11e3eb14..6580419e 100644
--- a/derp_server.go
+++ b/derp_server.go
@@ -77,17 +77,15 @@ func (h *Headscale) generateRegionLocalDERP() (tailcfg.DERPRegion, error) {
 		},
 	}
 
-	if h.cfg.DERP.STUNEnabled {
-		_, portStr, err := net.SplitHostPort(h.cfg.DERP.STUNAddr)
-		if err != nil {
-			return tailcfg.DERPRegion{}, err
-		}
-		port, err := strconv.Atoi(portStr)
-		if err != nil {
-			return tailcfg.DERPRegion{}, err
-		}
-		localDERPregion.Nodes[0].STUNPort = port
+	_, portSTUNStr, err := net.SplitHostPort(h.cfg.DERP.STUNAddr)
+	if err != nil {
+		return tailcfg.DERPRegion{}, err
 	}
+	portSTUN, err := strconv.Atoi(portSTUNStr)
+	if err != nil {
+		return tailcfg.DERPRegion{}, err
+	}
+	localDERPregion.Nodes[0].STUNPort = portSTUN
 
 	return localDERPregion, nil
 }

From 98ac88d5ef5d94fca6da47600d031f10e6833d62 Mon Sep 17 00:00:00 2001
From: Juan Font <juanfontalonso@gmail.com>
Date: Wed, 16 Mar 2022 18:45:34 +0100
Subject: [PATCH 2/6] Changed comment position

Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
---
 app.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/app.go b/app.go
index 0e10b3fb..aad41a20 100644
--- a/app.go
+++ b/app.go
@@ -501,7 +501,8 @@ func (h *Headscale) Serve() error {
 	h.DERPMap = GetDERPMap(h.cfg.DERP)
 
 	if h.cfg.DERP.ServerEnabled {
-		if h.cfg.DERP.STUNAddr == "" { // When embedded DERP is enabled we always need a STUN server address, embedded or external
+		// When embedded DERP is enabled we always need a STUN server address, embedded or external
+		if h.cfg.DERP.STUNAddr == "" {
 			return errSTUNAddressNotSet
 		}
 

From 8f5875efe4267ad859fae5e1afc21581856dc6af Mon Sep 17 00:00:00 2001
From: Juan Font Alonso <juanfontalonso@gmail.com>
Date: Wed, 16 Mar 2022 19:46:59 +0100
Subject: [PATCH 3/6] Reorg errors

---
 app.go | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/app.go b/app.go
index aad41a20..9f270108 100644
--- a/app.go
+++ b/app.go
@@ -47,6 +47,14 @@ import (
 	"tailscale.com/types/key"
 )
 
+const (
+	errSTUNAddressNotSet                   = Error("STUN address not set")
+	errUnsupportedDatabase                 = Error("unsupported DB")
+	errUnsupportedLetsEncryptChallengeType = Error(
+		"unknown value for Lets Encrypt challenge type",
+	)
+)
+
 const (
 	AuthPrefix         = "Bearer "
 	Postgres           = "postgres"
@@ -58,12 +66,6 @@ const (
 	registerCacheExpiration = time.Minute * 15
 	registerCacheCleanup    = time.Minute * 20
 
-	errUnsupportedDatabase                 = Error("unsupported DB")
-	errUnsupportedLetsEncryptChallengeType = Error(
-		"unknown value for Lets Encrypt challenge type",
-	)
-	errSTUNAddressNotSet = Error("STUN address not set")
-
 	DisabledClientAuth = "disabled"
 	RelaxedClientAuth  = "relaxed"
 	EnforcedClientAuth = "enforced"

From 2e6687209bd3334f96e1d105375085966beda808 Mon Sep 17 00:00:00 2001
From: Juan Font Alonso <juanfontalonso@gmail.com>
Date: Fri, 18 Mar 2022 12:58:00 +0100
Subject: [PATCH 4/6] Make STUN server mandatory if DERP embedded is enabled

---
 cmd/headscale/cli/utils.go                     |  8 +++++---
 config-example.yaml                            | 10 ++++------
 integration_test/etc_embedded_derp/config.yaml |  5 ++---
 3 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go
index eb26a835..768a9713 100644
--- a/cmd/headscale/cli/utils.go
+++ b/cmd/headscale/cli/utils.go
@@ -124,8 +124,11 @@ func GetDERPConfig() headscale.DERPConfig {
 	serverRegionID := viper.GetInt("derp.server.region_id")
 	serverRegionCode := viper.GetString("derp.server.region_code")
 	serverRegionName := viper.GetString("derp.server.region_name")
-	stunEnabled := viper.GetBool("derp.server.stun.enabled")
-	stunAddr := viper.GetString("derp.server.stun.listen_addr")
+	stunAddr := viper.GetString("derp.server.stun_listen_addr")
+
+	if serverEnabled && stunAddr == "" {
+		log.Fatal().Msg("derp.server.stun_listen_addr must be set if derp.server.enabled is true")
+	}
 
 	urlStrs := viper.GetStringSlice("derp.urls")
 
@@ -152,7 +155,6 @@ func GetDERPConfig() headscale.DERPConfig {
 		ServerRegionID:   serverRegionID,
 		ServerRegionCode: serverRegionCode,
 		ServerRegionName: serverRegionName,
-		STUNEnabled:      stunEnabled,
 		STUNAddr:         stunAddr,
 		URLs:             urls,
 		Paths:            paths,
diff --git a/config-example.yaml b/config-example.yaml
index 31d7a8aa..430b82c5 100644
--- a/config-example.yaml
+++ b/config-example.yaml
@@ -69,14 +69,12 @@ derp:
     region_code: "headscale"
     region_name: "Headscale Embedded DERP"
 
-    # Enabled by default when embedded DERP is enabled. Listens in UDP at the configured address for STUN connections
-    # to help on NAT traversal.
-    # If DERP is enabled, but STUN is disabled you still need to input an external STUN server in the listen_addr field.
+
+    # Listens in UDP at the configured address for STUN connections to help on NAT traversal.
+    # When the embedded DERP server is enabled stun_listen_addr MUST be defined.
     #
     # For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
-    stun:
-      enabled: true
-      listen_addr: "0.0.0.0:3478"
+    stun_listen_addr: "0.0.0.0:3478"
 
   # List of externally available DERP maps encoded in JSON
   urls:
diff --git a/integration_test/etc_embedded_derp/config.yaml b/integration_test/etc_embedded_derp/config.yaml
index 1531d347..a8b57af5 100644
--- a/integration_test/etc_embedded_derp/config.yaml
+++ b/integration_test/etc_embedded_derp/config.yaml
@@ -24,6 +24,5 @@ derp:
     region_id: 999
     region_code: "headscale"
     region_name: "Headscale Embedded DERP"
-    stun:
-      enabled: true
-      listen_addr: "0.0.0.0:3478"
+
+    stun_listen_addr: "0.0.0.0:3478"

From d5ce7d752315cf31520052b67c182f25872a3ec1 Mon Sep 17 00:00:00 2001
From: Juan Font Alonso <juanfontalonso@gmail.com>
Date: Fri, 18 Mar 2022 13:09:57 +0100
Subject: [PATCH 5/6] Prettier

---
 config-example.yaml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/config-example.yaml b/config-example.yaml
index 430b82c5..dee25cb3 100644
--- a/config-example.yaml
+++ b/config-example.yaml
@@ -69,7 +69,6 @@ derp:
     region_code: "headscale"
     region_name: "Headscale Embedded DERP"
 
-
     # Listens in UDP at the configured address for STUN connections to help on NAT traversal.
     # When the embedded DERP server is enabled stun_listen_addr MUST be defined.
     #

From db9ba17920ca3eeea306b8f82fc121403e4f3c29 Mon Sep 17 00:00:00 2001
From: Juan Font Alonso <juanfontalonso@gmail.com>
Date: Fri, 18 Mar 2022 13:10:35 +0100
Subject: [PATCH 6/6] Added missing file

---
 app.go | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/app.go b/app.go
index 9f270108..b87fb33c 100644
--- a/app.go
+++ b/app.go
@@ -127,7 +127,6 @@ type DERPConfig struct {
 	ServerRegionID   int
 	ServerRegionCode string
 	ServerRegionName string
-	STUNEnabled      bool
 	STUNAddr         string
 	URLs             []url.URL
 	Paths            []string
@@ -503,15 +502,13 @@ func (h *Headscale) Serve() error {
 	h.DERPMap = GetDERPMap(h.cfg.DERP)
 
 	if h.cfg.DERP.ServerEnabled {
-		// When embedded DERP is enabled we always need a STUN server address, embedded or external
+		// When embedded DERP is enabled we always need a STUN server
 		if h.cfg.DERP.STUNAddr == "" {
 			return errSTUNAddressNotSet
 		}
 
 		h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region
-		if h.cfg.DERP.STUNEnabled {
-			go h.ServeSTUN()
-		}
+		go h.ServeSTUN()
 	}
 
 	if h.cfg.DERP.AutoUpdate {