diff --git a/CHANGELOG.md b/CHANGELOG.md index fc17886f..fe706c45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Sanitise the node key passed to registration url [#823](https://github.com/juanfont/headscale/pull/823) - Add support for generating pre-auth keys with tags [#767](https://github.com/juanfont/headscale/pull/767) - Add support for evaluating `autoApprovers` ACL entries when a machine is registered [#763](https://github.com/juanfont/headscale/pull/763) +- Add config flag to allow Headscale to start if OIDC provider is down [#829](https://github.com/juanfont/headscale/pull/829) ## 0.16.4 (2022-08-21) diff --git a/README.md b/README.md index a0586f1b..2752d093 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,15 @@ make build Jiang Zhu + + + Benjamin +
+ Benjamin Roberts +
+ + + Nico/ @@ -202,8 +211,6 @@ make build Nico - - e-zk/ @@ -239,6 +246,8 @@ make build ohdearaugustin + + Moritz @@ -246,8 +255,6 @@ make build Moritz Poldrack - - GrigoriyMikhalkin/ @@ -255,6 +262,13 @@ make build GrigoriyMikhalkin + + + Mike +
+ Mike Lloyd +
+ Niek @@ -276,6 +290,8 @@ make build Azz + + Anton @@ -290,8 +306,13 @@ make build Aaron Bieber - - + + + Igor +
+ Igor Perepilitsyn +
+ Laurent @@ -308,11 +329,13 @@ make build - Hoàng + hdhoang/
- Hoàng Đức Hiếu + hdhoang
+ + bravechamp/ @@ -327,6 +350,13 @@ make build Deon Thomas + + + Jamie +
+ Jamie Greeff +
+ ChibangLW/ @@ -334,8 +364,6 @@ make build ChibangLW - - Mevan @@ -350,6 +378,8 @@ make build Michael G. + + Paul @@ -378,8 +408,6 @@ make build Artem Klevtsov - - Casey @@ -394,6 +422,8 @@ make build Pavlos Vinieratos + + Silver @@ -422,8 +452,6 @@ make build thomas - - Abraham @@ -438,6 +466,8 @@ make build Antoine POPINEAU + + Aofei @@ -466,8 +496,6 @@ make build Carson Yang - - kundel/ @@ -482,6 +510,8 @@ make build Felix Kronlage-Dammers + + Felix @@ -496,13 +526,6 @@ make build JJGadgets - - - Jamie -
- Jamie Greeff -
- Jim @@ -510,8 +533,13 @@ make build Jim Tittsler - - + + + Jonathan +
+ Jonathan de Jong +
+ Pierre @@ -526,6 +554,8 @@ make build Rasmus Moorats + + rcursaru/ @@ -554,8 +584,13 @@ make build Shaanan Cohney - - + + + Stefan +
+ Stefan VanBuren +
+ sophware/ @@ -563,6 +598,8 @@ make build sophware + + Tanner/ @@ -598,8 +635,6 @@ make build Tjerk Woudsma - - Yang @@ -607,6 +642,8 @@ make build Yang Bin + + Yujie @@ -642,8 +679,6 @@ make build henning mueller - - ignoramous/ @@ -651,11 +686,13 @@ make build ignoramous + + - lion24/ + sharkonet/
- lion24 + sharkonet
diff --git a/app.go b/app.go index 59101be3..3f43d7a1 100644 --- a/app.go +++ b/app.go @@ -53,8 +53,10 @@ const ( ) ErrFailedPrivateKey = Error("failed to read or create private key") - ErrFailedNoisePrivateKey = Error("failed to read or create Noise protocol private key") - ErrSamePrivateKeys = Error("private key and noise private key are the same") + ErrFailedNoisePrivateKey = Error( + "failed to read or create Noise protocol private key", + ) + ErrSamePrivateKeys = Error("private key and noise private key are the same") ) const ( @@ -193,7 +195,11 @@ func NewHeadscale(cfg *Config) (*Headscale, error) { if cfg.OIDC.Issuer != "" { err = app.initOIDC() if err != nil { - return nil, err + if cfg.OIDC.OnlyStartIfOIDCIsAvailable { + return nil, err + } else { + log.Warn().Err(err).Msg("failed to set up OIDC provider, falling back to CLI based authentication") + } } } @@ -448,16 +454,20 @@ func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *mux.Router { router.HandleFunc("/health", h.HealthHandler).Methods(http.MethodGet) router.HandleFunc("/key", h.KeyHandler).Methods(http.MethodGet) router.HandleFunc("/register/{nkey}", h.RegisterWebAPI).Methods(http.MethodGet) - router.HandleFunc("/machine/{mkey}/map", h.PollNetMapHandler).Methods(http.MethodPost) + router.HandleFunc("/machine/{mkey}/map", h.PollNetMapHandler). + Methods(http.MethodPost) router.HandleFunc("/machine/{mkey}", h.RegistrationHandler).Methods(http.MethodPost) router.HandleFunc("/oidc/register/{nkey}", h.RegisterOIDC).Methods(http.MethodGet) router.HandleFunc("/oidc/callback", h.OIDCCallback).Methods(http.MethodGet) router.HandleFunc("/apple", h.AppleConfigMessage).Methods(http.MethodGet) - router.HandleFunc("/apple/{platform}", h.ApplePlatformConfig).Methods(http.MethodGet) + router.HandleFunc("/apple/{platform}", h.ApplePlatformConfig). + Methods(http.MethodGet) router.HandleFunc("/windows", h.WindowsConfigMessage).Methods(http.MethodGet) - router.HandleFunc("/windows/tailscale.reg", h.WindowsRegConfig).Methods(http.MethodGet) + router.HandleFunc("/windows/tailscale.reg", h.WindowsRegConfig). + Methods(http.MethodGet) router.HandleFunc("/swagger", SwaggerUI).Methods(http.MethodGet) - router.HandleFunc("/swagger/v1/openapiv2.json", SwaggerAPIv1).Methods(http.MethodGet) + router.HandleFunc("/swagger/v1/openapiv2.json", SwaggerAPIv1). + Methods(http.MethodGet) if h.cfg.DERP.ServerEnabled { router.HandleFunc("/derp", h.DERPHandler) @@ -477,7 +487,8 @@ func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *mux.Router { func (h *Headscale) createNoiseMux() *mux.Router { router := mux.NewRouter() - router.HandleFunc("/machine/register", h.NoiseRegistrationHandler).Methods(http.MethodPost) + router.HandleFunc("/machine/register", h.NoiseRegistrationHandler). + Methods(http.MethodPost) router.HandleFunc("/machine/map", h.NoisePollNetMapHandler) return router @@ -827,9 +838,8 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) { ReadTimeout: HTTPReadTimeout, } - err := server.ListenAndServe() - go func() { + err := server.ListenAndServe() log.Fatal(). Caller(). Err(err). diff --git a/config-example.yaml b/config-example.yaml index 69672b24..72397c78 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -230,6 +230,7 @@ unix_socket_permission: "0770" # help us test it. # OpenID Connect # oidc: +# only_start_if_oidc_is_available: true # issuer: "https://your-oidc.issuer.com/path" # client_id: "your-oidc-client-id" # client_secret: "your-oidc-client-secret" diff --git a/config.go b/config.go index b000c566..494356d8 100644 --- a/config.go +++ b/config.go @@ -90,14 +90,15 @@ type LetsEncryptConfig struct { } type OIDCConfig struct { - Issuer string - ClientID string - ClientSecret string - Scope []string - ExtraParams map[string]string - AllowedDomains []string - AllowedUsers []string - StripEmaildomain bool + OnlyStartIfOIDCIsAvailable bool + Issuer string + ClientID string + ClientSecret string + Scope []string + ExtraParams map[string]string + AllowedDomains []string + AllowedUsers []string + StripEmaildomain bool } type DERPConfig struct { @@ -174,6 +175,7 @@ func LoadConfig(path string, isFile bool) error { viper.SetDefault("oidc.scope", []string{oidc.ScopeOpenID, "profile", "email"}) viper.SetDefault("oidc.strip_email_domain", true) + viper.SetDefault("oidc.only_start_if_oidc_is_available", true) viper.SetDefault("logtail.enabled", false) viper.SetDefault("randomize_client_port", false) @@ -559,6 +561,9 @@ func GetHeadscaleConfig() (*Config, error) { UnixSocketPermission: GetFileMode("unix_socket_permission"), OIDC: OIDCConfig{ + OnlyStartIfOIDCIsAvailable: viper.GetBool( + "oidc.only_start_if_oidc_is_available", + ), Issuer: viper.GetString("oidc.issuer"), ClientID: viper.GetString("oidc.client_id"), ClientSecret: viper.GetString("oidc.client_secret"), diff --git a/flake.lock b/flake.lock index 45b4c6d7..92184e8c 100644 --- a/flake.lock +++ b/flake.lock @@ -17,11 +17,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1662019588, - "narHash": "sha256-oPEjHKGGVbBXqwwL+UjsveJzghWiWV0n9ogo1X6l4cw=", + "lastModified": 1664106353, + "narHash": "sha256-HMJP80+DSxFySpWyuxz5+iNozS3+dVt0b4n6YMIU5/8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "2da64a81275b68fdad38af669afeda43d401e94b", + "rev": "79d3ca08920364759c63fd3eb562e99c0c17044a", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 7927221e..050fd747 100644 --- a/flake.nix +++ b/flake.nix @@ -6,163 +6,163 @@ flake-utils.url = "github:numtide/flake-utils"; }; - outputs = { self, nixpkgs, flake-utils, ... }: - let - headscaleVersion = if (self ? shortRev) then self.shortRev else "dev"; - in + outputs = { + self, + nixpkgs, + flake-utils, + ... + }: let + headscaleVersion = + if (self ? shortRev) + then self.shortRev + else "dev"; + in { - overlay = final: prev: - let - pkgs = nixpkgs.legacyPackages.${prev.system}; - in - rec { - headscale = - pkgs.buildGo119Module rec { - pname = "headscale"; - version = headscaleVersion; - src = pkgs.lib.cleanSource self; + overlay = _: prev: let + pkgs = nixpkgs.legacyPackages.${prev.system}; + in rec { + headscale = pkgs.buildGo119Module rec { + pname = "headscale"; + version = headscaleVersion; + src = pkgs.lib.cleanSource self; - # When updating go.mod or go.sum, a new sha will need to be calculated, - # update this if you have a mismatch after doing a change to thos files. - vendorSha256 = "sha256-DosFCSiQ5FURbIrt4NcPGkExc84t2MGMqe9XLxNHdIM="; + # When updating go.mod or go.sum, a new sha will need to be calculated, + # update this if you have a mismatch after doing a change to thos files. + vendorSha256 = "sha256-DosFCSiQ5FURbIrt4NcPGkExc84t2MGMqe9XLxNHdIM="; - ldflags = [ "-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}" ]; - }; - - golines = - pkgs.buildGoModule rec { - pname = "golines"; - version = "0.9.0"; - - src = pkgs.fetchFromGitHub { - owner = "segmentio"; - repo = "golines"; - rev = "v${version}"; - sha256 = "sha256-BUXEg+4r9L/gqe4DhTlhN55P3jWt7ZyWFQycO6QePrw="; - }; - - vendorSha256 = "sha256-sEzWUeVk5GB0H41wrp12P8sBWRjg0FHUX6ABDEEBqK8="; - - nativeBuildInputs = [ pkgs.installShellFiles ]; - }; - - golangci-lint = prev.golangci-lint.override { - # Override https://github.com/NixOS/nixpkgs/pull/166801 which changed this - # to buildGo118Module because it does not build on Darwin. - inherit (prev) buildGoModule; - }; - - # golangci-lint = - # pkgs.buildGo117Module rec { - # pname = "golangci-lint"; - # version = "1.46.2"; - # - # src = pkgs.fetchFromGitHub { - # owner = "golangci"; - # repo = "golangci-lint"; - # rev = "v${version}"; - # sha256 = "sha256-7sDAwWz+qoB/ngeH35tsJ5FZUfAQvQsU6kU9rUHIHMk="; - # }; - # - # vendorSha256 = "sha256-w38OKN6HPoz37utG/2QSPMai55IRDXCIIymeMe6ogIU="; - # - # nativeBuildInputs = [ pkgs.installShellFiles ]; - # }; - - protoc-gen-grpc-gateway = - pkgs.buildGoModule rec { - pname = "grpc-gateway"; - version = "2.8.0"; - - src = pkgs.fetchFromGitHub { - owner = "grpc-ecosystem"; - repo = "grpc-gateway"; - rev = "v${version}"; - sha256 = "sha256-8eBBBYJ+tBjB2fgPMX/ZlbN3eeS75e8TAZYOKXs6hcg="; - }; - - vendorSha256 = "sha256-AW2Gn/mlZyLMwF+NpK59eiOmQrYWW/9HPjbunYc9Ij4="; - - nativeBuildInputs = [ pkgs.installShellFiles ]; - - subPackages = [ "protoc-gen-grpc-gateway" "protoc-gen-openapiv2" ]; - }; + ldflags = ["-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}"]; }; - } // flake-utils.lib.eachDefaultSystem - (system: - let - pkgs = import nixpkgs { - overlays = [ self.overlay ]; - inherit system; + + golines = pkgs.buildGoModule rec { + pname = "golines"; + version = "0.9.0"; + + src = pkgs.fetchFromGitHub { + owner = "segmentio"; + repo = "golines"; + rev = "v${version}"; + sha256 = "sha256-BUXEg+4r9L/gqe4DhTlhN55P3jWt7ZyWFQycO6QePrw="; }; - buildDeps = with pkgs; [ git go_1_19 gnumake ]; - devDeps = with pkgs; - buildDeps ++ [ + + vendorSha256 = "sha256-sEzWUeVk5GB0H41wrp12P8sBWRjg0FHUX6ABDEEBqK8="; + + nativeBuildInputs = [pkgs.installShellFiles]; + }; + + golangci-lint = prev.golangci-lint.override { + # Override https://github.com/NixOS/nixpkgs/pull/166801 which changed this + # to buildGo118Module because it does not build on Darwin. + inherit (prev) buildGoModule; + }; + + # golangci-lint = + # pkgs.buildGo117Module rec { + # pname = "golangci-lint"; + # version = "1.46.2"; + # + # src = pkgs.fetchFromGitHub { + # owner = "golangci"; + # repo = "golangci-lint"; + # rev = "v${version}"; + # sha256 = "sha256-7sDAwWz+qoB/ngeH35tsJ5FZUfAQvQsU6kU9rUHIHMk="; + # }; + # + # vendorSha256 = "sha256-w38OKN6HPoz37utG/2QSPMai55IRDXCIIymeMe6ogIU="; + # + # nativeBuildInputs = [ pkgs.installShellFiles ]; + # }; + + protoc-gen-grpc-gateway = pkgs.buildGoModule rec { + pname = "grpc-gateway"; + version = "2.8.0"; + + src = pkgs.fetchFromGitHub { + owner = "grpc-ecosystem"; + repo = "grpc-gateway"; + rev = "v${version}"; + sha256 = "sha256-8eBBBYJ+tBjB2fgPMX/ZlbN3eeS75e8TAZYOKXs6hcg="; + }; + + vendorSha256 = "sha256-AW2Gn/mlZyLMwF+NpK59eiOmQrYWW/9HPjbunYc9Ij4="; + + nativeBuildInputs = [pkgs.installShellFiles]; + + subPackages = ["protoc-gen-grpc-gateway" "protoc-gen-openapiv2"]; + }; + }; + } + // flake-utils.lib.eachDefaultSystem + (system: let + pkgs = import nixpkgs { + overlays = [self.overlay]; + inherit system; + }; + buildDeps = with pkgs; [git go_1_19 gnumake]; + devDeps = with pkgs; + buildDeps + ++ [ + golangci-lint + golines + nodePackages.prettier + + # Protobuf dependencies + protobuf + protoc-gen-go + protoc-gen-go-grpc + protoc-gen-grpc-gateway + buf + clang-tools # clang-format + ]; + + # Add entry to build a docker image with headscale + # caveat: only works on Linux + # + # Usage: + # nix build .#headscale-docker + # docker load < result + headscale-docker = pkgs.dockerTools.buildLayeredImage { + name = "headscale"; + tag = headscaleVersion; + contents = [pkgs.headscale]; + config.Entrypoint = [(pkgs.headscale + "/bin/headscale")]; + }; + in rec { + # `nix develop` + devShell = pkgs.mkShell {buildInputs = devDeps;}; + + # `nix build` + packages = with pkgs; { + inherit headscale; + inherit headscale-docker; + }; + + defaultPackage = pkgs.headscale; + + # `nix run` + apps.headscale = flake-utils.lib.mkApp { + drv = packages.headscale; + }; + defaultApp = apps.headscale; + + checks = { + format = + pkgs.runCommand "check-format" + { + buildInputs = with pkgs; [ + gnumake + nixpkgs-fmt golangci-lint - golines nodePackages.prettier - - # Protobuf dependencies - protobuf - protoc-gen-go - protoc-gen-go-grpc - protoc-gen-grpc-gateway - buf - clang-tools # clang-format + golines + clang-tools ]; - - - # Add entry to build a docker image with headscale - # caveat: only works on Linux - # - # Usage: - # nix build .#headscale-docker - # docker load < result - headscale-docker = pkgs.dockerTools.buildLayeredImage { - name = "headscale"; - tag = headscaleVersion; - contents = [ pkgs.headscale ]; - config.Entrypoint = [ (pkgs.headscale + "/bin/headscale") ]; - }; - in - rec { - # `nix develop` - devShell = pkgs.mkShell { buildInputs = devDeps; }; - - # `nix build` - packages = with pkgs; { - inherit headscale; - inherit headscale-docker; - }; - - defaultPackage = pkgs.headscale; - - # `nix run` - apps.headscale = flake-utils.lib.mkApp { - drv = packages.headscale; - }; - defaultApp = apps.headscale; - - checks = { - format = pkgs.runCommand "check-format" - { - buildInputs = with pkgs; [ - gnumake - nixpkgs-fmt - golangci-lint - nodePackages.prettier - golines - clang-tools - ]; - } '' - ${pkgs.nixpkgs-fmt}/bin/nixpkgs-fmt ${./.} - ${pkgs.golangci-lint}/bin/golangci-lint run --fix --timeout 10m - ${pkgs.nodePackages.prettier}/bin/prettier --write '**/**.{ts,js,md,yaml,yml,sass,css,scss,html}' - ${pkgs.golines}/bin/golines --max-len=88 --base-formatter=gofumpt -w ${./.} - ${pkgs.clang-tools}/bin/clang-format -style="{BasedOnStyle: Google, IndentWidth: 4, AlignConsecutiveDeclarations: true, AlignConsecutiveAssignments: true, ColumnLimit: 0}" -i ${./.} - ''; - }; - - - }); + } '' + ${pkgs.nixpkgs-fmt}/bin/nixpkgs-fmt ${./.} + ${pkgs.golangci-lint}/bin/golangci-lint run --fix --timeout 10m + ${pkgs.nodePackages.prettier}/bin/prettier --write '**/**.{ts,js,md,yaml,yml,sass,css,scss,html}' + ${pkgs.golines}/bin/golines --max-len=88 --base-formatter=gofumpt -w ${./.} + ${pkgs.clang-tools}/bin/clang-format -style="{BasedOnStyle: Google, IndentWidth: 4, AlignConsecutiveDeclarations: true, AlignConsecutiveAssignments: true, ColumnLimit: 0}" -i ${./.} + ''; + }; + }); } diff --git a/integration_test/etc/alt-config.dump.gold.yaml b/integration_test/etc/alt-config.dump.gold.yaml index c9bd39b0..9df870fa 100644 --- a/integration_test/etc/alt-config.dump.gold.yaml +++ b/integration_test/etc/alt-config.dump.gold.yaml @@ -35,6 +35,7 @@ logtail: enabled: false metrics_listen_addr: 127.0.0.1:19090 oidc: + only_start_if_oidc_is_available: true scope: - openid - profile diff --git a/integration_test/etc/alt-env-config.dump.gold.yaml b/integration_test/etc/alt-env-config.dump.gold.yaml index 4df4bf44..2fa8ef44 100644 --- a/integration_test/etc/alt-env-config.dump.gold.yaml +++ b/integration_test/etc/alt-env-config.dump.gold.yaml @@ -34,6 +34,7 @@ logtail: enabled: false metrics_listen_addr: 127.0.0.1:19090 oidc: + only_start_if_oidc_is_available: true scope: - openid - profile diff --git a/integration_test/etc/config.dump.gold.yaml b/integration_test/etc/config.dump.gold.yaml index 158a1954..7bdd2c3e 100644 --- a/integration_test/etc/config.dump.gold.yaml +++ b/integration_test/etc/config.dump.gold.yaml @@ -35,6 +35,7 @@ logtail: enabled: false metrics_listen_addr: 127.0.0.1:9090 oidc: + only_start_if_oidc_is_available: true scope: - openid - profile diff --git a/protocol_common.go b/protocol_common.go index b4c6223c..465279aa 100644 --- a/protocol_common.go +++ b/protocol_common.go @@ -483,7 +483,7 @@ func (h *Headscale) handleNewMachineCommon( Bool("noise", machineKey.IsZero()). Str("machine", registerRequest.Hostinfo.Hostname). Msg("The node seems to be new, sending auth url") - if h.cfg.OIDC.Issuer != "" { + if h.oauth2Config != nil { resp.AuthURL = fmt.Sprintf( "%s/oidc/register/%s", strings.TrimSuffix(h.cfg.ServerURL, "/"), @@ -716,7 +716,7 @@ func (h *Headscale) handleMachineExpiredCommon( return } - if h.cfg.OIDC.Issuer != "" { + if h.oauth2Config != nil { resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", strings.TrimSuffix(h.cfg.ServerURL, "/"), NodePublicKeyStripPrefix(registerRequest.NodeKey))