From 94ca72e3704c3e84daf309db3a97ee22f452ab81 Mon Sep 17 00:00:00 2001 From: Tobias Cudnik Date: Tue, 13 Jun 2023 22:33:53 +0200 Subject: [PATCH 01/16] NET-173 Sync EndpointDetection config (#2374) * adding serverconfig logic and variable to hosts * - sync EndpointDetection on peerupdate - add EndpointDetection to env config * - endpoint detection always comes from the server - fixed ENDPOINT_DETECTION -> NETCLIENT_ENDPOINT_DETECTION --------- Co-authored-by: afeiszli --- config/config.go | 101 ++++++++++++++++++----------------- logic/hosts.go | 1 + logic/peers.go | 3 ++ models/host.go | 1 + mq/handlers.go | 3 +- scripts/netmaker.default.env | 1 + scripts/nm-quick.sh | 6 +-- servercfg/serverconf.go | 16 ++++++ 8 files changed, 78 insertions(+), 54 deletions(-) diff --git a/config/config.go b/config/config.go index 8e6f1302..fe95fea9 100644 --- a/config/config.go +++ b/config/config.go @@ -32,56 +32,57 @@ 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"` - NetclientAutoUpdate string `yaml:"netclientautoupdate"` - 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"` - DefaultProxyMode ProxyMode `yaml:"defaultproxymode"` - TurnServer string `yaml:"turn_server"` - TurnApiServer string `yaml:"turn_api_server"` - TurnPort int `yaml:"turn_port"` - TurnUserName string `yaml:"turn_username"` - TurnPassword string `yaml:"turn_password"` - UseTurn bool `yaml:"use_turn"` + 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"` + NetclientAutoUpdate string `yaml:"netclientautoupdate"` + NetclientEndpointDetection string `yaml:"netclientendpointdetection"` + 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"` + DefaultProxyMode ProxyMode `yaml:"defaultproxymode"` + TurnServer string `yaml:"turn_server"` + TurnApiServer string `yaml:"turn_api_server"` + TurnPort int `yaml:"turn_port"` + TurnUserName string `yaml:"turn_username"` + TurnPassword string `yaml:"turn_password"` + UseTurn bool `yaml:"use_turn"` } // ProxyMode - default proxy mode for server diff --git a/logic/hosts.go b/logic/hosts.go index a41bccf5..34fa9766 100644 --- a/logic/hosts.go +++ b/logic/hosts.go @@ -111,6 +111,7 @@ func CreateHost(h *models.Host) error { } h.HostPass = string(hash) h.AutoUpdate = servercfg.AutoUpdateEnabled() + h.EndpointDetection = servercfg.EndpointDetectionEnabled() // if another server has already updated proxyenabled, leave it alone if !h.ProxyEnabledSet { log.Println("checking default proxy", servercfg.GetServerConfig().DefaultProxyMode) diff --git a/logic/peers.go b/logic/peers.go index f2f1d08f..b3ecc1dc 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -167,6 +167,9 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host HostNetworkInfo: models.HostInfoMap{}, } + // endpoint detection always comes from the server + hostPeerUpdate.Host.EndpointDetection = servercfg.EndpointDetectionEnabled() + logger.Log(1, "peer update for host", host.ID.String()) peerIndexMap := make(map[string]int) for _, nodeID := range host.Nodes { diff --git a/models/host.go b/models/host.go index 12e79d40..f4656c07 100644 --- a/models/host.go +++ b/models/host.go @@ -48,6 +48,7 @@ type Host struct { IPForwarding bool `json:"ipforwarding" yaml:"ipforwarding"` DaemonInstalled bool `json:"daemoninstalled" yaml:"daemoninstalled"` AutoUpdate bool `json:"autoupdate" yaml:"autoupdate"` + EndpointDetection bool `json:"endpointdetection" yaml:"endpointdetection"` HostPass string `json:"hostpass" yaml:"hostpass"` Name string `json:"name" yaml:"name"` OS string `json:"os" yaml:"os"` diff --git a/mq/handlers.go b/mq/handlers.go index cf56f31d..c87b8bb5 100644 --- a/mq/handlers.go +++ b/mq/handlers.go @@ -438,7 +438,8 @@ func handleHostCheckin(h, currentHost *models.Host) bool { ifaceDelta := len(h.Interfaces) != len(currentHost.Interfaces) || !h.EndpointIP.Equal(currentHost.EndpointIP) || (len(h.NatType) > 0 && h.NatType != currentHost.NatType) || - h.DefaultInterface != currentHost.DefaultInterface + h.DefaultInterface != currentHost.DefaultInterface || + h.EndpointDetection != servercfg.EndpointDetectionEnabled() if ifaceDelta { // only save if something changes currentHost.EndpointIP = h.EndpointIP currentHost.Interfaces = h.Interfaces diff --git a/scripts/netmaker.default.env b/scripts/netmaker.default.env index a852d6e5..b99f4818 100644 --- a/scripts/netmaker.default.env +++ b/scripts/netmaker.default.env @@ -19,6 +19,7 @@ NETMAKER_ACCOUNT_ID= LICENSE_KEY= SERVER_IMAGE_TAG= UI_IMAGE_TAG= +NETCLIENT_ENDPOINT_DETECTION="disabled" # used for HA - identifies this server vs other servers NODE_ID="netmaker-server-1" METRICS_EXPORTER="off" diff --git a/scripts/nm-quick.sh b/scripts/nm-quick.sh index 42b3f1fb..98541f16 100755 --- a/scripts/nm-quick.sh +++ b/scripts/nm-quick.sh @@ -303,9 +303,9 @@ save_config() { ( local toCopy=("SERVER_HOST" "MASTER_KEY" "TURN_USERNAME" "TURN_PASSWORD" "MQ_USERNAME" "MQ_PASSWORD" "INSTALL_TYPE" "NODE_ID" "METRICS_EXPORTER" "PROMETHEUS" "DNS_MODE" "NETCLIENT_AUTO_UPDATE" "API_PORT" "CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "STUN_PORT" "VERBOSITY" - "DEFAULT_PROXY_MODE" "TURN_PORT" "USE_TURN" "DEBUG_MODE" "TURN_API_PORT" "REST_BACKEND" "DISABLE_REMOTE_IP_CHECK" - "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET" "FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" - "EXPORTER_API_PORT") + "DEFAULT_PROXY_MODE" "TURN_PORT" "USE_TURN" "DEBUG_MODE" "TURN_API_PORT" "REST_BACKEND" + "DISABLE_REMOTE_IP_CHECK" "NETCLIENT_ENDPOINT_DETECTION" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET" + "FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT") for name in "${toCopy[@]}"; do save_config_item $name "${!name}" done diff --git a/servercfg/serverconf.go b/servercfg/serverconf.go index 79d64cc1..ec8b6ef0 100644 --- a/servercfg/serverconf.go +++ b/servercfg/serverconf.go @@ -51,6 +51,11 @@ func GetServerConfig() config.ServerConfig { } else { cfg.NetclientAutoUpdate = "disabled" } + if EndpointDetectionEnabled() { + cfg.NetclientEndpointDetection = "enabled" + } else { + cfg.NetclientEndpointDetection = "disabled" + } if IsRestBackend() { cfg.RestBackend = "on" } @@ -432,6 +437,17 @@ func AutoUpdateEnabled() bool { return true } +// EndpointDetectionEnabled returns a boolean indicating whether netclient endpoint detection is enabled or disabled +// default is enabled +func EndpointDetectionEnabled() bool { + if os.Getenv("NETCLIENT_ENDPOINT_DETECTION") == "disabled" { + return false + } else if config.Config.Server.NetclientEndpointDetection == "disabled" { + return false + } + return true +} + // IsDNSMode - should it run with DNS func IsDNSMode() bool { isdns := true From 20998dd6131e4957c36754ab82d9f2da1417c9d2 Mon Sep 17 00:00:00 2001 From: Matthew R Kasun Date: Wed, 14 Jun 2023 21:37:18 -0400 Subject: [PATCH 02/16] prevent ingress creation on NATed host (#2395) --- logic/gateway.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/logic/gateway.go b/logic/gateway.go index e05d985a..60b4b7d3 100644 --- a/logic/gateway.go +++ b/logic/gateway.go @@ -112,6 +112,9 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq if host.FirewallInUse == models.FIREWALL_NONE { return models.Node{}, errors.New("firewall is not supported for ingress gateways") } + if host.NatType != models.NAT_Types.Public { + return models.Node{}, errors.New("ingress cannot be created on nodes behind NAT") + } network, err := GetParentNetwork(netid) if err != nil { From 6c2582651c1056921516cc08e51b95a8c56ecba2 Mon Sep 17 00:00:00 2001 From: Abhishek K <32607604+abhishek9686@users.noreply.github.com> Date: Mon, 19 Jun 2023 21:56:14 +0530 Subject: [PATCH 03/16] Net-137: Node relays (#2401) * revert relays * initial relay commit * get relayed allowed ips * add more relay validation checks, peer logic cleanup * rm relayed nodes from relay node when relayed node is deleted * fix egress updates for relayed nodes * rm unused func * remove debug logs * avoid adding egress ranges on the relayed gw node --------- Co-authored-by: Matthew R Kasun --- cli/cmd/host/create_relay.go | 22 --- cli/cmd/node/create_relay.go | 22 +++ cli/cmd/{host => node}/delete_relay.go | 12 +- cli/cmd/node/flags.go | 2 +- cli/cmd/node/update.go | 6 +- cli/functions/host.go | 17 +- controllers/hosts.go | 44 +---- controllers/node.go | 83 ++++----- controllers/relay.go | 199 --------------------- controllers/server.go | 2 +- ee/ee_controllers/relay.go | 97 ++++++++++ ee/initialize.go | 1 + logic/gateway.go | 3 + logic/nodes.go | 58 ++---- logic/peers.go | 199 ++++++++++----------- logic/relay.go | 234 ++++++++++--------------- logic/wireguard.go | 6 +- models/api_host.go | 8 - models/api_node.go | 10 +- models/node.go | 17 +- models/structs.go | 6 +- 21 files changed, 399 insertions(+), 649 deletions(-) delete mode 100644 cli/cmd/host/create_relay.go create mode 100644 cli/cmd/node/create_relay.go rename cli/cmd/{host => node}/delete_relay.go (51%) delete mode 100644 controllers/relay.go create mode 100644 ee/ee_controllers/relay.go diff --git a/cli/cmd/host/create_relay.go b/cli/cmd/host/create_relay.go deleted file mode 100644 index 2344af72..00000000 --- a/cli/cmd/host/create_relay.go +++ /dev/null @@ -1,22 +0,0 @@ -package host - -import ( - "strings" - - "github.com/gravitl/netmaker/cli/functions" - "github.com/spf13/cobra" -) - -var hostCreateRelayCmd = &cobra.Command{ - Use: "create_relay [HOST ID] [RELAYED HOST IDS (comma separated)]", - Args: cobra.ExactArgs(2), - Short: "Turn a Host into a Relay", - Long: `Turn a Host into a Relay`, - Run: func(cmd *cobra.Command, args []string) { - functions.PrettyPrint(functions.CreateRelay(args[0], strings.Split(args[1], ","))) - }, -} - -func init() { - rootCmd.AddCommand(hostCreateRelayCmd) -} diff --git a/cli/cmd/node/create_relay.go b/cli/cmd/node/create_relay.go new file mode 100644 index 00000000..e17dce57 --- /dev/null +++ b/cli/cmd/node/create_relay.go @@ -0,0 +1,22 @@ +package node + +import ( + "strings" + + "github.com/gravitl/netmaker/cli/functions" + "github.com/spf13/cobra" +) + +var hostCreateRelayCmd = &cobra.Command{ + Use: "create_relay [NETWORK][NODE ID] [RELAYED NODE IDS (comma separated)]", + Args: cobra.ExactArgs(3), + Short: "Turn a Node into a Relay", + Long: `Turn a Node into a Relay`, + Run: func(cmd *cobra.Command, args []string) { + functions.PrettyPrint(functions.CreateRelay(args[0], args[1], strings.Split(args[2], ","))) + }, +} + +func init() { + rootCmd.AddCommand(hostCreateRelayCmd) +} diff --git a/cli/cmd/host/delete_relay.go b/cli/cmd/node/delete_relay.go similarity index 51% rename from cli/cmd/host/delete_relay.go rename to cli/cmd/node/delete_relay.go index ccf203e6..91e2522d 100644 --- a/cli/cmd/host/delete_relay.go +++ b/cli/cmd/node/delete_relay.go @@ -1,4 +1,4 @@ -package host +package node import ( "github.com/gravitl/netmaker/cli/functions" @@ -6,12 +6,12 @@ import ( ) var hostDeleteRelayCmd = &cobra.Command{ - Use: "delete_relay [HOST ID]", - Args: cobra.ExactArgs(1), - Short: "Delete Relay role from a host", - Long: `Delete Relay role from a host`, + Use: "delete_relay [NETWORK] [NODE ID]", + Args: cobra.ExactArgs(2), + Short: "Delete Relay from a node", + Long: `Delete Relay from a node`, Run: func(cmd *cobra.Command, args []string) { - functions.PrettyPrint(functions.DeleteRelay(args[0])) + functions.PrettyPrint(functions.DeleteRelay(args[0], args[1])) }, } diff --git a/cli/cmd/node/flags.go b/cli/cmd/node/flags.go index b7da4d03..48f2f749 100644 --- a/cli/cmd/node/flags.go +++ b/cli/cmd/node/flags.go @@ -12,7 +12,7 @@ var ( postUp string postDown string keepAlive int - relayAddrs string + relayedNodes string egressGatewayRanges string expirationDateTime int defaultACL bool diff --git a/cli/cmd/node/update.go b/cli/cmd/node/update.go index ce0d9846..e2d2d383 100644 --- a/cli/cmd/node/update.go +++ b/cli/cmd/node/update.go @@ -35,8 +35,8 @@ var nodeUpdateCmd = &cobra.Command{ node.Address6 = address6 node.LocalAddress = localAddress node.PersistentKeepalive = int32(keepAlive) - if relayAddrs != "" { - node.RelayAddrs = strings.Split(relayAddrs, ",") + if relayedNodes != "" { + node.RelayedNodes = strings.Split(relayedNodes, ",") } if egressGatewayRanges != "" { node.EgressGatewayRanges = strings.Split(egressGatewayRanges, ",") @@ -62,7 +62,7 @@ func init() { nodeUpdateCmd.Flags().StringVar(&postUp, "post_up", "", "Commands to run after node is up `;` separated") nodeUpdateCmd.Flags().StringVar(&postDown, "post_down", "", "Commands to run after node is down `;` separated") nodeUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval in which packets are sent to keep connections open with peers") - nodeUpdateCmd.Flags().StringVar(&relayAddrs, "relay_addrs", "", "Addresses for relaying connections if node acts as a relay") + nodeUpdateCmd.Flags().StringVar(&relayedNodes, "relayed_nodes", "", "relayed nodes if node acts as a relay") nodeUpdateCmd.Flags().StringVar(&egressGatewayRanges, "egress_addrs", "", "Addresses for egressing traffic if node acts as an egress") nodeUpdateCmd.Flags().IntVar(&expirationDateTime, "expiry", 0, "UNIX timestamp after which node will lose access to the network") nodeUpdateCmd.Flags().BoolVar(&defaultACL, "acl", false, "Enable default ACL ?") diff --git a/cli/functions/host.go b/cli/functions/host.go index 16fac424..bfcf0f03 100644 --- a/cli/functions/host.go +++ b/cli/functions/host.go @@ -36,17 +36,18 @@ func DeleteHostFromNetwork(hostID, network string) *hostNetworksUpdatePayload { return request[hostNetworksUpdatePayload](http.MethodDelete, "/api/hosts/"+hostID+"/networks/"+network, nil) } -// CreateRelay - turn a host into a relay -func CreateRelay(hostID string, relayedHosts []string) *models.ApiHost { - return request[models.ApiHost](http.MethodPost, fmt.Sprintf("/api/hosts/%s/relay", hostID), &models.HostRelayRequest{ - HostID: hostID, - RelayedHosts: relayedHosts, +// CreateRelay - add relay to a node +func CreateRelay(netID, nodeID string, relayedNodes []string) *models.ApiNode { + return request[models.ApiNode](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/createrelay", netID, nodeID), &models.RelayRequest{ + NodeID: nodeID, + NetID: netID, + RelayedNodes: relayedNodes, }) } -// DeleteRelay - remove relay role from a host -func DeleteRelay(hostID string) *models.ApiHost { - return request[models.ApiHost](http.MethodDelete, fmt.Sprintf("/api/hosts/%s/relay", hostID), nil) +// DeleteRelay - remove relay from a node +func DeleteRelay(netID, nodeID string) *models.ApiNode { + return request[models.ApiNode](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s/deleterelay", netID, nodeID), nil) } // RefreshKeys - refresh wireguard keys diff --git a/controllers/hosts.go b/controllers/hosts.go index 03cb3456..da329c84 100644 --- a/controllers/hosts.go +++ b/controllers/hosts.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "net/http" - "reflect" "github.com/gorilla/mux" "github.com/gravitl/netmaker/logger" @@ -26,11 +25,9 @@ func hostHandlers(r *mux.Router) { r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(deleteHost))).Methods(http.MethodDelete) r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(addHostToNetwork))).Methods(http.MethodPost) r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(deleteHostFromNetwork))).Methods(http.MethodDelete) - r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(createHostRelay))).Methods(http.MethodPost) - r.HandleFunc("/api/hosts/{hostid}/relay", logic.SecurityCheck(false, http.HandlerFunc(deleteHostRelay))).Methods(http.MethodDelete) r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost) - r.HandleFunc("/api/v1/host", authorize(true, false, "host", http.HandlerFunc(pull))).Methods(http.MethodGet) - r.HandleFunc("/api/v1/host/{hostid}/signalpeer", authorize(true, false, "host", http.HandlerFunc(signalPeer))).Methods(http.MethodPost) + r.HandleFunc("/api/v1/host", Authorize(true, false, "host", http.HandlerFunc(pull))).Methods(http.MethodGet) + r.HandleFunc("/api/v1/host/{hostid}/signalpeer", Authorize(true, false, "host", http.HandlerFunc(signalPeer))).Methods(http.MethodPost) r.HandleFunc("/api/v1/auth-register/host", socketHandler) } @@ -174,13 +171,6 @@ func updateHost(w http.ResponseWriter, r *http.Request) { } newHost := newHostData.ConvertAPIHostToNMHost(currHost) - // check if relay information is changed - updateRelay := false - if newHost.IsRelay && len(newHost.RelayedHosts) > 0 { - if len(newHost.RelayedHosts) != len(currHost.RelayedHosts) || !reflect.DeepEqual(newHost.RelayedHosts, currHost.RelayedHosts) { - updateRelay = true - } - } logic.UpdateHost(newHost, currHost) // update the in memory struct values if err = logic.UpsertHost(newHost); err != nil { @@ -188,9 +178,6 @@ func updateHost(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - if updateRelay { - logic.UpdateHostRelay(currHost.ID.String(), currHost.RelayedHosts, newHost.RelayedHosts) - } // publish host update through MQ if err := mq.HostUpdate(&models.HostUpdate{ Action: models.UpdateHost, @@ -244,33 +231,6 @@ func deleteHost(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - if currHost.IsRelay { - if _, _, err := logic.DeleteHostRelay(hostid); err != nil { - logger.Log(0, r.Header.Get("user"), "failed to dissociate host from relays:", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - } - if currHost.IsRelayed { - relayHost, err := logic.GetHost(currHost.RelayedBy) - if err != nil { - logger.Log(0, r.Header.Get("user"), "failed to fetch relay host:", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - newRelayedHosts := make([]string, 0) - for _, relayedHostID := range relayHost.RelayedHosts { - if relayedHostID != hostid { - newRelayedHosts = append(newRelayedHosts, relayedHostID) - } - } - relayHost.RelayedHosts = newRelayedHosts - if err := logic.UpsertHost(relayHost); err != nil { - logger.Log(0, r.Header.Get("user"), "failed to update host relays:", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - } if err = logic.RemoveHost(currHost); err != nil { logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) diff --git a/controllers/node.go b/controllers/node.go index 4f06e9da..47684805 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -23,18 +23,16 @@ var hostIDHeader = "host-id" func nodeHandlers(r *mux.Router) { - r.HandleFunc("/api/nodes", authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods(http.MethodGet) - r.HandleFunc("/api/nodes/{network}", authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).Methods(http.MethodGet) - r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(getNode))).Methods(http.MethodGet) - r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(false, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPut) - r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete) - r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", authorize(false, true, "user", http.HandlerFunc(createRelay))).Methods(http.MethodPost) - r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", authorize(false, true, "user", http.HandlerFunc(deleteRelay))).Methods(http.MethodDelete) - r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", authorize(false, true, "user", http.HandlerFunc(createEgressGateway))).Methods(http.MethodPost) - r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete) + r.HandleFunc("/api/nodes", Authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods(http.MethodGet) + r.HandleFunc("/api/nodes/{network}", Authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).Methods(http.MethodGet) + r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(getNode))).Methods(http.MethodGet) + r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(false, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPut) + r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete) + r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", Authorize(false, true, "user", http.HandlerFunc(createEgressGateway))).Methods(http.MethodPost) + r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", Authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete) r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, http.HandlerFunc(createIngressGateway))).Methods(http.MethodPost) r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete) - r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost) + r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost) r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost) r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost) } @@ -154,7 +152,7 @@ func authenticate(response http.ResponseWriter, request *http.Request) { // even if it's technically ok // This is kind of a poor man's RBAC. There's probably a better/smarter way. // TODO: Consider better RBAC implementations -func authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc { +func Authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var errorResponse = models.ErrorResponse{ Code: http.StatusForbidden, Message: logic.Forbidden_Msg, @@ -633,12 +631,12 @@ func updateNode(w http.ResponseWriter, r *http.Request) { } newNode := newData.ConvertToServerNode(¤tNode) relayupdate := false - if currentNode.IsRelay && len(newNode.RelayAddrs) > 0 { - if len(newNode.RelayAddrs) != len(currentNode.RelayAddrs) { + if servercfg.Is_EE && newNode.IsRelay && len(newNode.RelayedNodes) > 0 { + if len(newNode.RelayedNodes) != len(currentNode.RelayedNodes) { relayupdate = true } else { - for i, addr := range newNode.RelayAddrs { - if addr != currentNode.RelayAddrs[i] { + for i, node := range newNode.RelayedNodes { + if node != currentNode.RelayedNodes[i] { relayupdate = true } } @@ -651,10 +649,6 @@ func updateNode(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - relayedUpdate := false - if currentNode.IsRelayed && (currentNode.Address.String() != newNode.Address.String() || currentNode.Address6.String() != newNode.Address6.String()) { - relayedUpdate = true - } ifaceDelta := logic.IfaceDelta(¤tNode, newNode) aclUpdate := currentNode.DefaultACL != newNode.DefaultACL if ifaceDelta && servercfg.Is_EE { @@ -671,16 +665,13 @@ func updateNode(w http.ResponseWriter, r *http.Request) { return } if relayupdate { - updatenodes := logic.UpdateRelay(currentNode.Network, currentNode.RelayAddrs, newNode.RelayAddrs) + updatenodes := logic.UpdateRelayed(currentNode.ID.String(), currentNode.RelayedNodes, newNode.RelayedNodes) if len(updatenodes) > 0 { for _, relayedNode := range updatenodes { runUpdates(&relayedNode, false) } } } - if relayedUpdate { - updateRelay(¤tNode, newNode) - } if servercfg.IsDNSMode() { logic.SetDNS() } @@ -690,8 +681,8 @@ func updateNode(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(apiNode) runUpdates(newNode, ifaceDelta) - go func(aclUpdate bool, newNode *models.Node) { - if aclUpdate { + go func(aclUpdate, relayupdate bool, newNode *models.Node) { + if aclUpdate || relayupdate { if err := mq.PublishPeerUpdate(); err != nil { logger.Log(0, "error during node ACL update for node", newNode.ID.String()) } @@ -699,7 +690,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) { if err := mq.PublishReplaceDNS(¤tNode, newNode, host); err != nil { logger.Log(1, "failed to publish dns update", err.Error()) } - }(aclUpdate, newNode) + }(aclUpdate, relayupdate, newNode) } // swagger:route DELETE /api/nodes/{network}/{nodeid} nodes deleteNode @@ -743,6 +734,22 @@ func deleteNode(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal")) return } + if node.IsRelayed { + // cleanup node from relayednodes on relay node + relayNode, err := logic.GetNodeByID(node.RelayedBy) + if err == nil { + relayedNodes := []string{} + for _, relayedNodeID := range relayNode.RelayedNodes { + if relayedNodeID == node.ID.String() { + continue + } + relayedNodes = append(relayedNodes, relayedNodeID) + } + relayNode.RelayedNodes = relayedNodes + logic.UpsertNode(&relayNode) + } + + } logic.ReturnSuccessResponse(w, r, nodeid+" deleted.") logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"]) if !fromNode { // notify node change @@ -778,30 +785,6 @@ func runUpdates(node *models.Node, ifaceDelta bool) { }() } -func updateRelay(oldnode, newnode *models.Node) { - relay := logic.FindRelay(oldnode) - newrelay := relay - //check if node's address has been updated and if so, update the relayAddrs of the relay node with the updated address of the relayed node - if oldnode.Address.String() != newnode.Address.String() { - for i, ip := range newrelay.RelayAddrs { - if ip == oldnode.Address.IP.String() { - newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], relay.RelayAddrs[i+1:]...) - newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address.IP.String()) - } - } - } - //check if node's address(v6) has been updated and if so, update the relayAddrs of the relay node with the updated address(v6) of the relayed node - if oldnode.Address6.String() != newnode.Address6.String() { - for i, ip := range newrelay.RelayAddrs { - if ip == oldnode.Address.IP.String() { - newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], newrelay.RelayAddrs[i+1:]...) - newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address6.IP.String()) - } - } - } - logic.UpdateNode(relay, newrelay) -} - func doesUserOwnNode(username, network, nodeID string) bool { u, err := logic.GetUser(username) if err != nil { diff --git a/controllers/relay.go b/controllers/relay.go deleted file mode 100644 index f8242359..00000000 --- a/controllers/relay.go +++ /dev/null @@ -1,199 +0,0 @@ -package controller - -import ( - "encoding/json" - "fmt" - "net/http" - - "github.com/gorilla/mux" - "github.com/gravitl/netmaker/logger" - "github.com/gravitl/netmaker/logic" - "github.com/gravitl/netmaker/models" - "github.com/gravitl/netmaker/mq" -) - -// swagger:route POST /api/nodes/{network}/{nodeid}/createrelay nodes createRelay -// -// Create a relay. -// -// Schemes: https -// -// Security: -// oauth -// -// Responses: -// 200: nodeResponse -func createRelay(w http.ResponseWriter, r *http.Request) { - var relay models.RelayRequest - var params = mux.Vars(r) - w.Header().Set("Content-Type", "application/json") - err := json.NewDecoder(r.Body).Decode(&relay) - if err != nil { - logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) - return - } - relay.NetID = params["network"] - relay.NodeID = params["nodeid"] - updatenodes, node, err := logic.CreateRelay(relay) - if err != nil { - logger.Log(0, r.Header.Get("user"), - fmt.Sprintf("failed to create relay on node [%s] on network [%s]: %v", relay.NodeID, relay.NetID, err)) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - - logger.Log(1, r.Header.Get("user"), "created relay on node", relay.NodeID, "on network", relay.NetID) - for _, relayedNode := range updatenodes { - - err = mq.NodeUpdate(&relayedNode) - if err != nil { - logger.Log(1, "error sending update to relayed node ", relayedNode.ID.String(), "on network", relay.NetID, ": ", err.Error()) - } - } - - apiNode := node.ConvertToAPINode() - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(apiNode) - runUpdates(&node, true) -} - -// swagger:route DELETE /api/nodes/{network}/{nodeid}/deleterelay nodes deleteRelay -// -// Remove a relay. -// -// Schemes: https -// -// Security: -// oauth -// -// Responses: -// 200: nodeResponse -func deleteRelay(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - var params = mux.Vars(r) - nodeid := params["nodeid"] - netid := params["network"] - updatenodes, node, err := logic.DeleteRelay(netid, nodeid) - if err != nil { - logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) - return - } - logger.Log(1, r.Header.Get("user"), "deleted relay server", nodeid, "on network", netid) - for _, relayedNode := range updatenodes { - err = mq.NodeUpdate(&relayedNode) - if err != nil { - logger.Log(1, "error sending update to relayed node ", relayedNode.ID.String(), "on network", netid, ": ", err.Error()) - } - } - apiNode := node.ConvertToAPINode() - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(apiNode) - runUpdates(&node, true) -} - -// swagger:route POST /api/hosts/{hostid}/relay hosts createHostRelay -// -// Create a relay. -// -// Schemes: https -// -// Security: -// oauth -// -// Responses: -// 200: nodeResponse -func createHostRelay(w http.ResponseWriter, r *http.Request) { - var relay models.HostRelayRequest - var params = mux.Vars(r) - w.Header().Set("Content-Type", "application/json") - err := json.NewDecoder(r.Body).Decode(&relay) - if err != nil { - logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) - return - } - relay.HostID = params["hostid"] - relayHost, relayedHosts, err := logic.CreateHostRelay(relay) - if err != nil { - logger.Log(0, r.Header.Get("user"), - fmt.Sprintf("failed to create relay on host [%s]: %v", relay.HostID, err)) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - - if err := mq.HostUpdate(&models.HostUpdate{ - Action: models.UpdateHost, - Host: *relayHost, - }); err != nil { - logger.Log(0, "failed to send host update: ", relayHost.ID.String(), err.Error()) - } - logger.Log(1, r.Header.Get("user"), "created relay on host", relay.HostID) - go func(relayHostID string) { - for _, relayedHost := range relayedHosts { - relayedHost.ProxyEnabled = true - logic.UpsertHost(&relayedHost) - if err := mq.HostUpdate(&models.HostUpdate{ - Action: models.UpdateHost, - Host: relayedHost, - }); err != nil { - logger.Log(0, "failed to send host update: ", relayedHost.ID.String(), err.Error()) - } - } - if err := mq.PublishPeerUpdate(); err != nil { - logger.Log(0, "fail to publish peer update: ", err.Error()) - } - - }(relay.HostID) - - apiHostData := relayHost.ConvertNMHostToAPI() - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(apiHostData) -} - -// swagger:route DELETE /api/hosts/{hostid}/relay hosts deleteHostRelay -// -// Remove a relay. -// -// Schemes: https -// -// Security: -// oauth -// -// Responses: -// 200: nodeResponse -func deleteHostRelay(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - var params = mux.Vars(r) - hostid := params["hostid"] - relayHost, relayed, err := logic.DeleteHostRelay(hostid) - if err != nil { - logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) - return - } - logger.Log(1, r.Header.Get("user"), "deleted relay host", hostid) - go func() { - if err := mq.PublishPeerUpdate(); err != nil { - logger.Log(0, "fail to publish peer update: ", err.Error()) - } - if err := mq.HostUpdate(&models.HostUpdate{ - Action: models.UpdateHost, - Host: *relayHost, - }); err != nil { - logger.Log(0, "failed to send host update: ", relayHost.Name, err.Error()) - } - for _, relayedHost := range relayed { - if err := mq.HostUpdate(&models.HostUpdate{ - Action: models.UpdateHost, - Host: relayedHost, - }); err != nil { - logger.Log(0, "failed to send host update: ", relayedHost.Name, err.Error()) - } - } - }() - apiHostData := relayHost.ConvertNMHostToAPI() - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(apiHostData) -} diff --git a/controllers/server.go b/controllers/server.go index a5d6ef66..bcbf7c9a 100644 --- a/controllers/server.go +++ b/controllers/server.go @@ -20,7 +20,7 @@ func serverHandlers(r *mux.Router) { resp.Write([]byte("Server is up and running!!")) })) r.HandleFunc("/api/server/getconfig", allowUsers(http.HandlerFunc(getConfig))).Methods(http.MethodGet) - r.HandleFunc("/api/server/getserverinfo", authorize(true, false, "node", http.HandlerFunc(getServerInfo))).Methods(http.MethodGet) + r.HandleFunc("/api/server/getserverinfo", Authorize(true, false, "node", http.HandlerFunc(getServerInfo))).Methods(http.MethodGet) r.HandleFunc("/api/server/status", http.HandlerFunc(getStatus)).Methods(http.MethodGet) } diff --git a/ee/ee_controllers/relay.go b/ee/ee_controllers/relay.go new file mode 100644 index 00000000..0818db7f --- /dev/null +++ b/ee/ee_controllers/relay.go @@ -0,0 +1,97 @@ +package ee_controllers + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/gorilla/mux" + controller "github.com/gravitl/netmaker/controllers" + "github.com/gravitl/netmaker/logger" + "github.com/gravitl/netmaker/logic" + "github.com/gravitl/netmaker/models" + "github.com/gravitl/netmaker/mq" +) + +// RelayHandlers - handle EE Relays +func RelayHandlers(r *mux.Router) { + + r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", controller.Authorize(false, true, "user", http.HandlerFunc(createRelay))).Methods(http.MethodPost) + r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", controller.Authorize(false, true, "user", http.HandlerFunc(deleteRelay))).Methods(http.MethodDelete) +} + +// swagger:route POST /api/nodes/{network}/{nodeid}/createrelay nodes createRelay +// +// Create a relay. +// +// Schemes: https +// +// Security: +// oauth +// +// Responses: +// 200: nodeResponse +func createRelay(w http.ResponseWriter, r *http.Request) { + var relayRequest models.RelayRequest + var params = mux.Vars(r) + w.Header().Set("Content-Type", "application/json") + err := json.NewDecoder(r.Body).Decode(&relayRequest) + if err != nil { + logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + return + } + relayRequest.NetID = params["network"] + relayRequest.NodeID = params["nodeid"] + _, relayNode, err := logic.CreateRelay(relayRequest) + if err != nil { + logger.Log(0, r.Header.Get("user"), + fmt.Sprintf("failed to create relay on node [%s] on network [%s]: %v", relayRequest.NodeID, relayRequest.NetID, err)) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + go mq.PublishPeerUpdate() + logger.Log(1, r.Header.Get("user"), "created relay on node", relayRequest.NodeID, "on network", relayRequest.NetID) + apiNode := relayNode.ConvertToAPINode() + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(apiNode) +} + +// swagger:route DELETE /api/nodes/{network}/{nodeid}/deleterelay nodes deleteRelay +// +// Remove a relay. +// +// Schemes: https +// +// Security: +// oauth +// +// Responses: +// 200: nodeResponse +func deleteRelay(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + var params = mux.Vars(r) + nodeid := params["nodeid"] + netid := params["network"] + updateNodes, node, err := logic.DeleteRelay(netid, nodeid) + if err != nil { + logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + return + } + logger.Log(1, r.Header.Get("user"), "deleted relay server", nodeid, "on network", netid) + go func() { + for _, relayedNode := range updateNodes { + err = mq.NodeUpdate(&relayedNode) + if err != nil { + logger.Log(1, "relayed node update ", relayedNode.ID.String(), "on network", relayedNode.Network, ": ", err.Error()) + + } + } + mq.PublishPeerUpdate() + }() + logger.Log(1, r.Header.Get("user"), "deleted relay on node", node.ID.String(), "on network", node.Network) + apiNode := node.ConvertToAPINode() + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(apiNode) +} diff --git a/ee/initialize.go b/ee/initialize.go index c0508246..ab5e3ba0 100644 --- a/ee/initialize.go +++ b/ee/initialize.go @@ -22,6 +22,7 @@ func InitEE() { ee_controllers.MetricHandlers, ee_controllers.NetworkUsersHandlers, ee_controllers.UserGroupsHandlers, + ee_controllers.RelayHandlers, ) logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() { // == License Handling == diff --git a/logic/gateway.go b/logic/gateway.go index 60b4b7d3..2677048c 100644 --- a/logic/gateway.go +++ b/logic/gateway.go @@ -102,6 +102,9 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq if err != nil { return models.Node{}, err } + if node.IsRelayed { + return models.Node{}, errors.New("ingress cannot be created on a relayed node") + } host, err := GetHost(node.HostID.String()) if err != nil { return models.Node{}, err diff --git a/logic/nodes.go b/logic/nodes.go index 4928cac0..1becdb64 100644 --- a/logic/nodes.go +++ b/logic/nodes.go @@ -75,6 +75,16 @@ func UpdateNodeCheckin(node *models.Node) error { return database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME) } +// UpsertNode - updates node in the DB +func UpsertNode(newNode *models.Node) error { + newNode.SetLastModified() + data, err := json.Marshal(newNode) + if err != nil { + return err + } + return database.Insert(newNode.ID.String(), string(data), database.NODES_TABLE_NAME) +} + // UpdateNode - takes a node and updates another node with it's values func UpdateNode(currentNode *models.Node, newNode *models.Node) error { if newNode.Address.IP.String() != currentNode.Address.IP.String() { @@ -85,7 +95,7 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error { } } nodeACLDelta := currentNode.DefaultACL != newNode.DefaultACL - newNode.Fill(currentNode) + newNode.Fill(currentNode, servercfg.Is_EE) // check for un-settable server values if err := ValidateNode(newNode, true); err != nil { @@ -338,34 +348,6 @@ func GetDeletedNodeByMacAddress(network string, macaddress string) (models.Node, return node, nil } -// GetNodeRelay - gets the relay node of a given network -func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) { - collection, err := database.FetchRecords(database.NODES_TABLE_NAME) - var relay models.Node - if err != nil { - if database.IsEmptyRecord(err) { - return relay, nil - } - logger.Log(2, err.Error()) - return relay, err - } - for _, value := range collection { - err := json.Unmarshal([]byte(value), &relay) - if err != nil { - logger.Log(2, err.Error()) - continue - } - if relay.IsRelay { - for _, addr := range relay.RelayAddrs { - if addr == relayedNodeAddr { - return relay, nil - } - } - } - } - return relay, errors.New(RELAY_NODE_ERR + " " + relayedNodeAddr) -} - func GetNodeByID(uuid string) (models.Node, error) { var record, err = database.FetchRecord(database.NODES_TABLE_NAME, uuid) if err != nil { @@ -399,24 +381,12 @@ func GetDeletedNodeByID(uuid string) (models.Node, error) { // FindRelay - returns the node that is the relay for a relayed node func FindRelay(node *models.Node) *models.Node { - if !node.IsRelayed { - return nil - } - peers, err := GetNetworkNodes(node.Network) + relay, err := GetNodeByID(node.RelayedBy) if err != nil { + logger.Log(0, "FindRelay: "+err.Error()) return nil } - for _, peer := range peers { - if !peer.IsRelay { - continue - } - for _, ip := range peer.RelayAddrs { - if ip == node.Address.IP.String() || ip == node.Address6.IP.String() { - return &peer - } - } - } - return nil + return &relay } // GetNetworkIngresses - gets the gateways of a network diff --git a/logic/peers.go b/logic/peers.go index b3ecc1dc..f642605c 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -29,41 +29,6 @@ func GetProxyUpdateForHost(ctx context.Context, host *models.Host) (models.Proxy Action: models.ProxyUpdate, } peerConfMap := make(map[string]models.PeerConf) - if host.IsRelayed { - relayHost, err := GetHost(host.RelayedBy) - if err == nil { - relayEndpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayHost.EndpointIP, GetPeerListenPort(relayHost))) - if err != nil { - logger.Log(1, "failed to resolve relay node endpoint: ", err.Error()) - } - proxyPayload.IsRelayed = true - proxyPayload.RelayedTo = relayEndpoint - } else { - logger.Log(0, "couldn't find relay host for: ", host.ID.String()) - } - } - if host.IsRelay { - relayedHosts := GetRelayedHosts(host) - relayPeersMap := make(map[string]models.RelayedConf) - for _, relayedHost := range relayedHosts { - relayedHost := relayedHost - payload, err := GetPeerUpdateForHost(ctx, "", &relayedHost, nil, nil) - if err == nil { - relayedEndpoint, udpErr := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayedHost.EndpointIP, GetPeerListenPort(&relayedHost))) - if udpErr == nil { - relayPeersMap[relayedHost.PublicKey.String()] = models.RelayedConf{ - RelayedPeerEndpoint: relayedEndpoint, - RelayedPeerPubKey: relayedHost.PublicKey.String(), - Peers: payload.Peers, - } - } - - } - } - proxyPayload.IsRelay = true - proxyPayload.RelayedPeerConf = relayPeersMap - - } var ingressStatus bool for _, nodeID := range host.Nodes { @@ -101,18 +66,6 @@ func GetProxyUpdateForHost(ctx context.Context, host *models.Host) (models.Proxy } } - if peerHost.IsRelayed && peerHost.RelayedBy != host.ID.String() { - relayHost, err := GetHost(peerHost.RelayedBy) - if err == nil { - relayTo, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayHost.EndpointIP, GetPeerListenPort(relayHost))) - if err == nil { - currPeerConf.IsRelayed = true - currPeerConf.RelayedTo = relayTo - } - - } - } - peerConfMap[peerHost.PublicKey.String()] = currPeerConf } if node.IsIngressGateway { @@ -198,58 +151,17 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host //skip yourself 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 } - - 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 - } + peerConfig := wgtypes.PeerConfig{ + PublicKey: peerHost.PublicKey, + PersistentKeepaliveInterval: &peer.PersistentKeepalive, + ReplaceAllowedIPs: true, } - peerConfig.Endpoint = &net.UDPAddr{ - IP: peerHost.EndpointIP, - Port: getPeerWgListenPort(peerHost), - } - - if uselocal { - peerConfig.Endpoint.IP = peer.LocalAddress.IP - peerConfig.Endpoint.Port = peerHost.ListenPort - } - 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) - } - } - } - if peer.IsEgressGateway { - allowedips = append(allowedips, getEgressIPs(&node, &peer)...) - } - 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 - } - if node.IsIngressGateway || node.IsEgressGateway { if peer.IsIngressGateway { _, extPeerIDAndAddrs, err := getExtPeers(&peer) @@ -282,6 +194,47 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host ID: peer.ID.String(), } } + if (node.IsRelayed && node.RelayedBy != peer.ID.String()) || (peer.IsRelayed && peer.RelayedBy != node.ID.String()) { + // if node is relayed and peer is not the relay, set remove to true + if _, ok := hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()]; ok { + continue + } + peerConfig.Remove = true + hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig) + peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1 + continue + } + + 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 + } + } + peerConfig.Endpoint = &net.UDPAddr{ + IP: peerHost.EndpointIP, + Port: getPeerWgListenPort(peerHost), + } + + if uselocal { + peerConfig.Endpoint.IP = peer.LocalAddress.IP + peerConfig.Endpoint.Port = peerHost.ListenPort + } + allowedips := GetAllowedIPs(&node, &peer, nil) + 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 + } peerProxyPort := GetProxyListenPort(peerHost) var nodePeer wgtypes.PeerConfig @@ -303,8 +256,9 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host nodePeer = peerConfig } else { peerAllowedIPs := hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].AllowedIPs - peerAllowedIPs = append(peerAllowedIPs, allowedips...) + peerAllowedIPs = append(peerAllowedIPs, peerConfig.AllowedIPs...) hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].AllowedIPs = peerAllowedIPs + hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].Remove = false hostPeerUpdate.HostPeerIDs[peerHost.PublicKey.String()][peer.ID.String()] = models.IDandAddr{ ID: peer.ID.String(), Address: peer.PrimaryAddress(), @@ -627,14 +581,15 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet } } } + if node.IsRelayed && node.RelayedBy == peer.ID.String() { + allowedips = append(allowedips, getAllowedIpsForRelayed(node, peer)...) + + } return allowedips } -func getEgressIPs(node, peer *models.Node) []net.IPNet { - host, err := GetHost(node.HostID.String()) - if err != nil { - logger.Log(0, "error retrieving host for node", node.ID.String(), err.Error()) - } +func getEgressIPs(peer *models.Node) []net.IPNet { + peerHost, err := GetHost(peer.HostID.String()) if err != nil { logger.Log(0, "error retrieving host for peer", peer.ID.String(), err.Error()) @@ -654,12 +609,12 @@ func getEgressIPs(node, peer *models.Node) []net.IPNet { } // getting the public ip of node if ipnet.Contains(peerHost.EndpointIP) && !internetGateway { // ensuring egress gateway range does not contain endpoint of node - logger.Log(2, "egress IP range of ", iprange, " overlaps with ", host.EndpointIP.String(), ", omitting") + logger.Log(2, "egress IP range of ", iprange, " overlaps with ", peerHost.EndpointIP.String(), ", omitting") continue // skip adding egress range if overlaps with node's ip } // TODO: Could put in a lot of great logic to avoid conflicts / bad routes - if ipnet.Contains(node.LocalAddress.IP) && !internetGateway { // ensuring egress gateway range does not contain public ip of node - logger.Log(2, "egress IP range of ", iprange, " overlaps with ", node.LocalAddress.String(), ", omitting") + if ipnet.Contains(peer.LocalAddress.IP) && !internetGateway { // ensuring egress gateway range does not contain public ip of node + logger.Log(2, "egress IP range of ", iprange, " overlaps with ", peer.LocalAddress.String(), ", omitting") continue // skip adding egress range if overlaps with node's local ip } if err != nil { @@ -690,12 +645,50 @@ func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet { // handle egress gateway peers if peer.IsEgressGateway { //hasGateway = true - egressIPs := getEgressIPs(node, peer) + egressIPs := getEgressIPs(peer) allowedips = append(allowedips, egressIPs...) } + if peer.IsRelay { + for _, relayedNodeID := range peer.RelayedNodes { + if node.ID.String() == relayedNodeID { + continue + } + relayedNode, err := GetNodeByID(relayedNodeID) + if err != nil { + continue + } + allowed := getRelayedAddresses(relayedNodeID) + if relayedNode.IsEgressGateway { + allowed = append(allowed, getEgressIPs(&relayedNode)...) + } + allowedips = append(allowedips, allowed...) + } + } return allowedips } +// getAllowedIpsForRelayed - returns the peerConfig for a node relayed by relay +func getAllowedIpsForRelayed(relayed, relay *models.Node) (allowedIPs []net.IPNet) { + if relayed.RelayedBy != relay.ID.String() { + logger.Log(0, "RelayedByRelay called with invalid parameters") + return + } + peers, err := GetNetworkNodes(relay.Network) + if err != nil { + logger.Log(0, "error getting network clients", err.Error()) + return + } + for _, peer := range peers { + if peer.ID == relayed.ID || peer.ID == relay.ID { + continue + } + if nodeacls.AreNodesAllowed(nodeacls.NetworkID(relayed.Network), nodeacls.NodeID(relayed.ID.String()), nodeacls.NodeID(peer.ID.String())) { + allowedIPs = append(allowedIPs, GetAllowedIPs(relayed, &peer, nil)...) + } + } + return +} + func getCIDRMaskFromAddr(addr string) net.IPMask { cidr := net.CIDRMask(32, 32) ipAddr, err := netip.ParseAddr(addr) diff --git a/logic/relay.go b/logic/relay.go index 89d0138b..03b7eb33 100644 --- a/logic/relay.go +++ b/logic/relay.go @@ -4,7 +4,7 @@ import ( "encoding/json" "errors" "fmt" - "time" + "net" "github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/logger" @@ -31,8 +31,7 @@ func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error) return returnnodes, models.Node{}, err } node.IsRelay = true - node.RelayAddrs = relay.RelayAddrs - + node.RelayedNodes = relay.RelayedNodes node.SetLastModified() nodeData, err := json.Marshal(&node) if err != nil { @@ -41,144 +40,98 @@ func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error) if err = database.Insert(node.ID.String(), string(nodeData), database.NODES_TABLE_NAME); err != nil { return returnnodes, models.Node{}, err } - returnnodes, err = SetRelayedNodes(true, node.Network, node.RelayAddrs) - if err != nil { - return returnnodes, node, err + returnnodes = SetRelayedNodes(true, relay.NodeID, relay.RelayedNodes) + for _, relayedNode := range returnnodes { + data, err := json.Marshal(&relayedNode) + if err != nil { + logger.Log(0, "marshalling relayed node", err.Error()) + continue + } + if err := database.Insert(relayedNode.ID.String(), string(data), database.NODES_TABLE_NAME); err != nil { + logger.Log(0, "inserting relayed node", err.Error()) + continue + } } return returnnodes, node, nil } -// CreateHostRelay - creates a host relay -func CreateHostRelay(relay models.HostRelayRequest) (relayHost *models.Host, relayedHosts []models.Host, err error) { - - relayHost, err = GetHost(relay.HostID) - if err != nil { - return - } - err = validateHostRelay(relay) - if err != nil { - return - } - relayHost.IsRelay = true - relayHost.ProxyEnabled = true - relayHost.RelayedHosts = relay.RelayedHosts - err = UpsertHost(relayHost) - if err != nil { - return - } - relayedHosts = SetRelayedHosts(true, relay.HostID, relay.RelayedHosts) - return -} - -// SetRelayedHosts - updates the relayed hosts status -func SetRelayedHosts(setRelayed bool, relayHostID string, relayedHostIDs []string) []models.Host { - var relayedHosts []models.Host - for _, relayedHostID := range relayedHostIDs { - host, err := GetHost(relayedHostID) - if err == nil { - if setRelayed { - host.IsRelayed = true - host.RelayedBy = relayHostID - host.ProxyEnabled = true - } else { - host.IsRelayed = false - host.RelayedBy = "" - } - err = UpsertHost(host) - if err == nil { - relayedHosts = append(relayedHosts, *host) - } - } - } - return relayedHosts -} - -// SetRelayedNodes- set relayed nodes -func SetRelayedNodes(setRelayed bool, networkName string, addrs []string) ([]models.Node, error) { +// SetRelayedNodes- sets and saves node as relayed +func SetRelayedNodes(setRelayed bool, relay string, relayed []string) []models.Node { var returnnodes []models.Node - networkNodes, err := GetNetworkNodes(networkName) - if err != nil { - return returnnodes, err - } - for _, node := range networkNodes { - for _, addr := range addrs { - if addr == node.Address.IP.String() || addr == node.Address6.IP.String() { - if setRelayed { - node.IsRelayed = true - } else { - node.IsRelayed = false - } - data, err := json.Marshal(&node) - if err != nil { - return returnnodes, err - } - database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME) - returnnodes = append(returnnodes, node) - } + for _, id := range relayed { + node, err := GetNodeByID(id) + if err != nil { + logger.Log(0, "setRelayedNodes.GetNodebyID", err.Error()) + continue } - } - return returnnodes, nil -} -func GetRelayedNodes(relayNode *models.Node) ([]models.Node, error) { - var returnnodes []models.Node - networkNodes, err := GetNetworkNodes(relayNode.Network) - if err != nil { - return returnnodes, err - } - for _, node := range networkNodes { - for _, addr := range relayNode.RelayAddrs { - if addr == node.Address.IP.String() || addr == node.Address6.IP.String() { - returnnodes = append(returnnodes, node) - } + node.IsRelayed = setRelayed + if node.IsRelayed { + node.RelayedBy = relay + } else { + node.RelayedBy = "" } + node.SetLastModified() + data, err := json.Marshal(&node) + if err != nil { + logger.Log(0, "setRelayedNodes.Marshal", err.Error()) + continue + } + if err := database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME); err != nil { + logger.Log(0, "setRelayedNodes.Insert", err.Error()) + continue + } + returnnodes = append(returnnodes, node) } - return returnnodes, nil + return returnnodes } -// GetRelayedHosts - gets the relayed hosts of a relay host -func GetRelayedHosts(relayHost *models.Host) []models.Host { - relayedHosts := []models.Host{} - - for _, hostID := range relayHost.RelayedHosts { - relayedHost, err := GetHost(hostID) - if err == nil { - relayedHosts = append(relayedHosts, *relayedHost) - } - } - return relayedHosts -} +//func GetRelayedNodes(relayNode *models.Node) (models.Node, error) { +// var returnnodes []models.Node +// networkNodes, err := GetNetworkNodes(relayNode.Network) +// if err != nil { +// return returnnodes, err +// } +// for _, node := range networkNodes { +// for _, addr := range relayNode.RelayAddrs { +// if addr == node.Address.IP.String() || addr == node.Address6.IP.String() { +// returnnodes = append(returnnodes, node) +// } +// } +// } +// return returnnodes, nil +//} // ValidateRelay - checks if relay is valid func ValidateRelay(relay models.RelayRequest) error { var err error //isIp := functions.IsIpCIDR(gateway.RangeString) - empty := len(relay.RelayAddrs) == 0 + empty := len(relay.RelayedNodes) == 0 if empty { - err = errors.New("IP Ranges Cannot Be Empty") + return errors.New("IP Ranges Cannot Be Empty") + } + node, err := GetNodeByID(relay.NodeID) + if err != nil { + return err + } + if node.IsRelay { + return errors.New("node is already acting as a relay") + } + for _, relayedNodeID := range relay.RelayedNodes { + relayedNode, err := GetNodeByID(relayedNodeID) + if err != nil { + return err + } + if relayedNode.IsIngressGateway { + return errors.New("cannot relay an ingress gateway (" + relayedNodeID + ")") + } } return err } -func validateHostRelay(relay models.HostRelayRequest) error { - if len(relay.RelayedHosts) == 0 { - return errors.New("relayed hosts are empty") - } - return nil -} - -// UpdateRelay - updates a relay -func UpdateRelay(network string, oldAddrs []string, newAddrs []string) []models.Node { - var returnnodes []models.Node - time.Sleep(time.Second / 4) - _, err := SetRelayedNodes(false, network, oldAddrs) - if err != nil { - logger.Log(1, err.Error()) - } - returnnodes, err = SetRelayedNodes(true, network, newAddrs) - if err != nil { - logger.Log(1, err.Error()) - } - return returnnodes +// UpdateRelayed - updates relay nodes +func UpdateRelayed(relay string, oldNodes []string, newNodes []string) []models.Node { + _ = SetRelayedNodes(false, relay, oldNodes) + return SetRelayedNodes(true, relay, newNodes) } // DeleteRelay - deletes a relay @@ -188,15 +141,10 @@ func DeleteRelay(network, nodeid string) ([]models.Node, models.Node, error) { if err != nil { return returnnodes, models.Node{}, err } - returnnodes, err = SetRelayedNodes(false, node.Network, node.RelayAddrs) - if err != nil { - return returnnodes, node, err - } - + returnnodes = SetRelayedNodes(false, nodeid, node.RelayedNodes) node.IsRelay = false - node.RelayAddrs = []string{} + node.RelayedNodes = []string{} node.SetLastModified() - data, err := json.Marshal(&node) if err != nil { return returnnodes, models.Node{}, err @@ -207,24 +155,20 @@ func DeleteRelay(network, nodeid string) ([]models.Node, models.Node, error) { return returnnodes, node, nil } -// DeleteHostRelay - removes host as relay -func DeleteHostRelay(relayHostID string) (relayHost *models.Host, relayedHosts []models.Host, err error) { - relayHost, err = GetHost(relayHostID) +func getRelayedAddresses(id string) []net.IPNet { + addrs := []net.IPNet{} + node, err := GetNodeByID(id) if err != nil { - return + logger.Log(0, "getRelayedAddresses: "+err.Error()) + return addrs } - relayedHosts = SetRelayedHosts(false, relayHostID, relayHost.RelayedHosts) - relayHost.IsRelay = false - relayHost.RelayedHosts = []string{} - err = UpsertHost(relayHost) - if err != nil { - return + if node.Address.IP != nil { + node.Address.Mask = net.CIDRMask(32, 32) + addrs = append(addrs, node.Address) } - return -} - -// UpdateHostRelay - updates the relay host with new relayed hosts -func UpdateHostRelay(relayHostID string, oldRelayedHosts, newRelayedHosts []string) { - _ = SetRelayedHosts(false, relayHostID, oldRelayedHosts) - _ = SetRelayedHosts(true, relayHostID, newRelayedHosts) + if node.Address6.IP != nil { + node.Address.Mask = net.CIDRMask(128, 128) + addrs = append(addrs, node.Address6) + } + return addrs } diff --git a/logic/wireguard.go b/logic/wireguard.go index c26c7217..7ef6cbab 100644 --- a/logic/wireguard.go +++ b/logic/wireguard.go @@ -29,11 +29,11 @@ func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool { } } if newNode.IsRelay { - if len(currentNode.RelayAddrs) != len(newNode.RelayAddrs) { + if len(currentNode.RelayedNodes) != len(newNode.RelayedNodes) { return true } - for _, address := range newNode.RelayAddrs { - if !StringSliceContains(currentNode.RelayAddrs, address) { + for _, node := range newNode.RelayedNodes { + if !StringSliceContains(currentNode.RelayedNodes, node) { return true } } diff --git a/models/api_host.go b/models/api_host.go index 36ab6e72..70da8764 100644 --- a/models/api_host.go +++ b/models/api_host.go @@ -67,10 +67,6 @@ func (h *Host) ConvertNMHostToAPI() *ApiHost { a.Verbosity = h.Verbosity a.Version = h.Version a.IsDefault = h.IsDefault - a.IsRelay = h.IsRelay - a.RelayedHosts = h.RelayedHosts - a.IsRelayed = h.IsRelayed - a.RelayedBy = h.RelayedBy return &a } @@ -108,10 +104,6 @@ func (a *ApiHost) ConvertAPIHostToNMHost(currentHost *Host) *Host { h.Nodes = currentHost.Nodes h.TrafficKeyPublic = currentHost.TrafficKeyPublic h.OS = currentHost.OS - h.RelayedBy = a.RelayedBy - h.RelayedHosts = a.RelayedHosts - h.IsRelay = a.IsRelay - h.IsRelayed = a.IsRelayed h.ProxyEnabled = a.ProxyEnabled h.IsDefault = a.IsDefault h.NatType = currentHost.NatType diff --git a/models/api_node.go b/models/api_node.go index f78a9d6f..843b0bf2 100644 --- a/models/api_node.go +++ b/models/api_node.go @@ -25,11 +25,12 @@ type ApiNode struct { NetworkRange6 string `json:"networkrange6"` IsRelayed bool `json:"isrelayed"` IsRelay bool `json:"isrelay"` + RelayedBy string `json:"relayedby" bson:"relayedby" yaml:"relayedby"` + RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"` IsEgressGateway bool `json:"isegressgateway"` IsIngressGateway bool `json:"isingressgateway"` EgressGatewayRanges []string `json:"egressgatewayranges"` EgressGatewayNatEnabled bool `json:"egressgatewaynatenabled"` - RelayAddrs []string `json:"relayaddrs"` FailoverNode string `json:"failovernode"` DNSOn bool `json:"dnson"` IngressDns string `json:"ingressdns"` @@ -53,6 +54,8 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node { convertedNode.HostID, _ = uuid.Parse(a.HostID) convertedNode.IsRelay = a.IsRelay convertedNode.IsRelayed = a.IsRelayed + convertedNode.RelayedBy = a.RelayedBy + convertedNode.RelayedNodes = a.RelayedNodes convertedNode.PendingDelete = a.PendingDelete convertedNode.Failover = a.Failover convertedNode.IsEgressGateway = a.IsEgressGateway @@ -66,7 +69,7 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node { convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled convertedNode.PersistentKeepalive = time.Second * time.Duration(a.PersistentKeepalive) - convertedNode.RelayAddrs = a.RelayAddrs + convertedNode.RelayedNodes = a.RelayedNodes convertedNode.DefaultACL = a.DefaultACL convertedNode.OwnerID = currentNode.OwnerID _, networkRange, err := net.ParseCIDR(a.NetworkRange) @@ -140,11 +143,12 @@ func (nm *Node) ConvertToAPINode() *ApiNode { } apiNode.IsRelayed = nm.IsRelayed apiNode.IsRelay = nm.IsRelay + apiNode.RelayedBy = nm.RelayedBy + apiNode.RelayedNodes = nm.RelayedNodes apiNode.IsEgressGateway = nm.IsEgressGateway apiNode.IsIngressGateway = nm.IsIngressGateway apiNode.EgressGatewayRanges = nm.EgressGatewayRanges apiNode.EgressGatewayNatEnabled = nm.EgressGatewayNatEnabled - apiNode.RelayAddrs = nm.RelayAddrs apiNode.FailoverNode = nm.FailoverNode.String() if isUUIDSet(apiNode.FailoverNode) { apiNode.FailoverNode = "" diff --git a/models/node.go b/models/node.go index 517dd747..7b9c32ed 100644 --- a/models/node.go +++ b/models/node.go @@ -69,6 +69,10 @@ type CommonNode struct { IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"` EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"` IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"` + IsRelayed bool `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"` + RelayedBy string `json:"relayedby" bson:"relayedby" yaml:"relayedby"` + IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"` + RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"` IngressDNS string `json:"ingressdns" yaml:"ingressdns"` DNSOn bool `json:"dnson" yaml:"dnson"` PersistentKeepalive time.Duration `json:"persistentkeepalive" yaml:"persistentkeepalive"` @@ -86,9 +90,6 @@ type Node struct { EgressGatewayRequest EgressGatewayRequest `json:"egressgatewayrequest" bson:"egressgatewayrequest" yaml:"egressgatewayrequest"` IngressGatewayRange string `json:"ingressgatewayrange" bson:"ingressgatewayrange" yaml:"ingressgatewayrange"` IngressGatewayRange6 string `json:"ingressgatewayrange6" bson:"ingressgatewayrange6" yaml:"ingressgatewayrange6"` - IsRelayed bool `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"` - IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"` - RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs" yaml:"relayaddrs"` // == PRO == DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"` OwnerID string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"` @@ -350,7 +351,7 @@ func (node *LegacyNode) SetDefaultFailover() { } // Node.Fill - fills other node data into calling node data if not set on calling node -func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftables present +func (newNode *Node) Fill(currentNode *Node, isEE bool) { // TODO add new field for nftables present newNode.ID = currentNode.ID newNode.HostID = currentNode.HostID // Revisit the logic for boolean values @@ -401,13 +402,13 @@ func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftable if newNode.Action == "" { newNode.Action = currentNode.Action } - if newNode.RelayAddrs == nil { - newNode.RelayAddrs = currentNode.RelayAddrs + if newNode.RelayedNodes == nil { + newNode.RelayedNodes = currentNode.RelayedNodes } - if newNode.IsRelay != currentNode.IsRelay { + if newNode.IsRelay != currentNode.IsRelay && isEE { newNode.IsRelay = currentNode.IsRelay } - if newNode.IsRelayed == currentNode.IsRelayed { + if newNode.IsRelayed == currentNode.IsRelayed && isEE { newNode.IsRelayed = currentNode.IsRelayed } if newNode.Server == "" { diff --git a/models/structs.go b/models/structs.go index 35b521bc..2343cf6c 100644 --- a/models/structs.go +++ b/models/structs.go @@ -153,9 +153,9 @@ type EgressGatewayRequest struct { // RelayRequest - relay request struct type RelayRequest struct { - NodeID string `json:"nodeid" bson:"nodeid"` - NetID string `json:"netid" bson:"netid"` - RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs"` + NodeID string `json:"nodeid"` + NetID string `json:"netid"` + RelayedNodes []string `json:"relayaddrs"` } // HostRelayRequest - struct for host relay creation From 9787ce6ae879af0d6aa2ab524753ea5e79d89ba3 Mon Sep 17 00:00:00 2001 From: Aceix Date: Tue, 20 Jun 2023 10:58:40 +0000 Subject: [PATCH 04/16] fix(NET-312): increase log levels for verbose logs (#2403) --- logic/peers.go | 3 ++- mq/handlers.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/logic/peers.go b/logic/peers.go index f642605c..926d8231 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -13,6 +13,7 @@ import ( "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/servercfg" "golang.org/x/exp/slices" + "golang.org/x/exp/slog" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) @@ -123,7 +124,7 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host // endpoint detection always comes from the server hostPeerUpdate.Host.EndpointDetection = servercfg.EndpointDetectionEnabled() - logger.Log(1, "peer update for host", host.ID.String()) + slog.Debug("peer update for host", "hostId", host.ID.String()) peerIndexMap := make(map[string]int) for _, nodeID := range host.Nodes { nodeID := nodeID diff --git a/mq/handlers.go b/mq/handlers.go index c87b8bb5..8881fb9e 100644 --- a/mq/handlers.go +++ b/mq/handlers.go @@ -240,7 +240,7 @@ func UpdateMetrics(client mqtt.Client, msg mqtt.Message) { } } } - slog.Info("updated node metrics", "id", id) + slog.Debug("updated node metrics", "id", id) } } From 161e1d32d4876f8714c7379598c4c77b9a7b355e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 19:34:41 +0530 Subject: [PATCH 05/16] Bump golang.org/x/crypto from 0.9.0 to 0.10.0 (#2406) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.9.0 to 0.10.0. - [Commits](https://github.com/golang/crypto/compare/v0.9.0...v0.10.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 511f4e1e..216d6869 100644 --- a/go.mod +++ b/go.mod @@ -15,11 +15,11 @@ require ( github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/stretchr/testify v1.8.4 github.com/txn2/txeh v1.4.0 - golang.org/x/crypto v0.9.0 + golang.org/x/crypto v0.10.0 golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.8.0 - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/sys v0.9.0 // indirect + golang.org/x/text v0.10.0 // indirect golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c // indirect golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31 google.golang.org/protobuf v1.28.1 // indirect diff --git a/go.sum b/go.sum index ac69be14..01d869f1 100644 --- a/go.sum +++ b/go.sum @@ -121,8 +121,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -152,8 +152,8 @@ golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -161,8 +161,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 52221fe4ae6f251611f614a727cb29ba5ca75359 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 19:41:02 +0530 Subject: [PATCH 06/16] Bump alpine from 3.18.0 to 3.18.2 (#2408) Bumps alpine from 3.18.0 to 3.18.2. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Dockerfile | 2 +- Dockerfile-quick | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index b7b0e904..e2d5a3c3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ COPY . . RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -w " -tags ${tags} . # RUN go build -tags=ee . -o netmaker main.go -FROM alpine:3.18.0 +FROM alpine:3.18.2 # add a c lib # set the working directory diff --git a/Dockerfile-quick b/Dockerfile-quick index 13b6cbc8..feb67a93 100644 --- a/Dockerfile-quick +++ b/Dockerfile-quick @@ -1,5 +1,5 @@ #first stage - builder -FROM alpine:3.18.0 +FROM alpine:3.18.2 ARG version WORKDIR /app COPY ./netmaker /root/netmaker From 2cc0da344fbda9f771d240c2f1c807794ce216d6 Mon Sep 17 00:00:00 2001 From: Christopher Blaha Date: Wed, 21 Jun 2023 10:15:44 -0400 Subject: [PATCH 07/16] update develop to v0.20.3 (#2412) --- .github/ISSUE_TEMPLATE/bug-report.yml | 1 + README.md | 2 +- compose/docker-compose.netclient.yml | 2 +- controllers/docs.go | 2 +- k8s/client/netclient-daemonset.yaml | 2 +- k8s/client/netclient.yaml | 2 +- k8s/server/netmaker-ui.yaml | 2 +- main.go | 2 +- release.md | 2 +- scripts/nm-upgrade-0-17-1-to-0-19-0.sh | 2 +- swagger.yaml | 2 +- 11 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 771a4666..c38014b0 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -31,6 +31,7 @@ body: label: Version description: What version are you running? options: + - v0.20.3 - v0.20.2 - v0.20.1 - v0.20.0 diff --git a/README.md b/README.md index 54b34dc3..c69a561d 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@

- + diff --git a/compose/docker-compose.netclient.yml b/compose/docker-compose.netclient.yml index 06a52094..f6c05c3c 100644 --- a/compose/docker-compose.netclient.yml +++ b/compose/docker-compose.netclient.yml @@ -3,7 +3,7 @@ version: "3.4" services: netclient: container_name: netclient - image: 'gravitl/netclient:v0.20.2' + image: 'gravitl/netclient:v0.20.3' hostname: netmaker-1 network_mode: host restart: on-failure diff --git a/controllers/docs.go b/controllers/docs.go index 589e6892..5408c50c 100644 --- a/controllers/docs.go +++ b/controllers/docs.go @@ -10,7 +10,7 @@ // // Schemes: https // BasePath: / -// Version: 0.20.2 +// Version: 0.20.3 // Host: netmaker.io // // Consumes: diff --git a/k8s/client/netclient-daemonset.yaml b/k8s/client/netclient-daemonset.yaml index a03156bf..09d3a8bd 100644 --- a/k8s/client/netclient-daemonset.yaml +++ b/k8s/client/netclient-daemonset.yaml @@ -16,7 +16,7 @@ spec: hostNetwork: true containers: - name: netclient - image: gravitl/netclient:v0.20.2 + image: gravitl/netclient:v0.20.3 env: - name: TOKEN value: "TOKEN_VALUE" diff --git a/k8s/client/netclient.yaml b/k8s/client/netclient.yaml index cdb4777b..8a7aecba 100644 --- a/k8s/client/netclient.yaml +++ b/k8s/client/netclient.yaml @@ -28,7 +28,7 @@ spec: # - "" containers: - name: netclient - image: gravitl/netclient:v0.20.2 + image: gravitl/netclient:v0.20.3 env: - name: TOKEN value: "TOKEN_VALUE" diff --git a/k8s/server/netmaker-ui.yaml b/k8s/server/netmaker-ui.yaml index 047360b1..a39e63e2 100644 --- a/k8s/server/netmaker-ui.yaml +++ b/k8s/server/netmaker-ui.yaml @@ -15,7 +15,7 @@ spec: spec: containers: - name: netmaker-ui - image: gravitl/netmaker-ui:v0.20.2 + image: gravitl/netmaker-ui:v0.20.3 ports: - containerPort: 443 env: diff --git a/main.go b/main.go index d63b13a9..5110b841 100644 --- a/main.go +++ b/main.go @@ -30,7 +30,7 @@ import ( "golang.org/x/exp/slog" ) -var version = "v0.20.2" +var version = "v0.20.3" // Start DB Connection and start API Request Handler func main() { diff --git a/release.md b/release.md index f3c59b98..1ba5b70e 100644 --- a/release.md +++ b/release.md @@ -1,5 +1,5 @@ -# Netmaker v0.20.2 +# Netmaker v0.20.3 ## whats new - diff --git a/scripts/nm-upgrade-0-17-1-to-0-19-0.sh b/scripts/nm-upgrade-0-17-1-to-0-19-0.sh index 5b005829..9dd8a36d 100644 --- a/scripts/nm-upgrade-0-17-1-to-0-19-0.sh +++ b/scripts/nm-upgrade-0-17-1-to-0-19-0.sh @@ -1,6 +1,6 @@ #!/bin/bash -LATEST="v0.20.2" +LATEST="v0.20.3" INSTALL_PATH="/root" trap restore_old_netmaker_instructions diff --git a/swagger.yaml b/swagger.yaml index d4ab4f59..88ac6c2a 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -704,7 +704,7 @@ info: API calls must be authenticated via a header of the format -H “Authorization: Bearer ” 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//authenticate endpoint, as documented below. title: Netmaker - version: 0.20.2 + version: 0.20.3 paths: /api/dns: get: From 5950ded7edb1e3e857f26c8da1636c10eb453df0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 19:47:10 +0530 Subject: [PATCH 08/16] Bump golang.org/x/oauth2 from 0.8.0 to 0.9.0 (#2407) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.8.0 to 0.9.0. - [Commits](https://github.com/golang/oauth2/compare/v0.8.0...v0.9.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 216d6869..e005aa51 100644 --- a/go.mod +++ b/go.mod @@ -16,8 +16,8 @@ require ( github.com/stretchr/testify v1.8.4 github.com/txn2/txeh v1.4.0 golang.org/x/crypto v0.10.0 - golang.org/x/net v0.10.0 // indirect - golang.org/x/oauth2 v0.8.0 + golang.org/x/net v0.11.0 // indirect + golang.org/x/oauth2 v0.9.0 golang.org/x/sys v0.9.0 // indirect golang.org/x/text v0.10.0 // indirect golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c // indirect diff --git a/go.sum b/go.sum index 01d869f1..e5091055 100644 --- a/go.sum +++ b/go.sum @@ -133,10 +133,10 @@ golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs= +golang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From 6e39ae417887084d826e3fea2921604ddb35f4c3 Mon Sep 17 00:00:00 2001 From: Matthew R Kasun Date: Thu, 22 Jun 2023 17:03:04 -0400 Subject: [PATCH 09/16] add domain flag to nm-quick (#2416) --- scripts/nm-quick.sh | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/scripts/nm-quick.sh b/scripts/nm-quick.sh index 98541f16..8ae3d80a 100755 --- a/scripts/nm-quick.sh +++ b/scripts/nm-quick.sh @@ -4,7 +4,7 @@ CONFIG_FILE=netmaker.env # location of nm-quick.sh (usually `/root`) SCRIPT_DIR=$(dirname "$(realpath "$0")") CONFIG_PATH="$SCRIPT_DIR/$CONFIG_FILE" -NM_QUICK_VERSION="0.1.0" +NM_QUICK_VERSION="0.1.1" LATEST=$(curl -s https://api.github.com/repos/gravitl/netmaker/releases/latest | grep "tag_name" | cut -d : -f 2,3 | tr -d [:space:],\") if [ $(id -u) -ne 0 ]; then @@ -17,11 +17,12 @@ unset BUILD_TYPE unset BUILD_TAG unset IMAGE_TAG unset AUTO_BUILD +unset NETMAKER_BASE_DOMAIN # usage - displays usage instructions usage() { echo "nm-quick.sh v$NM_QUICK_VERSION" - echo "usage: ./nm-quick.sh [-e] [-b buildtype] [-t tag] [-a auto]" + echo "usage: ./nm-quick.sh [-e] [-b buildtype] [-t tag] [-a auto] [-d domain]" echo " -e if specified, will install netmaker EE" echo " -b type of build; options:" echo " \"version\" - will install a specific version of Netmaker using remote git and dockerhub" @@ -29,14 +30,16 @@ 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 " -a auto-build; skip prompts and use defaults, if none provided" + echo " -d domain; if specified, will use this domain instead of auto-generating one" echo "examples:" echo " nm-quick.sh -e -b version -t $LATEST" echo " nm-quick.sh -e -b local -t feature_v0.17.2_newfeature" echo " nm-quick.sh -e -b branch -t develop" + echo " nm-quick.sh -e -b version -t $LATEST -a -d example.com" exit 1 } -while getopts evab:t: flag; do +while getopts evab:d:t: flag; do case "${flag}" in e) INSTALL_TYPE="ee" @@ -60,6 +63,9 @@ while getopts evab:t: flag; do t) BUILD_TAG=${OPTARG} ;; + d) + NETMAKER_BASE_DOMAIN=${OPTARG} + ;; esac done @@ -490,8 +496,9 @@ set_install_vars() { if [ "$IP_ADDR" = "" ]; then IP_ADDR=$(curl -s ifconfig.me) fi - - NETMAKER_BASE_DOMAIN=nm.$(echo $IP_ADDR | tr . -).nip.io + if [ "$NETMAKER_BASE_DOMAIN" = "" ]; then + NETMAKER_BASE_DOMAIN=nm.$(echo $IP_ADDR | tr . -).nip.io + fi SERVER_HOST=$IP_ADDR if test -z "$MASTER_KEY"; then MASTER_KEY=$( From 11933fc07e3480318a179224b2f91a9ab1ec6966 Mon Sep 17 00:00:00 2001 From: Gabriel de Souza Seibel Date: Mon, 26 Jun 2023 02:23:00 -0300 Subject: [PATCH 10/16] Don't expose user hashed password (#2419) --- controllers/user.go | 22 ++++--- controllers/user_test.go | 123 +++++++++++++++++++++++++++++++++++++++ logic/auth.go | 14 ----- logic/users.go | 24 ++++++++ 4 files changed, 160 insertions(+), 23 deletions(-) diff --git a/controllers/user.go b/controllers/user.go index 32fabcc1..fdd59fca 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -19,6 +19,9 @@ var ( upgrader = websocket.Upgrader{} ) +// verifyJWT makes logic.VerifyJWT fakeable/mockable in tests +var verifyJWT = logic.VerifyJWT + func userHandlers(r *mux.Router) { r.HandleFunc("/api/users/adm/hasadmin", hasAdmin).Methods(http.MethodGet) @@ -152,7 +155,7 @@ func getUser(w http.ResponseWriter, r *http.Request) { var params = mux.Vars(r) usernameFetched := params["username"] - user, err := logic.GetUser(usernameFetched) + user, err := logic.GetReturnUser(usernameFetched) if err != nil { logger.Log(0, usernameFetched, "failed to fetch user: ", err.Error()) @@ -230,7 +233,7 @@ func createAdmin(w http.ResponseWriter, r *http.Request) { } logger.Log(1, admin.UserName, "was made a new admin") - json.NewEncoder(w).Encode(admin) + json.NewEncoder(w).Encode(logic.ToReturnUser(admin)) } // swagger:route POST /api/users/{username} user createUser @@ -264,7 +267,7 @@ func createUser(w http.ResponseWriter, r *http.Request) { return } logger.Log(1, user.UserName, "was created") - json.NewEncoder(w).Encode(user) + json.NewEncoder(w).Encode(logic.ToReturnUser(user)) } // swagger:route PUT /api/users/networks/{username} user updateUserNetworks @@ -314,12 +317,13 @@ func updateUserNetworks(w http.ResponseWriter, r *http.Request) { } logger.Log(1, username, "status was updated") // re-read and return the new user struct - if userChange, err = logic.GetUser(username); err != nil { + returnUser, err := logic.GetReturnUser(username) + if err != nil { logger.Log(0, username, "failed to fetch user: ", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - json.NewEncoder(w).Encode(userChange) + json.NewEncoder(w).Encode(returnUser) } // swagger:route PUT /api/users/{username} user updateUser @@ -337,7 +341,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") var params = mux.Vars(r) // start here - jwtUser, _, isadmin, err := logic.VerifyJWT(r.Header.Get("Authorization")) + jwtUser, _, isadmin, err := verifyJWT(r.Header.Get("Authorization")) if err != nil { logger.Log(0, "verifyJWT error", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) @@ -385,7 +389,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) { return } logger.Log(1, username, "was updated") - json.NewEncoder(w).Encode(user) + json.NewEncoder(w).Encode(logic.ToReturnUser(*user)) } // swagger:route PUT /api/users/{username}/adm user updateUserAdm @@ -409,7 +413,7 @@ func updateUserAdm(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - if auth.IsOauthUser(user) != nil { + if auth.IsOauthUser(user) == nil { err := fmt.Errorf("cannot update user info for oauth user %s", username) logger.Log(0, err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden")) @@ -436,7 +440,7 @@ func updateUserAdm(w http.ResponseWriter, r *http.Request) { return } logger.Log(1, username, "was updated (admin)") - json.NewEncoder(w).Encode(user) + json.NewEncoder(w).Encode(logic.ToReturnUser(*user)) } // swagger:route DELETE /api/users/{username} user deleteUser diff --git a/controllers/user_test.go b/controllers/user_test.go index 655760ca..f98e341f 100644 --- a/controllers/user_test.go +++ b/controllers/user_test.go @@ -1,6 +1,12 @@ package controller import ( + "bytes" + "github.com/go-jose/go-jose/v3/json" + "github.com/gorilla/mux" + "io" + "net/http" + "net/http/httptest" "testing" "github.com/stretchr/testify/assert" @@ -19,6 +25,123 @@ func deleteAllUsers(t *testing.T) { } } +func TestGetUserNoHashedPassword(t *testing.T) { + // prepare existing user base + user := models.User{UserName: "freddie", Password: "password"} + haveOnlyOneUser(t, user) + + // prepare request + rec, req := prepareUserRequest(t, models.User{}, user.UserName) + + // test response + getUser(rec, req) + assertUserNameButNoPassword(t, rec.Body, user.UserName) +} + +func TestCreateAdminNoHashedPassword(t *testing.T) { + // prepare existing user base + deleteAllUsers(t) + + // prepare request + user := models.User{UserName: "jonathan", Password: "password"} + rec, req := prepareUserRequest(t, user, "") + + // test response + createAdmin(rec, req) + assertUserNameButNoPassword(t, rec.Body, user.UserName) +} + +func TestCreateUserNoHashedPassword(t *testing.T) { + // prepare existing user base + deleteAllUsers(t) + + // prepare request + user := models.User{UserName: "jonathan", Password: "password"} + rec, req := prepareUserRequest(t, user, "") + + // test response + createUser(rec, req) + assertUserNameButNoPassword(t, rec.Body, user.UserName) +} + +func TestUpdateUserNetworksNoHashedPassword(t *testing.T) { + // prepare existing user base + user1 := models.User{UserName: "joestar", Password: "jonathan"} + haveOnlyOneUser(t, user1) + + // prepare request + user2 := models.User{UserName: "joestar", Password: "joseph"} + rec, req := prepareUserRequest(t, user2, user1.UserName) + + // test response + updateUserNetworks(rec, req) + assertUserNameButNoPassword(t, rec.Body, user1.UserName) +} + +func TestUpdateUserNoHashedPassword(t *testing.T) { + // prepare existing user base + user1 := models.User{UserName: "dio", Password: "brando"} + haveOnlyOneUser(t, user1) + + // prepare request + user2 := models.User{UserName: "giorno", Password: "giovanna"} + rec, req := prepareUserRequest(t, user2, user1.UserName) + + // mock the jwt verification + oldVerify := verifyJWT + verifyJWT = func(bearerToken string) (username string, networks []string, isadmin bool, err error) { + return user1.UserName, user1.Networks, user1.IsAdmin, nil + } + defer func() { verifyJWT = oldVerify }() + + // test response + updateUser(rec, req) + assertUserNameButNoPassword(t, rec.Body, user2.UserName) +} + +func TestUpdateUserAdmNoHashedPassword(t *testing.T) { + // prepare existing user base + user1 := models.User{UserName: "dio", Password: "brando", IsAdmin: true} + haveOnlyOneUser(t, user1) + + // prepare request + user2 := models.User{UserName: "giorno", Password: "giovanna"} + rec, req := prepareUserRequest(t, user2, user1.UserName) + + // test response + updateUserAdm(rec, req) + assertUserNameButNoPassword(t, rec.Body, user2.UserName) +} + +func prepareUserRequest(t *testing.T, userForBody models.User, userNameForParam string) (*httptest.ResponseRecorder, *http.Request) { + bits, err := json.Marshal(userForBody) + assert.Nil(t, err) + body := bytes.NewReader(bits) + rec := httptest.NewRecorder() + req := httptest.NewRequest("ANY", "https://example.com", body) // only the body matters here + req = mux.SetURLVars(req, map[string]string{"username": userNameForParam}) + return rec, req +} + +func haveOnlyOneUser(t *testing.T, user models.User) { + deleteAllUsers(t) + var err error + if user.IsAdmin { + err = logic.CreateAdmin(&user) + } else { + err = logic.CreateUser(&user) + } + assert.Nil(t, err) +} + +func assertUserNameButNoPassword(t *testing.T, r io.Reader, userName string) { + var resp models.User + err := json.NewDecoder(r).Decode(&resp) + assert.Nil(t, err) + assert.Equal(t, userName, resp.UserName) + assert.Empty(t, resp.Password) +} + func TestHasAdmin(t *testing.T) { // delete all current users users, _ := logic.GetUsers() diff --git a/logic/auth.go b/logic/auth.go index c653cb52..6c2b210b 100644 --- a/logic/auth.go +++ b/logic/auth.go @@ -42,20 +42,6 @@ func HasAdmin() (bool, error) { return false, err } -// GetReturnUser - gets a user -func GetReturnUser(username string) (models.ReturnUser, error) { - - var user models.ReturnUser - record, err := database.FetchRecord(database.USERS_TABLE_NAME, username) - if err != nil { - return user, err - } - if err = json.Unmarshal([]byte(record), &user); err != nil { - return models.ReturnUser{}, err - } - return user, err -} - // GetUsers - gets users func GetUsers() ([]models.ReturnUser, error) { diff --git a/logic/users.go b/logic/users.go index 4581d852..cbbade5e 100644 --- a/logic/users.go +++ b/logic/users.go @@ -26,6 +26,30 @@ func GetUser(username string) (*models.User, error) { return &user, err } +// GetReturnUser - gets a user +func GetReturnUser(username string) (models.ReturnUser, error) { + + var user models.ReturnUser + record, err := database.FetchRecord(database.USERS_TABLE_NAME, username) + if err != nil { + return user, err + } + if err = json.Unmarshal([]byte(record), &user); err != nil { + return models.ReturnUser{}, err + } + return user, err +} + +// ToReturnUser - gets a user as a return user +func ToReturnUser(user models.User) models.ReturnUser { + return models.ReturnUser{ + UserName: user.UserName, + Networks: user.Networks, + IsAdmin: user.IsAdmin, + Groups: user.Groups, + } +} + // GetGroupUsers - gets users in a group func GetGroupUsers(group string) ([]models.ReturnUser, error) { var returnUsers []models.ReturnUser From aca65cf4ce926d64aa18e5ebb7b807fc2517d7f5 Mon Sep 17 00:00:00 2001 From: Matthew R Kasun Date: Mon, 26 Jun 2023 09:51:20 -0400 Subject: [PATCH 11/16] Net 374 (#2420) * add domain flag to nm-quick * get server to use * set up ssh * save server to tmp file --- .github/workflows/branchtest.yml | 57 ++++++++++++++++++++++++++-- .github/workflows/deletedroplets.yml | 28 ++++++++++++-- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/.github/workflows/branchtest.yml b/.github/workflows/branchtest.yml index 03c4ff58..222e9be5 100644 --- a/.github/workflows/branchtest.yml +++ b/.github/workflows/branchtest.yml @@ -10,12 +10,13 @@ jobs: skip-check: runs-on: ubuntu-latest outputs: - skip: ${{ steps.check.outputs.skip }} + skip: ${{ steps.skip.outputs.skip }} steps: - id: skip uses: fkirc/skip-duplicate-actions@v5 with: concurrent_skipping: 'always' + getbranch: runs-on: ubuntu-latest needs: skip-check @@ -29,7 +30,7 @@ jobs: repository: gravitl/netclient ref: develop - name: check if branch exists - id: checkbranch + id: getbranch run: | if git show-ref ${{ github.head_ref}}; then echo branch exists @@ -39,12 +40,62 @@ jobs: echo "netclientbranch=develop" >> $GITHUB_OUTPUT fi + getserver: + runs-on: ubuntu-latest + needs: skip-check + if: ${{ needs.skip-check.outputs.skip != 'true' }} + outputs: + netmakerserver: ${{ steps.getserver.outputs.server }} + steps: + - name: setup ssh + run: | + mkdir -p ~/.ssh/ + echo "$SSH_KEY" > ~/.ssh/id_devops + chmod 600 ~/.ssh/id_devops + cat >>~/.ssh/config <> /tmp/server + break + fi + done + echo server is $server + if [ "$server" == "" ] + then + echo server not set + exit 1 + fi + echo "netmakerserver=${ server }" >> $GITHUB_OUTPUT + - name: save server name + uses: actions/upload-artifact@v3 + with: + name: server + path: /tmp/ping + retention-days: 3 + terraform: - needs: getbranch + needs: [getbranch, getserver] uses: gravitl/devops/.github/workflows/terraform.yml@master with: netmakerbranch: ${{ github.head_ref }} netclientbranch: ${{ needs.getbranch.outputs.netclientbranch }} + server: ${{ needs.getserver.outputs.netmakerserver }} secrets: inherit diff --git a/.github/workflows/deletedroplets.yml b/.github/workflows/deletedroplets.yml index 406349a9..7ec939dd 100644 --- a/.github/workflows/deletedroplets.yml +++ b/.github/workflows/deletedroplets.yml @@ -16,6 +16,9 @@ jobs: with: run_id: ${{ github.event.workflow_run.id}} if_no_artifact_found: warn + - name: get server name + run: | + echo "SERVER=$(cat ./server/server) >> $GITHUB_ENV" - name: discord success message uses: appleboy/discord-action@master with: @@ -23,7 +26,7 @@ jobs: webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} color: "#42f545" username: "GitHub Bot" - message: "${{ github.repository }}: ${{ github.event.workflow_run.name }} was successful: droplets from this workflow (tag ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}) will be deleted in 15 min" + message: "${{ github.repository }}: ${{ github.event.workflow_run.name }} on dashboard.${{ env.SERVER }}.clustercat.com was successful: droplets from this workflow (tag ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}) will be deleted in 15 min" file: ./results/results.log - name: delete droplets if: success() || failure() @@ -36,6 +39,14 @@ jobs: env: DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }} TAG: ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }} + - name: mark server as available + uses: appleboy/ssh-action@master + with: + host: server.${{ env.SERVER }}.clustercat.com + username: root + key: ${{ secrets.TESTING_SSH_KEY }} + script: | + rm /tmp/branchtest on-failure: runs-on: ubuntu-latest @@ -46,6 +57,9 @@ jobs: with: run_id: ${{ github.event.workflow_run.id}} if_no_artifact_found: warn + - name: get server name + run: | + echo "SERVER=$(cat ./server/server) >> $GITHUB_ENV" - name: discord failure message uses: appleboy/discord-action@master with: @@ -53,7 +67,7 @@ jobs: webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} color: "#990000" username: "GitHub Bot" - message: "${{ github.repository }}: ${{ github.event.workflow_run.name }} failed: droplets from this workflow (tag ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt}}) will be deleted in 5 hours" + message: "${{ github.repository }}: ${{ github.event.workflow_run.name }} failed: droplets from this workflow (tag ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt}}) will be deleted in 3 hours" file: ./results/results.log - name: discord error message uses: appleboy/discord-action@master @@ -67,7 +81,7 @@ jobs: - name: delete droplets if: success() || failure() run: | - sleep 5h + sleep 3h curl -X GET \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \ @@ -75,3 +89,11 @@ jobs: env: DIGITALOCEAN_TOKEN: ${{ secrets.DIGITALOCEAN_TOKEN }} TAG: ${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }} + - name: mark server as available + uses: appleboy/ssh-action@master + with: + host: server.${{ env.SERVER }}.clustercat.com + username: root + key: ${{ secrets.TESTING_SSH_KEY }} + script: | + rm /tmp/branchtest From 34c56d5ea14fcc41197e9c9e81005e02276b13fa Mon Sep 17 00:00:00 2001 From: Aceix Date: Tue, 27 Jun 2023 07:00:25 +0000 Subject: [PATCH 12/16] Revert "prevent ingress creation on NATed host (#2395)" (#2422) This reverts commit 20998dd6131e4957c36754ab82d9f2da1417c9d2. --- logic/gateway.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/logic/gateway.go b/logic/gateway.go index 2677048c..556b108b 100644 --- a/logic/gateway.go +++ b/logic/gateway.go @@ -115,9 +115,6 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq if host.FirewallInUse == models.FIREWALL_NONE { return models.Node{}, errors.New("firewall is not supported for ingress gateways") } - if host.NatType != models.NAT_Types.Public { - return models.Node{}, errors.New("ingress cannot be created on nodes behind NAT") - } network, err := GetParentNetwork(netid) if err != nil { From 243cc7449057286f8339cd0009e9d909f6523974 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Tue, 27 Jun 2023 22:36:24 +0530 Subject: [PATCH 13/16] remove endpoint detection from host model --- logic/hosts.go | 1 - logic/peers.go | 3 +-- models/host.go | 1 - models/mqtt.go | 25 +++++++++++++------------ mq/handlers.go | 3 +-- 5 files changed, 15 insertions(+), 18 deletions(-) diff --git a/logic/hosts.go b/logic/hosts.go index 34fa9766..a41bccf5 100644 --- a/logic/hosts.go +++ b/logic/hosts.go @@ -111,7 +111,6 @@ func CreateHost(h *models.Host) error { } h.HostPass = string(hash) h.AutoUpdate = servercfg.AutoUpdateEnabled() - h.EndpointDetection = servercfg.EndpointDetectionEnabled() // if another server has already updated proxyenabled, leave it alone if !h.ProxyEnabledSet { log.Println("checking default proxy", servercfg.GetServerConfig().DefaultProxyMode) diff --git a/logic/peers.go b/logic/peers.go index 926d8231..252aecee 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -122,8 +122,7 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host } // endpoint detection always comes from the server - hostPeerUpdate.Host.EndpointDetection = servercfg.EndpointDetectionEnabled() - + hostPeerUpdate.EndpointDetection = servercfg.EndpointDetectionEnabled() slog.Debug("peer update for host", "hostId", host.ID.String()) peerIndexMap := make(map[string]int) for _, nodeID := range host.Nodes { diff --git a/models/host.go b/models/host.go index f4656c07..12e79d40 100644 --- a/models/host.go +++ b/models/host.go @@ -48,7 +48,6 @@ type Host struct { IPForwarding bool `json:"ipforwarding" yaml:"ipforwarding"` DaemonInstalled bool `json:"daemoninstalled" yaml:"daemoninstalled"` AutoUpdate bool `json:"autoupdate" yaml:"autoupdate"` - EndpointDetection bool `json:"endpointdetection" yaml:"endpointdetection"` HostPass string `json:"hostpass" yaml:"hostpass"` Name string `json:"name" yaml:"name"` OS string `json:"os" yaml:"os"` diff --git a/models/mqtt.go b/models/mqtt.go index 30e1c7b3..34114878 100644 --- a/models/mqtt.go +++ b/models/mqtt.go @@ -8,18 +8,19 @@ import ( // HostPeerUpdate - struct for host peer updates type HostPeerUpdate struct { - Host Host `json:"host" bson:"host" yaml:"host"` - Server string `json:"server" bson:"server" yaml:"server"` - ServerVersion string `json:"serverversion" bson:"serverversion" yaml:"serverversion"` - ServerAddrs []ServerAddr `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"` - NodePeers []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"` - Peers []wgtypes.PeerConfig - HostPeerIDs HostPeerMap `json:"hostpeerids" bson:"hostpeerids" yaml:"hostpeerids"` - ProxyUpdate ProxyManagerPayload `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"` - EgressInfo map[string]EgressInfo `json:"egress_info" bson:"egress_info" yaml:"egress_info"` // map key is node ID - IngressInfo IngressInfo `json:"ingress_info" bson:"ext_peers" yaml:"ext_peers"` - PeerIDs PeerMap `json:"peerids" bson:"peerids" yaml:"peerids"` - HostNetworkInfo HostInfoMap `json:"host_network_info,omitempty" bson:"host_network_info,omitempty" yaml:"host_network_info,omitempty"` + Host Host `json:"host" bson:"host" yaml:"host"` + Server string `json:"server" bson:"server" yaml:"server"` + ServerVersion string `json:"serverversion" bson:"serverversion" yaml:"serverversion"` + ServerAddrs []ServerAddr `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"` + NodePeers []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"` + Peers []wgtypes.PeerConfig + HostPeerIDs HostPeerMap `json:"hostpeerids" bson:"hostpeerids" yaml:"hostpeerids"` + ProxyUpdate ProxyManagerPayload `json:"proxy_update" bson:"proxy_update" yaml:"proxy_update"` + EgressInfo map[string]EgressInfo `json:"egress_info" bson:"egress_info" yaml:"egress_info"` // map key is node ID + IngressInfo IngressInfo `json:"ingress_info" bson:"ext_peers" yaml:"ext_peers"` + PeerIDs PeerMap `json:"peerids" bson:"peerids" yaml:"peerids"` + EndpointDetection bool `json:"endpointdetection" yaml:"endpointdetection"` + HostNetworkInfo HostInfoMap `json:"host_network_info,omitempty" bson:"host_network_info,omitempty" yaml:"host_network_info,omitempty"` } // IngressInfo - struct for ingress info diff --git a/mq/handlers.go b/mq/handlers.go index 8881fb9e..75d90b80 100644 --- a/mq/handlers.go +++ b/mq/handlers.go @@ -438,8 +438,7 @@ func handleHostCheckin(h, currentHost *models.Host) bool { ifaceDelta := len(h.Interfaces) != len(currentHost.Interfaces) || !h.EndpointIP.Equal(currentHost.EndpointIP) || (len(h.NatType) > 0 && h.NatType != currentHost.NatType) || - h.DefaultInterface != currentHost.DefaultInterface || - h.EndpointDetection != servercfg.EndpointDetectionEnabled() + h.DefaultInterface != currentHost.DefaultInterface if ifaceDelta { // only save if something changes currentHost.EndpointIP = h.EndpointIP currentHost.Interfaces = h.Interfaces From 84617359fa15d9c9b56489c0cc6a919f9d77c9d9 Mon Sep 17 00:00:00 2001 From: Aceix Date: Wed, 28 Jun 2023 14:53:11 +0000 Subject: [PATCH 14/16] fix(NET-379): send host NAT type over api (#2423) --- models/api_host.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/models/api_host.go b/models/api_host.go index 70da8764..abf29555 100644 --- a/models/api_host.go +++ b/models/api_host.go @@ -34,6 +34,7 @@ type ApiHost struct { RelayedBy string `json:"relayed_by" bson:"relayed_by" yaml:"relayed_by"` IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"` RelayedHosts []string `json:"relay_hosts" bson:"relay_hosts" yaml:"relay_hosts"` + NatType string `json:"nat_type" yaml:"nat_type"` } // Host.ConvertNMHostToAPI - converts a Netmaker host to an API editable host @@ -67,6 +68,7 @@ func (h *Host) ConvertNMHostToAPI() *ApiHost { a.Verbosity = h.Verbosity a.Version = h.Version a.IsDefault = h.IsDefault + a.NatType = h.NatType return &a } From 230e062c844758de3e7e7c3326e23e009c1aaba7 Mon Sep 17 00:00:00 2001 From: Abhishek K <32607604+abhishek9686@users.noreply.github.com> Date: Wed, 28 Jun 2023 20:33:06 +0530 Subject: [PATCH 15/16] GRA-1298: License check changes, free tier limits for saas (#2418) * set free tier limits through config * add host limit to config * check for host limit on free tier * fix license validation, replace node limit with hosts * add hosts to telemetry data * debug init * validate license every 1hr * hook manager, api to fetch server usage * hook manager, server usage api * encode json server usage api * update ngork url * update license validation endpoint * avoid setting limits on eer * adding hotfix * correct users limits env var * add comments to exported funcs --------- Co-authored-by: afeiszli --- config/config.go | 5 ++++ controllers/limits.go | 10 +------ controllers/server.go | 38 +++++++++++++++++++++++++++ ee/initialize.go | 21 +++------------ ee/license.go | 32 +++++++++-------------- ee/types.go | 58 ++++++++++++++--------------------------- ee/util.go | 9 ++++--- logic/hosts.go | 9 ++++++- logic/serverconf.go | 13 +++++++-- logic/telemetry.go | 3 +++ logic/timer.go | 36 +++++++++++++++++++++++++ main.go | 7 ++++- models/structs.go | 16 ++++++++++++ mq/emqx.go | 7 ++++- servercfg/serverconf.go | 53 +++++++++++++++++++++++++++++++++++++ 15 files changed, 225 insertions(+), 92 deletions(-) diff --git a/config/config.go b/config/config.go index fe95fea9..0dbafc85 100644 --- a/config/config.go +++ b/config/config.go @@ -83,6 +83,11 @@ type ServerConfig struct { TurnUserName string `yaml:"turn_username"` TurnPassword string `yaml:"turn_password"` UseTurn bool `yaml:"use_turn"` + UsersLimit int `yaml:"user_limit"` + ClientsLimit int `yaml:"client_limit"` + NetworksLimit int `yaml:"network_limit"` + HostsLimit int `yaml:"host_limit"` + DeployedByOperator bool `yaml:"deployed_by_operator"` } // ProxyMode - default proxy mode for server diff --git a/controllers/limits.go b/controllers/limits.go index 268ada2d..2b4cee15 100644 --- a/controllers/limits.go +++ b/controllers/limits.go @@ -6,7 +6,6 @@ import ( "github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/models" - "github.com/gravitl/netmaker/servercfg" ) // limit consts @@ -23,20 +22,13 @@ func checkFreeTierLimits(limit_choice int, next http.Handler) http.HandlerFunc { Code: http.StatusForbidden, Message: "free tier limits exceeded on networks", } - if logic.Free_Tier && servercfg.Is_EE { // check that free tier limits not exceeded + if logic.Free_Tier { // check that free tier limits not exceeded if limit_choice == networks_l { currentNetworks, err := logic.GetNetworks() if (err != nil && !database.IsEmptyRecord(err)) || len(currentNetworks) >= logic.Networks_Limit { logic.ReturnErrorResponse(w, r, errorResponse) return } - } else if limit_choice == node_l { - nodes, err := logic.GetAllNodes() - if (err != nil && !database.IsEmptyRecord(err)) || len(nodes) >= logic.Node_Limit { - errorResponse.Message = "free tier limits exceeded on nodes" - logic.ReturnErrorResponse(w, r, errorResponse) - return - } } else if limit_choice == users_l { users, err := logic.GetUsers() if (err != nil && !database.IsEmptyRecord(err)) || len(users) >= logic.Users_Limit { diff --git a/controllers/server.go b/controllers/server.go index bcbf7c9a..e9aa1b76 100644 --- a/controllers/server.go +++ b/controllers/server.go @@ -22,6 +22,38 @@ func serverHandlers(r *mux.Router) { r.HandleFunc("/api/server/getconfig", allowUsers(http.HandlerFunc(getConfig))).Methods(http.MethodGet) r.HandleFunc("/api/server/getserverinfo", Authorize(true, false, "node", http.HandlerFunc(getServerInfo))).Methods(http.MethodGet) r.HandleFunc("/api/server/status", http.HandlerFunc(getStatus)).Methods(http.MethodGet) + r.HandleFunc("/api/server/usage", Authorize(true, false, "user", http.HandlerFunc(getUsage))).Methods(http.MethodGet) +} +func getUsage(w http.ResponseWriter, r *http.Request) { + type usage struct { + Hosts int `json:"hosts"` + Clients int `json:"clients"` + Networks int `json:"networks"` + Users int `json:"users"` + } + var serverUsage usage + hosts, err := logic.GetAllHosts() + if err == nil { + serverUsage.Hosts = len(hosts) + } + clients, err := logic.GetAllExtClients() + if err == nil { + serverUsage.Clients = len(clients) + } + users, err := logic.GetUsers() + if err == nil { + serverUsage.Users = len(users) + } + networks, err := logic.GetNetworks() + if err == nil { + serverUsage.Networks = len(networks) + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(models.SuccessResponse{ + Code: http.StatusOK, + Response: serverUsage, + }) + } // swagger:route GET /api/server/status server getStatus @@ -41,6 +73,12 @@ func getStatus(w http.ResponseWriter, r *http.Request) { type status struct { DB bool `json:"db_connected"` Broker bool `json:"broker_connected"` + Usage struct { + Hosts int `json:"hosts"` + Clients int `json:"clients"` + Networks int `json:"networks"` + Users int `json:"users"` + } `json:"usage"` } currentServerStatus := status{ diff --git a/ee/initialize.go b/ee/initialize.go index ab5e3ba0..455ee59d 100644 --- a/ee/initialize.go +++ b/ee/initialize.go @@ -16,6 +16,7 @@ import ( // InitEE - Initialize EE Logic func InitEE() { setIsEnterprise() + servercfg.Is_EE = true models.SetLogo(retrieveEELogo()) controller.HttpHandlers = append( controller.HttpHandlers, @@ -27,13 +28,8 @@ func InitEE() { logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() { // == License Handling == ValidateLicense() - if Limits.FreeTier { - logger.Log(0, "proceeding with Free Tier license") - logic.SetFreeTierForTelemetry(true) - } else { - logger.Log(0, "proceeding with Paid Tier license") - logic.SetFreeTierForTelemetry(false) - } + logger.Log(0, "proceeding with Paid Tier license") + logic.SetFreeTierForTelemetry(false) // == End License Handling == AddLicenseHooks() resetFailover() @@ -46,17 +42,6 @@ func InitEE() { logic.AllowClientNodeAccess = eelogic.RemoveDeniedNodeFromClient } -func setControllerLimits() { - logic.Node_Limit = Limits.Nodes - logic.Users_Limit = Limits.Users - logic.Clients_Limit = Limits.Clients - logic.Free_Tier = Limits.FreeTier - servercfg.Is_EE = true - if logic.Free_Tier { - logic.Networks_Limit = 3 - } -} - func resetFailover() { nets, err := logic.GetNetworks() if err == nil { diff --git a/ee/license.go b/ee/license.go index 521326ec..d9b197ab 100644 --- a/ee/license.go +++ b/ee/license.go @@ -9,12 +9,13 @@ import ( "encoding/json" "fmt" "io" - "math" "net/http" + "time" "github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logic" + "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/netclient/ncutils" "github.com/gravitl/netmaker/servercfg" "golang.org/x/crypto/nacl/box" @@ -31,8 +32,14 @@ type apiServerConf struct { // AddLicenseHooks - adds the validation and cache clear hooks func AddLicenseHooks() { - logic.AddHook(ValidateLicense) - logic.AddHook(ClearLicenseCache) + logic.HookManagerCh <- models.HookDetails{ + Hook: ValidateLicense, + Interval: time.Hour, + } + logic.HookManagerCh <- models.HookDetails{ + Hook: ClearLicenseCache, + Interval: time.Hour, + } } // ValidateLicense - the initial license check for netmaker server @@ -58,8 +65,8 @@ func ValidateLicense() error { } licenseSecret := LicenseSecret{ - UserID: netmakerAccountID, - Limits: getCurrentServerLimit(), + AssociatedID: netmakerAccountID, + Limits: getCurrentServerLimit(), } secretData, err := json.Marshal(&licenseSecret) @@ -92,17 +99,6 @@ func ValidateLicense() error { logger.FatalLog0(errValidation.Error()) } - Limits.Networks = math.MaxInt - Limits.FreeTier = license.FreeTier == "yes" - Limits.Clients = license.LimitClients - Limits.Nodes = license.LimitNodes - Limits.Servers = license.LimitServers - Limits.Users = license.LimitUsers - if Limits.FreeTier { - Limits.Networks = 3 - } - setControllerLimits() - logger.Log(0, "License validation succeeded!") return nil } @@ -167,6 +163,7 @@ func validateLicenseKey(encryptedData []byte, publicKey *[32]byte) ([]byte, erro } msg := ValidateLicenseRequest{ + LicenseKey: servercfg.GetLicenseKey(), NmServerPubKey: base64encode(publicKeyBytes), EncryptedPart: base64encode(encryptedData), } @@ -180,9 +177,6 @@ func validateLicenseKey(encryptedData []byte, publicKey *[32]byte) ([]byte, erro if err != nil { return nil, err } - reqParams := req.URL.Query() - reqParams.Add("licensevalue", servercfg.GetLicenseKey()) - req.URL.RawQuery = reqParams.Encode() req.Header.Add("Content-Type", "application/json") req.Header.Add("Accept", "application/json") client := &http.Client{} diff --git a/ee/types.go b/ee/types.go index f59d21fd..17b5dd33 100644 --- a/ee/types.go +++ b/ee/types.go @@ -3,7 +3,7 @@ package ee import "fmt" const ( - api_endpoint = "https://api.controller.netmaker.io/api/v1/license/validate" + api_endpoint = "https://api.accounts.netmaker.io/api/v1/license/validate" license_cache_key = "license_response_cache" license_validation_err_msg = "invalid license" server_id_key = "nm-server-id" @@ -11,38 +11,17 @@ const ( var errValidation = fmt.Errorf(license_validation_err_msg) -// Limits - limits to be referenced throughout server -var Limits = GlobalLimits{ - Servers: 0, - Users: 0, - Nodes: 0, - Clients: 0, - Networks: 0, - FreeTier: false, -} - -// GlobalLimits - struct for holding global limits on this netmaker server in memory -type GlobalLimits struct { - Servers int - Users int - Nodes int - Clients int - FreeTier bool - Networks int -} - // LicenseKey - the license key struct representation with associated data type LicenseKey struct { - LicenseValue string `json:"license_value"` // actual (public) key and the unique value for the key - Expiration int64 `json:"expiration"` - LimitServers int `json:"limit_servers"` - LimitUsers int `json:"limit_users"` - LimitNodes int `json:"limit_nodes"` - LimitClients int `json:"limit_clients"` - Metadata string `json:"metadata"` - SubscriptionID string `json:"subscription_id"` // for a paid subscription (non-free-tier license) - FreeTier string `json:"free_tier"` // yes if free tier - IsActive string `json:"is_active"` // yes if active + LicenseValue string `json:"license_value"` // actual (public) key and the unique value for the key + Expiration int64 `json:"expiration"` + LimitServers int `json:"limit_servers"` + LimitUsers int `json:"limit_users"` + LimitHosts int `json:"limit_hosts"` + LimitNetworks int `json:"limit_networks"` + LimitClients int `json:"limit_clients"` + Metadata string `json:"metadata"` + IsActive bool `json:"is_active"` // yes if active } // ValidatedLicense - the validated license struct @@ -53,28 +32,31 @@ type ValidatedLicense struct { // LicenseSecret - the encrypted struct for sending user-id type LicenseSecret struct { - UserID string `json:"user_id" binding:"required"` // UUID for user foreign key to User table - Limits LicenseLimits `json:"limits" binding:"required"` + AssociatedID string `json:"associated_id" binding:"required"` // UUID for user foreign key to User table + Limits LicenseLimits `json:"limits" binding:"required"` } // LicenseLimits - struct license limits type LicenseLimits struct { - Servers int `json:"servers" binding:"required"` - Users int `json:"users" binding:"required"` - Nodes int `json:"nodes" binding:"required"` - Clients int `json:"clients" binding:"required"` + Servers int `json:"servers"` + Users int `json:"users"` + Hosts int `json:"hosts"` + Clients int `json:"clients"` + Networks int `json:"networks"` } // LicenseLimits.SetDefaults - sets the default values for limits func (l *LicenseLimits) SetDefaults() { l.Clients = 0 l.Servers = 1 - l.Nodes = 0 + l.Hosts = 0 l.Users = 1 + l.Networks = 0 } // ValidateLicenseRequest - used for request to validate license endpoint type ValidateLicenseRequest struct { + LicenseKey string `json:"license_key" binding:"required"` NmServerPubKey string `json:"nm_server_pub_key" binding:"required"` // Netmaker server public key used to send data back to Netmaker for the Netmaker server to decrypt (eg output from validating license) EncryptedPart string `json:"secret" binding:"required"` } diff --git a/ee/util.go b/ee/util.go index a45bc1a4..b63cf158 100644 --- a/ee/util.go +++ b/ee/util.go @@ -30,12 +30,11 @@ func base64decode(input string) []byte { return bytes } - func getCurrentServerLimit() (limits LicenseLimits) { limits.SetDefaults() - nodes, err := logic.GetAllNodes() + hosts, err := logic.GetAllHosts() if err == nil { - limits.Nodes = len(nodes) + limits.Hosts = len(hosts) } clients, err := logic.GetAllExtClients() if err == nil { @@ -45,5 +44,9 @@ func getCurrentServerLimit() (limits LicenseLimits) { if err == nil { limits.Users = len(users) } + networks, err := logic.GetNetworks() + if err == nil { + limits.Networks = len(networks) + } return } diff --git a/logic/hosts.go b/logic/hosts.go index 34fa9766..9f6db42a 100644 --- a/logic/hosts.go +++ b/logic/hosts.go @@ -93,7 +93,14 @@ func GetHost(hostid string) (*models.Host, error) { // CreateHost - creates a host if not exist func CreateHost(h *models.Host) error { - _, err := GetHost(h.ID.String()) + hosts, err := GetAllHosts() + if err != nil && !database.IsEmptyRecord(err) { + return err + } + if len(hosts) >= Hosts_Limit { + return errors.New("free tier limits exceeded on hosts") + } + _, err = GetHost(h.ID.String()) if (err != nil && !database.IsEmptyRecord(err)) || (err == nil) { return ErrHostExists } diff --git a/logic/serverconf.go b/logic/serverconf.go index 01c39346..4d73980d 100644 --- a/logic/serverconf.go +++ b/logic/serverconf.go @@ -4,17 +4,18 @@ import ( "encoding/json" "github.com/gravitl/netmaker/database" + "github.com/gravitl/netmaker/servercfg" ) var ( - // Node_Limit - dummy var for community - Node_Limit = 1000000000 // Networks_Limit - dummy var for community Networks_Limit = 1000000000 // Users_Limit - dummy var for community Users_Limit = 1000000000 // Clients_Limit - dummy var for community Clients_Limit = 1000000000 + // Hosts_Limit - dummy var for community + Hosts_Limit = 1000000000 // Free_Tier - specifies if free tier Free_Tier = false ) @@ -85,3 +86,11 @@ func StoreJWTSecret(privateKey string) error { } return database.Insert("nm-jwt-secret", string(data), database.SERVERCONF_TABLE_NAME) } + +func SetFreeTierLimits() { + Free_Tier = true + Users_Limit = servercfg.GetUserLimit() + Clients_Limit = servercfg.GetClientLimit() + Networks_Limit = servercfg.GetNetworkLimit() + Hosts_Limit = servercfg.GetHostLimit() +} diff --git a/logic/telemetry.go b/logic/telemetry.go index 4b9e8635..e867cbc8 100644 --- a/logic/telemetry.go +++ b/logic/telemetry.go @@ -60,6 +60,7 @@ func sendTelemetry() error { Event: "daily checkin", Properties: posthog.NewProperties(). Set("nodes", d.Nodes). + Set("hosts", d.Hosts). Set("servers", d.Servers). Set("non-server nodes", d.Count.NonServer). Set("extclients", d.ExtClients). @@ -84,6 +85,7 @@ func fetchTelemetryData() (telemetryData, error) { data.ExtClients = getDBLength(database.EXT_CLIENT_TABLE_NAME) data.Users = getDBLength(database.USERS_TABLE_NAME) data.Networks = getDBLength(database.NETWORKS_TABLE_NAME) + data.Hosts = getDBLength(database.HOSTS_TABLE_NAME) data.Version = servercfg.GetVersion() data.Servers = getServerCount() nodes, err := GetAllNodes() @@ -167,6 +169,7 @@ func getDBLength(dbname string) int { // telemetryData - What data to send to posthog type telemetryData struct { Nodes int + Hosts int ExtClients int Users int Count clientCount diff --git a/logic/timer.go b/logic/timer.go index 0ea8d1db..89070f2f 100644 --- a/logic/timer.go +++ b/logic/timer.go @@ -1,10 +1,13 @@ package logic import ( + "context" "fmt" + "sync" "time" "github.com/gravitl/netmaker/logger" + "github.com/gravitl/netmaker/models" ) // == Constants == @@ -12,6 +15,9 @@ import ( // How long to wait before sending telemetry to server (24 hours) const timer_hours_between_runs = 24 +// HookManagerCh - channel to add any new hooks +var HookManagerCh = make(chan models.HookDetails, 2) + // == Public == // TimerCheckpoint - Checks if 24 hours has passed since telemetry was last sent. If so, sends telemetry data to posthog @@ -40,6 +46,36 @@ func AddHook(ifaceToAdd interface{}) { timeHooks = append(timeHooks, ifaceToAdd) } +// StartHookManager - listens on `HookManagerCh` to run any hook +func StartHookManager(ctx context.Context, wg *sync.WaitGroup) { + defer wg.Done() + for { + select { + case <-ctx.Done(): + logger.Log(0, "## Stopping Hook Manager") + return + case newhook := <-HookManagerCh: + wg.Add(1) + go addHookWithInterval(ctx, wg, newhook.Hook, newhook.Interval) + } + } +} + +func addHookWithInterval(ctx context.Context, wg *sync.WaitGroup, hook func() error, interval time.Duration) { + defer wg.Done() + ticker := time.NewTicker(interval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + hook() + } + } + +} + // == private == // timeHooks - functions to run once a day, functions must take no parameters diff --git a/main.go b/main.go index 5110b841..5c5580bd 100644 --- a/main.go +++ b/main.go @@ -42,6 +42,9 @@ func main() { initialize() // initial db and acls setGarbageCollection() setVerbosity() + if servercfg.DeployedByOperator() && !servercfg.Is_EE { + logic.SetFreeTierLimits() + } defer database.CloseDB() ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, os.Interrupt) defer stop() @@ -89,7 +92,6 @@ func initialize() { // Client Mode Prereq Check if err != nil { logger.Log(1, "Timer error occurred: ", err.Error()) } - logic.EnterpriseCheck() var authProvider = auth.InitializeAuthProvider() @@ -150,6 +152,9 @@ func startControllers(wg *sync.WaitGroup, ctx context.Context) { // starts the stun server wg.Add(1) go stunserver.Start(wg, ctx) + + wg.Add(1) + go logic.StartHookManager(ctx, wg) } // Should we be using a context vice a waitgroup???????????? diff --git a/models/structs.go b/models/structs.go index 2343cf6c..bff547c3 100644 --- a/models/structs.go +++ b/models/structs.go @@ -2,6 +2,7 @@ package models import ( "strings" + "time" jwt "github.com/golang-jwt/jwt/v4" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" @@ -274,3 +275,18 @@ type StunServer struct { Domain string `json:"domain" yaml:"domain"` Port int `json:"port" yaml:"port"` } + +// HookDetails - struct to hold hook info +type HookDetails struct { + Hook func() error + Interval time.Duration +} + +// LicenseLimits - struct license limits +type LicenseLimits struct { + Servers int `json:"servers"` + Users int `json:"users"` + Hosts int `json:"hosts"` + Clients int `json:"clients"` + Networks int `json:"networks"` +} diff --git a/mq/emqx.go b/mq/emqx.go index cd6eb2d6..27776fe3 100644 --- a/mq/emqx.go +++ b/mq/emqx.go @@ -6,11 +6,14 @@ import ( "fmt" "io" "net/http" + "strings" "sync" "github.com/gravitl/netmaker/servercfg" ) +const already_exists = "ALREADY_EXISTS" + type ( emqxUser struct { UserID string `json:"user_id"` @@ -99,7 +102,9 @@ func CreateEmqxUser(username, password string, admin bool) error { if err != nil { return err } - return fmt.Errorf("error creating EMQX user %v", string(msg)) + if !strings.Contains(string(msg), already_exists) { + return fmt.Errorf("error creating EMQX user %v", string(msg)) + } } return nil } diff --git a/servercfg/serverconf.go b/servercfg/serverconf.go index ec8b6ef0..185fd870 100644 --- a/servercfg/serverconf.go +++ b/servercfg/serverconf.go @@ -10,6 +10,7 @@ import ( "time" "github.com/gravitl/netmaker/config" + "github.com/gravitl/netmaker/models" ) @@ -741,6 +742,58 @@ func IsProxyEnabled() bool { return enabled } +// GetNetworkLimit - fetches free tier limits on users +func GetUserLimit() int { + var userslimit int + if os.Getenv("USERS_LIMIT") != "" { + userslimit, _ = strconv.Atoi(os.Getenv("USERS_LIMIT")) + } else { + userslimit = config.Config.Server.UsersLimit + } + return userslimit +} + +// GetNetworkLimit - fetches free tier limits on networks +func GetNetworkLimit() int { + var networkslimit int + if os.Getenv("NETWORKS_LIMIT") != "" { + networkslimit, _ = strconv.Atoi(os.Getenv("NETWORKS_LIMIT")) + } else { + networkslimit = config.Config.Server.NetworksLimit + } + return networkslimit +} + +// GetClientLimit - fetches free tier limits on ext. clients +func GetClientLimit() int { + var clientsLimit int + if os.Getenv("CLIENTS_LIMIT") != "" { + clientsLimit, _ = strconv.Atoi(os.Getenv("CLIENTS_LIMIT")) + } else { + clientsLimit = config.Config.Server.ClientsLimit + } + return clientsLimit +} + +// GetHostLimit - fetches free tier limits on hosts +func GetHostLimit() int { + var hostsLimit int + if os.Getenv("HOSTS_LIMIT") != "" { + hostsLimit, _ = strconv.Atoi(os.Getenv("HOSTS_LIMIT")) + } else { + hostsLimit = config.Config.Server.HostsLimit + } + return hostsLimit +} + +// DeployedByOperator - returns true if the instance is deployed by netmaker operator +func DeployedByOperator() bool { + if os.Getenv("DEPLOYED_BY_OPERATOR") != "" { + return os.Getenv("DEPLOYED_BY_OPERATOR") == "true" + } + return config.Config.Server.DeployedByOperator +} + // GetDefaultProxyMode - default proxy mode for a server func GetDefaultProxyMode() config.ProxyMode { var ( From 38454ece79a46c95da418e139447de5c17b73144 Mon Sep 17 00:00:00 2001 From: Abhishek K <32607604+abhishek9686@users.noreply.github.com> Date: Wed, 28 Jun 2023 22:08:49 +0530 Subject: [PATCH 16/16] IOT-66: Single Peer update for IOT client (#2424) * send peer update to IOT client only when it is relayed * move node check * send relay del update for iot client * fix relay delete logic for iot * set relay node to true for iot peer update * add node addrs to peer update * revert tag --- ee/ee_controllers/relay.go | 10 +++++++ logic/peers.go | 55 ++++++++++++++++++++++++++++++++++++++ models/mqtt.go | 1 + models/node.go | 8 ++++++ mq/publishers.go | 24 +++++++++-------- 5 files changed, 87 insertions(+), 11 deletions(-) diff --git a/ee/ee_controllers/relay.go b/ee/ee_controllers/relay.go index 0818db7f..deffc08b 100644 --- a/ee/ee_controllers/relay.go +++ b/ee/ee_controllers/relay.go @@ -1,6 +1,7 @@ package ee_controllers import ( + "context" "encoding/json" "fmt" "net/http" @@ -87,6 +88,15 @@ func deleteRelay(w http.ResponseWriter, r *http.Request) { logger.Log(1, "relayed node update ", relayedNode.ID.String(), "on network", relayedNode.Network, ": ", err.Error()) } + h, err := logic.GetHost(relayedNode.HostID.String()) + if err == nil { + if h.OS == models.OS_Types.IoT { + node.IsRelay = true // for iot update to recognise that it has to delete relay peer + if err = mq.PublishSingleHostPeerUpdate(context.Background(), h, &node, nil); err != nil { + logger.Log(1, "failed to publish peer update to host", h.ID.String(), ": ", err.Error()) + } + } + } } mq.PublishPeerUpdate() }() diff --git a/logic/peers.go b/logic/peers.go index 926d8231..8697a38b 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -135,6 +135,61 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE { continue } + if host.OS == models.OS_Types.IoT { + hostPeerUpdate.NodeAddrs = append(hostPeerUpdate.NodeAddrs, node.PrimaryAddressIPNet()) + if node.IsRelayed { + relayNode, err := GetNodeByID(node.RelayedBy) + if err != nil { + continue + } + relayHost, err := GetHost(relayNode.HostID.String()) + if err != nil { + continue + } + relayPeer := wgtypes.PeerConfig{ + PublicKey: relayHost.PublicKey, + PersistentKeepaliveInterval: &relayNode.PersistentKeepalive, + ReplaceAllowedIPs: true, + AllowedIPs: GetAllowedIPs(&node, &relayNode, nil), + } + uselocal := false + if host.EndpointIP.String() == relayHost.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() == relayNode.LocalAddress.String() { + uselocal = false + } + } + relayPeer.Endpoint = &net.UDPAddr{ + IP: relayHost.EndpointIP, + Port: getPeerWgListenPort(relayHost), + } + + if uselocal { + relayPeer.Endpoint.IP = relayNode.LocalAddress.IP + relayPeer.Endpoint.Port = relayHost.ListenPort + } + + hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, relayPeer) + } else if deletedNode != nil && deletedNode.IsRelay { + relayHost, err := GetHost(deletedNode.HostID.String()) + if err != nil { + continue + } + relayPeer := wgtypes.PeerConfig{ + PublicKey: relayHost.PublicKey, + Remove: true, + } + hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, relayPeer) + } + continue + } + currentPeers := GetNetworkNodesMemory(allNodes, node.Network) var nodePeerMap map[string]models.PeerRouteInfo if node.IsIngressGateway || node.IsEgressGateway { diff --git a/models/mqtt.go b/models/mqtt.go index 30e1c7b3..3b4c6904 100644 --- a/models/mqtt.go +++ b/models/mqtt.go @@ -9,6 +9,7 @@ import ( // HostPeerUpdate - struct for host peer updates type HostPeerUpdate struct { Host Host `json:"host" bson:"host" yaml:"host"` + NodeAddrs []net.IPNet `json:"nodes_addrs" yaml:"nodes_addrs"` Server string `json:"server" bson:"server" yaml:"server"` ServerVersion string `json:"serverversion" bson:"serverversion" yaml:"serverversion"` ServerAddrs []ServerAddr `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"` diff --git a/models/node.go b/models/node.go index 7b9c32ed..2153627f 100644 --- a/models/node.go +++ b/models/node.go @@ -181,6 +181,14 @@ func isLess(ipA string, ipB string) bool { return bytes.Compare(ipNetA, ipNetB) < 0 } +// Node.PrimaryAddress - return ipv4 address if present, else return ipv6 +func (node *Node) PrimaryAddressIPNet() net.IPNet { + if node.Address.IP != nil { + return node.Address + } + return node.Address6 +} + // Node.PrimaryAddress - return ipv4 address if present, else return ipv6 func (node *Node) PrimaryAddress() string { if node.Address.IP != nil { diff --git a/mq/publishers.go b/mq/publishers.go index ec5eb059..80e7c76f 100644 --- a/mq/publishers.go +++ b/mq/publishers.go @@ -88,18 +88,20 @@ func PublishSingleHostPeerUpdate(ctx context.Context, host *models.Host, deleted if len(peerUpdate.Peers) == 0 { // no peers to send return nil } - proxyUpdate, err := logic.GetProxyUpdateForHost(ctx, host) - if err != nil { - return err - } - proxyUpdate.Server = servercfg.GetServer() - if host.ProxyEnabled { - proxyUpdate.Action = models.ProxyUpdate - } else { - proxyUpdate.Action = models.NoProxy - } + if host.OS != models.OS_Types.IoT { + proxyUpdate, err := logic.GetProxyUpdateForHost(ctx, host) + if err != nil { + return err + } + proxyUpdate.Server = servercfg.GetServer() + if host.ProxyEnabled { + proxyUpdate.Action = models.ProxyUpdate + } else { + proxyUpdate.Action = models.NoProxy + } - peerUpdate.ProxyUpdate = proxyUpdate + peerUpdate.ProxyUpdate = proxyUpdate + } data, err := json.Marshal(&peerUpdate) if err != nil {