Merge pull request #620 from gravitl/feature_v0.10.0_better_server

Feature v0.10.0 better server
This commit is contained in:
dcarns 2022-01-18 15:33:22 -05:00 committed by GitHub
commit de8c4d782d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 406 additions and 347 deletions

View file

@ -12,7 +12,6 @@ import (
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
"github.com/gravitl/netmaker/serverctl"
)
const ALL_NETWORK_ACCESS = "THIS_USER_HAS_ALL"
@ -226,9 +225,8 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
}
if servercfg.IsClientMode() != "off" {
var success bool
success, err = serverctl.AddNetwork(&network)
if err != nil || !success {
err = logic.ServerJoin(&network)
if err != nil {
logic.DeleteNetwork(network.NetID)
if err == nil {
err = errors.New("Failed to add server to network " + network.DisplayName)

View file

@ -32,7 +32,6 @@ func nodeHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes/{network}", createNode).Methods("POST")
r.HandleFunc("/api/nodes/adm/{network}/lastmodified", authorize(true, "network", http.HandlerFunc(getLastModified))).Methods("GET")
r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST")
}
func authenticate(response http.ResponseWriter, request *http.Request) {
@ -186,7 +185,7 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
r.Header.Set("ismasterkey", "yes")
}
if !isadmin && params["network"] != "" {
if functions.SliceContains(networks, params["network"]) {
if logic.StringSliceContains(networks, params["network"]) {
isnetadmin = true
}
}
@ -404,6 +403,11 @@ func createNode(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
if err = runServerPeerUpdate(node.Network, true); err != nil {
logger.Log(1, "internal error when creating node:", node.ID)
}
logger.Log(1, r.Header.Get("user"), "created new node", node.Name, "on network", node.Network)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node)
@ -414,11 +418,15 @@ func createNode(w http.ResponseWriter, r *http.Request) {
func uncordonNode(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
node, err := logic.UncordonNode(params["nodeid"])
var nodeid = params["nodeid"]
node, err := logic.UncordonNode(nodeid)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
if err = runServerPeerUpdate(node.Network, false); err != nil {
logger.Log(1, "internal error when approving node:", nodeid)
}
logger.Log(1, r.Header.Get("user"), "uncordoned node", node.Name)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode("SUCCESS")
@ -440,6 +448,9 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
if err = runServerPeerUpdate(gateway.NetID, true); err != nil {
logger.Log(1, "internal error when setting peers after creating egress on node:", gateway.NodeID)
}
logger.Log(1, r.Header.Get("user"), "created egress gateway on node", gateway.NodeID, "on network", gateway.NetID)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node)
@ -455,6 +466,9 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
if err = runServerPeerUpdate(netid, true); err != nil {
logger.Log(1, "internal error when setting peers after removing egress on node:", nodeid)
}
logger.Log(1, r.Header.Get("user"), "deleted egress gateway", nodeid, "on network", netid)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node)
@ -472,6 +486,7 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeid, "on network", netid)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node)
@ -486,6 +501,7 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node)
@ -530,11 +546,14 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
newNode.PostUp = node.PostUp
}
var shouldPeersUpdate = logic.ShouldPeersUpdate(&node, &newNode)
err = logic.UpdateNode(&node, &newNode)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
if relayupdate {
logic.UpdateRelay(node.Network, node.RelayAddrs, newNode.RelayAddrs)
if err = logic.NetworkNodesUpdatePullChanges(node.Network); err != nil {
@ -542,9 +561,11 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
}
}
if servercfg.IsDNSMode() {
if servercfg.IsDNSMode() { // TODO check when this should be updated..
err = logic.SetDNS()
}
err = runServerPeerUpdate(node.Network, shouldPeersUpdate)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
@ -572,6 +593,12 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
return
}
err = runServerPeerUpdate(node.Network, true)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
returnSuccessResponse(w, r, nodeid+" deleted.")
}

View file

