Compare commits

..

61 Commits

Author SHA1 Message Date
Juan Font
e2b5638ca0 Merge pull request #187 from juanfont/fix-arm64
Use CGO_ENABLED=1 when building arm64
2021-10-21 00:00:47 +02:00
Juan Font Alonso
5159b6d085 Trying to fix arm64 2021-10-20 23:10:59 +02:00
Juan Font
03d97c3872 Merge pull request #183 from juanfont/split-dns
Add support for Split DNS (Restricted Nameservers)
2021-10-20 10:53:52 +02:00
Juan Font
41c5a0ddf5 Apply suggestions from code review
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2021-10-20 09:35:56 +02:00
Juan Font
19165a40d2 Merge branch 'main' into split-dns 2021-10-20 00:19:34 +02:00
Juan Font Alonso
18b00b5d8d Add support for Split DNS (implements #179) 2021-10-19 20:51:43 +02:00
Juan Font
d2a162e3ee Merge pull request #178 from cure/refactor-sharing-tests
Apply some DRY to the sharing tests.
2021-10-19 18:45:37 +02:00
Ward Vandewege
d35f5fe498 Apply some DRY to the sharing tests. 2021-10-18 17:52:38 -04:00
Juan Font
9e1253ada1 Merge pull request #177 from cure/cli-unshare-node
Cli unshare node
2021-10-18 12:51:53 +02:00
Juan Font
244e79f575 Merge branch 'main' into cli-unshare-node 2021-10-18 12:34:13 +02:00
Juan Font
b4e6a32b4b Merge pull request #176 from cure/fix-sharing-check
Bugfix: the check to see if a node was already shared into a namespace
2021-10-18 12:34:04 +02:00
Juan Font
023cd8f4cd Merge branch 'main' into fix-sharing-check 2021-10-18 12:20:43 +02:00
Juan Font
10d24e64cd Merge pull request #174 from juanfont/fix-magic-dns-base-domain
Fix MagicDNS base domain
2021-10-18 12:16:07 +02:00
Juan Font Alonso
37e191a75d Solved merge 2021-10-17 23:59:44 +02:00
Juan Font Alonso
01a5fe3c51 Added tests, solved some bugs, and code reorg 2021-10-17 23:58:09 +02:00
Ward Vandewege
9e3339b4f1 Add cli support for unsharing a node from a namespace. 2021-10-17 16:29:46 -04:00
Ward Vandewege
b06e34f144 Bugfix: the check to see if a node was already shared into a namespace
was incorrect.
2021-10-17 15:53:39 -04:00
Kristoffer Dalby
ddf042cab1 Merge branch 'main' into fix-magic-dns-base-domain 2021-10-17 13:23:21 +01:00
Juan Font Alonso
687e8d12be Do not use the full application for getMapResponseDNSConfig 2021-10-17 12:10:03 +02:00
Juan Font Alonso
01f755ecf9 Send UserProfile info for the peers' namespaces 2021-10-17 12:07:01 +02:00
Juan Font Alonso
8094e6fdef Preload the Namespace from SharedMachines 2021-10-17 11:59:08 +02:00
Juan Font Alonso
061efa1822 Do not include BaseDomain as full route in DNSConfig + code reorg 2021-10-17 11:57:53 +02:00
Juan Font
9a7472218e Merge pull request #172 from cure/rename-namespaces
Rename namespaces
2021-10-17 00:30:36 +02:00
Ward Vandewege
7dcf4a5147 Add support for renaming namespaces. 2021-10-16 11:20:06 -04:00
Ward Vandewege
306a80cf57 Bugfix: when namespace destruction causes a database error, return the
error, not nil.
2021-10-16 11:14:37 -04:00
Juan Font
a9a1a8fb3c Merge pull request #171 from juanfont/force-flag
Added --force flag on node delete
2021-10-16 14:21:35 +02:00
Juan Font
85ddc0db33 Merge branch 'main' into force-flag 2021-10-16 14:09:59 +02:00
Juan Font
fddc2aa8fa Merge pull request #150 from juanfont/fix-shared-nodes
Fix shared nodes
2021-10-16 14:09:23 +02:00
Juan Font Alonso
be3a379d10 Added --force flag on node delete (fixes #164) 2021-10-16 12:30:52 +02:00
Juan Font Alonso
d0daff180e Added TODO in waiting 2021-10-16 11:36:16 +02:00
Juan Font Alonso
be36480a64 Reverted back values in integration tests 2021-10-16 11:06:33 +02:00
Juan Font
9f52a64a6a Merge branch 'main' into fix-shared-nodes 2021-10-16 10:22:42 +02:00
Juan Font
52511af8e4 Merge pull request #169 from juanfont/arm64-binaries
Generate arm64 binaries in goreleaser
2021-10-16 10:22:24 +02:00
Juan Font
ddb6bd795c Merge branch 'main' into arm64-binaries 2021-10-16 10:10:54 +02:00
Juan Font
271660a284 Merge pull request #167 from juanfont/authkey-namespace-preload
Preload AuthKey Namespace on list nodes
2021-10-16 10:10:42 +02:00
Juan Font
0b0f7db534 Merge branch 'main' into authkey-namespace-preload 2021-10-16 09:59:34 +02:00
Juan Font
5a7b377f6f Merge pull request #166 from juanfont/complete-expire-command
Improve help message to expire key
2021-10-16 09:59:15 +02:00
Juan Font
654d2b9910 Merge branch 'main' into complete-expire-command 2021-10-16 09:41:14 +02:00
Juan Font
829a8c4381 Merge pull request #165 from juanfont/expire-json
Show JSON on error when expiring key
2021-10-16 09:40:48 +02:00
Juan Font Alonso
5807562b56 Add arm64 binaries 2021-10-15 17:00:04 +02:00
Juan Font Alonso
985c6e7cc9 Preload AuthKey Namespace on list nodes (fixes #163) 2021-10-15 00:04:04 +02:00
Juan Font Alonso
0d13e16fed Improve help message to expire key (fixes #161) 2021-10-14 23:58:15 +02:00
Juan Font Alonso
91d135e069 Show JSON when error on expire key (fixes #162) 2021-10-14 23:54:07 +02:00
Juan Font Alonso
3e1e07e8c1 Fixed integration tests for shared nodes 2021-10-14 22:37:44 +02:00
Juan Font
6c4c761408 Merge branch 'main' into fix-shared-nodes 2021-10-13 23:54:30 +02:00
Juan Font
abfb1791f1 Merge pull request #159 from juanfont/better-pak-management
Add field AlreadyUsed to AuthKeys
2021-10-13 23:45:10 +02:00
Juan Font Alonso
7ce8c4c649 Fixed merge 2021-10-13 23:28:47 +02:00
Juan Font Alonso
2ddca366f2 Merge branch 'main' of https://github.com/juanfont/headscale into main 2021-10-13 23:23:12 +02:00
Juan Font Alonso
9a6ac6e3e6 Reword errSingleUseAuthKeyHasBeenUsed 2021-10-13 23:23:07 +02:00
Juan Font
cc3e8705bd Merge branch 'main' into better-pak-management 2021-10-13 23:04:00 +02:00
Juan Font
809a5b84e7 Merge pull request #156 from juanfont/disable-version-check-on-json
Disable version checker when using JSON output
2021-10-13 23:00:51 +02:00
Juan Font
06ae2a6c50 Merge branch 'main' into better-pak-management 2021-10-13 23:00:38 +02:00
Juan Font
93517aa6f8 Apply suggestions from code review
Renamed AlreadyUsed to Used

Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2021-10-13 22:51:55 +02:00
Juan Font
5f0f3705c0 Merge branch 'main' into disable-version-check-on-json 2021-10-13 22:44:18 +02:00
Juan Font
70ae18c3a8 Merge pull request #155 from juanfont/fix-json-delete-node
Add JSON output when deleting node
2021-10-13 22:44:00 +02:00
Juan Font Alonso
6aa763a1ae Expanded unit tests to better cover sharing nodes 2021-10-13 20:56:32 +02:00
Juan Font Alonso
ebfb8c8c5e Fix tests, as IDs of Machines where wrongly starting in 0 2021-10-13 20:48:50 +02:00
Juan Font Alonso
30788e1a70 Add AlreadyUsed field to Auth Keys (fixes #157 and #158) 2021-10-13 18:13:26 +02:00
Juan Font Alonso
27947c6746 This commit disables the version checker when JSON output (#153) 2021-10-13 00:18:55 +02:00
Juan Font Alonso
6924b7bf4c Output json when deleting node (fixes #152) 2021-10-12 23:48:08 +02:00
Juan Font Alonso
fa8cd96108 Get peers from namespaces where shared nodes are shared to
This is rather shameful. Shared nodes should have never worked without this.
2021-10-12 17:20:14 +02:00
20 changed files with 870 additions and 432 deletions

View File

@@ -20,6 +20,7 @@ builds:
- -mod=readonly - -mod=readonly
ldflags: ldflags:
- -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}} - -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}}
- id: linux-armhf - id: linux-armhf
main: ./cmd/headscale/headscale.go main: ./cmd/headscale/headscale.go
mod_timestamp: '{{ .CommitTimestamp }}' mod_timestamp: '{{ .CommitTimestamp }}'
@@ -49,9 +50,19 @@ builds:
- linux - linux
goarch: goarch:
- amd64 - amd64
goarm: main: ./cmd/headscale/headscale.go
- 6 mod_timestamp: '{{ .CommitTimestamp }}'
- 7 ldflags:
- -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}}
- id: linux-arm64
goos:
- linux
goarch:
- arm64
env:
- CGO_ENABLED=1
- CC=aarch64-linux-gnu-gcc-9
main: ./cmd/headscale/headscale.go main: ./cmd/headscale/headscale.go
mod_timestamp: '{{ .CommitTimestamp }}' mod_timestamp: '{{ .CommitTimestamp }}'
ldflags: ldflags:
@@ -63,6 +74,7 @@ archives:
- darwin-amd64 - darwin-amd64
- linux-armhf - linux-armhf
- linux-amd64 - linux-amd64
- linux-arm64
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
format: binary format: binary

28
api.go
View File

@@ -243,11 +243,7 @@ func (h *Headscale) getMapResponse(mKey wgkey.Key, req tailcfg.MapRequest, m *Ma
return nil, err return nil, err
} }
profile := tailcfg.UserProfile{ profiles := getMapResponseUserProfiles(*m, peers)
ID: tailcfg.UserID(m.NamespaceID),
LoginName: m.Namespace.Name,
DisplayName: m.Namespace.Name,
}
nodePeers, err := peers.toNodes(h.cfg.BaseDomain, h.cfg.DNSConfig, true) nodePeers, err := peers.toNodes(h.cfg.BaseDomain, h.cfg.DNSConfig, true)
if err != nil { if err != nil {
@@ -258,13 +254,13 @@ func (h *Headscale) getMapResponse(mKey wgkey.Key, req tailcfg.MapRequest, m *Ma
return nil, err return nil, err
} }
var dnsConfig *tailcfg.DNSConfig dnsConfig, err := getMapResponseDNSConfig(h.cfg.DNSConfig, h.cfg.BaseDomain, *m, peers)
if h.cfg.DNSConfig != nil && h.cfg.DNSConfig.Proxied { // if MagicDNS is enabled if err != nil {
// Only inject the Search Domain of the current namespace - shared nodes should use their full FQDN log.Error().
dnsConfig = h.cfg.DNSConfig.Clone() Str("func", "getMapResponse").
dnsConfig.Domains = append(dnsConfig.Domains, fmt.Sprintf("%s.%s", m.Namespace.Name, h.cfg.BaseDomain)) Err(err).
} else { Msg("Failed generate the DNSConfig")
dnsConfig = h.cfg.DNSConfig return nil, err
} }
resp := tailcfg.MapResponse{ resp := tailcfg.MapResponse{
@@ -275,10 +271,9 @@ func (h *Headscale) getMapResponse(mKey wgkey.Key, req tailcfg.MapRequest, m *Ma
Domain: h.cfg.BaseDomain, Domain: h.cfg.BaseDomain,
PacketFilter: *h.aclRules, PacketFilter: *h.aclRules,
DERPMap: h.cfg.DerpMap, DERPMap: h.cfg.DerpMap,
UserProfiles: profiles,
// TODO(juanfont): We should send the profiles of all the peers (this own namespace + those from the shared peers)
UserProfiles: []tailcfg.UserProfile{profile},
} }
log.Trace(). log.Trace().
Str("func", "getMapResponse"). Str("func", "getMapResponse").
Str("machine", req.Hostinfo.Hostname). Str("machine", req.Hostinfo.Hostname).
@@ -395,6 +390,9 @@ func (h *Headscale) handleAuthKey(c *gin.Context, db *gorm.DB, idKey wgkey.Key,
m.RegisterMethod = "authKey" m.RegisterMethod = "authKey"
db.Save(&m) db.Save(&m)
pak.Used = true
db.Save(&pak)
resp.MachineAuthorized = true resp.MachineAuthorized = true
resp.User = *pak.Namespace.toUser() resp.User = *pak.Namespace.toUser()
respBody, err := encode(resp, &idKey, h.privateKey) respBody, err := encode(resp, &idKey, h.privateKey)

5
app.go
View File

@@ -113,7 +113,10 @@ func NewHeadscale(cfg Config) (*Headscale, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
h.cfg.DNSConfig.Routes = make(map[string][]dnstype.Resolver) // we might have routes already from Split DNS
if h.cfg.DNSConfig.Routes == nil {
h.cfg.DNSConfig.Routes = make(map[string][]dnstype.Resolver)
}
for _, d := range magicDNSDomains { for _, d := range magicDNSDomains {
h.cfg.DNSConfig.Routes[d.WithoutTrailingDot()] = nil h.cfg.DNSConfig.Routes[d.WithoutTrailingDot()] = nil
} }

View File

@@ -15,6 +15,7 @@ func init() {
namespaceCmd.AddCommand(createNamespaceCmd) namespaceCmd.AddCommand(createNamespaceCmd)
namespaceCmd.AddCommand(listNamespacesCmd) namespaceCmd.AddCommand(listNamespacesCmd)
namespaceCmd.AddCommand(destroyNamespaceCmd) namespaceCmd.AddCommand(destroyNamespaceCmd)
namespaceCmd.AddCommand(renameNamespaceCmd)
} }
var namespaceCmd = &cobra.Command{ var namespaceCmd = &cobra.Command{
@@ -107,3 +108,31 @@ var listNamespacesCmd = &cobra.Command{
} }
}, },
} }
var renameNamespaceCmd = &cobra.Command{
Use: "rename OLD_NAME NEW_NAME",
Short: "Renames a namespace",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 2 {
return fmt.Errorf("Missing parameters")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
o, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
err = h.RenameNamespace(args[0], args[1])
if strings.HasPrefix(o, "json") {
JsonOutput(map[string]string{"Result": "Namespace renamed"}, err, o)
return
}
if err != nil {
fmt.Printf("Error renaming namespace: %s\n", err)
return
}
fmt.Printf("Namespace renamed\n")
},
}

