mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-28 10:51:44 +01:00 
			
		
		
		
	Merge branch 'main' into sanitise-machine-key-url
This commit is contained in:
		
						commit
						d06ba7b522
					
				@ -16,6 +16,8 @@
 | 
			
		||||
- 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)
 | 
			
		||||
- Sanitise the node key passed to registration url [#823](https://github.com/juanfont/headscale/pull/823)
 | 
			
		||||
- Add support for generating pre-auth keys with tags [#767](https://github.com/juanfont/headscale/pull/767)
 | 
			
		||||
- Add support for evaluating `autoApprovers` ACL entries when a machine is registered [#763](https://github.com/juanfont/headscale/pull/763)
 | 
			
		||||
 | 
			
		||||
## 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")
 | 
			
		||||
 | 
			
		||||
@ -11,11 +11,12 @@ import (
 | 
			
		||||
 | 
			
		||||
// ACLPolicy represents a Tailscale ACL Policy.
 | 
			
		||||
type ACLPolicy struct {
 | 
			
		||||
	Groups    Groups    `json:"groups"    yaml:"groups"`
 | 
			
		||||
	Hosts     Hosts     `json:"hosts"     yaml:"hosts"`
 | 
			
		||||
	TagOwners TagOwners `json:"tagOwners" yaml:"tagOwners"`
 | 
			
		||||
	ACLs      []ACL     `json:"acls"      yaml:"acls"`
 | 
			
		||||
	Tests     []ACLTest `json:"tests"     yaml:"tests"`
 | 
			
		||||
	Groups        Groups        `json:"groups"        yaml:"groups"`
 | 
			
		||||
	Hosts         Hosts         `json:"hosts"         yaml:"hosts"`
 | 
			
		||||
	TagOwners     TagOwners     `json:"tagOwners"     yaml:"tagOwners"`
 | 
			
		||||
	ACLs          []ACL         `json:"acls"          yaml:"acls"`
 | 
			
		||||
	Tests         []ACLTest     `json:"tests"         yaml:"tests"`
 | 
			
		||||
	AutoApprovers AutoApprovers `json:"autoApprovers" yaml:"autoApprovers"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ACL is a basic rule for the ACL Policy.
 | 
			
		||||
@ -42,6 +43,13 @@ type ACLTest struct {
 | 
			
		||||
	Deny   []string `json:"deny,omitempty" yaml:"deny,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AutoApprovers specify which users (namespaces?), groups or tags have their advertised routes
 | 
			
		||||
// or exit node status automatically enabled.
 | 
			
		||||
type AutoApprovers struct {
 | 
			
		||||
	Routes   map[string][]string `json:"routes"   yaml:"routes"`
 | 
			
		||||
	ExitNode []string            `json:"exitNode" yaml:"exitNode"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON allows to parse the Hosts directly into netip objects.
 | 
			
		||||
func (hosts *Hosts) UnmarshalJSON(data []byte) error {
 | 
			
		||||
	newHosts := Hosts{}
 | 
			
		||||
@ -100,3 +108,28 @@ func (policy ACLPolicy) IsZero() bool {
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the list of autoApproving namespaces, groups or tags for a given IPPrefix.
 | 
			
		||||
func (autoApprovers *AutoApprovers) GetRouteApprovers(
 | 
			
		||||
	prefix netip.Prefix,
 | 
			
		||||
) ([]string, error) {
 | 
			
		||||
	if prefix.Bits() == 0 {
 | 
			
		||||
		return autoApprovers.ExitNode, nil // 0.0.0.0/0, ::/0 or equivalent
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	approverAliases := []string{}
 | 
			
		||||
 | 
			
		||||
	for autoApprovedPrefix, autoApproverAliases := range autoApprovers.Routes {
 | 
			
		||||
		autoApprovedPrefix, err := netip.ParsePrefix(autoApprovedPrefix)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if autoApprovedPrefix.Bits() >= prefix.Bits() &&
 | 
			
		||||
			autoApprovedPrefix.Contains(prefix.Masked().Addr()) {
 | 
			
		||||
			approverAliases = append(approverAliases, autoApproverAliases...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return approverAliases, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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(
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										58
									
								
								machine.go
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								machine.go
									
									
									
									
									
								
							@ -949,6 +949,64 @@ func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Enabled any routes advertised by a machine that match the ACL autoApprovers policy.
 | 
			
		||||
func (h *Headscale) EnableAutoApprovedRoutes(machine *Machine) {
 | 
			
		||||
	if len(machine.IPAddresses) == 0 {
 | 
			
		||||
		return // This machine has no IPAddresses, so can't possibly match any autoApprovers ACLs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	approvedRoutes := make([]netip.Prefix, 0, len(machine.HostInfo.RoutableIPs))
 | 
			
		||||
	thisMachine := []Machine{*machine}
 | 
			
		||||
 | 
			
		||||
	for _, advertisedRoute := range machine.HostInfo.RoutableIPs {
 | 
			
		||||
		if contains(machine.EnabledRoutes, advertisedRoute) {
 | 
			
		||||
			continue // Skip routes that are already enabled for the node
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		routeApprovers, err := h.aclPolicy.AutoApprovers.GetRouteApprovers(
 | 
			
		||||
			advertisedRoute,
 | 
			
		||||
		)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Err(err).
 | 
			
		||||
				Str("advertisedRoute", advertisedRoute.String()).
 | 
			
		||||
				Uint64("machineId", machine.ID).
 | 
			
		||||
				Msg("Failed to resolve autoApprovers for advertised route")
 | 
			
		||||
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, approvedAlias := range routeApprovers {
 | 
			
		||||
			if approvedAlias == machine.Namespace.Name {
 | 
			
		||||
				approvedRoutes = append(approvedRoutes, advertisedRoute)
 | 
			
		||||
			} else {
 | 
			
		||||
				approvedIps, err := expandAlias(thisMachine, *h.aclPolicy, approvedAlias, h.cfg.OIDC.StripEmaildomain)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Err(err).
 | 
			
		||||
						Str("alias", approvedAlias).
 | 
			
		||||
						Msg("Failed to expand alias when processing autoApprovers policy")
 | 
			
		||||
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// approvedIPs should contain all of machine's IPs if it matches the rule, so check for first
 | 
			
		||||
				if contains(approvedIps, machine.IPAddresses[0].String()) {
 | 
			
		||||
					approvedRoutes = append(approvedRoutes, advertisedRoute)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, approvedRoute := range approvedRoutes {
 | 
			
		||||
		if !contains(machine.EnabledRoutes, approvedRoute) {
 | 
			
		||||
			log.Info().
 | 
			
		||||
				Str("route", approvedRoute.String()).
 | 
			
		||||
				Uint64("client", machine.ID).
 | 
			
		||||
				Msg("Enabling autoApproved route for client")
 | 
			
		||||
			machine.EnabledRoutes = append(machine.EnabledRoutes, approvedRoute)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (machine *Machine) RoutesToProto() *v1.Routes {
 | 
			
		||||
	availableRoutes := machine.GetAdvertisedRoutes()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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")
 | 
			
		||||
@ -1050,3 +1050,44 @@ func TestHeadscale_GenerateGivenName(t *testing.T) {
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Suite) TestAutoApproveRoutes(c *check.C) {
 | 
			
		||||
	err := app.LoadACLPolicy("./tests/acls/acl_policy_autoapprovers.hujson")
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	namespace, err := app.CreateNamespace("test")
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil, nil)
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	nodeKey := key.NewNode()
 | 
			
		||||
 | 
			
		||||
	defaultRoute := netip.MustParsePrefix("0.0.0.0/0")
 | 
			
		||||
	route1 := netip.MustParsePrefix("10.10.0.0/16")
 | 
			
		||||
	route2 := netip.MustParsePrefix("10.11.0.0/16")
 | 
			
		||||
 | 
			
		||||
	machine := Machine{
 | 
			
		||||
		ID:             0,
 | 
			
		||||
		MachineKey:     "foo",
 | 
			
		||||
		NodeKey:        NodePublicKeyStripPrefix(nodeKey.Public()),
 | 
			
		||||
		DiscoKey:       "faa",
 | 
			
		||||
		Hostname:       "test",
 | 
			
		||||
		NamespaceID:    namespace.ID,
 | 
			
		||||
		RegisterMethod: RegisterMethodAuthKey,
 | 
			
		||||
		AuthKeyID:      uint(pak.ID),
 | 
			
		||||
		HostInfo: HostInfo{
 | 
			
		||||
			RequestTags: []string{"tag:exit"},
 | 
			
		||||
			RoutableIPs: []netip.Prefix{defaultRoute, route1, route2},
 | 
			
		||||
		},
 | 
			
		||||
		IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.1")},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	app.db.Save(&machine)
 | 
			
		||||
 | 
			
		||||
	machine0ByID, err := app.GetMachineByID(0)
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	app.EnableAutoApprovedRoutes(machine0ByID)
 | 
			
		||||
	c.Assert(machine0ByID.GetEnabledRoutes(), check.HasLen, 3)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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(
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,11 @@ func (h *Headscale) handlePollCommon(
 | 
			
		||||
				Str("machine", machine.Hostname).
 | 
			
		||||
				Err(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// update routes with peer information
 | 
			
		||||
		h.EnableAutoApprovedRoutes(machine)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// From Tailscale client:
 | 
			
		||||
	//
 | 
			
		||||
	// ReadOnly is whether the client just wants to fetch the MapResponse,
 | 
			
		||||
 | 
			
		||||
@ -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")
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								tests/acls/acl_policy_autoapprovers.hujson
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								tests/acls/acl_policy_autoapprovers.hujson
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
// This ACL validates autoApprovers support for
 | 
			
		||||
// exit nodes and advertised routes
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    "tagOwners": {
 | 
			
		||||
        "tag:exit": ["test"],
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    "groups": {
 | 
			
		||||
        "group:test": ["test"]
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    "acls": [
 | 
			
		||||
        {"action": "accept", "users": ["*"], "ports": ["*:*"]},
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    "autoApprovers": {
 | 
			
		||||
        "exitNode": ["tag:exit"],
 | 
			
		||||
        "routes": {
 | 
			
		||||
            "10.10.0.0/16": ["group:test"],
 | 
			
		||||
            "10.11.0.0/16": ["test"],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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