mirror of
https://github.com/gravitl/netmaker.git
synced 2025-09-12 16:14:37 +08:00
Merge branch 'develop' into GRA-1217-tests
This commit is contained in:
commit
8ab81ca2da
44 changed files with 970 additions and 595 deletions
1
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
|
@ -31,6 +31,7 @@ body:
|
|||
label: Version
|
||||
description: What version are you running?
|
||||
options:
|
||||
- v0.18.3
|
||||
- v0.18.2
|
||||
- v0.18.1
|
||||
- v0.18.0
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
<p align="center">
|
||||
<a href="https://github.com/gravitl/netmaker/releases">
|
||||
<img src="https://img.shields.io/badge/Version-0.18.2-informational?style=flat-square" />
|
||||
<img src="https://img.shields.io/badge/Version-0.18.3-informational?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/gravitl/netmaker/tags">
|
||||
<img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" />
|
||||
|
|
|
@ -3,6 +3,7 @@ package acl
|
|||
import (
|
||||
"os"
|
||||
|
||||
"github.com/gravitl/netmaker/cli/cmd/commons"
|
||||
"github.com/gravitl/netmaker/cli/functions"
|
||||
"github.com/gravitl/netmaker/logic/acls"
|
||||
"github.com/guumaster/tablewriter"
|
||||
|
@ -16,23 +17,28 @@ var aclListCmd = &cobra.Command{
|
|||
Long: `List all ACLs associated with a network`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
aclSource := (map[acls.AclID]acls.ACL)(*functions.GetACL(args[0]))
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"From", "To", "Status"})
|
||||
for id, acl := range aclSource {
|
||||
for k, v := range (map[acls.AclID]byte)(acl) {
|
||||
row := []string{string(id), string(k)}
|
||||
switch v {
|
||||
case acls.NotAllowed:
|
||||
row = append(row, "Not Allowed")
|
||||
case acls.NotPresent:
|
||||
row = append(row, "Not Present")
|
||||
case acls.Allowed:
|
||||
row = append(row, "Allowed")
|
||||
switch commons.OutputFormat {
|
||||
case commons.JsonOutput:
|
||||
functions.PrettyPrint(aclSource)
|
||||
default:
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"From", "To", "Status"})
|
||||
for id, acl := range aclSource {
|
||||
for k, v := range (map[acls.AclID]byte)(acl) {
|
||||
row := []string{string(id), string(k)}
|
||||
switch v {
|
||||
case acls.NotAllowed:
|
||||
row = append(row, "Not Allowed")
|
||||
case acls.NotPresent:
|
||||
row = append(row, "Not Present")
|
||||
case acls.Allowed:
|
||||
row = append(row, "Allowed")
|
||||
}
|
||||
table.Append(row)
|
||||
}
|
||||
table.Append(row)
|
||||
}
|
||||
table.Render()
|
||||
}
|
||||
table.Render()
|
||||
},
|
||||
}
|
||||
|
||||
|
|
9
cli/cmd/commons/globals.go
Normal file
9
cli/cmd/commons/globals.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package commons
|
||||
|
||||
// OutputFormat flag defines the output format to stdout (Enum:- json)
|
||||
var OutputFormat string
|
||||
|
||||
const (
|
||||
// JsonOutput refers to json format output to stdout
|
||||
JsonOutput = "json"
|
||||
)
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gravitl/netmaker/cli/cmd/commons"
|
||||
"github.com/gravitl/netmaker/cli/functions"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
"github.com/guumaster/tablewriter"
|
||||
|
@ -31,12 +32,17 @@ var dnsListCmd = &cobra.Command{
|
|||
} else {
|
||||
data = *functions.GetDNS()
|
||||
}
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"Name", "Network", "IPv4 Address", "IPv6 Address"})
|
||||
for _, d := range data {
|
||||
table.Append([]string{d.Name, d.Network, d.Address, d.Address6})
|
||||
switch commons.OutputFormat {
|
||||
case commons.JsonOutput:
|
||||
functions.PrettyPrint(data)
|
||||
default:
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"Name", "Network", "IPv4 Address", "IPv6 Address"})
|
||||
for _, d := range data {
|
||||
table.Append([]string{d.Name, d.Network, d.Address, d.Address6})
|
||||
}
|
||||
table.Render()
|
||||
}
|
||||
table.Render()
|
||||
},
|
||||
}
|
||||
|
||||
|
|
47
cli/cmd/enrollment_key/create.go
Normal file
47
cli/cmd/enrollment_key/create.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package enrollment_key
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gravitl/netmaker/cli/functions"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
expiration int
|
||||
usesRemaining int
|
||||
networks string
|
||||
unlimited bool
|
||||
tags string
|
||||
)
|
||||
|
||||
var enrollmentKeyCreateCmd = &cobra.Command{
|
||||
Use: "create",
|
||||
Args: cobra.NoArgs,
|
||||
Short: "Create an enrollment key",
|
||||
Long: `Create an enrollment key`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
enrollKey := &models.APIEnrollmentKey{
|
||||
Expiration: int64(expiration),
|
||||
UsesRemaining: usesRemaining,
|
||||
Unlimited: unlimited,
|
||||
}
|
||||
if networks != "" {
|
||||
enrollKey.Networks = strings.Split(networks, ",")
|
||||
}
|
||||
if tags != "" {
|
||||
enrollKey.Tags = strings.Split(tags, ",")
|
||||
}
|
||||
functions.PrettyPrint(functions.CreateEnrollmentKey(enrollKey))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
enrollmentKeyCreateCmd.Flags().IntVar(&expiration, "expiration", 0, "Expiration time of the key in UNIX timestamp format")
|
||||
enrollmentKeyCreateCmd.Flags().IntVar(&usesRemaining, "uses", 0, "Number of times this key can be used")
|
||||
enrollmentKeyCreateCmd.Flags().StringVar(&networks, "networks", "", "Comma-separated list of networks which the enrollment key can access")
|
||||
enrollmentKeyCreateCmd.Flags().BoolVar(&unlimited, "unlimited", false, "Should the key have unlimited uses ?")
|
||||
enrollmentKeyCreateCmd.Flags().StringVar(&tags, "tags", "", "Comma-separated list of any additional tags")
|
||||
rootCmd.AddCommand(enrollmentKeyCreateCmd)
|
||||
}
|
23
cli/cmd/enrollment_key/delete.go
Normal file
23
cli/cmd/enrollment_key/delete.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package enrollment_key
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gravitl/netmaker/cli/functions"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var enrollmentKeyDeleteCmd = &cobra.Command{
|
||||
Use: "delete keyID",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "Delete an enrollment key",
|
||||
Long: `Delete an enrollment key`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
functions.DeleteEnrollmentKey(args[0])
|
||||
fmt.Println("Enrollment key ", args[0], " deleted")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(enrollmentKeyDeleteCmd)
|
||||
}
|
20
cli/cmd/enrollment_key/list.go
Normal file
20
cli/cmd/enrollment_key/list.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package enrollment_key
|
||||
|
||||
import (
|
||||
"github.com/gravitl/netmaker/cli/functions"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var enrollmentKeyListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Args: cobra.NoArgs,
|
||||
Short: "List enrollment keys",
|
||||
Long: `List enrollment keys`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
functions.PrettyPrint(functions.GetEnrollmentKeys())
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(enrollmentKeyListCmd)
|
||||
}
|
28
cli/cmd/enrollment_key/root.go
Normal file
28
cli/cmd/enrollment_key/root.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package enrollment_key
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "enrollment_key",
|
||||
Short: "Manage Enrollment Keys",
|
||||
Long: `Manage Enrollment Keys`,
|
||||
}
|
||||
|
||||
// GetRoot returns the root subcommand
|
||||
func GetRoot() *cobra.Command {
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
err := rootCmd.Execute()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gravitl/netmaker/cli/cmd/commons"
|
||||
"github.com/gravitl/netmaker/cli/functions"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
"github.com/guumaster/tablewriter"
|
||||
|
@ -25,12 +26,17 @@ var extClientListCmd = &cobra.Command{
|
|||
} else {
|
||||
data = *functions.GetAllExtClients()
|
||||
}
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"Client ID", "Network", "IPv4 Address", "IPv6 Address", "Enabled", "Last Modified"})
|
||||
for _, d := range data {
|
||||
table.Append([]string{d.ClientID, d.Network, d.Address, d.Address6, strconv.FormatBool(d.Enabled), time.Unix(d.LastModified, 0).String()})
|
||||
switch commons.OutputFormat {
|
||||
case commons.JsonOutput:
|
||||
functions.PrettyPrint(data)
|
||||
default:
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"Client ID", "Network", "IPv4 Address", "IPv6 Address", "Enabled", "Last Modified"})
|
||||
for _, d := range data {
|
||||
table.Append([]string{d.ClientID, d.Network, d.Address, d.Address6, strconv.FormatBool(d.Enabled), time.Unix(d.LastModified, 0).String()})
|
||||
}
|
||||
table.Render()
|
||||
}
|
||||
table.Render()
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gravitl/netmaker/cli/cmd/commons"
|
||||
"github.com/gravitl/netmaker/cli/functions"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -16,14 +17,19 @@ var networkListCmd = &cobra.Command{
|
|||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
networks := functions.GetNetworks()
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"NetId", "Address Range (IPv4)", "Address Range (IPv6)", "Network Last Modified", "Nodes Last Modified"})
|
||||
for _, n := range *networks {
|
||||
networkLastModified := time.Unix(n.NetworkLastModified, 0).Format(time.RFC3339)
|
||||
nodesLastModified := time.Unix(n.NodesLastModified, 0).Format(time.RFC3339)
|
||||
table.Append([]string{n.NetID, n.AddressRange, n.AddressRange6, networkLastModified, nodesLastModified})
|
||||
switch commons.OutputFormat {
|
||||
case commons.JsonOutput:
|
||||
functions.PrettyPrint(networks)
|
||||
default:
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"NetId", "Address Range (IPv4)", "Address Range (IPv6)", "Network Last Modified", "Nodes Last Modified"})
|
||||
for _, n := range *networks {
|
||||
networkLastModified := time.Unix(n.NetworkLastModified, 0).Format(time.RFC3339)
|
||||
nodesLastModified := time.Unix(n.NodesLastModified, 0).Format(time.RFC3339)
|
||||
table.Append([]string{n.NetID, n.AddressRange, n.AddressRange6, networkLastModified, nodesLastModified})
|
||||
}
|
||||
table.Render()
|
||||
}
|
||||
table.Render()
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/gravitl/netmaker/cli/cmd/commons"
|
||||
"github.com/gravitl/netmaker/cli/functions"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
"github.com/guumaster/tablewriter"
|
||||
|
@ -23,23 +24,28 @@ var nodeListCmd = &cobra.Command{
|
|||
} else {
|
||||
data = *functions.GetNodes()
|
||||
}
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"ID", "Addresses", "Network", "Egress", "Ingress", "Relay"})
|
||||
for _, d := range data {
|
||||
addresses := ""
|
||||
if d.Address != "" {
|
||||
addresses += d.Address
|
||||
}
|
||||
if d.Address6 != "" {
|
||||
switch commons.OutputFormat {
|
||||
case commons.JsonOutput:
|
||||
functions.PrettyPrint(data)
|
||||
default:
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"ID", "Addresses", "Network", "Egress", "Ingress", "Relay"})
|
||||
for _, d := range data {
|
||||
addresses := ""
|
||||
if d.Address != "" {
|
||||
addresses += ", "
|
||||
addresses += d.Address
|
||||
}
|
||||
addresses += d.Address6
|
||||
if d.Address6 != "" {
|
||||
if d.Address != "" {
|
||||
addresses += ", "
|
||||
}
|
||||
addresses += d.Address6
|
||||
}
|
||||
table.Append([]string{d.ID, addresses, d.Network,
|
||||
strconv.FormatBool(d.IsEgressGateway), strconv.FormatBool(d.IsIngressGateway), strconv.FormatBool(d.IsRelay)})
|
||||
}
|
||||
table.Append([]string{d.ID, addresses, d.Network,
|
||||
strconv.FormatBool(d.IsEgressGateway), strconv.FormatBool(d.IsIngressGateway), strconv.FormatBool(d.IsRelay)})
|
||||
table.Render()
|
||||
}
|
||||
table.Render()
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,10 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/gravitl/netmaker/cli/cmd/acl"
|
||||
"github.com/gravitl/netmaker/cli/cmd/commons"
|
||||
"github.com/gravitl/netmaker/cli/cmd/context"
|
||||
"github.com/gravitl/netmaker/cli/cmd/dns"
|
||||
"github.com/gravitl/netmaker/cli/cmd/enrollment_key"
|
||||
"github.com/gravitl/netmaker/cli/cmd/ext_client"
|
||||
"github.com/gravitl/netmaker/cli/cmd/host"
|
||||
"github.com/gravitl/netmaker/cli/cmd/keys"
|
||||
|
@ -41,6 +43,7 @@ func Execute() {
|
|||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVarP(&commons.OutputFormat, "output", "o", "", "List output in specific format (Enum:- json)")
|
||||
// Bind subcommands here
|
||||
rootCmd.AddCommand(network.GetRoot())
|
||||
rootCmd.AddCommand(context.GetRoot())
|
||||
|
@ -55,4 +58,5 @@ func init() {
|
|||
rootCmd.AddCommand(metrics.GetRoot())
|
||||
rootCmd.AddCommand(network_user.GetRoot())
|
||||
rootCmd.AddCommand(host.GetRoot())
|
||||
rootCmd.AddCommand(enrollment_key.GetRoot())
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gravitl/netmaker/cli/cmd/commons"
|
||||
"github.com/gravitl/netmaker/cli/functions"
|
||||
"github.com/guumaster/tablewriter"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -16,12 +17,18 @@ var userListCmd = &cobra.Command{
|
|||
Short: "List all users",
|
||||
Long: `List all users`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"Name", "Admin", "Networks", "Groups"})
|
||||
for _, d := range *functions.ListUsers() {
|
||||
table.Append([]string{d.UserName, strconv.FormatBool(d.IsAdmin), strings.Join(d.Networks, ", "), strings.Join(d.Groups, ", ")})
|
||||
data := functions.ListUsers()
|
||||
switch commons.OutputFormat {
|
||||
case commons.JsonOutput:
|
||||
functions.PrettyPrint(data)
|
||||
default:
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"Name", "Admin", "Networks", "Groups"})
|
||||
for _, d := range *data {
|
||||
table.Append([]string{d.UserName, strconv.FormatBool(d.IsAdmin), strings.Join(d.Networks, ", "), strings.Join(d.Groups, ", ")})
|
||||
}
|
||||
table.Render()
|
||||
}
|
||||
table.Render()
|
||||
},
|
||||
}
|
||||
|
||||
|
|
22
cli/functions/enrollment_keys.go
Normal file
22
cli/functions/enrollment_keys.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package functions
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gravitl/netmaker/models"
|
||||
)
|
||||
|
||||
// CreateEnrollmentKey - create an enrollment key
|
||||
func CreateEnrollmentKey(key *models.APIEnrollmentKey) *models.EnrollmentKey {
|
||||
return request[models.EnrollmentKey](http.MethodPost, "/api/v1/enrollment-keys", key)
|
||||
}
|
||||
|
||||
// GetEnrollmentKeys - gets all enrollment keys
|
||||
func GetEnrollmentKeys() *[]models.EnrollmentKey {
|
||||
return request[[]models.EnrollmentKey](http.MethodGet, "/api/v1/enrollment-keys", nil)
|
||||
}
|
||||
|
||||
// DeleteEnrollmentKey - delete an enrollment key
|
||||
func DeleteEnrollmentKey(keyID string) {
|
||||
request[any](http.MethodDelete, "/api/v1/enrollment-keys/"+keyID, nil)
|
||||
}
|
|
@ -13,7 +13,7 @@ services:
|
|||
BROKER_TYPE: "emqx"
|
||||
EMQX_REST_ENDPOINT: "http://mq:18083"
|
||||
SERVER_NAME: "NETMAKER_BASE_DOMAIN"
|
||||
STUN_DOMAIN: "stun.NETMAKER_BASE_DOMAIN"
|
||||
STUN_LIST: "stun.NETMAKER_BASE_DOMAIN:3478,stun1.netmaker.io:3478,stun2.netmaker.io:3478,stun1.l.google.com:19302,stun2.l.google.com:19302"
|
||||
SERVER_HOST: "SERVER_PUBLIC_IP"
|
||||
SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
|
||||
COREDNS_ADDR: "SERVER_PUBLIC_IP"
|
||||
|
|
|
@ -13,7 +13,7 @@ services:
|
|||
BROKER_TYPE: "emqx"
|
||||
EMQX_REST_ENDPOINT: "http://mq:18083"
|
||||
SERVER_NAME: "NETMAKER_BASE_DOMAIN"
|
||||
STUN_DOMAIN: "stun.NETMAKER_BASE_DOMAIN"
|
||||
STUN_LIST: "stun.NETMAKER_BASE_DOMAIN:3478,stun1.netmaker.io:3478,stun2.netmaker.io:3478,stun1.l.google.com:19302,stun2.l.google.com:19302"
|
||||
SERVER_HOST: "SERVER_PUBLIC_IP"
|
||||
SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
|
||||
COREDNS_ADDR: "SERVER_PUBLIC_IP"
|
||||
|
|
|
@ -11,7 +11,7 @@ services:
|
|||
environment:
|
||||
BROKER_ENDPOINT: "wss://broker.NETMAKER_BASE_DOMAIN"
|
||||
SERVER_NAME: "NETMAKER_BASE_DOMAIN"
|
||||
STUN_DOMAIN: "stun.NETMAKER_BASE_DOMAIN"
|
||||
STUN_LIST: "stun.NETMAKER_BASE_DOMAIN:3478,stun1.netmaker.io:3478,stun2.netmaker.io:3478,stun1.l.google.com:19302,stun2.l.google.com:19302"
|
||||
SERVER_HOST: "SERVER_PUBLIC_IP"
|
||||
SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
|
||||
COREDNS_ADDR: "SERVER_PUBLIC_IP"
|
||||
|
@ -24,10 +24,10 @@ services:
|
|||
DATABASE: "sqlite"
|
||||
NODE_ID: "netmaker-server-1"
|
||||
SERVER_BROKER_ENDPOINT: "ws://mq:1883"
|
||||
STUN_PORT: "3478"
|
||||
VERBOSITY: "1"
|
||||
MQ_PASSWORD: "REPLACE_MQ_PASSWORD"
|
||||
MQ_USERNAME: "REPLACE_MQ_USERNAME"
|
||||
STUN_PORT: "3478"
|
||||
ports:
|
||||
- "3478:3478/udp"
|
||||
netmaker-ui:
|
||||
|
|
|
@ -32,48 +32,48 @@ type EnvironmentConfig struct {
|
|||
|
||||
// ServerConfig - server conf struct
|
||||
type ServerConfig struct {
|
||||
CoreDNSAddr string `yaml:"corednsaddr"`
|
||||
APIConnString string `yaml:"apiconn"`
|
||||
APIHost string `yaml:"apihost"`
|
||||
APIPort string `yaml:"apiport"`
|
||||
Broker string `yam:"broker"`
|
||||
ServerBrokerEndpoint string `yaml:"serverbrokerendpoint"`
|
||||
BrokerType string `yaml:"brokertype"`
|
||||
EmqxRestEndpoint string `yaml:"emqxrestendpoint"`
|
||||
MasterKey string `yaml:"masterkey"`
|
||||
DNSKey string `yaml:"dnskey"`
|
||||
AllowedOrigin string `yaml:"allowedorigin"`
|
||||
NodeID string `yaml:"nodeid"`
|
||||
RestBackend string `yaml:"restbackend"`
|
||||
MessageQueueBackend string `yaml:"messagequeuebackend"`
|
||||
DNSMode string `yaml:"dnsmode"`
|
||||
DisableRemoteIPCheck string `yaml:"disableremoteipcheck"`
|
||||
Version string `yaml:"version"`
|
||||
SQLConn string `yaml:"sqlconn"`
|
||||
Platform string `yaml:"platform"`
|
||||
Database string `yaml:"database"`
|
||||
Verbosity int32 `yaml:"verbosity"`
|
||||
AuthProvider string `yaml:"authprovider"`
|
||||
OIDCIssuer string `yaml:"oidcissuer"`
|
||||
ClientID string `yaml:"clientid"`
|
||||
ClientSecret string `yaml:"clientsecret"`
|
||||
FrontendURL string `yaml:"frontendurl"`
|
||||
DisplayKeys string `yaml:"displaykeys"`
|
||||
AzureTenant string `yaml:"azuretenant"`
|
||||
Telemetry string `yaml:"telemetry"`
|
||||
HostNetwork string `yaml:"hostnetwork"`
|
||||
Server string `yaml:"server"`
|
||||
PublicIPService string `yaml:"publicipservice"`
|
||||
MQPassword string `yaml:"mqpassword"`
|
||||
MQUserName string `yaml:"mqusername"`
|
||||
MetricsExporter string `yaml:"metrics_exporter"`
|
||||
BasicAuth string `yaml:"basic_auth"`
|
||||
LicenseValue string `yaml:"license_value"`
|
||||
NetmakerAccountID string `yaml:"netmaker_account_id"`
|
||||
IsEE string `yaml:"is_ee"`
|
||||
StunPort int `yaml:"stun_port"`
|
||||
StunHost string `yaml:"stun_host"`
|
||||
Proxy string `yaml:"proxy"`
|
||||
CoreDNSAddr string `yaml:"corednsaddr"`
|
||||
APIConnString string `yaml:"apiconn"`
|
||||
APIHost string `yaml:"apihost"`
|
||||
APIPort string `yaml:"apiport"`
|
||||
Broker string `yam:"broker"`
|
||||
ServerBrokerEndpoint string `yaml:"serverbrokerendpoint"`
|
||||
BrokerType string `yaml:"brokertype"`
|
||||
EmqxRestEndpoint string `yaml:"emqxrestendpoint"`
|
||||
MasterKey string `yaml:"masterkey"`
|
||||
DNSKey string `yaml:"dnskey"`
|
||||
AllowedOrigin string `yaml:"allowedorigin"`
|
||||
NodeID string `yaml:"nodeid"`
|
||||
RestBackend string `yaml:"restbackend"`
|
||||
MessageQueueBackend string `yaml:"messagequeuebackend"`
|
||||
DNSMode string `yaml:"dnsmode"`
|
||||
DisableRemoteIPCheck string `yaml:"disableremoteipcheck"`
|
||||
Version string `yaml:"version"`
|
||||
SQLConn string `yaml:"sqlconn"`
|
||||
Platform string `yaml:"platform"`
|
||||
Database string `yaml:"database"`
|
||||
Verbosity int32 `yaml:"verbosity"`
|
||||
AuthProvider string `yaml:"authprovider"`
|
||||
OIDCIssuer string `yaml:"oidcissuer"`
|
||||
ClientID string `yaml:"clientid"`
|
||||
ClientSecret string `yaml:"clientsecret"`
|
||||
FrontendURL string `yaml:"frontendurl"`
|
||||
DisplayKeys string `yaml:"displaykeys"`
|
||||
AzureTenant string `yaml:"azuretenant"`
|
||||
Telemetry string `yaml:"telemetry"`
|
||||
HostNetwork string `yaml:"hostnetwork"`
|
||||
Server string `yaml:"server"`
|
||||
PublicIPService string `yaml:"publicipservice"`
|
||||
MQPassword string `yaml:"mqpassword"`
|
||||
MQUserName string `yaml:"mqusername"`
|
||||
MetricsExporter string `yaml:"metrics_exporter"`
|
||||
BasicAuth string `yaml:"basic_auth"`
|
||||
LicenseValue string `yaml:"license_value"`
|
||||
NetmakerAccountID string `yaml:"netmaker_account_id"`
|
||||
IsEE string `yaml:"is_ee"`
|
||||
StunPort int `yaml:"stun_port"`
|
||||
StunList string `yaml:"stun_list"`
|
||||
Proxy string `yaml:"proxy"`
|
||||
}
|
||||
|
||||
// SQLConfig - Generic SQL Config
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
//
|
||||
// Schemes: https
|
||||
// BasePath: /
|
||||
// Version: 0.18.2
|
||||
// Version: 0.18.3
|
||||
// Host: netmaker.io
|
||||
//
|
||||
// Consumes:
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/gravitl/netmaker/models/promodels"
|
||||
"github.com/gravitl/netmaker/mq"
|
||||
"github.com/skip2/go-qrcode"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
func extClientHandlers(r *mux.Router) {
|
||||
|
@ -317,16 +318,22 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
var extclient models.ExtClient
|
||||
var CustomExtClient models.CustomExtClient
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&CustomExtClient)
|
||||
var customExtClient models.CustomExtClient
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&customExtClient)
|
||||
if err == nil {
|
||||
if CustomExtClient.ClientID != "" && !validName(CustomExtClient.ClientID) {
|
||||
if customExtClient.ClientID != "" && !validName(customExtClient.ClientID) {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(errInvalidExtClientID, "badrequest"))
|
||||
return
|
||||
}
|
||||
extclient.ClientID = CustomExtClient.ClientID
|
||||
extclient.ClientID = customExtClient.ClientID
|
||||
if len(customExtClient.PublicKey) > 0 {
|
||||
if _, err := wgtypes.ParseKey(customExtClient.PublicKey); err != nil {
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(errInvalidExtClientPubKey, "badrequest"))
|
||||
return
|
||||
}
|
||||
extclient.PublicKey = customExtClient.PublicKey
|
||||
}
|
||||
}
|
||||
|
||||
extclient.Network = networkName
|
||||
|
@ -350,16 +357,13 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
|
|||
listenPort = host.ProxyListenPort
|
||||
}
|
||||
extclient.IngressGatewayEndpoint = host.EndpointIP.String() + ":" + strconv.FormatInt(int64(listenPort), 10)
|
||||
|
||||
extclient.Enabled = true
|
||||
parentNetwork, err := logic.GetNetwork(networkName)
|
||||
if err == nil { // check if parent network default ACL is enabled (yes) or not (no)
|
||||
extclient.Enabled = parentNetwork.DefaultACL == "yes"
|
||||
}
|
||||
// check pro settings
|
||||
|
||||
err = logic.CreateExtClient(&extclient)
|
||||
if err != nil {
|
||||
if err = logic.CreateExtClient(&extclient); err != nil {
|
||||
logger.Log(0, r.Header.Get("user"),
|
||||
fmt.Sprintf("failed to create new ext client on network [%s]: %v", networkName, err))
|
||||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
|
@ -389,8 +393,7 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
|
|||
logger.Log(0, r.Header.Get("user"), "created new ext client on network", networkName)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
go func() {
|
||||
err = mq.PublishPeerUpdate()
|
||||
if err != nil {
|
||||
if err := mq.PublishPeerUpdate(); err != nil {
|
||||
logger.Log(1, "error setting ext peers on "+nodeid+": "+err.Error())
|
||||
}
|
||||
if err := mq.PublishExtCLientDNS(&extclient); err != nil {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -433,7 +434,7 @@ func getNode(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
||||
return
|
||||
}
|
||||
hostPeerUpdate, err := logic.GetPeerUpdateForHost(node.Network, host, nil)
|
||||
hostPeerUpdate, err := logic.GetPeerUpdateForHost(context.Background(), node.Network, host, nil)
|
||||
if err != nil && !database.IsEmptyRecord(err) {
|
||||
logger.Log(0, r.Header.Get("user"),
|
||||
fmt.Sprintf("error fetching wg peers config for host [ %s ]: %v", host.ID.String(), err))
|
||||
|
@ -622,7 +623,7 @@ func createNode(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
}
|
||||
hostPeerUpdate, err := logic.GetPeerUpdateForHost(networkName, &data.Host, nil)
|
||||
hostPeerUpdate, err := logic.GetPeerUpdateForHost(context.Background(), networkName, &data.Host, nil)
|
||||
if err != nil && !database.IsEmptyRecord(err) {
|
||||
logger.Log(0, r.Header.Get("user"),
|
||||
fmt.Sprintf("error fetching wg peers config for host [ %s ]: %v", data.Host.ID.String(), err))
|
||||
|
@ -908,7 +909,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
|
|||
relayedUpdate = true
|
||||
}
|
||||
ifaceDelta := logic.IfaceDelta(¤tNode, newNode)
|
||||
|
||||
aclUpdate := currentNode.DefaultACL != newNode.DefaultACL
|
||||
if ifaceDelta && servercfg.Is_EE {
|
||||
if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID, currentNode.Network); err != nil {
|
||||
logger.Log(0, "failed to reset failover lists during node update for node", currentNode.ID.String(), currentNode.Network)
|
||||
|
@ -941,13 +942,17 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
|
|||
logger.Log(1, r.Header.Get("user"), "updated node", currentNode.ID.String(), "on network", currentNode.Network)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(apiNode)
|
||||
|
||||
runUpdates(newNode, ifaceDelta)
|
||||
go func() {
|
||||
go func(aclUpdate bool, newNode *models.Node) {
|
||||
if aclUpdate {
|
||||
if err := mq.PublishPeerUpdate(); err != nil {
|
||||
logger.Log(0, "error during node ACL update for node", newNode.ID.String())
|
||||
}
|
||||
}
|
||||
if err := mq.PublishReplaceDNS(¤tNode, newNode, host); err != nil {
|
||||
logger.Log(1, "failed to publish dns update", err.Error())
|
||||
}
|
||||
}()
|
||||
}(aclUpdate, newNode)
|
||||
}
|
||||
|
||||
// swagger:route DELETE /api/nodes/{network}/{nodeid} nodes deleteNode
|
||||
|
|
|
@ -5,7 +5,10 @@ import (
|
|||
"regexp"
|
||||
)
|
||||
|
||||
var errInvalidExtClientID = errors.New("ext client ID must be alphanumderic and/or dashes")
|
||||
var (
|
||||
errInvalidExtClientPubKey = errors.New("incorrect ext client public key")
|
||||
errInvalidExtClientID = errors.New("ext client ID must be alphanumderic and/or dashes")
|
||||
)
|
||||
|
||||
// allow only dashes and alphaneumeric for ext client and node names
|
||||
func validName(name string) bool {
|
||||
|
|
|
@ -59,6 +59,8 @@ const (
|
|||
HOSTS_TABLE_NAME = "hosts"
|
||||
// ENROLLMENT_KEYS_TABLE_NAME - table name for enrollmentkeys
|
||||
ENROLLMENT_KEYS_TABLE_NAME = "enrollmentkeys"
|
||||
// HOST_ACTIONS_TABLE_NAME - table name for enrollmentkeys
|
||||
HOST_ACTIONS_TABLE_NAME = "hostactions"
|
||||
|
||||
// == ERROR CONSTS ==
|
||||
// NO_RECORD - no singular result found
|
||||
|
@ -141,6 +143,7 @@ func createTables() {
|
|||
createTable(CACHE_TABLE_NAME)
|
||||
createTable(HOSTS_TABLE_NAME)
|
||||
createTable(ENROLLMENT_KEYS_TABLE_NAME)
|
||||
createTable(HOST_ACTIONS_TABLE_NAME)
|
||||
}
|
||||
|
||||
func createTable(tableName string) error {
|
||||
|
|
5
go.mod
5
go.mod
|
@ -13,7 +13,7 @@ require (
|
|||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/txn2/txeh v1.3.0
|
||||
golang.org/x/crypto v0.6.0
|
||||
golang.org/x/net v0.6.0 // indirect
|
||||
|
@ -42,8 +42,7 @@ require (
|
|||
|
||||
require (
|
||||
github.com/guumaster/tablewriter v0.0.10
|
||||
github.com/kr/pretty v0.3.0
|
||||
github.com/matryer/is v1.4.0
|
||||
github.com/matryer/is v1.4.1
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/spf13/cobra v1.6.1
|
||||
)
|
||||
|
|
8
go.sum
8
go.sum
|
@ -80,8 +80,8 @@ github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ic
|
|||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
|
||||
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
|
@ -140,8 +140,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/txn2/txeh v1.3.0 h1:vnbv63htVMZCaQgLqVBxKvj2+HHHFUzNW7I183zjg3E=
|
||||
github.com/txn2/txeh v1.3.0/go.mod h1:O7M6gUTPeMF+vsa4c4Ipx3JDkOYrruB1Wry8QRsMcw8=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
|
|
|
@ -16,7 +16,7 @@ spec:
|
|||
hostNetwork: true
|
||||
containers:
|
||||
- name: netclient
|
||||
image: gravitl/netclient:v0.18.2
|
||||
image: gravitl/netclient:v0.18.3
|
||||
env:
|
||||
- name: TOKEN
|
||||
value: "TOKEN_VALUE"
|
||||
|
|
|
@ -28,7 +28,7 @@ spec:
|
|||
# - "<node label value>"
|
||||
containers:
|
||||
- name: netclient
|
||||
image: gravitl/netclient:v0.18.2
|
||||
image: gravitl/netclient:v0.18.3
|
||||
env:
|
||||
- name: TOKEN
|
||||
value: "TOKEN_VALUE"
|
||||
|
|
|
@ -79,7 +79,7 @@ spec:
|
|||
value: "Kubernetes"
|
||||
- name: VERBOSITY
|
||||
value: "3"
|
||||
image: gravitl/netmaker:v0.18.2
|
||||
image: gravitl/netmaker:v0.18.3
|
||||
imagePullPolicy: Always
|
||||
name: netmaker
|
||||
ports:
|
||||
|
|
|
@ -15,7 +15,7 @@ spec:
|
|||
spec:
|
||||
containers:
|
||||
- name: netmaker-ui
|
||||
image: gravitl/netmaker-ui:v0.18.2
|
||||
image: gravitl/netmaker-ui:v0.18.3
|
||||
ports:
|
||||
- containerPort: 443
|
||||
env:
|
||||
|
|
|
@ -117,14 +117,15 @@ func GetExtClient(clientid string, network string) (models.ExtClient, error) {
|
|||
// CreateExtClient - creates an extclient
|
||||
func CreateExtClient(extclient *models.ExtClient) error {
|
||||
|
||||
if extclient.PrivateKey == "" {
|
||||
if len(extclient.PublicKey) == 0 {
|
||||
privateKey, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
extclient.PrivateKey = privateKey.String()
|
||||
extclient.PublicKey = privateKey.PublicKey().String()
|
||||
} else {
|
||||
extclient.PrivateKey = "[ENTER PRIVATE KEY]"
|
||||
}
|
||||
|
||||
parentNetwork, err := GetNetwork(extclient.Network)
|
||||
|
@ -156,7 +157,6 @@ func CreateExtClient(extclient *models.ExtClient) error {
|
|||
}
|
||||
|
||||
extclient.LastModified = time.Now().Unix()
|
||||
|
||||
key, err := GetRecordKey(extclient.ClientID, extclient.Network)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -1,37 +1,55 @@
|
|||
package hostactions
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gravitl/netmaker/database"
|
||||
"github.com/gravitl/netmaker/models"
|
||||
)
|
||||
|
||||
// nodeActionHandler - handles the storage of host action updates
|
||||
var nodeActionHandler sync.Map
|
||||
|
||||
// AddAction - adds a host action to a host's list to be retrieved from broker update
|
||||
func AddAction(hu models.HostUpdate) {
|
||||
currentRecords, ok := nodeActionHandler.Load(hu.Host.ID.String())
|
||||
if !ok { // no list exists yet
|
||||
nodeActionHandler.Store(hu.Host.ID.String(), []models.HostUpdate{hu})
|
||||
} else { // list exists, append to it
|
||||
currentList := currentRecords.([]models.HostUpdate)
|
||||
currentList = append(currentList, hu)
|
||||
nodeActionHandler.Store(hu.Host.ID.String(), currentList)
|
||||
hostID := hu.Host.ID.String()
|
||||
currentRecords, err := database.FetchRecord(database.HOST_ACTIONS_TABLE_NAME, hostID)
|
||||
if err != nil {
|
||||
if database.IsEmptyRecord(err) { // no list exists yet
|
||||
newEntry, err := json.Marshal([]models.HostUpdate{hu})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = database.Insert(hostID, string(newEntry), database.HOST_ACTIONS_TABLE_NAME)
|
||||
}
|
||||
return
|
||||
}
|
||||
var currentList []models.HostUpdate
|
||||
if err := json.Unmarshal([]byte(currentRecords), ¤tList); err != nil {
|
||||
return
|
||||
}
|
||||
currentList = append(currentList, hu)
|
||||
newData, err := json.Marshal(currentList)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = database.Insert(hostID, string(newData), database.HOST_ACTIONS_TABLE_NAME)
|
||||
}
|
||||
|
||||
// GetAction - gets an action if exists
|
||||
// TODO: may need to move to DB rather than sync map for HA
|
||||
func GetAction(id string) *models.HostUpdate {
|
||||
currentRecords, ok := nodeActionHandler.Load(id)
|
||||
if !ok {
|
||||
currentRecords, err := database.FetchRecord(database.HOST_ACTIONS_TABLE_NAME, id)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var currentList []models.HostUpdate
|
||||
if err = json.Unmarshal([]byte(currentRecords), ¤tList); err != nil {
|
||||
return nil
|
||||
}
|
||||
currentList := currentRecords.([]models.HostUpdate)
|
||||
if len(currentList) > 0 {
|
||||
hu := currentList[0]
|
||||
nodeActionHandler.Store(hu.Host.ID.String(), currentList[1:])
|
||||
newData, err := json.Marshal(currentList[1:])
|
||||
if err != nil {
|
||||
newData, _ = json.Marshal([]models.HostUpdate{})
|
||||
}
|
||||
_ = database.Insert(id, string(newData), database.HOST_ACTIONS_TABLE_NAME)
|
||||
return &hu
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -395,6 +395,21 @@ func HostExists(h *models.Host) bool {
|
|||
return (err != nil && !database.IsEmptyRecord(err)) || (err == nil)
|
||||
}
|
||||
|
||||
// GetHostByNodeID - returns a host if found to have a node's ID, else nil
|
||||
func GetHostByNodeID(id string) *models.Host {
|
||||
hosts, err := GetAllHosts()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for i := range hosts {
|
||||
h := hosts[i]
|
||||
if StringSliceContains(h.Nodes, id) {
|
||||
return &h
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updatePort(p *int) {
|
||||
*p++
|
||||
if *p > maxPort {
|
||||
|
|
|
@ -32,17 +32,24 @@ const (
|
|||
|
||||
// GetNetworkNodes - gets the nodes of a network
|
||||
func GetNetworkNodes(network string) ([]models.Node, error) {
|
||||
var nodes []models.Node
|
||||
allnodes, err := GetAllNodes()
|
||||
if err != nil {
|
||||
return []models.Node{}, err
|
||||
}
|
||||
for _, node := range allnodes {
|
||||
|
||||
return GetNetworkNodesMemory(allnodes, network), nil
|
||||
}
|
||||
|
||||
// GetNetworkNodesMemory - gets all nodes belonging to a network from list in memory
|
||||
func GetNetworkNodesMemory(allNodes []models.Node, network string) []models.Node {
|
||||
var nodes = []models.Node{}
|
||||
for i := range allNodes {
|
||||
node := allNodes[i]
|
||||
if node.Network == network {
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
return nodes
|
||||
}
|
||||
|
||||
// UpdateNode - takes a node and updates another node with it's values
|
||||
|
@ -83,8 +90,9 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
|
|||
|
||||
// DeleteNode - marks node for deletion (and adds to zombie list) if called by UI or deletes node if called by node
|
||||
func DeleteNode(node *models.Node, purge bool) error {
|
||||
alreadyDeleted := node.PendingDelete || node.Action == models.NODE_DELETE
|
||||
node.Action = models.NODE_DELETE
|
||||
if !purge {
|
||||
if !purge && !alreadyDeleted {
|
||||
newnode := *node
|
||||
newnode.PendingDelete = true
|
||||
if err := UpdateNode(node, &newnode); err != nil {
|
||||
|
@ -93,8 +101,15 @@ func DeleteNode(node *models.Node, purge bool) error {
|
|||
newZombie <- node.ID
|
||||
return nil
|
||||
}
|
||||
if alreadyDeleted {
|
||||
logger.Log(1, "forcibly deleting node", node.ID.String())
|
||||
}
|
||||
host, err := GetHost(node.HostID.String())
|
||||
if err != nil {
|
||||
logger.Log(1, "no host found for node", node.ID.String(), "deleting..")
|
||||
if delErr := deleteNodeByID(node); delErr != nil {
|
||||
logger.Log(0, "failed to delete node", node.ID.String(), delErr.Error())
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := DissasociateNodeFromHost(node, host); err != nil {
|
||||
|
|
409
logic/peers.go
409
logic/peers.go
|
@ -1,9 +1,9 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
|
@ -16,8 +16,15 @@ import (
|
|||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
var (
|
||||
// PeerUpdateCtx context to send to host peer updates
|
||||
PeerUpdateCtx context.Context
|
||||
// PeerUpdateStop - the cancel for PeerUpdateCtx
|
||||
PeerUpdateStop context.CancelFunc
|
||||
)
|
||||
|
||||
// GetProxyUpdateForHost - gets the proxy update for host
|
||||
func GetProxyUpdateForHost(host *models.Host) (models.ProxyManagerPayload, error) {
|
||||
func GetProxyUpdateForHost(ctx context.Context, host *models.Host) (models.ProxyManagerPayload, error) {
|
||||
proxyPayload := models.ProxyManagerPayload{
|
||||
Action: models.ProxyUpdate,
|
||||
}
|
||||
|
@ -40,7 +47,7 @@ func GetProxyUpdateForHost(host *models.Host) (models.ProxyManagerPayload, error
|
|||
relayPeersMap := make(map[string]models.RelayedConf)
|
||||
for _, relayedHost := range relayedHosts {
|
||||
relayedHost := relayedHost
|
||||
payload, err := GetPeerUpdateForHost("", &relayedHost, nil)
|
||||
payload, err := GetPeerUpdateForHost(ctx, "", &relayedHost, nil)
|
||||
if err == nil {
|
||||
relayedEndpoint, udpErr := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayedHost.EndpointIP, GetPeerListenPort(&relayedHost)))
|
||||
if udpErr == nil {
|
||||
|
@ -116,11 +123,24 @@ func GetProxyUpdateForHost(host *models.Host) (models.ProxyManagerPayload, error
|
|||
return proxyPayload, nil
|
||||
}
|
||||
|
||||
// ResetPeerUpdateContext - kills any current peer updates and resets the context
|
||||
func ResetPeerUpdateContext() {
|
||||
if PeerUpdateCtx != nil && PeerUpdateStop != nil {
|
||||
PeerUpdateStop() // tell any current peer updates to stop
|
||||
}
|
||||
|
||||
PeerUpdateCtx, PeerUpdateStop = context.WithCancel(context.Background())
|
||||
}
|
||||
|
||||
// GetPeerUpdateForHost - gets the consolidated peer update for the host from all networks
|
||||
func GetPeerUpdateForHost(network string, host *models.Host, deletedNode *models.Node) (models.HostPeerUpdate, error) {
|
||||
func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host, deletedNode *models.Node) (models.HostPeerUpdate, error) {
|
||||
if host == nil {
|
||||
return models.HostPeerUpdate{}, errors.New("host is nil")
|
||||
}
|
||||
allNodes, err := GetAllNodes()
|
||||
if err != nil {
|
||||
return models.HostPeerUpdate{}, err
|
||||
}
|
||||
// track which nodes are deleted
|
||||
// after peer calculation, if peer not in list, add delete config of peer
|
||||
hostPeerUpdate := models.HostPeerUpdate{
|
||||
|
@ -137,10 +157,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, deletedNode *models
|
|||
Peers: []wgtypes.PeerConfig{},
|
||||
NodePeers: []wgtypes.PeerConfig{},
|
||||
}
|
||||
var deletedNodes = []models.Node{} // used to track deleted nodes
|
||||
if deletedNode != nil {
|
||||
deletedNodes = append(deletedNodes, *deletedNode)
|
||||
}
|
||||
|
||||
logger.Log(1, "peer update for host", host.ID.String())
|
||||
peerIndexMap := make(map[string]int)
|
||||
for _, nodeID := range host.Nodes {
|
||||
|
@ -152,229 +169,219 @@ func GetPeerUpdateForHost(network string, host *models.Host, deletedNode *models
|
|||
if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE {
|
||||
continue
|
||||
}
|
||||
currentPeers, err := GetNetworkNodes(node.Network)
|
||||
if err != nil {
|
||||
log.Println("no network nodes")
|
||||
return models.HostPeerUpdate{}, err
|
||||
}
|
||||
currentPeers := GetNetworkNodesMemory(allNodes, node.Network)
|
||||
var nodePeerMap map[string]models.PeerRouteInfo
|
||||
if node.IsIngressGateway || node.IsEgressGateway {
|
||||
nodePeerMap = make(map[string]models.PeerRouteInfo)
|
||||
}
|
||||
for _, peer := range currentPeers {
|
||||
peer := peer
|
||||
if peer.ID.String() == node.ID.String() {
|
||||
logger.Log(2, "peer update, skipping self")
|
||||
//skip yourself
|
||||
continue
|
||||
}
|
||||
if peer.Action == models.NODE_DELETE || peer.PendingDelete {
|
||||
deletedNodes = append(deletedNodes, peer) // track deleted node for peer update
|
||||
continue
|
||||
}
|
||||
var peerConfig wgtypes.PeerConfig
|
||||
peerHost, err := GetHost(peer.HostID.String())
|
||||
if err != nil {
|
||||
logger.Log(1, "no peer host", peer.HostID.String(), err.Error())
|
||||
return models.HostPeerUpdate{}, err
|
||||
}
|
||||
|
||||
if !peer.Connected {
|
||||
logger.Log(2, "peer update, skipping unconnected node", peer.ID.String())
|
||||
//skip unconnected nodes
|
||||
continue
|
||||
}
|
||||
if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(peer.ID.String())) {
|
||||
logger.Log(2, "peer update, skipping node for acl")
|
||||
//skip if not permitted by acl
|
||||
continue
|
||||
}
|
||||
peerConfig.PublicKey = peerHost.PublicKey
|
||||
peerConfig.PersistentKeepaliveInterval = &peer.PersistentKeepalive
|
||||
peerConfig.ReplaceAllowedIPs = true
|
||||
uselocal := false
|
||||
if host.EndpointIP.String() == peerHost.EndpointIP.String() {
|
||||
//peer is on same network
|
||||
// set to localaddress
|
||||
uselocal = true
|
||||
if node.LocalAddress.IP == nil {
|
||||
// use public endpint
|
||||
uselocal = false
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
logger.Log(2, "cancelled peer update for host", host.Name, host.ID.String())
|
||||
return models.HostPeerUpdate{}, fmt.Errorf("peer update cancelled")
|
||||
default:
|
||||
peer := peer
|
||||
if peer.ID.String() == node.ID.String() {
|
||||
logger.Log(2, "peer update, skipping self")
|
||||
//skip yourself
|
||||
continue
|
||||
}
|
||||
if node.LocalAddress.String() == peer.LocalAddress.String() {
|
||||
uselocal = false
|
||||
var peerConfig wgtypes.PeerConfig
|
||||
peerHost, err := GetHost(peer.HostID.String())
|
||||
if err != nil {
|
||||
logger.Log(1, "no peer host", peer.HostID.String(), err.Error())
|
||||
return models.HostPeerUpdate{}, err
|
||||
}
|
||||
}
|
||||
peerConfig.Endpoint = &net.UDPAddr{
|
||||
IP: peerHost.EndpointIP,
|
||||
Port: GetPeerListenPort(peerHost),
|
||||
}
|
||||
|
||||
if uselocal {
|
||||
peerConfig.Endpoint.IP = peer.LocalAddress.IP
|
||||
}
|
||||
allowedips := GetAllowedIPs(&node, &peer, nil)
|
||||
if peer.IsIngressGateway {
|
||||
for _, entry := range peer.IngressGatewayRange {
|
||||
_, cidr, err := net.ParseCIDR(string(entry))
|
||||
if err == nil {
|
||||
allowedips = append(allowedips, *cidr)
|
||||
peerConfig.PublicKey = peerHost.PublicKey
|
||||
peerConfig.PersistentKeepaliveInterval = &peer.PersistentKeepalive
|
||||
peerConfig.ReplaceAllowedIPs = true
|
||||
uselocal := false
|
||||
if host.EndpointIP.String() == peerHost.EndpointIP.String() {
|
||||
//peer is on same network
|
||||
// set to localaddress
|
||||
uselocal = true
|
||||
if node.LocalAddress.IP == nil {
|
||||
// use public endpint
|
||||
uselocal = false
|
||||
}
|
||||
if node.LocalAddress.String() == peer.LocalAddress.String() {
|
||||
uselocal = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if peer.IsEgressGateway {
|
||||
allowedips = append(allowedips, getEgressIPs(&node, &peer)...)
|
||||
}
|
||||
peerConfig.AllowedIPs = allowedips
|
||||
if node.IsIngressGateway || node.IsEgressGateway {
|
||||
peerConfig.Endpoint = &net.UDPAddr{
|
||||
IP: peerHost.EndpointIP,
|
||||
Port: GetPeerListenPort(peerHost),
|
||||
}
|
||||
|
||||
if uselocal {
|
||||
peerConfig.Endpoint.IP = peer.LocalAddress.IP
|
||||
}
|
||||
allowedips := GetAllowedIPs(&node, &peer, nil)
|
||||
if peer.IsIngressGateway {
|
||||
_, extPeerIDAndAddrs, err := getExtPeers(&peer)
|
||||
if err == nil {
|
||||
for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
|
||||
extPeerIdAndAddr := extPeerIdAndAddr
|
||||
nodePeerMap[extPeerIdAndAddr.ID] = models.PeerRouteInfo{
|
||||
PeerAddr: net.IPNet{
|
||||
IP: net.ParseIP(extPeerIdAndAddr.Address),
|
||||
Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
|
||||
},
|
||||
PeerKey: extPeerIdAndAddr.ID,
|
||||
Allow: true,
|
||||
}
|
||||
for _, entry := range peer.IngressGatewayRange {
|
||||
_, cidr, err := net.ParseCIDR(string(entry))
|
||||
if err == nil {
|
||||
allowedips = append(allowedips, *cidr)
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.IsIngressGateway && peer.IsEgressGateway {
|
||||
hostPeerUpdate.IngressInfo.EgressRanges = append(hostPeerUpdate.IngressInfo.EgressRanges,
|
||||
peer.EgressGatewayRanges...)
|
||||
if peer.IsEgressGateway {
|
||||
allowedips = append(allowedips, getEgressIPs(&node, &peer)...)
|
||||
}
|
||||
nodePeerMap[peerHost.PublicKey.String()] = models.PeerRouteInfo{
|
||||
PeerAddr: net.IPNet{
|
||||
IP: net.ParseIP(peer.PrimaryAddress()),
|
||||
Mask: getCIDRMaskFromAddr(peer.PrimaryAddress()),
|
||||
},
|
||||
PeerKey: peerHost.PublicKey.String(),
|
||||
Allow: true,
|
||||
if peer.Action != models.NODE_DELETE &&
|
||||
!peer.PendingDelete &&
|
||||
peer.Connected &&
|
||||
nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(peer.ID.String())) &&
|
||||
(deletedNode == nil || (deletedNode != nil && peer.ID.String() != deletedNode.ID.String())) {
|
||||
peerConfig.AllowedIPs = allowedips // only append allowed IPs if valid connection
|
||||
}
|
||||
}
|
||||
|
||||
var nodePeer wgtypes.PeerConfig
|
||||
if _, ok := hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()]; !ok {
|
||||
hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()] = make(map[string]models.IDandAddr)
|
||||
hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig)
|
||||
peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1
|
||||
hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()][peer.ID.String()] = models.IDandAddr{
|
||||
ID: peer.ID.String(),
|
||||
Address: peer.PrimaryAddress(),
|
||||
Name: peerHost.Name,
|
||||
Network: peer.Network,
|
||||
}
|
||||
nodePeer = peerConfig
|
||||
} else {
|
||||
peerAllowedIPs := hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].AllowedIPs
|
||||
peerAllowedIPs = append(peerAllowedIPs, allowedips...)
|
||||
hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].AllowedIPs = peerAllowedIPs
|
||||
hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()][peer.ID.String()] = models.IDandAddr{
|
||||
ID: peer.ID.String(),
|
||||
Address: peer.PrimaryAddress(),
|
||||
Name: peerHost.Name,
|
||||
Network: peer.Network,
|
||||
}
|
||||
nodePeer = hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]]
|
||||
}
|
||||
|
||||
if node.Network == network { // add to peers map for metrics
|
||||
hostPeerUpdate.PeerIDs[peerHost.PublicKey.String()] = models.IDandAddr{
|
||||
ID: peer.ID.String(),
|
||||
Address: peer.PrimaryAddress(),
|
||||
Name: peerHost.Name,
|
||||
Network: peer.Network,
|
||||
}
|
||||
hostPeerUpdate.NodePeers = append(hostPeerUpdate.NodePeers, nodePeer)
|
||||
}
|
||||
}
|
||||
var extPeers []wgtypes.PeerConfig
|
||||
var extPeerIDAndAddrs []models.IDandAddr
|
||||
if node.IsIngressGateway {
|
||||
extPeers, extPeerIDAndAddrs, err = getExtPeers(&node)
|
||||
if err == nil {
|
||||
for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
|
||||
extPeerIdAndAddr := extPeerIdAndAddr
|
||||
nodePeerMap[extPeerIdAndAddr.ID] = models.PeerRouteInfo{
|
||||
if node.IsIngressGateway || node.IsEgressGateway {
|
||||
if peer.IsIngressGateway {
|
||||
_, extPeerIDAndAddrs, err := getExtPeers(&peer)
|
||||
if err == nil {
|
||||
for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
|
||||
extPeerIdAndAddr := extPeerIdAndAddr
|
||||
nodePeerMap[extPeerIdAndAddr.ID] = models.PeerRouteInfo{
|
||||
PeerAddr: net.IPNet{
|
||||
IP: net.ParseIP(extPeerIdAndAddr.Address),
|
||||
Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
|
||||
},
|
||||
PeerKey: extPeerIdAndAddr.ID,
|
||||
Allow: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.IsIngressGateway && peer.IsEgressGateway {
|
||||
hostPeerUpdate.IngressInfo.EgressRanges = append(hostPeerUpdate.IngressInfo.EgressRanges,
|
||||
peer.EgressGatewayRanges...)
|
||||
}
|
||||
nodePeerMap[peerHost.PublicKey.String()] = models.PeerRouteInfo{
|
||||
PeerAddr: net.IPNet{
|
||||
IP: net.ParseIP(extPeerIdAndAddr.Address),
|
||||
Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
|
||||
IP: net.ParseIP(peer.PrimaryAddress()),
|
||||
Mask: getCIDRMaskFromAddr(peer.PrimaryAddress()),
|
||||
},
|
||||
PeerKey: extPeerIdAndAddr.ID,
|
||||
PeerKey: peerHost.PublicKey.String(),
|
||||
Allow: true,
|
||||
}
|
||||
}
|
||||
hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...)
|
||||
for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
|
||||
extPeerIdAndAddr := extPeerIdAndAddr
|
||||
hostPeerUpdate.HostPeerIDs[extPeerIdAndAddr.ID] = make(map[string]models.IDandAddr)
|
||||
hostPeerUpdate.HostPeerIDs[extPeerIdAndAddr.ID][extPeerIdAndAddr.ID] = models.IDandAddr{
|
||||
ID: extPeerIdAndAddr.ID,
|
||||
Address: extPeerIdAndAddr.Address,
|
||||
Name: extPeerIdAndAddr.Name,
|
||||
Network: node.Network,
|
||||
|
||||
var nodePeer wgtypes.PeerConfig
|
||||
if _, ok := hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()]; !ok {
|
||||
hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()] = make(map[string]models.IDandAddr)
|
||||
hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig)
|
||||
peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1
|
||||
hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()][peer.ID.String()] = models.IDandAddr{
|
||||
ID: peer.ID.String(),
|
||||
Address: peer.PrimaryAddress(),
|
||||
Name: peerHost.Name,
|
||||
Network: peer.Network,
|
||||
}
|
||||
hostPeerUpdate.IngressInfo.ExtPeers[extPeerIdAndAddr.ID] = models.ExtClientInfo{
|
||||
Masquerade: true,
|
||||
IngGwAddr: net.IPNet{
|
||||
IP: net.ParseIP(node.PrimaryAddress()),
|
||||
Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
|
||||
},
|
||||
Network: node.PrimaryNetworkRange(),
|
||||
ExtPeerAddr: net.IPNet{
|
||||
IP: net.ParseIP(extPeerIdAndAddr.Address),
|
||||
Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
|
||||
},
|
||||
ExtPeerKey: extPeerIdAndAddr.ID,
|
||||
Peers: nodePeerMap,
|
||||
}
|
||||
if node.Network == network {
|
||||
hostPeerUpdate.PeerIDs[extPeerIdAndAddr.ID] = extPeerIdAndAddr
|
||||
hostPeerUpdate.NodePeers = append(hostPeerUpdate.NodePeers, extPeers...)
|
||||
nodePeer = peerConfig
|
||||
} else {
|
||||
peerAllowedIPs := hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].AllowedIPs
|
||||
peerAllowedIPs = append(peerAllowedIPs, allowedips...)
|
||||
hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].AllowedIPs = peerAllowedIPs
|
||||
hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()][peer.ID.String()] = models.IDandAddr{
|
||||
ID: peer.ID.String(),
|
||||
Address: peer.PrimaryAddress(),
|
||||
Name: peerHost.Name,
|
||||
Network: peer.Network,
|
||||
}
|
||||
nodePeer = hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]]
|
||||
}
|
||||
|
||||
if node.Network == network { // add to peers map for metrics
|
||||
hostPeerUpdate.PeerIDs[peerHost.PublicKey.String()] = models.IDandAddr{
|
||||
ID: peer.ID.String(),
|
||||
Address: peer.PrimaryAddress(),
|
||||
Name: peerHost.Name,
|
||||
Network: peer.Network,
|
||||
}
|
||||
hostPeerUpdate.NodePeers = append(hostPeerUpdate.NodePeers, nodePeer)
|
||||
}
|
||||
} else if !database.IsEmptyRecord(err) {
|
||||
logger.Log(1, "error retrieving external clients:", err.Error())
|
||||
}
|
||||
}
|
||||
if node.IsEgressGateway {
|
||||
hostPeerUpdate.EgressInfo[node.ID.String()] = models.EgressInfo{
|
||||
EgressID: node.ID.String(),
|
||||
Network: node.PrimaryNetworkRange(),
|
||||
EgressGwAddr: net.IPNet{
|
||||
IP: net.ParseIP(node.PrimaryAddress()),
|
||||
Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
|
||||
},
|
||||
GwPeers: nodePeerMap,
|
||||
EgressGWCfg: node.EgressGatewayRequest,
|
||||
var extPeers []wgtypes.PeerConfig
|
||||
var extPeerIDAndAddrs []models.IDandAddr
|
||||
if node.IsIngressGateway {
|
||||
extPeers, extPeerIDAndAddrs, err = getExtPeers(&node)
|
||||
if err == nil {
|
||||
for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
|
||||
extPeerIdAndAddr := extPeerIdAndAddr
|
||||
nodePeerMap[extPeerIdAndAddr.ID] = models.PeerRouteInfo{
|
||||
PeerAddr: net.IPNet{
|
||||
IP: net.ParseIP(extPeerIdAndAddr.Address),
|
||||
Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
|
||||
},
|
||||
PeerKey: extPeerIdAndAddr.ID,
|
||||
Allow: true,
|
||||
}
|
||||
}
|
||||
hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...)
|
||||
for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
|
||||
extPeerIdAndAddr := extPeerIdAndAddr
|
||||
hostPeerUpdate.HostPeerIDs[extPeerIdAndAddr.ID] = make(map[string]models.IDandAddr)
|
||||
hostPeerUpdate.HostPeerIDs[extPeerIdAndAddr.ID][extPeerIdAndAddr.ID] = models.IDandAddr{
|
||||
ID: extPeerIdAndAddr.ID,
|
||||
Address: extPeerIdAndAddr.Address,
|
||||
Name: extPeerIdAndAddr.Name,
|
||||
Network: node.Network,
|
||||
}
|
||||
hostPeerUpdate.IngressInfo.ExtPeers[extPeerIdAndAddr.ID] = models.ExtClientInfo{
|
||||
Masquerade: true,
|
||||
IngGwAddr: net.IPNet{
|
||||
IP: net.ParseIP(node.PrimaryAddress()),
|
||||
Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
|
||||
},
|
||||
Network: node.PrimaryNetworkRange(),
|
||||
ExtPeerAddr: net.IPNet{
|
||||
IP: net.ParseIP(extPeerIdAndAddr.Address),
|
||||
Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address),
|
||||
},
|
||||
ExtPeerKey: extPeerIdAndAddr.ID,
|
||||
Peers: nodePeerMap,
|
||||
}
|
||||
if node.Network == network {
|
||||
hostPeerUpdate.PeerIDs[extPeerIdAndAddr.ID] = extPeerIdAndAddr
|
||||
hostPeerUpdate.NodePeers = append(hostPeerUpdate.NodePeers, extPeers...)
|
||||
}
|
||||
}
|
||||
} else if !database.IsEmptyRecord(err) {
|
||||
logger.Log(1, "error retrieving external clients:", err.Error())
|
||||
}
|
||||
}
|
||||
if node.IsEgressGateway {
|
||||
hostPeerUpdate.EgressInfo[node.ID.String()] = models.EgressInfo{
|
||||
EgressID: node.ID.String(),
|
||||
Network: node.PrimaryNetworkRange(),
|
||||
EgressGwAddr: net.IPNet{
|
||||
IP: net.ParseIP(node.PrimaryAddress()),
|
||||
Mask: getCIDRMaskFromAddr(node.PrimaryAddress()),
|
||||
},
|
||||
GwPeers: nodePeerMap,
|
||||
EgressGWCfg: node.EgressGatewayRequest,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// run through delete nodes
|
||||
if len(deletedNodes) > 0 {
|
||||
for i := range deletedNodes {
|
||||
delNode := deletedNodes[i]
|
||||
delHost, err := GetHost(delNode.HostID.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if _, ok := hostPeerUpdate.HostPeerIDs[delHost.PublicKey.String()]; !ok {
|
||||
var peerConfig = wgtypes.PeerConfig{}
|
||||
peerConfig.PublicKey = delHost.PublicKey
|
||||
peerConfig.Endpoint = &net.UDPAddr{
|
||||
IP: delHost.EndpointIP,
|
||||
Port: GetPeerListenPort(delHost),
|
||||
}
|
||||
peerConfig.Remove = true
|
||||
peerConfig.AllowedIPs = []net.IPNet{delNode.Address, delNode.Address6}
|
||||
hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig)
|
||||
}
|
||||
// == post peer calculations ==
|
||||
// indicate removal if no allowed IPs were calculated
|
||||
for i := range hostPeerUpdate.Peers {
|
||||
peer := hostPeerUpdate.Peers[i]
|
||||
if len(peer.AllowedIPs) == 0 {
|
||||
peer.Remove = true
|
||||
}
|
||||
hostPeerUpdate.Peers[i] = peer
|
||||
}
|
||||
|
||||
for i := range hostPeerUpdate.NodePeers {
|
||||
peer := hostPeerUpdate.NodePeers[i]
|
||||
if len(peer.AllowedIPs) == 0 {
|
||||
peer.Remove = true
|
||||
}
|
||||
hostPeerUpdate.NodePeers[i] = peer
|
||||
}
|
||||
|
||||
return hostPeerUpdate, nil
|
||||
|
|
2
main.go
2
main.go
|
@ -27,7 +27,7 @@ import (
|
|||
stunserver "github.com/gravitl/netmaker/stun-server"
|
||||
)
|
||||
|
||||
var version = "v0.18.2"
|
||||
var version = "v0.18.3"
|
||||
|
||||
// Start DB Connection and start API Request Handler
|
||||
func main() {
|
||||
|
|
|
@ -8,12 +8,17 @@ import (
|
|||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
const PLACEHOLDER_KEY_TEXT = "ACCESS_KEY"
|
||||
const PLACEHOLDER_TOKEN_TEXT = "ACCESS_TOKEN"
|
||||
const (
|
||||
// PLACEHOLDER_KEY_TEXT - access key placeholder text if option turned off
|
||||
PLACEHOLDER_KEY_TEXT = "ACCESS_KEY"
|
||||
// PLACEHOLDER_TOKEN_TEXT - access key token placeholder text if option turned off
|
||||
PLACEHOLDER_TOKEN_TEXT = "ACCESS_TOKEN"
|
||||
)
|
||||
|
||||
// CustomExtClient - struct for CustomExtClient params
|
||||
type CustomExtClient struct {
|
||||
ClientID string `json:"clientid"`
|
||||
ClientID string `json:"clientid"`
|
||||
PublicKey string `json:"publickey,omitempty"`
|
||||
}
|
||||
|
||||
// AuthParams - struct for auth params
|
||||
|
@ -223,20 +228,20 @@ type NodeJoinResponse struct {
|
|||
|
||||
// ServerConfig - struct for dealing with the server information for a netclient
|
||||
type ServerConfig struct {
|
||||
CoreDNSAddr string `yaml:"corednsaddr"`
|
||||
API string `yaml:"api"`
|
||||
APIPort string `yaml:"apiport"`
|
||||
DNSMode string `yaml:"dnsmode"`
|
||||
Version string `yaml:"version"`
|
||||
MQPort string `yaml:"mqport"`
|
||||
MQUserName string `yaml:"mq_username"`
|
||||
MQPassword string `yaml:"mq_password"`
|
||||
Server string `yaml:"server"`
|
||||
Broker string `yaml:"broker"`
|
||||
Is_EE bool `yaml:"isee"`
|
||||
StunPort int `yaml:"stun_port"`
|
||||
StunHost string `yaml:"stun_host"`
|
||||
TrafficKey []byte `yaml:"traffickey"`
|
||||
CoreDNSAddr string `yaml:"corednsaddr"`
|
||||
API string `yaml:"api"`
|
||||
APIPort string `yaml:"apiport"`
|
||||
DNSMode string `yaml:"dnsmode"`
|
||||
Version string `yaml:"version"`
|
||||
MQPort string `yaml:"mqport"`
|
||||
MQUserName string `yaml:"mq_username"`
|
||||
MQPassword string `yaml:"mq_password"`
|
||||
Server string `yaml:"server"`
|
||||
Broker string `yaml:"broker"`
|
||||
Is_EE bool `yaml:"isee"`
|
||||
StunPort int `yaml:"stun_port"`
|
||||
StunList []StunServer `yaml:"stun_list"`
|
||||
TrafficKey []byte `yaml:"traffickey"`
|
||||
}
|
||||
|
||||
// User.NameInCharset - returns if name is in charset below or not
|
||||
|
@ -261,3 +266,9 @@ type JoinData struct {
|
|||
Node Node `json:"node" yaml:"node"`
|
||||
Key string `json:"key" yaml:"key"`
|
||||
}
|
||||
|
||||
// StunServer - struct to hold data required for using stun server
|
||||
type StunServer struct {
|
||||
Domain string `json:"domain" yaml:"domain"`
|
||||
Port int `json:"port" yaml:"port"`
|
||||
}
|
||||
|
|
417
mq/handlers.go
417
mq/handlers.go
|
@ -1,11 +1,13 @@
|
|||
package mq
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gravitl/netmaker/database"
|
||||
"github.com/gravitl/netmaker/logger"
|
||||
"github.com/gravitl/netmaker/logic"
|
||||
|
@ -22,22 +24,29 @@ func DefaultHandler(client mqtt.Client, msg mqtt.Message) {
|
|||
|
||||
// Ping message Handler -- handles ping topic from client nodes
|
||||
func Ping(client mqtt.Client, msg mqtt.Message) {
|
||||
go func() {
|
||||
id, err := getID(msg.Topic())
|
||||
if err != nil {
|
||||
logger.Log(0, "error getting node.ID sent on ping topic ")
|
||||
return
|
||||
}
|
||||
id, err := getID(msg.Topic())
|
||||
if err != nil {
|
||||
logger.Log(0, "error getting node.ID sent on ping topic ")
|
||||
return
|
||||
}
|
||||
node, err := logic.GetNodeByID(id)
|
||||
if err != nil {
|
||||
logger.Log(3, "mq-ping error getting node: ", err.Error())
|
||||
node, err := logic.GetNodeByID(id)
|
||||
if err != nil {
|
||||
logger.Log(3, "mq-ping error getting node: ", err.Error())
|
||||
record, err := database.FetchRecord(database.NODES_TABLE_NAME, id)
|
||||
if err != nil {
|
||||
logger.Log(3, "error reading database ", err.Error())
|
||||
return
|
||||
if database.IsEmptyRecord(err) {
|
||||
h := logic.GetHostByNodeID(id) // check if a host is still associated
|
||||
if h != nil { // inform host that node should be removed
|
||||
fakeNode := models.Node{}
|
||||
fakeNode.ID, _ = uuid.Parse(id)
|
||||
fakeNode.Action = models.NODE_DELETE
|
||||
fakeNode.PendingDelete = true
|
||||
if err := NodeUpdate(&fakeNode); err != nil {
|
||||
logger.Log(0, "failed to inform host", h.Name, h.ID.String(), "to remove node", id, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.Log(3, "record from database")
|
||||
logger.Log(3, record)
|
||||
return
|
||||
}
|
||||
decrypted, decryptErr := decryptMsg(&node, msg.Payload())
|
||||
|
@ -67,15 +76,163 @@ func Ping(client mqtt.Client, msg mqtt.Message) {
|
|||
return
|
||||
}
|
||||
|
||||
logger.Log(3, "ping processed for node", node.ID.String())
|
||||
// --TODO --set client version once feature is implemented.
|
||||
//node.SetClientVersion(msg.Payload())
|
||||
}()
|
||||
return
|
||||
}
|
||||
decrypted, decryptErr := decryptMsg(&node, msg.Payload())
|
||||
if decryptErr != nil {
|
||||
logger.Log(0, "error decrypting when updating node ", node.ID.String(), decryptErr.Error())
|
||||
return
|
||||
}
|
||||
var checkin models.NodeCheckin
|
||||
if err := json.Unmarshal(decrypted, &checkin); err != nil {
|
||||
logger.Log(1, "error unmarshaling payload ", err.Error())
|
||||
return
|
||||
}
|
||||
host, err := logic.GetHost(node.HostID.String())
|
||||
if err != nil {
|
||||
logger.Log(0, "error retrieving host for node ", node.ID.String(), err.Error())
|
||||
return
|
||||
}
|
||||
node.SetLastCheckIn()
|
||||
host.Version = checkin.Version
|
||||
node.Connected = checkin.Connected
|
||||
host.Interfaces = checkin.Ifaces
|
||||
for i := range host.Interfaces {
|
||||
host.Interfaces[i].AddressString = host.Interfaces[i].Address.String()
|
||||
}
|
||||
if err := logic.UpdateNode(&node, &node); err != nil {
|
||||
logger.Log(0, "error updating node", node.ID.String(), " on checkin", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
logger.Log(3, "ping processed for node", node.ID.String())
|
||||
// --TODO --set client version once feature is implemented.
|
||||
//node.SetClientVersion(msg.Payload())
|
||||
}
|
||||
|
||||
// UpdateNode message Handler -- handles updates from client nodes
|
||||
func UpdateNode(client mqtt.Client, msg mqtt.Message) {
|
||||
go func() {
|
||||
id, err := getID(msg.Topic())
|
||||
if err != nil {
|
||||
logger.Log(1, "error getting node.ID sent on ", msg.Topic(), err.Error())
|
||||
return
|
||||
}
|
||||
currentNode, err := logic.GetNodeByID(id)
|
||||
if err != nil {
|
||||
logger.Log(1, "error getting node ", id, err.Error())
|
||||
return
|
||||
}
|
||||
decrypted, decryptErr := decryptMsg(¤tNode, msg.Payload())
|
||||
if decryptErr != nil {
|
||||
logger.Log(1, "failed to decrypt message for node ", id, decryptErr.Error())
|
||||
return
|
||||
}
|
||||
var newNode models.Node
|
||||
if err := json.Unmarshal(decrypted, &newNode); err != nil {
|
||||
logger.Log(1, "error unmarshaling payload ", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ifaceDelta := logic.IfaceDelta(¤tNode, &newNode)
|
||||
if servercfg.Is_EE && ifaceDelta {
|
||||
if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID, currentNode.Network); err != nil {
|
||||
logger.Log(1, "failed to reset failover list during node update", currentNode.ID.String(), currentNode.Network)
|
||||
}
|
||||
}
|
||||
newNode.SetLastCheckIn()
|
||||
if err := logic.UpdateNode(¤tNode, &newNode); err != nil {
|
||||
logger.Log(1, "error saving node", err.Error())
|
||||
return
|
||||
}
|
||||
if ifaceDelta { // reduce number of unneeded updates, by only sending on iface changes
|
||||
if err = PublishPeerUpdate(); err != nil {
|
||||
logger.Log(0, "error updating peers when node", currentNode.ID.String(), "informed the server of an interface change", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
logger.Log(1, "updated node", id, newNode.ID.String())
|
||||
}
|
||||
|
||||
// UpdateHost message Handler -- handles host updates from clients
|
||||
func UpdateHost(client mqtt.Client, msg mqtt.Message) {
|
||||
id, err := getID(msg.Topic())
|
||||
if err != nil {
|
||||
logger.Log(1, "error getting host.ID sent on ", msg.Topic(), err.Error())
|
||||
return
|
||||
}
|
||||
currentHost, err := logic.GetHost(id)
|
||||
if err != nil {
|
||||
logger.Log(1, "error getting host ", id, err.Error())
|
||||
return
|
||||
}
|
||||
decrypted, decryptErr := decryptMsgWithHost(currentHost, msg.Payload())
|
||||
if decryptErr != nil {
|
||||
logger.Log(1, "failed to decrypt message for host ", id, decryptErr.Error())
|
||||
return
|
||||
}
|
||||
var hostUpdate models.HostUpdate
|
||||
if err := json.Unmarshal(decrypted, &hostUpdate); err != nil {
|
||||
logger.Log(1, "error unmarshaling payload ", err.Error())
|
||||
return
|
||||
}
|
||||
logger.Log(3, fmt.Sprintf("recieved host update: %s\n", hostUpdate.Host.ID.String()))
|
||||
var sendPeerUpdate bool
|
||||
switch hostUpdate.Action {
|
||||
case models.Acknowledgement:
|
||||
hu := hostactions.GetAction(currentHost.ID.String())
|
||||
if hu != nil {
|
||||
if err = HostUpdate(hu); err != nil {
|
||||
logger.Log(0, "failed to send new node to host", hostUpdate.Host.Name, currentHost.ID.String(), err.Error())
|
||||
return
|
||||
} else {
|
||||
if err = PublishSingleHostPeerUpdate(context.Background(), currentHost, nil); err != nil {
|
||||
logger.Log(0, "failed peers publish after join acknowledged", hostUpdate.Host.Name, currentHost.ID.String(), err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
case models.UpdateHost:
|
||||
sendPeerUpdate = logic.UpdateHostFromClient(&hostUpdate.Host, currentHost)
|
||||
err := logic.UpsertHost(currentHost)
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to update host: ", currentHost.ID.String(), err.Error())
|
||||
return
|
||||
}
|
||||
case models.DeleteHost:
|
||||
if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
|
||||
// delete EMQX credentials for host
|
||||
if err := DeleteEmqxUser(currentHost.ID.String()); err != nil {
|
||||
logger.Log(0, "failed to remove host credentials from EMQX: ", currentHost.ID.String(), err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := logic.DisassociateAllNodesFromHost(currentHost.ID.String()); err != nil {
|
||||
logger.Log(0, "failed to delete all nodes of host: ", currentHost.ID.String(), err.Error())
|
||||
return
|
||||
}
|
||||
if err := logic.RemoveHostByID(currentHost.ID.String()); err != nil {
|
||||
logger.Log(0, "failed to delete host: ", currentHost.ID.String(), err.Error())
|
||||
return
|
||||
}
|
||||
sendPeerUpdate = true
|
||||
}
|
||||
|
||||
if sendPeerUpdate {
|
||||
err := PublishPeerUpdate()
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to pulish peer update: ", err.Error())
|
||||
}
|
||||
}
|
||||
// if servercfg.Is_EE && ifaceDelta {
|
||||
// if err = logic.EnterpriseResetAllPeersFailovers(currentHost.ID.String(), currentHost.Network); err != nil {
|
||||
// logger.Log(1, "failed to reset failover list during node update", currentHost.ID.String(), currentHost.Network)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
// UpdateMetrics message Handler -- handles updates from client nodes for metrics
|
||||
func UpdateMetrics(client mqtt.Client, msg mqtt.Message) {
|
||||
if servercfg.Is_EE {
|
||||
id, err := getID(msg.Topic())
|
||||
if err != nil {
|
||||
logger.Log(1, "error getting node.ID sent on ", msg.Topic(), err.Error())
|
||||
|
@ -91,209 +248,75 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
|
|||
logger.Log(1, "failed to decrypt message for node ", id, decryptErr.Error())
|
||||
return
|
||||
}
|
||||
var newNode models.Node
|
||||
if err := json.Unmarshal(decrypted, &newNode); err != nil {
|
||||
|
||||
var newMetrics models.Metrics
|
||||
if err := json.Unmarshal(decrypted, &newMetrics); err != nil {
|
||||
logger.Log(1, "error unmarshaling payload ", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ifaceDelta := logic.IfaceDelta(¤tNode, &newNode)
|
||||
if servercfg.Is_EE && ifaceDelta {
|
||||
if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID, currentNode.Network); err != nil {
|
||||
logger.Log(1, "failed to reset failover list during node update", currentNode.ID.String(), currentNode.Network)
|
||||
}
|
||||
}
|
||||
newNode.SetLastCheckIn()
|
||||
if err := logic.UpdateNode(¤tNode, &newNode); err != nil {
|
||||
logger.Log(1, "error saving node", err.Error())
|
||||
shouldUpdate := updateNodeMetrics(¤tNode, &newMetrics)
|
||||
|
||||
if err = logic.UpdateMetrics(id, &newMetrics); err != nil {
|
||||
logger.Log(1, "faield to update node metrics", id, err.Error())
|
||||
return
|
||||
}
|
||||
if ifaceDelta { // reduce number of unneeded updates, by only sending on iface changes
|
||||
if err = PublishPeerUpdate(); err != nil {
|
||||
logger.Log(0, "error updating peers when node", currentNode.ID.String(), "informed the server of an interface change", err.Error())
|
||||
if servercfg.IsMetricsExporter() {
|
||||
if err := pushMetricsToExporter(newMetrics); err != nil {
|
||||
logger.Log(2, fmt.Sprintf("failed to push node: [%s] metrics to exporter, err: %v",
|
||||
currentNode.ID, err))
|
||||
}
|
||||
}
|
||||
|
||||
logger.Log(1, "updated node", id, newNode.ID.String())
|
||||
|
||||
}()
|
||||
}
|
||||
|
||||
// UpdateHost message Handler -- handles host updates from clients
|
||||
func UpdateHost(client mqtt.Client, msg mqtt.Message) {
|
||||
go func(msg mqtt.Message) {
|
||||
id, err := getID(msg.Topic())
|
||||
if err != nil {
|
||||
logger.Log(1, "error getting host.ID sent on ", msg.Topic(), err.Error())
|
||||
return
|
||||
}
|
||||
currentHost, err := logic.GetHost(id)
|
||||
if err != nil {
|
||||
logger.Log(1, "error getting host ", id, err.Error())
|
||||
return
|
||||
}
|
||||
decrypted, decryptErr := decryptMsgWithHost(currentHost, msg.Payload())
|
||||
if decryptErr != nil {
|
||||
logger.Log(1, "failed to decrypt message for host ", id, decryptErr.Error())
|
||||
return
|
||||
}
|
||||
var hostUpdate models.HostUpdate
|
||||
if err := json.Unmarshal(decrypted, &hostUpdate); err != nil {
|
||||
logger.Log(1, "error unmarshaling payload ", err.Error())
|
||||
return
|
||||
}
|
||||
logger.Log(3, fmt.Sprintf("recieved host update: %s\n", hostUpdate.Host.ID.String()))
|
||||
var sendPeerUpdate bool
|
||||
switch hostUpdate.Action {
|
||||
case models.Acknowledgement:
|
||||
hu := hostactions.GetAction(currentHost.ID.String())
|
||||
if hu != nil {
|
||||
if err = HostUpdate(hu); err != nil {
|
||||
logger.Log(0, "failed to send new node to host", hostUpdate.Host.Name, currentHost.ID.String(), err.Error())
|
||||
return
|
||||
} else {
|
||||
if err = PublishSingleHostPeerUpdate(currentHost, nil); err != nil {
|
||||
logger.Log(0, "failed peers publish after join acknowledged", hostUpdate.Host.Name, currentHost.ID.String(), err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
case models.UpdateHost:
|
||||
sendPeerUpdate = logic.UpdateHostFromClient(&hostUpdate.Host, currentHost)
|
||||
err := logic.UpsertHost(currentHost)
|
||||
if newMetrics.Connectivity != nil {
|
||||
err := logic.EnterpriseFailoverFunc(¤tNode)
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to update host: ", currentHost.ID.String(), err.Error())
|
||||
return
|
||||
}
|
||||
case models.DeleteHost:
|
||||
if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
|
||||
// delete EMQX credentials for host
|
||||
if err := DeleteEmqxUser(currentHost.ID.String()); err != nil {
|
||||
logger.Log(0, "failed to remove host credentials from EMQX: ", currentHost.ID.String(), err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := logic.DisassociateAllNodesFromHost(currentHost.ID.String()); err != nil {
|
||||
logger.Log(0, "failed to delete all nodes of host: ", currentHost.ID.String(), err.Error())
|
||||
return
|
||||
}
|
||||
if err := logic.RemoveHostByID(currentHost.ID.String()); err != nil {
|
||||
logger.Log(0, "failed to delete host: ", currentHost.ID.String(), err.Error())
|
||||
return
|
||||
}
|
||||
sendPeerUpdate = true
|
||||
}
|
||||
|
||||
if sendPeerUpdate {
|
||||
err := PublishPeerUpdate()
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to pulish peer update: ", err.Error())
|
||||
logger.Log(0, "failed to failover for node", currentNode.ID.String(), "on network", currentNode.Network, "-", err.Error())
|
||||
}
|
||||
}
|
||||
// if servercfg.Is_EE && ifaceDelta {
|
||||
// if err = logic.EnterpriseResetAllPeersFailovers(currentHost.ID.String(), currentHost.Network); err != nil {
|
||||
// logger.Log(1, "failed to reset failover list during node update", currentHost.ID.String(), currentHost.Network)
|
||||
// }
|
||||
// }
|
||||
|
||||
}(msg)
|
||||
}
|
||||
|
||||
// UpdateMetrics message Handler -- handles updates from client nodes for metrics
|
||||
func UpdateMetrics(client mqtt.Client, msg mqtt.Message) {
|
||||
if servercfg.Is_EE {
|
||||
go func() {
|
||||
id, err := getID(msg.Topic())
|
||||
if err != nil {
|
||||
logger.Log(1, "error getting node.ID sent on ", msg.Topic(), err.Error())
|
||||
return
|
||||
}
|
||||
currentNode, err := logic.GetNodeByID(id)
|
||||
if err != nil {
|
||||
logger.Log(1, "error getting node ", id, err.Error())
|
||||
return
|
||||
}
|
||||
decrypted, decryptErr := decryptMsg(¤tNode, msg.Payload())
|
||||
if decryptErr != nil {
|
||||
logger.Log(1, "failed to decrypt message for node ", id, decryptErr.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var newMetrics models.Metrics
|
||||
if err := json.Unmarshal(decrypted, &newMetrics); err != nil {
|
||||
logger.Log(1, "error unmarshaling payload ", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
shouldUpdate := updateNodeMetrics(¤tNode, &newMetrics)
|
||||
|
||||
if err = logic.UpdateMetrics(id, &newMetrics); err != nil {
|
||||
logger.Log(1, "faield to update node metrics", id, err.Error())
|
||||
return
|
||||
}
|
||||
if servercfg.IsMetricsExporter() {
|
||||
if err := pushMetricsToExporter(newMetrics); err != nil {
|
||||
logger.Log(2, fmt.Sprintf("failed to push node: [%s] metrics to exporter, err: %v",
|
||||
currentNode.ID, err))
|
||||
if shouldUpdate {
|
||||
logger.Log(2, "updating peers after node", currentNode.ID.String(), currentNode.Network, "detected connectivity issues")
|
||||
host, err := logic.GetHost(currentNode.HostID.String())
|
||||
if err == nil {
|
||||
if err = PublishSingleHostPeerUpdate(context.Background(), host, nil); err != nil {
|
||||
logger.Log(0, "failed to publish update after failover peer change for node", currentNode.ID.String(), currentNode.Network)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if newMetrics.Connectivity != nil {
|
||||
err := logic.EnterpriseFailoverFunc(¤tNode)
|
||||
if err != nil {
|
||||
logger.Log(0, "failed to failover for node", currentNode.ID.String(), "on network", currentNode.Network, "-", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if shouldUpdate {
|
||||
logger.Log(2, "updating peers after node", currentNode.ID.String(), currentNode.Network, "detected connectivity issues")
|
||||
host, err := logic.GetHost(currentNode.HostID.String())
|
||||
if err == nil {
|
||||
if err = PublishSingleHostPeerUpdate(host, nil); err != nil {
|
||||
logger.Log(0, "failed to publish update after failover peer change for node", currentNode.ID.String(), currentNode.Network)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.Log(1, "updated node metrics", id)
|
||||
}()
|
||||
logger.Log(1, "updated node metrics", id)
|
||||
}
|
||||
}
|
||||
|
||||
// ClientPeerUpdate message handler -- handles updating peers after signal from client nodes
|
||||
func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
|
||||
go func() {
|
||||
id, err := getID(msg.Topic())
|
||||
if err != nil {
|
||||
logger.Log(1, "error getting node.ID sent on ", msg.Topic(), err.Error())
|
||||
return
|
||||
}
|
||||
currentNode, err := logic.GetNodeByID(id)
|
||||
if err != nil {
|
||||
logger.Log(1, "error getting node ", id, err.Error())
|
||||
return
|
||||
}
|
||||
decrypted, decryptErr := decryptMsg(¤tNode, msg.Payload())
|
||||
if decryptErr != nil {
|
||||
logger.Log(1, "failed to decrypt message during client peer update for node ", id, decryptErr.Error())
|
||||
return
|
||||
}
|
||||
switch decrypted[0] {
|
||||
case ncutils.ACK:
|
||||
//do we still need this
|
||||
case ncutils.DONE:
|
||||
updateNodePeers(¤tNode)
|
||||
}
|
||||
|
||||
logger.Log(1, "sent peer updates after signal received from", id)
|
||||
}()
|
||||
}
|
||||
|
||||
func updateNodePeers(currentNode *models.Node) {
|
||||
if err := PublishPeerUpdate(); err != nil {
|
||||
logger.Log(1, "error publishing peer update ", err.Error())
|
||||
id, err := getID(msg.Topic())
|
||||
if err != nil {
|
||||
logger.Log(1, "error getting node.ID sent on ", msg.Topic(), err.Error())
|
||||
return
|
||||
}
|
||||
currentNode, err := logic.GetNodeByID(id)
|
||||
if err != nil {
|
||||
logger.Log(1, "error getting node ", id, err.Error())
|
||||
return
|
||||
}
|
||||
decrypted, decryptErr := decryptMsg(¤tNode, msg.Payload())
|
||||
if decryptErr != nil {
|
||||
logger.Log(1, "failed to decrypt message during client peer update for node ", id, decryptErr.Error())
|
||||
return
|
||||
}
|
||||
switch decrypted[0] {
|
||||
case ncutils.ACK:
|
||||
// do we still need this
|
||||
case ncutils.DONE:
|
||||
if err = PublishPeerUpdate(); err != nil {
|
||||
logger.Log(1, "error publishing peer update for node", currentNode.ID.String(), err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
logger.Log(1, "sent peer updates after signal received from", id)
|
||||
}
|
||||
|
||||
func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) bool {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package mq
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -23,10 +24,10 @@ func PublishPeerUpdate() error {
|
|||
logger.Log(1, "err getting all hosts", err.Error())
|
||||
return err
|
||||
}
|
||||
logic.ResetPeerUpdateContext()
|
||||
for _, host := range hosts {
|
||||
host := host
|
||||
err = PublishSingleHostPeerUpdate(&host, nil)
|
||||
if err != nil {
|
||||
if err = PublishSingleHostPeerUpdate(logic.PeerUpdateCtx, &host, nil); err != nil {
|
||||
logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error())
|
||||
}
|
||||
}
|
||||
|
@ -45,9 +46,10 @@ func PublishDeletedNodePeerUpdate(delNode *models.Node) error {
|
|||
logger.Log(1, "err getting all hosts", err.Error())
|
||||
return err
|
||||
}
|
||||
logic.ResetPeerUpdateContext()
|
||||
for _, host := range hosts {
|
||||
host := host
|
||||
if err = PublishSingleHostPeerUpdate(&host, delNode); err != nil {
|
||||
if err = PublishSingleHostPeerUpdate(logic.PeerUpdateCtx, &host, delNode); err != nil {
|
||||
logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error())
|
||||
}
|
||||
}
|
||||
|
@ -55,9 +57,9 @@ func PublishDeletedNodePeerUpdate(delNode *models.Node) error {
|
|||
}
|
||||
|
||||
// PublishSingleHostPeerUpdate --- determines and publishes a peer update to one host
|
||||
func PublishSingleHostPeerUpdate(host *models.Host, deletedNode *models.Node) error {
|
||||
func PublishSingleHostPeerUpdate(ctx context.Context, host *models.Host, deletedNode *models.Node) error {
|
||||
|
||||
peerUpdate, err := logic.GetPeerUpdateForHost("", host, deletedNode)
|
||||
peerUpdate, err := logic.GetPeerUpdateForHost(ctx, "", host, deletedNode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -65,7 +67,7 @@ func PublishSingleHostPeerUpdate(host *models.Host, deletedNode *models.Node) er
|
|||
return nil
|
||||
}
|
||||
if host.ProxyEnabled {
|
||||
proxyUpdate, err := logic.GetProxyUpdateForHost(host)
|
||||
proxyUpdate, err := logic.GetProxyUpdateForHost(ctx, host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -422,13 +424,12 @@ func sendPeers() {
|
|||
|
||||
//collectServerMetrics(networks[:])
|
||||
}
|
||||
|
||||
for _, host := range hosts {
|
||||
if force {
|
||||
if force {
|
||||
logic.ResetPeerUpdateContext()
|
||||
for _, host := range hosts {
|
||||
host := host
|
||||
logger.Log(2, "sending scheduled peer update (5 min)")
|
||||
err = PublishSingleHostPeerUpdate(&host, nil)
|
||||
if err != nil {
|
||||
if err = PublishSingleHostPeerUpdate(logic.PeerUpdateCtx, &host, nil); err != nil {
|
||||
logger.Log(1, "error publishing peer updates for host: ", host.ID.String(), " Err: ", err.Error())
|
||||
}
|
||||
}
|
||||
|
|
24
release.md
24
release.md
|
@ -1,7 +1,27 @@
|
|||
# Netmaker v0.18.1
|
||||
# Netmaker v0.18.3
|
||||
|
||||
## **Do not attempt upgrade from 0.17.x quite yet**
|
||||
|
||||
## whats new
|
||||
|
||||
- Enrollment Keys, give the ability for an admin to enroll clients into multiple networks, can be unlimited, time, or usage based
|
||||
- EMQX broker support and better MQTT support in general
|
||||
- Now you must specify BROKER_ENDPOINT
|
||||
- Also specify SERVER_BROKER_ENDPOINT, if not provided server will connect to broker over BROKER_ENDPOINT
|
||||
- Thsi gives ability for user to specify any broker endpoint and use any protocal on clients desired, such as, `mqtts://mybroker.com:8083`
|
||||
(we will still default to wss)
|
||||
|
||||
## whats fixed
|
||||
- Fixed default ACL behavior, should work as expected
|
||||
- Peer calculations enhancement
|
||||
- main routines share a context and docker stop/ctrl+c give expected results now
|
||||
- Github workflow edits
|
||||
- Removed Deprecated Local Network Range from client + server
|
||||
|
||||
## known issues
|
||||
- EnrollmentKeys may not function as intended in an HA setup
|
||||
- If a host does not receive a message to delete a node, it could become orphaned and un-deletable
|
||||
- Network interface routes may be removed after sometime/unintended network update
|
||||
- Upgrade script does not handle clients
|
||||
- Caddy does not handle netmaker exporter well for EE
|
||||
- Incorrect latency on metrics (EE)
|
||||
- Swagger docs not up to date
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
LATEST="v0.18.2"
|
||||
LATEST="v0.18.3"
|
||||
|
||||
print_logo() {(
|
||||
cat << "EOF"
|
||||
|
@ -40,7 +40,7 @@ usage () {(
|
|||
echo " \"branch\": - will install a specific branch using remote git and dockerhub "
|
||||
echo " -t tag of build; if buildtype=version, tag=version. If builtype=branch or builtype=local, tag=branch"
|
||||
echo "examples:"
|
||||
echo " nm-quick.sh -e -b version -t v0.18.2"
|
||||
echo " nm-quick.sh -e -b version -t v0.18.3"
|
||||
echo " nm-quick.sh -e -b local -t feature_v0.17.2_newfeature"
|
||||
echo " nm-quick.sh -e -b branch -t develop"
|
||||
exit 1
|
||||
|
|
|
@ -187,7 +187,7 @@ collect_server_settings() {
|
|||
|
||||
STUN_NAME="stun.$SERVER_NAME"
|
||||
echo "-----------------------------------------------------"
|
||||
echo "Netmaker v0.18.2 requires a new DNS entry for $STUN_NAME."
|
||||
echo "Netmaker v0.18.3 requires a new DNS entry for $STUN_NAME."
|
||||
echo "Please confirm this is added to your DNS provider before continuing"
|
||||
echo "(note: this is not required if using an nip.io address)"
|
||||
echo "-----------------------------------------------------"
|
||||
|
@ -245,7 +245,7 @@ set_compose() {
|
|||
sed -i "s/v0.17.1/testing/g" /root/docker-compose.yml
|
||||
|
||||
# RELEASE_REPLACE - Use this once release is ready
|
||||
#sed -i "s/v0.17.1/v0.18.2/g" /root/docker-compose.yml
|
||||
#sed -i "s/v0.17.1/v0.18.3/g" /root/docker-compose.yml
|
||||
yq ".services.netmaker.environment.SERVER_NAME = \"$SERVER_NAME\"" -i /root/docker-compose.yml
|
||||
yq ".services.netmaker.environment += {\"BROKER_NAME\": \"$BROKER_NAME\"}" -i /root/docker-compose.yml
|
||||
yq ".services.netmaker.environment += {\"STUN_NAME\": \"$STUN_NAME\"}" -i /root/docker-compose.yml
|
||||
|
@ -416,7 +416,7 @@ join_networks() {
|
|||
|
||||
# create an egress if necessary
|
||||
if [[ $HAS_EGRESS == "yes" ]]; then
|
||||
echo "Egress is currently unimplemented. Wait for 0.18.2"
|
||||
echo "Egress is currently unimplemented. Wait for 0.18.3"
|
||||
fi
|
||||
|
||||
echo "HAS INGRESS: $HAS_INGRESS"
|
||||
|
@ -447,7 +447,7 @@ join_networks() {
|
|||
cat << "EOF"
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
The Netmaker Upgrade Script: Upgrading to v0.18.2 so you don't have to!
|
||||
The Netmaker Upgrade Script: Upgrading to v0.18.3 so you don't have to!
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
EOF
|
||||
|
|
|
@ -43,7 +43,6 @@ func GetServerConfig() config.ServerConfig {
|
|||
cfg.AllowedOrigin = GetAllowedOrigin()
|
||||
cfg.RestBackend = "off"
|
||||
cfg.NodeID = GetNodeID()
|
||||
cfg.StunHost = GetStunAddr()
|
||||
cfg.StunPort = GetStunPort()
|
||||
cfg.BrokerType = GetBrokerType()
|
||||
cfg.EmqxRestEndpoint = GetEmqxRestEndpoint()
|
||||
|
@ -74,6 +73,7 @@ func GetServerConfig() config.ServerConfig {
|
|||
cfg.FrontendURL = GetFrontendURL()
|
||||
cfg.Telemetry = Telemetry()
|
||||
cfg.Server = GetServer()
|
||||
cfg.StunList = GetStunListString()
|
||||
cfg.Verbosity = GetVerbosity()
|
||||
cfg.IsEE = "no"
|
||||
if Is_EE {
|
||||
|
@ -99,8 +99,8 @@ func GetServerInfo() models.ServerConfig {
|
|||
}
|
||||
cfg.Version = GetVersion()
|
||||
cfg.Is_EE = Is_EE
|
||||
cfg.StunHost = GetStunAddr()
|
||||
cfg.StunPort = GetStunPort()
|
||||
cfg.StunList = GetStunList()
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
@ -177,15 +177,44 @@ func GetAPIPort() string {
|
|||
return apiport
|
||||
}
|
||||
|
||||
// GetStunAddr - gets the stun host address
|
||||
func GetStunAddr() string {
|
||||
stunAddr := ""
|
||||
if os.Getenv("STUN_DOMAIN") != "" {
|
||||
stunAddr = os.Getenv("STUN_DOMAIN")
|
||||
} else if config.Config.Server.StunHost != "" {
|
||||
stunAddr = config.Config.Server.StunHost
|
||||
// GetStunList - gets the stun servers
|
||||
func GetStunList() []models.StunServer {
|
||||
stunList := []models.StunServer{
|
||||
models.StunServer{
|
||||
Domain: "stun1.netmaker.io",
|
||||
Port: 3478,
|
||||
},
|
||||
models.StunServer{
|
||||
Domain: "stun2.netmaker.io",
|
||||
Port: 3478,
|
||||
},
|
||||
}
|
||||
return stunAddr
|
||||
parsed := false
|
||||
if os.Getenv("STUN_LIST") != "" {
|
||||
stuns, err := parseStunList(os.Getenv("STUN_LIST"))
|
||||
if err == nil {
|
||||
parsed = true
|
||||
stunList = stuns
|
||||
}
|
||||
}
|
||||
if !parsed && config.Config.Server.StunList != "" {
|
||||
stuns, err := parseStunList(config.Config.Server.StunList)
|
||||
if err == nil {
|
||||
stunList = stuns
|
||||
}
|
||||
}
|
||||
return stunList
|
||||
}
|
||||
|
||||
// GetStunList - gets the stun servers w/o parsing to struct
|
||||
func GetStunListString() string {
|
||||
stunList := "stun1.netmaker.io:3478,stun2.netmaker.io:3478"
|
||||
if os.Getenv("STUN_LIST") != "" {
|
||||
stunList = os.Getenv("STUN_LIST")
|
||||
} else if config.Config.Server.StunList != "" {
|
||||
stunList = config.Config.Server.StunList
|
||||
}
|
||||
return stunList
|
||||
}
|
||||
|
||||
// GetCoreDNSAddr - gets the core dns address
|
||||
|
@ -582,6 +611,7 @@ func GetNetmakerAccountID() string {
|
|||
return netmakerAccountID
|
||||
}
|
||||
|
||||
// GetStunPort - Get the port to run the stun server on
|
||||
func GetStunPort() int {
|
||||
port := 3478 //default
|
||||
if os.Getenv("STUN_PORT") != "" {
|
||||
|
@ -595,6 +625,7 @@ func GetStunPort() int {
|
|||
return port
|
||||
}
|
||||
|
||||
// IsProxyEnabled - is proxy on or off
|
||||
func IsProxyEnabled() bool {
|
||||
var enabled = false //default
|
||||
if os.Getenv("PROXY") != "" {
|
||||
|
@ -604,3 +635,33 @@ func IsProxyEnabled() bool {
|
|||
}
|
||||
return enabled
|
||||
}
|
||||
|
||||
// parseStunList - turn string into slice of StunServers
|
||||
func parseStunList(stunString string) ([]models.StunServer, error) {
|
||||
var err error
|
||||
stunServers := []models.StunServer{}
|
||||
stuns := strings.Split(stunString, ",")
|
||||
if len(stuns) == 0 {
|
||||
return stunServers, errors.New("no stun servers provided")
|
||||
}
|
||||
for _, stun := range stuns {
|
||||
stun = strings.Trim(stun, " ")
|
||||
stunInfo := strings.Split(stun, ":")
|
||||
if len(stunInfo) != 2 {
|
||||
continue
|
||||
}
|
||||
port, err := strconv.Atoi(stunInfo[1])
|
||||
if err != nil || port == 0 {
|
||||
continue
|
||||
}
|
||||
stunServers = append(stunServers, models.StunServer{
|
||||
Domain: stunInfo[0],
|
||||
Port: port,
|
||||
})
|
||||
|
||||
}
|
||||
if len(stunServers) == 0 {
|
||||
err = errors.New("no stun entries parsable")
|
||||
}
|
||||
return stunServers, err
|
||||
}
|
||||
|
|
|
@ -704,7 +704,7 @@ info:
|
|||
|
||||
API calls must be authenticated via a header of the format -H “Authorization: Bearer <YOUR_SECRET_KEY>” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes/<network>/authenticate endpoint, as documented below.
|
||||
title: Netmaker
|
||||
version: 0.18.2
|
||||
version: 0.18.3
|
||||
paths:
|
||||
/api/dns:
|
||||
get:
|
||||
|
|
Loading…
Add table
Reference in a new issue