View File

@@ -26,6 +26,7 @@ func init() {
nodeCmd.AddCommand(registerNodeCmd) nodeCmd.AddCommand(registerNodeCmd)
nodeCmd.AddCommand(deleteNodeCmd) nodeCmd.AddCommand(deleteNodeCmd)
nodeCmd.AddCommand(shareMachineCmd) nodeCmd.AddCommand(shareMachineCmd)
nodeCmd.AddCommand(unshareMachineCmd)
} }
var nodeCmd = &cobra.Command{ var nodeCmd = &cobra.Command{
@@ -129,6 +130,7 @@ var deleteNodeCmd = &cobra.Command{
return nil return nil
}, },
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp() h, err := getHeadscaleApp()
if err != nil { if err != nil {
log.Fatalf("Error initializing: %s", err) log.Fatalf("Error initializing: %s", err)
@@ -143,21 +145,32 @@ var deleteNodeCmd = &cobra.Command{
} }
confirm := false confirm := false
prompt := &survey.Confirm{ force, _ := cmd.Flags().GetBool("force")
Message: fmt.Sprintf("Do you want to remove the node %s?", m.Name), if !force {
} prompt := &survey.Confirm{
err = survey.AskOne(prompt, &confirm) Message: fmt.Sprintf("Do you want to remove the node %s?", m.Name),
if err != nil { }
return err = survey.AskOne(prompt, &confirm)
if err != nil {
return
}
} }
if confirm { if confirm || force {
err = h.DeleteMachine(m) err = h.DeleteMachine(m)
if strings.HasPrefix(output, "json") {
JsonOutput(map[string]string{"Result": "Node deleted"}, err, output)
return
}
if err != nil { if err != nil {
log.Fatalf("Error deleting node: %s", err) log.Fatalf("Error deleting node: %s", err)
} }
fmt.Printf("Node deleted\n") fmt.Printf("Node deleted\n")
} else { } else {
if strings.HasPrefix(output, "json") {
JsonOutput(map[string]string{"Result": "Node not deleted"}, err, output)
return
}
fmt.Printf("Node not deleted\n") fmt.Printf("Node not deleted\n")
} }
}, },
@@ -217,6 +230,55 @@ var shareMachineCmd = &cobra.Command{
}, },
} }
var unshareMachineCmd = &cobra.Command{
Use: "unshare ID",
Short: "Unshares a node from the specified namespace",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("missing parameters")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
namespace, err := cmd.Flags().GetString("namespace")
if err != nil {
log.Fatalf("Error getting namespace: %s", err)
}
output, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
n, err := h.GetNamespace(namespace)
if err != nil {
log.Fatalf("Error fetching namespace: %s", err)
}
id, err := strconv.Atoi(args[0])
if err != nil {
log.Fatalf("Error converting ID to integer: %s", err)
}
machine, err := h.GetMachineByID(uint64(id))
if err != nil {
log.Fatalf("Error getting node: %s", err)
}
err = h.RemoveSharedMachineFromNamespace(machine, n)
if strings.HasPrefix(output, "json") {
JsonOutput(map[string]string{"Result": "Node unshared"}, err, output)
return
}
if err != nil {
fmt.Printf("Error unsharing node: %s\n", err)
return
}
fmt.Println("Node unshared!")
},
}
func nodesToPtables(currentNamespace headscale.Namespace, machines []headscale.Machine) (pterm.TableData, error) { func nodesToPtables(currentNamespace headscale.Namespace, machines []headscale.Machine) (pterm.TableData, error) {
d := pterm.TableData{{"ID", "Name", "NodeKey", "Namespace", "IP address", "Ephemeral", "Last seen", "Online"}} d := pterm.TableData{{"ID", "Name", "NodeKey", "Namespace", "IP address", "Ephemeral", "Last seen", "Online"}}

View File

@@ -57,7 +57,7 @@ var listPreAuthKeys = &cobra.Command{
return return
} }
d := pterm.TableData{{"ID", "Key", "Reusable", "Ephemeral", "Expiration", "Created"}} d := pterm.TableData{{"ID", "Key", "Reusable", "Ephemeral", "Used", "Expiration", "Created"}}
for _, k := range *keys { for _, k := range *keys {
expiration := "-" expiration := "-"
if k.Expiration != nil { if k.Expiration != nil {
@@ -76,6 +76,7 @@ var listPreAuthKeys = &cobra.Command{
k.Key, k.Key,
reusable, reusable,
strconv.FormatBool(k.Ephemeral), strconv.FormatBool(k.Ephemeral),
fmt.Sprintf("%v", k.Used),
expiration, expiration,
k.CreatedAt.Format("2006-01-02 15:04:05"), k.CreatedAt.Format("2006-01-02 15:04:05"),
}) })
@@ -130,7 +131,7 @@ var createPreAuthKeyCmd = &cobra.Command{
} }
var expirePreAuthKeyCmd = &cobra.Command{ var expirePreAuthKeyCmd = &cobra.Command{
Use: "expire", Use: "expire KEY",
Short: "Expire a preauthkey", Short: "Expire a preauthkey",
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
@@ -152,6 +153,10 @@ var expirePreAuthKeyCmd = &cobra.Command{
k, err := h.GetPreAuthKey(n, args[0]) k, err := h.GetPreAuthKey(n, args[0])
if err != nil { if err != nil {
if strings.HasPrefix(o, "json") {
JsonOutput(k, err, o)
return
}
log.Fatalf("Error getting the key: %s", err) log.Fatalf("Error getting the key: %s", err)
} }

View File

@@ -9,6 +9,7 @@ import (
func init() { func init() {
rootCmd.PersistentFlags().StringP("output", "o", "", "Output format. Empty for human-readable, 'json' or 'json-line'") rootCmd.PersistentFlags().StringP("output", "o", "", "Output format. Empty for human-readable, 'json' or 'json-line'")
rootCmd.PersistentFlags().Bool("force", false, "Disable prompts and forces the execution")
} }
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{

View File

@@ -104,6 +104,33 @@ func GetDNSConfig() (*tailcfg.DNSConfig, string) {
dnsConfig.Nameservers = nameservers dnsConfig.Nameservers = nameservers
dnsConfig.Resolvers = resolvers dnsConfig.Resolvers = resolvers
} }
if viper.IsSet("dns_config.restricted_nameservers") {
if len(dnsConfig.Nameservers) > 0 {
dnsConfig.Routes = make(map[string][]dnstype.Resolver)
restrictedDNS := viper.GetStringMapStringSlice("dns_config.restricted_nameservers")
for domain, restrictedNameservers := range restrictedDNS {
restrictedResolvers := make([]dnstype.Resolver, len(restrictedNameservers))
for index, nameserverStr := range restrictedNameservers {
nameserver, err := netaddr.ParseIP(nameserverStr)
if err != nil {
log.Error().
Str("func", "getDNSConfig").
Err(err).
Msgf("Could not parse restricted nameserver IP: %s", nameserverStr)
}
restrictedResolvers[index] = dnstype.Resolver{
Addr: nameserver.String(),
}
}
dnsConfig.Routes[domain] = restrictedResolvers
}
} else {
log.Warn().
Msg("Warning: dns_config.restricted_nameservers is set, but no nameservers are configured. Ignoring restricted_nameservers.")
}
}
if viper.IsSet("dns_config.domains") { if viper.IsSet("dns_config.domains") {
dnsConfig.Domains = viper.GetStringSlice("dns_config.domains") dnsConfig.Domains = viper.GetStringSlice("dns_config.domains")
} }
@@ -262,3 +289,12 @@ func JsonOutput(result interface{}, errResult error, outputFormat string) {
} }
fmt.Println(string(j)) fmt.Println(string(j))
} }
func HasJsonOutputFlag() bool {
for _, arg := range os.Args {
if arg == "json" || arg == "json-line" {
return true
}
}
return false
}

View File

@@ -62,7 +62,8 @@ func main() {
zerolog.SetGlobalLevel(zerolog.DebugLevel) zerolog.SetGlobalLevel(zerolog.DebugLevel)
} }
if !viper.GetBool("disable_check_updates") { jsonOutput := cli.HasJsonOutputFlag()
if !viper.GetBool("disable_check_updates") && !jsonOutput {
if (runtime.GOOS == "linux" || runtime.GOOS == "darwin") && cli.Version != "dev" { if (runtime.GOOS == "linux" || runtime.GOOS == "darwin") && cli.Version != "dev" {
githubTag := &latest.GithubTag{ githubTag := &latest.GithubTag{
Owner: "juanfont", Owner: "juanfont",

31
dns.go
View File

@@ -4,7 +4,9 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/fatih/set"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/util/dnsname" "tailscale.com/util/dnsname"
) )
@@ -29,15 +31,10 @@ import (
// From the netmask we can find out the wildcard bits (the bits that are not set in the netmask). // From the netmask we can find out the wildcard bits (the bits that are not set in the netmask).
// This allows us to then calculate the subnets included in the subsequent class block and generate the entries. // This allows us to then calculate the subnets included in the subsequent class block and generate the entries.
func generateMagicDNSRootDomains(ipPrefix netaddr.IPPrefix, baseDomain string) ([]dnsname.FQDN, error) { func generateMagicDNSRootDomains(ipPrefix netaddr.IPPrefix, baseDomain string) ([]dnsname.FQDN, error) {
base, err := dnsname.ToFQDN(baseDomain)
if err != nil {
return nil, err
}
// TODO(juanfont): we are not handing out IPv6 addresses yet // TODO(juanfont): we are not handing out IPv6 addresses yet
// and in fact this is Tailscale.com's range (note the fd7a:115c:a1e0: range in the fc00::/7 network) // and in fact this is Tailscale.com's range (note the fd7a:115c:a1e0: range in the fc00::/7 network)
ipv6base := dnsname.FQDN("0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa.") ipv6base := dnsname.FQDN("0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa.")
fqdns := []dnsname.FQDN{base, ipv6base} fqdns := []dnsname.FQDN{ipv6base}
// Conversion to the std lib net.IPnet, a bit easier to operate // Conversion to the std lib net.IPnet, a bit easier to operate
netRange := ipPrefix.IPNet() netRange := ipPrefix.IPNet()
@@ -71,3 +68,25 @@ func generateMagicDNSRootDomains(ipPrefix netaddr.IPPrefix, baseDomain string) (
} }
return fqdns, nil return fqdns, nil
} }
func getMapResponseDNSConfig(dnsConfigOrig *tailcfg.DNSConfig, baseDomain string, m Machine, peers Machines) (*tailcfg.DNSConfig, error) {
var dnsConfig *tailcfg.DNSConfig
if dnsConfigOrig != nil && dnsConfigOrig.Proxied { // if MagicDNS is enabled
// Only inject the Search Domain of the current namespace - shared nodes should use their full FQDN
dnsConfig = dnsConfigOrig.Clone()
dnsConfig.Domains = append(dnsConfig.Domains, fmt.Sprintf("%s.%s", m.Namespace.Name, baseDomain))
namespaceSet := set.New(set.ThreadSafe)
namespaceSet.Add(m.Namespace)
for _, p := range peers {
namespaceSet.Add(p.Namespace)
}
for _, namespace := range namespaceSet.List() {
dnsRoute := fmt.Sprintf("%s.%s", namespace.(Namespace).Name, baseDomain)
dnsConfig.Routes[dnsRoute] = nil
}
} else {
dnsConfig = dnsConfigOrig
}
return dnsConfig, nil
}

View File

@@ -1,13 +1,17 @@
package headscale package headscale
import ( import (
"fmt"
"gopkg.in/check.v1" "gopkg.in/check.v1"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/dnstype"
) )
func (s *Suite) TestMagicDNSRootDomains100(c *check.C) { func (s *Suite) TestMagicDNSRootDomains100(c *check.C) {
prefix := netaddr.MustParseIPPrefix("100.64.0.0/10") prefix := netaddr.MustParseIPPrefix("100.64.0.0/10")
domains, err := generateMagicDNSRootDomains(prefix, "headscale.net") domains, err := generateMagicDNSRootDomains(prefix, "foobar.headscale.net")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
found := false found := false
@@ -61,3 +65,242 @@ func (s *Suite) TestMagicDNSRootDomains172(c *check.C) {
} }
c.Assert(found, check.Equals, true) c.Assert(found, check.Equals, true)
} }
func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
n1, err := h.CreateNamespace("shared1")
c.Assert(err, check.IsNil)
n2, err := h.CreateNamespace("shared2")
c.Assert(err, check.IsNil)
n3, err := h.CreateNamespace("shared3")
c.Assert(err, check.IsNil)
pak1n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak2n2, err := h.CreatePreAuthKey(n2.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak3n3, err := h.CreatePreAuthKey(n3.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak4n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = h.GetMachine(n1.Name, "test_get_shared_nodes_1")
c.Assert(err, check.NotNil)
m1 := &Machine{
ID: 1,
MachineKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
NodeKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
DiscoKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
Name: "test_get_shared_nodes_1",
NamespaceID: n1.ID,
Namespace: *n1,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.1",
AuthKeyID: uint(pak1n1.ID),
}
h.db.Save(m1)
_, err = h.GetMachine(n1.Name, m1.Name)
c.Assert(err, check.IsNil)
m2 := &Machine{
ID: 2,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_2",
NamespaceID: n2.ID,
Namespace: *n2,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.2",
AuthKeyID: uint(pak2n2.ID),
}
h.db.Save(m2)
_, err = h.GetMachine(n2.Name, m2.Name)
c.Assert(err, check.IsNil)
m3 := &Machine{
ID: 3,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_3",
NamespaceID: n3.ID,
Namespace: *n3,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.3",
AuthKeyID: uint(pak3n3.ID),
}
h.db.Save(m3)
_, err = h.GetMachine(n3.Name, m3.Name)
c.Assert(err, check.IsNil)
m4 := &Machine{
ID: 4,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_4",
NamespaceID: n1.ID,
Namespace: *n1,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.4",
AuthKeyID: uint(pak4n1.ID),
}
h.db.Save(m4)
err = h.AddSharedMachineToNamespace(m2, n1)
c.Assert(err, check.IsNil)
baseDomain := "foobar.headscale.net"
dnsConfigOrig := tailcfg.DNSConfig{
Routes: make(map[string][]dnstype.Resolver),
Domains: []string{baseDomain},
Proxied: true,
}
m1peers, err := h.getPeers(m1)
c.Assert(err, check.IsNil)
dnsConfig, err := getMapResponseDNSConfig(&dnsConfigOrig, baseDomain, *m1, m1peers)
c.Assert(err, check.IsNil)
c.Assert(dnsConfig, check.NotNil)
c.Assert(len(dnsConfig.Routes), check.Equals, 2)
routeN1 := fmt.Sprintf("%s.%s", n1.Name, baseDomain)
_, ok := dnsConfig.Routes[routeN1]
c.Assert(ok, check.Equals, true)
routeN2 := fmt.Sprintf("%s.%s", n2.Name, baseDomain)
_, ok = dnsConfig.Routes[routeN2]
c.Assert(ok, check.Equals, true)
routeN3 := fmt.Sprintf("%s.%s", n3.Name, baseDomain)
_, ok = dnsConfig.Routes[routeN3]
c.Assert(ok, check.Equals, false)
}
func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
n1, err := h.CreateNamespace("shared1")
c.Assert(err, check.IsNil)
n2, err := h.CreateNamespace("shared2")
c.Assert(err, check.IsNil)
n3, err := h.CreateNamespace("shared3")
c.Assert(err, check.IsNil)
pak1n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak2n2, err := h.CreatePreAuthKey(n2.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak3n3, err := h.CreatePreAuthKey(n3.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak4n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = h.GetMachine(n1.Name, "test_get_shared_nodes_1")
c.Assert(err, check.NotNil)
m1 := &Machine{
ID: 1,
MachineKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
NodeKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
DiscoKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
Name: "test_get_shared_nodes_1",
NamespaceID: n1.ID,
Namespace: *n1,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.1",
AuthKeyID: uint(pak1n1.ID),
}
h.db.Save(m1)
_, err = h.GetMachine(n1.Name, m1.Name)
c.Assert(err, check.IsNil)
m2 := &Machine{
ID: 2,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_2",
NamespaceID: n2.ID,
Namespace: *n2,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.2",
AuthKeyID: uint(pak2n2.ID),
}
h.db.Save(m2)
_, err = h.GetMachine(n2.Name, m2.Name)
c.Assert(err, check.IsNil)
m3 := &Machine{
ID: 3,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_3",
NamespaceID: n3.ID,
Namespace: *n3,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.3",
AuthKeyID: uint(pak3n3.ID),
}
h.db.Save(m3)
_, err = h.GetMachine(n3.Name, m3.Name)
c.Assert(err, check.IsNil)
m4 := &Machine{
ID: 4,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_4",
NamespaceID: n1.ID,
Namespace: *n1,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.4",
AuthKeyID: uint(pak4n1.ID),
}
h.db.Save(m4)
err = h.AddSharedMachineToNamespace(m2, n1)
c.Assert(err, check.IsNil)
baseDomain := "foobar.headscale.net"
dnsConfigOrig := tailcfg.DNSConfig{
Routes: make(map[string][]dnstype.Resolver),
Domains: []string{baseDomain},
Proxied: false,
}
m1peers, err := h.getPeers(m1)
c.Assert(err, check.IsNil)
dnsConfig, err := getMapResponseDNSConfig(&dnsConfigOrig, baseDomain, *m1, m1peers)
c.Assert(err, check.IsNil)
c.Assert(dnsConfig, check.NotNil)
c.Assert(len(dnsConfig.Routes), check.Equals, 0)
c.Assert(len(dnsConfig.Domains), check.Equals, 1)
}

View File

@@ -11,23 +11,29 @@ Long story short, you can define the DNS servers you want to use in your tailnet
## Configuration reference ## Configuration reference
The setup is done via the `config.json` file, under the `dns_config` key. The setup is done via the `config.yaml` file, under the `dns_config` key.
```json ```yaml
{ server_url: http://127.0.0.1:8001
"server_url": "http://127.0.0.1:8001", listen_addr: 0.0.0.0:8001
"listen_addr": "0.0.0.0:8001", private_key_path: private.key
"private_key_path": "private.key", dns_config:
//... nameservers:
"dns_config": { - 1.1.1.1
"nameservers": ["1.1.1.1", "8.8.8.8"], - 8.8.8.8
"domains": [], restricted_nameservers:
"magic_dns": true, foo.bar.com:
"base_domain": "example.com" - 1.1.1.1
} darp.headscale.net:
} - 1.1.1.1
- 8.8.8.8
domains: []
magic_dns: true
base_domain: example.com
``` ```
- `nameservers`: The list of DNS servers to use. - `nameservers`: The list of DNS servers to use.
- `domains`: Search domains to inject. - `domains`: Search domains to inject.
- `magic_dns`: Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/). Only works if there is at least a nameserver defined. - `magic_dns`: Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/). Only works if there is at least a nameserver defined.
- `base_domain`: Defines the base domain to create the hostnames for MagicDNS. `base_domain` must be a FQDNs, without the trailing dot. The FQDN of the hosts will be `hostname.namespace.base_domain` (e.g., _myhost.mynamespace.example.com_). - `base_domain`: Defines the base domain to create the hostnames for MagicDNS. `base_domain` must be a FQDNs, without the trailing dot. The FQDN of the hosts will be `hostname.namespace.base_domain` (e.g., _myhost.mynamespace.example.com_).
- `restricted_nameservers`: Split DNS (see https://tailscale.com/kb/1054/dns/), list of search domains and the DNS to query for each one.

View File

@@ -504,43 +504,43 @@ func (s *IntegrationTestSuite) TestSharedNodes() {
assert.Contains(s.T(), result, hostname) assert.Contains(s.T(), result, hostname)
} }
// TODO(kradalby): Figure out why these connections are not set up // TODO(juanfont): We have to find out why do we need to wait
// // TODO: See if we can have a more deterministic wait here. time.Sleep(100 * time.Second) // Wait for the nodes to receive updates
// time.Sleep(100 * time.Second)
// mainIps, err := getIPs(main.tailscales) mainIps, err := getIPs(main.tailscales)
// assert.Nil(s.T(), err) assert.Nil(s.T(), err)
// sharedIps, err := getIPs(shared.tailscales) sharedIps, err := getIPs(shared.tailscales)
// assert.Nil(s.T(), err) assert.Nil(s.T(), err)
// for hostname, tailscale := range main.tailscales { for hostname, tailscale := range main.tailscales {
// for peername, ip := range sharedIps { for peername, ip := range sharedIps {
// s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) { s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
// // We currently cant ping ourselves, so skip that. // We currently cant ping ourselves, so skip that.
// if peername != hostname { if peername != hostname {
// // We are only interested in "direct ping" which means what we // We are only interested in "direct ping" which means what we
// // might need a couple of more attempts before reaching the node. // might need a couple of more attempts before reaching the node.
// command := []string{ command := []string{
// "tailscale", "ping", "tailscale", "ping",
// "--timeout=1s", "--timeout=15s",
// "--c=20", "--c=20",
// "--until-direct=true", "--until-direct=true",
// ip.String(), ip.String(),
// } }
// fmt.Printf("Pinging from %s (%s) to %s (%s)\n", hostname, mainIps[hostname], peername, ip) fmt.Printf("Pinging from %s (%s) to %s (%s)\n", hostname, mainIps[hostname], peername, ip)
// result, err := executeCommand( result, err := executeCommand(
// &tailscale, &tailscale,
// command, command,
// ) []string{},
// assert.Nil(t, err) )
// fmt.Printf("Result for %s: %s\n", hostname, result) assert.Nil(t, err)
// assert.Contains(t, result, "pong") fmt.Printf("Result for %s: %s\n", hostname, result)
// } assert.Contains(t, result, "pong")
// }) }
// } })
// } }
}
} }
func (s *IntegrationTestSuite) TestTailDrop() { func (s *IntegrationTestSuite) TestTailDrop() {

View File

@@ -78,15 +78,15 @@ func (h *Headscale) getDirectPeers(m *Machine) (Machines, error) {
return machines, nil return machines, nil
} }
// getShared fetches machines that are shared to the `Namespace` of the machine we are getting peers for
func (h *Headscale) getShared(m *Machine) (Machines, error) { func (h *Headscale) getShared(m *Machine) (Machines, error) {
log.Trace(). log.Trace().
Str("func", "getShared"). Str("func", "getShared").
Str("machine", m.Name). Str("machine", m.Name).
Msg("Finding shared peers") Msg("Finding shared peers")
// We fetch here machines that are shared to the `Namespace` of the machine we are getting peers for
sharedMachines := []SharedMachine{} sharedMachines := []SharedMachine{}
if err := h.db.Preload("Namespace").Preload("Machine").Where("namespace_id = ?", if err := h.db.Preload("Namespace").Preload("Machine").Preload("Machine.Namespace").Where("namespace_id = ?",
m.NamespaceID).Find(&sharedMachines).Error; err != nil { m.NamespaceID).Find(&sharedMachines).Error; err != nil {
return Machines{}, err return Machines{}, err
} }
@@ -105,6 +105,37 @@ func (h *Headscale) getShared(m *Machine) (Machines, error) {
return peers, nil return peers, nil
} }
// getSharedTo fetches the machines of the namespaces this machine is shared in
func (h *Headscale) getSharedTo(m *Machine) (Machines, error) {
log.Trace().
Str("func", "getSharedTo").
Str("machine", m.Name).
Msg("Finding peers in namespaces this machine is shared with")
sharedMachines := []SharedMachine{}
if err := h.db.Preload("Namespace").Preload("Machine").Preload("Machine.Namespace").Where("machine_id = ?",
m.ID).Find(&sharedMachines).Error; err != nil {
return Machines{}, err
}
peers := make(Machines, 0)
for _, sharedMachine := range sharedMachines {
namespaceMachines, err := h.ListMachinesInNamespace(sharedMachine.Namespace.Name)
if err != nil {
return Machines{}, err
}
peers = append(peers, *namespaceMachines...)
}
sort.Slice(peers, func(i, j int) bool { return peers[i].ID < peers[j].ID })
log.Trace().
Str("func", "getSharedTo").
Str("machine", m.Name).
Msgf("Found peers we are shared with: %s", peers.String())
return peers, nil
}
func (h *Headscale) getPeers(m *Machine) (Machines, error) { func (h *Headscale) getPeers(m *Machine) (Machines, error) {
direct, err := h.getDirectPeers(m) direct, err := h.getDirectPeers(m)
if err != nil { if err != nil {
@@ -118,13 +149,24 @@ func (h *Headscale) getPeers(m *Machine) (Machines, error) {
shared, err := h.getShared(m) shared, err := h.getShared(m)
if err != nil { if err != nil {
log.Error(). log.Error().
Str("func", "getDirectPeers"). Str("func", "getShared").
Err(err).
Msg("Cannot fetch peers")
return Machines{}, err
}
sharedTo, err := h.getSharedTo(m)
if err != nil {
log.Error().
Str("func", "sharedTo").
Err(err). Err(err).
Msg("Cannot fetch peers") Msg("Cannot fetch peers")
return Machines{}, err return Machines{}, err
} }
peers := append(direct, shared...) peers := append(direct, shared...)
peers = append(peers, sharedTo...)
sort.Slice(peers, func(i, j int) bool { return peers[i].ID < peers[j].ID }) sort.Slice(peers, func(i, j int) bool { return peers[i].ID < peers[j].ID })
log.Trace(). log.Trace().

View File

@@ -59,6 +59,35 @@ func (h *Headscale) DestroyNamespace(name string) error {
} }
if result := h.db.Unscoped().Delete(&n); result.Error != nil { if result := h.db.Unscoped().Delete(&n); result.Error != nil {
return result.Error
}
return nil
}
// RenameNamespace renames a Namespace. Returns error if the Namespace does
// not exist or if another Namespace exists with the new name.
func (h *Headscale) RenameNamespace(oldName, newName string) error {
n, err := h.GetNamespace(oldName)
if err != nil {
return err
}
_, err = h.GetNamespace(newName)
if err == nil {
return errorNamespaceExists
}
if !errors.Is(err, errorNamespaceNotFound) {
return err
}
n.Name = newName
if result := h.db.Save(&n); result.Error != nil {
return result.Error
}
err = h.RequestMapUpdates(n.ID)
if err != nil {
return err return err
} }
@@ -91,7 +120,7 @@ func (h *Headscale) ListMachinesInNamespace(name string) (*[]Machine, error) {
} }
machines := []Machine{} machines := []Machine{}
if err := h.db.Preload("AuthKey").Preload("Namespace").Where(&Machine{NamespaceID: n.ID}).Find(&machines).Error; err != nil { if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Where(&Machine{NamespaceID: n.ID}).Find(&machines).Error; err != nil {
return nil, err return nil, err
} }
return &machines, nil return &machines, nil
@@ -216,3 +245,22 @@ func (n *Namespace) toUser() *tailcfg.User {
} }
return &u return &u
} }
func getMapResponseUserProfiles(m Machine, peers Machines) []tailcfg.UserProfile {
namespaceMap := make(map[string]Namespace)
namespaceMap[m.Namespace.Name] = m.Namespace
for _, p := range peers {
namespaceMap[p.Namespace.Name] = p.Namespace // not worth checking if already is there
}
profiles := []tailcfg.UserProfile{}
for _, namespace := range namespaceMap {
profiles = append(profiles,
tailcfg.UserProfile{
ID: tailcfg.UserID(namespace.ID),
LoginName: namespace.Name,
DisplayName: namespace.Name,
})
}
return profiles
}

View File

@@ -1,6 +1,7 @@
package headscale package headscale
import ( import (
"github.com/rs/zerolog/log"
"gopkg.in/check.v1" "gopkg.in/check.v1"
) )
@@ -46,3 +47,155 @@ func (s *Suite) TestDestroyNamespaceErrors(c *check.C) {
err = h.DestroyNamespace("test") err = h.DestroyNamespace("test")
c.Assert(err, check.Equals, errorNamespaceNotEmpty) c.Assert(err, check.Equals, errorNamespaceNotEmpty)
} }
func (s *Suite) TestRenameNamespace(c *check.C) {
n, err := h.CreateNamespace("test")
c.Assert(err, check.IsNil)
c.Assert(n.Name, check.Equals, "test")
ns, err := h.ListNamespaces()
c.Assert(err, check.IsNil)
c.Assert(len(*ns), check.Equals, 1)
err = h.RenameNamespace("test", "test_renamed")
c.Assert(err, check.IsNil)
_, err = h.GetNamespace("test")
c.Assert(err, check.Equals, errorNamespaceNotFound)
_, err = h.GetNamespace("test_renamed")
c.Assert(err, check.IsNil)
err = h.RenameNamespace("test_does_not_exit", "test")
c.Assert(err, check.Equals, errorNamespaceNotFound)
n2, err := h.CreateNamespace("test2")
c.Assert(err, check.IsNil)
c.Assert(n2.Name, check.Equals, "test2")
err = h.RenameNamespace("test2", "test_renamed")
c.Assert(err, check.Equals, errorNamespaceExists)
}
func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
n1, err := h.CreateNamespace("shared1")
c.Assert(err, check.IsNil)
n2, err := h.CreateNamespace("shared2")
c.Assert(err, check.IsNil)
n3, err := h.CreateNamespace("shared3")
c.Assert(err, check.IsNil)
pak1n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak2n2, err := h.CreatePreAuthKey(n2.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak3n3, err := h.CreatePreAuthKey(n3.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak4n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = h.GetMachine(n1.Name, "test_get_shared_nodes_1")
c.Assert(err, check.NotNil)
m1 := &Machine{
ID: 1,
MachineKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
NodeKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
DiscoKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
Name: "test_get_shared_nodes_1",
NamespaceID: n1.ID,
Namespace: *n1,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.1",
AuthKeyID: uint(pak1n1.ID),
}
h.db.Save(m1)
_, err = h.GetMachine(n1.Name, m1.Name)
c.Assert(err, check.IsNil)
m2 := &Machine{
ID: 2,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_2",
NamespaceID: n2.ID,
Namespace: *n2,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.2",
AuthKeyID: uint(pak2n2.ID),
}
h.db.Save(m2)
_, err = h.GetMachine(n2.Name, m2.Name)
c.Assert(err, check.IsNil)
m3 := &Machine{
ID: 3,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_3",
NamespaceID: n3.ID,
Namespace: *n3,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.3",
AuthKeyID: uint(pak3n3.ID),
}
h.db.Save(m3)
_, err = h.GetMachine(n3.Name, m3.Name)
c.Assert(err, check.IsNil)
m4 := &Machine{
ID: 4,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_4",
NamespaceID: n1.ID,
Namespace: *n1,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.4",
AuthKeyID: uint(pak4n1.ID),
}
h.db.Save(m4)
err = h.AddSharedMachineToNamespace(m2, n1)
c.Assert(err, check.IsNil)
m1peers, err := h.getPeers(m1)
c.Assert(err, check.IsNil)
userProfiles := getMapResponseUserProfiles(*m1, m1peers)
log.Trace().Msgf("userProfiles %#v", userProfiles)
c.Assert(len(userProfiles), check.Equals, 2)
found := false
for _, up := range userProfiles {
if up.DisplayName == n1.Name {
found = true
break
}
}
c.Assert(found, check.Equals, true)
found = false
for _, up := range userProfiles {
if up.DisplayName == n2.Name {
found = true
break
}
}
c.Assert(found, check.Equals, true)
}

View File

@@ -11,7 +11,7 @@ import (
const errorAuthKeyNotFound = Error("AuthKey not found") const errorAuthKeyNotFound = Error("AuthKey not found")
const errorAuthKeyExpired = Error("AuthKey expired") const errorAuthKeyExpired = Error("AuthKey expired")
const errorAuthKeyNotReusableAlreadyUsed = Error("AuthKey not reusable already used") const errSingleUseAuthKeyHasBeenUsed = Error("AuthKey has already been used")
// PreAuthKey describes a pre-authorization key usable in a particular namespace // PreAuthKey describes a pre-authorization key usable in a particular namespace
type PreAuthKey struct { type PreAuthKey struct {
@@ -21,6 +21,7 @@ type PreAuthKey struct {
Namespace Namespace Namespace Namespace
Reusable bool Reusable bool
Ephemeral bool `gorm:"default:false"` Ephemeral bool `gorm:"default:false"`
Used bool `gorm:"default:false"`
CreatedAt *time.Time CreatedAt *time.Time
Expiration *time.Time Expiration *time.Time
@@ -110,11 +111,10 @@ func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) {
return nil, err return nil, err
} }
if len(machines) != 0 { if len(machines) != 0 || pak.Used {
return nil, errorAuthKeyNotReusableAlreadyUsed return nil, errSingleUseAuthKeyHasBeenUsed
} }
// missing here validation on current usage
return &pak, nil return &pak, nil
} }

View File

@@ -87,7 +87,7 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) {
h.db.Save(&m) h.db.Save(&m)
p, err := h.checkKeyValidity(pak.Key) p, err := h.checkKeyValidity(pak.Key)
c.Assert(err, check.Equals, errorAuthKeyNotReusableAlreadyUsed) c.Assert(err, check.Equals, errSingleUseAuthKeyHasBeenUsed)
c.Assert(p, check.IsNil) c.Assert(p, check.IsNil)
} }
@@ -180,3 +180,16 @@ func (*Suite) TestExpirePreauthKey(c *check.C) {
c.Assert(err, check.Equals, errorAuthKeyExpired) c.Assert(err, check.Equals, errorAuthKeyExpired)
c.Assert(p, check.IsNil) c.Assert(p, check.IsNil)
} }
func (*Suite) TestNotReusableMarkedAsUsed(c *check.C) {
n, err := h.CreateNamespace("test6")
c.Assert(err, check.IsNil)
pak, err := h.CreatePreAuthKey(n.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak.Used = true
h.db.Save(&pak)
_, err = h.checkKeyValidity(pak.Key)
c.Assert(err, check.Equals, errSingleUseAuthKeyHasBeenUsed)
}

View File

@@ -21,12 +21,15 @@ func (h *Headscale) AddSharedMachineToNamespace(m *Machine, ns *Namespace) error
return errorSameNamespace return errorSameNamespace
} }
sharedMachine := SharedMachine{} sharedMachines := []SharedMachine{}
if err := h.db.Where("machine_id = ? AND namespace_id", m.ID, ns.ID).First(&sharedMachine).Error; err == nil { if err := h.db.Where("machine_id = ? AND namespace_id = ?", m.ID, ns.ID).Find(&sharedMachines).Error; err != nil {
return err
}
if len(sharedMachines) > 0 {
return errorMachineAlreadyShared return errorMachineAlreadyShared
} }
sharedMachine = SharedMachine{ sharedMachine := SharedMachine{
MachineID: m.ID, MachineID: m.ID,
Machine: *m, Machine: *m,
NamespaceID: ns.ID, NamespaceID: ns.ID,
@@ -37,6 +40,30 @@ func (h *Headscale) AddSharedMachineToNamespace(m *Machine, ns *Namespace) error
return nil return nil
} }
// RemoveSharedMachineFromNamespace removes a shared machine from a namespace
func (h *Headscale) RemoveSharedMachineFromNamespace(m *Machine, ns *Namespace) error {
if m.NamespaceID == ns.ID {
return errorSameNamespace
}
sharedMachine := SharedMachine{}
result := h.db.Where("machine_id = ? AND namespace_id = ?", m.ID, ns.ID).Unscoped().Delete(&sharedMachine)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
return errorMachineNotShared
}
err := h.RequestMapUpdates(ns.ID)
if err != nil {
return err
}
return nil
}
// RemoveSharedMachineFromAllNamespaces removes a machine as a shared node from all namespaces // RemoveSharedMachineFromAllNamespaces removes a machine as a shared node from all namespaces
func (h *Headscale) RemoveSharedMachineFromAllNamespaces(m *Machine) error { func (h *Headscale) RemoveSharedMachineFromAllNamespaces(m *Machine) error {
sharedMachine := SharedMachine{} sharedMachine := SharedMachine{}

View File

@@ -4,32 +4,26 @@ import (
"gopkg.in/check.v1" "gopkg.in/check.v1"
) )
func (s *Suite) TestBasicSharedNodesInNamespace(c *check.C) { func CreateNodeNamespace(c *check.C, namespace, node, key, IP string) (*Namespace, *Machine) {
n1, err := h.CreateNamespace("shared1") n1, err := h.CreateNamespace(namespace)
c.Assert(err, check.IsNil)
n2, err := h.CreateNamespace("shared2")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
pak1, err := h.CreatePreAuthKey(n1.Name, false, false, nil) pak1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
pak2, err := h.CreatePreAuthKey(n2.Name, false, false, nil) _, err = h.GetMachine(n1.Name, node)
c.Assert(err, check.IsNil)
_, err = h.GetMachine(n1.Name, "test_get_shared_nodes_1")
c.Assert(err, check.NotNil) c.Assert(err, check.NotNil)
m1 := &Machine{ m1 := &Machine{
ID: 0, ID: 0,
MachineKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", MachineKey: key,
NodeKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", NodeKey: key,
DiscoKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", DiscoKey: key,
Name: "test_get_shared_nodes_1", Name: node,
NamespaceID: n1.ID, NamespaceID: n1.ID,
Registered: true, Registered: true,
RegisterMethod: "authKey", RegisterMethod: "authKey",
IPAddress: "100.64.0.1", IPAddress: IP,
AuthKeyID: uint(pak1.ID), AuthKeyID: uint(pak1.ID),
} }
h.db.Save(m1) h.db.Save(m1)
@@ -37,22 +31,12 @@ func (s *Suite) TestBasicSharedNodesInNamespace(c *check.C) {
_, err = h.GetMachine(n1.Name, m1.Name) _, err = h.GetMachine(n1.Name, m1.Name)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
m2 := &Machine{ return n1, m1
ID: 1, }
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_2",
NamespaceID: n2.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.2",
AuthKeyID: uint(pak2.ID),
}
h.db.Save(m2)
_, err = h.GetMachine(n2.Name, m2.Name) func (s *Suite) TestBasicSharedNodesInNamespace(c *check.C) {
c.Assert(err, check.IsNil) n1, m1 := CreateNodeNamespace(c, "shared1", "test_get_shared_nodes_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1")
_, m2 := CreateNodeNamespace(c, "shared2", "test_get_shared_nodes_2", "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", "100.64.0.2")
p1s, err := h.getPeers(m1) p1s, err := h.getPeers(m1)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
@@ -68,54 +52,7 @@ func (s *Suite) TestBasicSharedNodesInNamespace(c *check.C) {
} }
func (s *Suite) TestSameNamespace(c *check.C) { func (s *Suite) TestSameNamespace(c *check.C) {
n1, err := h.CreateNamespace("shared1") n1, m1 := CreateNodeNamespace(c, "shared1", "test_get_shared_nodes_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1")
c.Assert(err, check.IsNil)
n2, err := h.CreateNamespace("shared2")
c.Assert(err, check.IsNil)
pak1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak2, err := h.CreatePreAuthKey(n2.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = h.GetMachine(n1.Name, "test_get_shared_nodes_1")
c.Assert(err, check.NotNil)
m1 := &Machine{
ID: 0,
MachineKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
NodeKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
DiscoKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
Name: "test_get_shared_nodes_1",
NamespaceID: n1.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.1",
AuthKeyID: uint(pak1.ID),
}
h.db.Save(m1)
_, err = h.GetMachine(n1.Name, m1.Name)
c.Assert(err, check.IsNil)
m2 := &Machine{
ID: 1,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_2",
NamespaceID: n2.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.2",
AuthKeyID: uint(pak2.ID),
}
h.db.Save(m2)
_, err = h.GetMachine(n2.Name, m2.Name)
c.Assert(err, check.IsNil)
p1s, err := h.getPeers(m1) p1s, err := h.getPeers(m1)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
@@ -125,55 +62,35 @@ func (s *Suite) TestSameNamespace(c *check.C) {
c.Assert(err, check.Equals, errorSameNamespace) c.Assert(err, check.Equals, errorSameNamespace)
} }
func (s *Suite) TestUnshare(c *check.C) {
n1, m1 := CreateNodeNamespace(c, "shared1", "test_unshare_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1")
_, m2 := CreateNodeNamespace(c, "shared2", "test_unshare_2", "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", "100.64.0.2")
p1s, err := h.getPeers(m1)
c.Assert(err, check.IsNil)
c.Assert(len(p1s), check.Equals, 0)
err = h.AddSharedMachineToNamespace(m2, n1)
c.Assert(err, check.IsNil)
p1s, err = h.getShared(m1)
c.Assert(err, check.IsNil)
c.Assert(len(p1s), check.Equals, 1)
err = h.RemoveSharedMachineFromNamespace(m2, n1)
c.Assert(err, check.IsNil)
p1s, err = h.getShared(m1)
c.Assert(err, check.IsNil)
c.Assert(len(p1s), check.Equals, 0)
err = h.RemoveSharedMachineFromNamespace(m2, n1)
c.Assert(err, check.Equals, errorMachineNotShared)
}
func (s *Suite) TestAlreadyShared(c *check.C) { func (s *Suite) TestAlreadyShared(c *check.C) {
n1, err := h.CreateNamespace("shared1") n1, m1 := CreateNodeNamespace(c, "shared1", "test_get_shared_nodes_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1")
c.Assert(err, check.IsNil) _, m2 := CreateNodeNamespace(c, "shared2", "test_get_shared_nodes_2", "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", "100.64.0.2")
n2, err := h.CreateNamespace("shared2")
c.Assert(err, check.IsNil)
pak1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak2, err := h.CreatePreAuthKey(n2.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = h.GetMachine(n1.Name, "test_get_shared_nodes_1")
c.Assert(err, check.NotNil)
m1 := &Machine{
ID: 0,
MachineKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
NodeKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
DiscoKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
Name: "test_get_shared_nodes_1",
NamespaceID: n1.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.1",
AuthKeyID: uint(pak1.ID),
}
h.db.Save(m1)
_, err = h.GetMachine(n1.Name, m1.Name)
c.Assert(err, check.IsNil)
m2 := &Machine{
ID: 1,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_2",
NamespaceID: n2.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.2",
AuthKeyID: uint(pak2.ID),
}
h.db.Save(m2)
_, err = h.GetMachine(n2.Name, m2.Name)
c.Assert(err, check.IsNil)
p1s, err := h.getPeers(m1) p1s, err := h.getPeers(m1)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
@@ -186,54 +103,8 @@ func (s *Suite) TestAlreadyShared(c *check.C) {
} }
func (s *Suite) TestDoNotIncludeRoutesOnShared(c *check.C) { func (s *Suite) TestDoNotIncludeRoutesOnShared(c *check.C) {
n1, err := h.CreateNamespace("shared1") n1, m1 := CreateNodeNamespace(c, "shared1", "test_get_shared_nodes_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1")
c.Assert(err, check.IsNil) _, m2 := CreateNodeNamespace(c, "shared2", "test_get_shared_nodes_2", "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", "100.64.0.2")
n2, err := h.CreateNamespace("shared2")
c.Assert(err, check.IsNil)
pak1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak2, err := h.CreatePreAuthKey(n2.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = h.GetMachine(n1.Name, "test_get_shared_nodes_1")
c.Assert(err, check.NotNil)
m1 := &Machine{
ID: 0,
MachineKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
NodeKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
DiscoKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
Name: "test_get_shared_nodes_1",
NamespaceID: n1.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.1",
AuthKeyID: uint(pak1.ID),
}
h.db.Save(m1)
_, err = h.GetMachine(n1.Name, m1.Name)
c.Assert(err, check.IsNil)
m2 := &Machine{
ID: 1,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_2",
NamespaceID: n2.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.2",
AuthKeyID: uint(pak2.ID),
}
h.db.Save(m2)
_, err = h.GetMachine(n2.Name, m2.Name)
c.Assert(err, check.IsNil)
p1s, err := h.getPeers(m1) p1s, err := h.getPeers(m1)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
@@ -249,86 +120,18 @@ func (s *Suite) TestDoNotIncludeRoutesOnShared(c *check.C) {
} }
func (s *Suite) TestComplexSharingAcrossNamespaces(c *check.C) { func (s *Suite) TestComplexSharingAcrossNamespaces(c *check.C) {
n1, err := h.CreateNamespace("shared1") n1, m1 := CreateNodeNamespace(c, "shared1", "test_get_shared_nodes_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1")
c.Assert(err, check.IsNil) _, m2 := CreateNodeNamespace(c, "shared2", "test_get_shared_nodes_2", "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", "100.64.0.2")
_, m3 := CreateNodeNamespace(c, "shared3", "test_get_shared_nodes_3", "6e704bee83eb93db6fc2c417d7882964cd3f8cc87082cbb645982e34020c76c8", "100.64.0.3")
n2, err := h.CreateNamespace("shared2")
c.Assert(err, check.IsNil)
n3, err := h.CreateNamespace("shared3")
c.Assert(err, check.IsNil)
pak1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak2, err := h.CreatePreAuthKey(n2.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak3, err := h.CreatePreAuthKey(n3.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak4, err := h.CreatePreAuthKey(n1.Name, false, false, nil) pak4, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, err = h.GetMachine(n1.Name, "test_get_shared_nodes_1")
c.Assert(err, check.NotNil)
m1 := &Machine{
ID: 0,
MachineKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
NodeKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
DiscoKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
Name: "test_get_shared_nodes_1",
NamespaceID: n1.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.1",
AuthKeyID: uint(pak1.ID),
}
h.db.Save(m1)
_, err = h.GetMachine(n1.Name, m1.Name)
c.Assert(err, check.IsNil)
m2 := &Machine{
ID: 1,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_2",
NamespaceID: n2.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.2",
AuthKeyID: uint(pak2.ID),
}
h.db.Save(m2)
_, err = h.GetMachine(n2.Name, m2.Name)
c.Assert(err, check.IsNil)
m3 := &Machine{
ID: 2,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_3",
NamespaceID: n3.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.3",
AuthKeyID: uint(pak3.ID),
}
h.db.Save(m3)
_, err = h.GetMachine(n3.Name, m3.Name)
c.Assert(err, check.IsNil)
m4 := &Machine{ m4 := &Machine{
ID: 3, ID: 4,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", MachineKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", NodeKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", DiscoKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f",
Name: "test_get_shared_nodes_4", Name: "test_get_shared_nodes_4",
NamespaceID: n1.ID, NamespaceID: n1.ID,
Registered: true, Registered: true,
@@ -343,109 +146,46 @@ func (s *Suite) TestComplexSharingAcrossNamespaces(c *check.C) {
p1s, err := h.getPeers(m1) p1s, err := h.getPeers(m1)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(p1s), check.Equals, 1) // nodes 1 and 4 c.Assert(len(p1s), check.Equals, 1) // node1 can see node4
c.Assert(p1s[0].Name, check.Equals, "test_get_shared_nodes_4") c.Assert(p1s[0].Name, check.Equals, m4.Name)
err = h.AddSharedMachineToNamespace(m2, n1) err = h.AddSharedMachineToNamespace(m2, n1)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
p1sAfter, err := h.getPeers(m1) p1sAfter, err := h.getPeers(m1)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(p1sAfter), check.Equals, 2) // nodes 1, 2, 4 c.Assert(len(p1sAfter), check.Equals, 2) // node1 can see node2 (shared) and node4 (same namespace)
c.Assert(p1sAfter[0].Name, check.Equals, "test_get_shared_nodes_2") c.Assert(p1sAfter[0].Name, check.Equals, m2.Name)
c.Assert(p1sAfter[1].Name, check.Equals, "test_get_shared_nodes_4") c.Assert(p1sAfter[1].Name, check.Equals, m4.Name)
node1shared, err := h.getShared(m1) node1shared, err := h.getShared(m1)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(node1shared), check.Equals, 1) // nodes 1, 2, 4 c.Assert(len(node1shared), check.Equals, 1) // node1 can see node2 as shared
c.Assert(node1shared[0].Name, check.Equals, "test_get_shared_nodes_2") c.Assert(node1shared[0].Name, check.Equals, m2.Name)
pAlone, err := h.getPeers(m3) pAlone, err := h.getPeers(m3)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(pAlone), check.Equals, 0) // node 3 is alone c.Assert(len(pAlone), check.Equals, 0) // node3 is alone
pSharedTo, err := h.getPeers(m2)
c.Assert(err, check.IsNil)
c.Assert(len(pSharedTo), check.Equals, 2) // node2 should see node1 (sharedTo) and node4 (sharedTo), as is shared in namespace1
c.Assert(pSharedTo[0].Name, check.Equals, m1.Name)
c.Assert(pSharedTo[1].Name, check.Equals, m4.Name)
} }
func (s *Suite) TestDeleteSharedMachine(c *check.C) { func (s *Suite) TestDeleteSharedMachine(c *check.C) {
n1, err := h.CreateNamespace("shared1") n1, m1 := CreateNodeNamespace(c, "shared1", "test_get_shared_nodes_1", "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66", "100.64.0.1")
c.Assert(err, check.IsNil) _, m2 := CreateNodeNamespace(c, "shared2", "test_get_shared_nodes_2", "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", "100.64.0.2")
_, m3 := CreateNodeNamespace(c, "shared3", "test_get_shared_nodes_3", "6e704bee83eb93db6fc2c417d7882964cd3f8cc87082cbb645982e34020c76c8", "100.64.0.3")
n2, err := h.CreateNamespace("shared2")
c.Assert(err, check.IsNil)
n3, err := h.CreateNamespace("shared3")
c.Assert(err, check.IsNil)
pak1n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak2n2, err := h.CreatePreAuthKey(n2.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak3n3, err := h.CreatePreAuthKey(n3.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak4n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil) pak4n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, err = h.GetMachine(n1.Name, "test_get_shared_nodes_1")
c.Assert(err, check.NotNil)
m1 := &Machine{
ID: 0,
MachineKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
NodeKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
DiscoKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
Name: "test_get_shared_nodes_1",
NamespaceID: n1.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.1",
AuthKeyID: uint(pak1n1.ID),
}
h.db.Save(m1)
_, err = h.GetMachine(n1.Name, m1.Name)
c.Assert(err, check.IsNil)
m2 := &Machine{
ID: 1,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_2",
NamespaceID: n2.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.2",
AuthKeyID: uint(pak2n2.ID),
}
h.db.Save(m2)
_, err = h.GetMachine(n2.Name, m2.Name)
c.Assert(err, check.IsNil)
m3 := &Machine{
ID: 2,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_3",
NamespaceID: n3.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.3",
AuthKeyID: uint(pak3n3.ID),
}
h.db.Save(m3)
_, err = h.GetMachine(n3.Name, m3.Name)
c.Assert(err, check.IsNil)
m4 := &Machine{ m4 := &Machine{
ID: 3, ID: 4,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", MachineKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", NodeKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863", DiscoKey: "4c3e07c3ecd40e9c945bb6797557c451850691c0409740578325e17009dd298f",
Name: "test_get_shared_nodes_4", Name: "test_get_shared_nodes_4",
NamespaceID: n1.ID, NamespaceID: n1.ID,
Registered: true, Registered: true,
@@ -461,7 +201,7 @@ func (s *Suite) TestDeleteSharedMachine(c *check.C) {
p1s, err := h.getPeers(m1) p1s, err := h.getPeers(m1)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(p1s), check.Equals, 1) // nodes 1 and 4 c.Assert(len(p1s), check.Equals, 1) // nodes 1 and 4
c.Assert(p1s[0].Name, check.Equals, "test_get_shared_nodes_4") c.Assert(p1s[0].Name, check.Equals, m4.Name)
err = h.AddSharedMachineToNamespace(m2, n1) err = h.AddSharedMachineToNamespace(m2, n1)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
@@ -469,13 +209,13 @@ func (s *Suite) TestDeleteSharedMachine(c *check.C) {
p1sAfter, err := h.getPeers(m1) p1sAfter, err := h.getPeers(m1)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(p1sAfter), check.Equals, 2) // nodes 1, 2, 4 c.Assert(len(p1sAfter), check.Equals, 2) // nodes 1, 2, 4
c.Assert(p1sAfter[0].Name, check.Equals, "test_get_shared_nodes_2") c.Assert(p1sAfter[0].Name, check.Equals, m2.Name)
c.Assert(p1sAfter[1].Name, check.Equals, "test_get_shared_nodes_4") c.Assert(p1sAfter[1].Name, check.Equals, m4.Name)
node1shared, err := h.getShared(m1) node1shared, err := h.getShared(m1)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(node1shared), check.Equals, 1) // nodes 1, 2, 4 c.Assert(len(node1shared), check.Equals, 1) // nodes 1, 2, 4
c.Assert(node1shared[0].Name, check.Equals, "test_get_shared_nodes_2") c.Assert(node1shared[0].Name, check.Equals, m2.Name)
pAlone, err := h.getPeers(m3) pAlone, err := h.getPeers(m3)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)