Combined client + server code, Added HA ability, minor bug fixes

This commit is contained in:
0xdcarns
2021-10-05 15:02:09 -04:00
parent cd99a6ba9a
commit 989676e77f
34 changed files with 1101 additions and 821 deletions

View File

@@ -2,157 +2,15 @@ package controller
import ( import (
"encoding/json" "encoding/json"
"strconv"
"strings" "strings"
"time"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/dnslogic"
"github.com/gravitl/netmaker/functions" "github.com/gravitl/netmaker/functions"
"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"
"golang.org/x/crypto/bcrypt"
) )
func GetPeersList(networkName string, excludeRelayed bool, relayedNodeAddr string) ([]models.Node, error) {
var peers []models.Node
var relayNode models.Node
var err error
if relayedNodeAddr == "" {
peers, err = GetNodePeers(networkName, excludeRelayed)
} else {
relayNode, err = GetNodeRelay(networkName, relayedNodeAddr)
if relayNode.Address != "" {
relayNode = setPeerInfo(relayNode)
network, err := models.GetNetwork(networkName)
if err == nil {
relayNode.AllowedIPs = append(relayNode.AllowedIPs, network.AddressRange)
} else {
relayNode.AllowedIPs = append(relayNode.AllowedIPs, relayNode.RelayAddrs...)
}
nodepeers, err := GetNodePeers(networkName, false)
if err == nil && relayNode.UDPHolePunch == "yes" {
for _, nodepeer := range nodepeers {
if nodepeer.Address == relayNode.Address {
relayNode.Endpoint = nodepeer.Endpoint
relayNode.ListenPort = nodepeer.ListenPort
}
}
}
peers = append(peers, relayNode)
}
}
return peers, err
}
func GetNodePeers(networkName string, excludeRelayed bool) ([]models.Node, error) {
var peers []models.Node
collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
if err != nil {
if database.IsEmptyRecord(err) {
return peers, nil
}
functions.PrintUserLog("", err.Error(), 2)
return nil, err
}
udppeers, errN := database.GetPeers(networkName)
if errN != nil {
functions.PrintUserLog("", errN.Error(), 2)
}
for _, value := range collection {
var node models.Node
var peer models.Node
err := json.Unmarshal([]byte(value), &node)
if err != nil {
functions.PrintUserLog("", err.Error(), 2)
continue
}
if node.IsEgressGateway == "yes" { // handle egress stuff
peer.EgressGatewayRanges = node.EgressGatewayRanges
peer.IsEgressGateway = node.IsEgressGateway
}
allow := node.IsRelayed != "yes" || !excludeRelayed
if node.Network == networkName && node.IsPending != "yes" && allow {
peer = setPeerInfo(node)
if node.UDPHolePunch == "yes" && errN == nil && functions.CheckEndpoint(udppeers[node.PublicKey]) {
endpointstring := udppeers[node.PublicKey]
endpointarr := strings.Split(endpointstring, ":")
if len(endpointarr) == 2 {
port, err := strconv.Atoi(endpointarr[1])
if err == nil {
peer.Endpoint = endpointarr[0]
peer.ListenPort = int32(port)
}
}
}
if node.IsRelay == "yes" {
network, err := models.GetNetwork(networkName)
if err == nil {
peer.AllowedIPs = append(peer.AllowedIPs, network.AddressRange)
} else {
peer.AllowedIPs = append(peer.AllowedIPs, node.RelayAddrs...)
}
}
peers = append(peers, peer)
}
}
return peers, err
}
func setPeerInfo(node models.Node) models.Node {
var peer models.Node
peer.RelayAddrs = node.RelayAddrs
peer.IsRelay = node.IsRelay
peer.IsRelayed = node.IsRelayed
peer.PublicKey = node.PublicKey
peer.Endpoint = node.Endpoint
peer.LocalAddress = node.LocalAddress
peer.ListenPort = node.ListenPort
peer.AllowedIPs = node.AllowedIPs
peer.UDPHolePunch = node.UDPHolePunch
peer.Address = node.Address
peer.Address6 = node.Address6
peer.EgressGatewayRanges = node.EgressGatewayRanges
peer.IsEgressGateway = node.IsEgressGateway
peer.IngressGatewayRange = node.IngressGatewayRange
peer.IsIngressGateway = node.IsIngressGateway
peer.IsPending = node.IsPending
return peer
}
func GetExtPeersList(macaddress string, networkName string) ([]models.ExtPeersResponse, error) {
var peers []models.ExtPeersResponse
records, err := database.FetchRecords(database.EXT_CLIENT_TABLE_NAME)
if err != nil {
return peers, err
}
for _, value := range records {
var peer models.ExtPeersResponse
var extClient models.ExtClient
err = json.Unmarshal([]byte(value), &peer)
if err != nil {
functions.PrintUserLog(models.NODE_SERVER_NAME, "failed to unmarshal peer", 2)
continue
}
err = json.Unmarshal([]byte(value), &extClient)
if err != nil {
functions.PrintUserLog(models.NODE_SERVER_NAME, "failed to unmarshal ext client", 2)
continue
}
if extClient.Network == networkName && extClient.IngressGatewayID == macaddress {
peers = append(peers, peer)
}
}
return peers, err
}
/** /**
* If being deleted by server, create a record in the DELETED_NODES_TABLE for the client to find * If being deleted by server, create a record in the DELETED_NODES_TABLE for the client to find
* If being deleted by the client, delete completely * If being deleted by the client, delete completely
@@ -183,7 +41,7 @@ func DeleteNode(key string, exterminate bool) error {
return err return err
} }
if servercfg.IsDNSMode() { if servercfg.IsDNSMode() {
err = SetDNS() err = dnslogic.SetDNS()
} }
return err return err
} }
@@ -235,93 +93,3 @@ func GetIntClient(clientid string) (models.IntClient, error) {
} }
return client, nil return client, nil
} }
func CreateNode(node models.Node, networkName string) (models.Node, error) {
//encrypt that password so we never see it
hash, err := bcrypt.GenerateFromPassword([]byte(node.Password), 5)
if err != nil {
return node, err
}
//set password to encrypted password
node.Password = string(hash)
node.Network = networkName
if node.Name == models.NODE_SERVER_NAME {
node.IsServer = "yes"
}
if servercfg.IsDNSMode() && node.DNSOn == ""{
node.DNSOn = "yes"
}
node.SetDefaults()
node.Address, err = functions.UniqueAddress(networkName)
if err != nil {
return node, err
}
node.Address6, err = functions.UniqueAddress6(networkName)
if err != nil {
return node, err
}
//Create a JWT for the node
tokenString, _ := functions.CreateJWT(node.MacAddress, networkName)
if tokenString == "" {
//returnErrorResponse(w, r, errorResponse)
return node, err
}
err = node.Validate(false)
if err != nil {
return node, err
}
key, err := functions.GetRecordKey(node.MacAddress, node.Network)
if err != nil {
return node, err
}
nodebytes, err := json.Marshal(&node)
if err != nil {
return node, err
}
err = database.Insert(key, string(nodebytes), database.NODES_TABLE_NAME)
if err != nil {
return node, err
}
if node.IsPending != "yes" {
functions.DecrimentKey(node.Network, node.AccessKey)
}
SetNetworkNodesLastModified(node.Network)
if servercfg.IsDNSMode() {
err = SetDNS()
}
return node, err
}
func SetNetworkServerPeers(networkName string) {
if currentPeersList, err := serverctl.GetPeers(networkName); err == nil {
if database.SetPeers(currentPeersList, networkName) {
functions.PrintUserLog(models.NODE_SERVER_NAME, "set new peers on network "+networkName, 1)
}
} else {
functions.PrintUserLog(models.NODE_SERVER_NAME, "could not set peers on network "+networkName, 1)
functions.PrintUserLog(models.NODE_SERVER_NAME, err.Error(), 1)
}
}
func SetNetworkNodesLastModified(networkName string) error {
timestamp := time.Now().Unix()
network, err := functions.GetParentNetwork(networkName)
if err != nil {
return err
}
network.NodesLastModified = timestamp
data, err := json.Marshal(&network)
if err != nil {
return err
}
err = database.Insert(networkName, string(data), database.NETWORKS_TABLE_NAME)
if err != nil {
return err
}
return nil
}

View File

@@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -13,20 +14,20 @@ func TestGetPeerList(t *testing.T) {
deleteAllNetworks() deleteAllNetworks()
createNet() createNet()
t.Run("NoNodes", func(t *testing.T) { t.Run("NoNodes", func(t *testing.T) {
peers, err := GetPeersList("skynet", false, "") peers, err := logic.GetPeersList("skynet", false, "")
assert.Nil(t, err) assert.Nil(t, err)
assert.Nil(t, peers) assert.Nil(t, peers)
}) })
node := createTestNode() node := createTestNode()
t.Run("One Node", func(t *testing.T) { t.Run("One Node", func(t *testing.T) {
peers, err := GetPeersList("skynet", false, "") peers, err := logic.GetPeersList("skynet", false, "")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, node.Address, peers[0].Address) assert.Equal(t, node.Address, peers[0].Address)
}) })
t.Run("Multiple Nodes", func(t *testing.T) { t.Run("Multiple Nodes", func(t *testing.T) {
createnode := models.Node{PublicKey: "RM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.2", MacAddress: "02:02:03:04:05:06", Password: "password", Network: "skynet"} createnode := models.Node{PublicKey: "RM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.2", MacAddress: "02:02:03:04:05:06", Password: "password", Network: "skynet"}
CreateNode(createnode, "skynet") logic.CreateNode(createnode, "skynet")
peers, err := GetPeersList("skynet", false, "") peers, err := logic.GetPeersList("skynet", false, "")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, len(peers), 2) assert.Equal(t, len(peers), 2)
foundNodeEndpoint := false foundNodeEndpoint := false
@@ -97,7 +98,7 @@ func TestCreateNode(t *testing.T) {
createnode := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet"} createnode := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet"}
//err := ValidateNodeCreate("skynet", createnode) //err := ValidateNodeCreate("skynet", createnode)
//assert.Nil(t, err) //assert.Nil(t, err)
node, err := CreateNode(createnode, "skynet") node, err := logic.CreateNode(createnode, "skynet")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "10.0.0.1", node.Endpoint) assert.Equal(t, "10.0.0.1", node.Endpoint)
assert.Equal(t, "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", node.PublicKey) assert.Equal(t, "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", node.PublicKey)
@@ -113,17 +114,17 @@ func TestSetNetworkNodesLastModified(t *testing.T) {
deleteAllNetworks() deleteAllNetworks()
createNet() createNet()
t.Run("InvalidNetwork", func(t *testing.T) { t.Run("InvalidNetwork", func(t *testing.T) {
err := SetNetworkNodesLastModified("badnet") err := logic.SetNetworkNodesLastModified("badnet")
assert.EqualError(t, err, "no result found") assert.EqualError(t, err, "no result found")
}) })
t.Run("NetworkExists", func(t *testing.T) { t.Run("NetworkExists", func(t *testing.T) {
err := SetNetworkNodesLastModified("skynet") err := logic.SetNetworkNodesLastModified("skynet")
assert.Nil(t, err) assert.Nil(t, err)
}) })
} }
func createTestNode() models.Node { func createTestNode() models.Node {
createnode := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet"} createnode := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet"}
node, _ := CreateNode(createnode, "skynet") node, _ := logic.CreateNode(createnode, "skynet")
return node return node
} }

View File

@@ -3,13 +3,13 @@ package controller
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/dnslogic"
"github.com/gravitl/netmaker/functions" "github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
"github.com/txn2/txeh"
) )
func dnsHandlers(r *mux.Router) { func dnsHandlers(r *mux.Router) {
@@ -63,7 +63,7 @@ func GetAllDNS() ([]models.DNSEntry, error) {
return []models.DNSEntry{}, err return []models.DNSEntry{}, err
} }
for _, net := range networks { for _, net := range networks {
netdns, err := GetDNS(net.NetID) netdns, err := dnslogic.GetDNS(net.NetID)
if err != nil { if err != nil {
return []models.DNSEntry{}, nil return []models.DNSEntry{}, nil
} }
@@ -103,7 +103,7 @@ func getCustomDNS(w http.ResponseWriter, r *http.Request) {
var dns []models.DNSEntry var dns []models.DNSEntry
var params = mux.Vars(r) var params = mux.Vars(r)
dns, err := GetCustomDNS(params["network"]) dns, err := dnslogic.GetCustomDNS(params["network"])
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
@@ -114,65 +114,11 @@ func getCustomDNS(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(dns) json.NewEncoder(w).Encode(dns)
} }
func GetCustomDNS(network string) ([]models.DNSEntry, error) {
var dns []models.DNSEntry
collection, err := database.FetchRecords(database.DNS_TABLE_NAME)
if err != nil {
return dns, err
}
for _, value := range collection { // filter for entries based on network
var entry models.DNSEntry
if err := json.Unmarshal([]byte(value), &entry); err != nil {
continue
}
if entry.Network == network {
dns = append(dns, entry)
}
}
return dns, err
}
func SetDNS() error {
hostfile := txeh.Hosts{}
var corefilestring string
networks, err := models.GetNetworks()
if err != nil && !database.IsEmptyRecord(err){
return err
}
for _, net := range networks {
corefilestring = corefilestring + net.NetID + " "
dns, err := GetDNS(net.NetID)
if err != nil && !database.IsEmptyRecord(err) {
return err
}
for _, entry := range dns {
hostfile.AddHost(entry.Address, entry.Name+"."+entry.Network)
}
}
if corefilestring == "" {
corefilestring = "example.com"
}
err = hostfile.SaveAs("./config/dnsconfig/netmaker.hosts")
if err != nil {
return err
}
if servercfg.IsSplitDNS() {
err = functions.SetCorefile(corefilestring)
}
return err
}
func GetDNSEntryNum(domain string, network string) (int, error) { func GetDNSEntryNum(domain string, network string) (int, error) {
num := 0 num := 0
entries, err := GetDNS(network) entries, err := dnslogic.GetDNS(network)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -195,7 +141,7 @@ func getDNS(w http.ResponseWriter, r *http.Request) {
var dns []models.DNSEntry var dns []models.DNSEntry
var params = mux.Vars(r) var params = mux.Vars(r)
dns, err := GetDNS(params["network"]) dns, err := dnslogic.GetDNS(params["network"])
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
@@ -204,22 +150,6 @@ func getDNS(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(dns) json.NewEncoder(w).Encode(dns)
} }
func GetDNS(network string) ([]models.DNSEntry, error) {
var dns []models.DNSEntry
dns, err := GetNodeDNS(network)
if err != nil && !database.IsEmptyRecord(err) {
return dns, err
}
customdns, err := GetCustomDNS(network)
if err != nil && !database.IsEmptyRecord(err) {
return dns, err
}
dns = append(dns, customdns...)
return dns, nil
}
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")
@@ -241,7 +171,7 @@ func createDNS(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
err = SetDNS() err = dnslogic.SetDNS()
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
@@ -296,7 +226,7 @@ func updateDNS(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "badrequest")) returnErrorResponse(w, r, formatError(err, "badrequest"))
return return
} }
err = SetDNS() err = dnslogic.SetDNS()
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
@@ -319,7 +249,7 @@ func deleteDNS(w http.ResponseWriter, r *http.Request) {
} }
entrytext := params["domain"] + "." + params["network"] entrytext := params["domain"] + "." + params["network"]
functions.PrintUserLog(models.NODE_SERVER_NAME, "deleted dns entry: "+entrytext, 1) functions.PrintUserLog(models.NODE_SERVER_NAME, "deleted dns entry: "+entrytext, 1)
err = SetDNS() err = dnslogic.SetDNS()
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
@@ -394,7 +324,7 @@ 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")
err := SetDNS() err := dnslogic.SetDNS()
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))

View File

@@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/dnslogic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -23,7 +24,7 @@ func TestGetCustomDNS(t *testing.T) {
deleteAllNetworks() deleteAllNetworks()
createNet() createNet()
createTestNode() createTestNode()
dns, err := GetCustomDNS("skynet") dns, err := dnslogic.GetCustomDNS("skynet")
assert.Nil(t, err) assert.Nil(t, err)
t.Log(dns) t.Log(dns)
} }
@@ -39,7 +40,7 @@ func TestGetDNSEntryNum(t *testing.T) {
func TestGetDNS(t *testing.T) { func TestGetDNS(t *testing.T) {
database.InitializeDatabase() database.InitializeDatabase()
deleteAllNetworks() deleteAllNetworks()
dns, err := GetDNS("skynet") dns, err := dnslogic.GetDNS("skynet")
assert.Nil(t, err) assert.Nil(t, err)
t.Log(dns) t.Log(dns)
} }

View File

@@ -13,6 +13,7 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/functions" "github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/skip2/go-qrcode" "github.com/skip2/go-qrcode"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
@@ -273,7 +274,7 @@ func CreateExtClient(extclient models.ExtClient) error {
if err = database.Insert(key, string(data), database.EXT_CLIENT_TABLE_NAME); err != nil { if err = database.Insert(key, string(data), database.EXT_CLIENT_TABLE_NAME); err != nil {
return err return err
} }
err = SetNetworkNodesLastModified(extclient.Network) err = logic.SetNetworkNodesLastModified(extclient.Network)
return err return err
} }

View File

@@ -12,6 +12,7 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/functions" "github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/logic"
"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"
@@ -337,8 +338,21 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
} }
func DeleteNetwork(network string) error { func DeleteNetwork(network string) error {
nodeCount, err := functions.GetNetworkNodeCount(network) nodeCount, err := functions.GetNetworkNonServerNodeCount(network)
if nodeCount == 0 || database.IsEmptyRecord(err) { if nodeCount == 0 || database.IsEmptyRecord(err) {
// delete server nodes first then db records
servers, err := logic.GetSortedNetworkServerNodes(network)
if err == nil {
for _, s := range servers {
if err = logic.DeleteNode(s.ID, true); err != nil {
functions.PrintUserLog("[netmaker]", "could not removed server "+s.Name+" before deleting network "+network, 2)
} else {
functions.PrintUserLog("[netmaker]", "removed server "+s.Name+" before deleting network "+network, 2)
}
}
} else {
functions.PrintUserLog("[netmaker]", "could not remove servers before deleting network "+network, 1)
}
return database.DeleteRecord(database.NETWORKS_TABLE_NAME, network) return database.DeleteRecord(database.NETWORKS_TABLE_NAME, network)
} }
return errors.New("node check failed. All nodes must be deleted before deleting network") return errors.New("node check failed. All nodes must be deleted before deleting network")

View File

@@ -8,6 +8,7 @@ import (
"github.com/gravitl/netmaker/functions" "github.com/gravitl/netmaker/functions"
nodepb "github.com/gravitl/netmaker/grpc" nodepb "github.com/gravitl/netmaker/grpc"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
) )
@@ -67,7 +68,7 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
} }
} }
node, err = CreateNode(node, node.Network) node, err = logic.CreateNode(node, node.Network)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -77,7 +78,7 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
Data: string(nodeData), Data: string(nodeData),
Type: nodepb.NODE_TYPE, Type: nodepb.NODE_TYPE,
} }
err = SetNetworkNodesLastModified(node.Network) err = logic.SetNetworkNodesLastModified(node.Network)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -134,15 +135,15 @@ func (s *NodeServiceServer) GetPeers(ctx context.Context, req *nodepb.Object) (*
if err != nil { if err != nil {
return nil, err return nil, err
} }
if node.IsServer == "yes" { if node.IsServer == "yes" && logic.IsLeader(&node){
SetNetworkServerPeers(macAndNetwork[1]) logic.SetNetworkServerPeers(&node)
} }
excludeIsRelayed := node.IsRelay != "yes" excludeIsRelayed := node.IsRelay != "yes"
var relayedNode string var relayedNode string
if node.IsRelayed == "yes" { if node.IsRelayed == "yes" {
relayedNode = node.Address relayedNode = node.Address
} }
peers, err := GetPeersList(macAndNetwork[1], excludeIsRelayed, relayedNode) peers, err := logic.GetPeersList(macAndNetwork[1], excludeIsRelayed, relayedNode)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -172,11 +173,10 @@ func (s *NodeServiceServer) GetExtPeers(ctx context.Context, req *nodepb.Object)
if len(macAndNetwork) != 2 { if len(macAndNetwork) != 2 {
return nil, errors.New("did not receive valid node id when fetching ext peers") return nil, errors.New("did not receive valid node id when fetching ext peers")
} }
peers, err := GetExtPeersList(macAndNetwork[0], macAndNetwork[1]) peers, err := logic.GetExtPeersList(macAndNetwork[0], macAndNetwork[1])
if err != nil { if err != nil {
return nil, err return nil, err
} }
// cursor.Next() returns a boolean, if false there are no more items and loop will break
var extPeers []models.Node var extPeers []models.Node
for i := 0; i < len(peers); i++ { for i := 0; i < len(peers); i++ {
extPeers = append(extPeers, models.Node{ extPeers = append(extPeers, models.Node{

View File

@@ -6,10 +6,11 @@ import (
"net/http" "net/http"
"strings" "strings"
"time" "time"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/dnslogic"
"github.com/gravitl/netmaker/functions" "github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/servercfg"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
@@ -267,7 +268,7 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
var nodes []models.Node var nodes []models.Node
var params = mux.Vars(r) var params = mux.Vars(r)
networkName := params["network"] networkName := params["network"]
nodes, err := GetNetworkNodes(networkName) nodes, err := logic.GetNetworkNodes(networkName)
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
@@ -279,29 +280,6 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(nodes) json.NewEncoder(w).Encode(nodes)
} }
func GetNetworkNodes(network string) ([]models.Node, error) {
var nodes []models.Node
collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
if err != nil {
if database.IsEmptyRecord(err) {
return []models.Node{}, nil
}
return nodes, err
}
for _, value := range collection {
var node models.Node
err := json.Unmarshal([]byte(value), &node)
if err != nil {
continue
}
if node.Network == network {
nodes = append(nodes, node)
}
}
return nodes, nil
}
//A separate function to get all nodes, not just nodes for a particular network. //A separate function to get all nodes, not just nodes for a particular network.
//Not quite sure if this is necessary. Probably necessary based on front end but may want to review after iteration 1 if it's being used or not //Not quite sure if this is necessary. Probably necessary based on front end but may want to review after iteration 1 if it's being used or not
func getAllNodes(w http.ResponseWriter, r *http.Request) { func getAllNodes(w http.ResponseWriter, r *http.Request) {
@@ -335,7 +313,7 @@ func getUsersNodes(user models.User) ([]models.Node, error) {
var nodes []models.Node var nodes []models.Node
var err error var err error
for _, networkName := range user.Networks { for _, networkName := range user.Networks {
tmpNodes, err := GetNetworkNodes(networkName) tmpNodes, err := logic.GetNetworkNodes(networkName)
if err != nil { if err != nil {
continue continue
} }
@@ -437,7 +415,7 @@ func createNode(w http.ResponseWriter, r *http.Request) {
} }
} }
node, err = CreateNode(node, networkName) node, err = logic.CreateNode(node, networkName)
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
@@ -682,7 +660,7 @@ func CreateIngressGateway(netid string, macaddress string) (models.Node, error)
if err != nil { if err != nil {
return models.Node{}, err return models.Node{}, err
} }
err = SetNetworkNodesLastModified(netid) err = logic.SetNetworkNodesLastModified(netid)
return node, err return node, err
} }
@@ -733,7 +711,7 @@ func DeleteIngressGateway(networkName string, macaddress string) (models.Node, e
if err != nil { if err != nil {
return models.Node{}, err return models.Node{}, err
} }
err = SetNetworkNodesLastModified(networkName) err = logic.SetNetworkNodesLastModified(networkName)
return node, err return node, err
} }
@@ -783,7 +761,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
} }
if servercfg.IsDNSMode() { if servercfg.IsDNSMode() {
err = SetDNS() err = dnslogic.SetDNS()
} }
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))

View File

@@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -81,19 +82,19 @@ func TestGetNetworkNodes(t *testing.T) {
deleteAllNetworks() deleteAllNetworks()
createNet() createNet()
t.Run("BadNet", func(t *testing.T) { t.Run("BadNet", func(t *testing.T) {
node, err := GetNetworkNodes("badnet") node, err := logic.GetNetworkNodes("badnet")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, []models.Node{}, node) assert.Equal(t, []models.Node{}, node)
//assert.Equal(t, "mongo: no documents in result", err.Error()) //assert.Equal(t, "mongo: no documents in result", err.Error())
}) })
t.Run("NoNodes", func(t *testing.T) { t.Run("NoNodes", func(t *testing.T) {
node, err := GetNetworkNodes("skynet") node, err := logic.GetNetworkNodes("skynet")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, []models.Node{}, node) assert.Equal(t, []models.Node{}, node)
}) })
t.Run("Success", func(t *testing.T) { t.Run("Success", func(t *testing.T) {
createTestNode() createTestNode()
node, err := GetNetworkNodes("skynet") node, err := logic.GetNetworkNodes("skynet")
assert.Nil(t, err) assert.Nil(t, err)
assert.NotEqual(t, []models.Node(nil), node) assert.NotEqual(t, []models.Node(nil), node)
}) })

View File

@@ -72,6 +72,21 @@ func CreateRelay(relay models.RelayRequest) (models.Node, error) {
return node, nil return node, nil
} }
func deleteRelay(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
nodeMac := params["macaddress"]
netid := params["network"]
node, err := DeleteRelay(netid, nodeMac)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
functions.PrintUserLog(r.Header.Get("user"), "deleted egress gateway "+nodeMac+" on network "+netid, 1)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node)
}
func SetRelayedNodes(yesOrno string, networkName string, addrs []string) error { func SetRelayedNodes(yesOrno string, networkName string, addrs []string) error {
collections, err := database.FetchRecords(database.NODES_TABLE_NAME) collections, err := database.FetchRecords(database.NODES_TABLE_NAME)
@@ -125,21 +140,6 @@ func UpdateRelay(network string, oldAddrs []string, newAddrs []string) {
} }
} }
func deleteRelay(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
nodeMac := params["macaddress"]
netid := params["network"]
node, err := DeleteRelay(netid, nodeMac)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
functions.PrintUserLog(r.Header.Get("user"), "deleted egress gateway "+nodeMac+" on network "+netid, 1)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node)
}
func DeleteRelay(network, macaddress string) (models.Node, error) { func DeleteRelay(network, macaddress string) (models.Node, error) {
node, err := functions.GetNodeByMacAddress(network, macaddress) node, err := functions.GetNodeByMacAddress(network, macaddress)
@@ -171,30 +171,3 @@ func DeleteRelay(network, macaddress string) (models.Node, error) {
} }
return node, nil return node, nil
} }
func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) {
collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
var relay models.Node
if err != nil {
if database.IsEmptyRecord(err) {
return relay, nil
}
functions.PrintUserLog("", err.Error(), 2)
return relay, err
}
for _, value := range collection {
err := json.Unmarshal([]byte(value), &relay)
if err != nil {
functions.PrintUserLog("", err.Error(), 2)
continue
}
if relay.IsRelay == "yes" {
for _, addr := range relay.RelayAddrs {
if addr == relayedNodeAddr {
return relay, nil
}
}
}
}
return relay, errors.New("could not find relay for node " + relayedNodeAddr)
}

View File

@@ -89,17 +89,17 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) {
//Search DB for node with Mac Address. Ignore pending nodes (they should not be able to authenticate with API untill approved). //Search DB for node with Mac Address. Ignore pending nodes (they should not be able to authenticate with API untill approved).
record, err := database.FetchRecord(database.USERS_TABLE_NAME, authRequest.UserName) record, err := database.FetchRecord(database.USERS_TABLE_NAME, authRequest.UserName)
if err != nil { if err != nil {
return "", errors.New("user " + authRequest.UserName + " not found") return "", errors.New("incorrect credentials")
} }
if err = json.Unmarshal([]byte(record), &result); err != nil { if err = json.Unmarshal([]byte(record), &result); err != nil {
return "", errors.New("user " + authRequest.UserName + " not found") return "", errors.New("incorrect credentials")
} }
//compare password from request to stored password in database //compare password from request to stored password in database
//might be able to have a common hash (certificates?) and compare those so that a password isn't passed in in plain text... //might be able to have a common hash (certificates?) and compare those so that a password isn't passed in in plain text...
//TODO: Consider a way of hashing the password client side before sending, or using certificates //TODO: Consider a way of hashing the password client side before sending, or using certificates
if err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(authRequest.Password)); err != nil { if err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(authRequest.Password)); err != nil {
return "", errors.New("wrong password") return "", errors.New("incorrect credentials")
} }
//Create a new JWT for the node //Create a new JWT for the node

View File

@@ -219,7 +219,7 @@ func TestVerifyAuthRequest(t *testing.T) {
authRequest.Password = "password" authRequest.Password = "password"
jwt, err := VerifyAuthRequest(authRequest) jwt, err := VerifyAuthRequest(authRequest)
assert.Equal(t, "", jwt) assert.Equal(t, "", jwt)
assert.EqualError(t, err, "user admin not found") assert.EqualError(t, err, "incorrect credentials")
}) })
t.Run("Non-Admin", func(t *testing.T) { t.Run("Non-Admin", func(t *testing.T) {
user := models.User{"nonadmin", "somepass", nil, false} user := models.User{"nonadmin", "somepass", nil, false}
@@ -235,7 +235,7 @@ func TestVerifyAuthRequest(t *testing.T) {
authRequest := models.UserAuthParams{"admin", "badpass"} authRequest := models.UserAuthParams{"admin", "badpass"}
jwt, err := VerifyAuthRequest(authRequest) jwt, err := VerifyAuthRequest(authRequest)
assert.Equal(t, "", jwt) assert.Equal(t, "", jwt)
assert.EqualError(t, err, "wrong password") assert.EqualError(t, err, "incorrect credentials")
}) })
t.Run("Success", func(t *testing.T) { t.Run("Success", func(t *testing.T) {
authRequest := models.UserAuthParams{"admin", "password"} authRequest := models.UserAuthParams{"admin", "password"}

104
dnslogic/dns.go Normal file
View File

@@ -0,0 +1,104 @@
package dnslogic
import (
"encoding/json"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
"github.com/txn2/txeh"
)
func SetDNS() error {
hostfile := txeh.Hosts{}
var corefilestring string
networks, err := models.GetNetworks()
if err != nil && !database.IsEmptyRecord(err) {
return err
}
for _, net := range networks {
corefilestring = corefilestring + net.NetID + " "
dns, err := GetDNS(net.NetID)
if err != nil && !database.IsEmptyRecord(err) {
return err
}
for _, entry := range dns {
hostfile.AddHost(entry.Address, entry.Name+"."+entry.Network)
}
}
if corefilestring == "" {
corefilestring = "example.com"
}
err = hostfile.SaveAs("./config/dnsconfig/netmaker.hosts")
if err != nil {
return err
}
if servercfg.IsSplitDNS() {
err = functions.SetCorefile(corefilestring)
}
return err
}
func GetDNS(network string) ([]models.DNSEntry, error) {
var dns []models.DNSEntry
dns, err := GetNodeDNS(network)
if err != nil && !database.IsEmptyRecord(err) {
return dns, err
}
customdns, err := GetCustomDNS(network)
if err != nil && !database.IsEmptyRecord(err) {
return dns, err
}
dns = append(dns, customdns...)
return dns, nil
}
func GetNodeDNS(network string) ([]models.DNSEntry, error) {
var dns []models.DNSEntry
collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
if err != nil {
return dns, err
}
for _, value := range collection {
var entry models.DNSEntry
var node models.Node
if err = json.Unmarshal([]byte(value), &node); err != nil {
continue
}
if err = json.Unmarshal([]byte(value), &entry); node.Network == network && err == nil {
dns = append(dns, entry)
}
}
return dns, nil
}
func GetCustomDNS(network string) ([]models.DNSEntry, error) {
var dns []models.DNSEntry
collection, err := database.FetchRecords(database.DNS_TABLE_NAME)
if err != nil {
return dns, err
}
for _, value := range collection { // filter for entries based on network
var entry models.DNSEntry
if err := json.Unmarshal([]byte(value), &entry); err != nil {
continue
}
if entry.Network == network {
dns = append(dns, entry)
}
}
return dns, err
}

View File

@@ -9,24 +9,16 @@ ENV GO111MODULE=auto
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=1 /usr/local/go/bin/go build -ldflags="-w -s" -o netmaker main.go RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=1 /usr/local/go/bin/go build -ldflags="-w -s" -o netmaker main.go
WORKDIR /app/netclient
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 /usr/local/go/bin/go build -ldflags="-w -s" -o netclient main.go
#second stage
FROM alpine:3.13.6 FROM alpine:3.13.6
# add a c lib # add a c lib
RUN apk add gcompat iptables RUN apk add gcompat iptables
# set the working directory # set the working directory
WORKDIR /root/ WORKDIR /root/
RUN mkdir /etc/netclient RUN mkdir -p /etc/netclient/config
COPY --from=builder /app/netmaker . COPY --from=builder /app/netmaker .
COPY --from=builder /app/config config COPY --from=builder /app/config config
COPY --from=builder /app/netclient/netclient /etc/netclient/netclient
RUN chmod 0755 /etc/netclient/netclient
EXPOSE 8081 EXPOSE 8081
EXPOSE 50051 EXPOSE 50051

View File

@@ -20,11 +20,6 @@ import (
"github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/servercfg"
) )
func CheckEndpoint(endpoint string) bool {
endpointarr := strings.Split(endpoint, ":")
return len(endpointarr) == 2
}
func PrintUserLog(username string, message string, loglevel int) { func PrintUserLog(username string, message string, loglevel int) {
log.SetFlags(log.Flags() &^ (log.Llongfile | log.Lshortfile)) log.SetFlags(log.Flags() &^ (log.Llongfile | log.Lshortfile))
if int32(loglevel) <= servercfg.GetVerbose() && servercfg.GetVerbose() != 0 { if int32(loglevel) <= servercfg.GetVerbose() && servercfg.GetVerbose() != 0 {
@@ -380,7 +375,7 @@ func IsMacAddressUnique(macaddress string, networkName string) (bool, error) {
return true, nil return true, nil
} }
func GetNetworkNodeCount(networkName string) (int, error) { func GetNetworkNonServerNodeCount(networkName string) (int, error) {
collection, err := database.FetchRecords(database.NODES_TABLE_NAME) collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
count := 0 count := 0
@@ -392,7 +387,7 @@ func GetNetworkNodeCount(networkName string) (int, error) {
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 { if node.Network == networkName && node.IsServer != "yes" {
count++ count++
} }
} }
@@ -491,12 +486,6 @@ func IsIpCIDR(host string) bool {
return ip != nil && ipnet != nil return ip != nil && ipnet != nil
} }
//This is used to validate public keys (make sure they're base64 encoded like all public keys should be).
func IsBase64(s string) bool {
_, err := base64.StdEncoding.DecodeString(s)
return err == nil
}
//This checks to make sure a network name is valid. //This checks to make sure a network name is valid.
//Switch to REGEX? //Switch to REGEX?
func NameInNetworkCharSet(name string) bool { func NameInNetworkCharSet(name string) bool {

38
logic/extpeers.go Normal file
View File

@@ -0,0 +1,38 @@
package logic
import (
"encoding/json"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/models"
)
func GetExtPeersList(macaddress string, networkName string) ([]models.ExtPeersResponse, error) {
var peers []models.ExtPeersResponse
records, err := database.FetchRecords(database.EXT_CLIENT_TABLE_NAME)
if err != nil {
return peers, err
}
for _, value := range records {
var peer models.ExtPeersResponse
var extClient models.ExtClient
err = json.Unmarshal([]byte(value), &peer)
if err != nil {
functions.PrintUserLog(models.NODE_SERVER_NAME, "failed to unmarshal peer", 2)
continue
}
err = json.Unmarshal([]byte(value), &extClient)
if err != nil {
functions.PrintUserLog(models.NODE_SERVER_NAME, "failed to unmarshal ext client", 2)
continue
}
if extClient.Network == networkName && extClient.IngressGatewayID == macaddress {
peers = append(peers, peer)
}
}
return peers, err
}

88
logic/nodes.go Normal file
View File

@@ -0,0 +1,88 @@
package logic
import (
"encoding/json"
"sort"
"time"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/models"
)
func GetNetworkNodes(network string) ([]models.Node, error) {
var nodes []models.Node
collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
if err != nil {
if database.IsEmptyRecord(err) {
return []models.Node{}, nil
}
return nodes, err
}
for _, value := range collection {
var node models.Node
err := json.Unmarshal([]byte(value), &node)
if err != nil {
continue
}
if node.Network == network {
nodes = append(nodes, node)
}
}
return nodes, nil
}
func GetSortedNetworkServerNodes(network string) ([]models.Node, error) {
var nodes []models.Node
collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
if err != nil {
if database.IsEmptyRecord(err) {
return []models.Node{}, nil
}
return nodes, err
}
for _, value := range collection {
var node models.Node
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
}
func GetPeers(node models.Node) ([]models.Node, error) {
if node.IsServer == "yes" && IsLeader(&node) {
SetNetworkServerPeers(&node)
}
excludeIsRelayed := node.IsRelay != "yes"
var relayedNode string
if node.IsRelayed == "yes" {
relayedNode = node.Address
}
peers, err := GetPeersList(node.Network, excludeIsRelayed, relayedNode)
if err != nil {
return nil, err
}
return peers, nil
}
func IsLeader(node *models.Node) bool {
nodes, err := GetSortedNetworkServerNodes(node.Network)
if err != nil {
functions.PrintUserLog("[netmaker]", "ERROR: COULD NOT RETRIEVE SERVER NODES. THIS WILL BREAK HOLE PUNCHING.", 0)
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
}

285
logic/util.go Normal file
View File

@@ -0,0 +1,285 @@
// package for logicing client and server code
package logic
import (
"encoding/json"
"strconv"
"strings"
"time"
"encoding/base64"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/dnslogic"
"github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/relay"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/crypto/bcrypt"
)
//This is used to validate public keys (make sure they're base64 encoded like all public keys should be).
func IsBase64(s string) bool {
_, err := base64.StdEncoding.DecodeString(s)
return err == nil
}
func CheckEndpoint(endpoint string) bool {
endpointarr := strings.Split(endpoint, ":")
return len(endpointarr) == 2
}
func SetNetworkServerPeers(node *models.Node) {
if currentPeersList, err := GetSystemPeers(node); err == nil {
if database.SetPeers(currentPeersList, node.Network) {
functions.PrintUserLog(models.NODE_SERVER_NAME, "set new peers on network "+node.Network, 1)
}
} else {
functions.PrintUserLog(models.NODE_SERVER_NAME, "could not set peers on network "+node.Network, 1)
functions.PrintUserLog(models.NODE_SERVER_NAME, err.Error(), 1)
}
}
func DeleteNode(key string, exterminate bool) error {
var err error
if !exterminate {
args := strings.Split(key, "###")
node, err := GetNode(args[0], args[1])
if err != nil {
return err
}
node.Action = models.NODE_DELETE
nodedata, err := json.Marshal(&node)
if err != nil {
return err
}
err = database.Insert(key, string(nodedata), database.DELETED_NODES_TABLE_NAME)
if err != nil {
return err
}
} else {
if err := database.DeleteRecord(database.DELETED_NODES_TABLE_NAME, key); err != nil {
functions.PrintUserLog("", err.Error(), 2)
}
}
if err := database.DeleteRecord(database.NODES_TABLE_NAME, key); err != nil {
return err
}
if servercfg.IsDNSMode() {
err = dnslogic.SetDNS()
}
return err
}
func CreateNode(node models.Node, networkName string) (models.Node, error) {
//encrypt that password so we never see it
hash, err := bcrypt.GenerateFromPassword([]byte(node.Password), 5)
if err != nil {
return node, err
}
//set password to encrypted password
node.Password = string(hash)
node.Network = networkName
if node.Name == models.NODE_SERVER_NAME {
node.IsServer = "yes"
}
if servercfg.IsDNSMode() && node.DNSOn == "" {
node.DNSOn = "yes"
}
node.SetDefaults()
node.Address, err = functions.UniqueAddress(networkName)
if err != nil {
return node, err
}
node.Address6, err = functions.UniqueAddress6(networkName)
if err != nil {
return node, err
}
//Create a JWT for the node
tokenString, _ := functions.CreateJWT(node.MacAddress, networkName)
if tokenString == "" {
//returnErrorResponse(w, r, errorResponse)
return node, err
}
err = node.Validate(false)
if err != nil {
return node, err
}
key, err := functions.GetRecordKey(node.MacAddress, node.Network)
if err != nil {
return node, err
}
nodebytes, err := json.Marshal(&node)
if err != nil {
return node, err
}
err = database.Insert(key, string(nodebytes), database.NODES_TABLE_NAME)
if err != nil {
return node, err
}
if node.IsPending != "yes" {
functions.DecrimentKey(node.Network, node.AccessKey)
}
SetNetworkNodesLastModified(node.Network)
if servercfg.IsDNSMode() {
err = dnslogic.SetDNS()
}
return node, err
}
func SetNetworkNodesLastModified(networkName string) error {
timestamp := time.Now().Unix()
network, err := functions.GetParentNetwork(networkName)
if err != nil {
return err
}
network.NodesLastModified = timestamp
data, err := json.Marshal(&network)
if err != nil {
return err
}
err = database.Insert(networkName, string(data), database.NETWORKS_TABLE_NAME)
if err != nil {
return err
}
return nil
}
func GetNode(macaddress string, network string) (models.Node, error) {
var node models.Node
key, err := functions.GetRecordKey(macaddress, network)
if err != nil {
return node, err
}
data, err := database.FetchRecord(database.NODES_TABLE_NAME, key)
if err != nil {
if data == "" {
data, err = database.FetchRecord(database.DELETED_NODES_TABLE_NAME, key)
err = json.Unmarshal([]byte(data), &node)
}
return node, err
}
if err = json.Unmarshal([]byte(data), &node); err != nil {
return node, err
}
node.SetDefaults()
return node, err
}
func GetNodePeers(networkName string, excludeRelayed bool) ([]models.Node, error) {
var peers []models.Node
collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
if err != nil {
if database.IsEmptyRecord(err) {
return peers, nil
}
functions.PrintUserLog("", err.Error(), 2)
return nil, err
}
udppeers, errN := database.GetPeers(networkName)
if errN != nil {
functions.PrintUserLog("", errN.Error(), 2)
}
for _, value := range collection {
var node models.Node
var peer models.Node
err := json.Unmarshal([]byte(value), &node)
if err != nil {
functions.PrintUserLog("", err.Error(), 2)
continue
}
if node.IsEgressGateway == "yes" { // handle egress stuff
peer.EgressGatewayRanges = node.EgressGatewayRanges
peer.IsEgressGateway = node.IsEgressGateway
}
allow := node.IsRelayed != "yes" || !excludeRelayed
if node.Network == networkName && node.IsPending != "yes" && allow {
peer = setPeerInfo(node)
if node.UDPHolePunch == "yes" && errN == nil && CheckEndpoint(udppeers[node.PublicKey]) {
endpointstring := udppeers[node.PublicKey]
endpointarr := strings.Split(endpointstring, ":")
if len(endpointarr) == 2 {
port, err := strconv.Atoi(endpointarr[1])
if err == nil {
peer.Endpoint = endpointarr[0]
peer.ListenPort = int32(port)
}
}
}
if node.IsRelay == "yes" {
network, err := models.GetNetwork(networkName)
if err == nil {
peer.AllowedIPs = append(peer.AllowedIPs, network.AddressRange)
} else {
peer.AllowedIPs = append(peer.AllowedIPs, node.RelayAddrs...)
}
}
peers = append(peers, peer)
}
}
return peers, err
}
func GetPeersList(networkName string, excludeRelayed bool, relayedNodeAddr string) ([]models.Node, error) {
var peers []models.Node
var relayNode models.Node
var err error
if relayedNodeAddr == "" {
peers, err = GetNodePeers(networkName, excludeRelayed)
} else {
relayNode, err = relay.GetNodeRelay(networkName, relayedNodeAddr)
if relayNode.Address != "" {
relayNode = setPeerInfo(relayNode)
network, err := models.GetNetwork(networkName)
if err == nil {
relayNode.AllowedIPs = append(relayNode.AllowedIPs, network.AddressRange)
} else {
relayNode.AllowedIPs = append(relayNode.AllowedIPs, relayNode.RelayAddrs...)
}
nodepeers, err := GetNodePeers(networkName, false)
if err == nil && relayNode.UDPHolePunch == "yes" {
for _, nodepeer := range nodepeers {
if nodepeer.Address == relayNode.Address {
relayNode.Endpoint = nodepeer.Endpoint
relayNode.ListenPort = nodepeer.ListenPort
}
}
}
peers = append(peers, relayNode)
}
}
return peers, err
}
func setPeerInfo(node models.Node) models.Node {
var peer models.Node
peer.RelayAddrs = node.RelayAddrs
peer.IsRelay = node.IsRelay
peer.IsServer = node.IsServer
peer.IsRelayed = node.IsRelayed
peer.PublicKey = node.PublicKey
peer.Endpoint = node.Endpoint
peer.LocalAddress = node.LocalAddress
peer.ListenPort = node.ListenPort
peer.AllowedIPs = node.AllowedIPs
peer.UDPHolePunch = node.UDPHolePunch
peer.Address = node.Address
peer.Address6 = node.Address6
peer.EgressGatewayRanges = node.EgressGatewayRanges
peer.IsEgressGateway = node.IsEgressGateway
peer.IngressGatewayRange = node.IngressGatewayRange
peer.IsIngressGateway = node.IsIngressGateway
peer.IsPending = node.IsPending
return peer
}

25
logic/wireguard.go Normal file
View File

@@ -0,0 +1,25 @@
package logic
import (
"github.com/gravitl/netmaker/models"
"golang.zx2c4.com/wireguard/wgctrl"
)
func GetSystemPeers(node *models.Node) (map[string]string, error) {
peers := make(map[string]string)
client, err := wgctrl.New()
if err != nil {
return peers, err
}
device, err := client.Device(node.Interface)
if err != nil {
return nil, err
}
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
}

View File

@@ -15,6 +15,7 @@ import (
controller "github.com/gravitl/netmaker/controllers" controller "github.com/gravitl/netmaker/controllers"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/dnslogic"
"github.com/gravitl/netmaker/functions" "github.com/gravitl/netmaker/functions"
nodepb "github.com/gravitl/netmaker/grpc" nodepb "github.com/gravitl/netmaker/grpc"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
@@ -81,14 +82,13 @@ func startControllers() {
go runGRPC(&waitnetwork) go runGRPC(&waitnetwork)
} }
// Run the client in goroutine locally if CLIENT_MODE is "contained" if servercfg.IsClientMode() == "on" {
if servercfg.IsClientMode() == "contained" {
waitnetwork.Add(1) waitnetwork.Add(1)
go runClient(&waitnetwork) go runClient(&waitnetwork)
} }
if servercfg.IsDNSMode() { if servercfg.IsDNSMode() {
err := controller.SetDNS() err := dnslogic.SetDNS()
if err != nil { if err != nil {
log.Println("error occurred initializing DNS:", err) log.Println("error occurred initializing DNS:", err)
} }
@@ -115,7 +115,6 @@ func startControllers() {
func runClient(wg *sync.WaitGroup) { func runClient(wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
log.Println("CLIENT_MODE running as contained")
go func() { go func() {
for { for {
if err := serverctl.HandleContainedClient(); err != nil { if err := serverctl.HandleContainedClient(); err != nil {

View File

@@ -7,7 +7,7 @@ import (
"net" "net"
"strings" "strings"
"time" "time"
"bytes"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
@@ -76,6 +76,18 @@ type Node struct {
MTU int32 `json:"mtu" bson:"mtu" yaml:"mtu"` MTU int32 `json:"mtu" bson:"mtu" yaml:"mtu"`
} }
type NodesArray []Node
func (a NodesArray) Len() int { return len(a) }
func (a NodesArray) Less(i, j int) bool { return isLess(a[i].Address, a[j].Address) }
func (a NodesArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func isLess(ipA string, ipB string) bool {
ipNetA := net.ParseIP(ipA)
ipNetB := net.ParseIP(ipB)
return bytes.Compare(ipNetA, ipNetB) < 0
}
func (node *Node) SetDefaultMTU() { func (node *Node) SetDefaultMTU() {
if node.MTU == 0 { if node.MTU == 0 {
node.MTU = 1280 node.MTU = 1280
@@ -263,9 +275,12 @@ func (node *Node) SetDefaults() {
} }
} }
// == Parent Network settings == // == Parent Network settings ==
node.CheckInInterval = parentNetwork.DefaultCheckInInterval if node.IsDualStack == "" {
node.IsDualStack = parentNetwork.IsDualStack node.IsDualStack = parentNetwork.IsDualStack
}
if node.MTU == 0 {
node.MTU = parentNetwork.DefaultMTU node.MTU = parentNetwork.DefaultMTU
}
// == node defaults if not set by parent == // == node defaults if not set by parent ==
node.SetIPForwardingDefault() node.SetIPForwardingDefault()
node.SetDNSOnDefault() node.SetDNSOnDefault()
@@ -535,9 +550,9 @@ func GetAllNodes() ([]Node, error) {
return nodes, nil return nodes, nil
} }
func GetID(macaddress string, network string) (string, error) { func (node *Node) GetID() (string, error) {
if macaddress == "" || network == "" { if node.MacAddress == "" || node.Network == "" {
return "", errors.New("unable to get record key") return "", errors.New("unable to get record key")
} }
return macaddress + "###" + network, nil return node.MacAddress + "###" + node.Network, nil
} }

View File

@@ -58,7 +58,7 @@ func Join(cfg config.ClientConfig, privateKey string) error {
func getWindowsInterval() int { func getWindowsInterval() int {
interval := 15 interval := 15
networks, err := functions.GetNetworks() networks, err := ncutils.GetSystemNetworks()
if err != nil { if err != nil {
return interval return interval
} }
@@ -90,12 +90,13 @@ func RunUserspaceDaemon() {
func CheckIn(cfg config.ClientConfig) error { func CheckIn(cfg config.ClientConfig) error {
var err error var err error
if cfg.Network == "" { if cfg.Network == "" {
ncutils.PrintLog("required, '-n', exiting", 0) ncutils.PrintLog("required, '-n', exiting", 0)
os.Exit(1) os.Exit(1)
} else if cfg.Network == "all" { } else if cfg.Network == "all" {
ncutils.PrintLog("running checkin for all networks", 1) ncutils.PrintLog("running checkin for all networks", 1)
networks, err := functions.GetNetworks() networks, err := ncutils.GetSystemNetworks()
if err != nil { if err != nil {
ncutils.PrintLog("error retrieving networks, exiting", 1) ncutils.PrintLog("error retrieving networks, exiting", 1)
return err return err
@@ -138,7 +139,7 @@ func Push(cfg config.ClientConfig) error {
var err error var err error
if cfg.Network == "all" || ncutils.IsWindows() { if cfg.Network == "all" || ncutils.IsWindows() {
ncutils.PrintLog("pushing config to server for all networks.", 0) ncutils.PrintLog("pushing config to server for all networks.", 0)
networks, err := functions.GetNetworks() networks, err := ncutils.GetSystemNetworks()
if err != nil { if err != nil {
ncutils.PrintLog("error retrieving networks, exiting.", 0) ncutils.PrintLog("error retrieving networks, exiting.", 0)
return err return err
@@ -164,7 +165,7 @@ func Pull(cfg config.ClientConfig) error {
var err error var err error
if cfg.Network == "all" { if cfg.Network == "all" {
ncutils.PrintLog("No network selected. Running Pull for all networks.", 0) ncutils.PrintLog("No network selected. Running Pull for all networks.", 0)
networks, err := functions.GetNetworks() networks, err := ncutils.GetSystemNetworks()
if err != nil { if err != nil {
ncutils.PrintLog("Error retrieving networks. Exiting.", 1) ncutils.PrintLog("Error retrieving networks. Exiting.", 1)
return err return err

View File

@@ -1,12 +1,15 @@
package functions package functions
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"os" "os"
"runtime" "runtime"
"strings" "strings"
nodepb "github.com/gravitl/netmaker/grpc" nodepb "github.com/gravitl/netmaker/grpc"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/auth" "github.com/gravitl/netmaker/netclient/auth"
"github.com/gravitl/netmaker/netclient/config" "github.com/gravitl/netmaker/netclient/config"
@@ -136,7 +139,6 @@ func CheckConfig(cliconf config.ClientConfig) error {
if newNode.IsPending == "yes" { if newNode.IsPending == "yes" {
return errors.New("node is pending") return errors.New("node is pending")
} }
actionCompleted := checkNodeActions(newNode, network, servercfg, &currentNode, cfg) actionCompleted := checkNodeActions(newNode, network, servercfg, &currentNode, cfg)
if actionCompleted == models.NODE_DELETE { if actionCompleted == models.NODE_DELETE {
return errors.New("node has been removed") return errors.New("node has been removed")
@@ -164,6 +166,9 @@ func Pull(network string, manual bool) (*models.Node, error) {
return nil, err return nil, err
} }
} }
var resNode models.Node // just need to fill this with either server calls or client calls
var ctx context.Context
if cfg.Node.IsServer != "yes" {
conn, err := grpc.Dial(cfg.Server.GRPCAddress, conn, err := grpc.Dial(cfg.Server.GRPCAddress,
ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL)) ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
if err != nil { if err != nil {
@@ -187,10 +192,15 @@ func Pull(network string, manual bool) (*models.Node, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
var resNode models.Node
if err = json.Unmarshal([]byte(readres.Data), &resNode); err != nil { if err = json.Unmarshal([]byte(readres.Data), &resNode); err != nil {
return nil, err return nil, err
} }
} else { // handle server side read
resNode, err = logic.GetNode(node.MacAddress, node.Network)
if err != nil && !ncutils.IsEmptyRecord(err) {
return nil, err
}
}
// ensure that the OS never changes // ensure that the OS never changes
resNode.OS = runtime.GOOS resNode.OS = runtime.GOOS
if resNode.PullChanges == "yes" || manual { if resNode.PullChanges == "yes" || manual {
@@ -211,6 +221,7 @@ func Pull(network string, manual bool) (*models.Node, error) {
if err != nil { if err != nil {
return &resNode, err return &resNode, err
} }
if resNode.IsServer != "yes" {
req := &nodepb.Object{ req := &nodepb.Object{
Data: string(nodeData), Data: string(nodeData),
Type: nodepb.NODE_TYPE, Type: nodepb.NODE_TYPE,
@@ -220,6 +231,11 @@ func Pull(network string, manual bool) (*models.Node, error) {
if err != nil { if err != nil {
return &resNode, err return &resNode, err
} }
} else { // handle server side update
if err = resNode.Update(&resNode); err != nil {
return &resNode, err
}
}
} else { } else {
if err = wireguard.SetWGConfig(network, true); err != nil { if err = wireguard.SetWGConfig(network, true); err != nil {
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
@@ -244,8 +260,10 @@ func Push(network string) error {
postnode := cfg.Node postnode := cfg.Node
// always set the OS on client // always set the OS on client
postnode.OS = runtime.GOOS postnode.OS = runtime.GOOS
var header metadata.MD postnode.SetLastCheckIn()
if postnode.IsServer != "yes" { // handle client side
var header metadata.MD
var wcclient nodepb.NodeServiceClient var wcclient nodepb.NodeServiceClient
conn, err := grpc.Dial(cfg.Server.GRPCAddress, conn, err := grpc.Dial(cfg.Server.GRPCAddress,
ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL)) ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
@@ -274,7 +292,6 @@ func Push(network string) error {
postnode.PublicKey = privateKeyWG.PublicKey().String() postnode.PublicKey = privateKeyWG.PublicKey().String()
} }
} }
postnode.SetLastCheckIn()
nodeData, err := json.Marshal(&postnode) nodeData, err := json.Marshal(&postnode)
if err != nil { if err != nil {
return err return err
@@ -293,6 +310,11 @@ func Push(network string) error {
if err != nil { if err != nil {
return err return err
} }
} else {
if err = postnode.Update(&postnode); err != nil {
return err
}
}
err = config.ModConfig(&postnode) err = config.ModConfig(&postnode)
return err return err
} }

View File

@@ -5,7 +5,6 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"net" "net"
"os" "os"
@@ -13,6 +12,7 @@ import (
"strings" "strings"
nodepb "github.com/gravitl/netmaker/grpc" nodepb "github.com/gravitl/netmaker/grpc"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/auth" "github.com/gravitl/netmaker/netclient/auth"
"github.com/gravitl/netmaker/netclient/config" "github.com/gravitl/netmaker/netclient/config"
@@ -138,7 +138,7 @@ func GetNode(network string) models.Node {
} }
func Uninstall() error { func Uninstall() error {
networks, err := GetNetworks() networks, err := ncutils.GetSystemNetworks()
if err != nil { if err != nil {
ncutils.PrintLog("unable to retrieve networks: "+err.Error(), 1) ncutils.PrintLog("unable to retrieve networks: "+err.Error(), 1)
ncutils.PrintLog("continuing uninstall without leaving networks", 1) ncutils.PrintLog("continuing uninstall without leaving networks", 1)
@@ -163,7 +163,6 @@ func Uninstall() error {
} }
func LeaveNetwork(network string) error { func LeaveNetwork(network string) error {
//need to implement checkin on server side
cfg, err := config.ReadConfig(network) cfg, err := config.ReadConfig(network)
if err != nil { if err != nil {
return err return err
@@ -171,19 +170,20 @@ func LeaveNetwork(network string) error {
servercfg := cfg.Server servercfg := cfg.Server
node := cfg.Node node := cfg.Node
if node.IsServer != "yes" {
var wcclient nodepb.NodeServiceClient var wcclient nodepb.NodeServiceClient
conn, err := grpc.Dial(cfg.Server.GRPCAddress, conn, err := grpc.Dial(cfg.Server.GRPCAddress,
ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL)) ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
if err != nil { if err != nil {
log.Printf("Unable to establish client connection to "+servercfg.GRPCAddress+": %v", err) log.Printf("Unable to establish client connection to "+servercfg.GRPCAddress+": %v", err)
} else { }
defer conn.Close() defer conn.Close()
wcclient = nodepb.NewNodeServiceClient(conn) wcclient = nodepb.NewNodeServiceClient(conn)
ctx, err := auth.SetJWT(wcclient, network) ctx, err := auth.SetJWT(wcclient, network)
if err != nil { if err != nil {
log.Printf("Failed to authenticate: %v", err) log.Printf("Failed to authenticate: %v", err)
} else { } else { // handle client side
node.SetID() node.SetID()
var header metadata.MD var header metadata.MD
_, err = wcclient.DeleteNode( _, err = wcclient.DeleteNode(
@@ -200,6 +200,13 @@ func LeaveNetwork(network string) error {
ncutils.PrintLog("removed machine from "+node.Network+" network on remote server", 1) ncutils.PrintLog("removed machine from "+node.Network+" network on remote server", 1)
} }
} }
} else { // handle server side
node.SetID()
if err = logic.DeleteNode(node.ID, true); err != nil {
ncutils.PrintLog("error removing server on network "+node.Network, 1)
} else {
ncutils.PrintLog("removed netmaker server instance on "+node.Network, 1)
}
} }
return RemoveLocalInstance(cfg, network) return RemoveLocalInstance(cfg, network)
} }
@@ -244,7 +251,7 @@ func DeleteInterface(ifacename string, postdown string) error {
func List() error { func List() error {
networks, err := GetNetworks() networks, err := ncutils.GetSystemNetworks()
if err != nil { if err != nil {
return err return err
} }
@@ -267,34 +274,6 @@ func List() error {
return nil return nil
} }
func GetNetworks() ([]string, error) {
var networks []string
files, err := ioutil.ReadDir(ncutils.GetNetclientPathSpecific())
if err != nil {
return networks, err
}
for _, f := range files {
if strings.Contains(f.Name(), "netconfig-") {
networkname := stringAfter(f.Name(), "netconfig-")
networks = append(networks, networkname)
}
}
return networks, err
}
func stringAfter(original string, substring string) string {
position := strings.LastIndex(original, substring)
if position == -1 {
return ""
}
adjustedPosition := position + len(substring)
if adjustedPosition >= len(original) {
return ""
}
return original[adjustedPosition:len(original)]
}
func WipeLocal(network string) error { func WipeLocal(network string) error {
cfg, err := config.ReadConfig(network) cfg, err := config.ReadConfig(network)
if err != nil { if err != nil {
@@ -302,7 +281,6 @@ func WipeLocal(network string) error {
} }
nodecfg := cfg.Node nodecfg := cfg.Node
ifacename := nodecfg.Interface ifacename := nodecfg.Interface
if ifacename != "" { if ifacename != "" {
if !ncutils.IsKernel() { if !ncutils.IsKernel() {
if err = wireguard.RemoveConf(ifacename, true); err == nil { if err = wireguard.RemoveConf(ifacename, true); err == nil {

View File

@@ -8,6 +8,7 @@ import (
"log" "log"
nodepb "github.com/gravitl/netmaker/grpc" nodepb "github.com/gravitl/netmaker/grpc"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/auth" "github.com/gravitl/netmaker/netclient/auth"
"github.com/gravitl/netmaker/netclient/config" "github.com/gravitl/netmaker/netclient/config"
@@ -28,7 +29,6 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
return err return err
} }
ncutils.Log("joining " + cfg.Network + " at " + cfg.Server.GRPCAddress)
err := config.Write(&cfg, cfg.Network) err := config.Write(&cfg, cfg.Network)
if err != nil { if err != nil {
return err return err
@@ -53,7 +53,6 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
cfg.Node.Endpoint = cfg.Node.LocalAddress cfg.Node.Endpoint = cfg.Node.LocalAddress
} else { } else {
cfg.Node.Endpoint, err = ncutils.GetPublicIP() cfg.Node.Endpoint, err = ncutils.GetPublicIP()
} }
if err != nil || cfg.Node.Endpoint == "" { if err != nil || cfg.Node.Endpoint == "" {
ncutils.Log("Error setting cfg.Node.Endpoint.") ncutils.Log("Error setting cfg.Node.Endpoint.")
@@ -82,17 +81,8 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
} }
} }
var wcclient nodepb.NodeServiceClient // differentiate between client/server here
var node models.Node // fill this node with appropriate calls
conn, err := grpc.Dial(cfg.Server.GRPCAddress,
ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
if err != nil {
log.Fatalf("Unable to establish client connection to "+cfg.Server.GRPCAddress+": %v", err)
}
defer conn.Close()
wcclient = nodepb.NewNodeServiceClient(conn)
postnode := &models.Node{ postnode := &models.Node{
Password: cfg.Node.Password, Password: cfg.Node.Password,
MacAddress: cfg.Node.MacAddress, MacAddress: cfg.Node.MacAddress,
@@ -112,6 +102,19 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
UDPHolePunch: cfg.Node.UDPHolePunch, UDPHolePunch: cfg.Node.UDPHolePunch,
} }
if cfg.Node.IsServer != "yes" {
ncutils.Log("joining " + cfg.Network + " at " + cfg.Server.GRPCAddress)
var wcclient nodepb.NodeServiceClient
conn, err := grpc.Dial(cfg.Server.GRPCAddress,
ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
if err != nil {
log.Fatalf("Unable to establish client connection to "+cfg.Server.GRPCAddress+": %v", err)
}
defer conn.Close()
wcclient = nodepb.NewNodeServiceClient(conn)
if err = config.ModConfig(postnode); err != nil { if err = config.ModConfig(postnode); err != nil {
return err return err
} }
@@ -133,10 +136,23 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
ncutils.PrintLog("node created on remote server...updating configs", 1) ncutils.PrintLog("node created on remote server...updating configs", 1)
nodeData := res.Data nodeData := res.Data
var node models.Node
if err = json.Unmarshal([]byte(nodeData), &node); err != nil { if err = json.Unmarshal([]byte(nodeData), &node); err != nil {
return err return err
} }
} else { // handle server side node creation
ncutils.Log("adding a server instance on network " + postnode.Network)
if err = config.ModConfig(postnode); err != nil {
return err
}
node, err = logic.CreateNode(*postnode, cfg.Network)
if err != nil {
return err
}
err = logic.SetNetworkNodesLastModified(node.Network)
if err != nil {
return err
}
}
// get free port based on returned default listen port // get free port based on returned default listen port
node.ListenPort, err = ncutils.GetFreePort(node.ListenPort) node.ListenPort, err = ncutils.GetFreePort(node.ListenPort)
@@ -177,9 +193,8 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
} }
} }
ncutils.Log("retrieving remote peers") ncutils.Log("retrieving peers")
peers, hasGateway, gateways, err := server.GetPeers(node.MacAddress, cfg.Network, cfg.Server.GRPCAddress, node.IsDualStack == "yes", node.IsIngressGateway == "yes") peers, hasGateway, gateways, err := server.GetPeers(node.MacAddress, cfg.Network, cfg.Server.GRPCAddress, node.IsDualStack == "yes", node.IsIngressGateway == "yes", node.IsServer == "yes")
if err != nil && !ncutils.IsEmptyRecord(err) { if err != nil && !ncutils.IsEmptyRecord(err) {
ncutils.Log("failed to retrieve peers") ncutils.Log("failed to retrieve peers")
return err return err

View File

@@ -24,7 +24,7 @@ func main() {
app := cli.NewApp() app := cli.NewApp()
app.Name = "Netclient CLI" app.Name = "Netclient CLI"
app.Usage = "Netmaker's netclient agent and CLI. Used to perform interactions with Netmaker server and set local WireGuard config." app.Usage = "Netmaker's netclient agent and CLI. Used to perform interactions with Netmaker server and set local WireGuard config."
app.Version = "v0.8.1" app.Version = "v0.8.3"
cliFlags := []cli.Flag{ cliFlags := []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{

View File

@@ -368,3 +368,31 @@ func PrintLog(message string, loglevel int) {
log.Println("[netclient]", message) log.Println("[netclient]", message)
} }
} }
func GetSystemNetworks() ([]string, error) {
var networks []string
files, err := ioutil.ReadDir(GetNetclientPathSpecific())
if err != nil {
return networks, err
}
for _, f := range files {
if strings.Contains(f.Name(), "netconfig-") {
networkname := stringAfter(f.Name(), "netconfig-")
networks = append(networks, networkname)
}
}
return networks, err
}
func stringAfter(original string, substring string) string {
position := strings.LastIndex(original, substring)
if position == -1 {
return ""
}
adjustedPosition := position + len(substring)
if adjustedPosition >= len(original) {
return ""
}
return original[adjustedPosition:len(original)]
}

View File

@@ -9,6 +9,7 @@ import (
"time" "time"
nodepb "github.com/gravitl/netmaker/grpc" nodepb "github.com/gravitl/netmaker/grpc"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/auth" "github.com/gravitl/netmaker/netclient/auth"
"github.com/gravitl/netmaker/netclient/config" "github.com/gravitl/netmaker/netclient/config"
@@ -40,6 +41,7 @@ func CheckIn(network string) (*models.Node, error) {
return nil, err return nil, err
} }
node := cfg.Node node := cfg.Node
if cfg.Node.IsServer != "yes" {
wcclient, err := getGrpcClient(cfg) wcclient, err := getGrpcClient(cfg)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -65,6 +67,7 @@ func CheckIn(network string) (*models.Node, error) {
if err = json.Unmarshal([]byte(response.GetData()), &node); err != nil { if err = json.Unmarshal([]byte(response.GetData()), &node); err != nil {
return nil, err return nil, err
} }
}
return &node, err return &node, err
} }
@@ -116,11 +119,11 @@ func RemoveNetwork(network string) error {
return err return err
} }
*/ */
func GetPeers(macaddress string, network string, server string, dualstack bool, isIngressGateway bool) ([]wgtypes.PeerConfig, bool, []string, error) {
func GetPeers(macaddress string, network string, server string, dualstack bool, isIngressGateway bool, isServer bool) ([]wgtypes.PeerConfig, bool, []string, error) {
hasGateway := false hasGateway := false
var gateways []string var gateways []string
var peers []wgtypes.PeerConfig var peers []wgtypes.PeerConfig
var wcclient nodepb.NodeServiceClient
cfg, err := config.ReadConfig(network) cfg, err := config.ReadConfig(network)
if err != nil { if err != nil {
log.Fatalf("Issue retrieving config for network: "+network+". Please investigate: %v", err) log.Fatalf("Issue retrieving config for network: "+network+". Please investigate: %v", err)
@@ -132,7 +135,9 @@ func GetPeers(macaddress string, network string, server string, dualstack bool,
if err != nil { if err != nil {
log.Fatalf("Issue with format of keepalive value. Please update netconfig: %v", err) log.Fatalf("Issue with format of keepalive value. Please update netconfig: %v", err)
} }
var nodes []models.Node // fill this either from server or client
if !isServer { // set peers client side
var wcclient nodepb.NodeServiceClient
conn, err := grpc.Dial(cfg.Server.GRPCAddress, conn, err := grpc.Dial(cfg.Server.GRPCAddress,
ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL)) ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
@@ -161,11 +166,17 @@ func GetPeers(macaddress string, network string, server string, dualstack bool,
log.Println(err) log.Println(err)
return nil, hasGateway, gateways, err return nil, hasGateway, gateways, err
} }
var nodes []models.Node
if err := json.Unmarshal([]byte(response.GetData()), &nodes); err != nil { if err := json.Unmarshal([]byte(response.GetData()), &nodes); err != nil {
log.Println("Error unmarshaling data for peers") log.Println("Error unmarshaling data for peers")
return nil, hasGateway, gateways, err return nil, hasGateway, gateways, err
} }
} else { // set peers serverside
nodes, err = logic.GetPeers(nodecfg)
if err != nil {
return nil, hasGateway, gateways, err
}
}
for _, node := range nodes { for _, node := range nodes {
pubkey, err := wgtypes.ParseKey(node.PublicKey) pubkey, err := wgtypes.ParseKey(node.PublicKey)
if err != nil { if err != nil {
@@ -240,7 +251,7 @@ func GetPeers(macaddress string, network string, server string, dualstack bool,
} }
allowedips = append(allowedips, addr6) allowedips = append(allowedips, addr6)
} }
if nodecfg.IsServer == "yes" { if nodecfg.IsServer == "yes" && !(node.IsServer == "yes"){
peer = wgtypes.PeerConfig{ peer = wgtypes.PeerConfig{
PublicKey: pubkey, PublicKey: pubkey,
PersistentKeepaliveInterval: &keepaliveserver, PersistentKeepaliveInterval: &keepaliveserver,
@@ -283,12 +294,15 @@ func GetPeers(macaddress string, network string, server string, dualstack bool,
} }
func GetExtPeers(macaddress string, network string, server string, dualstack bool) ([]wgtypes.PeerConfig, error) { func GetExtPeers(macaddress string, network string, server string, dualstack bool) ([]wgtypes.PeerConfig, error) {
var peers []wgtypes.PeerConfig var peers []wgtypes.PeerConfig
var wcclient nodepb.NodeServiceClient
cfg, err := config.ReadConfig(network) cfg, err := config.ReadConfig(network)
if err != nil { if err != nil {
log.Fatalf("Issue retrieving config for network: "+network+". Please investigate: %v", err) log.Fatalf("Issue retrieving config for network: "+network+". Please investigate: %v", err)
} }
nodecfg := cfg.Node nodecfg := cfg.Node
var extPeers []models.Node
if nodecfg.IsServer != "yes" { // fill extPeers with client side logic
var wcclient nodepb.NodeServiceClient
conn, err := grpc.Dial(cfg.Server.GRPCAddress, conn, err := grpc.Dial(cfg.Server.GRPCAddress,
ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL)) ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
@@ -317,10 +331,26 @@ func GetExtPeers(macaddress string, network string, server string, dualstack boo
log.Println(err) log.Println(err)
return nil, err return nil, err
} }
var extPeers []models.Node
if err = json.Unmarshal([]byte(responseObject.Data), &extPeers); err != nil { if err = json.Unmarshal([]byte(responseObject.Data), &extPeers); err != nil {
return nil, err return nil, err
} }
} else { // fill extPeers with server side logic
tempPeers, err := logic.GetExtPeersList(nodecfg.MacAddress, nodecfg.Network)
if err != nil {
return nil, err
}
for i := 0; i < len(tempPeers); i++ {
extPeers = append(extPeers, models.Node{
Address: tempPeers[i].Address,
Address6: tempPeers[i].Address6,
Endpoint: tempPeers[i].Endpoint,
PublicKey: tempPeers[i].PublicKey,
PersistentKeepalive: tempPeers[i].KeepAlive,
ListenPort: tempPeers[i].ListenPort,
LocalAddress: tempPeers[i].LocalAddress,
})
}
}
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 {

View File

@@ -267,7 +267,7 @@ func SetWGConfig(network string, peerupdate bool) error {
servercfg := cfg.Server servercfg := cfg.Server
nodecfg := cfg.Node nodecfg := cfg.Node
peers, hasGateway, gateways, err := server.GetPeers(nodecfg.MacAddress, nodecfg.Network, servercfg.GRPCAddress, nodecfg.IsDualStack == "yes", nodecfg.IsIngressGateway == "yes") peers, hasGateway, gateways, err := server.GetPeers(nodecfg.MacAddress, nodecfg.Network, servercfg.GRPCAddress, nodecfg.IsDualStack == "yes", nodecfg.IsIngressGateway == "yes", nodecfg.IsServer == "yes")
if err != nil { if err != nil {
return err return err
} }

37
relay/relay.go Normal file
View File

@@ -0,0 +1,37 @@
package relay
import (
"encoding/json"
"errors"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/models"
)
func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) {
collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
var relay models.Node
if err != nil {
if database.IsEmptyRecord(err) {
return relay, nil
}
functions.PrintUserLog("", err.Error(), 2)
return relay, err
}
for _, value := range collection {
err := json.Unmarshal([]byte(value), &relay)
if err != nil {
functions.PrintUserLog("", err.Error(), 2)
continue
}
if relay.IsRelay == "yes" {
for _, addr := range relay.RelayAddrs {
if addr == relayedNodeAddr {
return relay, nil
}
}
}
}
return relay, errors.New("could not find relay for node " + relayedNodeAddr)
}

View File

@@ -74,7 +74,7 @@ func GetAPIConnString() string {
return conn return conn
} }
func GetVersion() string { func GetVersion() string {
version := "0.8.1" version := "0.8.3"
if config.Config.Server.Version != "" { if config.Config.Server.Version != "" {
version = config.Config.Server.Version version = config.Config.Server.Version
} }

View File

@@ -6,11 +6,11 @@ import (
"io" "io"
"log" "log"
"os" "os"
"os/exec"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
nccommand "github.com/gravitl/netmaker/netclient/command"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/ncutils" "github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/servercfg"
) )
@@ -91,18 +91,8 @@ func copy(src, dst string) (int64, error) {
} }
func RemoveNetwork(network string) (bool, error) { func RemoveNetwork(network string) (bool, error) {
netclientPath := ncutils.GetNetclientPath() err := nccommand.Leave(config.ClientConfig{Network: network})
_, err := os.Stat(netclientPath + "/netclient")
if err != nil {
log.Println("could not find " + netclientPath + "/netclient")
return false, err
}
_, err = ncutils.RunCmd(netclientPath+"/netclient leave -n "+network, true)
if err == nil {
log.Println("Server removed from network " + network)
}
return true, err return true, err
} }
func InitServerNetclient() error { func InitServerNetclient() error {
@@ -114,82 +104,89 @@ func InitServerNetclient() error {
log.Println("could not find or create", netclientDir) log.Println("could not find or create", netclientDir)
return err return err
} }
_, err = os.Stat(netclientDir + "/netclient")
if os.IsNotExist(err) {
err = InstallNetclient()
if err != nil {
return err
}
}
err = os.Chmod(netclientDir+"/netclient", 0755)
if err != nil {
log.Println("could not change netclient binary permissions")
return err
}
return nil return nil
} }
func HandleContainedClient() error { func HandleContainedClient() error {
servernets, err := models.GetNetworks()
if err != nil && !database.IsEmptyRecord(err) {
return err
}
if len(servernets) > 0 {
if err != nil {
return err
}
log.SetFlags(log.Flags() &^ (log.Llongfile | log.Lshortfile)) log.SetFlags(log.Flags() &^ (log.Llongfile | log.Lshortfile))
err := SyncNetworks(servernets)
netclientPath := ncutils.GetNetclientPath() if err != nil && servercfg.GetVerbose() >= 1 {
checkinCMD := exec.Command(netclientPath+"/netclient", "checkin", "-n", "all") log.Printf("[server netclient] error syncing networks %s \n", err)
if servercfg.GetVerbose() >= 2 {
checkinCMD.Stdout = os.Stdout
}
checkinCMD.Stderr = os.Stderr
err := checkinCMD.Start()
if err != nil {
if servercfg.GetVerbose() >= 2 {
log.Println(err)
}
}
err = checkinCMD.Wait()
if err != nil {
if servercfg.GetVerbose() >= 2 {
log.Println(err)
} }
err = nccommand.CheckIn(config.ClientConfig{Network: "all"})
if err != nil && servercfg.GetVerbose() >= 1 {
log.Printf("[server netclient] error occurred %s \n", err)
} }
if servercfg.GetVerbose() >= 3 { if servercfg.GetVerbose() >= 3 {
log.Println("[server netclient]", "completed a checkin call") log.Println("[server netclient]", "completed a checkin call")
} }
}
return nil
}
func SyncNetworks(servernets []models.Network) error {
localnets, err := ncutils.GetSystemNetworks()
if err != nil {
return err
}
// check networks to join
for _, servernet := range servernets {
exists := false
for _, localnet := range localnets {
if servernet.NetID == localnet {
exists = true
}
}
if !exists {
success, err := AddNetwork(servernet.NetID)
if err != nil || !success {
if err == nil {
err = errors.New("network add failed for " + servernet.NetID)
}
log.Printf("[server] error adding network %s during sync %s \n", servernet.NetID, err)
}
}
}
// check networks to leave
for _, localnet := range localnets {
exists := false
for _, servernet := range servernets {
if servernet.NetID == localnet {
exists = true
}
}
if !exists {
success, err := RemoveNetwork(localnet)
if err != nil || !success {
if err == nil {
err = errors.New("network delete failed for " + localnet)
}
log.Printf("[server] error removing network %s during sync %s \n", localnet, err)
}
}
}
return nil return nil
} }
func AddNetwork(network string) (bool, error) { func AddNetwork(network string) (bool, error) {
pubip, err := servercfg.GetPublicIP() err := nccommand.Join(config.ClientConfig{
if err != nil { Network: network,
log.Println("could not get public IP.") Daemon: "off",
return false, err Node: models.Node{
} Network: network,
netclientPath := ncutils.GetNetclientPath() IsServer: "yes",
Name: models.NODE_SERVER_NAME,
token, err := functions.CreateServerToken(network) },
if err != nil { }, "")
log.Println("could not create server token for " + network)
return false, err
}
functions.PrintUserLog(models.NODE_SERVER_NAME, "executing network join: "+netclientPath+"netclient "+"join "+"-t "+token+" -name "+models.NODE_SERVER_NAME+" -endpoint "+pubip, 0)
var joinCMD *exec.Cmd
if servercfg.IsClientMode() == "contained" {
joinCMD = exec.Command(netclientPath+"/netclient", "join", "-t", token, "-name", models.NODE_SERVER_NAME, "-endpoint", pubip, "-daemon", "off", "-dnson", "no")
} else {
joinCMD = exec.Command(netclientPath+"/netclient", "join", "-t", token, "-name", models.NODE_SERVER_NAME, "-endpoint", pubip)
}
joinCMD.Stdout = os.Stdout
joinCMD.Stderr = os.Stderr
err = joinCMD.Start()
if err != nil {
log.Println(err)
}
log.Println("Waiting for join command to finish...")
err = joinCMD.Wait()
if err != nil {
log.Printf("Command finished with error: %v", err)
return false, err
}
log.Println("Server added to network " + network) log.Println("Server added to network " + network)
return true, err return true, err
} }

View File

@@ -1,30 +0,0 @@
package serverctl
import (
"github.com/gravitl/netmaker/functions"
"golang.zx2c4.com/wireguard/wgctrl"
)
func GetPeers(networkName string) (map[string]string, error) {
peers := make(map[string]string)
network, err := functions.GetParentNetwork(networkName)
if err != nil {
return peers, err
}
iface := network.DefaultInterface
client, err := wgctrl.New()
if err != nil {
return peers, err
}
device, err := client.Device(iface)
if err != nil {
return nil, err
}
for _, peer := range device.Peers {
if functions.IsBase64(peer.PublicKey.String()) && peer.Endpoint != nil && functions.CheckEndpoint(peer.Endpoint.String()) {
peers[peer.PublicKey.String()] = peer.Endpoint.String()
}
}
return peers, nil
}

View File

@@ -191,7 +191,7 @@ func TestAuthenticateUser(t *testing.T) {
password: "xxxxxxx", password: "xxxxxxx",
code: http.StatusBadRequest, code: http.StatusBadRequest,
tokenExpected: false, tokenExpected: false,
errMessage: "Wrong Password", errMessage: "Incorrect Credentials",
}, },
AuthorizeTestCase{ AuthorizeTestCase{
testname: "Valid User", testname: "Valid User",