Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
897d480
Add an embedded DERP server to Headscale
juanfont Mar 3, 2022
9d43f58
Added missing deps
juanfont Mar 3, 2022
23cde84
Merge branch 'main' into embedded-derp
juanfont Mar 3, 2022
607c1eb
Be consistent with uppercase DERP
juanfont Mar 4, 2022
22d2443
Move more stuff to common
juanfont Mar 4, 2022
09d78c7
Even more stuff moved to common
juanfont Mar 4, 2022
758b1ba
Renamed configuration items of the DERP server
juanfont Mar 5, 2022
df37d1a
Do not offer the option to be DERP insecure
juanfont Mar 5, 2022
b742379
Do not use the term embedded
juanfont Mar 5, 2022
88378c2
Rename the file to derp_server.go for coherence
juanfont Mar 5, 2022
e9eb90f
Added integration tests for the embedded DERP server
juanfont Mar 5, 2022
992efbd
Added missing private TLS key
juanfont Mar 5, 2022
237f7f1
Merge branch 'main' into embedded-derp
juanfont Mar 5, 2022
e78c002
Fix minor issue
juanfont Mar 5, 2022
54c3e00
Merge local DERP server region with other configured DERP sources
juanfont Mar 5, 2022
70910c4
Working /bootstrap-dns DERP helper
juanfont Mar 6, 2022
dc909ba
Improved logging on startup
juanfont Mar 6, 2022
eb50015
Make STUN server configurable
juanfont Mar 6, 2022
eb06054
Make DERP Region configurable
juanfont Mar 6, 2022
de2ea83
Linting here and there
juanfont Mar 6, 2022
e1fcf0d
Added more version
juanfont Mar 6, 2022
b47de07
Update Dockerfile.tailscale
juanfont Mar 6, 2022
580db9b
Mention that STUN is UDP
juanfont Mar 6, 2022
a27b386
Clarified expiration dates
juanfont Mar 6, 2022
b3fa66d
Check for DERP in test
juanfont Mar 6, 2022
05df8e9
Added missing file
juanfont Mar 6, 2022
15ed713
Merge branch 'embedded-derp' of https://github.com/juanfont/headscale…
juanfont Mar 6, 2022
03452a8
Prettied
juanfont Mar 6, 2022
dd26cbd
Merge branch 'main' into embedded-derp
kradalby Mar 8, 2022
cc0c88a
Added small integration test for stun
juanfont Mar 8, 2022
b41d899
Merge branch 'embedded-derp' of https://github.com/juanfont/headscale…
juanfont Mar 8, 2022
05c5e22
Updated CHANGELOG and README
juanfont Mar 8, 2022
e5d22b8
Merge branch 'main' into embedded-derp
juanfont Mar 8, 2022
bdbf620
Merge branch 'embedded-derp' of https://github.com/juanfont/headscale…
juanfont Mar 8, 2022
b803240
Added new line for prettier
juanfont Mar 8, 2022
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
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// development
integration_test.go
integration_test/
!integration_test/etc_embedded_derp/tls/server.crt

Dockerfile*
docker-compose*
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- Users can now use emails in ACL's groups [#372](https://github.com/juanfont/headscale/issues/372)
- Add shorthand aliases for commands and subcommands [#376](https://github.com/juanfont/headscale/pull/376)
- Add `/windows` endpoint for Windows configuration instructions + registry file download [#392](https://github.com/juanfont/headscale/pull/392)
- Added embedded DERP server into Headscale [#388](https://github.com/juanfont/headscale/pull/388)

### Changes

Expand Down
7 changes: 6 additions & 1 deletion Dockerfile.tailscale
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@ RUN apt-get update \
&& curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.gpg | apt-key add - \
&& curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.list | tee /etc/apt/sources.list.d/tailscale.list \
&& apt-get update \
&& apt-get install -y tailscale=${TAILSCALE_VERSION} dnsutils \
&& apt-get install -y ca-certificates tailscale=${TAILSCALE_VERSION} dnsutils \
&& rm -rf /var/lib/apt/lists/*

ADD integration_test/etc_embedded_derp/tls/server.crt /usr/local/share/ca-certificates/
RUN chmod 644 /usr/local/share/ca-certificates/server.crt

RUN update-ca-certificates
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ test_integration:
test_integration_cli:
go test -tags integration -v integration_cli_test.go integration_common_test.go

test_integration_derp:
go test -tags integration -v integration_embedded_derp_test.go integration_common_test.go

coverprofile_func:
go tool cover -func=coverage.out

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ one of the maintainers.
- Dual stack (IPv4 and IPv6)
- Routing advertising (including exit nodes)
- Ephemeral nodes
- Embedded [DERP server](https://tailscale.com/blog/how-tailscale-works/#encrypted-tcp-relays-derp)

## Client OS support

Expand Down
39 changes: 33 additions & 6 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,16 @@ type OIDCConfig struct {
}

type DERPConfig struct {
URLs []url.URL
Paths []string
AutoUpdate bool
UpdateFrequency time.Duration
ServerEnabled bool
ServerRegionID int
ServerRegionCode string
ServerRegionName string
STUNEnabled bool
STUNAddr string
Comment on lines +123 to +128
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we split this into DERPServerConfig and DERPSTUNConfig ?

At some point I want to have a go at getting rid of all the manual reading of options from viper, I think it can do that...

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe look into using HCL struct based parsing?

URLs []url.URL
Paths []string
AutoUpdate bool
UpdateFrequency time.Duration
}

type CLIConfig struct {
Expand All @@ -142,7 +148,8 @@ type Headscale struct {
dbDebug bool
privateKey *key.MachinePrivate

DERPMap *tailcfg.DERPMap
DERPMap *tailcfg.DERPMap
DERPServer *DERPServer

aclPolicy *ACLPolicy
aclRules []tailcfg.FilterRule
Expand Down Expand Up @@ -178,7 +185,6 @@ func LookupTLSClientAuthMode(mode string) (tls.ClientAuthType, bool) {
}
}

// NewHeadscale returns the Headscale app.
func NewHeadscale(cfg Config) (*Headscale, error) {
privKey, err := readOrCreatePrivateKey(cfg.PrivateKeyPath)
if err != nil {
Expand Down Expand Up @@ -239,6 +245,14 @@ func NewHeadscale(cfg Config) (*Headscale, error) {
}
}

if cfg.DERP.ServerEnabled {
embeddedDERPServer, err := app.NewDERPServer()
if err != nil {
return nil, err
}
app.DERPServer = embeddedDERPServer
}

return &app, nil
}

Expand Down Expand Up @@ -463,6 +477,12 @@ func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *gin.Engine {
router.GET("/swagger", SwaggerUI)
router.GET("/swagger/v1/openapiv2.json", SwaggerAPIv1)

if h.cfg.DERP.ServerEnabled {
router.Any("/derp", h.DERPHandler)
router.Any("/derp/probe", h.DERPProbeHandler)
router.Any("/bootstrap-dns", h.DERPBootstrapDNSHandler)
}

api := router.Group("/api")
api.Use(h.httpAuthenticationMiddleware)
{
Expand All @@ -481,6 +501,13 @@ func (h *Headscale) Serve() error {
// Fetch an initial DERP Map before we start serving
h.DERPMap = GetDERPMap(h.cfg.DERP)

if h.cfg.DERP.ServerEnabled {
h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region
if h.cfg.DERP.STUNEnabled {
go h.ServeSTUN()
}
}

if h.cfg.DERP.AutoUpdate {
derpMapCancelChannel := make(chan struct{})
defer func() { derpMapCancelChannel <- struct{}{} }()
Expand Down
7 changes: 3 additions & 4 deletions cmd/headscale/cli/server.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package cli

import (
"log"

"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)

Expand All @@ -19,12 +18,12 @@ var serveCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
log.Fatal().Caller().Err(err).Msg("Error initializing")
}

err = h.Serve()
if err != nil {
log.Fatalf("Error initializing: %s", err)
log.Fatal().Caller().Err(err).Msg("Error starting server")
}
},
}
21 changes: 17 additions & 4 deletions cmd/headscale/cli/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ func LoadConfig(path string) error {
}

func GetDERPConfig() headscale.DERPConfig {
serverEnabled := viper.GetBool("derp.server.enabled")
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")

urlStrs := viper.GetStringSlice("derp.urls")

urls := make([]url.URL, len(urlStrs))
Expand All @@ -138,10 +145,16 @@ func GetDERPConfig() headscale.DERPConfig {
updateFrequency := viper.GetDuration("derp.update_frequency")

return headscale.DERPConfig{
URLs: urls,
Paths: paths,
AutoUpdate: autoUpdate,
UpdateFrequency: updateFrequency,
ServerEnabled: serverEnabled,
ServerRegionID: serverRegionID,
ServerRegionCode: serverRegionCode,
ServerRegionName: serverRegionName,
STUNEnabled: stunEnabled,
STUNAddr: stunAddr,
URLs: urls,
Paths: paths,
AutoUpdate: autoUpdate,
UpdateFrequency: updateFrequency,
}
}

Expand Down
20 changes: 20 additions & 0 deletions config-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@ ip_prefixes:
# headscale needs a list of DERP servers that can be presented
# to the clients.
derp:
server:
# If enabled, runs the embedded DERP server and merges it into the rest of the DERP config
# The Headscale server_url defined above MUST be using https, DERP requires TLS to be in place
enabled: false

# Region ID to use for the embedded DERP server.
# The local DERP prevails if the region ID collides with other region ID coming from
# the regular DERP config.
region_id: 999

# Region code and name are displayed in the Tailscale UI to identify a DERP region
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
# For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
stun:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could possible call out that this is UDP, so people know what to open in firewall.

enabled: false
listen_addr: "0.0.0.0:3478"

# List of externally available DERP maps encoded in JSON
urls:
- https://controlplane.tailscale.com/derpmap/default
Expand Down
1 change: 1 addition & 0 deletions derp.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ func (h *Headscale) scheduledDERPMapUpdateWorker(cancelChan <-chan struct{}) {
case <-ticker.C:
log.Info().Msg("Fetching DERPMap updates")
h.DERPMap = GetDERPMap(h.cfg.DERP)
h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region

namespaces, err := h.ListNamespaces()
if err != nil {
Expand Down
Loading