@ -86,6 +86,12 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
return nil, err
}
err = runServerPeerUpdate(node.Network, true)
if err != nil {
logger.Log(1, "internal error when setting peers after node,", node.ID, "was created (gRPC)")
}
logger.Log(0, "new node,", node.Name, ", added on network,"+node.Network)
return response, nil
}
@ -106,6 +112,7 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.Object)
newnode.PostDown = node.PostDown
newnode.PostUp = node.PostUp
}
var shouldPeersUpdate = logic.ShouldPeersUpdate(&node, &newnode)
err = logic.UpdateNode(&node, &newnode)
if err != nil {
@ -119,6 +126,10 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.Object)
if errN != nil {
return nil, err
}
err = runServerPeerUpdate(newnode.Network, shouldPeersUpdate)
if err != nil {
logger.Log(1, "could not update peers on gRPC after node,", newnode.ID, "updated (gRPC), \nerror:", err.Error())
}
return &nodepb.Object{
Data: string(nodeData),
Type: nodepb.NODE_TYPE,
@ -138,6 +149,11 @@ func (s *NodeServiceServer) DeleteNode(ctx context.Context, req *nodepb.Object)
return nil, err
}
err = runServerPeerUpdate(node.Network, true)
if err != nil {
logger.Log(1, "internal error when setting peers after deleting node:", node.ID, "over gRPC")
}
return &nodepb.Object{
Data: "success",
Type: nodepb.STRING_TYPE,
@ -152,9 +168,6 @@ func (s *NodeServiceServer) GetPeers(ctx context.Context, req *nodepb.Object) (*
return nil, err
}
if node.IsServer == "yes" && logic.IsLeader(&node) {
logic.SetNetworkServerPeers(&node)
}
excludeIsRelayed := node.IsRelay != "yes"
var relayedNode string
if node.IsRelayed == "yes" {

View file

@ -26,6 +26,9 @@ func createRelay(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
if err = runServerPeerUpdate(relay.NetID, true); err != nil {
logger.Log(1, "internal error when creating relay on node:", relay.NodeID)
}
logger.Log(1, r.Header.Get("user"), "created relay on node", relay.NodeID, "on network", relay.NetID)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node)
@ -41,6 +44,9 @@ func deleteRelay(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
if err = runServerPeerUpdate(netid, true); err != nil {
logger.Log(1, "internal error when deleting relay on node:", nodeid)
}
logger.Log(1, r.Header.Get("user"), "deleted egress gateway", nodeid, "on network", netid)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node)

View file

@ -9,7 +9,6 @@ import (
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
"github.com/gravitl/netmaker/serverctl"
)
func serverHandlers(r *mux.Router) {
@ -70,9 +69,8 @@ func removeNetwork(w http.ResponseWriter, r *http.Request) {
// get params
var params = mux.Vars(r)
success, err := serverctl.RemoveNetwork(params["network"])
if err != nil || !success {
err := logic.DeleteNetwork(params["network"])
if err != nil {
json.NewEncoder(w).Encode("Could not remove server from network " + params["network"])
return
}

View file

@ -0,0 +1,23 @@
package controller
import (
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
)
func runServerPeerUpdate(network string, shouldPeerUpdate bool) error {
var currentServerNodeID, err = logic.GetNetworkServerNodeID(network)
if err != nil {
return err
}
var currentServerNode, currErr = logic.GetNodeByID(currentServerNodeID)
if currErr != nil {
return currErr
}
if err = logic.ServerUpdate(&currentServerNode, shouldPeerUpdate); err != nil {
logger.Log(1, "server node:", currentServerNode.ID, "failed update")
return err
}
return nil
}

View file

@ -31,20 +31,6 @@ func ParseIntClient(value string) (models.IntClient, error) {
return intClient, err
}
//Takes in an arbitrary field and value for field and checks to see if any other
//node has that value for the same field within the network
// SliceContains - sees if a slice contains something
func SliceContains(slice []string, item string) bool {
set := make(map[string]struct{}, len(slice))
for _, s := range slice {
set[s] = struct{}{}
}
_, ok := set[item]
return ok
}
// GetPeersList - gets peers for given network
func GetPeersList(networkName string) ([]models.PeersResponse, error) {

View file

@ -9,30 +9,22 @@ import (
"time"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
"github.com/gravitl/netmaker/validation"
"golang.org/x/crypto/bcrypt"
)
// GetNetworkNodes - gets the nodes of a network
func GetNetworkNodes(network string) ([]models.Node, error) {
var nodes = []models.Node{}
collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
var nodes, err = GetAllNodes()
if err != nil {
if database.IsEmptyRecord(err) {
return []models.Node{}, nil
}
return nodes, err
return []models.Node{}, err
}
for _, value := range collection {
var node models.Node
err := json.Unmarshal([]byte(value), &node)
if err != nil {
continue
}
for _, node := range nodes {
if node.Network == network {
nodes = append(nodes, node)
}
@ -86,7 +78,7 @@ func UncordonNode(nodeid string) (models.Node, error) {
// GetPeers - gets the peers of a given node
func GetPeers(node *models.Node) ([]models.Node, error) {
if IsLeader(node) {
SetNetworkServerPeers(node)
setNetworkServerPeers(node)
}
excludeIsRelayed := node.IsRelay != "yes"
var relayedNode string
@ -166,6 +158,128 @@ func ValidateNode(node *models.Node, isUpdate bool) error {
return err
}
// CreateNode - creates a node in database
func CreateNode(node *models.Node) error {
//encrypt that password so we never see it
hash, err := bcrypt.GenerateFromPassword([]byte(node.Password), 5)
if err != nil {
return err
}
//set password to encrypted password
node.Password = string(hash)
if node.Name == models.NODE_SERVER_NAME {
node.IsServer = "yes"
}
if node.DNSOn == "" {
if servercfg.IsDNSMode() {
node.DNSOn = "yes"
} else {
node.DNSOn = "no"
}
}
SetNodeDefaults(node)
node.Address, err = UniqueAddress(node.Network)
if err != nil {
return err
}
node.Address6, err = UniqueAddress6(node.Network)
if err != nil {
return err
}
// TODO: This covers legacy nodes, eventually want to remove legacy check
if node.IsServer == "yes" {
node.ID = uuid.NewString()
} else if node.IsServer != "yes" || (node.ID == "" || strings.Contains(node.ID, "###")) {
node.ID = uuid.NewString()
}
//Create a JWT for the node
tokenString, _ := CreateJWT(node.ID, node.MacAddress, node.Network)
if tokenString == "" {
//returnErrorResponse(w, r, errorResponse)
return err
}
err = ValidateNode(node, false)
if err != nil {
return err
}
nodebytes, err := json.Marshal(&node)
if err != nil {
return err
}
err = database.Insert(node.ID, string(nodebytes), database.NODES_TABLE_NAME)
if err != nil {
return err
}
if node.IsPending != "yes" {
DecrimentKey(node.Network, node.AccessKey)
}
SetNetworkNodesLastModified(node.Network)
if servercfg.IsDNSMode() {
err = SetDNS()
}
return err
}
// ShouldPeersUpdate - takes old node and sees if certain fields changing would trigger a peer update
func ShouldPeersUpdate(currentNode *models.Node, newNode *models.Node) bool {
SetNodeDefaults(newNode)
// single comparison statements
if newNode.Endpoint != currentNode.Endpoint ||
newNode.LocalAddress != currentNode.LocalAddress ||
newNode.PublicKey != currentNode.PublicKey ||
newNode.Address != currentNode.Address ||
newNode.IsEgressGateway != currentNode.IsEgressGateway ||
newNode.IsIngressGateway != currentNode.IsIngressGateway ||
newNode.IsRelay != currentNode.IsRelay ||
newNode.UDPHolePunch != currentNode.UDPHolePunch ||
newNode.IsPending != currentNode.IsPending ||
len(newNode.ExcludedAddrs) != len(currentNode.ExcludedAddrs) ||
len(newNode.AllowedIPs) != len(currentNode.AllowedIPs) {
return true
}
// multi-comparison statements
if newNode.IsDualStack == "yes" {
if newNode.Address6 != currentNode.Address6 {
return true
}
}
if newNode.IsEgressGateway == "yes" {
if len(currentNode.EgressGatewayRanges) != len(newNode.EgressGatewayRanges) {
return true
}
for _, address := range newNode.EgressGatewayRanges {
if !StringSliceContains(currentNode.EgressGatewayRanges, address) {
return true
}
}
}
if newNode.IsRelay == "yes" {
if len(currentNode.RelayAddrs) != len(newNode.RelayAddrs) {
return true
}
for _, address := range newNode.RelayAddrs {
if !StringSliceContains(currentNode.RelayAddrs, address) {
return true
}
}
}
for _, address := range newNode.AllowedIPs {
if !StringSliceContains(currentNode.AllowedIPs, address) {
return true
}
}
return false
}
// GetAllNodes - returns all nodes in the DB
func GetAllNodes() ([]models.Node, error) {
var nodes []models.Node

View file

@ -17,6 +17,7 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// == Public ==
// == Join, Checkin, and Leave for Server ==
// KUBERNETES_LISTEN_PORT - starting port for Kubernetes in order to use NodePort range
@ -26,7 +27,7 @@ const KUBERNETES_LISTEN_PORT = 31821
const KUBERNETES_SERVER_MTU = 1024
// ServerJoin - responsible for joining a server to a network
func ServerJoin(networkSettings *models.Network, serverID string) error {
func ServerJoin(networkSettings *models.Network) error {
if networkSettings == nil || networkSettings.NetID == "" {
return errors.New("no network provided")
@ -119,7 +120,7 @@ func ServerJoin(networkSettings *models.Network, serverID string) error {
if err = StorePrivKey(node.ID, privateKey); err != nil {
return err
}
if err = ServerPush(node); err != nil {
if err = serverPush(node); err != nil {
return err
}
@ -137,18 +138,12 @@ func ServerJoin(networkSettings *models.Network, serverID string) error {
return nil
}
// ServerCheckin - runs pulls and pushes for server
func ServerCheckin(serverID string, mac string, network string) error {
var serverNode = &models.Node{}
var currentNode, err = GetNodeByIDorMacAddress(serverID, mac, network)
if err != nil {
return err
}
serverNode = &currentNode
err = ServerPull(serverNode, false)
// ServerUpdate - updates the server
// replaces legacy Checkin code
func ServerUpdate(serverNode *models.Node, shouldPeerUpdate bool) error {
var err = serverPull(serverNode, shouldPeerUpdate)
if isDeleteError(err) {
return ServerLeave(currentNode.ID)
return DeleteNodeByID(serverNode, true)
} else if err != nil {
return err
}
@ -158,66 +153,7 @@ func ServerCheckin(serverID string, mac string, network string) error {
return errors.New("node has been removed")
}
return ServerPush(serverNode)
}
// ServerPull - pulls current config/peers for server
func ServerPull(serverNode *models.Node, onErr bool) error {
var err error
if serverNode.IPForwarding == "yes" {
if err = setIPForwardingLinux(); err != nil {
return err
}
}
serverNode.OS = runtime.GOOS
if serverNode.PullChanges == "yes" || onErr {
// 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)
}
serverNode.PullChanges = "no"
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
}
// ServerPush - pushes config changes for server checkins/join
func ServerPush(serverNode *models.Node) error {
serverNode.OS = runtime.GOOS
serverNode.SetLastCheckIn()
return UpdateNode(serverNode, serverNode)
}
// ServerLeave - removes a server node
func ServerLeave(serverID string) error {
var serverNode, err = GetNodeByID(serverID)
if err != nil {
return err
}
return DeleteNodeByID(&serverNode, true)
return serverPush(serverNode)
}
/**
@ -231,17 +167,14 @@ func GetServerPeers(serverNode *models.Node) ([]wgtypes.PeerConfig, bool, []stri
var gateways []string
var peers []wgtypes.PeerConfig
var nodes []models.Node // fill above fields from server or client
var err error
var nodecfg, err = GetNodeByIDorMacAddress(serverNode.ID, serverNode.MacAddress, serverNode.Network)
if err != nil {
return nil, hasGateway, gateways, err
}
nodes, err = GetPeers(&nodecfg)
nodes, err = GetPeers(serverNode)
if err != nil {
return nil, hasGateway, gateways, err
}
keepalive := nodecfg.PersistentKeepalive
keepalive := serverNode.PersistentKeepalive
keepalivedur, err := time.ParseDuration(strconv.FormatInt(int64(keepalive), 10) + "s")
if err != nil {
logger.Log(1, "Issue with format of keepalive duration value, Please view server config:", err.Error())
@ -255,11 +188,11 @@ func GetServerPeers(serverNode *models.Node) ([]wgtypes.PeerConfig, bool, []stri
return peers, hasGateway, gateways, err
}
if nodecfg.PublicKey == node.PublicKey {
if serverNode.PublicKey == node.PublicKey {
continue
}
if nodecfg.Endpoint == node.Endpoint {
if nodecfg.LocalAddress != node.LocalAddress && node.LocalAddress != "" {
if serverNode.Endpoint == node.Endpoint {
if serverNode.LocalAddress != node.LocalAddress && node.LocalAddress != "" {
node.Endpoint = node.LocalAddress
} else {
continue
@ -304,8 +237,8 @@ func GetServerPeers(serverNode *models.Node) ([]wgtypes.PeerConfig, bool, []stri
logger.Log(2, "egress IP range of", iprange, "overlaps with", node.Endpoint, ", omitting")
continue // skip adding egress range if overlaps with node's ip
}
if ipnet.Contains(net.ParseIP(nodecfg.LocalAddress)) { // ensuring egress gateway range does not contain public ip of node
logger.Log(2, "egress IP range of", iprange, "overlaps with", nodecfg.LocalAddress, ", omitting")
if ipnet.Contains(net.ParseIP(serverNode.LocalAddress)) { // ensuring egress gateway range does not contain public ip of node
logger.Log(2, "egress IP range of", iprange, "overlaps with", serverNode.LocalAddress, ", omitting")
continue // skip adding egress range if overlaps with node's local ip
}
gateways = append(gateways, iprange)
@ -422,7 +355,7 @@ func checkNodeActions(node *models.Node) string {
}
}
if node.Action == models.NODE_DELETE {
err := ServerLeave(node.ID)
err := DeleteNodeByID(node, true)
if err != nil {
logger.Log(1, "error deleting locally:", err.Error())
}
@ -431,6 +364,49 @@ func checkNodeActions(node *models.Node) string {
return ""
}
// == Private ==
func serverPull(serverNode *models.Node, onErr bool) error {
var err error
if serverNode.IPForwarding == "yes" {
if err = setIPForwardingLinux(); err != nil {
return err
}
}
serverNode.OS = runtime.GOOS
if serverNode.PullChanges == "yes" || onErr {
// 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)
}
serverNode.PullChanges = "no"
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
@ -452,3 +428,9 @@ func getServerLocalIP(networkSettings *models.Network) (string, error) {
}
return "", errors.New("could not find a local ip for server")
}
func serverPush(serverNode *models.Node) error {
serverNode.OS = runtime.GOOS
serverNode.SetLastCheckIn()
return UpdateNode(serverNode, serverNode)
}

View file

@ -5,17 +5,16 @@ import (
"encoding/base64"
"encoding/json"
"math/rand"
"os"
"strconv"
"strings"
"time"
"github.com/google/uuid"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/crypto/bcrypt"
)
// IsBase64 - checks if a string is in base64 format
@ -31,15 +30,13 @@ func CheckEndpoint(endpoint string) bool {
return len(endpointarr) == 2
}
// SetNetworkServerPeers - sets the network server peers of a given node
func SetNetworkServerPeers(node *models.Node) {
if currentPeersList, err := GetSystemPeers(node); err == nil {
if database.SetPeers(currentPeersList, node.Network) {
logger.Log(1, "set new peers on network", node.Network)
}
} else {
logger.Log(1, "could not set peers on network", node.Network, ":", err.Error())
// FileExists - checks if local file exists
func FileExists(f string) bool {
info, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
// DeleteNodeByMacAddress - deletes a node from database or moves into delete nodes table
@ -75,72 +72,6 @@ func DeleteNodeByMacAddress(node *models.Node, exterminate bool) error {
return removeLocalServer(node)
}
// CreateNode - creates a node in database
func CreateNode(node *models.Node) error {
//encrypt that password so we never see it
hash, err := bcrypt.GenerateFromPassword([]byte(node.Password), 5)
if err != nil {
return err
}
//set password to encrypted password
node.Password = string(hash)
if node.Name == models.NODE_SERVER_NAME {
node.IsServer = "yes"
}
if node.DNSOn == "" {
if servercfg.IsDNSMode() {
node.DNSOn = "yes"
} else {
node.DNSOn = "no"
}
}
SetNodeDefaults(node)
node.Address, err = UniqueAddress(node.Network)
if err != nil {
return err
}
node.Address6, err = UniqueAddress6(node.Network)
if err != nil {
return err
}
// TODO: This covers legacy nodes, eventually want to remove legacy check
if node.IsServer == "yes" {
node.ID = uuid.NewString()
} else if node.IsServer != "yes" || (node.ID == "" || strings.Contains(node.ID, "###")) {
node.ID = uuid.NewString()
}
//Create a JWT for the node
tokenString, _ := CreateJWT(node.ID, node.MacAddress, node.Network)
if tokenString == "" {
//returnErrorResponse(w, r, errorResponse)
return err
}
err = ValidateNode(node, false)
if err != nil {
return err
}
nodebytes, err := json.Marshal(&node)
if err != nil {
return err
}
err = database.Insert(node.ID, string(nodebytes), database.NODES_TABLE_NAME)
if err != nil {
return err
}
if node.IsPending != "yes" {
DecrimentKey(node.Network, node.AccessKey)
}
SetNetworkNodesLastModified(node.Network)
if servercfg.IsDNSMode() {
err = SetDNS()
}
return err
}
// SetNetworkNodesLastModified - sets the network nodes last modified
func SetNetworkNodesLastModified(networkName string) error {
@ -402,3 +333,16 @@ func StringSliceContains(slice []string, item string) bool {
}
return false
}
// == private ==
// sets the network server peers of a given node
func setNetworkServerPeers(serverNode *models.Node) {
if currentPeersList, err := getSystemPeers(serverNode); err == nil {
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())
}
}

View file

@ -16,8 +16,18 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// GetSystemPeers - gets the server peers
func GetSystemPeers(node *models.Node) (map[string]string, error) {
// 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
}
// == Private Functions ==
// gets the server peers locally
func getSystemPeers(node *models.Node) (map[string]string, error) {
peers := make(map[string]string)
client, err := wgctrl.New()
@ -37,16 +47,6 @@ func GetSystemPeers(node *models.Node) (map[string]string, error) {
return peers, nil
}
// 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
}
// == Private Functions ==
func initWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig, hasGateway bool, gateways []string) error {
key, err := wgtypes.ParseKey(privkey)
@ -93,16 +93,6 @@ func initWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
logger.Log(1, "error writing wg conf file to", confPath, ":", err.Error())
return err
}
if ncutils.IsWindows() {
wgConfPath := ncutils.GetWGPathSpecific() + ifacename + ".conf"
logger.Log(1, "writing wg conf file to:", confPath)
err = os.WriteFile(wgConfPath, []byte(newConf), 0644)
if err != nil {
logger.Log(1, "error writing wg conf file to", wgConfPath, ":", err.Error())
return err
}
confPath = wgConfPath
}
// spin up userspace + apply the conf file
var deviceiface = ifacename
d, _ := wgclient.Device(deviceiface)
@ -182,9 +172,10 @@ func setKernelDevice(ifacename string, address string) error {
return err
}
_, _ = ncutils.RunCmd("ip link delete dev "+ifacename, false)
_, _ = ncutils.RunCmd(ipExec+" link add dev "+ifacename+" type wireguard", true)
_, _ = ncutils.RunCmd(ipExec+" address add dev "+ifacename+" "+address+"/24", true) // this is a bug waiting to happen
// == best effort ==
ncutils.RunCmd("ip link delete dev "+ifacename, false)
ncutils.RunCmd(ipExec+" link add dev "+ifacename+" type wireguard", true)
ncutils.RunCmd(ipExec+" address add dev "+ifacename+" "+address+"/24", true) // this is a bug waiting to happen
return nil
}

17
main.go
View file

@ -9,7 +9,6 @@ import (
"runtime/debug"
"strconv"
"sync"
"time"
"github.com/gravitl/netmaker/auth"
controller "github.com/gravitl/netmaker/controllers"
@ -110,25 +109,9 @@ func startControllers() {
logger.Log(0, "No Server Mode selected, so nothing is being served! Set either Agent mode (AGENT_BACKEND) or Rest mode (REST_BACKEND) to 'true'.")
}
if servercfg.IsClientMode() == "on" {
var checkintime = time.Duration(servercfg.GetServerCheckinInterval()) * time.Second
for { // best effort currently
var serverGroup sync.WaitGroup
serverGroup.Add(1)
go runClient(&serverGroup)
serverGroup.Wait()
time.Sleep(checkintime)
}
}
waitnetwork.Wait()
}
func runClient(wg *sync.WaitGroup) {
defer wg.Done()
go serverctl.HandleContainedClient()
}
func runGRPC(wg *sync.WaitGroup) {
defer wg.Done()

View file

@ -73,6 +73,7 @@ type Node struct {
OS string `json:"os" bson:"os" yaml:"os"`
MTU int32 `json:"mtu" bson:"mtu" yaml:"mtu"`
Version string `json:"version" bson:"version" yaml:"version"`
ExcludedAddrs []string `json:"excludedaddrs" bson:"excludedaddrs" yaml:"excludedaddrs"`
}
// NodesArray - used for node sorting
@ -107,78 +108,91 @@ func (node *Node) SetDefaulIsPending() {
}
}
// Node.SetDefaultIsRelayed - set default is relayed
func (node *Node) SetDefaultIsRelayed() {
if node.IsRelayed == "" {
node.IsRelayed = "no"
}
}
// Node.SetDefaultIsRelay - set default isrelay
func (node *Node) SetDefaultIsRelay() {
if node.IsRelay == "" {
node.IsRelay = "no"
}
}
// Node.SetDefaultEgressGateway - sets default egress gateway status
func (node *Node) SetDefaultEgressGateway() {
if node.IsEgressGateway == "" {
node.IsEgressGateway = "no"
}
}
// Node.SetDefaultIngressGateway - sets default ingress gateway status
func (node *Node) SetDefaultIngressGateway() {
if node.IsIngressGateway == "" {
node.IsIngressGateway = "no"
}
}
// Node.SetDefaultAction - sets default action status
func (node *Node) SetDefaultAction() {
if node.Action == "" {
node.Action = NODE_NOOP
}
}
// Node.SetRoamingDefault - sets default roaming status
func (node *Node) SetRoamingDefault() {
if node.Roaming == "" {
node.Roaming = "yes"
}
}
// Node.SetPullChangesDefault - sets default pull changes status
func (node *Node) SetPullChangesDefault() {
if node.PullChanges == "" {
node.PullChanges = "no"
}
}
// Node.SetIPForwardingDefault - set ip forwarding default
func (node *Node) SetIPForwardingDefault() {
if node.IPForwarding == "" {
node.IPForwarding = "yes"
}
}
// Node.SetIsLocalDefault - set is local default
func (node *Node) SetIsLocalDefault() {
if node.IsLocal == "" {
node.IsLocal = "no"
}
}
// Node.SetDNSOnDefault - sets dns on default
func (node *Node) SetDNSOnDefault() {
if node.DNSOn == "" {
node.DNSOn = "yes"
}
}
// Node.SetIsDualStackDefault - set is dual stack default status
func (node *Node) SetIsDualStackDefault() {
if node.IsDualStack == "" {
node.IsDualStack = "no"
}
}
// Node.SetIsServerDefault - sets node isserver default
func (node *Node) SetIsServerDefault() {
if node.IsServer != "yes" {
node.IsServer = "no"
}
}
// Node.SetIsStaticDefault - set is static default
func (node *Node) SetIsStaticDefault() {
if node.IsServer == "yes" {
node.IsStatic = "yes"
@ -187,28 +201,41 @@ func (node *Node) SetIsStaticDefault() {
}
}
// Node.SetLastModified - set last modified initial time
func (node *Node) SetLastModified() {
node.LastModified = time.Now().Unix()
}
// Node.SetLastCheckIn - time.Now().Unix()
func (node *Node) SetLastCheckIn() {
node.LastCheckIn = time.Now().Unix()
}
// Node.SetLastPeerUpdate - sets last peer update time
func (node *Node) SetLastPeerUpdate() {
node.LastPeerUpdate = time.Now().Unix()
}
// Node.SetExpirationDateTime - sets node expiry time
func (node *Node) SetExpirationDateTime() {
node.ExpirationDateTime = time.Now().Unix() + TEN_YEARS_IN_SECONDS
}
// Node.SetDefaultName - sets a random name to node
func (node *Node) SetDefaultName() {
if node.Name == "" {
node.Name = GenerateNodeName()
}
}
// Node.SetDefaultExcludedAddrs - sets ExcludedAddrs to empty array if nil
func (node *Node) SetDefaultExcludedAddrs() {
if node.ExcludedAddrs == nil {
node.ExcludedAddrs = make([]string, 0)
}
}
// Node.Fill - fills other node data into calling node data if not set on calling node
func (newNode *Node) Fill(currentNode *Node) {
newNode.ID = currentNode.ID
@ -354,8 +381,15 @@ func (newNode *Node) Fill(currentNode *Node) {
if newNode.IsRelayed == "" {
newNode.IsRelayed = currentNode.IsRelayed
}
if newNode.Version == "" {
newNode.Version = currentNode.Version
}
if newNode.ExcludedAddrs == nil || len(newNode.ExcludedAddrs) != len(currentNode.ExcludedAddrs) {
newNode.ExcludedAddrs = currentNode.ExcludedAddrs
}
}
// StringWithCharset - returns random string inside defined charset
func StringWithCharset(length int, charset string) string {
b := make([]byte, length)
for i := range b {
@ -364,13 +398,14 @@ func StringWithCharset(length int, charset string) string {
return string(b)
}
//Check for valid IPv4 address
//Note: We dont handle IPv6 AT ALL!!!!! This definitely is needed at some point
//But for iteration 1, lets just stick to IPv4. Keep it simple stupid.
// IsIpv4Net - check for valid IPv4 address
// Note: We dont handle IPv6 AT ALL!!!!! This definitely is needed at some point
// But for iteration 1, lets just stick to IPv4. Keep it simple stupid.
func IsIpv4Net(host string) bool {
return net.ParseIP(host) != nil
}
// Node.NameInNodeCharset - returns if name is in charset below or not
func (node *Node) NameInNodeCharSet() bool {
charset := "abcdefghijklmnopqrstuvwxyz1234567890-"

View file

@ -156,3 +156,10 @@ type RelayRequest struct {
NetID string `json:"netid" bson:"netid"`
RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs"`
}
// ServerUpdateData - contains data to configure server
// and if it should set peers
type ServerUpdateData struct {
UpdatePeers bool `json:"updatepeers" bson:"updatepeers"`
Node Node `json:"servernode" bson:"servernode"`
}

View file

@ -1,51 +1,17 @@
package serverctl
import (
"encoding/json"
"errors"
"net"
"os"
"strings"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/servercfg"
)
// GetServerWGConf - gets the server WG configuration
func GetServerWGConf() (models.IntClient, error) {
var server models.IntClient
collection, err := database.FetchRecords(database.INT_CLIENTS_TABLE_NAME)
if err != nil {
return models.IntClient{}, errors.New("could not find comms server")
}
for _, value := range collection {
json.Unmarshal([]byte(value), &server)
if server.Network == "comms" && server.IsServer == "yes" {
return server, nil
}
}
return models.IntClient{}, errors.New("could not find comms server")
}
// FileExists - checks if local file exists
func FileExists(f string) bool {
info, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
// RemoveNetwork - removes a network locally on server
func RemoveNetwork(network string) (bool, error) {
err := logic.ServerLeave(network)
return true, err
}
// InitServerNetclient - intializes the server netclient
func InitServerNetclient() error {
netclientDir := ncutils.GetNetclientPath()
@ -59,78 +25,42 @@ func InitServerNetclient() error {
return nil
}
// HandleContainedClient - function for checkins on server
func HandleContainedClient() error {
servernets, err := logic.GetNetworks()
if err != nil && !database.IsEmptyRecord(err) {
// SyncServerNetwork - ensures a wg interface and node exists for server
func SyncServerNetwork(serverNode *models.Node) error {
serverNetworkSettings, err := logic.GetNetwork(serverNode.Network)
if err != nil {
return err
}
if len(servernets) > 0 {
if err != nil {
return err
}
for _, serverNet := range servernets {
var serverID, err = logic.GetNetworkServerNodeID(serverNet.NetID)
if err != nil {
logger.Log(1, "error occurred during server checkin:", err.Error())
continue
}
err = logic.ServerCheckin(serverID, servercfg.GetNodeID(), serverNet.NetID)
if err != nil {
logger.Log(1, "error occurred during server checkin:", err.Error())
} else {
logger.Log(3, "completed peers check of network", serverNet.NetID)
}
}
syncErr := SyncNetworks(servernets[:])
if syncErr != nil {
logger.Log(1, "error syncing networks:", syncErr.Error())
syncErr = nil
}
// logger.Log("completed a checkin call", 3)
}
return nil
}
// SyncNetworks - syncs the networks for servers
func SyncNetworks(servernets []models.Network) error {
localnets, err := net.Interfaces()
if err != nil {
return err
}
// check networks to join
for _, servernet := range servernets {
exists := false
for _, localnet := range localnets {
if servernet.DefaultInterface == localnet.Name {
exists = true
}
exists := false
for _, localnet := range localnets {
if serverNetworkSettings.DefaultInterface == localnet.Name {
exists = true
}
if !exists {
success, err := AddNetwork(&servernet)
if err != nil || !success {
if err == nil {
err = errors.New("network add failed for " + servernet.NetID)
}
if !strings.Contains(err.Error(), "macaddress_unique") { // ignore macaddress unique error throws
logger.Log(1, "error adding network", servernet.NetID, "during sync:", err.Error())
}
}
if !exists {
err := logic.ServerJoin(&serverNetworkSettings)
if err != nil {
if err == nil {
err = errors.New("network add failed for " + serverNetworkSettings.NetID)
}
if !strings.Contains(err.Error(), "macaddress_unique") { // ignore macaddress unique error throws
logger.Log(1, "error adding network", serverNetworkSettings.NetID, "during sync:", err.Error())
}
}
}
// check networks to leave
for _, localnet := range localnets {
if strings.Contains(localnet.Name, "nm-") {
var exists = ""
for _, servernet := range servernets {
if servernet.DefaultInterface == localnet.Name {
exists = servernet.NetID
}
if serverNetworkSettings.DefaultInterface == localnet.Name {
exists = serverNetworkSettings.NetID
}
if exists == "" {
success, err := RemoveNetwork(exists)
if err != nil || !success {
err := logic.DeleteNodeByID(serverNode, true)
if err != nil {
if err == nil {
err = errors.New("network delete failed for " + exists)
}
@ -139,12 +69,5 @@ func SyncNetworks(servernets []models.Network) error {
}
}
}
return nil
}
// AddNetwork - add a network to server in client mode
func AddNetwork(networkSettings *models.Network) (bool, error) {
var err = logic.ServerJoin(networkSettings, servercfg.GetNodeID())
return true, err
}

29
serverctl/serverq.go Normal file
View file

@ -0,0 +1,29 @@
package serverctl
import (
"fmt"
"github.com/gravitl/netmaker/models"
)
// ServerQueue - holds data to be updated across the server
var ServerQueue chan models.ServerUpdateData
func init() {
ServerQueue = make(chan models.ServerUpdateData, 100)
}
// Push - Pushes ServerUpdateData to be used later
func Push(serverData models.ServerUpdateData) {
ServerQueue <- serverData
}
// Pop - fetches first available data from queue
func Pop() (models.ServerUpdateData, error) {
select {
case serverData := <-ServerQueue:
return serverData, nil
default:
return models.ServerUpdateData{}, fmt.Errorf("empty server queue")
}
}