From 5cdc6bfcbe47d94871dbdee625240ba21782f392 Mon Sep 17 00:00:00 2001 From: Bela Lemle Date: Tue, 25 Jul 2023 14:49:33 +0200 Subject: [PATCH] [ASSIGN CUSTOM IP] register new machine with custom IPv4 address --- cmd/headscale/cli/nodes.go | 12 ++++++- docs/running-headscale-container.md | 6 ++++ docs/running-headscale-linux-manual.md | 6 ++++ docs/running-headscale-linux.md | 6 ++++ docs/running-headscale-openbsd.md | 6 ++++ hscontrol/auth.go | 1 + hscontrol/db/addresses.go | 18 ++++++++++ hscontrol/db/machine.go | 46 ++++++++++++++++++++++++-- hscontrol/grpcv1.go | 2 ++ hscontrol/oidc.go | 1 + proto/headscale/v1/machine.proto | 1 + 11 files changed, 101 insertions(+), 4 deletions(-) diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go index 31a06773..065384c9 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,19 @@ var registerNodeCmd = &cobra.Command{ return } + ip, err := cmd.Flags().GetString("ip") + 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/docs/running-headscale-container.md b/docs/running-headscale-container.md index 18dacd92..deb02ac4 100644 --- a/docs/running-headscale-container.md +++ b/docs/running-headscale-container.md @@ -127,6 +127,12 @@ docker exec headscale \ headscale --user myfirstuser nodes register --key ``` +Optionally use the --ip flag to assign a custom IPv4 address to your machine: + +```shell +docker exec headscale \ + headscale --user myfirstuser nodes register --ip --key +``` ### Register machine using a pre authenticated key Generate a key using the command line: diff --git a/docs/running-headscale-linux-manual.md b/docs/running-headscale-linux-manual.md index 03619d7a..2b51613c 100644 --- a/docs/running-headscale-linux-manual.md +++ b/docs/running-headscale-linux-manual.md @@ -102,6 +102,12 @@ Register the machine: headscale --user myfirstuser nodes register --key ``` +Optionally use the --ip flag to assign a custom IPv4 address to your machine: + +```shell +headscale --user myfirstuser nodes register --ip --key +``` + ### Register machine using a pre authenticated key Generate a key using the command line: diff --git a/docs/running-headscale-linux.md b/docs/running-headscale-linux.md index 66ccc3d3..06d99f8a 100644 --- a/docs/running-headscale-linux.md +++ b/docs/running-headscale-linux.md @@ -79,6 +79,12 @@ Register the machine: headscale --user myfirstuser nodes register --key ``` +Optionally use the --ip flag to assign a custom IPv4 address to your machine: + +```shell +headscale --user myfirstuser nodes register --ip --key +``` + ### Register machine using a pre authenticated key Generate a key using the command line: diff --git a/docs/running-headscale-openbsd.md b/docs/running-headscale-openbsd.md index b76c9135..18763278 100644 --- a/docs/running-headscale-openbsd.md +++ b/docs/running-headscale-openbsd.md @@ -139,6 +139,12 @@ Register the machine: headscale --user myfirstuser nodes register --key ``` +Optionally use the --ip flag to assign a custom IPv4 address to your machine: + +```shell +headscale --user myfirstuser nodes register --ip --key +``` + ### Register machine using a pre authenticated key Generate a key using the command line: diff --git a/hscontrol/auth.go b/hscontrol/auth.go index 43dfd2b0..c43fc35a 100644 --- a/hscontrol/auth.go +++ b/hscontrol/auth.go @@ -368,6 +368,7 @@ func (h *Headscale) handleAuthKey( machine, err = h.db.RegisterMachine( machineToRegister, + "", ) if err != nil { log.Error(). diff --git a/hscontrol/db/addresses.go b/hscontrol/db/addresses.go index 1a7d35de..20e32794 100644 --- a/hscontrol/db/addresses.go +++ b/hscontrol/db/addresses.go @@ -17,6 +17,24 @@ import ( var ErrCouldNotAllocateIP = errors.New("could not find any suitable IP") +func (hsdb *HSDatabase) isInRange(address netip.Addr) (bool, error) { + counter := 0 + for _, ipPrefix := range hsdb.ipPrefixes { + ipPrefixNetworkAddress, ipPrefixBroadcastAddress := util.GetIPPrefixEndpoints(ipPrefix) + + if ipPrefixNetworkAddress.Compare(address) == -1 && + ipPrefixBroadcastAddress.Compare(address) == 1 { + counter += 1 + } + } + + if counter > 0 { + return true, nil + } + + return false, ErrRequestedIPNotInPrefixRange +} + func (hsdb *HSDatabase) getAvailableIPs() (types.MachineAddresses, error) { var ips types.MachineAddresses var err error diff --git a/hscontrol/db/machine.go b/hscontrol/db/machine.go index f2139abb..614459e7 100644 --- a/hscontrol/db/machine.go +++ b/hscontrol/db/machine.go @@ -31,6 +31,7 @@ var ( ErrDifferentRegisteredUser = errors.New( "machine was previously registered with a different user", ) + ErrRequestedIPNotInPrefixRange = errors.New("the ip you provided is not in the configured prefix range") ) // ListPeers returns all peers of machine, regardless of any Policy or if the node is expired. @@ -355,6 +356,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 +367,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") @@ -394,6 +397,7 @@ func (hsdb *HSDatabase) RegisterMachineFromAuthCallback( machine, err := hsdb.RegisterMachine( registrationMachine, + requestedIP, ) if err == nil { @@ -410,13 +414,14 @@ func (hsdb *HSDatabase) RegisterMachineFromAuthCallback( } // RegisterMachine is executed from the CLI to register a new Machine using its MachineKey. -func (hsdb *HSDatabase) RegisterMachine(machine types.Machine, +func (hsdb *HSDatabase) RegisterMachine(machine types.Machine, requestedIP string, ) (*types.Machine, error) { log.Debug(). Str("machine", machine.Hostname). Str("machine_key", machine.MachineKey). Str("node_key", machine.NodeKey). Str("user", machine.User.Name). + Str("requestedIp", requestedIP). Msg("Registering machine") // If the machine exists and we had already IPs for it, we just save it @@ -452,7 +457,42 @@ func (hsdb *HSDatabase) RegisterMachine(machine types.Machine, return nil, err } - machine.IPAddresses = ips + if requestedIP != "" { + parsedip, err := netip.ParseAddr(requestedIP) + if err != nil { + return nil, err + } + + inrange, err := hsdb.isInRange(parsedip) + if !inrange { + 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()[1]) + // if ips[1].Is4() { + // avaliableipv6, err = netip.ParseAddr(ips.StringSlice()[0]) + //} + + // if err != nil { + // return nil, err + //} + + 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 +501,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..f0067d54 100644 --- a/hscontrol/oidc.go +++ b/hscontrol/oidc.go @@ -651,6 +651,7 @@ func (h *Headscale) registerMachineForOIDCCallback( user.Name, &expiry, util.RegisterMethodOIDC, + "", ); err != nil { util.LogErr(err, "could not register machine") writer.Header().Set("Content-Type", "text/plain; charset=utf-8") 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 {