diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 99cc36fa..37a8cb80 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -7,5 +7,5 @@ contact_links: url: "https://github.com/juanfont/headscale/blob/main/docs" about: "Find documentation about how to configure and run headscale." - name: "headscale Discord community" - url: "https://discord.com/invite/XcQxk2VHjx" + url: "https://discord.gg/xGj2TuqyxY" about: "Please ask and answer questions about usage of headscale here." diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8999f7bf..140ea0b0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.17.7 + go-version: 1.18.0 - name: Install dependencies run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ce0c35b..359ace64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # CHANGELOG -## 0.15.0 (2022-xx-xx) +## 0.16.0 (2022-xx-xx) + +### Changes + +- Headscale fails to serve if the ACL policy file cannot be parsed [#537](https://github.com/juanfont/headscale/pull/537) +- Fix labels cardinality error when registering unknown pre-auth key [#519](https://github.com/juanfont/headscale/pull/519) +- Fix send on closed channel crash in polling [#542](https://github.com/juanfont/headscale/pull/542) + +## 0.15.0 (2022-03-20) **Note:** Take a backup of your database before upgrading. @@ -19,7 +27,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) +- Added embedded DERP (and STUN) server into Headscale [#388](https://github.com/juanfont/headscale/pull/388) ### Changes @@ -29,6 +37,8 @@ - Fix a limitation in the ACLs that prevented users to write rules with `*` as source [#374](https://github.com/juanfont/headscale/issues/374) - Reduce the overhead of marshal/unmarshal for Hostinfo, routes and endpoints by using specific types in Machine [#371](https://github.com/juanfont/headscale/pull/371) - Apply normalization function to FQDN on hostnames when hosts registers and retrieve informations [#363](https://github.com/juanfont/headscale/issues/363) +- Fix a bug that prevented the use of `tailscale logout` with OIDC [#508](https://github.com/juanfont/headscale/issues/508) +- Added Tailscale repo HEAD and unstable releases channel to the integration tests targets [#513](https://github.com/juanfont/headscale/pull/513) ## 0.14.0 (2022-02-24) diff --git a/Dockerfile b/Dockerfile index 3ab9c1d1..8d53f6d9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Builder image -FROM docker.io/golang:1.17.8-bullseye AS build +FROM docker.io/golang:1.18.0-bullseye AS build ENV GOPATH /go WORKDIR /go/src/headscale diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 1f0d6353..45fa171d 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,5 +1,5 @@ # Builder image -FROM docker.io/golang:1.17.8-alpine AS build +FROM docker.io/golang:1.18.0-alpine AS build ENV GOPATH /go WORKDIR /go/src/headscale diff --git a/Dockerfile.debug b/Dockerfile.debug index e73c0647..91fe2893 100644 --- a/Dockerfile.debug +++ b/Dockerfile.debug @@ -1,5 +1,5 @@ # Builder image -FROM docker.io/golang:1.17.8-bullseye AS build +FROM docker.io/golang:1.18.0-bullseye AS build ENV GOPATH /go WORKDIR /go/src/headscale diff --git a/Dockerfile.tailscale b/Dockerfile.tailscale index 32a8ce7b..145ab6f7 100644 --- a/Dockerfile.tailscale +++ b/Dockerfile.tailscale @@ -1,11 +1,12 @@ FROM ubuntu:latest -ARG TAILSCALE_VERSION +ARG TAILSCALE_VERSION=* +ARG TAILSCALE_CHANNEL=stable RUN apt-get update \ && apt-get install -y gnupg curl \ - && 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 \ + && curl -fsSL https://pkgs.tailscale.com/${TAILSCALE_CHANNEL}/ubuntu/focal.gpg | apt-key add - \ + && curl -fsSL https://pkgs.tailscale.com/${TAILSCALE_CHANNEL}/ubuntu/focal.list | tee /etc/apt/sources.list.d/tailscale.list \ && apt-get update \ && apt-get install -y ca-certificates tailscale=${TAILSCALE_VERSION} dnsutils \ && rm -rf /var/lib/apt/lists/* diff --git a/Dockerfile.tailscale-HEAD b/Dockerfile.tailscale-HEAD new file mode 100644 index 00000000..b62f7e2b --- /dev/null +++ b/Dockerfile.tailscale-HEAD @@ -0,0 +1,21 @@ +FROM golang:latest + +RUN apt-get update \ + && apt-get install -y ca-certificates dnsutils git iptables \ + && rm -rf /var/lib/apt/lists/* + + +RUN git clone https://github.com/tailscale/tailscale.git + +WORKDIR tailscale + +RUN sh build_dist.sh tailscale.com/cmd/tailscale +RUN sh build_dist.sh tailscale.com/cmd/tailscaled + +RUN cp tailscale /usr/local/bin/ +RUN cp tailscaled /usr/local/bin/ + +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 diff --git a/Makefile b/Makefile index 73630d3f..74ecd89e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # Calculate version -version = $(shell ./scripts/version-at-commit.sh) +version = $(git describe --always --tags --dirty) rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) @@ -10,7 +10,7 @@ PROTO_SOURCES = $(call rwildcard,,*.proto) build: - GGO_ENABLED=0 go build -ldflags "-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$(version)" cmd/headscale/headscale.go + CGO_ENABLED=0 go build -trimpath -buildmode=pie -mod=readonly -ldflags "-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$(version)" cmd/headscale/headscale.go dev: lint test build diff --git a/README.md b/README.md index 1901ce63..e05f8b7d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ An open source, self-hosted implementation of the Tailscale control server. -Join our [Discord](https://discord.gg/XcQxk2VHjx) server for a chat. +Join our [Discord](https://discord.gg/c84AZQhmpx) server for a chat. **Note:** Always select the same GitHub tag as the released version you use to ensure you have the correct example configuration and documentation. @@ -232,6 +232,13 @@ make build Nico + + + Niek +
+ Niek van der Maas +
+ Eugen @@ -239,6 +246,8 @@ make build Eugen Biegler + + Aaron @@ -246,8 +255,6 @@ make build Aaron Bieber - - Fernando @@ -283,6 +290,15 @@ make build Paul Tötterman + + + + + Artem +
+ Artem Klevtsov +
+ Casey @@ -290,8 +306,6 @@ make build Casey Marshall - - Silver @@ -320,6 +334,8 @@ make build thomas + + Abraham @@ -328,14 +344,12 @@ make build - - Artem + + Aofei
- Artem Klevtsov + Aofei Sheng
- - Arthur @@ -350,6 +364,13 @@ make build Bryan Stenson + + + Carson +
+ Carson Yang +
+ Felix @@ -357,6 +378,8 @@ make build Felix Kronlage-Dammers + + Felix @@ -378,8 +401,6 @@ make build Jamie Greeff - - Jim @@ -401,6 +422,8 @@ make build rcursaru + + WhiteSource @@ -422,8 +445,6 @@ make build Shaanan Cohney - - Tanner/ @@ -445,6 +466,8 @@ make build The Gitter Badger + + Tianon @@ -466,8 +489,6 @@ make build Yang Bin - - Zakhar @@ -489,6 +510,8 @@ make build bravechamp + + derelm/ @@ -496,6 +519,13 @@ make build derelm + + + henning +
+ henning mueller +
+ ignoramous/ @@ -510,8 +540,6 @@ make build lion24 - - pernila/ @@ -526,6 +554,8 @@ make build Wakeful-Cloud + + zy/ diff --git a/api.go b/api.go index eab8076e..61ec1b5f 100644 --- a/api.go +++ b/api.go @@ -573,7 +573,7 @@ func (h *Headscale) handleAuthKey( machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name). Inc() } else { - machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error").Inc() + machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", "unknown").Inc() } return diff --git a/app.go b/app.go index 1809f95c..b87fb33c 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,11 +66,6 @@ const ( registerCacheExpiration = time.Minute * 15 registerCacheCleanup = time.Minute * 20 - errUnsupportedDatabase = Error("unsupported DB") - errUnsupportedLetsEncryptChallengeType = Error( - "unknown value for Lets Encrypt challenge type", - ) - DisabledClientAuth = "disabled" RelaxedClientAuth = "relaxed" EnforcedClientAuth = "enforced" @@ -124,7 +127,6 @@ type DERPConfig struct { ServerRegionID int ServerRegionCode string ServerRegionName string - STUNEnabled bool STUNAddr string URLs []url.URL Paths []string @@ -500,10 +502,13 @@ func (h *Headscale) Serve() error { 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() + // 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 + go h.ServeSTUN() } if h.cfg.DERP.AutoUpdate { diff --git a/cmd/headscale/cli/api_key.go b/cmd/headscale/cli/api_key.go index 06099aa1..aa056c54 100644 --- a/cmd/headscale/cli/api_key.go +++ b/cmd/headscale/cli/api_key.go @@ -23,7 +23,7 @@ func init() { apiKeysCmd.AddCommand(listAPIKeys) createAPIKeyCmd.Flags(). - DurationP("expiration", "e", DefaultAPIKeyExpiry, "Human-readable expiration of the key (30m, 24h, 365d...)") + DurationP("expiration", "e", DefaultAPIKeyExpiry, "Human-readable expiration of the key (e.g. 30m, 24h)") apiKeysCmd.AddCommand(createAPIKeyCmd) diff --git a/cmd/headscale/cli/preauthkeys.go b/cmd/headscale/cli/preauthkeys.go index 950cbcc5..7efb72fb 100644 --- a/cmd/headscale/cli/preauthkeys.go +++ b/cmd/headscale/cli/preauthkeys.go @@ -31,7 +31,7 @@ func init() { createPreAuthKeyCmd.PersistentFlags(). Bool("ephemeral", false, "Preauthkey for ephemeral nodes") createPreAuthKeyCmd.Flags(). - DurationP("expiration", "e", DefaultPreAuthKeyExpiry, "Human-readable expiration of the key (30m, 24h, 365d...)") + DurationP("expiration", "e", DefaultPreAuthKeyExpiry, "Human-readable expiration of the key (e.g. 30m, 24h)") } var preauthkeysCmd = &cobra.Command{ diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index dc7a4e9f..992d1254 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") @@ -121,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") @@ -149,7 +155,6 @@ func GetDERPConfig() headscale.DERPConfig { ServerRegionID: serverRegionID, ServerRegionCode: serverRegionCode, ServerRegionName: serverRegionName, - STUNEnabled: stunEnabled, STUNAddr: stunAddr, URLs: urls, Paths: paths, @@ -403,7 +408,7 @@ func getHeadscaleApp() (*headscale.Headscale, error) { aclPath := absPath(viper.GetString("acl_policy_path")) err = app.LoadACLPolicy(aclPath) if err != nil { - log.Error(). + log.Fatal(). Str("path", aclPath). Err(err). Msg("Could not load the ACL policy") diff --git a/config-example.yaml b/config-example.yaml index 2075e69a..dee25cb3 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -69,11 +69,11 @@ 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 + # 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: false - 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/derp-example.yaml b/derp-example.yaml index 0ebe32ed..732c4ba0 100644 --- a/derp-example.yaml +++ b/derp-example.yaml @@ -12,4 +12,4 @@ regions: ipv6: "2604:a880:400:d1::828:b001" stunport: 0 stunonly: false - derptestport: 0 + derpport: 0 diff --git a/derp.go b/derp.go index 7a9b2367..7abce683 100644 --- a/derp.go +++ b/derp.go @@ -148,7 +148,9 @@ 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 + if h.cfg.DERP.ServerEnabled { + h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region + } namespaces, err := h.ListNamespaces() if err != nil { 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 } diff --git a/docs/README.md b/docs/README.md index 7a3080e3..459a6c21 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,7 +3,7 @@ This page contains the official and community contributed documentation for `headscale`. If you are having trouble with following the documentation or get unexpected results, -please ask on [Discord](https://discord.gg/XcQxk2VHjx) instead of opening an Issue. +please ask on [Discord](https://discord.gg/c84AZQhmpx) instead of opening an Issue. ## Official documentation diff --git a/docs/running-headscale-container.md b/docs/running-headscale-container.md index 36e63de9..b36f3bbf 100644 --- a/docs/running-headscale-container.md +++ b/docs/running-headscale-container.md @@ -14,8 +14,8 @@ not work with alternatives like [Podman](https://podman.io). The Docker image ca 1. Prepare a directory on the host Docker node in your directory of choice, used to hold `headscale` configuration and the [SQLite](https://www.sqlite.org/) database: ```shell -mkdir ./headscale && cd ./headscale -mkdir ./config +mkdir -p ./headscale/config +cd ./headscale ``` 2. Create an empty SQlite datebase in the headscale directory: @@ -45,6 +45,17 @@ touch ./config/config.yaml ``` Modify the config file to your preferences before launching Docker container. +Here are some settings that you likely want: + +```yaml +server_url: http://your-host-name:8080 # Change to your hostname or host IP +# Listen to 0.0.0.0 so it's accessible outside the container +metrics_listen_addr: 0.0.0.0:9090 +# The default /var/lib/headscale path is not writable in the container +private_key_path: /etc/headscale/private.key +# The default /var/lib/headscale path is not writable in the container +db_path: /etc/headscale/db.sqlite +``` 4. Start the headscale server while working in the host headscale directory: @@ -61,6 +72,8 @@ docker run \ ``` +Note: use `0.0.0.0:8080:8080` instead of `127.0.0.1:8080:8080` if you want to expose the container externally. + This command will mount `config/` under `/etc/headscale`, forward port 8080 out of the container so the `headscale` instance becomes available and then detach so headscale runs in the background. @@ -87,7 +100,8 @@ curl http://127.0.0.1:9090/metrics 6. Create a namespace ([tailnet](https://tailscale.com/kb/1136/tailnet/)): ```shell -docker exec headscale -- headscale namespaces create myfirstnamespace +docker exec headscale \ + headscale namespaces create myfirstnamespace ``` ### Register a machine (normal login) @@ -101,7 +115,7 @@ tailscale up --login-server YOUR_HEADSCALE_URL To register a machine when running `headscale` in a container, take the headscale command and pass it to the container: ```shell -docker exec headscale -- \ +docker exec headscale \ headscale --namespace myfirstnamespace nodes register --key ``` @@ -110,7 +124,7 @@ docker exec headscale -- \ Generate a key using the command line: ```shell -docker exec headscale -- \ +docker exec headscale \ headscale --namespace myfirstnamespace preauthkeys create --reusable --expiration 24h ``` diff --git a/docs/running-headscale-linux.md b/docs/running-headscale-linux.md index 1e9d11c4..98a67f1d 100644 --- a/docs/running-headscale-linux.md +++ b/docs/running-headscale-linux.md @@ -178,7 +178,7 @@ systemctl status headscale Verify `headscale` is available: ```shell -curl http://127.0.0.1:8080/metrics +curl http://127.0.0.1:9090/metrics ``` `headscale` will now run in the background and start at boot. diff --git a/go.mod b/go.mod index 454ffd19..654d631d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/juanfont/headscale -go 1.17 +go 1.18 require ( github.com/AlecAivazis/survey/v2 v2.3.2 diff --git a/integration_cli_test.go b/integration_cli_test.go index 7ce0758f..9644037d 100644 --- a/integration_cli_test.go +++ b/integration_cli_test.go @@ -72,7 +72,7 @@ func (s *IntegrationCLITestSuite) SetupTest() { if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil { s.headscale = *pheadscale } else { - log.Fatalf("Could not start resource: %s", err) + log.Fatalf("Could not start headscale container: %s", err) } fmt.Println("Created headscale container") diff --git a/integration_common_test.go b/integration_common_test.go index 70285fc4..f7afb807 100644 --- a/integration_common_test.go +++ b/integration_common_test.go @@ -20,7 +20,7 @@ var ( IpPrefix4 = netaddr.MustParseIPPrefix("100.64.0.0/10") IpPrefix6 = netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/48") - tailscaleVersions = []string{"1.22.0", "1.20.4", "1.18.2", "1.16.2", "1.14.3", "1.12.3"} + tailscaleVersions = []string{"head", "unstable", "1.22.2", "1.20.4", "1.18.2", "1.16.2", "1.14.3", "1.12.3"} ) type TestNamespace struct { @@ -128,6 +128,49 @@ func DockerAllowNetworkAdministration(config *docker.HostConfig) { }) } +func getDockerBuildOptions(version string) *dockertest.BuildOptions { + var tailscaleBuildOptions *dockertest.BuildOptions + switch version { + case "head": + tailscaleBuildOptions = &dockertest.BuildOptions{ + Dockerfile: "Dockerfile.tailscale-HEAD", + ContextDir: ".", + BuildArgs: []docker.BuildArg{}, + } + case "unstable": + tailscaleBuildOptions = &dockertest.BuildOptions{ + Dockerfile: "Dockerfile.tailscale", + ContextDir: ".", + BuildArgs: []docker.BuildArg{ + { + Name: "TAILSCALE_VERSION", + Value: "*", // Installs the latest version https://askubuntu.com/a/824926 + }, + { + Name: "TAILSCALE_CHANNEL", + Value: "unstable", + }, + }, + } + default: + tailscaleBuildOptions = &dockertest.BuildOptions{ + Dockerfile: "Dockerfile.tailscale", + ContextDir: ".", + BuildArgs: []docker.BuildArg{ + { + Name: "TAILSCALE_VERSION", + Value: version, + }, + { + Name: "TAILSCALE_CHANNEL", + Value: "stable", + }, + }, + } + } + return tailscaleBuildOptions +} + func getIPs( tailscales map[string]dockertest.Resource, ) (map[string][]netaddr.IP, error) { diff --git a/integration_embedded_derp_test.go b/integration_embedded_derp_test.go index a1737173..54eec804 100644 --- a/integration_embedded_derp_test.go +++ b/integration_embedded_derp_test.go @@ -121,7 +121,7 @@ func (s *IntegrationDERPTestSuite) SetupSuite() { if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil { s.headscale = *pheadscale } else { - log.Fatalf("Could not start resource: %s", err) + log.Fatalf("Could not start headscale container: %s", err) } log.Println("Created headscale container to test DERP") @@ -245,16 +245,8 @@ func (s *IntegrationDERPTestSuite) Join( func (s *IntegrationDERPTestSuite) tailscaleContainer(identifier, version string, network dockertest.Network, ) (string, *dockertest.Resource) { - tailscaleBuildOptions := &dockertest.BuildOptions{ - Dockerfile: "Dockerfile.tailscale", - ContextDir: ".", - BuildArgs: []docker.BuildArg{ - { - Name: "TAILSCALE_VERSION", - Value: version, - }, - }, - } + tailscaleBuildOptions := getDockerBuildOptions(version) + hostname := fmt.Sprintf( "tailscale-%s-%s", strings.Replace(version, ".", "-", -1), @@ -279,7 +271,7 @@ func (s *IntegrationDERPTestSuite) tailscaleContainer(identifier, version string DockerAllowNetworkAdministration, ) if err != nil { - log.Fatalf("Could not start resource: %s", err) + log.Fatalf("Could not start tailscale container version %s: %s", version, err) } log.Printf("Created %s container\n", hostname) diff --git a/integration_test.go b/integration_test.go index 1649f322..52f1765b 100644 --- a/integration_test.go +++ b/integration_test.go @@ -168,16 +168,8 @@ func (s *IntegrationTestSuite) Join( func (s *IntegrationTestSuite) tailscaleContainer( namespace, identifier, version string, ) (string, *dockertest.Resource) { - tailscaleBuildOptions := &dockertest.BuildOptions{ - Dockerfile: "Dockerfile.tailscale", - ContextDir: ".", - BuildArgs: []docker.BuildArg{ - { - Name: "TAILSCALE_VERSION", - Value: version, - }, - }, - } + tailscaleBuildOptions := getDockerBuildOptions(version) + hostname := fmt.Sprintf( "%s-tailscale-%s-%s", namespace, @@ -200,7 +192,7 @@ func (s *IntegrationTestSuite) tailscaleContainer( DockerAllowNetworkAdministration, ) if err != nil { - log.Fatalf("Could not start resource: %s", err) + log.Fatalf("Could not start tailscale container version %s: %s", version, err) } log.Printf("Created %s container\n", hostname) @@ -249,7 +241,7 @@ func (s *IntegrationTestSuite) SetupSuite() { if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil { s.headscale = *pheadscale } else { - log.Fatalf("Could not start resource: %s", err) + log.Fatalf("Could not start headscale container: %s", err) } log.Println("Created headscale container") 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" diff --git a/oidc.go b/oidc.go index 29ce351f..598a208d 100644 --- a/oidc.go +++ b/oidc.go @@ -10,6 +10,7 @@ import ( "html/template" "net/http" "strings" + "time" "github.com/coreos/go-oidc/v3/oidc" "github.com/gin-gonic/gin" @@ -129,6 +130,10 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { oauth2Token, err := h.oauth2Config.Exchange(context.Background(), code) if err != nil { + log.Error(). + Err(err). + Caller(). + Msg("Could not exchange code for token") ctx.String(http.StatusBadRequest, "Could not exchange code for token") return @@ -229,7 +234,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) { Str("machine", machine.Name). Msg("machine already registered, reauthenticating") - h.RefreshMachine(machine, *machine.Expiry) + h.RefreshMachine(machine, time.Time{}) var content bytes.Buffer if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{ diff --git a/poll.go b/poll.go index 15945a9b..3bad0b89 100644 --- a/poll.go +++ b/poll.go @@ -175,32 +175,13 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) { Str("machine", machine.Name). Msg("Loading or creating update channel") - // TODO: could probably remove all that duplication once generics land. - closeChanWithLog := func(channel interface{}, name string) { - log.Trace(). - Str("handler", "PollNetMap"). - Str("machine", machine.Name). - Str("channel", "Done"). - Msg(fmt.Sprintf("Closing %s channel", name)) - - switch c := channel.(type) { - case (chan struct{}): - close(c) - - case (chan []byte): - close(c) - } - } - const chanSize = 8 updateChan := make(chan struct{}, chanSize) - defer closeChanWithLog(updateChan, "updateChan") pollDataChan := make(chan []byte, chanSize) - defer closeChanWithLog(pollDataChan, "pollDataChan") + defer closeChanWithLog(pollDataChan, machine.Name, "pollDataChan") keepAliveChan := make(chan []byte) - defer closeChanWithLog(keepAliveChan, "keepAliveChan") if req.OmitPeers && !req.Stream { log.Info(). @@ -273,7 +254,27 @@ func (h *Headscale) PollNetMapStream( updateChan chan struct{}, ) { { - ctx, cancel := context.WithCancel(ctx.Request.Context()) + machine, err := h.GetMachineByMachineKey(machineKey) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + log.Warn(). + Str("handler", "PollNetMap"). + Msgf("Ignoring request, cannot find machine with key %s", machineKey.String()) + ctx.String(http.StatusUnauthorized, "") + + return + } + log.Error(). + Str("handler", "PollNetMap"). + Msgf("Failed to fetch machine from the database with Machine key: %s", machineKey.String()) + ctx.String(http.StatusInternalServerError, "") + + return + } + + ctx := context.WithValue(ctx.Request.Context(), "machineName", machine.Name) + + ctx, cancel := context.WithCancel(ctx) defer cancel() go h.scheduledPollWorker( @@ -564,8 +565,8 @@ func (h *Headscale) PollNetMapStream( func (h *Headscale) scheduledPollWorker( ctx context.Context, - updateChan chan<- struct{}, - keepAliveChan chan<- []byte, + updateChan chan struct{}, + keepAliveChan chan []byte, machineKey key.MachinePublic, mapRequest tailcfg.MapRequest, machine *Machine, @@ -573,6 +574,17 @@ func (h *Headscale) scheduledPollWorker( keepAliveTicker := time.NewTicker(keepAliveInterval) updateCheckerTicker := time.NewTicker(updateCheckInterval) + defer closeChanWithLog( + updateChan, + fmt.Sprint(ctx.Value("machineName")), + "updateChan", + ) + defer closeChanWithLog( + keepAliveChan, + fmt.Sprint(ctx.Value("machineName")), + "updateChan", + ) + for { select { case <-ctx.Done(): @@ -606,3 +618,13 @@ func (h *Headscale) scheduledPollWorker( } } } + +func closeChanWithLog[C chan []byte | chan struct{}](channel C, machine, name string) { + log.Trace(). + Str("handler", "PollNetMap"). + Str("machine", machine). + Str("channel", "Done"). + Msg(fmt.Sprintf("Closing %s channel", name)) + + close(channel) +} diff --git a/scripts/version-at-commit.sh b/scripts/version-at-commit.sh deleted file mode 100755 index 2f7fab84..00000000 --- a/scripts/version-at-commit.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -set -e -o pipefail -commit="$1" -versionglob="v[0-9].[0-9]*.[0-9]*" -devsuffix=".dev" -if [ -z "$commit" ]; then - commit=`git log -n1 --first-parent "--format=format:%h"` -fi - -# automatically assign version -# -# handles the following cases: -# -# 0. no tags on the repository. Print "dev". -# -# 1. no local modifications and commit is directly tagged. Print tag. -# -# 2. no local modifications and commit is not tagged. Take greatest version tag in repo X.Y.Z and assign X.Y.(Z+1). Print that + $devsuffix + $timestamp. -# -# 3. local modifications. Print "dev". - -tags=$(git tag) -if [[ -z "$tags" ]]; then - echo "dev" -elif `git diff --quiet 2>/dev/null`; then - tagged=$(git tag --points-at "$commit") - if [[ -n "$tagged" ]] ; then - echo $tagged - else - nearest_tag=$(git describe --tags --abbrev=0 --match "$versionglob" "$commit") - v=$(echo $nearest_tag | perl -pe 's/(\d+)$/$1+1/e') - isodate=$(TZ=UTC git log -n1 --format=%cd --date=iso "$commit") - ts=$(TZ=UTC date --date="$isodate" "+%Y%m%d%H%M%S") - echo "${v}${devsuffix}${ts}" - fi -else - echo "dev" -fi