From 0db30e14724ae13d7037dff561a01b00b681bbb4 Mon Sep 17 00:00:00 2001 From: Bela Lemle Date: Mon, 24 Jul 2023 12:18:24 +0200 Subject: [PATCH] [ASSIGN CUSTOM IP] optionally assign a custom ip when registering a machine --- cmd/headscale/cli/nodes.go | 17 +++++++++- gen/go/headscale/v1/machine.pb.go | 13 +++++-- .../headscale/v1/headscale.swagger.json | 6 ++++ hscontrol/db/machine.go | 34 +++++++++++++++++-- hscontrol/grpcv1.go | 2 ++ hscontrol/oidc.go | 1 + hscontrol/types/machine.go | 1 + proto/headscale/v1/machine.proto | 1 + 8 files changed, 70 insertions(+), 5 deletions(-) diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index 31a06773..136adc5e 100644 --- a/cmd/headscale/cli/nodes.go +++ b/cmd/headscale/cli/nodes.go @@ -30,7 +30,7 @@ func init() { nodeCmd.AddCommand(listNodesCmd) registerNodeCmd.Flags().StringP("user", "u", "", "User") - + registerNodeCmd.Flags().StringP("ip", "p", "", "Ip") registerNodeCmd.Flags().StringP("namespace", "n", "", "User") registerNodeNamespaceFlag := registerNodeCmd.Flags().Lookup("namespace") registerNodeNamespaceFlag.Deprecated = deprecateNamespaceMessage @@ -132,9 +132,24 @@ var registerNodeCmd = &cobra.Command{ return } + ip, err := cmd.Flags().GetString("ip") + + if ip == "" { + ip = "0" + } + + if err != nil { + ErrorOutput( + err, + fmt.Sprintf("Error getting node ip from flag: %s", err), + output, + ) + } + request := &v1.RegisterMachineRequest{ Key: machineKey, User: user, + Ip: ip, } response, err := client.RegisterMachine(ctx, request) diff --git a/gen/go/headscale/v1/machine.pb.go b/gen/go/headscale/v1/machine.pb.go index c6b70dfb..3993bdda 100644 --- a/gen/go/headscale/v1/machine.pb.go +++ b/gen/go/headscale/v1/machine.pb.go @@ -263,6 +263,7 @@ type RegisterMachineRequest struct { User string `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"` Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Ip string `protobuf:"bytes,3,opt,name=ip,proto3" json:"ip,omitempty"` } func (x *RegisterMachineRequest) Reset() { @@ -311,6 +312,13 @@ func (x *RegisterMachineRequest) GetKey() string { return "" } +func (x *RegisterMachineRequest) GetIp() string { + if x != nil { + return x.Ip + } + return "" +} + type RegisterMachineResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1207,11 +1215,12 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{ 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x4a, 0x04, - 0x08, 0x0e, 0x10, 0x12, 0x22, 0x3e, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x08, 0x0e, 0x10, 0x12, 0x22, 0x4e, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x22, 0x4a, 0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x70, 0x22, 0x4a, 0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, diff --git a/gen/openapiv2/headscale/v1/headscale.swagger.json b/gen/openapiv2/headscale/v1/headscale.swagger.json index 83103c16..7a290ce6 100644 --- a/gen/openapiv2/headscale/v1/headscale.swagger.json +++ b/gen/openapiv2/headscale/v1/headscale.swagger.json @@ -193,6 +193,12 @@ "in": "query", "required": false, "type": "string" + }, + { + "name": "ip", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ diff --git a/hscontrol/db/machine.go b/hscontrol/db/machine.go index f2139abb..b14685b2 100644 --- a/hscontrol/db/machine.go +++ b/hscontrol/db/machine.go @@ -355,6 +355,7 @@ func (hsdb *HSDatabase) RegisterMachineFromAuthCallback( userName string, machineExpiry *time.Time, registrationMethod string, + requestedIP string, ) (*types.Machine, error) { nodeKey := key.NodePublic{} err := nodeKey.UnmarshalText([]byte(nodeKeyStr)) @@ -365,6 +366,7 @@ func (hsdb *HSDatabase) RegisterMachineFromAuthCallback( log.Debug(). Str("nodeKey", nodeKey.ShortString()). Str("userName", userName). + Str("requestedIp", requestedIP). Str("registrationMethod", registrationMethod). Str("expiresAt", fmt.Sprintf("%v", machineExpiry)). Msg("Registering machine from API/CLI or auth callback") @@ -387,6 +389,7 @@ func (hsdb *HSDatabase) RegisterMachineFromAuthCallback( registrationMachine.UserID = user.ID registrationMachine.RegisterMethod = registrationMethod + registrationMachine.RequestedIP = requestedIP if machineExpiry != nil { registrationMachine.Expiry = machineExpiry @@ -417,6 +420,7 @@ func (hsdb *HSDatabase) RegisterMachine(machine types.Machine, Str("machine_key", machine.MachineKey). Str("node_key", machine.NodeKey). Str("user", machine.User.Name). + Str("requestedIp", machine.RequestedIP). Msg("Registering machine") // If the machine exists and we had already IPs for it, we just save it @@ -452,7 +456,33 @@ func (hsdb *HSDatabase) RegisterMachine(machine types.Machine, return nil, err } - machine.IPAddresses = ips + if machine.RequestedIP != "0" { + parsedip, err := netip.ParseAddr(machine.RequestedIP) + if err != nil { + return nil, err + } + + usedIps, err := hsdb.getUsedIPs() + if err != nil { + return nil, err + } + + var staticip types.MachineAddresses + if !usedIps.Contains(parsedip) { + avaliableipv6, err := netip.ParseAddr(ips.StringSlice()[0]) + if err != nil { + return nil, err + } + + staticip = append(staticip, avaliableipv6) + staticip = append(staticip, parsedip) + machine.IPAddresses = staticip + } else { + return nil, err + } + } else { + machine.IPAddresses = ips + } if err := hsdb.db.Save(&machine).Error; err != nil { return nil, fmt.Errorf("failed register(save) machine in the database: %w", err) @@ -461,7 +491,7 @@ func (hsdb *HSDatabase) RegisterMachine(machine types.Machine, log.Trace(). Caller(). Str("machine", machine.Hostname). - Str("ip", strings.Join(ips.StringSlice(), ",")). + Str("ip", strings.Join(machine.IPAddresses.StringSlice(), ",")). Msg("Machine registered with the database") return &machine, nil diff --git a/hscontrol/grpcv1.go b/hscontrol/grpcv1.go index 74950c20..a3b8b954 100644 --- a/hscontrol/grpcv1.go +++ b/hscontrol/grpcv1.go @@ -173,6 +173,7 @@ func (api headscaleV1APIServer) RegisterMachine( log.Trace(). Str("user", request.GetUser()). Str("node_key", request.GetKey()). + Str("ip", request.GetIp()). Msg("Registering machine") machine, err := api.h.db.RegisterMachineFromAuthCallback( @@ -181,6 +182,7 @@ func (api headscaleV1APIServer) RegisterMachine( request.GetUser(), nil, util.RegisterMethodCLI, + request.GetIp(), ) if err != nil { return nil, err diff --git a/hscontrol/oidc.go b/hscontrol/oidc.go index 66383838..585f14ce 100644 --- a/hscontrol/oidc.go +++ b/hscontrol/oidc.go @@ -651,6 +651,7 @@ func (h *Headscale) registerMachineForOIDCCallback( user.Name, &expiry, util.RegisterMethodOIDC, + "0", ); err != nil { util.LogErr(err, "could not register machine") writer.Header().Set("Content-Type", "text/plain; charset=utf-8") diff --git a/hscontrol/types/machine.go b/hscontrol/types/machine.go index 4e5a940f..fc76d9b3 100644 --- a/hscontrol/types/machine.go +++ b/hscontrol/types/machine.go @@ -30,6 +30,7 @@ type Machine struct { NodeKey string DiscoKey string IPAddresses MachineAddresses + RequestedIP string // Hostname represents the name given by the Tailscale // client during registration diff --git a/proto/headscale/v1/machine.proto b/proto/headscale/v1/machine.proto index e3e6e530..e1dffa44 100644 --- a/proto/headscale/v1/machine.proto +++ b/proto/headscale/v1/machine.proto @@ -50,6 +50,7 @@ message Machine { message RegisterMachineRequest { string user = 1; string key = 2; + string ip = 3; } message RegisterMachineResponse {