mirror of
https://github.com/juanfont/headscale.git
synced 2024-11-26 08:53:05 +00:00
Migrate IP fields in database to dedicated columns (#1869)
This commit is contained in:
parent
85cef84e17
commit
2ce23df45a
39 changed files with 1885 additions and 1055 deletions
|
@ -39,6 +39,7 @@ after improving the test harness as part of adopting [#1460](https://github.com/
|
||||||
- `/var/lib/headscale` and `/var/run/headscale` is no longer created automatically, see [container docs](./docs/running-headscale-container.md)
|
- `/var/lib/headscale` and `/var/run/headscale` is no longer created automatically, see [container docs](./docs/running-headscale-container.md)
|
||||||
- Prefixes are now defined per v4 and v6 range. [#1756](https://github.com/juanfont/headscale/pull/1756)
|
- Prefixes are now defined per v4 and v6 range. [#1756](https://github.com/juanfont/headscale/pull/1756)
|
||||||
- `ip_prefixes` option is now `prefixes.v4` and `prefixes.v6`
|
- `ip_prefixes` option is now `prefixes.v4` and `prefixes.v6`
|
||||||
|
- `prefixes.allocation` can be set to assign IPs at `sequential` or `random`. [#1869](https://github.com/juanfont/headscale/pull/1869)
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ after improving the test harness as part of adopting [#1460](https://github.com/
|
||||||
- Turn off gRPC logging [#1640](https://github.com/juanfont/headscale/pull/1640) fixes [#1259](https://github.com/juanfont/headscale/issues/1259)
|
- Turn off gRPC logging [#1640](https://github.com/juanfont/headscale/pull/1640) fixes [#1259](https://github.com/juanfont/headscale/issues/1259)
|
||||||
- Added the possibility to manually create a DERP-map entry which can be customized, instead of automatically creating it. [#1565](https://github.com/juanfont/headscale/pull/1565)
|
- Added the possibility to manually create a DERP-map entry which can be customized, instead of automatically creating it. [#1565](https://github.com/juanfont/headscale/pull/1565)
|
||||||
- Add support for deleting api keys [#1702](https://github.com/juanfont/headscale/pull/1702)
|
- Add support for deleting api keys [#1702](https://github.com/juanfont/headscale/pull/1702)
|
||||||
|
- Add command to backfill IP addresses for nodes missing IPs from configured prefixes. [#1869](https://github.com/juanfont/headscale/pull/1869)
|
||||||
|
|
||||||
## 0.22.3 (2023-05-12)
|
## 0.22.3 (2023-05-12)
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,8 @@ func init() {
|
||||||
tagCmd.Flags().
|
tagCmd.Flags().
|
||||||
StringSliceP("tags", "t", []string{}, "List of tags to add to the node")
|
StringSliceP("tags", "t", []string{}, "List of tags to add to the node")
|
||||||
nodeCmd.AddCommand(tagCmd)
|
nodeCmd.AddCommand(tagCmd)
|
||||||
|
|
||||||
|
nodeCmd.AddCommand(backfillNodeIPsCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
var nodeCmd = &cobra.Command{
|
var nodeCmd = &cobra.Command{
|
||||||
|
@ -477,6 +479,57 @@ var moveNodeCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var backfillNodeIPsCmd = &cobra.Command{
|
||||||
|
Use: "backfillips",
|
||||||
|
Short: "Backfill IPs missing from nodes",
|
||||||
|
Long: `
|
||||||
|
Backfill IPs can be used to add/remove IPs from nodes
|
||||||
|
based on the current configuration of Headscale.
|
||||||
|
|
||||||
|
If there are nodes that does not have IPv4 or IPv6
|
||||||
|
even if prefixes for both are configured in the config,
|
||||||
|
this command can be used to assign IPs of the sort to
|
||||||
|
all nodes that are missing.
|
||||||
|
|
||||||
|
If you remove IPv4 or IPv6 prefixes from the config,
|
||||||
|
it can be run to remove the IPs that should no longer
|
||||||
|
be assigned to nodes.`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
var err error
|
||||||
|
output, _ := cmd.Flags().GetString("output")
|
||||||
|
|
||||||
|
confirm := false
|
||||||
|
prompt := &survey.Confirm{
|
||||||
|
Message: "Are you sure that you want to assign/remove IPs to/from nodes?",
|
||||||
|
}
|
||||||
|
err = survey.AskOne(prompt, &confirm)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if confirm {
|
||||||
|
ctx, client, conn, cancel := getHeadscaleCLIClient()
|
||||||
|
defer cancel()
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
changes, err := client.BackfillNodeIPs(ctx, &v1.BackfillNodeIPsRequest{Confirmed: confirm})
|
||||||
|
if err != nil {
|
||||||
|
ErrorOutput(
|
||||||
|
err,
|
||||||
|
fmt.Sprintf(
|
||||||
|
"Error backfilling IPs: %s",
|
||||||
|
status.Convert(err).Message(),
|
||||||
|
),
|
||||||
|
output,
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
SuccessOutput(changes, "Node IPs backfilled successfully", output)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func nodesToPtables(
|
func nodesToPtables(
|
||||||
currentUser string,
|
currentUser string,
|
||||||
showTags bool,
|
showTags bool,
|
||||||
|
|
|
@ -61,6 +61,11 @@ prefixes:
|
||||||
v6: fd7a:115c:a1e0::/48
|
v6: fd7a:115c:a1e0::/48
|
||||||
v4: 100.64.0.0/10
|
v4: 100.64.0.0/10
|
||||||
|
|
||||||
|
# Strategy used for allocation of IPs to nodes, available options:
|
||||||
|
# - sequential (default): assigns the next free IP from the previous given IP.
|
||||||
|
# - random: assigns the next free IP from a pseudo-random IP generator (crypto/rand).
|
||||||
|
allocation: sequential
|
||||||
|
|
||||||
# DERP is a relay system that Tailscale uses when a direct
|
# DERP is a relay system that Tailscale uses when a direct
|
||||||
# connection cannot be established.
|
# connection cannot be established.
|
||||||
# https://tailscale.com/blog/how-tailscale-works/#encrypted-tcp-relays-derp
|
# https://tailscale.com/blog/how-tailscale-works/#encrypted-tcp-relays-derp
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.32.0
|
// protoc-gen-go v1.33.0
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/apikey.proto
|
// source: headscale/v1/apikey.proto
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.32.0
|
// protoc-gen-go v1.33.0
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/device.proto
|
// source: headscale/v1/device.proto
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.32.0
|
// protoc-gen-go v1.33.0
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/headscale.proto
|
// source: headscale/v1/headscale.proto
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ var file_headscale_v1_headscale_proto_rawDesc = []byte{
|
||||||
0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73,
|
0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73,
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c,
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c,
|
||||||
0x65, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
0x65, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
0x6f, 0x32, 0xfd, 0x17, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x53,
|
0x6f, 0x32, 0x80, 0x19, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x53,
|
||||||
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x63, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65,
|
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x63, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65,
|
||||||
0x72, 0x12, 0x1c, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31,
|
0x72, 0x12, 0x1c, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31,
|
||||||
0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||||
|
@ -161,77 +161,85 @@ var file_headscale_v1_headscale_proto_rawDesc = []byte{
|
||||||
0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23,
|
0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23,
|
||||||
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f,
|
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x22, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f,
|
||||||
0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x7b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75,
|
0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x7b, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x75,
|
||||||
0x73, 0x65, 0x72, 0x12, 0x64, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73,
|
0x73, 0x65, 0x72, 0x12, 0x80, 0x01, 0x0a, 0x0f, 0x42, 0x61, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x6c,
|
||||||
0x12, 0x1e, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e,
|
0x4e, 0x6f, 0x64, 0x65, 0x49, 0x50, 0x73, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
|
||||||
0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x6c, 0x4e,
|
||||||
0x1a, 0x1f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e,
|
0x6f, 0x64, 0x65, 0x49, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e,
|
||||||
0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63,
|
||||||
0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f,
|
0x6b, 0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70,
|
||||||
0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x7c, 0x0a, 0x0b, 0x45, 0x6e, 0x61,
|
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x18, 0x2f, 0x61,
|
||||||
0x62, 0x6c, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73,
|
0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x66,
|
||||||
0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f,
|
0x69, 0x6c, 0x6c, 0x69, 0x70, 0x73, 0x12, 0x64, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75,
|
||||||
0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x68, 0x65, 0x61,
|
0x74, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e,
|
||||||
0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65,
|
0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75,
|
||||||
0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82,
|
0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e,
|
||||||
0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x20, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72,
|
0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
|
||||||
0x6f, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x7d,
|
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61,
|
||||||
0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x61,
|
0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x7c, 0x0a, 0x0b,
|
||||||
0x62, 0x6c, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73,
|
0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x68, 0x65,
|
||||||
0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52,
|
0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c,
|
||||||
0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65,
|
0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e,
|
||||||
|
0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61,
|
||||||
|
0x62, 0x6c, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
|
0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x22, 0x20, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76,
|
||||||
|
0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f,
|
||||||
|
0x69, 0x64, 0x7d, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x0c, 0x44,
|
||||||
|
0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x21, 0x2e, 0x68, 0x65,
|
||||||
0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x61, 0x62,
|
0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x61, 0x62,
|
||||||
0x6c, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
0x6c, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22,
|
||||||
0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x22, 0x21, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31,
|
0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69,
|
||||||
0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x69,
|
0x73, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||||
0x64, 0x7d, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x7f, 0x0a, 0x0d, 0x47, 0x65,
|
0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x22, 0x21, 0x2f, 0x61, 0x70, 0x69,
|
||||||
0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x68, 0x65,
|
0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x75, 0x74,
|
||||||
0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f,
|
0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x7f, 0x0a,
|
||||||
0x64, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
0x0d, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x22,
|
||||||
0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47,
|
0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65,
|
||||||
0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
|
0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x61,
|
0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76,
|
||||||
0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x7b, 0x6e, 0x6f, 0x64, 0x65,
|
0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52,
|
||||||
0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x75, 0x0a, 0x0b, 0x44,
|
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12,
|
||||||
0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x68, 0x65, 0x61,
|
0x1d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x7b, 0x6e,
|
||||||
0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
|
0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x75,
|
||||||
0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x68,
|
0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x20, 0x2e,
|
||||||
0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65,
|
0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c,
|
||||||
0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||||
0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x2a, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31,
|
0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44,
|
||||||
0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x69,
|
0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||||
0x64, 0x7d, 0x12, 0x70, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b,
|
0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x2a, 0x19, 0x2f, 0x61, 0x70, 0x69,
|
||||||
0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76,
|
0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x72, 0x6f, 0x75, 0x74,
|
||||||
0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65,
|
0x65, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x70, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41,
|
||||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c,
|
0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c,
|
||||||
0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65,
|
0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65,
|
||||||
0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02,
|
0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73,
|
||||||
0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70,
|
0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70,
|
||||||
0x69, 0x6b, 0x65, 0x79, 0x12, 0x77, 0x0a, 0x0c, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70,
|
0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3,
|
||||||
0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65,
|
0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31,
|
||||||
0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79,
|
0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x12, 0x77, 0x0a, 0x0c, 0x45, 0x78, 0x70, 0x69, 0x72,
|
||||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
|
0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
|
||||||
0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69,
|
0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69,
|
||||||
0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4,
|
0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61,
|
||||||
0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f,
|
0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65,
|
||||||
0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x2f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x12, 0x6a, 0x0a,
|
0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20,
|
||||||
0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x20, 0x2e, 0x68,
|
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f,
|
||||||
0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74,
|
0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x2f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65,
|
||||||
0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21,
|
0x12, 0x6a, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x12,
|
||||||
0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69,
|
0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c,
|
||||||
0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f,
|
0x74, 0x1a, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31,
|
||||||
0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x12, 0x76, 0x0a, 0x0c, 0x44, 0x65, 0x6c,
|
0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70,
|
||||||
0x65, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64,
|
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61,
|
||||||
0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41,
|
0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x12, 0x76, 0x0a, 0x0c,
|
||||||
0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68,
|
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68,
|
||||||
0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65,
|
0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65,
|
||||||
0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||||
0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x2a, 0x17, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76,
|
0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44,
|
||||||
0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x2f, 0x7b, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
|
0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
0x7d, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x2a, 0x17, 0x2f, 0x61, 0x70,
|
||||||
0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61,
|
0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x2f, 0x7b, 0x70, 0x72, 0x65,
|
||||||
0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72,
|
0x66, 0x69, 0x78, 0x7d, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
||||||
0x6f, 0x74, 0x6f, 0x33,
|
0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64,
|
||||||
|
0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62,
|
||||||
|
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_headscale_v1_headscale_proto_goTypes = []interface{}{
|
var file_headscale_v1_headscale_proto_goTypes = []interface{}{
|
||||||
|
@ -252,41 +260,43 @@ var file_headscale_v1_headscale_proto_goTypes = []interface{}{
|
||||||
(*RenameNodeRequest)(nil), // 14: headscale.v1.RenameNodeRequest
|
(*RenameNodeRequest)(nil), // 14: headscale.v1.RenameNodeRequest
|
||||||
(*ListNodesRequest)(nil), // 15: headscale.v1.ListNodesRequest
|
(*ListNodesRequest)(nil), // 15: headscale.v1.ListNodesRequest
|
||||||
(*MoveNodeRequest)(nil), // 16: headscale.v1.MoveNodeRequest
|
(*MoveNodeRequest)(nil), // 16: headscale.v1.MoveNodeRequest
|
||||||
(*GetRoutesRequest)(nil), // 17: headscale.v1.GetRoutesRequest
|
(*BackfillNodeIPsRequest)(nil), // 17: headscale.v1.BackfillNodeIPsRequest
|
||||||
(*EnableRouteRequest)(nil), // 18: headscale.v1.EnableRouteRequest
|
(*GetRoutesRequest)(nil), // 18: headscale.v1.GetRoutesRequest
|
||||||
(*DisableRouteRequest)(nil), // 19: headscale.v1.DisableRouteRequest
|
(*EnableRouteRequest)(nil), // 19: headscale.v1.EnableRouteRequest
|
||||||
(*GetNodeRoutesRequest)(nil), // 20: headscale.v1.GetNodeRoutesRequest
|
(*DisableRouteRequest)(nil), // 20: headscale.v1.DisableRouteRequest
|
||||||
(*DeleteRouteRequest)(nil), // 21: headscale.v1.DeleteRouteRequest
|
(*GetNodeRoutesRequest)(nil), // 21: headscale.v1.GetNodeRoutesRequest
|
||||||
(*CreateApiKeyRequest)(nil), // 22: headscale.v1.CreateApiKeyRequest
|
(*DeleteRouteRequest)(nil), // 22: headscale.v1.DeleteRouteRequest
|
||||||
(*ExpireApiKeyRequest)(nil), // 23: headscale.v1.ExpireApiKeyRequest
|
(*CreateApiKeyRequest)(nil), // 23: headscale.v1.CreateApiKeyRequest
|
||||||
(*ListApiKeysRequest)(nil), // 24: headscale.v1.ListApiKeysRequest
|
(*ExpireApiKeyRequest)(nil), // 24: headscale.v1.ExpireApiKeyRequest
|
||||||
(*DeleteApiKeyRequest)(nil), // 25: headscale.v1.DeleteApiKeyRequest
|
(*ListApiKeysRequest)(nil), // 25: headscale.v1.ListApiKeysRequest
|
||||||
(*GetUserResponse)(nil), // 26: headscale.v1.GetUserResponse
|
(*DeleteApiKeyRequest)(nil), // 26: headscale.v1.DeleteApiKeyRequest
|
||||||
(*CreateUserResponse)(nil), // 27: headscale.v1.CreateUserResponse
|
(*GetUserResponse)(nil), // 27: headscale.v1.GetUserResponse
|
||||||
(*RenameUserResponse)(nil), // 28: headscale.v1.RenameUserResponse
|
(*CreateUserResponse)(nil), // 28: headscale.v1.CreateUserResponse
|
||||||
(*DeleteUserResponse)(nil), // 29: headscale.v1.DeleteUserResponse
|
(*RenameUserResponse)(nil), // 29: headscale.v1.RenameUserResponse
|
||||||
(*ListUsersResponse)(nil), // 30: headscale.v1.ListUsersResponse
|
(*DeleteUserResponse)(nil), // 30: headscale.v1.DeleteUserResponse
|
||||||
(*CreatePreAuthKeyResponse)(nil), // 31: headscale.v1.CreatePreAuthKeyResponse
|
(*ListUsersResponse)(nil), // 31: headscale.v1.ListUsersResponse
|
||||||
(*ExpirePreAuthKeyResponse)(nil), // 32: headscale.v1.ExpirePreAuthKeyResponse
|
(*CreatePreAuthKeyResponse)(nil), // 32: headscale.v1.CreatePreAuthKeyResponse
|
||||||
(*ListPreAuthKeysResponse)(nil), // 33: headscale.v1.ListPreAuthKeysResponse
|
(*ExpirePreAuthKeyResponse)(nil), // 33: headscale.v1.ExpirePreAuthKeyResponse
|
||||||
(*DebugCreateNodeResponse)(nil), // 34: headscale.v1.DebugCreateNodeResponse
|
(*ListPreAuthKeysResponse)(nil), // 34: headscale.v1.ListPreAuthKeysResponse
|
||||||
(*GetNodeResponse)(nil), // 35: headscale.v1.GetNodeResponse
|
(*DebugCreateNodeResponse)(nil), // 35: headscale.v1.DebugCreateNodeResponse
|
||||||
(*SetTagsResponse)(nil), // 36: headscale.v1.SetTagsResponse
|
(*GetNodeResponse)(nil), // 36: headscale.v1.GetNodeResponse
|
||||||
(*RegisterNodeResponse)(nil), // 37: headscale.v1.RegisterNodeResponse
|
(*SetTagsResponse)(nil), // 37: headscale.v1.SetTagsResponse
|
||||||
(*DeleteNodeResponse)(nil), // 38: headscale.v1.DeleteNodeResponse
|
(*RegisterNodeResponse)(nil), // 38: headscale.v1.RegisterNodeResponse
|
||||||
(*ExpireNodeResponse)(nil), // 39: headscale.v1.ExpireNodeResponse
|
(*DeleteNodeResponse)(nil), // 39: headscale.v1.DeleteNodeResponse
|
||||||
(*RenameNodeResponse)(nil), // 40: headscale.v1.RenameNodeResponse
|
(*ExpireNodeResponse)(nil), // 40: headscale.v1.ExpireNodeResponse
|
||||||
(*ListNodesResponse)(nil), // 41: headscale.v1.ListNodesResponse
|
(*RenameNodeResponse)(nil), // 41: headscale.v1.RenameNodeResponse
|
||||||
(*MoveNodeResponse)(nil), // 42: headscale.v1.MoveNodeResponse
|
(*ListNodesResponse)(nil), // 42: headscale.v1.ListNodesResponse
|
||||||
(*GetRoutesResponse)(nil), // 43: headscale.v1.GetRoutesResponse
|
(*MoveNodeResponse)(nil), // 43: headscale.v1.MoveNodeResponse
|
||||||
(*EnableRouteResponse)(nil), // 44: headscale.v1.EnableRouteResponse
|
(*BackfillNodeIPsResponse)(nil), // 44: headscale.v1.BackfillNodeIPsResponse
|
||||||
(*DisableRouteResponse)(nil), // 45: headscale.v1.DisableRouteResponse
|
(*GetRoutesResponse)(nil), // 45: headscale.v1.GetRoutesResponse
|
||||||
(*GetNodeRoutesResponse)(nil), // 46: headscale.v1.GetNodeRoutesResponse
|
(*EnableRouteResponse)(nil), // 46: headscale.v1.EnableRouteResponse
|
||||||
(*DeleteRouteResponse)(nil), // 47: headscale.v1.DeleteRouteResponse
|
(*DisableRouteResponse)(nil), // 47: headscale.v1.DisableRouteResponse
|
||||||
(*CreateApiKeyResponse)(nil), // 48: headscale.v1.CreateApiKeyResponse
|
(*GetNodeRoutesResponse)(nil), // 48: headscale.v1.GetNodeRoutesResponse
|
||||||
(*ExpireApiKeyResponse)(nil), // 49: headscale.v1.ExpireApiKeyResponse
|
(*DeleteRouteResponse)(nil), // 49: headscale.v1.DeleteRouteResponse
|
||||||
(*ListApiKeysResponse)(nil), // 50: headscale.v1.ListApiKeysResponse
|
(*CreateApiKeyResponse)(nil), // 50: headscale.v1.CreateApiKeyResponse
|
||||||
(*DeleteApiKeyResponse)(nil), // 51: headscale.v1.DeleteApiKeyResponse
|
(*ExpireApiKeyResponse)(nil), // 51: headscale.v1.ExpireApiKeyResponse
|
||||||
|
(*ListApiKeysResponse)(nil), // 52: headscale.v1.ListApiKeysResponse
|
||||||
|
(*DeleteApiKeyResponse)(nil), // 53: headscale.v1.DeleteApiKeyResponse
|
||||||
}
|
}
|
||||||
var file_headscale_v1_headscale_proto_depIdxs = []int32{
|
var file_headscale_v1_headscale_proto_depIdxs = []int32{
|
||||||
0, // 0: headscale.v1.HeadscaleService.GetUser:input_type -> headscale.v1.GetUserRequest
|
0, // 0: headscale.v1.HeadscaleService.GetUser:input_type -> headscale.v1.GetUserRequest
|
||||||
|
@ -306,43 +316,45 @@ var file_headscale_v1_headscale_proto_depIdxs = []int32{
|
||||||
14, // 14: headscale.v1.HeadscaleService.RenameNode:input_type -> headscale.v1.RenameNodeRequest
|
14, // 14: headscale.v1.HeadscaleService.RenameNode:input_type -> headscale.v1.RenameNodeRequest
|
||||||
15, // 15: headscale.v1.HeadscaleService.ListNodes:input_type -> headscale.v1.ListNodesRequest
|
15, // 15: headscale.v1.HeadscaleService.ListNodes:input_type -> headscale.v1.ListNodesRequest
|
||||||
16, // 16: headscale.v1.HeadscaleService.MoveNode:input_type -> headscale.v1.MoveNodeRequest
|
16, // 16: headscale.v1.HeadscaleService.MoveNode:input_type -> headscale.v1.MoveNodeRequest
|
||||||
17, // 17: headscale.v1.HeadscaleService.GetRoutes:input_type -> headscale.v1.GetRoutesRequest
|
17, // 17: headscale.v1.HeadscaleService.BackfillNodeIPs:input_type -> headscale.v1.BackfillNodeIPsRequest
|
||||||
18, // 18: headscale.v1.HeadscaleService.EnableRoute:input_type -> headscale.v1.EnableRouteRequest
|
18, // 18: headscale.v1.HeadscaleService.GetRoutes:input_type -> headscale.v1.GetRoutesRequest
|
||||||
19, // 19: headscale.v1.HeadscaleService.DisableRoute:input_type -> headscale.v1.DisableRouteRequest
|
19, // 19: headscale.v1.HeadscaleService.EnableRoute:input_type -> headscale.v1.EnableRouteRequest
|
||||||
20, // 20: headscale.v1.HeadscaleService.GetNodeRoutes:input_type -> headscale.v1.GetNodeRoutesRequest
|
20, // 20: headscale.v1.HeadscaleService.DisableRoute:input_type -> headscale.v1.DisableRouteRequest
|
||||||
21, // 21: headscale.v1.HeadscaleService.DeleteRoute:input_type -> headscale.v1.DeleteRouteRequest
|
21, // 21: headscale.v1.HeadscaleService.GetNodeRoutes:input_type -> headscale.v1.GetNodeRoutesRequest
|
||||||
22, // 22: headscale.v1.HeadscaleService.CreateApiKey:input_type -> headscale.v1.CreateApiKeyRequest
|
22, // 22: headscale.v1.HeadscaleService.DeleteRoute:input_type -> headscale.v1.DeleteRouteRequest
|
||||||
23, // 23: headscale.v1.HeadscaleService.ExpireApiKey:input_type -> headscale.v1.ExpireApiKeyRequest
|
23, // 23: headscale.v1.HeadscaleService.CreateApiKey:input_type -> headscale.v1.CreateApiKeyRequest
|
||||||
24, // 24: headscale.v1.HeadscaleService.ListApiKeys:input_type -> headscale.v1.ListApiKeysRequest
|
24, // 24: headscale.v1.HeadscaleService.ExpireApiKey:input_type -> headscale.v1.ExpireApiKeyRequest
|
||||||
25, // 25: headscale.v1.HeadscaleService.DeleteApiKey:input_type -> headscale.v1.DeleteApiKeyRequest
|
25, // 25: headscale.v1.HeadscaleService.ListApiKeys:input_type -> headscale.v1.ListApiKeysRequest
|
||||||
26, // 26: headscale.v1.HeadscaleService.GetUser:output_type -> headscale.v1.GetUserResponse
|
26, // 26: headscale.v1.HeadscaleService.DeleteApiKey:input_type -> headscale.v1.DeleteApiKeyRequest
|
||||||
27, // 27: headscale.v1.HeadscaleService.CreateUser:output_type -> headscale.v1.CreateUserResponse
|
27, // 27: headscale.v1.HeadscaleService.GetUser:output_type -> headscale.v1.GetUserResponse
|
||||||
28, // 28: headscale.v1.HeadscaleService.RenameUser:output_type -> headscale.v1.RenameUserResponse
|
28, // 28: headscale.v1.HeadscaleService.CreateUser:output_type -> headscale.v1.CreateUserResponse
|
||||||
29, // 29: headscale.v1.HeadscaleService.DeleteUser:output_type -> headscale.v1.DeleteUserResponse
|
29, // 29: headscale.v1.HeadscaleService.RenameUser:output_type -> headscale.v1.RenameUserResponse
|
||||||
30, // 30: headscale.v1.HeadscaleService.ListUsers:output_type -> headscale.v1.ListUsersResponse
|
30, // 30: headscale.v1.HeadscaleService.DeleteUser:output_type -> headscale.v1.DeleteUserResponse
|
||||||
31, // 31: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse
|
31, // 31: headscale.v1.HeadscaleService.ListUsers:output_type -> headscale.v1.ListUsersResponse
|
||||||
32, // 32: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse
|
32, // 32: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse
|
||||||
33, // 33: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse
|
33, // 33: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse
|
||||||
34, // 34: headscale.v1.HeadscaleService.DebugCreateNode:output_type -> headscale.v1.DebugCreateNodeResponse
|
34, // 34: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse
|
||||||
35, // 35: headscale.v1.HeadscaleService.GetNode:output_type -> headscale.v1.GetNodeResponse
|
35, // 35: headscale.v1.HeadscaleService.DebugCreateNode:output_type -> headscale.v1.DebugCreateNodeResponse
|
||||||
36, // 36: headscale.v1.HeadscaleService.SetTags:output_type -> headscale.v1.SetTagsResponse
|
36, // 36: headscale.v1.HeadscaleService.GetNode:output_type -> headscale.v1.GetNodeResponse
|
||||||
37, // 37: headscale.v1.HeadscaleService.RegisterNode:output_type -> headscale.v1.RegisterNodeResponse
|
37, // 37: headscale.v1.HeadscaleService.SetTags:output_type -> headscale.v1.SetTagsResponse
|
||||||
38, // 38: headscale.v1.HeadscaleService.DeleteNode:output_type -> headscale.v1.DeleteNodeResponse
|
38, // 38: headscale.v1.HeadscaleService.RegisterNode:output_type -> headscale.v1.RegisterNodeResponse
|
||||||
39, // 39: headscale.v1.HeadscaleService.ExpireNode:output_type -> headscale.v1.ExpireNodeResponse
|
39, // 39: headscale.v1.HeadscaleService.DeleteNode:output_type -> headscale.v1.DeleteNodeResponse
|
||||||
40, // 40: headscale.v1.HeadscaleService.RenameNode:output_type -> headscale.v1.RenameNodeResponse
|
40, // 40: headscale.v1.HeadscaleService.ExpireNode:output_type -> headscale.v1.ExpireNodeResponse
|
||||||
41, // 41: headscale.v1.HeadscaleService.ListNodes:output_type -> headscale.v1.ListNodesResponse
|
41, // 41: headscale.v1.HeadscaleService.RenameNode:output_type -> headscale.v1.RenameNodeResponse
|
||||||
42, // 42: headscale.v1.HeadscaleService.MoveNode:output_type -> headscale.v1.MoveNodeResponse
|
42, // 42: headscale.v1.HeadscaleService.ListNodes:output_type -> headscale.v1.ListNodesResponse
|
||||||
43, // 43: headscale.v1.HeadscaleService.GetRoutes:output_type -> headscale.v1.GetRoutesResponse
|
43, // 43: headscale.v1.HeadscaleService.MoveNode:output_type -> headscale.v1.MoveNodeResponse
|
||||||
44, // 44: headscale.v1.HeadscaleService.EnableRoute:output_type -> headscale.v1.EnableRouteResponse
|
44, // 44: headscale.v1.HeadscaleService.BackfillNodeIPs:output_type -> headscale.v1.BackfillNodeIPsResponse
|
||||||
45, // 45: headscale.v1.HeadscaleService.DisableRoute:output_type -> headscale.v1.DisableRouteResponse
|
45, // 45: headscale.v1.HeadscaleService.GetRoutes:output_type -> headscale.v1.GetRoutesResponse
|
||||||
46, // 46: headscale.v1.HeadscaleService.GetNodeRoutes:output_type -> headscale.v1.GetNodeRoutesResponse
|
46, // 46: headscale.v1.HeadscaleService.EnableRoute:output_type -> headscale.v1.EnableRouteResponse
|
||||||
47, // 47: headscale.v1.HeadscaleService.DeleteRoute:output_type -> headscale.v1.DeleteRouteResponse
|
47, // 47: headscale.v1.HeadscaleService.DisableRoute:output_type -> headscale.v1.DisableRouteResponse
|
||||||
48, // 48: headscale.v1.HeadscaleService.CreateApiKey:output_type -> headscale.v1.CreateApiKeyResponse
|
48, // 48: headscale.v1.HeadscaleService.GetNodeRoutes:output_type -> headscale.v1.GetNodeRoutesResponse
|
||||||
49, // 49: headscale.v1.HeadscaleService.ExpireApiKey:output_type -> headscale.v1.ExpireApiKeyResponse
|
49, // 49: headscale.v1.HeadscaleService.DeleteRoute:output_type -> headscale.v1.DeleteRouteResponse
|
||||||
50, // 50: headscale.v1.HeadscaleService.ListApiKeys:output_type -> headscale.v1.ListApiKeysResponse
|
50, // 50: headscale.v1.HeadscaleService.CreateApiKey:output_type -> headscale.v1.CreateApiKeyResponse
|
||||||
51, // 51: headscale.v1.HeadscaleService.DeleteApiKey:output_type -> headscale.v1.DeleteApiKeyResponse
|
51, // 51: headscale.v1.HeadscaleService.ExpireApiKey:output_type -> headscale.v1.ExpireApiKeyResponse
|
||||||
26, // [26:52] is the sub-list for method output_type
|
52, // 52: headscale.v1.HeadscaleService.ListApiKeys:output_type -> headscale.v1.ListApiKeysResponse
|
||||||
0, // [0:26] is the sub-list for method input_type
|
53, // 53: headscale.v1.HeadscaleService.DeleteApiKey:output_type -> headscale.v1.DeleteApiKeyResponse
|
||||||
|
27, // [27:54] is the sub-list for method output_type
|
||||||
|
0, // [0:27] is the sub-list for method input_type
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
0, // [0:0] is the sub-list for field type_name
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
|
|
@ -795,6 +795,42 @@ func local_request_HeadscaleService_MoveNode_0(ctx context.Context, marshaler ru
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
filter_HeadscaleService_BackfillNodeIPs_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
|
||||||
|
)
|
||||||
|
|
||||||
|
func request_HeadscaleService_BackfillNodeIPs_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq BackfillNodeIPsRequest
|
||||||
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
if err := req.ParseForm(); err != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
|
}
|
||||||
|
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_BackfillNodeIPs_0); err != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := client.BackfillNodeIPs(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func local_request_HeadscaleService_BackfillNodeIPs_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq BackfillNodeIPsRequest
|
||||||
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
if err := req.ParseForm(); err != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
|
}
|
||||||
|
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_HeadscaleService_BackfillNodeIPs_0); err != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := server.BackfillNodeIPs(ctx, &protoReq)
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func request_HeadscaleService_GetRoutes_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
func request_HeadscaleService_GetRoutes_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
var protoReq GetRoutesRequest
|
var protoReq GetRoutesRequest
|
||||||
var metadata runtime.ServerMetadata
|
var metadata runtime.ServerMetadata
|
||||||
|
@ -1574,6 +1610,31 @@ func RegisterHeadscaleServiceHandlerServer(ctx context.Context, mux *runtime.Ser
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_HeadscaleService_BackfillNodeIPs_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
|
defer cancel()
|
||||||
|
var stream runtime.ServerTransportStream
|
||||||
|
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||||
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
|
var err error
|
||||||
|
var annotatedContext context.Context
|
||||||
|
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/headscale.v1.HeadscaleService/BackfillNodeIPs", runtime.WithHTTPPathPattern("/api/v1/node/backfillips"))
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := local_request_HeadscaleService_BackfillNodeIPs_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||||
|
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||||
|
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_HeadscaleService_BackfillNodeIPs_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
mux.Handle("GET", pattern_HeadscaleService_GetRoutes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
mux.Handle("GET", pattern_HeadscaleService_GetRoutes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
@ -2214,6 +2275,28 @@ func RegisterHeadscaleServiceHandlerClient(ctx context.Context, mux *runtime.Ser
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_HeadscaleService_BackfillNodeIPs_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
|
defer cancel()
|
||||||
|
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||||
|
var err error
|
||||||
|
var annotatedContext context.Context
|
||||||
|
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/BackfillNodeIPs", runtime.WithHTTPPathPattern("/api/v1/node/backfillips"))
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := request_HeadscaleService_BackfillNodeIPs_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||||
|
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_HeadscaleService_BackfillNodeIPs_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
mux.Handle("GET", pattern_HeadscaleService_GetRoutes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
mux.Handle("GET", pattern_HeadscaleService_GetRoutes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
@ -2450,6 +2533,8 @@ var (
|
||||||
|
|
||||||
pattern_HeadscaleService_MoveNode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "node", "node_id", "user"}, ""))
|
pattern_HeadscaleService_MoveNode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "node", "node_id", "user"}, ""))
|
||||||
|
|
||||||
|
pattern_HeadscaleService_BackfillNodeIPs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "node", "backfillips"}, ""))
|
||||||
|
|
||||||
pattern_HeadscaleService_GetRoutes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "routes"}, ""))
|
pattern_HeadscaleService_GetRoutes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "routes"}, ""))
|
||||||
|
|
||||||
pattern_HeadscaleService_EnableRoute_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "routes", "route_id", "enable"}, ""))
|
pattern_HeadscaleService_EnableRoute_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "routes", "route_id", "enable"}, ""))
|
||||||
|
@ -2504,6 +2589,8 @@ var (
|
||||||
|
|
||||||
forward_HeadscaleService_MoveNode_0 = runtime.ForwardResponseMessage
|
forward_HeadscaleService_MoveNode_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
|
forward_HeadscaleService_BackfillNodeIPs_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
forward_HeadscaleService_GetRoutes_0 = runtime.ForwardResponseMessage
|
forward_HeadscaleService_GetRoutes_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
forward_HeadscaleService_EnableRoute_0 = runtime.ForwardResponseMessage
|
forward_HeadscaleService_EnableRoute_0 = runtime.ForwardResponseMessage
|
||||||
|
|
|
@ -36,6 +36,7 @@ const (
|
||||||
HeadscaleService_RenameNode_FullMethodName = "/headscale.v1.HeadscaleService/RenameNode"
|
HeadscaleService_RenameNode_FullMethodName = "/headscale.v1.HeadscaleService/RenameNode"
|
||||||
HeadscaleService_ListNodes_FullMethodName = "/headscale.v1.HeadscaleService/ListNodes"
|
HeadscaleService_ListNodes_FullMethodName = "/headscale.v1.HeadscaleService/ListNodes"
|
||||||
HeadscaleService_MoveNode_FullMethodName = "/headscale.v1.HeadscaleService/MoveNode"
|
HeadscaleService_MoveNode_FullMethodName = "/headscale.v1.HeadscaleService/MoveNode"
|
||||||
|
HeadscaleService_BackfillNodeIPs_FullMethodName = "/headscale.v1.HeadscaleService/BackfillNodeIPs"
|
||||||
HeadscaleService_GetRoutes_FullMethodName = "/headscale.v1.HeadscaleService/GetRoutes"
|
HeadscaleService_GetRoutes_FullMethodName = "/headscale.v1.HeadscaleService/GetRoutes"
|
||||||
HeadscaleService_EnableRoute_FullMethodName = "/headscale.v1.HeadscaleService/EnableRoute"
|
HeadscaleService_EnableRoute_FullMethodName = "/headscale.v1.HeadscaleService/EnableRoute"
|
||||||
HeadscaleService_DisableRoute_FullMethodName = "/headscale.v1.HeadscaleService/DisableRoute"
|
HeadscaleService_DisableRoute_FullMethodName = "/headscale.v1.HeadscaleService/DisableRoute"
|
||||||
|
@ -71,6 +72,7 @@ type HeadscaleServiceClient interface {
|
||||||
RenameNode(ctx context.Context, in *RenameNodeRequest, opts ...grpc.CallOption) (*RenameNodeResponse, error)
|
RenameNode(ctx context.Context, in *RenameNodeRequest, opts ...grpc.CallOption) (*RenameNodeResponse, error)
|
||||||
ListNodes(ctx context.Context, in *ListNodesRequest, opts ...grpc.CallOption) (*ListNodesResponse, error)
|
ListNodes(ctx context.Context, in *ListNodesRequest, opts ...grpc.CallOption) (*ListNodesResponse, error)
|
||||||
MoveNode(ctx context.Context, in *MoveNodeRequest, opts ...grpc.CallOption) (*MoveNodeResponse, error)
|
MoveNode(ctx context.Context, in *MoveNodeRequest, opts ...grpc.CallOption) (*MoveNodeResponse, error)
|
||||||
|
BackfillNodeIPs(ctx context.Context, in *BackfillNodeIPsRequest, opts ...grpc.CallOption) (*BackfillNodeIPsResponse, error)
|
||||||
// --- Route start ---
|
// --- Route start ---
|
||||||
GetRoutes(ctx context.Context, in *GetRoutesRequest, opts ...grpc.CallOption) (*GetRoutesResponse, error)
|
GetRoutes(ctx context.Context, in *GetRoutesRequest, opts ...grpc.CallOption) (*GetRoutesResponse, error)
|
||||||
EnableRoute(ctx context.Context, in *EnableRouteRequest, opts ...grpc.CallOption) (*EnableRouteResponse, error)
|
EnableRoute(ctx context.Context, in *EnableRouteRequest, opts ...grpc.CallOption) (*EnableRouteResponse, error)
|
||||||
|
@ -245,6 +247,15 @@ func (c *headscaleServiceClient) MoveNode(ctx context.Context, in *MoveNodeReque
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *headscaleServiceClient) BackfillNodeIPs(ctx context.Context, in *BackfillNodeIPsRequest, opts ...grpc.CallOption) (*BackfillNodeIPsResponse, error) {
|
||||||
|
out := new(BackfillNodeIPsResponse)
|
||||||
|
err := c.cc.Invoke(ctx, HeadscaleService_BackfillNodeIPs_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *headscaleServiceClient) GetRoutes(ctx context.Context, in *GetRoutesRequest, opts ...grpc.CallOption) (*GetRoutesResponse, error) {
|
func (c *headscaleServiceClient) GetRoutes(ctx context.Context, in *GetRoutesRequest, opts ...grpc.CallOption) (*GetRoutesResponse, error) {
|
||||||
out := new(GetRoutesResponse)
|
out := new(GetRoutesResponse)
|
||||||
err := c.cc.Invoke(ctx, HeadscaleService_GetRoutes_FullMethodName, in, out, opts...)
|
err := c.cc.Invoke(ctx, HeadscaleService_GetRoutes_FullMethodName, in, out, opts...)
|
||||||
|
@ -350,6 +361,7 @@ type HeadscaleServiceServer interface {
|
||||||
RenameNode(context.Context, *RenameNodeRequest) (*RenameNodeResponse, error)
|
RenameNode(context.Context, *RenameNodeRequest) (*RenameNodeResponse, error)
|
||||||
ListNodes(context.Context, *ListNodesRequest) (*ListNodesResponse, error)
|
ListNodes(context.Context, *ListNodesRequest) (*ListNodesResponse, error)
|
||||||
MoveNode(context.Context, *MoveNodeRequest) (*MoveNodeResponse, error)
|
MoveNode(context.Context, *MoveNodeRequest) (*MoveNodeResponse, error)
|
||||||
|
BackfillNodeIPs(context.Context, *BackfillNodeIPsRequest) (*BackfillNodeIPsResponse, error)
|
||||||
// --- Route start ---
|
// --- Route start ---
|
||||||
GetRoutes(context.Context, *GetRoutesRequest) (*GetRoutesResponse, error)
|
GetRoutes(context.Context, *GetRoutesRequest) (*GetRoutesResponse, error)
|
||||||
EnableRoute(context.Context, *EnableRouteRequest) (*EnableRouteResponse, error)
|
EnableRoute(context.Context, *EnableRouteRequest) (*EnableRouteResponse, error)
|
||||||
|
@ -419,6 +431,9 @@ func (UnimplementedHeadscaleServiceServer) ListNodes(context.Context, *ListNodes
|
||||||
func (UnimplementedHeadscaleServiceServer) MoveNode(context.Context, *MoveNodeRequest) (*MoveNodeResponse, error) {
|
func (UnimplementedHeadscaleServiceServer) MoveNode(context.Context, *MoveNodeRequest) (*MoveNodeResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method MoveNode not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method MoveNode not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedHeadscaleServiceServer) BackfillNodeIPs(context.Context, *BackfillNodeIPsRequest) (*BackfillNodeIPsResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method BackfillNodeIPs not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedHeadscaleServiceServer) GetRoutes(context.Context, *GetRoutesRequest) (*GetRoutesResponse, error) {
|
func (UnimplementedHeadscaleServiceServer) GetRoutes(context.Context, *GetRoutesRequest) (*GetRoutesResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetRoutes not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method GetRoutes not implemented")
|
||||||
}
|
}
|
||||||
|
@ -765,6 +780,24 @@ func _HeadscaleService_MoveNode_Handler(srv interface{}, ctx context.Context, de
|
||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _HeadscaleService_BackfillNodeIPs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(BackfillNodeIPsRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(HeadscaleServiceServer).BackfillNodeIPs(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: HeadscaleService_BackfillNodeIPs_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(HeadscaleServiceServer).BackfillNodeIPs(ctx, req.(*BackfillNodeIPsRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
func _HeadscaleService_GetRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _HeadscaleService_GetRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(GetRoutesRequest)
|
in := new(GetRoutesRequest)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
|
@ -1002,6 +1035,10 @@ var HeadscaleService_ServiceDesc = grpc.ServiceDesc{
|
||||||
MethodName: "MoveNode",
|
MethodName: "MoveNode",
|
||||||
Handler: _HeadscaleService_MoveNode_Handler,
|
Handler: _HeadscaleService_MoveNode_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "BackfillNodeIPs",
|
||||||
|
Handler: _HeadscaleService_BackfillNodeIPs_Handler,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
MethodName: "GetRoutes",
|
MethodName: "GetRoutes",
|
||||||
Handler: _HeadscaleService_GetRoutes_Handler,
|
Handler: _HeadscaleService_GetRoutes_Handler,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.32.0
|
// protoc-gen-go v1.33.0
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/node.proto
|
// source: headscale/v1/node.proto
|
||||||
|
|
||||||
|
@ -1141,6 +1141,100 @@ func (x *DebugCreateNodeResponse) GetNode() *Node {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BackfillNodeIPsRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Confirmed bool `protobuf:"varint,1,opt,name=confirmed,proto3" json:"confirmed,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *BackfillNodeIPsRequest) Reset() {
|
||||||
|
*x = BackfillNodeIPsRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_headscale_v1_node_proto_msgTypes[19]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *BackfillNodeIPsRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*BackfillNodeIPsRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *BackfillNodeIPsRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_headscale_v1_node_proto_msgTypes[19]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use BackfillNodeIPsRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*BackfillNodeIPsRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_headscale_v1_node_proto_rawDescGZIP(), []int{19}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *BackfillNodeIPsRequest) GetConfirmed() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Confirmed
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type BackfillNodeIPsResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Changes []string `protobuf:"bytes,1,rep,name=changes,proto3" json:"changes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *BackfillNodeIPsResponse) Reset() {
|
||||||
|
*x = BackfillNodeIPsResponse{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_headscale_v1_node_proto_msgTypes[20]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *BackfillNodeIPsResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*BackfillNodeIPsResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *BackfillNodeIPsResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_headscale_v1_node_proto_msgTypes[20]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use BackfillNodeIPsResponse.ProtoReflect.Descriptor instead.
|
||||||
|
func (*BackfillNodeIPsResponse) Descriptor() ([]byte, []int) {
|
||||||
|
return file_headscale_v1_node_proto_rawDescGZIP(), []int{20}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *BackfillNodeIPsResponse) GetChanges() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Changes
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var File_headscale_v1_node_proto protoreflect.FileDescriptor
|
var File_headscale_v1_node_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_headscale_v1_node_proto_rawDesc = []byte{
|
var file_headscale_v1_node_proto_rawDesc = []byte{
|
||||||
|
@ -1260,18 +1354,25 @@ var file_headscale_v1_node_proto_rawDesc = []byte{
|
||||||
0x65, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
0x65, 0x61, 0x74, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
0x12, 0x26, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12,
|
0x12, 0x26, 0x0a, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12,
|
||||||
0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f,
|
0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f,
|
||||||
0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x2a, 0x82, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x67,
|
0x64, 0x65, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x22, 0x36, 0x0a, 0x16, 0x42, 0x61, 0x63, 0x6b,
|
||||||
0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x52,
|
0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55,
|
0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18,
|
||||||
0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18,
|
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64,
|
||||||
0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f,
|
0x22, 0x33, 0x0a, 0x17, 0x42, 0x61, 0x63, 0x6b, 0x66, 0x69, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65,
|
||||||
0x41, 0x55, 0x54, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45,
|
0x49, 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63,
|
||||||
0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x43, 0x4c,
|
0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68,
|
||||||
0x49, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f,
|
0x61, 0x6e, 0x67, 0x65, 0x73, 0x2a, 0x82, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74,
|
||||||
0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x03, 0x42, 0x29, 0x5a,
|
0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x47, 0x49,
|
||||||
0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e,
|
0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50,
|
||||||
0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67,
|
0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x47,
|
||||||
0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54,
|
||||||
|
0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x47, 0x49, 0x53,
|
||||||
|
0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x10, 0x02,
|
||||||
|
0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54,
|
||||||
|
0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x03, 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 (
|
var (
|
||||||
|
@ -1287,7 +1388,7 @@ func file_headscale_v1_node_proto_rawDescGZIP() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_headscale_v1_node_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
var file_headscale_v1_node_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
var file_headscale_v1_node_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
|
var file_headscale_v1_node_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
|
||||||
var file_headscale_v1_node_proto_goTypes = []interface{}{
|
var file_headscale_v1_node_proto_goTypes = []interface{}{
|
||||||
(RegisterMethod)(0), // 0: headscale.v1.RegisterMethod
|
(RegisterMethod)(0), // 0: headscale.v1.RegisterMethod
|
||||||
(*Node)(nil), // 1: headscale.v1.Node
|
(*Node)(nil), // 1: headscale.v1.Node
|
||||||
|
@ -1309,16 +1410,18 @@ var file_headscale_v1_node_proto_goTypes = []interface{}{
|
||||||
(*MoveNodeResponse)(nil), // 17: headscale.v1.MoveNodeResponse
|
(*MoveNodeResponse)(nil), // 17: headscale.v1.MoveNodeResponse
|
||||||
(*DebugCreateNodeRequest)(nil), // 18: headscale.v1.DebugCreateNodeRequest
|
(*DebugCreateNodeRequest)(nil), // 18: headscale.v1.DebugCreateNodeRequest
|
||||||
(*DebugCreateNodeResponse)(nil), // 19: headscale.v1.DebugCreateNodeResponse
|
(*DebugCreateNodeResponse)(nil), // 19: headscale.v1.DebugCreateNodeResponse
|
||||||
(*User)(nil), // 20: headscale.v1.User
|
(*BackfillNodeIPsRequest)(nil), // 20: headscale.v1.BackfillNodeIPsRequest
|
||||||
(*timestamppb.Timestamp)(nil), // 21: google.protobuf.Timestamp
|
(*BackfillNodeIPsResponse)(nil), // 21: headscale.v1.BackfillNodeIPsResponse
|
||||||
(*PreAuthKey)(nil), // 22: headscale.v1.PreAuthKey
|
(*User)(nil), // 22: headscale.v1.User
|
||||||
|
(*timestamppb.Timestamp)(nil), // 23: google.protobuf.Timestamp
|
||||||
|
(*PreAuthKey)(nil), // 24: headscale.v1.PreAuthKey
|
||||||
}
|
}
|
||||||
var file_headscale_v1_node_proto_depIdxs = []int32{
|
var file_headscale_v1_node_proto_depIdxs = []int32{
|
||||||
20, // 0: headscale.v1.Node.user:type_name -> headscale.v1.User
|
22, // 0: headscale.v1.Node.user:type_name -> headscale.v1.User
|
||||||
21, // 1: headscale.v1.Node.last_seen:type_name -> google.protobuf.Timestamp
|
23, // 1: headscale.v1.Node.last_seen:type_name -> google.protobuf.Timestamp
|
||||||
21, // 2: headscale.v1.Node.expiry:type_name -> google.protobuf.Timestamp
|
23, // 2: headscale.v1.Node.expiry:type_name -> google.protobuf.Timestamp
|
||||||
22, // 3: headscale.v1.Node.pre_auth_key:type_name -> headscale.v1.PreAuthKey
|
24, // 3: headscale.v1.Node.pre_auth_key:type_name -> headscale.v1.PreAuthKey
|
||||||
21, // 4: headscale.v1.Node.created_at:type_name -> google.protobuf.Timestamp
|
23, // 4: headscale.v1.Node.created_at:type_name -> google.protobuf.Timestamp
|
||||||
0, // 5: headscale.v1.Node.register_method:type_name -> headscale.v1.RegisterMethod
|
0, // 5: headscale.v1.Node.register_method:type_name -> headscale.v1.RegisterMethod
|
||||||
1, // 6: headscale.v1.RegisterNodeResponse.node:type_name -> headscale.v1.Node
|
1, // 6: headscale.v1.RegisterNodeResponse.node:type_name -> headscale.v1.Node
|
||||||
1, // 7: headscale.v1.GetNodeResponse.node:type_name -> headscale.v1.Node
|
1, // 7: headscale.v1.GetNodeResponse.node:type_name -> headscale.v1.Node
|
||||||
|
@ -1571,6 +1674,30 @@ func file_headscale_v1_node_proto_init() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
file_headscale_v1_node_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*BackfillNodeIPsRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_headscale_v1_node_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*BackfillNodeIPsResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
type x struct{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
|
@ -1578,7 +1705,7 @@ func file_headscale_v1_node_proto_init() {
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_headscale_v1_node_proto_rawDesc,
|
RawDescriptor: file_headscale_v1_node_proto_rawDesc,
|
||||||
NumEnums: 1,
|
NumEnums: 1,
|
||||||
NumMessages: 19,
|
NumMessages: 21,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.32.0
|
// protoc-gen-go v1.33.0
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/preauthkey.proto
|
// source: headscale/v1/preauthkey.proto
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.32.0
|
// protoc-gen-go v1.33.0
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/routes.proto
|
// source: headscale/v1/routes.proto
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.32.0
|
// protoc-gen-go v1.33.0
|
||||||
// protoc (unknown)
|
// protoc (unknown)
|
||||||
// source: headscale/v1/user.proto
|
// source: headscale/v1/user.proto
|
||||||
|
|
||||||
|
|
|
@ -194,6 +194,36 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/node/backfillips": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "HeadscaleService_BackfillNodeIPs",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A successful response.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/v1BackfillNodeIPsResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"description": "An unexpected error response.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/rpcStatus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "confirmed",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"HeadscaleService"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/node/register": {
|
"/api/v1/node/register": {
|
||||||
"post": {
|
"post": {
|
||||||
"operationId": "HeadscaleService_RegisterNode",
|
"operationId": "HeadscaleService_RegisterNode",
|
||||||
|
@ -886,6 +916,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"v1BackfillNodeIPsResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"changes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"v1CreateApiKeyRequest": {
|
"v1CreateApiKeyRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
_ "net/http/pprof" //nolint
|
_ "net/http/pprof" //nolint
|
||||||
"net/netip"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -56,6 +55,7 @@ import (
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/dnstype"
|
"tailscale.com/types/dnstype"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
|
"tailscale.com/util/dnsname"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -148,7 +148,7 @@ func NewHeadscale(cfg *types.Config) (*Headscale, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
app.ipAlloc, err = db.NewIPAllocator(app.db, *cfg.PrefixV4, *cfg.PrefixV6)
|
app.ipAlloc, err = db.NewIPAllocator(app.db, cfg.PrefixV4, cfg.PrefixV6, cfg.IPAllocation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,15 @@ func NewHeadscale(cfg *types.Config) (*Headscale, error) {
|
||||||
|
|
||||||
if app.cfg.DNSConfig != nil && app.cfg.DNSConfig.Proxied { // if MagicDNS
|
if app.cfg.DNSConfig != nil && app.cfg.DNSConfig.Proxied { // if MagicDNS
|
||||||
// TODO(kradalby): revisit why this takes a list.
|
// TODO(kradalby): revisit why this takes a list.
|
||||||
magicDNSDomains := util.GenerateMagicDNSRootDomains([]netip.Prefix{*cfg.PrefixV4, *cfg.PrefixV6})
|
|
||||||
|
var magicDNSDomains []dnsname.FQDN
|
||||||
|
if cfg.PrefixV4 != nil {
|
||||||
|
magicDNSDomains = append(magicDNSDomains, util.GenerateIPv4DNSRootDomain(*cfg.PrefixV4)...)
|
||||||
|
}
|
||||||
|
if cfg.PrefixV6 != nil {
|
||||||
|
magicDNSDomains = append(magicDNSDomains, util.GenerateIPv6DNSRootDomain(*cfg.PrefixV6)...)
|
||||||
|
}
|
||||||
|
|
||||||
// we might have routes already from Split DNS
|
// we might have routes already from Split DNS
|
||||||
if app.cfg.DNSConfig.Routes == nil {
|
if app.cfg.DNSConfig.Routes == nil {
|
||||||
app.cfg.DNSConfig.Routes = make(map[string][]*dnstype.Resolver)
|
app.cfg.DNSConfig.Routes = make(map[string][]*dnstype.Resolver)
|
||||||
|
|
|
@ -383,7 +383,7 @@ func (h *Headscale) handleAuthKey(
|
||||||
ForcedTags: pak.Proto().GetAclTags(),
|
ForcedTags: pak.Proto().GetAclTags(),
|
||||||
}
|
}
|
||||||
|
|
||||||
addrs, err := h.ipAlloc.Next()
|
ipv4, ipv6, err := h.ipAlloc.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
Caller().
|
Caller().
|
||||||
|
@ -397,7 +397,7 @@ func (h *Headscale) handleAuthKey(
|
||||||
|
|
||||||
node, err = h.db.RegisterNode(
|
node, err = h.db.RegisterNode(
|
||||||
nodeToRegister,
|
nodeToRegister,
|
||||||
addrs,
|
ipv4, ipv6,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().
|
log.Error().
|
||||||
|
@ -461,7 +461,6 @@ func (h *Headscale) handleAuthKey(
|
||||||
|
|
||||||
log.Info().
|
log.Info().
|
||||||
Str("node", registerRequest.Hostinfo.Hostname).
|
Str("node", registerRequest.Hostinfo.Hostname).
|
||||||
Str("ips", strings.Join(node.IPAddresses.StringSlice(), ", ")).
|
|
||||||
Msg("Successfully authenticated via AuthKey")
|
Msg("Successfully authenticated via AuthKey")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -330,6 +331,66 @@ func NewHeadscaleDatabase(
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Replace column with IP address list with dedicated
|
||||||
|
// IP v4 and v6 column.
|
||||||
|
// Note that previously, the list _could_ contain more
|
||||||
|
// than two addresses, which should not really happen.
|
||||||
|
// In that case, the first occurence of each type will
|
||||||
|
// be kept.
|
||||||
|
ID: "2024041121742",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
_ = tx.Migrator().AddColumn(&types.Node{}, "ipv4")
|
||||||
|
_ = tx.Migrator().AddColumn(&types.Node{}, "ipv6")
|
||||||
|
|
||||||
|
type node struct {
|
||||||
|
ID uint64 `gorm:"column:id"`
|
||||||
|
Addresses string `gorm:"column:ip_addresses"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodes []node
|
||||||
|
|
||||||
|
_ = tx.Raw("SELECT id, ip_addresses FROM nodes").Scan(&nodes).Error
|
||||||
|
|
||||||
|
for _, node := range nodes {
|
||||||
|
addrs := strings.Split(node.Addresses, ",")
|
||||||
|
|
||||||
|
if len(addrs) == 0 {
|
||||||
|
fmt.Errorf("no addresses found for node(%d)", node.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var v4 *netip.Addr
|
||||||
|
var v6 *netip.Addr
|
||||||
|
|
||||||
|
for _, addrStr := range addrs {
|
||||||
|
addr, err := netip.ParseAddr(addrStr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing IP for node(%d) from database: %w", node.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr.Is4() && v4 == nil {
|
||||||
|
v4 = &addr
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr.Is6() && v6 == nil {
|
||||||
|
v6 = &addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Save(&types.Node{ID: types.NodeID(node.ID), IPv4: v4, IPv6: v6}).Error
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("saving ip addresses to new columns: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = tx.Migrator().DropColumn(&types.Node{}, "ip_addresses")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Rollback: func(tx *gorm.DB) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/juanfont/headscale/hscontrol/types"
|
"github.com/juanfont/headscale/hscontrol/types"
|
||||||
"github.com/juanfont/headscale/hscontrol/util"
|
"github.com/juanfont/headscale/hscontrol/util"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"go4.org/netipx"
|
"go4.org/netipx"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
@ -20,13 +24,16 @@ import (
|
||||||
type IPAllocator struct {
|
type IPAllocator struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
||||||
prefix4 netip.Prefix
|
prefix4 *netip.Prefix
|
||||||
prefix6 netip.Prefix
|
prefix6 *netip.Prefix
|
||||||
|
|
||||||
// Previous IPs handed out
|
// Previous IPs handed out
|
||||||
prev4 netip.Addr
|
prev4 netip.Addr
|
||||||
prev6 netip.Addr
|
prev6 netip.Addr
|
||||||
|
|
||||||
|
// strategy used for handing out IP addresses.
|
||||||
|
strategy types.IPAllocationStrategy
|
||||||
|
|
||||||
// Set of all IPs handed out.
|
// Set of all IPs handed out.
|
||||||
// This might not be in sync with the database,
|
// This might not be in sync with the database,
|
||||||
// but it is more conservative. If saves to the
|
// but it is more conservative. If saves to the
|
||||||
|
@ -40,40 +47,71 @@ type IPAllocator struct {
|
||||||
// provided IPv4 and IPv6 prefix. It needs to be created
|
// provided IPv4 and IPv6 prefix. It needs to be created
|
||||||
// when headscale starts and needs to finish its read
|
// when headscale starts and needs to finish its read
|
||||||
// transaction before any writes to the database occur.
|
// transaction before any writes to the database occur.
|
||||||
func NewIPAllocator(db *HSDatabase, prefix4, prefix6 netip.Prefix) (*IPAllocator, error) {
|
func NewIPAllocator(
|
||||||
var addressesSlices []string
|
db *HSDatabase,
|
||||||
|
prefix4, prefix6 *netip.Prefix,
|
||||||
|
strategy types.IPAllocationStrategy,
|
||||||
|
) (*IPAllocator, error) {
|
||||||
|
ret := IPAllocator{
|
||||||
|
prefix4: prefix4,
|
||||||
|
prefix6: prefix6,
|
||||||
|
|
||||||
|
strategy: strategy,
|
||||||
|
}
|
||||||
|
|
||||||
|
var v4s []sql.NullString
|
||||||
|
var v6s []sql.NullString
|
||||||
|
|
||||||
if db != nil {
|
if db != nil {
|
||||||
db.Read(func(rx *gorm.DB) error {
|
err := db.Read(func(rx *gorm.DB) error {
|
||||||
return rx.Model(&types.Node{}).Pluck("ip_addresses", &addressesSlices).Error
|
return rx.Model(&types.Node{}).Pluck("ipv4", &v4s).Error
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("reading IPv4 addresses from database: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.Read(func(rx *gorm.DB) error {
|
||||||
|
return rx.Model(&types.Node{}).Pluck("ipv6", &v6s).Error
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("reading IPv6 addresses from database: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ips netipx.IPSetBuilder
|
var ips netipx.IPSetBuilder
|
||||||
|
|
||||||
// Add network and broadcast addrs to used pool so they
|
// Add network and broadcast addrs to used pool so they
|
||||||
// are not handed out to nodes.
|
// are not handed out to nodes.
|
||||||
network4, broadcast4 := util.GetIPPrefixEndpoints(prefix4)
|
if prefix4 != nil {
|
||||||
network6, broadcast6 := util.GetIPPrefixEndpoints(prefix6)
|
network4, broadcast4 := util.GetIPPrefixEndpoints(*prefix4)
|
||||||
ips.Add(network4)
|
ips.Add(network4)
|
||||||
ips.Add(broadcast4)
|
ips.Add(broadcast4)
|
||||||
ips.Add(network6)
|
|
||||||
ips.Add(broadcast6)
|
// Use network as starting point, it will be used to call .Next()
|
||||||
|
// TODO(kradalby): Could potentially take all the IPs loaded from
|
||||||
|
// the database into account to start at a more "educated" location.
|
||||||
|
ret.prev4 = network4
|
||||||
|
}
|
||||||
|
|
||||||
|
if prefix6 != nil {
|
||||||
|
network6, broadcast6 := util.GetIPPrefixEndpoints(*prefix6)
|
||||||
|
ips.Add(network6)
|
||||||
|
ips.Add(broadcast6)
|
||||||
|
|
||||||
|
ret.prev6 = network6
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch all the IP Addresses currently handed out from the Database
|
// Fetch all the IP Addresses currently handed out from the Database
|
||||||
// and add them to the used IP set.
|
// and add them to the used IP set.
|
||||||
for _, slice := range addressesSlices {
|
for _, addrStr := range append(v4s, v6s...) {
|
||||||
var machineAddresses types.NodeAddresses
|
if addrStr.Valid {
|
||||||
err := machineAddresses.Scan(slice)
|
addr, err := netip.ParseAddr(addrStr.String)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf("parsing IP address from database: %w", err)
|
||||||
"parsing IPs from database %v: %w", machineAddresses,
|
}
|
||||||
err,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ip := range machineAddresses {
|
ips.Add(addr)
|
||||||
ips.Add(ip)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,42 +124,61 @@ func NewIPAllocator(db *HSDatabase, prefix4, prefix6 netip.Prefix) (*IPAllocator
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &IPAllocator{
|
ret.usedIPs = ips
|
||||||
usedIPs: ips,
|
|
||||||
|
|
||||||
prefix4: prefix4,
|
return &ret, nil
|
||||||
prefix6: prefix6,
|
|
||||||
|
|
||||||
// Use network as starting point, it will be used to call .Next()
|
|
||||||
// TODO(kradalby): Could potentially take all the IPs loaded from
|
|
||||||
// the database into account to start at a more "educated" location.
|
|
||||||
prev4: network4,
|
|
||||||
prev6: network6,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IPAllocator) Next() (types.NodeAddresses, error) {
|
func (i *IPAllocator) Next() (*netip.Addr, *netip.Addr, error) {
|
||||||
i.mu.Lock()
|
i.mu.Lock()
|
||||||
defer i.mu.Unlock()
|
defer i.mu.Unlock()
|
||||||
|
|
||||||
v4, err := i.next(i.prev4, i.prefix4)
|
var err error
|
||||||
if err != nil {
|
var ret4 *netip.Addr
|
||||||
return nil, fmt.Errorf("allocating IPv4 address: %w", err)
|
var ret6 *netip.Addr
|
||||||
|
|
||||||
|
if i.prefix4 != nil {
|
||||||
|
ret4, err = i.next(i.prev4, i.prefix4)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("allocating IPv4 address: %w", err)
|
||||||
|
}
|
||||||
|
i.prev4 = *ret4
|
||||||
}
|
}
|
||||||
|
|
||||||
v6, err := i.next(i.prev6, i.prefix6)
|
if i.prefix6 != nil {
|
||||||
if err != nil {
|
ret6, err = i.next(i.prev6, i.prefix6)
|
||||||
return nil, fmt.Errorf("allocating IPv6 address: %w", err)
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("allocating IPv6 address: %w", err)
|
||||||
|
}
|
||||||
|
i.prev6 = *ret6
|
||||||
}
|
}
|
||||||
|
|
||||||
return types.NodeAddresses{*v4, *v6}, nil
|
return ret4, ret6, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrCouldNotAllocateIP = errors.New("failed to allocate IP")
|
var ErrCouldNotAllocateIP = errors.New("failed to allocate IP")
|
||||||
|
|
||||||
func (i *IPAllocator) next(prev netip.Addr, prefix netip.Prefix) (*netip.Addr, error) {
|
func (i *IPAllocator) nextLocked(prev netip.Addr, prefix *netip.Prefix) (*netip.Addr, error) {
|
||||||
// Get the first IP in our prefix
|
i.mu.Lock()
|
||||||
ip := prev.Next()
|
defer i.mu.Unlock()
|
||||||
|
|
||||||
|
return i.next(prev, prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IPAllocator) next(prev netip.Addr, prefix *netip.Prefix) (*netip.Addr, error) {
|
||||||
|
var err error
|
||||||
|
var ip netip.Addr
|
||||||
|
|
||||||
|
switch i.strategy {
|
||||||
|
case types.IPAllocationStrategySequential:
|
||||||
|
// Get the first IP in our prefix
|
||||||
|
ip = prev.Next()
|
||||||
|
case types.IPAllocationStrategyRandom:
|
||||||
|
ip, err = randomNext(*prefix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting random IP: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(kradalby): maybe this can be done less often.
|
// TODO(kradalby): maybe this can be done less often.
|
||||||
set, err := i.usedIPs.IPSet()
|
set, err := i.usedIPs.IPSet()
|
||||||
|
@ -136,7 +193,15 @@ func (i *IPAllocator) next(prev netip.Addr, prefix netip.Prefix) (*netip.Addr, e
|
||||||
|
|
||||||
// Check if the IP has already been allocated.
|
// Check if the IP has already been allocated.
|
||||||
if set.Contains(ip) {
|
if set.Contains(ip) {
|
||||||
ip = ip.Next()
|
switch i.strategy {
|
||||||
|
case types.IPAllocationStrategySequential:
|
||||||
|
ip = ip.Next()
|
||||||
|
case types.IPAllocationStrategyRandom:
|
||||||
|
ip, err = randomNext(*prefix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting random IP: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -146,3 +211,120 @@ func (i *IPAllocator) next(prev netip.Addr, prefix netip.Prefix) (*netip.Addr, e
|
||||||
return &ip, nil
|
return &ip, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func randomNext(pfx netip.Prefix) (netip.Addr, error) {
|
||||||
|
rang := netipx.RangeOfPrefix(pfx)
|
||||||
|
fromIP, toIP := rang.From(), rang.To()
|
||||||
|
|
||||||
|
var from, to big.Int
|
||||||
|
|
||||||
|
from.SetBytes(fromIP.AsSlice())
|
||||||
|
to.SetBytes(toIP.AsSlice())
|
||||||
|
|
||||||
|
// Find the max, this is how we can do "random range",
|
||||||
|
// get the "max" as 0 -> to - from and then add back from
|
||||||
|
// after.
|
||||||
|
tempMax := big.NewInt(0).Sub(&to, &from)
|
||||||
|
|
||||||
|
out, err := rand.Int(rand.Reader, tempMax)
|
||||||
|
if err != nil {
|
||||||
|
return netip.Addr{}, fmt.Errorf("generating random IP: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
valInRange := big.NewInt(0).Add(&from, out)
|
||||||
|
|
||||||
|
ip, ok := netip.AddrFromSlice(valInRange.Bytes())
|
||||||
|
if !ok {
|
||||||
|
return netip.Addr{}, fmt.Errorf("generated ip bytes are invalid ip")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pfx.Contains(ip) {
|
||||||
|
return netip.Addr{}, fmt.Errorf(
|
||||||
|
"generated ip(%s) not in prefix(%s)",
|
||||||
|
ip.String(),
|
||||||
|
pfx.String(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackfillNodeIPs will take a database transaction, and
|
||||||
|
// iterate through all of the current nodes in headscale
|
||||||
|
// and ensure it has IP addresses according to the current
|
||||||
|
// configuration.
|
||||||
|
// This means that if both IPv4 and IPv6 is set in the
|
||||||
|
// config, and some nodes are missing that type of IP,
|
||||||
|
// it will be added.
|
||||||
|
// If a prefix type has been removed (IPv4 or IPv6), it
|
||||||
|
// will remove the IPs in that family from the node.
|
||||||
|
func (db *HSDatabase) BackfillNodeIPs(i *IPAllocator) ([]string, error) {
|
||||||
|
var err error
|
||||||
|
var ret []string
|
||||||
|
err = db.Write(func(tx *gorm.DB) error {
|
||||||
|
if i == nil {
|
||||||
|
return errors.New("backfilling IPs: ip allocator was nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace().Msgf("starting to backfill IPs")
|
||||||
|
|
||||||
|
nodes, err := ListNodes(tx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("listing nodes to backfill IPs: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, node := range nodes {
|
||||||
|
log.Trace().Uint64("node.id", node.ID.Uint64()).Msg("checking if need backfill")
|
||||||
|
|
||||||
|
changed := false
|
||||||
|
// IPv4 prefix is set, but node ip is missing, alloc
|
||||||
|
if i.prefix4 != nil && node.IPv4 == nil {
|
||||||
|
ret4, err := i.nextLocked(i.prev4, i.prefix4)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to allocate ipv4 for node(%d): %w", node.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
node.IPv4 = ret4
|
||||||
|
changed = true
|
||||||
|
ret = append(ret, fmt.Sprintf("assigned IPv4 %q to Node(%d) %q", ret4.String(), node.ID, node.Hostname))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv6 prefix is set, but node ip is missing, alloc
|
||||||
|
if i.prefix6 != nil && node.IPv6 == nil {
|
||||||
|
ret6, err := i.nextLocked(i.prev6, i.prefix6)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to allocate ipv6 for node(%d): %w", node.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
node.IPv6 = ret6
|
||||||
|
changed = true
|
||||||
|
ret = append(ret, fmt.Sprintf("assigned IPv6 %q to Node(%d) %q", ret6.String(), node.ID, node.Hostname))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv4 prefix is not set, but node has IP, remove
|
||||||
|
if i.prefix4 == nil && node.IPv4 != nil {
|
||||||
|
ret = append(ret, fmt.Sprintf("removing IPv4 %q from Node(%d) %q", node.IPv4.String(), node.ID, node.Hostname))
|
||||||
|
node.IPv4 = nil
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv6 prefix is not set, but node has IP, remove
|
||||||
|
if i.prefix6 == nil && node.IPv6 != nil {
|
||||||
|
ret = append(ret, fmt.Sprintf("removing IPv6 %q from Node(%d) %q", node.IPv6.String(), node.ID, node.Hostname))
|
||||||
|
node.IPv6 = nil
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
err := tx.Save(node).Error
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("saving node(%d) after adding IPs: %w", node.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
|
@ -1,49 +1,41 @@
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
"github.com/juanfont/headscale/hscontrol/types"
|
"github.com/juanfont/headscale/hscontrol/types"
|
||||||
"github.com/juanfont/headscale/hscontrol/util"
|
"github.com/juanfont/headscale/hscontrol/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIPAllocator(t *testing.T) {
|
var mpp = func(pref string) *netip.Prefix {
|
||||||
mpp := func(pref string) netip.Prefix {
|
p := netip.MustParsePrefix(pref)
|
||||||
return netip.MustParsePrefix(pref)
|
return &p
|
||||||
}
|
}
|
||||||
na := func(pref string) netip.Addr {
|
var na = func(pref string) netip.Addr {
|
||||||
return netip.MustParseAddr(pref)
|
return netip.MustParseAddr(pref)
|
||||||
}
|
}
|
||||||
newDb := func() *HSDatabase {
|
var nap = func(pref string) *netip.Addr {
|
||||||
tmpDir, err := os.MkdirTemp("", "headscale-db-test-*")
|
n := na(pref)
|
||||||
if err != nil {
|
return &n
|
||||||
t.Fatalf("creating temp dir: %s", err)
|
}
|
||||||
}
|
|
||||||
db, _ = NewHeadscaleDatabase(
|
|
||||||
types.DatabaseConfig{
|
|
||||||
Type: "sqlite3",
|
|
||||||
Sqlite: types.SqliteConfig{
|
|
||||||
Path: tmpDir + "/headscale_test.db",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
|
|
||||||
return db
|
|
||||||
}
|
|
||||||
|
|
||||||
|
func TestIPAllocatorSequential(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
dbFunc func() *HSDatabase
|
dbFunc func() *HSDatabase
|
||||||
|
|
||||||
prefix4 netip.Prefix
|
prefix4 *netip.Prefix
|
||||||
prefix6 netip.Prefix
|
prefix6 *netip.Prefix
|
||||||
getCount int
|
getCount int
|
||||||
want []types.NodeAddresses
|
want4 []netip.Addr
|
||||||
|
want6 []netip.Addr
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "simple",
|
name: "simple",
|
||||||
|
@ -56,23 +48,49 @@ func TestIPAllocator(t *testing.T) {
|
||||||
|
|
||||||
getCount: 1,
|
getCount: 1,
|
||||||
|
|
||||||
want: []types.NodeAddresses{
|
want4: []netip.Addr{
|
||||||
{
|
na("100.64.0.1"),
|
||||||
na("100.64.0.1"),
|
},
|
||||||
na("fd7a:115c:a1e0::1"),
|
want6: []netip.Addr{
|
||||||
},
|
na("fd7a:115c:a1e0::1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple-v4",
|
||||||
|
dbFunc: func() *HSDatabase {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
|
||||||
|
prefix4: mpp("100.64.0.0/10"),
|
||||||
|
|
||||||
|
getCount: 1,
|
||||||
|
|
||||||
|
want4: []netip.Addr{
|
||||||
|
na("100.64.0.1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple-v6",
|
||||||
|
dbFunc: func() *HSDatabase {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
|
||||||
|
prefix6: mpp("fd7a:115c:a1e0::/48"),
|
||||||
|
|
||||||
|
getCount: 1,
|
||||||
|
|
||||||
|
want6: []netip.Addr{
|
||||||
|
na("fd7a:115c:a1e0::1"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "simple-with-db",
|
name: "simple-with-db",
|
||||||
dbFunc: func() *HSDatabase {
|
dbFunc: func() *HSDatabase {
|
||||||
db := newDb()
|
db := dbForTest(t, "simple-with-db")
|
||||||
|
|
||||||
db.DB.Save(&types.Node{
|
db.DB.Save(&types.Node{
|
||||||
IPAddresses: types.NodeAddresses{
|
IPv4: nap("100.64.0.1"),
|
||||||
na("100.64.0.1"),
|
IPv6: nap("fd7a:115c:a1e0::1"),
|
||||||
na("fd7a:115c:a1e0::1"),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return db
|
return db
|
||||||
|
@ -83,23 +101,21 @@ func TestIPAllocator(t *testing.T) {
|
||||||
|
|
||||||
getCount: 1,
|
getCount: 1,
|
||||||
|
|
||||||
want: []types.NodeAddresses{
|
want4: []netip.Addr{
|
||||||
{
|
na("100.64.0.2"),
|
||||||
na("100.64.0.2"),
|
},
|
||||||
na("fd7a:115c:a1e0::2"),
|
want6: []netip.Addr{
|
||||||
},
|
na("fd7a:115c:a1e0::2"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "before-after-free-middle-in-db",
|
name: "before-after-free-middle-in-db",
|
||||||
dbFunc: func() *HSDatabase {
|
dbFunc: func() *HSDatabase {
|
||||||
db := newDb()
|
db := dbForTest(t, "before-after-free-middle-in-db")
|
||||||
|
|
||||||
db.DB.Save(&types.Node{
|
db.DB.Save(&types.Node{
|
||||||
IPAddresses: types.NodeAddresses{
|
IPv4: nap("100.64.0.2"),
|
||||||
na("100.64.0.2"),
|
IPv6: nap("fd7a:115c:a1e0::2"),
|
||||||
na("fd7a:115c:a1e0::2"),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return db
|
return db
|
||||||
|
@ -110,15 +126,13 @@ func TestIPAllocator(t *testing.T) {
|
||||||
|
|
||||||
getCount: 2,
|
getCount: 2,
|
||||||
|
|
||||||
want: []types.NodeAddresses{
|
want4: []netip.Addr{
|
||||||
{
|
na("100.64.0.1"),
|
||||||
na("100.64.0.1"),
|
na("100.64.0.3"),
|
||||||
na("fd7a:115c:a1e0::1"),
|
},
|
||||||
},
|
want6: []netip.Addr{
|
||||||
{
|
na("fd7a:115c:a1e0::1"),
|
||||||
na("100.64.0.3"),
|
na("fd7a:115c:a1e0::3"),
|
||||||
na("fd7a:115c:a1e0::3"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -127,24 +141,347 @@ func TestIPAllocator(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
db := tt.dbFunc()
|
db := tt.dbFunc()
|
||||||
|
|
||||||
alloc, _ := NewIPAllocator(db, tt.prefix4, tt.prefix6)
|
alloc, _ := NewIPAllocator(
|
||||||
|
db,
|
||||||
|
tt.prefix4,
|
||||||
|
tt.prefix6,
|
||||||
|
types.IPAllocationStrategySequential,
|
||||||
|
)
|
||||||
|
|
||||||
spew.Dump(alloc)
|
spew.Dump(alloc)
|
||||||
|
|
||||||
t.Logf("prefixes: %q, %q", tt.prefix4.String(), tt.prefix6.String())
|
var got4s []netip.Addr
|
||||||
|
var got6s []netip.Addr
|
||||||
var got []types.NodeAddresses
|
|
||||||
|
|
||||||
for range tt.getCount {
|
for range tt.getCount {
|
||||||
gotSet, err := alloc.Next()
|
got4, got6, err := alloc.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("allocating next IP: %s", err)
|
t.Fatalf("allocating next IP: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
got = append(got, gotSet)
|
if got4 != nil {
|
||||||
|
got4s = append(got4s, *got4)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got6 != nil {
|
||||||
|
got6s = append(got6s, *got6)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if diff := cmp.Diff(tt.want, got, util.Comparers...); diff != "" {
|
if diff := cmp.Diff(tt.want4, got4s, util.Comparers...); diff != "" {
|
||||||
t.Errorf("IPAllocator unexpected result (-want +got):\n%s", diff)
|
t.Errorf("IPAllocator 4s unexpected result (-want +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := cmp.Diff(tt.want6, got6s, util.Comparers...); diff != "" {
|
||||||
|
t.Errorf("IPAllocator 6s unexpected result (-want +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIPAllocatorRandom(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
dbFunc func() *HSDatabase
|
||||||
|
|
||||||
|
getCount int
|
||||||
|
|
||||||
|
prefix4 *netip.Prefix
|
||||||
|
prefix6 *netip.Prefix
|
||||||
|
want4 bool
|
||||||
|
want6 bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple",
|
||||||
|
dbFunc: func() *HSDatabase {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
|
||||||
|
prefix4: mpp("100.64.0.0/10"),
|
||||||
|
prefix6: mpp("fd7a:115c:a1e0::/48"),
|
||||||
|
|
||||||
|
getCount: 1,
|
||||||
|
|
||||||
|
want4: true,
|
||||||
|
want6: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple-v4",
|
||||||
|
dbFunc: func() *HSDatabase {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
|
||||||
|
prefix4: mpp("100.64.0.0/10"),
|
||||||
|
|
||||||
|
getCount: 1,
|
||||||
|
|
||||||
|
want4: true,
|
||||||
|
want6: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple-v6",
|
||||||
|
dbFunc: func() *HSDatabase {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
|
||||||
|
prefix6: mpp("fd7a:115c:a1e0::/48"),
|
||||||
|
|
||||||
|
getCount: 1,
|
||||||
|
|
||||||
|
want4: false,
|
||||||
|
want6: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "generate-lots-of-random",
|
||||||
|
dbFunc: func() *HSDatabase {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
|
||||||
|
prefix4: mpp("100.64.0.0/10"),
|
||||||
|
prefix6: mpp("fd7a:115c:a1e0::/48"),
|
||||||
|
|
||||||
|
getCount: 1000,
|
||||||
|
|
||||||
|
want4: true,
|
||||||
|
want6: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
db := tt.dbFunc()
|
||||||
|
|
||||||
|
alloc, _ := NewIPAllocator(db, tt.prefix4, tt.prefix6, types.IPAllocationStrategyRandom)
|
||||||
|
|
||||||
|
spew.Dump(alloc)
|
||||||
|
|
||||||
|
for range tt.getCount {
|
||||||
|
got4, got6, err := alloc.Next()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("allocating next IP: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("addrs ipv4: %v, ipv6: %v", got4, got6)
|
||||||
|
|
||||||
|
if tt.want4 {
|
||||||
|
if got4 == nil {
|
||||||
|
t.Fatalf("expected ipv4 addr, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.want6 {
|
||||||
|
if got6 == nil {
|
||||||
|
t.Fatalf("expected ipv4 addr, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackfillIPAddresses(t *testing.T) {
|
||||||
|
fullNodeP := func(i int) *types.Node {
|
||||||
|
v4 := fmt.Sprintf("100.64.0.%d", i)
|
||||||
|
v6 := fmt.Sprintf("fd7a:115c:a1e0::%d", i)
|
||||||
|
return &types.Node{
|
||||||
|
IPv4DatabaseField: sql.NullString{
|
||||||
|
Valid: true,
|
||||||
|
String: v4,
|
||||||
|
},
|
||||||
|
IPv4: nap(v4),
|
||||||
|
IPv6DatabaseField: sql.NullString{
|
||||||
|
Valid: true,
|
||||||
|
String: v6,
|
||||||
|
},
|
||||||
|
IPv6: nap(v6),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
dbFunc func() *HSDatabase
|
||||||
|
|
||||||
|
prefix4 *netip.Prefix
|
||||||
|
prefix6 *netip.Prefix
|
||||||
|
want types.Nodes
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple-backfill-ipv6",
|
||||||
|
dbFunc: func() *HSDatabase {
|
||||||
|
db := dbForTest(t, "simple-backfill-ipv6")
|
||||||
|
|
||||||
|
db.DB.Save(&types.Node{
|
||||||
|
IPv4: nap("100.64.0.1"),
|
||||||
|
})
|
||||||
|
|
||||||
|
return db
|
||||||
|
},
|
||||||
|
|
||||||
|
prefix4: mpp("100.64.0.0/10"),
|
||||||
|
prefix6: mpp("fd7a:115c:a1e0::/48"),
|
||||||
|
|
||||||
|
want: types.Nodes{
|
||||||
|
&types.Node{
|
||||||
|
IPv4DatabaseField: sql.NullString{
|
||||||
|
Valid: true,
|
||||||
|
String: "100.64.0.1",
|
||||||
|
},
|
||||||
|
IPv4: nap("100.64.0.1"),
|
||||||
|
IPv6DatabaseField: sql.NullString{
|
||||||
|
Valid: true,
|
||||||
|
String: "fd7a:115c:a1e0::1",
|
||||||
|
},
|
||||||
|
IPv6: nap("fd7a:115c:a1e0::1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple-backfill-ipv4",
|
||||||
|
dbFunc: func() *HSDatabase {
|
||||||
|
db := dbForTest(t, "simple-backfill-ipv4")
|
||||||
|
|
||||||
|
db.DB.Save(&types.Node{
|
||||||
|
IPv6: nap("fd7a:115c:a1e0::1"),
|
||||||
|
})
|
||||||
|
|
||||||
|
return db
|
||||||
|
},
|
||||||
|
|
||||||
|
prefix4: mpp("100.64.0.0/10"),
|
||||||
|
prefix6: mpp("fd7a:115c:a1e0::/48"),
|
||||||
|
|
||||||
|
want: types.Nodes{
|
||||||
|
&types.Node{
|
||||||
|
IPv4DatabaseField: sql.NullString{
|
||||||
|
Valid: true,
|
||||||
|
String: "100.64.0.1",
|
||||||
|
},
|
||||||
|
IPv4: nap("100.64.0.1"),
|
||||||
|
IPv6DatabaseField: sql.NullString{
|
||||||
|
Valid: true,
|
||||||
|
String: "fd7a:115c:a1e0::1",
|
||||||
|
},
|
||||||
|
IPv6: nap("fd7a:115c:a1e0::1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple-backfill-remove-ipv6",
|
||||||
|
dbFunc: func() *HSDatabase {
|
||||||
|
db := dbForTest(t, "simple-backfill-remove-ipv6")
|
||||||
|
|
||||||
|
db.DB.Save(&types.Node{
|
||||||
|
IPv4: nap("100.64.0.1"),
|
||||||
|
IPv6: nap("fd7a:115c:a1e0::1"),
|
||||||
|
})
|
||||||
|
|
||||||
|
return db
|
||||||
|
},
|
||||||
|
|
||||||
|
prefix4: mpp("100.64.0.0/10"),
|
||||||
|
|
||||||
|
want: types.Nodes{
|
||||||
|
&types.Node{
|
||||||
|
IPv4DatabaseField: sql.NullString{
|
||||||
|
Valid: true,
|
||||||
|
String: "100.64.0.1",
|
||||||
|
},
|
||||||
|
IPv4: nap("100.64.0.1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "simple-backfill-remove-ipv4",
|
||||||
|
dbFunc: func() *HSDatabase {
|
||||||
|
db := dbForTest(t, "simple-backfill-remove-ipv4")
|
||||||
|
|
||||||
|
db.DB.Save(&types.Node{
|
||||||
|
IPv4: nap("100.64.0.1"),
|
||||||
|
IPv6: nap("fd7a:115c:a1e0::1"),
|
||||||
|
})
|
||||||
|
|
||||||
|
return db
|
||||||
|
},
|
||||||
|
|
||||||
|
prefix6: mpp("fd7a:115c:a1e0::/48"),
|
||||||
|
|
||||||
|
want: types.Nodes{
|
||||||
|
&types.Node{
|
||||||
|
IPv6DatabaseField: sql.NullString{
|
||||||
|
Valid: true,
|
||||||
|
String: "fd7a:115c:a1e0::1",
|
||||||
|
},
|
||||||
|
IPv6: nap("fd7a:115c:a1e0::1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multi-backfill-ipv6",
|
||||||
|
dbFunc: func() *HSDatabase {
|
||||||
|
db := dbForTest(t, "simple-backfill-ipv6")
|
||||||
|
|
||||||
|
db.DB.Save(&types.Node{
|
||||||
|
IPv4: nap("100.64.0.1"),
|
||||||
|
})
|
||||||
|
db.DB.Save(&types.Node{
|
||||||
|
IPv4: nap("100.64.0.2"),
|
||||||
|
})
|
||||||
|
db.DB.Save(&types.Node{
|
||||||
|
IPv4: nap("100.64.0.3"),
|
||||||
|
})
|
||||||
|
db.DB.Save(&types.Node{
|
||||||
|
IPv4: nap("100.64.0.4"),
|
||||||
|
})
|
||||||
|
|
||||||
|
return db
|
||||||
|
},
|
||||||
|
|
||||||
|
prefix4: mpp("100.64.0.0/10"),
|
||||||
|
prefix6: mpp("fd7a:115c:a1e0::/48"),
|
||||||
|
|
||||||
|
want: types.Nodes{
|
||||||
|
fullNodeP(1),
|
||||||
|
fullNodeP(2),
|
||||||
|
fullNodeP(3),
|
||||||
|
fullNodeP(4),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
comps := append(util.Comparers, cmpopts.IgnoreFields(types.Node{},
|
||||||
|
"ID",
|
||||||
|
"MachineKeyDatabaseField",
|
||||||
|
"NodeKeyDatabaseField",
|
||||||
|
"DiscoKeyDatabaseField",
|
||||||
|
"Endpoints",
|
||||||
|
"HostinfoDatabaseField",
|
||||||
|
"Hostinfo",
|
||||||
|
"Routes",
|
||||||
|
"CreatedAt",
|
||||||
|
"UpdatedAt",
|
||||||
|
))
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
db := tt.dbFunc()
|
||||||
|
|
||||||
|
alloc, err := NewIPAllocator(db, tt.prefix4, tt.prefix6, types.IPAllocationStrategySequential)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to set up ip alloc: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logs, err := db.BackfillNodeIPs(alloc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to backfill: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("backfill log: \n%s", strings.Join(logs, "\n"))
|
||||||
|
|
||||||
|
got, err := db.ListNodes()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get nodes: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := cmp.Diff(tt.want, got, comps...); diff != "" {
|
||||||
|
t.Errorf("Backfill unexpected result (-want +got):\n%s", diff)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/juanfont/headscale/hscontrol/types"
|
"github.com/juanfont/headscale/hscontrol/types"
|
||||||
|
@ -294,7 +293,8 @@ func RegisterNodeFromAuthCallback(
|
||||||
userName string,
|
userName string,
|
||||||
nodeExpiry *time.Time,
|
nodeExpiry *time.Time,
|
||||||
registrationMethod string,
|
registrationMethod string,
|
||||||
addrs types.NodeAddresses,
|
ipv4 *netip.Addr,
|
||||||
|
ipv6 *netip.Addr,
|
||||||
) (*types.Node, error) {
|
) (*types.Node, error) {
|
||||||
log.Debug().
|
log.Debug().
|
||||||
Str("machine_key", mkey.ShortString()).
|
Str("machine_key", mkey.ShortString()).
|
||||||
|
@ -330,7 +330,7 @@ func RegisterNodeFromAuthCallback(
|
||||||
node, err := RegisterNode(
|
node, err := RegisterNode(
|
||||||
tx,
|
tx,
|
||||||
registrationNode,
|
registrationNode,
|
||||||
addrs,
|
ipv4, ipv6,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -346,14 +346,14 @@ func RegisterNodeFromAuthCallback(
|
||||||
return nil, ErrNodeNotFoundRegistrationCache
|
return nil, ErrNodeNotFoundRegistrationCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hsdb *HSDatabase) RegisterNode(node types.Node, addrs types.NodeAddresses) (*types.Node, error) {
|
func (hsdb *HSDatabase) RegisterNode(node types.Node, ipv4 *netip.Addr, ipv6 *netip.Addr) (*types.Node, error) {
|
||||||
return Write(hsdb.DB, func(tx *gorm.DB) (*types.Node, error) {
|
return Write(hsdb.DB, func(tx *gorm.DB) (*types.Node, error) {
|
||||||
return RegisterNode(tx, node, addrs)
|
return RegisterNode(tx, node, ipv4, ipv6)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterNode is executed from the CLI to register a new Node using its MachineKey.
|
// RegisterNode is executed from the CLI to register a new Node using its MachineKey.
|
||||||
func RegisterNode(tx *gorm.DB, node types.Node, addrs types.NodeAddresses) (*types.Node, error) {
|
func RegisterNode(tx *gorm.DB, node types.Node, ipv4 *netip.Addr, ipv6 *netip.Addr) (*types.Node, error) {
|
||||||
log.Debug().
|
log.Debug().
|
||||||
Str("node", node.Hostname).
|
Str("node", node.Hostname).
|
||||||
Str("machine_key", node.MachineKey.ShortString()).
|
Str("machine_key", node.MachineKey.ShortString()).
|
||||||
|
@ -361,10 +361,10 @@ func RegisterNode(tx *gorm.DB, node types.Node, addrs types.NodeAddresses) (*typ
|
||||||
Str("user", node.User.Name).
|
Str("user", node.User.Name).
|
||||||
Msg("Registering node")
|
Msg("Registering node")
|
||||||
|
|
||||||
// If the node exists and we had already IPs for it, we just save it
|
// If the node exists and it already has IP(s), we just save it
|
||||||
// so we store the node.Expire and node.Nodekey that has been set when
|
// so we store the node.Expire and node.Nodekey that has been set when
|
||||||
// adding it to the registrationCache
|
// adding it to the registrationCache
|
||||||
if len(node.IPAddresses) > 0 {
|
if node.IPv4 != nil || node.IPv6 != nil {
|
||||||
if err := tx.Save(&node).Error; err != nil {
|
if err := tx.Save(&node).Error; err != nil {
|
||||||
return nil, fmt.Errorf("failed register existing node in the database: %w", err)
|
return nil, fmt.Errorf("failed register existing node in the database: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -380,7 +380,8 @@ func RegisterNode(tx *gorm.DB, node types.Node, addrs types.NodeAddresses) (*typ
|
||||||
return &node, nil
|
return &node, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
node.IPAddresses = addrs
|
node.IPv4 = ipv4
|
||||||
|
node.IPv6 = ipv6
|
||||||
|
|
||||||
if err := tx.Save(&node).Error; err != nil {
|
if err := tx.Save(&node).Error; err != nil {
|
||||||
return nil, fmt.Errorf("failed register(save) node in the database: %w", err)
|
return nil, fmt.Errorf("failed register(save) node in the database: %w", err)
|
||||||
|
@ -389,7 +390,6 @@ func RegisterNode(tx *gorm.DB, node types.Node, addrs types.NodeAddresses) (*typ
|
||||||
log.Trace().
|
log.Trace().
|
||||||
Caller().
|
Caller().
|
||||||
Str("node", node.Hostname).
|
Str("node", node.Hostname).
|
||||||
Str("ip", strings.Join(addrs.StringSlice(), ",")).
|
|
||||||
Msg("Node registered with the database")
|
Msg("Node registered with the database")
|
||||||
|
|
||||||
return &node, nil
|
return &node, nil
|
||||||
|
|
|
@ -188,13 +188,12 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
|
||||||
nodeKey := key.NewNode()
|
nodeKey := key.NewNode()
|
||||||
machineKey := key.NewMachine()
|
machineKey := key.NewMachine()
|
||||||
|
|
||||||
|
v4 := netip.MustParseAddr(fmt.Sprintf("100.64.0.%v", strconv.Itoa(index+1)))
|
||||||
node := types.Node{
|
node := types.Node{
|
||||||
ID: types.NodeID(index),
|
ID: types.NodeID(index),
|
||||||
MachineKey: machineKey.Public(),
|
MachineKey: machineKey.Public(),
|
||||||
NodeKey: nodeKey.Public(),
|
NodeKey: nodeKey.Public(),
|
||||||
IPAddresses: types.NodeAddresses{
|
IPv4: &v4,
|
||||||
netip.MustParseAddr(fmt.Sprintf("100.64.0.%v", strconv.Itoa(index+1))),
|
|
||||||
},
|
|
||||||
Hostname: "testnode" + strconv.Itoa(index),
|
Hostname: "testnode" + strconv.Itoa(index),
|
||||||
UserID: stor[index%2].user.ID,
|
UserID: stor[index%2].user.ID,
|
||||||
RegisterMethod: util.RegisterMethodAuthKey,
|
RegisterMethod: util.RegisterMethodAuthKey,
|
||||||
|
@ -301,27 +300,6 @@ func (s *Suite) TestExpireNode(c *check.C) {
|
||||||
c.Assert(nodeFromDB.IsExpired(), check.Equals, true)
|
c.Assert(nodeFromDB.IsExpired(), check.Equals, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestSerdeAddressStrignSlice(c *check.C) {
|
|
||||||
input := types.NodeAddresses([]netip.Addr{
|
|
||||||
netip.MustParseAddr("192.0.2.1"),
|
|
||||||
netip.MustParseAddr("2001:db8::1"),
|
|
||||||
})
|
|
||||||
serialized, err := input.Value()
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
if serial, ok := serialized.(string); ok {
|
|
||||||
c.Assert(serial, check.Equals, "192.0.2.1,2001:db8::1")
|
|
||||||
}
|
|
||||||
|
|
||||||
var deserialized types.NodeAddresses
|
|
||||||
err = deserialized.Scan(serialized)
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
|
|
||||||
c.Assert(len(deserialized), check.Equals, len(input))
|
|
||||||
for i := range deserialized {
|
|
||||||
c.Assert(deserialized[i], check.Equals, input[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Suite) TestGenerateGivenName(c *check.C) {
|
func (s *Suite) TestGenerateGivenName(c *check.C) {
|
||||||
user1, err := db.CreateUser("user-1")
|
user1, err := db.CreateUser("user-1")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
@ -561,6 +539,7 @@ func (s *Suite) TestAutoApproveRoutes(c *check.C) {
|
||||||
// Check if a subprefix of an autoapproved route is approved
|
// Check if a subprefix of an autoapproved route is approved
|
||||||
route2 := netip.MustParsePrefix("10.11.0.0/24")
|
route2 := netip.MustParsePrefix("10.11.0.0/24")
|
||||||
|
|
||||||
|
v4 := netip.MustParseAddr("100.64.0.1")
|
||||||
node := types.Node{
|
node := types.Node{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
MachineKey: machineKey.Public(),
|
MachineKey: machineKey.Public(),
|
||||||
|
@ -573,7 +552,7 @@ func (s *Suite) TestAutoApproveRoutes(c *check.C) {
|
||||||
RequestTags: []string{"tag:exit"},
|
RequestTags: []string{"tag:exit"},
|
||||||
RoutableIPs: []netip.Prefix{defaultRouteV4, defaultRouteV6, route1, route2},
|
RoutableIPs: []netip.Prefix{defaultRouteV4, defaultRouteV6, route1, route2},
|
||||||
},
|
},
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.1")},
|
IPv4: &v4,
|
||||||
}
|
}
|
||||||
|
|
||||||
db.DB.Save(&node)
|
db.DB.Save(&node)
|
||||||
|
|
|
@ -609,7 +609,7 @@ func EnableAutoApprovedRoutes(
|
||||||
aclPolicy *policy.ACLPolicy,
|
aclPolicy *policy.ACLPolicy,
|
||||||
node *types.Node,
|
node *types.Node,
|
||||||
) error {
|
) error {
|
||||||
if len(node.IPAddresses) == 0 {
|
if node.IPv4 == nil && node.IPv6 == nil {
|
||||||
return nil // This node has no IPAddresses, so can't possibly match any autoApprovers ACLs
|
return nil // This node has no IPAddresses, so can't possibly match any autoApprovers ACLs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -652,7 +652,7 @@ func EnableAutoApprovedRoutes(
|
||||||
}
|
}
|
||||||
|
|
||||||
// approvedIPs should contain all of node's IPs if it matches the rule, so check for first
|
// approvedIPs should contain all of node's IPs if it matches the rule, so check for first
|
||||||
if approvedIps.Contains(node.IPAddresses[0]) {
|
if approvedIps.Contains(*node.IPv4) {
|
||||||
approvedRoutes = append(approvedRoutes, advertisedRoute)
|
approvedRoutes = append(approvedRoutes, advertisedRoute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package hscontrol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -195,7 +196,7 @@ func (api headscaleV1APIServer) RegisterNode(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
addrs, err := api.h.ipAlloc.Next()
|
ipv4, ipv6, err := api.h.ipAlloc.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -208,7 +209,7 @@ func (api headscaleV1APIServer) RegisterNode(
|
||||||
request.GetUser(),
|
request.GetUser(),
|
||||||
nil,
|
nil,
|
||||||
util.RegisterMethodCLI,
|
util.RegisterMethodCLI,
|
||||||
addrs,
|
ipv4, ipv6,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -468,6 +469,24 @@ func (api headscaleV1APIServer) MoveNode(
|
||||||
return &v1.MoveNodeResponse{Node: node.Proto()}, nil
|
return &v1.MoveNodeResponse{Node: node.Proto()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api headscaleV1APIServer) BackfillNodeIPs(
|
||||||
|
ctx context.Context,
|
||||||
|
request *v1.BackfillNodeIPsRequest,
|
||||||
|
) (*v1.BackfillNodeIPsResponse, error) {
|
||||||
|
log.Trace().Msg("Backfill called")
|
||||||
|
|
||||||
|
if !request.Confirmed {
|
||||||
|
return nil, errors.New("not confirmed, aborting")
|
||||||
|
}
|
||||||
|
|
||||||
|
changes, err := api.h.db.BackfillNodeIPs(api.h.ipAlloc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &v1.BackfillNodeIPsResponse{Changes: changes}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (api headscaleV1APIServer) GetRoutes(
|
func (api headscaleV1APIServer) GetRoutes(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *v1.GetRoutesRequest,
|
request *v1.GetRoutesRequest,
|
||||||
|
|
|
@ -174,8 +174,8 @@ func addNextDNSMetadata(resolvers []*dnstype.Resolver, node *types.Node) {
|
||||||
"device_model": []string{node.Hostinfo.OS},
|
"device_model": []string{node.Hostinfo.OS},
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(node.IPAddresses) > 0 {
|
if len(node.IPs()) > 0 {
|
||||||
attrs.Add("device_ip", node.IPAddresses[0].String())
|
attrs.Add("device_ip", node.IPs()[0].String())
|
||||||
}
|
}
|
||||||
|
|
||||||
resolver.Addr = fmt.Sprintf("%s?%s", resolver.Addr, attrs.Encode())
|
resolver.Addr = fmt.Sprintf("%s?%s", resolver.Addr, attrs.Encode())
|
||||||
|
|
|
@ -17,6 +17,11 @@ import (
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var iap = func(ipStr string) *netip.Addr {
|
||||||
|
ip := netip.MustParseAddr(ipStr)
|
||||||
|
return &ip
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
|
func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
|
||||||
mach := func(hostname, username string, userid uint) *types.Node {
|
mach := func(hostname, username string, userid uint) *types.Node {
|
||||||
return &types.Node{
|
return &types.Node{
|
||||||
|
@ -176,17 +181,17 @@ func Test_fullMapResponse(t *testing.T) {
|
||||||
DiscoKey: mustDK(
|
DiscoKey: mustDK(
|
||||||
"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
||||||
),
|
),
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.1")},
|
IPv4: iap("100.64.0.1"),
|
||||||
Hostname: "mini",
|
Hostname: "mini",
|
||||||
GivenName: "mini",
|
GivenName: "mini",
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
User: types.User{Name: "mini"},
|
User: types.User{Name: "mini"},
|
||||||
ForcedTags: []string{},
|
ForcedTags: []string{},
|
||||||
AuthKeyID: 0,
|
AuthKeyID: 0,
|
||||||
AuthKey: &types.PreAuthKey{},
|
AuthKey: &types.PreAuthKey{},
|
||||||
LastSeen: &lastSeen,
|
LastSeen: &lastSeen,
|
||||||
Expiry: &expire,
|
Expiry: &expire,
|
||||||
Hostinfo: &tailcfg.Hostinfo{},
|
Hostinfo: &tailcfg.Hostinfo{},
|
||||||
Routes: []types.Route{
|
Routes: []types.Route{
|
||||||
{
|
{
|
||||||
Prefix: types.IPPrefix(netip.MustParsePrefix("0.0.0.0/0")),
|
Prefix: types.IPPrefix(netip.MustParsePrefix("0.0.0.0/0")),
|
||||||
|
@ -257,17 +262,17 @@ func Test_fullMapResponse(t *testing.T) {
|
||||||
DiscoKey: mustDK(
|
DiscoKey: mustDK(
|
||||||
"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
||||||
),
|
),
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.2")},
|
IPv4: iap("100.64.0.2"),
|
||||||
Hostname: "peer1",
|
Hostname: "peer1",
|
||||||
GivenName: "peer1",
|
GivenName: "peer1",
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
User: types.User{Name: "mini"},
|
User: types.User{Name: "mini"},
|
||||||
ForcedTags: []string{},
|
ForcedTags: []string{},
|
||||||
LastSeen: &lastSeen,
|
LastSeen: &lastSeen,
|
||||||
Expiry: &expire,
|
Expiry: &expire,
|
||||||
Hostinfo: &tailcfg.Hostinfo{},
|
Hostinfo: &tailcfg.Hostinfo{},
|
||||||
Routes: []types.Route{},
|
Routes: []types.Route{},
|
||||||
CreatedAt: created,
|
CreatedAt: created,
|
||||||
}
|
}
|
||||||
|
|
||||||
tailPeer1 := &tailcfg.Node{
|
tailPeer1 := &tailcfg.Node{
|
||||||
|
@ -312,17 +317,17 @@ func Test_fullMapResponse(t *testing.T) {
|
||||||
DiscoKey: mustDK(
|
DiscoKey: mustDK(
|
||||||
"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
||||||
),
|
),
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
|
IPv4: iap("100.64.0.3"),
|
||||||
Hostname: "peer2",
|
Hostname: "peer2",
|
||||||
GivenName: "peer2",
|
GivenName: "peer2",
|
||||||
UserID: 1,
|
UserID: 1,
|
||||||
User: types.User{Name: "peer2"},
|
User: types.User{Name: "peer2"},
|
||||||
ForcedTags: []string{},
|
ForcedTags: []string{},
|
||||||
LastSeen: &lastSeen,
|
LastSeen: &lastSeen,
|
||||||
Expiry: &expire,
|
Expiry: &expire,
|
||||||
Hostinfo: &tailcfg.Hostinfo{},
|
Hostinfo: &tailcfg.Hostinfo{},
|
||||||
Routes: []types.Route{},
|
Routes: []types.Route{},
|
||||||
CreatedAt: created,
|
CreatedAt: created,
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
|
|
@ -44,7 +44,7 @@ func tailNode(
|
||||||
pol *policy.ACLPolicy,
|
pol *policy.ACLPolicy,
|
||||||
cfg *types.Config,
|
cfg *types.Config,
|
||||||
) (*tailcfg.Node, error) {
|
) (*tailcfg.Node, error) {
|
||||||
addrs := node.IPAddresses.Prefixes()
|
addrs := node.Prefixes()
|
||||||
|
|
||||||
allowedIPs := append(
|
allowedIPs := append(
|
||||||
[]netip.Prefix{},
|
[]netip.Prefix{},
|
||||||
|
|
|
@ -89,9 +89,7 @@ func TestTailNode(t *testing.T) {
|
||||||
DiscoKey: mustDK(
|
DiscoKey: mustDK(
|
||||||
"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
"discokey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
||||||
),
|
),
|
||||||
IPAddresses: []netip.Addr{
|
IPv4: iap("100.64.0.1"),
|
||||||
netip.MustParseAddr("100.64.0.1"),
|
|
||||||
},
|
|
||||||
Hostname: "mini",
|
Hostname: "mini",
|
||||||
GivenName: "mini",
|
GivenName: "mini",
|
||||||
UserID: 0,
|
UserID: 0,
|
||||||
|
|
|
@ -597,7 +597,7 @@ func (h *Headscale) registerNodeForOIDCCallback(
|
||||||
machineKey *key.MachinePublic,
|
machineKey *key.MachinePublic,
|
||||||
expiry time.Time,
|
expiry time.Time,
|
||||||
) error {
|
) error {
|
||||||
addrs, err := h.ipAlloc.Next()
|
ipv4, ipv6, err := h.ipAlloc.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -611,7 +611,7 @@ func (h *Headscale) registerNodeForOIDCCallback(
|
||||||
user.Name,
|
user.Name,
|
||||||
&expiry,
|
&expiry,
|
||||||
util.RegisterMethodOIDC,
|
util.RegisterMethodOIDC,
|
||||||
addrs,
|
ipv4, ipv6,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,7 +229,7 @@ func ReduceFilterRules(node *types.Node, rules []tailcfg.FilterRule) []tailcfg.F
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.IPAddresses.InIPSet(expanded) {
|
if node.InIPSet(expanded) {
|
||||||
dests = append(dests, dest)
|
dests = append(dests, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +306,7 @@ func (pol *ACLPolicy) CompileSSHPolicy(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !node.IPAddresses.InIPSet(destSet) {
|
if !node.InIPSet(destSet) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -744,7 +744,7 @@ func (pol *ACLPolicy) expandIPsFromGroup(
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
filteredNodes := filterNodesByUser(nodes, user)
|
filteredNodes := filterNodesByUser(nodes, user)
|
||||||
for _, node := range filteredNodes {
|
for _, node := range filteredNodes {
|
||||||
node.IPAddresses.AppendToIPSet(&build)
|
node.AppendToIPSet(&build)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -760,7 +760,7 @@ func (pol *ACLPolicy) expandIPsFromTag(
|
||||||
// check for forced tags
|
// check for forced tags
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
if util.StringOrPrefixListContains(node.ForcedTags, alias) {
|
if util.StringOrPrefixListContains(node.ForcedTags, alias) {
|
||||||
node.IPAddresses.AppendToIPSet(&build)
|
node.AppendToIPSet(&build)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -792,7 +792,7 @@ func (pol *ACLPolicy) expandIPsFromTag(
|
||||||
}
|
}
|
||||||
|
|
||||||
if util.StringOrPrefixListContains(node.Hostinfo.RequestTags, alias) {
|
if util.StringOrPrefixListContains(node.Hostinfo.RequestTags, alias) {
|
||||||
node.IPAddresses.AppendToIPSet(&build)
|
node.AppendToIPSet(&build)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -815,7 +815,7 @@ func (pol *ACLPolicy) expandIPsFromUser(
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, node := range filteredNodes {
|
for _, node := range filteredNodes {
|
||||||
node.IPAddresses.AppendToIPSet(&build)
|
node.AppendToIPSet(&build)
|
||||||
}
|
}
|
||||||
|
|
||||||
return build.IPSet()
|
return build.IPSet()
|
||||||
|
@ -833,7 +833,7 @@ func (pol *ACLPolicy) expandIPsFromSingleIP(
|
||||||
build.Add(ip)
|
build.Add(ip)
|
||||||
|
|
||||||
for _, node := range matches {
|
for _, node := range matches {
|
||||||
node.IPAddresses.AppendToIPSet(&build)
|
node.AppendToIPSet(&build)
|
||||||
}
|
}
|
||||||
|
|
||||||
return build.IPSet()
|
return build.IPSet()
|
||||||
|
@ -850,11 +850,11 @@ func (pol *ACLPolicy) expandIPsFromIPPrefix(
|
||||||
// This is suboptimal and quite expensive, but if we only add the prefix, we will miss all the relevant IPv6
|
// This is suboptimal and quite expensive, but if we only add the prefix, we will miss all the relevant IPv6
|
||||||
// addresses for the hosts that belong to tailscale. This doesnt really affect stuff like subnet routers.
|
// addresses for the hosts that belong to tailscale. This doesnt really affect stuff like subnet routers.
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
for _, ip := range node.IPAddresses {
|
for _, ip := range node.IPs() {
|
||||||
// log.Trace().
|
// log.Trace().
|
||||||
// Msgf("checking if node ip (%s) is part of prefix (%s): %v, is single ip prefix (%v), addr: %s", ip.String(), prefix.String(), prefix.Contains(ip), prefix.IsSingleIP(), prefix.Addr().String())
|
// Msgf("checking if node ip (%s) is part of prefix (%s): %v, is single ip prefix (%v), addr: %s", ip.String(), prefix.String(), prefix.Contains(ip), prefix.IsSingleIP(), prefix.Addr().String())
|
||||||
if prefix.Contains(ip) {
|
if prefix.Contains(ip) {
|
||||||
node.IPAddresses.AppendToIPSet(&build)
|
node.AppendToIPSet(&build)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -31,6 +31,13 @@ var errOidcMutuallyExclusive = errors.New(
|
||||||
"oidc_client_secret and oidc_client_secret_path are mutually exclusive",
|
"oidc_client_secret and oidc_client_secret_path are mutually exclusive",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type IPAllocationStrategy string
|
||||||
|
|
||||||
|
const (
|
||||||
|
IPAllocationStrategySequential IPAllocationStrategy = "sequential"
|
||||||
|
IPAllocationStrategyRandom IPAllocationStrategy = "random"
|
||||||
|
)
|
||||||
|
|
||||||
// Config contains the initial Headscale configuration.
|
// Config contains the initial Headscale configuration.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ServerURL string
|
ServerURL string
|
||||||
|
@ -42,6 +49,7 @@ type Config struct {
|
||||||
NodeUpdateCheckInterval time.Duration
|
NodeUpdateCheckInterval time.Duration
|
||||||
PrefixV4 *netip.Prefix
|
PrefixV4 *netip.Prefix
|
||||||
PrefixV6 *netip.Prefix
|
PrefixV6 *netip.Prefix
|
||||||
|
IPAllocation IPAllocationStrategy
|
||||||
NoisePrivateKeyPath string
|
NoisePrivateKeyPath string
|
||||||
BaseDomain string
|
BaseDomain string
|
||||||
Log LogConfig
|
Log LogConfig
|
||||||
|
@ -230,6 +238,8 @@ func LoadConfig(path string, isFile bool) error {
|
||||||
viper.SetDefault("tuning.batch_change_delay", "800ms")
|
viper.SetDefault("tuning.batch_change_delay", "800ms")
|
||||||
viper.SetDefault("tuning.node_mapsession_buffered_chan_size", 30)
|
viper.SetDefault("tuning.node_mapsession_buffered_chan_size", 30)
|
||||||
|
|
||||||
|
viper.SetDefault("prefixes.allocation", IPAllocationStrategySequential)
|
||||||
|
|
||||||
if IsCLIConfigured() {
|
if IsCLIConfigured() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -579,18 +589,16 @@ func GetDNSConfig() (*tailcfg.DNSConfig, string) {
|
||||||
return nil, ""
|
return nil, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func Prefixes() (*netip.Prefix, *netip.Prefix, error) {
|
func PrefixV4() (*netip.Prefix, error) {
|
||||||
prefixV4Str := viper.GetString("prefixes.v4")
|
prefixV4Str := viper.GetString("prefixes.v4")
|
||||||
prefixV6Str := viper.GetString("prefixes.v6")
|
|
||||||
|
if prefixV4Str == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
prefixV4, err := netip.ParsePrefix(prefixV4Str)
|
prefixV4, err := netip.ParsePrefix(prefixV4Str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, fmt.Errorf("parsing IPv4 prefix from config: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
prefixV6, err := netip.ParsePrefix(prefixV6Str)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
builder := netipx.IPSetBuilder{}
|
builder := netipx.IPSetBuilder{}
|
||||||
|
@ -603,13 +611,33 @@ func Prefixes() (*netip.Prefix, *netip.Prefix, error) {
|
||||||
prefixV4Str, tsaddr.CGNATRange())
|
prefixV4Str, tsaddr.CGNATRange())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return &prefixV4, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrefixV6() (*netip.Prefix, error) {
|
||||||
|
prefixV6Str := viper.GetString("prefixes.v6")
|
||||||
|
|
||||||
|
if prefixV6Str == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixV6, err := netip.ParsePrefix(prefixV6Str)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing IPv6 prefix from config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
builder := netipx.IPSetBuilder{}
|
||||||
|
builder.AddPrefix(tsaddr.CGNATRange())
|
||||||
|
builder.AddPrefix(tsaddr.TailscaleULARange())
|
||||||
|
ipSet, _ := builder.IPSet()
|
||||||
|
|
||||||
if !ipSet.ContainsPrefix(prefixV6) {
|
if !ipSet.ContainsPrefix(prefixV6) {
|
||||||
log.Warn().
|
log.Warn().
|
||||||
Msgf("Prefix %s is not in the %s range. This is an unsupported configuration.",
|
Msgf("Prefix %s is not in the %s range. This is an unsupported configuration.",
|
||||||
prefixV6Str, tsaddr.TailscaleULARange())
|
prefixV6Str, tsaddr.TailscaleULARange())
|
||||||
}
|
}
|
||||||
|
|
||||||
return &prefixV4, &prefixV6, nil
|
return &prefixV6, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetHeadscaleConfig() (*Config, error) {
|
func GetHeadscaleConfig() (*Config, error) {
|
||||||
|
@ -624,11 +652,27 @@ func GetHeadscaleConfig() (*Config, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix4, prefix6, err := Prefixes()
|
prefix4, err := PrefixV4()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prefix6, err := PrefixV6()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allocStr := viper.GetString("prefixes.allocation")
|
||||||
|
var alloc IPAllocationStrategy
|
||||||
|
switch allocStr {
|
||||||
|
case string(IPAllocationStrategySequential):
|
||||||
|
alloc = IPAllocationStrategySequential
|
||||||
|
case string(IPAllocationStrategyRandom):
|
||||||
|
alloc = IPAllocationStrategyRandom
|
||||||
|
default:
|
||||||
|
log.Fatal().Msgf("config error, prefixes.allocation is set to %s, which is not a valid strategy, allowed options: %s, %s", allocStr, IPAllocationStrategySequential, IPAllocationStrategyRandom)
|
||||||
|
}
|
||||||
|
|
||||||
dnsConfig, baseDomain := GetDNSConfig()
|
dnsConfig, baseDomain := GetDNSConfig()
|
||||||
derpConfig := GetDERPConfig()
|
derpConfig := GetDERPConfig()
|
||||||
logConfig := GetLogTailConfig()
|
logConfig := GetLogTailConfig()
|
||||||
|
@ -655,8 +699,9 @@ func GetHeadscaleConfig() (*Config, error) {
|
||||||
GRPCAllowInsecure: viper.GetBool("grpc_allow_insecure"),
|
GRPCAllowInsecure: viper.GetBool("grpc_allow_insecure"),
|
||||||
DisableUpdateCheck: viper.GetBool("disable_check_updates"),
|
DisableUpdateCheck: viper.GetBool("disable_check_updates"),
|
||||||
|
|
||||||
PrefixV4: prefix4,
|
PrefixV4: prefix4,
|
||||||
PrefixV6: prefix6,
|
PrefixV6: prefix6,
|
||||||
|
IPAllocation: IPAllocationStrategy(alloc),
|
||||||
|
|
||||||
NoisePrivateKeyPath: util.AbsolutePathFromConfigPath(
|
NoisePrivateKeyPath: util.AbsolutePathFromConfigPath(
|
||||||
viper.GetString("noise.private_key_path"),
|
viper.GetString("noise.private_key_path"),
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql/driver"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -14,7 +13,6 @@ import (
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||||
"github.com/juanfont/headscale/hscontrol/policy/matcher"
|
"github.com/juanfont/headscale/hscontrol/policy/matcher"
|
||||||
"github.com/juanfont/headscale/hscontrol/util"
|
"github.com/juanfont/headscale/hscontrol/util"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"go4.org/netipx"
|
"go4.org/netipx"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
@ -83,7 +81,19 @@ type Node struct {
|
||||||
HostinfoDatabaseField string `gorm:"column:host_info"`
|
HostinfoDatabaseField string `gorm:"column:host_info"`
|
||||||
Hostinfo *tailcfg.Hostinfo `gorm:"-"`
|
Hostinfo *tailcfg.Hostinfo `gorm:"-"`
|
||||||
|
|
||||||
IPAddresses NodeAddresses
|
// IPv4DatabaseField is the string representation of v4 address,
|
||||||
|
// it is _only_ used for reading and writing the key to the
|
||||||
|
// database and should not be used.
|
||||||
|
// Use V4 instead.
|
||||||
|
IPv4DatabaseField sql.NullString `gorm:"column:ipv4"`
|
||||||
|
IPv4 *netip.Addr `gorm:"-"`
|
||||||
|
|
||||||
|
// IPv6DatabaseField is the string representation of v4 address,
|
||||||
|
// it is _only_ used for reading and writing the key to the
|
||||||
|
// database and should not be used.
|
||||||
|
// Use V6 instead.
|
||||||
|
IPv6DatabaseField sql.NullString `gorm:"column:ipv6"`
|
||||||
|
IPv6 *netip.Addr `gorm:"-"`
|
||||||
|
|
||||||
// Hostname represents the name given by the Tailscale
|
// Hostname represents the name given by the Tailscale
|
||||||
// client during registration
|
// client during registration
|
||||||
|
@ -123,89 +133,6 @@ type (
|
||||||
Nodes []*Node
|
Nodes []*Node
|
||||||
)
|
)
|
||||||
|
|
||||||
type NodeAddresses []netip.Addr
|
|
||||||
|
|
||||||
func (na NodeAddresses) Sort() {
|
|
||||||
sort.Slice(na, func(index1, index2 int) bool {
|
|
||||||
if na[index1].Is4() && na[index2].Is6() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if na[index1].Is6() && na[index2].Is4() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return na[index1].Compare(na[index2]) < 0
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (na NodeAddresses) StringSlice() []string {
|
|
||||||
na.Sort()
|
|
||||||
strSlice := make([]string, 0, len(na))
|
|
||||||
for _, addr := range na {
|
|
||||||
strSlice = append(strSlice, addr.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
return strSlice
|
|
||||||
}
|
|
||||||
|
|
||||||
func (na NodeAddresses) Prefixes() []netip.Prefix {
|
|
||||||
addrs := []netip.Prefix{}
|
|
||||||
for _, nodeAddress := range na {
|
|
||||||
ip := netip.PrefixFrom(nodeAddress, nodeAddress.BitLen())
|
|
||||||
addrs = append(addrs, ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
return addrs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (na NodeAddresses) InIPSet(set *netipx.IPSet) bool {
|
|
||||||
for _, nodeAddr := range na {
|
|
||||||
if set.Contains(nodeAddr) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendToIPSet adds the individual ips in NodeAddresses to a
|
|
||||||
// given netipx.IPSetBuilder.
|
|
||||||
func (na NodeAddresses) AppendToIPSet(build *netipx.IPSetBuilder) {
|
|
||||||
for _, ip := range na {
|
|
||||||
build.Add(ip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (na *NodeAddresses) Scan(destination interface{}) error {
|
|
||||||
switch value := destination.(type) {
|
|
||||||
case string:
|
|
||||||
addresses := strings.Split(value, ",")
|
|
||||||
*na = (*na)[:0]
|
|
||||||
for _, addr := range addresses {
|
|
||||||
if len(addr) < 1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
parsed, err := netip.ParseAddr(addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*na = append(*na, parsed)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("%w: unexpected data type %T", ErrNodeAddressesInvalid, destination)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value return json value, implement driver.Valuer interface.
|
|
||||||
func (na NodeAddresses) Value() (driver.Value, error) {
|
|
||||||
addresses := strings.Join(na.StringSlice(), ",")
|
|
||||||
|
|
||||||
return addresses, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsExpired returns whether the node registration has expired.
|
// IsExpired returns whether the node registration has expired.
|
||||||
func (node Node) IsExpired() bool {
|
func (node Node) IsExpired() bool {
|
||||||
// If Expiry is not set, the client has not indicated that
|
// If Expiry is not set, the client has not indicated that
|
||||||
|
@ -224,8 +151,65 @@ func (node *Node) IsEphemeral() bool {
|
||||||
return node.AuthKey != nil && node.AuthKey.Ephemeral
|
return node.AuthKey != nil && node.AuthKey.Ephemeral
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (node *Node) IPs() []netip.Addr {
|
||||||
|
var ret []netip.Addr
|
||||||
|
|
||||||
|
if node.IPv4 != nil {
|
||||||
|
ret = append(ret, *node.IPv4)
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.IPv6 != nil {
|
||||||
|
ret = append(ret, *node.IPv6)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *Node) Prefixes() []netip.Prefix {
|
||||||
|
addrs := []netip.Prefix{}
|
||||||
|
for _, nodeAddress := range node.IPs() {
|
||||||
|
ip := netip.PrefixFrom(nodeAddress, nodeAddress.BitLen())
|
||||||
|
addrs = append(addrs, ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
return addrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *Node) IPsAsString() []string {
|
||||||
|
var ret []string
|
||||||
|
|
||||||
|
if node.IPv4 != nil {
|
||||||
|
ret = append(ret, node.IPv4.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.IPv6 != nil {
|
||||||
|
ret = append(ret, node.IPv6.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *Node) InIPSet(set *netipx.IPSet) bool {
|
||||||
|
for _, nodeAddr := range node.IPs() {
|
||||||
|
if set.Contains(nodeAddr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendToIPSet adds the individual ips in NodeAddresses to a
|
||||||
|
// given netipx.IPSetBuilder.
|
||||||
|
func (node *Node) AppendToIPSet(build *netipx.IPSetBuilder) {
|
||||||
|
for _, ip := range node.IPs() {
|
||||||
|
build.Add(ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (node *Node) CanAccess(filter []tailcfg.FilterRule, node2 *Node) bool {
|
func (node *Node) CanAccess(filter []tailcfg.FilterRule, node2 *Node) bool {
|
||||||
allowedIPs := append([]netip.Addr{}, node2.IPAddresses...)
|
src := node.IPs()
|
||||||
|
allowedIPs := node2.IPs()
|
||||||
|
|
||||||
for _, route := range node2.Routes {
|
for _, route := range node2.Routes {
|
||||||
if route.Enabled {
|
if route.Enabled {
|
||||||
|
@ -237,7 +221,7 @@ func (node *Node) CanAccess(filter []tailcfg.FilterRule, node2 *Node) bool {
|
||||||
// TODO(kradalby): Cache or pregen this
|
// TODO(kradalby): Cache or pregen this
|
||||||
matcher := matcher.MatchFromFilterRule(rule)
|
matcher := matcher.MatchFromFilterRule(rule)
|
||||||
|
|
||||||
if !matcher.SrcsContainsIPs([]netip.Addr(node.IPAddresses)) {
|
if !matcher.SrcsContainsIPs(src) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,13 +234,16 @@ func (node *Node) CanAccess(filter []tailcfg.FilterRule, node2 *Node) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nodes Nodes) FilterByIP(ip netip.Addr) Nodes {
|
func (nodes Nodes) FilterByIP(ip netip.Addr) Nodes {
|
||||||
found := make(Nodes, 0)
|
var found Nodes
|
||||||
|
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
for _, mIP := range node.IPAddresses {
|
if node.IPv4 != nil && ip == *node.IPv4 {
|
||||||
if ip == mIP {
|
found = append(found, node)
|
||||||
found = append(found, node)
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if node.IPv6 != nil && ip == *node.IPv6 {
|
||||||
|
found = append(found, node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,10 +268,22 @@ func (node *Node) BeforeSave(tx *gorm.DB) error {
|
||||||
|
|
||||||
hi, err := json.Marshal(node.Hostinfo)
|
hi, err := json.Marshal(node.Hostinfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to marshal Hostinfo to store in db: %w", err)
|
return fmt.Errorf("marshalling Hostinfo to store in db: %w", err)
|
||||||
}
|
}
|
||||||
node.HostinfoDatabaseField = string(hi)
|
node.HostinfoDatabaseField = string(hi)
|
||||||
|
|
||||||
|
if node.IPv4 != nil {
|
||||||
|
node.IPv4DatabaseField.String, node.IPv4DatabaseField.Valid = node.IPv4.String(), true
|
||||||
|
} else {
|
||||||
|
node.IPv4DatabaseField.String, node.IPv4DatabaseField.Valid = "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.IPv6 != nil {
|
||||||
|
node.IPv6DatabaseField.String, node.IPv6DatabaseField.Valid = node.IPv6.String(), true
|
||||||
|
} else {
|
||||||
|
node.IPv6DatabaseField.String, node.IPv6DatabaseField.Valid = "", false
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,19 +295,19 @@ func (node *Node) BeforeSave(tx *gorm.DB) error {
|
||||||
func (node *Node) AfterFind(tx *gorm.DB) error {
|
func (node *Node) AfterFind(tx *gorm.DB) error {
|
||||||
var machineKey key.MachinePublic
|
var machineKey key.MachinePublic
|
||||||
if err := machineKey.UnmarshalText([]byte(node.MachineKeyDatabaseField)); err != nil {
|
if err := machineKey.UnmarshalText([]byte(node.MachineKeyDatabaseField)); err != nil {
|
||||||
return fmt.Errorf("failed to unmarshal machine key from db: %w", err)
|
return fmt.Errorf("unmarshalling machine key from db: %w", err)
|
||||||
}
|
}
|
||||||
node.MachineKey = machineKey
|
node.MachineKey = machineKey
|
||||||
|
|
||||||
var nodeKey key.NodePublic
|
var nodeKey key.NodePublic
|
||||||
if err := nodeKey.UnmarshalText([]byte(node.NodeKeyDatabaseField)); err != nil {
|
if err := nodeKey.UnmarshalText([]byte(node.NodeKeyDatabaseField)); err != nil {
|
||||||
return fmt.Errorf("failed to unmarshal node key from db: %w", err)
|
return fmt.Errorf("unmarshalling node key from db: %w", err)
|
||||||
}
|
}
|
||||||
node.NodeKey = nodeKey
|
node.NodeKey = nodeKey
|
||||||
|
|
||||||
var discoKey key.DiscoPublic
|
var discoKey key.DiscoPublic
|
||||||
if err := discoKey.UnmarshalText([]byte(node.DiscoKeyDatabaseField)); err != nil {
|
if err := discoKey.UnmarshalText([]byte(node.DiscoKeyDatabaseField)); err != nil {
|
||||||
return fmt.Errorf("failed to unmarshal disco key from db: %w", err)
|
return fmt.Errorf("unmarshalling disco key from db: %w", err)
|
||||||
}
|
}
|
||||||
node.DiscoKey = discoKey
|
node.DiscoKey = discoKey
|
||||||
|
|
||||||
|
@ -316,7 +315,7 @@ func (node *Node) AfterFind(tx *gorm.DB) error {
|
||||||
for idx, ep := range node.EndpointsDatabaseField {
|
for idx, ep := range node.EndpointsDatabaseField {
|
||||||
addrPort, err := netip.ParseAddrPort(ep)
|
addrPort, err := netip.ParseAddrPort(ep)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to parse endpoint from db: %w", err)
|
return fmt.Errorf("parsing endpoint from db: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoints[idx] = addrPort
|
endpoints[idx] = addrPort
|
||||||
|
@ -325,12 +324,28 @@ func (node *Node) AfterFind(tx *gorm.DB) error {
|
||||||
|
|
||||||
var hi tailcfg.Hostinfo
|
var hi tailcfg.Hostinfo
|
||||||
if err := json.Unmarshal([]byte(node.HostinfoDatabaseField), &hi); err != nil {
|
if err := json.Unmarshal([]byte(node.HostinfoDatabaseField), &hi); err != nil {
|
||||||
log.Trace().Err(err).Msgf("Hostinfo content: %s", node.HostinfoDatabaseField)
|
return fmt.Errorf("unmarshalling hostinfo from database: %w", err)
|
||||||
|
|
||||||
return fmt.Errorf("failed to unmarshal Hostinfo from db: %w", err)
|
|
||||||
}
|
}
|
||||||
node.Hostinfo = &hi
|
node.Hostinfo = &hi
|
||||||
|
|
||||||
|
if node.IPv4DatabaseField.Valid {
|
||||||
|
ip, err := netip.ParseAddr(node.IPv4DatabaseField.String)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing IPv4 from database: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
node.IPv4 = &ip
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.IPv6DatabaseField.Valid {
|
||||||
|
ip, err := netip.ParseAddr(node.IPv6DatabaseField.String)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing IPv6 from database: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
node.IPv6 = &ip
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,9 +354,11 @@ func (node *Node) Proto() *v1.Node {
|
||||||
Id: uint64(node.ID),
|
Id: uint64(node.ID),
|
||||||
MachineKey: node.MachineKey.String(),
|
MachineKey: node.MachineKey.String(),
|
||||||
|
|
||||||
NodeKey: node.NodeKey.String(),
|
NodeKey: node.NodeKey.String(),
|
||||||
DiscoKey: node.DiscoKey.String(),
|
DiscoKey: node.DiscoKey.String(),
|
||||||
IpAddresses: node.IPAddresses.StringSlice(),
|
|
||||||
|
// TODO(kradalby): replace list with v4, v6 field?
|
||||||
|
IpAddresses: node.IPsAsString(),
|
||||||
Name: node.Hostname,
|
Name: node.Hostname,
|
||||||
GivenName: node.GivenName,
|
GivenName: node.GivenName,
|
||||||
User: node.User.Proto(),
|
User: node.User.Proto(),
|
||||||
|
|
|
@ -12,6 +12,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_NodeCanAccess(t *testing.T) {
|
func Test_NodeCanAccess(t *testing.T) {
|
||||||
|
iap := func(ipStr string) *netip.Addr {
|
||||||
|
ip := netip.MustParseAddr(ipStr)
|
||||||
|
return &ip
|
||||||
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
node1 Node
|
node1 Node
|
||||||
|
@ -22,10 +26,10 @@ func Test_NodeCanAccess(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "no-rules",
|
name: "no-rules",
|
||||||
node1: Node{
|
node1: Node{
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("10.0.0.1")},
|
IPv4: iap("10.0.0.1"),
|
||||||
},
|
},
|
||||||
node2: Node{
|
node2: Node{
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("10.0.0.2")},
|
IPv4: iap("10.0.0.2"),
|
||||||
},
|
},
|
||||||
rules: []tailcfg.FilterRule{},
|
rules: []tailcfg.FilterRule{},
|
||||||
want: false,
|
want: false,
|
||||||
|
@ -33,10 +37,10 @@ func Test_NodeCanAccess(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "wildcard",
|
name: "wildcard",
|
||||||
node1: Node{
|
node1: Node{
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("10.0.0.1")},
|
IPv4: iap("10.0.0.1"),
|
||||||
},
|
},
|
||||||
node2: Node{
|
node2: Node{
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("10.0.0.2")},
|
IPv4: iap("10.0.0.2"),
|
||||||
},
|
},
|
||||||
rules: []tailcfg.FilterRule{
|
rules: []tailcfg.FilterRule{
|
||||||
{
|
{
|
||||||
|
@ -54,10 +58,10 @@ func Test_NodeCanAccess(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "other-cant-access-src",
|
name: "other-cant-access-src",
|
||||||
node1: Node{
|
node1: Node{
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.1")},
|
IPv4: iap("100.64.0.1"),
|
||||||
},
|
},
|
||||||
node2: Node{
|
node2: Node{
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
|
IPv4: iap("100.64.0.3"),
|
||||||
},
|
},
|
||||||
rules: []tailcfg.FilterRule{
|
rules: []tailcfg.FilterRule{
|
||||||
{
|
{
|
||||||
|
@ -72,10 +76,10 @@ func Test_NodeCanAccess(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "dest-cant-access-src",
|
name: "dest-cant-access-src",
|
||||||
node1: Node{
|
node1: Node{
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
|
IPv4: iap("100.64.0.3"),
|
||||||
},
|
},
|
||||||
node2: Node{
|
node2: Node{
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.2")},
|
IPv4: iap("100.64.0.2"),
|
||||||
},
|
},
|
||||||
rules: []tailcfg.FilterRule{
|
rules: []tailcfg.FilterRule{
|
||||||
{
|
{
|
||||||
|
@ -90,10 +94,10 @@ func Test_NodeCanAccess(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "src-can-access-dest",
|
name: "src-can-access-dest",
|
||||||
node1: Node{
|
node1: Node{
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.2")},
|
IPv4: iap("100.64.0.2"),
|
||||||
},
|
},
|
||||||
node2: Node{
|
node2: Node{
|
||||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
|
IPv4: iap("100.64.0.3"),
|
||||||
},
|
},
|
||||||
rules: []tailcfg.FilterRule{
|
rules: []tailcfg.FilterRule{
|
||||||
{
|
{
|
||||||
|
@ -118,32 +122,6 @@ func Test_NodeCanAccess(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNodeAddressesOrder(t *testing.T) {
|
|
||||||
machineAddresses := NodeAddresses{
|
|
||||||
netip.MustParseAddr("2001:db8::2"),
|
|
||||||
netip.MustParseAddr("100.64.0.2"),
|
|
||||||
netip.MustParseAddr("2001:db8::1"),
|
|
||||||
netip.MustParseAddr("100.64.0.1"),
|
|
||||||
}
|
|
||||||
|
|
||||||
strSlice := machineAddresses.StringSlice()
|
|
||||||
expected := []string{
|
|
||||||
"100.64.0.1",
|
|
||||||
"100.64.0.2",
|
|
||||||
"2001:db8::1",
|
|
||||||
"2001:db8::2",
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(strSlice) != len(expected) {
|
|
||||||
t.Fatalf("unexpected slice length: got %v, want %v", len(strSlice), len(expected))
|
|
||||||
}
|
|
||||||
for i, addr := range strSlice {
|
|
||||||
if addr != expected[i] {
|
|
||||||
t.Errorf("unexpected address at index %v: got %v, want %v", i, addr, expected[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNodeFQDN(t *testing.T) {
|
func TestNodeFQDN(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
|
@ -103,33 +103,7 @@ func CheckForFQDNRules(name string) error {
|
||||||
|
|
||||||
// From the netmask we can find out the wildcard bits (the bits that are not set in the netmask).
|
// From the netmask we can find out the wildcard bits (the bits that are not set in the netmask).
|
||||||
// This allows us to then calculate the subnets included in the subsequent class block and generate the entries.
|
// This allows us to then calculate the subnets included in the subsequent class block and generate the entries.
|
||||||
func GenerateMagicDNSRootDomains(ipPrefixes []netip.Prefix) []dnsname.FQDN {
|
func GenerateIPv4DNSRootDomain(ipPrefix netip.Prefix) []dnsname.FQDN {
|
||||||
fqdns := make([]dnsname.FQDN, 0, len(ipPrefixes))
|
|
||||||
for _, ipPrefix := range ipPrefixes {
|
|
||||||
var generateDNSRoot func(netip.Prefix) []dnsname.FQDN
|
|
||||||
switch ipPrefix.Addr().BitLen() {
|
|
||||||
case ipv4AddressLength:
|
|
||||||
generateDNSRoot = generateIPv4DNSRootDomain
|
|
||||||
|
|
||||||
case ipv6AddressLength:
|
|
||||||
generateDNSRoot = generateIPv6DNSRootDomain
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic(
|
|
||||||
fmt.Sprintf(
|
|
||||||
"unsupported IP version with address length %d",
|
|
||||||
ipPrefix.Addr().BitLen(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fqdns = append(fqdns, generateDNSRoot(ipPrefix)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fqdns
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateIPv4DNSRootDomain(ipPrefix netip.Prefix) []dnsname.FQDN {
|
|
||||||
// Conversion to the std lib net.IPnet, a bit easier to operate
|
// Conversion to the std lib net.IPnet, a bit easier to operate
|
||||||
netRange := netipx.PrefixIPNet(ipPrefix)
|
netRange := netipx.PrefixIPNet(ipPrefix)
|
||||||
maskBits, _ := netRange.Mask.Size()
|
maskBits, _ := netRange.Mask.Size()
|
||||||
|
@ -165,7 +139,27 @@ func generateIPv4DNSRootDomain(ipPrefix netip.Prefix) []dnsname.FQDN {
|
||||||
return fqdns
|
return fqdns
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateIPv6DNSRootDomain(ipPrefix netip.Prefix) []dnsname.FQDN {
|
// generateMagicDNSRootDomains generates a list of DNS entries to be included in `Routes` in `MapResponse`.
|
||||||
|
// This list of reverse DNS entries instructs the OS on what subnets and domains the Tailscale embedded DNS
|
||||||
|
// server (listening in 100.100.100.100 udp/53) should be used for.
|
||||||
|
//
|
||||||
|
// Tailscale.com includes in the list:
|
||||||
|
// - the `BaseDomain` of the user
|
||||||
|
// - the reverse DNS entry for IPv6 (0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa., see below more on IPv6)
|
||||||
|
// - the reverse DNS entries for the IPv4 subnets covered by the user's `IPPrefix`.
|
||||||
|
// In the public SaaS this is [64-127].100.in-addr.arpa.
|
||||||
|
//
|
||||||
|
// The main purpose of this function is then generating the list of IPv4 entries. For the 100.64.0.0/10, this
|
||||||
|
// is clear, and could be hardcoded. But we are allowing any range as `IPPrefix`, so we need to find out the
|
||||||
|
// subnets when we have 172.16.0.0/16 (i.e., [0-255].16.172.in-addr.arpa.), or any other subnet.
|
||||||
|
//
|
||||||
|
// How IN-ADDR.ARPA domains work is defined in RFC1035 (section 3.5). Tailscale.com seems to adhere to this,
|
||||||
|
// and do not make use of RFC2317 ("Classless IN-ADDR.ARPA delegation") - hence generating the entries for the next
|
||||||
|
// class block only.
|
||||||
|
|
||||||
|
// From the netmask we can find out the wildcard bits (the bits that are not set in the netmask).
|
||||||
|
// This allows us to then calculate the subnets included in the subsequent class block and generate the entries.
|
||||||
|
func GenerateIPv6DNSRootDomain(ipPrefix netip.Prefix) []dnsname.FQDN {
|
||||||
const nibbleLen = 4
|
const nibbleLen = 4
|
||||||
|
|
||||||
maskBits, _ := netipx.PrefixIPNet(ipPrefix).Mask.Size()
|
maskBits, _ := netipx.PrefixIPNet(ipPrefix).Mask.Size()
|
||||||
|
|
|
@ -148,10 +148,7 @@ func TestCheckForFQDNRules(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMagicDNSRootDomains100(t *testing.T) {
|
func TestMagicDNSRootDomains100(t *testing.T) {
|
||||||
prefixes := []netip.Prefix{
|
domains := GenerateIPv4DNSRootDomain(netip.MustParsePrefix("100.64.0.0/10"))
|
||||||
netip.MustParsePrefix("100.64.0.0/10"),
|
|
||||||
}
|
|
||||||
domains := GenerateMagicDNSRootDomains(prefixes)
|
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
|
@ -185,10 +182,7 @@ func TestMagicDNSRootDomains100(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMagicDNSRootDomains172(t *testing.T) {
|
func TestMagicDNSRootDomains172(t *testing.T) {
|
||||||
prefixes := []netip.Prefix{
|
domains := GenerateIPv4DNSRootDomain(netip.MustParsePrefix("172.16.0.0/16"))
|
||||||
netip.MustParsePrefix("172.16.0.0/16"),
|
|
||||||
}
|
|
||||||
domains := GenerateMagicDNSRootDomains(prefixes)
|
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
|
@ -213,20 +207,14 @@ func TestMagicDNSRootDomains172(t *testing.T) {
|
||||||
|
|
||||||
// Happens when netmask is a multiple of 4 bits (sounds likely).
|
// Happens when netmask is a multiple of 4 bits (sounds likely).
|
||||||
func TestMagicDNSRootDomainsIPv6Single(t *testing.T) {
|
func TestMagicDNSRootDomainsIPv6Single(t *testing.T) {
|
||||||
prefixes := []netip.Prefix{
|
domains := GenerateIPv6DNSRootDomain(netip.MustParsePrefix("fd7a:115c:a1e0::/48"))
|
||||||
netip.MustParsePrefix("fd7a:115c:a1e0::/48"),
|
|
||||||
}
|
|
||||||
domains := GenerateMagicDNSRootDomains(prefixes)
|
|
||||||
|
|
||||||
assert.Len(t, domains, 1)
|
assert.Len(t, domains, 1)
|
||||||
assert.Equal(t, "0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa.", domains[0].WithTrailingDot())
|
assert.Equal(t, "0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa.", domains[0].WithTrailingDot())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMagicDNSRootDomainsIPv6SingleMultiple(t *testing.T) {
|
func TestMagicDNSRootDomainsIPv6SingleMultiple(t *testing.T) {
|
||||||
prefixes := []netip.Prefix{
|
domains := GenerateIPv6DNSRootDomain(netip.MustParsePrefix("fd7a:115c:a1e0::/50"))
|
||||||
netip.MustParsePrefix("fd7a:115c:a1e0::/50"),
|
|
||||||
}
|
|
||||||
domains := GenerateMagicDNSRootDomains(prefixes)
|
|
||||||
|
|
||||||
yieldsRoot := func(dom string) bool {
|
yieldsRoot := func(dom string) bool {
|
||||||
for _, candidate := range domains {
|
for _, candidate := range domains {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||||
|
"github.com/juanfont/headscale/hscontrol/types"
|
||||||
"github.com/juanfont/headscale/integration/hsic"
|
"github.com/juanfont/headscale/integration/hsic"
|
||||||
"github.com/juanfont/headscale/integration/tsic"
|
"github.com/juanfont/headscale/integration/tsic"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
@ -39,6 +40,7 @@ func TestPingAllByIP(t *testing.T) {
|
||||||
hsic.WithEmbeddedDERPServerOnly(),
|
hsic.WithEmbeddedDERPServerOnly(),
|
||||||
hsic.WithTLS(),
|
hsic.WithTLS(),
|
||||||
hsic.WithHostnameAsServerURL(),
|
hsic.WithHostnameAsServerURL(),
|
||||||
|
hsic.WithIPAllocationStrategy(types.IPAllocationStrategyRandom),
|
||||||
)
|
)
|
||||||
assertNoErrHeadscaleEnv(t, err)
|
assertNoErrHeadscaleEnv(t, err)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package hsic
|
package hsic
|
||||||
|
|
||||||
|
import "github.com/juanfont/headscale/hscontrol/types"
|
||||||
|
|
||||||
// const (
|
// const (
|
||||||
// defaultEphemeralNodeInactivityTimeout = time.Second * 30
|
// defaultEphemeralNodeInactivityTimeout = time.Second * 30
|
||||||
// defaultNodeUpdateCheckInterval = time.Second * 10
|
// defaultNodeUpdateCheckInterval = time.Second * 10
|
||||||
|
@ -129,5 +131,9 @@ func DefaultConfigEnv() map[string]string {
|
||||||
"HEADSCALE_DERP_URLS": "https://controlplane.tailscale.com/derpmap/default",
|
"HEADSCALE_DERP_URLS": "https://controlplane.tailscale.com/derpmap/default",
|
||||||
"HEADSCALE_DERP_AUTO_UPDATE_ENABLED": "false",
|
"HEADSCALE_DERP_AUTO_UPDATE_ENABLED": "false",
|
||||||
"HEADSCALE_DERP_UPDATE_FREQUENCY": "1m",
|
"HEADSCALE_DERP_UPDATE_FREQUENCY": "1m",
|
||||||
|
|
||||||
|
// a bunch of tests (ACL/Policy) rely on predicable IP alloc,
|
||||||
|
// so ensure the sequential alloc is used by default.
|
||||||
|
"HEADSCALE_PREFIXES_ALLOCATION": string(types.IPAllocationStrategySequential),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||||
"github.com/juanfont/headscale/hscontrol/policy"
|
"github.com/juanfont/headscale/hscontrol/policy"
|
||||||
|
"github.com/juanfont/headscale/hscontrol/types"
|
||||||
"github.com/juanfont/headscale/hscontrol/util"
|
"github.com/juanfont/headscale/hscontrol/util"
|
||||||
"github.com/juanfont/headscale/integration/dockertestutil"
|
"github.com/juanfont/headscale/integration/dockertestutil"
|
||||||
"github.com/juanfont/headscale/integration/integrationutil"
|
"github.com/juanfont/headscale/integration/integrationutil"
|
||||||
|
@ -173,6 +174,13 @@ func WithPostgres() Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithIPAllocationStrategy sets the tests IP Allocation strategy.
|
||||||
|
func WithIPAllocationStrategy(strat types.IPAllocationStrategy) Option {
|
||||||
|
return func(hsic *HeadscaleInContainer) {
|
||||||
|
hsic.env["HEADSCALE_PREFIXES_ALLOCATION"] = string(strat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithEmbeddedDERPServerOnly configures Headscale to start
|
// WithEmbeddedDERPServerOnly configures Headscale to start
|
||||||
// and only use the embedded DERP server.
|
// and only use the embedded DERP server.
|
||||||
// It requires WithTLS and WithHostnameAsServerURL to be
|
// It requires WithTLS and WithHostnameAsServerURL to be
|
||||||
|
|
|
@ -123,6 +123,13 @@ service HeadscaleService {
|
||||||
post: "/api/v1/node/{node_id}/user"
|
post: "/api/v1/node/{node_id}/user"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpc BackfillNodeIPs(BackfillNodeIPsRequest) returns (BackfillNodeIPsResponse) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/api/v1/node/backfillips"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// --- Node end ---
|
// --- Node end ---
|
||||||
|
|
||||||
// --- Route start ---
|
// --- Route start ---
|
||||||
|
|
|
@ -126,3 +126,11 @@ message DebugCreateNodeRequest {
|
||||||
message DebugCreateNodeResponse {
|
message DebugCreateNodeResponse {
|
||||||
Node node = 1;
|
Node node = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message BackfillNodeIPsRequest {
|
||||||
|
bool confirmed = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BackfillNodeIPsResponse {
|
||||||
|
repeated string changes = 1;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue