Merge branch 'main' into metrics-listen

This commit is contained in:
Kristoffer Dalby 2022-03-02 11:35:33 +00:00 committed by GitHub
commit b61500670c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1094 additions and 948 deletions

View File

@ -10,6 +10,13 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Delete upstream contributor branch
# Allow continue on failure to account for when the
# upstream branch is deleted or does not exist.
continue-on-error: true
run: git push origin --delete update-contributors
- name: Create up-to-date contributors branch
run: git checkout -B update-contributors
- uses: BobAnkh/add-contributors@v0.2.2 - uses: BobAnkh/add-contributors@v0.2.2
with: with:
CONTRIBUTOR: "## Contributors" CONTRIBUTOR: "## Contributors"

View File

@ -29,6 +29,7 @@ linters:
- wrapcheck - wrapcheck
- dupl - dupl
- makezero - makezero
- maintidx
# We might want to enable this, but it might be a lot of work # We might want to enable this, but it might be a lot of work
- cyclop - cyclop

View File

@ -1,27 +1,39 @@
# CHANGELOG # CHANGELOG
**0.15.0 (2022-xx-xx):** ## 0.15.0 (2022-xx-xx)
**BREAKING**: **Note:** Take a backup of your database before upgrading.
### BREAKING
- Boundaries between Namespaces has been removed and all nodes can communicate by default [#357](https://github.com/juanfont/headscale/pull/357) - Boundaries between Namespaces has been removed and all nodes can communicate by default [#357](https://github.com/juanfont/headscale/pull/357)
- To limit access between nodes, use [ACLs](./docs/acls.md). - To limit access between nodes, use [ACLs](./docs/acls.md).
**Changes**: ### Features
- Add support for writing ACL files with YAML [#359](https://github.com/juanfont/headscale/pull/359)
- Users can now use emails in ACL's groups [#372](https://github.com/juanfont/headscale/issues/372)
- Add shorthand aliases for commands and subcommands [#376](https://github.com/juanfont/headscale/pull/376)
### Changes
- Fix a bug were the same IP could be assigned to multiple hosts if joined in quick succession [#346](https://github.com/juanfont/headscale/pull/346) - Fix a bug were the same IP could be assigned to multiple hosts if joined in quick succession [#346](https://github.com/juanfont/headscale/pull/346)
- Simplify the code behind registration of machines [#366](https://github.com/juanfont/headscale/pull/366)
- Nodes are now only written to database if they are registrated successfully
- Fix a limitation in the ACLs that prevented users to write rules with `*` as source [#374](https://github.com/juanfont/headscale/issues/374)
- Reduce the overhead of marshal/unmarshal for Hostinfo, routes and endpoints by using specific types in Machine [#371](https://github.com/juanfont/headscale/pull/371)
**0.14.0 (2022-02-24):** ## 0.14.0 (2022-02-24)
**UPCOMING BREAKING**: **UPCOMING ### BREAKING
From the **next** version (`0.15.0`), all machines will be able to communicate regardless of From the **next\*\* version (`0.15.0`), all machines will be able to communicate regardless of
if they are in the same namespace. This means that the behaviour currently limited to ACLs if they are in the same namespace. This means that the behaviour currently limited to ACLs
will become default. From version `0.15.0`, all limitation of communications must be done will become default. From version `0.15.0`, all limitation of communications must be done
with ACLs. with ACLs.
This is a part of aligning `headscale`'s behaviour with Tailscale's upstream behaviour. This is a part of aligning `headscale`'s behaviour with Tailscale's upstream behaviour.
**BREAKING**: ### BREAKING
- ACLs have been rewritten to align with the bevaviour Tailscale Control Panel provides. **NOTE:** This is only active if you use ACLs - ACLs have been rewritten to align with the bevaviour Tailscale Control Panel provides. **NOTE:** This is only active if you use ACLs
- Namespaces are now treated as Users - Namespaces are now treated as Users
@ -29,17 +41,17 @@ This is a part of aligning `headscale`'s behaviour with Tailscale's upstream beh
- Tags should now work correctly and adding a host to Headscale should now reload the rules. - Tags should now work correctly and adding a host to Headscale should now reload the rules.
- The documentation have a [fictional example](docs/acls.md) that should cover some use cases of the ACLs features - The documentation have a [fictional example](docs/acls.md) that should cover some use cases of the ACLs features
**Features**: ### Features
- Add support for configurable mTLS [docs](docs/tls.md#configuring-mutual-tls-authentication-mtls) [#297](https://github.com/juanfont/headscale/pull/297) - Add support for configurable mTLS [docs](docs/tls.md#configuring-mutual-tls-authentication-mtls) [#297](https://github.com/juanfont/headscale/pull/297)
**Changes**: ### Changes
- Remove dependency on CGO (switch from CGO SQLite to pure Go) [#346](https://github.com/juanfont/headscale/pull/346) - Remove dependency on CGO (switch from CGO SQLite to pure Go) [#346](https://github.com/juanfont/headscale/pull/346)
**0.13.0 (2022-02-18):** **0.13.0 (2022-02-18):**
**Features**: ### Features
- Add IPv6 support to the prefix assigned to namespaces - Add IPv6 support to the prefix assigned to namespaces
- Add API Key support - Add API Key support
@ -50,7 +62,7 @@ This is a part of aligning `headscale`'s behaviour with Tailscale's upstream beh
- `oidc.domain_map` option has been removed - `oidc.domain_map` option has been removed
- `strip_email_domain` option has been added (see [config-example.yaml](./config_example.yaml)) - `strip_email_domain` option has been added (see [config-example.yaml](./config_example.yaml))
**Changes**: ### Changes
- `ip_prefix` is now superseded by `ip_prefixes` in the configuration [#208](https://github.com/juanfont/headscale/pull/208) - `ip_prefix` is now superseded by `ip_prefixes` in the configuration [#208](https://github.com/juanfont/headscale/pull/208)
- Upgrade `tailscale` (1.20.4) and other dependencies to latest [#314](https://github.com/juanfont/headscale/pull/314) - Upgrade `tailscale` (1.20.4) and other dependencies to latest [#314](https://github.com/juanfont/headscale/pull/314)
@ -59,35 +71,35 @@ This is a part of aligning `headscale`'s behaviour with Tailscale's upstream beh
**0.12.4 (2022-01-29):** **0.12.4 (2022-01-29):**
**Changes**: ### Changes
- Make gRPC Unix Socket permissions configurable [#292](https://github.com/juanfont/headscale/pull/292) - Make gRPC Unix Socket permissions configurable [#292](https://github.com/juanfont/headscale/pull/292)
- Trim whitespace before reading Private Key from file [#289](https://github.com/juanfont/headscale/pull/289) - Trim whitespace before reading Private Key from file [#289](https://github.com/juanfont/headscale/pull/289)
- Add new command to generate a private key for `headscale` [#290](https://github.com/juanfont/headscale/pull/290) - Add new command to generate a private key for `headscale` [#290](https://github.com/juanfont/headscale/pull/290)
- Fixed issue where hosts deleted from control server may be written back to the database, as long as they are connected to the control server [#278](https://github.com/juanfont/headscale/pull/278) - Fixed issue where hosts deleted from control server may be written back to the database, as long as they are connected to the control server [#278](https://github.com/juanfont/headscale/pull/278)
**0.12.3 (2022-01-13):** ## 0.12.3 (2022-01-13)
**Changes**: ### Changes
- Added Alpine container [#270](https://github.com/juanfont/headscale/pull/270) - Added Alpine container [#270](https://github.com/juanfont/headscale/pull/270)
- Minor updates in dependencies [#271](https://github.com/juanfont/headscale/pull/271) - Minor updates in dependencies [#271](https://github.com/juanfont/headscale/pull/271)
**0.12.2 (2022-01-11):** ## 0.12.2 (2022-01-11)
Happy New Year! Happy New Year!
**Changes**: ### Changes
- Fix Docker release [#258](https://github.com/juanfont/headscale/pull/258) - Fix Docker release [#258](https://github.com/juanfont/headscale/pull/258)
- Rewrite main docs [#262](https://github.com/juanfont/headscale/pull/262) - Rewrite main docs [#262](https://github.com/juanfont/headscale/pull/262)
- Improve Docker docs [#263](https://github.com/juanfont/headscale/pull/263) - Improve Docker docs [#263](https://github.com/juanfont/headscale/pull/263)
**0.12.1 (2021-12-24):** ## 0.12.1 (2021-12-24)
(We are skipping 0.12.0 to correct a mishap done weeks ago with the version tagging) (We are skipping 0.12.0 to correct a mishap done weeks ago with the version tagging)
**BREAKING**: ### BREAKING
- Upgrade to Tailscale 1.18 [#229](https://github.com/juanfont/headscale/pull/229) - Upgrade to Tailscale 1.18 [#229](https://github.com/juanfont/headscale/pull/229)
- This change requires a new format for private key, private keys are now generated automatically: - This change requires a new format for private key, private keys are now generated automatically:
@ -95,19 +107,19 @@ Happy New Year!
2. Restart `headscale`, a new key will be generated. 2. Restart `headscale`, a new key will be generated.
3. Restart all Tailscale clients to fetch the new key 3. Restart all Tailscale clients to fetch the new key
**Changes**: ### Changes
- Unify configuration example [#197](https://github.com/juanfont/headscale/pull/197) - Unify configuration example [#197](https://github.com/juanfont/headscale/pull/197)
- Add stricter linting and formatting [#223](https://github.com/juanfont/headscale/pull/223) - Add stricter linting and formatting [#223](https://github.com/juanfont/headscale/pull/223)
**Features**: ### Features
- Add gRPC and HTTP API (HTTP API is currently disabled) [#204](https://github.com/juanfont/headscale/pull/204) - Add gRPC and HTTP API (HTTP API is currently disabled) [#204](https://github.com/juanfont/headscale/pull/204)
- Use gRPC between the CLI and the server [#206](https://github.com/juanfont/headscale/pull/206), [#212](https://github.com/juanfont/headscale/pull/212) - Use gRPC between the CLI and the server [#206](https://github.com/juanfont/headscale/pull/206), [#212](https://github.com/juanfont/headscale/pull/212)
- Beta OpenID Connect support [#126](https://github.com/juanfont/headscale/pull/126), [#227](https://github.com/juanfont/headscale/pull/227) - Beta OpenID Connect support [#126](https://github.com/juanfont/headscale/pull/126), [#227](https://github.com/juanfont/headscale/pull/227)
**0.11.0 (2021-10-25):** ## 0.11.0 (2021-10-25)
**BREAKING**: ### BREAKING
- Make headscale fetch DERP map from URL and file [#196](https://github.com/juanfont/headscale/pull/196) - Make headscale fetch DERP map from URL and file [#196](https://github.com/juanfont/headscale/pull/196)

95
acls.go
View File

@ -5,11 +5,13 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/tailscale/hujson" "github.com/tailscale/hujson"
"gopkg.in/yaml.v3"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
) )
@ -53,16 +55,36 @@ func (h *Headscale) LoadACLPolicy(path string) error {
return err return err
} }
switch filepath.Ext(path) {
case ".yml", ".yaml":
log.Debug().
Str("path", path).
Bytes("file", policyBytes).
Msg("Loading ACLs from YAML")
err := yaml.Unmarshal(policyBytes, &policy)
if err != nil {
return err
}
log.Trace().
Interface("policy", policy).
Msg("Loaded policy from YAML")
default:
ast, err := hujson.Parse(policyBytes) ast, err := hujson.Parse(policyBytes)
if err != nil { if err != nil {
return err return err
} }
ast.Standardize() ast.Standardize()
policyBytes = ast.Pack() policyBytes = ast.Pack()
err = json.Unmarshal(policyBytes, &policy) err = json.Unmarshal(policyBytes, &policy)
if err != nil { if err != nil {
return err return err
} }
}
if policy.IsZero() { if policy.IsZero() {
return errEmptyPolicy return errEmptyPolicy
} }
@ -138,7 +160,7 @@ func (h *Headscale) generateACLPolicySrcIP(
aclPolicy ACLPolicy, aclPolicy ACLPolicy,
u string, u string,
) ([]string, error) { ) ([]string, error) {
return expandAlias(machines, aclPolicy, u) return expandAlias(machines, aclPolicy, u, h.cfg.OIDC.StripEmaildomain)
} }
func (h *Headscale) generateACLPolicyDestPorts( func (h *Headscale) generateACLPolicyDestPorts(
@ -164,7 +186,12 @@ func (h *Headscale) generateACLPolicyDestPorts(
alias = fmt.Sprintf("%s:%s", tokens[0], tokens[1]) alias = fmt.Sprintf("%s:%s", tokens[0], tokens[1])
} }
expanded, err := expandAlias(machines, aclPolicy, alias) expanded, err := expandAlias(
machines,
aclPolicy,
alias,
h.cfg.OIDC.StripEmaildomain,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -196,6 +223,7 @@ func expandAlias(
machines []Machine, machines []Machine,
aclPolicy ACLPolicy, aclPolicy ACLPolicy,
alias string, alias string,
stripEmailDomain bool,
) ([]string, error) { ) ([]string, error) {
ips := []string{} ips := []string{}
if alias == "*" { if alias == "*" {
@ -203,7 +231,7 @@ func expandAlias(
} }
if strings.HasPrefix(alias, "group:") { if strings.HasPrefix(alias, "group:") {
namespaces, err := expandGroup(aclPolicy, alias) namespaces, err := expandGroup(aclPolicy, alias, stripEmailDomain)
if err != nil { if err != nil {
return ips, err return ips, err
} }
@ -218,20 +246,14 @@ func expandAlias(
} }
if strings.HasPrefix(alias, "tag:") { if strings.HasPrefix(alias, "tag:") {
owners, err := expandTagOwners(aclPolicy, alias) owners, err := expandTagOwners(aclPolicy, alias, stripEmailDomain)
if err != nil { if err != nil {
return ips, err return ips, err
} }
for _, namespace := range owners { for _, namespace := range owners {
machines := filterMachinesByNamespace(machines, namespace) machines := filterMachinesByNamespace(machines, namespace)
for _, machine := range machines { for _, machine := range machines {
if len(machine.HostInfo) == 0 { hi := machine.GetHostInfo()
continue
}
hi, err := machine.GetHostInfo()
if err != nil {
return ips, err
}
for _, t := range hi.RequestTags { for _, t := range hi.RequestTags {
if alias == t { if alias == t {
ips = append(ips, machine.IPAddresses.ToStringSlice()...) ips = append(ips, machine.IPAddresses.ToStringSlice()...)
@ -245,10 +267,8 @@ func expandAlias(
// if alias is a namespace // if alias is a namespace
nodes := filterMachinesByNamespace(machines, alias) nodes := filterMachinesByNamespace(machines, alias)
nodes, err := excludeCorrectlyTaggedNodes(aclPolicy, nodes, alias) nodes = excludeCorrectlyTaggedNodes(aclPolicy, nodes, alias)
if err != nil {
return ips, err
}
for _, n := range nodes { for _, n := range nodes {
ips = append(ips, n.IPAddresses.ToStringSlice()...) ips = append(ips, n.IPAddresses.ToStringSlice()...)
} }
@ -283,7 +303,7 @@ func excludeCorrectlyTaggedNodes(
aclPolicy ACLPolicy, aclPolicy ACLPolicy,
nodes []Machine, nodes []Machine,
namespace string, namespace string,
) ([]Machine, error) { ) []Machine {
out := []Machine{} out := []Machine{}
tags := []string{} tags := []string{}
for tag, ns := range aclPolicy.TagOwners { for tag, ns := range aclPolicy.TagOwners {
@ -293,15 +313,8 @@ func excludeCorrectlyTaggedNodes(
} }
// for each machine if tag is in tags list, don't append it. // for each machine if tag is in tags list, don't append it.
for _, machine := range nodes { for _, machine := range nodes {
if len(machine.HostInfo) == 0 { hi := machine.GetHostInfo()
out = append(out, machine)
continue
}
hi, err := machine.GetHostInfo()
if err != nil {
return out, err
}
found := false found := false
for _, t := range hi.RequestTags { for _, t := range hi.RequestTags {
if containsString(tags, t) { if containsString(tags, t) {
@ -315,7 +328,7 @@ func excludeCorrectlyTaggedNodes(
} }
} }
return out, nil return out
} }
func expandPorts(portsStr string) (*[]tailcfg.PortRange, error) { func expandPorts(portsStr string) (*[]tailcfg.PortRange, error) {
@ -374,7 +387,11 @@ func filterMachinesByNamespace(machines []Machine, namespace string) []Machine {
// expandTagOwners will return a list of namespace. An owner can be either a namespace or a group // expandTagOwners will return a list of namespace. An owner can be either a namespace or a group
// a group cannot be composed of groups. // a group cannot be composed of groups.
func expandTagOwners(aclPolicy ACLPolicy, tag string) ([]string, error) { func expandTagOwners(
aclPolicy ACLPolicy,
tag string,
stripEmailDomain bool,
) ([]string, error) {
var owners []string var owners []string
ows, ok := aclPolicy.TagOwners[tag] ows, ok := aclPolicy.TagOwners[tag]
if !ok { if !ok {
@ -386,7 +403,7 @@ func expandTagOwners(aclPolicy ACLPolicy, tag string) ([]string, error) {
} }
for _, owner := range ows { for _, owner := range ows {
if strings.HasPrefix(owner, "group:") { if strings.HasPrefix(owner, "group:") {
gs, err := expandGroup(aclPolicy, owner) gs, err := expandGroup(aclPolicy, owner, stripEmailDomain)
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
@ -401,8 +418,13 @@ func expandTagOwners(aclPolicy ACLPolicy, tag string) ([]string, error) {
// expandGroup will return the list of namespace inside the group // expandGroup will return the list of namespace inside the group
// after some validation. // after some validation.
func expandGroup(aclPolicy ACLPolicy, group string) ([]string, error) { func expandGroup(
groups, ok := aclPolicy.Groups[group] aclPolicy ACLPolicy,
group string,
stripEmailDomain bool,
) ([]string, error) {
outGroups := []string{}
aclGroups, ok := aclPolicy.Groups[group]
if !ok { if !ok {
return []string{}, fmt.Errorf( return []string{}, fmt.Errorf(
"group %v isn't registered. %w", "group %v isn't registered. %w",
@ -410,14 +432,23 @@ func expandGroup(aclPolicy ACLPolicy, group string) ([]string, error) {
errInvalidGroup, errInvalidGroup,
) )
} }
for _, g := range groups { for _, group := range aclGroups {
if strings.HasPrefix(g, "group:") { if strings.HasPrefix(group, "group:") {
return []string{}, fmt.Errorf( return []string{}, fmt.Errorf(
"%w. A group cannot be composed of groups. https://tailscale.com/kb/1018/acls/#groups", "%w. A group cannot be composed of groups. https://tailscale.com/kb/1018/acls/#groups",
errInvalidGroup, errInvalidGroup,
) )
} }
grp, err := NormalizeNamespaceName(group, stripEmailDomain)
if err != nil {
return []string{}, fmt.Errorf(
"failed to normalize group %q, err: %w",
group,
errInvalidGroup,
)
}
outGroups = append(outGroups, grp)
} }
return groups, nil return outGroups, nil
} }

View File

@ -6,7 +6,6 @@ import (
"testing" "testing"
"gopkg.in/check.v1" "gopkg.in/check.v1"
"gorm.io/datatypes"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
) )
@ -108,9 +107,12 @@ func (s *Suite) TestValidExpandTagOwnersInUsers(c *check.C) {
_, err = app.GetMachine("user1", "testmachine") _, err = app.GetMachine("user1", "testmachine")
c.Assert(err, check.NotNil) c.Assert(err, check.NotNil)
hostInfo := []byte( hostInfo := tailcfg.Hostinfo{
"{\"OS\":\"centos\",\"Hostname\":\"testmachine\",\"RequestTags\":[\"tag:test\"]}", OS: "centos",
) Hostname: "testmachine",
RequestTags: []string{"tag:test"},
}
machine := Machine{ machine := Machine{
ID: 0, ID: 0,
MachineKey: "foo", MachineKey: "foo",
@ -119,10 +121,9 @@ func (s *Suite) TestValidExpandTagOwnersInUsers(c *check.C) {
Name: "testmachine", Name: "testmachine",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
HostInfo: datatypes.JSON(hostInfo), HostInfo: HostInfo(hostInfo),
} }
app.db.Save(&machine) app.db.Save(&machine)
@ -152,9 +153,12 @@ func (s *Suite) TestValidExpandTagOwnersInPorts(c *check.C) {
_, err = app.GetMachine("user1", "testmachine") _, err = app.GetMachine("user1", "testmachine")
c.Assert(err, check.NotNil) c.Assert(err, check.NotNil)
hostInfo := []byte( hostInfo := tailcfg.Hostinfo{
"{\"OS\":\"centos\",\"Hostname\":\"testmachine\",\"RequestTags\":[\"tag:test\"]}", OS: "centos",
) Hostname: "testmachine",
RequestTags: []string{"tag:test"},
}
machine := Machine{ machine := Machine{
ID: 1, ID: 1,
MachineKey: "12345", MachineKey: "12345",
@ -163,10 +167,9 @@ func (s *Suite) TestValidExpandTagOwnersInPorts(c *check.C) {
Name: "testmachine", Name: "testmachine",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
HostInfo: datatypes.JSON(hostInfo), HostInfo: HostInfo(hostInfo),
} }
app.db.Save(&machine) app.db.Save(&machine)
@ -196,9 +199,12 @@ func (s *Suite) TestInvalidTagValidNamespace(c *check.C) {
_, err = app.GetMachine("user1", "testmachine") _, err = app.GetMachine("user1", "testmachine")
c.Assert(err, check.NotNil) c.Assert(err, check.NotNil)
hostInfo := []byte( hostInfo := tailcfg.Hostinfo{
"{\"OS\":\"centos\",\"Hostname\":\"testmachine\",\"RequestTags\":[\"tag:foo\"]}", OS: "centos",
) Hostname: "testmachine",
RequestTags: []string{"tag:foo"},
}
machine := Machine{ machine := Machine{
ID: 1, ID: 1,
MachineKey: "12345", MachineKey: "12345",
@ -207,10 +213,9 @@ func (s *Suite) TestInvalidTagValidNamespace(c *check.C) {
Name: "testmachine", Name: "testmachine",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
HostInfo: datatypes.JSON(hostInfo), HostInfo: HostInfo(hostInfo),
} }
app.db.Save(&machine) app.db.Save(&machine)
@ -239,9 +244,12 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) {
_, err = app.GetMachine("user1", "webserver") _, err = app.GetMachine("user1", "webserver")
c.Assert(err, check.NotNil) c.Assert(err, check.NotNil)
hostInfo := []byte( hostInfo := tailcfg.Hostinfo{
"{\"OS\":\"centos\",\"Hostname\":\"webserver\",\"RequestTags\":[\"tag:webapp\"]}", OS: "centos",
) Hostname: "webserver",
RequestTags: []string{"tag:webapp"},
}
machine := Machine{ machine := Machine{
ID: 1, ID: 1,
MachineKey: "12345", MachineKey: "12345",
@ -250,14 +258,16 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) {
Name: "webserver", Name: "webserver",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}, IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
HostInfo: datatypes.JSON(hostInfo), HostInfo: HostInfo(hostInfo),
} }
app.db.Save(&machine) app.db.Save(&machine)
_, err = app.GetMachine("user1", "user") _, err = app.GetMachine("user1", "user")
hostInfo = []byte("{\"OS\":\"debian\",\"Hostname\":\"user\"}") hostInfo2 := tailcfg.Hostinfo{
OS: "debian",
Hostname: "Hostname",
}
c.Assert(err, check.NotNil) c.Assert(err, check.NotNil)
machine = Machine{ machine = Machine{
ID: 2, ID: 2,
@ -267,10 +277,9 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) {
Name: "user", Name: "user",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")}, IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
HostInfo: datatypes.JSON(hostInfo), HostInfo: HostInfo(hostInfo2),
} }
app.db.Save(&machine) app.db.Save(&machine)
@ -328,6 +337,22 @@ func (s *Suite) TestPortWildcard(c *check.C) {
c.Assert(rules[0].SrcIPs[0], check.Equals, "*") c.Assert(rules[0].SrcIPs[0], check.Equals, "*")
} }
func (s *Suite) TestPortWildcardYAML(c *check.C) {
err := app.LoadACLPolicy("./tests/acls/acl_policy_basic_wildcards.yaml")
c.Assert(err, check.IsNil)
rules, err := app.generateACLRules()
c.Assert(err, check.IsNil)
c.Assert(rules, check.NotNil)
c.Assert(rules, check.HasLen, 1)
c.Assert(rules[0].DstPorts, check.HasLen, 1)
c.Assert(rules[0].DstPorts[0].Ports.First, check.Equals, uint16(0))
c.Assert(rules[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535))
c.Assert(rules[0].SrcIPs, check.HasLen, 1)
c.Assert(rules[0].SrcIPs[0], check.Equals, "*")
}
func (s *Suite) TestPortNamespace(c *check.C) { func (s *Suite) TestPortNamespace(c *check.C) {
namespace, err := app.CreateNamespace("testnamespace") namespace, err := app.CreateNamespace("testnamespace")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
@ -345,7 +370,6 @@ func (s *Suite) TestPortNamespace(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testmachine", Name: "testmachine",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: ips, IPAddresses: ips,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
@ -388,7 +412,6 @@ func (s *Suite) TestPortGroup(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testmachine", Name: "testmachine",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: ips, IPAddresses: ips,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
@ -416,6 +439,7 @@ func Test_expandGroup(t *testing.T) {
type args struct { type args struct {
aclPolicy ACLPolicy aclPolicy ACLPolicy
group string group string
stripEmailDomain bool
} }
tests := []struct { tests := []struct {
name string name string
@ -433,6 +457,7 @@ func Test_expandGroup(t *testing.T) {
}, },
}, },
group: "group:test", group: "group:test",
stripEmailDomain: true,
}, },
want: []string{"user1", "user2", "user3"}, want: []string{"user1", "user2", "user3"},
wantErr: false, wantErr: false,
@ -447,14 +472,53 @@ func Test_expandGroup(t *testing.T) {
}, },
}, },
group: "group:undefined", group: "group:undefined",
stripEmailDomain: true,
}, },
want: []string{}, want: []string{},
wantErr: true, wantErr: true,
}, },
{
name: "Expand emails in group",
args: args{
aclPolicy: ACLPolicy{
Groups: Groups{
"group:admin": []string{
"joe.bar@gmail.com",
"john.doe@yahoo.fr",
},
},
},
group: "group:admin",
stripEmailDomain: true,
},
want: []string{"joe.bar", "john.doe"},
wantErr: false,
},
{
name: "Expand emails in group",
args: args{
aclPolicy: ACLPolicy{
Groups: Groups{
"group:admin": []string{
"joe.bar@gmail.com",
"john.doe@yahoo.fr",
},
},
},
group: "group:admin",
stripEmailDomain: false,
},
want: []string{"joe.bar.gmail.com", "john.doe.yahoo.fr"},
wantErr: false,
},
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
got, err := expandGroup(test.args.aclPolicy, test.args.group) got, err := expandGroup(
test.args.aclPolicy,
test.args.group,
test.args.stripEmailDomain,
)
if (err != nil) != test.wantErr { if (err != nil) != test.wantErr {
t.Errorf("expandGroup() error = %v, wantErr %v", err, test.wantErr) t.Errorf("expandGroup() error = %v, wantErr %v", err, test.wantErr)
@ -471,6 +535,7 @@ func Test_expandTagOwners(t *testing.T) {
type args struct { type args struct {
aclPolicy ACLPolicy aclPolicy ACLPolicy
tag string tag string
stripEmailDomain bool
} }
tests := []struct { tests := []struct {
name string name string
@ -485,6 +550,7 @@ func Test_expandTagOwners(t *testing.T) {
TagOwners: TagOwners{"tag:test": []string{"user1"}}, TagOwners: TagOwners{"tag:test": []string{"user1"}},
}, },
tag: "tag:test", tag: "tag:test",
stripEmailDomain: true,
}, },
want: []string{"user1"}, want: []string{"user1"},
wantErr: false, wantErr: false,
@ -497,6 +563,7 @@ func Test_expandTagOwners(t *testing.T) {
TagOwners: TagOwners{"tag:test": []string{"group:foo"}}, TagOwners: TagOwners{"tag:test": []string{"group:foo"}},
}, },
tag: "tag:test", tag: "tag:test",
stripEmailDomain: true,
}, },
want: []string{"user1", "user2"}, want: []string{"user1", "user2"},
wantErr: false, wantErr: false,
@ -509,6 +576,7 @@ func Test_expandTagOwners(t *testing.T) {
TagOwners: TagOwners{"tag:test": []string{"group:foo", "user3"}}, TagOwners: TagOwners{"tag:test": []string{"group:foo", "user3"}},
}, },
tag: "tag:test", tag: "tag:test",
stripEmailDomain: true,
}, },
want: []string{"user1", "user2", "user3"}, want: []string{"user1", "user2", "user3"},
wantErr: false, wantErr: false,
@ -520,6 +588,7 @@ func Test_expandTagOwners(t *testing.T) {
TagOwners: TagOwners{"tag:foo": []string{"group:foo", "user1"}}, TagOwners: TagOwners{"tag:foo": []string{"group:foo", "user1"}},
}, },
tag: "tag:test", tag: "tag:test",
stripEmailDomain: true,
}, },
want: []string{}, want: []string{},
wantErr: true, wantErr: true,
@ -532,6 +601,7 @@ func Test_expandTagOwners(t *testing.T) {
TagOwners: TagOwners{"tag:test": []string{"group:foo", "user2"}}, TagOwners: TagOwners{"tag:test": []string{"group:foo", "user2"}},
}, },
tag: "tag:test", tag: "tag:test",
stripEmailDomain: true,
}, },
want: []string{}, want: []string{},
wantErr: true, wantErr: true,
@ -539,7 +609,11 @@ func Test_expandTagOwners(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
got, err := expandTagOwners(test.args.aclPolicy, test.args.tag) got, err := expandTagOwners(
test.args.aclPolicy,
test.args.tag,
test.args.stripEmailDomain,
)
if (err != nil) != test.wantErr { if (err != nil) != test.wantErr {
t.Errorf("expandTagOwners() error = %v, wantErr %v", err, test.wantErr) t.Errorf("expandTagOwners() error = %v, wantErr %v", err, test.wantErr)
@ -704,6 +778,7 @@ func Test_expandAlias(t *testing.T) {
machines []Machine machines []Machine
aclPolicy ACLPolicy aclPolicy ACLPolicy
alias string alias string
stripEmailDomain bool
} }
tests := []struct { tests := []struct {
name string name string
@ -724,6 +799,7 @@ func Test_expandAlias(t *testing.T) {
}, },
}, },
aclPolicy: ACLPolicy{}, aclPolicy: ACLPolicy{},
stripEmailDomain: true,
}, },
want: []string{"*"}, want: []string{"*"},
wantErr: false, wantErr: false,
@ -761,6 +837,7 @@ func Test_expandAlias(t *testing.T) {
aclPolicy: ACLPolicy{ aclPolicy: ACLPolicy{
Groups: Groups{"group:accountant": []string{"joe", "marc"}}, Groups: Groups{"group:accountant": []string{"joe", "marc"}},
}, },
stripEmailDomain: true,
}, },
want: []string{"100.64.0.1", "100.64.0.2", "100.64.0.3"}, want: []string{"100.64.0.1", "100.64.0.2", "100.64.0.3"},
wantErr: false, wantErr: false,
@ -798,6 +875,7 @@ func Test_expandAlias(t *testing.T) {
aclPolicy: ACLPolicy{ aclPolicy: ACLPolicy{
Groups: Groups{"group:accountant": []string{"joe", "marc"}}, Groups: Groups{"group:accountant": []string{"joe", "marc"}},
}, },
stripEmailDomain: true,
}, },
want: []string{}, want: []string{},
wantErr: true, wantErr: true,
@ -808,6 +886,7 @@ func Test_expandAlias(t *testing.T) {
alias: "10.0.0.3", alias: "10.0.0.3",
machines: []Machine{}, machines: []Machine{},
aclPolicy: ACLPolicy{}, aclPolicy: ACLPolicy{},
stripEmailDomain: true,
}, },
want: []string{"10.0.0.3"}, want: []string{"10.0.0.3"},
wantErr: false, wantErr: false,
@ -822,6 +901,7 @@ func Test_expandAlias(t *testing.T) {
"homeNetwork": netaddr.MustParseIPPrefix("192.168.1.0/24"), "homeNetwork": netaddr.MustParseIPPrefix("192.168.1.0/24"),
}, },
}, },
stripEmailDomain: true,
}, },
want: []string{"192.168.1.0/24"}, want: []string{"192.168.1.0/24"},
wantErr: false, wantErr: false,
@ -832,6 +912,7 @@ func Test_expandAlias(t *testing.T) {
alias: "10.0.0.1", alias: "10.0.0.1",
machines: []Machine{}, machines: []Machine{},
aclPolicy: ACLPolicy{}, aclPolicy: ACLPolicy{},
stripEmailDomain: true,
}, },
want: []string{"10.0.0.1"}, want: []string{"10.0.0.1"},
wantErr: false, wantErr: false,
@ -842,6 +923,7 @@ func Test_expandAlias(t *testing.T) {
alias: "10.0.0.0/16", alias: "10.0.0.0/16",
machines: []Machine{}, machines: []Machine{},
aclPolicy: ACLPolicy{}, aclPolicy: ACLPolicy{},
stripEmailDomain: true,
}, },
want: []string{"10.0.0.0/16"}, want: []string{"10.0.0.0/16"},
wantErr: false, wantErr: false,
@ -856,18 +938,22 @@ func Test_expandAlias(t *testing.T) {
netaddr.MustParseIP("100.64.0.1"), netaddr.MustParseIP("100.64.0.1"),
}, },
Namespace: Namespace{Name: "joe"}, Namespace: Namespace{Name: "joe"},
HostInfo: []byte( HostInfo: HostInfo{
"{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:hr-webserver\"]}", OS: "centos",
), Hostname: "foo",
RequestTags: []string{"tag:hr-webserver"},
},
}, },
{ {
IPAddresses: MachineAddresses{ IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"), netaddr.MustParseIP("100.64.0.2"),
}, },
Namespace: Namespace{Name: "joe"}, Namespace: Namespace{Name: "joe"},
HostInfo: []byte( HostInfo: HostInfo{
"{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:hr-webserver\"]}", OS: "centos",
), Hostname: "foo",
RequestTags: []string{"tag:hr-webserver"},
},
}, },
{ {
IPAddresses: MachineAddresses{ IPAddresses: MachineAddresses{
@ -885,6 +971,7 @@ func Test_expandAlias(t *testing.T) {
aclPolicy: ACLPolicy{ aclPolicy: ACLPolicy{
TagOwners: TagOwners{"tag:hr-webserver": []string{"joe"}}, TagOwners: TagOwners{"tag:hr-webserver": []string{"joe"}},
}, },
stripEmailDomain: true,
}, },
want: []string{"100.64.0.1", "100.64.0.2"}, want: []string{"100.64.0.1", "100.64.0.2"},
wantErr: false, wantErr: false,
@ -925,6 +1012,7 @@ func Test_expandAlias(t *testing.T) {
"tag:accountant-webserver": []string{"group:accountant"}, "tag:accountant-webserver": []string{"group:accountant"},
}, },
}, },
stripEmailDomain: true,
}, },
want: []string{}, want: []string{},
wantErr: true, wantErr: true,
@ -939,18 +1027,22 @@ func Test_expandAlias(t *testing.T) {
netaddr.MustParseIP("100.64.0.1"), netaddr.MustParseIP("100.64.0.1"),
}, },
Namespace: Namespace{Name: "joe"}, Namespace: Namespace{Name: "joe"},
HostInfo: []byte( HostInfo: HostInfo{
"{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:accountant-webserver\"]}", OS: "centos",
), Hostname: "foo",
RequestTags: []string{"tag:accountant-webserver"},
},
}, },
{ {
IPAddresses: MachineAddresses{ IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"), netaddr.MustParseIP("100.64.0.2"),
}, },
Namespace: Namespace{Name: "joe"}, Namespace: Namespace{Name: "joe"},
HostInfo: []byte( HostInfo: HostInfo{
"{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:accountant-webserver\"]}", OS: "centos",
), Hostname: "foo",
RequestTags: []string{"tag:accountant-webserver"},
},
}, },
{ {
IPAddresses: MachineAddresses{ IPAddresses: MachineAddresses{
@ -968,6 +1060,7 @@ func Test_expandAlias(t *testing.T) {
aclPolicy: ACLPolicy{ aclPolicy: ACLPolicy{
TagOwners: TagOwners{"tag:accountant-webserver": []string{"joe"}}, TagOwners: TagOwners{"tag:accountant-webserver": []string{"joe"}},
}, },
stripEmailDomain: true,
}, },
want: []string{"100.64.0.4"}, want: []string{"100.64.0.4"},
wantErr: false, wantErr: false,
@ -979,6 +1072,7 @@ func Test_expandAlias(t *testing.T) {
test.args.machines, test.args.machines,
test.args.aclPolicy, test.args.aclPolicy,
test.args.alias, test.args.alias,
test.args.stripEmailDomain,
) )
if (err != nil) != test.wantErr { if (err != nil) != test.wantErr {
t.Errorf("expandAlias() error = %v, wantErr %v", err, test.wantErr) t.Errorf("expandAlias() error = %v, wantErr %v", err, test.wantErr)
@ -1016,18 +1110,22 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
netaddr.MustParseIP("100.64.0.1"), netaddr.MustParseIP("100.64.0.1"),
}, },
Namespace: Namespace{Name: "joe"}, Namespace: Namespace{Name: "joe"},
HostInfo: []byte( HostInfo: HostInfo{
"{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:accountant-webserver\"]}", OS: "centos",
), Hostname: "foo",
RequestTags: []string{"tag:accountant-webserver"},
},
}, },
{ {
IPAddresses: MachineAddresses{ IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"), netaddr.MustParseIP("100.64.0.2"),
}, },
Namespace: Namespace{Name: "joe"}, Namespace: Namespace{Name: "joe"},
HostInfo: []byte( HostInfo: HostInfo{
"{\"OS\":\"centos\",\"Hostname\":\"foo\",\"RequestTags\":[\"tag:accountant-webserver\"]}", OS: "centos",
), Hostname: "foo",
RequestTags: []string{"tag:accountant-webserver"},
},
}, },
{ {
IPAddresses: MachineAddresses{ IPAddresses: MachineAddresses{
@ -1044,7 +1142,6 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
Namespace: Namespace{Name: "joe"}, Namespace: Namespace{Name: "joe"},
}, },
}, },
wantErr: false,
}, },
{ {
name: "all nodes have invalid tags, don't exclude them", name: "all nodes have invalid tags, don't exclude them",
@ -1058,18 +1155,22 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
netaddr.MustParseIP("100.64.0.1"), netaddr.MustParseIP("100.64.0.1"),
}, },
Namespace: Namespace{Name: "joe"}, Namespace: Namespace{Name: "joe"},
HostInfo: []byte( HostInfo: HostInfo{
"{\"OS\":\"centos\",\"Hostname\":\"hr-web1\",\"RequestTags\":[\"tag:hr-webserver\"]}", OS: "centos",
), Hostname: "hr-web1",
RequestTags: []string{"tag:hr-webserver"},
},
}, },
{ {
IPAddresses: MachineAddresses{ IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"), netaddr.MustParseIP("100.64.0.2"),
}, },
Namespace: Namespace{Name: "joe"}, Namespace: Namespace{Name: "joe"},
HostInfo: []byte( HostInfo: HostInfo{
"{\"OS\":\"centos\",\"Hostname\":\"hr-web2\",\"RequestTags\":[\"tag:hr-webserver\"]}", OS: "centos",
), Hostname: "hr-web2",
RequestTags: []string{"tag:hr-webserver"},
},
}, },
{ {
IPAddresses: MachineAddresses{ IPAddresses: MachineAddresses{
@ -1086,18 +1187,22 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
netaddr.MustParseIP("100.64.0.1"), netaddr.MustParseIP("100.64.0.1"),
}, },
Namespace: Namespace{Name: "joe"}, Namespace: Namespace{Name: "joe"},
HostInfo: []byte( HostInfo: HostInfo{
"{\"OS\":\"centos\",\"Hostname\":\"hr-web1\",\"RequestTags\":[\"tag:hr-webserver\"]}", OS: "centos",
), Hostname: "hr-web1",
RequestTags: []string{"tag:hr-webserver"},
},
}, },
{ {
IPAddresses: MachineAddresses{ IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"), netaddr.MustParseIP("100.64.0.2"),
}, },
Namespace: Namespace{Name: "joe"}, Namespace: Namespace{Name: "joe"},
HostInfo: []byte( HostInfo: HostInfo{
"{\"OS\":\"centos\",\"Hostname\":\"hr-web2\",\"RequestTags\":[\"tag:hr-webserver\"]}", OS: "centos",
), Hostname: "hr-web2",
RequestTags: []string{"tag:hr-webserver"},
},
}, },
{ {
IPAddresses: MachineAddresses{ IPAddresses: MachineAddresses{
@ -1106,25 +1211,15 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
Namespace: Namespace{Name: "joe"}, Namespace: Namespace{Name: "joe"},
}, },
}, },
wantErr: false,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
got, err := excludeCorrectlyTaggedNodes( got := excludeCorrectlyTaggedNodes(
test.args.aclPolicy, test.args.aclPolicy,
test.args.nodes, test.args.nodes,
test.args.namespace, test.args.namespace,
) )
if (err != nil) != test.wantErr {
t.Errorf(
"excludeCorrectlyTaggedNodes() error = %v, wantErr %v",
err,
test.wantErr,
)
return
}
if !reflect.DeepEqual(got, test.want) { if !reflect.DeepEqual(got, test.want) {
t.Errorf("excludeCorrectlyTaggedNodes() = %v, want %v", got, test.want) t.Errorf("excludeCorrectlyTaggedNodes() = %v, want %v", got, test.want)
} }

View File

@ -5,23 +5,24 @@ import (
"strings" "strings"
"github.com/tailscale/hujson" "github.com/tailscale/hujson"
"gopkg.in/yaml.v3"
"inet.af/netaddr" "inet.af/netaddr"
) )
// ACLPolicy represents a Tailscale ACL Policy. // ACLPolicy represents a Tailscale ACL Policy.
type ACLPolicy struct { type ACLPolicy struct {
Groups Groups `json:"Groups"` Groups Groups `json:"Groups" yaml:"Groups"`
Hosts Hosts `json:"Hosts"` Hosts Hosts `json:"Hosts" yaml:"Hosts"`
TagOwners TagOwners `json:"TagOwners"` TagOwners TagOwners `json:"TagOwners" yaml:"TagOwners"`
ACLs []ACL `json:"ACLs"` ACLs []ACL `json:"ACLs" yaml:"ACLs"`
Tests []ACLTest `json:"Tests"` Tests []ACLTest `json:"Tests" yaml:"Tests"`
} }
// ACL is a basic rule for the ACL Policy. // ACL is a basic rule for the ACL Policy.
type ACL struct { type ACL struct {
Action string `json:"Action"` Action string `json:"Action" yaml:"Action"`
Users []string `json:"Users"` Users []string `json:"Users" yaml:"Users"`
Ports []string `json:"Ports"` Ports []string `json:"Ports" yaml:"Ports"`
} }
// Groups references a series of alias in the ACL rules. // Groups references a series of alias in the ACL rules.
@ -35,9 +36,9 @@ type TagOwners map[string][]string
// ACLTest is not implemented, but should be use to check if a certain rule is allowed. // ACLTest is not implemented, but should be use to check if a certain rule is allowed.
type ACLTest struct { type ACLTest struct {
User string `json:"User"` User string `json:"User" yaml:"User"`
Allow []string `json:"Allow"` Allow []string `json:"Allow" yaml:"Allow"`
Deny []string `json:"Deny,omitempty"` Deny []string `json:"Deny,omitempty" yaml:"Deny,omitempty"`
} }
// UnmarshalJSON allows to parse the Hosts directly into netaddr objects. // UnmarshalJSON allows to parse the Hosts directly into netaddr objects.
@ -69,6 +70,27 @@ func (hosts *Hosts) UnmarshalJSON(data []byte) error {
return nil return nil
} }
// UnmarshalYAML allows to parse the Hosts directly into netaddr objects.
func (hosts *Hosts) UnmarshalYAML(data []byte) error {
newHosts := Hosts{}
hostIPPrefixMap := make(map[string]string)
err := yaml.Unmarshal(data, &hostIPPrefixMap)
if err != nil {
return err
}
for host, prefixStr := range hostIPPrefixMap {
prefix, err := netaddr.ParseIPPrefix(prefixStr)
if err != nil {
return err
}
newHosts[host] = prefix
}
*hosts = newHosts
return nil
}
// IsZero is perhaps a bit naive here. // IsZero is perhaps a bit naive here.
func (policy ACLPolicy) IsZero() bool { func (policy ACLPolicy) IsZero() bool {
if len(policy.Groups) == 0 && len(policy.Hosts) == 0 && len(policy.ACLs) == 0 { if len(policy.Groups) == 0 && len(policy.Hosts) == 0 && len(policy.ACLs) == 0 {

171
api.go
View File

@ -22,7 +22,7 @@ import (
const ( const (
reservedResponseHeaderSize = 4 reservedResponseHeaderSize = 4
RegisterMethodAuthKey = "authKey" RegisterMethodAuthKey = "authkey"
RegisterMethodOIDC = "oidc" RegisterMethodOIDC = "oidc"
RegisterMethodCLI = "cli" RegisterMethodCLI = "cli"
ErrRegisterMethodCLIDoesNotSupportExpire = Error( ErrRegisterMethodCLIDoesNotSupportExpire = Error(
@ -125,25 +125,50 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) {
machine, err := h.GetMachineByMachineKey(machineKey) machine, err := h.GetMachineByMachineKey(machineKey)
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
log.Info().Str("machine", req.Hostinfo.Hostname).Msg("New machine") log.Info().Str("machine", req.Hostinfo.Hostname).Msg("New machine")
newMachine := Machine{
Expiry: &time.Time{}, machineKeyStr := MachinePublicKeyStripPrefix(machineKey)
MachineKey: MachinePublicKeyStripPrefix(machineKey),
Name: req.Hostinfo.Hostname, // If the machine has AuthKey set, handle registration via PreAuthKeys
} if req.Auth.AuthKey != "" {
if err := h.db.Create(&newMachine).Error; err != nil { h.handleAuthKey(ctx, machineKey, req)
log.Error().
Caller().
Err(err).
Msg("Could not create row")
machineRegistrations.WithLabelValues("unknown", "web", "error", machine.Namespace.Name).
Inc()
return return
} }
machine = &newMachine
// The machine did not have a key to authenticate, which means
// that we rely on a method that calls back some how (OpenID or CLI)
// We create the machine and then keep it around until a callback
// happens
newMachine := Machine{
MachineKey: machineKeyStr,
Name: req.Hostinfo.Hostname,
NodeKey: NodePublicKeyStripPrefix(req.NodeKey),
LastSeen: &now,
Expiry: &time.Time{},
} }
if machine.Registered { if !req.Expiry.IsZero() {
log.Trace().
Caller().
Str("machine", req.Hostinfo.Hostname).
Time("expiry", req.Expiry).
Msg("Non-zero expiry time requested")
newMachine.Expiry = &req.Expiry
}
h.registrationCache.Set(
machineKeyStr,
newMachine,
registerCacheExpiration,
)
h.handleMachineRegistrationNew(ctx, machineKey, req)
return
}
// The machine is already registered, so we need to pass through reauth or key update.
if machine != nil {
// If the NodeKey stored in headscale is the same as the key presented in a registration // If the NodeKey stored in headscale is the same as the key presented in a registration
// request, then we have a node that is either: // request, then we have a node that is either:
// - Trying to log out (sending a expiry in the past) // - Trying to log out (sending a expiry in the past)
@ -180,15 +205,6 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) {
return return
} }
// If the machine has AuthKey set, handle registration via PreAuthKeys
if req.Auth.AuthKey != "" {
h.handleAuthKey(ctx, machineKey, req, *machine)
return
}
h.handleMachineRegistrationNew(ctx, machineKey, req, *machine)
} }
func (h *Headscale) getMapResponse( func (h *Headscale) getMapResponse(
@ -402,7 +418,7 @@ func (h *Headscale) handleMachineExpired(
Msg("Machine registration has expired. Sending a authurl to register") Msg("Machine registration has expired. Sending a authurl to register")
if registerRequest.Auth.AuthKey != "" { if registerRequest.Auth.AuthKey != "" {
h.handleAuthKey(ctx, machineKey, registerRequest, machine) h.handleAuthKey(ctx, machineKey, registerRequest)
return return
} }
@ -465,13 +481,12 @@ func (h *Headscale) handleMachineRegistrationNew(
ctx *gin.Context, ctx *gin.Context,
machineKey key.MachinePublic, machineKey key.MachinePublic,
registerRequest tailcfg.RegisterRequest, registerRequest tailcfg.RegisterRequest,
machine Machine,
) { ) {
resp := tailcfg.RegisterResponse{} resp := tailcfg.RegisterResponse{}
// The machine registration is new, redirect the client to the registration URL // The machine registration is new, redirect the client to the registration URL
log.Debug(). log.Debug().
Str("machine", machine.Name). Str("machine", registerRequest.Hostinfo.Hostname).
Msg("The node is sending us a new NodeKey, sending auth url") Msg("The node is sending us a new NodeKey, sending auth url")
if h.cfg.OIDC.Issuer != "" { if h.cfg.OIDC.Issuer != "" {
resp.AuthURL = fmt.Sprintf( resp.AuthURL = fmt.Sprintf(
@ -484,24 +499,6 @@ func (h *Headscale) handleMachineRegistrationNew(
strings.TrimSuffix(h.cfg.ServerURL, "/"), MachinePublicKeyStripPrefix(machineKey)) strings.TrimSuffix(h.cfg.ServerURL, "/"), MachinePublicKeyStripPrefix(machineKey))
} }
if !registerRequest.Expiry.IsZero() {
log.Trace().
Caller().
Str("machine", machine.Name).
Time("expiry", registerRequest.Expiry).
Msg("Non-zero expiry time requested, adding to cache")
h.requestedExpiryCache.Set(
machineKey.String(),
registerRequest.Expiry,
requestedExpiryCacheExpiration,
)
}
machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey)
// save the NodeKey
h.db.Save(&machine)
respBody, err := encode(resp, &machineKey, h.privateKey) respBody, err := encode(resp, &machineKey, h.privateKey)
if err != nil { if err != nil {
log.Error(). log.Error().
@ -520,19 +517,21 @@ func (h *Headscale) handleAuthKey(
ctx *gin.Context, ctx *gin.Context,
machineKey key.MachinePublic, machineKey key.MachinePublic,
registerRequest tailcfg.RegisterRequest, registerRequest tailcfg.RegisterRequest,
machine Machine,
) { ) {
machineKeyStr := MachinePublicKeyStripPrefix(machineKey)
log.Debug(). log.Debug().
Str("func", "handleAuthKey"). Str("func", "handleAuthKey").
Str("machine", registerRequest.Hostinfo.Hostname). Str("machine", registerRequest.Hostinfo.Hostname).
Msgf("Processing auth key for %s", registerRequest.Hostinfo.Hostname) Msgf("Processing auth key for %s", registerRequest.Hostinfo.Hostname)
resp := tailcfg.RegisterResponse{} resp := tailcfg.RegisterResponse{}
pak, err := h.checkKeyValidity(registerRequest.Auth.AuthKey) pak, err := h.checkKeyValidity(registerRequest.Auth.AuthKey)
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Str("func", "handleAuthKey"). Str("func", "handleAuthKey").
Str("machine", machine.Name). Str("machine", registerRequest.Hostinfo.Hostname).
Err(err). Err(err).
Msg("Failed authentication via AuthKey") Msg("Failed authentication via AuthKey")
resp.MachineAuthorized = false resp.MachineAuthorized = false
@ -541,76 +540,66 @@ func (h *Headscale) handleAuthKey(
log.Error(). log.Error().
Caller(). Caller().
Str("func", "handleAuthKey"). Str("func", "handleAuthKey").
Str("machine", machine.Name). Str("machine", registerRequest.Hostinfo.Hostname).
Err(err). Err(err).
Msg("Cannot encode message") Msg("Cannot encode message")
ctx.String(http.StatusInternalServerError, "") ctx.String(http.StatusInternalServerError, "")
machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
Inc() Inc()
return return
} }
ctx.Data(http.StatusUnauthorized, "application/json; charset=utf-8", respBody) ctx.Data(http.StatusUnauthorized, "application/json; charset=utf-8", respBody)
log.Error(). log.Error().
Caller(). Caller().
Str("func", "handleAuthKey"). Str("func", "handleAuthKey").
Str("machine", machine.Name). Str("machine", registerRequest.Hostinfo.Hostname).
Msg("Failed authentication via AuthKey") Msg("Failed authentication via AuthKey")
machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
Inc() Inc()
return return
} }
if machine.isRegistered() {
log.Trace().
Caller().
Str("machine", machine.Name).
Msg("machine already registered, reauthenticating")
h.RefreshMachine(&machine, registerRequest.Expiry)
} else {
log.Debug(). log.Debug().
Str("func", "handleAuthKey"). Str("func", "handleAuthKey").
Str("machine", machine.Name). Str("machine", registerRequest.Hostinfo.Hostname).
Msg("Authentication key was valid, proceeding to acquire IP addresses") Msg("Authentication key was valid, proceeding to acquire IP addresses")
h.ipAllocationMutex.Lock() nodeKey := NodePublicKeyStripPrefix(registerRequest.NodeKey)
now := time.Now().UTC()
ips, err := h.getAvailableIPs() machineToRegister := Machine{
Name: registerRequest.Hostinfo.Hostname,
NamespaceID: pak.Namespace.ID,
MachineKey: machineKeyStr,
RegisterMethod: RegisterMethodAuthKey,
Expiry: &registerRequest.Expiry,
NodeKey: nodeKey,
LastSeen: &now,
AuthKeyID: uint(pak.ID),
}
machine, err := h.RegisterMachine(
machineToRegister,
)
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Str("func", "handleAuthKey"). Err(err).
Str("machine", machine.Name). Msg("could not register machine")
Msg("Failed to find an available IP address") machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name).
Inc() Inc()
ctx.String(
http.StatusInternalServerError,
"could not register machine",
)
return return
} }
log.Info().
Str("func", "handleAuthKey").
Str("machine", machine.Name).
Str("ips", strings.Join(ips.ToStringSlice(), ",")).
Msgf("Assigning %s to %s", strings.Join(ips.ToStringSlice(), ","), machine.Name)
machine.Expiry = &registerRequest.Expiry h.UsePreAuthKey(pak)
machine.AuthKeyID = uint(pak.ID)
machine.IPAddresses = ips
machine.NamespaceID = pak.NamespaceID
machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey)
// we update it just in case
machine.Registered = true
machine.RegisterMethod = RegisterMethodAuthKey
h.db.Save(&machine)
h.ipAllocationMutex.Unlock()
}
pak.Used = true
h.db.Save(&pak)
resp.MachineAuthorized = true resp.MachineAuthorized = true
resp.User = *pak.Namespace.toUser() resp.User = *pak.Namespace.toUser()
@ -619,21 +608,21 @@ func (h *Headscale) handleAuthKey(
log.Error(). log.Error().
Caller(). Caller().
Str("func", "handleAuthKey"). Str("func", "handleAuthKey").
Str("machine", machine.Name). Str("machine", registerRequest.Hostinfo.Hostname).
Err(err). Err(err).
Msg("Cannot encode message") Msg("Cannot encode message")
machineRegistrations.WithLabelValues("new", "authkey", "error", machine.Namespace.Name). machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
Inc() Inc()
ctx.String(http.StatusInternalServerError, "Extremely sad!") ctx.String(http.StatusInternalServerError, "Extremely sad!")
return return
} }
machineRegistrations.WithLabelValues("new", "authkey", "success", machine.Namespace.Name). machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "success", pak.Namespace.Name).
Inc() Inc()
ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody) ctx.Data(http.StatusOK, "application/json; charset=utf-8", respBody)
log.Info(). log.Info().
Str("func", "handleAuthKey"). Str("func", "handleAuthKey").
Str("machine", machine.Name). Str("machine", registerRequest.Hostinfo.Hostname).
Str("ips", strings.Join(machine.IPAddresses.ToStringSlice(), ", ")). Str("ips", strings.Join(machine.IPAddresses.ToStringSlice(), ", ")).
Msg("Successfully authenticated via AuthKey") Msg("Successfully authenticated via AuthKey")
} }

15
app.go
View File

@ -55,8 +55,8 @@ const (
HTTPReadTimeout = 30 * time.Second HTTPReadTimeout = 30 * time.Second
privateKeyFileMode = 0o600 privateKeyFileMode = 0o600
requestedExpiryCacheExpiration = time.Minute * 5 registerCacheExpiration = time.Minute * 15
requestedExpiryCacheCleanupInterval = time.Minute * 10 registerCacheCleanup = time.Minute * 20
errUnsupportedDatabase = Error("unsupported DB") errUnsupportedDatabase = Error("unsupported DB")
errUnsupportedLetsEncryptChallengeType = Error( errUnsupportedLetsEncryptChallengeType = Error(
@ -151,9 +151,8 @@ type Headscale struct {
oidcProvider *oidc.Provider oidcProvider *oidc.Provider
oauth2Config *oauth2.Config oauth2Config *oauth2.Config
oidcStateCache *cache.Cache
requestedExpiryCache *cache.Cache registrationCache *cache.Cache
ipAllocationMutex sync.Mutex ipAllocationMutex sync.Mutex
} }
@ -203,9 +202,9 @@ func NewHeadscale(cfg Config) (*Headscale, error) {
return nil, errUnsupportedDatabase return nil, errUnsupportedDatabase
} }
requestedExpiryCache := cache.New( registrationCache := cache.New(
requestedExpiryCacheExpiration, registerCacheExpiration,
requestedExpiryCacheCleanupInterval, registerCacheCleanup,
) )
app := Headscale{ app := Headscale{
@ -214,7 +213,7 @@ func NewHeadscale(cfg Config) (*Headscale, error) {
dbString: dbString, dbString: dbString,
privateKey: privKey, privateKey: privKey,
aclRules: tailcfg.FilterAllowAll, // default allowall aclRules: tailcfg.FilterAllowAll, // default allowall
requestedExpiryCache: requestedExpiryCache, registrationCache: registrationCache,
} }
err = app.initDB() err = app.initDB()

View File

@ -5,7 +5,6 @@ import (
"os" "os"
"testing" "testing"
"github.com/patrickmn/go-cache"
"gopkg.in/check.v1" "gopkg.in/check.v1"
"inet.af/netaddr" "inet.af/netaddr"
) )
@ -50,10 +49,6 @@ func (s *Suite) ResetDB(c *check.C) {
cfg: cfg, cfg: cfg,
dbType: "sqlite3", dbType: "sqlite3",
dbString: tmpDir + "/headscale_test.db", dbString: tmpDir + "/headscale_test.db",
requestedExpiryCache: cache.New(
requestedExpiryCacheExpiration,
requestedExpiryCacheCleanupInterval,
),
} }
err = app.initDB() err = app.initDB()
if err != nil { if err != nil {

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"html/template" "html/template"
"net/http" "net/http"
textTemplate "text/template"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
@ -202,8 +203,8 @@ type AppleMobilePlatformConfig struct {
URL string URL string
} }
var commonTemplate = template.Must( var commonTemplate = textTemplate.Must(
template.New("mobileconfig").Parse(`<?xml version="1.0" encoding="UTF-8"?> textTemplate.New("mobileconfig").Parse(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
@ -229,7 +230,7 @@ var commonTemplate = template.Must(
</plist>`), </plist>`),
) )
var iosTemplate = template.Must(template.New("iosTemplate").Parse(` var iosTemplate = textTemplate.Must(textTemplate.New("iosTemplate").Parse(`
<dict> <dict>
<key>PayloadType</key> <key>PayloadType</key>
<string>io.tailscale.ipn.ios</string> <string>io.tailscale.ipn.ios</string>

View File

@ -1,41 +0,0 @@
package headscale
import (
"time"
"gopkg.in/check.v1"
"inet.af/netaddr"
)
func (s *Suite) TestRegisterMachine(c *check.C) {
namespace, err := app.CreateNamespace("test")
c.Assert(err, check.IsNil)
now := time.Now().UTC()
machine := Machine{
ID: 0,
MachineKey: "8ce002a935f8c394e55e78fbbb410576575ff8ec5cfa2e627e4b807f1be15b0e",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
NamespaceID: namespace.ID,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("10.0.0.1")},
Expiry: &now,
}
err = app.db.Save(&machine).Error
c.Assert(err, check.IsNil)
_, err = app.GetMachine(namespace.Name, machine.Name)
c.Assert(err, check.IsNil)
machineAfterRegistering, err := app.RegisterMachine(
machine.MachineKey,
namespace.Name,
)
c.Assert(err, check.IsNil)
c.Assert(machineAfterRegistering.Registered, check.Equals, true)
_, err = machineAfterRegistering.GetHostInfo()
c.Assert(err, check.IsNil)
}

View File

@ -38,11 +38,13 @@ func init() {
var apiKeysCmd = &cobra.Command{ var apiKeysCmd = &cobra.Command{
Use: "apikeys", Use: "apikeys",
Short: "Handle the Api keys in Headscale", Short: "Handle the Api keys in Headscale",
Aliases: []string{"apikey", "api"},
} }
var listAPIKeys = &cobra.Command{ var listAPIKeys = &cobra.Command{
Use: "list", Use: "list",
Short: "List the Api keys for headscale", Short: "List the Api keys for headscale",
Aliases: []string{"ls", "show"},
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output") output, _ := cmd.Flags().GetString("output")
@ -107,6 +109,7 @@ var createAPIKeyCmd = &cobra.Command{
Creates a new Api key, the Api key is only visible on creation Creates a new Api key, the Api key is only visible on creation
and cannot be retrieved again. and cannot be retrieved again.
If you loose a key, create a new one and revoke (expire) the old one.`, If you loose a key, create a new one and revoke (expire) the old one.`,
Aliases: []string{"c", "new"},
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output") output, _ := cmd.Flags().GetString("output")
@ -144,7 +147,7 @@ If you loose a key, create a new one and revoke (expire) the old one.`,
var expireAPIKeyCmd = &cobra.Command{ var expireAPIKeyCmd = &cobra.Command{
Use: "expire", Use: "expire",
Short: "Expire an ApiKey", Short: "Expire an ApiKey",
Aliases: []string{"revoke"}, Aliases: []string{"revoke", "exp", "e"},
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output") output, _ := cmd.Flags().GetString("output")

View File

@ -15,6 +15,7 @@ func init() {
var generateCmd = &cobra.Command{ var generateCmd = &cobra.Command{
Use: "generate", Use: "generate",
Short: "Generate commands", Short: "Generate commands",
Aliases: []string{"gen"},
} }
var generatePrivateKeyCmd = &cobra.Command{ var generatePrivateKeyCmd = &cobra.Command{

View File

@ -27,11 +27,13 @@ const (
var namespaceCmd = &cobra.Command{ var namespaceCmd = &cobra.Command{
Use: "namespaces", Use: "namespaces",
Short: "Manage the namespaces of Headscale", Short: "Manage the namespaces of Headscale",
Aliases: []string{"namespace", "ns", "user", "users"},
} }
var createNamespaceCmd = &cobra.Command{ var createNamespaceCmd = &cobra.Command{
Use: "create NAME", Use: "create NAME",
Short: "Creates a new namespace", Short: "Creates a new namespace",
Aliases: []string{"c", "new"},
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return errMissingParameter return errMissingParameter
@ -74,6 +76,7 @@ var createNamespaceCmd = &cobra.Command{
var destroyNamespaceCmd = &cobra.Command{ var destroyNamespaceCmd = &cobra.Command{
Use: "destroy NAME", Use: "destroy NAME",
Short: "Destroys a namespace", Short: "Destroys a namespace",
Aliases: []string{"delete"},
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return errMissingParameter return errMissingParameter
@ -146,6 +149,7 @@ var destroyNamespaceCmd = &cobra.Command{
var listNamespacesCmd = &cobra.Command{ var listNamespacesCmd = &cobra.Command{
Use: "list", Use: "list",
Short: "List all the namespaces", Short: "List all the namespaces",
Aliases: []string{"ls", "show"},
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output") output, _ := cmd.Flags().GetString("output")
@ -199,6 +203,7 @@ var listNamespacesCmd = &cobra.Command{
var renameNamespaceCmd = &cobra.Command{ var renameNamespaceCmd = &cobra.Command{
Use: "rename OLD_NAME NEW_NAME", Use: "rename OLD_NAME NEW_NAME",
Short: "Renames a namespace", Short: "Renames a namespace",
Aliases: []string{"mv"},
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
expectedArguments := 2 expectedArguments := 2
if len(args) < expectedArguments { if len(args) < expectedArguments {

View File

@ -51,6 +51,7 @@ func init() {
var nodeCmd = &cobra.Command{ var nodeCmd = &cobra.Command{
Use: "nodes", Use: "nodes",
Short: "Manage the nodes of Headscale", Short: "Manage the nodes of Headscale",
Aliases: []string{"node", "machine", "machines"},
} }
var registerNodeCmd = &cobra.Command{ var registerNodeCmd = &cobra.Command{
@ -106,6 +107,7 @@ var registerNodeCmd = &cobra.Command{
var listNodesCmd = &cobra.Command{ var listNodesCmd = &cobra.Command{
Use: "list", Use: "list",
Short: "List nodes", Short: "List nodes",
Aliases: []string{"ls", "show"},
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output") output, _ := cmd.Flags().GetString("output")
namespace, err := cmd.Flags().GetString("namespace") namespace, err := cmd.Flags().GetString("namespace")
@ -164,7 +166,7 @@ var expireNodeCmd = &cobra.Command{
Use: "expire", Use: "expire",
Short: "Expire (log out) a machine in your network", Short: "Expire (log out) a machine in your network",
Long: "Expiring a node will keep the node in the database and force it to reauthenticate.", Long: "Expiring a node will keep the node in the database and force it to reauthenticate.",
Aliases: []string{"logout"}, Aliases: []string{"logout", "exp", "e"},
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output") output, _ := cmd.Flags().GetString("output")
@ -208,6 +210,7 @@ var expireNodeCmd = &cobra.Command{
var deleteNodeCmd = &cobra.Command{ var deleteNodeCmd = &cobra.Command{
Use: "delete", Use: "delete",
Short: "Delete a node", Short: "Delete a node",
Aliases: []string{"del"},
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output") output, _ := cmd.Flags().GetString("output")

View File

@ -37,11 +37,13 @@ func init() {
var preauthkeysCmd = &cobra.Command{ var preauthkeysCmd = &cobra.Command{
Use: "preauthkeys", Use: "preauthkeys",
Short: "Handle the preauthkeys in Headscale", Short: "Handle the preauthkeys in Headscale",
Aliases: []string{"preauthkey", "authkey", "pre"},
} }
var listPreAuthKeys = &cobra.Command{ var listPreAuthKeys = &cobra.Command{
Use: "list", Use: "list",
Short: "List the preauthkeys for this namespace", Short: "List the preauthkeys for this namespace",
Aliases: []string{"ls", "show"},
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output") output, _ := cmd.Flags().GetString("output")
@ -120,6 +122,7 @@ var listPreAuthKeys = &cobra.Command{
var createPreAuthKeyCmd = &cobra.Command{ var createPreAuthKeyCmd = &cobra.Command{
Use: "create", Use: "create",
Short: "Creates a new preauthkey in the specified namespace", Short: "Creates a new preauthkey in the specified namespace",
Aliases: []string{"c", "new"},
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output") output, _ := cmd.Flags().GetString("output")
@ -174,6 +177,7 @@ var createPreAuthKeyCmd = &cobra.Command{
var expirePreAuthKeyCmd = &cobra.Command{ var expirePreAuthKeyCmd = &cobra.Command{
Use: "expire KEY", Use: "expire KEY",
Short: "Expire a preauthkey", Short: "Expire a preauthkey",
Aliases: []string{"revoke", "exp", "e"},
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return errMissingParameter return errMissingParameter

View File

@ -37,11 +37,13 @@ func init() {
var routesCmd = &cobra.Command{ var routesCmd = &cobra.Command{
Use: "routes", Use: "routes",
Short: "Manage the routes of Headscale", Short: "Manage the routes of Headscale",
Aliases: []string{"r", "route"},
} }
var listRoutesCmd = &cobra.Command{ var listRoutesCmd = &cobra.Command{
Use: "list", Use: "list",
Short: "List routes advertised and enabled by a given node", Short: "List routes advertised and enabled by a given node",
Aliases: []string{"ls", "show"},
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output") output, _ := cmd.Flags().GetString("output")

View File

@ -138,7 +138,8 @@ tls_key_path: ""
log_level: info log_level: info
# Path to a file containg ACL policies. # Path to a file containg ACL policies.
# Recommended path: /etc/headscale/acl.hujson # ACLs can be defined as YAML or HUJSON.
# https://tailscale.com/kb/1018/acls/
acl_policy_path: "" acl_policy_path: ""
## DNS ## DNS

109
db.go
View File

@ -1,13 +1,19 @@
package headscale package headscale
import ( import (
"database/sql/driver"
"encoding/json"
"errors" "errors"
"fmt"
"time" "time"
"github.com/glebarez/sqlite" "github.com/glebarez/sqlite"
"github.com/rs/zerolog/log"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/logger" "gorm.io/gorm/logger"
"inet.af/netaddr"
"tailscale.com/tailcfg"
) )
const ( const (
@ -34,6 +40,38 @@ func (h *Headscale) initDB() error {
_ = db.Migrator().RenameColumn(&Machine{}, "ip_address", "ip_addresses") _ = db.Migrator().RenameColumn(&Machine{}, "ip_address", "ip_addresses")
// If the Machine table has a column for registered,
// find all occourences of "false" and drop them. Then
// remove the column.
if db.Migrator().HasColumn(&Machine{}, "registered") {
log.Info().
Msg(`Database has legacy "registered" column in machine, removing...`)
machines := Machines{}
if err := h.db.Not("registered").Find(&machines).Error; err != nil {
log.Error().Err(err).Msg("Error accessing db")
}
for _, machine := range machines {
log.Info().
Str("machine", machine.Name).
Str("machine_key", machine.MachineKey).
Msg("Deleting unregistered machine")
if err := h.db.Delete(&Machine{}, machine.ID).Error; err != nil {
log.Error().
Err(err).
Str("machine", machine.Name).
Str("machine_key", machine.MachineKey).
Msg("Error deleting unregistered machine")
}
}
err := db.Migrator().DropColumn(&Machine{}, "registered")
if err != nil {
log.Error().Err(err).Msg("Error dropping registered column")
}
}
err = db.AutoMigrate(&Machine{}) err = db.AutoMigrate(&Machine{})
if err != nil { if err != nil {
return err return err
@ -141,3 +179,74 @@ func (h *Headscale) setValue(key string, value string) error {
return nil return nil
} }
// This is a "wrapper" type around tailscales
// Hostinfo to allow us to add database "serialization"
// methods. This allows us to use a typed values throughout
// the code and not have to marshal/unmarshal and error
// check all over the code.
type HostInfo tailcfg.Hostinfo
func (hi *HostInfo) Scan(destination interface{}) error {
switch value := destination.(type) {
case []byte:
return json.Unmarshal(value, hi)
case string:
return json.Unmarshal([]byte(value), hi)
default:
return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination)
}
}
// Value return json value, implement driver.Valuer interface.
func (hi HostInfo) Value() (driver.Value, error) {
bytes, err := json.Marshal(hi)
return string(bytes), err
}
type IPPrefixes []netaddr.IPPrefix
func (i *IPPrefixes) Scan(destination interface{}) error {
switch value := destination.(type) {
case []byte:
return json.Unmarshal(value, i)
case string:
return json.Unmarshal([]byte(value), i)
default:
return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination)
}
}
// Value return json value, implement driver.Valuer interface.
func (i IPPrefixes) Value() (driver.Value, error) {
bytes, err := json.Marshal(i)
return string(bytes), err
}
type StringList []string
func (i *StringList) Scan(destination interface{}) error {
switch value := destination.(type) {
case []byte:
return json.Unmarshal(value, i)
case string:
return json.Unmarshal([]byte(value), i)
default:
return fmt.Errorf("%w: unexpected data type %T", errMachineAddressesInvalid, destination)
}
}
// Value return json value, implement driver.Valuer interface.
func (i StringList) Value() (driver.Value, error) {
bytes, err := json.Marshal(i)
return string(bytes), err
}

View File

@ -164,7 +164,6 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
Name: "test_get_shared_nodes_1", Name: "test_get_shared_nodes_1",
NamespaceID: namespaceShared1.ID, NamespaceID: namespaceShared1.ID,
Namespace: *namespaceShared1, Namespace: *namespaceShared1,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")}, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")},
AuthKeyID: uint(preAuthKeyInShared1.ID), AuthKeyID: uint(preAuthKeyInShared1.ID),
@ -182,7 +181,6 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
Name: "test_get_shared_nodes_2", Name: "test_get_shared_nodes_2",
NamespaceID: namespaceShared2.ID, NamespaceID: namespaceShared2.ID,
Namespace: *namespaceShared2, Namespace: *namespaceShared2,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")}, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")},
AuthKeyID: uint(preAuthKeyInShared2.ID), AuthKeyID: uint(preAuthKeyInShared2.ID),
@ -200,7 +198,6 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
Name: "test_get_shared_nodes_3", Name: "test_get_shared_nodes_3",
NamespaceID: namespaceShared3.ID, NamespaceID: namespaceShared3.ID,
Namespace: *namespaceShared3, Namespace: *namespaceShared3,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")}, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")},
AuthKeyID: uint(preAuthKeyInShared3.ID), AuthKeyID: uint(preAuthKeyInShared3.ID),
@ -218,7 +215,6 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
Name: "test_get_shared_nodes_4", Name: "test_get_shared_nodes_4",
NamespaceID: namespaceShared1.ID, NamespaceID: namespaceShared1.ID,
Namespace: *namespaceShared1, Namespace: *namespaceShared1,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")}, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")},
AuthKeyID: uint(PreAuthKey2InShared1.ID), AuthKeyID: uint(PreAuthKey2InShared1.ID),
@ -311,7 +307,6 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
Name: "test_get_shared_nodes_1", Name: "test_get_shared_nodes_1",
NamespaceID: namespaceShared1.ID, NamespaceID: namespaceShared1.ID,
Namespace: *namespaceShared1, Namespace: *namespaceShared1,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")}, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")},
AuthKeyID: uint(preAuthKeyInShared1.ID), AuthKeyID: uint(preAuthKeyInShared1.ID),
@ -329,7 +324,6 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
Name: "test_get_shared_nodes_2", Name: "test_get_shared_nodes_2",
NamespaceID: namespaceShared2.ID, NamespaceID: namespaceShared2.ID,
Namespace: *namespaceShared2, Namespace: *namespaceShared2,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")}, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")},
AuthKeyID: uint(preAuthKeyInShared2.ID), AuthKeyID: uint(preAuthKeyInShared2.ID),
@ -347,7 +341,6 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
Name: "test_get_shared_nodes_3", Name: "test_get_shared_nodes_3",
NamespaceID: namespaceShared3.ID, NamespaceID: namespaceShared3.ID,
Namespace: *namespaceShared3, Namespace: *namespaceShared3,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")}, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")},
AuthKeyID: uint(preAuthKeyInShared3.ID), AuthKeyID: uint(preAuthKeyInShared3.ID),
@ -365,7 +358,6 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
Name: "test_get_shared_nodes_4", Name: "test_get_shared_nodes_4",
NamespaceID: namespaceShared1.ID, NamespaceID: namespaceShared1.ID,
Namespace: *namespaceShared1, Namespace: *namespaceShared1,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")}, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")},
AuthKeyID: uint(preAuthKey2InShared1.ID), AuthKeyID: uint(preAuthKey2InShared1.ID),

View File

@ -85,13 +85,12 @@ type Machine struct {
IpAddresses []string `protobuf:"bytes,5,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"` IpAddresses []string `protobuf:"bytes,5,rep,name=ip_addresses,json=ipAddresses,proto3" json:"ip_addresses,omitempty"`
Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"` Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"`
Namespace *Namespace `protobuf:"bytes,7,opt,name=namespace,proto3" json:"namespace,omitempty"` Namespace *Namespace `protobuf:"bytes,7,opt,name=namespace,proto3" json:"namespace,omitempty"`
Registered bool `protobuf:"varint,8,opt,name=registered,proto3" json:"registered,omitempty"` LastSeen *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"`
RegisterMethod RegisterMethod `protobuf:"varint,9,opt,name=register_method,json=registerMethod,proto3,enum=headscale.v1.RegisterMethod" json:"register_method,omitempty"` LastSuccessfulUpdate *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=last_successful_update,json=lastSuccessfulUpdate,proto3" json:"last_successful_update,omitempty"`
LastSeen *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` Expiry *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=expiry,proto3" json:"expiry,omitempty"`
LastSuccessfulUpdate *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=last_successful_update,json=lastSuccessfulUpdate,proto3" json:"last_successful_update,omitempty"` PreAuthKey *PreAuthKey `protobuf:"bytes,11,opt,name=pre_auth_key,json=preAuthKey,proto3" json:"pre_auth_key,omitempty"`
Expiry *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=expiry,proto3" json:"expiry,omitempty"` CreatedAt *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
PreAuthKey *PreAuthKey `protobuf:"bytes,13,opt,name=pre_auth_key,json=preAuthKey,proto3" json:"pre_auth_key,omitempty"` RegisterMethod RegisterMethod `protobuf:"varint,13,opt,name=register_method,json=registerMethod,proto3,enum=headscale.v1.RegisterMethod" json:"register_method,omitempty"`
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,14,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
} }
func (x *Machine) Reset() { func (x *Machine) Reset() {
@ -175,20 +174,6 @@ func (x *Machine) GetNamespace() *Namespace {
return nil return nil
} }
func (x *Machine) GetRegistered() bool {
if x != nil {
return x.Registered
}
return false
}
func (x *Machine) GetRegisterMethod() RegisterMethod {
if x != nil {
return x.RegisterMethod
}
return RegisterMethod_REGISTER_METHOD_UNSPECIFIED
}
func (x *Machine) GetLastSeen() *timestamppb.Timestamp { func (x *Machine) GetLastSeen() *timestamppb.Timestamp {
if x != nil { if x != nil {
return x.LastSeen return x.LastSeen
@ -224,6 +209,13 @@ func (x *Machine) GetCreatedAt() *timestamppb.Timestamp {
return nil return nil
} }
func (x *Machine) GetRegisterMethod() RegisterMethod {
if x != nil {
return x.RegisterMethod
}
return RegisterMethod_REGISTER_METHOD_UNSPECIFIED
}
type RegisterMachineRequest struct { type RegisterMachineRequest struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -822,7 +814,7 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{
0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70,
0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x68, 0x65, 0x61, 0x64, 0x73, 0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x68, 0x65, 0x61, 0x64, 0x73,
0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b,
0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfd, 0x04, 0x0a, 0x07, 0x4d, 0x61, 0x63, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdd, 0x04, 0x0a, 0x07, 0x4d, 0x61, 0x63,
0x68, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f,
0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69,
@ -836,33 +828,31 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63,
0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x09, 0x6c,
0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x09, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x50, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x75, 0x63,
0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x09,
0x6f, 0x64, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x64, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79,
0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x50, 0x0a, 0x16, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x61, 0x73, 0x74, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x5f, 0x75, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6d, 0x70, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x75, 0x63, 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e,
0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x41,
0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
0x79, 0x12, 0x3a, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41,
0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x74, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6d, 0x65,
0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61,
0x79, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x39, 0x0a, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74,
0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74,
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x48, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69,
0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63,
0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x48, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69,
0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
@ -962,12 +952,12 @@ var file_headscale_v1_machine_proto_goTypes = []interface{}{
} }
var file_headscale_v1_machine_proto_depIdxs = []int32{ var file_headscale_v1_machine_proto_depIdxs = []int32{
14, // 0: headscale.v1.Machine.namespace:type_name -> headscale.v1.Namespace 14, // 0: headscale.v1.Machine.namespace:type_name -> headscale.v1.Namespace
0, // 1: headscale.v1.Machine.register_method:type_name -> headscale.v1.RegisterMethod 15, // 1: headscale.v1.Machine.last_seen:type_name -> google.protobuf.Timestamp
15, // 2: headscale.v1.Machine.last_seen:type_name -> google.protobuf.Timestamp 15, // 2: headscale.v1.Machine.last_successful_update:type_name -> google.protobuf.Timestamp
15, // 3: headscale.v1.Machine.last_successful_update:type_name -> google.protobuf.Timestamp 15, // 3: headscale.v1.Machine.expiry:type_name -> google.protobuf.Timestamp
15, // 4: headscale.v1.Machine.expiry:type_name -> google.protobuf.Timestamp 16, // 4: headscale.v1.Machine.pre_auth_key:type_name -> headscale.v1.PreAuthKey
16, // 5: headscale.v1.Machine.pre_auth_key:type_name -> headscale.v1.PreAuthKey 15, // 5: headscale.v1.Machine.created_at:type_name -> google.protobuf.Timestamp
15, // 6: headscale.v1.Machine.created_at:type_name -> google.protobuf.Timestamp 0, // 6: headscale.v1.Machine.register_method:type_name -> headscale.v1.RegisterMethod
1, // 7: headscale.v1.RegisterMachineResponse.machine:type_name -> headscale.v1.Machine 1, // 7: headscale.v1.RegisterMachineResponse.machine:type_name -> headscale.v1.Machine
1, // 8: headscale.v1.GetMachineResponse.machine:type_name -> headscale.v1.Machine 1, // 8: headscale.v1.GetMachineResponse.machine:type_name -> headscale.v1.Machine
1, // 9: headscale.v1.ExpireMachineResponse.machine:type_name -> headscale.v1.Machine 1, // 9: headscale.v1.ExpireMachineResponse.machine:type_name -> headscale.v1.Machine

View File

@ -885,12 +885,6 @@
"namespace": { "namespace": {
"$ref": "#/definitions/v1Namespace" "$ref": "#/definitions/v1Namespace"
}, },
"registered": {
"type": "boolean"
},
"registerMethod": {
"$ref": "#/definitions/v1RegisterMethod"
},
"lastSeen": { "lastSeen": {
"type": "string", "type": "string",
"format": "date-time" "format": "date-time"
@ -909,6 +903,9 @@
"createdAt": { "createdAt": {
"type": "string", "type": "string",
"format": "date-time" "format": "date-time"
},
"registerMethod": {
"$ref": "#/definitions/v1RegisterMethod"
} }
} }
}, },

33
go.mod
View File

@ -13,12 +13,12 @@ require (
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.3 github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.3
github.com/infobloxopen/protoc-gen-gorm v1.1.0 github.com/infobloxopen/protoc-gen-gorm v1.1.0
github.com/klauspost/compress v1.14.2 github.com/klauspost/compress v1.14.4
github.com/ory/dockertest/v3 v3.8.1 github.com/ory/dockertest/v3 v3.8.1
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/philip-bui/grpc-zerolog v1.0.1 github.com/philip-bui/grpc-zerolog v1.0.1
github.com/prometheus/client_golang v1.12.1 github.com/prometheus/client_golang v1.12.1
github.com/pterm/pterm v0.12.36 github.com/pterm/pterm v0.12.37
github.com/rs/zerolog v1.26.1 github.com/rs/zerolog v1.26.1
github.com/spf13/cobra v1.3.0 github.com/spf13/cobra v1.3.0
github.com/spf13/viper v1.10.1 github.com/spf13/viper v1.10.1
@ -27,24 +27,24 @@ require (
github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e
github.com/zsais/go-gin-prometheus v0.1.0 github.com/zsais/go-gin-prometheus v0.1.0
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 golang.org/x/crypto v0.0.0-20220214200702-86341886e292
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
google.golang.org/genproto v0.0.0-20220210181026-6fee9acbd336 google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7
google.golang.org/grpc v1.44.0 google.golang.org/grpc v1.44.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0
google.golang.org/protobuf v1.27.1 google.golang.org/protobuf v1.27.1
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
gorm.io/datatypes v1.0.5 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
gorm.io/driver/postgres v1.3.1 gorm.io/driver/postgres v1.3.1
gorm.io/gorm v1.23.1 gorm.io/gorm v1.23.1
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6
tailscale.com v1.20.4 tailscale.com v1.22.0
) )
require ( require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/atomicgo/cursor v0.0.1 // indirect github.com/atomicgo/cursor v0.0.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
@ -52,13 +52,14 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/containerd/continuity v0.2.2 // indirect github.com/containerd/continuity v0.2.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denisenkom/go-mssqldb v0.12.0 // indirect
github.com/docker/cli v20.10.12+incompatible // indirect github.com/docker/cli v20.10.12+incompatible // indirect
github.com/docker/docker v20.10.12+incompatible // indirect github.com/docker/docker v20.10.12+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.14.7 // indirect github.com/glebarez/go-sqlite v1.14.8 // indirect
github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.10.0 // indirect github.com/go-playground/validator/v10 v10.10.0 // indirect
@ -92,7 +93,7 @@ require (
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect
github.com/lib/pq v1.10.3 // indirect github.com/lib/pq v1.10.3 // indirect
github.com/magiconair/properties v1.8.5 // indirect github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect
@ -121,7 +122,7 @@ require (
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect
github.com/ugorji/go/codec v1.2.6 // indirect github.com/ugorji/go/codec v1.2.7 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect
@ -129,20 +130,16 @@ require (
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
go4.org/mem v0.0.0-20210711025021-927187094b94 // indirect go4.org/mem v0.0.0-20210711025021-927187094b94 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect modernc.org/libc v1.14.5 // indirect
gorm.io/driver/mysql v1.3.2 // indirect
gorm.io/driver/sqlite v1.3.1 // indirect
gorm.io/driver/sqlserver v1.3.1 // indirect
modernc.org/libc v1.14.3 // indirect
modernc.org/mathutil v1.4.1 // indirect modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.0.5 // indirect modernc.org/memory v1.0.5 // indirect
modernc.org/sqlite v1.14.5 // indirect modernc.org/sqlite v1.14.7 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect sigs.k8s.io/yaml v1.3.0 // indirect
) )

76
go.sum
View File

@ -73,8 +73,9 @@ github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzX
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
@ -212,8 +213,9 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/glebarez/go-sqlite v1.14.7 h1:eXrKp59O5eWBfxv2Xfq5d7uex4+clKrOtWfMzzGSkoM=
github.com/glebarez/go-sqlite v1.14.7/go.mod h1:TKAw5tjyB/ocvVht7Xv4772qRAun5CG/xLCEbkDwNUc= github.com/glebarez/go-sqlite v1.14.7/go.mod h1:TKAw5tjyB/ocvVht7Xv4772qRAun5CG/xLCEbkDwNUc=
github.com/glebarez/go-sqlite v1.14.8 h1:30RsIS/olgfOMr7SxiCaYhpq50BTteA/CUKaWVOOHYg=
github.com/glebarez/go-sqlite v1.14.8/go.mod h1:gf9QVsKCYMcu+7nd+ZbDqvXnEXEb22qLcqRUQ9XEI34=
github.com/glebarez/sqlite v1.3.5 h1:R9op5nxb9Z10t4VXQSdAVyqRalLhWdLrlaT/iuvOGHI= github.com/glebarez/sqlite v1.3.5 h1:R9op5nxb9Z10t4VXQSdAVyqRalLhWdLrlaT/iuvOGHI=
github.com/glebarez/sqlite v1.3.5/go.mod h1:ZffEtp/afVhV+jvIzQi8wlYEIkuGAYshr9OPKM/NmQc= github.com/glebarez/sqlite v1.3.5/go.mod h1:ZffEtp/afVhV+jvIzQi8wlYEIkuGAYshr9OPKM/NmQc=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@ -412,7 +414,6 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/infobloxopen/atlas-app-toolkit v0.24.1-0.20210416193901-4c7518b07e08/go.mod h1:9BTHnpff654rY1J8KxSUOLJ+ZUDn2Vi3mmk26gQDo1M= github.com/infobloxopen/atlas-app-toolkit v0.24.1-0.20210416193901-4c7518b07e08/go.mod h1:9BTHnpff654rY1J8KxSUOLJ+ZUDn2Vi3mmk26gQDo1M=
github.com/infobloxopen/protoc-gen-gorm v1.1.0 h1:l6JKEkqMTFbtoGIfQmh/aOy7KfljgX4ql772LtG4kas= github.com/infobloxopen/protoc-gen-gorm v1.1.0 h1:l6JKEkqMTFbtoGIfQmh/aOy7KfljgX4ql772LtG4kas=
github.com/infobloxopen/protoc-gen-gorm v1.1.0/go.mod h1:ohzLmmFMWQztw2RBHunfjKSCjTPUW4JvbgU1Mdazwxg= github.com/infobloxopen/protoc-gen-gorm v1.1.0/go.mod h1:ohzLmmFMWQztw2RBHunfjKSCjTPUW4JvbgU1Mdazwxg=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
@ -434,7 +435,6 @@ github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5W
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
@ -450,7 +450,6 @@ github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01C
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38= github.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38=
github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
@ -458,7 +457,6 @@ github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8=
github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M= github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M=
github.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w= github.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w=
github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw= github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw=
@ -474,8 +472,6 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
@ -497,8 +493,8 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.14.2 h1:S0OHlFk/Gbon/yauFJ4FfJJF5V0fc5HbBTJazi28pRw= github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4=
github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
@ -535,8 +531,9 @@ github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc8
github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@ -558,7 +555,6 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.11 h1:gt+cp9c0XGqe9S/wAHTL3n/7MqY+siPWgWJgqdsFrzQ= github.com/mattn/go-sqlite3 v1.14.11 h1:gt+cp9c0XGqe9S/wAHTL3n/7MqY+siPWgWJgqdsFrzQ=
github.com/mattn/go-sqlite3 v1.14.11/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.11/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
@ -671,8 +667,8 @@ github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY
github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE= github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE=
github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU= github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU=
github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE= github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
github.com/pterm/pterm v0.12.36 h1:Ui5zZj7xA8lXR0CxWXlKGCQMW1cZVUMOS8jEXs6ur/g= github.com/pterm/pterm v0.12.37 h1:QGOyuaDUmY3yTbP0k6i0uPNqNHA9YofEBQDy0tIyKTA=
github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8= github.com/pterm/pterm v0.12.37/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
@ -762,10 +758,10 @@ github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9
github.com/twitchtv/twirp v7.1.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= github.com/twitchtv/twirp v7.1.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
@ -845,8 +841,6 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20220210151621-f4118a5b28e2 h1:XdAboW3BNMv9ocSCOk/u1MFioZGzCNkiJZ19v9Oe3Ig=
golang.org/x/crypto v0.0.0-20220210151621-f4118a5b28e2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -940,8 +934,9 @@ golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -959,8 +954,9 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -1063,9 +1059,10 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -1274,8 +1271,8 @@ google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ6
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220118154757-00ab72f36ad5/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220210181026-6fee9acbd336 h1:RK2ysGpQApbI6U7xn+ROT2rrm08lE/t8AcGqG8XI1CY= google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7 h1:ntPPoHzFW6Xp09ueznmahONZufyoSakK/piXnr2BU3I=
google.golang.org/genproto v0.0.0-20220210181026-6fee9acbd336/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -1359,23 +1356,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/datatypes v1.0.5 h1:3vHCfg4Bz8SDx83zE+ASskF+g/j0kWrcKrY9jFUyAl0=
gorm.io/datatypes v1.0.5/go.mod h1:acG/OHGwod+1KrbwPL1t+aavb7jOBOETeyl5M8K5VQs=
gorm.io/driver/mysql v1.2.2/go.mod h1:qsiz+XcAyMrS6QY+X3M9R6b/lKM1imKmcuK9kac5LTo=
gorm.io/driver/mysql v1.2.3 h1:cZqzlOfg5Kf1VIdLC1D9hT6Cy9BgxhExLj/2tIgUe7Y=
gorm.io/driver/mysql v1.2.3/go.mod h1:qsiz+XcAyMrS6QY+X3M9R6b/lKM1imKmcuK9kac5LTo=
gorm.io/driver/mysql v1.3.2 h1:QJryWiqQ91EvZ0jZL48NOpdlPdMjdip1hQ8bTgo4H7I=
gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
gorm.io/driver/postgres v1.2.3 h1:f4t0TmNMy9gh3TU2PX+EppoA6YsgFnyq8Ojtddb42To=
gorm.io/driver/postgres v1.2.3/go.mod h1:pJV6RgYQPG47aM1f0QeOzFH9HxQc8JcmAgjRCgS0wjs=
gorm.io/driver/postgres v1.3.1 h1:Pyv+gg1Gq1IgsLYytj/S2k7ebII3CzEdpqQkPOdH24g= gorm.io/driver/postgres v1.3.1 h1:Pyv+gg1Gq1IgsLYytj/S2k7ebII3CzEdpqQkPOdH24g=
gorm.io/driver/postgres v1.3.1/go.mod h1:WwvWOuR9unCLpGWCL6Y3JOeBWvbKi6JLhayiVclSZZU= gorm.io/driver/postgres v1.3.1/go.mod h1:WwvWOuR9unCLpGWCL6Y3JOeBWvbKi6JLhayiVclSZZU=
gorm.io/driver/sqlite v1.3.1 h1:bwfE+zTEWklBYoEodIOIBwuWHpnx52Z9zJFW5F33WLk=
gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg=
gorm.io/driver/sqlserver v1.3.1 h1:F5t6ScMzOgy1zukRTIZgLZwKahgt3q1woAILVolKpOI=
gorm.io/driver/sqlserver v1.3.1/go.mod h1:w25Vrx2BG+CJNUu/xKbFhaKlGxT/nzRkhWCCoptX8tQ=
gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk=
gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.1 h1:aj5IlhDzEPsoIyOPtTRVI+SyaN1u6k613sbt4pwbxG0= gorm.io/gorm v1.23.1 h1:aj5IlhDzEPsoIyOPtTRVI+SyaN1u6k613sbt4pwbxG0=
gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
@ -1450,7 +1432,10 @@ modernc.org/ccgo/v3 v3.14.0/go.mod h1:hBrkiBlUwvr5vV/ZH9YzXIp982jKE8Ek8tR1ytoAL6
modernc.org/ccgo/v3 v3.15.1/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0= modernc.org/ccgo/v3 v3.15.1/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0=
modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0= modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0=
modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8= modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8=
modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I=
modernc.org/ccgo/v3 v3.15.13/go.mod h1:QHtvdpeODlXjdK3tsbpyK+7U9JV4PQsrPGIbtmc0KfY=
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/ccorpus v1.11.4/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
@ -1494,8 +1479,9 @@ modernc.org/libc v1.13.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/libc v1.13.2/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= modernc.org/libc v1.13.2/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk= modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34= modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34=
modernc.org/libc v1.14.3 h1:ruQJ8VDhnWkUR/otUG/Ksw+sWHUw9cPAq6mjDaY/Y7c=
modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ= modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ=
modernc.org/libc v1.14.5 h1:DAHvwGoVRDZs5iJXnX9RJrgXSsorupCWmJ2ac964Owk=
modernc.org/libc v1.14.5/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
@ -1505,10 +1491,12 @@ modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14=
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.14.5 h1:bYrrjwH9Y7QUGk1MbchZDhRfmpGuEAs/D45sVjNbfvs=
modernc.org/sqlite v1.14.5/go.mod h1:YyX5Rx0WbXokitdWl2GJIDy4BrPxBP0PwwhpXOHCDLE= modernc.org/sqlite v1.14.5/go.mod h1:YyX5Rx0WbXokitdWl2GJIDy4BrPxBP0PwwhpXOHCDLE=
modernc.org/sqlite v1.14.7 h1:A+6rGjtRQbt9SORXfV+hUyXOP3mDf7J5uz+EES/CNPE=
modernc.org/sqlite v1.14.7/go.mod h1:yiCvMv3HblGmzENNIaNtFhfaNIwcla4u2JQEwJPzfEc=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.10.0/go.mod h1:WzWapmP/7dHVhFoyPpEaNSVTL8xtewhouN/cqSJ5A2s= modernc.org/tcl v1.10.0/go.mod h1:WzWapmP/7dHVhFoyPpEaNSVTL8xtewhouN/cqSJ5A2s=
modernc.org/tcl v1.11.0/go.mod h1:zsTUpbQ+NxQEjOjCUlImDLPv1sG8Ww0qp66ZvyOxCgw=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.2.21/go.mod h1:uXrObx4pGqXWIMliC5MiKuwAyMrltzwpteOFUP1PWCc= modernc.org/z v1.2.21/go.mod h1:uXrObx4pGqXWIMliC5MiKuwAyMrltzwpteOFUP1PWCc=
modernc.org/z v1.3.0/go.mod h1:+mvgLH814oDjtATDdT3rs84JnUIpkvAF5B8AVkNlE2g= modernc.org/z v1.3.0/go.mod h1:+mvgLH814oDjtATDdT3rs84JnUIpkvAF5B8AVkNlE2g=
@ -1517,5 +1505,5 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
tailscale.com v1.20.4 h1:7cl/Q2Sbo2Jb2dX7zA+Exbbl7DT5UGZ4iGhQ2xj23X0= tailscale.com v1.22.0 h1:/a1f6eKEl9vL/wGFP8mkhe7O1zDRGtWa9Ft2rGp5N80=
tailscale.com v1.20.4/go.mod h1:kjVy3ji2OH5lZhPLIIRacoY3CN4Bo3Yyb2mtoM8nfJ4= tailscale.com v1.22.0/go.mod h1:D2zuDnjHT7v4aCt71c4+ytQUUAGpnypW+DoubYLaHjg=

View File

@ -3,12 +3,10 @@ package headscale
import ( import (
"context" "context"
"encoding/json"
"time" "time"
"github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"gorm.io/datatypes"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
) )
@ -159,9 +157,11 @@ func (api headscaleV1APIServer) RegisterMachine(
Str("namespace", request.GetNamespace()). Str("namespace", request.GetNamespace()).
Str("machine_key", request.GetKey()). Str("machine_key", request.GetKey()).
Msg("Registering machine") Msg("Registering machine")
machine, err := api.h.RegisterMachine(
machine, err := api.h.RegisterMachineFromAuthCallback(
request.GetKey(), request.GetKey(),
request.GetNamespace(), request.GetNamespace(),
RegisterMethodCLI,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -262,13 +262,8 @@ func (api headscaleV1APIServer) GetMachineRoute(
return nil, err return nil, err
} }
routes, err := machine.RoutesToProto()
if err != nil {
return nil, err
}
return &v1.GetMachineRouteResponse{ return &v1.GetMachineRouteResponse{
Routes: routes, Routes: machine.RoutesToProto(),
}, nil }, nil
} }
@ -286,13 +281,8 @@ func (api headscaleV1APIServer) EnableMachineRoutes(
return nil, err return nil, err
} }
routes, err := machine.RoutesToProto()
if err != nil {
return nil, err
}
return &v1.EnableMachineRoutesResponse{ return &v1.EnableMachineRoutesResponse{
Routes: routes, Routes: machine.RoutesToProto(),
}, nil }, nil
} }
@ -379,13 +369,6 @@ func (api headscaleV1APIServer) DebugCreateMachine(
Hostname: "DebugTestMachine", Hostname: "DebugTestMachine",
} }
log.Trace().Caller().Interface("hostinfo", hostinfo).Msg("")
hostinfoJson, err := json.Marshal(hostinfo)
if err != nil {
return nil, err
}
newMachine := Machine{ newMachine := Machine{
MachineKey: request.GetKey(), MachineKey: request.GetKey(),
Name: request.GetName(), Name: request.GetName(),
@ -395,14 +378,14 @@ func (api headscaleV1APIServer) DebugCreateMachine(
LastSeen: &time.Time{}, LastSeen: &time.Time{},
LastSuccessfulUpdate: &time.Time{}, LastSuccessfulUpdate: &time.Time{},
HostInfo: datatypes.JSON(hostinfoJson), HostInfo: HostInfo(hostinfo),
} }
// log.Trace().Caller().Interface("machine", newMachine).Msg("") api.h.registrationCache.Set(
request.GetKey(),
if err := api.h.db.Create(&newMachine).Error; err != nil { newMachine,
return nil, err registerCacheExpiration,
} )
return &v1.DebugCreateMachineResponse{Machine: newMachine.toProto()}, nil return &v1.DebugCreateMachineResponse{Machine: newMachine.toProto()}, nil
} }

View File

@ -621,12 +621,6 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() {
assert.Equal(s.T(), "machine-4", listAll[3].Name) assert.Equal(s.T(), "machine-4", listAll[3].Name)
assert.Equal(s.T(), "machine-5", listAll[4].Name) assert.Equal(s.T(), "machine-5", listAll[4].Name)
assert.True(s.T(), listAll[0].Registered)
assert.True(s.T(), listAll[1].Registered)
assert.True(s.T(), listAll[2].Registered)
assert.True(s.T(), listAll[3].Registered)
assert.True(s.T(), listAll[4].Registered)
otherNamespaceMachineKeys := []string{ otherNamespaceMachineKeys := []string{
"b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e", "b5b444774186d4217adcec407563a1223929465ee2c68a4da13af0d0185b4f8e",
"dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584", "dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584",
@ -710,9 +704,6 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() {
assert.Equal(s.T(), "otherNamespace-machine-1", listAllWithotherNamespace[5].Name) assert.Equal(s.T(), "otherNamespace-machine-1", listAllWithotherNamespace[5].Name)
assert.Equal(s.T(), "otherNamespace-machine-2", listAllWithotherNamespace[6].Name) assert.Equal(s.T(), "otherNamespace-machine-2", listAllWithotherNamespace[6].Name)
assert.True(s.T(), listAllWithotherNamespace[5].Registered)
assert.True(s.T(), listAllWithotherNamespace[6].Registered)
// Test list all nodes after added otherNamespace // Test list all nodes after added otherNamespace
listOnlyotherNamespaceMachineNamespaceResult, err := ExecuteCommand( listOnlyotherNamespaceMachineNamespaceResult, err := ExecuteCommand(
&s.headscale, &s.headscale,
@ -752,9 +743,6 @@ func (s *IntegrationCLITestSuite) TestNodeCommand() {
listOnlyotherNamespaceMachineNamespace[1].Name, listOnlyotherNamespaceMachineNamespace[1].Name,
) )
assert.True(s.T(), listOnlyotherNamespaceMachineNamespace[0].Registered)
assert.True(s.T(), listOnlyotherNamespaceMachineNamespace[1].Registered)
// Delete a machines // Delete a machines
_, err = ExecuteCommand( _, err = ExecuteCommand(
&s.headscale, &s.headscale,
@ -979,7 +967,6 @@ func (s *IntegrationCLITestSuite) TestRouteCommand() {
assert.Equal(s.T(), uint64(1), machine.Id) assert.Equal(s.T(), uint64(1), machine.Id)
assert.Equal(s.T(), "route-machine", machine.Name) assert.Equal(s.T(), "route-machine", machine.Name)
assert.True(s.T(), machine.Registered)
listAllResult, err := ExecuteCommand( listAllResult, err := ExecuteCommand(
&s.headscale, &s.headscale,

View File

@ -2,7 +2,6 @@ package headscale
import ( import (
"database/sql/driver" "database/sql/driver"
"encoding/json"
"fmt" "fmt"
"sort" "sort"
"strconv" "strconv"
@ -13,7 +12,6 @@ import (
v1 "github.com/juanfont/headscale/gen/go/headscale/v1" v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
"gorm.io/datatypes"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/key" "tailscale.com/types/key"
@ -21,9 +19,12 @@ import (
const ( const (
errMachineNotFound = Error("machine not found") errMachineNotFound = Error("machine not found")
errMachineAlreadyRegistered = Error("machine already registered")
errMachineRouteIsNotAvailable = Error("route is not available on machine") errMachineRouteIsNotAvailable = Error("route is not available on machine")
errMachineAddressesInvalid = Error("failed to parse machine addresses") errMachineAddressesInvalid = Error("failed to parse machine addresses")
errMachineNotFoundRegistrationCache = Error(
"machine not found in registration cache",
)
errCouldNotConvertMachineInterface = Error("failed to convert machine interface")
errHostnameTooLong = Error("Hostname too long") errHostnameTooLong = Error("Hostname too long")
) )
@ -42,8 +43,9 @@ type Machine struct {
NamespaceID uint NamespaceID uint
Namespace Namespace `gorm:"foreignKey:NamespaceID"` Namespace Namespace `gorm:"foreignKey:NamespaceID"`
Registered bool // temp
RegisterMethod string RegisterMethod string
// TODO(kradalby): This seems like irrelevant information?
AuthKeyID uint AuthKeyID uint
AuthKey *PreAuthKey AuthKey *PreAuthKey
@ -51,9 +53,9 @@ type Machine struct {
LastSuccessfulUpdate *time.Time LastSuccessfulUpdate *time.Time
Expiry *time.Time Expiry *time.Time
HostInfo datatypes.JSON HostInfo HostInfo
Endpoints datatypes.JSON Endpoints StringList
EnabledRoutes datatypes.JSON EnabledRoutes IPPrefixes
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
@ -65,11 +67,6 @@ type (
MachinesP []*Machine MachinesP []*Machine
) )
// For the time being this method is rather naive.
func (machine Machine) isRegistered() bool {
return machine.Registered
}
type MachineAddresses []netaddr.IP type MachineAddresses []netaddr.IP
func (ma MachineAddresses) ToStringSlice() []string { func (ma MachineAddresses) ToStringSlice() []string {
@ -116,7 +113,7 @@ func (machine Machine) isExpired() bool {
// If Expiry is not set, the client has not indicated that // If Expiry is not set, the client has not indicated that
// it wants an expiry time, it is therefor considered // it wants an expiry time, it is therefor considered
// to mean "not expired" // to mean "not expired"
if machine.Expiry.IsZero() { if machine.Expiry == nil || machine.Expiry.IsZero() {
return false return false
} }
@ -173,6 +170,12 @@ func getFilteredByACLPeers(
machine.IPAddresses.ToStringSlice(), machine.IPAddresses.ToStringSlice(),
peer.IPAddresses.ToStringSlice(), peer.IPAddresses.ToStringSlice(),
) || // match source and destination ) || // match source and destination
matchSourceAndDestinationWithRule(
rule.SrcIPs,
dst,
peer.IPAddresses.ToStringSlice(),
machine.IPAddresses.ToStringSlice(),
) || // match return path
matchSourceAndDestinationWithRule( matchSourceAndDestinationWithRule(
rule.SrcIPs, rule.SrcIPs,
dst, dst,
@ -182,9 +185,21 @@ func getFilteredByACLPeers(
matchSourceAndDestinationWithRule( matchSourceAndDestinationWithRule(
rule.SrcIPs, rule.SrcIPs,
dst, dst,
[]string{"*"},
[]string{"*"},
) || // match source and all destination
matchSourceAndDestinationWithRule(
rule.SrcIPs,
dst,
[]string{"*"},
peer.IPAddresses.ToStringSlice(), peer.IPAddresses.ToStringSlice(),
) || // match source and all destination
matchSourceAndDestinationWithRule(
rule.SrcIPs,
dst,
[]string{"*"},
machine.IPAddresses.ToStringSlice(), machine.IPAddresses.ToStringSlice(),
) { // match return path ) { // match all sources and source
peers[peer.ID] = peer peers[peer.ID] = peer
} }
} }
@ -214,7 +229,7 @@ func (h *Headscale) ListPeers(machine *Machine) (Machines, error) {
Msg("Finding direct peers") Msg("Finding direct peers")
machines := Machines{} machines := Machines{}
if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Where("machine_key <> ? AND registered", if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Where("machine_key <> ?",
machine.MachineKey).Find(&machines).Error; err != nil { machine.MachineKey).Find(&machines).Error; err != nil {
log.Error().Err(err).Msg("Error accessing db") log.Error().Err(err).Msg("Error accessing db")
@ -277,7 +292,7 @@ func (h *Headscale) getValidPeers(machine *Machine) (Machines, error) {
} }
for _, peer := range peers { for _, peer := range peers {
if peer.isRegistered() && !peer.isExpired() { if !peer.isExpired() {
validPeers = append(validPeers, peer) validPeers = append(validPeers, peer)
} }
} }
@ -366,8 +381,6 @@ func (h *Headscale) RefreshMachine(machine *Machine, expiry time.Time) {
// DeleteMachine softs deletes a Machine from the database. // DeleteMachine softs deletes a Machine from the database.
func (h *Headscale) DeleteMachine(machine *Machine) error { func (h *Headscale) DeleteMachine(machine *Machine) error {
machine.Registered = false
h.db.Save(&machine) // we mark it as unregistered, just in case
if err := h.db.Delete(&machine).Error; err != nil { if err := h.db.Delete(&machine).Error; err != nil {
return err return err
} }
@ -393,20 +406,8 @@ func (h *Headscale) HardDeleteMachine(machine *Machine) error {
} }
// GetHostInfo returns a Hostinfo struct for the machine. // GetHostInfo returns a Hostinfo struct for the machine.
func (machine *Machine) GetHostInfo() (*tailcfg.Hostinfo, error) { func (machine *Machine) GetHostInfo() tailcfg.Hostinfo {
hostinfo := tailcfg.Hostinfo{} return tailcfg.Hostinfo(machine.HostInfo)
if len(machine.HostInfo) != 0 {
hi, err := machine.HostInfo.MarshalJSON()
if err != nil {
return nil, err
}
err = json.Unmarshal(hi, &hostinfo)
if err != nil {
return nil, err
}
}
return &hostinfo, nil
} }
func (h *Headscale) isOutdated(machine *Machine) bool { func (h *Headscale) isOutdated(machine *Machine) bool {
@ -536,54 +537,12 @@ func (machine Machine) toNode(
// TODO(kradalby): Needs investigation, We probably dont need this condition // TODO(kradalby): Needs investigation, We probably dont need this condition
// now that we dont have shared nodes // now that we dont have shared nodes
if includeRoutes { if includeRoutes {
routesStr := []string{} allowedIPs = append(allowedIPs, machine.EnabledRoutes...)
if len(machine.EnabledRoutes) != 0 {
allwIps, err := machine.EnabledRoutes.MarshalJSON()
if err != nil {
return nil, err
}
err = json.Unmarshal(allwIps, &routesStr)
if err != nil {
return nil, err
}
}
for _, routeStr := range routesStr {
ip, err := netaddr.ParseIPPrefix(routeStr)
if err != nil {
return nil, err
}
allowedIPs = append(allowedIPs, ip)
}
}
endpoints := []string{}
if len(machine.Endpoints) != 0 {
be, err := machine.Endpoints.MarshalJSON()
if err != nil {
return nil, err
}
err = json.Unmarshal(be, &endpoints)
if err != nil {
return nil, err
}
}
hostinfo := tailcfg.Hostinfo{}
if len(machine.HostInfo) != 0 {
hi, err := machine.HostInfo.MarshalJSON()
if err != nil {
return nil, err
}
err = json.Unmarshal(hi, &hostinfo)
if err != nil {
return nil, err
}
} }
var derp string var derp string
if hostinfo.NetInfo != nil { if machine.HostInfo.NetInfo != nil {
derp = fmt.Sprintf("127.3.3.40:%d", hostinfo.NetInfo.PreferredDERP) derp = fmt.Sprintf("127.3.3.40:%d", machine.HostInfo.NetInfo.PreferredDERP)
} else { } else {
derp = "127.3.3.40:0" // Zero means disconnected or unknown. derp = "127.3.3.40:0" // Zero means disconnected or unknown.
} }
@ -614,6 +573,8 @@ func (machine Machine) toNode(
hostname = machine.Name hostname = machine.Name
} }
hostInfo := machine.GetHostInfo()
node := tailcfg.Node{ node := tailcfg.Node{
ID: tailcfg.NodeID(machine.ID), // this is the actual ID ID: tailcfg.NodeID(machine.ID), // this is the actual ID
StableID: tailcfg.StableNodeID( StableID: tailcfg.StableNodeID(
@ -627,15 +588,15 @@ func (machine Machine) toNode(
DiscoKey: discoKey, DiscoKey: discoKey,
Addresses: addrs, Addresses: addrs,
AllowedIPs: allowedIPs, AllowedIPs: allowedIPs,
Endpoints: endpoints, Endpoints: machine.Endpoints,
DERP: derp, DERP: derp,
Hostinfo: hostinfo, Hostinfo: hostInfo.View(),
Created: machine.CreatedAt, Created: machine.CreatedAt,
LastSeen: machine.LastSeen, LastSeen: machine.LastSeen,
KeepAlive: true, KeepAlive: true,
MachineAuthorized: machine.Registered, MachineAuthorized: !machine.isExpired(),
Capabilities: []string{tailcfg.CapabilityFileSharing}, Capabilities: []string{tailcfg.CapabilityFileSharing},
} }
@ -653,8 +614,6 @@ func (machine *Machine) toProto() *v1.Machine {
Name: machine.Name, Name: machine.Name,
Namespace: machine.Namespace.toProto(), Namespace: machine.Namespace.toProto(),
Registered: machine.Registered,
// TODO(kradalby): Implement register method enum converter // TODO(kradalby): Implement register method enum converter
// RegisterMethod: , // RegisterMethod: ,
@ -682,74 +641,50 @@ func (machine *Machine) toProto() *v1.Machine {
return machineProto return machineProto
} }
// RegisterMachine is executed from the CLI to register a new Machine using its MachineKey. func (h *Headscale) RegisterMachineFromAuthCallback(
func (h *Headscale) RegisterMachine(
machineKeyStr string, machineKeyStr string,
namespaceName string, namespaceName string,
registrationMethod string,
) (*Machine, error) { ) (*Machine, error) {
if machineInterface, ok := h.registrationCache.Get(machineKeyStr); ok {
if registrationMachine, ok := machineInterface.(Machine); ok {
namespace, err := h.GetNamespace(namespaceName) namespace, err := h.GetNamespace(namespaceName)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf(
"failed to find namespace in register machine from auth callback, %w",
err,
)
} }
var machineKey key.MachinePublic registrationMachine.NamespaceID = namespace.ID
err = machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr))) registrationMachine.RegisterMethod = registrationMethod
if err != nil {
return nil, err machine, err := h.RegisterMachine(
registrationMachine,
)
return machine, err
} else {
return nil, errCouldNotConvertMachineInterface
}
} }
return nil, errMachineNotFoundRegistrationCache
}
// RegisterMachine is executed from the CLI to register a new Machine using its MachineKey.
func (h *Headscale) RegisterMachine(machine Machine,
) (*Machine, error) {
log.Trace(). log.Trace().
Caller(). Caller().
Str("machine_key_str", machineKeyStr). Str("machine_key", machine.MachineKey).
Str("machine_key", machineKey.String()).
Msg("Registering machine") Msg("Registering machine")
machine, err := h.GetMachineByMachineKey(machineKey)
if err != nil {
return nil, err
}
// TODO(kradalby): Currently, if it fails to find a requested expiry, non will be set
// This means that if a user is to slow with register a machine, it will possibly not
// have the correct expiry.
requestedTime := time.Time{}
if requestedTimeIf, found := h.requestedExpiryCache.Get(machineKey.String()); found {
log.Trace().
Caller().
Str("machine", machine.Name).
Msg("Expiry time found in cache, assigning to node")
if reqTime, ok := requestedTimeIf.(time.Time); ok {
requestedTime = reqTime
}
}
if machine.isRegistered() {
log.Trace().
Caller().
Str("machine", machine.Name).
Msg("machine already registered, reauthenticating")
h.RefreshMachine(machine, requestedTime)
return machine, nil
}
log.Trace(). log.Trace().
Caller(). Caller().
Str("machine", machine.Name). Str("machine", machine.Name).
Msg("Attempting to register machine") Msg("Attempting to register machine")
if machine.isRegistered() {
err := errMachineAlreadyRegistered
log.Error().
Caller().
Err(err).
Str("machine", machine.Name).
Msg("Attempting to register machine")
return nil, err
}
h.ipAllocationMutex.Lock() h.ipAllocationMutex.Lock()
defer h.ipAllocationMutex.Unlock() defer h.ipAllocationMutex.Unlock()
@ -764,17 +699,8 @@ func (h *Headscale) RegisterMachine(
return nil, err return nil, err
} }
log.Trace().
Caller().
Str("machine", machine.Name).
Str("ip", strings.Join(ips.ToStringSlice(), ",")).
Msg("Found IP for host")
machine.IPAddresses = ips machine.IPAddresses = ips
machine.NamespaceID = namespace.ID
machine.Registered = true
machine.RegisterMethod = RegisterMethodCLI
machine.Expiry = &requestedTime
h.db.Save(&machine) h.db.Save(&machine)
log.Trace(). log.Trace().
@ -783,40 +709,15 @@ func (h *Headscale) RegisterMachine(
Str("ip", strings.Join(ips.ToStringSlice(), ",")). Str("ip", strings.Join(ips.ToStringSlice(), ",")).
Msg("Machine registered with the database") Msg("Machine registered with the database")
return machine, nil return &machine, nil
} }
func (machine *Machine) GetAdvertisedRoutes() ([]netaddr.IPPrefix, error) { func (machine *Machine) GetAdvertisedRoutes() []netaddr.IPPrefix {
hostInfo, err := machine.GetHostInfo() return machine.HostInfo.RoutableIPs
if err != nil {
return nil, err
} }
return hostInfo.RoutableIPs, nil func (machine *Machine) GetEnabledRoutes() []netaddr.IPPrefix {
} return machine.EnabledRoutes
func (machine *Machine) GetEnabledRoutes() ([]netaddr.IPPrefix, error) {
data, err := machine.EnabledRoutes.MarshalJSON()
if err != nil {
return nil, err
}
routesStr := []string{}
err = json.Unmarshal(data, &routesStr)
if err != nil {
return nil, err
}
routes := make([]netaddr.IPPrefix, len(routesStr))
for index, routeStr := range routesStr {
route, err := netaddr.ParseIPPrefix(routeStr)
if err != nil {
return nil, err
}
routes[index] = route
}
return routes, nil
} }
func (machine *Machine) IsRoutesEnabled(routeStr string) bool { func (machine *Machine) IsRoutesEnabled(routeStr string) bool {
@ -825,10 +726,7 @@ func (machine *Machine) IsRoutesEnabled(routeStr string) bool {
return false return false
} }
enabledRoutes, err := machine.GetEnabledRoutes() enabledRoutes := machine.GetEnabledRoutes()
if err != nil {
return false
}
for _, enabledRoute := range enabledRoutes { for _, enabledRoute := range enabledRoutes {
if route == enabledRoute { if route == enabledRoute {
@ -852,13 +750,8 @@ func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error {
newRoutes[index] = route newRoutes[index] = route
} }
availableRoutes, err := machine.GetAdvertisedRoutes()
if err != nil {
return err
}
for _, newRoute := range newRoutes { for _, newRoute := range newRoutes {
if !containsIPPrefix(availableRoutes, newRoute) { if !containsIPPrefix(machine.GetAdvertisedRoutes(), newRoute) {
return fmt.Errorf( return fmt.Errorf(
"route (%s) is not available on node %s: %w", "route (%s) is not available on node %s: %w",
machine.Name, machine.Name,
@ -867,30 +760,19 @@ func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error {
} }
} }
routes, err := json.Marshal(newRoutes) machine.EnabledRoutes = newRoutes
if err != nil {
return err
}
machine.EnabledRoutes = datatypes.JSON(routes)
h.db.Save(&machine) h.db.Save(&machine)
return nil return nil
} }
func (machine *Machine) RoutesToProto() (*v1.Routes, error) { func (machine *Machine) RoutesToProto() *v1.Routes {
availableRoutes, err := machine.GetAdvertisedRoutes() availableRoutes := machine.GetAdvertisedRoutes()
if err != nil {
return nil, err
}
enabledRoutes, err := machine.GetEnabledRoutes() enabledRoutes := machine.GetEnabledRoutes()
if err != nil {
return nil, err
}
return &v1.Routes{ return &v1.Routes{
AdvertisedRoutes: ipPrefixToString(availableRoutes), AdvertisedRoutes: ipPrefixToString(availableRoutes),
EnabledRoutes: ipPrefixToString(enabledRoutes), EnabledRoutes: ipPrefixToString(enabledRoutes),
}, nil }
} }

View File

@ -29,16 +29,12 @@ func (s *Suite) TestGetMachine(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testmachine", Name: "testmachine",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
} }
app.db.Save(machine) app.db.Save(machine)
machineFromDB, err := app.GetMachine("test", "testmachine") _, err = app.GetMachine("test", "testmachine")
c.Assert(err, check.IsNil)
_, err = machineFromDB.GetHostInfo()
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
} }
@ -59,16 +55,12 @@ func (s *Suite) TestGetMachineByID(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testmachine", Name: "testmachine",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
} }
app.db.Save(&machine) app.db.Save(&machine)
machineByID, err := app.GetMachineByID(0) _, err = app.GetMachineByID(0)
c.Assert(err, check.IsNil)
_, err = machineByID.GetHostInfo()
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
} }
@ -82,7 +74,6 @@ func (s *Suite) TestDeleteMachine(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testmachine", Name: "testmachine",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(1), AuthKeyID: uint(1),
} }
@ -105,7 +96,6 @@ func (s *Suite) TestHardDeleteMachine(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testmachine3", Name: "testmachine3",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(1), AuthKeyID: uint(1),
} }
@ -136,7 +126,6 @@ func (s *Suite) TestListPeers(c *check.C) {
DiscoKey: "faa" + strconv.Itoa(index), DiscoKey: "faa" + strconv.Itoa(index),
Name: "testmachine" + strconv.Itoa(index), Name: "testmachine" + strconv.Itoa(index),
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
} }
@ -146,9 +135,6 @@ func (s *Suite) TestListPeers(c *check.C) {
machine0ByID, err := app.GetMachineByID(0) machine0ByID, err := app.GetMachineByID(0)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, err = machine0ByID.GetHostInfo()
c.Assert(err, check.IsNil)
peersOfMachine0, err := app.ListPeers(machine0ByID) peersOfMachine0, err := app.ListPeers(machine0ByID)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
@ -188,7 +174,6 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
}, },
Name: "testmachine" + strconv.Itoa(index), Name: "testmachine" + strconv.Itoa(index),
NamespaceID: stor[index%2].namespace.ID, NamespaceID: stor[index%2].namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(stor[index%2].key.ID), AuthKeyID: uint(stor[index%2].key.ID),
} }
@ -219,9 +204,6 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
c.Logf("Machine(%v), namespace: %v", testMachine.Name, testMachine.Namespace) c.Logf("Machine(%v), namespace: %v", testMachine.Name, testMachine.Namespace)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, err = testMachine.GetHostInfo()
c.Assert(err, check.IsNil)
machines, err := app.ListMachines() machines, err := app.ListMachines()
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
@ -258,7 +240,6 @@ func (s *Suite) TestExpireMachine(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testmachine", Name: "testmachine",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
Expiry: &time.Time{}, Expiry: &time.Time{},
@ -296,6 +277,7 @@ func (s *Suite) TestSerdeAddressStrignSlice(c *check.C) {
} }
} }
// nolint
func Test_getFilteredByACLPeers(t *testing.T) { func Test_getFilteredByACLPeers(t *testing.T) {
type args struct { type args struct {
machines []Machine machines []Machine
@ -443,7 +425,7 @@ func Test_getFilteredByACLPeers(t *testing.T) {
}, },
}, },
machine: &Machine{ // current machine machine: &Machine{ // current machine
ID: 1, ID: 2,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")}, IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
Namespace: Namespace{Name: "marc"}, Namespace: Namespace{Name: "marc"},
}, },
@ -456,6 +438,208 @@ func Test_getFilteredByACLPeers(t *testing.T) {
}, },
}, },
}, },
{
name: "rules allows all hosts to reach one destination",
args: args{
machines: []Machine{ // list of all machines in the database
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
{
SrcIPs: []string{"*"},
DstPorts: []tailcfg.NetPortRange{
{IP: "100.64.0.2"},
},
},
},
machine: &Machine{ // current machine
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
},
want: Machines{
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
},
},
{
name: "rules allows all hosts to reach one destination, destination can reach all hosts",
args: args{
machines: []Machine{ // list of all machines in the database
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
{
SrcIPs: []string{"*"},
DstPorts: []tailcfg.NetPortRange{
{IP: "100.64.0.2"},
},
},
},
machine: &Machine{ // current machine
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
},
want: Machines{
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
},
},
{
name: "rule allows all hosts to reach all destinations",
args: args{
machines: []Machine{ // list of all machines in the database
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
{
SrcIPs: []string{"*"},
DstPorts: []tailcfg.NetPortRange{
{IP: "*"},
},
},
},
machine: &Machine{ // current machine
ID: 2,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
Namespace: Namespace{Name: "marc"},
},
},
want: Machines{
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 3,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.3")},
Namespace: Namespace{Name: "mickael"},
},
},
},
{
name: "without rule all communications are forbidden",
args: args{
machines: []Machine{ // list of all machines in the database
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
},
rules: []tailcfg.FilterRule{ // list of all ACLRules registered
},
machine: &Machine{ // current machine
ID: 2,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
Namespace: Namespace{Name: "marc"},
},
},
want: Machines{},
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {

View File

@ -54,7 +54,6 @@ func (s *Suite) TestDestroyNamespaceErrors(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testmachine", Name: "testmachine",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
} }
@ -146,7 +145,6 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
Name: "test_get_shared_nodes_1", Name: "test_get_shared_nodes_1",
NamespaceID: namespaceShared1.ID, NamespaceID: namespaceShared1.ID,
Namespace: *namespaceShared1, Namespace: *namespaceShared1,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")}, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")},
AuthKeyID: uint(preAuthKeyShared1.ID), AuthKeyID: uint(preAuthKeyShared1.ID),
@ -164,7 +162,6 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
Name: "test_get_shared_nodes_2", Name: "test_get_shared_nodes_2",
NamespaceID: namespaceShared2.ID, NamespaceID: namespaceShared2.ID,
Namespace: *namespaceShared2, Namespace: *namespaceShared2,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")}, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")},
AuthKeyID: uint(preAuthKeyShared2.ID), AuthKeyID: uint(preAuthKeyShared2.ID),
@ -182,7 +179,6 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
Name: "test_get_shared_nodes_3", Name: "test_get_shared_nodes_3",
NamespaceID: namespaceShared3.ID, NamespaceID: namespaceShared3.ID,
Namespace: *namespaceShared3, Namespace: *namespaceShared3,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")}, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")},
AuthKeyID: uint(preAuthKeyShared3.ID), AuthKeyID: uint(preAuthKeyShared3.ID),
@ -200,7 +196,6 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
Name: "test_get_shared_nodes_4", Name: "test_get_shared_nodes_4",
NamespaceID: namespaceShared1.ID, NamespaceID: namespaceShared1.ID,
Namespace: *namespaceShared1, Namespace: *namespaceShared1,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")}, IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")},
AuthKeyID: uint(preAuthKey2Shared1.ID), AuthKeyID: uint(preAuthKey2Shared1.ID),

79
oidc.go
View File

@ -10,20 +10,15 @@ import (
"html/template" "html/template"
"net/http" "net/http"
"strings" "strings"
"time"
"github.com/coreos/go-oidc/v3/oidc" "github.com/coreos/go-oidc/v3/oidc"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/patrickmn/go-cache"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"gorm.io/gorm"
"tailscale.com/types/key" "tailscale.com/types/key"
) )
const ( const (
oidcStateCacheExpiration = time.Minute * 5
oidcStateCacheCleanupInterval = time.Minute * 10
randomByteSize = 16 randomByteSize = 16
) )
@ -61,14 +56,6 @@ func (h *Headscale) initOIDC() error {
} }
} }
// init the state cache if it hasn't been already
if h.oidcStateCache == nil {
h.oidcStateCache = cache.New(
oidcStateCacheExpiration,
oidcStateCacheCleanupInterval,
)
}
return nil return nil
} }
@ -101,7 +88,7 @@ func (h *Headscale) RegisterOIDC(ctx *gin.Context) {
stateStr := hex.EncodeToString(randomBlob)[:32] stateStr := hex.EncodeToString(randomBlob)[:32]
// place the machine key into the state cache, so it can be retrieved later // place the machine key into the state cache, so it can be retrieved later
h.oidcStateCache.Set(stateStr, machineKeyStr, oidcStateCacheExpiration) h.registrationCache.Set(stateStr, machineKeyStr, registerCacheExpiration)
authURL := h.oauth2Config.AuthCodeURL(stateStr) authURL := h.oauth2Config.AuthCodeURL(stateStr)
log.Debug().Msgf("Redirecting to %s for authentication", authURL) log.Debug().Msgf("Redirecting to %s for authentication", authURL)
@ -125,7 +112,6 @@ var oidcCallbackTemplate = template.Must(
</html>`), </html>`),
) )
// TODO: Why is the entire machine registration logic duplicated here?
// OIDCCallback handles the callback from the OIDC endpoint // OIDCCallback handles the callback from the OIDC endpoint
// Retrieves the mkey from the state cache and adds the machine to the users email namespace // Retrieves the mkey from the state cache and adds the machine to the users email namespace
// TODO: A confirmation page for new machines should be added to avoid phishing vulnerabilities // TODO: A confirmation page for new machines should be added to avoid phishing vulnerabilities
@ -197,7 +183,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
} }
// retrieve machinekey from state cache // retrieve machinekey from state cache
machineKeyIf, machineKeyFound := h.oidcStateCache.Get(state) machineKeyIf, machineKeyFound := h.registrationCache.Get(state)
if !machineKeyFound { if !machineKeyFound {
log.Error(). log.Error().
@ -207,10 +193,12 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
return return
} }
machineKeyStr, machineKeyOK := machineKeyIf.(string) machineKeyFromCache, machineKeyOK := machineKeyIf.(string)
var machineKey key.MachinePublic var machineKey key.MachinePublic
err = machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr))) err = machineKey.UnmarshalText(
[]byte(MachinePublicKeyEnsurePrefix(machineKeyFromCache)),
)
if err != nil { if err != nil {
log.Error(). log.Error().
Msg("could not parse machine public key") Msg("could not parse machine public key")
@ -229,33 +217,19 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
return return
} }
// TODO(kradalby): Currently, if it fails to find a requested expiry, non will be set // retrieve machine information if it exist
requestedTime := time.Time{} // The error is not important, because if it does not
if requestedTimeIf, found := h.requestedExpiryCache.Get(machineKey.String()); found { // exist, then this is a new machine and we will move
if reqTime, ok := requestedTimeIf.(time.Time); ok { // on to registration.
requestedTime = reqTime machine, _ := h.GetMachineByMachineKey(machineKey)
}
}
// retrieve machine information if machine != nil {
machine, err := h.GetMachineByMachineKey(machineKey)
if err != nil {
log.Error().Msg("machine key not found in database")
ctx.String(
http.StatusInternalServerError,
"could not get machine info from database",
)
return
}
if machine.isRegistered() {
log.Trace(). log.Trace().
Caller(). Caller().
Str("machine", machine.Name). Str("machine", machine.Name).
Msg("machine already registered, reauthenticating") Msg("machine already registered, reauthenticating")
h.RefreshMachine(machine, requestedTime) h.RefreshMachine(machine, *machine.Expiry)
var content bytes.Buffer var content bytes.Buffer
if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{ if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{
@ -279,8 +253,6 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
return return
} }
now := time.Now().UTC()
namespaceName, err := NormalizeNamespaceName( namespaceName, err := NormalizeNamespaceName(
claims.Email, claims.Email,
h.cfg.OIDC.StripEmaildomain, h.cfg.OIDC.StripEmaildomain,
@ -294,12 +266,12 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
return return
} }
// register the machine if it's new // register the machine if it's new
if !machine.Registered {
log.Debug().Msg("Registering new machine after successful callback") log.Debug().Msg("Registering new machine after successful callback")
namespace, err := h.GetNamespace(namespaceName) namespace, err := h.GetNamespace(namespaceName)
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, errNamespaceNotFound) {
namespace, err = h.CreateNamespace(namespaceName) namespace, err = h.CreateNamespace(namespaceName)
if err != nil { if err != nil {
@ -328,29 +300,26 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
return return
} }
ips, err := h.getAvailableIPs() machineKeyStr := MachinePublicKeyStripPrefix(machineKey)
_, err = h.RegisterMachineFromAuthCallback(
machineKeyStr,
namespace.Name,
RegisterMethodOIDC,
)
if err != nil { if err != nil {
log.Error(). log.Error().
Caller(). Caller().
Err(err). Err(err).
Msg("could not get an IP from the pool") Msg("could not register machine")
ctx.String( ctx.String(
http.StatusInternalServerError, http.StatusInternalServerError,
"could not get an IP from the pool", "could not register machine",
) )
return return
} }
machine.IPAddresses = ips
machine.NamespaceID = namespace.ID
machine.Registered = true
machine.RegisterMethod = RegisterMethodOIDC
machine.LastSuccessfulUpdate = &now
machine.Expiry = &requestedTime
h.db.Save(&machine)
}
var content bytes.Buffer var content bytes.Buffer
if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{ if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{
User: claims.Email, User: claims.Email,

21
poll.go
View File

@ -2,7 +2,6 @@ package headscale
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -11,7 +10,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"gorm.io/datatypes"
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/key" "tailscale.com/types/key"
@ -85,12 +83,8 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
Str("machine", machine.Name). Str("machine", machine.Name).
Msg("Found machine in database") Msg("Found machine in database")
hostinfo, err := json.Marshal(req.Hostinfo)
if err != nil {
return
}
machine.Name = req.Hostinfo.Hostname machine.Name = req.Hostinfo.Hostname
machine.HostInfo = datatypes.JSON(hostinfo) machine.HostInfo = HostInfo(*req.Hostinfo)
machine.DiscoKey = DiscoPublicKeyStripPrefix(req.DiscoKey) machine.DiscoKey = DiscoPublicKeyStripPrefix(req.DiscoKey)
now := time.Now().UTC() now := time.Now().UTC()
@ -114,18 +108,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
// The intended use is for clients to discover the DERP map at start-up // The intended use is for clients to discover the DERP map at start-up
// before their first real endpoint update. // before their first real endpoint update.
if !req.ReadOnly { if !req.ReadOnly {
endpoints, err := json.Marshal(req.Endpoints) machine.Endpoints = req.Endpoints
if err != nil {
log.Error().
Caller().
Str("func", "PollNetMapHandler").
Err(err).
Msg("Failed to mashal requested endpoints for the client")
ctx.String(http.StatusInternalServerError, ":(")
return
}
machine.Endpoints = datatypes.JSON(endpoints)
machine.LastSeen = &now machine.LastSeen = &now
} }
h.db.Updates(machine) h.db.Updates(machine)

View File

@ -113,6 +113,12 @@ func (h *Headscale) ExpirePreAuthKey(k *PreAuthKey) error {
return nil return nil
} }
// UsePreAuthKey marks a PreAuthKey as used.
func (h *Headscale) UsePreAuthKey(k *PreAuthKey) {
k.Used = true
h.db.Save(k)
}
// checkKeyValidity does the heavy lifting for validation of the PreAuthKey coming from a node // checkKeyValidity does the heavy lifting for validation of the PreAuthKey coming from a node
// If returns no error and a PreAuthKey, it can be used. // If returns no error and a PreAuthKey, it can be used.
func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) { func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) {

View File

@ -80,7 +80,6 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testest", Name: "testest",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
} }
@ -105,7 +104,6 @@ func (*Suite) TestReusableBeingUsedKey(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testest", Name: "testest",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
} }
@ -143,7 +141,6 @@ func (*Suite) TestEphemeralKey(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testest", Name: "testest",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
LastSeen: &now, LastSeen: &now,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),

View File

@ -22,16 +22,16 @@ message Machine {
string name = 6; string name = 6;
Namespace namespace = 7; Namespace namespace = 7;
bool registered = 8;
RegisterMethod register_method = 9;
google.protobuf.Timestamp last_seen = 10; google.protobuf.Timestamp last_seen = 8;
google.protobuf.Timestamp last_successful_update = 11; google.protobuf.Timestamp last_successful_update = 9;
google.protobuf.Timestamp expiry = 12; google.protobuf.Timestamp expiry = 10;
PreAuthKey pre_auth_key = 13; PreAuthKey pre_auth_key = 11;
google.protobuf.Timestamp created_at = 14; google.protobuf.Timestamp created_at = 12;
RegisterMethod register_method = 13;
// google.protobuf.Timestamp updated_at = 14; // google.protobuf.Timestamp updated_at = 14;
// google.protobuf.Timestamp deleted_at = 15; // google.protobuf.Timestamp deleted_at = 15;

View File

@ -1,9 +1,6 @@
package headscale package headscale
import ( import (
"encoding/json"
"gorm.io/datatypes"
"inet.af/netaddr" "inet.af/netaddr"
) )
@ -23,12 +20,7 @@ func (h *Headscale) GetAdvertisedNodeRoutes(
return nil, err return nil, err
} }
hostInfo, err := machine.GetHostInfo() return &machine.HostInfo.RoutableIPs, nil
if err != nil {
return nil, err
}
return &hostInfo.RoutableIPs, nil
} }
// Deprecated: use machine function instead // Deprecated: use machine function instead
@ -43,27 +35,7 @@ func (h *Headscale) GetEnabledNodeRoutes(
return nil, err return nil, err
} }
data, err := machine.EnabledRoutes.MarshalJSON() return machine.EnabledRoutes, nil
if err != nil {
return nil, err
}
routesStr := []string{}
err = json.Unmarshal(data, &routesStr)
if err != nil {
return nil, err
}
routes := make([]netaddr.IPPrefix, len(routesStr))
for index, routeStr := range routesStr {
route, err := netaddr.ParseIPPrefix(routeStr)
if err != nil {
return nil, err
}
routes[index] = route
}
return routes, nil
} }
// Deprecated: use machine function instead // Deprecated: use machine function instead
@ -135,12 +107,7 @@ func (h *Headscale) EnableNodeRoute(
return errRouteIsNotAvailable return errRouteIsNotAvailable
} }
routes, err := json.Marshal(enabledRoutes) machine.EnabledRoutes = enabledRoutes
if err != nil {
return err
}
machine.EnabledRoutes = datatypes.JSON(routes)
h.db.Save(&machine) h.db.Save(&machine)
return nil return nil

View File

@ -1,10 +1,7 @@
package headscale package headscale
import ( import (
"encoding/json"
"gopkg.in/check.v1" "gopkg.in/check.v1"
"gorm.io/datatypes"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
) )
@ -25,8 +22,6 @@ func (s *Suite) TestGetRoutes(c *check.C) {
hostInfo := tailcfg.Hostinfo{ hostInfo := tailcfg.Hostinfo{
RoutableIPs: []netaddr.IPPrefix{route}, RoutableIPs: []netaddr.IPPrefix{route},
} }
hostinfo, err := json.Marshal(hostInfo)
c.Assert(err, check.IsNil)
machine := Machine{ machine := Machine{
ID: 0, ID: 0,
@ -35,10 +30,9 @@ func (s *Suite) TestGetRoutes(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "test_get_route_machine", Name: "test_get_route_machine",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
HostInfo: datatypes.JSON(hostinfo), HostInfo: HostInfo(hostInfo),
} }
app.db.Save(&machine) app.db.Save(&machine)
@ -79,8 +73,6 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) {
hostInfo := tailcfg.Hostinfo{ hostInfo := tailcfg.Hostinfo{
RoutableIPs: []netaddr.IPPrefix{route, route2}, RoutableIPs: []netaddr.IPPrefix{route, route2},
} }
hostinfo, err := json.Marshal(hostInfo)
c.Assert(err, check.IsNil)
machine := Machine{ machine := Machine{
ID: 0, ID: 0,
@ -89,10 +81,9 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "test_enable_route_machine", Name: "test_enable_route_machine",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
HostInfo: datatypes.JSON(hostinfo), HostInfo: HostInfo(hostInfo),
} }
app.db.Save(&machine) app.db.Save(&machine)

View File

@ -0,0 +1,10 @@
---
Hosts:
host-1: 100.100.100.100/32
subnet-1: 100.100.101.100/24
ACLs:
- Action: accept
Users:
- "*"
Ports:
- host-1:*

View File

@ -196,10 +196,6 @@ func (h *Headscale) getUsedIPs() (*netaddr.IPSet, error) {
var addressesSlices []string var addressesSlices []string
h.db.Model(&Machine{}).Pluck("ip_addresses", &addressesSlices) h.db.Model(&Machine{}).Pluck("ip_addresses", &addressesSlices)
log.Trace().
Strs("addresses", addressesSlices).
Msg("Got allocated ip addresses from databases")
var ips netaddr.IPSetBuilder var ips netaddr.IPSetBuilder
for _, slice := range addressesSlices { for _, slice := range addressesSlices {
var machineAddresses MachineAddresses var machineAddresses MachineAddresses
@ -216,10 +212,6 @@ func (h *Headscale) getUsedIPs() (*netaddr.IPSet, error) {
} }
} }
log.Trace().
Interface("addresses", ips).
Msg("Parsed ip addresses that has been allocated from databases")
ipSet, err := ips.IPSet() ipSet, err := ips.IPSet()
if err != nil { if err != nil {
return &netaddr.IPSet{}, fmt.Errorf( return &netaddr.IPSet{}, fmt.Errorf(

View File

@ -36,7 +36,6 @@ func (s *Suite) TestGetUsedIps(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testmachine", Name: "testmachine",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
IPAddresses: ips, IPAddresses: ips,
@ -85,7 +84,6 @@ func (s *Suite) TestGetMultiIp(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testmachine", Name: "testmachine",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
IPAddresses: ips, IPAddresses: ips,
@ -176,7 +174,6 @@ func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) {
DiscoKey: "faa", DiscoKey: "faa",
Name: "testmachine", Name: "testmachine",
NamespaceID: namespace.ID, NamespaceID: namespace.ID,
Registered: true,
RegisterMethod: RegisterMethodAuthKey, RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID), AuthKeyID: uint(pak.ID),
} }