mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	Merge pull request #767 from tsujamin/preauthkey-tags
This commit is contained in:
		
						commit
						8fa05c1e72
					
				| @ -15,6 +15,7 @@ | ||||
| - Give a warning when running Headscale with reverse proxy improperly configured for WebSockets [#788](https://github.com/juanfont/headscale/pull/788) | ||||
| - Fix subnet routers with Primary Routes [#811](https://github.com/juanfont/headscale/pull/811) | ||||
| - Added support for JSON logs [#653](https://github.com/juanfont/headscale/issues/653) | ||||
| - Add support for generating pre-auth keys with tags [#767](https://github.com/juanfont/headscale/pull/767) | ||||
| 
 | ||||
| ## 0.16.4 (2022-08-21) | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										12
									
								
								acls_test.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								acls_test.go
									
									
									
									
									
								
							| @ -114,7 +114,7 @@ func (s *Suite) TestValidExpandTagOwnersInSources(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("user1") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachine("user1", "testmachine") | ||||
| @ -164,7 +164,7 @@ func (s *Suite) TestValidExpandTagOwnersInDestinations(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("user1") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachine("user1", "testmachine") | ||||
| @ -214,7 +214,7 @@ func (s *Suite) TestInvalidTagValidNamespace(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("user1") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachine("user1", "testmachine") | ||||
| @ -263,7 +263,7 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("user1") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachine("user1", "webserver") | ||||
| @ -395,7 +395,7 @@ func (s *Suite) TestPortNamespace(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("testnamespace") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachine("testnamespace", "testmachine") | ||||
| @ -437,7 +437,7 @@ func (s *Suite) TestPortGroup(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("testnamespace") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachine("testnamespace", "testmachine") | ||||
|  | ||||
| @ -3,6 +3,7 @@ package cli | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	v1 "github.com/juanfont/headscale/gen/go/headscale/v1" | ||||
| @ -33,6 +34,8 @@ func init() { | ||||
| 		Bool("ephemeral", false, "Preauthkey for ephemeral nodes") | ||||
| 	createPreAuthKeyCmd.Flags(). | ||||
| 		StringP("expiration", "e", DefaultPreAuthKeyExpiry, "Human-readable expiration of the key (e.g. 30m, 24h)") | ||||
| 	createPreAuthKeyCmd.Flags(). | ||||
| 		StringSlice("tags", []string{}, "Tags to automatically assign to node") | ||||
| } | ||||
| 
 | ||||
| var preauthkeysCmd = &cobra.Command{ | ||||
| @ -81,7 +84,16 @@ var listPreAuthKeys = &cobra.Command{ | ||||
| 		} | ||||
| 
 | ||||
| 		tableData := pterm.TableData{ | ||||
| 			{"ID", "Key", "Reusable", "Ephemeral", "Used", "Expiration", "Created"}, | ||||
| 			{ | ||||
| 				"ID", | ||||
| 				"Key", | ||||
| 				"Reusable", | ||||
| 				"Ephemeral", | ||||
| 				"Used", | ||||
| 				"Expiration", | ||||
| 				"Created", | ||||
| 				"Tags", | ||||
| 			}, | ||||
| 		} | ||||
| 		for _, key := range response.PreAuthKeys { | ||||
| 			expiration := "-" | ||||
| @ -96,6 +108,14 @@ var listPreAuthKeys = &cobra.Command{ | ||||
| 				reusable = fmt.Sprintf("%v", key.GetReusable()) | ||||
| 			} | ||||
| 
 | ||||
| 			aclTags := "" | ||||
| 
 | ||||
| 			for _, tag := range key.AclTags { | ||||
| 				aclTags += "," + tag | ||||
| 			} | ||||
| 
 | ||||
| 			aclTags = strings.TrimLeft(aclTags, ",") | ||||
| 
 | ||||
| 			tableData = append(tableData, []string{ | ||||
| 				key.GetId(), | ||||
| 				key.GetKey(), | ||||
| @ -104,6 +124,7 @@ var listPreAuthKeys = &cobra.Command{ | ||||
| 				strconv.FormatBool(key.GetUsed()), | ||||
| 				expiration, | ||||
| 				key.GetCreatedAt().AsTime().Format("2006-01-02 15:04:05"), | ||||
| 				aclTags, | ||||
| 			}) | ||||
| 
 | ||||
| 		} | ||||
| @ -136,6 +157,7 @@ var createPreAuthKeyCmd = &cobra.Command{ | ||||
| 
 | ||||
| 		reusable, _ := cmd.Flags().GetBool("reusable") | ||||
| 		ephemeral, _ := cmd.Flags().GetBool("ephemeral") | ||||
| 		tags, _ := cmd.Flags().GetStringSlice("tags") | ||||
| 
 | ||||
| 		log.Trace(). | ||||
| 			Bool("reusable", reusable). | ||||
| @ -147,6 +169,7 @@ var createPreAuthKeyCmd = &cobra.Command{ | ||||
| 			Namespace: namespace, | ||||
| 			Reusable:  reusable, | ||||
| 			Ephemeral: ephemeral, | ||||
| 			AclTags:   tags, | ||||
| 		} | ||||
| 
 | ||||
| 		durationStr, _ := cmd.Flags().GetString("expiration") | ||||
|  | ||||
							
								
								
									
										5
									
								
								db.go
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								db.go
									
									
									
									
									
								
							| @ -131,6 +131,11 @@ func (h *Headscale) initDB() error { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	err = db.AutoMigrate(&PreAuthKeyACLTag{}) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	_ = db.Migrator().DropTable("shared_machines") | ||||
| 
 | ||||
| 	err = db.AutoMigrate(&APIKey{}) | ||||
|  | ||||
| @ -126,6 +126,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | ||||
| 		false, | ||||
| 		false, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 	) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| @ -134,6 +135,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | ||||
| 		false, | ||||
| 		false, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 	) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| @ -142,6 +144,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | ||||
| 		false, | ||||
| 		false, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 	) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| @ -150,6 +153,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) { | ||||
| 		false, | ||||
| 		false, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 	) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| @ -269,6 +273,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | ||||
| 		false, | ||||
| 		false, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 	) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| @ -277,6 +282,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | ||||
| 		false, | ||||
| 		false, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 	) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| @ -285,6 +291,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | ||||
| 		false, | ||||
| 		false, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 	) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| @ -293,6 +300,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) { | ||||
| 		false, | ||||
| 		false, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 	) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc-gen-go v1.28.1
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/apikey.proto
 | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc-gen-go v1.28.1
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/device.proto
 | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc-gen-go v1.28.1
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/headscale.proto
 | ||||
| 
 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,4 +1,8 @@ | ||||
| // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // - protoc-gen-go-grpc v1.2.0
 | ||||
| // - protoc             (unknown)
 | ||||
| // source: headscale/v1/headscale.proto
 | ||||
| 
 | ||||
| package v1 | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc-gen-go v1.28.1
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/machine.proto
 | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc-gen-go v1.28.1
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/namespace.proto
 | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc-gen-go v1.28.1
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/preauthkey.proto
 | ||||
| 
 | ||||
| @ -34,6 +34,7 @@ type PreAuthKey struct { | ||||
| 	Used       bool                   `protobuf:"varint,6,opt,name=used,proto3" json:"used,omitempty"` | ||||
| 	Expiration *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=expiration,proto3" json:"expiration,omitempty"` | ||||
| 	CreatedAt  *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` | ||||
| 	AclTags    []string               `protobuf:"bytes,9,rep,name=acl_tags,json=aclTags,proto3" json:"acl_tags,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (x *PreAuthKey) Reset() { | ||||
| @ -124,6 +125,13 @@ func (x *PreAuthKey) GetCreatedAt() *timestamppb.Timestamp { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (x *PreAuthKey) GetAclTags() []string { | ||||
| 	if x != nil { | ||||
| 		return x.AclTags | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| type CreatePreAuthKeyRequest struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| @ -133,6 +141,7 @@ type CreatePreAuthKeyRequest struct { | ||||
| 	Reusable   bool                   `protobuf:"varint,2,opt,name=reusable,proto3" json:"reusable,omitempty"` | ||||
| 	Ephemeral  bool                   `protobuf:"varint,3,opt,name=ephemeral,proto3" json:"ephemeral,omitempty"` | ||||
| 	Expiration *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=expiration,proto3" json:"expiration,omitempty"` | ||||
| 	AclTags    []string               `protobuf:"bytes,5,rep,name=acl_tags,json=aclTags,proto3" json:"acl_tags,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (x *CreatePreAuthKeyRequest) Reset() { | ||||
| @ -195,6 +204,13 @@ func (x *CreatePreAuthKeyRequest) GetExpiration() *timestamppb.Timestamp { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (x *CreatePreAuthKeyRequest) GetAclTags() []string { | ||||
| 	if x != nil { | ||||
| 		return x.AclTags | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| type CreatePreAuthKeyResponse struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| @ -436,7 +452,7 @@ var file_headscale_v1_preauthkey_proto_rawDesc = []byte{ | ||||
| 	0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 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, 0x91, | ||||
| 	0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xac, | ||||
| 	0x02, 0x0a, 0x0a, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, | ||||
| 	0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, | ||||
| 	0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, | ||||
| @ -454,42 +470,45 @@ var file_headscale_v1_preauthkey_proto_rawDesc = []byte{ | ||||
| 	0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, | ||||
| 	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, | ||||
| 	0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, | ||||
| 	0x41, 0x74, 0x22, 0xad, 0x01, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, | ||||
| 	0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, | ||||
| 	0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, | ||||
| 	0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, | ||||
| 	0x72, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, | ||||
| 	0x72, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x70, 0x68, 0x65, | ||||
| 	0x6d, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x70, 0x68, | ||||
| 	0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, | ||||
| 	0x74, 0x69, 0x6f, 0x6e, 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, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, | ||||
| 	0x6f, 0x6e, 0x22, 0x56, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x41, | ||||
| 	0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, | ||||
| 	0x0a, 0x0c, 0x70, 0x72, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, | ||||
| 	0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, | ||||
| 	0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x0a, | ||||
| 	0x70, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x22, 0x49, 0x0a, 0x17, 0x45, 0x78, | ||||
| 	0x70, 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, | ||||
| 	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, | ||||
| 	0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, | ||||
| 	0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, | ||||
| 	0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x1a, 0x0a, 0x18, 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, 0x36, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, | ||||
| 	0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, | ||||
| 	0x41, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x63, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x09, | ||||
| 	0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x22, 0xc8, 0x01, | ||||
| 	0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, | ||||
| 	0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, | ||||
| 	0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, | ||||
| 	0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x75, 0x73, 0x61, | ||||
| 	0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x75, 0x73, 0x61, | ||||
| 	0x62, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, | ||||
| 	0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, | ||||
| 	0x6c, 0x12, 0x3a, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, | ||||
| 	0x08, 0x61, 0x63, 0x6c, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, | ||||
| 	0x07, 0x61, 0x63, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x22, 0x56, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, | ||||
| 	0x74, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, | ||||
| 	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, | ||||
| 	0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, | ||||
| 	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, | ||||
| 	0x68, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, | ||||
| 	0x22, 0x49, 0x0a, 0x17, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, | ||||
| 	0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, | ||||
| 	0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, | ||||
| 	0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x57, 0x0a, 0x17, 0x4c, 0x69, 0x73, | ||||
| 	0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, | ||||
| 	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, | ||||
| 	0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x65, | ||||
| 	0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x41, 0x75, | ||||
| 	0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 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, | ||||
| 	0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, | ||||
| 	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x1a, 0x0a, 0x18, 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, 0x36, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, | ||||
| 	0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, | ||||
| 	0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, | ||||
| 	0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, | ||||
| 	0x57, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, | ||||
| 	0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x72, | ||||
| 	0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, | ||||
| 	0x0b, 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, | ||||
| 	0x2e, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x0b, 0x70, 0x72, 0x65, | ||||
| 	0x41, 0x75, 0x74, 0x68, 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 ( | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT.
 | ||||
| // versions:
 | ||||
| // 	protoc-gen-go v1.27.1
 | ||||
| // 	protoc-gen-go v1.28.1
 | ||||
| // 	protoc        (unknown)
 | ||||
| // source: headscale/v1/routes.proto
 | ||||
| 
 | ||||
|  | ||||
| @ -824,6 +824,12 @@ | ||||
|         "expiration": { | ||||
|           "type": "string", | ||||
|           "format": "date-time" | ||||
|         }, | ||||
|         "aclTags": { | ||||
|           "type": "array", | ||||
|           "items": { | ||||
|             "type": "string" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @ -1102,6 +1108,12 @@ | ||||
|         "createdAt": { | ||||
|           "type": "string", | ||||
|           "format": "date-time" | ||||
|         }, | ||||
|         "aclTags": { | ||||
|           "type": "array", | ||||
|           "items": { | ||||
|             "type": "string" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|  | ||||
							
								
								
									
										10
									
								
								grpcv1.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								grpcv1.go
									
									
									
									
									
								
							| @ -106,11 +106,21 @@ func (api headscaleV1APIServer) CreatePreAuthKey( | ||||
| 		expiration = request.GetExpiration().AsTime() | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tag := range request.AclTags { | ||||
| 		err := validateTag(tag) | ||||
| 		if err != nil { | ||||
| 			return &v1.CreatePreAuthKeyResponse{ | ||||
| 				PreAuthKey: nil, | ||||
| 			}, status.Error(codes.InvalidArgument, err.Error()) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	preAuthKey, err := api.h.CreatePreAuthKey( | ||||
| 		request.GetNamespace(), | ||||
| 		request.GetReusable(), | ||||
| 		request.GetEphemeral(), | ||||
| 		&expiration, | ||||
| 		request.AclTags, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
|  | ||||
| @ -260,6 +260,8 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommand() { | ||||
| 				"24h", | ||||
| 				"--output", | ||||
| 				"json", | ||||
| 				"--tags", | ||||
| 				"tag:test1,tag:test2", | ||||
| 			}, | ||||
| 			[]string{}, | ||||
| 		) | ||||
| @ -333,6 +335,11 @@ func (s *IntegrationCLITestSuite) TestPreAuthKeyCommand() { | ||||
| 		listedPreAuthKeys[4].Expiration.AsTime().Before(time.Now().Add(time.Hour*26)), | ||||
| 	) | ||||
| 
 | ||||
| 	// Test that tags are present
 | ||||
| 	for i := 0; i < count; i++ { | ||||
| 		assert.Equal(s.T(), listedPreAuthKeys[i].AclTags, []string{"tag:test1", "tag:test2"}) | ||||
| 	} | ||||
| 
 | ||||
| 	// Expire three keys
 | ||||
| 	for i := 0; i < 3; i++ { | ||||
| 		_, _, err := ExecuteCommand( | ||||
|  | ||||
| @ -18,7 +18,7 @@ func (s *Suite) TestGetMachine(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachine("test", "testmachine") | ||||
| @ -44,7 +44,7 @@ func (s *Suite) TestGetMachineByID(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachineByID(0) | ||||
| @ -70,7 +70,7 @@ func (s *Suite) TestGetMachineByNodeKey(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachineByID(0) | ||||
| @ -98,7 +98,7 @@ func (s *Suite) TestGetMachineByAnyNodeKey(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachineByID(0) | ||||
| @ -171,7 +171,7 @@ func (s *Suite) TestListPeers(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachineByID(0) | ||||
| @ -214,7 +214,7 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) { | ||||
| 	for _, name := range []string{"test", "admin"} { | ||||
| 		namespace, err := app.CreateNamespace(name) | ||||
| 		c.Assert(err, check.IsNil) | ||||
| 		pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 		pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 		c.Assert(err, check.IsNil) | ||||
| 		stor = append(stor, base{namespace, pak}) | ||||
| 	} | ||||
| @ -294,7 +294,7 @@ func (s *Suite) TestExpireMachine(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachine("test", "testmachine") | ||||
| @ -350,7 +350,7 @@ func (s *Suite) TestSetTags(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachine("test", "testmachine") | ||||
|  | ||||
| @ -31,7 +31,7 @@ func (s *Suite) TestDestroyNamespaceErrors(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	err = app.DestroyNamespace("test") | ||||
| @ -44,7 +44,7 @@ func (s *Suite) TestDestroyNamespaceErrors(c *check.C) { | ||||
| 	namespace, err = app.CreateNamespace("test") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err = app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err = app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	machine := Machine{ | ||||
| @ -107,6 +107,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | ||||
| 		false, | ||||
| 		false, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 	) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| @ -115,6 +116,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | ||||
| 		false, | ||||
| 		false, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 	) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| @ -123,6 +125,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | ||||
| 		false, | ||||
| 		false, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 	) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| @ -131,6 +134,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) { | ||||
| 		false, | ||||
| 		false, | ||||
| 		nil, | ||||
| 		nil, | ||||
| 	) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| @ -380,7 +384,7 @@ func (s *Suite) TestSetMachineNamespace(c *check.C) { | ||||
| 	newNamespace, err := app.CreateNamespace("new") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(oldNamespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(oldNamespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	machine := Machine{ | ||||
|  | ||||
| @ -6,6 +6,7 @@ import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	v1 "github.com/juanfont/headscale/gen/go/headscale/v1" | ||||
| @ -18,6 +19,7 @@ const ( | ||||
| 	ErrPreAuthKeyExpired           = Error("AuthKey expired") | ||||
| 	ErrSingleUseAuthKeyHasBeenUsed = Error("AuthKey has already been used") | ||||
| 	ErrNamespaceMismatch           = Error("namespace mismatch") | ||||
| 	ErrPreAuthKeyACLTagInvalid     = Error("AuthKey tag is invalid") | ||||
| ) | ||||
| 
 | ||||
| // PreAuthKey describes a pre-authorization key usable in a particular namespace.
 | ||||
| @ -29,23 +31,38 @@ type PreAuthKey struct { | ||||
| 	Reusable    bool | ||||
| 	Ephemeral   bool `gorm:"default:false"` | ||||
| 	Used        bool `gorm:"default:false"` | ||||
| 	ACLTags     []PreAuthKeyACLTag | ||||
| 
 | ||||
| 	CreatedAt  *time.Time | ||||
| 	Expiration *time.Time | ||||
| } | ||||
| 
 | ||||
| // PreAuthKeyACLTag describes an autmatic tag applied to a node when registered with the associated PreAuthKey.
 | ||||
| type PreAuthKeyACLTag struct { | ||||
| 	ID           uint64 `gorm:"primary_key"` | ||||
| 	PreAuthKeyID uint64 | ||||
| 	Tag          string | ||||
| } | ||||
| 
 | ||||
| // CreatePreAuthKey creates a new PreAuthKey in a namespace, and returns it.
 | ||||
| func (h *Headscale) CreatePreAuthKey( | ||||
| 	namespaceName string, | ||||
| 	reusable bool, | ||||
| 	ephemeral bool, | ||||
| 	expiration *time.Time, | ||||
| 	aclTags []string, | ||||
| ) (*PreAuthKey, error) { | ||||
| 	namespace, err := h.GetNamespace(namespaceName) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tag := range aclTags { | ||||
| 		if !strings.HasPrefix(tag, "tag:") { | ||||
| 			return nil, fmt.Errorf("%w: '%s' did not begin with 'tag:'", ErrPreAuthKeyACLTagInvalid, tag) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	now := time.Now().UTC() | ||||
| 	kstr, err := h.generateKey() | ||||
| 	if err != nil { | ||||
| @ -62,8 +79,32 @@ func (h *Headscale) CreatePreAuthKey( | ||||
| 		Expiration:  expiration, | ||||
| 	} | ||||
| 
 | ||||
| 	if err := h.db.Save(&key).Error; err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create key in the database: %w", err) | ||||
| 	err = h.db.Transaction(func(db *gorm.DB) error { | ||||
| 		if err := db.Save(&key).Error; err != nil { | ||||
| 			return fmt.Errorf("failed to create key in the database: %w", err) | ||||
| 		} | ||||
| 
 | ||||
| 		if len(aclTags) > 0 { | ||||
| 			seenTags := map[string]bool{} | ||||
| 
 | ||||
| 			for _, tag := range aclTags { | ||||
| 				if !seenTags[tag] { | ||||
| 					if err := db.Save(&PreAuthKeyACLTag{PreAuthKeyID: key.ID, Tag: tag}).Error; err != nil { | ||||
| 						return fmt.Errorf( | ||||
| 							"failed to ceate key tag in the database: %w", | ||||
| 							err, | ||||
| 						) | ||||
| 					} | ||||
| 					seenTags[tag] = true | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return nil | ||||
| 	}) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &key, nil | ||||
| @ -77,7 +118,7 @@ func (h *Headscale) ListPreAuthKeys(namespaceName string) ([]PreAuthKey, error) | ||||
| 	} | ||||
| 
 | ||||
| 	keys := []PreAuthKey{} | ||||
| 	if err := h.db.Preload("Namespace").Where(&PreAuthKey{NamespaceID: namespace.ID}).Find(&keys).Error; err != nil { | ||||
| 	if err := h.db.Preload("Namespace").Preload("ACLTags").Where(&PreAuthKey{NamespaceID: namespace.ID}).Find(&keys).Error; err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| @ -101,11 +142,17 @@ func (h *Headscale) GetPreAuthKey(namespace string, key string) (*PreAuthKey, er | ||||
| // DestroyPreAuthKey destroys a preauthkey. Returns error if the PreAuthKey
 | ||||
| // does not exist.
 | ||||
| func (h *Headscale) DestroyPreAuthKey(pak PreAuthKey) error { | ||||
| 	if result := h.db.Unscoped().Delete(pak); result.Error != nil { | ||||
| 		return result.Error | ||||
| 	} | ||||
| 	return h.db.Transaction(func(db *gorm.DB) error { | ||||
| 		if result := db.Unscoped().Where(PreAuthKeyACLTag{PreAuthKeyID: pak.ID}).Delete(&PreAuthKeyACLTag{}); result.Error != nil { | ||||
| 			return result.Error | ||||
| 		} | ||||
| 
 | ||||
| 	return nil | ||||
| 		if result := db.Unscoped().Delete(pak); result.Error != nil { | ||||
| 			return result.Error | ||||
| 		} | ||||
| 
 | ||||
| 		return nil | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // MarkExpirePreAuthKey marks a PreAuthKey as expired.
 | ||||
| @ -131,7 +178,7 @@ func (h *Headscale) UsePreAuthKey(k *PreAuthKey) error { | ||||
| // If returns no error and a PreAuthKey, it can be used.
 | ||||
| func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) { | ||||
| 	pak := PreAuthKey{} | ||||
| 	if result := h.db.Preload("Namespace").First(&pak, "key = ?", k); errors.Is( | ||||
| 	if result := h.db.Preload("Namespace").Preload("ACLTags").First(&pak, "key = ?", k); errors.Is( | ||||
| 		result.Error, | ||||
| 		gorm.ErrRecordNotFound, | ||||
| 	) { | ||||
| @ -176,6 +223,7 @@ func (key *PreAuthKey) toProto() *v1.PreAuthKey { | ||||
| 		Ephemeral: key.Ephemeral, | ||||
| 		Reusable:  key.Reusable, | ||||
| 		Used:      key.Used, | ||||
| 		AclTags:   make([]string, len(key.ACLTags)), | ||||
| 	} | ||||
| 
 | ||||
| 	if key.Expiration != nil { | ||||
| @ -186,5 +234,9 @@ func (key *PreAuthKey) toProto() *v1.PreAuthKey { | ||||
| 		protoKey.CreatedAt = timestamppb.New(*key.CreatedAt) | ||||
| 	} | ||||
| 
 | ||||
| 	for idx := range key.ACLTags { | ||||
| 		protoKey.AclTags[idx] = key.ACLTags[idx].Tag | ||||
| 	} | ||||
| 
 | ||||
| 	return &protoKey | ||||
| } | ||||
|  | ||||
| @ -7,14 +7,14 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| func (*Suite) TestCreatePreAuthKey(c *check.C) { | ||||
| 	_, err := app.CreatePreAuthKey("bogus", true, false, nil) | ||||
| 	_, err := app.CreatePreAuthKey("bogus", true, false, nil, nil) | ||||
| 
 | ||||
| 	c.Assert(err, check.NotNil) | ||||
| 
 | ||||
| 	namespace, err := app.CreateNamespace("test") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	key, err := app.CreatePreAuthKey(namespace.Name, true, false, nil) | ||||
| 	key, err := app.CreatePreAuthKey(namespace.Name, true, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	// Did we get a valid key?
 | ||||
| @ -40,7 +40,7 @@ func (*Suite) TestExpiredPreAuthKey(c *check.C) { | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	now := time.Now() | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, true, false, &now) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, true, false, &now, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	key, err := app.checkKeyValidity(pak.Key) | ||||
| @ -58,7 +58,7 @@ func (*Suite) TestValidateKeyOk(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test3") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, true, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, true, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	key, err := app.checkKeyValidity(pak.Key) | ||||
| @ -70,7 +70,7 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test4") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	machine := Machine{ | ||||
| @ -94,7 +94,7 @@ func (*Suite) TestReusableBeingUsedKey(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test5") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, true, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, true, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	machine := Machine{ | ||||
| @ -118,7 +118,7 @@ func (*Suite) TestNotReusableNotBeingUsedKey(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test6") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	key, err := app.checkKeyValidity(pak.Key) | ||||
| @ -130,7 +130,7 @@ func (*Suite) TestEphemeralKey(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test7") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, true, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, true, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	now := time.Now() | ||||
| @ -165,7 +165,7 @@ func (*Suite) TestExpirePreauthKey(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test3") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, true, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, true, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(pak.Expiration, check.IsNil) | ||||
| 
 | ||||
| @ -182,7 +182,7 @@ func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test6") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	pak.Used = true | ||||
| 	app.db.Save(&pak) | ||||
| @ -190,3 +190,20 @@ func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) { | ||||
| 	_, err = app.checkKeyValidity(pak.Key) | ||||
| 	c.Assert(err, check.Equals, ErrSingleUseAuthKeyHasBeenUsed) | ||||
| } | ||||
| 
 | ||||
| func (*Suite) TestPreAuthKeyACLTags(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test8") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.CreatePreAuthKey(namespace.Name, false, false, nil, []string{"badtag"}) | ||||
| 	c.Assert(err, check.NotNil) // Confirm that malformed tags are rejected
 | ||||
| 
 | ||||
| 	tags := []string{"tag:test1", "tag:test2"} | ||||
| 	tagsWithDuplicate := []string{"tag:test1", "tag:test2", "tag:test2"} | ||||
| 	_, err = app.CreatePreAuthKey(namespace.Name, false, false, nil, tagsWithDuplicate) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	listedPaks, err := app.ListPreAuthKeys("test8") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 	c.Assert(listedPaks[0].toProto().AclTags, check.DeepEquals, tags) | ||||
| } | ||||
|  | ||||
| @ -13,6 +13,7 @@ message PreAuthKey { | ||||
|     bool                      used       = 6; | ||||
|     google.protobuf.Timestamp expiration = 7; | ||||
|     google.protobuf.Timestamp created_at = 8; | ||||
|     repeated string           acl_tags   = 9; | ||||
| } | ||||
| 
 | ||||
| message CreatePreAuthKeyRequest { | ||||
| @ -20,6 +21,7 @@ message CreatePreAuthKeyRequest { | ||||
|     bool                      reusable   = 2; | ||||
|     bool                      ephemeral  = 3; | ||||
|     google.protobuf.Timestamp expiration = 4; | ||||
|     repeated string           acl_tags   = 5; | ||||
| } | ||||
| 
 | ||||
| message CreatePreAuthKeyResponse { | ||||
|  | ||||
| @ -353,6 +353,24 @@ func (h *Headscale) handleAuthKeyCommon( | ||||
| 
 | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		aclTags := pak.toProto().AclTags | ||||
| 		if len(aclTags) > 0 { | ||||
| 			// This conditional preserves the existing behaviour, although SaaS would reset the tags on auth-key login
 | ||||
| 			err = h.SetTags(machine, aclTags) | ||||
| 
 | ||||
| 			if err != nil { | ||||
| 				log.Error(). | ||||
| 					Caller(). | ||||
| 					Bool("noise", machineKey.IsZero()). | ||||
| 					Str("machine", machine.Hostname). | ||||
| 					Strs("aclTags", aclTags). | ||||
| 					Err(err). | ||||
| 					Msg("Failed to set tags after refreshing machine") | ||||
| 
 | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		now := time.Now().UTC() | ||||
| 
 | ||||
| @ -378,6 +396,7 @@ func (h *Headscale) handleAuthKeyCommon( | ||||
| 			NodeKey:        nodeKey, | ||||
| 			LastSeen:       &now, | ||||
| 			AuthKeyID:      uint(pak.ID), | ||||
| 			ForcedTags:     pak.toProto().AclTags, | ||||
| 		} | ||||
| 
 | ||||
| 		machine, err = h.RegisterMachine( | ||||
|  | ||||
| @ -11,7 +11,7 @@ func (s *Suite) TestGetRoutes(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachine("test", "test_get_route_machine") | ||||
| @ -55,7 +55,7 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachine("test", "test_enable_route_machine") | ||||
|  | ||||
| @ -25,7 +25,7 @@ func (s *Suite) TestGetUsedIps(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test-ip") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachine("test", "testmachine") | ||||
| @ -73,7 +73,7 @@ func (s *Suite) TestGetMultiIp(c *check.C) { | ||||
| 		ips, err := app.getAvailableIPs() | ||||
| 		c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 		pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 		pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 		c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 		_, err = app.GetMachine("test", "testmachine") | ||||
| @ -163,7 +163,7 @@ func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) { | ||||
| 	namespace, err := app.CreateNamespace("test-ip") | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil) | ||||
| 	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil) | ||||
| 	c.Assert(err, check.IsNil) | ||||
| 
 | ||||
| 	_, err = app.GetMachine("test", "testmachine") | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user