From e7a2501fe865dc04cb999be4d50332dc4e0f57f0 Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Sun, 26 Sep 2021 16:53:05 +0800 Subject: [PATCH 01/70] initial work on OIDC (SSO) integration --- api.go | 17 +- app.go | 6 + cmd/headscale/cli/utils.go | 4 + go.mod | 3 + go.sum | 11 ++ oidc.go | 310 +++++++++++++++++++++++++++++++++++++ 6 files changed, 347 insertions(+), 4 deletions(-) create mode 100644 oidc.go diff --git a/api.go b/api.go index e2a56185..2c5a1321 100644 --- a/api.go +++ b/api.go @@ -133,8 +133,13 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { Str("handler", "Registration"). Str("machine", m.Name). Msg("Not registered and not NodeKey rotation. Sending a authurl to register") - resp.AuthURL = fmt.Sprintf("%s/register?key=%s", - h.cfg.ServerURL, mKey.HexString()) + + if h.cfg.OIDCEndpoint != "" { + resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", h.cfg.ServerURL, mKey.HexString()) + } else { + resp.AuthURL = fmt.Sprintf("%s/register?key=%s", + h.cfg.ServerURL, mKey.HexString()) + } respBody, err := encode(resp, &mKey, h.privateKey) if err != nil { log.Error(). @@ -199,8 +204,12 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { Str("handler", "Registration"). Str("machine", m.Name). Msg("The node is sending us a new NodeKey, sending auth url") - resp.AuthURL = fmt.Sprintf("%s/register?key=%s", - h.cfg.ServerURL, mKey.HexString()) + if h.cfg.OIDCEndpoint != "" { + resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", h.cfg.ServerURL, mKey.HexString()) + } else { + resp.AuthURL = fmt.Sprintf("%s/register?key=%s", + h.cfg.ServerURL, mKey.HexString()) + } respBody, err := encode(resp, &mKey, h.privateKey) if err != nil { log.Error(). diff --git a/app.go b/app.go index c903d83f..81871a87 100644 --- a/app.go +++ b/app.go @@ -45,6 +45,10 @@ type Config struct { TLSKeyPath string DNSConfig *tailcfg.DNSConfig + + OIDCEndpoint string + OIDCClientID string + OIDCClientSecret string } // Headscale represents the base app of the service @@ -168,6 +172,8 @@ func (h *Headscale) Serve() error { r.GET("/register", h.RegisterWebAPI) r.POST("/machine/:id/map", h.PollNetMapHandler) r.POST("/machine/:id", h.RegistrationHandler) + r.GET("/oidc/register/:mKey", h.RegisterOIDC) + r.GET("/oidc/callback", h.OIDCCallback) var err error timeout := 30 * time.Second diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 7ada6693..b7faad57 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -170,6 +170,10 @@ func getHeadscaleApp() (*headscale.Headscale, error) { TLSKeyPath: absPath(viper.GetString("tls_key_path")), DNSConfig: GetDNSConfig(), + + OIDCEndpoint: viper.GetString("oidc_endpoint"), + OIDCClientID: viper.GetString("oidc_client_id"), + OIDCClientSecret: viper.GetString("oidc_client_secret"), } h, err := headscale.NewHeadscale(cfg) diff --git a/go.mod b/go.mod index 8709119b..031460e8 100644 --- a/go.mod +++ b/go.mod @@ -17,8 +17,10 @@ require ( github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/opencontainers/runc v1.0.2 // indirect github.com/ory/dockertest/v3 v3.7.0 + github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pterm/pterm v0.12.30 github.com/rs/zerolog v1.25.0 + github.com/s12v/go-jwks v0.2.1 github.com/spf13/cobra v1.2.1 github.com/spf13/viper v1.8.1 github.com/stretchr/testify v1.7.0 @@ -28,6 +30,7 @@ require ( golang.org/x/net v0.0.0-20210913180222-943fd674d43e // indirect golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c + gopkg.in/square/go-jose.v2 v2.3.1 gopkg.in/yaml.v2 v2.4.0 gorm.io/datatypes v1.0.2 gorm.io/driver/postgres v1.1.1 diff --git a/go.sum b/go.sum index ac934dbe..195fb21d 100644 --- a/go.sum +++ b/go.sum @@ -711,6 +711,8 @@ github.com/ory/dockertest/v3 v3.7.0 h1:Bijzonc69Ont3OU0a3TWKJ1Rzlh3TsDXP1JrTAkSm github.com/ory/dockertest/v3 v3.7.0/go.mod h1:PvCCgnP7AfBZeVrzwiUTjZx/IUXlGLC1zQlUQrLIlUE= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -786,6 +788,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryancurrah/gomodguard v1.1.0/go.mod h1:4O8tr7hBODaGE6VIhfJDHcwzh5GUccKSJBU0UMXJFVM= github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/s12v/go-jwks v0.2.1 h1:2zShofKJoSXztWyh5ASPfpzuQrE+b+Sum9JJdif05Po= +github.com/s12v/go-jwks v0.2.1/go.mod h1:DmmtP4Etd59Y90j8zmTS4z61MKu0QPvgioAXv+mqyjQ= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= @@ -845,6 +849,8 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/square/go-jose v2.5.1+incompatible h1:FC+BwI9FzJZWpKaE0yUhFNbp/CyFHndARzuGVME/LGk= +github.com/square/go-jose v2.5.1+incompatible/go.mod h1:7MxpAF/1WTVUu8Am+T5kNy+t0902CaLWM4Z745MkOa8= github.com/ssgreg/nlreturn/v2 v2.1.0/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -958,6 +964,7 @@ go4.org/mem v0.0.0-20201119185036-c04c5a6ff174/go.mod h1:reUoABIJ9ikfM5sgtSF3Wus go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 h1:1tk03FUNpulq2cuWpXZWj649rwJpk0d20rxWiopKRmc= go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= +golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1021,6 +1028,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180729183719-c4299a1a0d85/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1097,6 +1105,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1436,6 +1445,8 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= diff --git a/oidc.go b/oidc.go new file mode 100644 index 00000000..0006cc32 --- /dev/null +++ b/oidc.go @@ -0,0 +1,310 @@ +package headscale + +import ( + "crypto/rand" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "github.com/gin-gonic/gin" + "github.com/patrickmn/go-cache" + "github.com/rs/zerolog/log" + "github.com/s12v/go-jwks" + "gopkg.in/square/go-jose.v2/jwt" + "gorm.io/gorm" + "io" + "net/http" + "net/url" + "strings" + "time" +) + +type OpenIDConfiguration struct { + Issuer string `json:"issuer"` + AuthorizationEndpoint string `json:"authorization_endpoint"` + TokenEndpoint string `json:"token_endpoint"` + JWKSURI string `json:"jwks_uri"` +} + +type OpenIDTokens struct { + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + IdToken string `json:"id_token"` + NotBeforePolicy int `json:"not-before-policy,omitempty"` + RefreshExpiresIn int `json:"refresh_expires_in"` + RefreshToken string `json:"refresh_token"` + Scope string `json:"scope"` + SessionState string `json:"session_state,omitempty"` + TokenType string `json:"token_type,omitempty"` +} + +type AccessToken struct { + jwt.Claims + Name string `json:"name,omitempty"` + Groups []string `json:"groups,omitempty"` + Email string `json:"email"` + Username string `json:"preferred_username,omitempty"` +} + +var oidcConfig *OpenIDConfiguration +var stateCache *cache.Cache +var jwksSource *jwks.WebSource +var jwksClient jwks.JWKSClient + +func verifyToken(token string) (*AccessToken, error) { + + if jwksClient == nil { + jwksSource = jwks.NewWebSource(oidcConfig.JWKSURI) + jwksClient = jwks.NewDefaultClient( + jwksSource, + time.Hour, // Refresh keys every 1 hour + 12*time.Hour, // Expire keys after 12 hours + ) + } + + //decode jwt + tok, err := jwt.ParseSigned(token) + if err != nil { + return nil, err + } + + if tok.Headers[0].KeyID != "" { + log.Debug().Msgf("Checking KID %s\n", tok.Headers[0].KeyID) + + jwk, err := jwksClient.GetSignatureKey(tok.Headers[0].KeyID) + if err != nil { + return nil, err + } + + claims := AccessToken{} + + err = tok.Claims(jwk.Certificates[0].PublicKey, &claims) + if err != nil { + return nil, err + } else { + + err = claims.Validate(jwt.Expected{ + Time: time.Now(), + }) + if err != nil { + return nil, err + } + + return &claims, nil + } + + } else { + return nil, err + } +} + +func getOIDCConfig(oidcConfigURL string) (*OpenIDConfiguration, error) { + client := &http.Client{} + req, err := http.NewRequest("GET", oidcConfigURL, nil) + if err != nil { + log.Error().Msgf("%v", err) + return nil, err + } + + log.Debug().Msgf("Requesting OIDC Config from %s", oidcConfigURL) + + oidcConfigResp, err := client.Do(req) + if err != nil { + log.Error().Msgf("%v", err) + return nil, err + } + defer oidcConfigResp.Body.Close() + + var oidcConfig OpenIDConfiguration + + err = json.NewDecoder(oidcConfigResp.Body).Decode(&oidcConfig) + if err != nil { + log.Error().Msgf("%v", err) + return nil, err + } + return &oidcConfig, nil +} + +func (h *Headscale) exchangeCodeForTokens(code string, redirectURI string) (*OpenIDTokens, error) { + var err error + + if oidcConfig == nil { + oidcConfig, err = getOIDCConfig(fmt.Sprintf("%s.well-known/openid-configuration", h.cfg.OIDCEndpoint)) + if err != nil { + return nil, err + } + } + + params := url.Values{} + params.Add("grant_type", "authorization_code") + params.Add("code", code) + params.Add("client_id", h.cfg.OIDCClientID) + params.Add("client_secret", h.cfg.OIDCClientSecret) + params.Add("redirect_uri", redirectURI) + + client := &http.Client{} + req, err := http.NewRequest("POST", oidcConfig.TokenEndpoint, strings.NewReader(params.Encode())) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + if err != nil { + log.Error().Msgf("%v", err) + return nil, err + } + + tokenResp, err := client.Do(req) + if err != nil { + log.Error().Msgf("%v", err) + return nil, err + } + defer tokenResp.Body.Close() + + if tokenResp.StatusCode != 200 { + b, _ := io.ReadAll(tokenResp.Body) + log.Error().Msgf("%s", b) + } + + var tokens OpenIDTokens + + err = json.NewDecoder(tokenResp.Body).Decode(&tokens) + if err != nil { + log.Error().Msgf("%v", err) + return nil, err + } + + log.Info().Msg("Successfully exchanged code for tokens") + + return &tokens, nil +} + +// RegisterOIDC redirects to the OIDC provider for authentication +// Puts machine key in cache so the callback can retrieve it using the oidc state param +// Listens in /oidc/register/:mKey +func (h *Headscale) RegisterOIDC(c *gin.Context) { + mKeyStr := c.Param("mKey") + if mKeyStr == "" { + c.String(http.StatusBadRequest, "Wrong params") + return + } + + var err error + + // grab oidc config if it hasn't been already + if oidcConfig == nil { + oidcConfig, err = getOIDCConfig(fmt.Sprintf("%s.well-known/openid-configuration", h.cfg.OIDCEndpoint)) + + if err != nil { + c.String(http.StatusInternalServerError, "Could not retrieve OIDC Config") + return + } + } + + b := make([]byte, 16) + _, err = rand.Read(b) + stateStr := hex.EncodeToString(b)[:32] + + // init the state cache if it hasn't been already + if stateCache == nil { + stateCache = cache.New(time.Minute*5, time.Minute*10) + } + + // place the machine key into the state cache, so it can be retrieved later + stateCache.Set(stateStr, mKeyStr, time.Minute*5) + + params := url.Values{} + params.Add("response_type", "code") + params.Add("client_id", h.cfg.OIDCClientID) + params.Add("redirect_uri", fmt.Sprintf("%s/oidc/callback", h.cfg.ServerURL)) + params.Add("scope", "openid") + params.Add("state", stateStr) + + authUrl := fmt.Sprintf("%s?%s", oidcConfig.AuthorizationEndpoint, params.Encode()) + log.Debug().Msg(authUrl) + + c.Redirect(http.StatusFound, authUrl) +} + +// OIDCCallback handles the callback from the OIDC endpoint +// Retrieves the mkey from the state cache, if the machine is not registered, presents a confirmation +// Listens in /oidc/callback +func (h *Headscale) OIDCCallback(c *gin.Context) { + + code := c.Query("code") + state := c.Query("state") + + if code == "" || state == "" { + c.String(http.StatusBadRequest, "Wrong params") + return + } + + redirectURI := fmt.Sprintf("%s/oidc/callback", h.cfg.ServerURL) + + tokens, err := h.exchangeCodeForTokens(code, redirectURI) + + if err != nil { + c.String(http.StatusBadRequest, "Could not exchange code for token") + return + } + + //verify tokens + claims, err := verifyToken(tokens.AccessToken) + + if err != nil { + c.String(http.StatusBadRequest, "invalid tokens") + return + } + + //retrieve machinekey from state cache + mKeyIf, mKeyFound := stateCache.Get(state) + + if !mKeyFound { + c.String(http.StatusBadRequest, "state has expired") + return + } + mKeyStr, mKeyOK := mKeyIf.(string) + + if !mKeyOK { + c.String(http.StatusInternalServerError, "could not get machine key from cache") + return + } + + // retrieve machine information + var m Machine + if result := h.db.Preload("Namespace").First(&m, "machine_key = ?", mKeyStr); errors.Is(result.Error, gorm.ErrRecordNotFound) { + log.Error().Msg("machine key not found in database") + c.String(http.StatusInternalServerError, "could not get machine info from database") + return + } + + //look for a namespace of the users email for now + if !m.Registered { + + ns, err := h.GetNamespace(claims.Email) + if err != nil { + ns, err = h.CreateNamespace(claims.Email) + } + + ip, err := h.getAvailableIP() + if err != nil { + c.String(http.StatusInternalServerError, "could not get an IP from the pool") + return + } + + m.IPAddress = ip.String() + m.NamespaceID = ns.ID + m.Registered = true + m.RegisterMethod = "oidc" + h.db.Save(&m) + } + + c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(fmt.Sprintf(` + + +

headscale

+

+ Authenticated, you can now close this window. +

+ + + +`))) +} From b22a9781a22a41834dcb87b96b8ae2f87df17d55 Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Sun, 26 Sep 2021 21:12:36 +0800 Subject: [PATCH 02/70] fix linter errors, error out if jwt does not contain a key id --- oidc.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/oidc.go b/oidc.go index 0006cc32..dabd8b03 100644 --- a/oidc.go +++ b/oidc.go @@ -94,7 +94,7 @@ func verifyToken(token string) (*AccessToken, error) { } } else { - return nil, err + return nil, errors.New("JWT does not contain a key id") } } @@ -200,6 +200,13 @@ func (h *Headscale) RegisterOIDC(c *gin.Context) { b := make([]byte, 16) _, err = rand.Read(b) + + if err != nil { + log.Error().Msg("could not read 16 bytes from rand") + c.String(http.StatusInternalServerError, "could not read 16 bytes from rand") + return + } + stateStr := hex.EncodeToString(b)[:32] // init the state cache if it hasn't been already @@ -281,6 +288,13 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { ns, err := h.GetNamespace(claims.Email) if err != nil { ns, err = h.CreateNamespace(claims.Email) + + if err != nil { + log.Error().Msgf("could not create new namespace '%s'", claims.Email) + c.String(http.StatusInternalServerError, "could not create new namespace") + return + } + } ip, err := h.getAvailableIP() @@ -301,10 +315,10 @@ func (h *Headscale) OIDCCallback(c *gin.Context) {

headscale

- Authenticated, you can now close this window. + Authenticated as %s, you can now close this window.

-`))) +`, claims.Email))) } From c487591437afb292701c8a905dc7ce9ad0477562 Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Wed, 6 Oct 2021 17:19:15 +0800 Subject: [PATCH 03/70] use go-oidc instead of verifying and extracting tokens ourselves, rename oidc_endpoint to oidc_issuer to be more inline with spec --- api.go | 4 +- app.go | 6 +- cmd/headscale/cli/utils.go | 2 +- go.mod | 7 +- go.sum | 8 +- machine.go | 5 + oidc.go | 222 ++++++++----------------------------- 7 files changed, 69 insertions(+), 185 deletions(-) diff --git a/api.go b/api.go index 2c5a1321..fb54f3cf 100644 --- a/api.go +++ b/api.go @@ -134,7 +134,7 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { Str("machine", m.Name). Msg("Not registered and not NodeKey rotation. Sending a authurl to register") - if h.cfg.OIDCEndpoint != "" { + if h.cfg.OIDCIssuer != "" { resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", h.cfg.ServerURL, mKey.HexString()) } else { resp.AuthURL = fmt.Sprintf("%s/register?key=%s", @@ -204,7 +204,7 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { Str("handler", "Registration"). Str("machine", m.Name). Msg("The node is sending us a new NodeKey, sending auth url") - if h.cfg.OIDCEndpoint != "" { + if h.cfg.OIDCIssuer != "" { resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", h.cfg.ServerURL, mKey.HexString()) } else { resp.AuthURL = fmt.Sprintf("%s/register?key=%s", diff --git a/app.go b/app.go index 6f24c82b..3c4b307a 100644 --- a/app.go +++ b/app.go @@ -46,7 +46,7 @@ type Config struct { DNSConfig *tailcfg.DNSConfig - OIDCEndpoint string + OIDCIssuer string OIDCClientID string OIDCClientSecret string } @@ -172,11 +172,11 @@ func (h *Headscale) Serve() error { r.GET("/register", h.RegisterWebAPI) r.POST("/machine/:id/map", h.PollNetMapHandler) r.POST("/machine/:id", h.RegistrationHandler) - r.GET("/oidc/register/:mKey", h.RegisterOIDC) + r.GET("/oidc/register/:mkey", h.RegisterOIDC) r.GET("/oidc/callback", h.OIDCCallback) r.GET("/apple", h.AppleMobileConfig) r.GET("/apple/:platform", h.ApplePlatformConfig) - + var err error timeout := 30 * time.Second diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index b7faad57..6ccdcdee 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -171,7 +171,7 @@ func getHeadscaleApp() (*headscale.Headscale, error) { DNSConfig: GetDNSConfig(), - OIDCEndpoint: viper.GetString("oidc_endpoint"), + OIDCIssuer: viper.GetString("oidc_issuer"), OIDCClientID: viper.GetString("oidc_client_id"), OIDCClientSecret: viper.GetString("oidc_client_secret"), } diff --git a/go.mod b/go.mod index c1d4561b..a770338b 100644 --- a/go.mod +++ b/go.mod @@ -7,11 +7,12 @@ require ( github.com/Microsoft/go-winio v0.5.0 // indirect github.com/cenkalti/backoff/v4 v4.1.1 // indirect github.com/containerd/continuity v0.1.0 // indirect + github.com/coreos/go-oidc/v3 v3.1.0 github.com/docker/cli v20.10.8+incompatible // indirect github.com/docker/docker v20.10.8+incompatible // indirect github.com/efekarakus/termcolor v1.0.1 github.com/gin-gonic/gin v1.7.4 - github.com/gofrs/uuid v4.0.0+incompatible // indirect + github.com/gofrs/uuid v4.0.0+incompatible github.com/google/go-github v17.0.0+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b @@ -28,13 +29,13 @@ require ( github.com/spf13/viper v1.8.1 github.com/stretchr/testify v1.7.0 github.com/tailscale/hujson v0.0.0-20210818175511-7360507a6e88 - github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e // indirect + github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 golang.org/x/net v0.0.0-20210913180222-943fd674d43e // indirect + golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c - gopkg.in/square/go-jose.v2 v2.3.1 gopkg.in/yaml.v2 v2.4.0 gorm.io/datatypes v1.0.2 gorm.io/driver/postgres v1.1.1 diff --git a/go.sum b/go.sum index 9a76d176..fc498e75 100644 --- a/go.sum +++ b/go.sum @@ -143,6 +143,8 @@ github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkE github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= +github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw= +github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -1067,6 +1069,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -1101,6 +1104,7 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 h1:0Ja1LBD+yisY6RWM/BH7TJVXWsSjs2VwBSmvSX4HdBc= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1355,6 +1359,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1452,8 +1457,9 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= diff --git a/machine.go b/machine.go index 1d4939c1..b5c821f5 100644 --- a/machine.go +++ b/machine.go @@ -50,6 +50,11 @@ func (m Machine) isAlreadyRegistered() bool { return m.Registered } +// isExpired returns whether the machine registration has expired +func (m Machine) isExpired() bool { + return time.Now().UTC().After(*m.Expiry) +} + // toNode converts a Machine into a Tailscale Node. includeRoutes is false for shared nodes // as per the expected behaviour in the official SaaS func (m Machine) toNode(includeRoutes bool) (*tailcfg.Node, error) { diff --git a/oidc.go b/oidc.go index dabd8b03..aa80911b 100644 --- a/oidc.go +++ b/oidc.go @@ -1,186 +1,37 @@ package headscale import ( + "context" "crypto/rand" "encoding/hex" - "encoding/json" "errors" "fmt" + "github.com/coreos/go-oidc/v3/oidc" "github.com/gin-gonic/gin" "github.com/patrickmn/go-cache" "github.com/rs/zerolog/log" - "github.com/s12v/go-jwks" - "gopkg.in/square/go-jose.v2/jwt" + "golang.org/x/oauth2" "gorm.io/gorm" - "io" "net/http" - "net/url" - "strings" "time" ) -type OpenIDConfiguration struct { - Issuer string `json:"issuer"` - AuthorizationEndpoint string `json:"authorization_endpoint"` - TokenEndpoint string `json:"token_endpoint"` - JWKSURI string `json:"jwks_uri"` -} - -type OpenIDTokens struct { - AccessToken string `json:"access_token"` - ExpiresIn int `json:"expires_in"` - IdToken string `json:"id_token"` - NotBeforePolicy int `json:"not-before-policy,omitempty"` - RefreshExpiresIn int `json:"refresh_expires_in"` - RefreshToken string `json:"refresh_token"` - Scope string `json:"scope"` - SessionState string `json:"session_state,omitempty"` - TokenType string `json:"token_type,omitempty"` -} - -type AccessToken struct { - jwt.Claims +type IDTokenClaims struct { Name string `json:"name,omitempty"` Groups []string `json:"groups,omitempty"` Email string `json:"email"` Username string `json:"preferred_username,omitempty"` } -var oidcConfig *OpenIDConfiguration +var oidcProvider *oidc.Provider +var oauth2Config *oauth2.Config var stateCache *cache.Cache -var jwksSource *jwks.WebSource -var jwksClient jwks.JWKSClient - -func verifyToken(token string) (*AccessToken, error) { - - if jwksClient == nil { - jwksSource = jwks.NewWebSource(oidcConfig.JWKSURI) - jwksClient = jwks.NewDefaultClient( - jwksSource, - time.Hour, // Refresh keys every 1 hour - 12*time.Hour, // Expire keys after 12 hours - ) - } - - //decode jwt - tok, err := jwt.ParseSigned(token) - if err != nil { - return nil, err - } - - if tok.Headers[0].KeyID != "" { - log.Debug().Msgf("Checking KID %s\n", tok.Headers[0].KeyID) - - jwk, err := jwksClient.GetSignatureKey(tok.Headers[0].KeyID) - if err != nil { - return nil, err - } - - claims := AccessToken{} - - err = tok.Claims(jwk.Certificates[0].PublicKey, &claims) - if err != nil { - return nil, err - } else { - - err = claims.Validate(jwt.Expected{ - Time: time.Now(), - }) - if err != nil { - return nil, err - } - - return &claims, nil - } - - } else { - return nil, errors.New("JWT does not contain a key id") - } -} - -func getOIDCConfig(oidcConfigURL string) (*OpenIDConfiguration, error) { - client := &http.Client{} - req, err := http.NewRequest("GET", oidcConfigURL, nil) - if err != nil { - log.Error().Msgf("%v", err) - return nil, err - } - - log.Debug().Msgf("Requesting OIDC Config from %s", oidcConfigURL) - - oidcConfigResp, err := client.Do(req) - if err != nil { - log.Error().Msgf("%v", err) - return nil, err - } - defer oidcConfigResp.Body.Close() - - var oidcConfig OpenIDConfiguration - - err = json.NewDecoder(oidcConfigResp.Body).Decode(&oidcConfig) - if err != nil { - log.Error().Msgf("%v", err) - return nil, err - } - return &oidcConfig, nil -} - -func (h *Headscale) exchangeCodeForTokens(code string, redirectURI string) (*OpenIDTokens, error) { - var err error - - if oidcConfig == nil { - oidcConfig, err = getOIDCConfig(fmt.Sprintf("%s.well-known/openid-configuration", h.cfg.OIDCEndpoint)) - if err != nil { - return nil, err - } - } - - params := url.Values{} - params.Add("grant_type", "authorization_code") - params.Add("code", code) - params.Add("client_id", h.cfg.OIDCClientID) - params.Add("client_secret", h.cfg.OIDCClientSecret) - params.Add("redirect_uri", redirectURI) - - client := &http.Client{} - req, err := http.NewRequest("POST", oidcConfig.TokenEndpoint, strings.NewReader(params.Encode())) - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - - if err != nil { - log.Error().Msgf("%v", err) - return nil, err - } - - tokenResp, err := client.Do(req) - if err != nil { - log.Error().Msgf("%v", err) - return nil, err - } - defer tokenResp.Body.Close() - - if tokenResp.StatusCode != 200 { - b, _ := io.ReadAll(tokenResp.Body) - log.Error().Msgf("%s", b) - } - - var tokens OpenIDTokens - - err = json.NewDecoder(tokenResp.Body).Decode(&tokens) - if err != nil { - log.Error().Msgf("%v", err) - return nil, err - } - - log.Info().Msg("Successfully exchanged code for tokens") - - return &tokens, nil -} // RegisterOIDC redirects to the OIDC provider for authentication // Puts machine key in cache so the callback can retrieve it using the oidc state param // Listens in /oidc/register/:mKey func (h *Headscale) RegisterOIDC(c *gin.Context) { - mKeyStr := c.Param("mKey") + mKeyStr := c.Param("mkey") if mKeyStr == "" { c.String(http.StatusBadRequest, "Wrong params") return @@ -189,13 +40,23 @@ func (h *Headscale) RegisterOIDC(c *gin.Context) { var err error // grab oidc config if it hasn't been already - if oidcConfig == nil { - oidcConfig, err = getOIDCConfig(fmt.Sprintf("%s.well-known/openid-configuration", h.cfg.OIDCEndpoint)) + if oauth2Config == nil { + oidcProvider, err = oidc.NewProvider(context.Background(), h.cfg.OIDCIssuer) if err != nil { + log.Error().Msgf("Could not retrieve OIDC Config: %s", err.Error()) c.String(http.StatusInternalServerError, "Could not retrieve OIDC Config") return } + + oauth2Config = &oauth2.Config{ + ClientID: h.cfg.OIDCClientID, + ClientSecret: h.cfg.OIDCClientSecret, + Endpoint: oidcProvider.Endpoint(), + RedirectURL: fmt.Sprintf("%s/oidc/callback", h.cfg.ServerURL), + Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, + } + } b := make([]byte, 16) @@ -217,21 +78,16 @@ func (h *Headscale) RegisterOIDC(c *gin.Context) { // place the machine key into the state cache, so it can be retrieved later stateCache.Set(stateStr, mKeyStr, time.Minute*5) - params := url.Values{} - params.Add("response_type", "code") - params.Add("client_id", h.cfg.OIDCClientID) - params.Add("redirect_uri", fmt.Sprintf("%s/oidc/callback", h.cfg.ServerURL)) - params.Add("scope", "openid") - params.Add("state", stateStr) - - authUrl := fmt.Sprintf("%s?%s", oidcConfig.AuthorizationEndpoint, params.Encode()) - log.Debug().Msg(authUrl) + authUrl := oauth2Config.AuthCodeURL(stateStr) + log.Debug().Msgf("Redirecting to %s for authentication", authUrl) c.Redirect(http.StatusFound, authUrl) } // OIDCCallback handles the callback from the OIDC endpoint -// Retrieves the mkey from the state cache, if the machine is not registered, presents a confirmation +// Retrieves the mkey from the state cache and adds the machine to the users email namespace +// TODO: A confirmation page for new machines should be added to avoid phishing vulnerabilities +// TODO: Add groups information from OIDC tokens into machine HostInfo // Listens in /oidc/callback func (h *Headscale) OIDCCallback(c *gin.Context) { @@ -243,20 +99,36 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { return } - redirectURI := fmt.Sprintf("%s/oidc/callback", h.cfg.ServerURL) - - tokens, err := h.exchangeCodeForTokens(code, redirectURI) - + oauth2Token, err := oauth2Config.Exchange(context.Background(), code) if err != nil { c.String(http.StatusBadRequest, "Could not exchange code for token") return } - //verify tokens - claims, err := verifyToken(tokens.AccessToken) + rawIDToken, rawIDTokenOK := oauth2Token.Extra("id_token").(string) + if !rawIDTokenOK { + c.String(http.StatusBadRequest, "Could not extract ID Token") + return + } + verifier := oidcProvider.Verifier(&oidc.Config{ClientID: h.cfg.OIDCClientID}) + + idToken, err := verifier.Verify(context.Background(), rawIDToken) if err != nil { - c.String(http.StatusBadRequest, "invalid tokens") + c.String(http.StatusBadRequest, "Failed to verify id token: %s", err.Error()) + return + } + + //userInfo, err := oidcProvider.UserInfo(context.Background(), oauth2.StaticTokenSource(oauth2Token)) + //if err != nil { + // c.String(http.StatusBadRequest, "Failed to retrieve userinfo: "+err.Error()) + // return + //} + + // Extract custom claims + var claims IDTokenClaims + if err = idToken.Claims(&claims); err != nil { + c.String(http.StatusBadRequest, "Failed to decode id token claims: "+err.Error()) return } From 35795c79c367c80590deefe51415bc364f82a024 Mon Sep 17 00:00:00 2001 From: unreality Date: Fri, 8 Oct 2021 15:26:31 +0800 Subject: [PATCH 04/70] Handle trailing slash on uris Co-authored-by: Kristoffer Dalby --- api.go | 8 ++++---- oidc.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api.go b/api.go index fb54f3cf..ddabd937 100644 --- a/api.go +++ b/api.go @@ -135,10 +135,10 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { Msg("Not registered and not NodeKey rotation. Sending a authurl to register") if h.cfg.OIDCIssuer != "" { - resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", h.cfg.ServerURL, mKey.HexString()) + resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", strings.TrimSuffix(h.cfg.ServerURL, "/"), mKey.HexString()) } else { resp.AuthURL = fmt.Sprintf("%s/register?key=%s", - h.cfg.ServerURL, mKey.HexString()) + strings.TrimSuffix(h.cfg.ServerURL, "/"), mKey.HexString()) } respBody, err := encode(resp, &mKey, h.privateKey) if err != nil { @@ -205,10 +205,10 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { Str("machine", m.Name). Msg("The node is sending us a new NodeKey, sending auth url") if h.cfg.OIDCIssuer != "" { - resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", h.cfg.ServerURL, mKey.HexString()) + resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", strings.TrimSuffix(h.cfg.ServerURL, "/"), mKey.HexString()) } else { resp.AuthURL = fmt.Sprintf("%s/register?key=%s", - h.cfg.ServerURL, mKey.HexString()) + strings.TrimSuffix(h.cfg.ServerURL, "/"), mKey.HexString()) } respBody, err := encode(resp, &mKey, h.privateKey) if err != nil { diff --git a/oidc.go b/oidc.go index aa80911b..328731ec 100644 --- a/oidc.go +++ b/oidc.go @@ -53,7 +53,7 @@ func (h *Headscale) RegisterOIDC(c *gin.Context) { ClientID: h.cfg.OIDCClientID, ClientSecret: h.cfg.OIDCClientSecret, Endpoint: oidcProvider.Endpoint(), - RedirectURL: fmt.Sprintf("%s/oidc/callback", h.cfg.ServerURL), + RedirectURL: fmt.Sprintf("%s/oidc/callback", strings.TrimSuffix(h.cfg.ServerURL, "/")), Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, } From e407d423d44c208d0989f9ec8e94ca29909a6f16 Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Fri, 8 Oct 2021 17:43:52 +0800 Subject: [PATCH 05/70] updates from code review --- api.go | 56 +++++++++++++++++++++--- app.go | 17 ++++++++ cmd/headscale/cli/utils.go | 13 ++++++ oidc.go | 88 ++++++++++++++++++++++---------------- 4 files changed, 131 insertions(+), 43 deletions(-) diff --git a/api.go b/api.go index ddabd937..02f28919 100644 --- a/api.go +++ b/api.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "net/http" + "strings" "time" "github.com/rs/zerolog/log" @@ -83,7 +84,7 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { if result := h.db.Preload("Namespace").First(&m, "machine_key = ?", mKey.HexString()); errors.Is(result.Error, gorm.ErrRecordNotFound) { log.Info().Str("machine", req.Hostinfo.Hostname).Msg("New machine") m = Machine{ - Expiry: &req.Expiry, + Expiry: &time.Time{}, MachineKey: mKey.HexString(), Name: req.Hostinfo.Hostname, NodeKey: wgkey.Key(req.NodeKey).HexString(), @@ -107,7 +108,33 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { // We have the updated key! if m.NodeKey == wgkey.Key(req.NodeKey).HexString() { - if m.Registered { + + if !req.Expiry.IsZero() && req.Expiry.UTC().Before(now) { + log.Debug(). + Str("handler", "Registration"). + Str("machine", m.Name). + Msg("Client requested logout") + + m.Expiry = &req.Expiry + h.db.Save(&m) + + resp.AuthURL = "" + resp.MachineAuthorized = false + resp.User = *m.Namespace.toUser() + respBody, err := encode(resp, &mKey, h.privateKey) + if err != nil { + log.Error(). + Str("handler", "Registration"). + Err(err). + Msg("Cannot encode message") + c.String(http.StatusInternalServerError, "") + return + } + c.Data(200, "application/json; charset=utf-8", respBody) + return + } + + if m.Registered && m.Expiry.UTC().After(now) { log.Debug(). Str("handler", "Registration"). Str("machine", m.Name). @@ -132,14 +159,19 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { log.Debug(). Str("handler", "Registration"). Str("machine", m.Name). - Msg("Not registered and not NodeKey rotation. Sending a authurl to register") + Msg("Not registered (or expired) and not NodeKey rotation. Sending a authurl to register") if h.cfg.OIDCIssuer != "" { - resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", strings.TrimSuffix(h.cfg.ServerURL, "/"), mKey.HexString()) + resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", + strings.TrimSuffix(h.cfg.ServerURL, "/"), mKey.HexString()) } else { resp.AuthURL = fmt.Sprintf("%s/register?key=%s", strings.TrimSuffix(h.cfg.ServerURL, "/"), mKey.HexString()) } + + m.Expiry = &req.Expiry // save the requested expiry time for retrieval later + h.db.Save(&m) + respBody, err := encode(resp, &mKey, h.privateKey) if err != nil { log.Error(). @@ -153,8 +185,8 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { return } - // The NodeKey we have matches OldNodeKey, which means this is a refresh after an key expiration - if m.NodeKey == wgkey.Key(req.OldNodeKey).HexString() { + // The NodeKey we have matches OldNodeKey, which means this is a refresh after a key expiration + if m.NodeKey == wgkey.Key(req.OldNodeKey).HexString() && m.Expiry.UTC().After(now) { log.Debug(). Str("handler", "Registration"). Str("machine", m.Name). @@ -179,14 +211,19 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { // We arrive here after a client is restarted without finalizing the authentication flow or // when headscale is stopped in the middle of the auth process. - if m.Registered { + if m.Registered && m.Expiry.UTC().After(now) { log.Debug(). Str("handler", "Registration"). Str("machine", m.Name). Msg("The node is sending us a new NodeKey, but machine is registered. All clear for /map") + + m.NodeKey = wgkey.Key(req.NodeKey).HexString() + h.db.Save(&m) + resp.AuthURL = "" resp.MachineAuthorized = true resp.User = *m.Namespace.toUser() + respBody, err := encode(resp, &mKey, h.privateKey) if err != nil { log.Error(). @@ -210,6 +247,11 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { resp.AuthURL = fmt.Sprintf("%s/register?key=%s", strings.TrimSuffix(h.cfg.ServerURL, "/"), mKey.HexString()) } + + m.Expiry = &req.Expiry // save the requested expiry time for retrieval later + m.NodeKey = wgkey.Key(req.NodeKey).HexString() // save the new nodekey + h.db.Save(&m) + respBody, err := encode(resp, &mKey, h.privateKey) if err != nil { log.Error(). diff --git a/app.go b/app.go index 3c4b307a..2ad72154 100644 --- a/app.go +++ b/app.go @@ -3,6 +3,9 @@ package headscale import ( "errors" "fmt" + "github.com/coreos/go-oidc/v3/oidc" + "github.com/patrickmn/go-cache" + "golang.org/x/oauth2" "net/http" "os" "strings" @@ -49,6 +52,9 @@ type Config struct { OIDCIssuer string OIDCClientID string OIDCClientSecret string + + MaxMachineExpiry time.Duration + DefaultMachineExpiry time.Duration } // Headscale represents the base app of the service @@ -68,6 +74,10 @@ type Headscale struct { clientsUpdateChannelMutex sync.Mutex lastStateChange sync.Map + + oidcProvider *oidc.Provider + oauth2Config *oauth2.Config + oidcStateCache *cache.Cache } // NewHeadscale returns the Headscale app @@ -107,6 +117,13 @@ func NewHeadscale(cfg Config) (*Headscale, error) { return nil, err } + if cfg.OIDCIssuer != "" { + err = h.initOIDC() + if err != nil { + return nil, err + } + } + return &h, nil } diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 6ccdcdee..67017aa0 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -144,6 +144,16 @@ func getHeadscaleApp() (*headscale.Headscale, error) { return nil, err } + maxMachineExpiry, _ := time.ParseDuration("8h") + if viper.GetDuration("max_machine_expiry") >= time.Second { + maxMachineExpiry = viper.GetDuration("max_machine_expiry") + } + + defaultMachineExpiry, _ := time.ParseDuration("8h") + if viper.GetDuration("default_machine_expiry") >= time.Second { + defaultMachineExpiry = viper.GetDuration("default_machine_expiry") + } + cfg := headscale.Config{ ServerURL: viper.GetString("server_url"), Addr: viper.GetString("listen_addr"), @@ -174,6 +184,9 @@ func getHeadscaleApp() (*headscale.Headscale, error) { OIDCIssuer: viper.GetString("oidc_issuer"), OIDCClientID: viper.GetString("oidc_client_id"), OIDCClientSecret: viper.GetString("oidc_client_secret"), + + MaxMachineExpiry: maxMachineExpiry, + DefaultMachineExpiry: defaultMachineExpiry, } h, err := headscale.NewHeadscale(cfg) diff --git a/oidc.go b/oidc.go index 328731ec..1220098c 100644 --- a/oidc.go +++ b/oidc.go @@ -13,6 +13,7 @@ import ( "golang.org/x/oauth2" "gorm.io/gorm" "net/http" + "strings" "time" ) @@ -23,9 +24,33 @@ type IDTokenClaims struct { Username string `json:"preferred_username,omitempty"` } -var oidcProvider *oidc.Provider -var oauth2Config *oauth2.Config -var stateCache *cache.Cache +func (h *Headscale) initOIDC() error { + var err error + // grab oidc config if it hasn't been already + if h.oauth2Config == nil { + h.oidcProvider, err = oidc.NewProvider(context.Background(), h.cfg.OIDCIssuer) + + if err != nil { + log.Error().Msgf("Could not retrieve OIDC Config: %s", err.Error()) + return err + } + + h.oauth2Config = &oauth2.Config{ + ClientID: h.cfg.OIDCClientID, + ClientSecret: h.cfg.OIDCClientSecret, + Endpoint: h.oidcProvider.Endpoint(), + RedirectURL: fmt.Sprintf("%s/oidc/callback", strings.TrimSuffix(h.cfg.ServerURL, "/")), + Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, + } + } + + // init the state cache if it hasn't been already + if h.oidcStateCache == nil { + h.oidcStateCache = cache.New(time.Minute*5, time.Minute*10) + } + + return nil +} // RegisterOIDC redirects to the OIDC provider for authentication // Puts machine key in cache so the callback can retrieve it using the oidc state param @@ -37,30 +62,8 @@ func (h *Headscale) RegisterOIDC(c *gin.Context) { return } - var err error - - // grab oidc config if it hasn't been already - if oauth2Config == nil { - oidcProvider, err = oidc.NewProvider(context.Background(), h.cfg.OIDCIssuer) - - if err != nil { - log.Error().Msgf("Could not retrieve OIDC Config: %s", err.Error()) - c.String(http.StatusInternalServerError, "Could not retrieve OIDC Config") - return - } - - oauth2Config = &oauth2.Config{ - ClientID: h.cfg.OIDCClientID, - ClientSecret: h.cfg.OIDCClientSecret, - Endpoint: oidcProvider.Endpoint(), - RedirectURL: fmt.Sprintf("%s/oidc/callback", strings.TrimSuffix(h.cfg.ServerURL, "/")), - Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, - } - - } - b := make([]byte, 16) - _, err = rand.Read(b) + _, err := rand.Read(b) if err != nil { log.Error().Msg("could not read 16 bytes from rand") @@ -70,15 +73,10 @@ func (h *Headscale) RegisterOIDC(c *gin.Context) { stateStr := hex.EncodeToString(b)[:32] - // init the state cache if it hasn't been already - if stateCache == nil { - stateCache = cache.New(time.Minute*5, time.Minute*10) - } - // place the machine key into the state cache, so it can be retrieved later - stateCache.Set(stateStr, mKeyStr, time.Minute*5) + h.oidcStateCache.Set(stateStr, mKeyStr, time.Minute*5) - authUrl := oauth2Config.AuthCodeURL(stateStr) + authUrl := h.oauth2Config.AuthCodeURL(stateStr) log.Debug().Msgf("Redirecting to %s for authentication", authUrl) c.Redirect(http.StatusFound, authUrl) @@ -99,7 +97,7 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { return } - oauth2Token, err := oauth2Config.Exchange(context.Background(), code) + oauth2Token, err := h.oauth2Config.Exchange(context.Background(), code) if err != nil { c.String(http.StatusBadRequest, "Could not exchange code for token") return @@ -111,7 +109,7 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { return } - verifier := oidcProvider.Verifier(&oidc.Config{ClientID: h.cfg.OIDCClientID}) + verifier := h.oidcProvider.Verifier(&oidc.Config{ClientID: h.cfg.OIDCClientID}) idToken, err := verifier.Verify(context.Background(), rawIDToken) if err != nil { @@ -133,7 +131,7 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { } //retrieve machinekey from state cache - mKeyIf, mKeyFound := stateCache.Get(state) + mKeyIf, mKeyFound := h.oidcStateCache.Get(state) if !mKeyFound { c.String(http.StatusBadRequest, "state has expired") @@ -157,6 +155,8 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { //look for a namespace of the users email for now if !m.Registered { + log.Debug().Msg("Registering new machine after successful callback") + ns, err := h.GetNamespace(claims.Email) if err != nil { ns, err = h.CreateNamespace(claims.Email) @@ -182,6 +182,22 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { h.db.Save(&m) } + if m.isExpired() { + maxExpiry := time.Now().UTC().Add(h.cfg.MaxMachineExpiry) + + // use the maximum expiry if it's sooner than the requested expiry + if maxExpiry.Before(*m.Expiry) { + log.Debug().Msgf("Clamping expiry time to maximum: %v (%v)", maxExpiry, h.cfg.MaxMachineExpiry) + m.Expiry = &maxExpiry + h.db.Save(&m) + } else if m.Expiry.IsZero() { + log.Debug().Msgf("Using default machine expiry time: %v (%v)", maxExpiry, h.cfg.MaxMachineExpiry) + defaultExpiry := time.Now().UTC().Add(h.cfg.DefaultMachineExpiry) + m.Expiry = &defaultExpiry + h.db.Save(&m) + } + } + c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(fmt.Sprintf(` From 74e6c1479e64ea13e49fbb4ca87f668dd14068ab Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Sun, 10 Oct 2021 17:22:42 +0800 Subject: [PATCH 06/70] updates from code review --- api.go | 71 +++++++++++++------------------------- app.go | 4 +-- cli.go | 3 ++ cmd/headscale/cli/utils.go | 18 +++++----- go.mod | 6 ++-- machine.go | 30 ++++++++++++++-- oidc.go | 43 ++++++++++------------- 7 files changed, 88 insertions(+), 87 deletions(-) diff --git a/api.go b/api.go index a70df5b3..bda9d9bd 100644 --- a/api.go +++ b/api.go @@ -65,7 +65,7 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { Str("handler", "Registration"). Err(err). Msg("Cannot parse machine key") - machineRegistrations.WithLabelValues("unkown", "web", "error", "unknown").Inc() + machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc() c.String(http.StatusInternalServerError, "Sad!") return } @@ -76,34 +76,33 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { Str("handler", "Registration"). Err(err). Msg("Cannot decode message") - machineRegistrations.WithLabelValues("unkown", "web", "error", "unknown").Inc() + machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc() c.String(http.StatusInternalServerError, "Very sad!") return } now := time.Now().UTC() - var m Machine - if result := h.db.Preload("Namespace").First(&m, "machine_key = ?", mKey.HexString()); errors.Is(result.Error, gorm.ErrRecordNotFound) { + m, err := h.GetMachineByMachineKey(mKey.HexString()) + if errors.Is(err, gorm.ErrRecordNotFound) { log.Info().Str("machine", req.Hostinfo.Hostname).Msg("New machine") - m = Machine{ - Expiry: &time.Time{}, - MachineKey: mKey.HexString(), - Name: req.Hostinfo.Hostname, - NodeKey: wgkey.Key(req.NodeKey).HexString(), - LastSuccessfulUpdate: &now, + newMachine := Machine{ + Expiry: &time.Time{}, + MachineKey: mKey.HexString(), + Name: req.Hostinfo.Hostname, } - if err := h.db.Create(&m).Error; err != nil { + if err := h.db.Create(&newMachine).Error; err != nil { log.Error(). Str("handler", "Registration"). Err(err). Msg("Could not create row") - machineRegistrations.WithLabelValues("unkown", "web", "error", m.Namespace.Name).Inc() + machineRegistrations.WithLabelValues("unknown", "web", "error", m.Namespace.Name).Inc() return } + m = &newMachine } if !m.Registered && req.Auth.AuthKey != "" { - h.handleAuthKey(c, h.db, mKey, req, m) + h.handleAuthKey(c, h.db, mKey, req, *m) return } @@ -112,13 +111,14 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { // We have the updated key! if m.NodeKey == wgkey.Key(req.NodeKey).HexString() { + // The client sends an Expiry in the past if the client is requesting a logout if !req.Expiry.IsZero() && req.Expiry.UTC().Before(now) { - log.Debug(). + log.Info(). Str("handler", "Registration"). Str("machine", m.Name). Msg("Client requested logout") - m.Expiry = &req.Expiry + m.Expiry = &req.Expiry // save the expiry so that the machine is marked as expired h.db.Save(&m) resp.AuthURL = "" @@ -138,6 +138,7 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { } if m.Registered && m.Expiry.UTC().After(now) { + // The machine registration is valid, respond with redirect to /map log.Debug(). Str("handler", "Registration"). Str("machine", m.Name). @@ -161,10 +162,11 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { return } + // The client has registered before, but has expired log.Debug(). Str("handler", "Registration"). Str("machine", m.Name). - Msg("Not registered (or expired) and not NodeKey rotation. Sending a authurl to register") + Msg("Machine registration has expired. Sending a authurl to register") if h.cfg.OIDCIssuer != "" { resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", @@ -174,7 +176,7 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { strings.TrimSuffix(h.cfg.ServerURL, "/"), mKey.HexString()) } - m.Expiry = &req.Expiry // save the requested expiry time for retrieval later + m.RequestedExpiry = &req.Expiry // save the requested expiry time for retrieval later in the authentication flow h.db.Save(&m) respBody, err := encode(resp, &mKey, h.privateKey) @@ -216,34 +218,7 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { return } - // We arrive here after a client is restarted without finalizing the authentication flow or - // when headscale is stopped in the middle of the auth process. - if m.Registered && m.Expiry.UTC().After(now) { - log.Debug(). - Str("handler", "Registration"). - Str("machine", m.Name). - Msg("The node is sending us a new NodeKey, but machine is registered. All clear for /map") - - m.NodeKey = wgkey.Key(req.NodeKey).HexString() - h.db.Save(&m) - - resp.AuthURL = "" - resp.MachineAuthorized = true - resp.User = *m.Namespace.toUser() - - respBody, err := encode(resp, &mKey, h.privateKey) - if err != nil { - log.Error(). - Str("handler", "Registration"). - Err(err). - Msg("Cannot encode message") - c.String(http.StatusInternalServerError, "") - return - } - c.Data(200, "application/json; charset=utf-8", respBody) - return - } - + // The machine registration is new, redirect the client to the registration URL log.Debug(). Str("handler", "Registration"). Str("machine", m.Name). @@ -255,8 +230,8 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { strings.TrimSuffix(h.cfg.ServerURL, "/"), mKey.HexString()) } - m.Expiry = &req.Expiry // save the requested expiry time for retrieval later - m.NodeKey = wgkey.Key(req.NodeKey).HexString() // save the new nodekey + m.RequestedExpiry = &req.Expiry // save the requested expiry time for retrieval later in the authentication flow + m.NodeKey = wgkey.Key(req.NodeKey).HexString() // save the NodeKey h.db.Save(&m) respBody, err := encode(resp, &mKey, h.privateKey) @@ -436,6 +411,8 @@ func (h *Headscale) handleAuthKey(c *gin.Context, db *gorm.DB, idKey wgkey.Key, m.RegisterMethod = "authKey" db.Save(&m) + h.updateMachineExpiry(&m) // TODO: do we want to do different expiry times for AuthKeys? + resp.MachineAuthorized = true resp.User = *pak.Namespace.toUser() respBody, err := encode(resp, &idKey, h.privateKey) diff --git a/app.go b/app.go index 9e688fe1..239998c2 100644 --- a/app.go +++ b/app.go @@ -59,8 +59,8 @@ type Config struct { OIDCClientID string OIDCClientSecret string - MaxMachineExpiry time.Duration - DefaultMachineExpiry time.Duration + MaxMachineRegistrationDuration time.Duration + DefaultMachineRegistrationDuration time.Duration } // Headscale represents the base app of the service diff --git a/cli.go b/cli.go index 9c5b66e5..8610b334 100644 --- a/cli.go +++ b/cli.go @@ -23,6 +23,8 @@ func (h *Headscale) RegisterMachine(key string, namespace string) (*Machine, err return nil, errors.New("Machine not found") } + h.updateMachineExpiry(&m) // update the machine's expiry before bailing if its already registered + if m.isAlreadyRegistered() { return nil, errors.New("Machine already registered") } @@ -36,5 +38,6 @@ func (h *Headscale) RegisterMachine(key string, namespace string) (*Machine, err m.Registered = true m.RegisterMethod = "cli" h.db.Save(&m) + return &m, nil } diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 17bc37e7..366e9597 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -144,14 +144,16 @@ func getHeadscaleApp() (*headscale.Headscale, error) { return nil, err } - maxMachineExpiry, _ := time.ParseDuration("8h") - if viper.GetDuration("max_machine_expiry") >= time.Second { - maxMachineExpiry = viper.GetDuration("max_machine_expiry") + // maxMachineRegistrationDuration is the maximum time a client can request for a client registration + maxMachineRegistrationDuration, _ := time.ParseDuration("10h") + if viper.GetDuration("max_machine_registration_duration") >= time.Second { + maxMachineRegistrationDuration = viper.GetDuration("max_machine_registration_duration") } - defaultMachineExpiry, _ := time.ParseDuration("8h") - if viper.GetDuration("default_machine_expiry") >= time.Second { - defaultMachineExpiry = viper.GetDuration("default_machine_expiry") + // defaultMachineRegistrationDuration is the default time assigned to a client registration if one is not specified by the client + defaultMachineRegistrationDuration, _ := time.ParseDuration("8h") + if viper.GetDuration("default_machine_registration_duration") >= time.Second { + defaultMachineRegistrationDuration = viper.GetDuration("default_machine_registration_duration") } cfg := headscale.Config{ @@ -188,8 +190,8 @@ func getHeadscaleApp() (*headscale.Headscale, error) { OIDCClientID: viper.GetString("oidc_client_id"), OIDCClientSecret: viper.GetString("oidc_client_secret"), - MaxMachineExpiry: maxMachineExpiry, - DefaultMachineExpiry: defaultMachineExpiry, + MaxMachineRegistrationDuration: maxMachineRegistrationDuration, // the maximum duration a client may request for expiry time + DefaultMachineRegistrationDuration: defaultMachineRegistrationDuration, // if a client does not request a specific expiry time, use this duration } h, err := headscale.NewHeadscale(cfg) diff --git a/go.mod b/go.mod index 7e137e19..5a116bb4 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/docker/cli v20.10.8+incompatible // indirect github.com/docker/docker v20.10.8+incompatible // indirect github.com/efekarakus/termcolor v1.0.1 - github.com/fatih/set v0.2.1 // indirect + github.com/fatih/set v0.2.1 github.com/gin-gonic/gin v1.7.4 github.com/gofrs/uuid v4.0.0+incompatible github.com/google/go-github v17.0.0+incompatible // indirect @@ -23,7 +23,7 @@ require ( github.com/opencontainers/runc v1.0.2 // indirect github.com/ory/dockertest/v3 v3.7.0 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/prometheus/client_golang v1.11.0 // indirect + github.com/prometheus/client_golang v1.11.0 github.com/pterm/pterm v0.12.30 github.com/rs/zerolog v1.25.0 github.com/s12v/go-jwks v0.2.1 @@ -33,7 +33,7 @@ require ( github.com/tailscale/hujson v0.0.0-20210818175511-7360507a6e88 github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect - github.com/zsais/go-gin-prometheus v0.1.0 // indirect + github.com/zsais/go-gin-prometheus v0.1.0 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 golang.org/x/net v0.0.0-20210913180222-943fd674d43e // indirect golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 diff --git a/machine.go b/machine.go index bd5caf0d..6eecbc6f 100644 --- a/machine.go +++ b/machine.go @@ -36,6 +36,7 @@ type Machine struct { LastSeen *time.Time LastSuccessfulUpdate *time.Time Expiry *time.Time + RequestedExpiry *time.Time // when a client connects, it may request a specific expiry time, use this field to store it HostInfo datatypes.JSON Endpoints datatypes.JSON @@ -59,8 +60,33 @@ func (m Machine) isAlreadyRegistered() bool { // isExpired returns whether the machine registration has expired func (m Machine) isExpired() bool { return time.Now().UTC().After(*m.Expiry) -} - +} + +// If the Machine is expired, updateMachineExpiry updates the Machine Expiry time to the maximum allowed duration, +// or the default duration if no Expiry time was requested by the client +func (h *Headscale) updateMachineExpiry(m *Machine) { + + if m.isExpired() { + now := time.Now().UTC() + maxExpiry := now.Add(h.cfg.MaxMachineRegistrationDuration) // calculate the maximum expiry + defaultExpiry := now.Add(h.cfg.DefaultMachineRegistrationDuration) // calculate the default expiry + + // clamp the expiry time of the machine registration to the maximum allowed, or use the default if none supplied + if maxExpiry.Before(*m.RequestedExpiry) { + log.Debug().Msgf("Clamping registration expiry time to maximum: %v (%v)", maxExpiry, h.cfg.MaxMachineRegistrationDuration) + m.Expiry = &maxExpiry + } else if m.RequestedExpiry.IsZero() { + log.Debug().Msgf("Using default machine registration expiry time: %v (%v)", defaultExpiry, h.cfg.DefaultMachineRegistrationDuration) + m.Expiry = &defaultExpiry + } else { + log.Debug().Msgf("Using requested machine registration expiry time: %v", m.RequestedExpiry) + m.Expiry = m.RequestedExpiry + } + + h.db.Save(&m) + } +} + func (h *Headscale) getDirectPeers(m *Machine) (Machines, error) { log.Trace(). Str("func", "getDirectPeers"). diff --git a/oidc.go b/oidc.go index 1220098c..01c54b44 100644 --- a/oidc.go +++ b/oidc.go @@ -4,14 +4,12 @@ import ( "context" "crypto/rand" "encoding/hex" - "errors" "fmt" "github.com/coreos/go-oidc/v3/oidc" "github.com/gin-gonic/gin" "github.com/patrickmn/go-cache" "github.com/rs/zerolog/log" "golang.org/x/oauth2" - "gorm.io/gorm" "net/http" "strings" "time" @@ -103,6 +101,8 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { return } + log.Debug().Msgf("AccessToken: %v", oauth2Token.AccessToken) + rawIDToken, rawIDTokenOK := oauth2Token.Extra("id_token").(string) if !rawIDTokenOK { c.String(http.StatusBadRequest, "Could not extract ID Token") @@ -117,16 +117,17 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { return } + // TODO: we can use userinfo at some point to grab additional information about the user (groups membership, etc) //userInfo, err := oidcProvider.UserInfo(context.Background(), oauth2.StaticTokenSource(oauth2Token)) //if err != nil { - // c.String(http.StatusBadRequest, "Failed to retrieve userinfo: "+err.Error()) + // c.String(http.StatusBadRequest, fmt.Sprintf("Failed to retrieve userinfo: %s", err)) // return //} // Extract custom claims var claims IDTokenClaims if err = idToken.Claims(&claims); err != nil { - c.String(http.StatusBadRequest, "Failed to decode id token claims: "+err.Error()) + c.String(http.StatusBadRequest, fmt.Sprintf("Failed to decode id token claims: %s", err)) return } @@ -134,39 +135,44 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { mKeyIf, mKeyFound := h.oidcStateCache.Get(state) if !mKeyFound { + log.Error().Msg("requested machine state key expired before authorisation completed") c.String(http.StatusBadRequest, "state has expired") return } mKeyStr, mKeyOK := mKeyIf.(string) if !mKeyOK { + log.Error().Msg("could not get machine key from cache") c.String(http.StatusInternalServerError, "could not get machine key from cache") return } // retrieve machine information - var m Machine - if result := h.db.Preload("Namespace").First(&m, "machine_key = ?", mKeyStr); errors.Is(result.Error, gorm.ErrRecordNotFound) { + m, err := h.GetMachineByMachineKey(mKeyStr) + + if err != nil { log.Error().Msg("machine key not found in database") c.String(http.StatusInternalServerError, "could not get machine info from database") return } - //look for a namespace of the users email for now + now := time.Now().UTC() + + // register the machine if it's new if !m.Registered { + nsName := strings.ReplaceAll(claims.Email, "@", "-") // TODO: Implement a better email sanitisation log.Debug().Msg("Registering new machine after successful callback") - ns, err := h.GetNamespace(claims.Email) + ns, err := h.GetNamespace(nsName) if err != nil { - ns, err = h.CreateNamespace(claims.Email) + ns, err = h.CreateNamespace(nsName) if err != nil { log.Error().Msgf("could not create new namespace '%s'", claims.Email) c.String(http.StatusInternalServerError, "could not create new namespace") return } - } ip, err := h.getAvailableIP() @@ -179,24 +185,11 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { m.NamespaceID = ns.ID m.Registered = true m.RegisterMethod = "oidc" + m.LastSuccessfulUpdate = &now h.db.Save(&m) } - if m.isExpired() { - maxExpiry := time.Now().UTC().Add(h.cfg.MaxMachineExpiry) - - // use the maximum expiry if it's sooner than the requested expiry - if maxExpiry.Before(*m.Expiry) { - log.Debug().Msgf("Clamping expiry time to maximum: %v (%v)", maxExpiry, h.cfg.MaxMachineExpiry) - m.Expiry = &maxExpiry - h.db.Save(&m) - } else if m.Expiry.IsZero() { - log.Debug().Msgf("Using default machine expiry time: %v (%v)", maxExpiry, h.cfg.MaxMachineExpiry) - defaultExpiry := time.Now().UTC().Add(h.cfg.DefaultMachineExpiry) - m.Expiry = &defaultExpiry - h.db.Save(&m) - } - } + h.updateMachineExpiry(m) c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(fmt.Sprintf(` From 8843188b8448cbcac4d603ff903a85144ab829a1 Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Sun, 10 Oct 2021 22:52:30 +0800 Subject: [PATCH 07/70] add notes to README.md about OIDC --- README.md | 55 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 5f691a6c..9d2ec159 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Headscale implements this coordination server. - [x] Support for alternative IP ranges in the tailnets (default Tailscale's 100.64.0.0/10) - [x] DNS (passing DNS servers to nodes) - [x] Share nodes between ~~users~~ namespaces +- [x] SSO (via OIDC) - [ ] MagicDNS / Smart DNS ## Client OS support @@ -100,7 +101,21 @@ Suggestions/PRs welcomed! docker exec headscale create myfirstnamespace ``` -5. Run the server +5. (Optional) Configure an OIDC Issuer + + You can optionally configure an OIDC endpoint to which your users will be redirected to authenticate with headscale. In config.json set the following parameters: + + ```json + { + "oidc_issuer": "https://your-oidc.issuer.com/path", + "oidc_client_id": "your-oidc-client-id", + "oidc_client_secret": "your-oidc-client-secret" + } + ``` + + If `oidc_issuer` is set, headscale will attempt to send your users to the OIDC server for authentication, otherwise it will give instructions on how to authorise clients via the CLI. + +6. Run the server ```shell headscale serve @@ -114,7 +129,7 @@ Suggestions/PRs welcomed! docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml -v $(pwd)/db.sqlite:/db.sqlite -p 127.0.0.1:8080:8080 headscale/headscale:x.x.x headscale serve ``` -6. If you used tailscale.com before in your nodes, make sure you clear the tailscald data folder +7. If you used tailscale.com before in your nodes, make sure you clear the tailscald data folder ```shell systemctl stop tailscaled @@ -122,26 +137,26 @@ Suggestions/PRs welcomed! systemctl start tailscaled ``` -7. Add your first machine +8. Add your first machine ```shell tailscale up --login-server YOUR_HEADSCALE_URL ``` -8. Navigate to the URL you will get with `tailscale up`, where you'll find your machine key. +9. Navigate to the URL you will get with `tailscale up`, where you'll find your machine key. If OIDC is configured, once you login your user will be added to a namespace automatically, and you can skip step 10. -9. In the server, register your machine to a namespace with the CLI - ```shell - headscale -n myfirstnamespace nodes register YOURMACHINEKEY - ``` - or docker: - ```shell - docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml headscale/headscale:x.x.x headscale -n myfirstnamespace nodes register YOURMACHINEKEY - ``` - or if your server is already running in docker: - ```shell - docker exec headscale -n myfirstnamespace nodes register YOURMACHINEKEY - ``` +10. In the server, register your machine to a namespace with the CLI + ```shell + headscale -n myfirstnamespace nodes register YOURMACHINEKEY + ``` + or docker: + ```shell + docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml headscale/headscale:x.x.x headscale -n myfirstnamespace nodes register YOURMACHINEKEY + ``` + or if your server is already running in docker: + ```shell + docker exec headscale -n myfirstnamespace nodes register YOURMACHINEKEY + ``` Alternatively, you can use Auth Keys to register your machines: @@ -218,6 +233,14 @@ Headscale's configuration file is named `config.json` or `config.yaml`. Headscal The fields starting with `db_` are used for the PostgreSQL connection information. +OpenID Connect settings: +``` + "oidc_issuer": "https://your-oidc.issuer.com/path", + "oidc_client_id": "your-oidc-client-id", + "oidc_client_secret": "your-oidc-client-secret" +``` + + ### Running the service via TLS (optional) ``` From 0603e29c46143b4de0d74a375d13cd7bfec4ab3c Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Fri, 15 Oct 2021 23:09:55 +0800 Subject: [PATCH 08/70] add login details to RegisterResponse so GUI clients show login display name --- api.go | 2 ++ namespaces.go | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/api.go b/api.go index bda9d9bd..d85221bc 100644 --- a/api.go +++ b/api.go @@ -147,6 +147,8 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { resp.AuthURL = "" resp.MachineAuthorized = true resp.User = *m.Namespace.toUser() + resp.Login = *m.Namespace.toLogin() + respBody, err := encode(resp, &mKey, h.privateKey) if err != nil { log.Error(). diff --git a/namespaces.go b/namespaces.go index 2bf62bb3..212df9a6 100644 --- a/namespaces.go +++ b/namespaces.go @@ -216,3 +216,14 @@ func (n *Namespace) toUser() *tailcfg.User { } return &u } + +func (n *Namespace) toLogin() *tailcfg.Login { + l := tailcfg.Login{ + ID: tailcfg.LoginID(n.ID), + LoginName: n.Name, + DisplayName: n.Name, + ProfilePicURL: "", + Domain: "headscale.net", + } + return &l +} From d0cd5af419d2fee7b1ae6a7b8510b27eb25d4c8d Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Sat, 16 Oct 2021 22:34:11 +0800 Subject: [PATCH 09/70] fix incorrect merge --- cmd/headscale/cli/utils.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index ba8d34ad..f29c389d 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -161,8 +161,7 @@ func getHeadscaleApp() (*headscale.Headscale, error) { return nil, err } - - // maxMachineRegistrationDuration is the maximum time a client can request for a client registration + // maxMachineRegistrationDuration is the maximum time a client can request for a client registration maxMachineRegistrationDuration, _ := time.ParseDuration("10h") if viper.GetDuration("max_machine_registration_duration") >= time.Second { maxMachineRegistrationDuration = viper.GetDuration("max_machine_registration_duration") @@ -174,7 +173,7 @@ func getHeadscaleApp() (*headscale.Headscale, error) { defaultMachineRegistrationDuration = viper.GetDuration("default_machine_registration_duration") } - dnsConfig, baseDomain := GetDNSConfig() + dnsConfig, baseDomain := GetDNSConfig() cfg := headscale.Config{ ServerURL: viper.GetString("server_url"), @@ -207,8 +206,6 @@ func getHeadscaleApp() (*headscale.Headscale, error) { ACMEEmail: viper.GetString("acme_email"), ACMEURL: viper.GetString("acme_url"), - DNSConfig: GetDNSConfig(), - OIDCIssuer: viper.GetString("oidc_issuer"), OIDCClientID: viper.GetString("oidc_client_id"), OIDCClientSecret: viper.GetString("oidc_client_secret"), From a347d276bd650223109d9f12cc9829e99651b6b9 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 18 Oct 2021 19:26:43 +0000 Subject: [PATCH 10/70] Fix broken machine test --- cli_test.go | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/cli_test.go b/cli_test.go index 528a115e..291b5df1 100644 --- a/cli_test.go +++ b/cli_test.go @@ -1,6 +1,8 @@ package headscale import ( + "time" + "gopkg.in/check.v1" ) @@ -8,14 +10,18 @@ func (s *Suite) TestRegisterMachine(c *check.C) { n, err := h.CreateNamespace("test") c.Assert(err, check.IsNil) + now := time.Now().UTC() + m := Machine{ - ID: 0, - MachineKey: "8ce002a935f8c394e55e78fbbb410576575ff8ec5cfa2e627e4b807f1be15b0e", - NodeKey: "bar", - DiscoKey: "faa", - Name: "testmachine", - NamespaceID: n.ID, - IPAddress: "10.0.0.1", + ID: 0, + MachineKey: "8ce002a935f8c394e55e78fbbb410576575ff8ec5cfa2e627e4b807f1be15b0e", + NodeKey: "bar", + DiscoKey: "faa", + Name: "testmachine", + NamespaceID: n.ID, + IPAddress: "10.0.0.1", + Expiry: &now, + RequestedExpiry: &now, } h.db.Save(&m) From 677bd9b657d0ca229cb5e97875e58b6f43571bed Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 18 Oct 2021 19:27:52 +0000 Subject: [PATCH 11/70] Implement namespace matching --- api.go | 6 +- app.go | 26 +++--- cmd/headscale/cli/utils.go | 24 ++++- oidc.go | 93 ++++++++++++-------- oidc_test.go | 173 +++++++++++++++++++++++++++++++++++++ 5 files changed, 267 insertions(+), 55 deletions(-) create mode 100644 oidc_test.go diff --git a/api.go b/api.go index cbe48072..c542b3aa 100644 --- a/api.go +++ b/api.go @@ -170,7 +170,7 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { Str("machine", m.Name). Msg("Machine registration has expired. Sending a authurl to register") - if h.cfg.OIDCIssuer != "" { + if h.cfg.OIDC.Issuer != "" { resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", strings.TrimSuffix(h.cfg.ServerURL, "/"), mKey.HexString()) } else { @@ -225,7 +225,7 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { Str("handler", "Registration"). Str("machine", m.Name). Msg("The node is sending us a new NodeKey, sending auth url") - if h.cfg.OIDCIssuer != "" { + if h.cfg.OIDC.Issuer != "" { resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s", strings.TrimSuffix(h.cfg.ServerURL, "/"), mKey.HexString()) } else { resp.AuthURL = fmt.Sprintf("%s/register?key=%s", @@ -424,7 +424,7 @@ func (h *Headscale) handleAuthKey(c *gin.Context, db *gorm.DB, idKey wgkey.Key, db.Save(&m) h.updateMachineExpiry(&m) // TODO: do we want to do different expiry times for AuthKeys? - + pak.Used = true db.Save(&pak) diff --git a/app.go b/app.go index 89c43589..c89856f8 100644 --- a/app.go +++ b/app.go @@ -3,9 +3,6 @@ package headscale import ( "errors" "fmt" - "github.com/coreos/go-oidc/v3/oidc" - "github.com/patrickmn/go-cache" - "golang.org/x/oauth2" "net/http" "os" "sort" @@ -13,6 +10,10 @@ import ( "sync" "time" + "github.com/coreos/go-oidc/v3/oidc" + "github.com/patrickmn/go-cache" + "golang.org/x/oauth2" + "github.com/rs/zerolog/log" "github.com/gin-gonic/gin" @@ -57,14 +58,19 @@ type Config struct { DNSConfig *tailcfg.DNSConfig - OIDCIssuer string - OIDCClientID string - OIDCClientSecret string + OIDC OIDCConfig MaxMachineRegistrationDuration time.Duration DefaultMachineRegistrationDuration time.Duration } +type OIDCConfig struct { + Issuer string + ClientID string + ClientSecret string + MatchMap map[string]string +} + // Headscale represents the base app of the service type Headscale struct { cfg Config @@ -122,14 +128,14 @@ func NewHeadscale(cfg Config) (*Headscale, error) { return nil, err } - if cfg.OIDCIssuer != "" { + if cfg.OIDC.Issuer != "" { err = h.initOIDC() if err != nil { return nil, err } - } + } - if h.cfg.DNSConfig != nil && h.cfg.DNSConfig.Proxied { // if MagicDNS + if h.cfg.DNSConfig != nil && h.cfg.DNSConfig.Proxied { // if MagicDNS magicDNSDomains, err := generateMagicDNSRootDomains(h.cfg.IPPrefix, h.cfg.BaseDomain) if err != nil { return nil, err @@ -294,7 +300,6 @@ func (h *Headscale) getLastStateChange(namespaces ...string) time.Time { times = append(times, lastChange) } - } sort.Slice(times, func(i, j int) bool { @@ -305,7 +310,6 @@ func (h *Headscale) getLastStateChange(namespaces ...string) time.Time { if len(times) == 0 { return time.Now().UTC() - } else { return times[0] } diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index f29c389d..4a598e77 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -7,6 +7,7 @@ import ( "io" "os" "path/filepath" + "regexp" "strings" "time" @@ -73,7 +74,6 @@ func LoadConfig(path string) error { } else { return nil } - } func GetDNSConfig() (*tailcfg.DNSConfig, string) { @@ -206,15 +206,19 @@ func getHeadscaleApp() (*headscale.Headscale, error) { ACMEEmail: viper.GetString("acme_email"), ACMEURL: viper.GetString("acme_url"), - OIDCIssuer: viper.GetString("oidc_issuer"), - OIDCClientID: viper.GetString("oidc_client_id"), - OIDCClientSecret: viper.GetString("oidc_client_secret"), + OIDC: headscale.OIDCConfig{ + Issuer: viper.GetString("oidc.issuer"), + ClientID: viper.GetString("oidc.client_id"), + ClientSecret: viper.GetString("oidc.client_secret"), + }, MaxMachineRegistrationDuration: maxMachineRegistrationDuration, // the maximum duration a client may request for expiry time DefaultMachineRegistrationDuration: defaultMachineRegistrationDuration, // if a client does not request a specific expiry time, use this duration } + cfg.OIDC.MatchMap = loadOIDCMatchMap() + h, err := headscale.NewHeadscale(cfg) if err != nil { return nil, err @@ -291,3 +295,15 @@ func HasJsonOutputFlag() bool { } return false } + +// loadOIDCMatchMap is a wrapper around viper to verifies that the keys in +// the match map is valid regex strings. +func loadOIDCMatchMap() map[string]string { + strMap := viper.GetStringMapString("oidc.domain_map") + + for oidcMatcher := range strMap { + _ = regexp.MustCompile(oidcMatcher) + } + + return strMap +} diff --git a/oidc.go b/oidc.go index 01c54b44..1b13963c 100644 --- a/oidc.go +++ b/oidc.go @@ -5,14 +5,16 @@ import ( "crypto/rand" "encoding/hex" "fmt" + "net/http" + "regexp" + "strings" + "time" + "github.com/coreos/go-oidc/v3/oidc" "github.com/gin-gonic/gin" "github.com/patrickmn/go-cache" "github.com/rs/zerolog/log" "golang.org/x/oauth2" - "net/http" - "strings" - "time" ) type IDTokenClaims struct { @@ -26,7 +28,7 @@ func (h *Headscale) initOIDC() error { var err error // grab oidc config if it hasn't been already if h.oauth2Config == nil { - h.oidcProvider, err = oidc.NewProvider(context.Background(), h.cfg.OIDCIssuer) + h.oidcProvider, err = oidc.NewProvider(context.Background(), h.cfg.OIDC.Issuer) if err != nil { log.Error().Msgf("Could not retrieve OIDC Config: %s", err.Error()) @@ -34,8 +36,8 @@ func (h *Headscale) initOIDC() error { } h.oauth2Config = &oauth2.Config{ - ClientID: h.cfg.OIDCClientID, - ClientSecret: h.cfg.OIDCClientSecret, + ClientID: h.cfg.OIDC.ClientID, + ClientSecret: h.cfg.OIDC.ClientSecret, Endpoint: h.oidcProvider.Endpoint(), RedirectURL: fmt.Sprintf("%s/oidc/callback", strings.TrimSuffix(h.cfg.ServerURL, "/")), Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, @@ -62,7 +64,6 @@ func (h *Headscale) RegisterOIDC(c *gin.Context) { b := make([]byte, 16) _, err := rand.Read(b) - if err != nil { log.Error().Msg("could not read 16 bytes from rand") c.String(http.StatusInternalServerError, "could not read 16 bytes from rand") @@ -86,7 +87,6 @@ func (h *Headscale) RegisterOIDC(c *gin.Context) { // TODO: Add groups information from OIDC tokens into machine HostInfo // Listens in /oidc/callback func (h *Headscale) OIDCCallback(c *gin.Context) { - code := c.Query("code") state := c.Query("state") @@ -109,7 +109,7 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { return } - verifier := h.oidcProvider.Verifier(&oidc.Config{ClientID: h.cfg.OIDCClientID}) + verifier := h.oidcProvider.Verifier(&oidc.Config{ClientID: h.cfg.OIDC.ClientID}) idToken, err := verifier.Verify(context.Background(), rawIDToken) if err != nil { @@ -131,7 +131,7 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { return } - //retrieve machinekey from state cache + // retrieve machinekey from state cache mKeyIf, mKeyFound := h.oidcStateCache.Get(state) if !mKeyFound { @@ -149,7 +149,6 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { // retrieve machine information m, err := h.GetMachineByMachineKey(mKeyStr) - if err != nil { log.Error().Msg("machine key not found in database") c.String(http.StatusInternalServerError, "could not get machine info from database") @@ -158,40 +157,40 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { now := time.Now().UTC() - // register the machine if it's new - if !m.Registered { - nsName := strings.ReplaceAll(claims.Email, "@", "-") // TODO: Implement a better email sanitisation + if nsName, ok := h.getNamespaceFromEmail(claims.Email); ok { + // register the machine if it's new + if !m.Registered { - log.Debug().Msg("Registering new machine after successful callback") - - ns, err := h.GetNamespace(nsName) - if err != nil { - ns, err = h.CreateNamespace(nsName) + log.Debug().Msg("Registering new machine after successful callback") + ns, err := h.GetNamespace(nsName) if err != nil { - log.Error().Msgf("could not create new namespace '%s'", claims.Email) - c.String(http.StatusInternalServerError, "could not create new namespace") + ns, err = h.CreateNamespace(nsName) + + if err != nil { + log.Error().Msgf("could not create new namespace '%s'", claims.Email) + c.String(http.StatusInternalServerError, "could not create new namespace") + return + } + } + + ip, err := h.getAvailableIP() + if err != nil { + c.String(http.StatusInternalServerError, "could not get an IP from the pool") return } + + m.IPAddress = ip.String() + m.NamespaceID = ns.ID + m.Registered = true + m.RegisterMethod = "oidc" + m.LastSuccessfulUpdate = &now + h.db.Save(&m) } - ip, err := h.getAvailableIP() - if err != nil { - c.String(http.StatusInternalServerError, "could not get an IP from the pool") - return - } + h.updateMachineExpiry(m) - m.IPAddress = ip.String() - m.NamespaceID = ns.ID - m.Registered = true - m.RegisterMethod = "oidc" - m.LastSuccessfulUpdate = &now - h.db.Save(&m) - } - - h.updateMachineExpiry(m) - - c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(fmt.Sprintf(` + c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(fmt.Sprintf(`

headscale

@@ -202,4 +201,24 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { `, claims.Email))) + + } + + log.Error(). + Str("email", claims.Email). + Str("username", claims.Username). + Str("machine", m.Name). + Msg("Email could not be mapped to a namespace") + c.String(http.StatusBadRequest, "email from claim could not be mapped to a namespace") +} + +func (h *Headscale) getNamespaceFromEmail(email string) (string, bool) { + for match, namespace := range h.cfg.OIDC.MatchMap { + regex := regexp.MustCompile(match) + if regex.MatchString(email) { + return namespace, true + } + } + + return "", false } diff --git a/oidc_test.go b/oidc_test.go new file mode 100644 index 00000000..ddb44e4c --- /dev/null +++ b/oidc_test.go @@ -0,0 +1,173 @@ +package headscale + +import ( + "sync" + "testing" + + "github.com/coreos/go-oidc/v3/oidc" + "github.com/patrickmn/go-cache" + "golang.org/x/oauth2" + "gorm.io/gorm" + "tailscale.com/tailcfg" + "tailscale.com/types/wgkey" +) + +func TestHeadscale_getNamespaceFromEmail(t *testing.T) { + type fields struct { + cfg Config + db *gorm.DB + dbString string + dbType string + dbDebug bool + publicKey *wgkey.Key + privateKey *wgkey.Private + aclPolicy *ACLPolicy + aclRules *[]tailcfg.FilterRule + lastStateChange sync.Map + oidcProvider *oidc.Provider + oauth2Config *oauth2.Config + oidcStateCache *cache.Cache + } + type args struct { + email string + } + tests := []struct { + name string + fields fields + args args + want string + want1 bool + }{ + { + name: "match all", + fields: fields{ + cfg: Config{ + OIDC: OIDCConfig{ + MatchMap: map[string]string{ + ".*": "space", + }, + }, + }, + }, + args: args{ + email: "test@example.no", + }, + want: "space", + want1: true, + }, + { + name: "match user", + fields: fields{ + cfg: Config{ + OIDC: OIDCConfig{ + MatchMap: map[string]string{ + "specific@user\\.no": "user-namespace", + }, + }, + }, + }, + args: args{ + email: "specific@user.no", + }, + want: "user-namespace", + want1: true, + }, + { + name: "match domain", + fields: fields{ + cfg: Config{ + OIDC: OIDCConfig{ + MatchMap: map[string]string{ + ".*@example\\.no": "example", + }, + }, + }, + }, + args: args{ + email: "test@example.no", + }, + want: "example", + want1: true, + }, + { + name: "multi match domain", + fields: fields{ + cfg: Config{ + OIDC: OIDCConfig{ + MatchMap: map[string]string{ + ".*@example\\.no": "exammple", + ".*@gmail\\.com": "gmail", + }, + }, + }, + }, + args: args{ + email: "someuser@gmail.com", + }, + want: "gmail", + want1: true, + }, + { + name: "no match domain", + fields: fields{ + cfg: Config{ + OIDC: OIDCConfig{ + MatchMap: map[string]string{ + ".*@dontknow.no": "never", + }, + }, + }, + }, + args: args{ + email: "test@wedontknow.no", + }, + want: "", + want1: false, + }, + { + name: "multi no match domain", + fields: fields{ + cfg: Config{ + OIDC: OIDCConfig{ + MatchMap: map[string]string{ + ".*@dontknow.no": "never", + ".*@wedontknow.no": "other", + ".*\\.no": "stuffy", + }, + }, + }, + }, + args: args{ + email: "tasy@nonofthem.com", + }, + want: "", + want1: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := &Headscale{ + cfg: tt.fields.cfg, + db: tt.fields.db, + dbString: tt.fields.dbString, + dbType: tt.fields.dbType, + dbDebug: tt.fields.dbDebug, + publicKey: tt.fields.publicKey, + privateKey: tt.fields.privateKey, + aclPolicy: tt.fields.aclPolicy, + aclRules: tt.fields.aclRules, + lastStateChange: tt.fields.lastStateChange, + oidcProvider: tt.fields.oidcProvider, + oauth2Config: tt.fields.oauth2Config, + oidcStateCache: tt.fields.oidcStateCache, + } + got, got1 := h.getNamespaceFromEmail(tt.args.email) + if got != tt.want { + t.Errorf("Headscale.getNamespaceFromEmail() got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("Headscale.getNamespaceFromEmail() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} From dbe193ad1783ccc323f4227d1fd849e3290f9454 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 19 Oct 2021 18:25:59 +0100 Subject: [PATCH 12/70] Fix up leftovers from kradalby PR --- README.md | 24 ++++++++++++++---------- oidc.go | 4 ++++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0cb41bcb..16b6c67f 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Headscale implements this coordination server. - [x] Support for alternative IP ranges in the tailnets (default Tailscale's 100.64.0.0/10) - [x] DNS (passing DNS servers to nodes) - [x] Share nodes between ~~users~~ namespaces -- [x] SSO (via OIDC) +- [x] Single-Sign-On (via Open ID Connect) - [x] MagicDNS (see `docs/`) ## Client OS support @@ -109,13 +109,14 @@ Suggestions/PRs welcomed! ```json { - "oidc_issuer": "https://your-oidc.issuer.com/path", - "oidc_client_id": "your-oidc-client-id", - "oidc_client_secret": "your-oidc-client-secret" + "oidc": { + "issuer": "https://your-oidc.issuer.com/path", + "client_id": "your-oidc-client-id", + "client_secret": "your-oidc-client-secret", + "domain_map": { + ".*": "default-namespace" + } } - ``` - - If `oidc_issuer` is set, headscale will attempt to send your users to the OIDC server for authentication, otherwise it will give instructions on how to authorise clients via the CLI. 6. Run the server @@ -237,9 +238,12 @@ The fields starting with `db_` are used for the PostgreSQL connection informatio OpenID Connect settings: ``` - "oidc_issuer": "https://your-oidc.issuer.com/path", - "oidc_client_id": "your-oidc-client-id", - "oidc_client_secret": "your-oidc-client-secret" + oidc: + issuer: "https://your-oidc.issuer.com/path" + client_id: "your-oidc-client-id" + client_secret: "your-oidc-client-secret" + domain_map: + ".*": default-namespace ``` diff --git a/oidc.go b/oidc.go index 1b13963c..51c443db 100644 --- a/oidc.go +++ b/oidc.go @@ -212,6 +212,10 @@ func (h *Headscale) OIDCCallback(c *gin.Context) { c.String(http.StatusBadRequest, "email from claim could not be mapped to a namespace") } +// getNamespaceFromEmail passes the users email through a list of "matchers" +// and iterates through them until it matches and returns a namespace. +// If no match is found, an empty string will be returned. +// TODO(kradalby): golang Maps key order is not stable, so this list is _not_ deterministic. Find a way to make the list of keys stable, preferably in the order presented in a users configuration. func (h *Headscale) getNamespaceFromEmail(email string) (string, bool) { for match, namespace := range h.cfg.OIDC.MatchMap { regex := regexp.MustCompile(match) From b85adbc40a5119b202f2312e0ac8dd2a3e1a3716 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 22 Oct 2021 18:14:29 +0100 Subject: [PATCH 13/70] Remove the need for multiple config files This commit removes the almost a 100% redundant tests (two fields were checked differently) and makes a single example configuration for users. --- cmd/headscale/headscale_test.go | 62 ++++++------------- ...yaml.sqlite.example => config-example.yaml | 20 ++++-- config.yaml.postgres.example | 30 --------- 3 files changed, 34 insertions(+), 78 deletions(-) rename config.yaml.sqlite.example => config-example.yaml (67%) delete mode 100644 config.yaml.postgres.example diff --git a/cmd/headscale/headscale_test.go b/cmd/headscale/headscale_test.go index 0c3add69..e4a20433 100644 --- a/cmd/headscale/headscale_test.go +++ b/cmd/headscale/headscale_test.go @@ -25,40 +25,6 @@ func (s *Suite) SetUpSuite(c *check.C) { } func (s *Suite) TearDownSuite(c *check.C) { - -} - -func (*Suite) TestPostgresConfigLoading(c *check.C) { - tmpDir, err := ioutil.TempDir("", "headscale") - if err != nil { - c.Fatal(err) - } - defer os.RemoveAll(tmpDir) - - path, err := os.Getwd() - if err != nil { - c.Fatal(err) - } - - // Symlink the example config file - err = os.Symlink(filepath.Clean(path+"/../../config.yaml.postgres.example"), filepath.Join(tmpDir, "config.yaml")) - if err != nil { - c.Fatal(err) - } - - // Load example config, it should load without validation errors - err = cli.LoadConfig(tmpDir) - c.Assert(err, check.IsNil) - - // Test that config file was interpreted correctly - c.Assert(viper.GetString("server_url"), check.Equals, "http://127.0.0.1:8080") - c.Assert(viper.GetString("listen_addr"), check.Equals, "0.0.0.0:8080") - c.Assert(viper.GetString("derp_map_path"), check.Equals, "derp.yaml") - c.Assert(viper.GetString("db_type"), check.Equals, "postgres") - c.Assert(viper.GetString("db_port"), check.Equals, "5432") - c.Assert(viper.GetString("tls_letsencrypt_hostname"), check.Equals, "") - c.Assert(viper.GetString("tls_letsencrypt_listen"), check.Equals, ":http") - c.Assert(viper.GetStringSlice("dns_config.nameservers")[0], check.Equals, "1.1.1.1") } func (*Suite) TestSqliteConfigLoading(c *check.C) { @@ -74,7 +40,7 @@ func (*Suite) TestSqliteConfigLoading(c *check.C) { } // Symlink the example config file - err = os.Symlink(filepath.Clean(path+"/../../config.yaml.sqlite.example"), filepath.Join(tmpDir, "config.yaml")) + err = os.Symlink(filepath.Clean(path+"/../../config-example.yaml"), filepath.Join(tmpDir, "config.yaml")) if err != nil { c.Fatal(err) } @@ -108,7 +74,7 @@ func (*Suite) TestDNSConfigLoading(c *check.C) { } // Symlink the example config file - err = os.Symlink(filepath.Clean(path+"/../../config.yaml.sqlite.example"), filepath.Join(tmpDir, "config.yaml")) + err = os.Symlink(filepath.Clean(path+"/../../config-example.yaml"), filepath.Join(tmpDir, "config.yaml")) if err != nil { c.Fatal(err) } @@ -128,7 +94,7 @@ func (*Suite) TestDNSConfigLoading(c *check.C) { func writeConfig(c *check.C, tmpDir string, configYaml []byte) { // Populate a custom config file configFile := filepath.Join(tmpDir, "config.yaml") - err := ioutil.WriteFile(configFile, configYaml, 0644) + err := ioutil.WriteFile(configFile, configYaml, 0o644) if err != nil { c.Fatalf("Couldn't write file %s", configFile) } @@ -139,10 +105,12 @@ func (*Suite) TestTLSConfigValidation(c *check.C) { if err != nil { c.Fatal(err) } - //defer os.RemoveAll(tmpDir) + // defer os.RemoveAll(tmpDir) fmt.Println(tmpDir) - configYaml := []byte("---\ntls_letsencrypt_hostname: \"example.com\"\ntls_letsencrypt_challenge_type: \"\"\ntls_cert_path: \"abc.pem\"") + configYaml := []byte( + "---\ntls_letsencrypt_hostname: \"example.com\"\ntls_letsencrypt_challenge_type: \"\"\ntls_cert_path: \"abc.pem\"", + ) writeConfig(c, tmpDir, configYaml) // Check configuration validation errors (1) @@ -150,13 +118,23 @@ func (*Suite) TestTLSConfigValidation(c *check.C) { c.Assert(err, check.NotNil) // check.Matches can not handle multiline strings tmp := strings.ReplaceAll(err.Error(), "\n", "***") - c.Assert(tmp, check.Matches, ".*Fatal config error: set either tls_letsencrypt_hostname or tls_cert_path/tls_key_path, not both.*") - c.Assert(tmp, check.Matches, ".*Fatal config error: the only supported values for tls_letsencrypt_challenge_type are.*") + c.Assert( + tmp, + check.Matches, + ".*Fatal config error: set either tls_letsencrypt_hostname or tls_cert_path/tls_key_path, not both.*", + ) + c.Assert( + tmp, + check.Matches, + ".*Fatal config error: the only supported values for tls_letsencrypt_challenge_type are.*", + ) c.Assert(tmp, check.Matches, ".*Fatal config error: server_url must start with https:// or http://.*") fmt.Println(tmp) // Check configuration validation errors (2) - configYaml = []byte("---\nserver_url: \"http://127.0.0.1:8080\"\ntls_letsencrypt_hostname: \"example.com\"\ntls_letsencrypt_challenge_type: \"TLS-ALPN-01\"") + configYaml = []byte( + "---\nserver_url: \"http://127.0.0.1:8080\"\ntls_letsencrypt_hostname: \"example.com\"\ntls_letsencrypt_challenge_type: \"TLS-ALPN-01\"", + ) writeConfig(c, tmpDir, configYaml) err = cli.LoadConfig(tmpDir) c.Assert(err, check.IsNil) diff --git a/config.yaml.sqlite.example b/config-example.yaml similarity index 67% rename from config.yaml.sqlite.example rename to config-example.yaml index 158b1e5b..494121b2 100644 --- a/config.yaml.sqlite.example +++ b/config-example.yaml @@ -9,18 +9,26 @@ ephemeral_node_inactivity_timeout: 30m db_type: sqlite3 db_path: db.sqlite +# # Postgres config +# db_type: postgres +# db_host: localhost +# db_port: 5432 +# db_name: headscale +# db_user: foo +# db_pass: bar + acme_url: https://acme-v02.api.letsencrypt.org/directory -acme_email: '' -tls_letsencrypt_hostname: '' +acme_email: "" +tls_letsencrypt_hostname: "" tls_letsencrypt_listen: ":http" tls_letsencrypt_cache_dir: ".cache" tls_letsencrypt_challenge_type: HTTP-01 -tls_cert_path: '' -tls_key_path: '' -acl_policy_path: '' +tls_cert_path: "" +tls_key_path: "" +acl_policy_path: "" dns_config: nameservers: - - 1.1.1.1 + - 1.1.1.1 domains: [] magic_dns: true base_domain: example.com diff --git a/config.yaml.postgres.example b/config.yaml.postgres.example deleted file mode 100644 index 569b42a9..00000000 --- a/config.yaml.postgres.example +++ /dev/null @@ -1,30 +0,0 @@ ---- -server_url: http://127.0.0.1:8080 -listen_addr: 0.0.0.0:8080 -private_key_path: private.key -derp_map_path: derp.yaml -ephemeral_node_inactivity_timeout: 30m - -# Postgres config -db_type: postgres -db_host: localhost -db_port: 5432 -db_name: headscale -db_user: foo -db_pass: bar - -acme_url: https://acme-v02.api.letsencrypt.org/directory -acme_email: '' -tls_letsencrypt_hostname: '' -tls_letsencrypt_listen: ":http" -tls_letsencrypt_cache_dir: ".cache" -tls_letsencrypt_challenge_type: HTTP-01 -tls_cert_path: '' -tls_key_path: '' -acl_policy_path: '' -dns_config: - nameservers: - - 1.1.1.1 - domains: [] - magic_dns: true - base_domain: example.com From 4d4d0de356d47f1e19c22b92d0ae0b8ef2fcc196 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 22 Oct 2021 18:27:11 +0100 Subject: [PATCH 14/70] Start adding comments to config --- config-example.yaml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/config-example.yaml b/config-example.yaml index 494121b2..d0b0bb6e 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -1,11 +1,20 @@ --- +# The url clients will connect to. +# Typically this will be a domain. server_url: http://127.0.0.1:8080 + +# Address to listen to / bind to on the server listen_addr: 0.0.0.0:8080 + +# Path to WireGuard private key file private_key_path: private.key + +# Path to a file containing a map of DERP nodes. derp_map_path: derp.yaml + ephemeral_node_inactivity_timeout: 30m -# SQLite config (uncomment it if you want to use SQLite) +# SQLite config db_type: sqlite3 db_path: db.sqlite @@ -19,16 +28,23 @@ db_path: db.sqlite acme_url: https://acme-v02.api.letsencrypt.org/directory acme_email: "" + tls_letsencrypt_hostname: "" tls_letsencrypt_listen: ":http" tls_letsencrypt_cache_dir: ".cache" tls_letsencrypt_challenge_type: HTTP-01 + tls_cert_path: "" tls_key_path: "" + +# Path to a file containg ACL policies. acl_policy_path: "" + dns_config: + # Upstream DNS servers nameservers: - 1.1.1.1 domains: [] + magic_dns: true base_domain: example.com From 8853315dcca2b8e7c43a14fec4f7a50e6ed61d89 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sat, 23 Oct 2021 10:40:15 +0100 Subject: [PATCH 15/70] Update config-example.yaml Co-authored-by: Juan Font --- config-example.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config-example.yaml b/config-example.yaml index d0b0bb6e..54e78f4a 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -12,6 +12,8 @@ private_key_path: private.key # Path to a file containing a map of DERP nodes. derp_map_path: derp.yaml +# Disables the automatic check for updates on startup +disable_check_updates: false ephemeral_node_inactivity_timeout: 30m # SQLite config From 746d4037da46ff1e6682b6d8a79579f7dfb87c94 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 24 Oct 2021 21:30:51 +0100 Subject: [PATCH 16/70] Fix config and tests --- config-example.yaml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/config-example.yaml b/config-example.yaml index 54e78f4a..59370eb5 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -9,8 +9,22 @@ listen_addr: 0.0.0.0:8080 # Path to WireGuard private key file private_key_path: private.key -# Path to a file containing a map of DERP nodes. -derp_map_path: derp.yaml +derp: + # List of externally available DERP maps encoded in JSON + urls: + - https://controlplane.tailscale.com/derpmap/default + + # Locally available DERP map files encoded in YAML + paths: + - derp-example.yaml + + # If enabled, a worker will be set up to periodically + # refresh the given sources and update the derpmap + # will be set up. + auto_update_enabled: true + + # How often should we check for updates? + update_frequency: 24h # Disables the automatic check for updates on startup disable_check_updates: false From c8e1afb14b9bf8db67b3cebdb1a90c184382ec3f Mon Sep 17 00:00:00 2001 From: Ward Vandewege Date: Sun, 24 Oct 2021 17:00:51 -0400 Subject: [PATCH 17/70] When attempting to unshare a node from the primary namespace, return errorMachineNotShared, not errorSameNamespace. Add test for same. --- sharing.go | 3 ++- sharing_test.go | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/sharing.go b/sharing.go index 879ed06f..5f6a8f45 100644 --- a/sharing.go +++ b/sharing.go @@ -43,7 +43,8 @@ func (h *Headscale) AddSharedMachineToNamespace(m *Machine, ns *Namespace) error // RemoveSharedMachineFromNamespace removes a shared machine from a namespace func (h *Headscale) RemoveSharedMachineFromNamespace(m *Machine, ns *Namespace) error { if m.NamespaceID == ns.ID { - return errorSameNamespace + // Can't unshare from primary namespace + return errorMachineNotShared } sharedMachine := SharedMachine{} diff --git a/sharing_test.go b/sharing_test.go index 140b05f2..1133fd92 100644 --- a/sharing_test.go +++ b/sharing_test.go @@ -86,6 +86,9 @@ func (s *Suite) TestUnshare(c *check.C) { err = h.RemoveSharedMachineFromNamespace(m2, n1) c.Assert(err, check.Equals, errorMachineNotShared) + + err = h.RemoveSharedMachineFromNamespace(m1, n1) + c.Assert(err, check.Equals, errorMachineNotShared) } func (s *Suite) TestAlreadyShared(c *check.C) { From dd7557850ec07e23d0c075998ffe1d84d9e3cf3b Mon Sep 17 00:00:00 2001 From: Ward Vandewege Date: Sun, 24 Oct 2021 17:02:57 -0400 Subject: [PATCH 18/70] cli changes for the `nodes` subcommand: * when listing nodes, a namespace is now optional, when it is not provided, all nodes are shown * when deleting, and sharing a node, remove the `namespace` flag, it was superfluous and unused * when unsharing a node, specify the namespace as an argument not a flag, making the UX the same as for sharing. Also refactor the share/unshare code to reuse the shared bits. --- cmd/headscale/cli/nodes.go | 149 ++++++++++++++++++------------------- 1 file changed, 72 insertions(+), 77 deletions(-) diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index c44aa5ed..954e338e 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -17,12 +17,13 @@ import ( func init() { rootCmd.AddCommand(nodeCmd) - nodeCmd.PersistentFlags().StringP("namespace", "n", "", "Namespace") - err := nodeCmd.MarkPersistentFlagRequired("namespace") + listNodesCmd.Flags().StringP("namespace", "n", "", "Namespace") + nodeCmd.AddCommand(listNodesCmd) + registerNodeCmd.Flags().StringP("namespace", "n", "", "Namespace") + err := registerNodeCmd.MarkFlagRequired("namespace") if err != nil { log.Fatalf(err.Error()) } - nodeCmd.AddCommand(listNodesCmd) nodeCmd.AddCommand(registerNodeCmd) nodeCmd.AddCommand(deleteNodeCmd) nodeCmd.AddCommand(shareMachineCmd) @@ -69,7 +70,7 @@ var registerNodeCmd = &cobra.Command{ var listNodesCmd = &cobra.Command{ Use: "list", - Short: "List the nodes in a given namespace", + Short: "List nodes", Run: func(cmd *cobra.Command, args []string) { n, err := cmd.Flags().GetString("namespace") if err != nil { @@ -82,23 +83,44 @@ var listNodesCmd = &cobra.Command{ log.Fatalf("Error initializing: %s", err) } - namespace, err := h.GetNamespace(n) - if err != nil { - log.Fatalf("Error fetching namespace: %s", err) + var namespaces []headscale.Namespace + var namespace *headscale.Namespace + var sharedMachines *[]headscale.Machine + if len(n) == 0 { + // no namespace provided, list all + tmp, err := h.ListNamespaces() + if err != nil { + log.Fatalf("Error fetching namespace: %s", err) + } + namespaces = *tmp + } else { + namespace, err = h.GetNamespace(n) + if err != nil { + log.Fatalf("Error fetching namespace: %s", err) + } + namespaces = append(namespaces, *namespace) + + sharedMachines, err = h.ListSharedMachinesInNamespace(n) + if err != nil { + log.Fatalf("Error fetching shared machines: %s", err) + } } - machines, err := h.ListMachinesInNamespace(n) - if err != nil { - log.Fatalf("Error fetching machines: %s", err) + var allMachines []headscale.Machine + for _, n := range namespaces { + machines, err := h.ListMachinesInNamespace(n.Name) + if err != nil { + log.Fatalf("Error fetching machines: %s", err) + } + allMachines = append(allMachines, *machines...) } - sharedMachines, err := h.ListSharedMachinesInNamespace(n) - if err != nil { - log.Fatalf("Error fetching shared machines: %s", err) + // listing sharedMachines is only relevant when a particular namespace is + // requested + if sharedMachines != nil { + allMachines = append(allMachines, *sharedMachines...) } - allMachines := append(*machines, *sharedMachines...) - if strings.HasPrefix(o, "json") { JsonOutput(allMachines, err, o) return @@ -108,7 +130,7 @@ var listNodesCmd = &cobra.Command{ log.Fatalf("Error getting nodes: %s", err) } - d, err := nodesToPtables(*namespace, allMachines) + d, err := nodesToPtables(namespace, allMachines) if err != nil { log.Fatalf("Error converting to table: %s", err) } @@ -176,6 +198,31 @@ var deleteNodeCmd = &cobra.Command{ }, } +func sharingWorker(cmd *cobra.Command, args []string) (*headscale.Headscale, string, *headscale.Machine, *headscale.Namespace) { + output, _ := cmd.Flags().GetString("output") + + h, err := getHeadscaleApp() + if err != nil { + log.Fatalf("Error initializing: %s", err) + } + + namespace, err := h.GetNamespace(args[1]) + if err != nil { + log.Fatalf("Error fetching namespace %s: %s", args[1], err) + } + + id, err := strconv.Atoi(args[0]) + if err != nil { + log.Fatalf("Error converting ID to integer: %s", err) + } + machine, err := h.GetMachineByID(uint64(id)) + if err != nil { + log.Fatalf("Error getting node: %s", err) + } + + return h, output, machine, namespace +} + var shareMachineCmd = &cobra.Command{ Use: "share ID namespace", Short: "Shares a node from the current namespace to the specified one", @@ -186,37 +233,8 @@ var shareMachineCmd = &cobra.Command{ return nil }, Run: func(cmd *cobra.Command, args []string) { - namespace, err := cmd.Flags().GetString("namespace") - if err != nil { - log.Fatalf("Error getting namespace: %s", err) - } - output, _ := cmd.Flags().GetString("output") - - h, err := getHeadscaleApp() - if err != nil { - log.Fatalf("Error initializing: %s", err) - } - - _, err = h.GetNamespace(namespace) - if err != nil { - log.Fatalf("Error fetching origin namespace: %s", err) - } - - destinationNamespace, err := h.GetNamespace(args[1]) - if err != nil { - log.Fatalf("Error fetching destination namespace: %s", err) - } - - id, err := strconv.Atoi(args[0]) - if err != nil { - log.Fatalf("Error converting ID to integer: %s", err) - } - machine, err := h.GetMachineByID(uint64(id)) - if err != nil { - log.Fatalf("Error getting node: %s", err) - } - - err = h.AddSharedMachineToNamespace(machine, destinationNamespace) + h, output, machine, namespace := sharingWorker(cmd, args) + err := h.AddSharedMachineToNamespace(machine, namespace) if strings.HasPrefix(output, "json") { JsonOutput(map[string]string{"Result": "Node shared"}, err, output) return @@ -231,41 +249,17 @@ var shareMachineCmd = &cobra.Command{ } var unshareMachineCmd = &cobra.Command{ - Use: "unshare ID", + Use: "unshare ID namespace", Short: "Unshares a node from the specified namespace", Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { + if len(args) < 2 { return fmt.Errorf("missing parameters") } return nil }, Run: func(cmd *cobra.Command, args []string) { - namespace, err := cmd.Flags().GetString("namespace") - if err != nil { - log.Fatalf("Error getting namespace: %s", err) - } - output, _ := cmd.Flags().GetString("output") - - h, err := getHeadscaleApp() - if err != nil { - log.Fatalf("Error initializing: %s", err) - } - - n, err := h.GetNamespace(namespace) - if err != nil { - log.Fatalf("Error fetching namespace: %s", err) - } - - id, err := strconv.Atoi(args[0]) - if err != nil { - log.Fatalf("Error converting ID to integer: %s", err) - } - machine, err := h.GetMachineByID(uint64(id)) - if err != nil { - log.Fatalf("Error getting node: %s", err) - } - - err = h.RemoveSharedMachineFromNamespace(machine, n) + h, output, machine, namespace := sharingWorker(cmd, args) + err := h.RemoveSharedMachineFromNamespace(machine, namespace) if strings.HasPrefix(output, "json") { JsonOutput(map[string]string{"Result": "Node unshared"}, err, output) return @@ -279,7 +273,7 @@ var unshareMachineCmd = &cobra.Command{ }, } -func nodesToPtables(currentNamespace headscale.Namespace, machines []headscale.Machine) (pterm.TableData, error) { +func nodesToPtables(currentNamespace *headscale.Namespace, machines []headscale.Machine) (pterm.TableData, error) { d := pterm.TableData{{"ID", "Name", "NodeKey", "Namespace", "IP address", "Ephemeral", "Last seen", "Online"}} for _, machine := range machines { @@ -307,9 +301,10 @@ func nodesToPtables(currentNamespace headscale.Namespace, machines []headscale.M } var namespace string - if currentNamespace.ID == machine.NamespaceID { + if (currentNamespace == nil) || (currentNamespace.ID == machine.NamespaceID) { namespace = pterm.LightMagenta(machine.Namespace.Name) } else { + // Shared into this namespace namespace = pterm.LightYellow(machine.Namespace.Name) } d = append(d, []string{strconv.FormatUint(machine.ID, 10), machine.Name, nodeKey.ShortString(), namespace, machine.IPAddress, strconv.FormatBool(ephemeral), lastSeenTime, online}) From 1d9954d8e9a247428121262383da2e6017a60bc7 Mon Sep 17 00:00:00 2001 From: Ward Vandewege Date: Sun, 24 Oct 2021 20:11:47 -0400 Subject: [PATCH 19/70] Fix integration test. --- integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_test.go b/integration_test.go index 53092423..a1a5e953 100644 --- a/integration_test.go +++ b/integration_test.go @@ -493,7 +493,7 @@ func (s *IntegrationTestSuite) TestSharedNodes() { result, err := executeCommand( &headscale, - []string{"headscale", "nodes", "share", "--namespace", "shared", fmt.Sprint(machine.ID), "main"}, + []string{"headscale", "nodes", "share", fmt.Sprint(machine.ID), "main"}, []string{}, ) assert.Nil(s.T(), err) From 4d3b638a3d2ce4d377cb2cdfef018812e7b0e0cf Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 25 Oct 2021 19:38:11 +0100 Subject: [PATCH 20/70] Add note about main containing unreleased changes #201 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a3c09396..91e13b92 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ An open source, self-hosted implementation of the Tailscale coordination server. Join our [Discord](https://discord.gg/XcQxk2VHjx) 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. The `main` branch might contain unreleased changes. + ## Overview Tailscale is [a modern VPN](https://tailscale.com/) built on top of [Wireguard](https://www.wireguard.com/). It [works like an overlay network](https://tailscale.com/blog/how-tailscale-works/) between the computers of your networks - using all kinds of [NAT traversal sorcery](https://tailscale.com/blog/how-nat-traversal-works/). From f9ece0087d0e0cbbf1ae726cce2eed340d53ed91 Mon Sep 17 00:00:00 2001 From: Ward Vandewege Date: Tue, 26 Oct 2021 08:50:25 -0400 Subject: [PATCH 21/70] Make the cli help a little more explicit for the nodes subcommand. --- cmd/headscale/cli/nodes.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index 954e338e..ba843f70 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -17,9 +17,9 @@ import ( func init() { rootCmd.AddCommand(nodeCmd) - listNodesCmd.Flags().StringP("namespace", "n", "", "Namespace") + listNodesCmd.Flags().StringP("namespace", "n", "", "Filter by namespace") nodeCmd.AddCommand(listNodesCmd) - registerNodeCmd.Flags().StringP("namespace", "n", "", "Namespace") + registerNodeCmd.Flags().StringP("namespace", "n", "", "Filter by namespace") err := registerNodeCmd.MarkFlagRequired("namespace") if err != nil { log.Fatalf(err.Error()) From b096a2e7e50b4eca45caabeacc94a3a9fb8b9191 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 26 Oct 2021 20:37:37 +0000 Subject: [PATCH 22/70] Create an initial gRPC service This commit adds protobuf files and tooling surrounding generating APIs and datatypes. --- .gitignore | 3 ++ buf.gen.yaml | 21 ++++++++++++ proto/buf.lock | 24 ++++++++++++++ proto/buf.yaml | 12 +++++++ proto/v1/headscale.proto | 71 ++++++++++++++++++++++++++++++++++++++++ tools.go | 12 +++++++ 6 files changed, 143 insertions(+) create mode 100644 buf.gen.yaml create mode 100644 proto/buf.lock create mode 100644 proto/buf.yaml create mode 100644 proto/v1/headscale.proto create mode 100644 tools.go diff --git a/.gitignore b/.gitignore index 610550b9..f45c47a5 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ config.yaml .idea test_output/ + +# Protobuf generated code +gen/ diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 00000000..d7b832ab --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,21 @@ +version: v1 +plugins: + - name: go + out: gen/go + opt: + - paths=source_relative + - name: go-grpc + out: gen/go + opt: + - paths=source_relative + - name: grpc-gateway + out: gen/go + opt: + - paths=source_relative + - generate_unbound_methods=true + # - name: gorm + # out: gen/go + # opt: + # - paths=source_relative,enums=string,gateway=true + - name: openapiv2 + out: gen/openapiv2 diff --git a/proto/buf.lock b/proto/buf.lock new file mode 100644 index 00000000..03cd7b89 --- /dev/null +++ b/proto/buf.lock @@ -0,0 +1,24 @@ +# Generated by buf. DO NOT EDIT. +version: v1 +deps: + - remote: buf.build + owner: googleapis + repository: googleapis + branch: main + commit: cd101b0abb7b4404a0b1ecc1afd4ce10 + digest: b1-H4GHwHVHcJBbVPg-Cdmnx812reFCDQws_QoQ0W2hYQA= + create_time: 2021-10-23T15:04:06.087748Z + - remote: buf.build + owner: grpc-ecosystem + repository: grpc-gateway + branch: main + commit: ff83506eb9cc4cf8972f49ce87e6ed3e + digest: b1-iLPHgLaoeWWinMiXXqPnxqE4BThtY3eSbswVGh9GOGI= + create_time: 2021-10-23T16:26:52.283938Z + - remote: buf.build + owner: ufoundit-dev + repository: protoc-gen-gorm + branch: main + commit: e2ecbaa0d37843298104bd29fd866df8 + digest: b1-SV9yKH_8P-IKTOlHZxP-bb0ALANYeEqH_mtPA0EWfLc= + create_time: 2021-10-08T06:03:05.64876Z diff --git a/proto/buf.yaml b/proto/buf.yaml new file mode 100644 index 00000000..7e524ba0 --- /dev/null +++ b/proto/buf.yaml @@ -0,0 +1,12 @@ +version: v1 +lint: + use: + - DEFAULT +breaking: + use: + - FILE + +deps: + - buf.build/googleapis/googleapis + - buf.build/grpc-ecosystem/grpc-gateway + - buf.build/ufoundit-dev/protoc-gen-gorm diff --git a/proto/v1/headscale.proto b/proto/v1/headscale.proto new file mode 100644 index 00000000..b6356b8f --- /dev/null +++ b/proto/v1/headscale.proto @@ -0,0 +1,71 @@ +syntax = "proto3"; +package headscale.v1; +option go_package = "github.com/juanfont/headscale/gen/go/v1"; + +import "google/protobuf/timestamp.proto"; +import "google/api/annotations.proto"; +import "options/gorm.proto"; + +enum RegisterMethod { + AUTH_KEY = 0; + CLI = 1; + OIDC = 2; +} + +message Namespace { + string Name = 1; +} + +message PreAuthKey { + uint64 ID = 1; + string Key = 2; + uint32 NamespaceID = 3; + Namespace Namespace = 4; + bool Reusable = 5; + bool Ephemeral = 6; + bool Used = 7; + + google.protobuf.Timestamp CreatedAt = 8; + google.protobuf.Timestamp Expiration = 9; +} + +message GetMachineRequest { + uint64 machine_id = 1; +} + +message Machine { + option(gorm.opts).ormable = true; + uint64 ID = 1; + string MachineKey = 2; + string NodeKey = 3; + string DiscoKey = 4; + string IPAddress = 5; + string Name = 6; + uint32 NamespaceID = 7; + + bool Registered = 8; + RegisterMethod RegisterMethod = 9; + uint32 AuthKeyID = 10; + PreAuthKey AuthKey = 11; + + google.protobuf.Timestamp LastSeen = 12; + google.protobuf.Timestamp LastSuccessfulUpdate = 13; + google.protobuf.Timestamp Expiry = 14; + + bytes HostInfo = 15; + bytes Endpoints = 16; + bytes EnabledRoutes = 17; + + google.protobuf.Timestamp CreatedAt = 18; + google.protobuf.Timestamp UpdatedAt = 19; + google.protobuf.Timestamp DeletedAt = 20; +} + +// Gin Router will prefix this with /api/v1 +service HeadscaleService { + rpc GetMachine(GetMachineRequest) returns(Machine) { + option(google.api.http) = { + get : "/api/v1/machine/{machine_id}" + }; + } +} diff --git a/tools.go b/tools.go new file mode 100644 index 00000000..287c1230 --- /dev/null +++ b/tools.go @@ -0,0 +1,12 @@ +//go:build tools +// +build tools + +package tools + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway" + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2" + _ "github.com/infobloxopen/protoc-gen-gorm" + _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" + _ "google.golang.org/protobuf/cmd/protoc-gen-go" +) From a9da7c8fd9e59c4320fe4a2d2fc7a7406ba0f59f Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 26 Oct 2021 20:41:35 +0000 Subject: [PATCH 23/70] Update go.mod --- go.mod | 10 ++++++- go.sum | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 65165e0d..002e3ede 100644 --- a/go.mod +++ b/go.mod @@ -10,12 +10,14 @@ require ( github.com/docker/cli v20.10.8+incompatible // indirect github.com/docker/docker v20.10.8+incompatible // indirect github.com/efekarakus/termcolor v1.0.1 - github.com/fatih/set v0.2.1 // indirect + github.com/fatih/set v0.2.1 github.com/gin-gonic/gin v1.7.4 github.com/gofrs/uuid v4.0.0+incompatible github.com/google/go-github v17.0.0+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b + github.com/infobloxopen/protoc-gen-gorm v1.0.1 github.com/klauspost/compress v1.13.5 github.com/lib/pq v1.10.3 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect @@ -24,6 +26,7 @@ require ( github.com/prometheus/client_golang v1.11.0 github.com/pterm/pterm v0.12.30 github.com/rs/zerolog v1.25.0 + github.com/soheilhy/cmux v0.1.5 github.com/spf13/cobra v1.2.1 github.com/spf13/viper v1.8.1 github.com/stretchr/testify v1.7.0 @@ -33,7 +36,12 @@ require ( github.com/zsais/go-gin-prometheus v0.1.0 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 golang.org/x/net v0.0.0-20210913180222-943fd674d43e // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 // indirect + google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 + google.golang.org/grpc v1.40.0 + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 + google.golang.org/protobuf v1.27.1 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/yaml.v2 v2.4.0 gorm.io/datatypes v1.0.2 diff --git a/go.sum b/go.sum index b429ca95..9e31f2fe 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +contrib.go.opencensus.io/exporter/ocagent v0.7.0/go.mod h1:IshRmMJBhDfFj5Y67nVhMYTTIze91RUeT73ipWKs/GY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AlecAivazis/survey/v2 v2.3.2 h1:TqTB+aDDCLYhf9/bD2TwSO8u8jDSmMUd2SUVO4gCnU8= github.com/AlecAivazis/survey/v2 v2.3.2/go.mod h1:TH2kPCDU3Kqq7pLbnCWwZXDBjnhZtmsCle5EiYDJ2fg= @@ -46,6 +47,7 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/Djarvur/go-err113 v0.0.0-20200511133814-5174e21577d5/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/Djarvur/go-err113 v0.1.0/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= @@ -70,6 +72,7 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEV github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= +github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= @@ -84,6 +87,7 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -105,12 +109,14 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/bombsimon/wsl/v3 v3.1.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= +github.com/bufbuild/buf v0.37.0/go.mod h1:lQ1m2HkIaGOFba6w/aC3KYBHhKEOESP3gaAEpS3dAFM= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= @@ -134,6 +140,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= @@ -168,9 +175,12 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denis-tingajkin/go-header v0.3.1/go.mod h1:sq/2IxMhaZX+RRcgHfCRx/m0M5na0fBt4/CRe7Lrji0= +github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.10.0 h1:QykgLZBorFE95+gO3u9esLd0BmbvpWp0/waNNZfHBM8= github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.1-0.20200107013213-dc14462fd587+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/cli v20.10.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.8+incompatible h1:/zO/6y9IOpcehE49yMRTV9ea0nBpb8OeqSskXLNfH1E= @@ -199,7 +209,10 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= @@ -214,6 +227,7 @@ github.com/frankban/quicktest v1.13.0/go.mod h1:qLE0fzW0VuyUAJgPU19zByoIr0HtCHN/ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -249,6 +263,7 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -278,6 +293,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -370,6 +387,7 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gookit/color v1.3.1/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ= @@ -377,6 +395,7 @@ github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/goreleaser/chglog v0.1.2/go.mod h1:tTZsFuSZK4epDXfjMkxzcGbrIOXprf0JFp47BjIr3B8= github.com/goreleaser/fileglob v0.3.1/go.mod h1:kNcPrPzjCp+Ox3jmXLU5QEsjhqrtLBm6OnXAif8KRl8= github.com/goreleaser/nfpm v1.10.3/go.mod h1:EEC7YD5wi+ol0MiAshpgPANBOkjXDl7wqTLVk68OBsk= @@ -394,10 +413,17 @@ github.com/gostaticanalysis/comment v1.3.0/go.mod h1:xMicKDx7XRXYdVwY9f9wQpDJVnq github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.3.0/go.mod h1:d2gYTOTUQklu06xp0AJYYmRdTVU1VKrqhkYfYag2L08= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.4.0/go.mod h1:IOyTYjcIO0rkmnGBfJTL0NJ11exy/Tc2QEuv7hCXp24= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 h1:rgxjzoDmDXw5q8HONgyHhBas4to0/XWRo/gPpJhsUNQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0/go.mod h1:qrJPVzv9YlhsrxJc3P/Q85nr0w1lIRikTl4JlhdDH5w= github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4= github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -441,6 +467,9 @@ github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/infobloxopen/atlas-app-toolkit v0.24.1-0.20210416193901-4c7518b07e08/go.mod h1:9BTHnpff654rY1J8KxSUOLJ+ZUDn2Vi3mmk26gQDo1M= +github.com/infobloxopen/protoc-gen-gorm v1.0.1 h1:IjvQ02gZSll+CjpWjxkLqrpxnvKAGfs5dXRJEpfZx2s= +github.com/infobloxopen/protoc-gen-gorm v1.0.1/go.mod h1:gTu86stnDQXwcNqLG9WNJfl3IPUIhxmGNqJ8z4826uo= github.com/insomniacslk/dhcp v0.0.0-20210621130208-1cac67f12b1e/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= @@ -508,9 +537,13 @@ github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jgautheron/goconst v0.0.0-20201117150253-ccae5bf973f3/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jhump/protoreflect v1.8.1/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s= +github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= +github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= @@ -555,9 +588,11 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4= github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -580,8 +615,10 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.1-0.20200116171513-9eb3fc897d6f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg= github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -591,6 +628,7 @@ github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQ github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -620,7 +658,9 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU= github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= @@ -690,6 +730,7 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nishanths/exhaustive v0.1.0/go.mod h1:S1j9110vxV1ECdCudXRkeMnFQ/DQk9ajLT0Uf2MYZQQ= +github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= @@ -745,6 +786,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.0/go.mod h1:41g+FIPlQUTDCveupEmEA65IoiQFrtgCeDopC4ajGIM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -835,6 +877,7 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= @@ -842,6 +885,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= @@ -856,6 +901,7 @@ github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.0.1-0.20201006035406-b97b5ead31f7/go.mod h1:yk5b0mALVusDL5fMM6Rd1wgnoO5jUPhwsQ6LQAJTidQ= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= @@ -911,6 +957,7 @@ github.com/tomarrell/wrapcheck v0.0.0-20200807122107-df9e8bcb914d/go.mod h1:yiFB github.com/tomarrell/wrapcheck v0.0.0-20201130113247-1683564d9756/go.mod h1:yiFB6fFoV7saXirUGfuK+cPtUh4NX/Hf5y2WC2lehu0= github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig= github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= +github.com/twitchtv/twirp v7.1.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= @@ -966,7 +1013,9 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -980,6 +1029,7 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg= go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc= @@ -1001,6 +1051,7 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1025,6 +1076,7 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1050,6 +1102,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1073,6 +1126,7 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1081,6 +1135,7 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -1094,6 +1149,7 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -1117,6 +1173,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1127,6 +1185,7 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1176,6 +1235,7 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1304,11 +1364,13 @@ golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200624225443-88f3c62a19ff/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -1357,6 +1419,7 @@ google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.25.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= @@ -1396,26 +1459,34 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210224155714-063164c882e6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210426193834-eac7f76ac494/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83 h1:3V2dxSZpz4zozWWUq36vUxXEKnSYitEH2LdsAx+RUmg= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1437,10 +1508,19 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0-dev.0.20201218190559-666aea1fb34c/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc/examples v0.0.0-20210309220351-d5b628860d4e/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= +google.golang.org/grpc/examples v0.0.0-20210601155443-8bdcb4c9ab8d/go.mod h1:bF8wuZSAZTcbF7ZPKrDI/qY52toTP/yxLpRRY4Eu9Js= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1451,9 +1531,12 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.25.1-0.20201208041424-160c7477e0e8/go.mod h1:hFxJC2f0epmp1elRCiEGJTKAWbwxZ2nvqZdHl3FQXCY= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From caa4d33cbd1d3db42c21fd59ca72b3f469f41b3e Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 26 Oct 2021 20:42:20 +0000 Subject: [PATCH 24/70] Add an initial grpcv1 service (implementing the proto generated service) --- grpcv1.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 grpcv1.go diff --git a/grpcv1.go b/grpcv1.go new file mode 100644 index 00000000..d9bbf531 --- /dev/null +++ b/grpcv1.go @@ -0,0 +1,33 @@ +package headscale + +import ( + "context" + + apiV1 "github.com/juanfont/headscale/gen/go/v1" +) + +type headscaleV1APIServer struct { // apiV1.HeadscaleServiceServer + apiV1.UnimplementedHeadscaleServiceServer + h *Headscale +} + +func newHeadscaleV1APIServer(h *Headscale) apiV1.HeadscaleServiceServer { + return headscaleV1APIServer{ + h: h, + } +} + +func (api headscaleV1APIServer) GetMachine( + ctx context.Context, + request *apiV1.GetMachineRequest, +) (*apiV1.Machine, error) { + m, err := api.h.GetMachineByID(request.MachineId) + if err != nil { + return nil, err + } + + // TODO(kradalby): Make this function actually do something + return &apiV1.Machine{Name: m.Name}, nil +} + +func (api headscaleV1APIServer) mustEmbedUnimplementedHeadscaleServiceServer() {} From 2f045b20fbaf630d6bbeb0c28ad62856d014653a Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 26 Oct 2021 20:42:56 +0000 Subject: [PATCH 25/70] Refactor tls and wire up grpc, grpc gateway/api This commit moves the TLS configuration into a seperate function. It also wires up the gRPC interface and prepares handing the API endpoints to the grpc gateway. --- app.go | 127 +++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 105 insertions(+), 22 deletions(-) diff --git a/app.go b/app.go index 546eb866..dbe30168 100644 --- a/app.go +++ b/app.go @@ -1,8 +1,11 @@ package headscale import ( + "context" + "crypto/tls" "errors" "fmt" + "net" "net/http" "net/url" "os" @@ -11,12 +14,16 @@ import ( "sync" "time" - "github.com/rs/zerolog/log" - "github.com/gin-gonic/gin" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + apiV1 "github.com/juanfont/headscale/gen/go/v1" + "github.com/rs/zerolog/log" + "github.com/soheilhy/cmux" ginprometheus "github.com/zsais/go-gin-prometheus" "golang.org/x/crypto/acme" "golang.org/x/crypto/acme/autocert" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc" "gorm.io/gorm" "inet.af/netaddr" "tailscale.com/tailcfg" @@ -24,7 +31,7 @@ import ( "tailscale.com/types/wgkey" ) -// Config contains the initial Headscale configuration +// Config contains the initial Headscale configuration. type Config struct { ServerURL string Addr string @@ -64,7 +71,7 @@ type DERPConfig struct { UpdateFrequency time.Duration } -// Headscale represents the base app of the service +// Headscale represents the base app of the service. type Headscale struct { cfg Config db *gorm.DB @@ -82,12 +89,13 @@ type Headscale struct { lastStateChange sync.Map } -// NewHeadscale returns the Headscale app +// NewHeadscale returns the Headscale app. func NewHeadscale(cfg Config) (*Headscale, error) { content, err := os.ReadFile(cfg.PrivateKeyPath) if err != nil { return nil, err } + privKey, err := wgkey.ParsePrivate(string(content)) if err != nil { return nil, err @@ -136,14 +144,14 @@ func NewHeadscale(cfg Config) (*Headscale, error) { return &h, nil } -// Redirect to our TLS url +// Redirect to our TLS url. func (h *Headscale) redirect(w http.ResponseWriter, req *http.Request) { target := h.cfg.ServerURL + req.URL.RequestURI() http.Redirect(w, req, target, http.StatusFound) } // expireEphemeralNodes deletes ephemeral machine records that have not been -// seen for longer than h.cfg.EphemeralNodeInactivityTimeout +// seen for longer than h.cfg.EphemeralNodeInactivityTimeout. func (h *Headscale) expireEphemeralNodes(milliSeconds int64) { ticker := time.NewTicker(time.Duration(milliSeconds) * time.Millisecond) for range ticker.C { @@ -155,18 +163,23 @@ func (h *Headscale) expireEphemeralNodesWorker() { namespaces, err := h.ListNamespaces() if err != nil { log.Error().Err(err).Msg("Error listing namespaces") + return } + for _, ns := range *namespaces { machines, err := h.ListMachinesInNamespace(ns.Name) if err != nil { log.Error().Err(err).Str("namespace", ns.Name).Msg("Error listing machines in namespace") + return } + for _, m := range *machines { if m.AuthKey != nil && m.LastSeen != nil && m.AuthKey.Ephemeral && time.Now().After(m.LastSeen.Add(h.cfg.EphemeralNodeInactivityTimeout)) { log.Info().Str("machine", m.Name).Msg("Ephemeral client removed from database") + err = h.db.Unscoped().Delete(m).Error if err != nil { log.Error(). @@ -176,12 +189,13 @@ func (h *Headscale) expireEphemeralNodesWorker() { } } } + h.setLastStateChangeToNow(ns.Name) } } // WatchForKVUpdates checks the KV DB table for requests to perform tailnet upgrades -// This is a way to communitate the CLI with the headscale server +// This is a way to communitate the CLI with the headscale server. func (h *Headscale) watchForKVUpdates(milliSeconds int64) { ticker := time.NewTicker(time.Duration(milliSeconds) * time.Millisecond) for range ticker.C { @@ -194,24 +208,60 @@ func (h *Headscale) watchForKVUpdatesWorker() { // more functions will come here in the future } -// Serve launches a GIN server with the Headscale API +// Serve launches a GIN server with the Headscale API. func (h *Headscale) Serve() error { + var err error + + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + + defer cancel() + + l, err := net.Listen("tcp", h.cfg.Addr) + if err != nil { + panic(err) + } + + // Create the cmux object that will multiplex 2 protocols on the same port. + // The two following listeners will be served on the same port below gracefully. + m := cmux.New(l) + // Match gRPC requests here + grpcListener := m.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc")) + // Otherwise match regular http requests. + httpListener := m.Match(cmux.Any()) + + // Now create the grpc server with those options. + grpcServer := grpc.NewServer() + + // TODO(kradalby): register the new server when we have authentication ready + // apiV1.RegisterHeadscaleServiceServer(grpcServer, newHeadscaleV1APIServer(h)) + + grpcGatewayMux := runtime.NewServeMux() + + opts := []grpc.DialOption{grpc.WithInsecure()} + + err = apiV1.RegisterHeadscaleServiceHandlerFromEndpoint(ctx, grpcGatewayMux, h.cfg.Addr, opts) + if err != nil { + return err + } + r := gin.Default() p := ginprometheus.NewPrometheus("gin") p.Use(r) - r.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{"healthy": "ok"}) }) + r.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"healthy": "ok"}) }) r.GET("/key", h.KeyHandler) r.GET("/register", h.RegisterWebAPI) r.POST("/machine/:id/map", h.PollNetMapHandler) r.POST("/machine/:id", h.RegistrationHandler) r.GET("/apple", h.AppleMobileConfig) r.GET("/apple/:platform", h.ApplePlatformConfig) - var err error - go h.watchForKVUpdates(5000) - go h.expireEphemeralNodes(5000) + r.Any("/api/v1/*any", gin.WrapF(grpcGatewayMux.ServeHTTP)) + r.StaticFile("/swagger/swagger.json", "gen/openapiv2/v1/headscale.swagger.json") + + updateMillisecondsWait := int64(5000) // Fetch an initial DERP Map before we start serving h.DERPMap = GetDERPMap(h.cfg.DERP) @@ -222,7 +272,11 @@ func (h *Headscale) Serve() error { go h.scheduledDERPMapUpdateWorker(derpMapCancelChannel) } - s := &http.Server{ + // I HATE THIS + go h.watchForKVUpdates(updateMillisecondsWait) + go h.expireEphemeralNodes(updateMillisecondsWait) + + httpServer := &http.Server{ Addr: h.cfg.Addr, Handler: r, ReadTimeout: 30 * time.Second, @@ -233,6 +287,29 @@ func (h *Headscale) Serve() error { WriteTimeout: 0, } + tlsConfig, err := h.getTLSSettings() + if err != nil { + log.Error().Err(err).Msg("Failed to set up TLS configuration") + + return err + } + + if tlsConfig != nil { + httpServer.TLSConfig = tlsConfig + } + + g := new(errgroup.Group) + + g.Go(func() error { return grpcServer.Serve(grpcListener) }) + g.Go(func() error { return httpServer.Serve(httpListener) }) + g.Go(func() error { return m.Serve() }) + + log.Info().Msgf("listening and serving (multiplexed HTTP and gRPC) on: %s", h.cfg.Addr) + + return g.Wait() +} + +func (h *Headscale) getTLSSettings() (*tls.Config, error) { if h.cfg.TLSLetsEncryptHostname != "" { if !strings.HasPrefix(h.cfg.ServerURL, "https://") { log.Warn().Msg("Listening with TLS but ServerURL does not start with https://") @@ -248,13 +325,11 @@ func (h *Headscale) Serve() error { Email: h.cfg.ACMEEmail, } - s.TLSConfig = m.TLSConfig() - if h.cfg.TLSLetsEncryptChallengeType == "TLS-ALPN-01" { // Configuration via autocert with TLS-ALPN-01 (https://tools.ietf.org/html/rfc8737) // The RFC requires that the validation is done on port 443; in other words, headscale // must be reachable on port 443. - err = s.ListenAndServeTLS("", "") + return m.TLSConfig(), nil } else if h.cfg.TLSLetsEncryptChallengeType == "HTTP-01" { // Configuration via autocert with HTTP-01. This requires listening on // port 80 for the certificate validation in addition to the headscale @@ -264,22 +339,30 @@ func (h *Headscale) Serve() error { Err(http.ListenAndServe(h.cfg.TLSLetsEncryptListen, m.HTTPHandler(http.HandlerFunc(h.redirect)))). Msg("failed to set up a HTTP server") }() - err = s.ListenAndServeTLS("", "") + + return m.TLSConfig(), nil } else { - return errors.New("unknown value for TLSLetsEncryptChallengeType") + return nil, errors.New("unknown value for TLSLetsEncryptChallengeType") } } else if h.cfg.TLSCertPath == "" { if !strings.HasPrefix(h.cfg.ServerURL, "http://") { log.Warn().Msg("Listening without TLS but ServerURL does not start with http://") } - err = s.ListenAndServe() + + return nil, nil } else { if !strings.HasPrefix(h.cfg.ServerURL, "https://") { log.Warn().Msg("Listening with TLS but ServerURL does not start with https://") } - err = s.ListenAndServeTLS(h.cfg.TLSCertPath, h.cfg.TLSKeyPath) + var err error + tlsConfig := &tls.Config{} + tlsConfig.ClientAuth = tls.RequireAnyClientCert + tlsConfig.NextProtos = []string{"http/1.1"} + tlsConfig.Certificates = make([]tls.Certificate, 1) + tlsConfig.Certificates[0], err = tls.LoadX509KeyPair(h.cfg.TLSCertPath, h.cfg.TLSKeyPath) + + return tlsConfig, err } - return err } func (h *Headscale) setLastStateChangeToNow(namespace string) { From b8c89cd63ca413bcce3a217779d8fb2f7aabc62a Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 26 Oct 2021 20:53:10 +0000 Subject: [PATCH 26/70] Add readme and makefile entry about code generation --- Makefile | 5 ++++- README.md | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 755253fc..4c1d6134 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # Calculate version version = $(shell ./scripts/version-at-commit.sh) -build: +build: generate go build -ldflags "-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$(version)" cmd/headscale/headscale.go dev: lint test build @@ -25,3 +25,6 @@ lint: compress: build upx --brute headscale +generate: + rm -rf gen + buf generate proto diff --git a/README.md b/README.md index 91e13b92..4e373315 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,41 @@ Please have a look at the documentation under [`docs/`](docs/). 1. We have nothing to do with Tailscale, or Tailscale Inc. 2. The purpose of writing this was to learn how Tailscale works. +## Contributing + +To contribute to Headscale you would need the lastest version of [Go](golang.org) and [Buf](https://buf.build)(Protobuf generator). + +### Install development tools + +- Go +- Buf +- Protobuf tools: + +```shell +go install \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \ + google.golang.org/protobuf/cmd/protoc-gen-go \ + google.golang.org/grpc/cmd/protoc-gen-go-grpc +``` + +Building the project requires the generation of Go code from Protobuf (in `proto/`) and it can be (re-)generated with: + +```shell +make generate +``` + +To run the tests: + +```shell +make test +``` + +To build the program: + +```shell +make build +``` ## Contributors From 11d987549fb0d234e1695325d9b27c8e15595386 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 26 Oct 2021 21:34:51 +0000 Subject: [PATCH 27/70] Ignore generated files for docker --- .dockerignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index f90134b3..b7a5c8a8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -14,4 +14,4 @@ docker-compose* README.md LICENSE .vscode - +gen/ From 6e764942a2c084e803c26ad749b0b384cc86e0e0 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 26 Oct 2021 21:35:18 +0000 Subject: [PATCH 28/70] Add grpc step to dockerfile --- Dockerfile | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6e216aad..ba248d35 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,23 @@ +FROM bufbuild/buf:1.0.0-rc6 as buf + FROM golang:1.17.1-bullseye AS build ENV GOPATH /go +COPY --from=buf /usr/local/bin/buf /usr/local/bin/buf + COPY go.mod go.sum /go/src/headscale/ WORKDIR /go/src/headscale RUN go mod download -COPY . /go/src/headscale +COPY . . + +RUN go install \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \ + google.golang.org/protobuf/cmd/protoc-gen-go \ + google.golang.org/grpc/cmd/protoc-gen-go-grpc + +RUN buf generate proto RUN go install -a -ldflags="-extldflags=-static" -tags netgo,sqlite_omit_load_extension ./cmd/headscale RUN test -e /go/bin/headscale From 8f2ef6a57de373cdeb5fb2236dbfd233bb57a15f Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 27 Oct 2021 06:40:39 +0000 Subject: [PATCH 29/70] Prepare for checking in generated code --- .dockerignore | 1 - .gitignore | 3 --- Dockerfile | 12 +----------- Makefile | 7 +++++++ README.md | 11 +++++------ 5 files changed, 13 insertions(+), 21 deletions(-) diff --git a/.dockerignore b/.dockerignore index b7a5c8a8..33f9aea2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -14,4 +14,3 @@ docker-compose* README.md LICENSE .vscode -gen/ diff --git a/.gitignore b/.gitignore index f45c47a5..610550b9 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,3 @@ config.yaml .idea test_output/ - -# Protobuf generated code -gen/ diff --git a/Dockerfile b/Dockerfile index ba248d35..9590070b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,23 +2,13 @@ FROM bufbuild/buf:1.0.0-rc6 as buf FROM golang:1.17.1-bullseye AS build ENV GOPATH /go - -COPY --from=buf /usr/local/bin/buf /usr/local/bin/buf +WORKDIR /go/src/headscale COPY go.mod go.sum /go/src/headscale/ -WORKDIR /go/src/headscale RUN go mod download COPY . . -RUN go install \ - github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \ - github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \ - google.golang.org/protobuf/cmd/protoc-gen-go \ - google.golang.org/grpc/cmd/protoc-gen-go-grpc - -RUN buf generate proto - RUN go install -a -ldflags="-extldflags=-static" -tags netgo,sqlite_omit_load_extension ./cmd/headscale RUN test -e /go/bin/headscale diff --git a/Makefile b/Makefile index 4c1d6134..3ce7025c 100644 --- a/Makefile +++ b/Makefile @@ -28,3 +28,10 @@ compress: build generate: rm -rf gen buf generate proto + +install-protobuf-plugins: + go install \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \ + google.golang.org/protobuf/cmd/protoc-gen-go \ + google.golang.org/grpc/cmd/protoc-gen-go-grpc diff --git a/README.md b/README.md index 4e373315..060824b9 100644 --- a/README.md +++ b/README.md @@ -71,18 +71,17 @@ To contribute to Headscale you would need the lastest version of [Go](golang.org - Protobuf tools: ```shell -go install \ - github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \ - github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \ - google.golang.org/protobuf/cmd/protoc-gen-go \ - google.golang.org/grpc/cmd/protoc-gen-go-grpc +make install-protobuf-plugins ``` -Building the project requires the generation of Go code from Protobuf (in `proto/`) and it can be (re-)generated with: +### Testing and building + +Some parts of the project requires the generation of Go code from Protobuf (if changes is made in `proto/`) and it must be (re-)generated with: ```shell make generate ``` +**Note**: Please check in changes from `gen/` in a separate commit to make it easier to review. To run the tests: From d4265779ef81fb083fb97ccf73e5b74e3601b2f4 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 27 Oct 2021 06:44:04 +0000 Subject: [PATCH 30/70] Check in generated code This does not have to be reviewed, here is some reasoning: Go (and go mod) is designed for having code available and we need to check in the generated code to make sure it is "go gettable". If we dont we give ourselves a headache trying to setup all the ci, tests etc to install and generate the code before it runs. Because the code isnt there, the plugins needed to generate the code fail to install... I didnt find any good documentation for this, but there is this github comment: https://github.com/golang/go/issues/34514#issuecomment-535406759 --- gen/go/v1/headscale.pb.go | 702 ++++++++++++++++++++++++ gen/go/v1/headscale.pb.gw.go | 185 +++++++ gen/go/v1/headscale_grpc.pb.go | 101 ++++ gen/openapiv2/v1/headscale.swagger.json | 210 +++++++ 4 files changed, 1198 insertions(+) create mode 100644 gen/go/v1/headscale.pb.go create mode 100644 gen/go/v1/headscale.pb.gw.go create mode 100644 gen/go/v1/headscale_grpc.pb.go create mode 100644 gen/openapiv2/v1/headscale.swagger.json diff --git a/gen/go/v1/headscale.pb.go b/gen/go/v1/headscale.pb.go new file mode 100644 index 00000000..104e3746 --- /dev/null +++ b/gen/go/v1/headscale.pb.go @@ -0,0 +1,702 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: v1/headscale.proto + +package v1 + +import ( + _ "github.com/infobloxopen/protoc-gen-gorm/options" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type RegisterMethod int32 + +const ( + RegisterMethod_AUTH_KEY RegisterMethod = 0 + RegisterMethod_CLI RegisterMethod = 1 + RegisterMethod_OIDC RegisterMethod = 2 +) + +// Enum value maps for RegisterMethod. +var ( + RegisterMethod_name = map[int32]string{ + 0: "AUTH_KEY", + 1: "CLI", + 2: "OIDC", + } + RegisterMethod_value = map[string]int32{ + "AUTH_KEY": 0, + "CLI": 1, + "OIDC": 2, + } +) + +func (x RegisterMethod) Enum() *RegisterMethod { + p := new(RegisterMethod) + *p = x + return p +} + +func (x RegisterMethod) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (RegisterMethod) Descriptor() protoreflect.EnumDescriptor { + return file_v1_headscale_proto_enumTypes[0].Descriptor() +} + +func (RegisterMethod) Type() protoreflect.EnumType { + return &file_v1_headscale_proto_enumTypes[0] +} + +func (x RegisterMethod) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use RegisterMethod.Descriptor instead. +func (RegisterMethod) EnumDescriptor() ([]byte, []int) { + return file_v1_headscale_proto_rawDescGZIP(), []int{0} +} + +type Namespace struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` +} + +func (x *Namespace) Reset() { + *x = Namespace{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_headscale_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Namespace) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Namespace) ProtoMessage() {} + +func (x *Namespace) ProtoReflect() protoreflect.Message { + mi := &file_v1_headscale_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Namespace.ProtoReflect.Descriptor instead. +func (*Namespace) Descriptor() ([]byte, []int) { + return file_v1_headscale_proto_rawDescGZIP(), []int{0} +} + +func (x *Namespace) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type PreAuthKey struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` + Key string `protobuf:"bytes,2,opt,name=Key,proto3" json:"Key,omitempty"` + NamespaceID uint32 `protobuf:"varint,3,opt,name=NamespaceID,proto3" json:"NamespaceID,omitempty"` + Namespace *Namespace `protobuf:"bytes,4,opt,name=Namespace,proto3" json:"Namespace,omitempty"` + Reusable bool `protobuf:"varint,5,opt,name=Reusable,proto3" json:"Reusable,omitempty"` + Ephemeral bool `protobuf:"varint,6,opt,name=Ephemeral,proto3" json:"Ephemeral,omitempty"` + Used bool `protobuf:"varint,7,opt,name=Used,proto3" json:"Used,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"` + Expiration *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=Expiration,proto3" json:"Expiration,omitempty"` +} + +func (x *PreAuthKey) Reset() { + *x = PreAuthKey{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_headscale_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PreAuthKey) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PreAuthKey) ProtoMessage() {} + +func (x *PreAuthKey) ProtoReflect() protoreflect.Message { + mi := &file_v1_headscale_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PreAuthKey.ProtoReflect.Descriptor instead. +func (*PreAuthKey) Descriptor() ([]byte, []int) { + return file_v1_headscale_proto_rawDescGZIP(), []int{1} +} + +func (x *PreAuthKey) GetID() uint64 { + if x != nil { + return x.ID + } + return 0 +} + +func (x *PreAuthKey) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *PreAuthKey) GetNamespaceID() uint32 { + if x != nil { + return x.NamespaceID + } + return 0 +} + +func (x *PreAuthKey) GetNamespace() *Namespace { + if x != nil { + return x.Namespace + } + return nil +} + +func (x *PreAuthKey) GetReusable() bool { + if x != nil { + return x.Reusable + } + return false +} + +func (x *PreAuthKey) GetEphemeral() bool { + if x != nil { + return x.Ephemeral + } + return false +} + +func (x *PreAuthKey) GetUsed() bool { + if x != nil { + return x.Used + } + return false +} + +func (x *PreAuthKey) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *PreAuthKey) GetExpiration() *timestamppb.Timestamp { + if x != nil { + return x.Expiration + } + return nil +} + +type GetMachineRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MachineId uint64 `protobuf:"varint,1,opt,name=machine_id,json=machineId,proto3" json:"machine_id,omitempty"` +} + +func (x *GetMachineRequest) Reset() { + *x = GetMachineRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_headscale_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMachineRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMachineRequest) ProtoMessage() {} + +func (x *GetMachineRequest) ProtoReflect() protoreflect.Message { + mi := &file_v1_headscale_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetMachineRequest.ProtoReflect.Descriptor instead. +func (*GetMachineRequest) Descriptor() ([]byte, []int) { + return file_v1_headscale_proto_rawDescGZIP(), []int{2} +} + +func (x *GetMachineRequest) GetMachineId() uint64 { + if x != nil { + return x.MachineId + } + return 0 +} + +type Machine struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` + MachineKey string `protobuf:"bytes,2,opt,name=MachineKey,proto3" json:"MachineKey,omitempty"` + NodeKey string `protobuf:"bytes,3,opt,name=NodeKey,proto3" json:"NodeKey,omitempty"` + DiscoKey string `protobuf:"bytes,4,opt,name=DiscoKey,proto3" json:"DiscoKey,omitempty"` + IPAddress string `protobuf:"bytes,5,opt,name=IPAddress,proto3" json:"IPAddress,omitempty"` + Name string `protobuf:"bytes,6,opt,name=Name,proto3" json:"Name,omitempty"` + NamespaceID uint32 `protobuf:"varint,7,opt,name=NamespaceID,proto3" json:"NamespaceID,omitempty"` + Registered bool `protobuf:"varint,8,opt,name=Registered,proto3" json:"Registered,omitempty"` + RegisterMethod RegisterMethod `protobuf:"varint,9,opt,name=RegisterMethod,proto3,enum=headscale.v1.RegisterMethod" json:"RegisterMethod,omitempty"` + AuthKeyID uint32 `protobuf:"varint,10,opt,name=AuthKeyID,proto3" json:"AuthKeyID,omitempty"` + AuthKey *PreAuthKey `protobuf:"bytes,11,opt,name=AuthKey,proto3" json:"AuthKey,omitempty"` + LastSeen *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=LastSeen,proto3" json:"LastSeen,omitempty"` + LastSuccessfulUpdate *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=LastSuccessfulUpdate,proto3" json:"LastSuccessfulUpdate,omitempty"` + Expiry *timestamppb.Timestamp `protobuf:"bytes,14,opt,name=Expiry,proto3" json:"Expiry,omitempty"` + HostInfo []byte `protobuf:"bytes,15,opt,name=HostInfo,proto3" json:"HostInfo,omitempty"` + Endpoints []byte `protobuf:"bytes,16,opt,name=Endpoints,proto3" json:"Endpoints,omitempty"` + EnabledRoutes []byte `protobuf:"bytes,17,opt,name=EnabledRoutes,proto3" json:"EnabledRoutes,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,18,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,19,opt,name=UpdatedAt,proto3" json:"UpdatedAt,omitempty"` + DeletedAt *timestamppb.Timestamp `protobuf:"bytes,20,opt,name=DeletedAt,proto3" json:"DeletedAt,omitempty"` +} + +func (x *Machine) Reset() { + *x = Machine{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_headscale_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Machine) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Machine) ProtoMessage() {} + +func (x *Machine) ProtoReflect() protoreflect.Message { + mi := &file_v1_headscale_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Machine.ProtoReflect.Descriptor instead. +func (*Machine) Descriptor() ([]byte, []int) { + return file_v1_headscale_proto_rawDescGZIP(), []int{3} +} + +func (x *Machine) GetID() uint64 { + if x != nil { + return x.ID + } + return 0 +} + +func (x *Machine) GetMachineKey() string { + if x != nil { + return x.MachineKey + } + return "" +} + +func (x *Machine) GetNodeKey() string { + if x != nil { + return x.NodeKey + } + return "" +} + +func (x *Machine) GetDiscoKey() string { + if x != nil { + return x.DiscoKey + } + return "" +} + +func (x *Machine) GetIPAddress() string { + if x != nil { + return x.IPAddress + } + return "" +} + +func (x *Machine) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Machine) GetNamespaceID() uint32 { + if x != nil { + return x.NamespaceID + } + return 0 +} + +func (x *Machine) GetRegistered() bool { + if x != nil { + return x.Registered + } + return false +} + +func (x *Machine) GetRegisterMethod() RegisterMethod { + if x != nil { + return x.RegisterMethod + } + return RegisterMethod_AUTH_KEY +} + +func (x *Machine) GetAuthKeyID() uint32 { + if x != nil { + return x.AuthKeyID + } + return 0 +} + +func (x *Machine) GetAuthKey() *PreAuthKey { + if x != nil { + return x.AuthKey + } + return nil +} + +func (x *Machine) GetLastSeen() *timestamppb.Timestamp { + if x != nil { + return x.LastSeen + } + return nil +} + +func (x *Machine) GetLastSuccessfulUpdate() *timestamppb.Timestamp { + if x != nil { + return x.LastSuccessfulUpdate + } + return nil +} + +func (x *Machine) GetExpiry() *timestamppb.Timestamp { + if x != nil { + return x.Expiry + } + return nil +} + +func (x *Machine) GetHostInfo() []byte { + if x != nil { + return x.HostInfo + } + return nil +} + +func (x *Machine) GetEndpoints() []byte { + if x != nil { + return x.Endpoints + } + return nil +} + +func (x *Machine) GetEnabledRoutes() []byte { + if x != nil { + return x.EnabledRoutes + } + return nil +} + +func (x *Machine) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *Machine) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +func (x *Machine) GetDeletedAt() *timestamppb.Timestamp { + if x != nil { + return x.DeletedAt + } + return nil +} + +var File_v1_headscale_proto protoreflect.FileDescriptor + +var file_v1_headscale_proto_rawDesc = []byte{ + 0x0a, 0x12, 0x76, 0x31, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, + 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x12, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x67, 0x6f, 0x72, 0x6d, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1f, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xcb, 0x02, 0x0a, 0x0a, 0x50, 0x72, 0x65, 0x41, 0x75, + 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x02, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x44, 0x12, 0x35, 0x0a, 0x09, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x68, + 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x08, 0x52, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x73, + 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x55, 0x73, 0x65, 0x64, 0x12, 0x38, + 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x3a, 0x0a, 0x0a, 0x45, 0x78, 0x70, 0x69, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x32, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, + 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, + 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, + 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0xcd, 0x06, 0x0a, 0x07, 0x4d, 0x61, 0x63, + 0x68, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x02, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x4b, + 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, + 0x65, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x4e, 0x6f, 0x64, 0x65, 0x4b, 0x65, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4e, 0x6f, 0x64, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1a, + 0x0a, 0x08, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x4b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x49, 0x50, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x49, + 0x50, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x44, 0x12, 0x1e, + 0x0a, 0x0a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x44, + 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, + 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x49, + 0x44, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, + 0x49, 0x44, 0x12, 0x32, 0x0a, 0x07, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x07, 0x41, + 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x08, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, + 0x65, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x4e, + 0x0a, 0x14, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x14, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x75, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x32, + 0x0a, 0x06, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x45, 0x78, 0x70, 0x69, + 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, + 0x0a, 0x09, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x09, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x24, 0x0a, 0x0d, + 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x11, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, + 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x38, 0x0a, 0x09, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x3a, 0x06, 0xba, 0xb9, 0x19, 0x02, 0x08, 0x01, 0x2a, 0x31, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x55, + 0x54, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x43, 0x4c, 0x49, 0x10, + 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x02, 0x32, 0x7e, 0x0a, 0x10, 0x48, + 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x6a, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x1f, 0x2e, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, + 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, + 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, + 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x42, 0x29, 0x5a, 0x27, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, + 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, + 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_v1_headscale_proto_rawDescOnce sync.Once + file_v1_headscale_proto_rawDescData = file_v1_headscale_proto_rawDesc +) + +func file_v1_headscale_proto_rawDescGZIP() []byte { + file_v1_headscale_proto_rawDescOnce.Do(func() { + file_v1_headscale_proto_rawDescData = protoimpl.X.CompressGZIP(file_v1_headscale_proto_rawDescData) + }) + return file_v1_headscale_proto_rawDescData +} + +var file_v1_headscale_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_v1_headscale_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_v1_headscale_proto_goTypes = []interface{}{ + (RegisterMethod)(0), // 0: headscale.v1.RegisterMethod + (*Namespace)(nil), // 1: headscale.v1.Namespace + (*PreAuthKey)(nil), // 2: headscale.v1.PreAuthKey + (*GetMachineRequest)(nil), // 3: headscale.v1.GetMachineRequest + (*Machine)(nil), // 4: headscale.v1.Machine + (*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp +} +var file_v1_headscale_proto_depIdxs = []int32{ + 1, // 0: headscale.v1.PreAuthKey.Namespace:type_name -> headscale.v1.Namespace + 5, // 1: headscale.v1.PreAuthKey.CreatedAt:type_name -> google.protobuf.Timestamp + 5, // 2: headscale.v1.PreAuthKey.Expiration:type_name -> google.protobuf.Timestamp + 0, // 3: headscale.v1.Machine.RegisterMethod:type_name -> headscale.v1.RegisterMethod + 2, // 4: headscale.v1.Machine.AuthKey:type_name -> headscale.v1.PreAuthKey + 5, // 5: headscale.v1.Machine.LastSeen:type_name -> google.protobuf.Timestamp + 5, // 6: headscale.v1.Machine.LastSuccessfulUpdate:type_name -> google.protobuf.Timestamp + 5, // 7: headscale.v1.Machine.Expiry:type_name -> google.protobuf.Timestamp + 5, // 8: headscale.v1.Machine.CreatedAt:type_name -> google.protobuf.Timestamp + 5, // 9: headscale.v1.Machine.UpdatedAt:type_name -> google.protobuf.Timestamp + 5, // 10: headscale.v1.Machine.DeletedAt:type_name -> google.protobuf.Timestamp + 3, // 11: headscale.v1.HeadscaleService.GetMachine:input_type -> headscale.v1.GetMachineRequest + 4, // 12: headscale.v1.HeadscaleService.GetMachine:output_type -> headscale.v1.Machine + 12, // [12:13] is the sub-list for method output_type + 11, // [11:12] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name +} + +func init() { file_v1_headscale_proto_init() } +func file_v1_headscale_proto_init() { + if File_v1_headscale_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_v1_headscale_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Namespace); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_headscale_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PreAuthKey); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_headscale_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMachineRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_headscale_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Machine); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_v1_headscale_proto_rawDesc, + NumEnums: 1, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_v1_headscale_proto_goTypes, + DependencyIndexes: file_v1_headscale_proto_depIdxs, + EnumInfos: file_v1_headscale_proto_enumTypes, + MessageInfos: file_v1_headscale_proto_msgTypes, + }.Build() + File_v1_headscale_proto = out.File + file_v1_headscale_proto_rawDesc = nil + file_v1_headscale_proto_goTypes = nil + file_v1_headscale_proto_depIdxs = nil +} diff --git a/gen/go/v1/headscale.pb.gw.go b/gen/go/v1/headscale.pb.gw.go new file mode 100644 index 00000000..4ae6db39 --- /dev/null +++ b/gen/go/v1/headscale.pb.gw.go @@ -0,0 +1,185 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: v1/headscale.proto + +/* +Package v1 is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package v1 + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +func request_HeadscaleService_GetMachine_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetMachineRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["machine_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id") + } + + protoReq.MachineId, err = runtime.Uint64(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err) + } + + msg, err := client.GetMachine(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_GetMachine_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetMachineRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["machine_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id") + } + + protoReq.MachineId, err = runtime.Uint64(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err) + } + + msg, err := server.GetMachine(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterHeadscaleServiceHandlerServer registers the http handlers for service HeadscaleService to "mux". +// UnaryRPC :call HeadscaleServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterHeadscaleServiceHandlerFromEndpoint instead. +func RegisterHeadscaleServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server HeadscaleServiceServer) error { + + mux.Handle("GET", pattern_HeadscaleService_GetMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/headscale.v1.HeadscaleService/GetMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_GetMachine_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_HeadscaleService_GetMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterHeadscaleServiceHandlerFromEndpoint is same as RegisterHeadscaleServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterHeadscaleServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterHeadscaleServiceHandler(ctx, mux, conn) +} + +// RegisterHeadscaleServiceHandler registers the http handlers for service HeadscaleService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterHeadscaleServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterHeadscaleServiceHandlerClient(ctx, mux, NewHeadscaleServiceClient(conn)) +} + +// RegisterHeadscaleServiceHandlerClient registers the http handlers for service HeadscaleService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "HeadscaleServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "HeadscaleServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "HeadscaleServiceClient" to call the correct interceptors. +func RegisterHeadscaleServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client HeadscaleServiceClient) error { + + mux.Handle("GET", pattern_HeadscaleService_GetMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/GetMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_GetMachine_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_HeadscaleService_GetMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_HeadscaleService_GetMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "machine", "machine_id"}, "")) +) + +var ( + forward_HeadscaleService_GetMachine_0 = runtime.ForwardResponseMessage +) diff --git a/gen/go/v1/headscale_grpc.pb.go b/gen/go/v1/headscale_grpc.pb.go new file mode 100644 index 00000000..3028d183 --- /dev/null +++ b/gen/go/v1/headscale_grpc.pb.go @@ -0,0 +1,101 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package v1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// HeadscaleServiceClient is the client API for HeadscaleService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type HeadscaleServiceClient interface { + GetMachine(ctx context.Context, in *GetMachineRequest, opts ...grpc.CallOption) (*Machine, error) +} + +type headscaleServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewHeadscaleServiceClient(cc grpc.ClientConnInterface) HeadscaleServiceClient { + return &headscaleServiceClient{cc} +} + +func (c *headscaleServiceClient) GetMachine(ctx context.Context, in *GetMachineRequest, opts ...grpc.CallOption) (*Machine, error) { + out := new(Machine) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/GetMachine", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// HeadscaleServiceServer is the server API for HeadscaleService service. +// All implementations must embed UnimplementedHeadscaleServiceServer +// for forward compatibility +type HeadscaleServiceServer interface { + GetMachine(context.Context, *GetMachineRequest) (*Machine, error) + mustEmbedUnimplementedHeadscaleServiceServer() +} + +// UnimplementedHeadscaleServiceServer must be embedded to have forward compatible implementations. +type UnimplementedHeadscaleServiceServer struct { +} + +func (UnimplementedHeadscaleServiceServer) GetMachine(context.Context, *GetMachineRequest) (*Machine, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetMachine not implemented") +} +func (UnimplementedHeadscaleServiceServer) mustEmbedUnimplementedHeadscaleServiceServer() {} + +// UnsafeHeadscaleServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to HeadscaleServiceServer will +// result in compilation errors. +type UnsafeHeadscaleServiceServer interface { + mustEmbedUnimplementedHeadscaleServiceServer() +} + +func RegisterHeadscaleServiceServer(s grpc.ServiceRegistrar, srv HeadscaleServiceServer) { + s.RegisterService(&HeadscaleService_ServiceDesc, srv) +} + +func _HeadscaleService_GetMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetMachineRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).GetMachine(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/GetMachine", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).GetMachine(ctx, req.(*GetMachineRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// HeadscaleService_ServiceDesc is the grpc.ServiceDesc for HeadscaleService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var HeadscaleService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "headscale.v1.HeadscaleService", + HandlerType: (*HeadscaleServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetMachine", + Handler: _HeadscaleService_GetMachine_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "v1/headscale.proto", +} diff --git a/gen/openapiv2/v1/headscale.swagger.json b/gen/openapiv2/v1/headscale.swagger.json new file mode 100644 index 00000000..a20225d3 --- /dev/null +++ b/gen/openapiv2/v1/headscale.swagger.json @@ -0,0 +1,210 @@ +{ + "swagger": "2.0", + "info": { + "title": "v1/headscale.proto", + "version": "version not set" + }, + "tags": [ + { + "name": "HeadscaleService" + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/api/v1/machine/{machineId}": { + "get": { + "operationId": "HeadscaleService_GetMachine", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1Machine" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "machineId", + "in": "path", + "required": true, + "type": "string", + "format": "uint64" + } + ], + "tags": [ + "HeadscaleService" + ] + } + } + }, + "definitions": { + "protobufAny": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } + } + } + }, + "v1Machine": { + "type": "object", + "properties": { + "ID": { + "type": "string", + "format": "uint64" + }, + "MachineKey": { + "type": "string" + }, + "NodeKey": { + "type": "string" + }, + "DiscoKey": { + "type": "string" + }, + "IPAddress": { + "type": "string" + }, + "Name": { + "type": "string" + }, + "NamespaceID": { + "type": "integer", + "format": "int64" + }, + "Registered": { + "type": "boolean" + }, + "RegisterMethod": { + "$ref": "#/definitions/v1RegisterMethod" + }, + "AuthKeyID": { + "type": "integer", + "format": "int64" + }, + "AuthKey": { + "$ref": "#/definitions/v1PreAuthKey" + }, + "LastSeen": { + "type": "string", + "format": "date-time" + }, + "LastSuccessfulUpdate": { + "type": "string", + "format": "date-time" + }, + "Expiry": { + "type": "string", + "format": "date-time" + }, + "HostInfo": { + "type": "string", + "format": "byte" + }, + "Endpoints": { + "type": "string", + "format": "byte" + }, + "EnabledRoutes": { + "type": "string", + "format": "byte" + }, + "CreatedAt": { + "type": "string", + "format": "date-time" + }, + "UpdatedAt": { + "type": "string", + "format": "date-time" + }, + "DeletedAt": { + "type": "string", + "format": "date-time" + } + } + }, + "v1Namespace": { + "type": "object", + "properties": { + "Name": { + "type": "string" + } + } + }, + "v1PreAuthKey": { + "type": "object", + "properties": { + "ID": { + "type": "string", + "format": "uint64" + }, + "Key": { + "type": "string" + }, + "NamespaceID": { + "type": "integer", + "format": "int64" + }, + "Namespace": { + "$ref": "#/definitions/v1Namespace" + }, + "Reusable": { + "type": "boolean" + }, + "Ephemeral": { + "type": "boolean" + }, + "Used": { + "type": "boolean" + }, + "CreatedAt": { + "type": "string", + "format": "date-time" + }, + "Expiration": { + "type": "string", + "format": "date-time" + } + } + }, + "v1RegisterMethod": { + "type": "string", + "enum": [ + "AUTH_KEY", + "CLI", + "OIDC" + ], + "default": "AUTH_KEY" + } + } +} From 2d9271909526e0ccdef9a750e673e34e9b3c68e3 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 27 Oct 2021 06:48:30 +0000 Subject: [PATCH 31/70] Dont try to generate code on every make build --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3ce7025c..9f5b1faa 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # Calculate version version = $(shell ./scripts/version-at-commit.sh) -build: generate +build: go build -ldflags "-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$(version)" cmd/headscale/headscale.go dev: lint test build From 6369cea10e33bd1505966777ee3696db3ef1ba3f Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 27 Oct 2021 06:58:16 +0000 Subject: [PATCH 32/70] Remove golint, its deprecated This commit removed `golint`, its deprecated: https://github.com/golang/lint and golangci-lint has overlapping features. --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 9f5b1faa..6bce4519 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,6 @@ coverprofile_html: go tool cover -html=coverage.out lint: - golint golangci-lint run --timeout 5m compress: build From acd9ebbdf882937b09e45d19841a58e68ea51eb5 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 27 Oct 2021 07:06:39 +0000 Subject: [PATCH 33/70] Let lint ignore grpcv1.go as it is placeholder --- Makefile | 2 +- grpcv1.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6bce4519..9605955c 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ coverprofile_html: go tool cover -html=coverage.out lint: - golangci-lint run --timeout 5m + golangci-lint run compress: build upx --brute headscale diff --git a/grpcv1.go b/grpcv1.go index d9bbf531..5fc2e1c8 100644 --- a/grpcv1.go +++ b/grpcv1.go @@ -1,3 +1,4 @@ +//nolint package headscale import ( From f7793721547291d1572ece96709c0695b73e10da Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 27 Oct 2021 07:07:19 +0000 Subject: [PATCH 34/70] Add golangcilint config --- .golangci.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .golangci.yaml diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 00000000..8d05e483 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,3 @@ +--- +run: + timeout: 5m From c9bd25d05c30c809624fe0bc114a5aed1a75c376 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 27 Oct 2021 07:07:44 +0000 Subject: [PATCH 35/70] Remove golint from github actions --- .github/workflows/lint.yml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c0286571..6b561d24 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -18,22 +18,3 @@ jobs: # below, but it's still much faster in the end than installing # golangci-lint manually in the `Run lint` step. - uses: golangci/golangci-lint-action@v2 - with: - args: --timeout 5m - - # Setup Go - - name: Setup Go - uses: actions/setup-go@v2 - with: - go-version: "1.16.3" # The Go version to download (if necessary) and use. - - # Install all the dependencies - - name: Install dependencies - run: | - go version - go install golang.org/x/lint/golint@latest - sudo apt update - sudo apt install -y make - - - name: Run lint - run: make lint From e91174e83f737eb013e6f3789caaa72cab970764 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 27 Oct 2021 07:08:24 +0000 Subject: [PATCH 36/70] Add gen explicitly to skip list --- .golangci.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.golangci.yaml b/.golangci.yaml index 8d05e483..a97c2bb5 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,3 +1,7 @@ --- run: timeout: 5m + +issues: + skip-dirs: + - gen From 5054ed41ac9caae5952a1095533ee4752e428c16 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 27 Oct 2021 07:10:32 +0000 Subject: [PATCH 37/70] Make ci lint fix if it can --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9605955c..5fdd2a53 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ coverprofile_html: go tool cover -html=coverage.out lint: - golangci-lint run + golangci-lint run --fix compress: build upx --brute headscale From d086cf469137f8116579e776daf010a4e6c662d8 Mon Sep 17 00:00:00 2001 From: Ward Vandewege Date: Wed, 27 Oct 2021 17:51:42 -0400 Subject: [PATCH 38/70] Move the namespace argument back to a flag for the share and unshare commands. --- cmd/headscale/cli/nodes.go | 33 ++++++++++++++++++++++++++------- integration_test.go | 2 +- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index ba843f70..1db56191 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -19,14 +19,28 @@ func init() { rootCmd.AddCommand(nodeCmd) listNodesCmd.Flags().StringP("namespace", "n", "", "Filter by namespace") nodeCmd.AddCommand(listNodesCmd) - registerNodeCmd.Flags().StringP("namespace", "n", "", "Filter by namespace") + + registerNodeCmd.Flags().StringP("namespace", "n", "", "Namespace") err := registerNodeCmd.MarkFlagRequired("namespace") if err != nil { log.Fatalf(err.Error()) } nodeCmd.AddCommand(registerNodeCmd) + nodeCmd.AddCommand(deleteNodeCmd) + + shareMachineCmd.Flags().StringP("namespace", "n", "", "Namespace") + err = shareMachineCmd.MarkFlagRequired("namespace") + if err != nil { + log.Fatalf(err.Error()) + } nodeCmd.AddCommand(shareMachineCmd) + + unshareMachineCmd.Flags().StringP("namespace", "n", "", "Namespace") + err = unshareMachineCmd.MarkFlagRequired("namespace") + if err != nil { + log.Fatalf(err.Error()) + } nodeCmd.AddCommand(unshareMachineCmd) } @@ -199,6 +213,11 @@ var deleteNodeCmd = &cobra.Command{ } func sharingWorker(cmd *cobra.Command, args []string) (*headscale.Headscale, string, *headscale.Machine, *headscale.Namespace) { + n, err := cmd.Flags().GetString("namespace") + if err != nil { + log.Fatalf("Error getting namespace: %s", err) + } + output, _ := cmd.Flags().GetString("output") h, err := getHeadscaleApp() @@ -206,9 +225,9 @@ func sharingWorker(cmd *cobra.Command, args []string) (*headscale.Headscale, str log.Fatalf("Error initializing: %s", err) } - namespace, err := h.GetNamespace(args[1]) + namespace, err := h.GetNamespace(n) if err != nil { - log.Fatalf("Error fetching namespace %s: %s", args[1], err) + log.Fatalf("Error fetching namespace %s: %s", n, err) } id, err := strconv.Atoi(args[0]) @@ -224,10 +243,10 @@ func sharingWorker(cmd *cobra.Command, args []string) (*headscale.Headscale, str } var shareMachineCmd = &cobra.Command{ - Use: "share ID namespace", + Use: "share ID", Short: "Shares a node from the current namespace to the specified one", Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { + if len(args) < 1 { return fmt.Errorf("missing parameters") } return nil @@ -249,10 +268,10 @@ var shareMachineCmd = &cobra.Command{ } var unshareMachineCmd = &cobra.Command{ - Use: "unshare ID namespace", + Use: "unshare ID", Short: "Unshares a node from the specified namespace", Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { + if len(args) < 1 { return fmt.Errorf("missing parameters") } return nil diff --git a/integration_test.go b/integration_test.go index a1a5e953..7ec75df3 100644 --- a/integration_test.go +++ b/integration_test.go @@ -493,7 +493,7 @@ func (s *IntegrationTestSuite) TestSharedNodes() { result, err := executeCommand( &headscale, - []string{"headscale", "nodes", "share", fmt.Sprint(machine.ID), "main"}, + []string{"headscale", "nodes", "share", fmt.Sprint(machine.ID), "-n", "main"}, []string{}, ) assert.Nil(s.T(), err) From 6c01b86e4ce5cbc36dc012ca2fc86a9856dcef43 Mon Sep 17 00:00:00 2001 From: Ward Vandewege Date: Thu, 28 Oct 2021 08:39:27 -0400 Subject: [PATCH 39/70] Update cmd/headscale/cli/nodes.go Co-authored-by: Kristoffer Dalby --- cmd/headscale/cli/nodes.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index 1db56191..0e778bc6 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -213,7 +213,7 @@ var deleteNodeCmd = &cobra.Command{ } func sharingWorker(cmd *cobra.Command, args []string) (*headscale.Headscale, string, *headscale.Machine, *headscale.Namespace) { - n, err := cmd.Flags().GetString("namespace") + namespaceStr, err := cmd.Flags().GetString("namespace") if err != nil { log.Fatalf("Error getting namespace: %s", err) } @@ -225,7 +225,7 @@ func sharingWorker(cmd *cobra.Command, args []string) (*headscale.Headscale, str log.Fatalf("Error initializing: %s", err) } - namespace, err := h.GetNamespace(n) + namespace, err := h.GetNamespace(namespaceStr) if err != nil { log.Fatalf("Error fetching namespace %s: %s", n, err) } From b00a2729e3b250b30fd995647123e81614ffcd7f Mon Sep 17 00:00:00 2001 From: Ward Vandewege Date: Thu, 28 Oct 2021 08:39:42 -0400 Subject: [PATCH 40/70] Update cmd/headscale/cli/nodes.go Co-authored-by: Kristoffer Dalby --- cmd/headscale/cli/nodes.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index 0e778bc6..825ef232 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -121,8 +121,8 @@ var listNodesCmd = &cobra.Command{ } var allMachines []headscale.Machine - for _, n := range namespaces { - machines, err := h.ListMachinesInNamespace(n.Name) + for _, namespace := range namespaces { + machines, err := h.ListMachinesInNamespace(namespace.Name) if err != nil { log.Fatalf("Error fetching machines: %s", err) } From 25c67cf2aa8a18b4443400cae98d8d7899611293 Mon Sep 17 00:00:00 2001 From: Ward Vandewege Date: Thu, 28 Oct 2021 08:40:30 -0400 Subject: [PATCH 41/70] Update integration_test.go Co-authored-by: Kristoffer Dalby --- integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_test.go b/integration_test.go index 7ec75df3..524dd32d 100644 --- a/integration_test.go +++ b/integration_test.go @@ -493,7 +493,7 @@ func (s *IntegrationTestSuite) TestSharedNodes() { result, err := executeCommand( &headscale, - []string{"headscale", "nodes", "share", fmt.Sprint(machine.ID), "-n", "main"}, + []string{"headscale", "nodes", "share", fmt.Sprint(machine.ID), "--namespace", "main"}, []string{}, ) assert.Nil(s.T(), err) From f9187bdfc4e1c7c269622a3c595d393d26ae5f8e Mon Sep 17 00:00:00 2001 From: Ward Vandewege Date: Thu, 28 Oct 2021 09:30:41 -0400 Subject: [PATCH 42/70] Switch to named arguments for all `nodes` subcommands. Update docs accordingly. Fix integration test failure. --- api.go | 2 +- cmd/headscale/cli/nodes.go | 64 +++++++++++++++++++------------------- docs/Running.md | 6 ++-- integration_test.go | 2 +- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/api.go b/api.go index a31cf529..0c1f3d41 100644 --- a/api.go +++ b/api.go @@ -43,7 +43,7 @@ func (h *Headscale) RegisterWebAPI(c *gin.Context) {

- headscale -n NAMESPACE nodes register %s + headscale -n NAMESPACE nodes register -k %s

diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index 825ef232..cdf37efb 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -25,8 +25,18 @@ func init() { if err != nil { log.Fatalf(err.Error()) } + registerNodeCmd.Flags().StringP("key", "k", "", "Key") + err = registerNodeCmd.MarkFlagRequired("key") + if err != nil { + log.Fatalf(err.Error()) + } nodeCmd.AddCommand(registerNodeCmd) + deleteNodeCmd.Flags().IntP("identifier", "i", 0, "Node identifier (ID)") + err = deleteNodeCmd.MarkFlagRequired("identifier") + if err != nil { + log.Fatalf(err.Error()) + } nodeCmd.AddCommand(deleteNodeCmd) shareMachineCmd.Flags().StringP("namespace", "n", "", "Namespace") @@ -34,6 +44,11 @@ func init() { if err != nil { log.Fatalf(err.Error()) } + shareMachineCmd.Flags().IntP("identifier", "i", 0, "Node identifier (ID)") + err = shareMachineCmd.MarkFlagRequired("identifier") + if err != nil { + log.Fatalf(err.Error()) + } nodeCmd.AddCommand(shareMachineCmd) unshareMachineCmd.Flags().StringP("namespace", "n", "", "Namespace") @@ -41,6 +56,11 @@ func init() { if err != nil { log.Fatalf(err.Error()) } + unshareMachineCmd.Flags().IntP("identifier", "i", 0, "Node identifier (ID)") + err = unshareMachineCmd.MarkFlagRequired("identifier") + if err != nil { + log.Fatalf(err.Error()) + } nodeCmd.AddCommand(unshareMachineCmd) } @@ -50,14 +70,8 @@ var nodeCmd = &cobra.Command{ } var registerNodeCmd = &cobra.Command{ - Use: "register machineID", + Use: "register", Short: "Registers a machine to your network", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return fmt.Errorf("missing parameters") - } - return nil - }, Run: func(cmd *cobra.Command, args []string) { n, err := cmd.Flags().GetString("namespace") if err != nil { @@ -69,7 +83,11 @@ var registerNodeCmd = &cobra.Command{ if err != nil { log.Fatalf("Error initializing: %s", err) } - m, err := h.RegisterMachine(args[0], n) + machineIDStr, err := cmd.Flags().GetString("key") + if err != nil { + log.Fatalf("Error getting machine ID: %s", err) + } + m, err := h.RegisterMachine(machineIDStr, n) if strings.HasPrefix(o, "json") { JsonOutput(m, err, o) return @@ -157,21 +175,15 @@ var listNodesCmd = &cobra.Command{ } var deleteNodeCmd = &cobra.Command{ - Use: "delete ID", + Use: "delete", Short: "Delete a node", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return fmt.Errorf("missing parameters") - } - return nil - }, Run: func(cmd *cobra.Command, args []string) { output, _ := cmd.Flags().GetString("output") h, err := getHeadscaleApp() if err != nil { log.Fatalf("Error initializing: %s", err) } - id, err := strconv.Atoi(args[0]) + id, err := cmd.Flags().GetInt("identifier") if err != nil { log.Fatalf("Error converting ID to integer: %s", err) } @@ -227,10 +239,10 @@ func sharingWorker(cmd *cobra.Command, args []string) (*headscale.Headscale, str namespace, err := h.GetNamespace(namespaceStr) if err != nil { - log.Fatalf("Error fetching namespace %s: %s", n, err) + log.Fatalf("Error fetching namespace %s: %s", namespaceStr, err) } - id, err := strconv.Atoi(args[0]) + id, err := cmd.Flags().GetInt("identifier") if err != nil { log.Fatalf("Error converting ID to integer: %s", err) } @@ -243,14 +255,8 @@ func sharingWorker(cmd *cobra.Command, args []string) (*headscale.Headscale, str } var shareMachineCmd = &cobra.Command{ - Use: "share ID", + Use: "share", Short: "Shares a node from the current namespace to the specified one", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return fmt.Errorf("missing parameters") - } - return nil - }, Run: func(cmd *cobra.Command, args []string) { h, output, machine, namespace := sharingWorker(cmd, args) err := h.AddSharedMachineToNamespace(machine, namespace) @@ -268,14 +274,8 @@ var shareMachineCmd = &cobra.Command{ } var unshareMachineCmd = &cobra.Command{ - Use: "unshare ID", + Use: "unshare", Short: "Unshares a node from the specified namespace", - Args: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return fmt.Errorf("missing parameters") - } - return nil - }, Run: func(cmd *cobra.Command, args []string) { h, output, machine, namespace := sharingWorker(cmd, args) err := h.RemoveSharedMachineFromNamespace(machine, namespace) diff --git a/docs/Running.md b/docs/Running.md index 08373653..dbb8704c 100644 --- a/docs/Running.md +++ b/docs/Running.md @@ -97,7 +97,7 @@ 9. In the server, register your machine to a namespace with the CLI ```shell - headscale -n myfirstnamespace nodes register YOURMACHINEKEY + headscale -n myfirstnamespace nodes register -k YOURMACHINEKEY ``` or docker: ```shell @@ -106,11 +106,11 @@ -v $(pwd)/config.json:/config.json \ -v $(pwd)/derp.yaml:/derp.yaml \ headscale/headscale:x.x.x \ - headscale -n myfirstnamespace nodes register YOURMACHINEKEY + headscale -n myfirstnamespace nodes register -k YOURMACHINEKEY ``` or if your server is already running in docker: ```shell - docker exec headscale -n myfirstnamespace nodes register YOURMACHINEKEY + docker exec headscale -n myfirstnamespace nodes register -k YOURMACHINEKEY ``` Alternatively, you can use Auth Keys to register your machines: diff --git a/integration_test.go b/integration_test.go index 524dd32d..f73d76f6 100644 --- a/integration_test.go +++ b/integration_test.go @@ -493,7 +493,7 @@ func (s *IntegrationTestSuite) TestSharedNodes() { result, err := executeCommand( &headscale, - []string{"headscale", "nodes", "share", fmt.Sprint(machine.ID), "--namespace", "main"}, + []string{"headscale", "nodes", "share", "--identifier", fmt.Sprint(machine.ID), "--namespace", "main"}, []string{}, ) assert.Nil(s.T(), err) From 2d252da221b3db0bb2d605f618d2d685f0035158 Mon Sep 17 00:00:00 2001 From: Raal Goff Date: Fri, 29 Oct 2021 21:35:07 +0800 Subject: [PATCH 43/70] suggested documentation and comments --- api.go | 11 +++++++++-- cmd/headscale/cli/utils.go | 17 ++++++++++------- machine.go | 6 ++++-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/api.go b/api.go index c7ae122c..36af5a06 100644 --- a/api.go +++ b/api.go @@ -111,7 +111,8 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { // We have the updated key! if m.NodeKey == wgkey.Key(req.NodeKey).HexString() { - // The client sends an Expiry in the past if the client is requesting a logout + // The client sends an Expiry in the past if the client is requesting to expire the key (aka logout) + // https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L648 if !req.Expiry.IsZero() && req.Expiry.UTC().Before(now) { log.Info(). Str("handler", "Registration"). @@ -178,7 +179,13 @@ func (h *Headscale) RegistrationHandler(c *gin.Context) { strings.TrimSuffix(h.cfg.ServerURL, "/"), mKey.HexString()) } - m.RequestedExpiry = &req.Expiry // save the requested expiry time for retrieval later in the authentication flow + // When a client connects, it may request a specific expiry time in its + // RegisterRequest (https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L634) + m.RequestedExpiry = &req.Expiry // RequestedExpiry is used to store the clients requested expiry time since the authentication flow is broken + // into two steps (which cant pass arbitrary data between them easily) and needs to be + // retrieved again after the user has authenticated. After the authentication flow + // completes, RequestedExpiry is copied into Expiry. + h.db.Save(&m) respBody, err := encode(resp, &mKey, h.privateKey) diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 4a598e77..0ba43b28 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -161,14 +161,18 @@ func getHeadscaleApp() (*headscale.Headscale, error) { return nil, err } - // maxMachineRegistrationDuration is the maximum time a client can request for a client registration - maxMachineRegistrationDuration, _ := time.ParseDuration("10h") + // maxMachineRegistrationDuration is the maximum time headscale will allow a client to (optionally) request for + // the machine key expiry time. RegisterRequests with Expiry times that are more than + // maxMachineRegistrationDuration in the future will be clamped to (now + maxMachineRegistrationDuration) + maxMachineRegistrationDuration, _ := time.ParseDuration("10h") // use 10h here because it is the length of a standard business day plus a small amount of leeway if viper.GetDuration("max_machine_registration_duration") >= time.Second { maxMachineRegistrationDuration = viper.GetDuration("max_machine_registration_duration") } - // defaultMachineRegistrationDuration is the default time assigned to a client registration if one is not specified by the client - defaultMachineRegistrationDuration, _ := time.ParseDuration("8h") + // defaultMachineRegistrationDuration is the default time assigned to a machine registration if one is not + // specified by the tailscale client. It is the default amount of time a machine registration is valid for + // (ie the amount of time before the user has to re-authenticate when requesting a connection) + defaultMachineRegistrationDuration, _ := time.ParseDuration("8h") // use 8h here because it's the length of a standard business day if viper.GetDuration("default_machine_registration_duration") >= time.Second { defaultMachineRegistrationDuration = viper.GetDuration("default_machine_registration_duration") } @@ -212,9 +216,8 @@ func getHeadscaleApp() (*headscale.Headscale, error) { ClientSecret: viper.GetString("oidc.client_secret"), }, - MaxMachineRegistrationDuration: maxMachineRegistrationDuration, // the maximum duration a client may request for expiry time - DefaultMachineRegistrationDuration: defaultMachineRegistrationDuration, // if a client does not request a specific expiry time, use this duration - + MaxMachineRegistrationDuration: maxMachineRegistrationDuration, + DefaultMachineRegistrationDuration: defaultMachineRegistrationDuration, } cfg.OIDC.MatchMap = loadOIDCMatchMap() diff --git a/machine.go b/machine.go index a43fa8de..f4ce0afb 100644 --- a/machine.go +++ b/machine.go @@ -36,7 +36,7 @@ type Machine struct { LastSeen *time.Time LastSuccessfulUpdate *time.Time Expiry *time.Time - RequestedExpiry *time.Time // when a client connects, it may request a specific expiry time, use this field to store it + RequestedExpiry *time.Time HostInfo datatypes.JSON Endpoints datatypes.JSON @@ -63,7 +63,9 @@ func (m Machine) isExpired() bool { } // If the Machine is expired, updateMachineExpiry updates the Machine Expiry time to the maximum allowed duration, -// or the default duration if no Expiry time was requested by the client +// or the default duration if no Expiry time was requested by the client. The expiry time here does not (yet) cause +// a client to be disconnected, however they will have to re-auth the machine if they attempt to reconnect after the +// expiry time. func (h *Headscale) updateMachineExpiry(m *Machine) { if m.isExpired() { From 06700c1dc4f69c44f5b49a164a874b1e25355cf9 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 29 Oct 2021 16:42:56 +0000 Subject: [PATCH 44/70] Setup proto linting --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 5fdd2a53..92beaefb 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,9 @@ coverprofile_html: lint: golangci-lint run --fix +proto-lint: + cd proto/ && buf lint + compress: build upx --brute headscale From 07bbeafa3b2a703f3dd2a6258463b90eef45e886 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 29 Oct 2021 16:43:10 +0000 Subject: [PATCH 45/70] Fix lint errors, add initial namespace rpc --- gen/go/headscale/v1/rpc.pb.go | 807 ++++++++++++++++++++ gen/go/headscale/v1/rpc.pb.gw.go | 414 ++++++++++ gen/go/headscale/v1/rpc_grpc.pb.go | 209 +++++ gen/go/v1/headscale.pb.go | 702 ----------------- gen/go/v1/headscale.pb.gw.go | 185 ----- gen/go/v1/headscale_grpc.pb.go | 101 --- gen/openapiv2/headscale/v1/rpc.swagger.json | 250 ++++++ gen/openapiv2/v1/headscale.swagger.json | 210 ----- proto/headscale/v1/rpc.proto | 106 +++ proto/v1/headscale.proto | 71 -- 10 files changed, 1786 insertions(+), 1269 deletions(-) create mode 100644 gen/go/headscale/v1/rpc.pb.go create mode 100644 gen/go/headscale/v1/rpc.pb.gw.go create mode 100644 gen/go/headscale/v1/rpc_grpc.pb.go delete mode 100644 gen/go/v1/headscale.pb.go delete mode 100644 gen/go/v1/headscale.pb.gw.go delete mode 100644 gen/go/v1/headscale_grpc.pb.go create mode 100644 gen/openapiv2/headscale/v1/rpc.swagger.json delete mode 100644 gen/openapiv2/v1/headscale.swagger.json create mode 100644 proto/headscale/v1/rpc.proto delete mode 100644 proto/v1/headscale.proto diff --git a/gen/go/headscale/v1/rpc.pb.go b/gen/go/headscale/v1/rpc.pb.go new file mode 100644 index 00000000..b510b961 --- /dev/null +++ b/gen/go/headscale/v1/rpc.pb.go @@ -0,0 +1,807 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: headscale/v1/rpc.proto + +package v1 + +import ( + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type RegisterMethod int32 + +const ( + RegisterMethod_REGISTER_METHOD_UNSPECIFIED RegisterMethod = 0 + RegisterMethod_REGISTER_METHOD_AUTH_KEY RegisterMethod = 1 + RegisterMethod_REGISTER_METHOD_CLI RegisterMethod = 2 + RegisterMethod_REGISTER_METHOD_OIDC RegisterMethod = 3 +) + +// Enum value maps for RegisterMethod. +var ( + RegisterMethod_name = map[int32]string{ + 0: "REGISTER_METHOD_UNSPECIFIED", + 1: "REGISTER_METHOD_AUTH_KEY", + 2: "REGISTER_METHOD_CLI", + 3: "REGISTER_METHOD_OIDC", + } + RegisterMethod_value = map[string]int32{ + "REGISTER_METHOD_UNSPECIFIED": 0, + "REGISTER_METHOD_AUTH_KEY": 1, + "REGISTER_METHOD_CLI": 2, + "REGISTER_METHOD_OIDC": 3, + } +) + +func (x RegisterMethod) Enum() *RegisterMethod { + p := new(RegisterMethod) + *p = x + return p +} + +func (x RegisterMethod) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (RegisterMethod) Descriptor() protoreflect.EnumDescriptor { + return file_headscale_v1_rpc_proto_enumTypes[0].Descriptor() +} + +func (RegisterMethod) Type() protoreflect.EnumType { + return &file_headscale_v1_rpc_proto_enumTypes[0] +} + +func (x RegisterMethod) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use RegisterMethod.Descriptor instead. +func (RegisterMethod) EnumDescriptor() ([]byte, []int) { + return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{0} +} + +type GetMachineRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MachineId uint64 `protobuf:"varint,1,opt,name=machine_id,json=machineId,proto3" json:"machine_id,omitempty"` +} + +func (x *GetMachineRequest) Reset() { + *x = GetMachineRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_rpc_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMachineRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMachineRequest) ProtoMessage() {} + +func (x *GetMachineRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_rpc_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetMachineRequest.ProtoReflect.Descriptor instead. +func (*GetMachineRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{0} +} + +func (x *GetMachineRequest) GetMachineId() uint64 { + if x != nil { + return x.MachineId + } + return 0 +} + +type GetMachineResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + MachineKey string `protobuf:"bytes,2,opt,name=machine_key,json=machineKey,proto3" json:"machine_key,omitempty"` + NodeKey string `protobuf:"bytes,3,opt,name=node_key,json=nodeKey,proto3" json:"node_key,omitempty"` + DiscoKey string `protobuf:"bytes,4,opt,name=disco_key,json=discoKey,proto3" json:"disco_key,omitempty"` + IpAddress string `protobuf:"bytes,5,opt,name=ip_address,json=ipAddress,proto3" json:"ip_address,omitempty"` + Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` + NamespaceId uint32 `protobuf:"varint,7,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + Registered bool `protobuf:"varint,8,opt,name=registered,proto3" json:"registered,omitempty"` + RegisterMethod RegisterMethod `protobuf:"varint,9,opt,name=register_method,json=registerMethod,proto3,enum=headscale.v1.RegisterMethod" json:"register_method,omitempty"` + AuthKeyId uint32 `protobuf:"varint,10,opt,name=auth_key_id,json=authKeyId,proto3" json:"auth_key_id,omitempty"` // PreAuthKey auth_key = 11; + LastSeen *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` + LastSuccessfulUpdate *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=last_successful_update,json=lastSuccessfulUpdate,proto3" json:"last_successful_update,omitempty"` + Expiry *timestamppb.Timestamp `protobuf:"bytes,14,opt,name=expiry,proto3" json:"expiry,omitempty"` +} + +func (x *GetMachineResponse) Reset() { + *x = GetMachineResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_rpc_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMachineResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMachineResponse) ProtoMessage() {} + +func (x *GetMachineResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_rpc_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetMachineResponse.ProtoReflect.Descriptor instead. +func (*GetMachineResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{1} +} + +func (x *GetMachineResponse) GetId() uint64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *GetMachineResponse) GetMachineKey() string { + if x != nil { + return x.MachineKey + } + return "" +} + +func (x *GetMachineResponse) GetNodeKey() string { + if x != nil { + return x.NodeKey + } + return "" +} + +func (x *GetMachineResponse) GetDiscoKey() string { + if x != nil { + return x.DiscoKey + } + return "" +} + +func (x *GetMachineResponse) GetIpAddress() string { + if x != nil { + return x.IpAddress + } + return "" +} + +func (x *GetMachineResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GetMachineResponse) GetNamespaceId() uint32 { + if x != nil { + return x.NamespaceId + } + return 0 +} + +func (x *GetMachineResponse) GetRegistered() bool { + if x != nil { + return x.Registered + } + return false +} + +func (x *GetMachineResponse) GetRegisterMethod() RegisterMethod { + if x != nil { + return x.RegisterMethod + } + return RegisterMethod_REGISTER_METHOD_UNSPECIFIED +} + +func (x *GetMachineResponse) GetAuthKeyId() uint32 { + if x != nil { + return x.AuthKeyId + } + return 0 +} + +func (x *GetMachineResponse) GetLastSeen() *timestamppb.Timestamp { + if x != nil { + return x.LastSeen + } + return nil +} + +func (x *GetMachineResponse) GetLastSuccessfulUpdate() *timestamppb.Timestamp { + if x != nil { + return x.LastSuccessfulUpdate + } + return nil +} + +func (x *GetMachineResponse) GetExpiry() *timestamppb.Timestamp { + if x != nil { + return x.Expiry + } + return nil +} + +type CreateNamespaceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *CreateNamespaceRequest) Reset() { + *x = CreateNamespaceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_rpc_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateNamespaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateNamespaceRequest) ProtoMessage() {} + +func (x *CreateNamespaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_rpc_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateNamespaceRequest.ProtoReflect.Descriptor instead. +func (*CreateNamespaceRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{2} +} + +func (x *CreateNamespaceRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type CreateNamespaceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *CreateNamespaceResponse) Reset() { + *x = CreateNamespaceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_rpc_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateNamespaceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateNamespaceResponse) ProtoMessage() {} + +func (x *CreateNamespaceResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_rpc_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateNamespaceResponse.ProtoReflect.Descriptor instead. +func (*CreateNamespaceResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{3} +} + +func (x *CreateNamespaceResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type DeleteNamespaceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *DeleteNamespaceRequest) Reset() { + *x = DeleteNamespaceRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_rpc_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteNamespaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteNamespaceRequest) ProtoMessage() {} + +func (x *DeleteNamespaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_rpc_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteNamespaceRequest.ProtoReflect.Descriptor instead. +func (*DeleteNamespaceRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{4} +} + +func (x *DeleteNamespaceRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type DeleteNamespaceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeleteNamespaceResponse) Reset() { + *x = DeleteNamespaceResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_rpc_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteNamespaceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteNamespaceResponse) ProtoMessage() {} + +func (x *DeleteNamespaceResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_rpc_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteNamespaceResponse.ProtoReflect.Descriptor instead. +func (*DeleteNamespaceResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{5} +} + +type ListNamespacesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ListNamespacesRequest) Reset() { + *x = ListNamespacesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_rpc_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListNamespacesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListNamespacesRequest) ProtoMessage() {} + +func (x *ListNamespacesRequest) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_rpc_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListNamespacesRequest.ProtoReflect.Descriptor instead. +func (*ListNamespacesRequest) Descriptor() ([]byte, []int) { + return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{6} +} + +type ListNamespacesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespaces []string `protobuf:"bytes,1,rep,name=namespaces,proto3" json:"namespaces,omitempty"` +} + +func (x *ListNamespacesResponse) Reset() { + *x = ListNamespacesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_headscale_v1_rpc_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListNamespacesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListNamespacesResponse) ProtoMessage() {} + +func (x *ListNamespacesResponse) ProtoReflect() protoreflect.Message { + mi := &file_headscale_v1_rpc_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListNamespacesResponse.ProtoReflect.Descriptor instead. +func (*ListNamespacesResponse) Descriptor() ([]byte, []int) { + return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{7} +} + +func (x *ListNamespacesResponse) GetNamespaces() []string { + if x != nil { + return x.Namespaces + } + return nil +} + +var File_headscale_v1_rpc_proto protoreflect.FileDescriptor + +var file_headscale_v1_rpc_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x72, + 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x32, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, + 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, + 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, + 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x99, 0x04, 0x0a, 0x12, 0x47, 0x65, + 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x4b, 0x65, + 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, + 0x64, 0x69, 0x73, 0x63, 0x6f, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x70, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, + 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, + 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, + 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, + 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x75, 0x74, + 0x68, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, + 0x65, 0x65, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, + 0x50, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, + 0x75, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x14, 0x6c, 0x61, 0x73, + 0x74, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x12, 0x32, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x79, 0x22, 0x2c, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x22, 0x2d, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x2c, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x22, 0x19, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x0a, 0x15, 0x4c, + 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x38, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2a, 0x82, + 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, + 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, + 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, + 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, + 0x48, 0x4f, 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x47, + 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x49, 0x44, + 0x43, 0x10, 0x03, 0x32, 0xfa, 0x03, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x75, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4d, + 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x1f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, + 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x1e, 0x12, 0x1c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, + 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, + 0x7c, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, + 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, + 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x79, 0x0a, + 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x2a, 0x11, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x76, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x68, 0x65, 0x61, + 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, + 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_headscale_v1_rpc_proto_rawDescOnce sync.Once + file_headscale_v1_rpc_proto_rawDescData = file_headscale_v1_rpc_proto_rawDesc +) + +func file_headscale_v1_rpc_proto_rawDescGZIP() []byte { + file_headscale_v1_rpc_proto_rawDescOnce.Do(func() { + file_headscale_v1_rpc_proto_rawDescData = protoimpl.X.CompressGZIP(file_headscale_v1_rpc_proto_rawDescData) + }) + return file_headscale_v1_rpc_proto_rawDescData +} + +var file_headscale_v1_rpc_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_headscale_v1_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_headscale_v1_rpc_proto_goTypes = []interface{}{ + (RegisterMethod)(0), // 0: headscale.v1.RegisterMethod + (*GetMachineRequest)(nil), // 1: headscale.v1.GetMachineRequest + (*GetMachineResponse)(nil), // 2: headscale.v1.GetMachineResponse + (*CreateNamespaceRequest)(nil), // 3: headscale.v1.CreateNamespaceRequest + (*CreateNamespaceResponse)(nil), // 4: headscale.v1.CreateNamespaceResponse + (*DeleteNamespaceRequest)(nil), // 5: headscale.v1.DeleteNamespaceRequest + (*DeleteNamespaceResponse)(nil), // 6: headscale.v1.DeleteNamespaceResponse + (*ListNamespacesRequest)(nil), // 7: headscale.v1.ListNamespacesRequest + (*ListNamespacesResponse)(nil), // 8: headscale.v1.ListNamespacesResponse + (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp +} +var file_headscale_v1_rpc_proto_depIdxs = []int32{ + 0, // 0: headscale.v1.GetMachineResponse.register_method:type_name -> headscale.v1.RegisterMethod + 9, // 1: headscale.v1.GetMachineResponse.last_seen:type_name -> google.protobuf.Timestamp + 9, // 2: headscale.v1.GetMachineResponse.last_successful_update:type_name -> google.protobuf.Timestamp + 9, // 3: headscale.v1.GetMachineResponse.expiry:type_name -> google.protobuf.Timestamp + 1, // 4: headscale.v1.HeadscaleService.GetMachine:input_type -> headscale.v1.GetMachineRequest + 3, // 5: headscale.v1.HeadscaleService.CreateNamespace:input_type -> headscale.v1.CreateNamespaceRequest + 5, // 6: headscale.v1.HeadscaleService.DeleteNamespace:input_type -> headscale.v1.DeleteNamespaceRequest + 7, // 7: headscale.v1.HeadscaleService.ListNamespaces:input_type -> headscale.v1.ListNamespacesRequest + 2, // 8: headscale.v1.HeadscaleService.GetMachine:output_type -> headscale.v1.GetMachineResponse + 4, // 9: headscale.v1.HeadscaleService.CreateNamespace:output_type -> headscale.v1.CreateNamespaceResponse + 6, // 10: headscale.v1.HeadscaleService.DeleteNamespace:output_type -> headscale.v1.DeleteNamespaceResponse + 8, // 11: headscale.v1.HeadscaleService.ListNamespaces:output_type -> headscale.v1.ListNamespacesResponse + 8, // [8:12] is the sub-list for method output_type + 4, // [4:8] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_headscale_v1_rpc_proto_init() } +func file_headscale_v1_rpc_proto_init() { + if File_headscale_v1_rpc_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_headscale_v1_rpc_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMachineRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_rpc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMachineResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_rpc_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateNamespaceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_rpc_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateNamespaceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_rpc_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteNamespaceRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_rpc_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteNamespaceResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_rpc_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListNamespacesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_headscale_v1_rpc_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListNamespacesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_headscale_v1_rpc_proto_rawDesc, + NumEnums: 1, + NumMessages: 8, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_headscale_v1_rpc_proto_goTypes, + DependencyIndexes: file_headscale_v1_rpc_proto_depIdxs, + EnumInfos: file_headscale_v1_rpc_proto_enumTypes, + MessageInfos: file_headscale_v1_rpc_proto_msgTypes, + }.Build() + File_headscale_v1_rpc_proto = out.File + file_headscale_v1_rpc_proto_rawDesc = nil + file_headscale_v1_rpc_proto_goTypes = nil + file_headscale_v1_rpc_proto_depIdxs = nil +} diff --git a/gen/go/headscale/v1/rpc.pb.gw.go b/gen/go/headscale/v1/rpc.pb.gw.go new file mode 100644 index 00000000..773a85a4 --- /dev/null +++ b/gen/go/headscale/v1/rpc.pb.gw.go @@ -0,0 +1,414 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: headscale/v1/rpc.proto + +/* +Package v1 is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package v1 + +import ( + "context" + "io" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = metadata.Join + +func request_HeadscaleService_GetMachine_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetMachineRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["machine_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id") + } + + protoReq.MachineId, err = runtime.Uint64(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err) + } + + msg, err := client.GetMachine(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_GetMachine_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetMachineRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["machine_id"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id") + } + + protoReq.MachineId, err = runtime.Uint64(val) + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err) + } + + msg, err := server.GetMachine(ctx, &protoReq) + return msg, metadata, err + +} + +func request_HeadscaleService_CreateNamespace_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateNamespaceRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.CreateNamespace(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_CreateNamespace_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq CreateNamespaceRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.CreateNamespace(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_HeadscaleService_DeleteNamespace_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_HeadscaleService_DeleteNamespace_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteNamespaceRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_DeleteNamespace_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.DeleteNamespace(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_DeleteNamespace_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteNamespaceRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_DeleteNamespace_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.DeleteNamespace(ctx, &protoReq) + return msg, metadata, err + +} + +func request_HeadscaleService_ListNamespaces_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListNamespacesRequest + var metadata runtime.ServerMetadata + + msg, err := client.ListNamespaces(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_HeadscaleService_ListNamespaces_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListNamespacesRequest + var metadata runtime.ServerMetadata + + msg, err := server.ListNamespaces(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterHeadscaleServiceHandlerServer registers the http handlers for service HeadscaleService to "mux". +// UnaryRPC :call HeadscaleServiceServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterHeadscaleServiceHandlerFromEndpoint instead. +func RegisterHeadscaleServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server HeadscaleServiceServer) error { + + mux.Handle("GET", pattern_HeadscaleService_GetMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/headscale.v1.HeadscaleService/GetMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_GetMachine_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_HeadscaleService_GetMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_CreateNamespace_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/headscale.v1.HeadscaleService/CreateNamespace", runtime.WithHTTPPathPattern("/api/v1/namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_CreateNamespace_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_HeadscaleService_CreateNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_HeadscaleService_DeleteNamespace_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/headscale.v1.HeadscaleService/DeleteNamespace", runtime.WithHTTPPathPattern("/api/v1/namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_DeleteNamespace_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_HeadscaleService_DeleteNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_HeadscaleService_ListNamespaces_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/headscale.v1.HeadscaleService/ListNamespaces", runtime.WithHTTPPathPattern("/api/v1/namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_HeadscaleService_ListNamespaces_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_HeadscaleService_ListNamespaces_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterHeadscaleServiceHandlerFromEndpoint is same as RegisterHeadscaleServiceHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterHeadscaleServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterHeadscaleServiceHandler(ctx, mux, conn) +} + +// RegisterHeadscaleServiceHandler registers the http handlers for service HeadscaleService to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterHeadscaleServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterHeadscaleServiceHandlerClient(ctx, mux, NewHeadscaleServiceClient(conn)) +} + +// RegisterHeadscaleServiceHandlerClient registers the http handlers for service HeadscaleService +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "HeadscaleServiceClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "HeadscaleServiceClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "HeadscaleServiceClient" to call the correct interceptors. +func RegisterHeadscaleServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client HeadscaleServiceClient) error { + + mux.Handle("GET", pattern_HeadscaleService_GetMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/GetMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_GetMachine_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_HeadscaleService_GetMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_HeadscaleService_CreateNamespace_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/CreateNamespace", runtime.WithHTTPPathPattern("/api/v1/namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_CreateNamespace_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_HeadscaleService_CreateNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_HeadscaleService_DeleteNamespace_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/DeleteNamespace", runtime.WithHTTPPathPattern("/api/v1/namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_DeleteNamespace_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_HeadscaleService_DeleteNamespace_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_HeadscaleService_ListNamespaces_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/ListNamespaces", runtime.WithHTTPPathPattern("/api/v1/namespace")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_HeadscaleService_ListNamespaces_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_HeadscaleService_ListNamespaces_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_HeadscaleService_GetMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "machine", "machine_id"}, "")) + + pattern_HeadscaleService_CreateNamespace_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "namespace"}, "")) + + pattern_HeadscaleService_DeleteNamespace_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "namespace"}, "")) + + pattern_HeadscaleService_ListNamespaces_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "namespace"}, "")) +) + +var ( + forward_HeadscaleService_GetMachine_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_CreateNamespace_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_DeleteNamespace_0 = runtime.ForwardResponseMessage + + forward_HeadscaleService_ListNamespaces_0 = runtime.ForwardResponseMessage +) diff --git a/gen/go/headscale/v1/rpc_grpc.pb.go b/gen/go/headscale/v1/rpc_grpc.pb.go new file mode 100644 index 00000000..618c26d1 --- /dev/null +++ b/gen/go/headscale/v1/rpc_grpc.pb.go @@ -0,0 +1,209 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package v1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// HeadscaleServiceClient is the client API for HeadscaleService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type HeadscaleServiceClient interface { + GetMachine(ctx context.Context, in *GetMachineRequest, opts ...grpc.CallOption) (*GetMachineResponse, error) + CreateNamespace(ctx context.Context, in *CreateNamespaceRequest, opts ...grpc.CallOption) (*CreateNamespaceResponse, error) + DeleteNamespace(ctx context.Context, in *DeleteNamespaceRequest, opts ...grpc.CallOption) (*DeleteNamespaceResponse, error) + ListNamespaces(ctx context.Context, in *ListNamespacesRequest, opts ...grpc.CallOption) (*ListNamespacesResponse, error) +} + +type headscaleServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewHeadscaleServiceClient(cc grpc.ClientConnInterface) HeadscaleServiceClient { + return &headscaleServiceClient{cc} +} + +func (c *headscaleServiceClient) GetMachine(ctx context.Context, in *GetMachineRequest, opts ...grpc.CallOption) (*GetMachineResponse, error) { + out := new(GetMachineResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/GetMachine", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) CreateNamespace(ctx context.Context, in *CreateNamespaceRequest, opts ...grpc.CallOption) (*CreateNamespaceResponse, error) { + out := new(CreateNamespaceResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/CreateNamespace", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) DeleteNamespace(ctx context.Context, in *DeleteNamespaceRequest, opts ...grpc.CallOption) (*DeleteNamespaceResponse, error) { + out := new(DeleteNamespaceResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/DeleteNamespace", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *headscaleServiceClient) ListNamespaces(ctx context.Context, in *ListNamespacesRequest, opts ...grpc.CallOption) (*ListNamespacesResponse, error) { + out := new(ListNamespacesResponse) + err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ListNamespaces", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// HeadscaleServiceServer is the server API for HeadscaleService service. +// All implementations must embed UnimplementedHeadscaleServiceServer +// for forward compatibility +type HeadscaleServiceServer interface { + GetMachine(context.Context, *GetMachineRequest) (*GetMachineResponse, error) + CreateNamespace(context.Context, *CreateNamespaceRequest) (*CreateNamespaceResponse, error) + DeleteNamespace(context.Context, *DeleteNamespaceRequest) (*DeleteNamespaceResponse, error) + ListNamespaces(context.Context, *ListNamespacesRequest) (*ListNamespacesResponse, error) + mustEmbedUnimplementedHeadscaleServiceServer() +} + +// UnimplementedHeadscaleServiceServer must be embedded to have forward compatible implementations. +type UnimplementedHeadscaleServiceServer struct { +} + +func (UnimplementedHeadscaleServiceServer) GetMachine(context.Context, *GetMachineRequest) (*GetMachineResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetMachine not implemented") +} +func (UnimplementedHeadscaleServiceServer) CreateNamespace(context.Context, *CreateNamespaceRequest) (*CreateNamespaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateNamespace not implemented") +} +func (UnimplementedHeadscaleServiceServer) DeleteNamespace(context.Context, *DeleteNamespaceRequest) (*DeleteNamespaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteNamespace not implemented") +} +func (UnimplementedHeadscaleServiceServer) ListNamespaces(context.Context, *ListNamespacesRequest) (*ListNamespacesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListNamespaces not implemented") +} +func (UnimplementedHeadscaleServiceServer) mustEmbedUnimplementedHeadscaleServiceServer() {} + +// UnsafeHeadscaleServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to HeadscaleServiceServer will +// result in compilation errors. +type UnsafeHeadscaleServiceServer interface { + mustEmbedUnimplementedHeadscaleServiceServer() +} + +func RegisterHeadscaleServiceServer(s grpc.ServiceRegistrar, srv HeadscaleServiceServer) { + s.RegisterService(&HeadscaleService_ServiceDesc, srv) +} + +func _HeadscaleService_GetMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetMachineRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).GetMachine(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/GetMachine", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).GetMachine(ctx, req.(*GetMachineRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_CreateNamespace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateNamespaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).CreateNamespace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/CreateNamespace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).CreateNamespace(ctx, req.(*CreateNamespaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_DeleteNamespace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteNamespaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).DeleteNamespace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/DeleteNamespace", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).DeleteNamespace(ctx, req.(*DeleteNamespaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HeadscaleService_ListNamespaces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListNamespacesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HeadscaleServiceServer).ListNamespaces(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/headscale.v1.HeadscaleService/ListNamespaces", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HeadscaleServiceServer).ListNamespaces(ctx, req.(*ListNamespacesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// HeadscaleService_ServiceDesc is the grpc.ServiceDesc for HeadscaleService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var HeadscaleService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "headscale.v1.HeadscaleService", + HandlerType: (*HeadscaleServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetMachine", + Handler: _HeadscaleService_GetMachine_Handler, + }, + { + MethodName: "CreateNamespace", + Handler: _HeadscaleService_CreateNamespace_Handler, + }, + { + MethodName: "DeleteNamespace", + Handler: _HeadscaleService_DeleteNamespace_Handler, + }, + { + MethodName: "ListNamespaces", + Handler: _HeadscaleService_ListNamespaces_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "headscale/v1/rpc.proto", +} diff --git a/gen/go/v1/headscale.pb.go b/gen/go/v1/headscale.pb.go deleted file mode 100644 index 104e3746..00000000 --- a/gen/go/v1/headscale.pb.go +++ /dev/null @@ -1,702 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.27.1 -// protoc v3.18.1 -// source: v1/headscale.proto - -package v1 - -import ( - _ "github.com/infobloxopen/protoc-gen-gorm/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type RegisterMethod int32 - -const ( - RegisterMethod_AUTH_KEY RegisterMethod = 0 - RegisterMethod_CLI RegisterMethod = 1 - RegisterMethod_OIDC RegisterMethod = 2 -) - -// Enum value maps for RegisterMethod. -var ( - RegisterMethod_name = map[int32]string{ - 0: "AUTH_KEY", - 1: "CLI", - 2: "OIDC", - } - RegisterMethod_value = map[string]int32{ - "AUTH_KEY": 0, - "CLI": 1, - "OIDC": 2, - } -) - -func (x RegisterMethod) Enum() *RegisterMethod { - p := new(RegisterMethod) - *p = x - return p -} - -func (x RegisterMethod) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (RegisterMethod) Descriptor() protoreflect.EnumDescriptor { - return file_v1_headscale_proto_enumTypes[0].Descriptor() -} - -func (RegisterMethod) Type() protoreflect.EnumType { - return &file_v1_headscale_proto_enumTypes[0] -} - -func (x RegisterMethod) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use RegisterMethod.Descriptor instead. -func (RegisterMethod) EnumDescriptor() ([]byte, []int) { - return file_v1_headscale_proto_rawDescGZIP(), []int{0} -} - -type Namespace struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` -} - -func (x *Namespace) Reset() { - *x = Namespace{} - if protoimpl.UnsafeEnabled { - mi := &file_v1_headscale_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Namespace) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Namespace) ProtoMessage() {} - -func (x *Namespace) ProtoReflect() protoreflect.Message { - mi := &file_v1_headscale_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Namespace.ProtoReflect.Descriptor instead. -func (*Namespace) Descriptor() ([]byte, []int) { - return file_v1_headscale_proto_rawDescGZIP(), []int{0} -} - -func (x *Namespace) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type PreAuthKey struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` - Key string `protobuf:"bytes,2,opt,name=Key,proto3" json:"Key,omitempty"` - NamespaceID uint32 `protobuf:"varint,3,opt,name=NamespaceID,proto3" json:"NamespaceID,omitempty"` - Namespace *Namespace `protobuf:"bytes,4,opt,name=Namespace,proto3" json:"Namespace,omitempty"` - Reusable bool `protobuf:"varint,5,opt,name=Reusable,proto3" json:"Reusable,omitempty"` - Ephemeral bool `protobuf:"varint,6,opt,name=Ephemeral,proto3" json:"Ephemeral,omitempty"` - Used bool `protobuf:"varint,7,opt,name=Used,proto3" json:"Used,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"` - Expiration *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=Expiration,proto3" json:"Expiration,omitempty"` -} - -func (x *PreAuthKey) Reset() { - *x = PreAuthKey{} - if protoimpl.UnsafeEnabled { - mi := &file_v1_headscale_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PreAuthKey) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PreAuthKey) ProtoMessage() {} - -func (x *PreAuthKey) ProtoReflect() protoreflect.Message { - mi := &file_v1_headscale_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PreAuthKey.ProtoReflect.Descriptor instead. -func (*PreAuthKey) Descriptor() ([]byte, []int) { - return file_v1_headscale_proto_rawDescGZIP(), []int{1} -} - -func (x *PreAuthKey) GetID() uint64 { - if x != nil { - return x.ID - } - return 0 -} - -func (x *PreAuthKey) GetKey() string { - if x != nil { - return x.Key - } - return "" -} - -func (x *PreAuthKey) GetNamespaceID() uint32 { - if x != nil { - return x.NamespaceID - } - return 0 -} - -func (x *PreAuthKey) GetNamespace() *Namespace { - if x != nil { - return x.Namespace - } - return nil -} - -func (x *PreAuthKey) GetReusable() bool { - if x != nil { - return x.Reusable - } - return false -} - -func (x *PreAuthKey) GetEphemeral() bool { - if x != nil { - return x.Ephemeral - } - return false -} - -func (x *PreAuthKey) GetUsed() bool { - if x != nil { - return x.Used - } - return false -} - -func (x *PreAuthKey) GetCreatedAt() *timestamppb.Timestamp { - if x != nil { - return x.CreatedAt - } - return nil -} - -func (x *PreAuthKey) GetExpiration() *timestamppb.Timestamp { - if x != nil { - return x.Expiration - } - return nil -} - -type GetMachineRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - MachineId uint64 `protobuf:"varint,1,opt,name=machine_id,json=machineId,proto3" json:"machine_id,omitempty"` -} - -func (x *GetMachineRequest) Reset() { - *x = GetMachineRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_v1_headscale_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetMachineRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetMachineRequest) ProtoMessage() {} - -func (x *GetMachineRequest) ProtoReflect() protoreflect.Message { - mi := &file_v1_headscale_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetMachineRequest.ProtoReflect.Descriptor instead. -func (*GetMachineRequest) Descriptor() ([]byte, []int) { - return file_v1_headscale_proto_rawDescGZIP(), []int{2} -} - -func (x *GetMachineRequest) GetMachineId() uint64 { - if x != nil { - return x.MachineId - } - return 0 -} - -type Machine struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` - MachineKey string `protobuf:"bytes,2,opt,name=MachineKey,proto3" json:"MachineKey,omitempty"` - NodeKey string `protobuf:"bytes,3,opt,name=NodeKey,proto3" json:"NodeKey,omitempty"` - DiscoKey string `protobuf:"bytes,4,opt,name=DiscoKey,proto3" json:"DiscoKey,omitempty"` - IPAddress string `protobuf:"bytes,5,opt,name=IPAddress,proto3" json:"IPAddress,omitempty"` - Name string `protobuf:"bytes,6,opt,name=Name,proto3" json:"Name,omitempty"` - NamespaceID uint32 `protobuf:"varint,7,opt,name=NamespaceID,proto3" json:"NamespaceID,omitempty"` - Registered bool `protobuf:"varint,8,opt,name=Registered,proto3" json:"Registered,omitempty"` - RegisterMethod RegisterMethod `protobuf:"varint,9,opt,name=RegisterMethod,proto3,enum=headscale.v1.RegisterMethod" json:"RegisterMethod,omitempty"` - AuthKeyID uint32 `protobuf:"varint,10,opt,name=AuthKeyID,proto3" json:"AuthKeyID,omitempty"` - AuthKey *PreAuthKey `protobuf:"bytes,11,opt,name=AuthKey,proto3" json:"AuthKey,omitempty"` - LastSeen *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=LastSeen,proto3" json:"LastSeen,omitempty"` - LastSuccessfulUpdate *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=LastSuccessfulUpdate,proto3" json:"LastSuccessfulUpdate,omitempty"` - Expiry *timestamppb.Timestamp `protobuf:"bytes,14,opt,name=Expiry,proto3" json:"Expiry,omitempty"` - HostInfo []byte `protobuf:"bytes,15,opt,name=HostInfo,proto3" json:"HostInfo,omitempty"` - Endpoints []byte `protobuf:"bytes,16,opt,name=Endpoints,proto3" json:"Endpoints,omitempty"` - EnabledRoutes []byte `protobuf:"bytes,17,opt,name=EnabledRoutes,proto3" json:"EnabledRoutes,omitempty"` - CreatedAt *timestamppb.Timestamp `protobuf:"bytes,18,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"` - UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,19,opt,name=UpdatedAt,proto3" json:"UpdatedAt,omitempty"` - DeletedAt *timestamppb.Timestamp `protobuf:"bytes,20,opt,name=DeletedAt,proto3" json:"DeletedAt,omitempty"` -} - -func (x *Machine) Reset() { - *x = Machine{} - if protoimpl.UnsafeEnabled { - mi := &file_v1_headscale_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Machine) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Machine) ProtoMessage() {} - -func (x *Machine) ProtoReflect() protoreflect.Message { - mi := &file_v1_headscale_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Machine.ProtoReflect.Descriptor instead. -func (*Machine) Descriptor() ([]byte, []int) { - return file_v1_headscale_proto_rawDescGZIP(), []int{3} -} - -func (x *Machine) GetID() uint64 { - if x != nil { - return x.ID - } - return 0 -} - -func (x *Machine) GetMachineKey() string { - if x != nil { - return x.MachineKey - } - return "" -} - -func (x *Machine) GetNodeKey() string { - if x != nil { - return x.NodeKey - } - return "" -} - -func (x *Machine) GetDiscoKey() string { - if x != nil { - return x.DiscoKey - } - return "" -} - -func (x *Machine) GetIPAddress() string { - if x != nil { - return x.IPAddress - } - return "" -} - -func (x *Machine) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *Machine) GetNamespaceID() uint32 { - if x != nil { - return x.NamespaceID - } - return 0 -} - -func (x *Machine) GetRegistered() bool { - if x != nil { - return x.Registered - } - return false -} - -func (x *Machine) GetRegisterMethod() RegisterMethod { - if x != nil { - return x.RegisterMethod - } - return RegisterMethod_AUTH_KEY -} - -func (x *Machine) GetAuthKeyID() uint32 { - if x != nil { - return x.AuthKeyID - } - return 0 -} - -func (x *Machine) GetAuthKey() *PreAuthKey { - if x != nil { - return x.AuthKey - } - return nil -} - -func (x *Machine) GetLastSeen() *timestamppb.Timestamp { - if x != nil { - return x.LastSeen - } - return nil -} - -func (x *Machine) GetLastSuccessfulUpdate() *timestamppb.Timestamp { - if x != nil { - return x.LastSuccessfulUpdate - } - return nil -} - -func (x *Machine) GetExpiry() *timestamppb.Timestamp { - if x != nil { - return x.Expiry - } - return nil -} - -func (x *Machine) GetHostInfo() []byte { - if x != nil { - return x.HostInfo - } - return nil -} - -func (x *Machine) GetEndpoints() []byte { - if x != nil { - return x.Endpoints - } - return nil -} - -func (x *Machine) GetEnabledRoutes() []byte { - if x != nil { - return x.EnabledRoutes - } - return nil -} - -func (x *Machine) GetCreatedAt() *timestamppb.Timestamp { - if x != nil { - return x.CreatedAt - } - return nil -} - -func (x *Machine) GetUpdatedAt() *timestamppb.Timestamp { - if x != nil { - return x.UpdatedAt - } - return nil -} - -func (x *Machine) GetDeletedAt() *timestamppb.Timestamp { - if x != nil { - return x.DeletedAt - } - return nil -} - -var File_v1_headscale_proto protoreflect.FileDescriptor - -var file_v1_headscale_proto_rawDesc = []byte{ - 0x0a, 0x12, 0x76, 0x31, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, - 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x1a, 0x12, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x67, 0x6f, 0x72, 0x6d, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1f, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xcb, 0x02, 0x0a, 0x0a, 0x50, 0x72, 0x65, 0x41, 0x75, - 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x02, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x4e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x44, 0x12, 0x35, 0x0a, 0x09, 0x4e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x68, - 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x08, 0x52, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, - 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x73, - 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x55, 0x73, 0x65, 0x64, 0x12, 0x38, - 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x3a, 0x0a, 0x0a, 0x45, 0x78, 0x70, 0x69, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x45, 0x78, 0x70, 0x69, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x32, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, - 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, - 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, - 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0xcd, 0x06, 0x0a, 0x07, 0x4d, 0x61, 0x63, - 0x68, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x02, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x4b, - 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, - 0x65, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x4e, 0x6f, 0x64, 0x65, 0x4b, 0x65, 0x79, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4e, 0x6f, 0x64, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1a, - 0x0a, 0x08, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x4b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x49, 0x50, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x49, - 0x50, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x44, 0x12, 0x1e, - 0x0a, 0x0a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x44, - 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, - 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, - 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x49, - 0x44, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, - 0x49, 0x44, 0x12, 0x32, 0x0a, 0x07, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x07, 0x41, - 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x08, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, - 0x65, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x4e, - 0x0a, 0x14, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x14, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x75, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x32, - 0x0a, 0x06, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x45, 0x78, 0x70, 0x69, - 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x0f, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, - 0x0a, 0x09, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x09, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x24, 0x0a, 0x0d, - 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x11, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, - 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x38, 0x0a, 0x09, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x64, 0x41, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x3a, 0x06, 0xba, 0xb9, 0x19, 0x02, 0x08, 0x01, 0x2a, 0x31, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x55, - 0x54, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x43, 0x4c, 0x49, 0x10, - 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x02, 0x32, 0x7e, 0x0a, 0x10, 0x48, - 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x6a, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x1f, 0x2e, - 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, - 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, - 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, - 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x42, 0x29, 0x5a, 0x27, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, - 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, - 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_v1_headscale_proto_rawDescOnce sync.Once - file_v1_headscale_proto_rawDescData = file_v1_headscale_proto_rawDesc -) - -func file_v1_headscale_proto_rawDescGZIP() []byte { - file_v1_headscale_proto_rawDescOnce.Do(func() { - file_v1_headscale_proto_rawDescData = protoimpl.X.CompressGZIP(file_v1_headscale_proto_rawDescData) - }) - return file_v1_headscale_proto_rawDescData -} - -var file_v1_headscale_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_v1_headscale_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_v1_headscale_proto_goTypes = []interface{}{ - (RegisterMethod)(0), // 0: headscale.v1.RegisterMethod - (*Namespace)(nil), // 1: headscale.v1.Namespace - (*PreAuthKey)(nil), // 2: headscale.v1.PreAuthKey - (*GetMachineRequest)(nil), // 3: headscale.v1.GetMachineRequest - (*Machine)(nil), // 4: headscale.v1.Machine - (*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp -} -var file_v1_headscale_proto_depIdxs = []int32{ - 1, // 0: headscale.v1.PreAuthKey.Namespace:type_name -> headscale.v1.Namespace - 5, // 1: headscale.v1.PreAuthKey.CreatedAt:type_name -> google.protobuf.Timestamp - 5, // 2: headscale.v1.PreAuthKey.Expiration:type_name -> google.protobuf.Timestamp - 0, // 3: headscale.v1.Machine.RegisterMethod:type_name -> headscale.v1.RegisterMethod - 2, // 4: headscale.v1.Machine.AuthKey:type_name -> headscale.v1.PreAuthKey - 5, // 5: headscale.v1.Machine.LastSeen:type_name -> google.protobuf.Timestamp - 5, // 6: headscale.v1.Machine.LastSuccessfulUpdate:type_name -> google.protobuf.Timestamp - 5, // 7: headscale.v1.Machine.Expiry:type_name -> google.protobuf.Timestamp - 5, // 8: headscale.v1.Machine.CreatedAt:type_name -> google.protobuf.Timestamp - 5, // 9: headscale.v1.Machine.UpdatedAt:type_name -> google.protobuf.Timestamp - 5, // 10: headscale.v1.Machine.DeletedAt:type_name -> google.protobuf.Timestamp - 3, // 11: headscale.v1.HeadscaleService.GetMachine:input_type -> headscale.v1.GetMachineRequest - 4, // 12: headscale.v1.HeadscaleService.GetMachine:output_type -> headscale.v1.Machine - 12, // [12:13] is the sub-list for method output_type - 11, // [11:12] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name -} - -func init() { file_v1_headscale_proto_init() } -func file_v1_headscale_proto_init() { - if File_v1_headscale_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_v1_headscale_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Namespace); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_v1_headscale_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PreAuthKey); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_v1_headscale_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMachineRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_v1_headscale_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Machine); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_v1_headscale_proto_rawDesc, - NumEnums: 1, - NumMessages: 4, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_v1_headscale_proto_goTypes, - DependencyIndexes: file_v1_headscale_proto_depIdxs, - EnumInfos: file_v1_headscale_proto_enumTypes, - MessageInfos: file_v1_headscale_proto_msgTypes, - }.Build() - File_v1_headscale_proto = out.File - file_v1_headscale_proto_rawDesc = nil - file_v1_headscale_proto_goTypes = nil - file_v1_headscale_proto_depIdxs = nil -} diff --git a/gen/go/v1/headscale.pb.gw.go b/gen/go/v1/headscale.pb.gw.go deleted file mode 100644 index 4ae6db39..00000000 --- a/gen/go/v1/headscale.pb.gw.go +++ /dev/null @@ -1,185 +0,0 @@ -// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: v1/headscale.proto - -/* -Package v1 is a reverse proxy. - -It translates gRPC into RESTful JSON APIs. -*/ -package v1 - -import ( - "context" - "io" - "net/http" - - "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" - "github.com/grpc-ecosystem/grpc-gateway/v2/utilities" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" -) - -// Suppress "imported and not used" errors -var _ codes.Code -var _ io.Reader -var _ status.Status -var _ = runtime.String -var _ = utilities.NewDoubleArray -var _ = metadata.Join - -func request_HeadscaleService_GetMachine_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq GetMachineRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["machine_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id") - } - - protoReq.MachineId, err = runtime.Uint64(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err) - } - - msg, err := client.GetMachine(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_HeadscaleService_GetMachine_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq GetMachineRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["machine_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id") - } - - protoReq.MachineId, err = runtime.Uint64(val) - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err) - } - - msg, err := server.GetMachine(ctx, &protoReq) - return msg, metadata, err - -} - -// RegisterHeadscaleServiceHandlerServer registers the http handlers for service HeadscaleService to "mux". -// UnaryRPC :call HeadscaleServiceServer directly. -// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. -// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterHeadscaleServiceHandlerFromEndpoint instead. -func RegisterHeadscaleServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server HeadscaleServiceServer) error { - - mux.Handle("GET", pattern_HeadscaleService_GetMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/headscale.v1.HeadscaleService/GetMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_HeadscaleService_GetMachine_0(rctx, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_HeadscaleService_GetMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -// RegisterHeadscaleServiceHandlerFromEndpoint is same as RegisterHeadscaleServiceHandler but -// automatically dials to "endpoint" and closes the connection when "ctx" gets done. -func RegisterHeadscaleServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) - if err != nil { - return err - } - defer func() { - if err != nil { - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - return - } - go func() { - <-ctx.Done() - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - }() - }() - - return RegisterHeadscaleServiceHandler(ctx, mux, conn) -} - -// RegisterHeadscaleServiceHandler registers the http handlers for service HeadscaleService to "mux". -// The handlers forward requests to the grpc endpoint over "conn". -func RegisterHeadscaleServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { - return RegisterHeadscaleServiceHandlerClient(ctx, mux, NewHeadscaleServiceClient(conn)) -} - -// RegisterHeadscaleServiceHandlerClient registers the http handlers for service HeadscaleService -// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "HeadscaleServiceClient". -// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "HeadscaleServiceClient" -// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in -// "HeadscaleServiceClient" to call the correct interceptors. -func RegisterHeadscaleServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client HeadscaleServiceClient) error { - - mux.Handle("GET", pattern_HeadscaleService_GetMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/GetMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}")) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_HeadscaleService_GetMachine_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_HeadscaleService_GetMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -var ( - pattern_HeadscaleService_GetMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "machine", "machine_id"}, "")) -) - -var ( - forward_HeadscaleService_GetMachine_0 = runtime.ForwardResponseMessage -) diff --git a/gen/go/v1/headscale_grpc.pb.go b/gen/go/v1/headscale_grpc.pb.go deleted file mode 100644 index 3028d183..00000000 --- a/gen/go/v1/headscale_grpc.pb.go +++ /dev/null @@ -1,101 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. - -package v1 - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// HeadscaleServiceClient is the client API for HeadscaleService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type HeadscaleServiceClient interface { - GetMachine(ctx context.Context, in *GetMachineRequest, opts ...grpc.CallOption) (*Machine, error) -} - -type headscaleServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewHeadscaleServiceClient(cc grpc.ClientConnInterface) HeadscaleServiceClient { - return &headscaleServiceClient{cc} -} - -func (c *headscaleServiceClient) GetMachine(ctx context.Context, in *GetMachineRequest, opts ...grpc.CallOption) (*Machine, error) { - out := new(Machine) - err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/GetMachine", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// HeadscaleServiceServer is the server API for HeadscaleService service. -// All implementations must embed UnimplementedHeadscaleServiceServer -// for forward compatibility -type HeadscaleServiceServer interface { - GetMachine(context.Context, *GetMachineRequest) (*Machine, error) - mustEmbedUnimplementedHeadscaleServiceServer() -} - -// UnimplementedHeadscaleServiceServer must be embedded to have forward compatible implementations. -type UnimplementedHeadscaleServiceServer struct { -} - -func (UnimplementedHeadscaleServiceServer) GetMachine(context.Context, *GetMachineRequest) (*Machine, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetMachine not implemented") -} -func (UnimplementedHeadscaleServiceServer) mustEmbedUnimplementedHeadscaleServiceServer() {} - -// UnsafeHeadscaleServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to HeadscaleServiceServer will -// result in compilation errors. -type UnsafeHeadscaleServiceServer interface { - mustEmbedUnimplementedHeadscaleServiceServer() -} - -func RegisterHeadscaleServiceServer(s grpc.ServiceRegistrar, srv HeadscaleServiceServer) { - s.RegisterService(&HeadscaleService_ServiceDesc, srv) -} - -func _HeadscaleService_GetMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetMachineRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(HeadscaleServiceServer).GetMachine(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/headscale.v1.HeadscaleService/GetMachine", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(HeadscaleServiceServer).GetMachine(ctx, req.(*GetMachineRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// HeadscaleService_ServiceDesc is the grpc.ServiceDesc for HeadscaleService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var HeadscaleService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "headscale.v1.HeadscaleService", - HandlerType: (*HeadscaleServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "GetMachine", - Handler: _HeadscaleService_GetMachine_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "v1/headscale.proto", -} diff --git a/gen/openapiv2/headscale/v1/rpc.swagger.json b/gen/openapiv2/headscale/v1/rpc.swagger.json new file mode 100644 index 00000000..e9db7cf6 --- /dev/null +++ b/gen/openapiv2/headscale/v1/rpc.swagger.json @@ -0,0 +1,250 @@ +{ + "swagger": "2.0", + "info": { + "title": "headscale/v1/rpc.proto", + "version": "version not set" + }, + "tags": [ + { + "name": "HeadscaleService" + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/api/v1/machine/{machineId}": { + "get": { + "operationId": "HeadscaleService_GetMachine", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1GetMachineResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "machineId", + "in": "path", + "required": true, + "type": "string", + "format": "uint64" + } + ], + "tags": [ + "HeadscaleService" + ] + } + }, + "/api/v1/namespace": { + "get": { + "operationId": "HeadscaleService_ListNamespaces", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1ListNamespacesResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "HeadscaleService" + ] + }, + "delete": { + "operationId": "HeadscaleService_DeleteNamespace", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1DeleteNamespaceResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "name", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "HeadscaleService" + ] + }, + "post": { + "operationId": "HeadscaleService_CreateNamespace", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1CreateNamespaceResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1CreateNamespaceRequest" + } + } + ], + "tags": [ + "HeadscaleService" + ] + } + } + }, + "definitions": { + "protobufAny": { + "type": "object", + "properties": { + "@type": { + "type": "string" + } + }, + "additionalProperties": {} + }, + "rpcStatus": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } + } + } + }, + "v1CreateNamespaceRequest": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "v1CreateNamespaceResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "v1DeleteNamespaceResponse": { + "type": "object" + }, + "v1GetMachineResponse": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uint64" + }, + "machineKey": { + "type": "string" + }, + "nodeKey": { + "type": "string" + }, + "discoKey": { + "type": "string" + }, + "ipAddress": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespaceId": { + "type": "integer", + "format": "int64" + }, + "registered": { + "type": "boolean" + }, + "registerMethod": { + "$ref": "#/definitions/v1RegisterMethod" + }, + "authKeyId": { + "type": "integer", + "format": "int64" + }, + "lastSeen": { + "type": "string", + "format": "date-time" + }, + "lastSuccessfulUpdate": { + "type": "string", + "format": "date-time" + }, + "expiry": { + "type": "string", + "format": "date-time" + } + } + }, + "v1ListNamespacesResponse": { + "type": "object", + "properties": { + "namespaces": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "v1RegisterMethod": { + "type": "string", + "enum": [ + "REGISTER_METHOD_UNSPECIFIED", + "REGISTER_METHOD_AUTH_KEY", + "REGISTER_METHOD_CLI", + "REGISTER_METHOD_OIDC" + ], + "default": "REGISTER_METHOD_UNSPECIFIED" + } + } +} diff --git a/gen/openapiv2/v1/headscale.swagger.json b/gen/openapiv2/v1/headscale.swagger.json deleted file mode 100644 index a20225d3..00000000 --- a/gen/openapiv2/v1/headscale.swagger.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "v1/headscale.proto", - "version": "version not set" - }, - "tags": [ - { - "name": "HeadscaleService" - } - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/api/v1/machine/{machineId}": { - "get": { - "operationId": "HeadscaleService_GetMachine", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v1Machine" - } - }, - "default": { - "description": "An unexpected error response.", - "schema": { - "$ref": "#/definitions/rpcStatus" - } - } - }, - "parameters": [ - { - "name": "machineId", - "in": "path", - "required": true, - "type": "string", - "format": "uint64" - } - ], - "tags": [ - "HeadscaleService" - ] - } - } - }, - "definitions": { - "protobufAny": { - "type": "object", - "properties": { - "@type": { - "type": "string" - } - }, - "additionalProperties": {} - }, - "rpcStatus": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - }, - "details": { - "type": "array", - "items": { - "$ref": "#/definitions/protobufAny" - } - } - } - }, - "v1Machine": { - "type": "object", - "properties": { - "ID": { - "type": "string", - "format": "uint64" - }, - "MachineKey": { - "type": "string" - }, - "NodeKey": { - "type": "string" - }, - "DiscoKey": { - "type": "string" - }, - "IPAddress": { - "type": "string" - }, - "Name": { - "type": "string" - }, - "NamespaceID": { - "type": "integer", - "format": "int64" - }, - "Registered": { - "type": "boolean" - }, - "RegisterMethod": { - "$ref": "#/definitions/v1RegisterMethod" - }, - "AuthKeyID": { - "type": "integer", - "format": "int64" - }, - "AuthKey": { - "$ref": "#/definitions/v1PreAuthKey" - }, - "LastSeen": { - "type": "string", - "format": "date-time" - }, - "LastSuccessfulUpdate": { - "type": "string", - "format": "date-time" - }, - "Expiry": { - "type": "string", - "format": "date-time" - }, - "HostInfo": { - "type": "string", - "format": "byte" - }, - "Endpoints": { - "type": "string", - "format": "byte" - }, - "EnabledRoutes": { - "type": "string", - "format": "byte" - }, - "CreatedAt": { - "type": "string", - "format": "date-time" - }, - "UpdatedAt": { - "type": "string", - "format": "date-time" - }, - "DeletedAt": { - "type": "string", - "format": "date-time" - } - } - }, - "v1Namespace": { - "type": "object", - "properties": { - "Name": { - "type": "string" - } - } - }, - "v1PreAuthKey": { - "type": "object", - "properties": { - "ID": { - "type": "string", - "format": "uint64" - }, - "Key": { - "type": "string" - }, - "NamespaceID": { - "type": "integer", - "format": "int64" - }, - "Namespace": { - "$ref": "#/definitions/v1Namespace" - }, - "Reusable": { - "type": "boolean" - }, - "Ephemeral": { - "type": "boolean" - }, - "Used": { - "type": "boolean" - }, - "CreatedAt": { - "type": "string", - "format": "date-time" - }, - "Expiration": { - "type": "string", - "format": "date-time" - } - } - }, - "v1RegisterMethod": { - "type": "string", - "enum": [ - "AUTH_KEY", - "CLI", - "OIDC" - ], - "default": "AUTH_KEY" - } - } -} diff --git a/proto/headscale/v1/rpc.proto b/proto/headscale/v1/rpc.proto new file mode 100644 index 00000000..2e6b0a67 --- /dev/null +++ b/proto/headscale/v1/rpc.proto @@ -0,0 +1,106 @@ +syntax = "proto3"; +package headscale.v1; +option go_package = "github.com/juanfont/headscale/gen/go/v1"; + +import "google/protobuf/timestamp.proto"; +import "google/api/annotations.proto"; + +enum RegisterMethod { + REGISTER_METHOD_UNSPECIFIED = 0; + REGISTER_METHOD_AUTH_KEY = 1; + REGISTER_METHOD_CLI = 2; + REGISTER_METHOD_OIDC = 3; +} + +// message PreAuthKey { +// uint64 id = 1; +// string key = 2; +// uint32 namespace_id = 3; +// Namespace namespace = 4; +// bool reusable = 5; +// bool ephemeral = 6; +// bool used = 7; +// +// google.protobuf.Timestamp created_at = 8; +// google.protobuf.Timestamp expiration = 9; +// } + +message GetMachineRequest { + uint64 machine_id = 1; +} + +message GetMachineResponse { + uint64 id = 1; + string machine_key = 2; + string node_key = 3; + string disco_key = 4; + string ip_address = 5; + string name = 6; + uint32 namespace_id = 7; + + bool registered = 8; + RegisterMethod register_method = 9; + uint32 auth_key_id = 10; + // PreAuthKey auth_key = 11; + + google.protobuf.Timestamp last_seen = 12; + google.protobuf.Timestamp last_successful_update = 13; + google.protobuf.Timestamp expiry = 14; + + // bytes host_info = 15; + // bytes endpoints = 16; + // bytes enabled_routes = 17; + + // google.protobuf.Timestamp created_at = 18; + // google.protobuf.Timestamp updated_at = 19; + // google.protobuf.Timestamp deleted_at = 20; +} + +message CreateNamespaceRequest { + string name = 1; +} + +message CreateNamespaceResponse { + string name = 1; +} + +message DeleteNamespaceRequest { + string name = 1; +} + +message DeleteNamespaceResponse { +} + +message ListNamespacesRequest { +} + +message ListNamespacesResponse { + repeated string namespaces = 1; +} + +service HeadscaleService { + rpc GetMachine(GetMachineRequest) returns(GetMachineResponse) { + option(google.api.http) = { + get : "/api/v1/machine/{machine_id}" + }; + } + + rpc CreateNamespace(CreateNamespaceRequest) returns(CreateNamespaceResponse) { + option(google.api.http) = { + post : "/api/v1/namespace" + body : "*" + }; + } + + rpc DeleteNamespace(DeleteNamespaceRequest) returns(DeleteNamespaceResponse) { + option(google.api.http) = { + delete : "/api/v1/namespace" + }; + } + + rpc ListNamespaces(ListNamespacesRequest) returns(ListNamespacesResponse) { + option(google.api.http) = { + get : "/api/v1/namespace" + }; + } +} diff --git a/proto/v1/headscale.proto b/proto/v1/headscale.proto deleted file mode 100644 index b6356b8f..00000000 --- a/proto/v1/headscale.proto +++ /dev/null @@ -1,71 +0,0 @@ -syntax = "proto3"; -package headscale.v1; -option go_package = "github.com/juanfont/headscale/gen/go/v1"; - -import "google/protobuf/timestamp.proto"; -import "google/api/annotations.proto"; -import "options/gorm.proto"; - -enum RegisterMethod { - AUTH_KEY = 0; - CLI = 1; - OIDC = 2; -} - -message Namespace { - string Name = 1; -} - -message PreAuthKey { - uint64 ID = 1; - string Key = 2; - uint32 NamespaceID = 3; - Namespace Namespace = 4; - bool Reusable = 5; - bool Ephemeral = 6; - bool Used = 7; - - google.protobuf.Timestamp CreatedAt = 8; - google.protobuf.Timestamp Expiration = 9; -} - -message GetMachineRequest { - uint64 machine_id = 1; -} - -message Machine { - option(gorm.opts).ormable = true; - uint64 ID = 1; - string MachineKey = 2; - string NodeKey = 3; - string DiscoKey = 4; - string IPAddress = 5; - string Name = 6; - uint32 NamespaceID = 7; - - bool Registered = 8; - RegisterMethod RegisterMethod = 9; - uint32 AuthKeyID = 10; - PreAuthKey AuthKey = 11; - - google.protobuf.Timestamp LastSeen = 12; - google.protobuf.Timestamp LastSuccessfulUpdate = 13; - google.protobuf.Timestamp Expiry = 14; - - bytes HostInfo = 15; - bytes Endpoints = 16; - bytes EnabledRoutes = 17; - - google.protobuf.Timestamp CreatedAt = 18; - google.protobuf.Timestamp UpdatedAt = 19; - google.protobuf.Timestamp DeletedAt = 20; -} - -// Gin Router will prefix this with /api/v1 -service HeadscaleService { - rpc GetMachine(GetMachineRequest) returns(Machine) { - option(google.api.http) = { - get : "/api/v1/machine/{machine_id}" - }; - } -} From c7fa9b6e4a7593eb37d729f158225cf4139fe6fd Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 29 Oct 2021 16:44:32 +0000 Subject: [PATCH 46/70] Setup create, delete and list namespace over grpc --- grpcv1.go | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/grpcv1.go b/grpcv1.go index 5fc2e1c8..08c977b8 100644 --- a/grpcv1.go +++ b/grpcv1.go @@ -4,7 +4,7 @@ package headscale import ( "context" - apiV1 "github.com/juanfont/headscale/gen/go/v1" + apiV1 "github.com/juanfont/headscale/gen/go/headscale/v1" ) type headscaleV1APIServer struct { // apiV1.HeadscaleServiceServer @@ -21,14 +21,55 @@ func newHeadscaleV1APIServer(h *Headscale) apiV1.HeadscaleServiceServer { func (api headscaleV1APIServer) GetMachine( ctx context.Context, request *apiV1.GetMachineRequest, -) (*apiV1.Machine, error) { - m, err := api.h.GetMachineByID(request.MachineId) +) (*apiV1.GetMachineResponse, error) { + // m, err := api.h.GetMachineByID(request.MachineId) + // if err != nil { + // return nil, err + // } + + // TODO(kradalby): Make this function actually do something + return &apiV1.GetMachineResponse{Name: "test"}, nil +} + +func (api headscaleV1APIServer) CreateNamespace( + ctx context.Context, + request *apiV1.CreateNamespaceRequest, +) (*apiV1.CreateNamespaceResponse, error) { + namespace, err := api.h.CreateNamespace(request.Name) if err != nil { return nil, err } - // TODO(kradalby): Make this function actually do something - return &apiV1.Machine{Name: m.Name}, nil + return &apiV1.CreateNamespaceResponse{Name: namespace.Name}, nil +} + +func (api headscaleV1APIServer) DeleteNamespace( + ctx context.Context, + request *apiV1.DeleteNamespaceRequest, +) (*apiV1.DeleteNamespaceResponse, error) { + err := api.h.DestroyNamespace(request.Name) + if err != nil { + return nil, err + } + + return &apiV1.DeleteNamespaceResponse{}, nil +} + +func (api headscaleV1APIServer) ListNamespaces( + ctx context.Context, + request *apiV1.ListNamespacesRequest, +) (*apiV1.ListNamespacesResponse, error) { + namespaces, err := api.h.ListNamespaces() + if err != nil { + return nil, err + } + + response := make([]string, len(*namespaces)) + for index, namespace := range *namespaces { + response[index] = namespace.Name + } + + return &apiV1.ListNamespacesResponse{Namespaces: response}, nil } func (api headscaleV1APIServer) mustEmbedUnimplementedHeadscaleServiceServer() {} From a23d82e33af1117160a43d48249359eb54b55540 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 29 Oct 2021 16:45:06 +0000 Subject: [PATCH 47/70] Setup API and prepare for API keys This commit sets up the API and gRPC endpoints and adds authentication to them. Currently there is no actual authentication implemented but it has been prepared for API keys. In addition, there is a allow put in place for gRPC traffic over localhost. This has two purposes: 1. grpc-gateway, which is the base of the API, connects to the gRPC service over localhost. 2. We do not want to break current "on server" behaviour which allows users to use the cli on the server without any fuzz --- app.go | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 163 insertions(+), 12 deletions(-) diff --git a/app.go b/app.go index dbe30168..eedc8739 100644 --- a/app.go +++ b/app.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "errors" "fmt" + "io" "net" "net/http" "net/url" @@ -16,7 +17,7 @@ import ( "github.com/gin-gonic/gin" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" - apiV1 "github.com/juanfont/headscale/gen/go/v1" + apiV1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/rs/zerolog/log" "github.com/soheilhy/cmux" ginprometheus "github.com/zsais/go-gin-prometheus" @@ -24,6 +25,12 @@ import ( "golang.org/x/crypto/acme/autocert" "golang.org/x/sync/errgroup" "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/peer" + "google.golang.org/grpc/reflection" + "google.golang.org/grpc/status" "gorm.io/gorm" "inet.af/netaddr" "tailscale.com/tailcfg" @@ -31,6 +38,12 @@ import ( "tailscale.com/types/wgkey" ) +const ( + LOCALHOST_V4 = "127.0.0.1" + LOCALHOST_V6 = "[::1]" + AUTH_PREFIX = "Bearer " +) + // Config contains the initial Headscale configuration. type Config struct { ServerURL string @@ -208,6 +221,110 @@ func (h *Headscale) watchForKVUpdatesWorker() { // more functions will come here in the future } +func IsLocalhost(host string) bool { + if strings.Contains(host, LOCALHOST_V4) || strings.Contains(host, LOCALHOST_V6) { + return true + } + + return false +} + +func (h *Headscale) grpcAuthenticationInterceptor(ctx context.Context, + req interface{}, + info *grpc.UnaryServerInfo, + handler grpc.UnaryHandler) (interface{}, error) { + + // Check if the request is coming from the on-server client. + // This is not secure, but it is to maintain maintainability + // with the "legacy" database-based client + // It is also neede for grpc-gateway to be able to connect to + // the server + p, _ := peer.FromContext(ctx) + + if IsLocalhost(p.Addr.String()) { + log.Trace().Caller().Str("client_address", p.Addr.String()).Msg("Client connected from localhost") + + return handler(ctx, req) + } + + log.Trace().Caller().Str("client_address", p.Addr.String()).Msg("Client is trying to authenticate") + + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + log.Error().Caller().Str("client_address", p.Addr.String()).Msg("Retrieving metadata is failed") + return ctx, status.Errorf(codes.InvalidArgument, "Retrieving metadata is failed") + } + + authHeader, ok := md["authorization"] + if !ok { + log.Error().Caller().Str("client_address", p.Addr.String()).Msg("Authorization token is not supplied") + return ctx, status.Errorf(codes.Unauthenticated, "Authorization token is not supplied") + } + + token := authHeader[0] + + if !strings.HasPrefix(token, AUTH_PREFIX) { + log.Error(). + Caller(). + Str("client_address", p.Addr.String()). + Msg(`missing "Bearer " prefix in "Authorization" header`) + return ctx, status.Error(codes.Unauthenticated, `missing "Bearer " prefix in "Authorization" header`) + } + + // TODO(kradalby): Implement API key backend: + // - Table in the DB + // - Key name + // - Encrypted + // - Expiry + // + // Currently all other than localhost traffic is unauthorized, this is intentional to allow + // us to make use of gRPC for our CLI, but not having to implement any of the remote capabilities + // and API key auth + return ctx, status.Error(codes.Unauthenticated, "Authentication is not implemented yet") + + //if strings.TrimPrefix(token, AUTH_PREFIX) != a.Token { + // log.Error().Caller().Str("client_address", p.Addr.String()).Msg("invalid token") + // return ctx, status.Error(codes.Unauthenticated, "invalid token") + //} + + // return handler(ctx, req) +} + +func (h *Headscale) httpAuthenticationMiddleware(c *gin.Context) { + log.Trace(). + Caller(). + Str("client_address", c.ClientIP()). + Msg("HTTP authentication invoked") + + authHeader := c.GetHeader("authorization") + + if !strings.HasPrefix(authHeader, AUTH_PREFIX) { + log.Error(). + Caller(). + Str("client_address", c.ClientIP()). + Msg(`missing "Bearer " prefix in "Authorization" header`) + c.AbortWithStatus(http.StatusUnauthorized) + + return + } + + c.AbortWithStatus(http.StatusUnauthorized) + + // TODO(kradalby): Implement API key backend + // Currently all traffic is unauthorized, this is intentional to allow + // us to make use of gRPC for our CLI, but not having to implement any of the remote capabilities + // and API key auth + // + // if strings.TrimPrefix(authHeader, AUTH_PREFIX) != a.Token { + // log.Error().Caller().Str("client_address", c.ClientIP()).Msg("invalid token") + // c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error", "unauthorized"}) + + // return + // } + + // c.Next() +} + // Serve launches a GIN server with the Headscale API. func (h *Headscale) Serve() error { var err error @@ -226,21 +343,25 @@ func (h *Headscale) Serve() error { // The two following listeners will be served on the same port below gracefully. m := cmux.New(l) // Match gRPC requests here - grpcListener := m.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc")) + grpcListener := m.MatchWithWriters( + cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"), + cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc+proto"), + ) // Otherwise match regular http requests. httpListener := m.Match(cmux.Any()) - // Now create the grpc server with those options. - grpcServer := grpc.NewServer() - - // TODO(kradalby): register the new server when we have authentication ready - // apiV1.RegisterHeadscaleServiceServer(grpcServer, newHeadscaleV1APIServer(h)) - grpcGatewayMux := runtime.NewServeMux() - opts := []grpc.DialOption{grpc.WithInsecure()} + grpcDialOptions := []grpc.DialOption{grpc.WithInsecure()} - err = apiV1.RegisterHeadscaleServiceHandlerFromEndpoint(ctx, grpcGatewayMux, h.cfg.Addr, opts) + _, port, err := net.SplitHostPort(h.cfg.Addr) + if err != nil { + return err + } + + // Connect to the gRPC server over localhost to skip + // the authentication. + err = apiV1.RegisterHeadscaleServiceHandlerFromEndpoint(ctx, grpcGatewayMux, LOCALHOST_V4+":"+port, grpcDialOptions) if err != nil { return err } @@ -258,10 +379,15 @@ func (h *Headscale) Serve() error { r.GET("/apple", h.AppleMobileConfig) r.GET("/apple/:platform", h.ApplePlatformConfig) - r.Any("/api/v1/*any", gin.WrapF(grpcGatewayMux.ServeHTTP)) r.StaticFile("/swagger/swagger.json", "gen/openapiv2/v1/headscale.swagger.json") - updateMillisecondsWait := int64(5000) + api := r.Group("/api") + api.Use(h.httpAuthenticationMiddleware) + { + api.Any("/v1/*any", gin.WrapF(grpcGatewayMux.ServeHTTP)) + } + + r.NoRoute(stdoutHandler) // Fetch an initial DERP Map before we start serving h.DERPMap = GetDERPMap(h.cfg.DERP) @@ -273,6 +399,7 @@ func (h *Headscale) Serve() error { } // I HATE THIS + updateMillisecondsWait := int64(5000) go h.watchForKVUpdates(updateMillisecondsWait) go h.expireEphemeralNodes(updateMillisecondsWait) @@ -287,6 +414,12 @@ func (h *Headscale) Serve() error { WriteTimeout: 0, } + grpcOptions := []grpc.ServerOption{ + grpc.UnaryInterceptor( + h.grpcAuthenticationInterceptor, + ), + } + tlsConfig, err := h.getTLSSettings() if err != nil { log.Error().Err(err).Msg("Failed to set up TLS configuration") @@ -296,8 +429,15 @@ func (h *Headscale) Serve() error { if tlsConfig != nil { httpServer.TLSConfig = tlsConfig + + grpcOptions = append(grpcOptions, grpc.Creds(credentials.NewTLS(tlsConfig))) } + grpcServer := grpc.NewServer(grpcOptions...) + + apiV1.RegisterHeadscaleServiceServer(grpcServer, newHeadscaleV1APIServer(h)) + reflection.Register(grpcServer) + g := new(errgroup.Group) g.Go(func() error { return grpcServer.Serve(grpcListener) }) @@ -394,3 +534,14 @@ func (h *Headscale) getLastStateChange(namespaces ...string) time.Time { return times[0] } } + +func stdoutHandler(c *gin.Context) { + b, _ := io.ReadAll(c.Request.Body) + + log.Trace(). + Interface("header", c.Request.Header). + Interface("proto", c.Request.Proto). + Interface("url", c.Request.URL). + Bytes("body", b). + Msg("Request did not match") +} From 6d10be8fff95989dc52ec53bb478a0fbb51a1ad1 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 29 Oct 2021 16:49:44 +0000 Subject: [PATCH 48/70] Change order of print/nil check in integration test --- integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration_test.go b/integration_test.go index 53092423..56088765 100644 --- a/integration_test.go +++ b/integration_test.go @@ -282,8 +282,8 @@ func (s *IntegrationTestSuite) SetupSuite() { []string{"headscale", "namespaces", "create", namespace}, []string{}, ) - assert.Nil(s.T(), err) fmt.Println("headscale create namespace result: ", result) + assert.Nil(s.T(), err) fmt.Printf("Creating pre auth key for %s\n", namespace) authKey, err := executeCommand( From 68dab0fe7b68e0abf8a3f78d42194c4f8b88de67 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 29 Oct 2021 17:04:58 +0000 Subject: [PATCH 49/70] Move localhost check to utils --- app.go | 8 -------- utils.go | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app.go b/app.go index eedc8739..c77a85a5 100644 --- a/app.go +++ b/app.go @@ -221,14 +221,6 @@ func (h *Headscale) watchForKVUpdatesWorker() { // more functions will come here in the future } -func IsLocalhost(host string) bool { - if strings.Contains(host, LOCALHOST_V4) || strings.Contains(host, LOCALHOST_V6) { - return true - } - - return false -} - func (h *Headscale) grpcAuthenticationInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, diff --git a/utils.go b/utils.go index cbe1d870..1aa31712 100644 --- a/utils.go +++ b/utils.go @@ -156,3 +156,11 @@ func tailNodesToString(nodes []*tailcfg.Node) string { func tailMapResponseToString(resp tailcfg.MapResponse) string { return fmt.Sprintf("{ Node: %s, Peers: %s }", resp.Node.Name, tailNodesToString(resp.Peers)) } + +func IsLocalhost(host string) bool { + if strings.Contains(host, LOCALHOST_V4) || strings.Contains(host, LOCALHOST_V6) { + return true + } + + return false +} From 002b5c1dad9ba24baa4b54edab586c5f49136152 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 29 Oct 2021 17:08:21 +0000 Subject: [PATCH 50/70] Add grpc token auth struct --- cmd/headscale/cli/utils.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 0768e1eb..30d5d83a 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -312,3 +312,18 @@ func HasJsonOutputFlag() bool { } return false } + +type tokenAuth struct { + token string +} + +// Return value is mapped to request headers. +func (t tokenAuth) GetRequestMetadata(ctx context.Context, in ...string) (map[string]string, error) { + return map[string]string{ + "authorization": "Bearer " + t.token, + }, nil +} + +func (tokenAuth) RequireTransportSecurity() bool { + return true +} From eefd82a574b475c654e2a0a467fc2e828938f8cc Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 29 Oct 2021 17:09:06 +0000 Subject: [PATCH 51/70] Move config loading out of the headscale app setup --- cmd/headscale/cli/utils.go | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 30d5d83a..f2053c93 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -1,18 +1,23 @@ package cli import ( + "context" "encoding/json" "errors" "fmt" + "net" "net/url" "os" "path/filepath" + "strconv" "strings" "time" "github.com/juanfont/headscale" + apiV1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/rs/zerolog/log" "github.com/spf13/viper" + "google.golang.org/grpc" "inet.af/netaddr" "tailscale.com/tailcfg" "tailscale.com/types/dnstype" @@ -202,23 +207,11 @@ func absPath(path string) string { return path } -func getHeadscaleApp() (*headscale.Headscale, error) { - // Minimum inactivity time out is keepalive timeout (60s) plus a few seconds - // to avoid races - minInactivityTimeout, _ := time.ParseDuration("65s") - if viper.GetDuration("ephemeral_node_inactivity_timeout") <= minInactivityTimeout { - err := fmt.Errorf( - "ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s\n", - viper.GetString("ephemeral_node_inactivity_timeout"), - minInactivityTimeout, - ) - return nil, err - } - +func getHeadscaleConfig() headscale.Config { dnsConfig, baseDomain := GetDNSConfig() derpConfig := GetDERPConfig() - cfg := headscale.Config{ + return headscale.Config{ ServerURL: viper.GetString("server_url"), Addr: viper.GetString("listen_addr"), PrivateKeyPath: absPath(viper.GetString("private_key_path")), @@ -250,6 +243,22 @@ func getHeadscaleApp() (*headscale.Headscale, error) { ACMEEmail: viper.GetString("acme_email"), ACMEURL: viper.GetString("acme_url"), } +} + +func getHeadscaleApp() (*headscale.Headscale, error) { + // Minimum inactivity time out is keepalive timeout (60s) plus a few seconds + // to avoid races + minInactivityTimeout, _ := time.ParseDuration("65s") + if viper.GetDuration("ephemeral_node_inactivity_timeout") <= minInactivityTimeout { + err := fmt.Errorf( + "ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s\n", + viper.GetString("ephemeral_node_inactivity_timeout"), + minInactivityTimeout, + ) + return nil, err + } + + cfg := getHeadscaleConfig() h, err := headscale.NewHeadscale(cfg) if err != nil { From 81b8610dff579b1516f6fe579eff7e3811a9f332 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 29 Oct 2021 17:15:52 +0000 Subject: [PATCH 52/70] Add helper function to setup grpc client for cli --- cmd/headscale/cli/utils.go | 58 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index f2053c93..829f4c68 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -281,6 +281,64 @@ func getHeadscaleApp() (*headscale.Headscale, error) { return h, nil } +func getHeadscaleGRPCClient() (apiV1.HeadscaleServiceClient, *grpc.ClientConn) { + grpcOptions := []grpc.DialOption{ + // TODO(kradalby): Make configurable + grpc.WithTimeout(5 * time.Second), + grpc.WithBlock(), + } + + address := os.Getenv("HEADSCALE_ADDRESS") + + // If the address is not set, we assume that we are on the server hosting headscale. + if address == "" { + log.Debug().Msgf("HEADSCALE_ADDRESS environment is not set, connecting to localhost.") + + cfg := getHeadscaleConfig() + + _, port, _ := net.SplitHostPort(cfg.Addr) + + address = "127.0.0.1" + ":" + port + + grpcOptions = append(grpcOptions, grpc.WithInsecure()) + } + + // If we are not connecting to a local server, require an API key for authentication + if !headscale.IsLocalhost(address) { + apiKey := os.Getenv("HEADSCALE_API_KEY") + if apiKey == "" { + log.Fatal().Msgf("HEADSCALE_API_KEY environment variable needs to be set.") + } + grpcOptions = append(grpcOptions, + grpc.WithPerRPCCredentials(tokenAuth{ + token: apiKey, + }), + ) + + insecureStr := os.Getenv("HEADSCALE_INSECURE") + if insecureStr != "" { + insecure, err := strconv.ParseBool(insecureStr) + if err != nil { + log.Fatal().Err(err).Msgf("Failed to parse HEADSCALE_INSECURE: %v", err) + } + + if insecure { + grpcOptions = append(grpcOptions, grpc.WithInsecure()) + } + } + } + + log.Trace().Caller().Str("address", address).Msg("Connecting via gRPC") + conn, err := grpc.Dial(address, grpcOptions...) + if err != nil { + log.Fatal().Err(err).Msgf("Could not connect: %v", err) + } + + client := apiV1.NewHeadscaleServiceClient(conn) + + return client, conn +} + func JsonOutput(result interface{}, errResult error, outputFormat string) { var j []byte var err error From 9ef031f0f841cb9fb93d7350857f9b7882f2b7f1 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 29 Oct 2021 17:16:54 +0000 Subject: [PATCH 53/70] Port create, delete and list of namespace to grpc --- cmd/headscale/cli/namespaces.go | 38 ++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/cmd/headscale/cli/namespaces.go b/cmd/headscale/cli/namespaces.go index 42870370..044347fe 100644 --- a/cmd/headscale/cli/namespaces.go +++ b/cmd/headscale/cli/namespaces.go @@ -1,12 +1,15 @@ package cli import ( + "context" "fmt" - "log" "strconv" "strings" + "time" + apiV1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/pterm/pterm" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) @@ -34,13 +37,21 @@ var createNamespaceCmd = &cobra.Command{ }, Run: func(cmd *cobra.Command, args []string) { o, _ := cmd.Flags().GetString("output") - h, err := getHeadscaleApp() - if err != nil { - log.Fatalf("Error initializing: %s", err) - } - namespace, err := h.CreateNamespace(args[0]) + + client, conn := getHeadscaleGRPCClient() + defer conn.Close() + + log.Trace().Interface("client", client).Msg("Obtained gRPC client") + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + request := &apiV1.CreateNamespaceRequest{Name: args[0]} + + log.Trace().Interface("request", request).Msg("Sending CreateNamespace request") + response, err := client.CreateNamespace(ctx, request) if strings.HasPrefix(o, "json") { - JsonOutput(namespace, err, o) + JsonOutput(response.Name, err, o) return } if err != nil { @@ -64,7 +75,7 @@ var destroyNamespaceCmd = &cobra.Command{ o, _ := cmd.Flags().GetString("output") h, err := getHeadscaleApp() if err != nil { - log.Fatalf("Error initializing: %s", err) + log.Fatal().Err(err).Msgf("Error initializing: %s", err) } err = h.DestroyNamespace(args[0]) if strings.HasPrefix(o, "json") { @@ -86,7 +97,7 @@ var listNamespacesCmd = &cobra.Command{ o, _ := cmd.Flags().GetString("output") h, err := getHeadscaleApp() if err != nil { - log.Fatalf("Error initializing: %s", err) + log.Fatal().Err(err).Msgf("Error initializing: %s", err) } namespaces, err := h.ListNamespaces() if strings.HasPrefix(o, "json") { @@ -100,11 +111,14 @@ var listNamespacesCmd = &cobra.Command{ d := pterm.TableData{{"ID", "Name", "Created"}} for _, n := range *namespaces { - d = append(d, []string{strconv.FormatUint(uint64(n.ID), 10), n.Name, n.CreatedAt.Format("2006-01-02 15:04:05")}) + d = append( + d, + []string{strconv.FormatUint(uint64(n.ID), 10), n.Name, n.CreatedAt.Format("2006-01-02 15:04:05")}, + ) } err = pterm.DefaultTable.WithHasHeader().WithData(d).Render() if err != nil { - log.Fatal(err) + log.Fatal().Err(err).Msg("") } }, } @@ -122,7 +136,7 @@ var renameNamespaceCmd = &cobra.Command{ o, _ := cmd.Flags().GetString("output") h, err := getHeadscaleApp() if err != nil { - log.Fatalf("Error initializing: %s", err) + log.Fatal().Err(err).Msgf("Error initializing: %s", err) } err = h.RenameNamespace(args[0], args[1]) if strings.HasPrefix(o, "json") { From 72fd2a2780388496ded857b566797855735c162e Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Fri, 29 Oct 2021 17:36:11 +0000 Subject: [PATCH 54/70] Fix lint error --- cmd/headscale/cli/utils.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 829f4c68..14ff2bf2 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -282,9 +282,11 @@ func getHeadscaleApp() (*headscale.Headscale, error) { } func getHeadscaleGRPCClient() (apiV1.HeadscaleServiceClient, *grpc.ClientConn) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + grpcOptions := []grpc.DialOption{ // TODO(kradalby): Make configurable - grpc.WithTimeout(5 * time.Second), grpc.WithBlock(), } @@ -329,7 +331,7 @@ func getHeadscaleGRPCClient() (apiV1.HeadscaleServiceClient, *grpc.ClientConn) { } log.Trace().Caller().Str("address", address).Msg("Connecting via gRPC") - conn, err := grpc.Dial(address, grpcOptions...) + conn, err := grpc.DialContext(ctx, address, grpcOptions...) if err != nil { log.Fatal().Err(err).Msgf("Could not connect: %v", err) } From 6aacada85231b5fa6a793018a150aac22611ff66 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sat, 30 Oct 2021 14:08:16 +0000 Subject: [PATCH 55/70] Switch from gRPC localhost to socket This commit changes the way CLI and grpc-gateway communicates with the gRPC backend to socket, instead of localhost. Unauthenticated access now goes on the socket, while the network interface will require API key (in the future). --- .dockerignore | 2 + app.go | 36 ++++++++---- cmd/headscale/cli/utils.go | 27 ++++++--- config-example.yaml | 5 ++ proto/headscale/v1/rpc.proto | 106 ----------------------------------- utils.go | 8 --- 6 files changed, 50 insertions(+), 134 deletions(-) delete mode 100644 proto/headscale/v1/rpc.proto diff --git a/.dockerignore b/.dockerignore index 33f9aea2..057a20e7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -14,3 +14,5 @@ docker-compose* README.md LICENSE .vscode + +*.sock diff --git a/app.go b/app.go index c77a85a5..94517d54 100644 --- a/app.go +++ b/app.go @@ -39,9 +39,7 @@ import ( ) const ( - LOCALHOST_V4 = "127.0.0.1" - LOCALHOST_V6 = "[::1]" - AUTH_PREFIX = "Bearer " + AUTH_PREFIX = "Bearer " ) // Config contains the initial Headscale configuration. @@ -75,6 +73,8 @@ type Config struct { ACMEEmail string DNSConfig *tailcfg.DNSConfig + + UnixSocket string } type DERPConfig struct { @@ -233,8 +233,9 @@ func (h *Headscale) grpcAuthenticationInterceptor(ctx context.Context, // the server p, _ := peer.FromContext(ctx) - if IsLocalhost(p.Addr.String()) { - log.Trace().Caller().Str("client_address", p.Addr.String()).Msg("Client connected from localhost") + // TODO(kradalby): Figure out what @ means (socket wise) and if it can be exploited + if p.Addr.String() == "@" { + log.Trace().Caller().Str("client_address", p.Addr.String()).Msg("Client connecting over socket") return handler(ctx, req) } @@ -326,14 +327,19 @@ func (h *Headscale) Serve() error { defer cancel() - l, err := net.Listen("tcp", h.cfg.Addr) + socketListener, err := net.Listen("unix", h.cfg.UnixSocket) + if err != nil { + panic(err) + } + + networkListener, err := net.Listen("tcp", h.cfg.Addr) if err != nil { panic(err) } // Create the cmux object that will multiplex 2 protocols on the same port. // The two following listeners will be served on the same port below gracefully. - m := cmux.New(l) + m := cmux.New(networkListener) // Match gRPC requests here grpcListener := m.MatchWithWriters( cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"), @@ -344,16 +350,23 @@ func (h *Headscale) Serve() error { grpcGatewayMux := runtime.NewServeMux() - grpcDialOptions := []grpc.DialOption{grpc.WithInsecure()} - - _, port, err := net.SplitHostPort(h.cfg.Addr) + // Make the grpc-gateway connect to grpc over socket + grpcGatewayConn, err := grpc.Dial( + h.cfg.UnixSocket, + []grpc.DialOption{ + grpc.WithInsecure(), + grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout("unix", addr, timeout) + }), + }..., + ) if err != nil { return err } // Connect to the gRPC server over localhost to skip // the authentication. - err = apiV1.RegisterHeadscaleServiceHandlerFromEndpoint(ctx, grpcGatewayMux, LOCALHOST_V4+":"+port, grpcDialOptions) + err = apiV1.RegisterHeadscaleServiceHandler(ctx, grpcGatewayMux, grpcGatewayConn) if err != nil { return err } @@ -432,6 +445,7 @@ func (h *Headscale) Serve() error { g := new(errgroup.Group) + g.Go(func() error { return grpcServer.Serve(socketListener) }) g.Go(func() error { return grpcServer.Serve(grpcListener) }) g.Go(func() error { return httpServer.Serve(httpListener) }) g.Go(func() error { return m.Serve() }) diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 14ff2bf2..1d6fdd61 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -48,6 +48,8 @@ func LoadConfig(path string) error { viper.SetDefault("dns_config", nil) + viper.SetDefault("unix_socket", "/var/run/headscale.sock") + err := viper.ReadInConfig() if err != nil { return fmt.Errorf("Fatal error reading config file: %s \n", err) @@ -242,6 +244,8 @@ func getHeadscaleConfig() headscale.Config { ACMEEmail: viper.GetString("acme_email"), ACMEURL: viper.GetString("acme_url"), + + UnixSocket: viper.GetString("unix_socket"), } } @@ -282,11 +286,11 @@ func getHeadscaleApp() (*headscale.Headscale, error) { } func getHeadscaleGRPCClient() (apiV1.HeadscaleServiceClient, *grpc.ClientConn) { + // TODO(kradalby): Make configurable ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() grpcOptions := []grpc.DialOption{ - // TODO(kradalby): Make configurable grpc.WithBlock(), } @@ -294,19 +298,24 @@ func getHeadscaleGRPCClient() (apiV1.HeadscaleServiceClient, *grpc.ClientConn) { // If the address is not set, we assume that we are on the server hosting headscale. if address == "" { - log.Debug().Msgf("HEADSCALE_ADDRESS environment is not set, connecting to localhost.") cfg := getHeadscaleConfig() - _, port, _ := net.SplitHostPort(cfg.Addr) + log.Debug(). + Str("socket", cfg.UnixSocket). + Msgf("HEADSCALE_ADDRESS environment is not set, connecting to unix socket.") - address = "127.0.0.1" + ":" + port + address = cfg.UnixSocket - grpcOptions = append(grpcOptions, grpc.WithInsecure()) - } - - // If we are not connecting to a local server, require an API key for authentication - if !headscale.IsLocalhost(address) { + grpcOptions = append( + grpcOptions, + grpc.WithInsecure(), + grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout("unix", addr, timeout) + }), + ) + } else { + // If we are not connecting to a local server, require an API key for authentication apiKey := os.Getenv("HEADSCALE_API_KEY") if apiKey == "" { log.Fatal().Msgf("HEADSCALE_API_KEY environment variable needs to be set.") diff --git a/config-example.yaml b/config-example.yaml index 59370eb5..0eaf4c2d 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -64,3 +64,8 @@ dns_config: magic_dns: true base_domain: example.com + +# Unix socket used for the CLI to connect without authentication +# Note: for local development, you probably want to change this to: +# unix_socket: ./headscale.sock +unix_socket: /var/run/headscale.sock diff --git a/proto/headscale/v1/rpc.proto b/proto/headscale/v1/rpc.proto deleted file mode 100644 index 2e6b0a67..00000000 --- a/proto/headscale/v1/rpc.proto +++ /dev/null @@ -1,106 +0,0 @@ -syntax = "proto3"; -package headscale.v1; -option go_package = "github.com/juanfont/headscale/gen/go/v1"; - -import "google/protobuf/timestamp.proto"; -import "google/api/annotations.proto"; - -enum RegisterMethod { - REGISTER_METHOD_UNSPECIFIED = 0; - REGISTER_METHOD_AUTH_KEY = 1; - REGISTER_METHOD_CLI = 2; - REGISTER_METHOD_OIDC = 3; -} - -// message PreAuthKey { -// uint64 id = 1; -// string key = 2; -// uint32 namespace_id = 3; -// Namespace namespace = 4; -// bool reusable = 5; -// bool ephemeral = 6; -// bool used = 7; -// -// google.protobuf.Timestamp created_at = 8; -// google.protobuf.Timestamp expiration = 9; -// } - -message GetMachineRequest { - uint64 machine_id = 1; -} - -message GetMachineResponse { - uint64 id = 1; - string machine_key = 2; - string node_key = 3; - string disco_key = 4; - string ip_address = 5; - string name = 6; - uint32 namespace_id = 7; - - bool registered = 8; - RegisterMethod register_method = 9; - uint32 auth_key_id = 10; - // PreAuthKey auth_key = 11; - - google.protobuf.Timestamp last_seen = 12; - google.protobuf.Timestamp last_successful_update = 13; - google.protobuf.Timestamp expiry = 14; - - // bytes host_info = 15; - // bytes endpoints = 16; - // bytes enabled_routes = 17; - - // google.protobuf.Timestamp created_at = 18; - // google.protobuf.Timestamp updated_at = 19; - // google.protobuf.Timestamp deleted_at = 20; -} - -message CreateNamespaceRequest { - string name = 1; -} - -message CreateNamespaceResponse { - string name = 1; -} - -message DeleteNamespaceRequest { - string name = 1; -} - -message DeleteNamespaceResponse { -} - -message ListNamespacesRequest { -} - -message ListNamespacesResponse { - repeated string namespaces = 1; -} - -service HeadscaleService { - rpc GetMachine(GetMachineRequest) returns(GetMachineResponse) { - option(google.api.http) = { - get : "/api/v1/machine/{machine_id}" - }; - } - - rpc CreateNamespace(CreateNamespaceRequest) returns(CreateNamespaceResponse) { - option(google.api.http) = { - post : "/api/v1/namespace" - body : "*" - }; - } - - rpc DeleteNamespace(DeleteNamespaceRequest) returns(DeleteNamespaceResponse) { - option(google.api.http) = { - delete : "/api/v1/namespace" - }; - } - - rpc ListNamespaces(ListNamespacesRequest) returns(ListNamespacesResponse) { - option(google.api.http) = { - get : "/api/v1/namespace" - }; - } -} diff --git a/utils.go b/utils.go index 1aa31712..cbe1d870 100644 --- a/utils.go +++ b/utils.go @@ -156,11 +156,3 @@ func tailNodesToString(nodes []*tailcfg.Node) string { func tailMapResponseToString(resp tailcfg.MapResponse) string { return fmt.Sprintf("{ Node: %s, Peers: %s }", resp.Node.Name, tailNodesToString(resp.Peers)) } - -func IsLocalhost(host string) bool { - if strings.Contains(host, LOCALHOST_V4) || strings.Contains(host, LOCALHOST_V6) { - return true - } - - return false -} From 434fac52b7f11aeb26094c25d0c0d3c859f77b3a Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sat, 30 Oct 2021 14:29:03 +0000 Subject: [PATCH 56/70] Fix lint error --- app.go | 4 +--- cmd/headscale/cli/utils.go | 5 +---- utils.go | 7 +++++++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app.go b/app.go index 94517d54..707c111e 100644 --- a/app.go +++ b/app.go @@ -355,9 +355,7 @@ func (h *Headscale) Serve() error { h.cfg.UnixSocket, []grpc.DialOption{ grpc.WithInsecure(), - grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { - return net.DialTimeout("unix", addr, timeout) - }), + grpc.WithContextDialer(GrpcSocketDialer), }..., ) if err != nil { diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 1d6fdd61..8e044bf5 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "net" "net/url" "os" "path/filepath" @@ -310,9 +309,7 @@ func getHeadscaleGRPCClient() (apiV1.HeadscaleServiceClient, *grpc.ClientConn) { grpcOptions = append( grpcOptions, grpc.WithInsecure(), - grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { - return net.DialTimeout("unix", addr, timeout) - }), + grpc.WithContextDialer(headscale.GrpcSocketDialer), ) } else { // If we are not connecting to a local server, require an API key for authentication diff --git a/utils.go b/utils.go index cbe1d870..555227ec 100644 --- a/utils.go +++ b/utils.go @@ -6,10 +6,12 @@ package headscale import ( + "context" "crypto/rand" "encoding/json" "fmt" "io" + "net" "strings" "golang.org/x/crypto/nacl/box" @@ -156,3 +158,8 @@ func tailNodesToString(nodes []*tailcfg.Node) string { func tailMapResponseToString(resp tailcfg.MapResponse) string { return fmt.Sprintf("{ Node: %s, Peers: %s }", resp.Node.Name, tailNodesToString(resp.Peers)) } + +func GrpcSocketDialer(ctx context.Context, addr string) (net.Conn, error) { + var d net.Dialer + return d.DialContext(ctx, "unix", addr) +} From 2b340e8fa4710a22b108660f906a3318dd86eba5 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sat, 30 Oct 2021 14:29:41 +0000 Subject: [PATCH 57/70] Rename protofile --- .../v1/{rpc.pb.go => headscale.pb.go} | 320 +++++++++--------- .../v1/{rpc.pb.gw.go => headscale.pb.gw.go} | 2 +- .../{rpc_grpc.pb.go => headscale_grpc.pb.go} | 2 +- ...pc.swagger.json => headscale.swagger.json} | 2 +- proto/headscale/v1/headscale.proto | 106 ++++++ 5 files changed, 269 insertions(+), 163 deletions(-) rename gen/go/headscale/v1/{rpc.pb.go => headscale.pb.go} (58%) rename gen/go/headscale/v1/{rpc.pb.gw.go => headscale.pb.gw.go} (99%) rename gen/go/headscale/v1/{rpc_grpc.pb.go => headscale_grpc.pb.go} (99%) rename gen/openapiv2/headscale/v1/{rpc.swagger.json => headscale.swagger.json} (99%) create mode 100644 proto/headscale/v1/headscale.proto diff --git a/gen/go/headscale/v1/rpc.pb.go b/gen/go/headscale/v1/headscale.pb.go similarity index 58% rename from gen/go/headscale/v1/rpc.pb.go rename to gen/go/headscale/v1/headscale.pb.go index b510b961..e050e931 100644 --- a/gen/go/headscale/v1/rpc.pb.go +++ b/gen/go/headscale/v1/headscale.pb.go @@ -2,7 +2,7 @@ // versions: // protoc-gen-go v1.27.1 // protoc v3.18.1 -// source: headscale/v1/rpc.proto +// source: headscale/v1/headscale.proto package v1 @@ -58,11 +58,11 @@ func (x RegisterMethod) String() string { } func (RegisterMethod) Descriptor() protoreflect.EnumDescriptor { - return file_headscale_v1_rpc_proto_enumTypes[0].Descriptor() + return file_headscale_v1_headscale_proto_enumTypes[0].Descriptor() } func (RegisterMethod) Type() protoreflect.EnumType { - return &file_headscale_v1_rpc_proto_enumTypes[0] + return &file_headscale_v1_headscale_proto_enumTypes[0] } func (x RegisterMethod) Number() protoreflect.EnumNumber { @@ -71,7 +71,7 @@ func (x RegisterMethod) Number() protoreflect.EnumNumber { // Deprecated: Use RegisterMethod.Descriptor instead. func (RegisterMethod) EnumDescriptor() ([]byte, []int) { - return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{0} + return file_headscale_v1_headscale_proto_rawDescGZIP(), []int{0} } type GetMachineRequest struct { @@ -85,7 +85,7 @@ type GetMachineRequest struct { func (x *GetMachineRequest) Reset() { *x = GetMachineRequest{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_rpc_proto_msgTypes[0] + mi := &file_headscale_v1_headscale_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -98,7 +98,7 @@ func (x *GetMachineRequest) String() string { func (*GetMachineRequest) ProtoMessage() {} func (x *GetMachineRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_rpc_proto_msgTypes[0] + mi := &file_headscale_v1_headscale_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -111,7 +111,7 @@ func (x *GetMachineRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMachineRequest.ProtoReflect.Descriptor instead. func (*GetMachineRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{0} + return file_headscale_v1_headscale_proto_rawDescGZIP(), []int{0} } func (x *GetMachineRequest) GetMachineId() uint64 { @@ -144,7 +144,7 @@ type GetMachineResponse struct { func (x *GetMachineResponse) Reset() { *x = GetMachineResponse{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_rpc_proto_msgTypes[1] + mi := &file_headscale_v1_headscale_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -157,7 +157,7 @@ func (x *GetMachineResponse) String() string { func (*GetMachineResponse) ProtoMessage() {} func (x *GetMachineResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_rpc_proto_msgTypes[1] + mi := &file_headscale_v1_headscale_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -170,7 +170,7 @@ func (x *GetMachineResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMachineResponse.ProtoReflect.Descriptor instead. func (*GetMachineResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{1} + return file_headscale_v1_headscale_proto_rawDescGZIP(), []int{1} } func (x *GetMachineResponse) GetId() uint64 { @@ -275,7 +275,7 @@ type CreateNamespaceRequest struct { func (x *CreateNamespaceRequest) Reset() { *x = CreateNamespaceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_rpc_proto_msgTypes[2] + mi := &file_headscale_v1_headscale_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -288,7 +288,7 @@ func (x *CreateNamespaceRequest) String() string { func (*CreateNamespaceRequest) ProtoMessage() {} func (x *CreateNamespaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_rpc_proto_msgTypes[2] + mi := &file_headscale_v1_headscale_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -301,7 +301,7 @@ func (x *CreateNamespaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateNamespaceRequest.ProtoReflect.Descriptor instead. func (*CreateNamespaceRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{2} + return file_headscale_v1_headscale_proto_rawDescGZIP(), []int{2} } func (x *CreateNamespaceRequest) GetName() string { @@ -322,7 +322,7 @@ type CreateNamespaceResponse struct { func (x *CreateNamespaceResponse) Reset() { *x = CreateNamespaceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_rpc_proto_msgTypes[3] + mi := &file_headscale_v1_headscale_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -335,7 +335,7 @@ func (x *CreateNamespaceResponse) String() string { func (*CreateNamespaceResponse) ProtoMessage() {} func (x *CreateNamespaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_rpc_proto_msgTypes[3] + mi := &file_headscale_v1_headscale_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -348,7 +348,7 @@ func (x *CreateNamespaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateNamespaceResponse.ProtoReflect.Descriptor instead. func (*CreateNamespaceResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{3} + return file_headscale_v1_headscale_proto_rawDescGZIP(), []int{3} } func (x *CreateNamespaceResponse) GetName() string { @@ -369,7 +369,7 @@ type DeleteNamespaceRequest struct { func (x *DeleteNamespaceRequest) Reset() { *x = DeleteNamespaceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_rpc_proto_msgTypes[4] + mi := &file_headscale_v1_headscale_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -382,7 +382,7 @@ func (x *DeleteNamespaceRequest) String() string { func (*DeleteNamespaceRequest) ProtoMessage() {} func (x *DeleteNamespaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_rpc_proto_msgTypes[4] + mi := &file_headscale_v1_headscale_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -395,7 +395,7 @@ func (x *DeleteNamespaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteNamespaceRequest.ProtoReflect.Descriptor instead. func (*DeleteNamespaceRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{4} + return file_headscale_v1_headscale_proto_rawDescGZIP(), []int{4} } func (x *DeleteNamespaceRequest) GetName() string { @@ -414,7 +414,7 @@ type DeleteNamespaceResponse struct { func (x *DeleteNamespaceResponse) Reset() { *x = DeleteNamespaceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_rpc_proto_msgTypes[5] + mi := &file_headscale_v1_headscale_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -427,7 +427,7 @@ func (x *DeleteNamespaceResponse) String() string { func (*DeleteNamespaceResponse) ProtoMessage() {} func (x *DeleteNamespaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_rpc_proto_msgTypes[5] + mi := &file_headscale_v1_headscale_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -440,7 +440,7 @@ func (x *DeleteNamespaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteNamespaceResponse.ProtoReflect.Descriptor instead. func (*DeleteNamespaceResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{5} + return file_headscale_v1_headscale_proto_rawDescGZIP(), []int{5} } type ListNamespacesRequest struct { @@ -452,7 +452,7 @@ type ListNamespacesRequest struct { func (x *ListNamespacesRequest) Reset() { *x = ListNamespacesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_rpc_proto_msgTypes[6] + mi := &file_headscale_v1_headscale_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -465,7 +465,7 @@ func (x *ListNamespacesRequest) String() string { func (*ListNamespacesRequest) ProtoMessage() {} func (x *ListNamespacesRequest) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_rpc_proto_msgTypes[6] + mi := &file_headscale_v1_headscale_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -478,7 +478,7 @@ func (x *ListNamespacesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListNamespacesRequest.ProtoReflect.Descriptor instead. func (*ListNamespacesRequest) Descriptor() ([]byte, []int) { - return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{6} + return file_headscale_v1_headscale_proto_rawDescGZIP(), []int{6} } type ListNamespacesResponse struct { @@ -492,7 +492,7 @@ type ListNamespacesResponse struct { func (x *ListNamespacesResponse) Reset() { *x = ListNamespacesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_headscale_v1_rpc_proto_msgTypes[7] + mi := &file_headscale_v1_headscale_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -505,7 +505,7 @@ func (x *ListNamespacesResponse) String() string { func (*ListNamespacesResponse) ProtoMessage() {} func (x *ListNamespacesResponse) ProtoReflect() protoreflect.Message { - mi := &file_headscale_v1_rpc_proto_msgTypes[7] + mi := &file_headscale_v1_headscale_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -518,7 +518,7 @@ func (x *ListNamespacesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListNamespacesResponse.ProtoReflect.Descriptor instead. func (*ListNamespacesResponse) Descriptor() ([]byte, []int) { - return file_headscale_v1_rpc_proto_rawDescGZIP(), []int{7} + return file_headscale_v1_headscale_proto_rawDescGZIP(), []int{7} } func (x *ListNamespacesResponse) GetNamespaces() []string { @@ -528,129 +528,129 @@ func (x *ListNamespacesResponse) GetNamespaces() []string { return nil } -var File_headscale_v1_rpc_proto protoreflect.FileDescriptor +var File_headscale_v1_headscale_proto protoreflect.FileDescriptor -var file_headscale_v1_rpc_proto_rawDesc = []byte{ - 0x0a, 0x16, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x72, - 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x32, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, - 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, - 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, - 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x99, 0x04, 0x0a, 0x12, 0x47, 0x65, - 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x4b, 0x65, - 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, - 0x64, 0x69, 0x73, 0x63, 0x6f, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x70, 0x5f, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, - 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, - 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, - 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x68, - 0x6f, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, - 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, - 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x75, 0x74, - 0x68, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, - 0x65, 0x65, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, - 0x50, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, - 0x75, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, +var file_headscale_v1_headscale_proto_rawDesc = []byte{ + 0x0a, 0x1c, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x68, + 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x32, 0x0a, 0x11, 0x47, + 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, + 0x99, 0x04, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, + 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x63, + 0x68, 0x69, 0x6e, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x4b, + 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x4b, 0x65, 0x79, 0x12, + 0x1d, 0x0a, 0x0a, 0x69, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, + 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0e, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1e, 0x0a, 0x0b, + 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x09, + 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x14, 0x6c, 0x61, 0x73, - 0x74, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x12, 0x32, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x65, - 0x78, 0x70, 0x69, 0x72, 0x79, 0x22, 0x2c, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x22, 0x2d, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x22, 0x2c, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x22, 0x19, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x0a, 0x15, 0x4c, - 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x38, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, - 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2a, 0x82, - 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, - 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, - 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, - 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, - 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, - 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, - 0x48, 0x4f, 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x47, - 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x49, 0x44, - 0x43, 0x10, 0x03, 0x32, 0xfa, 0x03, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x75, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4d, - 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x1f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, - 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1e, 0x12, 0x1c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, - 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, - 0x7c, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, + 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x50, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x75, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, + 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x22, 0x2c, 0x0a, 0x16, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x2d, 0x0a, 0x17, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x2c, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x19, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x17, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x38, 0x0a, 0x16, 0x4c, 0x69, + 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x73, 0x2a, 0x82, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x47, 0x49, 0x53, + 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x47, 0x49, + 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x48, + 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, + 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x10, 0x02, 0x12, + 0x18, 0x0a, 0x14, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, + 0x4f, 0x44, 0x5f, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x03, 0x32, 0xfa, 0x03, 0x0a, 0x10, 0x48, 0x65, + 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x75, + 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x1f, 0x2e, 0x68, + 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, + 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, + 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, + 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, + 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x7c, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, - 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x79, 0x0a, - 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, - 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x2a, 0x11, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x76, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x68, 0x65, 0x61, - 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, + 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, - 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x3a, 0x01, 0x2a, 0x12, 0x79, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, + 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, + 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x2a, 0x11, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x76, + 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, + 0x12, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, + 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, + 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( - file_headscale_v1_rpc_proto_rawDescOnce sync.Once - file_headscale_v1_rpc_proto_rawDescData = file_headscale_v1_rpc_proto_rawDesc + file_headscale_v1_headscale_proto_rawDescOnce sync.Once + file_headscale_v1_headscale_proto_rawDescData = file_headscale_v1_headscale_proto_rawDesc ) -func file_headscale_v1_rpc_proto_rawDescGZIP() []byte { - file_headscale_v1_rpc_proto_rawDescOnce.Do(func() { - file_headscale_v1_rpc_proto_rawDescData = protoimpl.X.CompressGZIP(file_headscale_v1_rpc_proto_rawDescData) +func file_headscale_v1_headscale_proto_rawDescGZIP() []byte { + file_headscale_v1_headscale_proto_rawDescOnce.Do(func() { + file_headscale_v1_headscale_proto_rawDescData = protoimpl.X.CompressGZIP(file_headscale_v1_headscale_proto_rawDescData) }) - return file_headscale_v1_rpc_proto_rawDescData + return file_headscale_v1_headscale_proto_rawDescData } -var file_headscale_v1_rpc_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_headscale_v1_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 8) -var file_headscale_v1_rpc_proto_goTypes = []interface{}{ +var file_headscale_v1_headscale_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_headscale_v1_headscale_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_headscale_v1_headscale_proto_goTypes = []interface{}{ (RegisterMethod)(0), // 0: headscale.v1.RegisterMethod (*GetMachineRequest)(nil), // 1: headscale.v1.GetMachineRequest (*GetMachineResponse)(nil), // 2: headscale.v1.GetMachineResponse @@ -662,7 +662,7 @@ var file_headscale_v1_rpc_proto_goTypes = []interface{}{ (*ListNamespacesResponse)(nil), // 8: headscale.v1.ListNamespacesResponse (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp } -var file_headscale_v1_rpc_proto_depIdxs = []int32{ +var file_headscale_v1_headscale_proto_depIdxs = []int32{ 0, // 0: headscale.v1.GetMachineResponse.register_method:type_name -> headscale.v1.RegisterMethod 9, // 1: headscale.v1.GetMachineResponse.last_seen:type_name -> google.protobuf.Timestamp 9, // 2: headscale.v1.GetMachineResponse.last_successful_update:type_name -> google.protobuf.Timestamp @@ -682,13 +682,13 @@ var file_headscale_v1_rpc_proto_depIdxs = []int32{ 0, // [0:4] is the sub-list for field type_name } -func init() { file_headscale_v1_rpc_proto_init() } -func file_headscale_v1_rpc_proto_init() { - if File_headscale_v1_rpc_proto != nil { +func init() { file_headscale_v1_headscale_proto_init() } +func file_headscale_v1_headscale_proto_init() { + if File_headscale_v1_headscale_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_headscale_v1_rpc_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_headscale_v1_headscale_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetMachineRequest); i { case 0: return &v.state @@ -700,7 +700,7 @@ func file_headscale_v1_rpc_proto_init() { return nil } } - file_headscale_v1_rpc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_headscale_v1_headscale_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetMachineResponse); i { case 0: return &v.state @@ -712,7 +712,7 @@ func file_headscale_v1_rpc_proto_init() { return nil } } - file_headscale_v1_rpc_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_headscale_v1_headscale_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateNamespaceRequest); i { case 0: return &v.state @@ -724,7 +724,7 @@ func file_headscale_v1_rpc_proto_init() { return nil } } - file_headscale_v1_rpc_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_headscale_v1_headscale_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateNamespaceResponse); i { case 0: return &v.state @@ -736,7 +736,7 @@ func file_headscale_v1_rpc_proto_init() { return nil } } - file_headscale_v1_rpc_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_headscale_v1_headscale_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteNamespaceRequest); i { case 0: return &v.state @@ -748,7 +748,7 @@ func file_headscale_v1_rpc_proto_init() { return nil } } - file_headscale_v1_rpc_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_headscale_v1_headscale_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteNamespaceResponse); i { case 0: return &v.state @@ -760,7 +760,7 @@ func file_headscale_v1_rpc_proto_init() { return nil } } - file_headscale_v1_rpc_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_headscale_v1_headscale_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListNamespacesRequest); i { case 0: return &v.state @@ -772,7 +772,7 @@ func file_headscale_v1_rpc_proto_init() { return nil } } - file_headscale_v1_rpc_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_headscale_v1_headscale_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListNamespacesResponse); i { case 0: return &v.state @@ -789,19 +789,19 @@ func file_headscale_v1_rpc_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_headscale_v1_rpc_proto_rawDesc, + RawDescriptor: file_headscale_v1_headscale_proto_rawDesc, NumEnums: 1, NumMessages: 8, NumExtensions: 0, NumServices: 1, }, - GoTypes: file_headscale_v1_rpc_proto_goTypes, - DependencyIndexes: file_headscale_v1_rpc_proto_depIdxs, - EnumInfos: file_headscale_v1_rpc_proto_enumTypes, - MessageInfos: file_headscale_v1_rpc_proto_msgTypes, + GoTypes: file_headscale_v1_headscale_proto_goTypes, + DependencyIndexes: file_headscale_v1_headscale_proto_depIdxs, + EnumInfos: file_headscale_v1_headscale_proto_enumTypes, + MessageInfos: file_headscale_v1_headscale_proto_msgTypes, }.Build() - File_headscale_v1_rpc_proto = out.File - file_headscale_v1_rpc_proto_rawDesc = nil - file_headscale_v1_rpc_proto_goTypes = nil - file_headscale_v1_rpc_proto_depIdxs = nil + File_headscale_v1_headscale_proto = out.File + file_headscale_v1_headscale_proto_rawDesc = nil + file_headscale_v1_headscale_proto_goTypes = nil + file_headscale_v1_headscale_proto_depIdxs = nil } diff --git a/gen/go/headscale/v1/rpc.pb.gw.go b/gen/go/headscale/v1/headscale.pb.gw.go similarity index 99% rename from gen/go/headscale/v1/rpc.pb.gw.go rename to gen/go/headscale/v1/headscale.pb.gw.go index 773a85a4..226afe50 100644 --- a/gen/go/headscale/v1/rpc.pb.gw.go +++ b/gen/go/headscale/v1/headscale.pb.gw.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: headscale/v1/rpc.proto +// source: headscale/v1/headscale.proto /* Package v1 is a reverse proxy. diff --git a/gen/go/headscale/v1/rpc_grpc.pb.go b/gen/go/headscale/v1/headscale_grpc.pb.go similarity index 99% rename from gen/go/headscale/v1/rpc_grpc.pb.go rename to gen/go/headscale/v1/headscale_grpc.pb.go index 618c26d1..a613e8e9 100644 --- a/gen/go/headscale/v1/rpc_grpc.pb.go +++ b/gen/go/headscale/v1/headscale_grpc.pb.go @@ -205,5 +205,5 @@ var HeadscaleService_ServiceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "headscale/v1/rpc.proto", + Metadata: "headscale/v1/headscale.proto", } diff --git a/gen/openapiv2/headscale/v1/rpc.swagger.json b/gen/openapiv2/headscale/v1/headscale.swagger.json similarity index 99% rename from gen/openapiv2/headscale/v1/rpc.swagger.json rename to gen/openapiv2/headscale/v1/headscale.swagger.json index e9db7cf6..567a921f 100644 --- a/gen/openapiv2/headscale/v1/rpc.swagger.json +++ b/gen/openapiv2/headscale/v1/headscale.swagger.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "title": "headscale/v1/rpc.proto", + "title": "headscale/v1/headscale.proto", "version": "version not set" }, "tags": [ diff --git a/proto/headscale/v1/headscale.proto b/proto/headscale/v1/headscale.proto new file mode 100644 index 00000000..2e6b0a67 --- /dev/null +++ b/proto/headscale/v1/headscale.proto @@ -0,0 +1,106 @@ +syntax = "proto3"; +package headscale.v1; +option go_package = "github.com/juanfont/headscale/gen/go/v1"; + +import "google/protobuf/timestamp.proto"; +import "google/api/annotations.proto"; + +enum RegisterMethod { + REGISTER_METHOD_UNSPECIFIED = 0; + REGISTER_METHOD_AUTH_KEY = 1; + REGISTER_METHOD_CLI = 2; + REGISTER_METHOD_OIDC = 3; +} + +// message PreAuthKey { +// uint64 id = 1; +// string key = 2; +// uint32 namespace_id = 3; +// Namespace namespace = 4; +// bool reusable = 5; +// bool ephemeral = 6; +// bool used = 7; +// +// google.protobuf.Timestamp created_at = 8; +// google.protobuf.Timestamp expiration = 9; +// } + +message GetMachineRequest { + uint64 machine_id = 1; +} + +message GetMachineResponse { + uint64 id = 1; + string machine_key = 2; + string node_key = 3; + string disco_key = 4; + string ip_address = 5; + string name = 6; + uint32 namespace_id = 7; + + bool registered = 8; + RegisterMethod register_method = 9; + uint32 auth_key_id = 10; + // PreAuthKey auth_key = 11; + + google.protobuf.Timestamp last_seen = 12; + google.protobuf.Timestamp last_successful_update = 13; + google.protobuf.Timestamp expiry = 14; + + // bytes host_info = 15; + // bytes endpoints = 16; + // bytes enabled_routes = 17; + + // google.protobuf.Timestamp created_at = 18; + // google.protobuf.Timestamp updated_at = 19; + // google.protobuf.Timestamp deleted_at = 20; +} + +message CreateNamespaceRequest { + string name = 1; +} + +message CreateNamespaceResponse { + string name = 1; +} + +message DeleteNamespaceRequest { + string name = 1; +} + +message DeleteNamespaceResponse { +} + +message ListNamespacesRequest { +} + +message ListNamespacesResponse { + repeated string namespaces = 1; +} + +service HeadscaleService { + rpc GetMachine(GetMachineRequest) returns(GetMachineResponse) { + option(google.api.http) = { + get : "/api/v1/machine/{machine_id}" + }; + } + + rpc CreateNamespace(CreateNamespaceRequest) returns(CreateNamespaceResponse) { + option(google.api.http) = { + post : "/api/v1/namespace" + body : "*" + }; + } + + rpc DeleteNamespace(DeleteNamespaceRequest) returns(DeleteNamespaceResponse) { + option(google.api.http) = { + delete : "/api/v1/namespace" + }; + } + + rpc ListNamespaces(ListNamespacesRequest) returns(ListNamespacesResponse) { + option(google.api.http) = { + get : "/api/v1/namespace" + }; + } +} From 482a31b66b53e8258369925c40fb6cdba752a37c Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sat, 30 Oct 2021 14:29:53 +0000 Subject: [PATCH 58/70] Setup swagger and swagger UI properly --- app.go | 4 ++-- swagger.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 swagger.go diff --git a/app.go b/app.go index 707c111e..50343d3c 100644 --- a/app.go +++ b/app.go @@ -381,8 +381,8 @@ func (h *Headscale) Serve() error { r.POST("/machine/:id", h.RegistrationHandler) r.GET("/apple", h.AppleMobileConfig) r.GET("/apple/:platform", h.ApplePlatformConfig) - - r.StaticFile("/swagger/swagger.json", "gen/openapiv2/v1/headscale.swagger.json") + r.GET("/swagger", SwaggerUI) + r.GET("/swagger/v1/openapiv2.json", SwaggerAPIv1) api := r.Group("/api") api.Use(h.httpAuthenticationMiddleware) diff --git a/swagger.go b/swagger.go new file mode 100644 index 00000000..17f57697 --- /dev/null +++ b/swagger.go @@ -0,0 +1,65 @@ +package headscale + +import ( + "bytes" + _ "embed" + "net/http" + "text/template" + + "github.com/rs/zerolog/log" + + "github.com/gin-gonic/gin" +) + +//go:embed gen/openapiv2/headscale/v1/headscale.swagger.json +var apiV1JSON []byte + +func SwaggerUI(c *gin.Context) { + t := template.Must(template.New("swagger").Parse(` + + + + + + + + +
+ + +`)) + + var payload bytes.Buffer + if err := t.Execute(&payload, struct{}{}); err != nil { + log.Error(). + Caller(). + Err(err). + Msg("Could not render Swagger") + c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Could not render Swagger")) + return + } + + c.Data(http.StatusOK, "text/html; charset=utf-8", payload.Bytes()) +} + +func SwaggerAPIv1(c *gin.Context) { + c.Data(http.StatusOK, "application/json; charset=utf-8", apiV1JSON) +} From cd2914dbc9c1af4c929b363f56d0c3b6d5edfd3e Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sat, 30 Oct 2021 15:35:58 +0000 Subject: [PATCH 59/70] Make note about oidc being experimental --- config-example.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/config-example.yaml b/config-example.yaml index f28b4191..d4aa7815 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -66,10 +66,16 @@ dns_config: base_domain: example.com -# Experimental: OpenID Connect +# headscale supports experimental OpenID connect support, +# it is still being tested and might have some bugs, please +# help us test it. +# OpenID Connect # oidc: # issuer: "https://your-oidc.issuer.com/path" # client_id: "your-oidc-client-id" # client_secret: "your-oidc-client-secret" +# +# # Domain map is used to map incomming users (by their email) to +# # a namespace. The key can be a string, or regex. # domain_map: # ".*": default-namespace From bac81176b25b97d1cb0fb9b48657bf1aaf8d8fdb Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sat, 30 Oct 2021 15:39:05 +0000 Subject: [PATCH 60/70] Remove lint from generated testcode --- oidc_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/oidc_test.go b/oidc_test.go index ddb44e4c..b501ff14 100644 --- a/oidc_test.go +++ b/oidc_test.go @@ -144,6 +144,7 @@ func TestHeadscale_getNamespaceFromEmail(t *testing.T) { want1: false, }, } + //nolint for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { h := &Headscale{ From 223c6118209cf38fd9a548bf4a0a84ac67d5eb5a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 31 Oct 2021 09:34:07 +0000 Subject: [PATCH 61/70] docs(README): update contributors --- README.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a3c09396..2e8f8277 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,13 @@ Please have a look at the documentation under [`docs/`](docs/). ohdearaugustin + + + unreality/ +
+ unreality +
+ Aaron @@ -98,6 +105,8 @@ Please have a look at the documentation under [`docs/`](docs/). Aaron Bieber + + Paul @@ -105,8 +114,6 @@ Please have a look at the documentation under [`docs/`](docs/). Paul Tötterman - - Casey @@ -142,6 +149,8 @@ Please have a look at the documentation under [`docs/`](docs/). Felix Kronlage-Dammers + + Felix @@ -149,8 +158,6 @@ Please have a look at the documentation under [`docs/`](docs/). Felix Yan - - Shaanan @@ -186,6 +193,8 @@ Please have a look at the documentation under [`docs/`](docs/). Tjerk Woudsma + + Zakhar @@ -193,8 +202,6 @@ Please have a look at the documentation under [`docs/`](docs/). Zakhar Bessarab - - derelm/ From ca15a53fad29482f86392d1e002d1fa7af316eab Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 31 Oct 2021 09:58:01 +0000 Subject: [PATCH 62/70] Add timeout to integration test for execCommand to fail faster --- integration_test.go | 58 +++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/integration_test.go b/integration_test.go index 07894188..1f30bcab 100644 --- a/integration_test.go +++ b/integration_test.go @@ -39,7 +39,7 @@ var ( headscale dockertest.Resource ) -var tailscaleVersions = []string{"1.14.3", "1.12.3"} +var tailscaleVersions = []string{"1.16.2", "1.14.3", "1.12.3"} type TestNamespace struct { count int @@ -99,26 +99,48 @@ func executeCommand(resource *dockertest.Resource, cmd []string, env []string) ( var stdout bytes.Buffer var stderr bytes.Buffer - exitCode, err := resource.Exec( - cmd, - dockertest.ExecOptions{ - Env: env, - StdOut: &stdout, - StdErr: &stderr, - }, - ) - if err != nil { - return "", err + // TODO(kradalby): Make configurable + timeout := 10 * time.Second + + type result struct { + exitCode int + err error } - if exitCode != 0 { - fmt.Println("Command: ", cmd) - fmt.Println("stdout: ", stdout.String()) - fmt.Println("stderr: ", stderr.String()) - return "", fmt.Errorf("command failed with: %s", stderr.String()) - } + resultChan := make(chan result, 1) - return stdout.String(), nil + // Run your long running function in it's own goroutine and pass back it's + // response into our channel. + go func() { + exitCode, err := resource.Exec( + cmd, + dockertest.ExecOptions{ + Env: env, + StdOut: &stdout, + StdErr: &stderr, + }, + ) + resultChan <- result{exitCode, err} + }() + + // Listen on our channel AND a timeout channel - which ever happens first. + select { + case res := <-resultChan: + if res.err != nil { + return "", res.err + } + + if res.exitCode != 0 { + fmt.Println("Command: ", cmd) + fmt.Println("stdout: ", stdout.String()) + fmt.Println("stderr: ", stderr.String()) + return "", fmt.Errorf("command failed with: %s", stderr.String()) + } + + return stdout.String(), nil + case <-time.After(timeout): + return "", fmt.Errorf("command timed out after %s", timeout) + } } func saveLog(resource *dockertest.Resource, basePath string) error { From 3f30bf1e33e136fe318c7358ea0836102661ab1b Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 31 Oct 2021 16:19:38 +0000 Subject: [PATCH 63/70] Ensure we set up TLS for http --- app.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app.go b/app.go index 683d3a72..d82eae5d 100644 --- a/app.go +++ b/app.go @@ -474,7 +474,16 @@ func (h *Headscale) Serve() error { g.Go(func() error { return grpcServer.Serve(socketListener) }) g.Go(func() error { return grpcServer.Serve(grpcListener) }) - g.Go(func() error { return httpServer.Serve(httpListener) }) + + if tlsConfig != nil { + g.Go(func() error { + tlsl := tls.NewListener(httpListener, tlsConfig) + return httpServer.Serve(tlsl) + }) + } else { + g.Go(func() error { return httpServer.Serve(httpListener) }) + } + g.Go(func() error { return m.Serve() }) log.Info().Msgf("listening and serving (multiplexed HTTP and gRPC) on: %s", h.cfg.Addr) From 12f2a7cee0ec7f0f84f60967000940c56e679409 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 31 Oct 2021 16:26:51 +0000 Subject: [PATCH 64/70] Move context per cure's suggestion --- cmd/headscale/cli/namespaces.go | 8 ++++---- cmd/headscale/cli/utils.go | 6 +----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/cmd/headscale/cli/namespaces.go b/cmd/headscale/cli/namespaces.go index 044347fe..1c4e3762 100644 --- a/cmd/headscale/cli/namespaces.go +++ b/cmd/headscale/cli/namespaces.go @@ -38,14 +38,14 @@ var createNamespaceCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { o, _ := cmd.Flags().GetString("output") - client, conn := getHeadscaleGRPCClient() + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + client, conn := getHeadscaleGRPCClient(ctx) defer conn.Close() log.Trace().Interface("client", client).Msg("Obtained gRPC client") - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - defer cancel() - request := &apiV1.CreateNamespaceRequest{Name: args[0]} log.Trace().Interface("request", request).Msg("Sending CreateNamespace request") diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 035f508e..cd467697 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -316,11 +316,7 @@ func getHeadscaleApp() (*headscale.Headscale, error) { return h, nil } -func getHeadscaleGRPCClient() (apiV1.HeadscaleServiceClient, *grpc.ClientConn) { - // TODO(kradalby): Make configurable - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - +func getHeadscaleGRPCClient(ctx context.Context) (apiV1.HeadscaleServiceClient, *grpc.ClientConn) { grpcOptions := []grpc.DialOption{ grpc.WithBlock(), } From 1c9b1ea91a772ac67df392636e7bfd0dfa1d100b Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 31 Oct 2021 16:34:20 +0000 Subject: [PATCH 65/70] Add todo --- app.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app.go b/app.go index d82eae5d..63c73ff7 100644 --- a/app.go +++ b/app.go @@ -473,6 +473,8 @@ func (h *Headscale) Serve() error { g := new(errgroup.Group) g.Go(func() error { return grpcServer.Serve(socketListener) }) + + // TODO(kradalby): Verify if we need the same TLS setup for gRPC as HTTP g.Go(func() error { return grpcServer.Serve(grpcListener) }) if tlsConfig != nil { From 8db45a4e75f7a1adb20cca7643f4e3030443a070 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 31 Oct 2021 19:52:34 +0000 Subject: [PATCH 66/70] Setup a seperate, non-tls, no auth, socket grpc --- app.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app.go b/app.go index 63c73ff7..faaef8db 100644 --- a/app.go +++ b/app.go @@ -261,11 +261,11 @@ func (h *Headscale) grpcAuthenticationInterceptor(ctx context.Context, p, _ := peer.FromContext(ctx) // TODO(kradalby): Figure out what @ means (socket wise) and if it can be exploited - if p.Addr.String() == "@" { - log.Trace().Caller().Str("client_address", p.Addr.String()).Msg("Client connecting over socket") + // if p.Addr.String() == "@" { + // log.Trace().Caller().Str("client_address", p.Addr.String()).Msg("Client connecting over socket") - return handler(ctx, req) - } + // return handler(ctx, req) + // } log.Trace().Caller().Str("client_address", p.Addr.String()).Msg("Client is trying to authenticate") @@ -467,12 +467,17 @@ func (h *Headscale) Serve() error { grpcServer := grpc.NewServer(grpcOptions...) + // Start the local gRPC server without TLS and without authentication + grpcSocket := grpc.NewServer() + apiV1.RegisterHeadscaleServiceServer(grpcServer, newHeadscaleV1APIServer(h)) + apiV1.RegisterHeadscaleServiceServer(grpcSocket, newHeadscaleV1APIServer(h)) reflection.Register(grpcServer) + reflection.Register(grpcSocket) g := new(errgroup.Group) - g.Go(func() error { return grpcServer.Serve(socketListener) }) + g.Go(func() error { return grpcSocket.Serve(socketListener) }) // TODO(kradalby): Verify if we need the same TLS setup for gRPC as HTTP g.Go(func() error { return grpcServer.Serve(grpcListener) }) From 53df9afc2a8fff867bd57ac89d9a46b2cc727ef2 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 31 Oct 2021 19:54:38 +0000 Subject: [PATCH 67/70] Fix step naming error --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f1773af9..ca4d4cf2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: sudo apt update sudo apt install -y make - - name: Run lint + - name: Run build run: make build - uses: actions/upload-artifact@v2 From b91c115adebf7e5298f0e922ae4fbfd20f1613c4 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sun, 31 Oct 2021 19:57:42 +0000 Subject: [PATCH 68/70] Remove "auth skip" for socket traffic --- app.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app.go b/app.go index faaef8db..a28a2152 100644 --- a/app.go +++ b/app.go @@ -260,13 +260,6 @@ func (h *Headscale) grpcAuthenticationInterceptor(ctx context.Context, // the server p, _ := peer.FromContext(ctx) - // TODO(kradalby): Figure out what @ means (socket wise) and if it can be exploited - // if p.Addr.String() == "@" { - // log.Trace().Caller().Str("client_address", p.Addr.String()).Msg("Client connecting over socket") - - // return handler(ctx, req) - // } - log.Trace().Caller().Str("client_address", p.Addr.String()).Msg("Client is trying to authenticate") md, ok := metadata.FromIncomingContext(ctx) From 9954a3c599dadfc54f0447ea815d9b0f07b2298c Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 2 Nov 2021 21:46:15 +0000 Subject: [PATCH 69/70] Add handling for closing the socket --- app.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app.go b/app.go index a28a2152..b61331a1 100644 --- a/app.go +++ b/app.go @@ -10,9 +10,11 @@ import ( "net/http" "net/url" "os" + "os/signal" "sort" "strings" "sync" + "syscall" "time" "github.com/coreos/go-oidc/v3/oidc" @@ -352,6 +354,19 @@ func (h *Headscale) Serve() error { panic(err) } + // Handle common process-killing signals so we can gracefully shut down: + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM) + go func(c chan os.Signal) { + // Wait for a SIGINT or SIGKILL: + sig := <-c + log.Printf("Caught signal %s: shutting down.", sig) + // Stop listening (and unlink the socket if unix type): + socketListener.Close() + // And we're done: + os.Exit(0) + }(sigc) + networkListener, err := net.Listen("tcp", h.cfg.Addr) if err != nil { panic(err) From 7c774bc5470d9cf7a7ca567c4a69ccb0d748b171 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 2 Nov 2021 21:49:19 +0000 Subject: [PATCH 70/70] Remove flag that cant be trapped --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index b61331a1..1f456665 100644 --- a/app.go +++ b/app.go @@ -356,7 +356,7 @@ func (h *Headscale) Serve() error { // Handle common process-killing signals so we can gracefully shut down: sigc := make(chan os.Signal, 1) - signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM) + signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) go func(c chan os.Signal) { // Wait for a SIGINT or SIGKILL: sig := <-c