mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	Merge pull request #306 from kradalby/apiwork
Introduce API keys and enable remote control API
This commit is contained in:
		
						commit
						73497382b7
					
				| @ -7,6 +7,9 @@ | ||||
| **Features**: | ||||
| 
 | ||||
| - Add IPv6 support to the prefix assigned to namespaces | ||||
| - Add API Key support | ||||
|   - Enable remote control of `headscale` via CLI [docs](docs/remote-cli.md) | ||||
|   - Enable HTTP API (beta, subject to change) | ||||
| 
 | ||||
| **Changes**: | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										164
									
								
								api_key.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								api_key.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,164 @@ | ||||
| package headscale | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	v1 "github.com/juanfont/headscale/gen/go/headscale/v1" | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| 	"google.golang.org/protobuf/types/known/timestamppb" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	apiPrefixLength = 7 | ||||
| 	apiKeyLength    = 32 | ||||
| 	apiKeyParts     = 2 | ||||
| 
 | ||||
| 	errAPIKeyFailedToParse = Error("Failed to parse ApiKey") | ||||
| ) | ||||
| 
 | ||||
| // APIKey describes the datamodel for API keys used to remotely authenticate with
 | ||||
| // headscale.
 | ||||
| type APIKey struct { | ||||
| 	ID     uint64 `gorm:"primary_key"` | ||||
| 	Prefix string `gorm:"uniqueIndex"` | ||||
| 	Hash   []byte | ||||
| 
 | ||||
| 	CreatedAt  *time.Time | ||||
| 	Expiration *time.Time | ||||
| 	LastSeen   *time.Time | ||||
| } | ||||
| 
 | ||||
| // CreateAPIKey creates a new ApiKey in a namespace, and returns it.
 | ||||
