refactor join

also removes server node
suspect there may be issues with proxy
and UI interactions with nodes
This commit is contained in:
Matthew R. Kasun
2022-12-20 15:29:09 -05:00
parent c086e9499d
commit 38cd4d7700
100 changed files with 1027 additions and 8833 deletions

View File

@@ -29,10 +29,10 @@ func dnsHandlers(r *mux.Router) {
// //
// Gets node DNS entries associated with a network. // Gets node DNS entries associated with a network.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
func getNodeDNS(w http.ResponseWriter, r *http.Request) { func getNodeDNS(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
@@ -55,14 +55,13 @@ func getNodeDNS(w http.ResponseWriter, r *http.Request) {
// //
// Gets all DNS entries. // Gets all DNS entries.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
//
// Responses:
// 200: dnsResponse
// //
// Responses:
// 200: dnsResponse
func getAllDNS(w http.ResponseWriter, r *http.Request) { func getAllDNS(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
dns, err := logic.GetAllDNS() dns, err := logic.GetAllDNS()
@@ -79,14 +78,13 @@ func getAllDNS(w http.ResponseWriter, r *http.Request) {
// //
// Gets custom DNS entries associated with a network. // Gets custom DNS entries associated with a network.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
//
// Responses:
// 200: dnsResponse
// //
// Responses:
// 200: dnsResponse
func getCustomDNS(w http.ResponseWriter, r *http.Request) { func getCustomDNS(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
@@ -109,14 +107,13 @@ func getCustomDNS(w http.ResponseWriter, r *http.Request) {
// //
// Gets all DNS entries associated with the network. // Gets all DNS entries associated with the network.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
//
// Responses:
// 200: dnsResponse
// //
// Responses:
// 200: dnsResponse
func getDNS(w http.ResponseWriter, r *http.Request) { func getDNS(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
@@ -139,14 +136,13 @@ func getDNS(w http.ResponseWriter, r *http.Request) {
// //
// Create a DNS entry. // Create a DNS entry.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
//
// Responses:
// 200: dnsResponse
// //
// Responses:
// 200: dnsResponse
func createDNS(w http.ResponseWriter, r *http.Request) { func createDNS(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
@@ -180,16 +176,8 @@ func createDNS(w http.ResponseWriter, r *http.Request) {
} }
logger.Log(1, "new DNS record added:", entry.Name) logger.Log(1, "new DNS record added:", entry.Name)
if servercfg.IsMessageQueueBackend() { if servercfg.IsMessageQueueBackend() {
serverNode, err := logic.GetNetworkServerLocal(entry.Network) if err = mq.PublishPeerUpdate(entry.Network, false); err != nil {
if err != nil { logger.Log(0, "failed to publish peer update after ACL update on", entry.Network)
logger.Log(1, "failed to find server node after DNS update on", entry.Network)
} else {
if err = logic.ServerUpdate(&serverNode, false); err != nil {
logger.Log(1, "failed to update server node after DNS update on", entry.Network)
}
if err = mq.PublishPeerUpdate(&serverNode, false); err != nil {
logger.Log(0, "failed to publish peer update after ACL update on", entry.Network)
}
} }
} }
logger.Log(2, r.Header.Get("user"), logger.Log(2, r.Header.Get("user"),
@@ -202,14 +190,14 @@ func createDNS(w http.ResponseWriter, r *http.Request) {
// //
// Delete a DNS entry. // Delete a DNS entry.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: stringJSONResponse // 200: stringJSONResponse
// *: stringJSONResponse // *: stringJSONResponse
func deleteDNS(w http.ResponseWriter, r *http.Request) { func deleteDNS(w http.ResponseWriter, r *http.Request) {
// Set header // Set header
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
@@ -270,14 +258,14 @@ func GetDNSEntry(domain string, network string) (models.DNSEntry, error) {
// //
// Push DNS entries to nameserver. // Push DNS entries to nameserver.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: dnsStringJSONResponse // 200: dnsStringJSONResponse
// *: dnsStringJSONResponse // *: dnsStringJSONResponse
func pushDNS(w http.ResponseWriter, r *http.Request) { func pushDNS(w http.ResponseWriter, r *http.Request) {
// Set header // Set header
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")

View File

@@ -29,7 +29,6 @@ import (
serverconfigpkg "github.com/gravitl/netmaker/config" serverconfigpkg "github.com/gravitl/netmaker/config"
"github.com/gravitl/netmaker/logic/acls" "github.com/gravitl/netmaker/logic/acls"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/config"
) )
var _ = useUnused() // "use" the function to prevent "unused function" errors var _ = useUnused() // "use" the function to prevent "unused function" errors
@@ -303,18 +302,18 @@ type nodeLastModifiedResponse struct {
} }
// swagger:parameters register // swagger:parameters register
type registerRequestBodyParam struct { //type registerRequestBodyParam struct {
// Register Request // // Register Request
// in: body // // in: body
RegisterRequest config.RegisterRequest `json:"register_request"` // RegisterRequest config.RegisterRequest `json:"register_request"`
} //}
//
// swagger:response registerResponse //// swagger:response registerResponse
type registerResponse struct { //type registerResponse struct {
// Register Response // // Register Response
// in: body // // in: body
RegisterResponse config.RegisterResponse `json:"register_response"` // RegisterResponse config.RegisterResponse `json:"register_response"`
} //}
// swagger:response boolResponse // swagger:response boolResponse
type boolResponse struct { type boolResponse struct {
@@ -388,8 +387,8 @@ func useUnused() bool {
_ = serverConfigResponse{} _ = serverConfigResponse{}
_ = nodeGetResponse{} _ = nodeGetResponse{}
_ = nodeLastModifiedResponse{} _ = nodeLastModifiedResponse{}
_ = registerRequestBodyParam{} // _ = registerRequestBodyParam{}
_ = registerResponse{} // _ = registerResponse{}
_ = boolResponse{} _ = boolResponse{}
_ = userBodyParam{} _ = userBodyParam{}
_ = userBodyResponse{} _ = userBodyResponse{}

View File

@@ -35,7 +35,7 @@ func checkIngressExists(nodeID string) bool {
if err != nil { if err != nil {
return false return false
} }
return node.IsIngressGateway == "yes" return node.IsIngressGateway
} }
// swagger:route GET /api/extclients/{network} ext_client getNetworkExtClients // swagger:route GET /api/extclients/{network} ext_client getNetworkExtClients
@@ -184,6 +184,13 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return return
} }
host, err := logic.GetHost(gwnode.HostID.String())
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to get host for ingress gateway node [%s] info: %v", client.IngressGatewayID, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
network, err := logic.GetParentNetwork(client.Network) network, err := logic.GetParentNetwork(client.Network)
if err != nil { if err != nil {
@@ -207,7 +214,7 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
if network.DefaultKeepalive != 0 { if network.DefaultKeepalive != 0 {
keepalive = "PersistentKeepalive = " + strconv.Itoa(int(network.DefaultKeepalive)) keepalive = "PersistentKeepalive = " + strconv.Itoa(int(network.DefaultKeepalive))
} }
gwendpoint := gwnode.Endpoint + ":" + strconv.Itoa(int(gwnode.ListenPort)) gwendpoint := gwnode.EndpointIP.String() + ":" + strconv.Itoa(host.ListenPort)
newAllowedIPs := network.AddressRange newAllowedIPs := network.AddressRange
if newAllowedIPs != "" && network.AddressRange6 != "" { if newAllowedIPs != "" && network.AddressRange6 != "" {
newAllowedIPs += "," newAllowedIPs += ","
@@ -226,8 +233,8 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
} }
defaultMTU := 1420 defaultMTU := 1420
if gwnode.MTU != 0 { if host.MTU != 0 {
defaultMTU = int(gwnode.MTU) defaultMTU = host.MTU
} }
config := fmt.Sprintf(`[Interface] config := fmt.Sprintf(`[Interface]
Address = %s Address = %s
@@ -245,7 +252,7 @@ Endpoint = %s
client.PrivateKey, client.PrivateKey,
defaultMTU, defaultMTU,
defaultDNS, defaultDNS,
gwnode.PublicKey, host.PublicKey,
newAllowedIPs, newAllowedIPs,
gwendpoint, gwendpoint,
keepalive) keepalive)
@@ -327,11 +334,16 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return return
} }
listenPort := node.LocalListenPort host, err := logic.GetHost(node.HostID.String())
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to get ingress gateway host for node [%s] info: %v", nodeid, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
listenPort := host.LocalListenPort
if node.Proxy { if node.Proxy {
listenPort = node.ProxyListenPort listenPort = host.ProxyListenPort
} }
extclient.IngressGatewayEndpoint = node.Endpoint + ":" + strconv.FormatInt(int64(listenPort), 10) extclient.IngressGatewayEndpoint = node.EndpointIP.String() + ":" + strconv.FormatInt(int64(listenPort), 10)
extclient.Enabled = true extclient.Enabled = true
parentNetwork, err := logic.GetNetwork(networkName) parentNetwork, err := logic.GetNetwork(networkName)
@@ -462,7 +474,7 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
if changedEnabled { // need to send a peer update to the ingress node as enablement of one of it's clients has changed if changedEnabled { // need to send a peer update to the ingress node as enablement of one of it's clients has changed
if ingressNode, err := logic.GetNodeByID(newclient.IngressGatewayID); err == nil { if ingressNode, err := logic.GetNodeByID(newclient.IngressGatewayID); err == nil {
if err = mq.PublishExtPeerUpdate(&ingressNode); err != nil { if err = mq.PublishExtPeerUpdate(&ingressNode); err != nil {
logger.Log(1, "error setting ext peers on", ingressNode.ID, ":", err.Error()) logger.Log(1, "error setting ext peers on", ingressNode.ID.String(), ":", err.Error())
} }
} }
} }
@@ -534,7 +546,7 @@ func deleteExtClient(w http.ResponseWriter, r *http.Request) {
err = mq.PublishExtPeerUpdate(&ingressnode) err = mq.PublishExtPeerUpdate(&ingressnode)
if err != nil { if err != nil {
logger.Log(1, "error setting ext peers on "+ingressnode.ID+": "+err.Error()) logger.Log(1, "error setting ext peers on "+ingressnode.ID.String()+": "+err.Error())
} }
logger.Log(0, r.Header.Get("user"), logger.Log(0, r.Header.Get("user"),

View File

@@ -36,13 +36,13 @@ func networkHandlers(r *mux.Router) {
// //
// Lists all networks. // Lists all networks.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: getNetworksSliceResponse // 200: getNetworksSliceResponse
func getNetworks(w http.ResponseWriter, r *http.Request) { func getNetworks(w http.ResponseWriter, r *http.Request) {
headerNetworks := r.Header.Get("networks") headerNetworks := r.Header.Get("networks")
@@ -87,13 +87,13 @@ func getNetworks(w http.ResponseWriter, r *http.Request) {
// //
// Get a network. // Get a network.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: networkBodyResponse // 200: networkBodyResponse
func getNetwork(w http.ResponseWriter, r *http.Request) { func getNetwork(w http.ResponseWriter, r *http.Request) {
// set header. // set header.
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
@@ -118,13 +118,13 @@ func getNetwork(w http.ResponseWriter, r *http.Request) {
// //
// Update keys for a network. // Update keys for a network.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: networkBodyResponse // 200: networkBodyResponse
func keyUpdate(w http.ResponseWriter, r *http.Request) { func keyUpdate(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
@@ -145,11 +145,9 @@ func keyUpdate(w http.ResponseWriter, r *http.Request) {
return return
} }
for _, node := range nodes { for _, node := range nodes {
logger.Log(2, "updating node ", node.Name, " for a key update") logger.Log(2, "updating node ", node.ID.String(), " for a key update")
if node.IsServer != "yes" { if err = mq.NodeUpdate(&node); err != nil {
if err = mq.NodeUpdate(&node); err != nil { logger.Log(1, "failed to send update to node during a network wide key update", node.ID.String(), err.Error())
logger.Log(1, "failed to send update to node during a network wide key update", node.Name, node.ID, err.Error())
}
} }
} }
} }
@@ -158,13 +156,13 @@ func keyUpdate(w http.ResponseWriter, r *http.Request) {
// //
// Update a network. // Update a network.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: networkBodyResponse // 200: networkBodyResponse
func updateNetwork(w http.ResponseWriter, r *http.Request) { func updateNetwork(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
@@ -248,16 +246,6 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
return return
} }
} }
if holepunchupdate {
err = logic.UpdateNetworkHolePunching(network.NetID, newNetwork.DefaultUDPHolePunch)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to update network [%s] hole punching: %v",
network.NetID, err.Error()))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
}
if rangeupdate4 || rangeupdate6 || localrangeupdate || holepunchupdate { if rangeupdate4 || rangeupdate6 || localrangeupdate || holepunchupdate {
nodes, err := logic.GetNetworkNodes(network.NetID) nodes, err := logic.GetNetworkNodes(network.NetID)
if err != nil { if err != nil {
@@ -281,13 +269,13 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
// //
// Update a network ACL (Access Control List). // Update a network ACL (Access Control List).
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: aclContainerResponse // 200: aclContainerResponse
func updateNetworkACL(w http.ResponseWriter, r *http.Request) { func updateNetworkACL(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
@@ -318,19 +306,10 @@ func updateNetworkACL(w http.ResponseWriter, r *http.Request) {
// send peer updates // send peer updates
if servercfg.IsMessageQueueBackend() { if servercfg.IsMessageQueueBackend() {
serverNode, err := logic.GetNetworkServerLocal(netname) if err = mq.PublishPeerUpdate(netname, false); err != nil {
if err != nil { logger.Log(0, "failed to publish peer update after ACL update on", netname)
logger.Log(1, "failed to find server node after ACL update on", netname)
} else {
if err = logic.ServerUpdate(&serverNode, false); err != nil {
logger.Log(1, "failed to update server node after ACL update on", netname)
}
if err = mq.PublishPeerUpdate(&serverNode, false); err != nil {
logger.Log(0, "failed to publish peer update after ACL update on", netname)
}
} }
} }
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(newNetACL) json.NewEncoder(w).Encode(newNetACL)
} }
@@ -339,13 +318,13 @@ func updateNetworkACL(w http.ResponseWriter, r *http.Request) {
// //
// Get a network ACL (Access Control List). // Get a network ACL (Access Control List).
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: aclContainerResponse // 200: aclContainerResponse
func getNetworkACL(w http.ResponseWriter, r *http.Request) { func getNetworkACL(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
@@ -367,13 +346,13 @@ func getNetworkACL(w http.ResponseWriter, r *http.Request) {
// //
// Delete a network. Will not delete if there are any nodes that belong to the network. // Delete a network. Will not delete if there are any nodes that belong to the network.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: stringJSONResponse // 200: stringJSONResponse
func deleteNetwork(w http.ResponseWriter, r *http.Request) { func deleteNetwork(w http.ResponseWriter, r *http.Request) {
// Set header // Set header
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
@@ -414,13 +393,13 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
// //
// Create a network. // Create a network.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: networkBodyResponse // 200: networkBodyResponse
func createNetwork(w http.ResponseWriter, r *http.Request) { func createNetwork(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
@@ -468,20 +447,6 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
event.Commands, err.Error())) event.Commands, err.Error()))
} }
if servercfg.IsClientMode() != "off" {
_, err := logic.ServerJoin(&network)
if err != nil {
logic.DeleteNetwork(network.NetID)
if err == nil {
err = errors.New("Failed to add server to network " + network.NetID)
}
logger.Log(0, r.Header.Get("user"), "failed to create network: ",
err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
}
logger.Log(1, r.Header.Get("user"), "created network", network.NetID) logger.Log(1, r.Header.Get("user"), "created network", network.NetID)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(network) json.NewEncoder(w).Encode(network)
@@ -491,13 +456,13 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
// //
// Create a network access key. // Create a network access key.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: accessKeyBodyResponse // 200: accessKeyBodyResponse
// //
// BEGIN KEY MANAGEMENT SECTION // BEGIN KEY MANAGEMENT SECTION
func createAccessKey(w http.ResponseWriter, r *http.Request) { func createAccessKey(w http.ResponseWriter, r *http.Request) {
@@ -545,13 +510,13 @@ func createAccessKey(w http.ResponseWriter, r *http.Request) {
// //
// Get network access keys for a network. // Get network access keys for a network.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: accessKeySliceBodyResponse // 200: accessKeySliceBodyResponse
func getAccessKeys(w http.ResponseWriter, r *http.Request) { func getAccessKeys(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
@@ -575,14 +540,14 @@ func getAccessKeys(w http.ResponseWriter, r *http.Request) {
// //
// Delete a network access key. // Delete a network access key.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: // 200:
// *: stringJSONResponse // *: stringJSONResponse
// //
// delete key. Has to do a little funky logic since it's not a collection item // delete key. Has to do a little funky logic since it's not a collection item
func deleteAccessKey(w http.ResponseWriter, r *http.Request) { func deleteAccessKey(w http.ResponseWriter, r *http.Request) {

View File

@@ -33,7 +33,6 @@ func nodeHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods("DELETE") r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods("DELETE")
r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, http.HandlerFunc(createIngressGateway))).Methods("POST") r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, http.HandlerFunc(createIngressGateway))).Methods("POST")
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods("DELETE") r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods("DELETE")
r.HandleFunc("/api/nodes/{network}/{nodeid}/approve", authorize(false, true, "user", http.HandlerFunc(uncordonNode))).Methods("POST")
r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods("POST") r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods("POST")
r.HandleFunc("/api/nodes/{network}", nodeauth(checkFreeTierLimits(node_l, http.HandlerFunc(createNode)))).Methods("POST") r.HandleFunc("/api/nodes/{network}", nodeauth(checkFreeTierLimits(node_l, http.HandlerFunc(createNode)))).Methods("POST")
r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST") r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST")
@@ -53,7 +52,7 @@ func nodeHandlers(r *mux.Router) {
func authenticate(response http.ResponseWriter, request *http.Request) { func authenticate(response http.ResponseWriter, request *http.Request) {
var authRequest models.AuthParams var authRequest models.AuthParams
var result models.LegacyNode var result models.Node
var errorResponse = models.ErrorResponse{ var errorResponse = models.ErrorResponse{
Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.", Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
} }
@@ -95,8 +94,9 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
return return
} }
} }
host, err := logic.GetHost(result.HostID.String())
err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(authRequest.Password)) err = bcrypt.CompareHashAndPassword([]byte(host.HostPass), []byte(authRequest.Password))
if err != nil { if err != nil {
errorResponse.Code = http.StatusBadRequest errorResponse.Code = http.StatusBadRequest
errorResponse.Message = err.Error() errorResponse.Message = err.Error()
@@ -117,9 +117,9 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
}, },
{ {
Command: mq.CreateClientCmd, Command: mq.CreateClientCmd,
Username: result.HostID, Username: result.HostID.String(),
Password: authRequest.Password, Password: authRequest.Password,
Textname: result.Name, Textname: host.Name,
Roles: []mq.MqDynSecRole{ Roles: []mq.MqDynSecRole{
{ {
Rolename: mq.NodeRole, Rolename: mq.NodeRole,
@@ -354,13 +354,9 @@ func authorize(nodesAllowed, networkCheck bool, authNetwork string, next http.Ha
// Responses: // Responses:
// 200: nodeSliceResponse // 200: nodeSliceResponse
func getNetworkNodes(w http.ResponseWriter, r *http.Request) { func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var nodes []models.LegacyNode
var params = mux.Vars(r) var params = mux.Vars(r)
networkName := params["network"] networkName := params["network"]
nodes, err := logic.GetNetworkNodes(networkName) nodes, err := logic.GetNetworkNodes(networkName)
if err != nil { if err != nil {
logger.Log(0, r.Header.Get("user"), logger.Log(0, r.Header.Get("user"),
@@ -369,12 +365,6 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
return return
} }
for _, node := range nodes {
if len(node.NetworkSettings.AccessKeys) > 0 {
node.NetworkSettings.AccessKeys = []models.AccessKey{} // not to be sent back to client; client already knows how to join the network
}
}
//Returns all the nodes in JSON format //Returns all the nodes in JSON format
logger.Log(2, r.Header.Get("user"), "fetched nodes on network", networkName) logger.Log(2, r.Header.Get("user"), "fetched nodes on network", networkName)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
@@ -403,7 +393,7 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return return
} }
var nodes []models.LegacyNode var nodes []models.Node
if user.IsAdmin || r.Header.Get("ismasterkey") == "yes" { if user.IsAdmin || r.Header.Get("ismasterkey") == "yes" {
nodes, err = logic.GetAllNodes() nodes, err = logic.GetAllNodes()
if err != nil { if err != nil {
@@ -426,8 +416,8 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(nodes) json.NewEncoder(w).Encode(nodes)
} }
func getUsersNodes(user models.User) ([]models.LegacyNode, error) { func getUsersNodes(user models.User) ([]models.Node, error) {
var nodes []models.LegacyNode var nodes []models.Node
var err error var err error
for _, networkName := range user.Networks { for _, networkName := range user.Networks {
tmpNodes, err := logic.GetNetworkNodes(networkName) tmpNodes, err := logic.GetNetworkNodes(networkName)
@@ -473,15 +463,26 @@ func getNode(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return return
} }
host, err := logic.GetHost(node.HostID.String())
if len(node.NetworkSettings.AccessKeys) > 0 { if err != nil {
node.NetworkSettings.AccessKeys = []models.AccessKey{} // not to be sent back to client; client already knows how to join the network logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("error fetching host for node [ %s ] info: %v", nodeid, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
} }
server := servercfg.GetServerInfo()
network, err := logic.GetNetwork(node.Network)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("error fetching network for node [ %s ] info: %v", nodeid, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
legacy := node.Legacy(host, &server, &network)
response := models.NodeGet{ response := models.NodeGet{
Node: node, Node: *legacy,
//Peers: peerUpdate.Peers, Peers: peerUpdate.Peers,
ServerConfig: servercfg.GetServerInfo(), ServerConfig: server,
PeerIDs: peerUpdate.PeerIDs, PeerIDs: peerUpdate.PeerIDs,
} }
if node.Proxy { if node.Proxy {
@@ -494,8 +495,8 @@ func getNode(w http.ResponseWriter, r *http.Request) {
} }
if servercfg.Is_EE && nodeRequest { if servercfg.Is_EE && nodeRequest {
if err = logic.EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil { if err = logic.EnterpriseResetAllPeersFailovers(node.ID.String(), node.Network); err != nil {
logger.Log(1, "failed to reset failover list during node config pull", node.Name, node.Network) logger.Log(1, "failed to reset failover list during node config pull", node.ID.String(), node.Network)
} }
} }
@@ -633,7 +634,6 @@ func createNode(w http.ResponseWriter, r *http.Request) {
return return
} }
} }
peerUpdate, err := logic.GetPeerUpdate(&data.Node) peerUpdate, err := logic.GetPeerUpdate(&data.Node)
if err != nil && !database.IsEmptyRecord(err) { if err != nil && !database.IsEmptyRecord(err) {
logger.Log(0, r.Header.Get("user"), logger.Log(0, r.Header.Get("user"),
@@ -681,61 +681,30 @@ func createNode(w http.ResponseWriter, r *http.Request) {
event.Commands, err.Error())) event.Commands, err.Error()))
} }
response := models.NodeGet{ response := models.NodeJoinResponse{
Node: data.Node, Node: data.Node,
//Peers: peerUpdate.Peers,
ServerConfig: server, ServerConfig: server,
PeerIDs: peerUpdate.PeerIDs, PeerIDs: peerUpdate.PeerIDs,
} }
//host, newNode := node.ConvertToNewNode() //host, newNode := node.ConvertToNewNode()
logic.SaveHost(data.Host)
logic.SaveNode(data.Node) logic.UpsertHost(&data.Host)
//logic.CreateNode()
//logic.SaveNode(data.Node)
logger.Log(1, r.Header.Get("user"), "created new node", data.Host.Name, "on network", networkName) logger.Log(1, r.Header.Get("user"), "created new node", data.Host.Name, "on network", networkName)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response) json.NewEncoder(w).Encode(response)
go func() { go func() {
if err := mq.PublishPeerUpdate(data.Node, true); err != nil { if err := mq.PublishPeerUpdate(data.Node.Network, true); err != nil {
logger.Log(1, "failed a peer update after creation of node", data.Host.Name) logger.Log(1, "failed a peer update after creation of node", data.Host.Name)
} }
}() }()
//runForceServerUpdate(&data.Node, true) //runForceServerUpdate(&data.Node, true)
} }
// swagger:route POST /api/nodes/{network}/{nodeid}/approve nodes uncordonNode
//
// Takes a node out of pending state.
//
// Schemes: https
//
// Security:
// oauth
//
// Responses:
// 200: nodeResponse
//
// Takes node out of pending state
// TODO: May want to use cordon/uncordon terminology instead of "ispending".
func uncordonNode(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
var nodeid = params["nodeid"]
node, err := logic.UncordonNode(nodeid)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to uncordon node [%s]: %v", node.Name, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
logger.Log(1, r.Header.Get("user"), "uncordoned node", node.Name)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode("SUCCESS")
runUpdates(&node, false)
}
// == EGRESS == // == EGRESS ==
// swagger:route POST /api/nodes/{network}/{nodeid}/creategateway nodes createEgressGateway // swagger:route POST /api/nodes/{network}/{nodeid}/creategateway nodes createEgressGateway
@@ -844,7 +813,7 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
if servercfg.Is_EE && failoverReqBody.Failover { if servercfg.Is_EE && failoverReqBody.Failover {
if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil { if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
logger.Log(1, "failed to reset failover list during failover create", node.Name, node.Network) logger.Log(1, "failed to reset failover list during failover create", node.ID.String(), node.Network)
} }
} }
@@ -882,7 +851,7 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
if servercfg.Is_EE && wasFailover { if servercfg.Is_EE && wasFailover {
if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil { if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
logger.Log(1, "failed to reset failover list during failover create", node.Name, node.Network) logger.Log(1, "failed to reset failover list during failover create", node.ID.String(), node.Network)
} }
} }
@@ -909,7 +878,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r) var params = mux.Vars(r)
var node models.LegacyNode var node models.Node
//start here //start here
nodeid := params["nodeid"] nodeid := params["nodeid"]
node, err := logic.GetNodeByID(nodeid) node, err := logic.GetNodeByID(nodeid)
@@ -920,7 +889,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
return return
} }
var newNode models.LegacyNode var newNode models.Node
// we decode our body request params // we decode our body request params
err = json.NewDecoder(r.Body).Decode(&newNode) err = json.NewDecoder(r.Body).Decode(&newNode)
if err != nil { if err != nil {
@@ -929,7 +898,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
return return
} }
relayupdate := false relayupdate := false
if node.IsRelay == "yes" && len(newNode.RelayAddrs) > 0 { if node.IsRelay && len(newNode.RelayAddrs) > 0 {
if len(newNode.RelayAddrs) != len(node.RelayAddrs) { if len(newNode.RelayAddrs) != len(node.RelayAddrs) {
relayupdate = true relayupdate = true
} else { } else {
@@ -941,7 +910,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
} }
} }
relayedUpdate := false relayedUpdate := false
if node.IsRelayed == "yes" && (node.Address != newNode.Address || node.Address6 != newNode.Address6) { if node.IsRelayed && (node.Address.String() != newNode.Address.String() || node.Address6.String() != newNode.Address6.String()) {
relayedUpdate = true relayedUpdate = true
} }
@@ -949,29 +918,11 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
newNode.PostDown = node.PostDown newNode.PostDown = node.PostDown
newNode.PostUp = node.PostUp newNode.PostUp = node.PostUp
} }
ifaceDelta := logic.IfaceDelta(&node, &newNode) ifaceDelta := logic.IfaceDelta(&node, &newNode)
// for a hub change also need to update the existing hub
if newNode.IsHub == "yes" && node.IsHub != "yes" {
nodeToUpdate, err := logic.UnsetHub(newNode.Network)
if err != nil {
logger.Log(2, "failed to unset hubs", err.Error())
}
if err := mq.NodeUpdate(nodeToUpdate); err != nil {
logger.Log(2, "failed to update hub node", nodeToUpdate.Name, err.Error())
}
if nodeToUpdate.IsServer == "yes" {
// set ifacdelta true to force server to update peeers
if err := logic.ServerUpdate(nodeToUpdate, true); err != nil {
logger.Log(2, "failed to update server node on hub change", err.Error())
}
}
}
if ifaceDelta && servercfg.Is_EE { if ifaceDelta && servercfg.Is_EE {
if err = logic.EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil { if err = logic.EnterpriseResetAllPeersFailovers(node.ID.String(), node.Network); err != nil {
logger.Log(0, "failed to reset failover lists during node update for node", node.Name, node.Network) logger.Log(0, "failed to reset failover lists during node update for node", node.ID.String(), node.Network)
} }
} }
@@ -996,7 +947,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
if servercfg.IsDNSMode() { if servercfg.IsDNSMode() {
logic.SetDNS() logic.SetDNS()
} }
logger.Log(1, r.Header.Get("user"), "updated node", node.ID, "on network", node.Network) logger.Log(1, r.Header.Get("user"), "updated node", node.ID.String(), "on network", node.Network)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(newNode) json.NewEncoder(w).Encode(newNode)
@@ -1028,13 +979,6 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return return
} }
if isServer(&node) {
err := fmt.Errorf("cannot delete server node")
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to delete node [ %s ]: %v", nodeid, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
if r.Header.Get("ismaster") != "yes" { if r.Header.Get("ismaster") != "yes" {
username := r.Header.Get("user") username := r.Header.Get("user")
if username != "" && !doesUserOwnNode(username, params["network"], nodeid) { if username != "" && !doesUserOwnNode(username, params["network"], nodeid) {
@@ -1069,7 +1013,7 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
Commands: []mq.MqDynSecCmd{ Commands: []mq.MqDynSecCmd{
{ {
Command: mq.DeleteClientCmd, Command: mq.DeleteClientCmd,
Username: node.HostID, Username: node.HostID.String(),
}, },
}, },
} }
@@ -1082,84 +1026,35 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
logic.ReturnSuccessResponse(w, r, nodeid+" deleted.") logic.ReturnSuccessResponse(w, r, nodeid+" deleted.")
logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"]) logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
runUpdates(&node, false) runUpdates(&node, false)
runForceServerUpdate(&node, false)
} }
func runUpdates(node *models.LegacyNode, ifaceDelta bool) { func runUpdates(node *models.Node, ifaceDelta bool) {
go func() { // don't block http response go func() { // don't block http response
// publish node update if not server // publish node update if not server
if err := mq.NodeUpdate(node); err != nil { if err := mq.NodeUpdate(node); err != nil {
logger.Log(1, "error publishing node update to node", node.Name, node.ID, err.Error()) logger.Log(1, "error publishing node update to node", node.ID.String(), err.Error())
}
if err := runServerUpdate(node, ifaceDelta); err != nil {
logger.Log(1, "error running server update", err.Error())
}
}()
}
// updates local peers for a server on a given node's network
func runServerUpdate(node *models.LegacyNode, ifaceDelta bool) error {
if servercfg.IsClientMode() != "on" || !isServer(node) {
return nil
}
currentServerNode, err := logic.GetNetworkServerLocal(node.Network)
if err != nil {
return err
}
if ifaceDelta && logic.IsLeader(&currentServerNode) {
if err := mq.PublishPeerUpdate(&currentServerNode, false); err != nil {
logger.Log(1, "failed to publish peer update "+err.Error())
}
}
if err := logic.ServerUpdate(&currentServerNode, ifaceDelta); err != nil {
logger.Log(1, "server node:", currentServerNode.ID, "failed update")
return err
}
return nil
}
func runForceServerUpdate(node *models.LegacyNode, publishPeerUpdateToNode bool) {
go func() {
if err := mq.PublishPeerUpdate(node, publishPeerUpdateToNode); err != nil {
logger.Log(1, "failed a peer update after creation of node", node.Name)
}
var currentServerNode, getErr = logic.GetNetworkServerLeader(node.Network)
if getErr == nil {
if err := logic.ServerUpdate(&currentServerNode, false); err != nil {
logger.Log(1, "server node:", currentServerNode.ID, "failed update")
}
} }
}() }()
} }
func isServer(node *models.LegacyNode) bool { func updateRelay(oldnode, newnode *models.Node) {
return node.IsServer == "yes"
}
func updateRelay(oldnode, newnode *models.LegacyNode) {
relay := logic.FindRelay(oldnode) relay := logic.FindRelay(oldnode)
newrelay := relay 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 //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 != newnode.Address { if oldnode.Address.String() != newnode.Address.String() {
for i, ip := range newrelay.RelayAddrs { for i, ip := range newrelay.RelayAddrs {
if ip == oldnode.Address { if ip == oldnode.Address.IP.String() {
newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], relay.RelayAddrs[i+1:]...) newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], relay.RelayAddrs[i+1:]...)
newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address) 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 //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 != newnode.Address6 { if oldnode.Address6.String() != newnode.Address6.String() {
for i, ip := range newrelay.RelayAddrs { for i, ip := range newrelay.RelayAddrs {
if ip == oldnode.Address { if ip == oldnode.Address.IP.String() {
newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], newrelay.RelayAddrs[i+1:]...) newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], newrelay.RelayAddrs[i+1:]...)
newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address6) newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address6.IP.String())
} }
} }
} }

View File

@@ -16,13 +16,13 @@ import (
// //
// Create a relay. // Create a relay.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: nodeResponse // 200: nodeResponse
func createRelay(w http.ResponseWriter, r *http.Request) { func createRelay(w http.ResponseWriter, r *http.Request) {
var relay models.RelayRequest var relay models.RelayRequest
var params = mux.Vars(r) var params = mux.Vars(r)
@@ -48,7 +48,7 @@ func createRelay(w http.ResponseWriter, r *http.Request) {
err = mq.NodeUpdate(&relayedNode) err = mq.NodeUpdate(&relayedNode)
if err != nil { if err != nil {
logger.Log(1, "error sending update to relayed node ", relayedNode.Name, "on network", relay.NetID, ": ", err.Error()) logger.Log(1, "error sending update to relayed node ", relayedNode.ID.String(), "on network", relay.NetID, ": ", err.Error())
} }
} }
@@ -61,13 +61,13 @@ func createRelay(w http.ResponseWriter, r *http.Request) {
// //
// Remove a relay. // Remove a relay.
// //
// Schemes: https // Schemes: https
// //
// Security: // Security:
// oauth // oauth
// //
// Responses: // Responses:
// 200: nodeResponse // 200: nodeResponse
func deleteRelay(w http.ResponseWriter, r *http.Request) { func deleteRelay(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
@@ -83,7 +83,7 @@ func deleteRelay(w http.ResponseWriter, r *http.Request) {
for _, relayedNode := range updatenodes { for _, relayedNode := range updatenodes {
err = mq.NodeUpdate(&relayedNode) err = mq.NodeUpdate(&relayedNode)
if err != nil { if err != nil {
logger.Log(1, "error sending update to relayed node ", relayedNode.Name, "on network", netid, ": ", err.Error()) logger.Log(1, "error sending update to relayed node ", relayedNode.ID.String(), "on network", netid, ": ", err.Error())
} }
} }
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)

View File

@@ -142,7 +142,7 @@ func createTable(tableName string) error {
// IsJSONString - checks if valid json // IsJSONString - checks if valid json
func IsJSONString(value string) bool { func IsJSONString(value string) bool {
var jsonInt interface{} var jsonInt interface{}
var nodeInt models.LegacyNode var nodeInt models.Node
return json.Unmarshal([]byte(value), &jsonInt) == nil || json.Unmarshal([]byte(value), &nodeInt) == nil return json.Unmarshal([]byte(value), &jsonInt) == nil || json.Unmarshal([]byte(value), &nodeInt) == nil
} }

View File

@@ -60,12 +60,12 @@ func getNetworkNodesMetrics(w http.ResponseWriter, r *http.Request) {
for i := range networkNodes { for i := range networkNodes {
id := networkNodes[i].ID id := networkNodes[i].ID
metrics, err := logic.GetMetrics(id) metrics, err := logic.GetMetrics(id.String())
if err != nil { if err != nil {
logger.Log(1, r.Header.Get("user"), "failed to append metrics of node", id, "during network metrics fetch", err.Error()) logger.Log(1, r.Header.Get("user"), "failed to append metrics of node", id.String(), "during network metrics fetch", err.Error())
continue continue
} }
networkMetrics.Nodes[id] = *metrics networkMetrics.Nodes[id.String()] = *metrics
} }
logger.Log(1, r.Header.Get("user"), "fetched metrics for network", network) logger.Log(1, r.Header.Get("user"), "fetched metrics for network", network)
@@ -101,9 +101,9 @@ func getNetworkExtMetrics(w http.ResponseWriter, r *http.Request) {
for i := range ingresses { for i := range ingresses {
id := ingresses[i].ID id := ingresses[i].ID
ingressMetrics, err := logic.GetMetrics(id) ingressMetrics, err := logic.GetMetrics(id.String())
if err != nil { if err != nil {
logger.Log(1, r.Header.Get("user"), "failed to append external client metrics from ingress node", id, err.Error()) logger.Log(1, r.Header.Get("user"), "failed to append external client metrics from ingress node", id.String(), err.Error())
continue continue
} }
if ingressMetrics.Connectivity == nil { if ingressMetrics.Connectivity == nil {
@@ -142,12 +142,12 @@ func getAllMetrics(w http.ResponseWriter, r *http.Request) {
for i := range allNodes { for i := range allNodes {
id := allNodes[i].ID id := allNodes[i].ID
metrics, err := logic.GetMetrics(id) metrics, err := logic.GetMetrics(id.String())
if err != nil { if err != nil {
logger.Log(1, r.Header.Get("user"), "failed to append metrics of node", id, "during all nodes metrics fetch", err.Error()) logger.Log(1, r.Header.Get("user"), "failed to append metrics of node", id.String(), "during all nodes metrics fetch", err.Error())
continue continue
} }
networkMetrics.Nodes[id] = *metrics networkMetrics.Nodes[id.String()] = *metrics
} }
logger.Log(1, r.Header.Get("user"), "fetched metrics for all nodes on server") logger.Log(1, r.Header.Get("user"), "fetched metrics for all nodes on server")

View File

@@ -34,9 +34,9 @@ type NetworkUserDataMap map[NetworkName]NetworkUserData
// NetworkUserData - data struct for network users // NetworkUserData - data struct for network users
type NetworkUserData struct { type NetworkUserData struct {
Nodes []models.LegacyNode `json:"nodes" bson:"nodes" yaml:"nodes"` Nodes []models.Node `json:"nodes" bson:"nodes" yaml:"nodes"`
Clients []models.ExtClient `json:"clients" bson:"clients" yaml:"clients"` Clients []models.ExtClient `json:"clients" bson:"clients" yaml:"clients"`
Vpn []models.LegacyNode `json:"vpns" bson:"vpns" yaml:"vpns"` Vpn []models.Node `json:"vpns" bson:"vpns" yaml:"vpns"`
Networks []models.Network `json:"networks" bson:"networks" yaml:"networks"` Networks []models.Network `json:"networks" bson:"networks" yaml:"networks"`
User promodels.NetworkUser `json:"user" bson:"user" yaml:"user"` User promodels.NetworkUser `json:"user" bson:"user" yaml:"user"`
} }
@@ -80,9 +80,9 @@ func getNetworkUserData(w http.ResponseWriter, r *http.Request) {
netID := networks[i].NetID netID := networks[i].NetID
newData := NetworkUserData{ newData := NetworkUserData{
Nodes: []models.LegacyNode{}, Nodes: []models.Node{},
Clients: []models.ExtClient{}, Clients: []models.ExtClient{},
Vpn: []models.LegacyNode{}, Vpn: []models.Node{},
Networks: []models.Network{}, Networks: []models.Network{},
} }
netUser, err := pro.GetNetworkUser(netID, promodels.NetworkUserID(networkUserName)) netUser, err := pro.GetNetworkUser(netID, promodels.NetworkUserID(networkUserName))
@@ -110,16 +110,16 @@ func getNetworkUserData(w http.ResponseWriter, r *http.Request) {
// if access level is NODE_ACCESS, filter nodes // if access level is NODE_ACCESS, filter nodes
if netUser.AccessLevel == pro.NODE_ACCESS { if netUser.AccessLevel == pro.NODE_ACCESS {
for i := range netNodes { for i := range netNodes {
if logic.StringSliceContains(netUser.Nodes, netNodes[i].ID) { if logic.StringSliceContains(netUser.Nodes, netNodes[i].ID.String()) {
newData.Nodes = append(newData.Nodes, netNodes[i]) newData.Nodes = append(newData.Nodes, netNodes[i])
} }
} }
} else { // net admin so, get all nodes and ext clients on network... } else { // net admin so, get all nodes and ext clients on network...
newData.Nodes = netNodes newData.Nodes = netNodes
for i := range netNodes { for i := range netNodes {
if netNodes[i].IsIngressGateway == "yes" { if netNodes[i].IsIngressGateway {
newData.Vpn = append(newData.Vpn, netNodes[i]) newData.Vpn = append(newData.Vpn, netNodes[i])
if clients, err := logic.GetExtClientsByID(netNodes[i].ID, netID); err == nil { if clients, err := logic.GetExtClientsByID(netNodes[i].ID.String(), netID); err == nil {
newData.Clients = append(newData.Clients, clients...) newData.Clients = append(newData.Clients, clients...)
} }
} }
@@ -134,7 +134,7 @@ func getNetworkUserData(w http.ResponseWriter, r *http.Request) {
} }
} }
for i := range netNodes { for i := range netNodes {
if netNodes[i].IsIngressGateway == "yes" { if netNodes[i].IsIngressGateway {
newData.Vpn = append(newData.Vpn, netNodes[i]) newData.Vpn = append(newData.Vpn, netNodes[i])
} }
} }

View File

@@ -1,13 +1,14 @@
package logic package logic
import ( import (
"github.com/google/uuid"
"github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
) )
// SetFailover - finds a suitable failover candidate and sets it // SetFailover - finds a suitable failover candidate and sets it
func SetFailover(node *models.LegacyNode) error { func SetFailover(node *models.Node) error {
failoverNode := determineFailoverCandidate(node) failoverNode := determineFailoverCandidate(node)
if failoverNode != nil { if failoverNode != nil {
return setFailoverNode(failoverNode, node) return setFailoverNode(failoverNode, node)
@@ -24,11 +25,11 @@ func ResetFailover(network string) error {
for _, node := range nodes { for _, node := range nodes {
err = SetFailover(&node) err = SetFailover(&node)
if err != nil { if err != nil {
logger.Log(2, "error setting failover for node", node.Name, ":", err.Error()) logger.Log(2, "error setting failover for node", node.ID.String(), ":", err.Error())
} }
err = WipeFailover(node.ID) err = WipeFailover(node.ID.String())
if err != nil { if err != nil {
logger.Log(2, "error wiping failover for node", node.Name, ":", err.Error()) logger.Log(2, "error wiping failover for node", node.ID.String(), ":", err.Error())
} }
} }
return nil return nil
@@ -36,29 +37,29 @@ func ResetFailover(network string) error {
// determineFailoverCandidate - returns a list of nodes that // determineFailoverCandidate - returns a list of nodes that
// are suitable for relaying a given node // are suitable for relaying a given node
func determineFailoverCandidate(nodeToBeRelayed *models.LegacyNode) *models.LegacyNode { func determineFailoverCandidate(nodeToBeRelayed *models.Node) *models.Node {
currentNetworkNodes, err := logic.GetNetworkNodes(nodeToBeRelayed.Network) currentNetworkNodes, err := logic.GetNetworkNodes(nodeToBeRelayed.Network)
if err != nil { if err != nil {
return nil return nil
} }
currentMetrics, err := logic.GetMetrics(nodeToBeRelayed.ID) currentMetrics, err := logic.GetMetrics(nodeToBeRelayed.ID.String())
if err != nil || currentMetrics == nil || currentMetrics.Connectivity == nil { if err != nil || currentMetrics == nil || currentMetrics.Connectivity == nil {
return nil return nil
} }
minLatency := int64(9223372036854775807) // max signed int64 value minLatency := int64(9223372036854775807) // max signed int64 value
var fastestCandidate *models.LegacyNode var fastestCandidate *models.Node
for i := range currentNetworkNodes { for i := range currentNetworkNodes {
if currentNetworkNodes[i].ID == nodeToBeRelayed.ID { if currentNetworkNodes[i].ID == nodeToBeRelayed.ID {
continue continue
} }
if currentMetrics.Connectivity[currentNetworkNodes[i].ID].Connected && (currentNetworkNodes[i].Failover == "yes") { if currentMetrics.Connectivity[currentNetworkNodes[i].ID.String()].Connected && (currentNetworkNodes[i].Failover) {
if currentMetrics.Connectivity[currentNetworkNodes[i].ID].Latency < int64(minLatency) { if currentMetrics.Connectivity[currentNetworkNodes[i].ID.String()].Latency < int64(minLatency) {
fastestCandidate = &currentNetworkNodes[i] fastestCandidate = &currentNetworkNodes[i]
minLatency = currentMetrics.Connectivity[currentNetworkNodes[i].ID].Latency minLatency = currentMetrics.Connectivity[currentNetworkNodes[i].ID.String()].Latency
} }
} }
} }
@@ -67,10 +68,10 @@ func determineFailoverCandidate(nodeToBeRelayed *models.LegacyNode) *models.Lega
} }
// setFailoverNode - changes node's failover node // setFailoverNode - changes node's failover node
func setFailoverNode(failoverNode, node *models.LegacyNode) error { func setFailoverNode(failoverNode, node *models.Node) error {
node.FailoverNode = failoverNode.ID node.FailoverNode = failoverNode.ID
nodeToUpdate, err := logic.GetNodeByID(node.ID) nodeToUpdate, err := logic.GetNodeByID(node.ID.String())
if err != nil { if err != nil {
return err return err
} }
@@ -95,25 +96,25 @@ func WipeFailover(nodeid string) error {
// WipeAffectedFailoversOnly - wipes failovers for nodes that have given node (ID) // WipeAffectedFailoversOnly - wipes failovers for nodes that have given node (ID)
// in their respective failover lists // in their respective failover lists
func WipeAffectedFailoversOnly(nodeid, network string) error { func WipeAffectedFailoversOnly(nodeid uuid.UUID, network string) error {
currentNetworkNodes, err := logic.GetNetworkNodes(network) currentNetworkNodes, err := logic.GetNetworkNodes(network)
if err != nil { if err != nil {
return nil return nil
} }
WipeFailover(nodeid) WipeFailover(nodeid.String())
for i := range currentNetworkNodes { for i := range currentNetworkNodes {
currNodeID := currentNetworkNodes[i].ID currNodeID := currentNetworkNodes[i].ID
if currNodeID == nodeid { if currNodeID == nodeid {
continue continue
} }
currMetrics, err := logic.GetMetrics(currNodeID) currMetrics, err := logic.GetMetrics(currNodeID.String())
if err != nil || currMetrics == nil { if err != nil || currMetrics == nil {
continue continue
} }
if currMetrics.FailoverPeers != nil { if currMetrics.FailoverPeers != nil {
if len(currMetrics.FailoverPeers[nodeid]) > 0 { if len(currMetrics.FailoverPeers[nodeid.String()]) > 0 {
WipeFailover(currNodeID) WipeFailover(currNodeID.String())
} }
} }
} }

View File

@@ -45,6 +45,5 @@ func getCurrentServerLimit() (limits LicenseLimits) {
if err == nil { if err == nil {
limits.Users = len(users) limits.Users = len(users)
} }
limits.Servers = logic.GetServerCount()
return return
} }

View File

@@ -75,7 +75,7 @@ func GetNodeDNS(network string) ([]models.DNSEntry, error) {
for _, value := range collection { for _, value := range collection {
var entry models.DNSEntry var entry models.DNSEntry
var node models.LegacyNode var node models.Node
if err = json.Unmarshal([]byte(value), &node); err != nil { if err = json.Unmarshal([]byte(value), &node); err != nil {
continue continue
} }

View File

@@ -11,7 +11,7 @@ import (
) )
// GetExtPeersList - gets the ext peers lists // GetExtPeersList - gets the ext peers lists
func GetExtPeersList(node *models.LegacyNode) ([]models.ExtPeersResponse, error) { func GetExtPeersList(node *models.Node) ([]models.ExtPeersResponse, error) {
var peers []models.ExtPeersResponse var peers []models.ExtPeersResponse
records, err := database.FetchRecords(database.EXT_CLIENT_TABLE_NAME) records, err := database.FetchRecords(database.EXT_CLIENT_TABLE_NAME)
@@ -34,7 +34,7 @@ func GetExtPeersList(node *models.LegacyNode) ([]models.ExtPeersResponse, error)
continue continue
} }
if extClient.Enabled && extClient.Network == node.Network && extClient.IngressGatewayID == node.ID { if extClient.Enabled && extClient.Network == node.Network && extClient.IngressGatewayID == node.ID.String() {
peers = append(peers, peer) peers = append(peers, peer)
} }
} }
@@ -50,14 +50,14 @@ func GetEgressRangesOnNetwork(client *models.ExtClient) ([]string, error) {
return []string{}, err return []string{}, err
} }
for _, nodeData := range nodesData { for _, nodeData := range nodesData {
var currentNode models.LegacyNode var currentNode models.Node
if err = json.Unmarshal([]byte(nodeData), &currentNode); err != nil { if err = json.Unmarshal([]byte(nodeData), &currentNode); err != nil {
continue continue
} }
if currentNode.Network != client.Network { if currentNode.Network != client.Network {
continue continue
} }
if currentNode.IsEgressGateway == "yes" { // add the egress gateway range(s) to the result if currentNode.IsEgressGateway { // add the egress gateway range(s) to the result
if len(currentNode.EgressGatewayRanges) > 0 { if len(currentNode.EgressGatewayRanges) > 0 {
result = append(result, currentNode.EgressGatewayRanges...) result = append(result, currentNode.EgressGatewayRanges...)
} }
@@ -137,13 +137,13 @@ func CreateExtClient(extclient *models.ExtClient) error {
if err != nil { if err != nil {
return err return err
} }
extclient.Address = newAddress extclient.Address = newAddress.String()
extclientInternalAddr, err := UniqueAddress(extclient.Network, true) extclientInternalAddr, err := UniqueAddress(extclient.Network, true)
if err != nil { if err != nil {
return err return err
} }
extclient.InternalIPAddr = extclientInternalAddr extclient.InternalIPAddr = extclientInternalAddr.String()
} }
} }
@@ -153,12 +153,12 @@ func CreateExtClient(extclient *models.ExtClient) error {
if err != nil { if err != nil {
return err return err
} }
extclient.Address6 = addr6 extclient.Address6 = addr6.String()
extclientInternalAddr6, err := UniqueAddress6(extclient.Network, true) extclientInternalAddr6, err := UniqueAddress6(extclient.Network, true)
if err != nil { if err != nil {
return err return err
} }
extclient.InternalIPAddr6 = extclientInternalAddr6 extclient.InternalIPAddr6 = extclientInternalAddr6.String()
} }
} }

View File

@@ -14,46 +14,47 @@ import (
) )
// CreateEgressGateway - creates an egress gateway // CreateEgressGateway - creates an egress gateway
func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.LegacyNode, error) { func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, error) {
node, err := GetNodeByID(gateway.NodeID)
if err != nil {
return models.Node{}, err
}
host, err := GetHost(node.HostID.String())
if err != nil {
return models.Node{}, err
}
for i, cidr := range gateway.Ranges { for i, cidr := range gateway.Ranges {
normalized, err := NormalizeCIDR(cidr) normalized, err := NormalizeCIDR(cidr)
if err != nil { if err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
gateway.Ranges[i] = normalized gateway.Ranges[i] = normalized
} }
node, err := GetNodeByID(gateway.NodeID) if host.OS != "linux" && host.OS != "freebsd" { // add in darwin later
if err != nil { return models.Node{}, errors.New(host.OS + " is unsupported for egress gateways")
return models.LegacyNode{}, err
} }
if node.OS != "linux" && node.OS != "freebsd" { // add in darwin later if host.OS == "linux" && host.FirewallInUse == models.FIREWALL_NONE {
return models.LegacyNode{}, errors.New(node.OS + " is unsupported for egress gateways") return models.Node{}, errors.New("firewall is not supported for egress gateways")
}
if node.OS == "linux" && node.FirewallInUse == models.FIREWALL_NONE {
return models.LegacyNode{}, errors.New("firewall is not supported for egress gateways")
} }
if gateway.NatEnabled == "" { if gateway.NatEnabled == "" {
gateway.NatEnabled = "yes" gateway.NatEnabled = "yes"
} }
err = ValidateEgressGateway(gateway) err = ValidateEgressGateway(gateway)
if err != nil { if err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
node.IsEgressGateway = "yes" node.IsEgressGateway = true
node.EgressGatewayRanges = gateway.Ranges node.EgressGatewayRanges = gateway.Ranges
node.EgressGatewayNatEnabled = gateway.NatEnabled node.EgressGatewayNatEnabled = gateway.NatEnabled
node.EgressGatewayRequest = gateway // store entire request for use when preserving the egress gateway node.EgressGatewayRequest = gateway // store entire request for use when preserving the egress gateway
postUpCmd := "" postUpCmd := ""
postDownCmd := "" postDownCmd := ""
ipv4, ipv6 := getNetworkProtocols(gateway.Ranges) ipv4, ipv6 := getNetworkProtocols(gateway.Ranges)
//no support for ipv6 and ip6tables in netmaker container logger.Log(3, "creating egress gateway firewall in use is '", host.FirewallInUse, "'")
if node.IsServer == "yes" { iface := "netmaker"
ipv6 = false if host.OS == "linux" {
} switch host.FirewallInUse {
logger.Log(3, "creating egress gateway firewall in use is '", node.FirewallInUse, "'")
if node.OS == "linux" {
switch node.FirewallInUse {
case models.FIREWALL_NFTABLES: case models.FIREWALL_NFTABLES:
// nftables only supported on Linux // nftables only supported on Linux
// assumes chains eg FORWARD and postrouting already exist // assumes chains eg FORWARD and postrouting already exist
@@ -62,14 +63,14 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.LegacyNode
// removing the chain with rules in it would remove all rules in that section (not safe // removing the chain with rules in it would remove all rules in that section (not safe
// if there are remaining rules on the host that need to stay). In practice the chain is removed // if there are remaining rules on the host that need to stay). In practice the chain is removed
// when non-empty even though the removal of a non-empty chain should not be possible per nftables wiki. // when non-empty even though the removal of a non-empty chain should not be possible per nftables wiki.
postUpCmd, postDownCmd = firewallNFTCommandsCreateEgress(node.Interface, gateway.Interface, gateway.Ranges, node.EgressGatewayNatEnabled, ipv4, ipv6) postUpCmd, postDownCmd = firewallNFTCommandsCreateEgress(iface, gateway.Interface, gateway.Ranges, node.EgressGatewayNatEnabled, ipv4, ipv6)
default: // iptables assumed default: // iptables assumed
logger.Log(3, "creating egress gateway nftables is not present") logger.Log(3, "creating egress gateway nftables is not present")
postUpCmd, postDownCmd = firewallIPTablesCommandsCreateEgress(node.Interface, gateway.Interface, node.EgressGatewayNatEnabled, ipv4, ipv6) postUpCmd, postDownCmd = firewallIPTablesCommandsCreateEgress(iface, gateway.Interface, node.EgressGatewayNatEnabled, ipv4, ipv6)
} }
} }
if node.OS == "freebsd" { if host.OS == "freebsd" {
// spacing around ; is important for later parsing of postup/postdown in wireguard/common.go // spacing around ; is important for later parsing of postup/postdown in wireguard/common.go
postUpCmd = "kldload ipfw ipfw_nat ; " postUpCmd = "kldload ipfw ipfw_nat ; "
postUpCmd += "ipfw disable one_pass ; " postUpCmd += "ipfw disable one_pass ; "
@@ -108,8 +109,8 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.LegacyNode
if err != nil { if err != nil {
return node, err return node, err
} }
if err = database.Insert(node.ID, string(nodeData), database.NODES_TABLE_NAME); err != nil { if err = database.Insert(node.ID.String(), string(nodeData), database.NODES_TABLE_NAME); err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
return node, nil return node, nil
} }
@@ -130,14 +131,17 @@ func ValidateEgressGateway(gateway models.EgressGatewayRequest) error {
} }
// DeleteEgressGateway - deletes egress from node // DeleteEgressGateway - deletes egress from node
func DeleteEgressGateway(network, nodeid string) (models.LegacyNode, error) { func DeleteEgressGateway(network, nodeid string) (models.Node, error) {
node, err := GetNodeByID(nodeid) node, err := GetNodeByID(nodeid)
if err != nil { if err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
host, err := GetHost(node.HostID.String())
if err != nil {
return models.Node{}, err
node.IsEgressGateway = "no" }
node.IsEgressGateway = false
node.EgressGatewayRanges = []string{} node.EgressGatewayRanges = []string{}
node.EgressGatewayRequest = models.EgressGatewayRequest{} // remove preserved request as the egress gateway is gone node.EgressGatewayRequest = models.EgressGatewayRequest{} // remove preserved request as the egress gateway is gone
// needed in case we don't preserve a gateway (i.e., no ingress to preserve) // needed in case we don't preserve a gateway (i.e., no ingress to preserve)
@@ -147,19 +151,20 @@ func DeleteEgressGateway(network, nodeid string) (models.LegacyNode, error) {
cidrs = append(cidrs, node.IngressGatewayRange) cidrs = append(cidrs, node.IngressGatewayRange)
cidrs = append(cidrs, node.IngressGatewayRange6) cidrs = append(cidrs, node.IngressGatewayRange6)
ipv4, ipv6 := getNetworkProtocols(cidrs) ipv4, ipv6 := getNetworkProtocols(cidrs)
logger.Log(3, "deleting egress gateway firewall in use is '", node.FirewallInUse, "'") logger.Log(3, "deleting egress gateway firewall in use is '", host.FirewallInUse, "'")
if node.IsIngressGateway == "yes" { // check if node is still an ingress gateway before completely deleting postdown/up rules if node.IsIngressGateway { // check if node is still an ingress gateway before completely deleting postdown/up rules
// still have an ingress gateway so preserve it // still have an ingress gateway so preserve it
if node.OS == "linux" { iface := "netmaker"
switch node.FirewallInUse { if host.OS == "linux" {
switch host.FirewallInUse {
case models.FIREWALL_NFTABLES: case models.FIREWALL_NFTABLES:
// nftables only supported on Linux // nftables only supported on Linux
// assumes chains eg FORWARD and postrouting already exist // assumes chains eg FORWARD and postrouting already exist
logger.Log(3, "deleting egress gateway nftables is present") logger.Log(3, "deleting egress gateway nftables is present")
node.PostUp, node.PostDown = firewallNFTCommandsCreateIngress(node.Interface) node.PostUp, node.PostDown = firewallNFTCommandsCreateIngress(iface)
default: default:
logger.Log(3, "deleting egress gateway nftables is not present") logger.Log(3, "deleting egress gateway nftables is not present")
node.PostUp, node.PostDown = firewallIPTablesCommandsCreateIngress(node.Interface, ipv4, ipv6) node.PostUp, node.PostDown = firewallIPTablesCommandsCreateIngress(iface, ipv4, ipv6)
} }
} }
// no need to preserve ingress gateway on FreeBSD as ingress is not supported on that OS // no need to preserve ingress gateway on FreeBSD as ingress is not supported on that OS
@@ -168,53 +173,52 @@ func DeleteEgressGateway(network, nodeid string) (models.LegacyNode, error) {
data, err := json.Marshal(&node) data, err := json.Marshal(&node)
if err != nil { if err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
if err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME); err != nil { if err = database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME); err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
return node, nil return node, nil
} }
// CreateIngressGateway - creates an ingress gateway // CreateIngressGateway - creates an ingress gateway
func CreateIngressGateway(netid string, nodeid string, failover bool) (models.LegacyNode, error) { func CreateIngressGateway(netid string, nodeid string, failover bool) (models.Node, error) {
var postUpCmd, postDownCmd string var postUpCmd, postDownCmd string
node, err := GetNodeByID(nodeid) node, err := GetNodeByID(nodeid)
if node.FirewallInUse == models.FIREWALL_NONE {
return models.LegacyNode{}, errors.New("firewall is not supported for ingress gateways")
}
if err != nil { if err != nil {
return models.LegacyNode{}, err return models.Node{}, err
}
host, err := GetHost(node.HostID.String())
if err != nil {
return models.Node{}, err
}
if host.FirewallInUse == models.FIREWALL_NONE {
return models.Node{}, errors.New("firewall is not supported for ingress gateways")
} }
network, err := GetParentNetwork(netid) network, err := GetParentNetwork(netid)
if err != nil { if err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
node.IsIngressGateway = "yes" node.IsIngressGateway = true
cidrs := []string{} cidrs := []string{}
cidrs = append(cidrs, network.AddressRange) cidrs = append(cidrs, network.AddressRange)
cidrs = append(cidrs, network.AddressRange6) cidrs = append(cidrs, network.AddressRange6)
node.IngressGatewayRange = network.AddressRange node.IngressGatewayRange = network.AddressRange
node.IngressGatewayRange6 = network.AddressRange6 node.IngressGatewayRange6 = network.AddressRange6
ipv4, ipv6 := getNetworkProtocols(cidrs) ipv4, ipv6 := getNetworkProtocols(cidrs)
//no support for ipv6 and ip6tables in netmaker container logger.Log(3, "creating ingress gateway firewall in use is '", host.FirewallInUse, "'")
if node.IsServer == "yes" { iface := "netmaker"
ipv6 = false switch host.FirewallInUse {
}
logger.Log(3, "creating ingress gateway firewall in use is '", node.FirewallInUse, "'")
switch node.FirewallInUse {
case models.FIREWALL_NFTABLES: case models.FIREWALL_NFTABLES:
// nftables only supported on Linux // nftables only supported on Linux
// assumes chains eg FORWARD and postrouting already exist // assumes chains eg FORWARD and postrouting already exist
logger.Log(3, "creating ingress gateway nftables is present") logger.Log(3, "creating ingress gateway nftables is present")
postUpCmd, postDownCmd = firewallNFTCommandsCreateIngress(node.Interface) postUpCmd, postDownCmd = firewallNFTCommandsCreateIngress(iface)
default: default:
logger.Log(3, "creating ingress gateway using nftables is not present") logger.Log(3, "creating ingress gateway using nftables is not present")
postUpCmd, postDownCmd = firewallIPTablesCommandsCreateIngress(node.Interface, ipv4, ipv6) postUpCmd, postDownCmd = firewallIPTablesCommandsCreateIngress(iface, ipv4, ipv6)
} }
if node.PostUp != "" { if node.PostUp != "" {
@@ -230,52 +234,51 @@ func CreateIngressGateway(netid string, nodeid string, failover bool) (models.Le
node.SetLastModified() node.SetLastModified()
node.PostUp = postUpCmd node.PostUp = postUpCmd
node.PostDown = postDownCmd node.PostDown = postDownCmd
node.UDPHolePunch = "no"
if failover && servercfg.Is_EE { if failover && servercfg.Is_EE {
node.Failover = "yes" node.Failover = true
} }
data, err := json.Marshal(&node) data, err := json.Marshal(&node)
if err != nil { if err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME) err = database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
if err != nil { if err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
err = SetNetworkNodesLastModified(netid) err = SetNetworkNodesLastModified(netid)
return node, err return node, err
} }
// DeleteIngressGateway - deletes an ingress gateway // DeleteIngressGateway - deletes an ingress gateway
func DeleteIngressGateway(networkName string, nodeid string) (models.LegacyNode, bool, error) { func DeleteIngressGateway(networkName string, nodeid string) (models.Node, bool, error) {
node, err := GetNodeByID(nodeid) node, err := GetNodeByID(nodeid)
if err != nil { if err != nil {
return models.LegacyNode{}, false, err return models.Node{}, false, err
} }
network, err := GetParentNetwork(networkName) //host, err := GetHost(node.ID.String())
//if err != nil {
//return models.Node{}, false, err
//}
//network, err := GetParentNetwork(networkName)
if err != nil { if err != nil {
return models.LegacyNode{}, false, err return models.Node{}, false, err
} }
// delete ext clients belonging to ingress gateway // delete ext clients belonging to ingress gateway
if err = DeleteGatewayExtClients(node.ID, networkName); err != nil { if err = DeleteGatewayExtClients(node.ID.String(), networkName); err != nil {
return models.LegacyNode{}, false, err return models.Node{}, false, err
} }
logger.Log(3, "deleting ingress gateway") logger.Log(3, "deleting ingress gateway")
wasFailover := node.Failover == "yes" wasFailover := node.Failover == true
if node.IsServer != "yes" { node.LastModified = time.Now()
node.UDPHolePunch = network.DefaultUDPHolePunch node.IsIngressGateway = false
}
node.LastModified = time.Now().Unix()
node.IsIngressGateway = "no"
node.IngressGatewayRange = "" node.IngressGatewayRange = ""
node.Failover = "no" node.Failover = false
// default to removing postup and postdown // default to removing postup and postdown
node.PostUp = "" node.PostUp = ""
node.PostDown = "" node.PostDown = ""
logger.Log(3, "deleting ingress gateway firewall in use is '", node.FirewallInUse, "' and isEgressGateway is", node.IsEgressGateway) //logger.Log(3, "deleting ingress gateway firewall in use is '", host.FirewallInUse, "' and isEgressGateway is", node.IsEgressGateway)
if node.EgressGatewayRequest.NodeID != "" { if node.EgressGatewayRequest.NodeID != "" {
_, err := CreateEgressGateway(node.EgressGatewayRequest) _, err := CreateEgressGateway(node.EgressGatewayRequest)
if err != nil { if err != nil {
@@ -286,11 +289,11 @@ func DeleteIngressGateway(networkName string, nodeid string) (models.LegacyNode,
data, err := json.Marshal(&node) data, err := json.Marshal(&node)
if err != nil { if err != nil {
return models.LegacyNode{}, false, err return models.Node{}, false, err
} }
err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME) err = database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
if err != nil { if err != nil {
return models.LegacyNode{}, wasFailover, err return models.Node{}, wasFailover, err
} }
err = SetNetworkNodesLastModified(networkName) err = SetNetworkNodesLastModified(networkName)
return node, wasFailover, err return node, wasFailover, err

View File

@@ -74,7 +74,6 @@ func UpdateHost(newHost, currentHost *models.Host) {
newHost.OS = currentHost.OS newHost.OS = currentHost.OS
newHost.IPForwarding = currentHost.IPForwarding newHost.IPForwarding = currentHost.IPForwarding
newHost.HostPass = currentHost.HostPass newHost.HostPass = currentHost.HostPass
newHost.NodePassword = currentHost.NodePassword
newHost.MacAddress = currentHost.MacAddress newHost.MacAddress = currentHost.MacAddress
newHost.Debug = currentHost.Debug newHost.Debug = currentHost.Debug
newHost.Nodes = currentHost.Nodes newHost.Nodes = currentHost.Nodes

View File

@@ -5,7 +5,6 @@ import (
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/wireguard"
) )
// GetMetrics - gets the metrics // GetMetrics - gets the metrics
@@ -38,69 +37,3 @@ func UpdateMetrics(nodeid string, metrics *models.Metrics) error {
func DeleteMetrics(nodeid string) error { func DeleteMetrics(nodeid string) error {
return database.DeleteRecord(database.METRICS_TABLE_NAME, nodeid) return database.DeleteRecord(database.METRICS_TABLE_NAME, nodeid)
} }
// CollectServerMetrics - collects metrics for given server node
func CollectServerMetrics(serverID string, networkNodes []models.LegacyNode) *models.Metrics {
newServerMetrics := models.Metrics{}
newServerMetrics.Connectivity = make(map[string]models.Metric)
var serverNode models.LegacyNode
for i := range networkNodes {
currNodeID := networkNodes[i].ID
if currNodeID == serverID {
serverNode = networkNodes[i]
continue
}
if currMetrics, err := GetMetrics(currNodeID); err == nil {
if currMetrics.Connectivity != nil && currMetrics.Connectivity[serverID].Connected {
metrics := currMetrics.Connectivity[serverID]
metrics.NodeName = networkNodes[i].Name
metrics.IsServer = "no"
newServerMetrics.Connectivity[currNodeID] = metrics
}
} else {
newServerMetrics.Connectivity[currNodeID] = models.Metric{
Connected: false,
Latency: 999,
}
}
}
if serverNode.IsIngressGateway == "yes" {
clients, err := GetExtClientsByID(serverID, serverNode.Network)
if err == nil {
peers, err := wireguard.GetDevicePeers(serverNode.Interface)
if err == nil {
for i := range clients {
for j := range peers {
if clients[i].PublicKey == peers[j].PublicKey.String() {
if peers[j].ReceiveBytes > 0 &&
peers[j].TransmitBytes > 0 {
newServerMetrics.Connectivity[clients[i].ClientID] = models.Metric{
NodeName: clients[i].ClientID,
TotalTime: 5,
Uptime: 5,
IsServer: "no",
TotalReceived: peers[j].ReceiveBytes,
TotalSent: peers[j].TransmitBytes,
Connected: true,
Latency: -1, // can not determine latency on server currently
}
} else {
newServerMetrics.Connectivity[clients[i].ClientID] = models.Metric{
NodeName: clients[i].ClientID,
TotalTime: 5,
Uptime: 0,
IsServer: "no",
Connected: false,
Latency: 999,
}
}
}
}
}
}
}
}
return &newServerMetrics
}

View File

@@ -90,7 +90,7 @@ func Collect(iface, network string, proxy bool, peerMap models.PeerMap) (*models
} }
// GetExchangedBytesForNode - get exchanged bytes for current node peers // GetExchangedBytesForNode - get exchanged bytes for current node peers
func GetExchangedBytesForNode(node *models.LegacyNode, metrics *models.Metrics) error { func GetExchangedBytesForNode(node *models.Node, metrics *models.Metrics) error {
peers, err := logic.GetPeerUpdate(node) peers, err := logic.GetPeerUpdate(node)
if err != nil { if err != nil {
@@ -102,7 +102,7 @@ func GetExchangedBytesForNode(node *models.LegacyNode, metrics *models.Metrics)
return err return err
} }
defer wgclient.Close() defer wgclient.Close()
device, err := wgclient.Device(node.Interface) device, err := wgclient.Device("netmaker")
if err != nil { if err != nil {
return err return err
} }

View File

@@ -5,7 +5,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"os/exec"
"strings" "strings"
"github.com/c-robinson/iplib" "github.com/c-robinson/iplib"
@@ -15,7 +14,6 @@ import (
"github.com/gravitl/netmaker/logic/acls/nodeacls" "github.com/gravitl/netmaker/logic/acls/nodeacls"
"github.com/gravitl/netmaker/logic/pro" "github.com/gravitl/netmaker/logic/pro"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/validation" "github.com/gravitl/netmaker/validation"
) )
@@ -51,18 +49,6 @@ func DeleteNetwork(network string) error {
nodeCount, err := GetNetworkNonServerNodeCount(network) nodeCount, err := GetNetworkNonServerNodeCount(network)
if nodeCount == 0 || database.IsEmptyRecord(err) { if nodeCount == 0 || database.IsEmptyRecord(err) {
// delete server nodes first then db records // delete server nodes first then db records
servers, err := GetSortedNetworkServerNodes(network)
if err == nil {
for _, s := range servers {
if err = DeleteNode(&s, true); err != nil {
logger.Log(2, "could not removed server", s.Name, "before deleting network", network)
} else {
logger.Log(2, "removed server", s.Name, "before deleting network", network)
}
}
} else {
logger.Log(1, "could not remove servers before deleting network", network)
}
if err = pro.RemoveAllNetworkUsers(network); err != nil { if err = pro.RemoveAllNetworkUsers(network); err != nil {
logger.Log(0, "failed to remove network users on network delete for network", network, err.Error()) logger.Log(0, "failed to remove network users on network delete for network", network, err.Error())
} }
@@ -135,11 +121,11 @@ func GetNetworkNonServerNodeCount(networkName string) (int, error) {
return count, err return count, err
} }
for _, value := range collection { for _, value := range collection {
var node models.LegacyNode var node models.Node
if err = json.Unmarshal([]byte(value), &node); err != nil { if err = json.Unmarshal([]byte(value), &node); err != nil {
return count, err return count, err
} else { } else {
if node.Network == networkName && node.IsServer != "yes" { if node.Network == networkName {
count++ count++
} }
} }
@@ -178,22 +164,22 @@ func GetNetworkSettings(networkname string) (models.Network, error) {
} }
// UniqueAddress - see if address is unique // UniqueAddress - see if address is unique
func UniqueAddress(networkName string, reverse bool) (string, error) { func UniqueAddress(networkName string, reverse bool) (net.IP, error) {
add := net.IP{}
var network models.Network var network models.Network
network, err := GetParentNetwork(networkName) network, err := GetParentNetwork(networkName)
if err != nil { if err != nil {
logger.Log(0, "UniqueAddressServer encountered an error") logger.Log(0, "UniqueAddressServer encountered an error")
return "666", err return add, err
} }
if network.IsIPv4 == "no" { if network.IsIPv4 == "no" {
return "", fmt.Errorf("IPv4 not active on network " + networkName) return add, fmt.Errorf("IPv4 not active on network " + networkName)
} }
//ensure AddressRange is valid //ensure AddressRange is valid
if _, _, err := net.ParseCIDR(network.AddressRange); err != nil { if _, _, err := net.ParseCIDR(network.AddressRange); err != nil {
logger.Log(0, "UniqueAddress encountered an error") logger.Log(0, "UniqueAddress encountered an error")
return "666", err return add, err
} }
net4 := iplib.Net4FromStr(network.AddressRange) net4 := iplib.Net4FromStr(network.AddressRange)
newAddrs := net4.FirstAddress() newAddrs := net4.FirstAddress()
@@ -205,7 +191,7 @@ func UniqueAddress(networkName string, reverse bool) (string, error) {
for { for {
if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, false) && if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, false) &&
IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, false) { IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, false) {
return newAddrs.String(), nil return newAddrs, nil
} }
if reverse { if reverse {
newAddrs, err = net4.PreviousIP(newAddrs) newAddrs, err = net4.PreviousIP(newAddrs)
@@ -217,7 +203,7 @@ func UniqueAddress(networkName string, reverse bool) (string, error) {
} }
} }
return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", errors.New("ERROR: No unique addresses available. Check network subnet") return add, errors.New("ERROR: No unique addresses available. Check network subnet")
} }
// IsIPUnique - checks if an IP is unique // IsIPUnique - checks if an IP is unique
@@ -231,16 +217,16 @@ func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool {
} }
for _, value := range collection { // filter for _, value := range collection { // filter
var node models.LegacyNode var node models.Node
if err = json.Unmarshal([]byte(value), &node); err != nil { if err = json.Unmarshal([]byte(value), &node); err != nil {
continue continue
} }
if isIpv6 { if isIpv6 {
if node.Address6 == ip && node.Network == network { if node.Address6.String() == ip && node.Network == network {
return false return false
} }
} else { } else {
if node.Address == ip && node.Network == network { if node.Address.String() == ip && node.Network == network {
return false return false
} }
} }
@@ -250,21 +236,21 @@ func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool {
} }
// UniqueAddress6 - see if ipv6 address is unique // UniqueAddress6 - see if ipv6 address is unique
func UniqueAddress6(networkName string, reverse bool) (string, error) { func UniqueAddress6(networkName string, reverse bool) (net.IP, error) {
add := net.IP{}
var network models.Network var network models.Network
network, err := GetParentNetwork(networkName) network, err := GetParentNetwork(networkName)
if err != nil { if err != nil {
fmt.Println("Network Not Found") fmt.Println("Network Not Found")
return "", err return add, err
} }
if network.IsIPv6 == "no" { if network.IsIPv6 == "no" {
return "", fmt.Errorf("IPv6 not active on network " + networkName) return add, fmt.Errorf("IPv6 not active on network " + networkName)
} }
//ensure AddressRange is valid //ensure AddressRange is valid
if _, _, err := net.ParseCIDR(network.AddressRange6); err != nil { if _, _, err := net.ParseCIDR(network.AddressRange6); err != nil {
return "666", err return add, err
} }
net6 := iplib.Net6FromStr(network.AddressRange6) net6 := iplib.Net6FromStr(network.AddressRange6)
@@ -273,13 +259,13 @@ func UniqueAddress6(networkName string, reverse bool) (string, error) {
newAddrs, err = net6.PreviousIP(net6.LastAddress()) newAddrs, err = net6.PreviousIP(net6.LastAddress())
} }
if err != nil { if err != nil {
return "", err return add, err
} }
for { for {
if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, true) && if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, true) &&
IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, true) { IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, true) {
return newAddrs.String(), nil return newAddrs, nil
} }
if reverse { if reverse {
newAddrs, err = net6.PreviousIP(newAddrs) newAddrs, err = net6.PreviousIP(newAddrs)
@@ -291,23 +277,21 @@ func UniqueAddress6(networkName string, reverse bool) (string, error) {
} }
} }
return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", errors.New("ERROR: No unique IPv6 addresses available. Check network subnet") return add, errors.New("ERROR: No unique IPv6 addresses available. Check network subnet")
} }
// GetLocalIP - gets the local ip // GetLocalIP - gets the local ip
func GetLocalIP(node models.LegacyNode) string { func GetLocalIP(node models.Node) string {
var local string var local string
ifaces, err := net.Interfaces() ifaces, err := net.Interfaces()
if err != nil { if err != nil {
return local return local
} }
_, localrange, err := net.ParseCIDR(node.LocalRange) host, err := GetHost(node.HostID.String())
if err != nil { if err != nil {
return local return local
} }
localrange := host.LocalRange
found := false found := false
for _, i := range ifaces { for _, i := range ifaces {
if i.Flags&net.FlagUp == 0 { if i.Flags&net.FlagUp == 0 {
@@ -327,7 +311,7 @@ func GetLocalIP(node models.LegacyNode) string {
if !found { if !found {
ip = v.IP ip = v.IP
local = ip.String() local = ip.String()
if node.IsLocal == "yes" { if node.IsLocal {
found = localrange.Contains(ip) found = localrange.Contains(ip)
} else { } else {
found = true found = true
@@ -337,7 +321,7 @@ func GetLocalIP(node models.LegacyNode) string {
if !found { if !found {
ip = v.IP ip = v.IP
local = ip.String() local = ip.String()
if node.IsLocal == "yes" { if node.IsLocal {
found = localrange.Contains(ip) found = localrange.Contains(ip)
} else { } else {
@@ -361,7 +345,7 @@ func UpdateNetworkLocalAddresses(networkName string) error {
for _, value := range collection { for _, value := range collection {
var node models.LegacyNode var node models.Node
err := json.Unmarshal([]byte(value), &node) err := json.Unmarshal([]byte(value), &node)
if err != nil { if err != nil {
@@ -369,53 +353,27 @@ func UpdateNetworkLocalAddresses(networkName string) error {
return err return err
} }
if node.Network == networkName { if node.Network == networkName {
var ipaddr string var ipaddr net.IP
var iperr error var iperr error
if node.IsServer == "yes" { ipaddr, iperr = UniqueAddress(networkName, false)
ipaddr, iperr = UniqueAddress(networkName, true)
} else {
ipaddr, iperr = UniqueAddress(networkName, false)
}
if iperr != nil { if iperr != nil {
fmt.Println("error in node address assignment!") fmt.Println("error in node address assignment!")
return iperr return iperr
} }
node.Address = ipaddr node.Address.IP = ipaddr
newNodeData, err := json.Marshal(&node) newNodeData, err := json.Marshal(&node)
if err != nil { if err != nil {
logger.Log(1, "error in node address assignment!") logger.Log(1, "error in node address assignment!")
return err return err
} }
database.Insert(node.ID, string(newNodeData), database.NODES_TABLE_NAME) database.Insert(node.ID.String(), string(newNodeData), database.NODES_TABLE_NAME)
} }
} }
return nil return nil
} }
// UpdateNetworkLocalAddresses - updates network localaddresses
func UpdateNetworkHolePunching(networkName string, holepunch string) error {
nodes, err := GetNetworkNodes(networkName)
if err != nil {
return err
}
for _, node := range nodes {
if node.IsServer != "yes" {
node.UDPHolePunch = holepunch
newNodeData, err := json.Marshal(&node)
if err != nil {
logger.Log(1, "error in node hole punch assignment")
return err
}
database.Insert(node.ID, string(newNodeData), database.NODES_TABLE_NAME)
}
}
return nil
}
// RemoveNetworkNodeIPv6Addresses - removes network node IPv6 addresses // RemoveNetworkNodeIPv6Addresses - removes network node IPv6 addresses
func RemoveNetworkNodeIPv6Addresses(networkName string) error { func RemoveNetworkNodeIPv6Addresses(networkName string) error {
@@ -426,19 +384,19 @@ func RemoveNetworkNodeIPv6Addresses(networkName string) error {
for _, value := range collections { for _, value := range collections {
var node models.LegacyNode var node models.Node
err := json.Unmarshal([]byte(value), &node) err := json.Unmarshal([]byte(value), &node)
if err != nil { if err != nil {
fmt.Println("error in node address assignment!") fmt.Println("error in node address assignment!")
return err return err
} }
if node.Network == networkName { if node.Network == networkName {
node.Address6 = "" node.Address6.IP = nil
data, err := json.Marshal(&node) data, err := json.Marshal(&node)
if err != nil { if err != nil {
return err return err
} }
database.Insert(node.ID, string(data), database.NODES_TABLE_NAME) database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
} }
} }
@@ -455,31 +413,27 @@ func UpdateNetworkNodeAddresses(networkName string) error {
for _, value := range collections { for _, value := range collections {
var node models.LegacyNode var node models.Node
err := json.Unmarshal([]byte(value), &node) err := json.Unmarshal([]byte(value), &node)
if err != nil { if err != nil {
logger.Log(1, "error in node ipv4 address assignment!") logger.Log(1, "error in node ipv4 address assignment!")
return err return err
} }
if node.Network == networkName { if node.Network == networkName {
var ipaddr string var ipaddr net.IP
var iperr error var iperr error
if node.IsServer == "yes" { ipaddr, iperr = UniqueAddress(networkName, false)
ipaddr, iperr = UniqueAddress(networkName, true)
} else {
ipaddr, iperr = UniqueAddress(networkName, false)
}
if iperr != nil { if iperr != nil {
logger.Log(1, "error in node ipv4 address assignment!") logger.Log(1, "error in node ipv4 address assignment!")
return iperr return iperr
} }
node.Address = ipaddr node.Address.IP = ipaddr
data, err := json.Marshal(&node) data, err := json.Marshal(&node)
if err != nil { if err != nil {
return err return err
} }
database.Insert(node.ID, string(data), database.NODES_TABLE_NAME) database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
} }
} }
@@ -496,31 +450,27 @@ func UpdateNetworkNodeAddresses6(networkName string) error {
for _, value := range collections { for _, value := range collections {
var node models.LegacyNode var node models.Node
err := json.Unmarshal([]byte(value), &node) err := json.Unmarshal([]byte(value), &node)
if err != nil { if err != nil {
logger.Log(1, "error in node ipv6 address assignment!") logger.Log(1, "error in node ipv6 address assignment!")
return err return err
} }
if node.Network == networkName { if node.Network == networkName {
var ipaddr string var ipaddr net.IP
var iperr error var iperr error
if node.IsServer == "yes" { ipaddr, iperr = UniqueAddress6(networkName, false)
ipaddr, iperr = UniqueAddress6(networkName, true)
} else {
ipaddr, iperr = UniqueAddress6(networkName, false)
}
if iperr != nil { if iperr != nil {
logger.Log(1, "error in node ipv6 address assignment!") logger.Log(1, "error in node ipv6 address assignment!")
return iperr return iperr
} }
node.Address6 = ipaddr node.Address6.IP = ipaddr
data, err := json.Marshal(&node) data, err := json.Marshal(&node)
if err != nil { if err != nil {
return err return err
} }
database.Insert(node.ID, string(data), database.NODES_TABLE_NAME) database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
} }
} }
@@ -687,7 +637,7 @@ func networkNodesUpdateAction(networkName string, action string) error {
} }
for _, value := range collections { for _, value := range collections {
var node models.LegacyNode var node models.Node
err := json.Unmarshal([]byte(value), &node) err := json.Unmarshal([]byte(value), &node)
if err != nil { if err != nil {
fmt.Println("error in node address assignment!") fmt.Println("error in node address assignment!")
@@ -702,55 +652,8 @@ func networkNodesUpdateAction(networkName string, action string) error {
if err != nil { if err != nil {
return err return err
} }
database.Insert(node.ID, string(data), database.NODES_TABLE_NAME) database.Insert(node.ID.String(), string(data), database.NODES_TABLE_NAME)
} }
} }
return nil return nil
} }
func deleteInterface(ifacename string, postdown string) error {
var err error
if !ncutils.IsKernel() {
err = RemoveConf(ifacename, true)
} else {
ipExec, errN := exec.LookPath("ip")
err = errN
if err != nil {
logger.Log(1, err.Error())
}
_, err = ncutils.RunCmd(ipExec+" link del "+ifacename, false)
if postdown != "" {
_, err = ncutils.RunCmd(postdown, false)
}
}
return err
}
func isInterfacePresent(iface string, address string) (string, bool) {
var interfaces []net.Interface
var err error
interfaces, err = net.Interfaces()
if err != nil {
logger.Log(0, "ERROR: could not read interfaces")
return "", true
}
for _, currIface := range interfaces {
var currAddrs []net.Addr
currAddrs, err = currIface.Addrs()
if err != nil || len(currAddrs) == 0 {
continue
}
for _, addr := range currAddrs {
if strings.Contains(addr.String(), address) && currIface.Name != iface {
// logger.Log(2, "found iface", addr.String(), currIface.Name)
interfaces = nil
currAddrs = nil
return currIface.Name, false
}
}
currAddrs = nil
}
interfaces = nil
// logger.Log(2, "failed to find iface", iface)
return "", true
}

View File

@@ -5,7 +5,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"sort" "net"
"time" "time"
validator "github.com/go-playground/validator/v10" validator "github.com/go-playground/validator/v10"
@@ -19,7 +19,6 @@ import (
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/ncutils" "github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/servercfg"
"github.com/gravitl/netmaker/validation"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
@@ -33,11 +32,11 @@ const (
) )
// GetNetworkNodes - gets the nodes of a network // GetNetworkNodes - gets the nodes of a network
func GetNetworkNodes(network string) ([]models.LegacyNode, error) { func GetNetworkNodes(network string) ([]models.Node, error) {
var nodes []models.LegacyNode var nodes []models.Node
allnodes, err := GetAllNodes() allnodes, err := GetAllNodes()
if err != nil { if err != nil {
return []models.LegacyNode{}, err return []models.Node{}, err
} }
for _, node := range allnodes { for _, node := range allnodes {
if node.Network == network { if node.Network == network {
@@ -47,92 +46,11 @@ func GetNetworkNodes(network string) ([]models.LegacyNode, error) {
return nodes, nil return nodes, nil
} }
// GetSortedNetworkServerNodes - gets nodes of a network, except sorted by update time
func GetSortedNetworkServerNodes(network string) ([]models.LegacyNode, error) {
var nodes []models.LegacyNode
collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
if err != nil {
if database.IsEmptyRecord(err) {
return []models.LegacyNode{}, nil
}
return nodes, err
}
for _, value := range collection {
var node models.LegacyNode
err := json.Unmarshal([]byte(value), &node)
if err != nil {
continue
}
if node.Network == network && node.IsServer == "yes" {
nodes = append(nodes, node)
}
}
sort.Sort(models.NodesArray(nodes))
return nodes, nil
}
// GetServerNodes - gets the server nodes of a network
func GetServerNodes(network string) []models.LegacyNode {
var serverNodes = make([]models.LegacyNode, 0)
var nodes, err = GetNetworkNodes(network)
if err != nil {
return serverNodes
}
for _, node := range nodes {
if node.IsServer == "yes" {
serverNodes = append(serverNodes, node)
}
}
return serverNodes
}
// UncordonNode - approves a node to join a network
func UncordonNode(nodeid string) (models.LegacyNode, error) {
node, err := GetNodeByID(nodeid)
if err != nil {
return models.LegacyNode{}, err
}
node.SetLastModified()
node.IsPending = "no"
data, err := json.Marshal(&node)
if err != nil {
return node, err
}
err = database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
return node, err
}
// SetIfLeader - gets the peers of a given server node
func SetPeersIfLeader(node *models.LegacyNode) {
if IsLeader(node) {
setNetworkServerPeers(node)
}
}
// IsLeader - determines if a given server node is a leader
func IsLeader(node *models.LegacyNode) bool {
nodes, err := GetSortedNetworkServerNodes(node.Network)
if err != nil {
logger.Log(0, "ERROR: COULD NOT RETRIEVE SERVER NODES. THIS WILL BREAK HOLE PUNCHING.")
return false
}
for _, n := range nodes {
if n.LastModified > time.Now().Add(-1*time.Minute).Unix() {
return n.Address == node.Address
}
}
return len(nodes) <= 1 || nodes[1].Address == node.Address
}
// == DB related functions ==
// UpdateNode - takes a node and updates another node with it's values // UpdateNode - takes a node and updates another node with it's values
func UpdateNode(currentNode *models.LegacyNode, newNode *models.LegacyNode) error { func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
if newNode.Address != currentNode.Address { if newNode.Address.String() != currentNode.Address.String() {
if network, err := GetParentNetwork(newNode.Network); err == nil { if network, err := GetParentNetwork(newNode.Network); err == nil {
if !IsAddressInCIDR(newNode.Address, network.AddressRange) { if !IsAddressInCIDR(newNode.Address.String(), network.AddressRange) {
return fmt.Errorf("invalid address provided; out of network range for node %s", newNode.ID) return fmt.Errorf("invalid address provided; out of network range for node %s", newNode.ID)
} }
} }
@@ -140,9 +58,6 @@ func UpdateNode(currentNode *models.LegacyNode, newNode *models.LegacyNode) erro
nodeACLDelta := currentNode.DefaultACL != newNode.DefaultACL nodeACLDelta := currentNode.DefaultACL != newNode.DefaultACL
newNode.Fill(currentNode) newNode.Fill(currentNode)
if currentNode.IsServer == "yes" && !validateServer(currentNode, newNode) {
return fmt.Errorf("this operation is not supported on server nodes")
}
// check for un-settable server values // check for un-settable server values
if err := ValidateNode(newNode, true); err != nil { if err := ValidateNode(newNode, true); err != nil {
return err return err
@@ -151,7 +66,7 @@ func UpdateNode(currentNode *models.LegacyNode, newNode *models.LegacyNode) erro
if newNode.ID == currentNode.ID { if newNode.ID == currentNode.ID {
if nodeACLDelta { if nodeACLDelta {
if err := updateProNodeACLS(newNode); err != nil { if err := updateProNodeACLS(newNode); err != nil {
logger.Log(1, "failed to apply node level ACLs during creation of node", newNode.ID, "-", err.Error()) logger.Log(1, "failed to apply node level ACLs during creation of node", newNode.ID.String(), "-", err.Error())
return err return err
} }
} }
@@ -160,15 +75,15 @@ func UpdateNode(currentNode *models.LegacyNode, newNode *models.LegacyNode) erro
if data, err := json.Marshal(newNode); err != nil { if data, err := json.Marshal(newNode); err != nil {
return err return err
} else { } else {
return database.Insert(newNode.ID, string(data), database.NODES_TABLE_NAME) return database.Insert(newNode.ID.String(), string(data), database.NODES_TABLE_NAME)
} }
} }
return fmt.Errorf("failed to update node " + currentNode.ID + ", cannot change ID.") return fmt.Errorf("failed to update node " + currentNode.ID.String() + ", cannot change ID.")
} }
// DeleteNode - marks node for deletion if called by UI or deletes node if called by node // DeleteNode - marks node for deletion if called by UI or deletes node if called by node
func DeleteNode(node *models.LegacyNode, purge bool) error { func DeleteNode(node *models.Node, purge bool) error {
if !purge { if !purge {
newnode := node newnode := node
newnode.PendingDelete = true newnode.PendingDelete = true
@@ -182,8 +97,9 @@ func DeleteNode(node *models.LegacyNode, purge bool) error {
return err return err
} }
if servercfg.Is_EE { if servercfg.Is_EE {
if err := EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil { host, _ := GetHost(node.HostID.String())
logger.Log(0, "failed to reset failover lists during node delete for node", node.Name, node.Network) if err := EnterpriseResetAllPeersFailovers(node.ID.String(), node.Network); err != nil {
logger.Log(0, "failed to reset failover lists during node delete for node", host.Name, node.Network)
} }
} }
@@ -191,12 +107,12 @@ func DeleteNode(node *models.LegacyNode, purge bool) error {
} }
// DeleteNodeByID - deletes a node from database // DeleteNodeByID - deletes a node from database
func DeleteNodeByID(node *models.LegacyNode) error { func DeleteNodeByID(node *models.Node) error {
var err error var err error
var key = node.ID var key = node.ID.String()
//delete any ext clients as required //delete any ext clients as required
if node.IsIngressGateway == "yes" { if node.IsIngressGateway {
if err := DeleteGatewayExtClients(node.ID, node.Network); err != nil { if err := DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil {
logger.Log(0, "failed to deleted ext clients", err.Error()) logger.Log(0, "failed to deleted ext clients", err.Error())
} }
} }
@@ -209,34 +125,31 @@ func DeleteNodeByID(node *models.LegacyNode) error {
SetDNS() SetDNS()
} }
if node.OwnerID != "" { if node.OwnerID != "" {
err = pro.DissociateNetworkUserNode(node.OwnerID, node.Network, node.ID) err = pro.DissociateNetworkUserNode(node.OwnerID, node.Network, node.ID.String())
if err != nil { if err != nil {
logger.Log(0, "failed to dissasociate", node.OwnerID, "from node", node.ID, ":", err.Error()) logger.Log(0, "failed to dissasociate", node.OwnerID, "from node", node.ID.String(), ":", err.Error())
} }
} }
_, err = nodeacls.RemoveNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID)) _, err = nodeacls.RemoveNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()))
if err != nil { if err != nil {
// ignoring for now, could hit a nil pointer if delete called twice // ignoring for now, could hit a nil pointer if delete called twice
logger.Log(2, "attempted to remove node ACL for node", node.Name, node.ID) logger.Log(2, "attempted to remove node ACL for node", node.ID.String())
} }
// removeZombie <- node.ID // removeZombie <- node.ID
if err = DeleteMetrics(node.ID); err != nil { if err = DeleteMetrics(node.ID.String()); err != nil {
logger.Log(1, "unable to remove metrics from DB for node", node.ID, err.Error()) logger.Log(1, "unable to remove metrics from DB for node", node.ID.String(), err.Error())
}
if node.IsServer == "yes" {
return removeLocalServer(node)
} }
return nil return nil
} }
// IsNodeIDUnique - checks if node id is unique // IsNodeIDUnique - checks if node id is unique
func IsNodeIDUnique(node *models.LegacyNode) (bool, error) { func IsNodeIDUnique(node *models.Node) (bool, error) {
_, err := database.FetchRecord(database.NODES_TABLE_NAME, node.ID) _, err := database.FetchRecord(database.NODES_TABLE_NAME, node.ID.String())
return database.IsEmptyRecord(err), err return database.IsEmptyRecord(err), err
} }
// ValidateNode - validates node values // ValidateNode - validates node values
func ValidateNode(node *models.LegacyNode, isUpdate bool) error { func ValidateNode(node *models.Node, isUpdate bool) error {
v := validator.New() v := validator.New()
_ = v.RegisterValidation("id_unique", func(fl validator.FieldLevel) bool { _ = v.RegisterValidation("id_unique", func(fl validator.FieldLevel) bool {
if isUpdate { if isUpdate {
@@ -249,18 +162,7 @@ func ValidateNode(node *models.LegacyNode, isUpdate bool) error {
_, err := GetNetworkByNode(node) _, err := GetNetworkByNode(node)
return err == nil return err == nil
}) })
_ = v.RegisterValidation("in_charset", func(fl validator.FieldLevel) bool {
isgood := node.NameInNodeCharSet()
return isgood
})
_ = v.RegisterValidation("checkyesorno", func(fl validator.FieldLevel) bool {
return validation.CheckYesOrNo(fl)
})
_ = v.RegisterValidation("checkyesornoorunset", func(fl validator.FieldLevel) bool {
return validation.CheckYesOrNoOrUnset(fl)
})
err := v.Struct(node) err := v.Struct(node)
return err return err
} }
@@ -271,7 +173,7 @@ func IsFailoverPresent(network string) bool {
return false return false
} }
for i := range netNodes { for i := range netNodes {
if netNodes[i].Failover == "yes" { if netNodes[i].Failover {
return true return true
} }
} }
@@ -279,23 +181,24 @@ func IsFailoverPresent(network string) bool {
} }
// CreateNode - creates a node in database // CreateNode - creates a node in database
func CreateNode(node *models.LegacyNode) error { func CreateNode(node *models.Node) error {
host, err := GetHost(node.HostID.String())
if err != nil {
return err
}
//encrypt that password so we never see it //encrypt that password so we never see it
hash, err := bcrypt.GenerateFromPassword([]byte(node.Password), 5) hash, err := bcrypt.GenerateFromPassword([]byte(host.HostPass), 5)
if err != nil { if err != nil {
return err return err
} }
//set password to encrypted password //set password to encrypted password
node.Password = string(hash) host.HostPass = string(hash)
if node.Name == models.NODE_SERVER_NAME { if !node.DNSOn {
node.IsServer = "yes"
}
if node.DNSOn == "" {
if servercfg.IsDNSMode() { if servercfg.IsDNSMode() {
node.DNSOn = "yes" node.DNSOn = true
} else { } else {
node.DNSOn = "no" node.DNSOn = false
} }
} }
@@ -313,34 +216,29 @@ func CreateNode(node *models.LegacyNode) error {
node.DefaultACL = "unset" node.DefaultACL = "unset"
} }
reverse := node.IsServer == "yes" if node.Address.IP == nil {
if node.Address == "" {
if parentNetwork.IsIPv4 == "yes" { if parentNetwork.IsIPv4 == "yes" {
if node.Address, err = UniqueAddress(node.Network, reverse); err != nil { if node.Address.IP, err = UniqueAddress(node.Network, false); err != nil {
return err return err
} }
} }
} else if !IsIPUnique(node.Network, node.Address, database.NODES_TABLE_NAME, false) { } else if !IsIPUnique(node.Network, node.Address.String(), database.NODES_TABLE_NAME, false) {
return fmt.Errorf("invalid address: ipv4 " + node.Address + " is not unique") return fmt.Errorf("invalid address: ipv4 " + node.Address.String() + " is not unique")
} }
if node.Address6 == "" { if node.Address6.IP == nil {
if parentNetwork.IsIPv6 == "yes" { if parentNetwork.IsIPv6 == "yes" {
if node.Address6, err = UniqueAddress6(node.Network, reverse); err != nil { if node.Address6.IP, err = UniqueAddress6(node.Network, false); err != nil {
return err return err
} }
} }
} else if !IsIPUnique(node.Network, node.Address6, database.NODES_TABLE_NAME, true) { } else if !IsIPUnique(node.Network, node.Address6.String(), database.NODES_TABLE_NAME, true) {
return fmt.Errorf("invalid address: ipv6 " + node.Address6 + " is not unique") return fmt.Errorf("invalid address: ipv6 " + node.Address6.String() + " is not unique")
}
node.ID = uuid.NewString()
if node.IsServer == "yes" {
node.HostID = uuid.NewString()
} }
node.ID = uuid.New()
//Create a JWT for the node //Create a JWT for the node
tokenString, _ := CreateJWT(node.ID, node.MacAddress, node.Network) tokenString, _ := CreateJWT(node.ID.String(), host.MacAddress.String(), node.Network)
if tokenString == "" { if tokenString == "" {
//logic.ReturnErrorResponse(w, r, errorResponse) //logic.ReturnErrorResponse(w, r, errorResponse)
return err return err
@@ -349,34 +247,30 @@ func CreateNode(node *models.LegacyNode) error {
if err != nil { if err != nil {
return err return err
} }
CheckZombies(node) CheckZombies(node, host.MacAddress)
nodebytes, err := json.Marshal(&node) nodebytes, err := json.Marshal(&node)
if err != nil { if err != nil {
return err return err
} }
err = database.Insert(node.ID, string(nodebytes), database.NODES_TABLE_NAME) err = database.Insert(node.ID.String(), string(nodebytes), database.NODES_TABLE_NAME)
if err != nil { if err != nil {
return err return err
} }
_, err = nodeacls.CreateNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID), defaultACLVal) _, err = nodeacls.CreateNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), defaultACLVal)
if err != nil { if err != nil {
logger.Log(1, "failed to create node ACL for node,", node.ID, "err:", err.Error()) logger.Log(1, "failed to create node ACL for node,", node.ID.String(), "err:", err.Error())
return err return err
} }
if err = updateProNodeACLS(node); err != nil { if err = updateProNodeACLS(node); err != nil {
logger.Log(1, "failed to apply node level ACLs during creation of node", node.ID, "-", err.Error()) logger.Log(1, "failed to apply node level ACLs during creation of node", node.ID.String(), "-", err.Error())
return err return err
} }
if node.IsPending != "yes" { if err = UpdateMetrics(node.ID.String(), &models.Metrics{Connectivity: make(map[string]models.Metric)}); err != nil {
DecrimentKey(node.Network, node.AccessKey) logger.Log(1, "failed to initialize metrics for node", node.ID.String(), err.Error())
}
if err = UpdateMetrics(node.ID, &models.Metrics{Connectivity: make(map[string]models.Metric)}); err != nil {
logger.Log(1, "failed to initialize metrics for node", node.Name, node.ID, err.Error())
} }
SetNetworkNodesLastModified(node.Network) SetNetworkNodesLastModified(node.Network)
@@ -387,21 +281,21 @@ func CreateNode(node *models.LegacyNode) error {
} }
// GetAllNodes - returns all nodes in the DB // GetAllNodes - returns all nodes in the DB
func GetAllNodes() ([]models.LegacyNode, error) { func GetAllNodes() ([]models.Node, error) {
var nodes []models.LegacyNode var nodes []models.Node
collection, err := database.FetchRecords(database.NODES_TABLE_NAME) collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
if err != nil { if err != nil {
if database.IsEmptyRecord(err) { if database.IsEmptyRecord(err) {
return []models.LegacyNode{}, nil return []models.Node{}, nil
} }
return []models.LegacyNode{}, err return []models.Node{}, err
} }
for _, value := range collection { for _, value := range collection {
var node models.LegacyNode var node models.Node
if err := json.Unmarshal([]byte(value), &node); err != nil { if err := json.Unmarshal([]byte(value), &node); err != nil {
return []models.LegacyNode{}, err return []models.Node{}, err
} }
// add node to our array // add node to our array
nodes = append(nodes, node) nodes = append(nodes, node)
@@ -410,24 +304,6 @@ func GetAllNodes() ([]models.LegacyNode, error) {
return nodes, nil return nodes, nil
} }
// CheckIsServer - check if a node is the server node
func CheckIsServer(node *models.LegacyNode) bool {
nodeData, err := database.FetchRecords(database.NODES_TABLE_NAME)
if err != nil && !database.IsEmptyRecord(err) {
return false
}
for _, value := range nodeData {
var tmpNode models.LegacyNode
if err := json.Unmarshal([]byte(value), &tmpNode); err != nil {
continue
}
if tmpNode.Network == node.Network && tmpNode.MacAddress != node.MacAddress {
return false
}
}
return true
}
// GetNetworkByNode - gets the network model from a node // GetNetworkByNode - gets the network model from a node
func GetNetworkByNode(node *models.Node) (models.Network, error) { func GetNetworkByNode(node *models.Node) (models.Network, error) {
@@ -443,28 +319,21 @@ func GetNetworkByNode(node *models.Node) (models.Network, error) {
} }
// SetNodeDefaults - sets the defaults of a node to avoid empty fields // SetNodeDefaults - sets the defaults of a node to avoid empty fields
func SetNodeDefaults(node *models.LegacyNode) { func SetNodeDefaults(node *models.Node) {
//TODO: Maybe I should make Network a part of the node struct. Then we can just query the Network object for stuff.
parentNetwork, _ := GetNetworkByNode(node) parentNetwork, _ := GetNetworkByNode(node)
node.NetworkSettings = parentNetwork _, cidr, _ := net.ParseCIDR(parentNetwork.AddressRange)
node.NetworkSettings.AccessKeys = []models.AccessKey{} node.NetworkRange = *cidr
_, cidr, _ = net.ParseCIDR(parentNetwork.AddressRange6)
node.NetworkRange6 = *cidr
node.ExpirationDateTime = time.Now().Add(models.TEN_YEARS_IN_SECONDS)
node.ExpirationDateTime = time.Now().Unix() + models.TEN_YEARS_IN_SECONDS if node.DefaultACL == "" {
if node.DefaultACL == "" && node.IsServer != "yes" {
node.DefaultACL = parentNetwork.DefaultACL node.DefaultACL = parentNetwork.DefaultACL
} }
if node.ListenPort == 0 {
node.ListenPort = parentNetwork.DefaultListenPort
}
if node.Interface == "" {
node.Interface = parentNetwork.DefaultInterface
}
if node.PersistentKeepalive == 0 { if node.PersistentKeepalive == 0 {
node.PersistentKeepalive = parentNetwork.DefaultKeepalive node.PersistentKeepalive = int(parentNetwork.DefaultKeepalive)
} }
if node.PostUp == "" { if node.PostUp == "" {
postup := parentNetwork.DefaultPostUp postup := parentNetwork.DefaultPostUp
@@ -474,44 +343,41 @@ func SetNodeDefaults(node *models.LegacyNode) {
postdown := parentNetwork.DefaultPostDown postdown := parentNetwork.DefaultPostDown
node.PostDown = postdown node.PostDown = postdown
} }
if node.IsStatic == "" {
node.IsStatic = "no"
}
if node.UDPHolePunch == "" {
node.UDPHolePunch = parentNetwork.DefaultUDPHolePunch
if node.UDPHolePunch == "" {
node.UDPHolePunch = "yes"
}
}
// == Parent Network settings == // == Parent Network settings ==
if node.MTU == 0 {
node.MTU = parentNetwork.DefaultMTU
}
// == node defaults if not set by parent == // == node defaults if not set by parent ==
node.SetIPForwardingDefault() ///TODO ___ REVISIT ------
node.SetDNSOnDefault() ///TODO ___ REVISIT ------
node.SetIsLocalDefault() ///TODO ___ REVISIT ------
node.SetLastModified() ///TODO ___ REVISIT ------
node.SetDefaultName() ///TODO ___ REVISIT ------
node.SetLastCheckIn() //node.SetIPForwardingDefault()
node.SetLastPeerUpdate() //node.SetDNSOnDefault()
node.SetDefaultAction() //node.SetIsLocalDefault()
node.SetIsServerDefault() //node.SetLastModified()
node.SetIsStaticDefault() //node.SetDefaultName()
node.SetDefaultEgressGateway() //node.SetLastCheckIn()
node.SetDefaultIngressGateway() //node.SetLastPeerUpdate()
node.SetDefaulIsPending() //node.SetDefaultAction()
node.SetDefaultMTU() //node.SetIsServerDefault()
node.SetDefaultNFTablesPresent() //node.SetIsStaticDefault()
node.SetDefaultIsRelayed() //node.SetDefaultEgressGateway()
node.SetDefaultIsRelay() //node.SetDefaultIngressGateway()
node.SetDefaultIsDocker() //node.SetDefaulIsPending()
node.SetDefaultIsK8S() //node.SetDefaultMTU()
node.SetDefaultIsHub() //node.SetDefaultNFTablesPresent()
node.SetDefaultConnected() //node.SetDefaultIsRelayed()
node.SetDefaultACL() //node.SetDefaultIsRelay()
node.SetDefaultFailover() //node.SetDefaultIsDocker()
//node.SetDefaultIsK8S()
//node.SetDefaultIsHub()
//node.SetDefaultConnected()
//node.SetDefaultACL()
//node.SetDefaultFailover()
///TODO ___ REVISIT ------
///TODO ___ REVISIT ------
///TODO ___ REVISIT ------
///TODO ___ REVISIT ------
} }
// GetRecordKey - get record key // GetRecordKey - get record key
@@ -524,9 +390,9 @@ func GetRecordKey(id string, network string) (string, error) {
} }
// GetNodeByMacAddress - gets a node by mac address // GetNodeByMacAddress - gets a node by mac address
func GetNodeByMacAddress(network string, macaddress string) (models.LegacyNode, error) { func GetNodeByMacAddress(network string, macaddress string) (models.Node, error) {
var node models.LegacyNode var node models.Node
key, err := GetRecordKey(macaddress, network) key, err := GetRecordKey(macaddress, network)
if err != nil { if err != nil {
@@ -535,11 +401,11 @@ func GetNodeByMacAddress(network string, macaddress string) (models.LegacyNode,
record, err := database.FetchRecord(database.NODES_TABLE_NAME, key) record, err := database.FetchRecord(database.NODES_TABLE_NAME, key)
if err != nil { if err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
if err = json.Unmarshal([]byte(record), &node); err != nil { if err = json.Unmarshal([]byte(record), &node); err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
SetNodeDefaults(&node) SetNodeDefaults(&node)
@@ -548,14 +414,14 @@ func GetNodeByMacAddress(network string, macaddress string) (models.LegacyNode,
} }
// GetNodesByAddress - gets a node by mac address // GetNodesByAddress - gets a node by mac address
func GetNodesByAddress(network string, addresses []string) ([]models.LegacyNode, error) { func GetNodesByAddress(network string, addresses []string) ([]models.Node, error) {
var nodes []models.LegacyNode var nodes []models.Node
allnodes, err := GetAllNodes() allnodes, err := GetAllNodes()
if err != nil { if err != nil {
return []models.LegacyNode{}, err return []models.Node{}, err
} }
for _, node := range allnodes { for _, node := range allnodes {
if node.Network == network && ncutils.StringSliceContains(addresses, node.Address) { if node.Network == network && ncutils.StringSliceContains(addresses, node.Address.String()) {
nodes = append(nodes, node) nodes = append(nodes, node)
} }
} }
@@ -563,9 +429,9 @@ func GetNodesByAddress(network string, addresses []string) ([]models.LegacyNode,
} }
// GetDeletedNodeByMacAddress - get a deleted node // GetDeletedNodeByMacAddress - get a deleted node
func GetDeletedNodeByMacAddress(network string, macaddress string) (models.LegacyNode, error) { func GetDeletedNodeByMacAddress(network string, macaddress string) (models.Node, error) {
var node models.LegacyNode var node models.Node
key, err := GetRecordKey(macaddress, network) key, err := GetRecordKey(macaddress, network)
if err != nil { if err != nil {
@@ -574,11 +440,11 @@ func GetDeletedNodeByMacAddress(network string, macaddress string) (models.Legac
record, err := database.FetchRecord(database.DELETED_NODES_TABLE_NAME, key) record, err := database.FetchRecord(database.DELETED_NODES_TABLE_NAME, key)
if err != nil { if err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
if err = json.Unmarshal([]byte(record), &node); err != nil { if err = json.Unmarshal([]byte(record), &node); err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
SetNodeDefaults(&node) SetNodeDefaults(&node)
@@ -587,9 +453,9 @@ func GetDeletedNodeByMacAddress(network string, macaddress string) (models.Legac
} }
// GetNodeRelay - gets the relay node of a given network // GetNodeRelay - gets the relay node of a given network
func GetNodeRelay(network string, relayedNodeAddr string) (models.LegacyNode, error) { func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) {
collection, err := database.FetchRecords(database.NODES_TABLE_NAME) collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
var relay models.LegacyNode var relay models.Node
if err != nil { if err != nil {
if database.IsEmptyRecord(err) { if database.IsEmptyRecord(err) {
return relay, nil return relay, nil
@@ -603,7 +469,7 @@ func GetNodeRelay(network string, relayedNodeAddr string) (models.LegacyNode, er
logger.Log(2, err.Error()) logger.Log(2, err.Error())
continue continue
} }
if relay.IsRelay == "yes" { if relay.IsRelay {
for _, addr := range relay.RelayAddrs { for _, addr := range relay.RelayAddrs {
if addr == relayedNodeAddr { if addr == relayedNodeAddr {
return relay, nil return relay, nil
@@ -614,30 +480,30 @@ func GetNodeRelay(network string, relayedNodeAddr string) (models.LegacyNode, er
return relay, errors.New(RELAY_NODE_ERR + " " + relayedNodeAddr) return relay, errors.New(RELAY_NODE_ERR + " " + relayedNodeAddr)
} }
func GetNodeByID(uuid string) (models.LegacyNode, error) { func GetNodeByID(uuid string) (models.Node, error) {
var record, err = database.FetchRecord(database.NODES_TABLE_NAME, uuid) var record, err = database.FetchRecord(database.NODES_TABLE_NAME, uuid)
if err != nil { if err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
var node models.LegacyNode var node models.Node
if err = json.Unmarshal([]byte(record), &node); err != nil { if err = json.Unmarshal([]byte(record), &node); err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
return node, nil return node, nil
} }
// GetDeletedNodeByID - get a deleted node // GetDeletedNodeByID - get a deleted node
func GetDeletedNodeByID(uuid string) (models.LegacyNode, error) { func GetDeletedNodeByID(uuid string) (models.Node, error) {
var node models.LegacyNode var node models.Node
record, err := database.FetchRecord(database.DELETED_NODES_TABLE_NAME, uuid) record, err := database.FetchRecord(database.DELETED_NODES_TABLE_NAME, uuid)
if err != nil { if err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
if err = json.Unmarshal([]byte(record), &node); err != nil { if err = json.Unmarshal([]byte(record), &node); err != nil {
return models.LegacyNode{}, err return models.Node{}, err
} }
SetNodeDefaults(&node) SetNodeDefaults(&node)
@@ -645,81 +511,9 @@ func GetDeletedNodeByID(uuid string) (models.LegacyNode, error) {
return node, nil return node, nil
} }
// GetNetworkServerNodeID - get network server node ID if exists
func GetNetworkServerLeader(network string) (models.LegacyNode, error) {
nodes, err := GetSortedNetworkServerNodes(network)
if err != nil {
return models.LegacyNode{}, err
}
for _, node := range nodes {
if IsLeader(&node) {
return node, nil
}
}
return models.LegacyNode{}, errors.New("could not find server leader")
}
// GetNetworkServerNodeID - get network server node ID if exists
func GetNetworkServerLocal(network string) (models.LegacyNode, error) {
nodes, err := GetSortedNetworkServerNodes(network)
if err != nil {
return models.LegacyNode{}, err
}
mac := servercfg.GetNodeID()
if mac == "" {
return models.LegacyNode{}, fmt.Errorf("error retrieving local server node: server node ID is unset")
}
for _, node := range nodes {
if mac == node.MacAddress {
return node, nil
}
}
return models.LegacyNode{}, errors.New("could not find node for local server")
}
// IsLocalServer - get network server node ID if exists
func IsLocalServer(node *models.LegacyNode) bool {
var islocal bool
local, err := GetNetworkServerLocal(node.Network)
if err != nil {
return islocal
}
return node.ID != "" && local.ID == node.ID
}
// validateServer - make sure servers dont change port or address
func validateServer(currentNode, newNode *models.LegacyNode) bool {
return (newNode.Address == currentNode.Address &&
newNode.ListenPort == currentNode.ListenPort &&
newNode.IsServer == "yes")
}
// unsetHub - unset hub on network nodes
func UnsetHub(networkName string) (*models.LegacyNode, error) {
var nodesToUpdate models.LegacyNode
nodes, err := GetNetworkNodes(networkName)
if err != nil {
return &nodesToUpdate, err
}
for i := range nodes {
if nodes[i].IsHub == "yes" {
nodes[i].IsHub = "no"
nodesToUpdate = nodes[i]
newNodeData, err := json.Marshal(&nodes[i])
if err != nil {
logger.Log(1, "error on node during hub update")
return &nodesToUpdate, err
}
database.Insert(nodes[i].ID, string(newNodeData), database.NODES_TABLE_NAME)
}
}
return &nodesToUpdate, nil
}
// FindRelay - returns the node that is the relay for a relayed node // FindRelay - returns the node that is the relay for a relayed node
func FindRelay(node *models.LegacyNode) *models.LegacyNode { func FindRelay(node *models.Node) *models.Node {
if node.IsRelayed == "no" { if !node.IsRelayed {
return nil return nil
} }
peers, err := GetNetworkNodes(node.Network) peers, err := GetNetworkNodes(node.Network)
@@ -727,11 +521,11 @@ func FindRelay(node *models.LegacyNode) *models.LegacyNode {
return nil return nil
} }
for _, peer := range peers { for _, peer := range peers {
if peer.IsRelay == "no" { if !peer.IsRelay {
continue continue
} }
for _, ip := range peer.RelayAddrs { for _, ip := range peer.RelayAddrs {
if ip == node.Address || ip == node.Address6 { if ip == node.Address.IP.String() || ip == node.Address6.IP.String() {
return &peer return &peer
} }
} }
@@ -739,16 +533,16 @@ func FindRelay(node *models.LegacyNode) *models.LegacyNode {
return nil return nil
} }
func findNode(ip string) (*models.LegacyNode, error) { func findNode(ip string) (*models.Node, error) {
nodes, err := GetAllNodes() nodes, err := GetAllNodes()
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, node := range nodes { for _, node := range nodes {
if node.Address == ip { if node.Address.IP.String() == ip {
return &node, nil return &node, nil
} }
if node.Address6 == ip { if node.Address6.IP.String() == ip {
return &node, nil return &node, nil
} }
} }
@@ -756,14 +550,14 @@ func findNode(ip string) (*models.LegacyNode, error) {
} }
// GetNetworkIngresses - gets the gateways of a network // GetNetworkIngresses - gets the gateways of a network
func GetNetworkIngresses(network string) ([]models.LegacyNode, error) { func GetNetworkIngresses(network string) ([]models.Node, error) {
var ingresses []models.LegacyNode var ingresses []models.Node
netNodes, err := GetNetworkNodes(network) netNodes, err := GetNetworkNodes(network)
if err != nil { if err != nil {
return []models.LegacyNode{}, err return []models.Node{}, err
} }
for i := range netNodes { for i := range netNodes {
if netNodes[i].IsIngressGateway == "yes" { if netNodes[i].IsIngressGateway {
ingresses = append(ingresses, netNodes[i]) ingresses = append(ingresses, netNodes[i])
} }
} }
@@ -772,7 +566,7 @@ func GetNetworkIngresses(network string) ([]models.LegacyNode, error) {
// == PRO == // == PRO ==
func updateProNodeACLS(node *models.LegacyNode) error { func updateProNodeACLS(node *models.Node) error {
// == PRO node ACLs == // == PRO node ACLs ==
networkNodes, err := GetNetworkNodes(node.Network) networkNodes, err := GetNetworkNodes(node.Network)
if err != nil { if err != nil {
@@ -799,12 +593,12 @@ func PurgePendingNodes(ctx context.Context) {
} }
for _, node := range nodes { for _, node := range nodes {
if node.PendingDelete { if node.PendingDelete {
modified := time.Unix(node.LastModified, 0) modified := node.LastModified
if time.Since(modified) > NodePurgeTime { if time.Since(modified) > NodePurgeTime {
if err := DeleteNode(&node, true); err != nil { if err := DeleteNode(&node, true); err != nil {
logger.Log(0, "failed to purge node", node.ID, err.Error()) logger.Log(0, "failed to purge node", node.ID.String(), err.Error())
} else { } else {
logger.Log(0, "purged node ", node.ID) logger.Log(0, "purged node ", node.ID.String())
} }
} }

View File

@@ -21,7 +21,14 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
) )
func GetPeersForProxy(node *models.LegacyNode, onlyPeers bool) (manager.ProxyManagerPayload, error) { // GetPeersforProxy calculates the peers for a proxy
// TODO ==========================
// TODO ==========================
// TODO ==========================
// TODO ==========================
// TODO ==========================
// revisit this logic with new host/node models.
func GetPeersForProxy(node *models.Node, onlyPeers bool) (manager.ProxyManagerPayload, error) {
proxyPayload := manager.ProxyManagerPayload{} proxyPayload := manager.ProxyManagerPayload{}
var peers []wgtypes.PeerConfig var peers []wgtypes.PeerConfig
peerConfMap := make(map[string]proxy_models.PeerConf) peerConfMap := make(map[string]proxy_models.PeerConf)
@@ -31,36 +38,47 @@ func GetPeersForProxy(node *models.LegacyNode, onlyPeers bool) (manager.ProxyMan
return proxyPayload, err return proxyPayload, err
} }
if !onlyPeers { if !onlyPeers {
if node.IsRelayed == "yes" { if node.IsRelayed {
relayNode := FindRelay(node) relayNode := FindRelay(node)
if relayNode != nil { if relayNode != nil {
relayEndpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayNode.Endpoint, relayNode.LocalListenPort)) host, err := GetHost(relayNode.HostID.String())
if err != nil {
logger.Log(0, "error retrieving host for relay node", relayNode.HostID.String(), err.Error())
}
relayEndpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayNode.EndpointIP, host.LocalListenPort))
if err != nil { if err != nil {
logger.Log(1, "failed to resolve relay node endpoint: ", err.Error()) logger.Log(1, "failed to resolve relay node endpoint: ", err.Error())
} }
proxyPayload.IsRelayed = true proxyPayload.IsRelayed = true
proxyPayload.RelayedTo = relayEndpoint proxyPayload.RelayedTo = relayEndpoint
} else { } else {
logger.Log(0, "couldn't find relay node for: ", node.ID, node.PublicKey) logger.Log(0, "couldn't find relay node for: ", node.ID.String())
} }
} }
if node.IsRelay == "yes" { if node.IsRelay {
host, err := GetHost(node.HostID.String())
if err != nil {
logger.Log(0, "error retrieving host for relay node", node.ID.String(), err.Error())
}
relayedNodes, err := GetRelayedNodes(node) relayedNodes, err := GetRelayedNodes(node)
if err != nil { if err != nil {
logger.Log(1, "failed to relayed nodes: ", node.Name, err.Error()) logger.Log(1, "failed to relayed nodes: ", node.ID.String(), err.Error())
proxyPayload.IsRelay = false proxyPayload.IsRelay = false
} else { } else {
relayPeersMap := make(map[string]proxy_models.RelayedConf) relayPeersMap := make(map[string]proxy_models.RelayedConf)
for _, relayedNode := range relayedNodes { for _, relayedNode := range relayedNodes {
payload, err := GetPeersForProxy(&relayedNode, true) payload, err := GetPeersForProxy(&relayedNode, true)
if err == nil { if err == nil {
relayedEndpoint, udpErr := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayedNode.Endpoint, relayedNode.LocalListenPort)) relayedHost, err := GetHost(relayedNode.HostID.String())
if err != nil {
logger.Log(0, "error retrieving host for relayNode", relayedNode.ID.String(), err.Error())
}
relayedEndpoint, udpErr := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayedNode.EndpointIP, host.LocalListenPort))
if udpErr == nil { if udpErr == nil {
relayPeersMap[relayedNode.PublicKey] = proxy_models.RelayedConf{ relayPeersMap[host.PublicKey.String()] = proxy_models.RelayedConf{
RelayedPeerEndpoint: relayedEndpoint, RelayedPeerEndpoint: relayedEndpoint,
RelayedPeerPubKey: relayedNode.PublicKey, RelayedPeerPubKey: relayedHost.PublicKey.String(),
Peers: payload.Peers, Peers: payload.Peers,
} }
} }
@@ -79,26 +97,25 @@ func GetPeersForProxy(node *models.LegacyNode, onlyPeers bool) (manager.ProxyMan
//skip yourself //skip yourself
continue continue
} }
pubkey, err := wgtypes.ParseKey(peer.PublicKey) host, err := GetHost(peer.HostID.String())
if err != nil { if err != nil {
logger.Log(1, "failed to parse node pub key: ", peer.ID)
continue continue
} }
proxyStatus := peer.Proxy proxyStatus := peer.Proxy
listenPort := peer.LocalListenPort listenPort := host.LocalListenPort
if proxyStatus { if proxyStatus {
listenPort = peer.ProxyListenPort listenPort = host.ProxyListenPort
if listenPort == 0 { if listenPort == 0 {
listenPort = proxy_models.NmProxyPort listenPort = proxy_models.NmProxyPort
} }
} else if listenPort == 0 { } else if listenPort == 0 {
listenPort = peer.ListenPort listenPort = host.ListenPort
} }
endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", peer.Endpoint, listenPort)) endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", peer.EndpointIP, listenPort))
if err != nil { if err != nil {
logger.Log(1, "failed to resolve udp addr for node: ", peer.ID, peer.Endpoint, err.Error()) logger.Log(1, "failed to resolve udp addr for node: ", peer.ID.String(), peer.EndpointIP.String(), err.Error())
continue continue
} }
allowedips := GetAllowedIPs(node, &peer, nil, false) allowedips := GetAllowedIPs(node, &peer, nil, false)
@@ -107,34 +124,36 @@ func GetPeersForProxy(node *models.LegacyNode, onlyPeers bool) (manager.ProxyMan
// set_keepalive // set_keepalive
keepalive, _ = time.ParseDuration(strconv.FormatInt(int64(node.PersistentKeepalive), 10) + "s") keepalive, _ = time.ParseDuration(strconv.FormatInt(int64(node.PersistentKeepalive), 10) + "s")
} }
if peer.IsServer == "yes" {
proxyStatus = servercfg.IsProxyEnabled()
}
peers = append(peers, wgtypes.PeerConfig{ peers = append(peers, wgtypes.PeerConfig{
PublicKey: pubkey, PublicKey: host.PublicKey,
Endpoint: endpoint, Endpoint: endpoint,
AllowedIPs: allowedips, AllowedIPs: allowedips,
PersistentKeepaliveInterval: &keepalive, PersistentKeepaliveInterval: &keepalive,
ReplaceAllowedIPs: true, ReplaceAllowedIPs: true,
}) })
peerConfMap[peer.PublicKey] = proxy_models.PeerConf{ peerConfMap[host.PublicKey.String()] = proxy_models.PeerConf{
Address: net.ParseIP(peer.PrimaryAddress()), Address: net.ParseIP(peer.PrimaryAddress()),
Proxy: proxyStatus, Proxy: proxyStatus,
PublicListenPort: listenPort, PublicListenPort: int32(listenPort),
} }
if !onlyPeers && peer.IsRelayed == "yes" { if !onlyPeers && peer.IsRelayed {
relayNode := FindRelay(&peer) relayNode := FindRelay(&peer)
if relayNode != nil { if relayNode != nil {
relayTo, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayNode.Endpoint, relayNode.LocalListenPort)) relayHost, err := GetHost(relayNode.HostID.String())
if err != nil {
logger.Log(0, "error retrieving host for relayNode", relayNode.ID.String(), err.Error())
continue
}
relayTo, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayNode.EndpointIP, relayHost.LocalListenPort))
if err == nil { if err == nil {
peerConfMap[peer.PublicKey] = proxy_models.PeerConf{ peerConfMap[host.PublicKey.String()] = proxy_models.PeerConf{
IsRelayed: true, IsRelayed: true,
RelayedTo: relayTo, RelayedTo: relayTo,
Address: net.ParseIP(peer.PrimaryAddress()), Address: net.ParseIP(peer.PrimaryAddress()),
Proxy: proxyStatus, Proxy: proxyStatus,
PublicListenPort: listenPort, PublicListenPort: int32(listenPort),
} }
} }
@@ -142,7 +161,7 @@ func GetPeersForProxy(node *models.LegacyNode, onlyPeers bool) (manager.ProxyMan
} }
} }
if node.IsIngressGateway == "yes" { if node.IsIngressGateway {
var extPeers []wgtypes.PeerConfig var extPeers []wgtypes.PeerConfig
extPeers, peerConfMap, err = getExtPeersForProxy(node, peerConfMap) extPeers, peerConfMap, err = getExtPeersForProxy(node, peerConfMap)
if err == nil { if err == nil {
@@ -153,36 +172,31 @@ func GetPeersForProxy(node *models.LegacyNode, onlyPeers bool) (manager.ProxyMan
} }
} }
proxyPayload.IsIngress = node.IsIngressGateway == "yes" proxyPayload.IsIngress = node.IsIngressGateway
addr := node.Address addr := node.Address
if addr == "" { if addr.String() == "" {
addr = node.Address6 addr = node.Address6
} }
proxyPayload.WgAddr = addr proxyPayload.WgAddr = addr.String()
proxyPayload.Peers = peers proxyPayload.Peers = peers
proxyPayload.PeerMap = peerConfMap proxyPayload.PeerMap = peerConfMap
proxyPayload.Network = node.Network proxyPayload.Network = node.Network
proxyPayload.InterfaceName = node.Interface //proxyPayload.InterfaceName = node.Interface
//hardcode or read from host ??
proxyPayload.InterfaceName = "netmaker"
return proxyPayload, nil return proxyPayload, nil
} }
// GetPeerUpdate - gets a wireguard peer config for each peer of a node // GetPeerUpdate - gets a wireguard peer config for each peer of a node
func GetPeerUpdate(node *models.LegacyNode) (models.PeerUpdate, error) { func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
var peerUpdate models.PeerUpdate var peerUpdate models.PeerUpdate
var peers []wgtypes.PeerConfig var peers []wgtypes.PeerConfig
var serverNodeAddresses = []models.ServerAddr{} var serverNodeAddresses = []models.ServerAddr{}
var isP2S bool
network, err := GetNetwork(node.Network)
if err != nil {
return peerUpdate, err
} else if network.IsPointToSite == "yes" && node.IsHub != "yes" {
isP2S = true
}
var peerMap = make(models.PeerMap) var peerMap = make(models.PeerMap)
var metrics *models.Metrics var metrics *models.Metrics
if servercfg.Is_EE { if servercfg.Is_EE {
metrics, _ = GetMetrics(node.ID) metrics, _ = GetMetrics(node.ID.String())
} }
if metrics == nil { if metrics == nil {
metrics = &models.Metrics{} metrics = &models.Metrics{}
@@ -202,7 +216,7 @@ func GetPeerUpdate(node *models.LegacyNode) (models.PeerUpdate, error) {
return models.PeerUpdate{}, err return models.PeerUpdate{}, err
} }
if node.IsRelayed == "yes" && !node.Proxy { if node.IsRelayed && !node.Proxy {
return GetPeerUpdateForRelayedNode(node, udppeers) return GetPeerUpdateForRelayedNode(node, udppeers)
} }
@@ -214,49 +228,51 @@ func GetPeerUpdate(node *models.LegacyNode) (models.PeerUpdate, error) {
//skip yourself //skip yourself
continue continue
} }
// on point to site networks -- get peers regularily if you are the hub --- otherwise the only peer is the hub if node.Connected {
if node.NetworkSettings.IsPointToSite == "yes" && node.IsHub == "no" && peer.IsHub == "no" {
continue
}
if node.Connected != "yes" {
//skip unconnected nodes //skip unconnected nodes
continue continue
} }
// if the node is not a server, set the endpoint // if the node is not a server, set the endpoint
var setEndpoint = !(node.IsServer == "yes") var setEndpoint = true
if peer.IsRelayed == "yes" { if peer.IsRelayed {
if !peer.Proxy && !(node.IsRelay == "yes" && ncutils.StringSliceContains(node.RelayAddrs, peer.PrimaryAddress())) { if !peer.Proxy && !(node.IsRelay && ncutils.StringSliceContains(node.RelayAddrs, peer.PrimaryAddress())) {
//skip -- will be added to relay //skip -- will be added to relay
continue continue
} else if node.IsRelay == "yes" && ncutils.StringSliceContains(node.RelayAddrs, peer.PrimaryAddress()) { } else if node.IsRelay && ncutils.StringSliceContains(node.RelayAddrs, peer.PrimaryAddress()) {
// dont set peer endpoint if it's relayed by node // dont set peer endpoint if it's relayed by node
setEndpoint = false setEndpoint = false
} }
} }
if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID), nodeacls.NodeID(peer.ID)) { if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(peer.ID.String())) {
//skip if not permitted by acl //skip if not permitted by acl
continue continue
} }
if isP2S && peer.IsHub != "yes" { if len(metrics.FailoverPeers[peer.ID.String()]) > 0 && IsFailoverPresent(node.Network) {
logger.Log(2, "peer", peer.ID.String(), peer.PrimaryAddress(), "was found to be in failover peers list for node", node.ID.String(), node.PrimaryAddress())
continue continue
} }
if len(metrics.FailoverPeers[peer.ID]) > 0 && IsFailoverPresent(node.Network) {
logger.Log(2, "peer", peer.Name, peer.PrimaryAddress(), "was found to be in failover peers list for node", node.Name, node.PrimaryAddress())
continue
}
pubkey, err := wgtypes.ParseKey(peer.PublicKey)
if err != nil { if err != nil {
return models.PeerUpdate{}, err return models.PeerUpdate{}, err
} }
if node.Endpoint == peer.Endpoint { host, err := GetHost(node.HostID.String())
if err != nil {
logger.Log(0, "error retrieving host for node", node.ID.String(), err.Error())
return models.PeerUpdate{}, err
}
peerHost, err := GetHost(peer.HostID.String())
if err != nil {
logger.Log(0, "error retrieving host for peer", node.ID.String(), err.Error())
return models.PeerUpdate{}, err
}
if node.EndpointIP.String() == peer.EndpointIP.String() {
//peer is on same network //peer is on same network
// set_local // set_local
if node.LocalAddress != peer.LocalAddress && peer.LocalAddress != "" { if host.LocalAddress.String() != peerHost.LocalAddress.String() && peerHost.LocalAddress.IP != nil {
peer.Endpoint = peer.LocalAddress peer.EndpointIP = peerHost.LocalAddress.IP
if peer.LocalListenPort != 0 { if peerHost.LocalListenPort != 0 {
peer.ListenPort = peer.LocalListenPort peerHost.ListenPort = peerHost.LocalListenPort
} }
} else { } else {
continue continue
@@ -274,25 +290,25 @@ func GetPeerUpdate(node *models.LegacyNode) (models.PeerUpdate, error) {
if setEndpoint { if setEndpoint {
var setUDPPort = false var setUDPPort = false
if peer.UDPHolePunch == "yes" && errN == nil && CheckEndpoint(udppeers[peer.PublicKey]) { if CheckEndpoint(udppeers[peerHost.PublicKey.String()]) {
endpointstring := udppeers[peer.PublicKey] endpointstring := udppeers[peerHost.PublicKey.String()]
endpointarr := strings.Split(endpointstring, ":") endpointarr := strings.Split(endpointstring, ":")
if len(endpointarr) == 2 { if len(endpointarr) == 2 {
port, err := strconv.Atoi(endpointarr[1]) port, err := strconv.Atoi(endpointarr[1])
if err == nil { if err == nil {
setUDPPort = true setUDPPort = true
peer.ListenPort = int32(port) peerHost.ListenPort = port
} }
} }
} }
// if udp hole punching is on, but udp hole punching did not set it, use the LocalListenPort instead // if udp hole punching is on, but udp hole punching did not set it, use the LocalListenPort instead
// or, if port is for some reason zero use the LocalListenPort // or, if port is for some reason zero use the LocalListenPort
// but only do this if LocalListenPort is not zero // but only do this if LocalListenPort is not zero
if ((peer.UDPHolePunch == "yes" && !setUDPPort) || peer.ListenPort == 0) && peer.LocalListenPort != 0 { if ((!setUDPPort) || peerHost.ListenPort == 0) && peerHost.LocalListenPort != 0 {
peer.ListenPort = peer.LocalListenPort peerHost.ListenPort = peerHost.LocalListenPort
} }
endpoint := peer.Endpoint + ":" + strconv.FormatInt(int64(peer.ListenPort), 10) endpoint := peer.EndpointIP.String() + ":" + strconv.FormatInt(int64(peerHost.ListenPort), 10)
address, err = net.ResolveUDPAddr("udp", endpoint) address, err = net.ResolveUDPAddr("udp", endpoint)
if err != nil { if err != nil {
return models.PeerUpdate{}, err return models.PeerUpdate{}, err
@@ -309,7 +325,7 @@ func GetPeerUpdate(node *models.LegacyNode) (models.PeerUpdate, error) {
keepalive, _ = time.ParseDuration(strconv.FormatInt(int64(node.PersistentKeepalive), 10) + "s") keepalive, _ = time.ParseDuration(strconv.FormatInt(int64(node.PersistentKeepalive), 10) + "s")
} }
var peerData = wgtypes.PeerConfig{ var peerData = wgtypes.PeerConfig{
PublicKey: pubkey, PublicKey: peerHost.PublicKey,
Endpoint: address, Endpoint: address,
ReplaceAllowedIPs: true, ReplaceAllowedIPs: true,
AllowedIPs: allowedips, AllowedIPs: allowedips,
@@ -317,18 +333,15 @@ func GetPeerUpdate(node *models.LegacyNode) (models.PeerUpdate, error) {
} }
peers = append(peers, peerData) peers = append(peers, peerData)
peerMap[peer.PublicKey] = models.IDandAddr{ peerMap[peerHost.PublicKey.String()] = models.IDandAddr{
Name: peer.Name, Name: peerHost.Name,
ID: peer.ID, ID: peer.ID.String(),
Address: peer.PrimaryAddress(), Address: peer.PrimaryAddress(),
IsServer: peer.IsServer, IsServer: "no",
} }
if peer.IsServer == "yes" {
serverNodeAddresses = append(serverNodeAddresses, models.ServerAddr{IsLeader: IsLeader(&peer), Address: peer.Address})
}
} }
if node.IsIngressGateway == "yes" { if node.IsIngressGateway {
extPeers, idsAndAddr, err := getExtPeers(node, true) extPeers, idsAndAddr, err := getExtPeers(node, true)
if err == nil { if err == nil {
peers = append(peers, extPeers...) peers = append(peers, extPeers...)
@@ -349,13 +362,17 @@ func GetPeerUpdate(node *models.LegacyNode) (models.PeerUpdate, error) {
return peerUpdate, nil return peerUpdate, nil
} }
func getExtPeers(node *models.LegacyNode, forIngressNode bool) ([]wgtypes.PeerConfig, []models.IDandAddr, error) { func getExtPeers(node *models.Node, forIngressNode bool) ([]wgtypes.PeerConfig, []models.IDandAddr, error) {
var peers []wgtypes.PeerConfig var peers []wgtypes.PeerConfig
var idsAndAddr []models.IDandAddr var idsAndAddr []models.IDandAddr
extPeers, err := GetNetworkExtClients(node.Network) extPeers, err := GetNetworkExtClients(node.Network)
if err != nil { if err != nil {
return peers, idsAndAddr, err return peers, idsAndAddr, err
} }
host, err := GetHost(node.HostID.String())
if err != nil {
return peers, idsAndAddr, err
}
for _, extPeer := range extPeers { for _, extPeer := range extPeers {
pubkey, err := wgtypes.ParseKey(extPeer.PublicKey) pubkey, err := wgtypes.ParseKey(extPeer.PublicKey)
if err != nil { if err != nil {
@@ -363,7 +380,7 @@ func getExtPeers(node *models.LegacyNode, forIngressNode bool) ([]wgtypes.PeerCo
continue continue
} }
if node.PublicKey == extPeer.PublicKey { if host.PublicKey.String() == extPeer.PublicKey {
continue continue
} }
@@ -428,8 +445,12 @@ func getExtPeers(node *models.LegacyNode, forIngressNode bool) ([]wgtypes.PeerCo
} }
func getExtPeersForProxy(node *models.LegacyNode, proxyPeerConf map[string]proxy_models.PeerConf) ([]wgtypes.PeerConfig, map[string]proxy_models.PeerConf, error) { func getExtPeersForProxy(node *models.Node, proxyPeerConf map[string]proxy_models.PeerConf) ([]wgtypes.PeerConfig, map[string]proxy_models.PeerConf, error) {
var peers []wgtypes.PeerConfig var peers []wgtypes.PeerConfig
host, err := GetHost(node.HostID.String())
if err != nil {
logger.Log(0, "error retrieving host for node", node.ID.String(), err.Error())
}
extPeers, err := GetNetworkExtClients(node.Network) extPeers, err := GetNetworkExtClients(node.Network)
if err != nil { if err != nil {
@@ -442,7 +463,7 @@ func getExtPeersForProxy(node *models.LegacyNode, proxyPeerConf map[string]proxy
continue continue
} }
if node.PublicKey == extPeer.PublicKey { if host.PublicKey.String() == extPeer.PublicKey {
continue continue
} }
@@ -482,7 +503,7 @@ func getExtPeersForProxy(node *models.LegacyNode, proxyPeerConf map[string]proxy
Address: net.ParseIP(extPeer.Address), Address: net.ParseIP(extPeer.Address),
ExtInternalIp: net.ParseIP(extInternalPrimaryAddr), ExtInternalIp: net.ParseIP(extInternalPrimaryAddr),
} }
if extPeer.IngressGatewayID == node.ID { if extPeer.IngressGatewayID == node.ID.String() {
extConf.IsAttachedExtClient = true extConf.IsAttachedExtClient = true
} }
ingGatewayUdpAddr, err := net.ResolveUDPAddr("udp", extPeer.IngressGatewayEndpoint) ingGatewayUdpAddr, err := net.ResolveUDPAddr("udp", extPeer.IngressGatewayEndpoint)
@@ -499,34 +520,34 @@ func getExtPeersForProxy(node *models.LegacyNode, proxyPeerConf map[string]proxy
} }
// GetAllowedIPs - calculates the wireguard allowedip field for a peer of a node based on the peer and node settings // GetAllowedIPs - calculates the wireguard allowedip field for a peer of a node based on the peer and node settings
func GetAllowedIPs(node, peer *models.LegacyNode, metrics *models.Metrics, fetchRelayedIps bool) []net.IPNet { func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics, fetchRelayedIps bool) []net.IPNet {
var allowedips []net.IPNet var allowedips []net.IPNet
allowedips = getNodeAllowedIPs(peer, node) allowedips = getNodeAllowedIPs(peer, node)
// handle ingress gateway peers // handle ingress gateway peers
if peer.IsIngressGateway == "yes" { if peer.IsIngressGateway {
extPeers, _, err := getExtPeers(peer, false) extPeers, _, err := getExtPeers(peer, false)
if err != nil { if err != nil {
logger.Log(2, "could not retrieve ext peers for ", peer.Name, err.Error()) logger.Log(2, "could not retrieve ext peers for ", peer.ID.String(), err.Error())
} }
for _, extPeer := range extPeers { for _, extPeer := range extPeers {
allowedips = append(allowedips, extPeer.AllowedIPs...) allowedips = append(allowedips, extPeer.AllowedIPs...)
} }
// if node is a failover node, add allowed ips from nodes it is handling // if node is a failover node, add allowed ips from nodes it is handling
if metrics != nil && peer.Failover == "yes" && metrics.FailoverPeers != nil { if metrics != nil && peer.Failover && metrics.FailoverPeers != nil {
// traverse through nodes that need handling // traverse through nodes that need handling
logger.Log(3, "peer", peer.Name, "was found to be failover for", node.Name, "checking failover peers...") logger.Log(3, "peer", peer.ID.String(), "was found to be failover for", node.ID.String(), "checking failover peers...")
for k := range metrics.FailoverPeers { for k := range metrics.FailoverPeers {
// if FailoverNode is me for this node, add allowedips // if FailoverNode is me for this node, add allowedips
if metrics.FailoverPeers[k] == peer.ID { if metrics.FailoverPeers[k] == peer.ID.String() {
// get original node so we can traverse the allowed ips // get original node so we can traverse the allowed ips
nodeToFailover, err := GetNodeByID(k) nodeToFailover, err := GetNodeByID(k)
if err == nil { if err == nil {
failoverNodeMetrics, err := GetMetrics(nodeToFailover.ID) failoverNodeMetrics, err := GetMetrics(nodeToFailover.ID.String())
if err == nil && failoverNodeMetrics != nil { if err == nil && failoverNodeMetrics != nil {
if len(failoverNodeMetrics.NodeName) > 0 { if len(failoverNodeMetrics.NodeName) > 0 {
allowedips = append(allowedips, getNodeAllowedIPs(&nodeToFailover, peer)...) allowedips = append(allowedips, getNodeAllowedIPs(&nodeToFailover, peer)...)
logger.Log(0, "failing over node", nodeToFailover.Name, nodeToFailover.PrimaryAddress(), "to failover node", peer.Name) logger.Log(0, "failing over node", nodeToFailover.ID.String(), nodeToFailover.PrimaryAddress(), "to failover node", peer.ID.String())
} }
} }
} }
@@ -535,7 +556,7 @@ func GetAllowedIPs(node, peer *models.LegacyNode, metrics *models.Metrics, fetch
} }
} }
// handle relay gateway peers // handle relay gateway peers
if fetchRelayedIps && peer.IsRelay == "yes" { if fetchRelayedIps && peer.IsRelay {
for _, ip := range peer.RelayAddrs { for _, ip := range peer.RelayAddrs {
//find node ID of relayed peer //find node ID of relayed peer
relayedPeer, err := findNode(ip) relayedPeer, err := findNode(ip)
@@ -551,7 +572,7 @@ func GetAllowedIPs(node, peer *models.LegacyNode, metrics *models.Metrics, fetch
continue continue
} }
//check if acl permits comms //check if acl permits comms
if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID), nodeacls.NodeID(relayedPeer.ID)) { if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(relayedPeer.ID.String())) {
continue continue
} }
if iplib.Version(net.ParseIP(ip)) == 4 { if iplib.Version(net.ParseIP(ip)) == 4 {
@@ -573,11 +594,11 @@ func GetAllowedIPs(node, peer *models.LegacyNode, metrics *models.Metrics, fetch
logger.Log(1, "unable to find node for relayed address", ip, err.Error()) logger.Log(1, "unable to find node for relayed address", ip, err.Error())
continue continue
} }
if relayedNode.IsEgressGateway == "yes" { if relayedNode.IsEgressGateway {
extAllowedIPs := getEgressIPs(node, relayedNode) extAllowedIPs := getEgressIPs(node, relayedNode)
allowedips = append(allowedips, extAllowedIPs...) allowedips = append(allowedips, extAllowedIPs...)
} }
if relayedNode.IsIngressGateway == "yes" { if relayedNode.IsIngressGateway {
extPeers, _, err := getExtPeers(relayedNode, false) extPeers, _, err := getExtPeers(relayedNode, false)
if err == nil { if err == nil {
for _, extPeer := range extPeers { for _, extPeer := range extPeers {
@@ -595,8 +616,12 @@ func GetAllowedIPs(node, peer *models.LegacyNode, metrics *models.Metrics, fetch
func getPeerDNS(network string) string { func getPeerDNS(network string) string {
var dns string var dns string
if nodes, err := GetNetworkNodes(network); err == nil { if nodes, err := GetNetworkNodes(network); err == nil {
for i := range nodes { for i, node := range nodes {
dns = dns + fmt.Sprintf("%s %s.%s\n", nodes[i].Address, nodes[i].Name, nodes[i].Network) host, err := GetHost(node.HostID.String())
if err != nil {
logger.Log(0, "error retrieving host for node", node.ID.String(), err.Error())
}
dns = dns + fmt.Sprintf("%s %s.%s\n", nodes[i].Address, host.Name, nodes[i].Network)
} }
} }
@@ -611,7 +636,7 @@ func getPeerDNS(network string) string {
// GetPeerUpdateForRelayedNode - calculates peer update for a relayed node by getting the relay // GetPeerUpdateForRelayedNode - calculates peer update for a relayed node by getting the relay
// copying the relay node's allowed ips and making appropriate substitutions // copying the relay node's allowed ips and making appropriate substitutions
func GetPeerUpdateForRelayedNode(node *models.LegacyNode, udppeers map[string]string) (models.PeerUpdate, error) { func GetPeerUpdateForRelayedNode(node *models.Node, udppeers map[string]string) (models.PeerUpdate, error) {
var peerUpdate models.PeerUpdate var peerUpdate models.PeerUpdate
var peers []wgtypes.PeerConfig var peers []wgtypes.PeerConfig
var serverNodeAddresses = []models.ServerAddr{} var serverNodeAddresses = []models.ServerAddr{}
@@ -623,18 +648,12 @@ func GetPeerUpdateForRelayedNode(node *models.LegacyNode, udppeers map[string]st
} }
//add relay to lists of allowed ip //add relay to lists of allowed ip
if relay.Address != "" { if relay.Address.IP != nil {
relayIP := net.IPNet{ relayIP := relay.Address
IP: net.ParseIP(relay.Address),
Mask: net.CIDRMask(32, 32),
}
allowedips = append(allowedips, relayIP) allowedips = append(allowedips, relayIP)
} }
if relay.Address6 != "" { if relay.Address6.IP != nil {
relayIP6 := net.IPNet{ relayIP6 := relay.Address6
IP: net.ParseIP(relay.Address6),
Mask: net.CIDRMask(128, 128),
}
allowedips = append(allowedips, relayIP6) allowedips = append(allowedips, relayIP6)
} }
//get PeerUpdate for relayed node //get PeerUpdate for relayed node
@@ -657,19 +676,19 @@ func GetPeerUpdateForRelayedNode(node *models.LegacyNode, udppeers map[string]st
logger.Log(0, "failed to find node for ip", allowedips[i].IP.String()) logger.Log(0, "failed to find node for ip", allowedips[i].IP.String())
continue continue
} }
if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID), nodeacls.NodeID(target.ID)) { if !nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(target.ID.String())) {
logger.Log(0, "deleting node from relayednode per acl", node.Name, target.Name) logger.Log(0, "deleting node from relayednode per acl", node.ID.String(), target.ID.String())
allowedips = append(allowedips[:i], allowedips[i+1:]...) allowedips = append(allowedips[:i], allowedips[i+1:]...)
} }
} }
//delete self from allowed ips //delete self from allowed ips
for i := len(allowedips) - 1; i >= 0; i-- { for i := len(allowedips) - 1; i >= 0; i-- {
if allowedips[i].IP.String() == node.Address || allowedips[i].IP.String() == node.Address6 { if allowedips[i].IP.String() == node.Address.IP.String() || allowedips[i].IP.String() == node.Address6.IP.String() {
allowedips = append(allowedips[:i], allowedips[i+1:]...) allowedips = append(allowedips[:i], allowedips[i+1:]...)
} }
} }
//delete egressrange from allowedip if we are egress gateway //delete egressrange from allowedip if we are egress gateway
if node.IsEgressGateway == "yes" { if node.IsEgressGateway {
for i := len(allowedips) - 1; i >= 0; i-- { for i := len(allowedips) - 1; i >= 0; i-- {
if StringSliceContains(node.EgressGatewayRanges, allowedips[i].String()) { if StringSliceContains(node.EgressGatewayRanges, allowedips[i].String()) {
allowedips = append(allowedips[:i], allowedips[i+1:]...) allowedips = append(allowedips[:i], allowedips[i+1:]...)
@@ -677,7 +696,7 @@ func GetPeerUpdateForRelayedNode(node *models.LegacyNode, udppeers map[string]st
} }
} }
//delete extclients from allowedip if we are ingress gateway //delete extclients from allowedip if we are ingress gateway
if node.IsIngressGateway == "yes" { if node.IsIngressGateway {
for i := len(allowedips) - 1; i >= 0; i-- { for i := len(allowedips) - 1; i >= 0; i-- {
if strings.Contains(node.IngressGatewayRange, allowedips[i].IP.String()) { if strings.Contains(node.IngressGatewayRange, allowedips[i].IP.String()) {
allowedips = append(allowedips[:i], allowedips[i+1:]...) allowedips = append(allowedips[:i], allowedips[i+1:]...)
@@ -685,7 +704,7 @@ func GetPeerUpdateForRelayedNode(node *models.LegacyNode, udppeers map[string]st
} }
} }
//add egress range if relay is egress //add egress range if relay is egress
if relay.IsEgressGateway == "yes" { if relay.IsEgressGateway {
var ip *net.IPNet var ip *net.IPNet
for _, cidr := range relay.EgressGatewayRanges { for _, cidr := range relay.EgressGatewayRanges {
_, ip, err = net.ParseCIDR(cidr) _, ip, err = net.ParseCIDR(cidr)
@@ -695,31 +714,34 @@ func GetPeerUpdateForRelayedNode(node *models.LegacyNode, udppeers map[string]st
} }
allowedips = append(allowedips, *ip) allowedips = append(allowedips, *ip)
} }
relayHost, err := GetHost(relay.HostID.String())
pubkey, err := wgtypes.ParseKey(relay.PublicKey) if err != nil {
logger.Log(0, "error retrieving host for relay node", node.ID.String(), err.Error())
}
if err != nil { if err != nil {
return models.PeerUpdate{}, err return models.PeerUpdate{}, err
} }
var setUDPPort = false var setUDPPort = false
if relay.UDPHolePunch == "yes" && CheckEndpoint(udppeers[relay.PublicKey]) { var listenPort int
endpointstring := udppeers[relay.PublicKey] if CheckEndpoint(udppeers[relayHost.PublicKey.String()]) {
endpointstring := udppeers[relayHost.PublicKey.String()]
endpointarr := strings.Split(endpointstring, ":") endpointarr := strings.Split(endpointstring, ":")
if len(endpointarr) == 2 { if len(endpointarr) == 2 {
port, err := strconv.Atoi(endpointarr[1]) port, err := strconv.Atoi(endpointarr[1])
if err == nil { if err == nil {
setUDPPort = true setUDPPort = true
relay.ListenPort = int32(port) listenPort = port
} }
} }
} }
// if udp hole punching is on, but udp hole punching did not set it, use the LocalListenPort instead // if udp hole punching is on, but udp hole punching did not set it, use the LocalListenPort instead
// or, if port is for some reason zero use the LocalListenPort // or, if port is for some reason zero use the LocalListenPort
// but only do this if LocalListenPort is not zero // but only do this if LocalListenPort is not zero
if ((relay.UDPHolePunch == "yes" && !setUDPPort) || relay.ListenPort == 0) && relay.LocalListenPort != 0 { if ((!setUDPPort) || relayHost.ListenPort == 0) && relayHost.LocalListenPort != 0 {
relay.ListenPort = relay.LocalListenPort listenPort = relayHost.LocalListenPort
} }
endpoint := relay.Endpoint + ":" + strconv.FormatInt(int64(relay.ListenPort), 10) endpoint := relay.EndpointIP.String() + ":" + strconv.FormatInt(int64(listenPort), 10)
address, err := net.ResolveUDPAddr("udp", endpoint) address, err := net.ResolveUDPAddr("udp", endpoint)
if err != nil { if err != nil {
return models.PeerUpdate{}, err return models.PeerUpdate{}, err
@@ -730,23 +752,20 @@ func GetPeerUpdateForRelayedNode(node *models.LegacyNode, udppeers map[string]st
keepalive, _ = time.ParseDuration(strconv.FormatInt(int64(node.PersistentKeepalive), 10) + "s") keepalive, _ = time.ParseDuration(strconv.FormatInt(int64(node.PersistentKeepalive), 10) + "s")
} }
var peerData = wgtypes.PeerConfig{ var peerData = wgtypes.PeerConfig{
PublicKey: pubkey, PublicKey: relayHost.PublicKey,
Endpoint: address, Endpoint: address,
ReplaceAllowedIPs: true, ReplaceAllowedIPs: true,
AllowedIPs: allowedips, AllowedIPs: allowedips,
PersistentKeepaliveInterval: &keepalive, PersistentKeepaliveInterval: &keepalive,
} }
peers = append(peers, peerData) peers = append(peers, peerData)
if relay.IsServer == "yes" {
serverNodeAddresses = append(serverNodeAddresses, models.ServerAddr{IsLeader: IsLeader(relay), Address: relay.Address})
}
//if ingress add extclients //if ingress add extclients
if node.IsIngressGateway == "yes" { if node.IsIngressGateway {
extPeers, _, err := getExtPeers(node, true) extPeers, _, err := getExtPeers(node, true)
if err == nil { if err == nil {
peers = append(peers, extPeers...) peers = append(peers, extPeers...)
} else { } else {
logger.Log(2, "could not retrieve ext peers for ", node.Name, err.Error()) logger.Log(2, "could not retrieve ext peers for ", node.ID.String(), err.Error())
} }
} }
peerUpdate.Network = node.Network peerUpdate.Network = node.Network
@@ -757,7 +776,11 @@ func GetPeerUpdateForRelayedNode(node *models.LegacyNode, udppeers map[string]st
return peerUpdate, nil return peerUpdate, nil
} }
func getEgressIPs(node, peer *models.LegacyNode) []net.IPNet { 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())
}
//check for internet gateway //check for internet gateway
internetGateway := false internetGateway := false
if slices.Contains(peer.EgressGatewayRanges, "0.0.0.0/0") || slices.Contains(peer.EgressGatewayRanges, "::/0") { if slices.Contains(peer.EgressGatewayRanges, "0.0.0.0/0") || slices.Contains(peer.EgressGatewayRanges, "::/0") {
@@ -770,14 +793,14 @@ func getEgressIPs(node, peer *models.LegacyNode) []net.IPNet {
logger.Log(1, "could not parse gateway IP range. Not adding ", iprange) logger.Log(1, "could not parse gateway IP range. Not adding ", iprange)
continue // if can't parse CIDR continue // if can't parse CIDR
} }
nodeEndpointArr := strings.Split(peer.Endpoint, ":") // getting the public ip of node nodeEndpointArr := strings.Split(peer.EndpointIP.String(), ":") // getting the public ip of node
if ipnet.Contains(net.ParseIP(nodeEndpointArr[0])) && !internetGateway { // ensuring egress gateway range does not contain endpoint of node if ipnet.Contains(net.ParseIP(nodeEndpointArr[0])) && !internetGateway { // ensuring egress gateway range does not contain endpoint of node
logger.Log(2, "egress IP range of ", iprange, " overlaps with ", node.Endpoint, ", omitting") logger.Log(2, "egress IP range of ", iprange, " overlaps with ", node.EndpointIP.String(), ", omitting")
continue // skip adding egress range if overlaps with node's ip 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 // TODO: Could put in a lot of great logic to avoid conflicts / bad routes
if ipnet.Contains(net.ParseIP(node.LocalAddress)) && !internetGateway { // ensuring egress gateway range does not contain public ip of node if ipnet.Contains(net.ParseIP(host.LocalAddress.String())) && !internetGateway { // ensuring egress gateway range does not contain public ip of node
logger.Log(2, "egress IP range of ", iprange, " overlaps with ", node.LocalAddress, ", omitting") logger.Log(2, "egress IP range of ", iprange, " overlaps with ", host.LocalAddress.String(), ", omitting")
continue // skip adding egress range if overlaps with node's local ip continue // skip adding egress range if overlaps with node's local ip
} }
if err != nil { if err != nil {
@@ -789,42 +812,34 @@ func getEgressIPs(node, peer *models.LegacyNode) []net.IPNet {
return allowedips return allowedips
} }
func getNodeAllowedIPs(peer, node *models.LegacyNode) []net.IPNet { func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet {
var allowedips = []net.IPNet{} var allowedips = []net.IPNet{}
if peer.Address != "" { if peer.Address.IP != nil {
var peeraddr = net.IPNet{ allowedips = append(allowedips, peer.Address)
IP: net.ParseIP(peer.Address),
Mask: net.CIDRMask(32, 32),
}
allowedips = append(allowedips, peeraddr)
} }
if peer.Address6 != "" { if peer.Address6.IP != nil {
var addr6 = net.IPNet{ allowedips = append(allowedips, peer.Address6)
IP: net.ParseIP(peer.Address6),
Mask: net.CIDRMask(128, 128),
}
allowedips = append(allowedips, addr6)
} }
// handle manually set peers // handle manually set peers
for _, allowedIp := range peer.AllowedIPs { for _, allowedIp := range peer.AllowedIPs {
// parsing as a CIDR first. If valid CIDR, append // parsing as a CIDR first. If valid CIDR, append
if _, ipnet, err := net.ParseCIDR(allowedIp); err == nil { if _, ipnet, err := net.ParseCIDR(allowedIp); err == nil {
nodeEndpointArr := strings.Split(node.Endpoint, ":") nodeEndpointArr := strings.Split(node.EndpointIP.String(), ":")
if !ipnet.Contains(net.IP(nodeEndpointArr[0])) && ipnet.IP.String() != peer.Address { // don't need to add an allowed ip that already exists.. if !ipnet.Contains(net.IP(nodeEndpointArr[0])) && ipnet.IP.String() != peer.Address.IP.String() { // don't need to add an allowed ip that already exists..
allowedips = append(allowedips, *ipnet) allowedips = append(allowedips, *ipnet)
} }
} else { // parsing as an IP second. If valid IP, check if ipv4 or ipv6, then append } else { // parsing as an IP second. If valid IP, check if ipv4 or ipv6, then append
if iplib.Version(net.ParseIP(allowedIp)) == 4 && allowedIp != peer.Address { if iplib.Version(net.ParseIP(allowedIp)) == 4 && allowedIp != peer.Address.IP.String() {
ipnet := net.IPNet{ ipnet := net.IPNet{
IP: net.ParseIP(allowedIp), IP: net.ParseIP(allowedIp),
Mask: net.CIDRMask(32, 32), Mask: net.CIDRMask(32, 32),
} }
allowedips = append(allowedips, ipnet) allowedips = append(allowedips, ipnet)
} else if iplib.Version(net.ParseIP(allowedIp)) == 6 && allowedIp != peer.Address6 { } else if iplib.Version(net.ParseIP(allowedIp)) == 6 && allowedIp != peer.Address6.IP.String() {
ipnet := net.IPNet{ ipnet := net.IPNet{
IP: net.ParseIP(allowedIp), IP: net.ParseIP(allowedIp),
Mask: net.CIDRMask(128, 128), Mask: net.CIDRMask(128, 128),
@@ -834,17 +849,9 @@ func getNodeAllowedIPs(peer, node *models.LegacyNode) []net.IPNet {
} }
} }
// handle egress gateway peers // handle egress gateway peers
if peer.IsEgressGateway == "yes" { if peer.IsEgressGateway {
//hasGateway = true //hasGateway = true
egressIPs := getEgressIPs(node, peer) egressIPs := getEgressIPs(node, peer)
// remove internet gateway if server
if node.IsServer == "yes" {
for i := len(egressIPs) - 1; i >= 0; i-- {
if egressIPs[i].String() == "0.0.0.0/0" || egressIPs[i].String() == "::/0" {
egressIPs = append(egressIPs[:i], egressIPs[i+1:]...)
}
}
}
allowedips = append(allowedips, egressIPs...) allowedips = append(allowedips, egressIPs...)
} }
return allowedips return allowedips

View File

@@ -175,7 +175,7 @@ func RemoveAllNetworkUsers(network string) error {
// IsUserNodeAllowed - given a list of nodes, determine if the user's node is allowed based on ID // IsUserNodeAllowed - given a list of nodes, determine if the user's node is allowed based on ID
// Checks if node is in given nodes list as well as being in user's list // Checks if node is in given nodes list as well as being in user's list
func IsUserNodeAllowed(nodes []models.LegacyNode, network, userID, nodeID string) bool { func IsUserNodeAllowed(nodes []models.Node, network, userID, nodeID string) bool {
netUser, err := GetNetworkUser(network, promodels.NetworkUserID(userID)) netUser, err := GetNetworkUser(network, promodels.NetworkUserID(userID))
if err != nil { if err != nil {
@@ -183,7 +183,7 @@ func IsUserNodeAllowed(nodes []models.LegacyNode, network, userID, nodeID string
} }
for i := range nodes { for i := range nodes {
if nodes[i].ID == nodeID { if nodes[i].ID.String() == nodeID {
for j := range netUser.Nodes { for j := range netUser.Nodes {
if netUser.Nodes[j] == nodeID { if netUser.Nodes[j] == nodeID {
return true return true

View File

@@ -7,16 +7,16 @@ import (
) )
// AdjustNodeAcls - adjusts ACLs based on a node's default value // AdjustNodeAcls - adjusts ACLs based on a node's default value
func AdjustNodeAcls(node *models.LegacyNode, networkNodes []models.LegacyNode) error { func AdjustNodeAcls(node *models.Node, networkNodes []models.Node) error {
networkID := nodeacls.NetworkID(node.Network) networkID := nodeacls.NetworkID(node.Network)
nodeID := nodeacls.NodeID(node.ID) nodeID := nodeacls.NodeID(node.ID.String())
currentACLs, err := nodeacls.FetchAllACLs(networkID) currentACLs, err := nodeacls.FetchAllACLs(networkID)
if err != nil { if err != nil {
return err return err
} }
for i := range networkNodes { for i := range networkNodes {
currentNodeID := nodeacls.NodeID(networkNodes[i].ID) currentNodeID := nodeacls.NodeID(networkNodes[i].ID.String())
if currentNodeID == nodeID { if currentNodeID == nodeID {
continue continue
} }

View File

@@ -12,21 +12,25 @@ import (
) )
// CreateRelay - creates a relay // CreateRelay - creates a relay
func CreateRelay(relay models.RelayRequest) ([]models.LegacyNode, models.LegacyNode, error) { func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error) {
var returnnodes []models.LegacyNode var returnnodes []models.Node
node, err := GetNodeByID(relay.NodeID) node, err := GetNodeByID(relay.NodeID)
if err != nil { if err != nil {
return returnnodes, models.LegacyNode{}, err return returnnodes, models.Node{}, err
} }
if node.OS != "linux" { host, err := GetHost(node.ID.String())
return returnnodes, models.LegacyNode{}, fmt.Errorf("only linux machines can be relay nodes") if err != nil {
return returnnodes, models.Node{}, err
}
if host.OS != "linux" {
return returnnodes, models.Node{}, fmt.Errorf("only linux machines can be relay nodes")
} }
err = ValidateRelay(relay) err = ValidateRelay(relay)
if err != nil { if err != nil {
return returnnodes, models.LegacyNode{}, err return returnnodes, models.Node{}, err
} }
node.IsRelay = "yes" node.IsRelay = true
node.RelayAddrs = relay.RelayAddrs node.RelayAddrs = relay.RelayAddrs
node.SetLastModified() node.SetLastModified()
@@ -34,8 +38,8 @@ func CreateRelay(relay models.RelayRequest) ([]models.LegacyNode, models.LegacyN
if err != nil { if err != nil {
return returnnodes, node, err return returnnodes, node, err
} }
if err = database.Insert(node.ID, string(nodeData), database.NODES_TABLE_NAME); err != nil { if err = database.Insert(node.ID.String(), string(nodeData), database.NODES_TABLE_NAME); err != nil {
return returnnodes, models.LegacyNode{}, err return returnnodes, models.Node{}, err
} }
returnnodes, err = SetRelayedNodes(true, node.Network, node.RelayAddrs) returnnodes, err = SetRelayedNodes(true, node.Network, node.RelayAddrs)
if err != nil { if err != nil {
@@ -45,45 +49,41 @@ func CreateRelay(relay models.RelayRequest) ([]models.LegacyNode, models.LegacyN
} }
// SetRelayedNodes- set relayed nodes // SetRelayedNodes- set relayed nodes
func SetRelayedNodes(setRelayed bool, networkName string, addrs []string) ([]models.LegacyNode, error) { func SetRelayedNodes(setRelayed bool, networkName string, addrs []string) ([]models.Node, error) {
var returnnodes []models.LegacyNode var returnnodes []models.Node
networkNodes, err := GetNetworkNodes(networkName) networkNodes, err := GetNetworkNodes(networkName)
if err != nil { if err != nil {
return returnnodes, err return returnnodes, err
} }
for _, node := range networkNodes { for _, node := range networkNodes {
if node.IsServer != "yes" { for _, addr := range addrs {
for _, addr := range addrs { if addr == node.Address.IP.String() || addr == node.Address6.IP.String() {
if addr == node.Address || addr == node.Address6 { if setRelayed {
if setRelayed { node.IsRelayed = true
node.IsRelayed = "yes" } else {
} else { node.IsRelayed = false
node.IsRelayed = "no"
}
data, err := json.Marshal(&node)
if err != nil {
return returnnodes, err
}
database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
returnnodes = append(returnnodes, node)
} }
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)
} }
} }
} }
return returnnodes, nil return returnnodes, nil
} }
func GetRelayedNodes(relayNode *models.LegacyNode) ([]models.LegacyNode, error) { func GetRelayedNodes(relayNode *models.Node) ([]models.Node, error) {
var returnnodes []models.LegacyNode var returnnodes []models.Node
networkNodes, err := GetNetworkNodes(relayNode.Network) networkNodes, err := GetNetworkNodes(relayNode.Network)
if err != nil { if err != nil {
return returnnodes, err return returnnodes, err
} }
for _, node := range networkNodes { for _, node := range networkNodes {
if node.IsServer != "yes" { for _, addr := range relayNode.RelayAddrs {
for _, addr := range relayNode.RelayAddrs { if addr == node.Address.IP.String() || addr == node.Address6.IP.String() {
if addr == node.Address || addr == node.Address6 { returnnodes = append(returnnodes, node)
returnnodes = append(returnnodes, node)
}
} }
} }
} }
@@ -102,8 +102,8 @@ func ValidateRelay(relay models.RelayRequest) error {
} }
// UpdateRelay - updates a relay // UpdateRelay - updates a relay
func UpdateRelay(network string, oldAddrs []string, newAddrs []string) []models.LegacyNode { func UpdateRelay(network string, oldAddrs []string, newAddrs []string) []models.Node {
var returnnodes []models.LegacyNode var returnnodes []models.Node
time.Sleep(time.Second / 4) time.Sleep(time.Second / 4)
_, err := SetRelayedNodes(false, network, oldAddrs) _, err := SetRelayedNodes(false, network, oldAddrs)
if err != nil { if err != nil {
@@ -117,27 +117,27 @@ func UpdateRelay(network string, oldAddrs []string, newAddrs []string) []models.
} }
// DeleteRelay - deletes a relay // DeleteRelay - deletes a relay
func DeleteRelay(network, nodeid string) ([]models.LegacyNode, models.LegacyNode, error) { func DeleteRelay(network, nodeid string) ([]models.Node, models.Node, error) {
var returnnodes []models.LegacyNode var returnnodes []models.Node
node, err := GetNodeByID(nodeid) node, err := GetNodeByID(nodeid)
if err != nil { if err != nil {
return returnnodes, models.LegacyNode{}, err return returnnodes, models.Node{}, err
} }
returnnodes, err = SetRelayedNodes(false, node.Network, node.RelayAddrs) returnnodes, err = SetRelayedNodes(false, node.Network, node.RelayAddrs)
if err != nil { if err != nil {
return returnnodes, node, err return returnnodes, node, err
} }
node.IsRelay = "no" node.IsRelay = false
node.RelayAddrs = []string{} node.RelayAddrs = []string{}
node.SetLastModified() node.SetLastModified()
data, err := json.Marshal(&node) data, err := json.Marshal(&node)
if err != nil { if err != nil {
return returnnodes, models.LegacyNode{}, err return returnnodes, models.Node{}, err
} }
if err = database.Insert(nodeid, string(data), database.NODES_TABLE_NAME); err != nil { if err = database.Insert(nodeid, string(data), database.NODES_TABLE_NAME); err != nil {
return returnnodes, models.LegacyNode{}, err return returnnodes, models.Node{}, err
} }
return returnnodes, node, nil return returnnodes, node, nil
} }

View File

@@ -1,22 +1,10 @@
package logic package logic
import ( import (
"encoding/json"
"errors"
"fmt"
"net"
"os"
"runtime"
"strings" "strings"
"github.com/gravitl/netclient/nmproxy/manager" "github.com/gravitl/netclient/nmproxy/manager"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/wireguard"
"github.com/gravitl/netmaker/servercfg"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
) )
var ProxyMgmChan = make(chan *manager.ProxyManagerPayload, 100) var ProxyMgmChan = make(chan *manager.ProxyManagerPayload, 100)
@@ -25,7 +13,7 @@ var ProxyMgmChan = make(chan *manager.ProxyManagerPayload, 100)
var EnterpriseCheckFuncs []func() var EnterpriseCheckFuncs []func()
// EnterpriseFailoverFunc - interface to control failover funcs // EnterpriseFailoverFunc - interface to control failover funcs
var EnterpriseFailoverFunc func(node *models.LegacyNode) error var EnterpriseFailoverFunc func(node *models.Node) error
// EnterpriseResetFailoverFunc - interface to control reset failover funcs // EnterpriseResetFailoverFunc - interface to control reset failover funcs
var EnterpriseResetFailoverFunc func(network string) error var EnterpriseResetFailoverFunc func(network string) error
@@ -41,153 +29,6 @@ const KUBERNETES_LISTEN_PORT = 31821
// KUBERNETES_SERVER_MTU - ideal mtu for kubernetes deployments right now // KUBERNETES_SERVER_MTU - ideal mtu for kubernetes deployments right now
const KUBERNETES_SERVER_MTU = 1024 const KUBERNETES_SERVER_MTU = 1024
// ServerJoin - responsible for joining a server to a network
func ServerJoin(networkSettings *models.Network) (models.LegacyNode, error) {
var returnNode models.LegacyNode
if networkSettings == nil || networkSettings.NetID == "" {
return returnNode, errors.New("no network provided")
}
var err error
var currentServers = GetServerNodes(networkSettings.NetID)
var serverCount = 1
if currentServers != nil {
serverCount = len(currentServers) + 1
}
var ishub = "no"
if networkSettings.IsPointToSite == "yes" {
nodes, err := GetNetworkNodes(networkSettings.NetID)
if err != nil || nodes == nil {
ishub = "yes"
} else {
sethub := true
for i := range nodes {
if nodes[i].IsHub == "yes" {
sethub = false
}
}
if sethub {
ishub = "yes"
}
}
}
var node = &models.LegacyNode{
IsServer: "yes",
DNSOn: "no",
IsStatic: "yes",
Name: fmt.Sprintf("%s-%d", models.NODE_SERVER_NAME, serverCount),
MacAddress: servercfg.GetNodeID(),
ID: "", // will be set to new uuid
UDPHolePunch: "no",
IsLocal: networkSettings.IsLocal,
LocalRange: networkSettings.LocalRange,
OS: runtime.GOOS,
Version: servercfg.Version,
IsHub: ishub,
NetworkSettings: *networkSettings,
}
SetNodeDefaults(node)
if servercfg.GetPlatform() == "Kubernetes" {
node.ListenPort = KUBERNETES_LISTEN_PORT
node.MTU = KUBERNETES_SERVER_MTU
}
if node.LocalRange != "" && node.LocalAddress == "" {
logger.Log(1, "local vpn, getting local address from range:", networkSettings.LocalRange)
node.LocalAddress, err = getServerLocalIP(networkSettings)
if err != nil {
node.LocalAddress = ""
node.IsLocal = "no"
}
}
if node.Endpoint == "" {
if node.IsLocal == "yes" && node.LocalAddress != "" {
node.Endpoint = node.LocalAddress
} else {
node.Endpoint, err = servercfg.GetPublicIP()
}
if err != nil || node.Endpoint == "" {
logger.Log(0, "Error setting server node Endpoint.")
return returnNode, err
}
}
var privateKey = ""
// Generate and set public/private WireGuard Keys
if privateKey == "" {
wgPrivatekey, err := wgtypes.GeneratePrivateKey()
if err != nil {
logger.Log(1, err.Error())
return returnNode, err
}
privateKey = wgPrivatekey.String()
node.PublicKey = wgPrivatekey.PublicKey().String()
}
node.Network = networkSettings.NetID
logger.Log(2, "adding a server instance on network", node.Network)
if err != nil {
return returnNode, err
}
err = SetNetworkNodesLastModified(node.Network)
if err != nil {
return returnNode, err
}
// get free port based on returned default listen port
node.ListenPort, err = ncutils.GetFreePort(node.ListenPort)
if err != nil {
logger.Log(2, "Error retrieving port:", err.Error())
} else {
logger.Log(1, "Set client port to", fmt.Sprintf("%d", node.ListenPort), "for network", node.Network)
}
// safety check. If returned node from server is local, but not currently configured as local, set to local addr
if node.IsLocal == "yes" && node.LocalRange != "" {
node.LocalAddress, err = ncutils.GetLocalIP(node.LocalRange)
if err != nil {
return returnNode, err
}
node.Endpoint = node.LocalAddress
}
if err = CreateNode(node); err != nil {
return returnNode, err
}
if err = StorePrivKey(node.ID, privateKey); err != nil {
return returnNode, err
}
peers, err := GetPeerUpdate(node)
if err != nil && !ncutils.IsEmptyRecord(err) {
logger.Log(1, "failed to retrieve peers")
return returnNode, err
}
err = wireguard.InitWireguard(node, privateKey, peers.Peers)
if err != nil {
return returnNode, err
}
if servercfg.IsProxyEnabled() {
proxyPayload, err := GetPeersForProxy(node, false)
if err != nil && !ncutils.IsEmptyRecord(err) {
logger.Log(1, "failed to retrieve peers")
return returnNode, err
}
ProxyMgmChan <- &proxyPayload
}
return *node, nil
}
// EnterpriseCheck - Runs enterprise functions if presented // EnterpriseCheck - Runs enterprise functions if presented
func EnterpriseCheck() { func EnterpriseCheck() {
for _, check := range EnterpriseCheckFuncs { for _, check := range EnterpriseCheckFuncs {
@@ -195,163 +36,8 @@ func EnterpriseCheck() {
} }
} }
// ServerUpdate - updates the server
// replaces legacy Checkin code
func ServerUpdate(serverNode *models.LegacyNode, ifaceDelta bool) error {
if !IsLocalServer(serverNode) {
logger.Log(1, "skipping server update as not the leader")
return nil
}
var err = ServerPull(serverNode, ifaceDelta)
if isDeleteError(err) {
return DeleteNode(serverNode, true)
} else if err != nil && !ifaceDelta {
err = ServerPull(serverNode, true)
if err != nil {
return err
}
}
actionCompleted := checkNodeActions(serverNode)
if actionCompleted == models.NODE_DELETE {
return errors.New("node has been removed")
}
return serverPush(serverNode)
}
// == Private == // == Private ==
func isDeleteError(err error) bool { func isDeleteError(err error) bool {
return err != nil && strings.Contains(err.Error(), models.NODE_DELETE) return err != nil && strings.Contains(err.Error(), models.NODE_DELETE)
} }
func checkNodeActions(node *models.LegacyNode) string {
if node.Action == models.NODE_UPDATE_KEY {
err := setWGKeyConfig(node)
if err != nil {
logger.Log(1, "unable to process reset keys request:", err.Error())
return ""
}
}
if node.Action == models.NODE_DELETE {
err := DeleteNode(node, true)
if err != nil {
logger.Log(1, "error deleting locally:", err.Error())
}
return models.NODE_DELETE
}
return ""
}
// == Private ==
// ServerPull - performs a server pull
func ServerPull(serverNode *models.LegacyNode, ifaceDelta bool) error {
if serverNode.IsServer != "yes" {
return fmt.Errorf("attempted pull from non-server node: %s - %s", serverNode.Name, serverNode.ID)
}
var err error
if serverNode.IPForwarding == "yes" {
if err = setIPForwardingLinux(); err != nil {
return err
}
}
serverNode.OS = runtime.GOOS
if ifaceDelta {
// check for interface change
// checks if address is in use by another interface
var oldIfaceName, isIfacePresent = isInterfacePresent(serverNode.Interface, serverNode.Address)
if !isIfacePresent {
if err = deleteInterface(oldIfaceName, serverNode.PostDown); err != nil {
logger.Log(1, "could not delete old interface", oldIfaceName)
}
logger.Log(1, "removed old interface", oldIfaceName)
}
if err = setWGConfig(serverNode, false); err != nil {
return err
}
// handle server side update
if err = UpdateNode(serverNode, serverNode); err != nil {
return err
}
} else {
if err = setWGConfig(serverNode, true); err != nil {
if errors.Is(err, os.ErrNotExist) {
return ServerPull(serverNode, true)
} else {
return err
}
}
}
return nil
}
func getServerLocalIP(networkSettings *models.Network) (string, error) {
var networkCIDR = networkSettings.LocalRange
var currentAddresses, _ = net.InterfaceAddrs()
var _, currentCIDR, cidrErr = net.ParseCIDR(networkCIDR)
if cidrErr != nil {
logger.Log(1, "error on server local IP, invalid CIDR provided:", networkCIDR)
return "", cidrErr
}
for _, addr := range currentAddresses {
ip, _, err := net.ParseCIDR(addr.String())
if err != nil {
continue
}
if currentCIDR.Contains(ip) {
logger.Log(1, "found local ip on network,", networkSettings.NetID, ", set to", ip.String())
return ip.String(), nil
}
}
return "", errors.New("could not find a local ip for server")
}
func serverPush(serverNode *models.LegacyNode) error {
serverNode.OS = runtime.GOOS
serverNode.SetLastCheckIn()
return UpdateNode(serverNode, serverNode)
}
// AddServerIDIfNotPresent - add's current server ID to DB if not present
func AddServerIDIfNotPresent() error {
currentNodeID := servercfg.GetNodeID()
currentServerIDs := models.ServerIDs{}
record, err := database.FetchRecord(database.SERVERCONF_TABLE_NAME, server_id_key)
if err != nil && !database.IsEmptyRecord(err) {
return err
} else if err == nil {
if err = json.Unmarshal([]byte(record), &currentServerIDs); err != nil {
return err
}
}
if !StringSliceContains(currentServerIDs.ServerIDs, currentNodeID) {
currentServerIDs.ServerIDs = append(currentServerIDs.ServerIDs, currentNodeID)
data, err := json.Marshal(&currentServerIDs)
if err != nil {
return err
}
return database.Insert(server_id_key, string(data), database.SERVERCONF_TABLE_NAME)
}
return nil
}
// GetServerCount - fetches server count from DB
func GetServerCount() int {
if record, err := database.FetchRecord(database.SERVERCONF_TABLE_NAME, server_id_key); err == nil {
currentServerIDs := models.ServerIDs{}
if err = json.Unmarshal([]byte(record), &currentServerIDs); err == nil {
return len(currentServerIDs.ServerIDs)
}
}
return 1
}

View File

@@ -84,7 +84,7 @@ func fetchTelemetryData() (telemetryData, error) {
data.Users = getDBLength(database.USERS_TABLE_NAME) data.Users = getDBLength(database.USERS_TABLE_NAME)
data.Networks = getDBLength(database.NETWORKS_TABLE_NAME) data.Networks = getDBLength(database.NETWORKS_TABLE_NAME)
data.Version = servercfg.GetVersion() data.Version = servercfg.GetVersion()
data.Servers = GetServerCount() //data.Servers = GetServerCount()
nodes, err := GetAllNodes() nodes, err := GetAllNodes()
if err == nil { if err == nil {
data.Nodes = len(nodes) data.Nodes = len(nodes)
@@ -111,10 +111,14 @@ func setTelemetryTimestamp(telRecord *models.Telemetry) error {
} }
// getClientCount - returns counts of nodes with various OS types and conditions // getClientCount - returns counts of nodes with various OS types and conditions
func getClientCount(nodes []models.LegacyNode) clientCount { func getClientCount(nodes []models.Node) clientCount {
var count clientCount var count clientCount
for _, node := range nodes { for _, node := range nodes {
switch node.OS { host, err := GetHost(node.HostID.String())
if err != nil {
continue
}
switch host.OS {
case "darwin": case "darwin":
count.MacOS += 1 count.MacOS += 1
case "windows": case "windows":
@@ -124,9 +128,6 @@ func getClientCount(nodes []models.LegacyNode) clientCount {
case "freebsd": case "freebsd":
count.FreeBSD += 1 count.FreeBSD += 1
} }
if !(node.IsServer == "yes") {
count.NonServer += 1
}
} }
return count return count
} }

View File

@@ -16,7 +16,6 @@ import (
"github.com/c-robinson/iplib" "github.com/c-robinson/iplib"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/ncutils" "github.com/gravitl/netmaker/netclient/ncutils"
) )
@@ -146,31 +145,6 @@ func StringSliceContains(slice []string, item string) bool {
// == private == // == private ==
// sets the network server peers of a given node
func setNetworkServerPeers(serverNode *models.LegacyNode) {
if currentPeersList, err := getSystemPeers(serverNode); err == nil {
if currentPeersList == nil {
currentPeersList = make(map[string]string)
}
if database.SetPeers(currentPeersList, serverNode.Network) {
logger.Log(1, "set new peers on network", serverNode.Network)
}
} else {
logger.Log(1, "could not set peers on network", serverNode.Network, ":", err.Error())
}
}
// ShouldPublishPeerPorts - Gets ports from iface, sets, and returns true if they are different
func ShouldPublishPeerPorts(serverNode *models.LegacyNode) bool {
if currentPeersList, err := getSystemPeers(serverNode); err == nil {
if database.SetPeers(currentPeersList, serverNode.Network) {
logger.Log(1, "set new peers on network", serverNode.Network)
return true
}
}
return false
}
// NormalCIDR - returns the first address of CIDR // NormalCIDR - returns the first address of CIDR
func NormalizeCIDR(address string) (string, error) { func NormalizeCIDR(address string) (string, error) {
ip, IPNet, err := net.ParseCIDR(address) ip, IPNet, err := net.ParseCIDR(address)

View File

@@ -1,72 +1,25 @@
package logic package logic
import ( import (
"os"
"os/exec"
"strings"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/wireguard"
"github.com/gravitl/netmaker/servercfg"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
) )
// RemoveConf - removes a configuration for a given WireGuard interface
func RemoveConf(iface string, printlog bool) error {
var err error
confPath := ncutils.GetNetclientPathSpecific() + iface + ".conf"
err = removeWGQuickConf(confPath, printlog)
return err
}
// HasPeerConnected - checks if a client node has connected over WG
func HasPeerConnected(node *models.LegacyNode) bool {
client, err := wgctrl.New()
if err != nil {
return false
}
defer client.Close()
device, err := client.Device(node.Interface)
if err != nil {
return false
}
for _, peer := range device.Peers {
if peer.PublicKey.String() == node.PublicKey {
if peer.Endpoint != nil {
return true
}
}
}
return false
}
// IfaceDelta - checks if the new node causes an interface change // IfaceDelta - checks if the new node causes an interface change
func IfaceDelta(currentNode *models.LegacyNode, newNode *models.LegacyNode) bool { func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
// single comparison statements // single comparison statements
if newNode.Endpoint != currentNode.Endpoint || if newNode.Address.String() != currentNode.Address.String() ||
newNode.PublicKey != currentNode.PublicKey || newNode.Address6.String() != currentNode.Address6.String() ||
newNode.Address != currentNode.Address ||
newNode.Address6 != currentNode.Address6 ||
newNode.IsEgressGateway != currentNode.IsEgressGateway || newNode.IsEgressGateway != currentNode.IsEgressGateway ||
newNode.IsIngressGateway != currentNode.IsIngressGateway || newNode.IsIngressGateway != currentNode.IsIngressGateway ||
newNode.IsRelay != currentNode.IsRelay || newNode.IsRelay != currentNode.IsRelay ||
newNode.UDPHolePunch != currentNode.UDPHolePunch ||
newNode.IsPending != currentNode.IsPending ||
newNode.ListenPort != currentNode.ListenPort ||
newNode.LocalListenPort != currentNode.LocalListenPort ||
newNode.MTU != currentNode.MTU ||
newNode.PersistentKeepalive != currentNode.PersistentKeepalive || newNode.PersistentKeepalive != currentNode.PersistentKeepalive ||
newNode.DNSOn != currentNode.DNSOn || newNode.DNSOn != currentNode.DNSOn ||
newNode.Connected != currentNode.Connected || newNode.Connected != currentNode.Connected ||
len(newNode.AllowedIPs) != len(currentNode.AllowedIPs) { len(newNode.AllowedIPs) != len(currentNode.AllowedIPs) {
return true return true
} }
// multi-comparison statements // multi-comparison statements
if newNode.IsEgressGateway == "yes" { if newNode.IsEgressGateway {
if len(currentNode.EgressGatewayRanges) != len(newNode.EgressGatewayRanges) { if len(currentNode.EgressGatewayRanges) != len(newNode.EgressGatewayRanges) {
return true return true
} }
@@ -76,8 +29,7 @@ func IfaceDelta(currentNode *models.LegacyNode, newNode *models.LegacyNode) bool
} }
} }
} }
if newNode.IsRelay {
if newNode.IsRelay == "yes" {
if len(currentNode.RelayAddrs) != len(newNode.RelayAddrs) { if len(currentNode.RelayAddrs) != len(newNode.RelayAddrs) {
return true return true
} }
@@ -87,7 +39,6 @@ func IfaceDelta(currentNode *models.LegacyNode, newNode *models.LegacyNode) bool
} }
} }
} }
for _, address := range newNode.AllowedIPs { for _, address := range newNode.AllowedIPs {
if !StringSliceContains(currentNode.AllowedIPs, address) { if !StringSliceContains(currentNode.AllowedIPs, address) {
return true return true
@@ -97,147 +48,3 @@ func IfaceDelta(currentNode *models.LegacyNode, newNode *models.LegacyNode) bool
} }
// == Private Functions == // == Private Functions ==
// gets the server peers locally
func getSystemPeers(node *models.LegacyNode) (map[string]string, error) {
peers := make(map[string]string)
client, err := wgctrl.New()
if err != nil {
return peers, err
}
defer client.Close()
device, err := client.Device(node.Interface)
if err != nil {
return nil, err
}
if device.Peers != nil && len(device.Peers) > 0 {
for _, peer := range device.Peers {
if IsBase64(peer.PublicKey.String()) && peer.Endpoint != nil && CheckEndpoint(peer.Endpoint.String()) {
peers[peer.PublicKey.String()] = peer.Endpoint.String()
}
}
}
return peers, nil
}
func removeWGQuickConf(confPath string, printlog bool) error {
if _, err := ncutils.RunCmd("wg-quick down "+confPath, printlog); err != nil {
return err
}
return nil
}
func setWGConfig(node *models.LegacyNode, peerupdate bool) error {
peers, err := GetPeerUpdate(node)
if err != nil {
return err
}
privkey, err := FetchPrivKey(node.ID)
if err != nil {
return err
}
if peerupdate {
if err := wireguard.SetPeers(node.Interface, node, peers.Peers); err != nil {
logger.Log(0, "error updating peers", err.Error())
return err
}
// logger.Log(0, "--------> UPDATE PEERS IN PROXY.....")
// ProxyMgmChan <- &manager.ManagerAction{
// Action: manager.UpdatePeer,
// Payload: manager.ManagerPayload{
// InterfaceName: node.Interface,
// Peers: peers.Peers,
// },
// }
logger.Log(2, "updated peers on server", node.Name)
} else {
err = wireguard.InitWireguard(node, privkey, peers.Peers)
if err != nil {
logger.Log(0, "failed to set wg config on server: ", node.Name, err.Error())
return err
}
logger.Log(3, "finished setting wg config on server", node.Name)
}
if servercfg.IsProxyEnabled() {
logger.Log(0, "--------> ADD/Update INTERFACE TO PROXY.....")
proxyPayload, err := GetPeersForProxy(node, false)
if err != nil {
logger.Log(0, "failed to get peers for proxy: ", err.Error())
} else {
ProxyMgmChan <- &proxyPayload
}
}
return nil
}
func setWGKeyConfig(node *models.LegacyNode) error {
privatekey, err := wgtypes.GeneratePrivateKey()
if err != nil {
return err
}
privkeystring := privatekey.String()
publickey := privatekey.PublicKey()
node.PublicKey = publickey.String()
err = StorePrivKey(node.ID, privkeystring)
if err != nil {
return err
}
if node.Action == models.NODE_UPDATE_KEY {
node.Action = models.NODE_NOOP
}
return setWGConfig(node, false)
}
func removeLocalServer(node *models.LegacyNode) error {
var err error
var ifacename = node.Interface
if err = RemovePrivKey(node.ID); err != nil {
logger.Log(1, "failed to remove server conf from db", node.ID)
}
if ifacename != "" {
if !ncutils.IsKernel() {
if err = RemoveConf(ifacename, true); err == nil {
logger.Log(1, "removed WireGuard interface:", ifacename)
}
} else {
ipExec, err := exec.LookPath("ip")
if err != nil {
return err
}
out, err := ncutils.RunCmd(ipExec+" link del "+ifacename, false)
dontprint := strings.Contains(out, "does not exist") || strings.Contains(out, "Cannot find device")
if err != nil && !dontprint {
logger.Log(1, "error running command:", ipExec, "link del", ifacename)
logger.Log(1, out)
}
if node.PostDown != "" {
ncutils.RunCmd(node.PostDown, false)
}
}
}
home := ncutils.GetNetclientPathSpecific()
if ncutils.FileExists(home + "netconfig-" + node.Network) {
_ = os.Remove(home + "netconfig-" + node.Network)
}
if ncutils.FileExists(home + "nettoken-" + node.Network) {
_ = os.Remove(home + "nettoken-" + node.Network)
}
if ncutils.FileExists(home + "secret-" + node.Network) {
_ = os.Remove(home + "secret-" + node.Network)
}
if ncutils.FileExists(home + "wgkey-" + node.Network) {
_ = os.Remove(home + "wgkey-" + node.Network)
}
if ncutils.FileExists(home + "nm-" + node.Network + ".conf") {
_ = os.Remove(home + "nm-" + node.Network + ".conf")
}
return err
}

View File

@@ -2,8 +2,10 @@ package logic
import ( import (
"context" "context"
"net"
"time" "time"
"github.com/google/uuid"
"github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
) )
@@ -16,22 +18,26 @@ const (
) )
var ( var (
zombies []string zombies []uuid.UUID
removeZombie chan string = make(chan (string), 10) removeZombie chan uuid.UUID = make(chan (uuid.UUID), 10)
newZombie chan string = make(chan (string), 10) newZombie chan uuid.UUID = make(chan (uuid.UUID), 10)
) )
// CheckZombies - checks if new node has same macaddress as existing node // CheckZombies - checks if new node has same macaddress as existing node
// if so, existing node is added to zombie node quarantine list // if so, existing node is added to zombie node quarantine list
func CheckZombies(newnode *models.LegacyNode) { func CheckZombies(newnode *models.Node, mac net.HardwareAddr) {
nodes, err := GetNetworkNodes(newnode.Network) nodes, err := GetNetworkNodes(newnode.Network)
if err != nil { if err != nil {
logger.Log(1, "Failed to retrieve network nodes", newnode.Network, err.Error()) logger.Log(1, "Failed to retrieve network nodes", newnode.Network, err.Error())
return return
} }
for _, node := range nodes { for _, node := range nodes {
if node.MacAddress == newnode.MacAddress { host, err := GetHost(node.HostID.String())
logger.Log(0, "adding ", node.ID, " to zombie list") if err != nil {
}
if host.MacAddress.String() == mac.String() {
logger.Log(0, "adding ", node.ID.String(), " to zombie list")
newZombie <- node.ID newZombie <- node.ID
} }
} }
@@ -46,14 +52,14 @@ func ManageZombies(ctx context.Context) {
case <-ctx.Done(): case <-ctx.Done():
return return
case id := <-newZombie: case id := <-newZombie:
logger.Log(1, "adding", id, "to zombie quaratine list") logger.Log(1, "adding", id.String(), "to zombie quaratine list")
zombies = append(zombies, id) zombies = append(zombies, id)
case id := <-removeZombie: case id := <-removeZombie:
found := false found := false
if len(zombies) > 0 { if len(zombies) > 0 {
for i := len(zombies) - 1; i >= 0; i-- { for i := len(zombies) - 1; i >= 0; i-- {
if zombies[i] == id { if zombies[i] == id {
logger.Log(1, "removing zombie from quaratine list", zombies[i]) logger.Log(1, "removing zombie from quaratine list", zombies[i].String())
zombies = append(zombies[:i], zombies[i+1:]...) zombies = append(zombies[:i], zombies[i+1:]...)
found = true found = true
} }
@@ -66,19 +72,19 @@ func ManageZombies(ctx context.Context) {
logger.Log(3, "checking for zombie nodes") logger.Log(3, "checking for zombie nodes")
if len(zombies) > 0 { if len(zombies) > 0 {
for i := len(zombies) - 1; i >= 0; i-- { for i := len(zombies) - 1; i >= 0; i-- {
node, err := GetNodeByID(zombies[i]) node, err := GetNodeByID(zombies[i].String())
if err != nil { if err != nil {
logger.Log(1, "error retrieving zombie node", zombies[i], err.Error()) logger.Log(1, "error retrieving zombie node", zombies[i].String(), err.Error())
logger.Log(1, "deleting ", node.Name, " from zombie list") logger.Log(1, "deleting ", node.ID.String(), " from zombie list")
zombies = append(zombies[:i], zombies[i+1:]...) zombies = append(zombies[:i], zombies[i+1:]...)
continue continue
} }
if time.Since(time.Unix(node.LastCheckIn, 0)) > time.Minute*ZOMBIE_DELETE_TIME { if time.Since(node.LastCheckIn) > time.Minute*ZOMBIE_DELETE_TIME {
if err := DeleteNode(&node, true); err != nil { if err := DeleteNode(&node, true); err != nil {
logger.Log(1, "error deleting zombie node", zombies[i], err.Error()) logger.Log(1, "error deleting zombie node", zombies[i].String(), err.Error())
continue continue
} }
logger.Log(1, "deleting zombie node", node.Name) logger.Log(1, "deleting zombie node", node.ID.String())
zombies = append(zombies[:i], zombies[i+1:]...) zombies = append(zombies[:i], zombies[i+1:]...)
} }
} }
@@ -104,13 +110,13 @@ func InitializeZombies() {
if node.ID == othernode.ID { if node.ID == othernode.ID {
continue continue
} }
if node.MacAddress == othernode.MacAddress { if node.HostID == othernode.HostID {
if node.LastCheckIn > othernode.LastCheckIn { if node.LastCheckIn.After(othernode.LastCheckIn) {
zombies = append(zombies, othernode.ID) zombies = append(zombies, othernode.ID)
logger.Log(1, "adding ", othernode.Name, " with ID ", othernode.ID, " to zombie list") logger.Log(1, "adding", othernode.ID.String(), "to zombie list")
} else { } else {
zombies = append(zombies, node.ID) zombies = append(zombies, node.ID)
logger.Log(1, "adding ", node.Name, " with ID ", node.ID, " to zombie list") logger.Log(1, "adding", node.ID.String(), "to zombie list")
} }
} }
} }

10
main.go
View File

@@ -71,9 +71,6 @@ func initialize() { // Client Mode Prereq Check
logger.FatalLog("Error connecting to database: ", err.Error()) logger.FatalLog("Error connecting to database: ", err.Error())
} }
logger.Log(0, "database successfully connected") logger.Log(0, "database successfully connected")
if err = logic.AddServerIDIfNotPresent(); err != nil {
logger.Log(1, "failed to save server ID")
}
logic.SetJWTSecret() logic.SetJWTSecret()
@@ -112,9 +109,6 @@ func initialize() { // Client Mode Prereq Check
if uid != 0 { if uid != 0 {
logger.FatalLog("To run in client mode requires root privileges. Either disable client mode or run with sudo.") logger.FatalLog("To run in client mode requires root privileges. Either disable client mode or run with sudo.")
} }
if err := serverctl.InitServerNetclient(); err != nil {
logger.FatalLog("Did not find netclient to use CLIENT_MODE")
}
} }
// initialize iptables to ensure gateways work correctly and mq is forwarded if containerized // initialize iptables to ensure gateways work correctly and mq is forwarded if containerized
if servercfg.ManageIPTables() != "off" { if servercfg.ManageIPTables() != "off" {
@@ -184,10 +178,6 @@ func startControllers() {
waitnetwork.Add(1) waitnetwork.Add(1)
//go nmproxy.Start(ctx, logic.ProxyMgmChan, servercfg.GetAPIHost()) //go nmproxy.Start(ctx, logic.ProxyMgmChan, servercfg.GetAPIHost())
err := serverctl.SyncServerNetworkWithProxy()
if err != nil {
logger.Log(0, "failed to sync proxy with server interfaces: ", err.Error())
}
quit := make(chan os.Signal, 1) quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGTERM, os.Interrupt) signal.Notify(quit, syscall.SIGTERM, os.Interrupt)
<-quit <-quit

View File

@@ -18,8 +18,8 @@ type Host struct {
HostPass string `json:"hostpass" yaml:"hostpass"` HostPass string `json:"hostpass" yaml:"hostpass"`
Name string `json:"name" yaml:"name"` Name string `json:"name" yaml:"name"`
OS string `json:"os" yaml:"os"` OS string `json:"os" yaml:"os"`
Interface string `json:"interface" yaml:"interface"`
Debug bool `json:"debug" yaml:"debug"` Debug bool `json:"debug" yaml:"debug"`
NodePassword string `json:"nodepassword" yaml:"nodepassword"`
ListenPort int `json:"listenport" yaml:"listenport"` ListenPort int `json:"listenport" yaml:"listenport"`
LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"` LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"`
LocalRange net.IPNet `json:"localrange" yaml:"localrange"` LocalRange net.IPNet `json:"localrange" yaml:"localrange"`

View File

@@ -8,7 +8,6 @@ import (
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
"golang.org/x/crypto/bcrypt"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
) )
@@ -44,7 +43,7 @@ var seededRand *rand.Rand = rand.New(
// NodeCheckin - struct for node checkins with server // NodeCheckin - struct for node checkins with server
type NodeCheckin struct { type NodeCheckin struct {
Version string Version string
Connected string Connected bool
Ifaces []Iface Ifaces []Iface
} }
@@ -58,6 +57,7 @@ type Iface struct {
// CommonNode - represents a commonn node data elements shared by netmaker and netclient // CommonNode - represents a commonn node data elements shared by netmaker and netclient
type CommonNode struct { type CommonNode struct {
ID uuid.UUID `json:"id" yaml:"id"` ID uuid.UUID `json:"id" yaml:"id"`
HostID uuid.UUID `json:"hostid" yaml:"hostid"`
Network string `json:"network" yaml:"network"` Network string `json:"network" yaml:"network"`
NetworkRange net.IPNet `json:"networkrange" yaml:"networkrange"` NetworkRange net.IPNet `json:"networkrange" yaml:"networkrange"`
NetworkRange6 net.IPNet `json:"networkrange6" yaml:"networkrange6"` NetworkRange6 net.IPNet `json:"networkrange6" yaml:"networkrange6"`
@@ -71,14 +71,11 @@ type CommonNode struct {
PostUp string `json:"postup" yaml:"postup"` PostUp string `json:"postup" yaml:"postup"`
PostDown string `json:"postdown" yaml:"postdown"` PostDown string `json:"postdown" yaml:"postdown"`
Action string `json:"action" yaml:"action"` Action string `json:"action" yaml:"action"`
IsServer bool `json:"isserver" yaml:"isserver"`
IsLocal bool `json:"islocal" yaml:"islocal"` IsLocal bool `json:"islocal" yaml:"islocal"`
IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"` IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"`
IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"` IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"`
IsStatic bool `json:"isstatic" yaml:"isstatic"` IsStatic bool `json:"isstatic" yaml:"isstatic"`
IsPending bool `json:"ispending" yaml:"ispending"`
DNSOn bool `json:"dnson" yaml:"dnson"` DNSOn bool `json:"dnson" yaml:"dnson"`
IsHub bool `json:"ishub" yaml:"ishub"`
PersistentKeepalive int `json:"persistentkeepalive" yaml:"persistentkeepalive"` PersistentKeepalive int `json:"persistentkeepalive" yaml:"persistentkeepalive"`
Peers []wgtypes.PeerConfig `json:"peers" yaml:"peers"` Peers []wgtypes.PeerConfig `json:"peers" yaml:"peers"`
Proxy bool `json:"proxy" bson:"proxy" yaml:"proxy"` Proxy bool `json:"proxy" bson:"proxy" yaml:"proxy"`
@@ -87,10 +84,27 @@ type CommonNode struct {
// Node - a model of a network node // Node - a model of a network node
type Node struct { type Node struct {
CommonNode CommonNode
PendingDelete bool `json:"pendingdelete" bson:"pendingdelete" yaml:"pendingdelete"` PendingDelete bool `json:"pendingdelete" bson:"pendingdelete" yaml:"pendingdelete"`
LastModified time.Time `json:"lastmodified" bson:"lastmodified" yaml:"lastmodified"`
LastCheckIn time.Time `json:"lastcheckin" bson:"lastcheckin" yaml:"lastcheckin"`
LastPeerUpdate time.Time `json:"lastpeerupdate" bson:"lastpeerupdate" yaml:"lastpeerupdate"`
ExpirationDateTime time.Time `json:"expdatetime" bson:"expdatetime" yaml:"expdatetime"`
AllowedIPs []string `json:"allowedips" bson:"allowedips" yaml:"allowedips"`
EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
EgressGatewayNatEnabled string `json:"egressgatewaynatenabled" bson:"egressgatewaynatenabled" yaml:"egressgatewaynatenabled"`
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" validate:"checkyesorno"`
RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs" yaml:"relayaddrs"`
IsDocker string `json:"isdocker" bson:"isdocker" yaml:"isdocker" validate:"checkyesorno"`
IsK8S string `json:"isk8s" bson:"isk8s" yaml:"isk8s" validate:"checkyesorno"`
// == PRO == // == PRO ==
DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"` DefaultACL string `json:"defaultacl,omitempty" bson:"defaultacl,omitempty" yaml:"defaultacl,omitempty" validate:"checkyesornoorunset"`
OwnerID string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"` OwnerID string `json:"ownerid,omitempty" bson:"ownerid,omitempty" yaml:"ownerid,omitempty"`
FailoverNode uuid.UUID `json:"failovernode" bson:"failovernode" yaml:"failovernode"`
Failover bool `json:"failover" bson:"failover" yaml:"failover"`
} }
// LegacyNode - legacy struct for node model // LegacyNode - legacy struct for node model
@@ -162,13 +176,15 @@ type LegacyNode struct {
} }
// NodesArray - used for node sorting // NodesArray - used for node sorting
type NodesArray []LegacyNode type NodesArray []Node
// NodesArray.Len - gets length of node array // NodesArray.Len - gets length of node array
func (a NodesArray) Len() int { return len(a) } func (a NodesArray) Len() int { return len(a) }
// NodesArray.Less - gets returns lower rank of two node addresses // NodesArray.Less - gets returns lower rank of two node addressesFill
func (a NodesArray) Less(i, j int) bool { return isLess(a[i].Address, a[j].Address) } func (a NodesArray) Less(i, j int) bool {
return isLess(a[i].Address.IP.String(), a[j].Address.IP.String())
}
// NodesArray.Swap - swaps two nodes in array // NodesArray.Swap - swaps two nodes in array
func (a NodesArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a NodesArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
@@ -180,11 +196,11 @@ func isLess(ipA string, ipB string) bool {
} }
// Node.PrimaryAddress - return ipv4 address if present, else return ipv6 // Node.PrimaryAddress - return ipv4 address if present, else return ipv6
func (node *LegacyNode) PrimaryAddress() string { func (node *Node) PrimaryAddress() string {
if node.Address != "" { if node.Address.IP != nil {
return node.Address return node.Address.IP.String()
} }
return node.Address6 return node.Address6.IP.String()
} }
// Node.SetDefaultConnected // Node.SetDefaultConnected
@@ -218,13 +234,6 @@ func (node *LegacyNode) SetDefaultNFTablesPresent() {
} }
} }
// Node.SetDefaulIsPending - sets ispending default
func (node *LegacyNode) SetDefaulIsPending() {
if node.IsPending == "" {
node.IsPending = "no"
}
}
// Node.SetDefaultIsRelayed - set default is relayed // Node.SetDefaultIsRelayed - set default is relayed
func (node *LegacyNode) SetDefaultIsRelayed() { func (node *LegacyNode) SetDefaultIsRelayed() {
if node.IsRelayed == "" { if node.IsRelayed == "" {
@@ -326,23 +335,23 @@ func (node *LegacyNode) SetIsStaticDefault() {
} }
// Node.SetLastModified - set last modified initial time // Node.SetLastModified - set last modified initial time
func (node *LegacyNode) SetLastModified() { func (node *Node) SetLastModified() {
node.LastModified = time.Now().Unix() node.LastModified = time.Now()
} }
// Node.SetLastCheckIn - time.Now().Unix() // Node.SetLastCheckIn - set checkin time of node
func (node *LegacyNode) SetLastCheckIn() { func (node *Node) SetLastCheckIn() {
node.LastCheckIn = time.Now().Unix() node.LastCheckIn = time.Now()
} }
// Node.SetLastPeerUpdate - sets last peer update time // Node.SetLastPeerUpdate - sets last peer update time
func (node *LegacyNode) SetLastPeerUpdate() { func (node *Node) SetLastPeerUpdate() {
node.LastPeerUpdate = time.Now().Unix() node.LastPeerUpdate = time.Now()
} }
// Node.SetExpirationDateTime - sets node expiry time // Node.SetExpirationDateTime - sets node expiry time
func (node *LegacyNode) SetExpirationDateTime() { func (node *Node) SetExpirationDateTime() {
node.ExpirationDateTime = time.Now().Unix() + TEN_YEARS_IN_SECONDS node.ExpirationDateTime = time.Now().Add(TEN_YEARS_IN_SECONDS)
} }
// Node.SetDefaultName - sets a random name to node // Node.SetDefaultName - sets a random name to node
@@ -360,33 +369,18 @@ func (node *LegacyNode) SetDefaultFailover() {
} }
// Node.Fill - fills other node data into calling node data if not set on calling node // Node.Fill - fills other node data into calling node data if not set on calling node
func (newNode *LegacyNode) Fill(currentNode *LegacyNode) { // TODO add new field for nftables present func (newNode *Node) Fill(currentNode *Node) { // TODO add new field for nftables present
newNode.ID = currentNode.ID newNode.ID = currentNode.ID
if newNode.Address == "" { // Revisit the logic for boolean values
// TODO ---- !!!!!!!!!!!!!!!!!!!!!!!!!!!!
// TODO ---- !!!!!!!!!!!!!!!!!!!!!!!!!!
if newNode.Address.String() == "" {
newNode.Address = currentNode.Address newNode.Address = currentNode.Address
} }
if newNode.Address6 == "" { if newNode.Address6.String() == "" {
newNode.Address6 = currentNode.Address6 newNode.Address6 = currentNode.Address6
} }
if newNode.LocalAddress == "" {
newNode.LocalAddress = currentNode.LocalAddress
}
if newNode.Name == "" {
newNode.Name = currentNode.Name
}
if newNode.ListenPort == 0 {
newNode.ListenPort = currentNode.ListenPort
}
if newNode.LocalListenPort == 0 {
newNode.LocalListenPort = currentNode.LocalListenPort
}
if newNode.PublicKey == "" {
newNode.PublicKey = currentNode.PublicKey
}
if newNode.Endpoint == "" {
newNode.Endpoint = currentNode.Endpoint
}
if newNode.PostUp == "" { if newNode.PostUp == "" {
newNode.PostUp = currentNode.PostUp newNode.PostUp = currentNode.PostUp
} }
@@ -399,48 +393,25 @@ func (newNode *LegacyNode) Fill(currentNode *LegacyNode) { // TODO add new field
if newNode.PersistentKeepalive < 0 { if newNode.PersistentKeepalive < 0 {
newNode.PersistentKeepalive = currentNode.PersistentKeepalive newNode.PersistentKeepalive = currentNode.PersistentKeepalive
} }
if newNode.AccessKey == "" { if newNode.LastModified != currentNode.LastModified {
newNode.AccessKey = currentNode.AccessKey
}
if newNode.Interface == "" {
newNode.Interface = currentNode.Interface
}
if newNode.LastModified == 0 {
newNode.LastModified = currentNode.LastModified newNode.LastModified = currentNode.LastModified
} }
if newNode.ExpirationDateTime == 0 { if newNode.ExpirationDateTime.IsZero() {
newNode.ExpirationDateTime = currentNode.ExpirationDateTime newNode.ExpirationDateTime = currentNode.ExpirationDateTime
} }
if newNode.LastPeerUpdate == 0 { if newNode.LastPeerUpdate.IsZero() {
newNode.LastPeerUpdate = currentNode.LastPeerUpdate newNode.LastPeerUpdate = currentNode.LastPeerUpdate
} }
if newNode.LastCheckIn == 0 { if newNode.LastCheckIn.IsZero() {
newNode.LastCheckIn = currentNode.LastCheckIn newNode.LastCheckIn = currentNode.LastCheckIn
} }
if newNode.MacAddress == "" {
newNode.MacAddress = currentNode.MacAddress
}
if newNode.Password != "" {
err := bcrypt.CompareHashAndPassword([]byte(newNode.Password), []byte(currentNode.Password))
if err != nil && currentNode.Password != newNode.Password {
hash, err := bcrypt.GenerateFromPassword([]byte(newNode.Password), 5)
if err == nil {
newNode.Password = string(hash)
}
}
} else {
newNode.Password = currentNode.Password
}
if newNode.Network == "" { if newNode.Network == "" {
newNode.Network = currentNode.Network newNode.Network = currentNode.Network
} }
if newNode.IsPending == "" { if newNode.IsEgressGateway != currentNode.IsEgressGateway {
newNode.IsPending = currentNode.IsPending
}
if newNode.IsEgressGateway == "" {
newNode.IsEgressGateway = currentNode.IsEgressGateway newNode.IsEgressGateway = currentNode.IsEgressGateway
} }
if newNode.IsIngressGateway == "" { if newNode.IsIngressGateway != currentNode.IsIngressGateway {
newNode.IsIngressGateway = currentNode.IsIngressGateway newNode.IsIngressGateway = currentNode.IsIngressGateway
} }
if newNode.EgressGatewayRanges == nil { if newNode.EgressGatewayRanges == nil {
@@ -452,73 +423,46 @@ func (newNode *LegacyNode) Fill(currentNode *LegacyNode) { // TODO add new field
if newNode.IngressGatewayRange6 == "" { if newNode.IngressGatewayRange6 == "" {
newNode.IngressGatewayRange6 = currentNode.IngressGatewayRange6 newNode.IngressGatewayRange6 = currentNode.IngressGatewayRange6
} }
if newNode.IsStatic == "" { if newNode.IsStatic != currentNode.IsStatic {
newNode.IsStatic = currentNode.IsStatic newNode.IsStatic = currentNode.IsStatic
} }
if newNode.UDPHolePunch == "" { if newNode.DNSOn != currentNode.DNSOn {
newNode.UDPHolePunch = currentNode.UDPHolePunch
}
if newNode.DNSOn == "" {
newNode.DNSOn = currentNode.DNSOn newNode.DNSOn = currentNode.DNSOn
} }
if newNode.IsLocal == "" { if newNode.IsLocal != currentNode.IsLocal {
newNode.IsLocal = currentNode.IsLocal newNode.IsLocal = currentNode.IsLocal
} }
if newNode.IPForwarding == "" {
newNode.IPForwarding = currentNode.IPForwarding
}
if newNode.Action == "" { if newNode.Action == "" {
newNode.Action = currentNode.Action newNode.Action = currentNode.Action
} }
if newNode.IsServer == "" {
newNode.IsServer = currentNode.IsServer
}
if newNode.IsServer == "yes" {
newNode.IsStatic = "yes"
newNode.Connected = "yes"
}
if newNode.MTU == 0 {
newNode.MTU = currentNode.MTU
}
if newNode.OS == "" {
newNode.OS = currentNode.OS
}
if newNode.RelayAddrs == nil { if newNode.RelayAddrs == nil {
newNode.RelayAddrs = currentNode.RelayAddrs newNode.RelayAddrs = currentNode.RelayAddrs
} }
if newNode.IsRelay == "" { if newNode.IsRelay != currentNode.IsRelay {
newNode.IsRelay = currentNode.IsRelay newNode.IsRelay = currentNode.IsRelay
} }
if newNode.IsRelayed == "" { if newNode.IsRelayed == currentNode.IsRelayed {
newNode.IsRelayed = currentNode.IsRelayed newNode.IsRelayed = currentNode.IsRelayed
} }
if newNode.IsDocker == "" { if newNode.IsDocker == currentNode.IsDocker {
newNode.IsDocker = currentNode.IsDocker newNode.IsDocker = currentNode.IsDocker
} }
if newNode.IsK8S == "" { if newNode.IsK8S != currentNode.IsK8S {
newNode.IsK8S = currentNode.IsK8S newNode.IsK8S = currentNode.IsK8S
} }
if newNode.Version == "" {
newNode.Version = currentNode.Version
}
if newNode.IsHub == "" {
newNode.IsHub = currentNode.IsHub
}
if newNode.Server == "" { if newNode.Server == "" {
newNode.Server = currentNode.Server newNode.Server = currentNode.Server
} }
if newNode.Connected == "" { if newNode.Connected != currentNode.Connected {
newNode.Connected = currentNode.Connected newNode.Connected = currentNode.Connected
} }
if newNode.DefaultACL == "" { if newNode.DefaultACL == "" {
newNode.DefaultACL = currentNode.DefaultACL newNode.DefaultACL = currentNode.DefaultACL
} }
if newNode.Failover != currentNode.Failover {
if newNode.Failover == "" {
newNode.Failover = currentNode.Failover newNode.Failover = currentNode.Failover
} }
newNode.Proxy = currentNode.Proxy newNode.Proxy = currentNode.Proxy
newNode.TrafficKeys = currentNode.TrafficKeys
} }
// StringWithCharset - returns random string inside defined charset // StringWithCharset - returns random string inside defined charset
@@ -553,12 +497,12 @@ func (node *LegacyNode) NameInNodeCharSet() bool {
// == PRO == // == PRO ==
// Node.DoesACLAllow - checks if default ACL on node is "yes" // Node.DoesACLAllow - checks if default ACL on node is "yes"
func (node *LegacyNode) DoesACLAllow() bool { func (node *Node) DoesACLAllow() bool {
return node.DefaultACL == "yes" return node.DefaultACL == "yes"
} }
// Node.DoesACLDeny - checks if default ACL on node is "no" // Node.DoesACLDeny - checks if default ACL on node is "no"
func (node *LegacyNode) DoesACLDeny() bool { func (node *Node) DoesACLDeny() bool {
return node.DefaultACL == "no" return node.DefaultACL == "no"
} }
@@ -587,7 +531,7 @@ func (ln *LegacyNode) ConvertToNewNode() (*Host, *Node) {
gateway, _ := net.ResolveUDPAddr("udp", ln.InternetGateway) gateway, _ := net.ResolveUDPAddr("udp", ln.InternetGateway)
host.InternetGateway = *gateway host.InternetGateway = *gateway
id, _ := uuid.Parse(ln.ID) id, _ := uuid.Parse(ln.ID)
host.Nodes = append(host.Nodes, id) host.Nodes = append(host.Nodes, id.String())
} }
id, _ := uuid.Parse(ln.ID) id, _ := uuid.Parse(ln.ID)
node.ID = id node.ID = id
@@ -607,7 +551,6 @@ func (ln *LegacyNode) ConvertToNewNode() (*Host, *Node) {
node.PostUp = ln.PostUp node.PostUp = ln.PostUp
node.PostDown = ln.PostDown node.PostDown = ln.PostDown
node.Action = ln.Action node.Action = ln.Action
node.IsServer = parseBool(ln.IsServer)
node.IsLocal = parseBool(ln.IsLocal) node.IsLocal = parseBool(ln.IsLocal)
node.IsEgressGateway = parseBool(ln.IsEgressGateway) node.IsEgressGateway = parseBool(ln.IsEgressGateway)
node.IsIngressGateway = parseBool(ln.IsIngressGateway) node.IsIngressGateway = parseBool(ln.IsIngressGateway)
@@ -619,11 +562,77 @@ func (ln *LegacyNode) ConvertToNewNode() (*Host, *Node) {
return &host, &node return &host, &node
} }
// Node.Legacy converts node to legacy format
func (n *Node) Legacy(h *Host, s *ServerConfig, net *Network) *LegacyNode {
l := LegacyNode{}
l.ID = n.ID.String()
l.HostID = h.ID.String()
l.Address = n.Address.String()
l.Address6 = n.Address6.String()
l.LocalAddress = h.LocalAddress.String()
l.Interfaces = n.Interfaces
l.Name = h.Name
l.NetworkSettings = *net
l.ListenPort = int32(h.ListenPort)
l.LocalListenPort = int32(h.LocalListenPort)
l.ProxyListenPort = int32(h.ProxyListenPort)
l.PublicKey = h.PublicKey.String()
l.Endpoint = n.EndpointIP.String()
l.PostUp = n.PostUp
l.PostDown = n.PostDown
//l.AllowedIPs =
l.PersistentKeepalive = int32(n.PersistentKeepalive)
l.AccessKey = ""
l.Interface = "netmaker"
//l.LastModified =
//l.ExpirationDateTime
//l.LastPeerUpdate
//l.LastCheckIn
l.MacAddress = h.MacAddress.String()
l.Password = h.HostPass
l.Network = n.Network
//l.IsRelayed = formatBool(n.Is)
//l.IsRelay = formatBool(n.IsRelay)
//l.IsDocker = formatBool(n.IsDocker)
//l.IsK8S = formatBool(n.IsK8S)
l.IsEgressGateway = formatBool(n.IsEgressGateway)
l.IsIngressGateway = formatBool(n.IsIngressGateway)
//l.EgressGatewayRanges = n.EgressGatewayRanges
//l.EgressGatewayNatEnabled = n.EgressGatewayNatEnabled
//l.RelayAddrs = n.RelayAddrs
//l.FailoverNode = n.FailoverNode
//l.IngressGatewayRange = n.IngressGatewayRange
//l.IngressGatewayRange6 = n.IngressGatewayRange6
l.IsStatic = formatBool(n.IsStatic)
l.UDPHolePunch = formatBool(true)
l.DNSOn = formatBool(n.DNSOn)
l.Action = n.Action
l.IsLocal = formatBool(n.IsLocal)
l.LocalRange = h.LocalRange.String()
l.IPForwarding = formatBool(h.IPForwarding)
l.OS = h.OS
l.MTU = int32(h.MTU)
l.Version = h.Version
l.Server = n.Server
l.TrafficKeys.Mine = h.TrafficKeyPublic
l.TrafficKeys.Server = s.TrafficKey
l.FirewallInUse = h.FirewallInUse
l.InternetGateway = h.InternetGateway.String()
l.Connected = formatBool(n.Connected)
//l.PendingDelete = formatBool(n.PendingDelete)
l.Proxy = n.Proxy
l.DefaultACL = n.DefaultACL
l.OwnerID = n.OwnerID
//l.Failover = n.Failover
return &l
}
// Node.NetworkSettings updates a node with network settings // Node.NetworkSettings updates a node with network settings
func (node *Node) NetworkSettings(n Network) { func (node *Node) NetworkSettings(n Network) {
_, cidr, _ := net.ParseCIDR(n.AddressRange) _, cidr, _ := net.ParseCIDR(n.AddressRange)
node.NetworkRange = *cidr node.NetworkRange = *cidr
_, cidr, _ = net.ParseCIDR(n.AddressRange6)
node.NetworkRange6 = *cidr
} }
func parseBool(s string) bool { func parseBool(s string) bool {

View File

@@ -6,6 +6,7 @@ import (
jwt "github.com/golang-jwt/jwt/v4" jwt "github.com/golang-jwt/jwt/v4"
"github.com/gravitl/netclient/nmproxy/manager" "github.com/gravitl/netclient/nmproxy/manager"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
) )
const PLACEHOLDER_KEY_TEXT = "ACCESS_KEY" const PLACEHOLDER_KEY_TEXT = "ACCESS_KEY"
@@ -202,10 +203,18 @@ type TrafficKeys struct {
// NodeGet - struct for a single node get response // NodeGet - struct for a single node get response
type NodeGet struct { type NodeGet struct {
Node Node `json:"node" bson:"node" yaml:"node"` Node LegacyNode `json:"node" bson:"node" yaml:"node"`
Host Host `json:"host" yaml:"host"` Host Host `json:"host" yaml:"host"`
// Peers are included in Node Peers []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"`
//Peers []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"` ServerConfig ServerConfig `json:"serverconfig" bson:"serverconfig" yaml:"serverconfig"`
PeerIDs PeerMap `json:"peerids,omitempty" bson:"peerids,omitempty" yaml:"peerids,omitempty"`
ProxyUpdate manager.ProxyManagerPayload `json:"proxy_update,omitempty" bson:"proxy_update,omitempty" yaml:"proxy_update,omitempty"`
}
// NodeJoinResponse data returned to node in response to join
type NodeJoinResponse struct {
Node Node `json:"node" bson:"node" yaml:"node"`
Host Host `json:"host" yaml:"host"`
ServerConfig ServerConfig `json:"serverconfig" bson:"serverconfig" yaml:"serverconfig"` ServerConfig ServerConfig `json:"serverconfig" bson:"serverconfig" yaml:"serverconfig"`
PeerIDs PeerMap `json:"peerids,omitempty" bson:"peerids,omitempty" yaml:"peerids,omitempty"` PeerIDs PeerMap `json:"peerids,omitempty" bson:"peerids,omitempty" yaml:"peerids,omitempty"`
ProxyUpdate manager.ProxyManagerPayload `json:"proxy_update,omitempty" bson:"proxy_update,omitempty" yaml:"proxy_update,omitempty"` ProxyUpdate manager.ProxyManagerPayload `json:"proxy_update,omitempty" bson:"proxy_update,omitempty" yaml:"proxy_update,omitempty"`
@@ -246,7 +255,7 @@ type ServerIDs struct {
// JoinData - struct to hold data required for node to join a network on server // JoinData - struct to hold data required for node to join a network on server
type JoinData struct { type JoinData struct {
Host Host `json:"host yaml:"host"` Host Host `json:"host" yaml:"host"`
Node Node `json:"node" yaml:"node"` Node Node `json:"node" yaml:"node"`
Key string `json:"key" yaml:"key"` Key string `json:"key" yaml:"key"`
} }

View File

@@ -41,7 +41,7 @@ func Ping(client mqtt.Client, msg mqtt.Message) {
} }
decrypted, decryptErr := decryptMsg(&node, msg.Payload()) decrypted, decryptErr := decryptMsg(&node, msg.Payload())
if decryptErr != nil { if decryptErr != nil {
logger.Log(0, "error decrypting when updating node ", node.ID, decryptErr.Error()) logger.Log(0, "error decrypting when updating node ", node.ID.String(), decryptErr.Error())
return return
} }
var checkin models.NodeCheckin var checkin models.NodeCheckin
@@ -49,19 +49,24 @@ func Ping(client mqtt.Client, msg mqtt.Message) {
logger.Log(1, "error unmarshaling payload ", err.Error()) logger.Log(1, "error unmarshaling payload ", err.Error())
return return
} }
host, err := logic.GetHost(node.HostID.String())
if err != nil {
logger.Log(0, "error retrieving host for node ", node.ID.String(), err.Error())
return
}
node.SetLastCheckIn() node.SetLastCheckIn()
node.Version = checkin.Version host.Version = checkin.Version
node.Connected = checkin.Connected node.Connected = checkin.Connected
node.Interfaces = checkin.Ifaces node.Interfaces = checkin.Ifaces
for i := range node.Interfaces { for i := range node.Interfaces {
node.Interfaces[i].AddressString = node.Interfaces[i].Address.String() node.Interfaces[i].AddressString = node.Interfaces[i].Address.String()
} }
if err := logic.UpdateNode(&node, &node); err != nil { if err := logic.UpdateNode(&node, &node); err != nil {
logger.Log(0, "error updating node", node.Name, node.ID, " on checkin", err.Error()) logger.Log(0, "error updating node", node.ID.String(), " on checkin", err.Error())
return return
} }
logger.Log(3, "ping processed for node", node.Name, node.ID) logger.Log(3, "ping processed for node", node.ID.String())
// --TODO --set client version once feature is implemented. // --TODO --set client version once feature is implemented.
//node.SetClientVersion(msg.Payload()) //node.SetClientVersion(msg.Payload())
}() }()
@@ -85,15 +90,15 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
logger.Log(1, "failed to decrypt message for node ", id, decryptErr.Error()) logger.Log(1, "failed to decrypt message for node ", id, decryptErr.Error())
return return
} }
var newNode models.LegacyNode var newNode models.Node
if err := json.Unmarshal(decrypted, &newNode); err != nil { if err := json.Unmarshal(decrypted, &newNode); err != nil {
logger.Log(1, "error unmarshaling payload ", err.Error()) logger.Log(1, "error unmarshaling payload ", err.Error())
return return
} }
ifaceDelta := logic.IfaceDelta(&currentNode, &newNode) ifaceDelta := logic.IfaceDelta(&currentNode, &newNode)
if servercfg.Is_EE && ifaceDelta { if servercfg.Is_EE && ifaceDelta {
if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID, currentNode.Network); err != nil { if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID.String(), currentNode.Network); err != nil {
logger.Log(1, "failed to reset failover list during node update", currentNode.Name, currentNode.Network) logger.Log(1, "failed to reset failover list during node update", currentNode.ID.String(), currentNode.Network)
} }
} }
newNode.SetLastCheckIn() newNode.SetLastCheckIn()
@@ -102,12 +107,13 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
return return
} }
if ifaceDelta { // reduce number of unneeded updates, by only sending on iface changes if ifaceDelta { // reduce number of unneeded updates, by only sending on iface changes
if err = PublishPeerUpdate(&currentNode, true); err != nil { if err = PublishPeerUpdate(currentNode.Network, true); err != nil {
logger.Log(0, "error updating peers when node", currentNode.Name, currentNode.ID, "informed the server of an interface change", err.Error()) logger.Log(0, "error updating peers when node", currentNode.ID.String(), "informed the server of an interface change", err.Error())
} }
} }
logger.Log(1, "updated node", id, newNode.Name) logger.Log(1, "updated node", id, newNode.ID.String())
}() }()
} }
@@ -140,31 +146,31 @@ func UpdateMetrics(client mqtt.Client, msg mqtt.Message) {
shouldUpdate := updateNodeMetrics(&currentNode, &newMetrics) shouldUpdate := updateNodeMetrics(&currentNode, &newMetrics)
if err = logic.UpdateMetrics(id, &newMetrics); err != nil { if err = logic.UpdateMetrics(id, &newMetrics); err != nil {
logger.Log(1, "faield to update node metrics", id, currentNode.Name, err.Error()) logger.Log(1, "faield to update node metrics", id, err.Error())
return return
} }
if servercfg.IsMetricsExporter() { if servercfg.IsMetricsExporter() {
if err := pushMetricsToExporter(newMetrics); err != nil { if err := pushMetricsToExporter(newMetrics); err != nil {
logger.Log(2, fmt.Sprintf("failed to push node: [%s] metrics to exporter, err: %v", logger.Log(2, fmt.Sprintf("failed to push node: [%s] metrics to exporter, err: %v",
currentNode.Name, err)) currentNode.ID, err))
} }
} }
if newMetrics.Connectivity != nil { if newMetrics.Connectivity != nil {
err := logic.EnterpriseFailoverFunc(&currentNode) err := logic.EnterpriseFailoverFunc(&currentNode)
if err != nil { if err != nil {
logger.Log(0, "failed to failover for node", currentNode.Name, "on network", currentNode.Network, "-", err.Error()) logger.Log(0, "failed to failover for node", currentNode.ID.String(), "on network", currentNode.Network, "-", err.Error())
} }
} }
if shouldUpdate { if shouldUpdate {
logger.Log(2, "updating peers after node", currentNode.Name, currentNode.Network, "detected connectivity issues") logger.Log(2, "updating peers after node", currentNode.ID.String(), currentNode.Network, "detected connectivity issues")
if err = PublishSinglePeerUpdate(&currentNode); err != nil { if err = PublishSinglePeerUpdate(&currentNode); err != nil {
logger.Log(0, "failed to publish update after failover peer change for node", currentNode.Name, currentNode.Network) logger.Log(0, "failed to publish update after failover peer change for node", currentNode.ID.String(), currentNode.Network)
} }
} }
logger.Log(1, "updated node metrics", id, currentNode.Name) logger.Log(1, "updated node metrics", id)
}() }()
} }
} }
@@ -189,47 +195,29 @@ func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
} }
switch decrypted[0] { switch decrypted[0] {
case ncutils.ACK: case ncutils.ACK:
currentServerNode, err := logic.GetNetworkServerLocal(currentNode.Network) //do we still need this
if err != nil {
return
}
if err := logic.ServerUpdate(&currentServerNode, false); err != nil {
logger.Log(1, "server node:", currentServerNode.ID, "failed update")
return
}
case ncutils.DONE: case ncutils.DONE:
updateNodePeers(&currentNode) updateNodePeers(&currentNode)
} }
logger.Log(1, "sent peer updates after signal received from", id, currentNode.Name) logger.Log(1, "sent peer updates after signal received from", id)
}() }()
} }
func updateNodePeers(currentNode *models.LegacyNode) { func updateNodePeers(currentNode *models.Node) {
currentServerNode, err := logic.GetNetworkServerLocal(currentNode.Network) if err := PublishPeerUpdate(currentNode.Network, false); err != nil {
if err != nil { logger.Log(1, "error publishing peer update ", err.Error())
logger.Log(1, "failed to get server node failed update\n", err.Error())
return return
} }
if err := logic.ServerUpdate(&currentServerNode, false); err != nil {
logger.Log(1, "server node:", currentServerNode.ID, "failed update")
return
}
if logic.IsLeader(&currentServerNode) {
if err := PublishPeerUpdate(currentNode, false); err != nil {
logger.Log(1, "error publishing peer update ", err.Error())
return
}
}
} }
func updateNodeMetrics(currentNode *models.LegacyNode, newMetrics *models.Metrics) bool { func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) bool {
if newMetrics.FailoverPeers == nil { if newMetrics.FailoverPeers == nil {
newMetrics.FailoverPeers = make(map[string]string) newMetrics.FailoverPeers = make(map[string]string)
} }
oldMetrics, err := logic.GetMetrics(currentNode.ID) oldMetrics, err := logic.GetMetrics(currentNode.ID.String())
if err != nil { if err != nil {
logger.Log(1, "error finding old metrics for node", currentNode.ID, currentNode.Name) logger.Log(1, "error finding old metrics for node", currentNode.ID.String())
return false return false
} }
if oldMetrics.FailoverPeers == nil { if oldMetrics.FailoverPeers == nil {
@@ -237,8 +225,8 @@ func updateNodeMetrics(currentNode *models.LegacyNode, newMetrics *models.Metric
} }
var attachedClients []models.ExtClient var attachedClients []models.ExtClient
if currentNode.IsIngressGateway == "yes" { if currentNode.IsIngressGateway {
clients, err := logic.GetExtClientsByID(currentNode.ID, currentNode.Network) clients, err := logic.GetExtClientsByID(currentNode.ID.String(), currentNode.Network)
if err == nil { if err == nil {
attachedClients = clients attachedClients = clients
} }
@@ -292,12 +280,12 @@ func updateNodeMetrics(currentNode *models.LegacyNode, newMetrics *models.Metric
return false return false
} }
for _, node := range nodes { for _, node := range nodes {
if !newMetrics.Connectivity[node.ID].Connected && if !newMetrics.Connectivity[node.ID.String()].Connected &&
len(newMetrics.Connectivity[node.ID].NodeName) > 0 && len(newMetrics.Connectivity[node.ID.String()].NodeName) > 0 &&
node.Connected == "yes" && node.Connected == true &&
len(node.FailoverNode) > 0 && len(node.FailoverNode) > 0 &&
node.Failover != "yes" { !node.Failover {
newMetrics.FailoverPeers[node.ID] = node.FailoverNode newMetrics.FailoverPeers[node.ID.String()] = node.FailoverNode.String()
} }
} }
shouldUpdate := len(oldMetrics.FailoverPeers) == 0 && len(newMetrics.FailoverPeers) > 0 shouldUpdate := len(oldMetrics.FailoverPeers) == 0 && len(newMetrics.FailoverPeers) > 0

View File

@@ -9,48 +9,31 @@ import (
"github.com/gravitl/netclient/nmproxy/manager" "github.com/gravitl/netclient/nmproxy/manager"
"github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/logic/metrics"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/servercfg"
"github.com/gravitl/netmaker/serverctl" "github.com/gravitl/netmaker/serverctl"
) )
// PublishPeerUpdate --- deterines and publishes a peer update to all the peers of a node // PublishPeerUpdate --- deterines and publishes a peer update to all the peers of a node
func PublishPeerUpdate(newNode *models.LegacyNode, publishToSelf bool) error { func PublishPeerUpdate(network string, publishToSelf bool) error {
if !servercfg.IsMessageQueueBackend() { if !servercfg.IsMessageQueueBackend() {
return nil return nil
} }
networkNodes, err := logic.GetNetworkNodes(newNode.Network) networkNodes, err := logic.GetNetworkNodes(network)
if err != nil { if err != nil {
logger.Log(1, "err getting Network Nodes", err.Error()) logger.Log(1, "err getting Network Nodes", err.Error())
return err return err
} }
for _, node := range networkNodes { for _, node := range networkNodes {
if node.IsServer == "yes" {
if servercfg.IsProxyEnabled() {
err := PublishProxyPeerUpdate(&node)
if err != nil {
logger.Log(0, "failed to send proxy update for server: ", err.Error())
}
}
continue
}
if !publishToSelf && newNode.ID == node.ID {
//skip self
continue
}
err = PublishSinglePeerUpdate(&node) err = PublishSinglePeerUpdate(&node)
if err != nil { if err != nil {
logger.Log(1, "failed to publish peer update to node", node.Name, "on network", node.Network, ":", err.Error()) logger.Log(1, "failed to publish peer update to node", node.ID.String(), "on network", node.Network, ":", err.Error())
} }
} }
return err return err
} }
func PublishProxyPeerUpdate(node *models.LegacyNode) error { func PublishProxyPeerUpdate(node *models.Node) error {
proxyUpdate, err := logic.GetPeersForProxy(node, false) proxyUpdate, err := logic.GetPeersForProxy(node, false)
if err != nil { if err != nil {
return err return err
@@ -65,7 +48,7 @@ func PublishProxyPeerUpdate(node *models.LegacyNode) error {
} }
// PublishSinglePeerUpdate --- determines and publishes a peer update to one node // PublishSinglePeerUpdate --- determines and publishes a peer update to one node
func PublishSinglePeerUpdate(node *models.LegacyNode) error { func PublishSinglePeerUpdate(node *models.Node) error {
peerUpdate, err := logic.GetPeerUpdate(node) peerUpdate, err := logic.GetPeerUpdate(node)
if err != nil { if err != nil {
@@ -89,16 +72,8 @@ func PublishSinglePeerUpdate(node *models.LegacyNode) error {
} }
// PublishPeerUpdate --- publishes a peer update to all the peers of a node // PublishPeerUpdate --- publishes a peer update to all the peers of a node
func PublishExtPeerUpdate(node *models.LegacyNode) error { func PublishExtPeerUpdate(node *models.Node) error {
var err error var err error
if logic.IsLocalServer(node) {
if err = logic.ServerUpdate(node, false); err != nil {
logger.Log(1, "server node:", node.ID, "failed to update peers with ext clients")
return err
} else {
return nil
}
}
if !servercfg.IsMessageQueueBackend() { if !servercfg.IsMessageQueueBackend() {
return nil return nil
} }
@@ -120,22 +95,21 @@ func PublishExtPeerUpdate(node *models.LegacyNode) error {
if err = publish(node, fmt.Sprintf("peers/%s/%s", node.Network, node.ID), data); err != nil { if err = publish(node, fmt.Sprintf("peers/%s/%s", node.Network, node.ID), data); err != nil {
return err return err
} }
go PublishPeerUpdate(node, false) go PublishPeerUpdate(node.Network, false)
return nil return nil
} }
// NodeUpdate -- publishes a node update // NodeUpdate -- publishes a node update
func NodeUpdate(node *models.LegacyNode) error { func NodeUpdate(node *models.Node) error {
var err error var err error
if !servercfg.IsMessageQueueBackend() || node.IsServer == "yes" { if !servercfg.IsMessageQueueBackend() {
return nil return nil
} }
logger.Log(3, "publishing node update to "+node.Name) logger.Log(3, "publishing node update to "+node.ID.String())
if len(node.NetworkSettings.AccessKeys) > 0 { //if len(node.NetworkSettings.AccessKeys) > 0 {
node.NetworkSettings.AccessKeys = []models.AccessKey{} // not to be sent (don't need to spread access keys around the network; we need to know how to reach other nodes, not become them) //node.NetworkSettings.AccessKeys = []models.AccessKey{} // not to be sent (don't need to spread access keys around the network; we need to know how to reach other nodes, not become them)
} //}
data, err := json.Marshal(node) data, err := json.Marshal(node)
if err != nil { if err != nil {
@@ -143,13 +117,13 @@ func NodeUpdate(node *models.LegacyNode) error {
return err return err
} }
if err = publish(node, fmt.Sprintf("update/%s/%s", node.Network, node.ID), data); err != nil { if err = publish(node, fmt.Sprintf("update/%s/%s", node.Network, node.ID), data); err != nil {
logger.Log(2, "error publishing node update to peer ", node.ID, err.Error()) logger.Log(2, "error publishing node update to peer ", node.ID.String(), err.Error())
return err return err
} }
if node.Proxy { if node.Proxy {
err = PublishProxyPeerUpdate(node) err = PublishProxyPeerUpdate(node)
if err != nil { if err != nil {
logger.Log(1, "failed to publish proxy update to node", node.Name, "on network", node.Network, ":", err.Error()) logger.Log(1, "failed to publish proxy update to node", node.ID.String(), "on network", node.Network, ":", err.Error())
} }
} }
@@ -157,15 +131,11 @@ func NodeUpdate(node *models.LegacyNode) error {
} }
// ProxyUpdate -- publishes updates to peers related to proxy // ProxyUpdate -- publishes updates to peers related to proxy
func ProxyUpdate(proxyPayload *manager.ProxyManagerPayload, node *models.LegacyNode) error { func ProxyUpdate(proxyPayload *manager.ProxyManagerPayload, node *models.Node) error {
if !servercfg.IsMessageQueueBackend() || !node.Proxy { if !servercfg.IsMessageQueueBackend() || !node.Proxy {
return nil return nil
} }
if node.IsServer == "yes" { logger.Log(3, "publishing proxy update to "+node.ID.String())
logic.ProxyMgmChan <- proxyPayload
return nil
}
logger.Log(3, "publishing proxy update to "+node.Name)
data, err := json.Marshal(proxyPayload) data, err := json.Marshal(proxyPayload)
if err != nil { if err != nil {
@@ -173,7 +143,7 @@ func ProxyUpdate(proxyPayload *manager.ProxyManagerPayload, node *models.LegacyN
return err return err
} }
if err = publish(node, fmt.Sprintf("proxy/%s/%s", node.Network, node.ID), data); err != nil { if err = publish(node, fmt.Sprintf("proxy/%s/%s", node.Network, node.ID), data); err != nil {
logger.Log(2, "error publishing proxy update to peer ", node.ID, err.Error()) logger.Log(2, "error publishing proxy update to peer ", node.ID.String(), err.Error())
return err return err
} }
return nil return nil
@@ -204,45 +174,18 @@ func sendPeers() {
logger.Log(3, "error occurred on timer,", err.Error()) logger.Log(3, "error occurred on timer,", err.Error())
} }
collectServerMetrics(networks[:]) //collectServerMetrics(networks[:])
} }
for _, network := range networks { for _, network := range networks {
serverNode, errN := logic.GetNetworkServerLocal(network.NetID) if force {
if errN == nil { logger.Log(2, "sending scheduled peer update (5 min)")
serverNode.SetLastCheckIn() err = PublishPeerUpdate(network.NetID, false)
if err := logic.UpdateNode(&serverNode, &serverNode); err != nil { if err != nil {
logger.Log(0, "failed checkin for server node", serverNode.Name, "on network", network.NetID, err.Error()) logger.Log(1, "error publishing udp port updates for network", network.NetID)
logger.Log(1, err.Error())
} }
} }
isLeader := logic.IsLeader(&serverNode)
if errN == nil && isLeader {
if network.DefaultUDPHolePunch == "yes" {
if logic.ShouldPublishPeerPorts(&serverNode) || force {
if force {
logger.Log(2, "sending scheduled peer update (5 min)")
}
err = PublishPeerUpdate(&serverNode, false)
if err != nil {
logger.Log(1, "error publishing udp port updates for network", network.NetID)
logger.Log(1, errN.Error())
}
}
}
} else {
if isLeader {
logger.Log(1, "unable to retrieve leader for network ", network.NetID)
}
logger.Log(2, "server checkin complete for server", serverNode.Name, "on network", network.NetID)
serverctl.SyncServerNetwork(network.NetID)
if errN != nil {
logger.Log(1, errN.Error())
}
if servercfg.IsProxyEnabled() {
serverctl.SyncServerNetworkWithProxy()
}
}
} }
} }
@@ -255,54 +198,54 @@ func ServerStartNotify() error {
for i := range nodes { for i := range nodes {
nodes[i].Action = models.NODE_FORCE_UPDATE nodes[i].Action = models.NODE_FORCE_UPDATE
if err = NodeUpdate(&nodes[i]); err != nil { if err = NodeUpdate(&nodes[i]); err != nil {
logger.Log(1, "error when notifying node", nodes[i].Name, " - ", nodes[i].ID, "of a server startup") logger.Log(1, "error when notifying node", nodes[i].ID.String(), "of a server startup")
} }
} }
return nil return nil
} }
// function to collect and store metrics for server nodes // function to collect and store metrics for server nodes
func collectServerMetrics(networks []models.Network) { //func collectServerMetrics(networks []models.Network) {
if !servercfg.Is_EE { // if !servercfg.Is_EE {
return // return
} // }
if len(networks) > 0 { // if len(networks) > 0 {
for i := range networks { // for i := range networks {
currentNetworkNodes, err := logic.GetNetworkNodes(networks[i].NetID) // currentNetworkNodes, err := logic.GetNetworkNodes(networks[i].NetID)
if err != nil { // if err != nil {
continue // continue
} // }
currentServerNodes := logic.GetServerNodes(networks[i].NetID) // currentServerNodes := logic.GetServerNodes(networks[i].NetID)
if len(currentServerNodes) > 0 { // if len(currentServerNodes) > 0 {
for i := range currentServerNodes { // for i := range currentServerNodes {
if logic.IsLocalServer(&currentServerNodes[i]) { // if logic.IsLocalServer(&currentServerNodes[i]) {
serverMetrics := logic.CollectServerMetrics(currentServerNodes[i].ID, currentNetworkNodes) // serverMetrics := logic.CollectServerMetrics(currentServerNodes[i].ID, currentNetworkNodes)
if serverMetrics != nil { // if serverMetrics != nil {
serverMetrics.NodeName = currentServerNodes[i].Name // serverMetrics.NodeName = currentServerNodes[i].Name
serverMetrics.NodeID = currentServerNodes[i].ID // serverMetrics.NodeID = currentServerNodes[i].ID
serverMetrics.IsServer = "yes" // serverMetrics.IsServer = "yes"
serverMetrics.Network = currentServerNodes[i].Network // serverMetrics.Network = currentServerNodes[i].Network
if err = metrics.GetExchangedBytesForNode(&currentServerNodes[i], serverMetrics); err != nil { // if err = metrics.GetExchangedBytesForNode(&currentServerNodes[i], serverMetrics); err != nil {
logger.Log(1, fmt.Sprintf("failed to update exchanged bytes info for server: %s, err: %v", // logger.Log(1, fmt.Sprintf("failed to update exchanged bytes info for server: %s, err: %v",
currentServerNodes[i].Name, err)) // currentServerNodes[i].Name, err))
} // }
updateNodeMetrics(&currentServerNodes[i], serverMetrics) // updateNodeMetrics(&currentServerNodes[i], serverMetrics)
if err = logic.UpdateMetrics(currentServerNodes[i].ID, serverMetrics); err != nil { // if err = logic.UpdateMetrics(currentServerNodes[i].ID, serverMetrics); err != nil {
logger.Log(1, "failed to update metrics for server node", currentServerNodes[i].ID) // logger.Log(1, "failed to update metrics for server node", currentServerNodes[i].ID)
} // }
if servercfg.IsMetricsExporter() { // if servercfg.IsMetricsExporter() {
logger.Log(2, "-------------> SERVER METRICS: ", fmt.Sprintf("%+v", serverMetrics)) // logger.Log(2, "-------------> SERVER METRICS: ", fmt.Sprintf("%+v", serverMetrics))
if err := pushMetricsToExporter(*serverMetrics); err != nil { // if err := pushMetricsToExporter(*serverMetrics); err != nil {
logger.Log(2, "failed to push server metrics to exporter: ", err.Error()) // logger.Log(2, "failed to push server metrics to exporter: ", err.Error())
} // }
} // }
} // }
} // }
} // }
} // }
} // }
} // }
} //}
func pushMetricsToExporter(metrics models.Metrics) error { func pushMetricsToExporter(metrics models.Metrics) error {
logger.Log(2, "----> Pushing metrics to exporter") logger.Log(2, "----> Pushing metrics to exporter")

View File

@@ -11,10 +11,14 @@ import (
"github.com/gravitl/netmaker/netclient/ncutils" "github.com/gravitl/netmaker/netclient/ncutils"
) )
func decryptMsg(node *models.LegacyNode, msg []byte) ([]byte, error) { func decryptMsg(node *models.Node, msg []byte) ([]byte, error) {
if len(msg) <= 24 { // make sure message is of appropriate length if len(msg) <= 24 { // make sure message is of appropriate length
return nil, fmt.Errorf("recieved invalid message from broker %v", msg) return nil, fmt.Errorf("recieved invalid message from broker %v", msg)
} }
host, err := logic.GetHost(node.HostID.String())
if err != nil {
return nil, err
}
trafficKey, trafficErr := logic.RetrievePrivateTrafficKey() // get server private key trafficKey, trafficErr := logic.RetrievePrivateTrafficKey() // get server private key
if trafficErr != nil { if trafficErr != nil {
@@ -24,19 +28,19 @@ func decryptMsg(node *models.LegacyNode, msg []byte) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
nodePubTKey, err := ncutils.ConvertBytesToKey(node.TrafficKeys.Mine) nodePubTKey, err := ncutils.ConvertBytesToKey(host.TrafficKeyPublic)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if strings.Contains(node.Version, "0.10.0") { if strings.Contains(host.Version, "0.10.0") {
return ncutils.BoxDecrypt(msg, nodePubTKey, serverPrivTKey) return ncutils.BoxDecrypt(msg, nodePubTKey, serverPrivTKey)
} }
return ncutils.DeChunk(msg, nodePubTKey, serverPrivTKey) return ncutils.DeChunk(msg, nodePubTKey, serverPrivTKey)
} }
func encryptMsg(node *models.LegacyNode, msg []byte) ([]byte, error) { func encryptMsg(node *models.Node, msg []byte) ([]byte, error) {
// fetch server public key to be certain hasn't changed in transit // fetch server public key to be certain hasn't changed in transit
trafficKey, trafficErr := logic.RetrievePrivateTrafficKey() trafficKey, trafficErr := logic.RetrievePrivateTrafficKey()
if trafficErr != nil { if trafficErr != nil {
@@ -48,19 +52,23 @@ func encryptMsg(node *models.LegacyNode, msg []byte) ([]byte, error) {
return nil, err return nil, err
} }
nodePubKey, err := ncutils.ConvertBytesToKey(node.TrafficKeys.Mine) host, err := logic.GetHost(node.HostID.String())
if err != nil {
return nil, err
}
nodePubKey, err := ncutils.ConvertBytesToKey(host.TrafficKeyPublic)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if strings.Contains(node.Version, "0.10.0") { if strings.Contains(host.Version, "0.10.0") {
return ncutils.BoxEncrypt(msg, nodePubKey, serverPrivKey) return ncutils.BoxEncrypt(msg, nodePubKey, serverPrivKey)
} }
return ncutils.Chunk(msg, nodePubKey, serverPrivKey) return ncutils.Chunk(msg, nodePubKey, serverPrivKey)
} }
func publish(node *models.LegacyNode, dest string, msg []byte) error { func publish(node *models.Node, dest string, msg []byte) error {
encrypted, encryptErr := encryptMsg(node, msg) encrypted, encryptErr := encryptMsg(node, msg)
if encryptErr != nil { if encryptErr != nil {
return encryptErr return encryptErr

View File

@@ -1,9 +0,0 @@
FROM debian:latest
RUN apt-get update && apt-get -y install systemd procps
WORKDIR /root/
COPY netclient .
CMD ["./netclient checkin"]

View File

@@ -1,44 +0,0 @@
package auth
import (
"os"
"github.com/gravitl/netmaker/netclient/ncutils"
// "os"
)
// StoreSecret - stores auth secret locally
func StoreSecret(key string, network string) error {
d1 := []byte(key)
return os.WriteFile(ncutils.GetNetclientPathSpecific()+"secret-"+network, d1, 0600)
}
// RetrieveSecret - fetches secret locally
func RetrieveSecret(network string) (string, error) {
dat, err := ncutils.GetFileWithRetry(ncutils.GetNetclientPathSpecific()+"secret-"+network, 3)
return string(dat), err
}
// StoreTrafficKey - stores traffic key
func StoreTrafficKey(key *[32]byte, network string) error {
var data, err = ncutils.ConvertKeyToBytes(key)
if err != nil {
return err
}
return os.WriteFile(ncutils.GetNetclientPathSpecific()+"traffic-"+network, data, 0600)
}
// RetrieveTrafficKey - reads traffic file locally
func RetrieveTrafficKey(network string) (*[32]byte, error) {
data, err := ncutils.GetFileWithRetry(ncutils.GetNetclientPathSpecific()+"traffic-"+network, 2)
if err != nil {
return nil, err
}
return ncutils.ConvertBytesToKey(data)
}
// Configuraion - struct for mac and pass
type Configuration struct {
MacAddress string
Password string
}

View File

@@ -1,42 +0,0 @@
#!/bin/bash
VERSION=${VERSION:-"develop"}
echo "build with version tag: $VERSION"
readonly __HOST_ARCH=${1:-"amd64"} # change this for your machine.
readonly __HOST_GOOSE=${2:-"linux"} # change this for your machine.
readonly __EXEC_DIR=$(dirname "$(realpath $0)") && cd $__EXEC_DIR
__darwin=( arm64 amd64 )
__linux=( amd64 arm arm64 mips mips64 mips64le mipsle ppc64 ppc64le riscv64 s390x 386 )
__freebsd=( amd64 arm arm64 386 )
__windows=( amd64 arm arm64 386 )
function build
{
local _goarch=${1:-"None"} && if [[ $_goarch == "None" ]]; then exit 1; fi
local _goose="${2:-"None"}" && if [[ $_goose == "None" ]]; then exit 1; fi
local _goarm=${3:-""}
local _out=build/netclient-$_goose-$_goarch$_goarm && mkdir -p build
if [ "$_goarch" == "arm" ] && [ "$_goarm" == "" ]; then
build $_goarch $_goose 5 && build $_goarch $_goose 6 && build $_goarch $_goose 7
else
if [[ $_goarch == mips* ]]; then
#At present GOMIPS64 based binaries are not generated through this script, more details about GOMIPS environment variables in https://go.dev/doc/asm#mips .
echo $_out-softfloat
GOARM=$_goarm GOMIPS=softfloat GOARCH=$_goarch GOOS=$_goose GOHOSTARCH=$__HOST_ARCH CGO_ENABLED=0 go build -ldflags="-X 'main.version=$VERSION'" -o $_out-softfloat
echo $_out
GOARM=$_goarm GOARCH=$_goarch GOOS=$_goose GOHOSTARCH=$__HOST_ARCH CGO_ENABLED=0 go build -ldflags="-X 'main.version=$VERSION'" -o $_out
else
echo $_out
GOARM=$_goarm GOARCH=$_goarch GOOS=$_goose GOHOSTARCH=$__HOST_ARCH CGO_ENABLED=0 go build -ldflags="-X 'main.version=$VERSION'" -o $_out
fi
fi
}
for arch in ${__linux[*]}; do build "$arch" "linux"; done
for arch in ${__freebsd[*]}; do build "$arch" "freebsd"; done
for arch in ${__darwin[*]}; do build "$arch" "darwin"; done
for arch in ${__windows[*]}; do build "$arch" "windows"; done

View File

@@ -1,15 +0,0 @@
[Unit]
Description=Netclient Daemon
Documentation=https://docs.netmaker.org https://k8s.netmaker.org
After=network-online.target
Wants=network-online.target
[Service]
User=root
Type=simple
ExecStart=/sbin/netclient daemon
Restart=on-failure
RestartSec=15s
[Install]
WantedBy=multi-user.target

View File

@@ -1,148 +0,0 @@
package cli_options
import (
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/command"
"github.com/gravitl/netmaker/netclient/config"
"github.com/urfave/cli/v2"
)
// GetCommands - return commands that CLI uses
func GetCommands(cliFlags []cli.Flag) []*cli.Command {
return []*cli.Command{
{
Name: "join",
Usage: "Join a Netmaker network.",
Flags: cliFlags,
Action: func(c *cli.Context) error {
parseVerbosity(c)
cfg, pvtKey, err := config.GetCLIConfig(c)
if err != nil {
return err
}
err = command.Join(&cfg, pvtKey)
return err
},
},
{
Name: "leave",
Usage: "Leave a Netmaker network.",
Flags: cliFlags,
// the action, or code that will be executed when
// we execute our `ns` command
Action: func(c *cli.Context) error {
parseVerbosity(c)
cfg, _, err := config.GetCLIConfig(c)
if err != nil {
return err
}
err = command.Leave(&cfg)
return err
},
},
{
Name: "pull",
Usage: "Pull latest configuration and peers from server.",
Flags: cliFlags,
// the action, or code that will be executed when
// we execute our `ns` command
Action: func(c *cli.Context) error {
parseVerbosity(c)
cfg, _, err := config.GetCLIConfig(c)
if err != nil {
return err
}
err = command.Pull(&cfg)
return err
},
},
{
Name: "list",
Usage: "Get list of networks.",
Flags: cliFlags,
// the action, or code that will be executed when
// we execute our `ns` command
Action: func(c *cli.Context) error {
parseVerbosity(c)
cfg, _, err := config.GetCLIConfig(c)
if err != nil {
return err
}
err = command.List(cfg)
return err
},
},
{
Name: "uninstall",
Usage: "Uninstall the netclient system service.",
Flags: cliFlags,
// the action, or code that will be executed when
// we execute our `ns` command
Action: func(c *cli.Context) error {
parseVerbosity(c)
err := command.Uninstall()
return err
},
},
{
Name: "daemon",
Usage: "run netclient as daemon",
Flags: cliFlags,
Action: func(c *cli.Context) error {
// set max verbosity for daemon regardless
logger.Verbosity = 4
err := command.Daemon()
return err
},
},
{
Name: "install",
Usage: "install binary and daemon",
Flags: cliFlags,
Action: func(c *cli.Context) error {
parseVerbosity(c)
return command.Install()
},
},
{
Name: "connect",
Usage: "connect netclient to a given network if disconnected",
Flags: cliFlags,
Action: func(c *cli.Context) error {
parseVerbosity(c)
cfg, _, err := config.GetCLIConfig(c)
if err != nil {
return err
}
return command.Connect(cfg)
},
},
{
Name: "disconnect",
Usage: "disconnect netclient from a given network if connected",
Flags: cliFlags,
Action: func(c *cli.Context) error {
parseVerbosity(c)
cfg, _, err := config.GetCLIConfig(c)
if err != nil {
return err
}
return command.Disconnect(cfg)
},
},
}
}
// == Private funcs ==
func parseVerbosity(c *cli.Context) {
if c.Bool("v") {
logger.Verbosity = 1
} else if c.Bool("vv") {
logger.Verbosity = 2
} else if c.Bool("vvv") {
logger.Verbosity = 3
} else if c.Bool("vvvv") {
logger.Verbosity = 4
}
}

View File

@@ -1,236 +0,0 @@
package cli_options
import "github.com/urfave/cli/v2"
// GetFlags - Returns the flags used by cli
func GetFlags(hostname string) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "network",
Aliases: []string{"n"},
EnvVars: []string{"NETCLIENT_NETWORK"},
Value: "all",
Usage: "Network to perform specified action against.",
},
&cli.StringFlag{
Name: "proxy",
// Aliases: []string{"np"},
EnvVars: []string{"NETMAKER_PROXY"},
Value: "off",
Usage: "To enable/disable proxy.",
},
&cli.StringFlag{
Name: "password",
Aliases: []string{"p"},
EnvVars: []string{"NETCLIENT_PASSWORD"},
Value: "",
Usage: "Password for authenticating with netmaker.",
},
&cli.StringFlag{
Name: "endpoint",
Aliases: []string{"e"},
EnvVars: []string{"NETCLIENT_ENDPOINT"},
Value: "",
Usage: "Reachable (usually public) address for WireGuard (not the private WG address).",
},
&cli.StringFlag{
Name: "macaddress",
Aliases: []string{"m"},
EnvVars: []string{"NETCLIENT_MACADDRESS"},
Value: "",
Usage: "Mac Address for this machine. Used as a unique identifier within Netmaker network.",
},
&cli.StringFlag{
Name: "publickey",
Aliases: []string{"pubkey"},
EnvVars: []string{"NETCLIENT_PUBLICKEY"},
Value: "",
Usage: "Public Key for WireGuard Interface.",
},
&cli.StringFlag{
Name: "privatekey",
Aliases: []string{"privkey"},
EnvVars: []string{"NETCLIENT_PRIVATEKEY"},
Value: "",
Usage: "Private Key for WireGuard Interface.",
},
&cli.StringFlag{
Name: "port",
EnvVars: []string{"NETCLIENT_PORT"},
Value: "",
Usage: "Port for WireGuard Interface.",
},
&cli.IntFlag{
Name: "keepalive",
EnvVars: []string{"NETCLIENT_KEEPALIVE"},
Value: 0,
Usage: "Default PersistentKeepAlive for Peers in WireGuard Interface.",
},
&cli.StringFlag{
Name: "operatingsystem",
Aliases: []string{"os"},
EnvVars: []string{"NETCLIENT_OS"},
Value: "",
Usage: "Operating system of machine (linux, darwin, windows, freebsd).",
},
&cli.StringFlag{
Name: "publicipservice",
Aliases: []string{"ip-service"},
EnvVars: []string{"NETCLIENT_IP_SERVICE"},
Value: "",
Usage: "The service to call to obtain the public IP of the machine that is running netclient.",
},
&cli.StringFlag{
Name: "name",
EnvVars: []string{"NETCLIENT_NAME"},
Value: hostname,
Usage: "Identifiable name for machine within Netmaker network.",
},
&cli.StringFlag{
Name: "localaddress",
EnvVars: []string{"NETCLIENT_LOCALADDRESS"},
Value: "",
Usage: "Local address for machine. Can be used in place of Endpoint for machines on the same LAN.",
},
&cli.StringFlag{
Name: "isstatic",
Aliases: []string{"st"},
EnvVars: []string{"NETCLIENT_IS_STATIC"},
Value: "",
Usage: "Indicates if client is static by default (will not change addresses automatically).",
},
&cli.StringFlag{
Name: "address",
Aliases: []string{"a"},
EnvVars: []string{"NETCLIENT_ADDRESS"},
Value: "",
Usage: "WireGuard address for machine within Netmaker network.",
},
&cli.StringFlag{
Name: "addressIPv6",
Aliases: []string{"a6"},
EnvVars: []string{"NETCLIENT_ADDRESSIPV6"},
Value: "",
Usage: "WireGuard address for machine within Netmaker network.",
},
&cli.StringFlag{
Name: "interface",
Aliases: []string{"i"},
EnvVars: []string{"NETCLIENT_INTERFACE"},
Value: "",
Usage: "WireGuard local network interface name.",
},
&cli.StringFlag{
Name: "apiserver",
EnvVars: []string{"NETCLIENT_API_SERVER"},
Value: "",
Usage: "Address + API Port (e.g. 1.2.3.4:8081) of Netmaker server.",
},
&cli.StringFlag{
Name: "key",
Aliases: []string{"k"},
EnvVars: []string{"NETCLIENT_ACCESSKEY"},
Value: "",
Usage: "Access Key for signing up machine with Netmaker server during initial 'add'.",
},
&cli.StringFlag{
Name: "token",
Aliases: []string{"t"},
EnvVars: []string{"NETCLIENT_ACCESSTOKEN"},
Value: "",
Usage: "Access Token for signing up machine with Netmaker server during initial 'add'.",
},
&cli.StringFlag{
Name: "server",
Aliases: []string{"s"},
EnvVars: []string{"HOST_SERVER"},
Value: "",
Usage: "Host server (domain of API) [Example: api.example.com]. Do not include \"http(s)://\" use it for the Single Sign-on along with the network parameter",
},
&cli.StringFlag{
Name: "user",
Aliases: []string{"u"},
EnvVars: []string{"USER_NAME"},
Value: "",
Usage: "User name provided upon joins if joining over basic auth is desired.",
},
&cli.StringFlag{
Name: "localrange",
EnvVars: []string{"NETCLIENT_LOCALRANGE"},
Value: "",
Usage: "Local Range if network is local, for instance 192.168.1.0/24.",
},
&cli.StringFlag{
Name: "dnson",
EnvVars: []string{"NETCLIENT_DNS"},
Value: "yes",
Usage: "Sets private dns if 'yes'. Ignores if 'no'. Will retrieve from network if unset.",
},
&cli.StringFlag{
Name: "islocal",
EnvVars: []string{"NETCLIENT_IS_LOCAL"},
Value: "",
Usage: "Sets endpoint to local address if 'yes'. Ignores if 'no'. Will retrieve from network if unset.",
},
&cli.StringFlag{
Name: "udpholepunch",
EnvVars: []string{"NETCLIENT_UDP_HOLEPUNCH"},
Value: "",
Usage: "Turns on udp holepunching if 'yes'. Ignores if 'no'. Will retrieve from network if unset.",
},
&cli.StringFlag{
Name: "ipforwarding",
EnvVars: []string{"NETCLIENT_IPFORWARDING"},
Value: "on",
Usage: "Sets ip forwarding on if 'on'. Ignores if 'off'. On by default.",
},
&cli.StringFlag{
Name: "postup",
EnvVars: []string{"NETCLIENT_POSTUP"},
Value: "",
Usage: "Sets PostUp command for WireGuard.",
},
&cli.StringFlag{
Name: "postdown",
EnvVars: []string{"NETCLIENT_POSTDOWN"},
Value: "",
Usage: "Sets PostDown command for WireGuard.",
},
&cli.StringFlag{
Name: "daemon",
EnvVars: []string{"NETCLIENT_DAEMON"},
Value: "on",
Usage: "Installs daemon if 'on'. Ignores if 'off'. On by default.",
},
&cli.StringFlag{
Name: "roaming",
EnvVars: []string{"NETCLIENT_ROAMING"},
Value: "yes",
Usage: "Checks for IP changes if 'yes'. Ignores if 'no'. Yes by default.",
},
&cli.BoolFlag{
Name: "verbosity-level-1",
Aliases: []string{"v"},
Value: false,
Usage: "Netclient Verbosity level 1.",
},
&cli.BoolFlag{
Name: "verbosity-level-2",
Aliases: []string{"vv"},
Value: false,
Usage: "Netclient Verbosity level 2.",
},
&cli.BoolFlag{
Name: "verbosity-level-3",
Aliases: []string{"vvv"},
Value: false,
Usage: "Netclient Verbosity level 3.",
},
&cli.BoolFlag{
Name: "verbosity-level-4",
Aliases: []string{"vvvv"},
Value: false,
Usage: "Netclient Verbosity level 4.",
},
}
}

View File

@@ -1,167 +0,0 @@
package command
import (
"errors"
"fmt"
"strings"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/daemon"
"github.com/gravitl/netmaker/netclient/functions"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// Join - join command to run from cli
func Join(cfg *config.ClientConfig, privateKey string) error {
var err error
//join network
if cfg.SsoServer != "" {
// User wants to get access key from the OIDC server
// Do that before the Joining Network flow by performing the end point auth flow
// if performed successfully an access key is obtained from the server and then we
// proceed with the usual flow 'pretending' that user is feeded us with an access token
if len(cfg.Network) == 0 || cfg.Network == "all" {
return fmt.Errorf("no network provided. Specify network with \"-n <net name>\"")
}
logger.Log(1, "Logging into %s via:", cfg.Network, cfg.SsoServer)
err = functions.JoinViaSSo(cfg, privateKey)
if err != nil {
logger.Log(0, "Join failed: ", err.Error())
return err
}
if cfg.AccessKey == "" {
return errors.New("login failed")
}
}
logger.Log(1, "Joining network: ", cfg.Network)
err = functions.JoinNetwork(cfg, privateKey)
if err != nil {
if !strings.Contains(err.Error(), "ALREADY_INSTALLED") {
logger.Log(0, "error installing: ", err.Error())
err = functions.WipeLocal(cfg)
if err != nil {
logger.Log(1, "error removing artifacts: ", err.Error())
}
if cfg.Daemon != "off" {
if ncutils.IsLinux() {
err = daemon.RemoveSystemDServices()
}
if err != nil {
logger.Log(1, "error removing services: ", err.Error())
}
if ncutils.IsFreeBSD() {
daemon.RemoveFreebsdDaemon()
}
}
}
if err != nil && strings.Contains(err.Error(), "ALREADY_INSTALLED") {
logger.Log(0, err.Error())
err = nil
}
return err
}
logger.Log(1, "joined", cfg.Network)
return err
}
// Leave - runs the leave command from cli
func Leave(cfg *config.ClientConfig) error {
err := functions.LeaveNetwork(cfg.Network)
if err != nil {
logger.Log(1, "error attempting to leave network "+cfg.Network)
} else {
logger.Log(0, "success")
}
return err
}
// Pull - runs pull command from cli
func Pull(cfg *config.ClientConfig) error {
var err error
var networks = []string{}
if cfg.Network == "all" {
logger.Log(0, "No network selected. Running Pull for all networks.")
networks, err = ncutils.GetSystemNetworks()
if err != nil {
logger.Log(1, "Error retrieving networks. Exiting.")
return err
}
} else {
networks = append(networks, cfg.Network)
}
var currentServers = make(map[string]config.ClientConfig)
for _, network := range networks {
currCfg, err := config.ReadConfig(network)
if err != nil {
logger.Log(1, "could not read config when pulling for network", network)
continue
}
_, err = functions.Pull(network, true)
if err != nil {
logger.Log(1, "error pulling network config for network: ", network, "\n", err.Error())
} else {
logger.Log(1, "pulled network config for "+network)
}
currentServers[currCfg.Server.Server] = *currCfg
}
daemon.Restart()
logger.Log(1, "reset network", cfg.Network, "and peer configs")
return err
}
// List - runs list command from cli
func List(cfg config.ClientConfig) error {
_, err := functions.List(cfg.Network)
return err
}
// Uninstall - runs uninstall command from cli
func Uninstall() error {
logger.Log(0, "uninstalling netclient...")
err := functions.Uninstall()
logger.Log(0, "uninstalled netclient")
return err
}
// Daemon - runs the daemon
func Daemon() error {
err := functions.Daemon()
return err
}
// Install - installs binary and daemon
func Install() error {
return functions.Install()
}
// Connect - re-instates a connection of a node
func Connect(cfg config.ClientConfig) error {
networkName := cfg.Network
if networkName == "" {
networkName = cfg.Node.Network
}
if networkName == "all" {
return fmt.Errorf("no network specified")
}
return functions.Connect(networkName)
}
// Disconnect - disconnects a connection of a node
func Disconnect(cfg config.ClientConfig) error {
networkName := cfg.Network
if networkName == "" {
networkName = cfg.Node.Network
}
if networkName == "all" {
return fmt.Errorf("no network specified")
}
return functions.Disconnect(networkName)
}

View File

@@ -1,352 +0,0 @@
package config
import (
"crypto/ed25519"
"crypto/x509"
"crypto/x509/pkix"
"errors"
"fmt"
"log"
"os"
"sync"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/global_settings"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v3"
)
var (
configLock sync.Mutex
)
// ClientConfig - struct for dealing with client configuration
type ClientConfig struct {
Server models.ServerConfig `yaml:"server"`
Node models.LegacyNode `yaml:"node"`
NetworkSettings models.Network `yaml:"networksettings"`
Network string `yaml:"network"`
Daemon string `yaml:"daemon"`
OperatingSystem string `yaml:"operatingsystem"`
AccessKey string `yaml:"accesskey"`
PublicIPService string `yaml:"publicipservice"`
SsoServer string `yaml:"sso"`
}
// RegisterRequest - struct for registation with netmaker server
type RegisterRequest struct {
Key ed25519.PrivateKey
CommonName pkix.Name
}
// RegisterResponse - the response to register function
type RegisterResponse struct {
CA x509.Certificate
CAPubKey ed25519.PublicKey
Cert x509.Certificate
CertPubKey ed25519.PublicKey
Broker string
Port string
}
// Write - writes the config of a client to disk
func Write(config *ClientConfig, network string) error {
configLock.Lock()
defer configLock.Unlock()
if network == "" {
err := errors.New("no network provided - exiting")
return err
}
_, err := os.Stat(ncutils.GetNetclientPath() + "/config")
if os.IsNotExist(err) {
os.MkdirAll(ncutils.GetNetclientPath()+"/config", 0700)
} else if err != nil {
return err
}
home := ncutils.GetNetclientPathSpecific()
file := fmt.Sprintf(home + "netconfig-" + network)
f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
if err != nil {
return err
}
defer f.Close()
err = yaml.NewEncoder(f).Encode(config)
if err != nil {
return err
}
return f.Sync()
}
// ConfigFileExists - return true if config file exists
func (config *ClientConfig) ConfigFileExists() bool {
home := ncutils.GetNetclientPathSpecific()
file := fmt.Sprintf(home + "netconfig-" + config.Network)
info, err := os.Stat(file)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
// ClientConfig.ReadConfig - used to read config from client disk into memory
func (config *ClientConfig) ReadConfig() {
network := config.Network
if network == "" {
return
}
//home, err := homedir.Dir()
home := ncutils.GetNetclientPathSpecific()
file := fmt.Sprintf(home + "netconfig-" + network)
//f, err := os.Open(file)
f, err := os.OpenFile(file, os.O_RDONLY, 0600)
if err != nil {
logger.Log(1, "trouble opening file: ", err.Error())
if err = ReplaceWithBackup(network); err != nil {
log.Fatal(err)
}
f.Close()
f, err = os.Open(file)
if err != nil {
log.Fatal(err)
}
}
defer f.Close()
if err := yaml.NewDecoder(f).Decode(&config); err != nil {
logger.Log(0, "no config or invalid, replacing with backup")
if err = ReplaceWithBackup(network); err != nil {
log.Fatal(err)
}
f.Close()
f, err = os.Open(file)
if err != nil {
log.Fatal(err)
}
defer f.Close()
if err := yaml.NewDecoder(f).Decode(&config); err != nil {
log.Fatal(err)
}
}
}
// ModNodeConfig - overwrites the node inside client config on disk
func ModNodeConfig(node *models.LegacyNode) error {
network := node.Network
if network == "" {
return errors.New("no network provided")
}
var modconfig ClientConfig
if FileExists(ncutils.GetNetclientPathSpecific() + "netconfig-" + network) {
useconfig, err := ReadConfig(network)
if err != nil {
return err
}
modconfig = *useconfig
}
modconfig.Node = (*node)
modconfig.NetworkSettings = node.NetworkSettings
return Write(&modconfig, network)
}
// ModNodeConfig - overwrites the server settings inside client config on disk
func ModServerConfig(scfg *models.ServerConfig, network string) error {
var modconfig ClientConfig
if FileExists(ncutils.GetNetclientPathSpecific() + "netconfig-" + network) {
useconfig, err := ReadConfig(network)
if err != nil {
return err
}
modconfig = *useconfig
}
modconfig.Server = (*scfg)
return Write(&modconfig, network)
}
// SaveBackup - saves a backup file of a given network
func SaveBackup(network string) error {
var configPath = ncutils.GetNetclientPathSpecific() + "netconfig-" + network
var backupPath = ncutils.GetNetclientPathSpecific() + "backup.netconfig-" + network
if FileExists(configPath) {
input, err := os.ReadFile(configPath)
if err != nil {
logger.Log(0, "failed to read ", configPath, " to make a backup")
return err
}
if err = os.WriteFile(backupPath, input, 0600); err != nil {
logger.Log(0, "failed to copy backup to ", backupPath)
return err
}
}
return nil
}
// ReplaceWithBackup - replaces netconfig file with backup
func ReplaceWithBackup(network string) error {
var backupPath = ncutils.GetNetclientPathSpecific() + "backup.netconfig-" + network
var configPath = ncutils.GetNetclientPathSpecific() + "netconfig-" + network
if FileExists(backupPath) {
input, err := os.ReadFile(backupPath)
if err != nil {
logger.Log(0, "failed to read file ", backupPath, " to backup network: ", network)
return err
}
if err = os.WriteFile(configPath, input, 0600); err != nil {
logger.Log(0, "failed backup ", backupPath, " to ", configPath)
return err
}
}
logger.Log(0, "used backup file for network: ", network)
return nil
}
// GetCLIConfig - gets the cli flags as a config
func GetCLIConfig(c *cli.Context) (ClientConfig, string, error) {
var cfg ClientConfig
if c.String("token") != "" {
accesstoken, err := ParseAccessToken(c.String("token"))
if err != nil {
return cfg, "", err
}
cfg.Network = accesstoken.ClientConfig.Network
cfg.Node.Network = accesstoken.ClientConfig.Network
cfg.AccessKey = accesstoken.ClientConfig.Key
cfg.Node.LocalRange = accesstoken.ClientConfig.LocalRange
//cfg.Server.Server = accesstoken.ServerConfig.Server
cfg.Server.API = accesstoken.APIConnString
if c.String("key") != "" {
cfg.AccessKey = c.String("key")
}
if c.String("proxy") != "" {
cfg.Node.Proxy = c.String("proxy") == "on"
}
log.Println("_______________> PROXY: ", cfg.Node.Proxy)
if c.String("network") != "all" {
cfg.Network = c.String("network")
cfg.Node.Network = c.String("network")
}
if c.String("localrange") != "" {
cfg.Node.LocalRange = c.String("localrange")
}
if c.String("corednsaddr") != "" {
cfg.Server.CoreDNSAddr = c.String("corednsaddr")
}
if c.String("apiserver") != "" {
cfg.Server.API = c.String("apiserver")
}
} else if c.String("server") != "" {
cfg.SsoServer = c.String("server")
cfg.Network = c.String("network")
cfg.Node.Network = c.String("network")
global_settings.User = c.String("user")
} else {
cfg.AccessKey = c.String("key")
cfg.Network = c.String("network")
cfg.Node.Network = c.String("network")
cfg.Node.LocalRange = c.String("localrange")
cfg.Server.CoreDNSAddr = c.String("corednsaddr")
cfg.Server.API = c.String("apiserver")
}
cfg.PublicIPService = c.String("publicipservice")
// populate the map as we're not running as a daemon so won't be building the map otherwise
// (and the map will be used by GetPublicIP()).
global_settings.PublicIPServices[cfg.Network] = cfg.PublicIPService
cfg.Node.Name = c.String("name")
cfg.Node.Interface = c.String("interface")
cfg.Node.Password = c.String("password")
cfg.Node.MacAddress = c.String("macaddress")
cfg.Node.LocalAddress = c.String("localaddress")
cfg.Node.Address = c.String("address")
cfg.Node.Address6 = c.String("address6")
//cfg.Node.Roaming = c.String("roaming")
cfg.Node.DNSOn = c.String("dnson")
cfg.Node.IsLocal = c.String("islocal")
cfg.Node.IsStatic = c.String("isstatic")
cfg.Node.PostUp = c.String("postup")
cfg.Node.PostDown = c.String("postdown")
cfg.Node.ListenPort = int32(c.Int("port"))
cfg.Node.PersistentKeepalive = int32(c.Int("keepalive"))
cfg.Node.PublicKey = c.String("publickey")
privateKey := c.String("privatekey")
cfg.Node.Endpoint = c.String("endpoint")
cfg.Node.IPForwarding = c.String("ipforwarding")
cfg.OperatingSystem = c.String("operatingsystem")
cfg.Daemon = c.String("daemon")
cfg.Node.UDPHolePunch = c.String("udpholepunch")
cfg.Node.MTU = int32(c.Int("mtu"))
return cfg, privateKey, nil
}
// ReadConfig - reads a config of a client from disk for specified network
func ReadConfig(network string) (*ClientConfig, error) {
if network == "" {
err := errors.New("no network provided - exiting")
return nil, err
}
home := ncutils.GetNetclientPathSpecific()
file := fmt.Sprintf(home + "netconfig-" + network)
f, err := os.Open(file)
if err != nil {
if err = ReplaceWithBackup(network); err != nil {
return nil, err
}
f, err = os.Open(file)
if err != nil {
return nil, err
}
}
defer f.Close()
var cfg ClientConfig
decoder := yaml.NewDecoder(f)
err = decoder.Decode(&cfg)
if err != nil {
if err = ReplaceWithBackup(network); err != nil {
return nil, err
}
f.Close()
f, err = os.Open(file)
if err != nil {
return nil, err
}
defer f.Close()
if err := yaml.NewDecoder(f).Decode(&cfg); err != nil {
return nil, err
}
}
return &cfg, err
}
// FileExists - checks if a file exists on disk
func FileExists(f string) bool {
info, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
// GetNode - parses a network specified client config for node data
func GetNode(network string) models.LegacyNode {
modcfg, err := ReadConfig(network)
if err != nil {
log.Fatalf("Error: %v", err)
}
var node models.LegacyNode
node.Fill(&modcfg.Node)
return node
}

View File

@@ -1,31 +0,0 @@
package config
import (
"encoding/base64"
"encoding/json"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
)
var (
// GuiActive - indicates if gui is active or not
GuiActive = false
// GuiRun - holds function for main to call
GuiRun interface{}
)
// ParseAccessToken - used to parse the base64 encoded access token
func ParseAccessToken(token string) (*models.AccessToken, error) {
tokenbytes, err := base64.StdEncoding.DecodeString(token)
if err != nil {
logger.Log(0, "error decoding token", err.Error())
return nil, err
}
var accesstoken models.AccessToken
if err := json.Unmarshal(tokenbytes, &accesstoken); err != nil {
logger.Log(0, "error decoding token", err.Error())
return nil, err
}
return &accesstoken, nil
}

View File

@@ -1,94 +0,0 @@
package daemon
import (
"errors"
"fmt"
"os"
"runtime"
"syscall"
"time"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// InstallDaemon - Calls the correct function to install the netclient as a daemon service on the given operating system.
func InstallDaemon() error {
os := runtime.GOOS
var err error
switch os {
case "windows":
err = SetupWindowsDaemon()
case "darwin":
err = SetupMacDaemon()
case "linux":
err = SetupSystemDDaemon()
case "freebsd":
err = SetupFreebsdDaemon()
default:
err = errors.New("this os is not yet supported for daemon mode. Run join cmd with flag '--daemon off'")
}
return err
}
// Restart - restarts a system daemon
func Restart() error {
if ncutils.IsWindows() {
RestartWindowsDaemon()
return nil
}
pid, err := ncutils.ReadPID()
if err != nil {
return fmt.Errorf("failed to find pid %w", err)
}
p, err := os.FindProcess(pid)
if err != nil {
return fmt.Errorf("failed to find running process for pid %d -- %w", pid, err)
}
if err := p.Signal(syscall.SIGHUP); err != nil {
return fmt.Errorf("SIGHUP failed -- %w", err)
}
return nil
}
// Start - starts system daemon
func Start() error {
os := runtime.GOOS
var err error
switch os {
case "windows":
RestartWindowsDaemon()
case "darwin":
RestartLaunchD()
case "linux":
RestartSystemD()
case "freebsd":
FreebsdDaemon("restart")
default:
err = errors.New("this os is not yet supported for daemon mode. Run join cmd with flag '--daemon off'")
}
return err
}
// Stop - stops a system daemon
func Stop() error {
os := runtime.GOOS
var err error
time.Sleep(time.Second)
switch os {
case "windows":
RunWinSWCMD("stop")
case "darwin":
StopLaunchD()
case "linux":
StopSystemD()
case "freebsd":
FreebsdDaemon("stop")
default:
err = errors.New("no OS daemon to stop")
}
return err
}

View File

@@ -1,135 +0,0 @@
package daemon
import (
"log"
"os"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// SetupFreebsdDaemon -- sets up daemon for freebsd
func SetupFreebsdDaemon() error {
binarypath, err := os.Executable()
if err != nil {
return err
}
_, err = os.Stat("/etc/netclient/config")
if os.IsNotExist(err) {
os.MkdirAll("/etc/netclient/config", 0744)
} else if err != nil {
log.Println("couldnt find or create /etc/netclient")
return err
}
//install binary
if ncutils.FileExists(EXEC_DIR + "netclient") {
logger.Log(0, "updating netclient binary in ", EXEC_DIR)
}
err = ncutils.Copy(binarypath, EXEC_DIR+"netclient")
if err != nil {
logger.Log(0, err.Error())
return err
}
rcFile := `#!/bin/sh
#
# PROVIDE: netclient
# REQUIRE: LOGIN
# KEYWORD: shutdown
# Description:
# This script runs netclient as a service as root on boot
# How to use:
# Place this file in /usr/local/etc/rc.d/
# Add netclient="YES" to /etc/rc.config.d/netclient
# To pass args, add netclient_args="daemon" to /etc/rc.config.d/netclient
# Freebsd rc library
. /etc/rc.subr
# General Info
name="netclient" # Safe name of program
program_name="netclient" # Name of exec
title="netclient" # Title to display in top/htop
# RC.config vars
load_rc_config $name # Loading rc config vars
: ${netclient_enable="YES"} # Default: enable netclient
: ${netclient_runAs="root"} # Default: Run Node-RED as root
# Freebsd Setup
rcvar=netclient_enable # Enables the rc.conf YES/NO flag
pidfile="/var/run/${program_name}.pid" # File that allows the system to keep track of node-red status
# Env Setup
#export HOME=$( getent passwd "$netclient_runAs" | cut -d: -f6 ) # Gets the home directory of the runAs user
# Command Setup
exec_path="/sbin/${program_name}" # Path to the netclient exec
output_file="/var/log/${program_name}.log" # Path to netclient logs
# Command
command="/usr/sbin/daemon"
command_args="-r -t ${title} -u ${netclient_runAs} -o ${output_file} -P ${pidfile} ${exec_path} ${netclient_args}"
# Loading Config
load_rc_config ${name}
run_rc_command "$1"
`
rcConfig := `netclient="YES"
netclient_args="daemon"`
rcbytes := []byte(rcFile)
if !ncutils.FileExists("/etc/rc.d/netclient") {
err := os.WriteFile("/etc/rc.d/netclient", rcbytes, 0744)
if err != nil {
return err
}
rcConfigbytes := []byte(rcConfig)
if !ncutils.FileExists("/etc/rc.conf.d/netclient") {
err := os.WriteFile("/etc/rc.conf.d/netclient", rcConfigbytes, 0644)
if err != nil {
return err
}
FreebsdDaemon("start")
return nil
}
}
return nil
}
// FreebsdDaemon - accepts args to service netclient and applies
func FreebsdDaemon(command string) {
_, _ = ncutils.RunCmdFormatted("service netclient "+command, true)
}
// CleanupFreebsd - removes config files and netclient binary
func CleanupFreebsd() {
ncutils.RunCmd("service netclient stop", false)
RemoveFreebsdDaemon()
if err := os.RemoveAll(ncutils.GetNetclientPath()); err != nil {
logger.Log(1, "Removing netclient configs: ", err.Error())
}
if err := os.Remove(EXEC_DIR + "netclient"); err != nil {
logger.Log(1, "Removing netclient binary: ", err.Error())
}
}
// RemoveFreebsdDaemon - remove freebsd daemon
func RemoveFreebsdDaemon() {
if ncutils.FileExists("/etc/rc.d/netclient") {
err := os.Remove("/etc/rc.d/netclient")
if err != nil {
logger.Log(0, "Error removing /etc/rc.d/netclient. Please investigate.")
}
}
if ncutils.FileExists("/etc/rc.conf.d/netclient") {
err := os.Remove("/etc/rc.conf.d/netclient")
if err != nil {
logger.Log(0, "Error removing /etc/rc.conf.d/netclient. Please investigate.")
}
}
}

View File

@@ -1,117 +0,0 @@
package daemon
import (
"log"
"os"
"time"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/ncutils"
)
const MAC_SERVICE_NAME = "com.gravitl.netclient"
const MAC_EXEC_DIR = "/usr/local/bin/"
// SetupMacDaemon - Creates a daemon service from the netclient under LaunchAgents for MacOS
func SetupMacDaemon() error {
binarypath, err := os.Executable()
if err != nil {
return err
}
if ncutils.FileExists(MAC_EXEC_DIR + "netclient") {
logger.Log(0, "updating netclient binary in", MAC_EXEC_DIR)
}
err = ncutils.Copy(binarypath, MAC_EXEC_DIR+"netclient")
if err != nil {
logger.Log(0, err.Error())
return err
}
err = CreateMacService(MAC_SERVICE_NAME)
if err != nil {
return err
}
_, err = ncutils.RunCmd("launchctl load /Library/LaunchDaemons/"+MAC_SERVICE_NAME+".plist", true)
return err
}
// CleanupMac - Removes the netclient checkin daemon from LaunchDaemons
func CleanupMac() {
_, err := ncutils.RunCmd("launchctl unload /Library/LaunchDaemons/"+MAC_SERVICE_NAME+".plist", true)
if ncutils.FileExists("/Library/LaunchDaemons/" + MAC_SERVICE_NAME + ".plist") {
err = os.Remove("/Library/LaunchDaemons/" + MAC_SERVICE_NAME + ".plist")
}
if err != nil {
logger.Log(1, err.Error())
}
os.RemoveAll(ncutils.GetNetclientPath())
os.Remove(MAC_EXEC_DIR + "netclient")
}
// RestartLaunchD - restart launch daemon
func RestartLaunchD() {
ncutils.RunCmd("launchctl unload /Library/LaunchDaemons/"+MAC_SERVICE_NAME+".plist", true)
time.Sleep(time.Second >> 2)
ncutils.RunCmd("launchctl load /Library/LaunchDaemons/"+MAC_SERVICE_NAME+".plist", true)
}
// StopLaunchD - stop launch daemon
func StopLaunchD() {
ncutils.RunCmd("launchctl unload /Library/LaunchDaemons/"+MAC_SERVICE_NAME+".plist", true)
}
// CreateMacService - Creates the mac service file for LaunchDaemons
func CreateMacService(servicename string) error {
_, err := os.Stat("/Library/LaunchDaemons")
if os.IsNotExist(err) {
os.Mkdir("/Library/LaunchDaemons", 0755)
} else if err != nil {
log.Println("couldnt find or create /Library/LaunchDaemons")
return err
}
daemonstring := MacDaemonString()
daemonbytes := []byte(daemonstring)
if !ncutils.FileExists("/Library/LaunchDaemons/com.gravitl.netclient.plist") {
err = os.WriteFile("/Library/LaunchDaemons/com.gravitl.netclient.plist", daemonbytes, 0644)
}
return err
}
// MacDaemonString - the file contents for the mac netclient daemon service (launchdaemon)
func MacDaemonString() string {
return `<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\" >
<plist version='1.0'>
<dict>
<key>Label</key><string>com.gravitl.netclient</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/netclient</string>
<string>daemon</string>
</array>
<key>StandardOutPath</key><string>/var/log/com.gravitl.netclient.log</string>
<key>StandardErrorPath</key><string>/var/log/com.gravitl.netclient.log</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>AbandonProcessGroup</key><true/>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
</dict>
</plist>
`
}
// MacTemplateData - struct to represent the mac service
type MacTemplateData struct {
Label string
Interval string
}

View File

@@ -1,138 +0,0 @@
package daemon
import (
//"github.com/davecgh/go-spew/spew"
"log"
"os"
"path/filepath"
"time"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/ncutils"
)
const EXEC_DIR = "/sbin/"
// SetupSystemDDaemon - sets system daemon for supported machines
func SetupSystemDDaemon() error {
if ncutils.IsWindows() {
return nil
}
binarypath, err := os.Executable()
if err != nil {
return err
}
_, err = os.Stat("/etc/netclient/config")
if os.IsNotExist(err) {
os.MkdirAll("/etc/netclient/config", 0744)
} else if err != nil {
log.Println("couldnt find or create /etc/netclient")
return err
}
//install binary
if ncutils.FileExists(EXEC_DIR + "netclient") {
logger.Log(0, "updating netclient binary in", EXEC_DIR)
}
err = ncutils.Copy(binarypath, EXEC_DIR+"netclient")
if err != nil {
logger.Log(0, err.Error())
return err
}
systemservice := `[Unit]
Description=Netclient Daemon
Documentation=https://docs.netmaker.org https://k8s.netmaker.org
After=network-online.target
Wants=network-online.target
[Service]
User=root
Type=simple
ExecStart=/sbin/netclient daemon
Restart=on-failure
RestartSec=15s
[Install]
WantedBy=multi-user.target
`
servicebytes := []byte(systemservice)
if !ncutils.FileExists("/etc/systemd/system/netclient.service") {
err = os.WriteFile("/etc/systemd/system/netclient.service", servicebytes, 0644)
if err != nil {
logger.Log(0, err.Error())
return err
}
}
_, _ = ncutils.RunCmd("systemctl enable netclient.service", true)
_, _ = ncutils.RunCmd("systemctl daemon-reload", true)
_, _ = ncutils.RunCmd("systemctl start netclient.service", true)
return nil
}
// RestartSystemD - restarts systemd service
func RestartSystemD() {
logger.Log(1, "restarting netclient.service")
time.Sleep(time.Second)
_, _ = ncutils.RunCmd("systemctl restart netclient.service", true)
}
// CleanupLinux - cleans up neclient configs
func CleanupLinux() {
if _, err := ncutils.RunCmd("systemctl stop netclient", false); err != nil {
logger.Log(0, "failed to stop netclient service", err.Error())
}
RemoveSystemDServices()
if err := os.RemoveAll(ncutils.GetNetclientPath()); err != nil {
logger.Log(1, "Removing netclient configs: ", err.Error())
}
if err := os.Remove(EXEC_DIR + "netclient"); err != nil {
logger.Log(1, "Removing netclient binary: ", err.Error())
}
}
// StopSystemD - tells system to stop systemd
func StopSystemD() {
ncutils.RunCmd("systemctl stop netclient.service", false)
}
// RemoveSystemDServices - removes the systemd services on a machine
func RemoveSystemDServices() error {
//sysExec, err := exec.LookPath("systemctl")
var err error
if !ncutils.IsWindows() && isOnlyService() {
if err != nil {
logger.Log(0, err.Error())
}
ncutils.RunCmd("systemctl disable netclient.service", false)
ncutils.RunCmd("systemctl disable netclient.timer", false)
if ncutils.FileExists("/etc/systemd/system/netclient.service") {
err = os.Remove("/etc/systemd/system/netclient.service")
if err != nil {
logger.Log(0, "Error removing /etc/systemd/system/netclient.service. Please investigate.")
}
}
if ncutils.FileExists("/etc/systemd/system/netclient.timer") {
err = os.Remove("/etc/systemd/system/netclient.timer")
if err != nil {
logger.Log(0, "Error removing /etc/systemd/system/netclient.timer. Please investigate.")
}
}
ncutils.RunCmd("systemctl daemon-reload", false)
ncutils.RunCmd("systemctl reset-failed", false)
logger.Log(0, "removed systemd remnants if any existed")
}
return nil
}
func isOnlyService() bool {
files, err := filepath.Glob("/etc/netclient/config/netconfig-*")
if err != nil {
return false
}
return len(files) == 0
}

View File

@@ -1,106 +0,0 @@
package daemon
import (
"fmt"
"log"
"os"
"strings"
"time"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// SetupWindowsDaemon - sets up the Windows daemon service
func SetupWindowsDaemon() error {
if ncutils.FileExists(ncutils.GetNetclientPathSpecific() + "winsw.xml") {
logger.Log(0, "updating netclient service")
}
if err := writeServiceConfig(); err != nil {
return err
}
if ncutils.FileExists(ncutils.GetNetclientPathSpecific() + "winsw.exe") {
logger.Log(0, "updating netclient binary")
}
err := ncutils.GetEmbedded()
if err != nil {
return err
}
logger.Log(0, "finished daemon setup")
//get exact formatted commands
RunWinSWCMD("install")
time.Sleep(time.Millisecond)
RunWinSWCMD("start")
return nil
}
// RestartWindowsDaemon - restarts windows service
func RestartWindowsDaemon() {
RunWinSWCMD("stop")
time.Sleep(time.Millisecond)
RunWinSWCMD("start")
}
// CleanupWindows - cleans up windows files
func CleanupWindows() {
if !ncutils.FileExists(ncutils.GetNetclientPathSpecific() + "winsw.xml") {
writeServiceConfig()
}
RunWinSWCMD("stop")
RunWinSWCMD("uninstall")
os.RemoveAll(ncutils.GetNetclientPath())
log.Println("Netclient on Windows, uninstalled")
}
func writeServiceConfig() error {
serviceConfigPath := ncutils.GetNetclientPathSpecific() + "winsw.xml"
scriptString := fmt.Sprintf(`<service>
<id>netclient</id>
<name>Netclient</name>
<description>Connects Windows nodes to one or more Netmaker networks.</description>
<executable>%v</executable>
<arguments>daemon</arguments>
<log mode="roll"></log>
</service>
`, strings.Replace(ncutils.GetNetclientPathSpecific()+"netclient.exe", `\\`, `\`, -1))
if !ncutils.FileExists(serviceConfigPath) {
err := os.WriteFile(serviceConfigPath, []byte(scriptString), 0600)
if err != nil {
return err
}
logger.Log(0, "wrote the daemon config file to the Netclient directory")
}
return nil
}
// RunWinSWCMD - Run a command with the winsw.exe tool (start, stop, install, uninstall)
func RunWinSWCMD(command string) {
// check if command allowed
allowedCommands := map[string]bool{
"start": true,
"stop": true,
"install": true,
"uninstall": true,
}
if !allowedCommands[command] {
logger.Log(0, "command "+command+" unsupported by winsw")
return
}
// format command
dirPath := strings.Replace(ncutils.GetNetclientPathSpecific(), `\\`, `\`, -1)
winCmd := fmt.Sprintf(`"%swinsw.exe" "%s"`, dirPath, command)
logger.Log(0, "running "+command+" of Windows Netclient daemon")
// run command and log for success/failure
out, err := ncutils.RunCmdFormatted(winCmd, true)
if err != nil {
logger.Log(0, "error with "+command+" of Windows Netclient daemon: "+err.Error()+" : "+out)
} else {
logger.Log(0, "successfully ran "+command+" of Windows Netclient daemon")
}
}

View File

@@ -1,78 +0,0 @@
package functions
import (
"strconv"
"strings"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/functions/upgrades"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// UpdateClientConfig - function is called on daemon start to update clientConfig if required
// Usage : set update required to true and and update logic to function
func UpdateClientConfig() {
defer upgrades.ReleaseUpgrades()
upgrades.InitializeUpgrades()
networks, _ := ncutils.GetSystemNetworks()
if len(networks) == 0 {
return
}
logger.Log(0, "checking for netclient updates...")
for _, network := range networks {
cfg := config.ClientConfig{}
cfg.Network = network
cfg.ReadConfig()
major, minor, _ := Version(cfg.Node.Version)
if major == 0 && minor < 14 {
logger.Log(0, "schema of network", cfg.Network, "is out of date and cannot be updated\n Correct behaviour of netclient cannot be guaranteed")
continue
}
configChanged := false
for _, u := range upgrades.Upgrades {
if ncutils.StringSliceContains(u.RequiredVersions, cfg.Node.Version) {
logger.Log(0, "upgrading node", cfg.Node.Name, "on network", cfg.Node.Network, "from", cfg.Node.Version, "to", u.NewVersion)
upgrades.UpgradeFunction(u.OP)(&cfg)
cfg.Node.Version = u.NewVersion
configChanged = true
}
}
if configChanged {
//save and publish
if err := PublishNodeUpdate(&cfg); err != nil {
logger.Log(0, "error publishing node update during schema change", err.Error())
}
if err := config.ModNodeConfig(&cfg.Node); err != nil {
logger.Log(0, "error saving local config for node,", cfg.Node.Name, ", on network,", cfg.Node.Network)
}
}
}
logger.Log(0, "finished updates")
}
// Version - parse version string into component parts
// version string must be semantic version of form 1.2.3 or v1.2.3
// otherwise 0, 0, 0 will be returned.
func Version(version string) (int, int, int) {
var major, minor, patch int
var errMajor, errMinor, errPatch error
parts := strings.Split(version, ".")
//ensure semantic version
if len(parts) < 3 {
return major, minor, patch
}
if strings.Contains(parts[0], "v") {
majors := strings.Split(parts[0], "v")
major, errMajor = strconv.Atoi(majors[1])
} else {
major, errMajor = strconv.Atoi(parts[0])
}
minor, errMinor = strconv.Atoi(parts[1])
patch, errPatch = strconv.Atoi(parts[2])
if errMajor != nil || errMinor != nil || errPatch != nil {
return 0, 0, 0
}
return major, minor, patch
}

View File

@@ -1,431 +0,0 @@
package functions
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/daemon"
"github.com/gravitl/netmaker/netclient/local"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/wireguard"
"github.com/gravitl/netmaker/nm-proxy/manager"
"golang.zx2c4.com/wireguard/wgctrl"
)
// LINUX_APP_DATA_PATH - linux path
const LINUX_APP_DATA_PATH = "/etc/netmaker"
// HTTP_TIMEOUT - timeout in seconds for http requests
const HTTP_TIMEOUT = 30
// HTTPClient - http client to be reused by all
var HTTPClient http.Client
// SetHTTPClient -sets http client with sane default
func SetHTTPClient() {
HTTPClient = http.Client{
Timeout: HTTP_TIMEOUT * time.Second,
}
}
// ListPorts - lists ports of WireGuard devices
func ListPorts() error {
wgclient, err := wgctrl.New()
if err != nil {
return err
}
defer wgclient.Close()
devices, err := wgclient.Devices()
if err != nil {
return err
}
fmt.Println("Here are your ports:")
for _, i := range devices {
fmt.Println(i.ListenPort)
}
return err
}
func getPrivateAddr() (string, error) {
var local string
conn, err := net.Dial("udp", "8.8.8.8:80")
if err == nil {
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
localIP := localAddr.IP
local = localIP.String()
}
if local == "" {
local, err = getPrivateAddrBackup()
}
if local == "" {
err = errors.New("could not find local ip")
}
if net.ParseIP(local).To16() != nil {
local = "[" + local + "]"
}
return local, err
}
func getPrivateAddrBackup() (string, error) {
ifaces, err := net.Interfaces()
if err != nil {
return "", err
}
var local string
found := false
for _, i := range ifaces {
if i.Flags&net.FlagUp == 0 {
continue // interface down
}
if i.Flags&net.FlagLoopback != 0 {
continue // loopback interface
}
addrs, err := i.Addrs()
if err != nil {
return "", err
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
if !found {
ip = v.IP
local = ip.String()
found = true
}
case *net.IPAddr:
if !found {
ip = v.IP
local = ip.String()
found = true
}
}
}
}
if !found {
err := errors.New("local ip address not found")
return "", err
}
return local, err
}
func getInterfaces() (*[]models.Iface, error) {
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}
var data []models.Iface
var link models.Iface
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 {
continue // interface down
}
if iface.Flags&net.FlagLoopback != 0 {
continue // loopback interface
}
addrs, err := iface.Addrs()
if err != nil {
return nil, err
}
for _, addr := range addrs {
link.Name = iface.Name
_, cidr, err := net.ParseCIDR(addr.String())
if err != nil {
continue
}
link.Address = *cidr
data = append(data, link)
}
}
return &data, nil
}
// GetNode - gets node locally
func GetNode(network string) models.LegacyNode {
modcfg, err := config.ReadConfig(network)
if err != nil {
log.Fatalf("Error: %v", err)
}
return modcfg.Node
}
// Uninstall - uninstalls networks from client
func Uninstall() error {
networks, err := ncutils.GetSystemNetworks()
if err != nil {
logger.Log(1, "unable to retrieve networks: ", err.Error())
logger.Log(1, "continuing uninstall without leaving networks")
} else {
for _, network := range networks {
err = LeaveNetwork(network)
if err != nil {
logger.Log(1, "encounter issue leaving network", network, ":", err.Error())
}
}
}
err = nil
// clean up OS specific stuff
if ncutils.IsWindows() {
daemon.CleanupWindows()
} else if ncutils.IsMac() {
daemon.CleanupMac()
} else if ncutils.IsLinux() {
daemon.CleanupLinux()
} else if ncutils.IsFreeBSD() {
daemon.CleanupFreebsd()
} else if !ncutils.IsKernel() {
logger.Log(1, "manual cleanup required")
}
return err
}
// LeaveNetwork - client exits a network
func LeaveNetwork(network string) error {
cfg, err := config.ReadConfig(network)
if err != nil {
return err
}
logger.Log(2, "deleting node from server")
if err := deleteNodeFromServer(cfg); err != nil {
logger.Log(0, "error deleting node from server", err.Error())
}
logger.Log(2, "deleting wireguard interface")
if err := deleteLocalNetwork(cfg); err != nil {
logger.Log(0, "error deleting wireguard interface", err.Error())
}
logger.Log(2, "deleting configuration files")
if err := WipeLocal(cfg); err != nil {
logger.Log(0, "error deleting local network files", err.Error())
}
logger.Log(2, "removing dns entries")
if err := removeHostDNS(cfg.Node.Interface, ncutils.IsWindows()); err != nil {
logger.Log(0, "failed to delete dns entries for", cfg.Node.Interface, err.Error())
}
ProxyMgmChan <- &manager.ManagerAction{
Action: manager.DeleteInterface,
Payload: manager.ManagerPayload{
InterfaceName: cfg.Node.Interface,
},
}
logger.Log(2, "restarting daemon")
return daemon.Restart()
}
func deleteNodeFromServer(cfg *config.ClientConfig) error {
node := cfg.Node
if node.IsServer == "yes" {
return errors.New("attempt to delete server node ... not permitted")
}
token, err := Authenticate(cfg)
if err != nil {
return fmt.Errorf("unable to authenticate %w", err)
}
url := "https://" + cfg.Server.API + "/api/nodes/" + cfg.Network + "/" + cfg.Node.ID
response, err := API("", http.MethodDelete, url, token)
if err != nil {
return fmt.Errorf("error deleting node on server: %w", err)
}
if response.StatusCode != http.StatusOK {
bodybytes, _ := io.ReadAll(response.Body)
defer response.Body.Close()
return fmt.Errorf("error deleting node from network %s on server %s %s", cfg.Network, response.Status, string(bodybytes))
}
return nil
}
func deleteLocalNetwork(cfg *config.ClientConfig) error {
wgClient, wgErr := wgctrl.New()
if wgErr != nil {
return wgErr
}
removeIface := cfg.Node.Interface
queryAddr := cfg.Node.PrimaryAddress()
if ncutils.IsMac() {
var macIface string
macIface, wgErr = local.GetMacIface(queryAddr)
if wgErr == nil && removeIface != "" {
removeIface = macIface
}
}
dev, devErr := wgClient.Device(removeIface)
if devErr != nil {
return fmt.Errorf("error flushing routes %w", devErr)
}
local.FlushPeerRoutes(removeIface, queryAddr, dev.Peers[:])
_, cidr, cidrErr := net.ParseCIDR(cfg.NetworkSettings.AddressRange)
if cidrErr != nil {
return fmt.Errorf("error flushing routes %w", cidrErr)
}
local.RemoveCIDRRoute(removeIface, queryAddr, cidr)
return nil
}
// DeleteInterface - delete an interface of a network
func DeleteInterface(ifacename string, postdown string) error {
return wireguard.RemoveConf(ifacename, true)
}
// WipeLocal - wipes local instance
func WipeLocal(cfg *config.ClientConfig) error {
if err := wireguard.RemoveConf(cfg.Node.Interface, true); err == nil {
logger.Log(1, "network:", cfg.Node.Network, "removed WireGuard interface: ", cfg.Node.Interface)
} else if strings.Contains(err.Error(), "does not exist") {
err = nil
}
dir := ncutils.GetNetclientPathSpecific()
fail := false
files, err := filepath.Glob(dir + "*" + cfg.Node.Network)
if err != nil {
logger.Log(0, "no matching files", err.Error())
fail = true
}
for _, file := range files {
if err := os.Remove(file); err != nil {
logger.Log(0, "failed to delete file", file, err.Error())
fail = true
}
}
if cfg.Node.Interface != "" {
if ncutils.FileExists(dir + cfg.Node.Interface + ".conf") {
if err := os.Remove(dir + cfg.Node.Interface + ".conf"); err != nil {
logger.Log(0, err.Error())
fail = true
}
}
}
if fail {
return errors.New("not all files were deleted")
}
return nil
}
// GetNetmakerPath - gets netmaker path locally
func GetNetmakerPath() string {
return LINUX_APP_DATA_PATH
}
// API function to interact with netmaker api endpoints. response from endpoint is returned
func API(data any, method, url, authorization string) (*http.Response, error) {
var request *http.Request
var err error
if data != "" {
payload, err := json.Marshal(data)
if err != nil {
return nil, fmt.Errorf("error encoding data %w", err)
}
request, err = http.NewRequest(method, url, bytes.NewBuffer(payload))
if err != nil {
return nil, fmt.Errorf("error creating http request %w", err)
}
request.Header.Set("Content-Type", "application/json")
} else {
request, err = http.NewRequest(method, url, nil)
if err != nil {
return nil, fmt.Errorf("error creating http request %w", err)
}
}
if authorization != "" {
request.Header.Set("authorization", "Bearer "+authorization)
}
request.Header.Set("requestfrom", "node")
return HTTPClient.Do(request)
}
// Authenticate authenticates with api to permit subsequent interactions with the api
func Authenticate(cfg *config.ClientConfig) (string, error) {
pass, err := os.ReadFile(ncutils.GetNetclientPathSpecific() + "secret-" + cfg.Network)
if err != nil {
return "", fmt.Errorf("could not read secrets file %w", err)
}
data := models.AuthParams{
MacAddress: cfg.Node.MacAddress,
ID: cfg.Node.ID,
Password: string(pass),
}
url := "https://" + cfg.Server.API + "/api/nodes/adm/" + cfg.Network + "/authenticate"
response, err := API(data, http.MethodPost, url, "")
if err != nil {
return "", err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
bodybytes, _ := io.ReadAll(response.Body)
return "", fmt.Errorf("failed to authenticate %s %s", response.Status, string(bodybytes))
}
resp := models.SuccessResponse{}
if err := json.NewDecoder(response.Body).Decode(&resp); err != nil {
return "", fmt.Errorf("error decoding respone %w", err)
}
tokenData := resp.Response.(map[string]interface{})
token := tokenData["AuthToken"]
return token.(string), nil
}
// RegisterWithServer calls the register endpoint with privatekey and commonname - api returns ca and client certificate
func SetServerInfo(cfg *config.ClientConfig) error {
cfg, err := config.ReadConfig(cfg.Network)
if err != nil {
return err
}
url := "https://" + cfg.Server.API + "/api/server/getserverinfo"
logger.Log(1, "server at "+url)
token, err := Authenticate(cfg)
if err != nil {
return err
}
response, err := API("", http.MethodGet, url, token)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(response.Status)
}
var resp models.ServerConfig
if err := json.NewDecoder(response.Body).Decode(&resp); err != nil {
return errors.New("unmarshal cert error " + err.Error())
}
// set broker information on register
cfg.Server.Server = resp.Server
cfg.Server.MQPort = resp.MQPort
if err = config.ModServerConfig(&cfg.Server, cfg.Node.Network); err != nil {
logger.Log(0, "error overwriting config with broker information: "+err.Error())
}
return nil
}
func informPortChange(node *models.LegacyNode) {
if node.ListenPort == 0 {
logger.Log(0, "network:", node.Network, "UDP hole punching enabled for node", node.Name)
} else {
logger.Log(0, "network:", node.Network, "node", node.Name, "is using port", strconv.Itoa(int(node.ListenPort)))
}
}

View File

@@ -1,60 +0,0 @@
package functions
import (
"fmt"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/daemon"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/wireguard"
)
// Connect - will attempt to connect a node on given network
func Connect(network string) error {
cfg, err := config.ReadConfig(network)
if err != nil {
return err
}
if cfg.Node.Connected == "yes" {
return fmt.Errorf("node already connected")
}
cfg.Node.Connected = "yes"
filePath := ncutils.GetNetclientPathSpecific() + cfg.Node.Interface + ".conf"
if err = wireguard.ApplyConf(&cfg.Node, cfg.Node.Interface, filePath); err != nil {
return err
}
if err := setupMQTTSingleton(cfg); err != nil {
return err
}
if err := PublishNodeUpdate(cfg); err != nil {
return err
}
daemon.Restart()
return nil
}
// Disconnect - attempts to disconnect a node on given network
func Disconnect(network string) error {
cfg, err := config.ReadConfig(network)
if err != nil {
return err
}
if cfg.Node.Connected == "no" {
return fmt.Errorf("node already disconnected")
}
cfg.Node.Connected = "no"
filePath := ncutils.GetNetclientPathSpecific() + cfg.Node.Interface + ".conf"
if err = wireguard.ApplyConf(&cfg.Node, cfg.Node.Interface, filePath); err != nil {
return err
}
if err := setupMQTTSingleton(cfg); err != nil {
return err
}
if err := PublishNodeUpdate(cfg); err != nil {
return err
}
daemon.Restart()
return nil
}

View File

@@ -1,428 +0,0 @@
package functions
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
"os/signal"
"strings"
"sync"
"syscall"
"time"
mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/netclient/auth"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/global_settings"
"github.com/gravitl/netmaker/netclient/local"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/wireguard"
nmproxy "github.com/gravitl/netmaker/nm-proxy"
"github.com/gravitl/netmaker/nm-proxy/manager"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
var ProxyMgmChan = make(chan *manager.ManagerAction, 100)
var messageCache = new(sync.Map)
var serverSet map[string]bool
var mqclient mqtt.Client
const lastNodeUpdate = "lnu"
const lastPeerUpdate = "lpu"
type cachedMessage struct {
Message string
LastSeen time.Time
}
// Daemon runs netclient daemon from command line
func Daemon() error {
logger.Log(0, "netclient daemon started -- version:", ncutils.Version)
UpdateClientConfig()
if err := ncutils.SavePID(); err != nil {
return err
}
// reference required to eliminate unused statticcheck
serverSet = make(map[string]bool)
serverSet["dummy"] = false
// set ipforwarding on startup
err := local.SetIPForwarding()
if err != nil {
logger.Log(0, err.Error())
}
// == add waitgroup and cancel for checkin routine ==
wg := sync.WaitGroup{}
quit := make(chan os.Signal, 1)
reset := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGTERM, os.Interrupt)
signal.Notify(reset, syscall.SIGHUP)
cancel := startGoRoutines(&wg)
for {
select {
case <-quit:
cancel()
logger.Log(0, "shutting down netclient daemon")
wg.Wait()
if mqclient != nil {
mqclient.Disconnect(250)
}
logger.Log(0, "shutdown complete")
return nil
case <-reset:
logger.Log(0, "received reset")
cancel()
wg.Wait()
if mqclient != nil {
mqclient.Disconnect(250)
}
logger.Log(0, "restarting daemon")
cancel = startGoRoutines(&wg)
}
}
}
func startGoRoutines(wg *sync.WaitGroup) context.CancelFunc {
ctx, cancel := context.WithCancel(context.Background())
serverSet := make(map[string]bool)
networks, _ := ncutils.GetSystemNetworks()
for _, network := range networks {
logger.Log(3, "initializing network", network)
cfg := config.ClientConfig{}
cfg.Network = network
cfg.ReadConfig()
if cfg.Node.Connected == "yes" {
if err := wireguard.ApplyConf(&cfg.Node, cfg.Node.Interface, ncutils.GetNetclientPathSpecific()+cfg.Node.Interface+".conf"); err != nil {
logger.Log(0, "failed to start ", cfg.Node.Interface, "wg interface", err.Error())
}
if cfg.PublicIPService != "" {
global_settings.PublicIPServices[network] = cfg.PublicIPService
}
}
server := cfg.Server.Server
if !serverSet[server] {
// == subscribe to all nodes for each on machine ==
serverSet[server] = true
logger.Log(1, "started daemon for server ", server)
local.SetNetmakerDomainRoute(cfg.Server.API)
wg.Add(1)
go messageQueue(ctx, wg, &cfg)
}
}
wg.Add(1)
go Checkin(ctx, wg)
if len(networks) != 0 {
cfg := config.ClientConfig{}
cfg.Network = networks[0]
cfg.ReadConfig()
apiHost, _, err := net.SplitHostPort(cfg.Server.API)
if err == nil {
wg.Add(1)
go nmproxy.Start(ctx, ProxyMgmChan, apiHost)
logger.Log(0, "Proxy Shutting down....")
}
}
go func(networks []string) {
// for _, network := range networks {
// logger.Log(0, "Collecting interface and peers info to configure proxy...")
// cfg := config.ClientConfig{}
// cfg.Network = network
// cfg.ReadConfig()
// node, err := GetNodeInfo(&cfg)
// if err != nil {
// log.Println("Failed to get node info: ", err)
// continue
// }
// ProxyMgmChan <- &manager.ManagerAction{
// Action: manager.AddInterface,
// Payload: manager.ManagerPayload{
// InterfaceName: node.Node.Interface,
// Peers: node.Peers,
// },
// }
// }
}(networks)
return cancel
}
func GetNodeInfo(cfg *config.ClientConfig) (models.NodeGet, error) {
var nodeGET models.NodeGet
token, err := Authenticate(cfg)
if err != nil {
return nodeGET, err
}
url := fmt.Sprintf("https://%s/api/nodes/%s/%s", cfg.Server.API, cfg.Network, cfg.Node.ID)
response, err := API("", http.MethodGet, url, token)
if err != nil {
return nodeGET, err
}
if response.StatusCode != http.StatusOK {
bytes, err := io.ReadAll(response.Body)
if err != nil {
fmt.Println(err)
}
return nodeGET, (fmt.Errorf("%s %w", string(bytes), err))
}
defer response.Body.Close()
if err := json.NewDecoder(response.Body).Decode(&nodeGET); err != nil {
return nodeGET, fmt.Errorf("error decoding node %w", err)
}
return nodeGET, nil
}
// UpdateKeys -- updates private key and returns new publickey
func UpdateKeys(nodeCfg *config.ClientConfig, client mqtt.Client) error {
logger.Log(0, "interface:", nodeCfg.Node.Interface, "received message to update wireguard keys for network ", nodeCfg.Network)
key, err := wgtypes.GeneratePrivateKey()
if err != nil {
logger.Log(0, "network:", nodeCfg.Node.Network, "error generating privatekey ", err.Error())
return err
}
file := ncutils.GetNetclientPathSpecific() + nodeCfg.Node.Interface + ".conf"
if err := wireguard.UpdatePrivateKey(file, key.String()); err != nil {
logger.Log(0, "network:", nodeCfg.Node.Network, "error updating wireguard key ", err.Error())
return err
}
if storeErr := wireguard.StorePrivKey(key.String(), nodeCfg.Network); storeErr != nil {
logger.Log(0, "network:", nodeCfg.Network, "failed to save private key", storeErr.Error())
return storeErr
}
nodeCfg.Node.PublicKey = key.PublicKey().String()
PublishNodeUpdate(nodeCfg)
return nil
}
// == Private ==
// sets MQ client subscriptions for a specific node config
// should be called for each node belonging to a given server
func setSubscriptions(client mqtt.Client, nodeCfg *config.ClientConfig) {
if token := client.Subscribe(fmt.Sprintf("update/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID), 0, mqtt.MessageHandler(NodeUpdate)); token.WaitTimeout(mq.MQ_TIMEOUT*time.Second) && token.Error() != nil {
if token.Error() == nil {
logger.Log(0, "network:", nodeCfg.Node.Network, "connection timeout")
} else {
logger.Log(0, "network:", nodeCfg.Node.Network, token.Error().Error())
}
return
}
if token := client.Subscribe(fmt.Sprintf("proxy/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID), 0, mqtt.MessageHandler(ProxyUpdate)); token.WaitTimeout(mq.MQ_TIMEOUT*time.Second) && token.Error() != nil {
if token.Error() == nil {
logger.Log(0, "###### network:", nodeCfg.Node.Network, "connection timeout")
} else {
logger.Log(0, "###### network:", nodeCfg.Node.Network, token.Error().Error())
}
return
}
logger.Log(3, fmt.Sprintf("subscribed to node updates for node %s update/%s/%s", nodeCfg.Node.Name, nodeCfg.Node.Network, nodeCfg.Node.ID))
if token := client.Subscribe(fmt.Sprintf("peers/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID), 0, mqtt.MessageHandler(UpdatePeers)); token.Wait() && token.Error() != nil {
logger.Log(0, "network", nodeCfg.Node.Network, token.Error().Error())
return
}
logger.Log(3, fmt.Sprintf("subscribed to peer updates for node %s peers/%s/%s", nodeCfg.Node.Name, nodeCfg.Node.Network, nodeCfg.Node.ID))
}
// on a delete usually, pass in the nodecfg to unsubscribe client broker communications
// for the node in nodeCfg
func unsubscribeNode(client mqtt.Client, nodeCfg *config.ClientConfig) {
client.Unsubscribe(fmt.Sprintf("update/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID))
var ok = true
if token := client.Unsubscribe(fmt.Sprintf("update/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID)); token.WaitTimeout(mq.MQ_TIMEOUT*time.Second) && token.Error() != nil {
if token.Error() == nil {
logger.Log(1, "network:", nodeCfg.Node.Network, "unable to unsubscribe from updates for node ", nodeCfg.Node.Name, "\n", "connection timeout")
} else {
logger.Log(1, "network:", nodeCfg.Node.Network, "unable to unsubscribe from updates for node ", nodeCfg.Node.Name, "\n", token.Error().Error())
}
ok = false
}
if token := client.Unsubscribe(fmt.Sprintf("peers/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID)); token.WaitTimeout(mq.MQ_TIMEOUT*time.Second) && token.Error() != nil {
if token.Error() == nil {
logger.Log(1, "network:", nodeCfg.Node.Network, "unable to unsubscribe from peer updates for node", nodeCfg.Node.Name, "\n", "connection timeout")
} else {
logger.Log(1, "network:", nodeCfg.Node.Network, "unable to unsubscribe from peer updates for node", nodeCfg.Node.Name, "\n", token.Error().Error())
}
ok = false
}
if ok {
logger.Log(1, "network:", nodeCfg.Node.Network, "successfully unsubscribed node ", nodeCfg.Node.ID, " : ", nodeCfg.Node.Name)
}
}
// sets up Message Queue and subsribes/publishes updates to/from server
// the client should subscribe to ALL nodes that exist on server locally
func messageQueue(ctx context.Context, wg *sync.WaitGroup, cfg *config.ClientConfig) {
defer wg.Done()
logger.Log(0, "network:", cfg.Node.Network, "netclient message queue started for server:", cfg.Server.Server)
err := setupMQTT(cfg)
if err != nil {
logger.Log(0, "unable to connect to broker", cfg.Server.Server, err.Error())
return
}
//defer mqclient.Disconnect(250)
<-ctx.Done()
logger.Log(0, "shutting down message queue for server", cfg.Server.Server)
}
// func setMQTTSingenton creates a connection to broker for single use (ie to publish a message)
// only to be called from cli (eg. connect/disconnect, join, leave) and not from daemon ---
func setupMQTTSingleton(cfg *config.ClientConfig) error {
opts := mqtt.NewClientOptions()
server := cfg.Server.Server
port := cfg.Server.MQPort
pass, err := os.ReadFile(ncutils.GetNetclientPathSpecific() + "secret-" + cfg.Network)
if err != nil {
return fmt.Errorf("could not read secrets file %w", err)
}
opts.AddBroker("wss://" + server + ":" + port)
opts.SetUsername(cfg.Node.ID)
opts.SetPassword(string(pass))
mqclient = mqtt.NewClient(opts)
var connecterr error
opts.SetClientID(ncutils.MakeRandomString(23))
if token := mqclient.Connect(); !token.WaitTimeout(30*time.Second) || token.Error() != nil {
logger.Log(0, "unable to connect to broker, retrying ...")
if token.Error() == nil {
connecterr = errors.New("connect timeout")
} else {
connecterr = token.Error()
}
}
return connecterr
}
// setupMQTT creates a connection to broker and returns client
// this function is primarily used to create a connection to publish to the broker
func setupMQTT(cfg *config.ClientConfig) error {
opts := mqtt.NewClientOptions()
server := cfg.Server.Server
port := cfg.Server.MQPort
pass, err := os.ReadFile(ncutils.GetNetclientPathSpecific() + "secret-" + cfg.Network)
if err != nil {
return fmt.Errorf("could not read secrets file %w", err)
}
opts.AddBroker(fmt.Sprintf("wss://%s:%s", server, port))
opts.SetUsername(cfg.Node.ID)
opts.SetPassword(string(pass))
opts.SetClientID(ncutils.MakeRandomString(23))
opts.SetDefaultPublishHandler(All)
opts.SetAutoReconnect(true)
opts.SetConnectRetry(true)
opts.SetConnectRetryInterval(time.Second << 2)
opts.SetKeepAlive(time.Minute >> 1)
opts.SetWriteTimeout(time.Minute)
opts.SetOnConnectHandler(func(client mqtt.Client) {
networks, err := ncutils.GetSystemNetworks()
if err != nil {
logger.Log(0, "error retriving networks", err.Error())
}
for _, network := range networks {
var currNodeCfg config.ClientConfig
currNodeCfg.Network = network
currNodeCfg.ReadConfig()
setSubscriptions(client, &currNodeCfg)
}
})
opts.SetOrderMatters(true)
opts.SetResumeSubs(true)
opts.SetConnectionLostHandler(func(c mqtt.Client, e error) {
logger.Log(0, "network:", cfg.Node.Network, "detected broker connection lost for", cfg.Server.Server)
})
mqclient = mqtt.NewClient(opts)
var connecterr error
for count := 0; count < 3; count++ {
connecterr = nil
if token := mqclient.Connect(); !token.WaitTimeout(30*time.Second) || token.Error() != nil {
logger.Log(0, "unable to connect to broker, retrying ...")
if token.Error() == nil {
connecterr = errors.New("connect timeout")
} else {
connecterr = token.Error()
}
if err := checkBroker(cfg.Server.Server, cfg.Server.MQPort); err != nil {
logger.Log(0, "could not connect to broker", cfg.Server.Server, err.Error())
}
}
}
if connecterr != nil {
logger.Log(0, "failed to establish connection to broker: ", connecterr.Error())
return connecterr
}
return nil
}
// publishes a message to server to update peers on this peer's behalf
func publishSignal(nodeCfg *config.ClientConfig, signal byte) error {
if err := publish(nodeCfg, fmt.Sprintf("signal/%s", nodeCfg.Node.ID), []byte{signal}, 1); err != nil {
return err
}
return nil
}
func parseNetworkFromTopic(topic string) string {
return strings.Split(topic, "/")[1]
}
// should only ever use node client configs
func decryptMsg(nodeCfg *config.ClientConfig, msg []byte) ([]byte, error) {
if len(msg) <= 24 { // make sure message is of appropriate length
return nil, fmt.Errorf("recieved invalid message from broker %v", msg)
}
// setup the keys
diskKey, keyErr := auth.RetrieveTrafficKey(nodeCfg.Node.Network)
if keyErr != nil {
return nil, keyErr
}
serverPubKey, err := ncutils.ConvertBytesToKey(nodeCfg.Node.TrafficKeys.Server)
if err != nil {
return nil, err
}
return ncutils.DeChunk(msg, serverPubKey, diskKey)
}
// == Message Caches ==
func insert(network, which, cache string) {
var newMessage = cachedMessage{
Message: cache,
LastSeen: time.Now(),
}
messageCache.Store(fmt.Sprintf("%s%s", network, which), newMessage)
}
func read(network, which string) string {
val, isok := messageCache.Load(fmt.Sprintf("%s%s", network, which))
if isok {
var readMessage = val.(cachedMessage) // fetch current cached message
if readMessage.LastSeen.IsZero() {
return ""
}
if time.Now().After(readMessage.LastSeen.Add(time.Hour * 24)) { // check if message has been there over a minute
messageCache.Delete(fmt.Sprintf("%s%s", network, which)) // remove old message if expired
return ""
}
return readMessage.Message // return current message if not expired
}
return ""
}

View File

@@ -1,19 +0,0 @@
package functions
import (
"time"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/daemon"
)
//Install - installs binary/daemon
func Install() error {
daemon.Stop()
if err := daemon.InstallDaemon(); err != nil {
logger.Log(0, "error installing daemon", err.Error())
return err
}
time.Sleep(time.Second * 5)
return daemon.Restart()
}

View File

@@ -1,412 +0,0 @@
package functions
import (
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"os"
"os/signal"
"runtime"
"strings"
"syscall"
"github.com/gorilla/websocket"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/models/promodels"
"github.com/gravitl/netmaker/netclient/auth"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/daemon"
"github.com/gravitl/netmaker/netclient/global_settings"
"github.com/gravitl/netmaker/netclient/local"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/wireguard"
"golang.org/x/crypto/nacl/box"
"golang.org/x/term"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// JoinViaSso - Handles the Single Sign-On flow on the end point VPN client side
// Contacts the server provided by the user (and thus specified in cfg.SsoServer)
// get the URL to authenticate with a provider and shows the user the URL.
// Then waits for user to authenticate with the URL.
// Upon user successful auth flow finished - server should return access token to the requested network
// Otherwise the error message is sent which can be displayed to the user
func JoinViaSSo(cfg *config.ClientConfig, privateKey string) error {
// User must tell us which network he is joining
if cfg.Node.Network == "" {
return errors.New("no network provided")
}
// Prepare a channel for interrupt
// Channel to listen for interrupt signal to terminate gracefully
interrupt := make(chan os.Signal, 1)
// Notify the interrupt channel for SIGINT
signal.Notify(interrupt, os.Interrupt)
// Web Socket is used, construct the URL accordingly ...
socketUrl := fmt.Sprintf("wss://%s/api/oauth/node-handler", cfg.SsoServer)
// Dial the netmaker server controller
conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil)
if err != nil {
logger.Log(0, fmt.Sprintf("error connecting to %s : %s", cfg.Server.API, err.Error()))
return err
}
// Don't forget to close when finished
defer conn.Close()
// Find and set node MacAddress
if cfg.Node.MacAddress == "" {
macs, err := ncutils.GetMacAddr()
if err != nil {
//if macaddress can't be found set to random string
cfg.Node.MacAddress = ncutils.MakeRandomString(18)
} else {
cfg.Node.MacAddress = macs[0]
}
}
var loginMsg promodels.LoginMsg
loginMsg.Mac = cfg.Node.MacAddress
loginMsg.Network = cfg.Node.Network
if global_settings.User != "" {
fmt.Printf("Continuing with user, %s.\nPlease input password:\n", global_settings.User)
pass, err := term.ReadPassword(int(syscall.Stdin))
if err != nil || string(pass) == "" {
logger.FatalLog("no password provided, exiting")
}
loginMsg.User = global_settings.User
loginMsg.Password = string(pass)
fmt.Println("attempting login...")
}
msgTx, err := json.Marshal(loginMsg)
if err != nil {
logger.Log(0, fmt.Sprintf("failed to marshal message %+v", loginMsg))
return err
}
err = conn.WriteMessage(websocket.TextMessage, []byte(msgTx))
if err != nil {
logger.FatalLog("Error during writing to websocket:", err.Error())
return err
}
// if user provided, server will handle authentication
if loginMsg.User == "" {
// We are going to get instructions on how to authenticate
// Wait to receive something from server
_, msg, err := conn.ReadMessage()
if err != nil {
return err
}
// Print message from the netmaker controller to the user
fmt.Printf("Please visit:\n %s \n to authenticate", string(msg))
}
// Now the user is authenticating and we need to block until received
// An answer from the server.
// Server waits ~5 min - If takes too long timeout will be triggered by the server
done := make(chan struct{})
defer close(done)
// Following code will run in a separate go routine
// it reads a message from the server which either contains 'AccessToken:' string or not
// if not - then it contains an Error to display.
// if yes - then AccessToken is to be used to proceed joining the network
go func() {
for {
msgType, msg, err := conn.ReadMessage()
if err != nil {
if msgType < 0 {
logger.Log(1, "received close message from server")
done <- struct{}{}
return
}
// Error reading a message from the server
if !strings.Contains(err.Error(), "normal") {
logger.Log(0, "read:", err.Error())
}
return
}
if msgType == websocket.CloseMessage {
logger.Log(1, "received close message from server")
done <- struct{}{}
return
}
// Get the access token from the response
if strings.Contains(string(msg), "AccessToken: ") {
// Access was granted
rxToken := strings.TrimPrefix(string(msg), "AccessToken: ")
accesstoken, err := config.ParseAccessToken(rxToken)
if err != nil {
logger.Log(0, fmt.Sprintf("failed to parse received access token %s,err=%s\n", accesstoken, err.Error()))
return
}
cfg.Network = accesstoken.ClientConfig.Network
cfg.Node.Network = accesstoken.ClientConfig.Network
cfg.AccessKey = accesstoken.ClientConfig.Key
cfg.Node.LocalRange = accesstoken.ClientConfig.LocalRange
//cfg.Server.Server = accesstoken.ServerConfig.Server
cfg.Server.API = accesstoken.APIConnString
} else {
// Access was not granted. Display a message from the server
logger.Log(0, "Message from server:", string(msg))
cfg.AccessKey = ""
return
}
}
}()
for {
select {
case <-done:
logger.Log(1, "finished")
return nil
case <-interrupt:
logger.Log(0, "interrupt received, closing connection")
// Cleanly close the connection by sending a close message and then
// waiting (with timeout) for the server to close the connection.
err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
logger.Log(0, "write close:", err.Error())
return err
}
return nil
}
}
}
// JoinNetwork - helps a client join a network
func JoinNetwork(cfg *config.ClientConfig, privateKey string) error {
if cfg.Node.Network == "" {
return errors.New("no network provided")
}
var err error
if local.HasNetwork(cfg.Network) {
err := errors.New("ALREADY_INSTALLED. Netclient appears to already be installed for " + cfg.Network + ". To re-install, please remove by executing 'sudo netclient leave -n " + cfg.Network + "'. Then re-run the install command.")
return err
}
err = config.Write(cfg, cfg.Network)
if err != nil {
return err
}
if cfg.Node.Password == "" {
cfg.Node.Password = logic.GenPassWord()
}
//check if ListenPort was set on command line
if cfg.Node.ListenPort != 0 {
cfg.Node.UDPHolePunch = "no"
}
var trafficPubKey, trafficPrivKey, errT = box.GenerateKey(rand.Reader) // generate traffic keys
if errT != nil {
return errT
}
// == handle keys ==
if err = auth.StoreSecret(cfg.Node.Password, cfg.Node.Network); err != nil {
return err
}
if err = auth.StoreTrafficKey(trafficPrivKey, cfg.Node.Network); err != nil {
return err
}
trafficPubKeyBytes, err := ncutils.ConvertKeyToBytes(trafficPubKey)
if err != nil {
return err
} else if trafficPubKeyBytes == nil {
return fmt.Errorf("traffic key is nil")
}
cfg.Node.TrafficKeys.Mine = trafficPubKeyBytes
cfg.Node.TrafficKeys.Server = nil
// == end handle keys ==
if cfg.Node.LocalAddress == "" {
intIP, err := getPrivateAddr()
if err == nil {
cfg.Node.LocalAddress = intIP
} else {
logger.Log(1, "network:", cfg.Network, "error retrieving private address: ", err.Error())
}
}
if len(cfg.Node.Interfaces) == 0 {
ip, err := getInterfaces()
if err != nil {
logger.Log(0, "failed to retrive local interfaces", err.Error())
} else {
cfg.Node.Interfaces = *ip
}
}
// set endpoint if blank. set to local if local net, retrieve from function if not
if cfg.Node.Endpoint == "" {
if cfg.Node.IsLocal == "yes" && cfg.Node.LocalAddress != "" {
cfg.Node.Endpoint = cfg.Node.LocalAddress
} else {
cfg.Node.Endpoint, err = ncutils.GetPublicIP(cfg.Server.API)
}
if err != nil || cfg.Node.Endpoint == "" {
logger.Log(0, "network:", cfg.Network, "error setting cfg.Node.Endpoint.")
return err
}
}
// Generate and set public/private WireGuard Keys
if privateKey == "" {
wgPrivatekey, err := wgtypes.GeneratePrivateKey()
if err != nil {
log.Fatal(err)
}
privateKey = wgPrivatekey.String()
cfg.Node.PublicKey = wgPrivatekey.PublicKey().String()
}
// Find and set node MacAddress
if cfg.Node.MacAddress == "" {
macs, err := ncutils.GetMacAddr()
if err != nil || len(macs) == 0 {
//if macaddress can't be found set to random string
cfg.Node.MacAddress = ncutils.MakeRandomString(18)
} else {
cfg.Node.MacAddress = macs[0]
}
}
if ncutils.IsFreeBSD() {
cfg.Node.UDPHolePunch = "no"
cfg.Node.FirewallInUse = models.FIREWALL_IPTABLES // nftables not supported by FreeBSD
}
if cfg.Node.FirewallInUse == "" {
if ncutils.IsNFTablesPresent() {
cfg.Node.FirewallInUse = models.FIREWALL_NFTABLES
} else if ncutils.IsIPTablesPresent() {
cfg.Node.FirewallInUse = models.FIREWALL_IPTABLES
} else {
cfg.Node.FirewallInUse = models.FIREWALL_NONE
}
}
// make sure name is appropriate, if not, give blank name
cfg.Node.Name = formatName(cfg.Node)
cfg.Node.OS = runtime.GOOS
cfg.Node.Version = ncutils.Version
cfg.Node.AccessKey = cfg.AccessKey
//not sure why this is needed ... setnode defaults should take care of this on server
cfg.Node.IPForwarding = "yes"
logger.Log(0, "joining "+cfg.Network+" at "+cfg.Server.API)
url := "https://" + cfg.Server.API + "/api/nodes/" + cfg.Network
response, err := API(cfg.Node, http.MethodPost, url, cfg.AccessKey)
if err != nil {
return fmt.Errorf("error creating node %w", err)
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
bodybytes, _ := io.ReadAll(response.Body)
return fmt.Errorf("error creating node %s %s", response.Status, string(bodybytes))
}
var nodeGET models.NodeGet
if err := json.NewDecoder(response.Body).Decode(&nodeGET); err != nil {
//not sure the next line will work as response.Body probably needs to be reset before it can be read again
bodybytes, _ := io.ReadAll(response.Body)
return fmt.Errorf("error decoding node from server %w %s", err, string(bodybytes))
}
node := nodeGET.Node
if nodeGET.Peers == nil {
nodeGET.Peers = []wgtypes.PeerConfig{}
}
// safety check. If returned node from server is local, but not currently configured as local, set to local addr
if cfg.Node.IsLocal != "yes" && node.IsLocal == "yes" && node.LocalRange != "" {
node.LocalAddress, err = ncutils.GetLocalIP(node.LocalRange)
if err != nil {
return err
}
node.Endpoint = node.LocalAddress
}
if ncutils.IsFreeBSD() {
node.UDPHolePunch = "no"
cfg.Node.IsStatic = "yes"
}
cfg.Server = nodeGET.ServerConfig
err = wireguard.StorePrivKey(privateKey, cfg.Network)
if err != nil {
return err
}
if node.IsPending == "yes" {
logger.Log(0, "network:", cfg.Network, "node is marked as PENDING.")
logger.Log(0, "network:", cfg.Network, "awaiting approval from Admin before configuring WireGuard.")
if cfg.Daemon != "off" {
return daemon.InstallDaemon()
}
}
logger.Log(1, "network:", cfg.Node.Network, "node created on remote server...updating configs")
err = ncutils.ModPort(&node)
if err != nil {
return err
}
informPortChange(&node)
err = config.ModNodeConfig(&node)
if err != nil {
return err
}
err = config.ModServerConfig(&cfg.Server, node.Network)
if err != nil {
return err
}
// attempt to make backup
if err = config.SaveBackup(node.Network); err != nil {
logger.Log(0, "network:", node.Network, "failed to make backup, node will not auto restore if config is corrupted")
}
local.SetNetmakerDomainRoute(cfg.Server.API)
cfg.Node = node
logger.Log(0, "starting wireguard")
err = wireguard.InitWireguard(&node, privateKey, nodeGET.Peers[:])
if err != nil {
return err
}
if cfg.Server.Server == "" {
return errors.New("did not receive broker address from registration")
}
if cfg.Daemon == "install" || ncutils.IsFreeBSD() {
err = daemon.InstallDaemon()
if err != nil {
return err
}
}
if err := daemon.Restart(); err != nil {
logger.Log(3, "daemon restart failed:", err.Error())
if err := daemon.Start(); err != nil {
return err
}
}
return nil
}
// format name appropriately. Set to blank on failure
func formatName(node models.LegacyNode) string {
// Logic to properly format name
if !node.NameInNodeCharSet() {
node.Name = ncutils.DNSFormatString(node.Name)
}
if len(node.Name) > models.MAX_NAME_LENGTH {
node.Name = ncutils.ShortenString(node.Name, models.MAX_NAME_LENGTH)
}
if !node.NameInNodeCharSet() || len(node.Name) > models.MAX_NAME_LENGTH {
logger.Log(1, "network:", node.Network, "could not properly format name: "+node.Name)
logger.Log(1, "network:", node.Network, "setting name to blank")
node.Name = ""
}
return node.Name
}

View File

@@ -1,147 +0,0 @@
package functions
import (
"encoding/json"
"fmt"
"io"
"net/http"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/ncutils"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// Peer - the peer struct for list
type Peer struct {
Name string `json:"name,omitempty"`
Interface string `json:"interface,omitempty"`
PrivateIPv4 string `json:"private_ipv4,omitempty"`
PrivateIPv6 string `json:"private_ipv6,omitempty"`
PublicKey string `json:"public_key,omitempty"`
PublicEndpoint string `json:"public_endpoint,omitempty"`
Addresses []address `json:"addresses,omitempty"`
}
// Network - the local node network representation for list command
type Network struct {
Name string `json:"name"`
ID string `json:"node_id"`
CurrentNode Peer `json:"current_node"`
Peers []Peer `json:"peers"`
}
type address struct {
CIDR string `json:"cidr,omitempty"`
IP string `json:"ip,omitempty"`
}
// List - lists the current peers for the local node with name and node ID
func List(network string) ([]Network, error) {
nets := []Network{}
var err error
var networks []string
if network == "all" {
networks, err = ncutils.GetSystemNetworks()
if err != nil {
return nil, err
}
} else {
networks = append(networks, network)
}
for _, network := range networks {
net, err := getNetwork(network)
if err != nil {
logger.Log(1, network+": Could not retrieve network configuration.")
return nil, err
}
peers, err := getPeers(network)
if err == nil && len(peers) > 0 {
net.Peers = peers
}
nets = append(nets, net)
}
jsoncfg, _ := json.Marshal(struct {
Networks []Network `json:"networks"`
}{nets})
fmt.Println(string(jsoncfg))
return nets, nil
}
func getNetwork(network string) (Network, error) {
cfg, err := config.ReadConfig(network)
if err != nil {
return Network{}, fmt.Errorf("reading configuration for network %v: %w", network, err)
}
//peers, err := getPeers(network)
peers := []Peer{}
if err != nil {
return Network{}, fmt.Errorf("listing peers for network %v: %w", network, err)
}
return Network{
Name: network,
ID: cfg.Node.ID,
Peers: peers,
CurrentNode: Peer{
Name: cfg.Node.Name,
Interface: cfg.Node.Interface,
PrivateIPv4: cfg.Node.Address,
PrivateIPv6: cfg.Node.Address6,
PublicEndpoint: cfg.Node.Endpoint,
},
}, nil
}
func getPeers(network string) ([]Peer, error) {
cfg, err := config.ReadConfig(network)
if err != nil {
return []Peer{}, err
}
token, err := Authenticate(cfg)
if err != nil {
return nil, err
}
url := "https://" + cfg.Server.API + "/api/nodes/" + cfg.Network + "/" + cfg.Node.ID
response, err := API("", http.MethodGet, url, token)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
bytes, err := io.ReadAll(response.Body)
if err != nil {
fmt.Println(err)
}
return nil, (fmt.Errorf("%s %w", string(bytes), err))
}
defer response.Body.Close()
var nodeGET models.NodeGet
if err := json.NewDecoder(response.Body).Decode(&nodeGET); err != nil {
return nil, fmt.Errorf("error decoding node %w", err)
}
if nodeGET.Peers == nil {
nodeGET.Peers = []wgtypes.PeerConfig{}
}
peers := []Peer{}
for _, peer := range nodeGET.Peers {
var addresses = []address{}
for j := range peer.AllowedIPs {
newAddress := address{
CIDR: peer.AllowedIPs[j].String(),
IP: peer.AllowedIPs[j].IP.String(),
}
addresses = append(addresses, newAddress)
}
peers = append(peers, Peer{
PublicKey: peer.PublicKey.String(),
PublicEndpoint: peer.Endpoint.String(),
Addresses: addresses,
})
}
return peers, nil
}

View File

@@ -1,63 +0,0 @@
//go:build !freebsd
// +build !freebsd
package functions
import (
"strconv"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/local"
"github.com/gravitl/netmaker/netclient/ncutils"
"golang.zx2c4.com/wireguard/wgctrl"
)
// GetLocalListenPort - Gets the port running on the local interface
func GetLocalListenPort(ifacename string) (int32, error) {
client, err := wgctrl.New()
if err != nil {
logger.Log(0, "failed to start wgctrl")
return 0, err
}
defer client.Close()
device, err := client.Device(ifacename)
if err != nil {
logger.Log(0, "failed to parse interface", ifacename)
return 0, err
}
return int32(device.ListenPort), nil
}
// UpdateLocalListenPort - check local port, if different, mod config and publish
func UpdateLocalListenPort(nodeCfg *config.ClientConfig) error {
var err error
ifacename := getRealIface(nodeCfg.Node.Interface, nodeCfg.Node.Address)
localPort, err := GetLocalListenPort(ifacename)
if err != nil {
logger.Log(1, "network:", nodeCfg.Node.Network, "error encountered checking local listen port: ", ifacename, err.Error())
} else if nodeCfg.Node.LocalListenPort != localPort && localPort != 0 {
logger.Log(1, "network:", nodeCfg.Node.Network, "local port has changed from ", strconv.Itoa(int(nodeCfg.Node.LocalListenPort)), " to ", strconv.Itoa(int(localPort)))
nodeCfg.Node.LocalListenPort = localPort
err = config.ModNodeConfig(&nodeCfg.Node)
if err != nil {
return err
}
if err := PublishNodeUpdate(nodeCfg); err != nil {
logger.Log(0, "could not publish local port change", err.Error())
}
}
return err
}
func getRealIface(ifacename string, address string) string {
var deviceiface = ifacename
var err error
if ncutils.IsMac() { // if node is Mac (Darwin) get the tunnel name first
deviceiface, err = local.GetMacIface(address)
if err != nil || deviceiface == "" {
deviceiface = ifacename
}
}
return deviceiface
}

View File

@@ -1,50 +0,0 @@
//go:build freebsd
// +build freebsd
package functions
import (
"errors"
"strconv"
"strings"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// GetLocalListenPort - Gets the port running on the local interface
func GetLocalListenPort(ifacename string) (int32, error) {
portstring, err := ncutils.RunCmd("wg show "+ifacename+" listen-port", false)
if err != nil {
return 0, err
}
portstring = strings.TrimSuffix(portstring, "\n")
i, err := strconv.ParseInt(portstring, 10, 32)
if err != nil {
return 0, err
} else if i == 0 {
return 0, errors.New("parsed port is unset or invalid")
}
return int32(i), nil
}
// UpdateLocalListenPort - check local port, if different, mod config and publish
func UpdateLocalListenPort(nodeCfg *config.ClientConfig) error {
var err error
localPort, err := GetLocalListenPort(nodeCfg.Node.Interface)
if err != nil {
logger.Log(1, "network:", nodeCfg.Node.Network, "error encountered checking local listen port for interface : ", nodeCfg.Node.Interface, err.Error())
} else if nodeCfg.Node.LocalListenPort != localPort && localPort != 0 {
logger.Log(1, "network:", nodeCfg.Node.Network, "local port has changed from ", strconv.Itoa(int(nodeCfg.Node.LocalListenPort)), " to ", strconv.Itoa(int(localPort)))
nodeCfg.Node.LocalListenPort = localPort
err = config.ModNodeConfig(&nodeCfg.Node)
if err != nil {
return err
}
if err := PublishNodeUpdate(nodeCfg); err != nil {
logger.Log(0, "network:", nodeCfg.Node.Network, "could not publish local port change")
}
}
return err
}

View File

@@ -1,369 +0,0 @@
package functions
import (
"encoding/json"
"errors"
"fmt"
"net"
"os"
"runtime"
"strings"
"time"
mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/wireguard"
"github.com/gravitl/netmaker/nm-proxy/manager"
"github.com/guumaster/hostctl/pkg/file"
"github.com/guumaster/hostctl/pkg/parser"
"github.com/guumaster/hostctl/pkg/types"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// All -- mqtt message hander for all ('#') topics
var All mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
logger.Log(0, "default message handler -- received message but not handling")
logger.Log(0, "topic: "+string(msg.Topic()))
//logger.Log(0, "Message: " + string(msg.Payload()))
}
func ProxyUpdate(client mqtt.Client, msg mqtt.Message) {
var nodeCfg config.ClientConfig
var proxyUpdate manager.ManagerAction
var network = parseNetworkFromTopic(msg.Topic())
nodeCfg.Network = network
nodeCfg.ReadConfig()
logger.Log(0, "---------> Recieved a proxy update")
data, dataErr := decryptMsg(&nodeCfg, msg.Payload())
if dataErr != nil {
return
}
err := json.Unmarshal([]byte(data), &proxyUpdate)
if err != nil {
logger.Log(0, "error unmarshalling proxy update data"+err.Error())
return
}
ProxyMgmChan <- &proxyUpdate
}
// NodeUpdate -- mqtt message handler for /update/<NodeID> topic
func NodeUpdate(client mqtt.Client, msg mqtt.Message) {
var newNode models.LegacyNode
var nodeCfg config.ClientConfig
var network = parseNetworkFromTopic(msg.Topic())
nodeCfg.Network = network
nodeCfg.ReadConfig()
data, dataErr := decryptMsg(&nodeCfg, msg.Payload())
if dataErr != nil {
return
}
err := json.Unmarshal([]byte(data), &newNode)
if err != nil {
logger.Log(0, "error unmarshalling node update data"+err.Error())
return
}
if newNode.Proxy {
if newNode.Proxy != nodeCfg.Node.Proxy {
if err := config.Write(&nodeCfg, nodeCfg.Network); err != nil {
logger.Log(0, nodeCfg.Node.Network, "error updating node configuration: ", err.Error())
}
}
logger.Log(0, "Node is attached with proxy,ignore this node update...")
return
}
// see if cache hit, if so skip
var currentMessage = read(newNode.Network, lastNodeUpdate)
if currentMessage == string(data) {
return
}
insert(newNode.Network, lastNodeUpdate, string(data)) // store new message in cache
logger.Log(0, "network:", newNode.Network, "received message to update node "+newNode.Name)
// ensure that OS never changes
newNode.OS = runtime.GOOS
// check if interface needs to delta
ifaceDelta := ncutils.IfaceDelta(&nodeCfg.Node, &newNode)
shouldDNSChange := nodeCfg.Node.DNSOn != newNode.DNSOn
hubChange := nodeCfg.Node.IsHub != newNode.IsHub
keepaliveChange := nodeCfg.Node.PersistentKeepalive != newNode.PersistentKeepalive
nodeCfg.Node = newNode
switch newNode.Action {
case models.NODE_DELETE:
logger.Log(0, "network:", nodeCfg.Node.Network, " received delete request for %s", nodeCfg.Node.Name)
unsubscribeNode(client, &nodeCfg)
if err = LeaveNetwork(nodeCfg.Node.Network); err != nil {
if !strings.Contains("rpc error", err.Error()) {
logger.Log(0, "failed to leave, please check that local files for network", nodeCfg.Node.Network, "were removed")
return
}
}
logger.Log(0, nodeCfg.Node.Name, "was removed from network", nodeCfg.Node.Network)
return
case models.NODE_UPDATE_KEY:
// == get the current key for node ==
oldPrivateKey, retErr := wireguard.RetrievePrivKey(nodeCfg.Network)
if retErr != nil {
break
}
if err := UpdateKeys(&nodeCfg, client); err != nil {
logger.Log(0, "err updating wireguard keys, reusing last key\n", err.Error())
if key, parseErr := wgtypes.ParseKey(oldPrivateKey); parseErr == nil {
wireguard.StorePrivKey(key.String(), nodeCfg.Network)
nodeCfg.Node.PublicKey = key.PublicKey().String()
}
}
ifaceDelta = true
case models.NODE_FORCE_UPDATE:
ifaceDelta = true
case models.NODE_NOOP:
default:
}
// Save new config
nodeCfg.Node.Action = models.NODE_NOOP
if err := config.Write(&nodeCfg, nodeCfg.Network); err != nil {
logger.Log(0, nodeCfg.Node.Network, "error updating node configuration: ", err.Error())
}
nameserver := nodeCfg.Server.CoreDNSAddr
privateKey, err := wireguard.RetrievePrivKey(newNode.Network)
if err != nil {
logger.Log(0, "error reading PrivateKey "+err.Error())
return
}
file := ncutils.GetNetclientPathSpecific() + nodeCfg.Node.Interface + ".conf"
if newNode.ListenPort != nodeCfg.Node.LocalListenPort {
if err := wireguard.RemoveConf(newNode.Interface, false); err != nil {
logger.Log(0, "error remove interface", newNode.Interface, err.Error())
}
err = ncutils.ModPort(&newNode)
if err != nil {
logger.Log(0, "network:", nodeCfg.Node.Network, "error modifying node port on", newNode.Name, "-", err.Error())
return
}
ifaceDelta = true
informPortChange(&newNode)
}
if err := wireguard.UpdateWgInterface(file, privateKey, nameserver, newNode); err != nil {
logger.Log(0, "error updating wireguard config "+err.Error())
return
}
if keepaliveChange {
wireguard.UpdateKeepAlive(file, newNode.PersistentKeepalive)
}
logger.Log(0, "applying WG conf to "+file)
err = wireguard.ApplyConf(&nodeCfg.Node, nodeCfg.Node.Interface, file)
if err != nil {
logger.Log(0, "error restarting wg after node update -", err.Error())
return
}
time.Sleep(time.Second)
// if newNode.DNSOn == "yes" {
// for _, server := range newNode.NetworkSettings.DefaultServerAddrs {
// if server.IsLeader {
// go local.SetDNSWithRetry(newNode, server.Address)
// break
// }
// }
// }
if ifaceDelta { // if a change caused an ifacedelta we need to notify the server to update the peers
doneErr := publishSignal(&nodeCfg, ncutils.DONE)
if doneErr != nil {
logger.Log(0, "network:", nodeCfg.Node.Network, "could not notify server to update peers after interface change")
} else {
logger.Log(0, "network:", nodeCfg.Node.Network, "signalled finished interface update to server")
}
} else if hubChange {
doneErr := publishSignal(&nodeCfg, ncutils.DONE)
if doneErr != nil {
logger.Log(0, "network:", nodeCfg.Node.Network, "could not notify server to update peers after hub change")
} else {
logger.Log(0, "network:", nodeCfg.Node.Network, "signalled finished hub update to server")
}
}
//deal with DNS
if newNode.DNSOn != "yes" && shouldDNSChange && nodeCfg.Node.Interface != "" {
logger.Log(0, "network:", nodeCfg.Node.Network, "settng DNS off")
if err := removeHostDNS(nodeCfg.Node.Interface, ncutils.IsWindows()); err != nil {
logger.Log(0, "network:", nodeCfg.Node.Network, "error removing netmaker profile from /etc/hosts "+err.Error())
}
// _, err := ncutils.RunCmd("/usr/bin/resolvectl revert "+nodeCfg.Node.Interface, true)
// if err != nil {
// logger.Log(0, "error applying dns" + err.Error())
// }
}
_ = UpdateLocalListenPort(&nodeCfg)
}
// UpdatePeers -- mqtt message handler for peers/<Network>/<NodeID> topic
func UpdatePeers(client mqtt.Client, msg mqtt.Message) {
var peerUpdate models.PeerUpdate
var network = parseNetworkFromTopic(msg.Topic())
var cfg = config.ClientConfig{}
cfg.Network = network
cfg.ReadConfig()
data, dataErr := decryptMsg(&cfg, msg.Payload())
if dataErr != nil {
return
}
err := json.Unmarshal([]byte(data), &peerUpdate)
if err != nil {
logger.Log(0, "error unmarshalling peer data")
return
}
// see if cached hit, if so skip
var currentMessage = read(peerUpdate.Network, lastPeerUpdate)
if currentMessage == string(data) {
return
}
insert(peerUpdate.Network, lastPeerUpdate, string(data))
// check version
if peerUpdate.ServerVersion != ncutils.Version {
logger.Log(0, "server/client version mismatch server: ", peerUpdate.ServerVersion, " client: ", ncutils.Version)
}
if peerUpdate.ServerVersion != cfg.Server.Version {
logger.Log(1, "updating server version")
cfg.Server.Version = peerUpdate.ServerVersion
config.Write(&cfg, cfg.Network)
}
// if cfg.Node.Proxy {
// ProxyMgmChan <- &peerUpdate.ProxyUpdate
// return
// }
file := ncutils.GetNetclientPathSpecific() + cfg.Node.Interface + ".conf"
internetGateway, err := wireguard.UpdateWgPeers(file, peerUpdate.Peers)
if err != nil {
logger.Log(0, "error updating wireguard peers"+err.Error())
return
}
//check if internet gateway has changed
oldGateway, err := net.ResolveUDPAddr("udp", cfg.Node.InternetGateway)
// note: may want to remove second part (oldGateway == &net.UDPAddr{})
// since it's a pointer, will never be true
if err != nil || (oldGateway == &net.UDPAddr{}) {
oldGateway = nil
}
if (internetGateway == nil && oldGateway != nil) || (internetGateway != nil && internetGateway.String() != oldGateway.String()) {
cfg.Node.InternetGateway = internetGateway.String()
if err := config.ModNodeConfig(&cfg.Node); err != nil {
logger.Log(0, "failed to save internet gateway", err.Error())
}
if err := wireguard.ApplyConf(&cfg.Node, cfg.Node.Interface, file); err != nil {
logger.Log(0, "error applying internet gateway", err.Error())
}
UpdateLocalListenPort(&cfg)
return
}
// queryAddr := cfg.Node.PrimaryAddress()
//err = wireguard.SyncWGQuickConf(cfg.Node.Interface, file)
// var iface = cfg.Node.Interface
// if ncutils.IsMac() {
// iface, err = local.GetMacIface(queryAddr)
// if err != nil {
// logger.Log(0, "error retrieving mac iface: "+err.Error())
// return
// }
// }
// err = wireguard.SetPeers(iface, &cfg.Node, peerUpdate.Peers)
// if err != nil {
// logger.Log(0, "error syncing wg after peer update: "+err.Error())
// return
// }
logger.Log(0, "network:", cfg.Node.Network, "received peer update for node "+cfg.Node.Name+" "+cfg.Node.Network)
if cfg.Node.DNSOn == "yes" {
if err := setHostDNS(peerUpdate.DNS, cfg.Node.Interface, ncutils.IsWindows()); err != nil {
logger.Log(0, "network:", cfg.Node.Network, "error updating /etc/hosts "+err.Error())
return
}
} else {
if err := removeHostDNS(cfg.Node.Interface, ncutils.IsWindows()); err != nil {
logger.Log(0, "network:", cfg.Node.Network, "error removing profile from /etc/hosts "+err.Error())
return
}
}
_ = UpdateLocalListenPort(&cfg)
}
func setHostDNS(dns, iface string, windows bool) error {
etchosts := "/etc/hosts"
temp := os.TempDir()
lockfile := temp + "/netclient-lock"
if windows {
etchosts = "c:\\windows\\system32\\drivers\\etc\\hosts"
lockfile = temp + "\\netclient-lock"
}
if _, err := os.Stat(lockfile); !errors.Is(err, os.ErrNotExist) {
return errors.New("/etc/hosts file is locked .... aborting")
}
lock, err := os.Create(lockfile)
if err != nil {
return fmt.Errorf("could not create lock file %w", err)
}
lock.Close()
defer os.Remove(lockfile)
dnsdata := strings.NewReader(dns)
profile, err := parser.ParseProfile(dnsdata)
if err != nil {
return err
}
hosts, err := file.NewFile(etchosts)
if err != nil {
return err
}
profile.Name = strings.ToLower(iface)
profile.Status = types.Enabled
if err := hosts.ReplaceProfile(profile); err != nil {
return err
}
if err := hosts.Flush(); err != nil {
return err
}
return nil
}
func removeHostDNS(iface string, windows bool) error {
etchosts := "/etc/hosts"
temp := os.TempDir()
lockfile := temp + "/netclient-lock"
if windows {
etchosts = "c:\\windows\\system32\\drivers\\etc\\hosts"
lockfile = temp + "\\netclient-lock"
}
if _, err := os.Stat(lockfile); !errors.Is(err, os.ErrNotExist) {
return errors.New("/etc/hosts file is locked .... aborting")
}
lock, err := os.Create(lockfile)
if err != nil {
return fmt.Errorf("could not create lock file %w", err)
}
lock.Close()
defer os.Remove(lockfile)
hosts, err := file.NewFile(etchosts)
if err != nil {
return err
}
if err := hosts.RemoveProfile(strings.ToLower(iface)); err != nil {
if err == types.ErrUnknownProfile {
return nil
}
return err
}
if err := hosts.Flush(); err != nil {
return err
}
return nil
}

View File

@@ -1,304 +0,0 @@
package functions
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"strconv"
"sync"
"time"
"github.com/cloverstd/tcping/ping"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic/metrics"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/auth"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/ncutils"
)
var metricsCache = new(sync.Map)
// Checkin -- go routine that checks for public or local ip changes, publishes changes
//
// if there are no updates, simply "pings" the server as a checkin
func Checkin(ctx context.Context, wg *sync.WaitGroup) {
logger.Log(2, "starting checkin goroutine")
defer wg.Done()
ticker := time.NewTicker(time.Minute * ncutils.CheckInInterval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
logger.Log(0, "checkin routine closed")
return
case <-ticker.C:
if mqclient != nil && mqclient.IsConnected() {
checkin()
} else {
logger.Log(0, "MQ client is not connected, skipping checkin...")
}
}
}
}
func checkin() {
networks, _ := ncutils.GetSystemNetworks()
logger.Log(3, "checkin with server(s) for all networks")
for _, network := range networks {
var nodeCfg config.ClientConfig
nodeCfg.Network = network
nodeCfg.ReadConfig()
// check for nftables present if on Linux
if ncutils.IsLinux() {
if ncutils.IsNFTablesPresent() {
nodeCfg.Node.FirewallInUse = models.FIREWALL_NFTABLES
} else {
nodeCfg.Node.FirewallInUse = models.FIREWALL_IPTABLES
}
} else {
// defaults to iptables for now, may need another default for non-Linux OSes
nodeCfg.Node.FirewallInUse = models.FIREWALL_IPTABLES
}
if nodeCfg.Node.Connected == "yes" {
if nodeCfg.Node.IsStatic != "yes" {
extIP, err := ncutils.GetPublicIP(nodeCfg.Server.API)
if err != nil {
logger.Log(1, "error encountered checking public ip addresses: ", err.Error())
}
if nodeCfg.Node.Endpoint != extIP && extIP != "" {
logger.Log(1, "network:", nodeCfg.Node.Network, "endpoint has changed from ", nodeCfg.Node.Endpoint, " to ", extIP)
nodeCfg.Node.Endpoint = extIP
if err := PublishNodeUpdate(&nodeCfg); err != nil {
logger.Log(0, "network:", nodeCfg.Node.Network, "could not publish endpoint change")
}
}
intIP, err := getPrivateAddr()
if err != nil {
logger.Log(1, "network:", nodeCfg.Node.Network, "error encountered checking private ip addresses: ", err.Error())
}
if nodeCfg.Node.LocalAddress != intIP && intIP != "" {
logger.Log(1, "network:", nodeCfg.Node.Network, "local Address has changed from ", nodeCfg.Node.LocalAddress, " to ", intIP)
nodeCfg.Node.LocalAddress = intIP
if err := PublishNodeUpdate(&nodeCfg); err != nil {
logger.Log(0, "Network: ", nodeCfg.Node.Network, " could not publish local address change")
}
}
_ = UpdateLocalListenPort(&nodeCfg)
} else if nodeCfg.Node.IsLocal == "yes" && nodeCfg.Node.LocalRange != "" {
localIP, err := ncutils.GetLocalIP(nodeCfg.Node.LocalRange)
if err != nil {
logger.Log(1, "network:", nodeCfg.Node.Network, "error encountered checking local ip addresses: ", err.Error())
}
if nodeCfg.Node.Endpoint != localIP && localIP != "" {
logger.Log(1, "network:", nodeCfg.Node.Network, "endpoint has changed from "+nodeCfg.Node.Endpoint+" to ", localIP)
nodeCfg.Node.Endpoint = localIP
if err := PublishNodeUpdate(&nodeCfg); err != nil {
logger.Log(0, "network:", nodeCfg.Node.Network, "could not publish localip change")
}
}
}
}
//check version
if nodeCfg.Node.Version != ncutils.Version {
nodeCfg.Node.Version = ncutils.Version
config.Write(&nodeCfg, nodeCfg.Network)
}
Hello(&nodeCfg)
if nodeCfg.Server.Is_EE && nodeCfg.Node.Connected == "yes" {
logger.Log(0, "collecting metrics for node", nodeCfg.Node.Name)
publishMetrics(&nodeCfg)
}
}
}
// PublishNodeUpdates -- saves node and pushes changes to broker
func PublishNodeUpdate(nodeCfg *config.ClientConfig) error {
if err := config.Write(nodeCfg, nodeCfg.Network); err != nil {
return err
}
data, err := json.Marshal(nodeCfg.Node)
if err != nil {
return err
}
if err = publish(nodeCfg, fmt.Sprintf("update/%s", nodeCfg.Node.ID), data, 1); err != nil {
return err
}
logger.Log(0, "network:", nodeCfg.Node.Network, "sent a node update to server for node", nodeCfg.Node.Name, ", ", nodeCfg.Node.ID)
return nil
}
// Hello -- ping the broker to let server know node it's alive and well
func Hello(nodeCfg *config.ClientConfig) {
var checkin models.NodeCheckin
checkin.Version = ncutils.Version
checkin.Connected = nodeCfg.Node.Connected
ip, err := getInterfaces()
if err != nil {
logger.Log(0, "failed to retrieve local interfaces", err.Error())
} else {
nodeCfg.Node.Interfaces = *ip
config.Write(nodeCfg, nodeCfg.Network)
}
checkin.Ifaces = nodeCfg.Node.Interfaces
data, err := json.Marshal(checkin)
if err != nil {
logger.Log(0, "unable to marshal checkin data", err.Error())
return
}
if err := publish(nodeCfg, fmt.Sprintf("ping/%s", nodeCfg.Node.ID), data, 0); err != nil {
logger.Log(0, fmt.Sprintf("Network: %s error publishing ping, %v", nodeCfg.Node.Network, err))
logger.Log(0, "running pull on "+nodeCfg.Node.Network+" to reconnect")
_, err := Pull(nodeCfg.Node.Network, true)
if err != nil {
logger.Log(0, "could not run pull on "+nodeCfg.Node.Network+", error: "+err.Error())
}
} else {
logger.Log(3, "checkin for", nodeCfg.Network, "complete")
}
}
// publishMetrics - publishes the metrics of a given nodecfg
func publishMetrics(nodeCfg *config.ClientConfig) {
token, err := Authenticate(nodeCfg)
if err != nil {
logger.Log(1, "failed to authenticate when publishing metrics", err.Error())
return
}
url := fmt.Sprintf("https://%s/api/nodes/%s/%s", nodeCfg.Server.API, nodeCfg.Network, nodeCfg.Node.ID)
response, err := API("", http.MethodGet, url, token)
if err != nil {
logger.Log(1, "failed to read from server during metrics publish", err.Error())
return
}
if response.StatusCode != http.StatusOK {
bytes, err := io.ReadAll(response.Body)
if err != nil {
fmt.Println(err)
}
logger.Log(0, fmt.Sprintf("%s %s", string(bytes), err.Error()))
return
}
defer response.Body.Close()
var nodeGET models.NodeGet
if err := json.NewDecoder(response.Body).Decode(&nodeGET); err != nil {
logger.Log(0, "failed to decode node when running metrics update", err.Error())
return
}
metrics, err := metrics.Collect(nodeCfg.Node.Interface, nodeGET.Node.Network, nodeGET.Node.Proxy, nodeGET.PeerIDs)
if err != nil {
logger.Log(0, "failed metric collection for node", nodeCfg.Node.Name, err.Error())
}
metrics.Network = nodeCfg.Node.Network
metrics.NodeName = nodeCfg.Node.Name
metrics.NodeID = nodeCfg.Node.ID
metrics.IsServer = "no"
data, err := json.Marshal(metrics)
if err != nil {
logger.Log(0, "something went wrong when marshalling metrics data for node", nodeCfg.Node.Name, err.Error())
}
if err = publish(nodeCfg, fmt.Sprintf("metrics/%s", nodeCfg.Node.ID), data, 1); err != nil {
logger.Log(0, "error occurred during publishing of metrics on node", nodeCfg.Node.Name, err.Error())
logger.Log(0, "aggregating metrics locally until broker connection re-established")
val, ok := metricsCache.Load(nodeCfg.Node.ID)
if !ok {
metricsCache.Store(nodeCfg.Node.ID, data)
} else {
var oldMetrics models.Metrics
err = json.Unmarshal(val.([]byte), &oldMetrics)
if err == nil {
for k := range oldMetrics.Connectivity {
currentMetric := metrics.Connectivity[k]
if currentMetric.Latency == 0 {
currentMetric.Latency = oldMetrics.Connectivity[k].Latency
}
currentMetric.Uptime += oldMetrics.Connectivity[k].Uptime
currentMetric.TotalTime += oldMetrics.Connectivity[k].TotalTime
metrics.Connectivity[k] = currentMetric
}
newData, err := json.Marshal(metrics)
if err == nil {
metricsCache.Store(nodeCfg.Node.ID, newData)
}
}
}
} else {
metricsCache.Delete(nodeCfg.Node.ID)
logger.Log(0, "published metrics for node", nodeCfg.Node.Name)
}
}
// node cfg is required in order to fetch the traffic keys of that node for encryption
func publish(nodeCfg *config.ClientConfig, dest string, msg []byte, qos byte) error {
// setup the keys
trafficPrivKey, err := auth.RetrieveTrafficKey(nodeCfg.Node.Network)
if err != nil {
return err
}
serverPubKey, err := ncutils.ConvertBytesToKey(nodeCfg.Node.TrafficKeys.Server)
if err != nil {
return err
}
encrypted, err := ncutils.Chunk(msg, serverPubKey, trafficPrivKey)
if err != nil {
return err
}
if mqclient == nil {
return errors.New("unable to publish ... no mqclient")
}
if token := mqclient.Publish(dest, qos, false, encrypted); !token.WaitTimeout(30*time.Second) || token.Error() != nil {
logger.Log(0, "could not connect to broker at "+nodeCfg.Server.Server+":"+nodeCfg.Server.MQPort)
var err error
if token.Error() == nil {
err = errors.New("connection timeout")
} else {
err = token.Error()
}
if err != nil {
return err
}
}
return nil
}
func checkBroker(broker string, port string) error {
if broker == "" {
return errors.New("error: broker address is blank")
}
if port == "" {
return errors.New("error: broker port is blank")
}
_, err := net.LookupIP(broker)
if err != nil {
return errors.New("nslookup failed for broker ... check dns records")
}
pinger := ping.NewTCPing()
intPort, err := strconv.Atoi(port)
if err != nil {
logger.Log(1, "error converting port to int: "+err.Error())
}
pinger.SetTarget(&ping.Target{
Protocol: ping.TCP,
Host: broker,
Port: intPort,
Counter: 3,
Interval: 1 * time.Second,
Timeout: 2 * time.Second,
})
pingerDone := pinger.Start()
<-pingerDone
if pinger.Result().SuccessCounter == 0 {
return errors.New("unable to connect to broker port ... check netmaker server and firewalls")
}
return nil
}

View File

@@ -1,109 +0,0 @@
package functions
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"runtime"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/local"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/wireguard"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
//homedir "github.com/mitchellh/go-homedir"
)
// Pull - pulls the latest config from the server, if manual it will overwrite
func Pull(network string, iface bool) (*models.LegacyNode, error) {
cfg, err := config.ReadConfig(network)
if err != nil {
return nil, err
}
if cfg.Node.IPForwarding == "yes" && !ncutils.IsWindows() {
if err = local.SetIPForwarding(); err != nil {
return nil, err
}
}
token, err := Authenticate(cfg)
if err != nil {
return nil, err
}
url := "https://" + cfg.Server.API + "/api/nodes/" + cfg.Network + "/" + cfg.Node.ID
response, err := API("", http.MethodGet, url, token)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
bytes, err := io.ReadAll(response.Body)
if err != nil {
fmt.Println(err)
}
return nil, (fmt.Errorf("%s %w", string(bytes), err))
}
defer response.Body.Close()
var nodeGET models.NodeGet
if err := json.NewDecoder(response.Body).Decode(&nodeGET); err != nil {
return nil, fmt.Errorf("error decoding node %w", err)
}
resNode := nodeGET.Node
// ensure that the OS never changes
resNode.OS = runtime.GOOS
if nodeGET.Peers == nil {
nodeGET.Peers = []wgtypes.PeerConfig{}
}
if nodeGET.ServerConfig.API != "" && nodeGET.ServerConfig.MQPort != "" {
if err = config.ModServerConfig(&nodeGET.ServerConfig, resNode.Network); err != nil {
logger.Log(0, "unable to update server config: "+err.Error())
}
}
// if nodeGET.Node.Proxy {
// ProxyMgmChan <- &manager.ManagerAction{
// Action: manager.AddInterface,
// Payload: nodeGET.ProxyUpdate,
// }
// }
if !nodeGET.Node.Proxy {
if nodeGET.Node.ListenPort != cfg.Node.LocalListenPort {
if err := wireguard.RemoveConf(resNode.Interface, false); err != nil {
logger.Log(0, "error remove interface", resNode.Interface, err.Error())
}
err = ncutils.ModPort(&resNode)
if err != nil {
return nil, err
}
informPortChange(&resNode)
}
}
if err = config.ModNodeConfig(&resNode); err != nil {
return nil, err
}
if !nodeGET.Node.Proxy {
if iface {
if err = wireguard.SetWGConfig(network, false, nodeGET.Peers[:]); err != nil {
return nil, err
}
} else {
if err = wireguard.SetWGConfig(network, true, nodeGET.Peers[:]); err != nil {
if errors.Is(err, os.ErrNotExist) && !ncutils.IsFreeBSD() {
return Pull(network, true)
} else {
return nil, err
}
}
}
}
var bkupErr = config.SaveBackup(network)
if bkupErr != nil {
logger.Log(0, "unable to update backup file for", network)
}
return &resNode, err
}

View File

@@ -1,13 +0,0 @@
package upgrades
import "github.com/gravitl/netmaker/netclient/config"
// UpgradeFunction - logic for upgrade
type UpgradeFunction func(*config.ClientConfig)
// UpgradeInfo - struct for holding upgrade info
type UpgradeInfo struct {
RequiredVersions []string
NewVersion string
OP UpgradeFunction
}

View File

@@ -1,25 +0,0 @@
package upgrades
// InitializeUpgrades - initializes written upgrades
func InitializeUpgrades() {
addUpgrades([]UpgradeInfo{
upgrade0145,
upgrade0146,
upgrade0160,
upgrade0161,
upgrade0162,
})
}
// Upgrades - holds all upgrade funcs
var Upgrades = []UpgradeInfo{}
// addUpgrades - Adds upgrades to make to client
func addUpgrades(upgrades []UpgradeInfo) {
Upgrades = append(Upgrades, upgrades...)
}
// ReleaseUpgrades - releases upgrade funcs from memory
func ReleaseUpgrades() {
Upgrades = nil
}

View File

@@ -1,24 +0,0 @@
package upgrades
import (
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/config"
)
var upgrade0145 = UpgradeInfo{
RequiredVersions: []string{
"v0.14.0",
"v0.14.1",
"v0.14.2",
"v0.14.3",
"v0.14.4",
},
NewVersion: "v0.14.5",
OP: update0145,
}
func update0145(cfg *config.ClientConfig) {
// do stuff for 14.X -> 14.5
// No-op
logger.Log(0, "updating schema for 0.14.5")
}

View File

@@ -1,25 +0,0 @@
package upgrades
import (
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/config"
)
var upgrade0146 = UpgradeInfo{
RequiredVersions: []string{
"v0.14.0",
"v0.14.1",
"v0.14.2",
"v0.14.3",
"v0.14.4",
"v0.14.5",
},
NewVersion: "v0.14.6",
OP: update0146,
}
func update0146(cfg *config.ClientConfig) {
// do stuff for 14.X -> 14.5
// No-op
logger.Log(0, "updating schema for 0.14.6")
}

View File

@@ -1,23 +0,0 @@
package upgrades
import (
"github.com/gravitl/netmaker/netclient/config"
)
var upgrade0160 = UpgradeInfo{
RequiredVersions: []string{
"v0.14.6",
"v0.15.0",
"v0.15.1",
"v0.15.2",
},
NewVersion: "v0.16.0",
OP: update0160,
}
func update0160(cfg *config.ClientConfig) {
// set connect default if not present 15.X -> 16.0
if cfg.Node.Connected == "" {
cfg.Node.SetDefaultConnected()
}
}

View File

@@ -1,24 +0,0 @@
package upgrades
import (
"github.com/gravitl/netmaker/netclient/config"
)
var upgrade0161 = UpgradeInfo{
RequiredVersions: []string{
"v0.14.6",
"v0.15.0",
"v0.15.1",
"v0.15.2",
"v0.16.1",
},
NewVersion: "v0.16.1",
OP: update0161,
}
func update0161(cfg *config.ClientConfig) {
// set connect default if not present 15.X -> 16.0
if cfg.Node.Connected == "" {
cfg.Node.SetDefaultConnected()
}
}

View File

@@ -1,22 +0,0 @@
package upgrades
import (
"github.com/gravitl/netmaker/netclient/config"
)
var upgrade0162 = UpgradeInfo{
RequiredVersions: []string{
"v0.14.6",
"v0.15.0",
"v0.15.1",
"v0.15.2",
"v0.16.1",
},
NewVersion: "v0.16.2",
OP: update0162,
}
func update0162(cfg *config.ClientConfig) {
// set connect default if not present 15.X -> 16.0
update0161(cfg)
}

View File

@@ -1,33 +0,0 @@
package components
import (
"image/color"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/widget"
)
// ColoredButton - renders a colored button with text
func ColoredButton(text string, tapped func(), color color.Color) *fyne.Container {
btn := widget.NewButton(text, tapped)
bgColor := canvas.NewRectangle(color)
return container.New(
layout.NewMaxLayout(),
bgColor,
btn,
)
}
// ColoredIconButton - renders a colored button with an icon
func ColoredIconButton(text string, icon fyne.Resource, tapped func(), color color.Color) *fyne.Container {
btn := widget.NewButtonWithIcon(text, icon, tapped)
bgColor := canvas.NewRectangle(color)
return container.New(
layout.NewMaxLayout(),
btn,
bgColor,
)
}

View File

@@ -1,22 +0,0 @@
package components
import "image/color"
var (
// Red_color - preferred red color
Red_color = color.NRGBA{R: 233, G: 10, B: 17, A: 155}
// Gravitl_color - gravitl primary sea green color
Gravitl_color = color.NRGBA{R: 14, G: 173, B: 105, A: 155}
// Blue_color - preferred blue color
Blue_color = color.NRGBA{R: 17, G: 157, B: 164, A: 155}
// Danger_color - preferred danger color
Danger_color = color.NRGBA{R: 223, G: 71, B: 89, A: 155}
// Purple_color - preferred purple color
Purple_color = color.NRGBA{R: 115, G: 80, B: 159, A: 155}
// Orange_color - preferred orange color
Orange_color = color.NRGBA{R: 253, G: 76, B: 65, A: 155}
// Grey_color - preferred grey color
Grey_color = color.NRGBA{R: 27, G: 27, B: 27, A: 155}
// Gold_color - preferred gold color
Gold_color = color.NRGBA{R: 218, G: 165, B: 32, A: 155}
)

View File

@@ -1,23 +0,0 @@
package components
import (
"image/color"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/widget"
)
// ColoredText - renders a colored label
func ColoredText(text string, color color.Color) *fyne.Container {
btn := widget.NewLabel(text)
btn.Wrapping = fyne.TextWrapWord
bgColor := canvas.NewRectangle(color)
return container.New(
layout.NewMaxLayout(),
bgColor,
btn,
)
}

View File

@@ -1,39 +0,0 @@
package components
import (
"image/color"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
// NewToolbarLabelButton - makes a toolbar button cell with label
func NewToolbarLabelButton(label string, icon fyne.Resource, onclick func(), colour color.Color) widget.ToolbarItem {
l := ColoredIconButton(label, icon, onclick, colour)
l.MinSize()
return &toolbarLabelButton{l}
}
// NewToolbarLabel - makes a toolbar text cell
func NewToolbarLabel(label string) widget.ToolbarItem {
l := widget.NewLabel(label)
l.MinSize()
return &toolbarLabel{l}
}
type toolbarLabelButton struct {
*fyne.Container
}
type toolbarLabel struct {
*widget.Label
}
func (t *toolbarLabelButton) ToolbarObject() fyne.CanvasObject {
return container.NewCenter(t.Container)
}
func (t *toolbarLabel) ToolbarObject() fyne.CanvasObject {
return container.NewCenter(t.Label)
}

View File

@@ -1,21 +0,0 @@
package views
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/gravitl/netmaker/netclient/gui/components"
)
// GetConfirmation - displays a confirmation message
func GetConfirmation(msg string, onCancel, onConfirm func()) fyne.CanvasObject {
return container.NewGridWithColumns(1,
container.NewCenter(widget.NewLabel(msg)),
container.NewCenter(
container.NewHBox(
components.ColoredIconButton("Confirm", theme.ConfirmIcon(), onConfirm, components.Gravitl_color),
components.ColoredIconButton("Cancel", theme.CancelIcon(), onCancel, components.Danger_color),
)),
)
}

View File

@@ -1,25 +0,0 @@
package views
import (
"fyne.io/fyne/v2"
)
// CurrentContent - the content currently being displayed
var CurrentContent *fyne.Container
// RemoveContent - removes a rendered content
func RemoveContent(name string) {
CurrentContent.Remove(GetView(name))
}
// AddContent - adds content to be rendered
func AddContent(name string) {
CurrentContent.Add(GetView(name))
}
// RefreshComponent - refreshes the component to re-render
func RefreshComponent(name string, c fyne.CanvasObject) {
RemoveContent(name)
SetView(name, c)
AddContent(name)
}

View File

@@ -1,61 +0,0 @@
package views
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/functions"
"github.com/gravitl/netmaker/netclient/gui/components"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// GetJoinView - get's the join screen where a user inputs an access token
func GetJoinView() fyne.CanvasObject {
input := widget.NewMultiLineEntry()
input.SetPlaceHolder("access token here...")
submitBtn := components.ColoredIconButton("Submit", theme.UploadIcon(), func() {
// ErrorNotify("Could not process token")
LoadingNotify()
var cfg config.ClientConfig
accesstoken, err := config.ParseAccessToken(input.Text)
if err != nil {
ErrorNotify("Failed to parse access token!")
return
}
cfg.Network = accesstoken.ClientConfig.Network
cfg.Node.Network = accesstoken.ClientConfig.Network
cfg.Node.Name = ncutils.GetHostname()
cfg.AccessKey = accesstoken.ClientConfig.Key
cfg.Node.LocalRange = accesstoken.ClientConfig.LocalRange
cfg.Server.API = accesstoken.APIConnString
err = functions.JoinNetwork(&cfg, "")
if err != nil {
ErrorNotify("Failed to join " + cfg.Network + "!")
return
}
networks, err := ncutils.GetSystemNetworks()
if err != nil {
ErrorNotify("Failed to read local networks!")
return
}
SuccessNotify("Joined " + cfg.Network + "!")
input.Text = ""
RefreshComponent(Networks, GetNetworksView(networks))
ShowView(Networks)
// TODO
// - call join
// - display loading
// - on error display error notification
// - on success notify success, refresh networks & networks view, display networks view
}, components.Blue_color)
return container.NewGridWithColumns(1,
container.NewCenter(widget.NewLabel("Join new network with Access Token")),
input,
container.NewCenter(submitBtn),
)
}

View File

@@ -1,236 +0,0 @@
package views
import (
"fmt"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/functions"
"github.com/gravitl/netmaker/netclient/gui/components"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// GetNetworksView - displays the view of all networks
func GetNetworksView(networks []string) fyne.CanvasObject {
// renders := []fyne.CanvasObject{}
if len(networks) == 0 {
return container.NewCenter(widget.NewLabel("No networks present"))
}
grid := container.New(layout.NewGridLayout(5),
container.NewCenter(widget.NewLabel("Network Name")),
container.NewCenter(widget.NewLabel("Node Info")),
container.NewCenter(widget.NewLabel("Pull Latest")),
container.NewCenter(widget.NewLabel("Leave network")),
container.NewCenter(widget.NewLabel("Connection status")),
)
for i := range networks {
network := &networks[i]
grid.Add(
container.NewCenter(widget.NewLabel(*network)),
)
grid.Add(
components.ColoredIconButton("info", theme.InfoIcon(), func() {
RefreshComponent(NetDetails, GetSingleNetworkView(*network))
ShowView(NetDetails)
}, components.Gold_color),
)
grid.Add(
components.ColoredIconButton("pull", theme.DownloadIcon(), func() {
// TODO call pull with network name
pull(*network)
}, components.Blue_color),
)
grid.Add(
components.ColoredIconButton("leave", theme.DeleteIcon(), func() {
leave(*network)
}, components.Danger_color),
)
cfg, err := config.ReadConfig(*network)
if err != nil {
fmt.Println(err)
}
if cfg.Node.Connected == "yes" {
grid.Add(
components.ColoredIconButton("disconnect", theme.CheckButtonCheckedIcon(), func() {
disconnect(*network)
}, components.Gravitl_color),
)
} else {
grid.Add(
components.ColoredIconButton("connect", theme.CheckButtonIcon(), func() {
connect(*network)
}, components.Danger_color),
)
}
// renders = append(renders, container.NewCenter(netToolbar))
}
return container.NewCenter(grid)
}
// GetSingleNetworkView - returns details and option to pull a network
func GetSingleNetworkView(network string) fyne.CanvasObject {
if len(network) == 0 {
return container.NewCenter(widget.NewLabel("No valid network selected"))
}
// == read node values ==
LoadingNotify()
nets, err := functions.List(network)
if err != nil || len(nets) < 1 {
ClearNotification()
return container.NewCenter(widget.NewLabel("No data retrieved."))
}
var nodecfg config.ClientConfig
nodecfg.Network = network
nodecfg.ReadConfig()
nodeID := nodecfg.Node.ID
lastCheckInTime := time.Unix(nodecfg.Node.LastCheckIn, 0)
lastCheckIn := lastCheckInTime.Format("2006-01-02 15:04:05")
privateAddr := nodecfg.Node.Address
privateAddr6 := nodecfg.Node.Address6
endpoint := nodecfg.Node.Endpoint
health := " (HEALTHY)"
if time.Now().After(lastCheckInTime.Add(time.Minute * 30)) {
health = " (ERROR)"
} else if time.Now().After(lastCheckInTime.Add(time.Minute * 5)) {
health = " (WARNING)"
}
lastCheckIn += health
version := nodecfg.Node.Version
pullBtn := components.ColoredButton("pull "+network, func() { pull(network) }, components.Blue_color)
pullBtn.Resize(fyne.NewSize(pullBtn.Size().Width, 50))
view := container.NewGridWithColumns(1, widget.NewRichTextFromMarkdown(fmt.Sprintf(`### %s
- ID: %s
- Last Check In: %s
- Endpoint: %s
- Address (IPv4): %s
- Address6 (IPv6): %s
- Version: %s
### Peers
`, network, nodeID, lastCheckIn, endpoint, privateAddr, privateAddr6, version)),
)
netDetailsView := container.NewCenter(
view,
)
peerView := container.NewVBox()
for _, p := range nets[0].Peers {
peerString := ""
endpointEntry := widget.NewEntry()
endpointEntry.Text = fmt.Sprintf("Endpoint: %s", p.PublicEndpoint)
endpointEntry.Disable()
newEntry := widget.NewEntry()
for i, addr := range p.Addresses {
if i > 0 && i < len(p.Addresses) {
peerString += ", "
}
peerString += addr.IP
}
newEntry.Text = peerString
newEntry.Disable()
peerView.Add(widget.NewLabel(fmt.Sprintf("Peer: %s", p.PublicKey)))
peerView.Add(container.NewVBox(container.NewVBox(endpointEntry), container.NewVBox(newEntry)))
}
peerScroller := container.NewVScroll(peerView)
view.Add(peerScroller)
view.Add(container.NewVBox(pullBtn))
netDetailsView.Refresh()
ClearNotification()
return netDetailsView
}
// == private ==
func pull(network string) {
LoadingNotify()
_, err := functions.Pull(network, true)
if err != nil {
ErrorNotify("Failed to pull " + network + " : " + err.Error())
} else {
SuccessNotify("Pulled " + network + "!")
}
}
func leave(network string) {
confirmView := GetConfirmation("Confirm leaving "+network+"?", func() {
ShowView(Networks)
}, func() {
LoadingNotify()
err := functions.LeaveNetwork(network)
if err != nil {
ErrorNotify("Failed to leave " + network + " : " + err.Error())
} else {
SuccessNotify("Left " + network)
}
networks, err := ncutils.GetSystemNetworks()
if err != nil {
networks = []string{}
ErrorNotify("Failed to read local networks!")
}
RefreshComponent(Networks, GetNetworksView(networks))
ShowView(Networks)
})
RefreshComponent(Confirm, confirmView)
ShowView(Confirm)
}
func connect(network string) {
confirmView := GetConfirmation("Confirm connecting "+network+"?", func() {
ShowView(Networks)
}, func() {
LoadingNotify()
err := functions.Connect(network)
if err != nil {
ErrorNotify("Failed to connect " + network + " : " + err.Error())
} else {
SuccessNotify("connected to " + network)
}
networks, err := ncutils.GetSystemNetworks()
if err != nil {
networks = []string{}
ErrorNotify("Failed to read local networks!")
}
RefreshComponent(Networks, GetNetworksView(networks))
ShowView(Networks)
})
RefreshComponent(Confirm, confirmView)
ShowView(Confirm)
}
func disconnect(network string) {
confirmView := GetConfirmation("Confirm disconnecting "+network+"?", func() {
ShowView(Networks)
}, func() {
LoadingNotify()
fmt.Println(network)
err := functions.Disconnect(network)
if err != nil {
ErrorNotify("Failed to disconnect " + network + " : " + err.Error())
} else {
SuccessNotify("disconnected from " + network)
}
networks, err := ncutils.GetSystemNetworks()
if err != nil {
networks = []string{}
ErrorNotify("Failed to read local networks!")
}
RefreshComponent(Networks, GetNetworksView(networks))
ShowView(Networks)
})
RefreshComponent(Confirm, confirmView)
ShowView(Confirm)
}

View File

@@ -1,38 +0,0 @@
package views
import (
"image/color"
"fyne.io/fyne/v2"
"github.com/gravitl/netmaker/netclient/gui/components"
)
// GenerateNotification - generates a notification
func GenerateNotification(text string, c color.Color) fyne.CanvasObject {
return components.ColoredText(text, c)
}
// ChangeNotification - changes the current notification in the view
func ChangeNotification(text string, c color.Color) {
RefreshComponent(Notify, GenerateNotification(text, c))
}
// ClearNotification - hides the notifications
func ClearNotification() {
RefreshComponent(Notify, GenerateNotification("", color.Transparent))
}
// LoadingNotify - changes notification to loading...
func LoadingNotify() {
RefreshComponent(Notify, GenerateNotification("loading...", components.Blue_color))
}
// ErrorNotify - changes notification to a specified error
func ErrorNotify(msg string) {
RefreshComponent(Notify, GenerateNotification(msg, components.Danger_color))
}
// SuccessNotify - changes notification to a specified success message
func SuccessNotify(msg string) {
RefreshComponent(Notify, GenerateNotification(msg, components.Gravitl_color))
}

View File

@@ -1,44 +0,0 @@
package views
import (
"fyne.io/fyne/v2"
)
var (
// Views - the map of all the view components
views = make(map[string]fyne.CanvasObject)
)
const (
Networks = "networks"
NetDetails = "netdetails"
Notify = "notification"
Join = "join"
Confirm = "confirm"
)
// GetView - returns the requested view and sets the CurrentView state
func GetView(viewName string) fyne.CanvasObject {
return views[viewName]
}
// SetView - sets a view in the views map
func SetView(viewName string, component fyne.CanvasObject) {
views[viewName] = component
}
// HideView - hides a specific view
func HideView(viewName string) {
views[viewName].Hide()
}
// ShowView - show's a specific view
func ShowView(viewName string) {
for k := range views {
if k == Notify {
continue
}
HideView(k)
}
views[viewName].Show()
}

View File

@@ -1,147 +0,0 @@
package gui
import (
"embed"
"image/color"
"os"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/agnivade/levenshtein"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/functions"
"github.com/gravitl/netmaker/netclient/gui/components"
"github.com/gravitl/netmaker/netclient/gui/components/views"
"github.com/gravitl/netmaker/netclient/ncutils"
)
//go:embed nm-logo-sm.png
var logoContent embed.FS
// Run - run's the netclient GUI
func Run(networks []string) error {
defer func() {
if r := recover(); r != nil {
logger.Log(0, "No monitor detected, please use CLI commands; use -help for more info.")
}
}()
a := app.New()
window := a.NewWindow("Netclient - " + ncutils.Version)
img, err := logoContent.ReadFile("nm-logo-sm.png")
if err != nil {
logger.Log(0, "failed to read logo", err.Error())
return err
}
window.SetIcon(&fyne.StaticResource{StaticName: "Netmaker logo", StaticContent: img})
window.Resize(fyne.NewSize(600, 450))
networkView := container.NewVScroll(views.GetNetworksView(networks))
networkView.SetMinSize(fyne.NewSize(400, 300))
views.SetView(views.Networks, networkView)
netDetailsViews := container.NewVScroll(views.GetSingleNetworkView(""))
netDetailsViews.SetMinSize(fyne.NewSize(400, 300))
views.SetView(views.NetDetails, netDetailsViews)
window.SetFixedSize(false)
searchBar := widget.NewEntry()
searchBar.PlaceHolder = "Search a Network ..."
searchBar.TextStyle = fyne.TextStyle{
Italic: true,
}
searchBar.OnChanged = func(text string) {
if text == "" {
networkView = container.NewVScroll(views.GetNetworksView(networks))
networkView.SetMinSize(fyne.NewSize(400, 300))
views.RefreshComponent(views.Networks, networkView)
views.ShowView(views.Networks)
return
}
opts := []string{}
for _, n := range networks {
r := levenshtein.ComputeDistance(text, n)
if r <= 2 {
opts = append(opts, n)
}
}
// fmt.Println(opts)
networkView = container.NewVScroll(views.GetNetworksView(opts))
networkView.SetMinSize(fyne.NewSize(400, 300))
views.RefreshComponent(views.Networks, networkView)
views.ShowView(views.Networks)
opts = nil
}
toolbar := container.NewCenter(widget.NewToolbar(
components.NewToolbarLabelButton("Networks", theme.HomeIcon(), func() {
searchBar.Show()
views.ShowView(views.Networks)
views.ClearNotification()
}, components.Blue_color),
components.NewToolbarLabelButton("Join new", theme.ContentAddIcon(), func() {
searchBar.Hide()
views.ShowView(views.Join)
}, components.Gravitl_color),
components.NewToolbarLabelButton("Uninstall", theme.ErrorIcon(), func() {
searchBar.Hide()
confirmView := views.GetConfirmation("Confirm Netclient uninstall?", func() {
views.ShowView(views.Networks)
}, func() {
views.LoadingNotify()
err := functions.Uninstall()
if err != nil {
views.ErrorNotify("Failed to uninstall: \n" + err.Error())
} else {
views.SuccessNotify("Uninstalled Netclient!")
}
networks, err := ncutils.GetSystemNetworks()
if err != nil {
networks = []string{}
}
views.RefreshComponent(views.Networks, views.GetNetworksView(networks))
views.ShowView(views.Networks)
})
views.RefreshComponent(views.Confirm, confirmView)
views.ShowView(views.Confirm)
}, components.Red_color),
components.NewToolbarLabelButton("Close", theme.ContentClearIcon(), func() {
os.Exit(0)
}, components.Purple_color),
))
joinView := views.GetJoinView()
views.SetView(views.Join, joinView)
confirmView := views.GetConfirmation("", func() {}, func() {})
views.SetView(views.Confirm, confirmView)
views.ShowView(views.Networks)
initialNotification := views.GenerateNotification("", color.Transparent)
views.SetView(views.Notify, initialNotification)
views.CurrentContent = container.NewVBox()
views.CurrentContent.Add(container.NewGridWithRows(
2,
toolbar,
searchBar,
))
views.CurrentContent.Add(views.GetView(views.Networks))
views.CurrentContent.Add(views.GetView(views.NetDetails))
views.CurrentContent.Add(views.GetView(views.Notify))
views.CurrentContent.Add(views.GetView(views.Join))
window.SetContent(views.CurrentContent)
window.ShowAndRun()
return nil
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,142 +0,0 @@
package local
import (
//"github.com/davecgh/go-spew/spew"
"errors"
"log"
"net"
"os"
"os/exec"
"runtime"
"strings"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// SetIPForwarding - Sets IP forwarding if it's mac or linux
func SetIPForwarding() error {
os := runtime.GOOS
var err error
switch os {
case "linux":
err = SetIPForwardingUnix()
case "freebsd":
err = SetIPForwardingFreeBSD()
case "darwin":
err = SetIPForwardingMac()
default:
err = errors.New("this OS is not currently supported")
}
return err
}
// SetIPForwardingLinux - sets the ipforwarding for linux
func SetIPForwardingUnix() error {
// ipv4
out, err := ncutils.RunCmd("sysctl net.ipv4.ip_forward", true)
if err != nil {
log.Println("WARNING: Error encountered setting ip forwarding. This can break functionality.")
return err
} else {
s := strings.Fields(string(out))
if s[2] != "1" {
_, err = ncutils.RunCmd("sysctl -w net.ipv4.ip_forward=1", true)
if err != nil {
log.Println("WARNING: Error encountered setting ip forwarding. You may want to investigate this.")
return err
}
}
}
// ipv6
out, err = ncutils.RunCmd("sysctl net.ipv6.conf.all.forwarding", true)
if err != nil {
log.Println("WARNING: Error encountered setting ipv6 forwarding. This can break functionality.")
return err
} else {
s := strings.Fields(string(out))
if s[2] != "1" {
_, err = ncutils.RunCmd("sysctl -w net.ipv6.conf.all.forwarding=1", true)
if err != nil {
log.Println("WARNING: Error encountered setting ipv6 forwarding. You may want to investigate this.")
return err
}
}
}
return nil
}
// SetIPForwardingLinux - sets the ipforwarding for linux
func SetIPForwardingFreeBSD() error {
out, err := ncutils.RunCmd("sysctl net.inet.ip.forwarding", true)
if err != nil {
log.Println("WARNING: Error encountered setting ip forwarding. This can break functionality.")
return err
} else {
s := strings.Fields(string(out))
if s[1] != "1" {
_, err = ncutils.RunCmd("sysctl -w net.inet.ip.forwarding=1", true)
if err != nil {
log.Println("WARNING: Error encountered setting ip forwarding. You may want to investigate this.")
return err
}
}
}
return nil
}
// SetIPForwardingMac - sets ip forwarding for mac
func SetIPForwardingMac() error {
_, err := ncutils.RunCmd("sysctl -w net.inet.ip.forwarding=1", true)
if err != nil {
log.Println("WARNING: Error encountered setting ip forwarding. This can break functionality.")
}
return err
}
// IsWGInstalled - checks if WireGuard is installed
func IsWGInstalled() bool {
out, err := ncutils.RunCmd("wg help", true)
if err != nil {
_, err = exec.LookPath(os.Getenv("WG_QUICK_USERSPACE_IMPLEMENTATION"))
return err == nil
}
return strings.Contains(out, "Available subcommand")
}
// GetMacIface - gets mac interface
func GetMacIface(ipstring string) (string, error) {
var wgiface string
_, checknet, err := net.ParseCIDR(ipstring + "/24")
if err != nil {
return wgiface, errors.New("could not parse ip " + ipstring)
}
ifaces, err := net.Interfaces()
if err != nil {
return wgiface, err
}
for _, iface := range ifaces {
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
ip := addr.(*net.IPNet).IP
if checknet.Contains(ip) {
wgiface = iface.Name
break
}
}
}
if wgiface == "" {
err = errors.New("could not find iface for address " + ipstring)
}
return wgiface, err
}
// HasNetwork - checks if a network exists locally
func HasNetwork(network string) bool {
return ncutils.FileExists(ncutils.GetNetclientPathSpecific() + "netconfig-" + network)
}

View File

@@ -1,159 +0,0 @@
package local
import (
"fmt"
"net"
"strings"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/ncutils"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// TODO handle ipv6 in future
// SetPeerRoutes - sets/removes ip routes for each peer on a network
func SetPeerRoutes(iface string, oldPeers map[string]bool, newPeers []wgtypes.PeerConfig) {
// get the default route
var hasRoute bool
gwIP, gwIface, err := GetDefaultRoute()
if err != nil {
logger.Log(0, "error getting default route:", err.Error())
}
if gwIP != "" && gwIface != "" && err == nil {
hasRoute = true
}
// traverse through all recieved peers
for _, peer := range newPeers {
for _, allowedIP := range peer.AllowedIPs {
if !oldPeers[allowedIP.String()] {
if err := setRoute(iface, &allowedIP, allowedIP.IP.String()); err != nil {
logger.Log(1, err.Error())
}
} else {
delete(oldPeers, allowedIP.String())
}
}
if peer.Endpoint == nil {
continue
}
if hasRoute && !ncutils.IpIsPrivate(peer.Endpoint.IP) {
ipNet, err := ncutils.GetIPNetFromString(peer.Endpoint.IP.String())
if err != nil {
logger.Log(0, "error parsing ip:", err.Error())
}
SetExplicitRoute(gwIface, &ipNet, gwIP)
}
}
// traverse through all remaining existing peers
for i := range oldPeers {
ip, err := ncutils.GetIPNetFromString(i)
if err != nil {
logger.Log(1, err.Error())
} else {
deleteRoute(iface, &ip, ip.IP.String())
}
}
}
// SetCurrentPeerRoutes - sets all the current peers
func SetCurrentPeerRoutes(iface, currentAddr string, peers []wgtypes.PeerConfig) {
// get the default route
var hasRoute bool
gwIP, gwIface, err := GetDefaultRoute()
if err != nil {
logger.Log(0, "error getting default route:", err.Error())
}
if gwIP != "" && gwIface != "" && err == nil {
hasRoute = true
}
// traverse through all recieved peers
for _, peer := range peers {
for _, allowedIP := range peer.AllowedIPs {
setRoute(iface, &allowedIP, currentAddr)
}
if peer.Endpoint == nil {
continue
}
if hasRoute && !ncutils.IpIsPrivate(peer.Endpoint.IP) {
ipNet, err := ncutils.GetIPNetFromString(peer.Endpoint.IP.String())
if err != nil {
logger.Log(0, "error parsing ip:", err.Error())
}
SetExplicitRoute(gwIface, &ipNet, gwIP)
}
}
}
// FlushPeerRoutes - removes all current peer routes
func FlushPeerRoutes(iface, currentAddr string, peers []wgtypes.Peer) {
// get the default route
var hasRoute bool
gwIP, gwIface, err := GetDefaultRoute()
if err != nil {
logger.Log(0, "error getting default route:", err.Error())
}
if gwIP != "" && gwIface != "" && err == nil {
hasRoute = true
}
for _, peer := range peers {
for _, allowedIP := range peer.AllowedIPs {
deleteRoute(iface, &allowedIP, currentAddr)
}
if peer.Endpoint == nil {
continue
}
if hasRoute && !ncutils.IpIsPrivate(peer.Endpoint.IP) {
ipNet, err := ncutils.GetIPNetFromString(peer.Endpoint.IP.String())
if err != nil {
logger.Log(0, "error parsing ip:", err.Error())
}
deleteRoute(gwIface, &ipNet, gwIP)
}
}
}
// SetCIDRRoute - sets the CIDR route, used on join and restarts
func SetCIDRRoute(iface, currentAddr string, cidr *net.IPNet) {
setCidr(iface, currentAddr, cidr)
}
// RemoveCIDRRoute - removes a static cidr route
func RemoveCIDRRoute(iface, currentAddr string, cidr *net.IPNet) {
removeCidr(iface, cidr, currentAddr)
}
// SetNetmakerDomainRoute - sets explicit route over Gateway for a given DNS name
func SetNetmakerDomainRoute(domainRaw string) error {
parts := strings.Split(domainRaw, ":")
hostname := parts[0]
var address net.IPNet
gwIP, gwIface, err := GetDefaultRoute()
if err != nil {
return fmt.Errorf("error getting default route: %w", err)
}
ips, err := net.LookupIP(hostname)
if err != nil {
return err
}
for _, ip := range ips {
if ipv4 := ip.To4(); ipv4 != nil {
address, err = ncutils.GetIPNetFromString(ipv4.String())
if err == nil {
break
}
}
}
if err != nil || address.IP == nil {
return fmt.Errorf("address not found")
}
return SetExplicitRoute(gwIface, &address, gwIP)
}

View File

@@ -1,86 +0,0 @@
package local
import (
"fmt"
"github.com/c-robinson/iplib"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/ncutils"
"net"
"regexp"
"strings"
)
// GetDefaultRoute - Gets the default route (ip and interface) on a mac machine
func GetDefaultRoute() (string, string, error) {
var ipaddr string
var iface string
var err error
var outLine string
output, err := ncutils.RunCmd("netstat -nr", false)
for _, line := range strings.Split(strings.TrimSuffix(output, "\n"), "\n") {
if strings.Contains(line, "default") {
outLine = line
break
}
}
space := regexp.MustCompile(`\s+`)
outFormatted := space.ReplaceAllString(outLine, " ")
if err != nil {
return ipaddr, iface, err
}
outputSlice := strings.Split(string(outFormatted), " ")
if !strings.Contains(outputSlice[0], "default") {
return ipaddr, iface, fmt.Errorf("could not find default gateway")
}
ipaddr = outputSlice[1]
if err = ncutils.CheckIPAddress(ipaddr); err != nil {
return ipaddr, iface, err
}
iface = outputSlice[3]
return ipaddr, iface, err
}
// route -n add -net 10.0.0.0/8 192.168.0.254
// networksetup -setadditionalroutes Ethernet 192.168.1.0 255.255.255.0 10.0.0.2 persistent
func setRoute(iface string, addr *net.IPNet, address string) error {
var err error
var out string
var inetx = "inet"
if strings.Contains(addr.IP.String(), ":") {
inetx = "inet6"
}
out, err = ncutils.RunCmd("route -n get -"+inetx+" "+addr.IP.String(), false)
if err != nil {
return err
}
if !(strings.Contains(out, iface)) {
_, err = ncutils.RunCmd("route -q -n add -"+inetx+" "+addr.String()+" -interface "+iface, false)
}
return err
}
// SetExplicitRoute - sets route via explicit ip address
func SetExplicitRoute(iface string, destination *net.IPNet, gateway string) error {
return setRoute(iface, destination, gateway)
}
func deleteRoute(iface string, addr *net.IPNet, address string) error {
var err error
_, err = ncutils.RunCmd("route -q -n delete "+addr.String(), false)
return err
}
func setCidr(iface, address string, addr *net.IPNet) {
if iplib.Version(addr.IP) == 4 {
ncutils.RunCmd("route -q -n add -net "+addr.String()+" "+address, false)
} else if iplib.Version(addr.IP) == 6 {
ncutils.RunCmd("route -A inet6 -q -n add -net "+addr.String()+" "+address, false)
} else {
logger.Log(1, "could not parse address: "+addr.String())
}
}
func removeCidr(iface string, addr *net.IPNet, address string) {
ncutils.RunCmd("route -q -n delete "+addr.String()+" -interface "+iface, false)
}

View File

@@ -1,69 +0,0 @@
package local
import (
"fmt"
"net"
"strings"
"github.com/c-robinson/iplib"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// GetDefaultRoute - Gets the default route (ip and interface) on a freebsd machine
func GetDefaultRoute() (string, string, error) {
var ipaddr string
var iface string
var err error
output, err := ncutils.RunCmd("route show default", true)
if err != nil {
return ipaddr, iface, err
}
outFormatted := strings.ReplaceAll(output, "\n", "")
if !strings.Contains(outFormatted, "default") && !strings.Contains(outFormatted, "interface:") {
return ipaddr, iface, fmt.Errorf("could not find default gateway")
}
outputSlice := strings.Split(string(outFormatted), " ")
for i, outString := range outputSlice {
if outString == "gateway:" {
ipaddr = outputSlice[i+1]
}
if outString == "interface:" {
iface = outputSlice[i+1]
}
}
return ipaddr, iface, err
}
func setRoute(iface string, addr *net.IPNet, address string) error {
_, err := ncutils.RunCmd("route add -net "+addr.String()+" -interface "+iface, false)
return err
}
// SetExplicitRoute - sets route via explicit ip address
func SetExplicitRoute(iface string, destination *net.IPNet, gateway string) error {
_, err := ncutils.RunCmd("route add "+destination.String()+" "+gateway, false)
return err
}
func deleteRoute(iface string, addr *net.IPNet, address string) error {
var err error
_, _ = ncutils.RunCmd("route delete -net "+addr.String()+" -interface "+iface, false)
return err
}
func setCidr(iface, address string, addr *net.IPNet) {
if iplib.Version(addr.IP) == 4 {
ncutils.RunCmd("route add -net "+addr.String()+" -interface "+iface, false)
} else if iplib.Version(addr.IP) == 6 {
ncutils.RunCmd("route add -net -inet6 "+addr.String()+" -interface "+iface, false)
} else {
logger.Log(1, "could not parse address: "+addr.String())
}
ncutils.RunCmd("route add -net "+addr.String()+" -interface "+iface, false)
}
func removeCidr(iface string, addr *net.IPNet, address string) {
ncutils.RunCmd("route delete -net "+addr.String()+" -interface "+iface, false)
}

View File

@@ -1,74 +0,0 @@
package local
import (
//"github.com/davecgh/go-spew/spew"
"fmt"
"net"
"strings"
"github.com/c-robinson/iplib"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// GetDefaultRoute - Gets the default route (ip and interface) on a linux machine
func GetDefaultRoute() (string, string, error) {
var ipaddr string
var iface string
var err error
output, err := ncutils.RunCmd("ip route show default", false)
if err != nil {
return ipaddr, iface, err
}
outputSlice := strings.Split(output, " ")
if !strings.Contains(outputSlice[0], "default") {
return ipaddr, iface, fmt.Errorf("could not find default gateway")
}
for i, outString := range outputSlice {
if outString == "via" {
ipaddr = outputSlice[i+1]
}
if outString == "dev" {
iface = outputSlice[i+1]
}
}
return ipaddr, iface, err
}
func setRoute(iface string, addr *net.IPNet, address string) error {
out, err := ncutils.RunCmd(fmt.Sprintf("ip route get %s", addr.IP.String()), false)
if err != nil || !strings.Contains(out, iface) {
_, err = ncutils.RunCmd(fmt.Sprintf("ip route add %s dev %s", addr.String(), iface), false)
}
return err
}
// SetExplicitRoute - sets route via explicit ip address
func SetExplicitRoute(iface string, destination *net.IPNet, gateway string) error {
_, err := ncutils.RunCmd(fmt.Sprintf("ip route add %s via %s dev %s", destination.String(), gateway, iface), false)
return err
}
func deleteRoute(iface string, addr *net.IPNet, address string) error {
var err error
out, _ := ncutils.RunCmd(fmt.Sprintf("ip route get %s", addr.IP.String()), false)
if strings.Contains(out, iface) {
_, err = ncutils.RunCmd(fmt.Sprintf("ip route del %s dev %s", addr.String(), iface), false)
}
return err
}
func setCidr(iface, address string, addr *net.IPNet) {
if iplib.Version(addr.IP) == 4 {
ncutils.RunCmd("ip -4 route add "+addr.String()+" dev "+iface, false)
} else if iplib.Version(addr.IP) == 6 {
ncutils.RunCmd("ip -6 route add "+addr.String()+" dev "+iface, false)
} else {
logger.Log(1, "could not parse address: "+addr.String())
}
}
func removeCidr(iface string, addr *net.IPNet, address string) {
ncutils.RunCmd("ip route delete "+addr.String()+" dev "+iface, false)
}

View File

@@ -1,77 +0,0 @@
package local
import (
"fmt"
"net"
"regexp"
"strings"
"time"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// GetDefaultRoute - Gets the default route (ip and interface) on a windows machine
func GetDefaultRoute() (string, string, error) {
var ipaddr string
var iface string
var err error
var outLine string
output, err := ncutils.RunCmd("netstat -rn", false)
if err != nil {
return ipaddr, iface, err
}
var startLook bool
for _, line := range strings.Split(strings.TrimSuffix(output, "\n"), "\n") {
if strings.Contains(line, "Active Routes:") {
startLook = true
}
if startLook && strings.Contains(line, "0.0.0.0") {
outLine = line
break
}
}
if outLine == "" {
return ipaddr, iface, fmt.Errorf("could not find default gateway")
}
space := regexp.MustCompile(`\s+`)
outputSlice := strings.Split(strings.TrimSpace(space.ReplaceAllString(outLine, " ")), " ")
ipaddr = outputSlice[len(outputSlice)-3]
if err = ncutils.CheckIPAddress(ipaddr); err != nil {
return ipaddr, iface, fmt.Errorf("invalid output for ip address check: " + err.Error())
}
iface = "irrelevant"
return ipaddr, iface, err
}
func setRoute(iface string, addr *net.IPNet, address string) error {
var err error
_, err = ncutils.RunCmd("route ADD "+addr.String()+" "+address, false)
time.Sleep(time.Second >> 2)
ncutils.RunCmd("route CHANGE "+addr.IP.String()+" MASK "+addr.Mask.String()+" "+address, false)
return err
}
// SetExplicitRoute - sets route via explicit ip address
func SetExplicitRoute(iface string, destination *net.IPNet, gateway string) error {
var err error
_, err = ncutils.RunCmd("route ADD "+destination.String()+" "+gateway, false)
time.Sleep(time.Second >> 2)
ncutils.RunCmd("route CHANGE "+destination.IP.String()+" MASK "+destination.Mask.String()+" "+gateway, false)
return err
}
func deleteRoute(iface string, addr *net.IPNet, address string) error {
var err error
_, err = ncutils.RunCmd("route DELETE "+addr.IP.String()+" MASK "+addr.Mask.String()+" "+address, false)
return err
}
func setCidr(iface, address string, addr *net.IPNet) {
ncutils.RunCmd("route ADD "+addr.String()+" "+address, false)
time.Sleep(time.Second >> 2)
ncutils.RunCmd("route CHANGE "+addr.IP.String()+" MASK "+addr.Mask.String()+" "+address, false)
}
func removeCidr(iface string, addr *net.IPNet, address string) {
ncutils.RunCmd("route DELETE "+addr.String(), false)
}

Binary file not shown.

View File

@@ -1,60 +0,0 @@
//go:generate goversioninfo -icon=windowsdata/resource/netclient.ico -manifest=netclient.exe.manifest.xml -64=true -o=netclient.syso
// -build gui
package main
import (
"os"
"runtime/debug"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/cli_options"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/functions"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/ncwindows"
"github.com/urfave/cli/v2"
)
var version = "dev"
func main() {
app := cli.NewApp()
app.Name = "Netclient"
app.Version = version
ncutils.SetVersion(version)
cliFlags := cli_options.GetFlags(ncutils.GetHostname())
app.Commands = cli_options.GetCommands(cliFlags[:])
app.Description = "Used to perform interactions with Netmaker server and set local WireGuard config."
app.Usage = "Netmaker's netclient agent and CLI."
app.UsageText = "netclient [global options] command [command options] [arguments...]. Adjust verbosity of given command with -v, -vv or -vvv (max)."
setGarbageCollection()
functions.SetHTTPClient()
if ncutils.IsWindows() {
ncwindows.InitWindows()
} else {
ncutils.CheckUID()
ncutils.CheckWG()
if ncutils.IsLinux() {
ncutils.CheckFirewall()
}
}
if len(os.Args) <= 1 && config.GuiActive {
config.GuiRun.(func())()
} else {
err := app.Run(os.Args)
if err != nil {
logger.FatalLog(err.Error())
}
}
}
func setGarbageCollection() {
_, gcset := os.LookupEnv("GOGC")
if !gcset {
debug.SetGCPercent(ncutils.DEFAULT_GC_PERCENT)
}
}

View File

@@ -1,22 +0,0 @@
//go:build gui
// +build gui
package main
import (
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/gui"
"github.com/gravitl/netmaker/netclient/ncutils"
)
func init() {
config.GuiActive = true
config.GuiRun = func() {
networks, err := ncutils.GetSystemNetworks()
if err != nil {
networks = []string{}
}
gui.Run(networks)
}
}

View File

@@ -1,46 +0,0 @@
package ncwindows
import (
"log"
"os"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// InitWindows - Initialize windows directory & files and such
func InitWindows() {
_, directoryErr := os.Stat(ncutils.GetNetclientPath()) // Check if data directory exists or not
if os.IsNotExist(directoryErr) { // create a data directory
os.Mkdir(ncutils.GetNetclientPath(), 0755)
}
wdPath, wdErr := os.Getwd() // get the current working directory
if wdErr != nil {
log.Fatal("failed to get current directory..")
}
dataPath := ncutils.GetNetclientPathSpecific() + "netclient.exe"
currentPath := wdPath + "\\netclient.exe"
_, dataNetclientErr := os.Stat(dataPath)
_, currentNetclientErr := os.Stat(currentPath)
if currentPath == dataPath && currentNetclientErr == nil {
logger.Log(0, "netclient.exe is in proper location, "+currentPath)
} else if os.IsNotExist(dataNetclientErr) { // check and see if netclient.exe is in appdata
if currentNetclientErr == nil { // copy it if it exists locally
input, err := os.ReadFile(currentPath)
if err != nil {
log.Println("failed to find netclient.exe")
return
}
if err = os.WriteFile(dataPath, input, 0700); err != nil {
log.Println("failed to copy netclient.exe to", ncutils.GetNetclientPath())
return
}
} else {
log.Fatalf("[netclient] netclient.exe not found in current working directory: %s \nexiting.", wdPath)
}
}
log.Println("Gravitl Netclient on Windows started")
}

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="0.17.0.0"
processorArchitecture="*"
name="netclient.exe"
type="win32"
/>
<description>Windows Netclient</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

View File

@@ -1,8 +0,0 @@
#include <windows.h>
#pragma code_page(65001)
#define STRINGIZE(x) #x
#define EXPAND(x) STRINGIZE(x)
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST netclient.exe.manifest.xml
wintun.dll RCDATA wintun.dll

View File

@@ -1,43 +0,0 @@
{
"FixedFileInfo": {
"FileVersion": {
"Major": 0,
"Minor": 17,
"Patch": 0,
"Build": 0
},
"ProductVersion": {
"Major": 0,
"Minor": 17,
"Patch": 0,
"Build": 0
},
"FileFlagsMask": "3f",
"FileFlags ": "00",
"FileOS": "040004",
"FileType": "01",
"FileSubType": "00"
},
"StringFileInfo": {
"Comments": "",
"CompanyName": "Gravitl",
"FileDescription": "",
"FileVersion": "",
"InternalName": "",
"LegalCopyright": "",
"LegalTrademarks": "",
"OriginalFilename": "",
"PrivateBuild": "",
"ProductName": "Netclient",
"ProductVersion": "v0.17.0.0",
"SpecialBuild": ""
},
"VarFileInfo": {
"Translation": {
"LangID": "0409",
"CharsetID": "04B0"
}
},
"IconPath": "windowsdata/resource/netclient.ico",
"ManifestPath": "netclient.exe.manifest.xml"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 B

View File

@@ -1,589 +0,0 @@
package wireguard
import (
"fmt"
"net"
"runtime"
"strconv"
"strings"
"time"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/local"
"github.com/gravitl/netmaker/netclient/ncutils"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"gopkg.in/ini.v1"
)
const (
section_interface = "Interface"
section_peers = "Peer"
)
// SetPeers - sets peers on a given WireGuard interface
func SetPeers(iface string, node *models.LegacyNode, peers []wgtypes.PeerConfig) error {
var devicePeers []wgtypes.Peer
var keepalive = node.PersistentKeepalive
var oldPeerAllowedIps = make(map[string]bool, len(peers))
var err error
devicePeers, err = GetDevicePeers(iface)
if err != nil {
return err
}
if len(devicePeers) > 1 && len(peers) == 0 {
logger.Log(1, "no peers pulled")
return err
}
for _, peer := range peers {
// make sure peer has AllowedIP's before comparison
hasPeerIP := len(peer.AllowedIPs) > 0
for _, currentPeer := range devicePeers {
// make sure currenPeer has AllowedIP's before comparison
hascurrentPeerIP := len(currentPeer.AllowedIPs) > 0
if hasPeerIP && hascurrentPeerIP &&
currentPeer.AllowedIPs[0].String() == peer.AllowedIPs[0].String() &&
currentPeer.PublicKey.String() != peer.PublicKey.String() {
_, err := ncutils.RunCmd("wg set "+iface+" peer "+currentPeer.PublicKey.String()+" remove", true)
if err != nil {
logger.Log(0, "error removing peer", peer.Endpoint.String())
}
}
}
udpendpoint := peer.Endpoint.String()
var allowedips string
var iparr []string
for _, ipaddr := range peer.AllowedIPs {
if hasPeerIP {
iparr = append(iparr, ipaddr.String())
}
}
if len(iparr) > 0 {
allowedips = strings.Join(iparr, ",")
}
keepAliveString := strconv.Itoa(int(keepalive))
if keepAliveString == "0" {
keepAliveString = "15"
}
if node.IsServer == "yes" || peer.Endpoint == nil {
_, err = ncutils.RunCmd("wg set "+iface+" peer "+peer.PublicKey.String()+
" persistent-keepalive "+keepAliveString+
" allowed-ips "+allowedips, true)
} else {
_, err = ncutils.RunCmd("wg set "+iface+" peer "+peer.PublicKey.String()+
" endpoint "+udpendpoint+
" persistent-keepalive "+keepAliveString+
" allowed-ips "+allowedips, true)
}
if err != nil {
logger.Log(0, "error setting peer", peer.PublicKey.String())
}
}
if len(devicePeers) > 0 {
for _, currentPeer := range devicePeers {
shouldDelete := true
if len(peers) > 0 {
for _, peer := range peers {
if len(peer.AllowedIPs) > 0 && len(currentPeer.AllowedIPs) > 0 &&
peer.AllowedIPs[0].String() == currentPeer.AllowedIPs[0].String() {
shouldDelete = false
}
// re-check this if logic is not working, added in case of allowedips not working
if peer.PublicKey.String() == currentPeer.PublicKey.String() {
shouldDelete = false
}
}
if shouldDelete {
output, err := ncutils.RunCmd("wg set "+iface+" peer "+currentPeer.PublicKey.String()+" remove", true)
if err != nil {
logger.Log(0, output, "error removing peer", currentPeer.PublicKey.String())
}
}
for _, ip := range currentPeer.AllowedIPs {
oldPeerAllowedIps[ip.String()] = true
}
}
}
}
// if routes are wrong, come back to this, but should work...I would think. Or we should get it working.
if len(peers) > 0 {
local.SetPeerRoutes(iface, oldPeerAllowedIps, peers)
}
return nil
}
// Initializes a WireGuard interface
func InitWireguard(node *models.LegacyNode, privkey string, peers []wgtypes.PeerConfig) error {
key, err := wgtypes.ParseKey(privkey)
if err != nil {
return err
}
wgclient, err := wgctrl.New()
if err != nil {
return err
}
defer wgclient.Close()
//nodecfg := modcfg.Node
var ifacename string
if node.Interface != "" {
ifacename = node.Interface
} else {
return fmt.Errorf("no interface to configure")
}
if node.PrimaryAddress() == "" {
return fmt.Errorf("no address to configure")
}
if err := WriteWgConfig(node, key.String(), peers); err != nil {
logger.Log(1, "error writing wg conf file: ", err.Error())
return err
}
// spin up userspace / windows interface + apply the conf file
confPath := ncutils.GetNetclientPathSpecific() + ifacename + ".conf"
var deviceiface = ifacename
var mErr error
if ncutils.IsMac() { // if node is Mac (Darwin) get the tunnel name first
deviceiface, mErr = local.GetMacIface(node.PrimaryAddress())
if mErr != nil || deviceiface == "" {
deviceiface = ifacename
}
}
// ensure you clear any existing interface first
RemoveConfGraceful(deviceiface)
ApplyConf(node, ifacename, confPath) // Apply initially
logger.Log(1, "waiting for interface...") // ensure interface is created
output, _ := ncutils.RunCmd("wg", false)
starttime := time.Now()
ifaceReady := strings.Contains(output, deviceiface)
for !ifaceReady && !(time.Now().After(starttime.Add(time.Second << 4))) {
if ncutils.IsMac() { // if node is Mac (Darwin) get the tunnel name first
deviceiface, mErr = local.GetMacIface(node.PrimaryAddress())
if mErr != nil || deviceiface == "" {
deviceiface = ifacename
}
}
output, _ = ncutils.RunCmd("wg", false)
err = ApplyConf(node, node.Interface, confPath)
time.Sleep(time.Second)
ifaceReady = strings.Contains(output, deviceiface)
}
//wgclient does not work well on freebsd
if node.OS == "freebsd" {
if !ifaceReady {
return fmt.Errorf("could not reliably create interface, please check wg installation and retry")
}
} else {
_, devErr := wgclient.Device(deviceiface)
if !ifaceReady || devErr != nil {
fmt.Printf("%v\n", devErr)
return fmt.Errorf("could not reliably create interface, please check wg installation and retry")
}
}
logger.Log(1, "interface ready - netclient.. ENGAGE")
if !ncutils.HasWgQuick() && ncutils.IsLinux() {
err = SetPeers(ifacename, node, peers)
if err != nil {
logger.Log(1, "error setting peers: ", err.Error())
}
time.Sleep(time.Second)
}
//ipv4
if node.Address != "" {
_, cidr, cidrErr := net.ParseCIDR(node.NetworkSettings.AddressRange)
if cidrErr == nil {
local.SetCIDRRoute(ifacename, node.Address, cidr)
} else {
logger.Log(1, "could not set cidr route properly: ", cidrErr.Error())
}
local.SetCurrentPeerRoutes(ifacename, node.Address, peers)
}
if node.Address6 != "" {
//ipv6
_, cidr, cidrErr := net.ParseCIDR(node.NetworkSettings.AddressRange6)
if cidrErr == nil {
local.SetCIDRRoute(ifacename, node.Address6, cidr)
} else {
logger.Log(1, "could not set cidr route properly: ", cidrErr.Error())
}
local.SetCurrentPeerRoutes(ifacename, node.Address6, peers)
}
return err
}
// SetWGConfig - sets the WireGuard Config of a given network and checks if it needs a peer update
func SetWGConfig(network string, peerupdate bool, peers []wgtypes.PeerConfig) error {
cfg, err := config.ReadConfig(network)
if err != nil {
return err
}
privkey, err := RetrievePrivKey(network)
if err != nil {
return err
}
if peerupdate && !ncutils.IsFreeBSD() && !(ncutils.IsLinux() && !ncutils.IsKernel()) {
var iface string
iface = cfg.Node.Interface
if ncutils.IsMac() {
iface, err = local.GetMacIface(cfg.Node.PrimaryAddress())
if err != nil {
return err
}
}
err = SetPeers(iface, &cfg.Node, peers)
} else {
err = InitWireguard(&cfg.Node, privkey, peers)
}
return err
}
// RemoveConf - removes a configuration for a given WireGuard interface
func RemoveConf(iface string, printlog bool) error {
os := runtime.GOOS
if ncutils.IsLinux() && !ncutils.HasWgQuick() {
os = "nowgquick"
}
var err error
switch os {
case "nowgquick":
err = RemoveWithoutWGQuick(iface)
case "windows":
err = RemoveWindowsConf(iface, printlog)
default:
confPath := ncutils.GetNetclientPathSpecific() + iface + ".conf"
err = RemoveWGQuickConf(confPath, printlog)
}
return err
}
// ApplyConf - applys a conf on disk to WireGuard interface
func ApplyConf(node *models.LegacyNode, ifacename string, confPath string) error {
os := runtime.GOOS
if ncutils.IsLinux() && !ncutils.HasWgQuick() {
os = "nowgquick"
}
var isConnected = node.Connected != "no"
var err error
switch os {
case "windows":
ApplyWindowsConf(confPath, ifacename, isConnected)
case "nowgquick":
ApplyWithoutWGQuick(node, ifacename, confPath, isConnected)
default:
ApplyWGQuickConf(confPath, ifacename, isConnected)
}
var nodeCfg config.ClientConfig
nodeCfg.Network = node.Network
if !(node.IsServer == "yes") {
nodeCfg.ReadConfig()
if nodeCfg.NetworkSettings.AddressRange != "" {
ip, cidr, err := net.ParseCIDR(nodeCfg.NetworkSettings.AddressRange)
if err == nil {
local.SetCIDRRoute(node.Interface, ip.String(), cidr)
}
}
if nodeCfg.NetworkSettings.AddressRange6 != "" {
ip, cidr, err := net.ParseCIDR(nodeCfg.NetworkSettings.AddressRange6)
if err == nil {
local.SetCIDRRoute(node.Interface, ip.String(), cidr)
}
}
}
return err
}
// WriteWgConfig - creates a wireguard config file
func WriteWgConfig(node *models.LegacyNode, privateKey string, peers []wgtypes.PeerConfig) error {
options := ini.LoadOptions{
AllowNonUniqueSections: true,
AllowShadows: true,
}
wireguard := ini.Empty(options)
wireguard.Section(section_interface).Key("PrivateKey").SetValue(privateKey)
if node.ListenPort > 0 && node.UDPHolePunch != "yes" {
wireguard.Section(section_interface).Key("ListenPort").SetValue(strconv.Itoa(int(node.ListenPort)))
}
addrString := node.Address
if node.Address6 != "" {
if addrString != "" {
addrString += ","
}
addrString += node.Address6
}
wireguard.Section(section_interface).Key("Address").SetValue(addrString)
// need to figure out DNS
//if node.DNSOn == "yes" {
// wireguard.Section(section_interface).Key("DNS").SetValue(cfg.Server.CoreDNSAddr)
//}
//need to split postup/postdown because ini lib adds a ` and the ` breaks freebsd
//works fine on others
if node.PostUp != "" {
if node.OS == "freebsd" {
parts := strings.Split(node.PostUp, " ; ")
for i, part := range parts {
if i == 0 {
wireguard.Section(section_interface).Key("PostUp").SetValue(part)
}
wireguard.Section(section_interface).Key("PostUp").AddShadow(part)
}
} else {
wireguard.Section(section_interface).Key("PostUp").SetValue((node.PostUp))
}
}
if node.PostDown != "" {
if node.OS == "freebsd" {
parts := strings.Split(node.PostDown, " ; ")
for i, part := range parts {
if i == 0 {
wireguard.Section(section_interface).Key("PostDown").SetValue(part)
}
wireguard.Section(section_interface).Key("PostDown").AddShadow(part)
}
} else {
wireguard.Section(section_interface).Key("PostDown").SetValue((node.PostDown))
}
}
if node.MTU != 0 {
wireguard.Section(section_interface).Key("MTU").SetValue(strconv.FormatInt(int64(node.MTU), 10))
}
for i, peer := range peers {
wireguard.SectionWithIndex(section_peers, i).Key("PublicKey").SetValue(peer.PublicKey.String())
if peer.PresharedKey != nil {
wireguard.SectionWithIndex(section_peers, i).Key("PreSharedKey").SetValue(peer.PresharedKey.String())
}
if peer.AllowedIPs != nil {
var allowedIPs string
for i, ip := range peer.AllowedIPs {
if i == 0 {
allowedIPs = ip.String()
} else {
allowedIPs = allowedIPs + ", " + ip.String()
}
}
wireguard.SectionWithIndex(section_peers, i).Key("AllowedIps").SetValue(allowedIPs)
}
if peer.Endpoint != nil {
wireguard.SectionWithIndex(section_peers, i).Key("Endpoint").SetValue(peer.Endpoint.String())
}
if peer.PersistentKeepaliveInterval != nil && peer.PersistentKeepaliveInterval.Seconds() > 0 {
wireguard.SectionWithIndex(section_peers, i).Key("PersistentKeepalive").SetValue(strconv.FormatInt((int64)(peer.PersistentKeepaliveInterval.Seconds()), 10))
}
}
if err := wireguard.SaveTo(ncutils.GetNetclientPathSpecific() + node.Interface + ".conf"); err != nil {
return err
}
return nil
}
// UpdateWgPeers - updates the peers of a network
func UpdateWgPeers(file string, peers []wgtypes.PeerConfig) (*net.UDPAddr, error) {
var internetGateway *net.UDPAddr
options := ini.LoadOptions{
AllowNonUniqueSections: true,
AllowShadows: true,
}
wireguard, err := ini.LoadSources(options, file)
if err != nil {
return internetGateway, err
}
//delete the peers sections as they are going to be replaced
wireguard.DeleteSection(section_peers)
for i, peer := range peers {
wireguard.SectionWithIndex(section_peers, i).Key("PublicKey").SetValue(peer.PublicKey.String())
if peer.PresharedKey != nil {
wireguard.SectionWithIndex(section_peers, i).Key("PreSharedKey").SetValue(peer.PresharedKey.String())
}
if peer.AllowedIPs != nil {
var allowedIPs string
for i, ip := range peer.AllowedIPs {
if i == 0 {
allowedIPs = ip.String()
} else {
allowedIPs = allowedIPs + ", " + ip.String()
}
}
wireguard.SectionWithIndex(section_peers, i).Key("AllowedIps").SetValue(allowedIPs)
if strings.Contains(allowedIPs, "0.0.0.0/0") || strings.Contains(allowedIPs, "::/0") {
internetGateway = peer.Endpoint
}
}
if peer.Endpoint != nil {
wireguard.SectionWithIndex(section_peers, i).Key("Endpoint").SetValue(peer.Endpoint.String())
}
if peer.PersistentKeepaliveInterval != nil && peer.PersistentKeepaliveInterval.Seconds() > 0 {
wireguard.SectionWithIndex(section_peers, i).Key("PersistentKeepalive").SetValue(strconv.FormatInt((int64)(peer.PersistentKeepaliveInterval.Seconds()), 10))
}
}
if err := wireguard.SaveTo(file); err != nil {
return internetGateway, err
}
return internetGateway, nil
}
// UpdateWgInterface - updates the interface section of a wireguard config file
func UpdateWgInterface(file, privateKey, nameserver string, node models.LegacyNode) error {
options := ini.LoadOptions{
AllowNonUniqueSections: true,
AllowShadows: true,
}
wireguard, err := ini.LoadSources(options, file)
if err != nil {
return err
}
if node.UDPHolePunch == "yes" {
node.ListenPort = 0
}
wireguard.DeleteSection(section_interface)
wireguard.Section(section_interface).Key("PrivateKey").SetValue(privateKey)
wireguard.Section(section_interface).Key("ListenPort").SetValue(strconv.Itoa(int(node.ListenPort)))
addrString := node.Address
if node.Address6 != "" {
if addrString != "" {
addrString += ","
}
addrString += node.Address6
}
wireguard.Section(section_interface).Key("Address").SetValue(addrString)
//if node.DNSOn == "yes" {
// wireguard.Section(section_interface).Key("DNS").SetValue(nameserver)
//}
//need to split postup/postdown because ini lib adds a quotes which breaks freebsd
if node.PostUp != "" {
if node.OS == "freebsd" {
parts := strings.Split(node.PostUp, " ; ")
for i, part := range parts {
if i == 0 {
wireguard.Section(section_interface).Key("PostUp").SetValue(part)
}
wireguard.Section(section_interface).Key("PostUp").AddShadow(part)
}
} else {
wireguard.Section(section_interface).Key("PostUp").SetValue(node.PostUp)
}
}
if node.PostDown != "" {
if node.OS == "freebsd" {
parts := strings.Split(node.PostDown, " ; ")
for i, part := range parts {
if i == 0 {
wireguard.Section(section_interface).Key("PostDown").SetValue(part)
}
wireguard.Section(section_interface).Key("PostDown").AddShadow(part)
}
} else {
wireguard.Section(section_interface).Key("PostDown").SetValue(node.PostDown)
}
}
if node.MTU != 0 {
wireguard.Section(section_interface).Key("MTU").SetValue(strconv.FormatInt(int64(node.MTU), 10))
}
if err := wireguard.SaveTo(file); err != nil {
return err
}
return nil
}
// UpdatePrivateKey - updates the private key of a wireguard config file
func UpdatePrivateKey(file, privateKey string) error {
options := ini.LoadOptions{
AllowNonUniqueSections: true,
AllowShadows: true,
}
wireguard, err := ini.LoadSources(options, file)
if err != nil {
return err
}
wireguard.Section(section_interface).Key("PrivateKey").SetValue(privateKey)
if err := wireguard.SaveTo(file); err != nil {
return err
}
return nil
}
// UpdateKeepAlive - updates the persistentkeepalive of all peers
func UpdateKeepAlive(file string, keepalive int32) error {
options := ini.LoadOptions{
AllowNonUniqueSections: true,
AllowShadows: true,
}
wireguard, err := ini.LoadSources(options, file)
if err != nil {
return err
}
peers, err := wireguard.SectionsByName(section_peers)
if err != nil {
return err
}
newvalue := strconv.Itoa(int(keepalive))
for i := range peers {
wireguard.SectionWithIndex(section_peers, i).Key("PersistentKeepALive").SetValue(newvalue)
}
if err := wireguard.SaveTo(file); err != nil {
return err
}
return nil
}
// RemoveConfGraceful - Run remove conf and wait for it to actually be gone before proceeding
func RemoveConfGraceful(ifacename string) {
// ensure you clear any existing interface first
wgclient, err := wgctrl.New()
if err != nil {
logger.Log(0, "could not create wgclient")
return
}
defer wgclient.Close()
d, _ := wgclient.Device(ifacename)
startTime := time.Now()
for d != nil && d.Name == ifacename {
if err = RemoveConf(ifacename, false); err != nil { // remove interface first
if strings.Contains(err.Error(), "does not exist") {
err = nil
break
}
}
time.Sleep(time.Second >> 2)
d, _ = wgclient.Device(ifacename)
if time.Now().After(startTime.Add(time.Second << 4)) {
break
}
}
time.Sleep(time.Second << 1)
}
// GetDevicePeers - gets the current device's peers
func GetDevicePeers(iface string) ([]wgtypes.Peer, error) {
if ncutils.IsFreeBSD() {
if devicePeers, err := ncutils.GetPeers(iface); err != nil {
return nil, err
} else {
return devicePeers, nil
}
} else {
client, err := wgctrl.New()
if err != nil {
logger.Log(0, "failed to start wgctrl")
return nil, err
}
defer client.Close()
device, err := client.Device(iface)
if err != nil {
logger.Log(0, "failed to parse interface", iface)
return nil, err
}
return device.Peers, nil
}
}

View File

@@ -1,27 +0,0 @@
package wireguard
import (
"errors"
"fmt"
"strings"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// GetRealIface - retrieves tun iface based on reference iface name from config file
func GetRealIface(iface string) (string, error) {
ncutils.RunCmd("wg show interfaces", false)
ifacePath := "/var/run/wireguard/" + iface + ".name"
if !(ncutils.FileExists(ifacePath)) {
return "", errors.New(ifacePath + " does not exist")
}
realIfaceName, err := ncutils.GetFileAsString(ifacePath)
if err != nil {
return "", err
}
realIfaceName = strings.TrimSpace(realIfaceName)
if !(ncutils.FileExists(fmt.Sprintf("/var/run/wireguard/%s.sock", realIfaceName))) {
return "", errors.New("interface file does not exist")
}
return realIfaceName, nil
}

View File

@@ -1,168 +0,0 @@
package wireguard
import (
"errors"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/ncutils"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
const disconnect_error = "node disconnected"
// ApplyWithoutWGQuick - Function for running the equivalent of "wg-quick up" for linux if wg-quick is missing
func ApplyWithoutWGQuick(node *models.LegacyNode, ifacename, confPath string, isConnected bool) error {
ipExec, err := exec.LookPath("ip")
if err != nil {
return err
}
wgclient, err := wgctrl.New()
if err != nil {
return err
}
defer wgclient.Close()
privkey, err := RetrievePrivKey(node.Network)
if err != nil {
return err
}
key, err := wgtypes.ParseKey(privkey)
if err != nil {
return err
}
conf := wgtypes.Config{}
nodeport := int(node.ListenPort)
if node.UDPHolePunch == "yes" &&
node.IsServer == "no" &&
node.IsIngressGateway != "yes" {
conf = wgtypes.Config{
PrivateKey: &key,
}
} else {
conf = wgtypes.Config{
PrivateKey: &key,
ListenPort: &nodeport,
}
}
var address4 string
var address6 string
var mask4 string
var mask6 string
if node.Address != "" {
netmaskArr := strings.Split(node.NetworkSettings.AddressRange, "/")
var netmask = "32"
if len(netmaskArr) == 2 {
netmask = netmaskArr[1]
}
mask4 = netmask
address4 = node.Address
}
if node.Address6 != "" {
netmaskArr := strings.Split(node.NetworkSettings.AddressRange6, "/")
var netmask = "128"
if len(netmaskArr) == 2 {
netmask = netmaskArr[1]
}
mask6 = netmask
address6 = node.Address6
}
err = setKernelDevice(ifacename, address4, mask4, address6, mask6, isConnected)
if err != nil {
if err.Error() == disconnect_error {
return nil
}
}
_, err = wgclient.Device(ifacename)
if err != nil {
if !os.IsNotExist(err) {
return errors.New("Unknown config error: " + err.Error())
}
}
err = wgclient.ConfigureDevice(ifacename, conf)
if err != nil {
if os.IsNotExist(err) {
logger.Log(0, "Could not configure device: ", err.Error())
}
}
if _, err := ncutils.RunCmd(ipExec+" link set down dev "+ifacename, false); err != nil {
logger.Log(1, "attempted to remove interface before editing")
return err
}
if node.PostDown != "" {
ncutils.RunCmd(node.PostDown, false)
}
// set MTU of node interface
if _, err := ncutils.RunCmd(ipExec+" link set mtu "+strconv.Itoa(int(node.MTU))+" up dev "+ifacename, true); err != nil {
logger.Log(1, "failed to create interface with mtu ", strconv.Itoa(int(node.MTU)), "-", ifacename)
return err
}
if node.PostUp != "" {
ncutils.RunCmd(node.PostUp, false)
}
if node.Address6 != "" {
logger.Log(1, "adding address: ", node.Address6)
netmaskArr := strings.Split(node.NetworkSettings.AddressRange6, "/")
var netmask = "64"
if len(netmaskArr) == 2 {
netmask = netmaskArr[1]
}
ncutils.RunCmd(ipExec+" address add dev "+ifacename+" "+node.Address6+"/"+netmask, true)
}
return nil
}
// RemoveWithoutWGQuick - Function for running the equivalent of "wg-quick down" for linux if wg-quick is missing
func RemoveWithoutWGQuick(ifacename string) error {
ipExec, err := exec.LookPath("ip")
if err != nil {
return err
}
out, err := ncutils.RunCmd(ipExec+" link del "+ifacename, false)
dontprint := strings.Contains(out, "does not exist") || strings.Contains(out, "Cannot find device")
if err != nil && !dontprint {
logger.Log(1, "error running command: ", ipExec, " link del ", ifacename)
logger.Log(1, out)
}
network := strings.ReplaceAll(ifacename, "nm-", "")
nodeconf, err := config.ReadConfig(network)
if nodeconf != nil && err == nil {
if nodeconf.Node.PostDown != "" {
ncutils.RunCmd(nodeconf.Node.PostDown, false)
}
} else if err != nil {
logger.Log(1, "error retrieving config: ", err.Error())
}
return err
}
func setKernelDevice(ifacename, address4, mask4, address6, mask6 string, isConnected bool) error {
ipExec, err := exec.LookPath("ip")
if err != nil {
return err
}
// == best effort ==
ncutils.RunCmd("ip link delete dev "+ifacename, false)
if !isConnected {
return fmt.Errorf(disconnect_error)
}
ncutils.RunCmd(ipExec+" link add dev "+ifacename+" type wireguard", true)
if address4 != "" {
ncutils.RunCmd(ipExec+" address add dev "+ifacename+" "+address4+"/"+mask4, true)
}
if address6 != "" {
ncutils.RunCmd(ipExec+" address add dev "+ifacename+" "+address6+"/"+mask6, true)
}
return nil
}

View File

@@ -1,51 +0,0 @@
package wireguard
import (
"fmt"
"os"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// ApplyWGQuickConf - applies wg-quick commands if os supports
func ApplyWGQuickConf(confPath, ifacename string, isConnected bool) error {
if ncutils.IsWindows() {
return ApplyWindowsConf(confPath, ifacename, isConnected)
} else {
_, err := os.Stat(confPath)
if err != nil {
logger.Log(0, confPath+" does not exist "+err.Error())
return err
}
if ncutils.IfaceExists(ifacename) {
ncutils.RunCmd("wg-quick down "+confPath, true)
}
if !isConnected {
return nil
}
_, err = ncutils.RunCmd("wg-quick up "+confPath, true)
return err
}
}
// RemoveWGQuickConf - calls wg-quick down
func RemoveWGQuickConf(confPath string, printlog bool) error {
_, err := ncutils.RunCmd(fmt.Sprintf("wg-quick down %s", confPath), printlog)
return err
}
// StorePrivKey - stores wg priv key on disk locally
func StorePrivKey(key string, network string) error {
var err error
d1 := []byte(key)
err = os.WriteFile(ncutils.GetNetclientPathSpecific()+"wgkey-"+network, d1, 0600)
return err
}
// RetrievePrivKey - reads wg priv key from local disk
func RetrievePrivKey(network string) (string, error) {
dat, err := ncutils.GetFileWithRetry(ncutils.GetNetclientPathSpecific()+"wgkey-"+network, 2)
return string(dat), err
}

View File

@@ -1,30 +0,0 @@
package wireguard
import (
"fmt"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// ApplyWindowsConf - applies the WireGuard configuration file on Windows
func ApplyWindowsConf(confPath, iface string, isConnected bool) error {
RemoveConfGraceful(iface) // have to remove gracefully before applying windows conf
if !isConnected {
return nil
}
var commandLine = fmt.Sprintf(`wireguard.exe /installtunnelservice "%s"`, confPath)
if _, err := ncutils.RunCmdFormatted(commandLine, false); err != nil {
return err
}
return nil
}
// RemoveWindowsConf - removes the WireGuard configuration file on Windows and dpapi file
func RemoveWindowsConf(ifacename string, printlog bool) error {
if _, err := ncutils.RunCmd("wireguard.exe /uninstalltunnelservice "+ifacename, printlog); err != nil {
logger.Log(1, err.Error())
}
return nil
}

View File

@@ -1,8 +1,6 @@
package serverctl package serverctl
import ( import (
"net"
"os"
"strings" "strings"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
@@ -11,8 +9,6 @@ import (
"github.com/gravitl/netmaker/logic/acls" "github.com/gravitl/netmaker/logic/acls"
"github.com/gravitl/netmaker/logic/acls/nodeacls" "github.com/gravitl/netmaker/logic/acls/nodeacls"
"github.com/gravitl/netmaker/logic/pro" "github.com/gravitl/netmaker/logic/pro"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/servercfg"
) )
const ( const (
@@ -20,110 +16,6 @@ const (
NETMAKER_BINARY_NAME = "netmaker" NETMAKER_BINARY_NAME = "netmaker"
) )
// InitServerNetclient - intializes the server netclient
// 1. Check if config directory exists, if not attempt to make
// 2. Check current networks and run pull to get interface up to date in case of restart
func InitServerNetclient() error {
netclientDir := ncutils.GetNetclientPath()
_, err := os.Stat(netclientDir + "/config")
if os.IsNotExist(err) {
os.MkdirAll(netclientDir+"/config", 0700)
} else if err != nil {
logger.Log(1, "could not find or create", netclientDir)
return err
}
var networks, netsErr = logic.GetNetworks()
if netsErr == nil || database.IsEmptyRecord(netsErr) {
for _, network := range networks {
var currentServerNode, nodeErr = logic.GetNetworkServerLocal(network.NetID)
if nodeErr == nil {
if currentServerNode.Version != servercfg.Version {
currentServerNode.Version = servercfg.Version
logic.UpdateNode(&currentServerNode, &currentServerNode)
}
if err = logic.ServerPull(&currentServerNode, true); err != nil {
logger.Log(1, "failed pull for network", network.NetID, ", on server node", currentServerNode.ID)
}
}
if err = logic.InitializeNetUsers(&network); err != nil {
logger.Log(0, "something went wrong syncing usrs on network", network.NetID, "-", err.Error())
}
}
}
return nil
}
func SyncServerNetworkWithProxy() error {
networks, err := logic.GetNetworks()
if err != nil {
logger.Log(1, "error retrieving networks for keepalive", err.Error())
}
for _, network := range networks {
serverNetworkSettings, err := logic.GetNetwork(network.NetID)
if err != nil {
continue
}
localnets, err := net.Interfaces()
if err != nil {
return err
}
ifaceExists := false
for _, localnet := range localnets {
if serverNetworkSettings.DefaultInterface == localnet.Name {
ifaceExists = true
}
}
if ifaceExists {
serverNode, err := logic.GetNetworkServerLocal(network.NetID)
if err != nil {
logger.Log(1, "failed to retrieve local server node: ", serverNode.ID)
continue
}
proxyPayload, err := logic.GetPeersForProxy(&serverNode, false)
if err != nil && !ncutils.IsEmptyRecord(err) {
logger.Log(1, "failed to retrieve peers for server node: ", serverNode.ID)
continue
}
logic.ProxyMgmChan <- &proxyPayload
}
}
return nil
}
// SyncServerNetwork - ensures a wg interface and node exists for server
func SyncServerNetwork(network string) error {
serverNetworkSettings, err := logic.GetNetwork(network)
if err != nil {
return err
}
localnets, err := net.Interfaces()
if err != nil {
return err
}
ifaceExists := false
for _, localnet := range localnets {
if serverNetworkSettings.DefaultInterface == localnet.Name {
ifaceExists = true
}
}
serverNode, err := logic.GetNetworkServerLocal(network)
if !ifaceExists && (err == nil && serverNode.ID != "") {
return logic.ServerUpdate(&serverNode, true)
} else if !ifaceExists {
_, err := logic.ServerJoin(&serverNetworkSettings)
if err != nil {
logger.Log(0, "network add failed for "+serverNetworkSettings.NetID)
}
}
return nil
}
func SetDefaults() error { func SetDefaults() error {
if err := setNodeDefaults(); err != nil { if err := setNodeDefaults(); err != nil {
return err return err
@@ -150,10 +42,10 @@ func setNodeDefaults() error {
for i := range nodes { for i := range nodes {
logic.SetNodeDefaults(&nodes[i]) logic.SetNodeDefaults(&nodes[i])
logic.UpdateNode(&nodes[i], &nodes[i]) logic.UpdateNode(&nodes[i], &nodes[i])
currentNodeACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(nodes[i].Network), nodeacls.NodeID(nodes[i].ID)) currentNodeACL, err := nodeacls.FetchNodeACL(nodeacls.NetworkID(nodes[i].Network), nodeacls.NodeID(nodes[i].ID.String()))
if (err != nil && (database.IsEmptyRecord(err) || strings.Contains(err.Error(), "no node ACL present"))) || currentNodeACL == nil { if (err != nil && (database.IsEmptyRecord(err) || strings.Contains(err.Error(), "no node ACL present"))) || currentNodeACL == nil {
if _, err = nodeacls.CreateNodeACL(nodeacls.NetworkID(nodes[i].Network), nodeacls.NodeID(nodes[i].ID), acls.Allowed); err != nil { if _, err = nodeacls.CreateNodeACL(nodeacls.NetworkID(nodes[i].Network), nodeacls.NodeID(nodes[i].ID.String()), acls.Allowed); err != nil {
logger.Log(1, "could not create a default ACL for node", nodes[i].ID) logger.Log(1, "could not create a default ACL for node", nodes[i].ID.String())
} }
} }
} }