| func (h *Headscale) CreateAPIKey( | ||||
| 	expiration *time.Time, | ||||
| ) (string, *APIKey, error) { | ||||
| 	prefix, err := GenerateRandomStringURLSafe(apiPrefixLength) | ||||
| 	if err != nil { | ||||
| 		return "", nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	toBeHashed, err := GenerateRandomStringURLSafe(apiKeyLength) | ||||
| 	if err != nil { | ||||
| 		return "", nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Key to return to user, this will only be visible _once_
 | ||||
| 	keyStr := prefix + "." + toBeHashed | ||||
| 
 | ||||
| 	hash, err := bcrypt.GenerateFromPassword([]byte(toBeHashed), bcrypt.DefaultCost) | ||||
| 	if err != nil { | ||||
| 		return "", nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	key := APIKey{ | ||||
| 		Prefix:     prefix, | ||||
| 		Hash:       hash, | ||||
| 		Expiration: expiration, | ||||
| 	} | ||||
| 	h.db.Save(&key) | ||||
| 
 | ||||
| 	return keyStr, &key, nil | ||||
| } | ||||
| 
 | ||||
| // ListAPIKeys returns the list of ApiKeys for a namespace.
 | ||||
| func (h *Headscale) ListAPIKeys() ([]APIKey, error) { | ||||
| 	keys := []APIKey{} | ||||
| 	if err := h.db.Find(&keys).Error; err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return keys, nil | ||||
| } | ||||
| 
 | ||||
| // GetAPIKey returns a ApiKey for a given key.
 | ||||
| func (h *Headscale) GetAPIKey(prefix string) (*APIKey, error) { | ||||
| 	key := APIKey{} | ||||
| 	if result := h.db.First(&key, "prefix = ?", prefix); result.Error != nil { | ||||
| 		return nil, result.Error | ||||
| 	} | ||||
| 
 | ||||
| 	return &key, nil | ||||
| } | ||||
| 
 | ||||
| // GetAPIKeyByID returns a ApiKey for a given id.
 | ||||
| func (h *Headscale) GetAPIKeyByID(id uint64) (*APIKey, error) { | ||||
| 	key := APIKey{} | ||||
| 	if result := h.db.Find(&APIKey{ID: id}).First(&key); result.Error != nil { | ||||
| 		return nil, result.Error | ||||
| 	} | ||||
| 
 | ||||
| 	return &key, nil | ||||
| } | ||||
| 
 | ||||
| // DestroyAPIKey destroys a ApiKey. Returns error if the ApiKey
 | ||||
| // does not exist.
 | ||||
| func (h *Headscale) DestroyAPIKey(key APIKey) error { | ||||
| 	if result := h.db.Unscoped().Delete(key); result.Error != nil { | ||||
| 		return result.Error | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // ExpireAPIKey marks a ApiKey as expired.
 | ||||
| func (h *Headscale) ExpireAPIKey(key *APIKey) error { | ||||
| 	if err := h.db.Model(&key).Update("Expiration", time.Now()).Error; err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (h *Headscale) ValidateAPIKey(keyStr string) (bool, error) { | ||||
| 	prefix, hash, err := splitAPIKey(keyStr) | ||||
| 	if err != nil { | ||||
| 		return false, fmt.Errorf("failed to validate api key: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	key, err := h.GetAPIKey(prefix) | ||||
| 	if err != nil { | ||||
| 		return false, fmt.Errorf("failed to validate api key: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if key.Expiration.Before(time.Now()) { | ||||
| 		return false, nil | ||||
| 	} | ||||
| 
 | ||||
| 	if err := bcrypt.CompareHashAndPassword(key.Hash, []byte(hash)); err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
| 
 | ||||
| 	return true, nil | ||||
| } | ||||
| 
 | ||||
| func splitAPIKey(key string) (string, string, error) { | ||||
| 	parts := strings.Split(key, ".") | ||||
| 	if len(parts) != apiKeyParts { | ||||
| 		return "", "", errAPIKeyFailedToParse | ||||
| 	} | ||||
| 
 | ||||
| 	return parts[0], parts[1], nil | ||||
| } | ||||
| 
 | ||||
| func (key *APIKey) toProto() *v1.ApiKey { | ||||
| 	protoKey := v1.ApiKey{ | ||||
| 		Id:     key.ID, | ||||
| 		Prefix: key.Prefix, | ||||
| 	} | ||||
| 
 | ||||
| 	if key.Expiration != nil { | ||||
| 		protoKey.Expiration = timestamppb.New(*key.Expiration) | ||||
| 	} | ||||
| 
 | ||||
| 	if key.CreatedAt != nil { | ||||
| 		protoKey.CreatedAt = timestamppb.New(*key.CreatedAt) | ||||
| 	} | ||||
| 
 | ||||
| 	if key.LastSeen != nil { | ||||
| 		protoKey.LastSeen = timestamppb.New(*key.LastSeen) | ||||
| 	} | ||||
| 
 | ||||
| 	return &protoKey | ||||
| } | ||||
							
								
								
									
										89
									
								
								api_key_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								api_key_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,89 @@ | ||||
| package headscale | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	"gopkg.in/check.v1" | ||||
| ) | ||||
| 
 | ||||
| func (*Suite) TestCreateAPIKey(c *check.C) { | ||||
| 	apiKeyStr, apiKey, err := app.CreateAPIKey(nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(apiKey, check.NotNil) | ||||
| 
 | ||||
| 	// Did we get a valid key?
 | ||||
| 	c.Assert(apiKey.Prefix, check.NotNil) | ||||
| 	c.Assert(apiKey.Hash, check.NotNil) | ||||
| 	c.Assert(apiKeyStr, check.Not(check.Equals), "") | ||||
| 
 | ||||
| 	_, err = app.ListAPIKeys() | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	keys, err := app.ListAPIKeys() | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(len(keys), check.Equals, 1) | ||||
| } | ||||
| 
 | ||||
| func (*Suite) TestAPIKeyDoesNotExist(c *check.C) { | ||||
| 	key, err := app.GetAPIKey("does-not-exist") | ||||
| 	c.Assert(err, check.NotNil) | ||||
| 	c.Assert(key, check.IsNil) | ||||
| } | ||||
| 
 | ||||
| func (*Suite) TestValidateAPIKeyOk(c *check.C) { | ||||
| 	nowPlus2 := time.Now().Add(2 * time.Hour) | ||||
| 	apiKeyStr, apiKey, err := app.CreateAPIKey(&nowPlus2) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(apiKey, check.NotNil) | ||||
| 
 | ||||
| 	valid, err := app.ValidateAPIKey(apiKeyStr) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(valid, check.Equals, true) | ||||
| } | ||||
| 
 | ||||
| func (*Suite) TestValidateAPIKeyNotOk(c *check.C) { | ||||
| 	nowMinus2 := time.Now().Add(time.Duration(-2) * time.Hour) | ||||
| 	apiKeyStr, apiKey, err := app.CreateAPIKey(&nowMinus2) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(apiKey, check.NotNil) | ||||
| 
 | ||||
| 	valid, err := app.ValidateAPIKey(apiKeyStr) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(valid, check.Equals, false) | ||||
| 
 | ||||
| 	now := time.Now() | ||||
| 	apiKeyStrNow, apiKey, err := app.CreateAPIKey(&now) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(apiKey, check.NotNil) | ||||
| 
 | ||||
| 	validNow, err := app.ValidateAPIKey(apiKeyStrNow) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(validNow, check.Equals, false) | ||||
| 
 | ||||
| 	validSilly, err := app.ValidateAPIKey("nota.validkey") | ||||
| 	c.Assert(err, check.NotNil) | ||||
| 	c.Assert(validSilly, check.Equals, false) | ||||
| 
 | ||||
| 	validWithErr, err := app.ValidateAPIKey("produceerrorkey") | ||||
| 	c.Assert(err, check.NotNil) | ||||
| 	c.Assert(validWithErr, check.Equals, false) | ||||
| } | ||||
| 
 | ||||
| func (*Suite) TestExpireAPIKey(c *check.C) { | ||||
| 	nowPlus2 := time.Now().Add(2 * time.Hour) | ||||
| 	apiKeyStr, apiKey, err := app.CreateAPIKey(&nowPlus2) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(apiKey, check.NotNil) | ||||
| 
 | ||||
| 	valid, err := app.ValidateAPIKey(apiKeyStr) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(valid, check.Equals, true) | ||||
| 
 | ||||
| 	err = app.ExpireAPIKey(apiKey) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(apiKey.Expiration, check.NotNil) | ||||
| 
 | ||||
| 	notValid, err := app.ValidateAPIKey(apiKeyStr) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(notValid, check.Equals, false) | ||||
| } | ||||
							
								
								
									
										309
									
								
								app.go
									
									
									
									
									
								
							
							
						
						
									
										309
									
								
								app.go
									
									
									
									
									
								
							| @ -27,7 +27,6 @@ import ( | ||||
| 	zerolog "github.com/philip-bui/grpc-zerolog" | ||||
| 	zl "github.com/rs/zerolog" | ||||
| 	"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" | ||||
| @ -36,6 +35,7 @@ import ( | ||||
| 	"google.golang.org/grpc" | ||||
| 	"google.golang.org/grpc/codes" | ||||
| 	"google.golang.org/grpc/credentials" | ||||
| 	"google.golang.org/grpc/credentials/insecure" | ||||
| 	"google.golang.org/grpc/metadata" | ||||
| 	"google.golang.org/grpc/peer" | ||||
| 	"google.golang.org/grpc/reflection" | ||||
| @ -68,6 +68,8 @@ const ( | ||||
| type Config struct { | ||||
| 	ServerURL                      string | ||||
| 	Addr                           string | ||||
| 	GRPCAddr                       string | ||||
| 	GRPCAllowInsecure              bool | ||||
| 	EphemeralNodeInactivityTimeout time.Duration | ||||
| 	IPPrefixes                     []netaddr.IPPrefix | ||||
| 	PrivateKeyPath                 string | ||||
| @ -121,8 +123,8 @@ type DERPConfig struct { | ||||
| type CLIConfig struct { | ||||
| 	Address  string | ||||
| 	APIKey   string | ||||
| 	Insecure bool | ||||
| 	Timeout  time.Duration | ||||
| 	Insecure bool | ||||
| } | ||||
| 
 | ||||
| // Headscale represents the base app of the service.
 | ||||
| @ -325,26 +327,26 @@ func (h *Headscale) grpcAuthenticationInterceptor(ctx context.Context, | ||||
| 		) | ||||
| 	} | ||||
| 
 | ||||
| 	// 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", | ||||
| 	) | ||||
| 	valid, err := h.ValidateAPIKey(strings.TrimPrefix(token, AuthPrefix)) | ||||
| 	if err != nil { | ||||
| 		log.Error(). | ||||
| 			Caller(). | ||||
| 			Err(err). | ||||
| 			Str("client_address", client.Addr.String()). | ||||
| 			Msg("failed to validate token") | ||||
| 
 | ||||
| 	// 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 ctx, status.Error(codes.Internal, "failed to validate token") | ||||
| 	} | ||||
| 
 | ||||
| 	// return handler(ctx, req)
 | ||||
| 	if !valid { | ||||
| 		log.Info(). | ||||
| 			Str("client_address", client.Addr.String()). | ||||
| 			Msg("invalid token") | ||||
| 
 | ||||
| 		return ctx, status.Error(codes.Unauthenticated, "invalid token") | ||||
| 	} | ||||
| 
 | ||||
| 	return handler(ctx, req) | ||||
| } | ||||
| 
 | ||||
| func (h *Headscale) httpAuthenticationMiddleware(ctx *gin.Context) { | ||||
| @ -367,19 +369,30 @@ func (h *Headscale) httpAuthenticationMiddleware(ctx *gin.Context) { | ||||
| 
 | ||||
| 	ctx.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"})
 | ||||
| 	valid, err := h.ValidateAPIKey(strings.TrimPrefix(authHeader, AuthPrefix)) | ||||
| 	if err != nil { | ||||
| 		log.Error(). | ||||
| 			Caller(). | ||||
| 			Err(err). | ||||
| 			Str("client_address", ctx.ClientIP()). | ||||
| 			Msg("failed to validate token") | ||||
| 
 | ||||
| 	// 	return
 | ||||
| 	// }
 | ||||
| 		ctx.AbortWithStatus(http.StatusInternalServerError) | ||||
| 
 | ||||
| 	// c.Next()
 | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if !valid { | ||||
| 		log.Info(). | ||||
| 			Str("client_address", ctx.ClientIP()). | ||||
| 			Msg("invalid token") | ||||
| 
 | ||||
| 		ctx.AbortWithStatus(http.StatusUnauthorized) | ||||
| 
 | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	ctx.Next() | ||||
| } | ||||
| 
 | ||||
| // ensureUnixSocketIsAbsent will check if the given path for headscales unix socket is clear
 | ||||
| @ -393,15 +406,71 @@ func (h *Headscale) ensureUnixSocketIsAbsent() error { | ||||
| 	return os.Remove(h.cfg.UnixSocket) | ||||
| } | ||||
| 
 | ||||
| func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *gin.Engine { | ||||
| 	router := gin.Default() | ||||
| 
 | ||||
| 	prometheus := ginprometheus.NewPrometheus("gin") | ||||
| 	prometheus.Use(router) | ||||
| 
 | ||||
| 	router.GET( | ||||
| 		"/health", | ||||
| 		func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"healthy": "ok"}) }, | ||||
| 	) | ||||
| 	router.GET("/key", h.KeyHandler) | ||||
| 	router.GET("/register", h.RegisterWebAPI) | ||||
| 	router.POST("/machine/:id/map", h.PollNetMapHandler) | ||||
| 	router.POST("/machine/:id", h.RegistrationHandler) | ||||
| 	router.GET("/oidc/register/:mkey", h.RegisterOIDC) | ||||
| 	router.GET("/oidc/callback", h.OIDCCallback) | ||||
| 	router.GET("/apple", h.AppleMobileConfig) | ||||
| 	router.GET("/apple/:platform", h.ApplePlatformConfig) | ||||
| 	router.GET("/swagger", SwaggerUI) | ||||
| 	router.GET("/swagger/v1/openapiv2.json", SwaggerAPIv1) | ||||
| 
 | ||||
| 	api := router.Group("/api") | ||||
| 	api.Use(h.httpAuthenticationMiddleware) | ||||
| 	{ | ||||
| 		api.Any("/v1/*any", gin.WrapF(grpcMux.ServeHTTP)) | ||||
| 	} | ||||
| 
 | ||||
| 	router.NoRoute(stdoutHandler) | ||||
| 
 | ||||
| 	return router | ||||
| } | ||||
| 
 | ||||
| // Serve launches a GIN server with the Headscale API.
 | ||||
| func (h *Headscale) Serve() error { | ||||
| 	var err error | ||||
| 
 | ||||
| 	// Fetch an initial DERP Map before we start serving
 | ||||
| 	h.DERPMap = GetDERPMap(h.cfg.DERP) | ||||
| 
 | ||||
| 	if h.cfg.DERP.AutoUpdate { | ||||
| 		derpMapCancelChannel := make(chan struct{}) | ||||
| 		defer func() { derpMapCancelChannel <- struct{}{} }() | ||||
| 		go h.scheduledDERPMapUpdateWorker(derpMapCancelChannel) | ||||
| 	} | ||||
| 
 | ||||
| 	go h.expireEphemeralNodes(updateInterval) | ||||
| 
 | ||||
| 	if zl.GlobalLevel() == zl.TraceLevel { | ||||
| 		zerolog.RespLog = true | ||||
| 	} else { | ||||
| 		zerolog.RespLog = false | ||||
| 	} | ||||
| 
 | ||||
| 	// Prepare group for running listeners
 | ||||
| 	errorGroup := new(errgroup.Group) | ||||
| 
 | ||||
| 	ctx := context.Background() | ||||
| 	ctx, cancel := context.WithCancel(ctx) | ||||
| 
 | ||||
| 	defer cancel() | ||||
| 
 | ||||
| 	//
 | ||||
| 	//
 | ||||
| 	// Set up LOCAL listeners
 | ||||
| 	//
 | ||||
| 
 | ||||
| 	err = h.ensureUnixSocketIsAbsent() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to remove old socket file: %w", err) | ||||
| @ -430,32 +499,13 @@ func (h *Headscale) Serve() error { | ||||
| 		os.Exit(0) | ||||
| 	}(sigc) | ||||
| 
 | ||||
| 	networkListener, err := net.Listen("tcp", h.cfg.Addr) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to bind to TCP address: %w", 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.
 | ||||
| 	networkMutex := cmux.New(networkListener) | ||||
| 	// Match gRPC requests here
 | ||||
| 	grpcListener := networkMutex.MatchWithWriters( | ||||
| 		cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"), | ||||
| 		cmux.HTTP2MatchHeaderFieldSendSettings( | ||||
| 			"content-type", | ||||
| 			"application/grpc+proto", | ||||
| 		), | ||||
| 	) | ||||
| 	// Otherwise match regular http requests.
 | ||||
| 	httpListener := networkMutex.Match(cmux.Any()) | ||||
| 
 | ||||
| 	grpcGatewayMux := runtime.NewServeMux() | ||||
| 
 | ||||
| 	// Make the grpc-gateway connect to grpc over socket
 | ||||
| 	grpcGatewayConn, err := grpc.Dial( | ||||
| 		h.cfg.UnixSocket, | ||||
| 		[]grpc.DialOption{ | ||||
| 			grpc.WithInsecure(), | ||||
| 			grpc.WithTransportCredentials(insecure.NewCredentials()), | ||||
| 			grpc.WithContextDialer(GrpcSocketDialer), | ||||
| 		}..., | ||||
| 	) | ||||
| @ -470,44 +520,80 @@ func (h *Headscale) Serve() error { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	router := gin.Default() | ||||
| 	// Start the local gRPC server without TLS and without authentication
 | ||||
| 	grpcSocket := grpc.NewServer(zerolog.UnaryInterceptor()) | ||||
| 
 | ||||
| 	prometheus := ginprometheus.NewPrometheus("gin") | ||||
| 	prometheus.Use(router) | ||||
| 	v1.RegisterHeadscaleServiceServer(grpcSocket, newHeadscaleV1APIServer(h)) | ||||
| 	reflection.Register(grpcSocket) | ||||
| 
 | ||||
| 	router.GET( | ||||
| 		"/health", | ||||
| 		func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"healthy": "ok"}) }, | ||||
| 	) | ||||
| 	router.GET("/key", h.KeyHandler) | ||||
| 	router.GET("/register", h.RegisterWebAPI) | ||||
| 	router.POST("/machine/:id/map", h.PollNetMapHandler) | ||||
| 	router.POST("/machine/:id", h.RegistrationHandler) | ||||
| 	router.GET("/oidc/register/:mkey", h.RegisterOIDC) | ||||
| 	router.GET("/oidc/callback", h.OIDCCallback) | ||||
| 	router.GET("/apple", h.AppleMobileConfig) | ||||
| 	router.GET("/apple/:platform", h.ApplePlatformConfig) | ||||
| 	router.GET("/swagger", SwaggerUI) | ||||
| 	router.GET("/swagger/v1/openapiv2.json", SwaggerAPIv1) | ||||
| 	errorGroup.Go(func() error { return grpcSocket.Serve(socketListener) }) | ||||
| 
 | ||||
| 	api := router.Group("/api") | ||||
| 	api.Use(h.httpAuthenticationMiddleware) | ||||
| 	{ | ||||
| 		api.Any("/v1/*any", gin.WrapF(grpcGatewayMux.ServeHTTP)) | ||||
| 	//
 | ||||
| 	//
 | ||||
| 	// Set up REMOTE listeners
 | ||||
| 	//
 | ||||
| 
 | ||||
| 	tlsConfig, err := h.getTLSSettings() | ||||
| 	if err != nil { | ||||
| 		log.Error().Err(err).Msg("Failed to set up TLS configuration") | ||||
| 
 | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	router.NoRoute(stdoutHandler) | ||||
| 	//
 | ||||
| 	//
 | ||||
| 	// gRPC setup
 | ||||
| 	//
 | ||||
| 
 | ||||
| 	// Fetch an initial DERP Map before we start serving
 | ||||
| 	h.DERPMap = GetDERPMap(h.cfg.DERP) | ||||
| 	// We are sadly not able to run gRPC and HTTPS (2.0) on the same
 | ||||
| 	// port because the connection mux does not support matching them
 | ||||
| 	// since they are so similar. There is multiple issues open and we
 | ||||
| 	// can revisit this if changes:
 | ||||
| 	// https://github.com/soheilhy/cmux/issues/68
 | ||||
| 	// https://github.com/soheilhy/cmux/issues/91
 | ||||
| 
 | ||||
| 	if h.cfg.DERP.AutoUpdate { | ||||
| 		derpMapCancelChannel := make(chan struct{}) | ||||
| 		defer func() { derpMapCancelChannel <- struct{}{} }() | ||||
| 		go h.scheduledDERPMapUpdateWorker(derpMapCancelChannel) | ||||
| 	if tlsConfig != nil || h.cfg.GRPCAllowInsecure { | ||||
| 		log.Info().Msgf("Enabling remote gRPC at %s", h.cfg.GRPCAddr) | ||||
| 
 | ||||
| 		grpcOptions := []grpc.ServerOption{ | ||||
| 			grpc.UnaryInterceptor( | ||||
| 				grpc_middleware.ChainUnaryServer( | ||||
| 					h.grpcAuthenticationInterceptor, | ||||
| 					zerolog.NewUnaryServerInterceptor(), | ||||
| 				), | ||||
| 			), | ||||
| 		} | ||||
| 
 | ||||
| 		if tlsConfig != nil { | ||||
| 			grpcOptions = append(grpcOptions, | ||||
| 				grpc.Creds(credentials.NewTLS(tlsConfig)), | ||||
| 			) | ||||
| 		} else { | ||||
| 			log.Warn().Msg("gRPC is running without security") | ||||
| 		} | ||||
| 
 | ||||
| 		grpcServer := grpc.NewServer(grpcOptions...) | ||||
| 
 | ||||
| 		v1.RegisterHeadscaleServiceServer(grpcServer, newHeadscaleV1APIServer(h)) | ||||
| 		reflection.Register(grpcServer) | ||||
| 
 | ||||
| 		grpcListener, err := net.Listen("tcp", h.cfg.GRPCAddr) | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to bind to TCP address: %w", err) | ||||
| 		} | ||||
| 
 | ||||
| 		errorGroup.Go(func() error { return grpcServer.Serve(grpcListener) }) | ||||
| 
 | ||||
| 		log.Info(). | ||||
| 			Msgf("listening and serving gRPC on: %s", h.cfg.GRPCAddr) | ||||
| 	} | ||||
| 
 | ||||
| 	go h.expireEphemeralNodes(updateInterval) | ||||
| 	//
 | ||||
| 	//
 | ||||
| 	// HTTP setup
 | ||||
| 	//
 | ||||
| 
 | ||||
| 	router := h.createRouter(grpcGatewayMux) | ||||
| 
 | ||||
| 	httpServer := &http.Server{ | ||||
| 		Addr:        h.cfg.Addr, | ||||
| @ -520,65 +606,21 @@ func (h *Headscale) Serve() error { | ||||
| 		WriteTimeout: 0, | ||||
| 	} | ||||
| 
 | ||||
| 	if zl.GlobalLevel() == zl.TraceLevel { | ||||
| 		zerolog.RespLog = true | ||||
| 	} else { | ||||
| 		zerolog.RespLog = false | ||||
| 	} | ||||
| 
 | ||||
| 	grpcOptions := []grpc.ServerOption{ | ||||
| 		grpc.UnaryInterceptor( | ||||
| 			grpc_middleware.ChainUnaryServer( | ||||
| 				h.grpcAuthenticationInterceptor, | ||||
| 				zerolog.NewUnaryServerInterceptor(), | ||||
| 			), | ||||
| 		), | ||||
| 	} | ||||
| 
 | ||||
| 	tlsConfig, err := h.getTLSSettings() | ||||
| 	if err != nil { | ||||
| 		log.Error().Err(err).Msg("Failed to set up TLS configuration") | ||||
| 
 | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	var httpListener net.Listener | ||||
| 	if tlsConfig != nil { | ||||
| 		httpServer.TLSConfig = tlsConfig | ||||
| 
 | ||||
| 		grpcOptions = append(grpcOptions, grpc.Creds(credentials.NewTLS(tlsConfig))) | ||||
| 	} | ||||
| 
 | ||||
| 	grpcServer := grpc.NewServer(grpcOptions...) | ||||
| 
 | ||||
| 	// Start the local gRPC server without TLS and without authentication
 | ||||
| 	grpcSocket := grpc.NewServer(zerolog.UnaryInterceptor()) | ||||
| 
 | ||||
| 	v1.RegisterHeadscaleServiceServer(grpcServer, newHeadscaleV1APIServer(h)) | ||||
| 	v1.RegisterHeadscaleServiceServer(grpcSocket, newHeadscaleV1APIServer(h)) | ||||
| 	reflection.Register(grpcServer) | ||||
| 	reflection.Register(grpcSocket) | ||||
| 
 | ||||
| 	errorGroup := new(errgroup.Group) | ||||
| 
 | ||||
| 	errorGroup.Go(func() error { return grpcSocket.Serve(socketListener) }) | ||||
| 
 | ||||
| 	// TODO(kradalby): Verify if we need the same TLS setup for gRPC as HTTP
 | ||||
| 	errorGroup.Go(func() error { return grpcServer.Serve(grpcListener) }) | ||||
| 
 | ||||
| 	if tlsConfig != nil { | ||||
| 		errorGroup.Go(func() error { | ||||
| 			tlsl := tls.NewListener(httpListener, tlsConfig) | ||||
| 
 | ||||
| 			return httpServer.Serve(tlsl) | ||||
| 		}) | ||||
| 		httpListener, err = tls.Listen("tcp", h.cfg.Addr, tlsConfig) | ||||
| 	} else { | ||||
| 		errorGroup.Go(func() error { return httpServer.Serve(httpListener) }) | ||||
| 		httpListener, err = net.Listen("tcp", h.cfg.Addr) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to bind to TCP address: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	errorGroup.Go(func() error { return networkMutex.Serve() }) | ||||
| 	errorGroup.Go(func() error { return httpServer.Serve(httpListener) }) | ||||
| 
 | ||||
| 	log.Info(). | ||||
| 		Msgf("listening and serving (multiplexed HTTP and gRPC) on: %s", h.cfg.Addr) | ||||
| 		Msgf("listening and serving HTTP on: %s", h.cfg.Addr) | ||||
| 
 | ||||
| 	return errorGroup.Wait() | ||||
| } | ||||
| @ -614,6 +656,7 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) { | ||||
| 			// service, which can be configured to run on any other port.
 | ||||
| 			go func() { | ||||
| 				log.Fatal(). | ||||
| 					Caller(). | ||||
| 					Err(http.ListenAndServe(h.cfg.TLSLetsEncryptListen, certManager.HTTPHandler(http.HandlerFunc(h.redirect)))). | ||||
| 					Msg("failed to set up a HTTP server") | ||||
| 			}() | ||||
|  | ||||
							
								
								
									
										183
									
								
								cmd/headscale/cli/api_key.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								cmd/headscale/cli/api_key.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,183 @@ | ||||
| package cli | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/juanfont/headscale" | ||||
| 	v1 "github.com/juanfont/headscale/gen/go/headscale/v1" | ||||
| 	"github.com/pterm/pterm" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"google.golang.org/protobuf/types/known/timestamppb" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	// 90 days.
 | ||||
| 	DefaultAPIKeyExpiry = 90 * 24 * time.Hour | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	rootCmd.AddCommand(apiKeysCmd) | ||||
| 	apiKeysCmd.AddCommand(listAPIKeys) | ||||
| 
 | ||||
| 	createAPIKeyCmd.Flags(). | ||||
| 		DurationP("expiration", "e", DefaultAPIKeyExpiry, "Human-readable expiration of the key (30m, 24h, 365d...)") | ||||
| 
 | ||||
| 	apiKeysCmd.AddCommand(createAPIKeyCmd) | ||||
| 
 | ||||
| 	expireAPIKeyCmd.Flags().StringP("prefix", "p", "", "ApiKey prefix") | ||||
| 	err := expireAPIKeyCmd.MarkFlagRequired("prefix") | ||||
| 	if err != nil { | ||||
| 		log.Fatal().Err(err).Msg("") | ||||
| 	} | ||||
| 	apiKeysCmd.AddCommand(expireAPIKeyCmd) | ||||
| } | ||||
| 
 | ||||
| var apiKeysCmd = &cobra.Command{ | ||||
| 	Use:   "apikeys", | ||||
| 	Short: "Handle the Api keys in Headscale", | ||||
| } | ||||
| 
 | ||||
| var listAPIKeys = &cobra.Command{ | ||||
| 	Use:   "list", | ||||
| 	Short: "List the Api keys for headscale", | ||||
| 	Run: func(cmd *cobra.Command, args []string) { | ||||
| 		output, _ := cmd.Flags().GetString("output") | ||||
| 
 | ||||
| 		ctx, client, conn, cancel := getHeadscaleCLIClient() | ||||
| 		defer cancel() | ||||
| 		defer conn.Close() | ||||
| 
 | ||||
| 		request := &v1.ListApiKeysRequest{} | ||||
| 
 | ||||
| 		response, err := client.ListApiKeys(ctx, request) | ||||
| 		if err != nil { | ||||
| 			ErrorOutput( | ||||
| 				err, | ||||
| 				fmt.Sprintf("Error getting the list of keys: %s", err), | ||||
| 				output, | ||||
| 			) | ||||
| 
 | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		if output != "" { | ||||
| 			SuccessOutput(response.ApiKeys, "", output) | ||||
| 
 | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		tableData := pterm.TableData{ | ||||
| 			{"ID", "Prefix", "Expiration", "Created"}, | ||||
| 		} | ||||
| 		for _, key := range response.ApiKeys { | ||||
| 			expiration := "-" | ||||
| 
 | ||||
| 			if key.GetExpiration() != nil { | ||||
| 				expiration = ColourTime(key.Expiration.AsTime()) | ||||
| 			} | ||||
| 
 | ||||
| 			tableData = append(tableData, []string{ | ||||
| 				strconv.FormatUint(key.GetId(), headscale.Base10), | ||||
| 				key.GetPrefix(), | ||||
| 				expiration, | ||||
| 				key.GetCreatedAt().AsTime().Format(HeadscaleDateTimeFormat), | ||||
| 			}) | ||||
| 
 | ||||
| 		} | ||||
| 		err = pterm.DefaultTable.WithHasHeader().WithData(tableData).Render() | ||||
| 		if err != nil { | ||||
| 			ErrorOutput( | ||||
| 				err, | ||||
| 				fmt.Sprintf("Failed to render pterm table: %s", err), | ||||
| 				output, | ||||
| 			) | ||||
| 
 | ||||
| 			return | ||||
| 		} | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| var createAPIKeyCmd = &cobra.Command{ | ||||
| 	Use:   "create", | ||||
| 	Short: "Creates a new Api key", | ||||
| 	Long: ` | ||||
| Creates a new Api key, the Api key is only visible on creation | ||||
| and cannot be retrieved again. | ||||
| If you loose a key, create a new one and revoke (expire) the old one.`, | ||||
| 	Run: func(cmd *cobra.Command, args []string) { | ||||
| 		output, _ := cmd.Flags().GetString("output") | ||||
| 
 | ||||
| 		log.Trace(). | ||||
| 			Msg("Preparing to create ApiKey") | ||||
| 
 | ||||
| 		request := &v1.CreateApiKeyRequest{} | ||||
| 
 | ||||
| 		duration, _ := cmd.Flags().GetDuration("expiration") | ||||
| 		expiration := time.Now().UTC().Add(duration) | ||||
| 
 | ||||
| 		log.Trace().Dur("expiration", duration).Msg("expiration has been set") | ||||
| 
 | ||||
| 		request.Expiration = timestamppb.New(expiration) | ||||
| 
 | ||||
| 		ctx, client, conn, cancel := getHeadscaleCLIClient() | ||||
| 		defer cancel() | ||||
| 		defer conn.Close() | ||||
| 
 | ||||
| 		response, err := client.CreateApiKey(ctx, request) | ||||
| 		if err != nil { | ||||
| 			ErrorOutput( | ||||
| 				err, | ||||
| 				fmt.Sprintf("Cannot create Api Key: %s\n", err), | ||||
| 				output, | ||||
| 			) | ||||
| 
 | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		SuccessOutput(response.ApiKey, response.ApiKey, output) | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| var expireAPIKeyCmd = &cobra.Command{ | ||||
| 	Use:     "expire", | ||||
| 	Short:   "Expire an ApiKey", | ||||
| 	Aliases: []string{"revoke"}, | ||||
| 	Run: func(cmd *cobra.Command, args []string) { | ||||
| 		output, _ := cmd.Flags().GetString("output") | ||||
| 
 | ||||
| 		prefix, err := cmd.Flags().GetString("prefix") | ||||
| 		if err != nil { | ||||
| 			ErrorOutput( | ||||
| 				err, | ||||
| 				fmt.Sprintf("Error getting prefix from CLI flag: %s", err), | ||||
| 				output, | ||||
| 			) | ||||
| 
 | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		ctx, client, conn, cancel := getHeadscaleCLIClient() | ||||
| 		defer cancel() | ||||
| 		defer conn.Close() | ||||
| 
 | ||||
| 		request := &v1.ExpireApiKeyRequest{ | ||||
| 			Prefix: prefix, | ||||
| 		} | ||||
| 
 | ||||
| 		response, err := client.ExpireApiKey(ctx, request) | ||||
| 		if err != nil { | ||||
| 			ErrorOutput( | ||||
| 				err, | ||||
| 				fmt.Sprintf("Cannot expire Api Key: %s\n", err), | ||||
| 				output, | ||||
| 			) | ||||
| 
 | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		SuccessOutput(response, "Key expired", output) | ||||
| 	}, | ||||
| } | ||||
| @ -83,7 +83,7 @@ var listPreAuthKeys = &cobra.Command{ | ||||
| 		for _, key := range response.PreAuthKeys { | ||||
| 			expiration := "-" | ||||
| 			if key.GetExpiration() != nil { | ||||
| 				expiration = key.Expiration.AsTime().Format("2006-01-02 15:04:05") | ||||
| 				expiration = ColourTime(key.Expiration.AsTime()) | ||||
| 			} | ||||
| 
 | ||||
| 			var reusable string | ||||
|  | ||||
							
								
								
									
										19
									
								
								cmd/headscale/cli/pterm_style.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								cmd/headscale/cli/pterm_style.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| package cli | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/pterm/pterm" | ||||
| ) | ||||
| 
 | ||||
| func ColourTime(date time.Time) string { | ||||
| 	dateStr := date.Format("2006-01-02 15:04:05") | ||||
| 
 | ||||
| 	if date.After(time.Now()) { | ||||
| 		dateStr = pterm.LightGreen(dateStr) | ||||
| 	} else { | ||||
| 		dateStr = pterm.LightRed(dateStr) | ||||
| 	} | ||||
| 
 | ||||
| 	return dateStr | ||||
| } | ||||
| @ -2,6 +2,7 @@ package cli | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/tls" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| @ -19,6 +20,8 @@ import ( | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"google.golang.org/grpc" | ||||
| 	"google.golang.org/grpc/credentials" | ||||
| 	"google.golang.org/grpc/credentials/insecure" | ||||
| 	"gopkg.in/yaml.v2" | ||||
| 	"inet.af/netaddr" | ||||
| 	"tailscale.com/tailcfg" | ||||
| @ -26,7 +29,8 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	PermissionFallback = 0o700 | ||||
| 	PermissionFallback      = 0o700 | ||||
| 	HeadscaleDateTimeFormat = "2006-01-02 15:04:05" | ||||
| ) | ||||
| 
 | ||||
| func LoadConfig(path string) error { | ||||
| @ -54,8 +58,11 @@ func LoadConfig(path string) error { | ||||
| 	viper.SetDefault("unix_socket", "/var/run/headscale.sock") | ||||
| 	viper.SetDefault("unix_socket_permission", "0o770") | ||||
| 
 | ||||
| 	viper.SetDefault("cli.insecure", false) | ||||
| 	viper.SetDefault("grpc_listen_addr", ":50443") | ||||
| 	viper.SetDefault("grpc_allow_insecure", false) | ||||
| 
 | ||||
| 	viper.SetDefault("cli.timeout", "5s") | ||||
| 	viper.SetDefault("cli.insecure", false) | ||||
| 
 | ||||
| 	if err := viper.ReadInConfig(); err != nil { | ||||
| 		return fmt.Errorf("fatal error reading config file: %w", err) | ||||
| @ -270,12 +277,16 @@ func getHeadscaleConfig() headscale.Config { | ||||
| 
 | ||||
| 	if len(prefixes) < 1 { | ||||
| 		prefixes = append(prefixes, netaddr.MustParseIPPrefix("100.64.0.0/10")) | ||||
| 		log.Warn().Msgf("'ip_prefixes' not configured, falling back to default: %v", prefixes) | ||||
| 		log.Warn(). | ||||
| 			Msgf("'ip_prefixes' not configured, falling back to default: %v", prefixes) | ||||
| 	} | ||||
| 
 | ||||
| 	return headscale.Config{ | ||||
| 		ServerURL:      viper.GetString("server_url"), | ||||
| 		Addr:           viper.GetString("listen_addr"), | ||||
| 		ServerURL:         viper.GetString("server_url"), | ||||
| 		Addr:              viper.GetString("listen_addr"), | ||||
| 		GRPCAddr:          viper.GetString("grpc_listen_addr"), | ||||
| 		GRPCAllowInsecure: viper.GetBool("grpc_allow_insecure"), | ||||
| 
 | ||||
| 		IPPrefixes:     prefixes, | ||||
| 		PrivateKeyPath: absPath(viper.GetString("private_key_path")), | ||||
| 		BaseDomain:     baseDomain, | ||||
| @ -321,8 +332,8 @@ func getHeadscaleConfig() headscale.Config { | ||||
| 		CLI: headscale.CLIConfig{ | ||||
| 			Address:  viper.GetString("cli.address"), | ||||
| 			APIKey:   viper.GetString("cli.api_key"), | ||||
| 			Insecure: viper.GetBool("cli.insecure"), | ||||
| 			Timeout:  viper.GetDuration("cli.timeout"), | ||||
| 			Insecure: viper.GetBool("cli.insecure"), | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
| @ -393,14 +404,14 @@ func getHeadscaleCLIClient() (context.Context, v1.HeadscaleServiceClient, *grpc. | ||||
| 
 | ||||
| 		grpcOptions = append( | ||||
| 			grpcOptions, | ||||
| 			grpc.WithInsecure(), | ||||
| 			grpc.WithTransportCredentials(insecure.NewCredentials()), | ||||
| 			grpc.WithContextDialer(headscale.GrpcSocketDialer), | ||||
| 		) | ||||
| 	} else { | ||||
| 		// If we are not connecting to a local server, require an API key for authentication
 | ||||
| 		apiKey := cfg.CLI.APIKey | ||||
| 		if apiKey == "" { | ||||
| 			log.Fatal().Msgf("HEADSCALE_CLI_API_KEY environment variable needs to be set.") | ||||
| 			log.Fatal().Caller().Msgf("HEADSCALE_CLI_API_KEY environment variable needs to be set.") | ||||
| 		} | ||||
| 		grpcOptions = append(grpcOptions, | ||||
| 			grpc.WithPerRPCCredentials(tokenAuth{ | ||||
| @ -409,14 +420,27 @@ func getHeadscaleCLIClient() (context.Context, v1.HeadscaleServiceClient, *grpc. | ||||
| 		) | ||||
| 
 | ||||
| 		if cfg.CLI.Insecure { | ||||
| 			grpcOptions = append(grpcOptions, grpc.WithInsecure()) | ||||
| 			tlsConfig := &tls.Config{ | ||||
| 				// turn of gosec as we are intentionally setting
 | ||||
| 				// insecure.
 | ||||
| 				//nolint:gosec
 | ||||
| 				InsecureSkipVerify: true, | ||||
| 			} | ||||
| 
 | ||||
| 			grpcOptions = append(grpcOptions, | ||||
| 				grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), | ||||
| 			) | ||||
| 		} else { | ||||
| 			grpcOptions = append(grpcOptions, | ||||
| 				grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")), | ||||
| 			) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	log.Trace().Caller().Str("address", address).Msg("Connecting via gRPC") | ||||
| 	conn, err := grpc.DialContext(ctx, address, grpcOptions...) | ||||
| 	if err != nil { | ||||
| 		log.Fatal().Err(err).Msgf("Could not connect: %v", err) | ||||
| 		log.Fatal().Caller().Err(err).Msgf("Could not connect: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	client := v1.NewHeadscaleServiceClient(conn) | ||||
| @ -425,21 +449,21 @@ func getHeadscaleCLIClient() (context.Context, v1.HeadscaleServiceClient, *grpc. | ||||
| } | ||||
| 
 | ||||
| func SuccessOutput(result interface{}, override string, outputFormat string) { | ||||
| 	var j []byte | ||||
| 	var jsonBytes []byte | ||||
| 	var err error | ||||
| 	switch outputFormat { | ||||
| 	case "json": | ||||
| 		j, err = json.MarshalIndent(result, "", "\t") | ||||
| 		jsonBytes, err = json.MarshalIndent(result, "", "\t") | ||||
| 		if err != nil { | ||||
| 			log.Fatal().Err(err) | ||||
| 		} | ||||
| 	case "json-line": | ||||
| 		j, err = json.Marshal(result) | ||||
| 		jsonBytes, err = json.Marshal(result) | ||||
| 		if err != nil { | ||||
| 			log.Fatal().Err(err) | ||||
| 		} | ||||
| 	case "yaml": | ||||
| 		j, err = yaml.Marshal(result) | ||||
| 		jsonBytes, err = yaml.Marshal(result) | ||||
| 		if err != nil { | ||||
| 			log.Fatal().Err(err) | ||||
| 		} | ||||
| @ -451,7 +475,7 @@ func SuccessOutput(result interface{}, override string, outputFormat string) { | ||||
| 	} | ||||
| 
 | ||||
| 	//nolint
 | ||||
| 	fmt.Println(string(j)) | ||||
| 	fmt.Println(string(jsonBytes)) | ||||
| } | ||||
| 
 | ||||
| func ErrorOutput(errResult error, override string, outputFormat string) { | ||||
|  | ||||
| @ -44,7 +44,7 @@ func main() { | ||||
| 	}) | ||||
| 
 | ||||
| 	if err := cli.LoadConfig(""); err != nil { | ||||
| 		log.Fatal().Err(err) | ||||
| 		log.Fatal().Caller().Err(err) | ||||
| 	} | ||||
| 
 | ||||
| 	machineOutput := cli.HasMachineOutputFlag() | ||||
|  | ||||
| @ -61,7 +61,11 @@ func (*Suite) TestConfigLoading(c *check.C) { | ||||
| 	c.Assert(viper.GetString("tls_letsencrypt_listen"), check.Equals, ":http") | ||||
| 	c.Assert(viper.GetString("tls_letsencrypt_challenge_type"), check.Equals, "HTTP-01") | ||||
| 	c.Assert(viper.GetStringSlice("dns_config.nameservers")[0], check.Equals, "1.1.1.1") | ||||
| 	c.Assert(cli.GetFileMode("unix_socket_permission"), check.Equals, fs.FileMode(0o770)) | ||||
| 	c.Assert( | ||||
| 		cli.GetFileMode("unix_socket_permission"), | ||||
| 		check.Equals, | ||||
| 		fs.FileMode(0o770), | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| func (*Suite) TestDNSConfigLoading(c *check.C) { | ||||
|  | ||||
| @ -16,6 +16,19 @@ server_url: http://127.0.0.1:8080 | ||||
| # | ||||
| listen_addr: 0.0.0.0:8080 | ||||
| 
 | ||||
| # Address to listen for gRPC. | ||||
| # gRPC is used for controlling a headscale server | ||||
| # remotely with the CLI | ||||
| # Note: Remote access _only_ works if you have | ||||
| # valid certificates. | ||||
| grpc_listen_addr: 0.0.0.0:50443 | ||||
| 
 | ||||
| # Allow the gRPC admin interface to run in INSECURE | ||||
| # mode. This is not recommended as the traffic will | ||||
| # be unencrypted. Only enable if you know what you | ||||
| # are doing. | ||||
| grpc_allow_insecure: false | ||||
| 
 | ||||
| # Private key used encrypt the traffic between headscale | ||||
| # and Tailscale clients. | ||||
| # The private key file which will be | ||||
|  | ||||
							
								
								
									
										5
									
								
								db.go
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								db.go
									
									
									
									
									
								
							| @ -58,6 +58,11 @@ func (h *Headscale) initDB() error { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	err = db.AutoMigrate(&APIKey{}) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	err = h.setValue("db_version", dbVersion) | ||||
| 
 | ||||
| 	return err | ||||
|  | ||||
							
								
								
									
										11
									
								
								dns.go
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								dns.go
									
									
									
									
									
								
							| @ -51,7 +51,12 @@ func generateMagicDNSRootDomains(ipPrefixes []netaddr.IPPrefix) []dnsname.FQDN { | ||||
| 			generateDNSRoot = generateIPv6DNSRootDomain | ||||
| 
 | ||||
| 		default: | ||||
| 			panic(fmt.Sprintf("unsupported IP version with address length %d", ipPrefix.IP().BitLen())) | ||||
| 			panic( | ||||
| 				fmt.Sprintf( | ||||
| 					"unsupported IP version with address length %d", | ||||
| 					ipPrefix.IP().BitLen(), | ||||
| 				), | ||||
| 			) | ||||
| 		} | ||||
| 
 | ||||
| 		fqdns = append(fqdns, generateDNSRoot(ipPrefix)...) | ||||
| @ -115,7 +120,9 @@ func generateIPv6DNSRootDomain(ipPrefix netaddr.IPPrefix) []dnsname.FQDN { | ||||
| 	// function is called only once over the lifetime of a server process.
 | ||||
| 	prefixConstantParts := []string{} | ||||
| 	for i := 0; i < maskBits/nibbleLen; i++ { | ||||
| 		prefixConstantParts = append([]string{string(nibbleStr[i])}, prefixConstantParts...) | ||||
| 		prefixConstantParts = append( | ||||
| 			[]string{string(nibbleStr[i])}, | ||||
| 			prefixConstantParts...) | ||||
| 	} | ||||
| 
 | ||||
| 	makeDomain := func(variablePrefix ...string) (dnsname.FQDN, error) { | ||||
|  | ||||
| @ -81,7 +81,11 @@ func (s *Suite) TestMagicDNSRootDomainsIPv6Single(c *check.C) { | ||||
| 	domains := generateMagicDNSRootDomains(prefixes) | ||||
| 
 | ||||
| 	c.Assert(len(domains), check.Equals, 1) | ||||
| 	c.Assert(domains[0].WithTrailingDot(), check.Equals, "0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa.") | ||||
| 	c.Assert( | ||||
| 		domains[0].WithTrailingDot(), | ||||
| 		check.Equals, | ||||
| 		"0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa.", | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| func (s *Suite) TestMagicDNSRootDomainsIPv6SingleMultiple(c *check.C) { | ||||
|  | ||||
| @ -10,6 +10,7 @@ please ask on [Discord](https://discord.gg/XcQxk2VHjx) instead of opening an Iss | ||||
| ### How-to | ||||
| 
 | ||||
| - [Running headscale on Linux](running-headscale-linux.md) | ||||
| - [Control headscale remotly](remote-cli.md) | ||||
| - [Using a Windows client with headscale](windows-client.md) | ||||
| 
 | ||||
| ### References | ||||
|  | ||||
							
								
								
									
										100
									
								
								docs/remote-cli.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								docs/remote-cli.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,100 @@ | ||||
| # Controlling `headscale` with remote CLI | ||||
| 
 | ||||
| ## Prerequisit | ||||
| 
 | ||||
| - A workstation to run `headscale` (could be Linux, macOS, other supported platforms) | ||||
| - A `headscale` server (version `0.13.0` or newer) | ||||
| - Access to create API keys (local access to the `headscale` server) | ||||
| - `headscale` _must_ be served over TLS/HTTPS | ||||
|   - Remote access does _not_ support unencrypted traffic. | ||||
| - Port `50443` must be open in the firewall (or port overriden by `grpc_listen_addr` option) | ||||
| 
 | ||||
| ## Goal | ||||
| 
 | ||||
| This documentation has the goal of showing a user how-to set control a `headscale` instance | ||||
| from a remote machine with the `headscale` command line binary. | ||||
| 
 | ||||
| ## Create an API key | ||||
| 
 | ||||
| We need to create an API key to authenticate our remote `headscale` when using it from our workstation. | ||||
| 
 | ||||
| To create a API key, log into your `headscale` server and generate a key: | ||||
| 
 | ||||
| ```shell | ||||
| headscale apikeys create --expiration 90d | ||||
| ``` | ||||
| 
 | ||||
| Copy the output of the command and save it for later. Please not that you can not retrieve a key again, | ||||
| if the key is lost, expire the old one, and create a new key. | ||||
| 
 | ||||
| To list the keys currently assosicated with the server: | ||||
| 
 | ||||
| ```shell | ||||
| headscale apikeys list | ||||
| ``` | ||||
| 
 | ||||
| and to expire a key: | ||||
| 
 | ||||
| ```shell | ||||
| headscale apikeys expire --prefix "<PREFIX>" | ||||
| ``` | ||||
| 
 | ||||
| ## Download and configure `headscale` | ||||
| 
 | ||||
| 1. Download the latest [`headscale` binary from GitHub's release page](https://github.com/juanfont/headscale/releases): | ||||
| 
 | ||||
| 2. Put the binary somewhere in your `PATH`, e.g. `/usr/local/bin/headcale` | ||||
| 
 | ||||
| 3. Make `headscale` executable: | ||||
| 
 | ||||
| ```shell | ||||
| chmod +x /usr/local/bin/headscale | ||||
| ``` | ||||
| 
 | ||||
| 4. Configure the CLI through Environment Variables | ||||
| 
 | ||||
| ```shell | ||||
| export HEADSCALE_CLI_ADDRESS="<HEADSCALE ADDRESS>:<PORT>" | ||||
| export HEADSCALE_CLI_API_KEY="<API KEY FROM PREVIOUS STAGE>" | ||||
| ``` | ||||
| 
 | ||||
| for example: | ||||
| 
 | ||||
| ```shell | ||||
| export HEADSCALE_CLI_ADDRESS="headscale.example.com:50443" | ||||
| export HEADSCALE_CLI_API_KEY="abcde12345" | ||||
| ``` | ||||
| 
 | ||||
| This will tell the `headscale` binary to connect to a remote instance, instead of looking | ||||
| for a local instance (which is what it does on the server). | ||||
| 
 | ||||
| The API key is needed to make sure that your are allowed to access the server. The key is _not_ | ||||
| needed when running directly on the server, as the connection is local. | ||||
| 
 | ||||
| 5. Test the connection | ||||
| 
 | ||||
| Let us run the headscale command to verify that we can connect by listing our nodes: | ||||
| 
 | ||||
| ```shell | ||||
| headscale nodes list | ||||
| ``` | ||||
| 
 | ||||
| You should now be able to see a list of your nodes from your workstation, and you can | ||||
| now control the `headscale` server from your workstation. | ||||
| 
 | ||||
| ## Behind a proxy | ||||
| 
 | ||||
| It is possible to run the gRPC remote endpoint behind a reverse proxy, like Nginx, and have it run on the _same_ port as `headscale`. | ||||
| 
 | ||||
| While this is _not a supported_ feature, an example on how this can be set up on | ||||
| [NixOS is shown here](https://github.com/kradalby/dotfiles/blob/4489cdbb19cddfbfae82cd70448a38fde5a76711/machines/headscale.oracldn/headscale.nix#L61-L91). | ||||
| 
 | ||||
| ## Troubleshooting | ||||
| 
 | ||||
| Checklist: | ||||
| 
 | ||||
| - Make sure you have the _same_ `headscale` version on your server and workstation | ||||
| - Make sure you use version `0.13.0` or newer. | ||||
| - Verify that your TLS certificate is valid and trusted | ||||
|   - If you do not have access to a trusted certificate (e.g. from Let's Encrypt), add your self signed certificate to the trust store of your OS or | ||||
|   - Set `HEADSCALE_CLI_INSECURE` to 0 in your environement | ||||
							
								
								
									
										559
									
								
								gen/go/headscale/v1/apikey.pb.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										559
									
								
								gen/go/headscale/v1/apikey.pb.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,559 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/apikey.proto
 | ||||
| 
 | ||||
| package v1 | ||||
| 
 | ||||
| import ( | ||||
| 	reflect "reflect" | ||||
| 	sync "sync" | ||||
| 
 | ||||
| 	protoreflect "google.golang.org/protobuf/reflect/protoreflect" | ||||
| 	protoimpl "google.golang.org/protobuf/runtime/protoimpl" | ||||
| 	timestamppb "google.golang.org/protobuf/types/known/timestamppb" | ||||
| ) | ||||
| 
 | ||||
| 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 ApiKey struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| 	unknownFields protoimpl.UnknownFields | ||||
| 
 | ||||
| 	Id         uint64                 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` | ||||
| 	Prefix     string                 `protobuf:"bytes,2,opt,name=prefix,proto3" json:"prefix,omitempty"` | ||||
| 	Expiration *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=expiration,proto3" json:"expiration,omitempty"` | ||||
| 	CreatedAt  *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` | ||||
| 	LastSeen   *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (x *ApiKey) Reset() { | ||||
| 	*x = ApiKey{} | ||||
| 	if protoimpl.UnsafeEnabled { | ||||
| 		mi := &file_headscale_v1_apikey_proto_msgTypes[0] | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		ms.StoreMessageInfo(mi) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (x *ApiKey) String() string { | ||||
| 	return protoimpl.X.MessageStringOf(x) | ||||
| } | ||||
| 
 | ||||
| func (*ApiKey) ProtoMessage() {} | ||||
| 
 | ||||
| func (x *ApiKey) ProtoReflect() protoreflect.Message { | ||||
| 	mi := &file_headscale_v1_apikey_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 ApiKey.ProtoReflect.Descriptor instead.
 | ||||
| func (*ApiKey) Descriptor() ([]byte, []int) { | ||||
| 	return file_headscale_v1_apikey_proto_rawDescGZIP(), []int{0} | ||||
| } | ||||
| 
 | ||||
| func (x *ApiKey) GetId() uint64 { | ||||
| 	if x != nil { | ||||
| 		return x.Id | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| func (x *ApiKey) GetPrefix() string { | ||||
| 	if x != nil { | ||||
| 		return x.Prefix | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| func (x *ApiKey) GetExpiration() *timestamppb.Timestamp { | ||||
| 	if x != nil { | ||||
| 		return x.Expiration | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (x *ApiKey) GetCreatedAt() *timestamppb.Timestamp { | ||||
| 	if x != nil { | ||||
| 		return x.CreatedAt | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (x *ApiKey) GetLastSeen() *timestamppb.Timestamp { | ||||
| 	if x != nil { | ||||
| 		return x.LastSeen | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| type CreateApiKeyRequest struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| 	unknownFields protoimpl.UnknownFields | ||||
| 
 | ||||
| 	Expiration *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=expiration,proto3" json:"expiration,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (x *CreateApiKeyRequest) Reset() { | ||||
| 	*x = CreateApiKeyRequest{} | ||||
| 	if protoimpl.UnsafeEnabled { | ||||
| 		mi := &file_headscale_v1_apikey_proto_msgTypes[1] | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		ms.StoreMessageInfo(mi) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (x *CreateApiKeyRequest) String() string { | ||||
| 	return protoimpl.X.MessageStringOf(x) | ||||
| } | ||||
| 
 | ||||
| func (*CreateApiKeyRequest) ProtoMessage() {} | ||||
| 
 | ||||
| func (x *CreateApiKeyRequest) ProtoReflect() protoreflect.Message { | ||||
| 	mi := &file_headscale_v1_apikey_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 CreateApiKeyRequest.ProtoReflect.Descriptor instead.
 | ||||
| func (*CreateApiKeyRequest) Descriptor() ([]byte, []int) { | ||||
| 	return file_headscale_v1_apikey_proto_rawDescGZIP(), []int{1} | ||||
| } | ||||
| 
 | ||||
| func (x *CreateApiKeyRequest) GetExpiration() *timestamppb.Timestamp { | ||||
| 	if x != nil { | ||||
| 		return x.Expiration | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| type CreateApiKeyResponse struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| 	unknownFields protoimpl.UnknownFields | ||||
| 
 | ||||
| 	ApiKey string `protobuf:"bytes,1,opt,name=api_key,json=apiKey,proto3" json:"api_key,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (x *CreateApiKeyResponse) Reset() { | ||||
| 	*x = CreateApiKeyResponse{} | ||||
| 	if protoimpl.UnsafeEnabled { | ||||
| 		mi := &file_headscale_v1_apikey_proto_msgTypes[2] | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		ms.StoreMessageInfo(mi) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (x *CreateApiKeyResponse) String() string { | ||||
| 	return protoimpl.X.MessageStringOf(x) | ||||
| } | ||||
| 
 | ||||
| func (*CreateApiKeyResponse) ProtoMessage() {} | ||||
| 
 | ||||
| func (x *CreateApiKeyResponse) ProtoReflect() protoreflect.Message { | ||||
| 	mi := &file_headscale_v1_apikey_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 CreateApiKeyResponse.ProtoReflect.Descriptor instead.
 | ||||
| func (*CreateApiKeyResponse) Descriptor() ([]byte, []int) { | ||||
| 	return file_headscale_v1_apikey_proto_rawDescGZIP(), []int{2} | ||||
| } | ||||
| 
 | ||||
| func (x *CreateApiKeyResponse) GetApiKey() string { | ||||
| 	if x != nil { | ||||
| 		return x.ApiKey | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| type ExpireApiKeyRequest struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| 	unknownFields protoimpl.UnknownFields | ||||
| 
 | ||||
| 	Prefix string `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (x *ExpireApiKeyRequest) Reset() { | ||||
| 	*x = ExpireApiKeyRequest{} | ||||
| 	if protoimpl.UnsafeEnabled { | ||||
| 		mi := &file_headscale_v1_apikey_proto_msgTypes[3] | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		ms.StoreMessageInfo(mi) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (x *ExpireApiKeyRequest) String() string { | ||||
| 	return protoimpl.X.MessageStringOf(x) | ||||
| } | ||||
| 
 | ||||
| func (*ExpireApiKeyRequest) ProtoMessage() {} | ||||
| 
 | ||||
| func (x *ExpireApiKeyRequest) ProtoReflect() protoreflect.Message { | ||||
| 	mi := &file_headscale_v1_apikey_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 ExpireApiKeyRequest.ProtoReflect.Descriptor instead.
 | ||||
| func (*ExpireApiKeyRequest) Descriptor() ([]byte, []int) { | ||||
| 	return file_headscale_v1_apikey_proto_rawDescGZIP(), []int{3} | ||||
| } | ||||
| 
 | ||||
| func (x *ExpireApiKeyRequest) GetPrefix() string { | ||||
| 	if x != nil { | ||||
| 		return x.Prefix | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
| 
 | ||||
| type ExpireApiKeyResponse struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| 	unknownFields protoimpl.UnknownFields | ||||
| } | ||||
| 
 | ||||
| func (x *ExpireApiKeyResponse) Reset() { | ||||
| 	*x = ExpireApiKeyResponse{} | ||||
| 	if protoimpl.UnsafeEnabled { | ||||
| 		mi := &file_headscale_v1_apikey_proto_msgTypes[4] | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		ms.StoreMessageInfo(mi) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (x *ExpireApiKeyResponse) String() string { | ||||
| 	return protoimpl.X.MessageStringOf(x) | ||||
| } | ||||
| 
 | ||||
| func (*ExpireApiKeyResponse) ProtoMessage() {} | ||||
| 
 | ||||
| func (x *ExpireApiKeyResponse) ProtoReflect() protoreflect.Message { | ||||
| 	mi := &file_headscale_v1_apikey_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 ExpireApiKeyResponse.ProtoReflect.Descriptor instead.
 | ||||
| func (*ExpireApiKeyResponse) Descriptor() ([]byte, []int) { | ||||
| 	return file_headscale_v1_apikey_proto_rawDescGZIP(), []int{4} | ||||
| } | ||||
| 
 | ||||
| type ListApiKeysRequest struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| 	unknownFields protoimpl.UnknownFields | ||||
| } | ||||
| 
 | ||||
| func (x *ListApiKeysRequest) Reset() { | ||||
| 	*x = ListApiKeysRequest{} | ||||
| 	if protoimpl.UnsafeEnabled { | ||||
| 		mi := &file_headscale_v1_apikey_proto_msgTypes[5] | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		ms.StoreMessageInfo(mi) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (x *ListApiKeysRequest) String() string { | ||||
| 	return protoimpl.X.MessageStringOf(x) | ||||
| } | ||||
| 
 | ||||
| func (*ListApiKeysRequest) ProtoMessage() {} | ||||
| 
 | ||||
| func (x *ListApiKeysRequest) ProtoReflect() protoreflect.Message { | ||||
| 	mi := &file_headscale_v1_apikey_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 ListApiKeysRequest.ProtoReflect.Descriptor instead.
 | ||||
| func (*ListApiKeysRequest) Descriptor() ([]byte, []int) { | ||||
| 	return file_headscale_v1_apikey_proto_rawDescGZIP(), []int{5} | ||||
| } | ||||
| 
 | ||||
| type ListApiKeysResponse struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| 	unknownFields protoimpl.UnknownFields | ||||
| 
 | ||||
| 	ApiKeys []*ApiKey `protobuf:"bytes,1,rep,name=api_keys,json=apiKeys,proto3" json:"api_keys,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (x *ListApiKeysResponse) Reset() { | ||||
| 	*x = ListApiKeysResponse{} | ||||
| 	if protoimpl.UnsafeEnabled { | ||||
| 		mi := &file_headscale_v1_apikey_proto_msgTypes[6] | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		ms.StoreMessageInfo(mi) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (x *ListApiKeysResponse) String() string { | ||||
| 	return protoimpl.X.MessageStringOf(x) | ||||
| } | ||||
| 
 | ||||
| func (*ListApiKeysResponse) ProtoMessage() {} | ||||
| 
 | ||||
| func (x *ListApiKeysResponse) ProtoReflect() protoreflect.Message { | ||||
| 	mi := &file_headscale_v1_apikey_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 ListApiKeysResponse.ProtoReflect.Descriptor instead.
 | ||||
| func (*ListApiKeysResponse) Descriptor() ([]byte, []int) { | ||||
| 	return file_headscale_v1_apikey_proto_rawDescGZIP(), []int{6} | ||||
| } | ||||
| 
 | ||||
| func (x *ListApiKeysResponse) GetApiKeys() []*ApiKey { | ||||
| 	if x != nil { | ||||
| 		return x.ApiKeys | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| var File_headscale_v1_apikey_proto protoreflect.FileDescriptor | ||||
| 
 | ||||
| var file_headscale_v1_apikey_proto_rawDesc = []byte{ | ||||
| 	0x0a, 0x19, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x61, | ||||
| 	0x70, 0x69, 0x6b, 0x65, 0x79, 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, 0x22, 0xe0, 0x01, 0x0a, 0x06, 0x41, | ||||
| 	0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, | ||||
| 	0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, | ||||
| 	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x3a, 0x0a, | ||||
| 	0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 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, 0x65, | ||||
| 	0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, | ||||
| 	0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 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, 0x63, 0x72, 0x65, 0x61, 0x74, | ||||
| 	0x65, 0x64, 0x41, 0x74, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, | ||||
| 	0x6e, 0x18, 0x05, 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, 0x22, 0x51, 0x0a, | ||||
| 	0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, | ||||
| 	0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, | ||||
| 	0x6f, 0x6e, 0x18, 0x01, 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, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, | ||||
| 	0x22, 0x2f, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, | ||||
| 	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x61, 0x70, 0x69, 0x5f, | ||||
| 	0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x70, 0x69, 0x4b, 0x65, | ||||
| 	0x79, 0x22, 0x2d, 0x0a, 0x13, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, | ||||
| 	0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, | ||||
| 	0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, | ||||
| 	0x22, 0x16, 0x0a, 0x14, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, | ||||
| 	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, | ||||
| 	0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x46, | ||||
| 	0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, | ||||
| 	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, | ||||
| 	0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, | ||||
| 	0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x07, 0x61, | ||||
| 	0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 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_apikey_proto_rawDescOnce sync.Once | ||||
| 	file_headscale_v1_apikey_proto_rawDescData = file_headscale_v1_apikey_proto_rawDesc | ||||
| ) | ||||
| 
 | ||||
| func file_headscale_v1_apikey_proto_rawDescGZIP() []byte { | ||||
| 	file_headscale_v1_apikey_proto_rawDescOnce.Do(func() { | ||||
| 		file_headscale_v1_apikey_proto_rawDescData = protoimpl.X.CompressGZIP(file_headscale_v1_apikey_proto_rawDescData) | ||||
| 	}) | ||||
| 	return file_headscale_v1_apikey_proto_rawDescData | ||||
| } | ||||
| 
 | ||||
| var file_headscale_v1_apikey_proto_msgTypes = make([]protoimpl.MessageInfo, 7) | ||||
| var file_headscale_v1_apikey_proto_goTypes = []interface{}{ | ||||
| 	(*ApiKey)(nil),                // 0: headscale.v1.ApiKey
 | ||||
| 	(*CreateApiKeyRequest)(nil),   // 1: headscale.v1.CreateApiKeyRequest
 | ||||
| 	(*CreateApiKeyResponse)(nil),  // 2: headscale.v1.CreateApiKeyResponse
 | ||||
| 	(*ExpireApiKeyRequest)(nil),   // 3: headscale.v1.ExpireApiKeyRequest
 | ||||
| 	(*ExpireApiKeyResponse)(nil),  // 4: headscale.v1.ExpireApiKeyResponse
 | ||||
| 	(*ListApiKeysRequest)(nil),    // 5: headscale.v1.ListApiKeysRequest
 | ||||
| 	(*ListApiKeysResponse)(nil),   // 6: headscale.v1.ListApiKeysResponse
 | ||||
| 	(*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp
 | ||||
| } | ||||
| var file_headscale_v1_apikey_proto_depIdxs = []int32{ | ||||
| 	7, // 0: headscale.v1.ApiKey.expiration:type_name -> google.protobuf.Timestamp
 | ||||
| 	7, // 1: headscale.v1.ApiKey.created_at:type_name -> google.protobuf.Timestamp
 | ||||
| 	7, // 2: headscale.v1.ApiKey.last_seen:type_name -> google.protobuf.Timestamp
 | ||||
| 	7, // 3: headscale.v1.CreateApiKeyRequest.expiration:type_name -> google.protobuf.Timestamp
 | ||||
| 	0, // 4: headscale.v1.ListApiKeysResponse.api_keys:type_name -> headscale.v1.ApiKey
 | ||||
| 	5, // [5:5] is the sub-list for method output_type
 | ||||
| 	5, // [5:5] is the sub-list for method input_type
 | ||||
| 	5, // [5:5] is the sub-list for extension type_name
 | ||||
| 	5, // [5:5] is the sub-list for extension extendee
 | ||||
| 	0, // [0:5] is the sub-list for field type_name
 | ||||
| } | ||||
| 
 | ||||
| func init() { file_headscale_v1_apikey_proto_init() } | ||||
| func file_headscale_v1_apikey_proto_init() { | ||||
| 	if File_headscale_v1_apikey_proto != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	if !protoimpl.UnsafeEnabled { | ||||
| 		file_headscale_v1_apikey_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { | ||||
| 			switch v := v.(*ApiKey); i { | ||||
| 			case 0: | ||||
| 				return &v.state | ||||
| 			case 1: | ||||
| 				return &v.sizeCache | ||||
| 			case 2: | ||||
| 				return &v.unknownFields | ||||
| 			default: | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 		file_headscale_v1_apikey_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { | ||||
| 			switch v := v.(*CreateApiKeyRequest); i { | ||||
| 			case 0: | ||||
| 				return &v.state | ||||
| 			case 1: | ||||
| 				return &v.sizeCache | ||||
| 			case 2: | ||||
| 				return &v.unknownFields | ||||
| 			default: | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 		file_headscale_v1_apikey_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { | ||||
| 			switch v := v.(*CreateApiKeyResponse); i { | ||||
| 			case 0: | ||||
| 				return &v.state | ||||
| 			case 1: | ||||
| 				return &v.sizeCache | ||||
| 			case 2: | ||||
| 				return &v.unknownFields | ||||
| 			default: | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 		file_headscale_v1_apikey_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { | ||||
| 			switch v := v.(*ExpireApiKeyRequest); i { | ||||
| 			case 0: | ||||
| 				return &v.state | ||||
| 			case 1: | ||||
| 				return &v.sizeCache | ||||
| 			case 2: | ||||
| 				return &v.unknownFields | ||||
| 			default: | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 		file_headscale_v1_apikey_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { | ||||
| 			switch v := v.(*ExpireApiKeyResponse); i { | ||||
| 			case 0: | ||||
| 				return &v.state | ||||
| 			case 1: | ||||
| 				return &v.sizeCache | ||||
| 			case 2: | ||||
| 				return &v.unknownFields | ||||
| 			default: | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 		file_headscale_v1_apikey_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { | ||||
| 			switch v := v.(*ListApiKeysRequest); i { | ||||
| 			case 0: | ||||
| 				return &v.state | ||||
| 			case 1: | ||||
| 				return &v.sizeCache | ||||
| 			case 2: | ||||
| 				return &v.unknownFields | ||||
| 			default: | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 		file_headscale_v1_apikey_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { | ||||
| 			switch v := v.(*ListApiKeysResponse); 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_apikey_proto_rawDesc, | ||||
| 			NumEnums:      0, | ||||
| 			NumMessages:   7, | ||||
| 			NumExtensions: 0, | ||||
| 			NumServices:   0, | ||||
| 		}, | ||||
| 		GoTypes:           file_headscale_v1_apikey_proto_goTypes, | ||||
| 		DependencyIndexes: file_headscale_v1_apikey_proto_depIdxs, | ||||
| 		MessageInfos:      file_headscale_v1_apikey_proto_msgTypes, | ||||
| 	}.Build() | ||||
| 	File_headscale_v1_apikey_proto = out.File | ||||
| 	file_headscale_v1_apikey_proto_rawDesc = nil | ||||
| 	file_headscale_v1_apikey_proto_goTypes = nil | ||||
| 	file_headscale_v1_apikey_proto_depIdxs = nil | ||||
| } | ||||
| @ -1,17 +1,18 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc        v3.17.3
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/device.proto
 | ||||
| 
 | ||||
| package v1 | ||||
| 
 | ||||
| import ( | ||||
| 	reflect "reflect" | ||||
| 	sync "sync" | ||||
| 
 | ||||
| 	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 ( | ||||
|  | ||||
| @ -1,16 +1,17 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc        v3.17.3
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/headscale.proto
 | ||||
| 
 | ||||
| package v1 | ||||
| 
 | ||||
| import ( | ||||
| 	reflect "reflect" | ||||
| 
 | ||||
| 	_ "google.golang.org/genproto/googleapis/api/annotations" | ||||
| 	protoreflect "google.golang.org/protobuf/reflect/protoreflect" | ||||
| 	protoimpl "google.golang.org/protobuf/runtime/protoimpl" | ||||
| 	reflect "reflect" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| @ -34,162 +35,185 @@ var file_headscale_v1_headscale_proto_rawDesc = []byte{ | ||||
| 	0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, | ||||
| 	0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2e, 0x70, 0x72, | ||||
| 	0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, | ||||
| 	0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xf4, | ||||
| 	0x12, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, | ||||
| 	0x69, 0x63, 0x65, 0x12, 0x77, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, | ||||
| 	0x61, 0x63, 0x65, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, | ||||
| 	0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, | ||||
| 	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, | ||||
| 	0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, | ||||
| 	0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, | ||||
| 	0x02, 0x1a, 0x12, 0x18, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, | ||||
| 	0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 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, 0x96, 0x01, 0x0a, 0x0f, 0x52, | ||||
| 	0x65, 0x6e, 0x61, 0x6d, 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, 0x52, 0x65, | ||||
| 	0x6e, 0x61, 0x6d, 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, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, | ||||
| 	0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, 0xd3, 0xe4, | ||||
| 	0x93, 0x02, 0x30, 0x22, 0x2e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, | ||||
| 	0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6f, 0x6c, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, | ||||
| 	0x7d, 0x2f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x2f, 0x7b, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, | ||||
| 	0x6d, 0x65, 0x7d, 0x12, 0x80, 0x01, 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, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x2a, 0x18, 0x2f, 0x61, | ||||
| 	0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, | ||||
| 	0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 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, 0x12, 0x80, | ||||
| 	0x01, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, | ||||
| 	0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, | ||||
| 	0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, | ||||
| 	0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x68, 0x65, 0x61, | ||||
| 	0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, | ||||
| 	0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, | ||||
| 	0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xcb, 0x15, 0x0a, 0x10, 0x48, 0x65, | ||||
| 	0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x77, | ||||
| 	0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x21, | ||||
| 	0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, | ||||
| 	0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, | ||||
| 	0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, | ||||
| 	0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, | ||||
| 	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, | ||||
| 	0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, | ||||
| 	0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 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, | ||||
| 	0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, | ||||
| 	0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x61, 0x70, 0x69, | ||||
| 	0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b, 0x65, 0x79, 0x3a, 0x01, | ||||
| 	0x2a, 0x12, 0x87, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, | ||||
| 	0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, | ||||
| 	0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, | ||||
| 	0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, | ||||
| 	0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, | ||||
| 	0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, | ||||
| 	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, | ||||
| 	0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b, 0x65, | ||||
| 	0x79, 0x2f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x7a, 0x0a, 0x0f, 0x4c, | ||||
| 	0x69, 0x73, 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x24, | ||||
| 	0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, | ||||
| 	0x73, 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, | ||||
| 	0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, | ||||
| 	0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, | ||||
| 	0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, | ||||
| 	0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, | ||||
| 	0x61, 0x75, 0x74, 0x68, 0x6b, 0x65, 0x79, 0x12, 0x89, 0x01, 0x0a, 0x12, 0x44, 0x65, 0x62, 0x75, | ||||
| 	0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x27, | ||||
| 	0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, | ||||
| 	0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, | ||||
| 	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, | ||||
| 	0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, | ||||
| 	0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, | ||||
| 	0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, | ||||
| 	0x76, 0x31, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, | ||||
| 	0x3a, 0x01, 0x2a, 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, 0x80, 0x01, 0x0a, 0x0f, 0x52, | ||||
| 	0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x24, | ||||
| 	0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, | ||||
| 	0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, | ||||
| 	0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, | ||||
| 	0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, | ||||
| 	0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, | ||||
| 	0x93, 0x02, 0x1a, 0x22, 0x18, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, | ||||
| 	0x68, 0x69, 0x6e, 0x65, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x7e, 0x0a, | ||||
| 	0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x22, | ||||
| 	0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, | ||||
| 	0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, | ||||
| 	0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, | ||||
| 	0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, | ||||
| 	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x2a, | ||||
| 	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, 0x85, 0x01, | ||||
| 	0x0a, 0x0d, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, | ||||
| 	0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, | ||||
| 	0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, | ||||
| 	0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, | ||||
| 	0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, | ||||
| 	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, | ||||
| 	0x22, 0x23, 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, 0x2f, 0x65, | ||||
| 	0x78, 0x70, 0x69, 0x72, 0x65, 0x12, 0x6e, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, | ||||
| 	0x68, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, | ||||
| 	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, | ||||
| 	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, | ||||
| 	0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, | ||||
| 	0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, | ||||
| 	0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, | ||||
| 	0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x8d, 0x01, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, | ||||
| 	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, | ||||
| 	0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, | ||||
| 	0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, | ||||
| 	0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, | ||||
| 	0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, | ||||
| 	0xd3, 0xe4, 0x93, 0x02, 0x30, 0x22, 0x2e, 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, 0x2f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, | ||||
| 	0x70, 0x61, 0x63, 0x65, 0x7d, 0x12, 0x95, 0x01, 0x0a, 0x0e, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, | ||||
| 	0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, | ||||
| 	0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d, | ||||
| 	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, | ||||
| 	0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, | ||||
| 	0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, | ||||
| 	0x6e, 0x73, 0x65, 0x22, 0x38, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22, 0x30, 0x2f, 0x61, 0x70, | ||||
| 	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, 0x96, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x6e, 0x61, 0x6d, 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, 0x52, 0x65, 0x6e, 0x61, 0x6d, 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, 0x52, | ||||
| 	0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, | ||||
| 	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x30, 0x22, 0x2e, | ||||
| 	0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, | ||||
| 	0x65, 0x2f, 0x7b, 0x6f, 0x6c, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x72, 0x65, 0x6e, | ||||
| 	0x61, 0x6d, 0x65, 0x2f, 0x7b, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x80, | ||||
| 	0x01, 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, | ||||
| 	0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x2a, 0x18, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, | ||||
| 	0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, | ||||
| 	0x7d, 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, 0x12, 0x80, 0x01, 0x0a, 0x10, 0x43, 0x72, | ||||
| 	0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x25, | ||||
| 	0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, | ||||
| 	0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, | ||||
| 	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, | ||||
| 	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, | ||||
| 	0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, | ||||
| 	0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, | ||||
| 	0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b, 0x65, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x87, 0x01, 0x0a, | ||||
| 	0x10, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, | ||||
| 	0x79, 0x12, 0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, | ||||
| 	0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, | ||||
| 	0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, | ||||
| 	0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x50, 0x72, | ||||
| 	0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, | ||||
| 	0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x22, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, | ||||
| 	0x31, 0x2f, 0x70, 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b, 0x65, 0x79, 0x2f, 0x65, 0x78, 0x70, | ||||
| 	0x69, 0x72, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x7a, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, | ||||
| 	0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, | ||||
| 	0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x65, | ||||
| 	0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, | ||||
| 	0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, | ||||
| 	0x69, 0x73, 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, | ||||
| 	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, | ||||
| 	0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b, | ||||
| 	0x65, 0x79, 0x12, 0x89, 0x01, 0x0a, 0x12, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, | ||||
| 	0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x27, 0x2e, 0x68, 0x65, 0x61, 0x64, | ||||
| 	0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, | ||||
| 	0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, | ||||
| 	0x73, 0x74, 0x1a, 0x28, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, | ||||
| 	0x31, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, | ||||
| 	0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, | ||||
| 	0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x65, | ||||
| 	0x62, 0x75, 0x67, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x3a, 0x01, 0x2a, 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, 0x80, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, | ||||
| 	0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, | ||||
| 	0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, | ||||
| 	0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, | ||||
| 	0x25, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, | ||||
| 	0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, | ||||
| 	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x18, | ||||
| 	0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, | ||||
| 	0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x7e, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, | ||||
| 	0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, | ||||
| 	0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, | ||||
| 	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, | ||||
| 	0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, | ||||
| 	0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, | ||||
| 	0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x2a, 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, 0x85, 0x01, 0x0a, 0x0d, 0x45, 0x78, 0x70, | ||||
| 	0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x22, 0x2e, 0x68, 0x65, 0x61, | ||||
| 	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, | ||||
| 	0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, | ||||
| 	0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, | ||||
| 	0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, | ||||
| 	0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x22, 0x23, 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, 0x2f, 0x75, 0x6e, 0x73, 0x68, 0x61, 0x72, | ||||
| 	0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x12, 0x8b, 0x01, | ||||
| 	0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, | ||||
| 	0x65, 0x12, 0x24, 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, 0x6f, 0x75, 0x74, 0x65, | ||||
| 	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 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, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, | ||||
| 	0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, | ||||
| 	0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, | ||||
| 	0x12, 0x6e, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, | ||||
| 	0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, | ||||
| 	0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, | ||||
| 	0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, | ||||
| 	0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, | ||||
| 	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, | ||||
| 	0x0f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, | ||||
| 	0x12, 0x8d, 0x01, 0x0a, 0x0c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, | ||||
| 	0x65, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, | ||||
| 	0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, | ||||
| 	0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, | ||||
| 	0x2e, 0x76, 0x31, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, | ||||
| 	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x30, | ||||
| 	0x22, 0x2e, 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, 0x2f, 0x73, | ||||
| 	0x68, 0x61, 0x72, 0x65, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, | ||||
| 	0x12, 0x95, 0x01, 0x0a, 0x0e, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, | ||||
| 	0x69, 0x6e, 0x65, 0x12, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, | ||||
| 	0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, | ||||
| 	0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, | ||||
| 	0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x4d, | ||||
| 	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, | ||||
| 	0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x22, 0x30, 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, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x97, 0x01, 0x0a, 0x13, | ||||
| 	0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, | ||||
| 	0x74, 0x65, 0x73, 0x12, 0x28, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, | ||||
| 	0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, | ||||
| 	0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, | ||||
| 	0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, | ||||
| 	0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, | ||||
| 	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, | ||||
| 	0x22, 0x23, 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, 0x2f, 0x72, | ||||
| 	0x6f, 0x75, 0x74, 0x65, 0x73, 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, | ||||
| 	0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75, 0x6e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x2f, 0x7b, 0x6e, 0x61, | ||||
| 	0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x7d, 0x12, 0x8b, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, | ||||
| 	0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x24, 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, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, | ||||
| 	0x73, 0x74, 0x1a, 0x25, 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, 0x6f, 0x75, 0x74, | ||||
| 	0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, | ||||
| 	0x25, 0x12, 0x23, 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, 0x2f, | ||||
| 	0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x97, 0x01, 0x0a, 0x13, 0x45, 0x6e, 0x61, 0x62, 0x6c, | ||||
| 	0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x28, | ||||
| 	0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, | ||||
| 	0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, | ||||
| 	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, | ||||
| 	0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, | ||||
| 	0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, | ||||
| 	0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x22, 0x23, 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, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, | ||||
| 	0x12, 0x70, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, | ||||
| 	0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, | ||||
| 	0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, | ||||
| 	0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, | ||||
| 	0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, | ||||
| 	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x22, | ||||
| 	0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x3a, | ||||
| 	0x01, 0x2a, 0x12, 0x77, 0x0a, 0x0c, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, | ||||
| 	0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, | ||||
| 	0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, | ||||
| 	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, | ||||
| 	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, | ||||
| 	0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, | ||||
| 	0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, | ||||
| 	0x79, 0x2f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0b, 0x4c, | ||||
| 	0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x20, 0x2e, 0x68, 0x65, 0x61, | ||||
| 	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, | ||||
| 	0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x68, | ||||
| 	0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, | ||||
| 	0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, | ||||
| 	0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, | ||||
| 	0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 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_headscale_proto_goTypes = []interface{}{ | ||||
| @ -211,24 +235,30 @@ var file_headscale_v1_headscale_proto_goTypes = []interface{}{ | ||||
| 	(*UnshareMachineRequest)(nil),       // 15: headscale.v1.UnshareMachineRequest
 | ||||
| 	(*GetMachineRouteRequest)(nil),      // 16: headscale.v1.GetMachineRouteRequest
 | ||||
| 	(*EnableMachineRoutesRequest)(nil),  // 17: headscale.v1.EnableMachineRoutesRequest
 | ||||
| 	(*GetNamespaceResponse)(nil),        // 18: headscale.v1.GetNamespaceResponse
 | ||||
| 	(*CreateNamespaceResponse)(nil),     // 19: headscale.v1.CreateNamespaceResponse
 | ||||
| 	(*RenameNamespaceResponse)(nil),     // 20: headscale.v1.RenameNamespaceResponse
 | ||||
| 	(*DeleteNamespaceResponse)(nil),     // 21: headscale.v1.DeleteNamespaceResponse
 | ||||
| 	(*ListNamespacesResponse)(nil),      // 22: headscale.v1.ListNamespacesResponse
 | ||||
| 	(*CreatePreAuthKeyResponse)(nil),    // 23: headscale.v1.CreatePreAuthKeyResponse
 | ||||
| 	(*ExpirePreAuthKeyResponse)(nil),    // 24: headscale.v1.ExpirePreAuthKeyResponse
 | ||||
| 	(*ListPreAuthKeysResponse)(nil),     // 25: headscale.v1.ListPreAuthKeysResponse
 | ||||
| 	(*DebugCreateMachineResponse)(nil),  // 26: headscale.v1.DebugCreateMachineResponse
 | ||||
| 	(*GetMachineResponse)(nil),          // 27: headscale.v1.GetMachineResponse
 | ||||
| 	(*RegisterMachineResponse)(nil),     // 28: headscale.v1.RegisterMachineResponse
 | ||||
| 	(*DeleteMachineResponse)(nil),       // 29: headscale.v1.DeleteMachineResponse
 | ||||
| 	(*ExpireMachineResponse)(nil),       // 30: headscale.v1.ExpireMachineResponse
 | ||||
| 	(*ListMachinesResponse)(nil),        // 31: headscale.v1.ListMachinesResponse
 | ||||
| 	(*ShareMachineResponse)(nil),        // 32: headscale.v1.ShareMachineResponse
 | ||||
| 	(*UnshareMachineResponse)(nil),      // 33: headscale.v1.UnshareMachineResponse
 | ||||
| 	(*GetMachineRouteResponse)(nil),     // 34: headscale.v1.GetMachineRouteResponse
 | ||||
| 	(*EnableMachineRoutesResponse)(nil), // 35: headscale.v1.EnableMachineRoutesResponse
 | ||||
| 	(*CreateApiKeyRequest)(nil),         // 18: headscale.v1.CreateApiKeyRequest
 | ||||
| 	(*ExpireApiKeyRequest)(nil),         // 19: headscale.v1.ExpireApiKeyRequest
 | ||||
| 	(*ListApiKeysRequest)(nil),          // 20: headscale.v1.ListApiKeysRequest
 | ||||
| 	(*GetNamespaceResponse)(nil),        // 21: headscale.v1.GetNamespaceResponse
 | ||||
| 	(*CreateNamespaceResponse)(nil),     // 22: headscale.v1.CreateNamespaceResponse
 | ||||
| 	(*RenameNamespaceResponse)(nil),     // 23: headscale.v1.RenameNamespaceResponse
 | ||||
| 	(*DeleteNamespaceResponse)(nil),     // 24: headscale.v1.DeleteNamespaceResponse
 | ||||
| 	(*ListNamespacesResponse)(nil),      // 25: headscale.v1.ListNamespacesResponse
 | ||||
| 	(*CreatePreAuthKeyResponse)(nil),    // 26: headscale.v1.CreatePreAuthKeyResponse
 | ||||
| 	(*ExpirePreAuthKeyResponse)(nil),    // 27: headscale.v1.ExpirePreAuthKeyResponse
 | ||||
| 	(*ListPreAuthKeysResponse)(nil),     // 28: headscale.v1.ListPreAuthKeysResponse
 | ||||
| 	(*DebugCreateMachineResponse)(nil),  // 29: headscale.v1.DebugCreateMachineResponse
 | ||||
| 	(*GetMachineResponse)(nil),          // 30: headscale.v1.GetMachineResponse
 | ||||
| 	(*RegisterMachineResponse)(nil),     // 31: headscale.v1.RegisterMachineResponse
 | ||||
| 	(*DeleteMachineResponse)(nil),       // 32: headscale.v1.DeleteMachineResponse
 | ||||
| 	(*ExpireMachineResponse)(nil),       // 33: headscale.v1.ExpireMachineResponse
 | ||||
| 	(*ListMachinesResponse)(nil),        // 34: headscale.v1.ListMachinesResponse
 | ||||
| 	(*ShareMachineResponse)(nil),        // 35: headscale.v1.ShareMachineResponse
 | ||||
| 	(*UnshareMachineResponse)(nil),      // 36: headscale.v1.UnshareMachineResponse
 | ||||
| 	(*GetMachineRouteResponse)(nil),     // 37: headscale.v1.GetMachineRouteResponse
 | ||||
| 	(*EnableMachineRoutesResponse)(nil), // 38: headscale.v1.EnableMachineRoutesResponse
 | ||||
| 	(*CreateApiKeyResponse)(nil),        // 39: headscale.v1.CreateApiKeyResponse
 | ||||
| 	(*ExpireApiKeyResponse)(nil),        // 40: headscale.v1.ExpireApiKeyResponse
 | ||||
| 	(*ListApiKeysResponse)(nil),         // 41: headscale.v1.ListApiKeysResponse
 | ||||
| } | ||||
| var file_headscale_v1_headscale_proto_depIdxs = []int32{ | ||||
| 	0,  // 0: headscale.v1.HeadscaleService.GetNamespace:input_type -> headscale.v1.GetNamespaceRequest
 | ||||
| @ -249,26 +279,32 @@ var file_headscale_v1_headscale_proto_depIdxs = []int32{ | ||||
| 	15, // 15: headscale.v1.HeadscaleService.UnshareMachine:input_type -> headscale.v1.UnshareMachineRequest
 | ||||
| 	16, // 16: headscale.v1.HeadscaleService.GetMachineRoute:input_type -> headscale.v1.GetMachineRouteRequest
 | ||||
| 	17, // 17: headscale.v1.HeadscaleService.EnableMachineRoutes:input_type -> headscale.v1.EnableMachineRoutesRequest
 | ||||
| 	18, // 18: headscale.v1.HeadscaleService.GetNamespace:output_type -> headscale.v1.GetNamespaceResponse
 | ||||
| 	19, // 19: headscale.v1.HeadscaleService.CreateNamespace:output_type -> headscale.v1.CreateNamespaceResponse
 | ||||
| 	20, // 20: headscale.v1.HeadscaleService.RenameNamespace:output_type -> headscale.v1.RenameNamespaceResponse
 | ||||
| 	21, // 21: headscale.v1.HeadscaleService.DeleteNamespace:output_type -> headscale.v1.DeleteNamespaceResponse
 | ||||
| 	22, // 22: headscale.v1.HeadscaleService.ListNamespaces:output_type -> headscale.v1.ListNamespacesResponse
 | ||||
| 	23, // 23: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse
 | ||||
| 	24, // 24: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse
 | ||||
| 	25, // 25: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse
 | ||||
| 	26, // 26: headscale.v1.HeadscaleService.DebugCreateMachine:output_type -> headscale.v1.DebugCreateMachineResponse
 | ||||
| 	27, // 27: headscale.v1.HeadscaleService.GetMachine:output_type -> headscale.v1.GetMachineResponse
 | ||||
| 	28, // 28: headscale.v1.HeadscaleService.RegisterMachine:output_type -> headscale.v1.RegisterMachineResponse
 | ||||
| 	29, // 29: headscale.v1.HeadscaleService.DeleteMachine:output_type -> headscale.v1.DeleteMachineResponse
 | ||||
| 	30, // 30: headscale.v1.HeadscaleService.ExpireMachine:output_type -> headscale.v1.ExpireMachineResponse
 | ||||
| 	31, // 31: headscale.v1.HeadscaleService.ListMachines:output_type -> headscale.v1.ListMachinesResponse
 | ||||
| 	32, // 32: headscale.v1.HeadscaleService.ShareMachine:output_type -> headscale.v1.ShareMachineResponse
 | ||||
| 	33, // 33: headscale.v1.HeadscaleService.UnshareMachine:output_type -> headscale.v1.UnshareMachineResponse
 | ||||
| 	34, // 34: headscale.v1.HeadscaleService.GetMachineRoute:output_type -> headscale.v1.GetMachineRouteResponse
 | ||||
| 	35, // 35: headscale.v1.HeadscaleService.EnableMachineRoutes:output_type -> headscale.v1.EnableMachineRoutesResponse
 | ||||
| 	18, // [18:36] is the sub-list for method output_type
 | ||||
| 	0,  // [0:18] is the sub-list for method input_type
 | ||||
| 	18, // 18: headscale.v1.HeadscaleService.CreateApiKey:input_type -> headscale.v1.CreateApiKeyRequest
 | ||||
| 	19, // 19: headscale.v1.HeadscaleService.ExpireApiKey:input_type -> headscale.v1.ExpireApiKeyRequest
 | ||||
| 	20, // 20: headscale.v1.HeadscaleService.ListApiKeys:input_type -> headscale.v1.ListApiKeysRequest
 | ||||
| 	21, // 21: headscale.v1.HeadscaleService.GetNamespace:output_type -> headscale.v1.GetNamespaceResponse
 | ||||
| 	22, // 22: headscale.v1.HeadscaleService.CreateNamespace:output_type -> headscale.v1.CreateNamespaceResponse
 | ||||
| 	23, // 23: headscale.v1.HeadscaleService.RenameNamespace:output_type -> headscale.v1.RenameNamespaceResponse
 | ||||
| 	24, // 24: headscale.v1.HeadscaleService.DeleteNamespace:output_type -> headscale.v1.DeleteNamespaceResponse
 | ||||
| 	25, // 25: headscale.v1.HeadscaleService.ListNamespaces:output_type -> headscale.v1.ListNamespacesResponse
 | ||||
| 	26, // 26: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse
 | ||||
| 	27, // 27: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse
 | ||||
| 	28, // 28: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse
 | ||||
| 	29, // 29: headscale.v1.HeadscaleService.DebugCreateMachine:output_type -> headscale.v1.DebugCreateMachineResponse
 | ||||
| 	30, // 30: headscale.v1.HeadscaleService.GetMachine:output_type -> headscale.v1.GetMachineResponse
 | ||||
| 	31, // 31: headscale.v1.HeadscaleService.RegisterMachine:output_type -> headscale.v1.RegisterMachineResponse
 | ||||
| 	32, // 32: headscale.v1.HeadscaleService.DeleteMachine:output_type -> headscale.v1.DeleteMachineResponse
 | ||||
| 	33, // 33: headscale.v1.HeadscaleService.ExpireMachine:output_type -> headscale.v1.ExpireMachineResponse
 | ||||
| 	34, // 34: headscale.v1.HeadscaleService.ListMachines:output_type -> headscale.v1.ListMachinesResponse
 | ||||
| 	35, // 35: headscale.v1.HeadscaleService.ShareMachine:output_type -> headscale.v1.ShareMachineResponse
 | ||||
| 	36, // 36: headscale.v1.HeadscaleService.UnshareMachine:output_type -> headscale.v1.UnshareMachineResponse
 | ||||
| 	37, // 37: headscale.v1.HeadscaleService.GetMachineRoute:output_type -> headscale.v1.GetMachineRouteResponse
 | ||||
| 	38, // 38: headscale.v1.HeadscaleService.EnableMachineRoutes:output_type -> headscale.v1.EnableMachineRoutesResponse
 | ||||
| 	39, // 39: headscale.v1.HeadscaleService.CreateApiKey:output_type -> headscale.v1.CreateApiKeyResponse
 | ||||
| 	40, // 40: headscale.v1.HeadscaleService.ExpireApiKey:output_type -> headscale.v1.ExpireApiKeyResponse
 | ||||
| 	41, // 41: headscale.v1.HeadscaleService.ListApiKeys:output_type -> headscale.v1.ListApiKeysResponse
 | ||||
| 	21, // [21:42] is the sub-list for method output_type
 | ||||
| 	0,  // [0:21] is the sub-list for method input_type
 | ||||
| 	0,  // [0:0] is the sub-list for extension type_name
 | ||||
| 	0,  // [0:0] is the sub-list for extension extendee
 | ||||
| 	0,  // [0:0] is the sub-list for field type_name
 | ||||
| @ -283,6 +319,7 @@ func file_headscale_v1_headscale_proto_init() { | ||||
| 	file_headscale_v1_preauthkey_proto_init() | ||||
| 	file_headscale_v1_machine_proto_init() | ||||
| 	file_headscale_v1_routes_proto_init() | ||||
| 	file_headscale_v1_apikey_proto_init() | ||||
| 	type x struct{} | ||||
| 	out := protoimpl.TypeBuilder{ | ||||
| 		File: protoimpl.DescBuilder{ | ||||
|  | ||||
| @ -891,6 +891,92 @@ func local_request_HeadscaleService_EnableMachineRoutes_0(ctx context.Context, m | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func request_HeadscaleService_CreateApiKey_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { | ||||
| 	var protoReq CreateApiKeyRequest | ||||
| 	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.CreateApiKey(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) | ||||
| 	return msg, metadata, err | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func local_request_HeadscaleService_CreateApiKey_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { | ||||
| 	var protoReq CreateApiKeyRequest | ||||
| 	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.CreateApiKey(ctx, &protoReq) | ||||
| 	return msg, metadata, err | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func request_HeadscaleService_ExpireApiKey_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { | ||||
| 	var protoReq ExpireApiKeyRequest | ||||
| 	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.ExpireApiKey(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) | ||||
| 	return msg, metadata, err | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func local_request_HeadscaleService_ExpireApiKey_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { | ||||
| 	var protoReq ExpireApiKeyRequest | ||||
| 	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.ExpireApiKey(ctx, &protoReq) | ||||
| 	return msg, metadata, err | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func request_HeadscaleService_ListApiKeys_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { | ||||
| 	var protoReq ListApiKeysRequest | ||||
| 	var metadata runtime.ServerMetadata | ||||
| 
 | ||||
| 	msg, err := client.ListApiKeys(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) | ||||
| 	return msg, metadata, err | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func local_request_HeadscaleService_ListApiKeys_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { | ||||
| 	var protoReq ListApiKeysRequest | ||||
| 	var metadata runtime.ServerMetadata | ||||
| 
 | ||||
| 	msg, err := server.ListApiKeys(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.
 | ||||
| @ -1311,6 +1397,75 @@ func RegisterHeadscaleServiceHandlerServer(ctx context.Context, mux *runtime.Ser | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| 	mux.Handle("POST", pattern_HeadscaleService_CreateApiKey_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/CreateApiKey", runtime.WithHTTPPathPattern("/api/v1/apikey")) | ||||
| 		if err != nil { | ||||
| 			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) | ||||
| 			return | ||||
| 		} | ||||
| 		resp, md, err := local_request_HeadscaleService_CreateApiKey_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_CreateApiKey_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| 	mux.Handle("POST", pattern_HeadscaleService_ExpireApiKey_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/ExpireApiKey", runtime.WithHTTPPathPattern("/api/v1/apikey/expire")) | ||||
| 		if err != nil { | ||||
| 			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) | ||||
| 			return | ||||
| 		} | ||||
| 		resp, md, err := local_request_HeadscaleService_ExpireApiKey_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_ExpireApiKey_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| 	mux.Handle("GET", pattern_HeadscaleService_ListApiKeys_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/ListApiKeys", runtime.WithHTTPPathPattern("/api/v1/apikey")) | ||||
| 		if err != nil { | ||||
| 			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) | ||||
| 			return | ||||
| 		} | ||||
| 		resp, md, err := local_request_HeadscaleService_ListApiKeys_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_ListApiKeys_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| @ -1712,6 +1867,66 @@ func RegisterHeadscaleServiceHandlerClient(ctx context.Context, mux *runtime.Ser | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| 	mux.Handle("POST", pattern_HeadscaleService_CreateApiKey_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/CreateApiKey", runtime.WithHTTPPathPattern("/api/v1/apikey")) | ||||
| 		if err != nil { | ||||
| 			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) | ||||
| 			return | ||||
| 		} | ||||
| 		resp, md, err := request_HeadscaleService_CreateApiKey_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_CreateApiKey_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| 	mux.Handle("POST", pattern_HeadscaleService_ExpireApiKey_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/ExpireApiKey", runtime.WithHTTPPathPattern("/api/v1/apikey/expire")) | ||||
| 		if err != nil { | ||||
| 			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) | ||||
| 			return | ||||
| 		} | ||||
| 		resp, md, err := request_HeadscaleService_ExpireApiKey_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_ExpireApiKey_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| 	mux.Handle("GET", pattern_HeadscaleService_ListApiKeys_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/ListApiKeys", runtime.WithHTTPPathPattern("/api/v1/apikey")) | ||||
| 		if err != nil { | ||||
| 			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) | ||||
| 			return | ||||
| 		} | ||||
| 		resp, md, err := request_HeadscaleService_ListApiKeys_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_ListApiKeys_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) | ||||
| 
 | ||||
| 	}) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| @ -1751,6 +1966,12 @@ var ( | ||||
| 	pattern_HeadscaleService_GetMachineRoute_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "machine", "machine_id", "routes"}, "")) | ||||
| 
 | ||||
| 	pattern_HeadscaleService_EnableMachineRoutes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "machine", "machine_id", "routes"}, "")) | ||||
| 
 | ||||
| 	pattern_HeadscaleService_CreateApiKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "apikey"}, "")) | ||||
| 
 | ||||
| 	pattern_HeadscaleService_ExpireApiKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "apikey", "expire"}, "")) | ||||
| 
 | ||||
| 	pattern_HeadscaleService_ListApiKeys_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "apikey"}, "")) | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| @ -1789,4 +2010,10 @@ var ( | ||||
| 	forward_HeadscaleService_GetMachineRoute_0 = runtime.ForwardResponseMessage | ||||
| 
 | ||||
| 	forward_HeadscaleService_EnableMachineRoutes_0 = runtime.ForwardResponseMessage | ||||
| 
 | ||||
| 	forward_HeadscaleService_CreateApiKey_0 = runtime.ForwardResponseMessage | ||||
| 
 | ||||
| 	forward_HeadscaleService_ExpireApiKey_0 = runtime.ForwardResponseMessage | ||||
| 
 | ||||
| 	forward_HeadscaleService_ListApiKeys_0 = runtime.ForwardResponseMessage | ||||
| ) | ||||
|  | ||||
| @ -4,6 +4,7 @@ package v1 | ||||
| 
 | ||||
| import ( | ||||
| 	context "context" | ||||
| 
 | ||||
| 	grpc "google.golang.org/grpc" | ||||
| 	codes "google.golang.org/grpc/codes" | ||||
| 	status "google.golang.org/grpc/status" | ||||
| @ -40,6 +41,10 @@ type HeadscaleServiceClient interface { | ||||
| 	// --- Route start ---
 | ||||
| 	GetMachineRoute(ctx context.Context, in *GetMachineRouteRequest, opts ...grpc.CallOption) (*GetMachineRouteResponse, error) | ||||
| 	EnableMachineRoutes(ctx context.Context, in *EnableMachineRoutesRequest, opts ...grpc.CallOption) (*EnableMachineRoutesResponse, error) | ||||
| 	// --- ApiKeys start ---
 | ||||
| 	CreateApiKey(ctx context.Context, in *CreateApiKeyRequest, opts ...grpc.CallOption) (*CreateApiKeyResponse, error) | ||||
| 	ExpireApiKey(ctx context.Context, in *ExpireApiKeyRequest, opts ...grpc.CallOption) (*ExpireApiKeyResponse, error) | ||||
| 	ListApiKeys(ctx context.Context, in *ListApiKeysRequest, opts ...grpc.CallOption) (*ListApiKeysResponse, error) | ||||
| } | ||||
| 
 | ||||
| type headscaleServiceClient struct { | ||||
| @ -212,6 +217,33 @@ func (c *headscaleServiceClient) EnableMachineRoutes(ctx context.Context, in *En | ||||
| 	return out, nil | ||||
| } | ||||
| 
 | ||||
| func (c *headscaleServiceClient) CreateApiKey(ctx context.Context, in *CreateApiKeyRequest, opts ...grpc.CallOption) (*CreateApiKeyResponse, error) { | ||||
| 	out := new(CreateApiKeyResponse) | ||||
| 	err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/CreateApiKey", in, out, opts...) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return out, nil | ||||
| } | ||||
| 
 | ||||
| func (c *headscaleServiceClient) ExpireApiKey(ctx context.Context, in *ExpireApiKeyRequest, opts ...grpc.CallOption) (*ExpireApiKeyResponse, error) { | ||||
| 	out := new(ExpireApiKeyResponse) | ||||
| 	err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ExpireApiKey", in, out, opts...) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return out, nil | ||||
| } | ||||
| 
 | ||||
| func (c *headscaleServiceClient) ListApiKeys(ctx context.Context, in *ListApiKeysRequest, opts ...grpc.CallOption) (*ListApiKeysResponse, error) { | ||||
| 	out := new(ListApiKeysResponse) | ||||
| 	err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ListApiKeys", 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
 | ||||
| @ -238,6 +270,10 @@ type HeadscaleServiceServer interface { | ||||
| 	// --- Route start ---
 | ||||
| 	GetMachineRoute(context.Context, *GetMachineRouteRequest) (*GetMachineRouteResponse, error) | ||||
| 	EnableMachineRoutes(context.Context, *EnableMachineRoutesRequest) (*EnableMachineRoutesResponse, error) | ||||
| 	// --- ApiKeys start ---
 | ||||
| 	CreateApiKey(context.Context, *CreateApiKeyRequest) (*CreateApiKeyResponse, error) | ||||
| 	ExpireApiKey(context.Context, *ExpireApiKeyRequest) (*ExpireApiKeyResponse, error) | ||||
| 	ListApiKeys(context.Context, *ListApiKeysRequest) (*ListApiKeysResponse, error) | ||||
| 	mustEmbedUnimplementedHeadscaleServiceServer() | ||||
| } | ||||
| 
 | ||||
| @ -299,6 +335,15 @@ func (UnimplementedHeadscaleServiceServer) GetMachineRoute(context.Context, *Get | ||||
| func (UnimplementedHeadscaleServiceServer) EnableMachineRoutes(context.Context, *EnableMachineRoutesRequest) (*EnableMachineRoutesResponse, error) { | ||||
| 	return nil, status.Errorf(codes.Unimplemented, "method EnableMachineRoutes not implemented") | ||||
| } | ||||
| func (UnimplementedHeadscaleServiceServer) CreateApiKey(context.Context, *CreateApiKeyRequest) (*CreateApiKeyResponse, error) { | ||||
| 	return nil, status.Errorf(codes.Unimplemented, "method CreateApiKey not implemented") | ||||
| } | ||||
| func (UnimplementedHeadscaleServiceServer) ExpireApiKey(context.Context, *ExpireApiKeyRequest) (*ExpireApiKeyResponse, error) { | ||||
| 	return nil, status.Errorf(codes.Unimplemented, "method ExpireApiKey not implemented") | ||||
| } | ||||
| func (UnimplementedHeadscaleServiceServer) ListApiKeys(context.Context, *ListApiKeysRequest) (*ListApiKeysResponse, error) { | ||||
| 	return nil, status.Errorf(codes.Unimplemented, "method ListApiKeys not implemented") | ||||
| } | ||||
| func (UnimplementedHeadscaleServiceServer) mustEmbedUnimplementedHeadscaleServiceServer() {} | ||||
| 
 | ||||
| // UnsafeHeadscaleServiceServer may be embedded to opt out of forward compatibility for this service.
 | ||||
| @ -636,6 +681,60 @@ func _HeadscaleService_EnableMachineRoutes_Handler(srv interface{}, ctx context. | ||||
| 	return interceptor(ctx, in, info, handler) | ||||
| } | ||||
| 
 | ||||
| func _HeadscaleService_CreateApiKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | ||||
| 	in := new(CreateApiKeyRequest) | ||||
| 	if err := dec(in); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if interceptor == nil { | ||||
| 		return srv.(HeadscaleServiceServer).CreateApiKey(ctx, in) | ||||
| 	} | ||||
| 	info := &grpc.UnaryServerInfo{ | ||||
| 		Server:     srv, | ||||
| 		FullMethod: "/headscale.v1.HeadscaleService/CreateApiKey", | ||||
| 	} | ||||
| 	handler := func(ctx context.Context, req interface{}) (interface{}, error) { | ||||
| 		return srv.(HeadscaleServiceServer).CreateApiKey(ctx, req.(*CreateApiKeyRequest)) | ||||
| 	} | ||||
| 	return interceptor(ctx, in, info, handler) | ||||
| } | ||||
| 
 | ||||
| func _HeadscaleService_ExpireApiKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | ||||
| 	in := new(ExpireApiKeyRequest) | ||||
| 	if err := dec(in); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if interceptor == nil { | ||||
| 		return srv.(HeadscaleServiceServer).ExpireApiKey(ctx, in) | ||||
| 	} | ||||
| 	info := &grpc.UnaryServerInfo{ | ||||
| 		Server:     srv, | ||||
| 		FullMethod: "/headscale.v1.HeadscaleService/ExpireApiKey", | ||||
| 	} | ||||
| 	handler := func(ctx context.Context, req interface{}) (interface{}, error) { | ||||
| 		return srv.(HeadscaleServiceServer).ExpireApiKey(ctx, req.(*ExpireApiKeyRequest)) | ||||
| 	} | ||||
| 	return interceptor(ctx, in, info, handler) | ||||
| } | ||||
| 
 | ||||
| func _HeadscaleService_ListApiKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | ||||
| 	in := new(ListApiKeysRequest) | ||||
| 	if err := dec(in); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if interceptor == nil { | ||||
| 		return srv.(HeadscaleServiceServer).ListApiKeys(ctx, in) | ||||
| 	} | ||||
| 	info := &grpc.UnaryServerInfo{ | ||||
| 		Server:     srv, | ||||
| 		FullMethod: "/headscale.v1.HeadscaleService/ListApiKeys", | ||||
| 	} | ||||
| 	handler := func(ctx context.Context, req interface{}) (interface{}, error) { | ||||
| 		return srv.(HeadscaleServiceServer).ListApiKeys(ctx, req.(*ListApiKeysRequest)) | ||||
| 	} | ||||
| 	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)
 | ||||
| @ -715,6 +814,18 @@ var HeadscaleService_ServiceDesc = grpc.ServiceDesc{ | ||||
| 			MethodName: "EnableMachineRoutes", | ||||
| 			Handler:    _HeadscaleService_EnableMachineRoutes_Handler, | ||||
| 		}, | ||||
| 		{ | ||||
| 			MethodName: "CreateApiKey", | ||||
| 			Handler:    _HeadscaleService_CreateApiKey_Handler, | ||||
| 		}, | ||||
| 		{ | ||||
| 			MethodName: "ExpireApiKey", | ||||
| 			Handler:    _HeadscaleService_ExpireApiKey_Handler, | ||||
| 		}, | ||||
| 		{ | ||||
| 			MethodName: "ListApiKeys", | ||||
| 			Handler:    _HeadscaleService_ListApiKeys_Handler, | ||||
| 		}, | ||||
| 	}, | ||||
| 	Streams:  []grpc.StreamDesc{}, | ||||
| 	Metadata: "headscale/v1/headscale.proto", | ||||
|  | ||||
| @ -1,17 +1,18 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc        v3.17.3
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/machine.proto
 | ||||
| 
 | ||||
| package v1 | ||||
| 
 | ||||
| import ( | ||||
| 	reflect "reflect" | ||||
| 	sync "sync" | ||||
| 
 | ||||
| 	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 ( | ||||
|  | ||||
| @ -1,17 +1,18 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc        v3.17.3
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/namespace.proto
 | ||||
| 
 | ||||
| package v1 | ||||
| 
 | ||||
| import ( | ||||
| 	reflect "reflect" | ||||
| 	sync "sync" | ||||
| 
 | ||||
| 	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 ( | ||||
|  | ||||
| @ -1,17 +1,18 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc        v3.17.3
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/preauthkey.proto
 | ||||
| 
 | ||||
| package v1 | ||||
| 
 | ||||
| import ( | ||||
| 	reflect "reflect" | ||||
| 	sync "sync" | ||||
| 
 | ||||
| 	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 ( | ||||
|  | ||||
| @ -1,16 +1,17 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc        v3.17.3
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/routes.proto
 | ||||
| 
 | ||||
| package v1 | ||||
| 
 | ||||
| import ( | ||||
| 	protoreflect "google.golang.org/protobuf/reflect/protoreflect" | ||||
| 	protoimpl "google.golang.org/protobuf/runtime/protoimpl" | ||||
| 	reflect "reflect" | ||||
| 	sync "sync" | ||||
| 
 | ||||
| 	protoreflect "google.golang.org/protobuf/reflect/protoreflect" | ||||
| 	protoimpl "google.golang.org/protobuf/runtime/protoimpl" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
|  | ||||
							
								
								
									
										43
									
								
								gen/openapiv2/headscale/v1/apikey.swagger.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								gen/openapiv2/headscale/v1/apikey.swagger.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| { | ||||
|   "swagger": "2.0", | ||||
|   "info": { | ||||
|     "title": "headscale/v1/apikey.proto", | ||||
|     "version": "version not set" | ||||
|   }, | ||||
|   "consumes": [ | ||||
|     "application/json" | ||||
|   ], | ||||
|   "produces": [ | ||||
|     "application/json" | ||||
|   ], | ||||
|   "paths": {}, | ||||
|   "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" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -16,6 +16,91 @@ | ||||
|     "application/json" | ||||
|   ], | ||||
|   "paths": { | ||||
|     "/api/v1/apikey": { | ||||
|       "get": { | ||||
|         "operationId": "HeadscaleService_ListApiKeys", | ||||
|         "responses": { | ||||
|           "200": { | ||||
|             "description": "A successful response.", | ||||
|             "schema": { | ||||
|               "$ref": "#/definitions/v1ListApiKeysResponse" | ||||
|             } | ||||
|           }, | ||||
|           "default": { | ||||
|             "description": "An unexpected error response.", | ||||
|             "schema": { | ||||
|               "$ref": "#/definitions/rpcStatus" | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "tags": [ | ||||
|           "HeadscaleService" | ||||
|         ] | ||||
|       }, | ||||
|       "post": { | ||||
|         "summary": "--- ApiKeys start ---", | ||||
|         "operationId": "HeadscaleService_CreateApiKey", | ||||
|         "responses": { | ||||
|           "200": { | ||||
|             "description": "A successful response.", | ||||
|             "schema": { | ||||
|               "$ref": "#/definitions/v1CreateApiKeyResponse" | ||||
|             } | ||||
|           }, | ||||
|           "default": { | ||||
|             "description": "An unexpected error response.", | ||||
|             "schema": { | ||||
|               "$ref": "#/definitions/rpcStatus" | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "parameters": [ | ||||
|           { | ||||
|             "name": "body", | ||||
|             "in": "body", | ||||
|             "required": true, | ||||
|             "schema": { | ||||
|               "$ref": "#/definitions/v1CreateApiKeyRequest" | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|         "tags": [ | ||||
|           "HeadscaleService" | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/api/v1/apikey/expire": { | ||||
|       "post": { | ||||
|         "operationId": "HeadscaleService_ExpireApiKey", | ||||
|         "responses": { | ||||
|           "200": { | ||||
|             "description": "A successful response.", | ||||
|             "schema": { | ||||
|               "$ref": "#/definitions/v1ExpireApiKeyResponse" | ||||
|             } | ||||
|           }, | ||||
|           "default": { | ||||
|             "description": "An unexpected error response.", | ||||
|             "schema": { | ||||
|               "$ref": "#/definitions/rpcStatus" | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "parameters": [ | ||||
|           { | ||||
|             "name": "body", | ||||
|             "in": "body", | ||||
|             "required": true, | ||||
|             "schema": { | ||||
|               "$ref": "#/definitions/v1ExpireApiKeyRequest" | ||||
|             } | ||||
|           } | ||||
|         ], | ||||
|         "tags": [ | ||||
|           "HeadscaleService" | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "/api/v1/debug/machine": { | ||||
|       "post": { | ||||
|         "summary": "--- Machine start ---", | ||||
| @ -596,6 +681,47 @@ | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "v1ApiKey": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|         "id": { | ||||
|           "type": "string", | ||||
|           "format": "uint64" | ||||
|         }, | ||||
|         "prefix": { | ||||
|           "type": "string" | ||||
|         }, | ||||
|         "expiration": { | ||||
|           "type": "string", | ||||
|           "format": "date-time" | ||||
|         }, | ||||
|         "createdAt": { | ||||
|           "type": "string", | ||||
|           "format": "date-time" | ||||
|         }, | ||||
|         "lastSeen": { | ||||
|           "type": "string", | ||||
|           "format": "date-time" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "v1CreateApiKeyRequest": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|         "expiration": { | ||||
|           "type": "string", | ||||
|           "format": "date-time" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "v1CreateApiKeyResponse": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|         "apiKey": { | ||||
|           "type": "string" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "v1CreateNamespaceRequest": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
| @ -680,6 +806,17 @@ | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "v1ExpireApiKeyRequest": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|         "prefix": { | ||||
|           "type": "string" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "v1ExpireApiKeyResponse": { | ||||
|       "type": "object" | ||||
|     }, | ||||
|     "v1ExpireMachineResponse": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
| @ -726,6 +863,17 @@ | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "v1ListApiKeysResponse": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|         "apiKeys": { | ||||
|           "type": "array", | ||||
|           "items": { | ||||
|             "$ref": "#/definitions/v1ApiKey" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "v1ListMachinesResponse": { | ||||
|       "type": "object", | ||||
|       "properties": { | ||||
|  | ||||
							
								
								
									
										56
									
								
								grpcv1.go
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								grpcv1.go
									
									
									
									
									
								
							| @ -349,6 +349,62 @@ func (api headscaleV1APIServer) EnableMachineRoutes( | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (api headscaleV1APIServer) CreateApiKey( | ||||
| 	ctx context.Context, | ||||
| 	request *v1.CreateApiKeyRequest, | ||||
| ) (*v1.CreateApiKeyResponse, error) { | ||||
| 	var expiration time.Time | ||||
| 	if request.GetExpiration() != nil { | ||||
| 		expiration = request.GetExpiration().AsTime() | ||||
| 	} | ||||
| 
 | ||||
| 	apiKey, _, err := api.h.CreateAPIKey( | ||||
| 		&expiration, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &v1.CreateApiKeyResponse{ApiKey: apiKey}, nil | ||||
| } | ||||
| 
 | ||||
| func (api headscaleV1APIServer) ExpireApiKey( | ||||
| 	ctx context.Context, | ||||
| 	request *v1.ExpireApiKeyRequest, | ||||
| ) (*v1.ExpireApiKeyResponse, error) { | ||||
| 	var apiKey *APIKey | ||||
| 	var err error | ||||
| 
 | ||||
| 	apiKey, err = api.h.GetAPIKey(request.Prefix) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	err = api.h.ExpireAPIKey(apiKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &v1.ExpireApiKeyResponse{}, nil | ||||
| } | ||||
| 
 | ||||
| func (api headscaleV1APIServer) ListApiKeys( | ||||
| 	ctx context.Context, | ||||
| 	request *v1.ListApiKeysRequest, | ||||
| ) (*v1.ListApiKeysResponse, error) { | ||||
| 	apiKeys, err := api.h.ListAPIKeys() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	response := make([]*v1.ApiKey, len(apiKeys)) | ||||
| 	for index, key := range apiKeys { | ||||
| 		response[index] = key.toProto() | ||||
| 	} | ||||
| 
 | ||||
| 	return &v1.ListApiKeysResponse{ApiKeys: response}, nil | ||||
| } | ||||
| 
 | ||||
| // The following service calls are for testing and debugging
 | ||||
| func (api headscaleV1APIServer) DebugCreateMachine( | ||||
| 	ctx context.Context, | ||||
|  | ||||
| @ -1193,3 +1193,148 @@ func (s *IntegrationCLITestSuite) TestRouteCommand() { | ||||
| 		"route (route-machine) is not available on node", | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| func (s *IntegrationCLITestSuite) TestApiKeyCommand() { | ||||
| 	count := 5 | ||||
| 
 | ||||
| 	keys := make([]string, count) | ||||
| 
 | ||||
| 	for i := 0; i < count; i++ { | ||||
| 		apiResult, err := ExecuteCommand( | ||||
| 			&s.headscale, | ||||
| 			[]string{ | ||||
| 				"headscale", | ||||
| 				"apikeys", | ||||
| 				"create", | ||||
| 				"--expiration", | ||||
| 				"24h", | ||||
| 				"--output", | ||||
| 				"json", | ||||
| 			}, | ||||
| 			[]string{}, | ||||
| 		) | ||||
| 		assert.Nil(s.T(), err) | ||||
| 		assert.NotEmpty(s.T(), apiResult) | ||||
| 
 | ||||
| 		// var apiKey v1.ApiKey
 | ||||
| 		// err = json.Unmarshal([]byte(apiResult), &apiKey)
 | ||||
| 		// assert.Nil(s.T(), err)
 | ||||
| 
 | ||||
| 		keys[i] = apiResult | ||||
| 	} | ||||
| 
 | ||||
| 	assert.Len(s.T(), keys, 5) | ||||
| 
 | ||||
| 	// Test list of keys
 | ||||
| 	listResult, err := ExecuteCommand( | ||||
| 		&s.headscale, | ||||
| 		[]string{ | ||||
| 			"headscale", | ||||
| 			"apikeys", | ||||
| 			"list", | ||||
| 			"--output", | ||||
| 			"json", | ||||
| 		}, | ||||
| 		[]string{}, | ||||
| 	) | ||||
| 	assert.Nil(s.T(), err) | ||||
| 
 | ||||
| 	var listedApiKeys []v1.ApiKey | ||||
| 	err = json.Unmarshal([]byte(listResult), &listedApiKeys) | ||||
| 	assert.Nil(s.T(), err) | ||||
| 
 | ||||
| 	assert.Len(s.T(), listedApiKeys, 5) | ||||
| 
 | ||||
| 	assert.Equal(s.T(), uint64(1), listedApiKeys[0].Id) | ||||
| 	assert.Equal(s.T(), uint64(2), listedApiKeys[1].Id) | ||||
| 	assert.Equal(s.T(), uint64(3), listedApiKeys[2].Id) | ||||
| 	assert.Equal(s.T(), uint64(4), listedApiKeys[3].Id) | ||||
| 	assert.Equal(s.T(), uint64(5), listedApiKeys[4].Id) | ||||
| 
 | ||||
| 	assert.NotEmpty(s.T(), listedApiKeys[0].Prefix) | ||||
| 	assert.NotEmpty(s.T(), listedApiKeys[1].Prefix) | ||||
| 	assert.NotEmpty(s.T(), listedApiKeys[2].Prefix) | ||||
| 	assert.NotEmpty(s.T(), listedApiKeys[3].Prefix) | ||||
| 	assert.NotEmpty(s.T(), listedApiKeys[4].Prefix) | ||||
| 
 | ||||
| 	assert.True(s.T(), listedApiKeys[0].Expiration.AsTime().After(time.Now())) | ||||
| 	assert.True(s.T(), listedApiKeys[1].Expiration.AsTime().After(time.Now())) | ||||
| 	assert.True(s.T(), listedApiKeys[2].Expiration.AsTime().After(time.Now())) | ||||
| 	assert.True(s.T(), listedApiKeys[3].Expiration.AsTime().After(time.Now())) | ||||
| 	assert.True(s.T(), listedApiKeys[4].Expiration.AsTime().After(time.Now())) | ||||
| 
 | ||||
| 	assert.True( | ||||
| 		s.T(), | ||||
| 		listedApiKeys[0].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), | ||||
| 	) | ||||
| 	assert.True( | ||||
| 		s.T(), | ||||
| 		listedApiKeys[1].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), | ||||
| 	) | ||||
| 	assert.True( | ||||
| 		s.T(), | ||||
| 		listedApiKeys[2].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), | ||||
| 	) | ||||
| 	assert.True( | ||||
| 		s.T(), | ||||
| 		listedApiKeys[3].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), | ||||
| 	) | ||||
| 	assert.True( | ||||
| 		s.T(), | ||||
| 		listedApiKeys[4].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), | ||||
| 	) | ||||
| 
 | ||||
| 	expiredPrefixes := make(map[string]bool) | ||||
| 
 | ||||
| 	// Expire three keys
 | ||||
| 	for i := 0; i < 3; i++ { | ||||
| 		_, err := ExecuteCommand( | ||||
| 			&s.headscale, | ||||
| 			[]string{ | ||||
| 				"headscale", | ||||
| 				"apikeys", | ||||
| 				"expire", | ||||
| 				"--prefix", | ||||
| 				listedApiKeys[i].Prefix, | ||||
| 			}, | ||||
| 			[]string{}, | ||||
| 		) | ||||
| 		assert.Nil(s.T(), err) | ||||
| 
 | ||||
| 		expiredPrefixes[listedApiKeys[i].Prefix] = true | ||||
| 	} | ||||
| 
 | ||||
| 	// Test list pre auth keys after expire
 | ||||
| 	listAfterExpireResult, err := ExecuteCommand( | ||||
| 		&s.headscale, | ||||
| 		[]string{ | ||||
| 			"headscale", | ||||
| 			"apikeys", | ||||
| 			"list", | ||||
| 			"--output", | ||||
| 			"json", | ||||
| 		}, | ||||
| 		[]string{}, | ||||
| 	) | ||||
| 	assert.Nil(s.T(), err) | ||||
| 
 | ||||
| 	var listedAfterExpireApiKeys []v1.ApiKey | ||||
| 	err = json.Unmarshal([]byte(listAfterExpireResult), &listedAfterExpireApiKeys) | ||||
| 	assert.Nil(s.T(), err) | ||||
| 
 | ||||
| 	for index := range listedAfterExpireApiKeys { | ||||
| 		if _, ok := expiredPrefixes[listedAfterExpireApiKeys[index].Prefix]; ok { | ||||
| 			// Expired
 | ||||
| 			assert.True( | ||||
| 				s.T(), | ||||
| 				listedAfterExpireApiKeys[index].Expiration.AsTime().Before(time.Now()), | ||||
| 			) | ||||
| 		} else { | ||||
| 			// Not expired
 | ||||
| 			assert.False( | ||||
| 				s.T(), | ||||
| 				listedAfterExpireApiKeys[index].Expiration.AsTime().Before(time.Now()), | ||||
| 			) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -8,16 +8,17 @@ import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"inet.af/netaddr" | ||||
| 
 | ||||
| 	"github.com/ory/dockertest/v3" | ||||
| 	"github.com/ory/dockertest/v3/docker" | ||||
| 	"inet.af/netaddr" | ||||
| ) | ||||
| 
 | ||||
| const DOCKER_EXECUTE_TIMEOUT = 10 * time.Second | ||||
| 
 | ||||
| var IpPrefix4 = netaddr.MustParseIPPrefix("100.64.0.0/10") | ||||
| var IpPrefix6 = netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/48") | ||||
| var ( | ||||
| 	IpPrefix4 = netaddr.MustParseIPPrefix("100.64.0.0/10") | ||||
| 	IpPrefix6 = netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/48") | ||||
| ) | ||||
| 
 | ||||
| type ExecuteCommandConfig struct { | ||||
| 	timeout time.Duration | ||||
|  | ||||
							
								
								
									
										35
									
								
								proto/headscale/v1/apikey.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								proto/headscale/v1/apikey.proto
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| syntax = "proto3"; | ||||
| package headscale.v1; | ||||
| option  go_package = "github.com/juanfont/headscale/gen/go/v1"; | ||||
| 
 | ||||
| import "google/protobuf/timestamp.proto"; | ||||
| 
 | ||||
| message ApiKey { | ||||
|     uint64                    id         = 1; | ||||
|     string                    prefix     = 2; | ||||
|     google.protobuf.Timestamp expiration = 3; | ||||
|     google.protobuf.Timestamp created_at = 4; | ||||
|     google.protobuf.Timestamp last_seen  = 5; | ||||
| } | ||||
| 
 | ||||
| message CreateApiKeyRequest { | ||||
|     google.protobuf.Timestamp expiration = 1; | ||||
| } | ||||
| 
 | ||||
| message CreateApiKeyResponse { | ||||
|     string api_key = 1; | ||||
| } | ||||
| 
 | ||||
| message ExpireApiKeyRequest { | ||||
|     string prefix = 1; | ||||
| } | ||||
| 
 | ||||
| message ExpireApiKeyResponse { | ||||
| } | ||||
| 
 | ||||
| message ListApiKeysRequest { | ||||
| } | ||||
| 
 | ||||
| message ListApiKeysResponse { | ||||
|     repeated ApiKey api_keys = 1; | ||||
| } | ||||
| @ -8,6 +8,7 @@ import "headscale/v1/namespace.proto"; | ||||
| import "headscale/v1/preauthkey.proto"; | ||||
| import "headscale/v1/machine.proto"; | ||||
| import "headscale/v1/routes.proto"; | ||||
| import "headscale/v1/apikey.proto"; | ||||
| // import "headscale/v1/device.proto"; | ||||
| 
 | ||||
| service HeadscaleService { | ||||
| @ -131,6 +132,28 @@ service HeadscaleService { | ||||
|     } | ||||
|     // --- Route end --- | ||||
| 
 | ||||
|     // --- ApiKeys start --- | ||||
|     rpc CreateApiKey(CreateApiKeyRequest) returns (CreateApiKeyResponse) { | ||||
|         option (google.api.http) = { | ||||
|             post: "/api/v1/apikey" | ||||
|             body: "*" | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     rpc ExpireApiKey(ExpireApiKeyRequest) returns (ExpireApiKeyResponse) { | ||||
|         option (google.api.http) = { | ||||
|             post: "/api/v1/apikey/expire" | ||||
|             body: "*" | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     rpc ListApiKeys(ListApiKeysRequest) returns (ListApiKeysResponse) { | ||||
|         option (google.api.http) = { | ||||
|             get: "/api/v1/apikey" | ||||
|         }; | ||||
|     } | ||||
|     // --- ApiKeys end --- | ||||
| 
 | ||||
|     // Implement Tailscale API | ||||
|     // rpc GetDevice(GetDeviceRequest) returns(GetDeviceResponse) { | ||||
|     //     option(google.api.http) = { | ||||
|  | ||||
| @ -14,13 +14,13 @@ enum RegisterMethod { | ||||
| } | ||||
| 
 | ||||
| message Machine { | ||||
|     uint64 id           = 1; | ||||
|     string machine_key  = 2; | ||||
|     string node_key     = 3; | ||||
|     string disco_key    = 4; | ||||
|     repeated string ip_addresses   = 5; | ||||
|     string name         = 6; | ||||
|     Namespace namespace = 7; | ||||
|     uint64          id           = 1; | ||||
|     string          machine_key  = 2; | ||||
|     string          node_key     = 3; | ||||
|     string          disco_key    = 4; | ||||
|     repeated string ip_addresses = 5; | ||||
|     string          name         = 6; | ||||
|     Namespace namespace          = 7; | ||||
| 
 | ||||
|     bool           registered      = 8; | ||||
|     RegisterMethod register_method = 9; | ||||
|  | ||||
							
								
								
									
										28
									
								
								utils.go
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								utils.go
									
									
									
									
									
								
							| @ -7,6 +7,8 @@ package headscale | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/rand" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| @ -278,3 +280,29 @@ func containsIPPrefix(prefixes []netaddr.IPPrefix, prefix netaddr.IPPrefix) bool | ||||
| 
 | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // GenerateRandomBytes returns securely generated random bytes.
 | ||||
| // It will return an error if the system's secure random
 | ||||
| // number generator fails to function correctly, in which
 | ||||
| // case the caller should not continue.
 | ||||
| func GenerateRandomBytes(n int) ([]byte, error) { | ||||
| 	bytes := make([]byte, n) | ||||
| 
 | ||||
| 	// Note that err == nil only if we read len(b) bytes.
 | ||||
| 	if _, err := rand.Read(bytes); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return bytes, nil | ||||
| } | ||||
| 
 | ||||
| // GenerateRandomStringURLSafe returns a URL-safe, base64 encoded
 | ||||
| // securely generated random string.
 | ||||
| // It will return an error if the system's secure random
 | ||||
| // number generator fails to function correctly, in which
 | ||||
| // case the caller should not continue.
 | ||||
| func GenerateRandomStringURLSafe(n int) (string, error) { | ||||
| 	b, err := GenerateRandomBytes(n) | ||||
| 
 | ||||
| 	return base64.RawURLEncoding.EncodeToString(b), err | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user