Merge pull request #823 from gravitl/v0.11.0

V0.11.0
This commit is contained in:
Alex Feiszli 2022-02-21 22:43:13 -05:00 committed by GitHub
commit db4dcf4f54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
76 changed files with 2250 additions and 2183 deletions

View file

@ -47,6 +47,7 @@ jobs:
env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=7 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm7/netclient main.go
env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-freebsd-arm64/netclient main.go
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin/netclient main.go
env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="-X 'main.version=${NETMAKER_VERSION}'" -o build/netclient-darwin-arm64/netclient main.go
- name: Upload netmaker x86 to Release
uses: svenstaro/upload-release-action@v2
@ -177,3 +178,13 @@ jobs:
overwrite: true
prerelease: true
asset_name: netclient-darwin
- name: Upload darwin-arm64 to Release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: netclient/build/netclient-darwin-arm64/netclient
tag: ${{ env.NETMAKER_VERSION }}
overwrite: true
prerelease: true
asset_name: netclient-darwin-arm64

View file

@ -38,18 +38,51 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: build client
run: |
cd netclient
go build -ldflags="-X 'main.version=testing'" .
curl -H 'Authorization: Bearer ${{ secrets.NUSAK_MASTERKEY }}' \
-H 'Content-Type: multipart/form-data' --form upload='./netclient' \
-X POST https://dashboard.nusak.ca/api/file/netclient
#deploy:
#runs-on: ubuntu-latest
#steps:
#- name: Deploy Testing Server and Client(s)
# run: |
# curl -X POST https://api.github.com/mattkasun/terraform-test/dispatches \
# -H 'Accept: application/vnd.github.everest-preview+json' \
# -u ${{ secrets.ACCESS_TOKEN }}
go build -ldflags="-X 'main.version=testing'" -o build/netclient
- name: deploy
uses: mdallasanta/ssh-scp-deploy@v1.1.0
with:
local: ./netclient/build/netclient # Local file path - REQUIRED false - DEFAULT ./
remote: /var/www/files/testing/ # Remote file path - REQUIRED false - DEFAULT ~/
host: fileserver.clustercat.com # Remote server address - REQUIRED true
#port: ${{secrets.PORT}} # Remote server port - REQUIRED false - DEFAULT 22
user: root # Remote server user - REQUIRED true
#password: ${{secrets.PASSWORD}} # User password - REQUIRED at least one of "password" or "key"
key: ${{secrets.TESTING_SSH_KEY}} # Remote server private key - REQUIRED at least one of "password" or "key"
#pre_upload: echo "This will be executed before the upload!" # Command to run via ssh before scp upload - REQUIRED false
#post_upload: echo "This will be executed after the upload!" # Command to run via ssh after scp upload - REQUIRED false
#ssh_options: -o StrictHostKeyChecking=no # A set of ssh_option separated by -o - REQUIRED false - DEFAULT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
#scp_options: -v # Flags to use during scp - REQUIRED false - DEFAULT ''
netmaker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: build server
run:
go build -ldflags="-X 'main.version=testing'" -o build/netmaker
- name: deploy
uses: mdallasanta/ssh-scp-deploy@v1.1.0
with:
local: ./build/netmaker # Local file path - REQUIRED false - DEFAULT ./
remote: /var/www/files/testing/ # Remote file path - REQUIRED false - DEFAULT ~/
host: fileserver.clustercat.com # Remote server address - REQUIRED true
#port: ${{secrets.PORT}} # Remote server port - REQUIRED false - DEFAULT 22
user: root # Remote server user - REQUIRED true
#password: ${{secrets.PASSWORD}} # User password - REQUIRED at least one of "password" or "key"
key: ${{secrets.TESTING_SSH_KEY}} # Remote server private key - REQUIRED at least one of "password" or "key"
#pre_upload: echo "This will be executed before the upload!" # Command to run via ssh before scp upload - REQUIRED false
#post_upload: echo "This will be executed after the upload!" # Command to run via ssh after scp upload - REQUIRED false
#ssh_options: -o StrictHostKeyChecking=no # A set of ssh_option separated by -o - REQUIRED false - DEFAULT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
#scp_options: -v # Flags to use during scp - REQUIRED false - DEFAULT ''

View file

@ -8,7 +8,7 @@
<p align="center">
<a href="https://github.com/gravitl/netmaker/releases">
<img src="https://img.shields.io/badge/Version-0.10.0-informational?style=flat-square" />
<img src="https://img.shields.io/badge/Version-0.11.0-informational?style=flat-square" />
</a>
<a href="https://hub.docker.com/r/gravitl/netmaker/tags">
<img src="https://img.shields.io/docker/pulls/gravitl/netmaker" />

View file

@ -3,7 +3,7 @@ version: "3.4"
services:
netmaker:
container_name: netmaker
image: gravitl/netmaker:v0.10.0
image: gravitl/netmaker:v0.11.0
volumes:
- dnsconfig:/root/config/dnsconfig
- /usr/bin/wg:/usr/bin/wg
@ -46,7 +46,7 @@ services:
container_name: netmaker-ui
depends_on:
- netmaker
image: gravitl/netmaker-ui:v0.10.0
image: gravitl/netmaker-ui:v0.11.0
links:
- "netmaker:api"
ports:

View file

@ -3,7 +3,7 @@ version: "3.4"
services:
netmaker:
container_name: netmaker
image: gravitl/netmaker:v0.10.0
image: gravitl/netmaker:v0.11.0
volumes:
- dnsconfig:/root/config/dnsconfig
- /usr/bin/wg:/usr/bin/wg
@ -41,7 +41,7 @@ services:
container_name: netmaker-ui
depends_on:
- netmaker
image: gravitl/netmaker-ui:0.10.0
image: gravitl/netmaker-ui:0.11.0
links:
- "netmaker:api"
ports:
@ -87,4 +87,4 @@ volumes:
sqldata: {}
dnsconfig: {}
mosquitto_data: {}
mosquitto_logs: {}
mosquitto_logs: {}

View file

@ -3,7 +3,7 @@ version: "3.4"
services:
netmaker:
container_name: netmaker
image: gravitl/netmaker:v0.10.0
image: gravitl/netmaker:v0.11.0
volumes:
- dnsconfig:/root/config/dnsconfig
- /usr/bin/wg:/usr/bin/wg
@ -46,7 +46,7 @@ services:
container_name: netmaker-ui
depends_on:
- netmaker
image: gravitl/netmaker-ui:v0.10.0
image: gravitl/netmaker-ui:v0.11.0
links:
- "netmaker:api"
ports:

View file

@ -3,7 +3,7 @@ version: "3.4"
services:
netmaker:
container_name: netmaker
image: gravitl/netmaker:v0.10.0
image: gravitl/netmaker:v0.11.0
volumes:
- dnsconfig:/root/config/dnsconfig
- /usr/bin/wg:/usr/bin/wg
@ -46,7 +46,7 @@ services:
container_name: netmaker-ui
depends_on:
- netmaker
image: gravitl/netmaker-ui:v0.10.0
image: gravitl/netmaker-ui:v0.11.0
links:
- "netmaker:api"
ports:
@ -79,4 +79,4 @@ volumes:
caddy_conf: {}
sqldata: {}
mosquitto_data: {}
mosquitto_logs: {}
mosquitto_logs: {}

View file

@ -2,7 +2,7 @@ services:
netmaker: # The Primary Server for running Netmaker
privileged: true # Necessary to run sudo/root level commands on host system. Likely using this if running with host networking on.
container_name: netmaker
image: gravitl/netmaker:v0.10.0
image: gravitl/netmaker:v0.11.0
volumes: # Volume mounts necessary for CLIENT_MODE to control wireguard networking on host (except dnsconfig, which is where dns config files are stored for use by CoreDNS)
- dnsconfig:/root/config/dnsconfig # Netmaker writes Corefile to this location, which gets mounted by CoreDNS for DNS configuration.
- /usr/bin/wg:/usr/bin/wg
@ -45,7 +45,7 @@ services:
container_name: netmaker-ui
depends_on:
- netmaker
image: gravitl/netmaker-ui:v0.10.0
image: gravitl/netmaker-ui:v0.11.0
links:
- "netmaker:api"
ports:
@ -88,4 +88,4 @@ volumes:
sqldata: {} # storage for embedded sqlite
dnsconfig: {} # storage for coredns
mosquitto_data: {} # storage for mqtt data
mosquitto_logs: {} # storage for mqtt logs
mosquitto_logs: {} # storage for mqtt logs

View file

@ -3,7 +3,7 @@ version: "3.4"
services:
netmaker:
container_name: netmaker
image: gravitl/netmaker:v0.10.0
image: gravitl/netmaker:v0.11.0
volumes:
- dnsconfig:/root/config/dnsconfig
- /usr/bin/wg:/usr/bin/wg
@ -46,7 +46,7 @@ services:
container_name: netmaker-ui
depends_on:
- netmaker
image: gravitl/netmaker-ui:v0.10.0
image: gravitl/netmaker-ui:v0.11.0
links:
- "netmaker:api"
ports:

View file

@ -53,15 +53,12 @@ type ServerConfig struct {
MessageQueueBackend string `yaml:"messagequeuebackend"`
ClientMode string `yaml:"clientmode"`
DNSMode string `yaml:"dnsmode"`
SplitDNS string `yaml:"splitdns"`
DisableRemoteIPCheck string `yaml:"disableremoteipcheck"`
DisableDefaultNet string `yaml:"disabledefaultnet"`
GRPCSSL string `yaml:"grpcssl"`
Version string `yaml:"version"`
SQLConn string `yaml:"sqlconn"`
Platform string `yaml:"platform"`
Database string `yaml:"database"`
CheckinInterval string `yaml:"checkininterval"`
DefaultNodeLimit int32 `yaml:"defaultnodelimit"`
Verbosity int32 `yaml:"verbosity"`
ServerCheckinInterval int64 `yaml:"servercheckininterval"`
@ -77,6 +74,9 @@ type ServerConfig struct {
ManageIPTables string `yaml:"manageiptables"`
PortForwardServices string `yaml:"portforwardservices"`
HostNetwork string `yaml:"hostnetwork"`
CommsCIDR string `yaml:"commscidr"`
MQPort string `yaml:"mqport"`
CommsID string `yaml:"commsid"`
}
// SQLConfig - Generic SQL Config

View file

@ -125,7 +125,7 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
gwnode, err := logic.GetNodeByID(client.IngressGatewayID)
if err != nil {
logger.Log(1, fmt.Sprintf("%s %s %s", r.Header.Get("user"), "Could not retrieve Ingress Gateway Node", client.IngressGatewayID))
logger.Log(1, r.Header.Get("user"), "Could not retrieve Ingress Gateway Node", client.IngressGatewayID)
returnErrorResponse(w, r, formatError(err, "internal"))
return
}

View file

@ -14,9 +14,13 @@ import (
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/servercfg"
"github.com/gravitl/netmaker/serverctl"
)
// ALL_NETWORK_ACCESS - represents all networks
const ALL_NETWORK_ACCESS = "THIS_USER_HAS_ALL"
// NO_NETWORKS_PRESENT - represents no networks
const NO_NETWORKS_PRESENT = "THIS_USER_HAS_NONE"
func networkHandlers(r *mux.Router) {
@ -26,7 +30,7 @@ func networkHandlers(r *mux.Router) {
r.HandleFunc("/api/networks/{networkname}", securityCheck(false, http.HandlerFunc(updateNetwork))).Methods("PUT")
r.HandleFunc("/api/networks/{networkname}/nodelimit", securityCheck(true, http.HandlerFunc(updateNetworkNodeLimit))).Methods("PUT")
r.HandleFunc("/api/networks/{networkname}", securityCheck(true, http.HandlerFunc(deleteNetwork))).Methods("DELETE")
r.HandleFunc("/api/networks/{networkname}/keyupdate", securityCheck(false, http.HandlerFunc(keyUpdate))).Methods("POST")
r.HandleFunc("/api/networks/{networkname}/keyupdate", securityCheck(true, http.HandlerFunc(keyUpdate))).Methods("POST")
r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(false, http.HandlerFunc(createAccessKey))).Methods("POST")
r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(false, http.HandlerFunc(getAccessKeys))).Methods("GET")
r.HandleFunc("/api/networks/{networkname}/keys/{name}", securityCheck(false, http.HandlerFunc(deleteAccessKey))).Methods("DELETE")
@ -43,7 +47,7 @@ func getNetworks(w http.ResponseWriter, r *http.Request) {
return
}
allnetworks := []models.Network{}
err := errors.New("Networks Error")
var err error
if networksSlice[0] == ALL_NETWORK_ACCESS {
allnetworks, err = logic.GetNetworks()
if err != nil && !database.IsEmptyRecord(err) {
@ -64,6 +68,7 @@ func getNetworks(w http.ResponseWriter, r *http.Request) {
allnetworks[i] = net
}
}
logger.Log(2, r.Header.Get("user"), "fetched networks.")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(allnetworks)
@ -75,6 +80,10 @@ func getNetwork(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
netname := params["networkname"]
if isCommsEdit(w, r, netname) {
return
}
network, err := logic.GetNetwork(netname)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
@ -92,6 +101,10 @@ func keyUpdate(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
netname := params["networkname"]
if isCommsEdit(w, r, netname) {
return
}
network, err := logic.KeyUpdate(netname)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
@ -106,17 +119,13 @@ func keyUpdate(w http.ResponseWriter, r *http.Request) {
return
}
for _, node := range nodes {
fmt.Println("updating node ", node.Name, " for a key update")
if err := mq.NodeUpdate(&node); err != nil {
logger.Log(2, "failed key update ", node.Name)
logger.Log(2, "updating node ", node.Name, " for a key update")
if node.IsServer != "yes" {
if err = mq.NodeUpdate(&node); err != nil {
logger.Log(1, "failed to send update to node during a network wide key update", node.Name, node.ID, err.Error())
}
}
}
node, err := logic.GetNetworkServerLeader(netname)
if err != nil {
logger.Log(2, "failed to get server node")
return
}
runUpdates(&node, false)
}
// Update a network
@ -125,6 +134,10 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
var network models.Network
netname := params["networkname"]
if isCommsEdit(w, r, netname) {
return
}
network, err := logic.GetParentNetwork(netname)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
@ -181,7 +194,9 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
return
}
for _, node := range nodes {
runUpdates(&node, true)
if err = mq.NodeUpdate(&node); err != nil {
logger.Log(1, "failed to send update to node during a network wide update", node.Name, node.ID, err.Error())
}
}
}
@ -227,8 +242,11 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
network := params["networkname"]
err := logic.DeleteNetwork(network)
if isCommsEdit(w, r, network) {
return
}
err := logic.DeleteNetwork(network)
if err != nil {
errtype := "badrequest"
if strings.Contains(err.Error(), "Node check failed") {
@ -267,7 +285,7 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
if err != nil {
logic.DeleteNetwork(network.NetID)
if err == nil {
err = errors.New("Failed to add server to network " + network.DisplayName)
err = errors.New("Failed to add server to network " + network.NetID)
}
returnErrorResponse(w, r, formatError(err, "internal"))
return
@ -286,6 +304,9 @@ func createAccessKey(w http.ResponseWriter, r *http.Request) {
var accesskey models.AccessKey
//start here
netname := params["networkname"]
if isCommsEdit(w, r, netname) {
return
}
network, err := logic.GetParentNetwork(netname)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
@ -338,3 +359,21 @@ func deleteAccessKey(w http.ResponseWriter, r *http.Request) {
logger.Log(1, r.Header.Get("user"), "deleted access key", keyname, "on network,", netname)
w.WriteHeader(http.StatusOK)
}
func isCommsEdit(w http.ResponseWriter, r *http.Request, netname string) bool {
if netname == serverctl.COMMS_NETID {
returnErrorResponse(w, r, formatError(fmt.Errorf("cannot access comms network"), "internal"))
return true
}
return false
}
func filterCommsNetwork(networks []models.Network) []models.Network {
var filterdNets []models.Network
for i := range networks {
if networks[i].IsComms != "yes" && networks[i].NetID != servercfg.GetCommsID() {
filterdNets = append(filterdNets, networks[i])
}
}
return filterdNets
}

View file

@ -1,12 +1,13 @@
package controller
import (
"os"
"testing"
"time"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/serverctl"
"github.com/stretchr/testify/assert"
)
@ -23,7 +24,8 @@ func TestCreateNetwork(t *testing.T) {
var network models.Network
network.NetID = "skynet"
network.AddressRange = "10.0.0.1/24"
network.DisplayName = "mynetwork"
// if tests break - check here (removed displayname)
//network.DisplayName = "mynetwork"
err := logic.CreateNetwork(network)
assert.Nil(t, err)
@ -60,20 +62,6 @@ func TestDeleteNetwork(t *testing.T) {
})
}
func TestKeyUpdate(t *testing.T) {
t.Skip() //test is failing on last assert --- not sure why
database.InitializeDatabase()
createNet()
existing, err := logic.GetNetwork("skynet")
assert.Nil(t, err)
time.Sleep(time.Second * 1)
network, err := logic.KeyUpdate("skynet")
assert.Nil(t, err)
network, err = logic.GetNetwork("skynet")
assert.Nil(t, err)
assert.Greater(t, network.KeyUpdateTimeStamp, existing.KeyUpdateTimeStamp)
}
func TestCreateKey(t *testing.T) {
database.InitializeDatabase()
createNet()
@ -193,6 +181,7 @@ func TestSecurityCheck(t *testing.T) {
//these seem to work but not sure it the tests are really testing the functionality
database.InitializeDatabase()
os.Setenv("MASTER_KEY", "secretkey")
t.Run("NoNetwork", func(t *testing.T) {
err, networks, username := SecurityCheck(false, "", "Bearer secretkey")
assert.Nil(t, err)
@ -243,28 +232,6 @@ func TestValidateNetworkUpdate(t *testing.T) {
},
errMessage: "Field validation for 'AddressRange6' failed on the 'cidr' tag",
},
{
testname: "BadDisplayName",
network: models.Network{
DisplayName: "skynet*",
},
errMessage: "Field validation for 'DisplayName' failed on the 'alphanum' tag",
},
{
testname: "DisplayNameTooLong",
network: models.Network{
DisplayName: "Thisisareallylongdisplaynamethatistoolong",
},
errMessage: "Field validation for 'DisplayName' failed on the 'max' tag",
},
{
testname: "DisplayNameTooShort",
network: models.Network{
DisplayName: "1",
},
errMessage: "Field validation for 'DisplayName' failed on the 'min' tag",
},
{
testname: "InvalidNetID",
network: models.Network{
@ -307,20 +274,6 @@ func TestValidateNetworkUpdate(t *testing.T) {
},
errMessage: "Field validation for 'LocalRange' failed on the 'cidr' tag",
},
{
testname: "CheckInIntervalTooBig",
network: models.Network{
DefaultCheckInInterval: 100001,
},
errMessage: "Field validation for 'DefaultCheckInInterval' failed on the 'max' tag",
},
{
testname: "CheckInIntervalTooSmall",
network: models.Network{
DefaultCheckInInterval: 1,
},
errMessage: "Field validation for 'DefaultCheckInInterval' failed on the 'min' tag",
},
}
for _, tc := range cases {
t.Run(tc.testname, func(t *testing.T) {
@ -344,9 +297,9 @@ func createNet() {
var network models.Network
network.NetID = "skynet"
network.AddressRange = "10.0.0.1/24"
network.DisplayName = "mynetwork"
_, err := logic.GetNetwork("skynet")
if err != nil {
logic.CreateNetwork(network)
}
serverctl.InitializeCommsNetwork()
}

View file

@ -5,7 +5,6 @@ import (
"fmt"
"net/http"
"strings"
"time"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/database"
@ -182,6 +181,13 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
var isAuthorized = false
var nodeID = ""
username, networks, isadmin, errN := logic.VerifyUserToken(authToken)
if errN != nil {
errorResponse = models.ErrorResponse{
Code: http.StatusUnauthorized, Message: "W1R3: Unauthorized, Invalid Token Processed.",
}
returnErrorResponse(w, r, errorResponse)
return
}
isnetadmin := isadmin
if errN == nil && isadmin {
nodeID = "mastermac"
@ -257,6 +263,7 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
var nodes []models.Node
var params = mux.Vars(r)
networkName := params["network"]
nodes, err := logic.GetNetworkNodes(networkName)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
@ -293,9 +300,9 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
}
}
//Return all the nodes in JSON format
logger.Log(2, r.Header.Get("user"), "fetched nodes")
logger.Log(3, r.Header.Get("user"), "fetched all nodes they have access to")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(nodes)
json.NewEncoder(w).Encode(filterCommsNodes(nodes))
}
func getUsersNodes(user models.User) ([]models.Node, error) {
@ -323,6 +330,10 @@ func getNode(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
if logic.IsNodeInComms(&node) {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
logger.Log(2, r.Header.Get("user"), "fetched node", params["nodeid"])
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node)
@ -389,8 +400,8 @@ func createNode(w http.ResponseWriter, r *http.Request) {
validKey := logic.IsKeyValid(networkName, node.AccessKey)
if !validKey {
//Check to see if network will allow manual sign up
//may want to switch this up with the valid key check and avoid a DB call that way.
// Check to see if network will allow manual sign up
// may want to switch this up with the valid key check and avoid a DB call that way.
if network.AllowManualSignUp == "yes" {
node.IsPending = "yes"
} else {
@ -411,12 +422,11 @@ func createNode(w http.ResponseWriter, r *http.Request) {
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)
runUpdates(&node, false)
runForceServerUpdate(&node)
}
//Takes node out of pending state
//TODO: May want to use cordon/uncordon terminology instead of "ispending".
// Takes node out of pending state
// TODO: May want to use cordon/uncordon terminology instead of "ispending".
func uncordonNode(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
@ -430,9 +440,11 @@ func uncordonNode(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode("SUCCESS")
runUpdates(&node, true)
runUpdates(&node, false)
}
// == EGRESS ==
func createEgressGateway(w http.ResponseWriter, r *http.Request) {
var gateway models.EgressGatewayRequest
var params = mux.Vars(r)
@ -532,7 +544,6 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "badrequest"))
return
}
newNode.PullChanges = "yes"
relayupdate := false
if node.IsRelay == "yes" && len(newNode.RelayAddrs) > 0 {
if len(newNode.RelayAddrs) != len(node.RelayAddrs) {
@ -551,6 +562,8 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
newNode.PostUp = node.PostUp
}
ifaceDelta := logic.IfaceDelta(&node, &newNode)
err = logic.UpdateNode(&node, &newNode)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
@ -563,10 +576,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
}
if len(updatenodes) > 0 {
for _, relayedNode := range updatenodes {
err = mq.NodeUpdate(&relayedNode)
if err != nil {
logger.Log(1, "error sending update to relayed node ", relayedNode.Address, "on network", node.Network, ": ", err.Error())
}
runUpdates(&relayedNode, false)
}
}
}
@ -579,7 +589,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(newNode)
runUpdates(&newNode, true)
runUpdates(&newNode, ifaceDelta)
}
func deleteNode(w http.ResponseWriter, r *http.Request) {
@ -600,11 +610,6 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
}
//send update to node to be deleted before deleting on server otherwise message cannot be sent
node.Action = models.NODE_DELETE
if err := mq.NodeUpdate(&node); err != nil {
logger.Log(1, "error publishing node update", err.Error())
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
err = logic.DeleteNodeByID(&node, false)
if err != nil {
@ -613,25 +618,59 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
}
returnSuccessResponse(w, r, nodeid+" deleted.")
time.Sleep(time.Second << 1)
logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
runUpdates(&node, false)
runForceServerUpdate(&node)
}
func runUpdates(node *models.Node, nodeUpdate bool) error {
//don't publish to server node
if nodeUpdate && !isServer(node) {
if err := mq.NodeUpdate(node); err != nil {
logger.Log(1, "error publishing node update", err.Error())
return err
func runUpdates(node *models.Node, ifaceDelta bool) {
go func() { // don't block http response
err := logic.TimerCheckpoint()
if err != nil {
logger.Log(3, "error occurred on timer,", err.Error())
}
// publish node update if not server
if err := mq.NodeUpdate(node); err != nil {
logger.Log(1, "error publishing node update to node", node.Name, node.ID, err.Error())
}
if err := runServerUpdate(node, ifaceDelta); err != nil {
logger.Log(1, "error running server update", err.Error())
}
}()
}
// updates local peers for a server on a given node's network
func runServerUpdate(node *models.Node, ifaceDelta bool) error {
if servercfg.IsClientMode() != "on" || !isServer(node) {
return nil
}
if err := runServerPeerUpdate(node, isServer(node)); err != nil {
logger.Log(1, "internal error when running peer node:", err.Error())
currentServerNode, err := logic.GetNetworkServerLocal(node.Network)
if err != nil {
return err
}
if ifaceDelta && logic.IsLeader(&currentServerNode) {
if err := mq.PublishPeerUpdate(&currentServerNode); err != nil {
logger.Log(1, "failed to publish peer update "+err.Error())
}
}
if err := logic.ServerUpdate(&currentServerNode, ifaceDelta); err != nil {
logger.Log(1, "server node:", currentServerNode.ID, "failed update")
return err
}
return nil
}
func filterCommsNodes(nodes []models.Node) []models.Node {
var filterdNodes []models.Node
for i := range nodes {
if !logic.IsNodeInComms(&nodes[i]) {
filterdNodes = append(filterdNodes, nodes[i])
}
}
return filterdNodes
}

View file

@ -92,6 +92,12 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
Server: key,
}
commID, err := logic.FetchCommsNetID()
if err != nil {
return nil, err
}
node.CommID = commID
err = logic.CreateNode(&node)
if err != nil {
return nil, err
@ -107,7 +113,7 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
Type: nodepb.NODE_TYPE,
}
runUpdates(&node, false)
runForceServerUpdate(&node)
go func(node *models.Node) {
if node.UDPHolePunch == "yes" {
@ -134,6 +140,7 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.Object)
}
// NodeServiceServer.UpdateNode updates a node and responds over gRPC
// DELETE ONE DAY - DEPRECATED
func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.Object) (*nodepb.Object, error) {
var newnode models.Node
@ -166,8 +173,6 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.Object)
return nil, err
}
runUpdates(&newnode, false)
return &nodepb.Object{
Data: string(nodeData),
Type: nodepb.NODE_TYPE,
@ -175,10 +180,10 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.Object)
}
func getServerAddrs(node *models.Node) {
serverNodes := logic.GetServerNodes(node.Network)
serverNodes := logic.GetServerNodes(serverctl.COMMS_NETID)
//pubIP, _ := servercfg.GetPublicIP()
if len(serverNodes) == 0 {
if err := serverctl.SyncServerNetwork(node.Network); err != nil {
if err := serverctl.SyncServerNetwork(serverctl.COMMS_NETID); err != nil {
return
}
}
@ -217,7 +222,7 @@ func (s *NodeServiceServer) DeleteNode(ctx context.Context, req *nodepb.Object)
return nil, err
}
runServerPeerUpdate(&node, false)
runForceServerUpdate(&node)
return &nodepb.Object{
Data: "success",
@ -233,12 +238,7 @@ func (s *NodeServiceServer) GetPeers(ctx context.Context, req *nodepb.Object) (*
return nil, err
}
excludeIsRelayed := node.IsRelay != "yes"
var relayedNode string
if node.IsRelayed == "yes" {
relayedNode = node.Address
}
peers, err := logic.GetPeersList(node.Network, excludeIsRelayed, relayedNode)
peers, err := logic.GetPeersList(&node)
if err != nil {
if strings.Contains(err.Error(), logic.RELAY_NODE_ERR) {
peers, err = logic.PeerListUnRelay(node.ID, node.Network)
@ -309,3 +309,18 @@ func getNodeFromRequestData(data string) (models.Node, error) {
func isServer(node *models.Node) bool {
return node.IsServer == "yes"
}
func runForceServerUpdate(node *models.Node) {
go func() {
if err := mq.PublishPeerUpdate(node); err != nil {
logger.Log(1, "failed a peer update after creation of node", node.Name)
}
var currentServerNode, getErr = logic.GetNetworkServerLeader(node.Network)
if getErr == nil {
if err := logic.ServerUpdate(&currentServerNode, false); err != nil {
logger.Log(1, "server node:", currentServerNode.ID, "failed update")
}
}
}()
}

View file

@ -98,9 +98,9 @@ func SecurityCheck(reqAdmin bool, netname string, token string) (error, []string
return nil, userNetworks, username
}
//Consider a more secure way of setting master key
// Consider a more secure way of setting master key
func authenticateMaster(tokenString string) bool {
return tokenString == servercfg.GetMasterKey()
return tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != ""
}
//Consider a more secure way of setting master key

View file

@ -49,7 +49,7 @@ func securityCheckServer(adminonly bool, next http.Handler) http.HandlerFunc {
returnErrorResponse(w, r, errorResponse)
return
}
if adminonly && !isadmin && !authenticateMasterServer(authToken) {
if adminonly && !isadmin && !authenticateMaster(authToken) {
returnErrorResponse(w, r, errorResponse)
return
}
@ -57,11 +57,6 @@ func securityCheckServer(adminonly bool, next http.Handler) http.HandlerFunc {
}
}
//Consider a more secure way of setting master key
func authenticateMasterServer(tokenString string) bool {
return tokenString == servercfg.GetMasterKey()
}
func removeNetwork(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")

View file

@ -1,34 +0,0 @@
package controller
import (
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/servercfg"
)
func runServerPeerUpdate(node *models.Node, ifaceDelta bool) error {
err := logic.TimerCheckpoint()
if err != nil {
logger.Log(3, "error occurred on timer,", err.Error())
}
if err := mq.PublishPeerUpdate(node); err != nil {
logger.Log(0, "failed to inform peers of new node ", err.Error())
}
if servercfg.IsClientMode() != "on" {
return nil
}
var currentServerNode, getErr = logic.GetNetworkServerLeader(node.Network)
if err != nil {
return getErr
}
if err = logic.ServerUpdate(&currentServerNode, ifaceDelta); err != nil {
logger.Log(1, "server node:", currentServerNode.ID, "failed update")
return err
}
return nil
}

View file

@ -12,6 +12,7 @@ import (
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
)
func userHandlers(r *mux.Router) {
@ -166,6 +167,11 @@ func createUser(w http.ResponseWriter, r *http.Request) {
// get node from body of request
_ = json.NewDecoder(r.Body).Decode(&user)
if !user.IsAdmin && isAddingComms(user.Networks) {
returnErrorResponse(w, r, formatError(fmt.Errorf("can not add comms network to non admin"), "badrequest"))
return
}
user, err := logic.CreateUser(user)
if err != nil {
@ -194,6 +200,10 @@ func updateUserNetworks(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
if !userchange.IsAdmin && isAddingComms(userchange.Networks) {
returnErrorResponse(w, r, formatError(fmt.Errorf("can not add comms network to non admin"), "badrequest"))
return
}
err = logic.UpdateUserNetworks(userchange.Networks, userchange.IsAdmin, &user)
if err != nil {
@ -219,6 +229,10 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(fmt.Errorf("can not update user info for oauth user %s", username), "forbidden"))
return
}
if !user.IsAdmin && isAddingComms(user.Networks) {
returnErrorResponse(w, r, formatError(fmt.Errorf("can not add comms network to non admin"), "badrequest"))
return
}
var userchange models.User
// we decode our body request params
err = json.NewDecoder(r.Body).Decode(&userchange)
@ -288,3 +302,13 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
logger.Log(1, username, "was deleted")
json.NewEncoder(w).Encode(params["username"] + " deleted.")
}
func isAddingComms(networks []string) bool {
commsID := servercfg.GetCommsID()
for i := range networks {
if networks[i] == commsID {
return true
}
}
return false
}

View file

@ -32,9 +32,6 @@ const DNS_TABLE_NAME = "dns"
// EXT_CLIENT_TABLE_NAME - ext client table
const EXT_CLIENT_TABLE_NAME = "extclients"
// INT_CLIENTS_TABLE_NAME - int client table
const INT_CLIENTS_TABLE_NAME = "intclients"
// PEERS_TABLE_NAME - peers table
const PEERS_TABLE_NAME = "peers"
@ -126,7 +123,6 @@ func createTables() {
createTable(USERS_TABLE_NAME)
createTable(DNS_TABLE_NAME)
createTable(EXT_CLIENT_TABLE_NAME)
createTable(INT_CLIENTS_TABLE_NAME)
createTable(PEERS_TABLE_NAME)
createTable(SERVERCONF_TABLE_NAME)
createTable(SERVER_UUID_TABLE_NAME)

View file

@ -2,100 +2,12 @@ package functions
import (
"encoding/json"
"log"
"strings"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
)
// ParseNode - parses a node into a model
func ParseNode(value string) (models.Node, error) {
var node models.Node
err := json.Unmarshal([]byte(value), &node)
return node, err
}
// ParseExtClient - parses an extclient into a model
func ParseExtClient(value string) (models.ExtClient, error) {
var extClient models.ExtClient
err := json.Unmarshal([]byte(value), &extClient)
return extClient, err
}
// ParseIntClient - parses int client
func ParseIntClient(value string) (models.IntClient, error) {
var intClient models.IntClient
err := json.Unmarshal([]byte(value), &intClient)
return intClient, err
}
// GetPeersList - gets peers for given network
func GetPeersList(networkName string) ([]models.PeersResponse, error) {
var peers []models.PeersResponse
collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
if err != nil {
return peers, err
}
for _, value := range collection {
var peer models.PeersResponse
err := json.Unmarshal([]byte(value), &peer)
if err != nil {
continue // try the rest
}
peers = append(peers, peer)
}
return peers, err
}
// GetIntPeersList - get int peers list
func GetIntPeersList() ([]models.PeersResponse, error) {
var peers []models.PeersResponse
records, err := database.FetchRecords(database.INT_CLIENTS_TABLE_NAME)
if err != nil {
return peers, err
}
// parse the peers
for _, value := range records {
var peer models.PeersResponse
err := json.Unmarshal([]byte(value), &peer)
if err != nil {
log.Fatal(err)
}
// add the node to our node array
//maybe better to just return this? But then that's just GetNodes...
peers = append(peers, peer)
}
return peers, err
}
// GetServerIntClient - get server int client
func GetServerIntClient() (*models.IntClient, error) {
intClients, err := database.FetchRecords(database.INT_CLIENTS_TABLE_NAME)
for _, value := range intClients {
var intClient models.IntClient
err = json.Unmarshal([]byte(value), &intClient)
if err != nil {
return nil, err
}
if intClient.IsServer == "yes" && intClient.Network == "comms" {
return &intClient, nil
}
}
return nil, err
}
// NetworkExists - check if network exists
func NetworkExists(name string) (bool, error) {
@ -107,54 +19,6 @@ func NetworkExists(name string) (bool, error) {
return len(network) > 0, nil
}
// IsNetworkDisplayNameUnique - checks if network display name unique
func IsNetworkDisplayNameUnique(name string) (bool, error) {
isunique := true
dbs, err := logic.GetNetworks()
if err != nil {
return database.IsEmptyRecord(err), err
}
for i := 0; i < len(dbs); i++ {
if name == dbs[i].DisplayName {
isunique = false
}
}
return isunique, nil
}
// IsKeyValidGlobal - checks if a key is valid globally
func IsKeyValidGlobal(keyvalue string) bool {
networks, _ := logic.GetNetworks()
var key models.AccessKey
foundkey := false
isvalid := false
for _, network := range networks {
for i := len(network.AccessKeys) - 1; i >= 0; i-- {
currentkey := network.AccessKeys[i]
if currentkey.Value == keyvalue {
key = currentkey
foundkey = true
break
}
}
if foundkey {
break
}
}
if foundkey {
if key.Uses > 0 {
isvalid = true
}
}
return isvalid
}
// NameInDNSCharSet - name in dns char set
func NameInDNSCharSet(name string) bool {
@ -186,37 +50,6 @@ func RemoveDeletedNode(nodeid string) bool {
return database.DeleteRecord(database.DELETED_NODES_TABLE_NAME, nodeid) == nil
}
// DeleteAllIntClients - delete all int clients
func DeleteAllIntClients() error {
err := database.DeleteAllRecords(database.INT_CLIENTS_TABLE_NAME)
if err != nil {
return err
}
return nil
}
// GetAllIntClients - get all int clients
func GetAllIntClients() ([]models.IntClient, error) {
var clients []models.IntClient
collection, err := database.FetchRecords(database.INT_CLIENTS_TABLE_NAME)
if err != nil {
return clients, err
}
for _, value := range collection {
var client models.IntClient
err := json.Unmarshal([]byte(value), &client)
if err != nil {
return []models.IntClient{}, err
}
// add node to our array
clients = append(clients, client)
}
return clients, nil
}
// GetAllExtClients - get all ext clients
func GetAllExtClients() ([]models.ExtClient, error) {
var extclients []models.ExtClient

7
go.mod
View file

@ -5,7 +5,7 @@ go 1.17
require (
github.com/eclipse/paho.mqtt.golang v1.3.5
github.com/go-playground/validator/v10 v10.10.0
github.com/golang-jwt/jwt/v4 v4.2.0
github.com/golang-jwt/jwt/v4 v4.3.0
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0
github.com/gorilla/handlers v1.5.1
@ -27,7 +27,7 @@ require (
google.golang.org/genproto v0.0.0-20210201151548-94839c025ad4 // indirect
google.golang.org/grpc v1.44.0
google.golang.org/protobuf v1.27.1
gopkg.in/ini.v1 v1.66.3
gopkg.in/ini.v1 v1.66.4
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
)
@ -46,10 +46,13 @@ require (
github.com/google/go-cmp v0.5.5 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mdlayher/genetlink v1.0.0 // indirect
github.com/mdlayher/netlink v1.4.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect

8
go.sum
View file

@ -46,8 +46,8 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog=
github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -299,8 +299,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.66.3 h1:jRskFVxYaMGAMUbN0UZ7niA9gzL9B49DOqE78vg0k3w=
gopkg.in/ini.v1 v1.66.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -4,44 +4,26 @@ import (
"fmt"
"os"
"sort"
"strconv"
"strings"
"sync"
"time"
)
// TimeFormatDay - format of the day for timestamps
const TimeFormatDay = "2006-01-02"
// TimeFormat - total time format
const TimeFormat = "2006-01-02 15:04:05"
// == fields ==
var currentLogs = make(map[string]string)
func makeString(message ...string) string {
return strings.Join(message, " ")
}
func getVerbose() int32 {
level, err := strconv.Atoi(os.Getenv("VERBOSITY"))
if err != nil || level < 0 {
level = 0
}
if level > 3 {
level = 3
}
return int32(level)
}
// ResetLogs - reallocates logs map
func ResetLogs() {
currentLogs = make(map[string]string)
}
var mu sync.Mutex
// Log - handles adding logs
func Log(verbosity int, message ...string) {
var mu sync.Mutex
mu.Lock()
defer mu.Unlock()
var currentTime = time.Now()
var currentMessage = makeString(message...)
var currentMessage = MakeString(" ", message...)
if int32(verbosity) <= getVerbose() && getVerbose() >= 0 {
fmt.Printf("[netmaker] %s %s \n", currentTime.Format(TimeFormat), currentMessage)
}
@ -74,9 +56,10 @@ func Dump() string {
for i := range dumpLogs {
var currLog = dumpLogs[i]
dumpString += fmt.Sprintf("[netmaker] %s %s \n", currLog.Value.Format(TimeFormat), currLog.Key)
dumpString += MakeString(" ", "[netmaker]", currLog.Value.Format(TimeFormat), currLog.Key, "\n")
}
resetLogs()
return dumpString
}
@ -84,13 +67,14 @@ func Dump() string {
func DumpFile(filePath string) {
f, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
panic(err)
fmt.Println(MakeString(" ", "could not open log file", filePath))
return
}
defer f.Close()
if _, err = f.WriteString(Dump()); err != nil {
panic(err)
fmt.Println("could not dump logs")
}
}
@ -108,6 +92,13 @@ func FatalLog(message ...string) {
var mu sync.Mutex
mu.Lock()
defer mu.Unlock()
fmt.Printf("[netmaker] Fatal: %s \n", makeString(message...))
fmt.Printf("[netmaker] Fatal: %s \n", MakeString(" ", message...))
os.Exit(2)
}
// == private ==
// resetLogs - reallocates logs map
func resetLogs() {
currentLogs = make(map[string]string)
}

30
logger/util.go Normal file
View file

@ -0,0 +1,30 @@
package logger
import (
"os"
"strconv"
"strings"
)
// MakeString - makes a string using golang string builder
func MakeString(delimeter string, message ...string) string {
var builder strings.Builder
for i := range message {
builder.WriteString(message[i])
if delimeter != "" && i != len(message)-1 {
builder.WriteString(delimeter)
}
}
return builder.String()
}
func getVerbose() int32 {
level, err := strconv.Atoi(os.Getenv("VERBOSITY"))
if err != nil || level < 0 {
level = 0
}
if level > 3 {
level = 3
}
return int32(level)
}

View file

@ -49,18 +49,17 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models
netID := network.NetID
commsNetID, err := FetchCommsNetID()
if err != nil {
return models.AccessKey{}, errors.New("could not retrieve comms netid")
}
var accessToken models.AccessToken
s := servercfg.GetServerConfig()
servervals := models.ServerConfig{
CoreDNSAddr: s.CoreDNSAddr,
APIConnString: s.APIConnString,
APIHost: s.APIHost,
APIPort: s.APIPort,
GRPCConnString: s.GRPCConnString,
GRPCHost: s.GRPCHost,
GRPCPort: s.GRPCPort,
GRPCSSL: s.GRPCSSL,
CheckinInterval: s.CheckinInterval,
GRPCConnString: s.GRPCConnString,
GRPCSSL: s.GRPCSSL,
CommsNetwork: commsNetID,
}
accessToken.ServerConfig = servervals
accessToken.ClientConfig.Network = netID
@ -148,7 +147,7 @@ func DecrimentKey(networkName string, keyvalue string) {
var network models.Network
network, err := GetParentNetwork(networkName)
if err != nil {
if err != nil || network.IsComms == "yes" {
return
}
@ -176,13 +175,21 @@ func DecrimentKey(networkName string, keyvalue string) {
// IsKeyValid - check if key is valid
func IsKeyValid(networkname string, keyvalue string) bool {
network, _ := GetParentNetwork(networkname)
network, err := GetParentNetwork(networkname)
if err != nil {
return false
}
accesskeys := network.AccessKeys
if network.IsComms == "yes" {
accesskeys = getAllAccessKeys()
}
var key models.AccessKey
foundkey := false
isvalid := false
for i := len(network.AccessKeys) - 1; i >= 0; i-- {
currentkey := network.AccessKeys[i]
for i := len(accesskeys) - 1; i >= 0; i-- {
currentkey := accesskeys[i]
if currentkey.Value == keyvalue {
key = currentkey
foundkey = true
@ -236,3 +243,15 @@ func genKey() string {
}
return string(b)
}
func getAllAccessKeys() []models.AccessKey {
var accesskeys = make([]models.AccessKey, 0)
networks, err := GetNetworks()
if err != nil {
return accesskeys
}
for i := range networks {
accesskeys = append(accesskeys, networks[i].AccessKeys...)
}
return accesskeys
}

View file

@ -8,7 +8,6 @@ import (
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
"github.com/txn2/txeh"
)
@ -39,9 +38,12 @@ func SetDNS() error {
if err != nil {
return err
}
/* if something goes wrong with server DNS, check here
// commented out bc we were not using IsSplitDNS
if servercfg.IsSplitDNS() {
err = SetCorefile(corefilestring)
}
*/
return err
}

View file

@ -48,7 +48,6 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
node.PostUp = postUpCmd
node.PostDown = postDownCmd
node.SetLastModified()
node.PullChanges = "yes"
nodeData, err := json.Marshal(&node)
if err != nil {
return node, err
@ -94,7 +93,6 @@ func DeleteEgressGateway(network, nodeid string) (models.Node, error) {
node.PostDown = "iptables -D FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -D POSTROUTING -o " + node.Interface + " -j MASQUERADE"
}
node.SetLastModified()
node.PullChanges = "yes"
data, err := json.Marshal(&node)
if err != nil {
@ -142,7 +140,6 @@ func CreateIngressGateway(netid string, nodeid string) (models.Node, error) {
node.SetLastModified()
node.PostUp = postUpCmd
node.PostDown = postDownCmd
node.PullChanges = "yes"
node.UDPHolePunch = "no"
data, err := json.Marshal(&node)
@ -177,7 +174,6 @@ func DeleteIngressGateway(networkName string, nodeid string) (models.Node, error
node.LastModified = time.Now().Unix()
node.IsIngressGateway = "no"
node.IngressGatewayRange = ""
node.PullChanges = "yes"
data, err := json.Marshal(&node)
if err != nil {

View file

@ -80,7 +80,7 @@ func CreateUserJWT(username string, networks []string, isadmin bool) (response s
func VerifyUserToken(tokenString string) (username string, networks []string, isadmin bool, err error) {
claims := &models.UserClaims{}
if tokenString == servercfg.GetMasterKey() {
if tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != "" {
return "masteradministrator", nil, true, nil
}
@ -104,7 +104,7 @@ func VerifyToken(tokenString string) (nodeID string, mac string, network string,
//this may be a stupid way of serving up a master key
//TODO: look into a different method. Encryption?
if tokenString == servercfg.GetMasterKey() {
if tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != "" {
return "mastermac", "", "", nil
}

View file

@ -8,7 +8,6 @@ import (
"net"
"os/exec"
"strings"
"time"
"github.com/go-playground/validator/v10"
"github.com/gravitl/netmaker/database"
@ -68,7 +67,6 @@ func CreateNetwork(network models.Network) error {
network.SetDefaults()
network.SetNodesLastModified()
network.SetNetworkLastModified()
network.KeyUpdateTimeStamp = time.Now().Unix()
err := ValidateNetwork(&network, false)
if err != nil {
@ -106,7 +104,6 @@ func NetworkNodesUpdatePullChanges(networkName string) error {
return err
}
if node.Network == networkName {
node.PullChanges = "yes"
data, err := json.Marshal(&node)
if err != nil {
return err
@ -190,19 +187,13 @@ func UniqueAddress(networkName string) (string, error) {
offset = false
continue
}
if networkName == "comms" {
if IsIPUnique(networkName, ip.String(), database.INT_CLIENTS_TABLE_NAME, false) {
return ip.String(), err
}
} else {
if IsIPUnique(networkName, ip.String(), database.NODES_TABLE_NAME, false) && IsIPUnique(networkName, ip.String(), database.EXT_CLIENT_TABLE_NAME, false) {
return ip.String(), err
}
if IsIPUnique(networkName, ip.String(), database.NODES_TABLE_NAME, false) && IsIPUnique(networkName, ip.String(), database.EXT_CLIENT_TABLE_NAME, false) {
return ip.String(), err
}
}
//TODO
err1 := errors.New("ERROR: No unique addresses available. Check network subnet.")
err1 := errors.New("ERROR: No unique addresses available. Check network subnet")
return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", err1
}
@ -301,7 +292,7 @@ func UniqueAddress6(networkName string) (string, error) {
}
}
//TODO
err1 := errors.New("ERROR: No unique addresses available. Check network subnet.")
err1 := errors.New("ERROR: No unique addresses available. Check network subnet")
return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", err1
}
@ -380,7 +371,13 @@ func UpdateNetworkLocalAddresses(networkName string) error {
return err
}
if node.Network == networkName {
ipaddr, iperr := UniqueAddress(networkName)
var ipaddr string
var iperr error
if node.IsServer == "yes" {
ipaddr, iperr = UniqueAddressServer(networkName)
} else {
ipaddr, iperr = UniqueAddress(networkName)
}
if iperr != nil {
fmt.Println("error in node address assignment!")
return iperr
@ -440,7 +437,6 @@ func RemoveNetworkNodeIPv6Addresses(networkName string) error {
if node.Network == networkName {
node.IsDualStack = "no"
node.Address6 = ""
node.PullChanges = "yes"
data, err := json.Marshal(&node)
if err != nil {
return err
@ -469,14 +465,19 @@ func UpdateNetworkNodeAddresses(networkName string) error {
return err
}
if node.Network == networkName {
ipaddr, iperr := UniqueAddress(networkName)
var ipaddr string
var iperr error
if node.IsServer == "yes" {
ipaddr, iperr = UniqueAddressServer(networkName)
} else {
ipaddr, iperr = UniqueAddress(networkName)
}
if iperr != nil {
fmt.Println("error in node address assignment!")
return iperr
}
node.Address = ipaddr
node.PullChanges = "yes"
data, err := json.Marshal(&node)
if err != nil {
return err
@ -488,27 +489,6 @@ func UpdateNetworkNodeAddresses(networkName string) error {
return nil
}
// IsNetworkDisplayNameUnique - checks if displayname is unique from other networks
func IsNetworkDisplayNameUnique(network *models.Network) (bool, error) {
isunique := true
records, err := GetNetworks()
if err != nil && !database.IsEmptyRecord(err) {
return false, err
}
for i := 0; i < len(records); i++ {
if network.NetID == records[i].DisplayName {
isunique = false
}
}
return isunique, nil
}
// IsNetworkNameUnique - checks to see if any other networks have the same name (id)
func IsNetworkNameUnique(network *models.Network) (bool, error) {
@ -600,14 +580,6 @@ func ValidateNetwork(network *models.Network, isUpdate bool) error {
return isFieldUnique && inCharSet
})
//
_ = v.RegisterValidation("displayname_valid", func(fl validator.FieldLevel) bool {
isFieldUnique, _ := IsNetworkDisplayNameUnique(network)
inCharSet := network.DisplayNameInNetworkCharSet()
if isUpdate {
return inCharSet
}
return isFieldUnique && inCharSet
})
_ = v.RegisterValidation("checkyesorno", func(fl validator.FieldLevel) bool {
return validation.CheckYesOrNo(fl)
})

View file

@ -85,7 +85,6 @@ func UncordonNode(nodeid string) (models.Node, error) {
}
node.SetLastModified()
node.IsPending = "no"
node.PullChanges = "yes"
data, err := json.Marshal(&node)
if err != nil {
return node, err
@ -100,12 +99,7 @@ func GetPeers(node *models.Node) ([]models.Node, error) {
if 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)
peers, err := GetPeersList(node)
if err != nil {
if strings.Contains(err.Error(), RELAY_NODE_ERR) {
peers, err = PeerListUnRelay(node.ID, node.Network)
@ -145,6 +139,13 @@ func IsLeader(node *models.Node) bool {
// UpdateNode - takes a node and updates another node with it's values
func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
var err error
if newNode.IsHub == "yes" && currentNode.IsHub != "yes" {
if err = unsetHub(newNode.Network); err != nil {
return err
}
}
if newNode.Address != currentNode.Address {
if network, err := GetParentNetwork(newNode.Network); err == nil {
if !IsAddressInCIDR(newNode.Address, network.AddressRange) {
@ -350,13 +351,7 @@ func SetNodeDefaults(node *models.Node) {
if node.ListenPort == 0 {
node.ListenPort = parentNetwork.DefaultListenPort
}
if node.SaveConfig == "" {
if parentNetwork.DefaultSaveConfig != "" {
node.SaveConfig = parentNetwork.DefaultSaveConfig
} else {
node.SaveConfig = "yes"
}
}
if node.Interface == "" {
node.Interface = parentNetwork.DefaultInterface
}
@ -396,8 +391,6 @@ func SetNodeDefaults(node *models.Node) {
node.SetDefaultName()
node.SetLastCheckIn()
node.SetLastPeerUpdate()
node.SetRoamingDefault()
node.SetPullChangesDefault()
node.SetDefaultAction()
node.SetIsServerDefault()
node.SetIsStaticDefault()
@ -409,7 +402,7 @@ func SetNodeDefaults(node *models.Node) {
node.SetDefaultIsRelay()
node.SetDefaultIsDocker()
node.SetDefaultIsK8S()
node.KeyUpdateTimeStamp = time.Now().Unix()
node.SetDefaultIsHub()
}
// GetRecordKey - get record key
@ -512,32 +505,6 @@ func GetNodeRelay(network string, relayedNodeAddr string) (models.Node, error) {
return relay, errors.New(RELAY_NODE_ERR + " " + relayedNodeAddr)
}
// GetNodeByIDorMacAddress - gets the node, if a mac address exists, but not id, then it should delete it and recreate in DB with new ID
/*
func GetNodeByIDorMacAddress(uuid string, macaddress string, network string) (models.Node, error) {
var node models.Node
var err error
node, err = GetNodeByID(uuid)
if err != nil && macaddress != "" && network != "" {
node, err = GetNodeByMacAddress(network, macaddress)
if err != nil {
return models.Node{}, err
}
err = DeleteNodeByMacAddress(&node, true) // remove node
if err != nil {
return models.Node{}, err
}
err = CreateNode(&node)
if err != nil {
return models.Node{}, err
}
logger.Log(2, "rewriting legacy node data; node now has id,", node.ID)
node.PullChanges = "yes"
}
return node, err
}
*/
// GetNodeByID - get node by uuid, should have been set by create
func GetNodeByID(uuid string) (models.Node, error) {
var record, err = database.FetchRecord(database.NODES_TABLE_NAME, uuid)
if err != nil {
@ -611,6 +578,11 @@ func IsLocalServer(node *models.Node) bool {
return node.ID != "" && local.ID == node.ID
}
// IsNodeInComms returns if node is in comms network or not
func IsNodeInComms(node *models.Node) bool {
return node.Network == servercfg.GetCommsID() && node.IsServer != "yes"
}
// validateServer - make sure servers dont change port or address
func validateServer(currentNode, newNode *models.Node) bool {
return (newNode.Address == currentNode.Address &&
@ -640,3 +612,25 @@ func isMacAddressUnique(macaddress string, networkName string) (bool, error) {
return isunique, nil
}
// unsetHub - unset hub on network nodes
func unsetHub(networkName string) error {
nodes, err := GetNetworkNodes(networkName)
if err != nil {
return err
}
for i := range nodes {
if nodes[i].IsHub == "yes" {
nodes[i].IsHub = "no"
newNodeData, err := json.Marshal(&nodes[i])
if err != nil {
logger.Log(1, "error on node during hub update")
return err
}
database.Insert(nodes[i].ID, string(newNodeData), database.NODES_TABLE_NAME)
}
}
return nil
}

View file

@ -7,12 +7,142 @@ import (
"strings"
"time"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/ncutils"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// GetHubPeer - in HubAndSpoke networks, if not the hub, return the hub
/*
func GetHubPeer(networkName string) []models.Node {
var hubpeer = make([]models.Node, 0)
servernodes, err := GetNetworkNodes(networkName)
if err != nil {
return hubpeer
}
for i := range servernodes {
if servernodes[i].IsHub == "yes" {
return []models.Node{servernodes[i]}
}
}
return hubpeer
}
*/
// GetNodePeers - fetches peers for a given node
func GetNodePeers(networkName string, excludeRelayed bool, isP2S bool) ([]models.Node, error) {
var peers []models.Node
var networkNodes, egressNetworkNodes, err = getNetworkEgressAndNodes(networkName)
if err != nil {
return peers, nil
}
udppeers, errN := database.GetPeers(networkName)
if errN != nil {
logger.Log(2, errN.Error())
}
for _, node := range networkNodes {
var peer = models.Node{}
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 := GetNetwork(networkName)
if err == nil {
peer.AllowedIPs = append(peer.AllowedIPs, network.AddressRange)
} else {
peer.AllowedIPs = append(peer.AllowedIPs, node.RelayAddrs...)
}
for _, egressNode := range egressNetworkNodes {
if egressNode.IsRelayed == "yes" && StringSliceContains(node.RelayAddrs, egressNode.Address) {
peer.AllowedIPs = append(peer.AllowedIPs, egressNode.EgressGatewayRanges...)
}
}
}
if !isP2S || peer.IsHub == "yes" {
peers = append(peers, peer)
}
}
}
return peers, err
}
// GetPeersList - gets the peers of a given network
func GetPeersList(refnode *models.Node) ([]models.Node, error) {
var peers []models.Node
var err error
var isP2S bool
var networkName = refnode.Network
var excludeRelayed = refnode.IsRelay != "yes"
var relayedNodeAddr string
if refnode.IsRelayed == "yes" {
relayedNodeAddr = refnode.Address
}
network, err := GetNetwork(networkName)
if err != nil {
return peers, err
} else if network.IsPointToSite == "yes" && refnode.IsHub != "yes" {
isP2S = true
}
if relayedNodeAddr == "" {
peers, err = GetNodePeers(networkName, excludeRelayed, isP2S)
} else {
var relayNode models.Node
relayNode, err = GetNodeRelay(networkName, relayedNodeAddr)
if relayNode.Address != "" {
var peerNode = setPeerInfo(&relayNode)
network, err := GetNetwork(networkName)
if err == nil {
peerNode.AllowedIPs = append(peerNode.AllowedIPs, network.AddressRange)
var _, egressNetworkNodes, err = getNetworkEgressAndNodes(networkName)
if err == nil {
for _, egress := range egressNetworkNodes {
if egress.Address != relayedNodeAddr {
peerNode.AllowedIPs = append(peerNode.AllowedIPs, egress.EgressGatewayRanges...)
}
}
}
} else {
peerNode.AllowedIPs = append(peerNode.AllowedIPs, peerNode.RelayAddrs...)
}
nodepeers, err := GetNodePeers(networkName, false, isP2S)
if err == nil && peerNode.UDPHolePunch == "yes" {
for _, nodepeer := range nodepeers {
if nodepeer.Address == peerNode.Address {
// peerNode.Endpoint = nodepeer.Endpoint
peerNode.ListenPort = nodepeer.ListenPort
}
}
}
if !isP2S || peerNode.IsHub == "yes" {
peers = append(peers, peerNode)
}
}
}
return peers, err
}
// GetPeerUpdate - gets a wireguard peer config for each peer of a node
func GetPeerUpdate(node *models.Node) (models.PeerUpdate, error) {
var peerUpdate models.PeerUpdate

View file

@ -30,7 +30,6 @@ func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error)
node.RelayAddrs = relay.RelayAddrs
node.SetLastModified()
node.PullChanges = "yes"
nodeData, err := json.Marshal(&node)
if err != nil {
return returnnodes, node, err
@ -90,14 +89,14 @@ func SetRelayedNodes(yesOrno string, networkName string, addrs []string) ([]mode
}
// SetNodeIsRelayed - Sets IsRelayed to on or off for relay
func SetNodeIsRelayed(yesOrno string, id string) error {
func SetNodeIsRelayed(yesOrno string, id string) (models.Node, error) {
node, err := GetNodeByID(id)
if err != nil {
return err
return node, err
}
network, err := GetNetworkByNode(&node)
if err != nil {
return err
return node, err
}
node.IsRelayed = yesOrno
if yesOrno == "yes" {
@ -107,18 +106,18 @@ func SetNodeIsRelayed(yesOrno string, id string) error {
}
data, err := json.Marshal(&node)
if err != nil {
return err
return node, err
}
return database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
return node, database.Insert(node.ID, string(data), database.NODES_TABLE_NAME)
}
// PeerListUnRelay - call this function if a relayed node fails to get its relay: unrelays node and gets new peer list
func PeerListUnRelay(id string, network string) ([]models.Node, error) {
err := SetNodeIsRelayed("no", id)
node, err := SetNodeIsRelayed("no", id)
if err != nil {
return nil, err
}
return GetPeersList(network, true, "")
return GetPeersList(&node)
}
// ValidateRelay - checks if relay is valid
@ -162,7 +161,6 @@ func DeleteRelay(network, nodeid string) ([]models.Node, models.Node, error) {
node.IsRelay = "no"
node.RelayAddrs = []string{}
node.SetLastModified()
node.PullChanges = "yes"
data, err := json.Marshal(&node)
if err != nil {

View file

@ -39,7 +39,24 @@ func ServerJoin(networkSettings *models.Network) (models.Node, error) {
if currentServers != nil {
serverCount = len(currentServers) + 1
}
var ishub = "no"
if networkSettings.IsPointToSite == "yes" || networkSettings.IsComms == "yes" {
nodes, err := GetNetworkNodes(networkSettings.NetID)
if err != nil || nodes == nil {
ishub = "yes"
} else {
sethub := true
for i := range nodes {
if nodes[i].IsHub == "yes" {
sethub = false
}
}
if sethub {
ishub = "yes"
}
}
}
var node = &models.Node{
IsServer: "yes",
DNSOn: "no",
@ -52,6 +69,7 @@ func ServerJoin(networkSettings *models.Network) (models.Node, error) {
LocalRange: networkSettings.LocalRange,
OS: runtime.GOOS,
Version: servercfg.Version,
IsHub: ishub,
}
SetNodeDefaults(node)
@ -389,7 +407,7 @@ func ServerPull(serverNode *models.Node, ifaceDelta bool) error {
}
serverNode.OS = runtime.GOOS
if serverNode.PullChanges == "yes" || ifaceDelta {
if ifaceDelta {
// check for interface change
// checks if address is in use by another interface
var oldIfaceName, isIfacePresent = isInterfacePresent(serverNode.Interface, serverNode.Address)
@ -399,7 +417,6 @@ func ServerPull(serverNode *models.Node, ifaceDelta bool) error {
}
logger.Log(1, "removed old interface", oldIfaceName)
}
serverNode.PullChanges = "no"
if err = setWGConfig(serverNode, false); err != nil {
return err
}

View file

@ -72,3 +72,32 @@ func StoreJWTSecret(privateKey string) error {
}
return database.Insert("nm-jwt-secret", string(data), database.SERVERCONF_TABLE_NAME)
}
// FetchCommsNetID - fetches comms netid from db
func FetchCommsNetID() (string, error) {
var dbData string
var err error
var fetchedData = serverData{}
dbData, err = database.FetchRecord(database.SERVERCONF_TABLE_NAME, "nm-comms-id")
if err != nil {
return "", err
}
err = json.Unmarshal([]byte(dbData), &fetchedData)
if err != nil {
return "", err
}
return fetchedData.PrivateKey, nil
}
// StoreCommsNetID - stores server comms network netid if needed
func StoreCommsNetID(netid string) error {
var newData = serverData{}
var err error
var data []byte
newData.PrivateKey = netid
data, err = json.Marshal(&newData)
if err != nil {
return err
}
return database.Insert("nm-comms-id", string(data), database.SERVERCONF_TABLE_NAME)
}

View file

@ -10,7 +10,6 @@ import (
"math/rand"
"net"
"os"
"strconv"
"strings"
"time"
@ -130,101 +129,6 @@ func DeleteNodeByID(node *models.Node, exterminate bool) error {
return removeLocalServer(node)
}
// GetNodePeers - fetches peers for a given node
func GetNodePeers(networkName string, excludeRelayed bool) ([]models.Node, error) {
var peers []models.Node
var networkNodes, egressNetworkNodes, err = getNetworkEgressAndNodes(networkName)
if err != nil {
return peers, nil
}
udppeers, errN := database.GetPeers(networkName)
if errN != nil {
logger.Log(2, errN.Error())
}
for _, node := range networkNodes {
var peer = models.Node{}
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 := GetNetwork(networkName)
if err == nil {
peer.AllowedIPs = append(peer.AllowedIPs, network.AddressRange)
} else {
peer.AllowedIPs = append(peer.AllowedIPs, node.RelayAddrs...)
}
for _, egressNode := range egressNetworkNodes {
if egressNode.IsRelayed == "yes" && StringSliceContains(node.RelayAddrs, egressNode.Address) {
peer.AllowedIPs = append(peer.AllowedIPs, egressNode.EgressGatewayRanges...)
}
}
}
peers = append(peers, peer)
}
}
return peers, err
}
// GetPeersList - gets the peers of a given network
func GetPeersList(networkName string, excludeRelayed bool, relayedNodeAddr string) ([]models.Node, error) {
var peers []models.Node
var err error
if relayedNodeAddr == "" {
peers, err = GetNodePeers(networkName, excludeRelayed)
} else {
var relayNode models.Node
relayNode, err = GetNodeRelay(networkName, relayedNodeAddr)
if relayNode.Address != "" {
var peerNode = setPeerInfo(&relayNode)
network, err := GetNetwork(networkName)
if err == nil {
peerNode.AllowedIPs = append(peerNode.AllowedIPs, network.AddressRange)
var _, egressNetworkNodes, err = getNetworkEgressAndNodes(networkName)
if err == nil {
for _, egress := range egressNetworkNodes {
if egress.Address != relayedNodeAddr {
peerNode.AllowedIPs = append(peerNode.AllowedIPs, egress.EgressGatewayRanges...)
}
}
}
} else {
peerNode.AllowedIPs = append(peerNode.AllowedIPs, peerNode.RelayAddrs...)
}
nodepeers, err := GetNodePeers(networkName, false)
if err == nil && peerNode.UDPHolePunch == "yes" {
for _, nodepeer := range nodepeers {
if nodepeer.Address == peerNode.Address {
// peerNode.Endpoint = nodepeer.Endpoint
peerNode.ListenPort = nodepeer.ListenPort
}
}
}
peers = append(peers, peerNode)
}
}
return peers, err
}
// RandomString - returns a random string in a charset
func RandomString(length int) string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
@ -283,6 +187,7 @@ func setPeerInfo(node *models.Node) models.Node {
peer.UDPHolePunch = node.UDPHolePunch
peer.Address = node.Address
peer.Address6 = node.Address6
peer.IsHub = node.IsHub
peer.EgressGatewayRanges = node.EgressGatewayRanges
peer.IsEgressGateway = node.IsEgressGateway
peer.IngressGatewayRange = node.IngressGatewayRange

View file

@ -46,6 +46,63 @@ func HasPeerConnected(node *models.Node) bool {
return false
}
// IfaceDelta - checks if the new node causes an interface change
func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
// 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 ||
newNode.ListenPort != currentNode.ListenPort ||
newNode.MTU != currentNode.MTU ||
newNode.PersistentKeepalive != currentNode.PersistentKeepalive ||
newNode.DNSOn != currentNode.DNSOn ||
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
}
// == Private Functions ==
// gets the server peers locally

BIN
main

Binary file not shown.

10
main.go
View file

@ -40,6 +40,11 @@ func main() {
func initialize() { // Client Mode Prereq Check
var err error
if servercfg.GetMasterKey() == "" {
logger.Log(0, "warning: MASTER_KEY not set, this could make account recovery difficult")
}
if servercfg.GetNodeID() == "" {
logger.FatalLog("error: must set NODE_ID, currently blank")
}
@ -76,6 +81,9 @@ func initialize() { // Client Mode Prereq Check
if err := serverctl.InitServerNetclient(); err != nil {
logger.FatalLog("Did not find netclient to use CLIENT_MODE")
}
if err := serverctl.InitializeCommsNetwork(); err != nil {
logger.FatalLog("could not inintialize comms network")
}
}
// initialize iptables to ensure gateways work correctly and mq is forwarded if containerized
if servercfg.ManageIPTables() != "off" {
@ -187,7 +195,7 @@ func runGRPC(wg *sync.WaitGroup) {
// Should we be using a context vice a waitgroup????????????
func runMessageQueue(wg *sync.WaitGroup) {
defer wg.Done()
logger.Log(0, fmt.Sprintf("connecting to mq broker at %s", servercfg.GetMessageQueueEndpoint()))
logger.Log(0, "connecting to mq broker at", servercfg.GetMessageQueueEndpoint())
var client = mq.SetupMQTT(false) // Set up the subscription listener
ctx, cancel := context.WithCancel(context.Background())
go mq.Keepalive(ctx)

View file

@ -3,7 +3,6 @@ package models
type AccessToken struct {
ServerConfig
ClientConfig
WG
}
type ClientConfig struct {
@ -13,21 +12,7 @@ type ClientConfig struct {
}
type ServerConfig struct {
CoreDNSAddr string `json:"corednsaddr"`
APIConnString string `json:"apiconn"`
APIHost string `json:"apihost"`
APIPort string `json:"apiport"`
GRPCConnString string `json:"grpcconn"`
GRPCHost string `json:"grpchost"`
GRPCPort string `json:"grpcport"`
GRPCSSL string `json:"grpcssl"`
CheckinInterval string `json:"checkininterval"`
}
type WG struct {
GRPCWireGuard string `json:"grpcwg"`
GRPCWGAddress string `json:"grpcwgaddr"`
GRPCWGPort string `json:"grpcwgport"`
GRPCWGPubKey string `json:"grpcwgpubkey"`
GRPCWGEndpoint string `json:"grpcwgendpoint"`
GRPCConnString string `json:"grpcconn"`
GRPCSSL string `json:"grpcssl"`
CommsNetwork string `json:"commsnetwork"`
}

View file

@ -1,10 +1,7 @@
package models
import (
"strings"
"time"
"github.com/gravitl/netmaker/servercfg"
)
// Network Struct - contains info for a given unique network
@ -12,7 +9,6 @@ import (
type Network struct {
AddressRange string `json:"addressrange" bson:"addressrange" validate:"required,cidr"`
AddressRange6 string `json:"addressrange6" bson:"addressrange6" validate:"regexp=^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$"`
DisplayName string `json:"displayname,omitempty" bson:"displayname,omitempty" validate:"omitempty,min=1,max=20,displayname_valid"`
NetID string `json:"netid" bson:"netid" validate:"required,min=1,max=12,netid_valid"`
NodesLastModified int64 `json:"nodeslastmodified" bson:"nodeslastmodified"`
NetworkLastModified int64 `json:"networklastmodified" bson:"networklastmodified"`
@ -21,24 +17,21 @@ type Network struct {
NodeLimit int32 `json:"nodelimit" bson:"nodelimit"`
DefaultPostUp string `json:"defaultpostup" bson:"defaultpostup"`
DefaultPostDown string `json:"defaultpostdown" bson:"defaultpostdown"`
KeyUpdateTimeStamp int64 `json:"keyupdatetimestamp" bson:"keyupdatetimestamp"`
DefaultKeepalive int32 `json:"defaultkeepalive" bson:"defaultkeepalive" validate:"omitempty,max=1000"`
DefaultSaveConfig string `json:"defaultsaveconfig" bson:"defaultsaveconfig" validate:"checkyesorno"`
AccessKeys []AccessKey `json:"accesskeys" bson:"accesskeys"`
AllowManualSignUp string `json:"allowmanualsignup" bson:"allowmanualsignup" validate:"checkyesorno"`
IsLocal string `json:"islocal" bson:"islocal" validate:"checkyesorno"`
IsDualStack string `json:"isdualstack" bson:"isdualstack" validate:"checkyesorno"`
IsIPv4 string `json:"isipv4" bson:"isipv4" validate:"checkyesorno"`
IsIPv6 string `json:"isipv6" bson:"isipv6" validate:"checkyesorno"`
IsGRPCHub string `json:"isgrpchub" bson:"isgrpchub" validate:"checkyesorno"`
IsPointToSite string `json:"ispointtosite" bson:"ispointtosite" validate:"checkyesorno"`
IsComms string `json:"iscomms" bson:"iscomms" validate:"checkyesorno"`
LocalRange string `json:"localrange" bson:"localrange" validate:"omitempty,cidr"`
// checkin interval is depreciated at the network level. Set on server with CHECKIN_INTERVAL
DefaultCheckInInterval int32 `json:"checkininterval,omitempty" bson:"checkininterval,omitempty" validate:"omitempty,numeric,min=2,max=100000"`
DefaultUDPHolePunch string `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"checkyesorno"`
DefaultExtClientDNS string `json:"defaultextclientdns" bson:"defaultextclientdns"`
DefaultMTU int32 `json:"defaultmtu" bson:"defaultmtu"`
DefaultServerAddrs []ServerAddr `json:"defaultserveraddrs" bson:"defaultserveraddrs" yaml:"defaultserveraddrs"`
DefaultUDPHolePunch string `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"checkyesorno"`
DefaultExtClientDNS string `json:"defaultextclientdns" bson:"defaultextclientdns"`
DefaultMTU int32 `json:"defaultmtu" bson:"defaultmtu"`
// consider removing - may be depreciated
DefaultServerAddrs []ServerAddr `json:"defaultserveraddrs" bson:"defaultserveraddrs" yaml:"defaultserveraddrs"`
}
// SaveData - sensitive fields of a network that should be kept the same
@ -46,19 +39,6 @@ type SaveData struct { // put sensitive fields here
NetID string `json:"netid" bson:"netid" validate:"required,min=1,max=12,netid_valid"`
}
// Network.DisplayNameInNetworkCharSet - checks if displayname uses valid characters
func (network *Network) DisplayNameInNetworkCharSet() bool {
charset := "abcdefghijklmnopqrstuvwxyz1234567890-_./;% ^#()!@$*"
for _, char := range network.DisplayName {
if !strings.Contains(charset, strings.ToLower(string(char))) {
return false
}
}
return true
}
// Network.SetNodesLastModified - sets nodes last modified on network, depricated
func (network *Network) SetNodesLastModified() {
network.NodesLastModified = time.Now().Unix()
@ -72,20 +52,16 @@ func (network *Network) SetNetworkLastModified() {
// Network.SetDefaults - sets default values for a network struct
func (network *Network) SetDefaults() {
if network.DefaultUDPHolePunch == "" {
if servercfg.IsClientMode() != "off" {
network.DefaultUDPHolePunch = "yes"
} else {
network.DefaultUDPHolePunch = "no"
}
network.DefaultUDPHolePunch = "no"
}
if network.IsLocal == "" {
network.IsLocal = "no"
}
if network.IsGRPCHub == "" {
network.IsGRPCHub = "no"
if network.IsPointToSite == "" {
network.IsPointToSite = "no"
}
if network.DisplayName == "" {
network.DisplayName = network.NetID
if network.IsComms == "" {
network.IsComms = "no"
}
if network.DefaultInterface == "" {
if len(network.NetID) < 13 {
@ -100,16 +76,9 @@ func (network *Network) SetDefaults() {
if network.NodeLimit == 0 {
network.NodeLimit = 999999999
}
if network.DefaultSaveConfig == "" {
network.DefaultSaveConfig = "no"
}
if network.DefaultKeepalive == 0 {
network.DefaultKeepalive = 20
}
//Check-In Interval for Nodes, In Seconds
if network.DefaultCheckInInterval == 0 {
network.DefaultCheckInInterval = 30
}
if network.AllowManualSignUp == "" {
network.AllowManualSignUp = "no"
}

View file

@ -10,15 +10,23 @@ import (
"golang.org/x/crypto/bcrypt"
)
const TEN_YEARS_IN_SECONDS = 300000000
const MAX_NAME_LENGTH = 62
// == ACTIONS == (can only be set by GRPC)
const NODE_UPDATE_KEY = "updatekey"
const NODE_SERVER_NAME = "netmaker"
const NODE_DELETE = "delete"
const NODE_IS_PENDING = "pending"
const NODE_NOOP = "noop"
const (
// NODE_SERVER_NAME - the default server name
NODE_SERVER_NAME = "netmaker"
// TEN_YEARS_IN_SECONDS - ten years in seconds
TEN_YEARS_IN_SECONDS = 300000000
// MAX_NAME_LENGTH - max name length of node
MAX_NAME_LENGTH = 62
// == ACTIONS == (can only be set by GRPC)
// NODE_UPDATE_KEY - action to update key
NODE_UPDATE_KEY = "updatekey"
// NODE_DELETE - delete node action
NODE_DELETE = "delete"
// NODE_IS_PENDING - node pending status
NODE_IS_PENDING = "pending"
// NODE_NOOP - node no op action
NODE_NOOP = "noop"
)
var seededRand *rand.Rand = rand.New(
rand.NewSource(time.Now().UnixNano()))
@ -38,45 +46,41 @@ type Node struct {
PostDown string `json:"postdown" bson:"postdown" yaml:"postdown"`
AllowedIPs []string `json:"allowedips" bson:"allowedips" yaml:"allowedips"`
PersistentKeepalive int32 `json:"persistentkeepalive" bson:"persistentkeepalive" yaml:"persistentkeepalive" validate:"omitempty,numeric,max=1000"`
SaveConfig string `json:"saveconfig" bson:"saveconfig" yaml:"saveconfig" validate:"checkyesorno"`
IsHub string `json:"ishub" bson:"ishub" yaml:"ishub" validate:"checkyesorno"`
AccessKey string `json:"accesskey" bson:"accesskey" yaml:"accesskey"`
Interface string `json:"interface" bson:"interface" yaml:"interface"`
LastModified int64 `json:"lastmodified" bson:"lastmodified" yaml:"lastmodified"`
KeyUpdateTimeStamp int64 `json:"keyupdatetimestamp" bson:"keyupdatetimestamp" yaml:"keyupdatetimestamp"`
ExpirationDateTime int64 `json:"expdatetime" bson:"expdatetime" yaml:"expdatetime"`
LastPeerUpdate int64 `json:"lastpeerupdate" bson:"lastpeerupdate" yaml:"lastpeerupdate"`
LastCheckIn int64 `json:"lastcheckin" bson:"lastcheckin" yaml:"lastcheckin"`
MacAddress string `json:"macaddress" bson:"macaddress" yaml:"macaddress" validate:"macaddress_unique"`
// checkin interval is depreciated at the network level. Set on server with CHECKIN_INTERVAL
CheckInInterval int32 `json:"checkininterval" bson:"checkininterval" yaml:"checkininterval"`
Password string `json:"password" bson:"password" yaml:"password" validate:"required,min=6"`
Network string `json:"network" bson:"network" yaml:"network" validate:"network_exists"`
IsRelayed string `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
IsPending string `json:"ispending" bson:"ispending" yaml:"ispending"`
IsRelay string `json:"isrelay" bson:"isrelay" yaml:"isrelay" validate:"checkyesorno"`
IsDocker string `json:"isdocker" bson:"isdocker" yaml:"isdocker" validate:"checkyesorno"`
IsK8S string `json:"isk8s" bson:"isk8s" yaml:"isk8s" validate:"checkyesorno"`
IsEgressGateway string `json:"isegressgateway" bson:"isegressgateway" yaml:"isegressgateway"`
IsIngressGateway string `json:"isingressgateway" bson:"isingressgateway" yaml:"isingressgateway"`
EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs" yaml:"relayaddrs"`
IngressGatewayRange string `json:"ingressgatewayrange" bson:"ingressgatewayrange" yaml:"ingressgatewayrange"`
IsStatic string `json:"isstatic" bson:"isstatic" yaml:"isstatic" validate:"checkyesorno"`
UDPHolePunch string `json:"udpholepunch" bson:"udpholepunch" yaml:"udpholepunch" validate:"checkyesorno"`
PullChanges string `json:"pullchanges" bson:"pullchanges" yaml:"pullchanges" validate:"checkyesorno"`
DNSOn string `json:"dnson" bson:"dnson" yaml:"dnson" validate:"checkyesorno"`
IsDualStack string `json:"isdualstack" bson:"isdualstack" yaml:"isdualstack" validate:"checkyesorno"`
IsServer string `json:"isserver" bson:"isserver" yaml:"isserver" validate:"checkyesorno"`
Action string `json:"action" bson:"action" yaml:"action"`
IsLocal string `json:"islocal" bson:"islocal" yaml:"islocal" validate:"checkyesorno"`
LocalRange string `json:"localrange" bson:"localrange" yaml:"localrange"`
Roaming string `json:"roaming" bson:"roaming" yaml:"roaming" validate:"checkyesorno"`
IPForwarding string `json:"ipforwarding" bson:"ipforwarding" yaml:"ipforwarding" validate:"checkyesorno"`
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"`
TrafficKeys TrafficKeys `json:"traffickeys" bson:"traffickeys" yaml:"traffickeys"`
Password string `json:"password" bson:"password" yaml:"password" validate:"required,min=6"`
Network string `json:"network" bson:"network" yaml:"network" validate:"network_exists"`
IsRelayed string `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
IsPending string `json:"ispending" bson:"ispending" yaml:"ispending"`
IsRelay string `json:"isrelay" bson:"isrelay" yaml:"isrelay" validate:"checkyesorno"`
IsDocker string `json:"isdocker" bson:"isdocker" yaml:"isdocker" validate:"checkyesorno"`
IsK8S string `json:"isk8s" bson:"isk8s" yaml:"isk8s" validate:"checkyesorno"`
IsEgressGateway string `json:"isegressgateway" bson:"isegressgateway" yaml:"isegressgateway"`
IsIngressGateway string `json:"isingressgateway" bson:"isingressgateway" yaml:"isingressgateway"`
EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
RelayAddrs []string `json:"relayaddrs" bson:"relayaddrs" yaml:"relayaddrs"`
IngressGatewayRange string `json:"ingressgatewayrange" bson:"ingressgatewayrange" yaml:"ingressgatewayrange"`
IsStatic string `json:"isstatic" bson:"isstatic" yaml:"isstatic" validate:"checkyesorno"`
UDPHolePunch string `json:"udpholepunch" bson:"udpholepunch" yaml:"udpholepunch" validate:"checkyesorno"`
//PullChanges string `json:"pullchanges" bson:"pullchanges" yaml:"pullchanges" validate:"checkyesorno"`
DNSOn string `json:"dnson" bson:"dnson" yaml:"dnson" validate:"checkyesorno"`
IsDualStack string `json:"isdualstack" bson:"isdualstack" yaml:"isdualstack" validate:"checkyesorno"`
IsServer string `json:"isserver" bson:"isserver" yaml:"isserver" validate:"checkyesorno"`
Action string `json:"action" bson:"action" yaml:"action"`
IsLocal string `json:"islocal" bson:"islocal" yaml:"islocal" validate:"checkyesorno"`
LocalRange string `json:"localrange" bson:"localrange" yaml:"localrange"`
IPForwarding string `json:"ipforwarding" bson:"ipforwarding" yaml:"ipforwarding" validate:"checkyesorno"`
OS string `json:"os" bson:"os" yaml:"os"`
MTU int32 `json:"mtu" bson:"mtu" yaml:"mtu"`
Version string `json:"version" bson:"version" yaml:"version"`
CommID string `json:"commid" bson:"commid" yaml:"comid"`
TrafficKeys TrafficKeys `json:"traffickeys" bson:"traffickeys" yaml:"traffickeys"`
}
// NodesArray - used for node sorting
@ -118,6 +122,13 @@ func (node *Node) SetDefaultIsRelayed() {
}
}
// Node.SetDefaultIsRelayed - set default is relayed
func (node *Node) SetDefaultIsHub() {
if node.IsHub == "" {
node.IsHub = "no"
}
}
// Node.SetDefaultIsRelay - set default isrelay
func (node *Node) SetDefaultIsRelay() {
if node.IsRelay == "" {
@ -161,18 +172,11 @@ func (node *Node) SetDefaultAction() {
}
// 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"
}
}
//func (node *Node) SetRoamingDefault() {
// if node.Roaming == "" {
// node.Roaming = "yes"
// }
//}
// Node.SetIPForwardingDefault - set ip forwarding default
func (node *Node) SetIPForwardingDefault() {
@ -245,13 +249,6 @@ func (node *Node) SetDefaultName() {
}
}
// 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
@ -273,8 +270,6 @@ func (newNode *Node) Fill(currentNode *Node) {
}
if newNode.PublicKey == "" && newNode.IsStatic != "yes" {
newNode.PublicKey = currentNode.PublicKey
} else {
newNode.KeyUpdateTimeStamp = time.Now().Unix()
}
if newNode.Endpoint == "" && newNode.IsStatic != "yes" {
newNode.Endpoint = currentNode.Endpoint
@ -291,9 +286,6 @@ func (newNode *Node) Fill(currentNode *Node) {
if newNode.PersistentKeepalive == 0 {
newNode.PersistentKeepalive = currentNode.PersistentKeepalive
}
if newNode.SaveConfig == "" {
newNode.SaveConfig = currentNode.SaveConfig
}
if newNode.AccessKey == "" {
newNode.AccessKey = currentNode.AccessKey
}
@ -303,9 +295,6 @@ func (newNode *Node) Fill(currentNode *Node) {
if newNode.LastModified == 0 {
newNode.LastModified = currentNode.LastModified
}
if newNode.KeyUpdateTimeStamp == 0 {
newNode.LastModified = currentNode.LastModified
}
if newNode.ExpirationDateTime == 0 {
newNode.ExpirationDateTime = currentNode.ExpirationDateTime
}
@ -318,9 +307,6 @@ func (newNode *Node) Fill(currentNode *Node) {
if newNode.MacAddress == "" {
newNode.MacAddress = currentNode.MacAddress
}
if newNode.CheckInInterval == 0 {
newNode.CheckInInterval = currentNode.CheckInInterval
}
if newNode.Password != "" {
err := bcrypt.CompareHashAndPassword([]byte(newNode.Password), []byte(currentNode.Password))
if err != nil && currentNode.Password != newNode.Password {
@ -354,7 +340,7 @@ func (newNode *Node) Fill(currentNode *Node) {
newNode.IsStatic = currentNode.IsStatic
}
if newNode.UDPHolePunch == "" {
newNode.UDPHolePunch = currentNode.SaveConfig
newNode.UDPHolePunch = currentNode.UDPHolePunch
}
if newNode.DNSOn == "" {
newNode.DNSOn = currentNode.DNSOn
@ -368,12 +354,9 @@ func (newNode *Node) Fill(currentNode *Node) {
if newNode.IPForwarding == "" {
newNode.IPForwarding = currentNode.IPForwarding
}
if newNode.PullChanges == "" {
newNode.PullChanges = currentNode.PullChanges
}
if newNode.Roaming == "" {
newNode.Roaming = currentNode.Roaming
}
//if newNode.Roaming == "" {
//newNode.Roaming = currentNode.Roaming
//}
if newNode.Action == "" {
newNode.Action = currentNode.Action
}
@ -407,9 +390,6 @@ func (newNode *Node) Fill(currentNode *Node) {
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

131
mq/handlers.go Normal file
View file

@ -0,0 +1,131 @@
package mq
import (
"encoding/json"
mqtt "github.com/eclipse/paho.mqtt.golang"
"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"
)
// DefaultHandler default message queue handler - only called when GetDebug == true
func DefaultHandler(client mqtt.Client, msg mqtt.Message) {
logger.Log(0, "MQTT Message: Topic: ", string(msg.Topic()), " Message: ", string(msg.Payload()))
}
// Ping message Handler -- handles ping topic from client nodes
func Ping(client mqtt.Client, msg mqtt.Message) {
go func() {
id, err := getID(msg.Topic())
if err != nil {
logger.Log(0, "error getting node.ID sent on ping topic ")
return
}
node, err := logic.GetNodeByID(id)
if err != nil {
logger.Log(0, "mq-ping error getting node: ", err.Error())
record, err := database.FetchRecord(database.NODES_TABLE_NAME, id)
if err != nil {
logger.Log(0, "error reading database ", err.Error())
return
}
logger.Log(0, "record from database")
logger.Log(0, record)
return
}
_, decryptErr := decryptMsg(&node, msg.Payload())
if decryptErr != nil {
logger.Log(0, "error decrypting when updating node ", node.ID, decryptErr.Error())
return
}
node.SetLastCheckIn()
if err := logic.UpdateNode(&node, &node); err != nil {
logger.Log(0, "error updating node", node.Name, node.ID, " on checkin", err.Error())
return
}
logger.Log(3, "ping processed for node", node.ID)
// --TODO --set client version once feature is implemented.
//node.SetClientVersion(msg.Payload())
}()
}
// UpdateNode message Handler -- handles updates from client nodes
func UpdateNode(client mqtt.Client, msg mqtt.Message) {
go func() {
id, err := getID(msg.Topic())
if err != nil {
logger.Log(1, "error getting node.ID sent on ", msg.Topic(), err.Error())
return
}
currentNode, err := logic.GetNodeByID(id)
if err != nil {
logger.Log(1, "error getting node ", id, err.Error())
return
}
decrypted, decryptErr := decryptMsg(&currentNode, msg.Payload())
if decryptErr != nil {
logger.Log(1, "failed to decrypt message for node ", id, decryptErr.Error())
return
}
var newNode models.Node
if err := json.Unmarshal(decrypted, &newNode); err != nil {
logger.Log(1, "error unmarshaling payload ", err.Error())
return
}
if err := logic.UpdateNode(&currentNode, &newNode); err != nil {
logger.Log(1, "error saving node", err.Error())
return
}
logger.Log(1, "updated node", id, newNode.Name)
}()
}
// ClientPeerUpdate message handler -- handles updating peers after signal from client nodes
func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
go func() {
id, err := getID(msg.Topic())
if err != nil {
logger.Log(1, "error getting node.ID sent on ", msg.Topic(), err.Error())
return
}
currentNode, err := logic.GetNodeByID(id)
if err != nil {
logger.Log(1, "error getting node ", id, err.Error())
return
}
decrypted, decryptErr := decryptMsg(&currentNode, msg.Payload())
if decryptErr != nil {
logger.Log(1, "failed to decrypt message during client peer update for node ", id, decryptErr.Error())
return
}
switch decrypted[0] {
case ncutils.ACK:
currentServerNode, err := logic.GetNetworkServerLocal(currentNode.Network)
if err != nil {
return
}
if err := logic.ServerUpdate(&currentServerNode, false); err != nil {
logger.Log(1, "server node:", currentServerNode.ID, "failed update")
return
}
case ncutils.DONE:
currentServerNode, err := logic.GetNetworkServerLocal(currentNode.Network)
if err != nil {
return
}
if err := logic.ServerUpdate(&currentServerNode, false); err != nil {
logger.Log(1, "server node:", currentServerNode.ID, "failed update")
return
}
if err := PublishPeerUpdate(&currentNode); err != nil {
logger.Log(1, "error publishing peer update ", err.Error())
return
}
}
logger.Log(1, "sent peer updates after signal received from", id, currentNode.Name)
}()
}

226
mq/mq.go
View file

@ -2,18 +2,11 @@ package mq
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"strings"
"time"
mqtt "github.com/eclipse/paho.mqtt.golang"
"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"
)
@ -25,170 +18,6 @@ const MQ_DISCONNECT = 250
var peer_force_send = 0
// DefaultHandler default message queue handler - only called when GetDebug == true
func DefaultHandler(client mqtt.Client, msg mqtt.Message) {
logger.Log(0, "MQTT Message: Topic: ", string(msg.Topic()), " Message: ", string(msg.Payload()))
}
// Ping message Handler -- handles ping topic from client nodes
func Ping(client mqtt.Client, msg mqtt.Message) {
logger.Log(0, "Ping Handler: ", msg.Topic())
go func() {
id, err := GetID(msg.Topic())
if err != nil {
logger.Log(0, "error getting node.ID sent on ping topic ")
return
}
node, err := logic.GetNodeByID(id)
if err != nil {
logger.Log(0, "mq-ping error getting node: ", err.Error())
record, err := database.FetchRecord(database.NODES_TABLE_NAME, id)
if err != nil {
logger.Log(0, "error reading database ", err.Error())
return
}
logger.Log(0, "record from database")
logger.Log(0, record)
return
}
_, decryptErr := decryptMsg(&node, msg.Payload())
if decryptErr != nil {
logger.Log(0, "error updating node ", node.ID, err.Error())
return
}
node.SetLastCheckIn()
if err := logic.UpdateNode(&node, &node); err != nil {
logger.Log(0, "error updating node ", err.Error())
}
logger.Log(3, "ping processed for node", node.ID)
// --TODO --set client version once feature is implemented.
//node.SetClientVersion(msg.Payload())
}()
}
// UpdateNode message Handler -- handles updates from client nodes
func UpdateNode(client mqtt.Client, msg mqtt.Message) {
go func() {
id, err := GetID(msg.Topic())
if err != nil {
logger.Log(1, "error getting node.ID sent on ", msg.Topic(), err.Error())
return
}
currentNode, err := logic.GetNodeByID(id)
if err != nil {
logger.Log(1, "error getting node ", id, err.Error())
return
}
decrypted, decryptErr := decryptMsg(&currentNode, msg.Payload())
if decryptErr != nil {
logger.Log(1, "failed to decrypt message for node ", id, decryptErr.Error())
return
}
logger.Log(1, "Update Node Handler", id)
var newNode models.Node
if err := json.Unmarshal(decrypted, &newNode); err != nil {
logger.Log(1, "error unmarshaling payload ", err.Error())
return
}
if err := logic.UpdateNode(&currentNode, &newNode); err != nil {
logger.Log(1, "error saving node", err.Error())
}
if err := PublishPeerUpdate(&newNode); err != nil {
logger.Log(1, "error publishing peer update ", err.Error())
return
}
logger.Log(1, "no need to update peers")
}()
}
// PublishPeerUpdate --- deterines and publishes a peer update to all the peers of a node
func PublishPeerUpdate(newNode *models.Node) error {
if !servercfg.IsMessageQueueBackend() {
return nil
}
networkNodes, err := logic.GetNetworkNodes(newNode.Network)
if err != nil {
logger.Log(1, "err getting Network Nodes", err.Error())
return err
}
for _, node := range networkNodes {
if node.IsServer == "yes" || node.ID == newNode.ID {
continue
}
peerUpdate, err := logic.GetPeerUpdate(&node)
if err != nil {
logger.Log(1, "error getting peer update for node", node.ID, err.Error())
continue
}
data, err := json.Marshal(&peerUpdate)
if err != nil {
logger.Log(2, "error marshaling peer update for node", node.ID, err.Error())
continue
}
if err = publish(&node, fmt.Sprintf("peers/%s/%s", node.Network, node.ID), data); err != nil {
logger.Log(1, "failed to publish peer update for node", node.ID)
} else {
logger.Log(1, fmt.Sprintf("sent peer update for node %s on network: %s ", node.Name, node.Network))
}
}
return nil
}
// PublishPeerUpdate --- deterines and publishes a peer update to all the peers of a node
func PublishExtPeerUpdate(node *models.Node) error {
var err error
if logic.IsLocalServer(node) {
if err = logic.ServerUpdate(node, false); err != nil {
logger.Log(1, "server node:", node.ID, "failed to update peers with ext clients")
return err
} else {
return nil
}
}
if !servercfg.IsMessageQueueBackend() {
return nil
}
peerUpdate, err := logic.GetPeerUpdate(node)
if err != nil {
return err
}
data, err := json.Marshal(&peerUpdate)
if err != nil {
return err
}
return publish(node, fmt.Sprintf("peers/%s/%s", node.Network, node.ID), data)
}
// GetID -- decodes a message queue topic and returns the embedded node.ID
func GetID(topic string) (string, error) {
parts := strings.Split(topic, "/")
count := len(parts)
if count == 1 {
return "", errors.New("invalid topic")
}
//the last part of the topic will be the node.ID
return parts[count-1], nil
}
// NodeUpdate -- publishes a node update
func NodeUpdate(node *models.Node) error {
if !servercfg.IsMessageQueueBackend() {
return nil
}
logger.Log(3, "publishing node update to "+node.Name)
data, err := json.Marshal(node)
if err != nil {
logger.Log(2, "error marshalling node update ", err.Error())
return err
}
if err = publish(node, fmt.Sprintf("update/%s/%s", node.Network, node.ID), data); err != nil {
logger.Log(2, "error publishing node update to peer ", node.ID, err.Error())
return err
}
return nil
}
// SetupMQTT creates a connection to broker and return client
func SetupMQTT(publish bool) mqtt.Client {
opts := mqtt.NewClientOptions()
@ -216,6 +45,10 @@ func SetupMQTT(publish bool) mqtt.Client {
client.Disconnect(240)
logger.Log(0, "node update subscription failed")
}
if token := client.Subscribe("signal/#", 0, mqtt.MessageHandler(ClientPeerUpdate)); token.Wait() && token.Error() != nil {
client.Disconnect(240)
logger.Log(0, "node client subscription failed")
}
opts.SetOrderMatters(true)
opts.SetResumeSubs(true)
@ -248,54 +81,3 @@ func Keepalive(ctx context.Context) {
}
}
}
// sendPeers - retrieve networks, send peer ports to all peers
func sendPeers() {
var force bool
peer_force_send++
if peer_force_send == 5 {
force = true
peer_force_send = 0
}
networks, err := logic.GetNetworks()
if err != nil {
logger.Log(1, "error retrieving networks for keepalive", err.Error())
}
for _, network := range networks {
serverNode, errN := logic.GetNetworkServerLeader(network.NetID)
if errN == nil {
serverNode.SetLastCheckIn()
logic.UpdateNode(&serverNode, &serverNode)
if network.DefaultUDPHolePunch == "yes" {
if logic.ShouldPublishPeerPorts(&serverNode) || force {
if force {
logger.Log(2, "sending scheduled peer update (5 min)")
}
err = PublishPeerUpdate(&serverNode)
if err != nil {
logger.Log(1, "error publishing udp port updates for network", network.NetID)
logger.Log(1, errN.Error())
}
}
}
} else {
logger.Log(1, "unable to retrieve leader for network ", network.NetID)
logger.Log(1, errN.Error())
continue
}
}
}
// func publishServerKeepalive(client mqtt.Client, network *models.Network) {
// nodes, err := logic.GetNetworkNodes(network.NetID)
// if err != nil {
// return
// }
// for _, node := range nodes {
// if token := client.Publish(fmt.Sprintf("serverkeepalive/%s/%s", network.NetID, node.ID), 0, false, servercfg.GetVersion()); token.Wait() && token.Error() != nil {
// logger.Log(1, "error publishing server keepalive for network", network.NetID, token.Error().Error())
// } else {
// logger.Log(2, "keepalive sent for network/node", network.NetID, node.ID)
// }
// }
// }

127
mq/publishers.go Normal file
View file

@ -0,0 +1,127 @@
package mq
import (
"encoding/json"
"fmt"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
)
// PublishPeerUpdate --- deterines and publishes a peer update to all the peers of a node
func PublishPeerUpdate(newNode *models.Node) error {
if !servercfg.IsMessageQueueBackend() {
return nil
}
networkNodes, err := logic.GetNetworkNodes(newNode.Network)
if err != nil {
logger.Log(1, "err getting Network Nodes", err.Error())
return err
}
for _, node := range networkNodes {
if node.IsServer == "yes" || node.ID == newNode.ID {
continue
}
peerUpdate, err := logic.GetPeerUpdate(&node)
if err != nil {
logger.Log(1, "error getting peer update for node", node.ID, err.Error())
continue
}
data, err := json.Marshal(&peerUpdate)
if err != nil {
logger.Log(2, "error marshaling peer update for node", node.ID, err.Error())
continue
}
if err = publish(&node, fmt.Sprintf("peers/%s/%s", node.Network, node.ID), data); err != nil {
logger.Log(1, "failed to publish peer update for node", node.ID)
} else {
if node.Network != servercfg.GetCommsID() {
logger.Log(1, "sent peer update for node", node.Name, "on network:", node.Network)
}
}
}
return nil
}
// PublishPeerUpdate --- publishes a peer update to all the peers of a node
func PublishExtPeerUpdate(node *models.Node) error {
var err error
if logic.IsLocalServer(node) {
if err = logic.ServerUpdate(node, false); err != nil {
logger.Log(1, "server node:", node.ID, "failed to update peers with ext clients")
return err
} else {
return nil
}
}
if !servercfg.IsMessageQueueBackend() {
return nil
}
peerUpdate, err := logic.GetPeerUpdate(node)
if err != nil {
return err
}
data, err := json.Marshal(&peerUpdate)
if err != nil {
return err
}
return publish(node, fmt.Sprintf("peers/%s/%s", node.Network, node.ID), data)
}
// NodeUpdate -- publishes a node update
func NodeUpdate(node *models.Node) error {
if !servercfg.IsMessageQueueBackend() || node.IsServer == "yes" {
return nil
}
logger.Log(3, "publishing node update to "+node.Name)
data, err := json.Marshal(node)
if err != nil {
logger.Log(2, "error marshalling node update ", err.Error())
return err
}
if err = publish(node, fmt.Sprintf("update/%s/%s", node.Network, node.ID), data); err != nil {
logger.Log(2, "error publishing node update to peer ", node.ID, err.Error())
return err
}
return nil
}
// sendPeers - retrieve networks, send peer ports to all peers
func sendPeers() {
var force bool
peer_force_send++
if peer_force_send == 5 {
force = true
peer_force_send = 0
}
networks, err := logic.GetNetworks()
if err != nil {
logger.Log(1, "error retrieving networks for keepalive", err.Error())
}
for _, network := range networks {
serverNode, errN := logic.GetNetworkServerLeader(network.NetID)
if errN == nil {
serverNode.SetLastCheckIn()
logic.UpdateNode(&serverNode, &serverNode)
if network.DefaultUDPHolePunch == "yes" {
if logic.ShouldPublishPeerPorts(&serverNode) || force {
if force {
logger.Log(2, "sending scheduled peer update (5 min)")
}
err = PublishPeerUpdate(&serverNode)
if err != nil {
logger.Log(1, "error publishing udp port updates for network", network.NetID)
logger.Log(1, errN.Error())
}
}
}
} else {
logger.Log(1, "unable to retrieve leader for network ", network.NetID)
logger.Log(1, errN.Error())
continue
}
}
}

View file

@ -2,6 +2,7 @@ package mq
import (
"fmt"
"strings"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
@ -10,7 +11,7 @@ import (
func decryptMsg(node *models.Node, msg []byte) ([]byte, error) {
if len(msg) <= 24 { // make sure message is of appropriate length
return nil, fmt.Errorf("recieved invalid message from broker %s", string(msg))
return nil, fmt.Errorf("recieved invalid message from broker %v", msg)
}
trafficKey, trafficErr := logic.RetrievePrivateTrafficKey() // get server private key
@ -26,7 +27,11 @@ func decryptMsg(node *models.Node, msg []byte) ([]byte, error) {
return nil, err
}
return ncutils.BoxDecrypt(msg, nodePubTKey, serverPrivTKey)
if strings.Contains(node.Version, "0.10.0") {
return ncutils.BoxDecrypt(msg, nodePubTKey, serverPrivTKey)
}
return ncutils.DeChunk(msg, nodePubTKey, serverPrivTKey)
}
func encryptMsg(node *models.Node, msg []byte) ([]byte, error) {
@ -46,7 +51,11 @@ func encryptMsg(node *models.Node, msg []byte) ([]byte, error) {
return nil, err
}
return ncutils.BoxEncrypt(msg, nodePubKey, serverPrivKey)
if strings.Contains(node.Version, "0.10.0") {
return ncutils.BoxEncrypt(msg, nodePubKey, serverPrivKey)
}
return ncutils.Chunk(msg, nodePubKey, serverPrivKey)
}
func publish(node *models.Node, dest string, msg []byte) error {
@ -61,3 +70,14 @@ func publish(node *models.Node, dest string, msg []byte) error {
}
return nil
}
// decodes a message queue topic and returns the embedded node.ID
func getID(topic string) (string, error) {
parts := strings.Split(topic, "/")
count := len(parts)
if count == 1 {
return "", fmt.Errorf("invalid topic")
}
//the last part of the topic will be the node.ID
return parts[count-1], nil
}

View file

@ -43,40 +43,10 @@ func GetCommands(cliFlags []cli.Flag) []*cli.Command {
if err != nil {
return err
}
err = command.Leave(cfg)
err = command.Leave(cfg, c.String("force") == "yes")
return err
},
},
{
Name: "checkin",
Usage: "Checks for local changes and then checks into the specified Netmaker network to ask about remote changes.",
Flags: cliFlags,
// the action, or code that will be executed when
// we execute our `ns` command
Action: func(c *cli.Context) error {
cfg, _, err := config.GetCLIConfig(c)
if err != nil {
return err
}
err = command.CheckIn(cfg)
return err
},
},
// {
// Name: "push",
// Usage: "Push configuration changes to server.",
// Flags: cliFlags,
// // the action, or code that will be executed when
// // we execute our `ns` command
// Action: func(c *cli.Context) error {
// cfg, _, err := config.GetCLIConfig(c)
// if err != nil {
// return err
// }
// err = command.Push(cfg)
// return err
// },
// },
{
Name: "pull",
Usage: "Pull latest configuration and peers from server.",

View file

@ -198,5 +198,11 @@ func GetFlags(hostname string) []cli.Flag {
Value: "yes",
Usage: "Checks for IP changes if 'yes'. Ignores if 'no'. Yes by default.",
},
&cli.StringFlag{
Name: "force",
EnvVars: []string{"NETCLIENT_FORCE"},
Value: "no",
Usage: "Allows to run the command with force, if otherwise prevented.",
},
}
}

View file

@ -1,10 +1,7 @@
package command
import (
"os"
"strconv"
"strings"
"time"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/daemon"
@ -12,15 +9,47 @@ import (
"github.com/gravitl/netmaker/netclient/ncutils"
)
// JoinComms -- Join the message queue comms network if it doesn't have it
// tries to ping if already found locally, if fail ping pull for best effort for communication
func JoinComms(cfg *config.ClientConfig) error {
var commsCfg config.ClientConfig
commsCfg.Network = cfg.Server.CommsNetwork
commsCfg.Node.Network = cfg.Server.CommsNetwork
commsCfg.Server.AccessKey = cfg.Server.AccessKey
commsCfg.Server.GRPCAddress = cfg.Server.GRPCAddress
commsCfg.Server.GRPCSSL = cfg.Server.GRPCSSL
commsCfg.Server.CoreDNSAddr = cfg.Server.CoreDNSAddr
if commsCfg.ConfigFileExists() {
commsCfg.ReadConfig()
}
if commsCfg.Node.Name == "" {
if err := functions.JoinNetwork(commsCfg, "", true); err != nil {
return err
}
} else { // check if comms is currently reachable
if err := functions.PingServer(&commsCfg); err != nil {
if err = Pull(commsCfg); err != nil {
return err
}
}
}
return nil
}
// Join - join command to run from cli
func Join(cfg config.ClientConfig, privateKey string) error {
var err error
err = functions.JoinNetwork(cfg, privateKey)
//check if comms network exists
if err = JoinComms(&cfg); err != nil {
return err
}
//join network
err = functions.JoinNetwork(cfg, privateKey, false)
if err != nil && !cfg.DebugOn {
if !strings.Contains(err.Error(), "ALREADY_INSTALLED") {
ncutils.PrintLog("error installing: "+err.Error(), 1)
err = functions.LeaveNetwork(cfg.Network)
err = functions.LeaveNetwork(cfg.Network, true)
if err != nil {
err = functions.WipeLocal(cfg.Network)
if err != nil {
@ -48,134 +77,30 @@ func Join(cfg config.ClientConfig, privateKey string) error {
return err
}
ncutils.PrintLog("joined "+cfg.Network, 1)
if ncutils.IsWindows() {
ncutils.PrintLog("setting up WireGuard app", 0)
time.Sleep(time.Second >> 1)
functions.Pull(cfg.Network, true)
}
return err
}
func getWindowsInterval() int {
interval := 15
networks, err := ncutils.GetSystemNetworks()
if err != nil {
return interval
}
cfg, err := config.ReadConfig(networks[0])
if err != nil {
return interval
}
netint, err := strconv.Atoi(cfg.Server.CheckinInterval)
if err == nil && netint != 0 {
interval = netint
}
return interval
}
// RunUserspaceDaemon - runs continual checkins
func RunUserspaceDaemon() {
cfg := config.ClientConfig{
Network: "all",
}
interval := getWindowsInterval()
dur := time.Duration(interval) * time.Second
for {
CheckIn(cfg)
time.Sleep(dur)
}
}
// CheckIn - runs checkin command from cli
func CheckIn(cfg config.ClientConfig) error {
//log.Println("checkin --- diabled for now")
//return nil
var err error
var errN error
if cfg.Network == "" {
ncutils.PrintLog("required, '-n', exiting", 0)
os.Exit(1)
} else if cfg.Network == "all" {
ncutils.PrintLog("running checkin for all networks", 1)
networks, err := ncutils.GetSystemNetworks()
if err != nil {
ncutils.PrintLog("error retrieving networks, exiting", 1)
return err
/*
if ncutils.IsWindows() {
ncutils.PrintLog("setting up WireGuard app", 0)
time.Sleep(time.Second >> 1)
functions.Pull(cfg.Network, true)
}
for _, network := range networks {
currConf, err := config.ReadConfig(network)
if err != nil {
continue
}
err = functions.CheckConfig(*currConf)
if err != nil {
if strings.Contains(err.Error(), "could not find iface") {
err = Pull(cfg)
if err != nil {
ncutils.PrintLog(err.Error(), 1)
}
} else {
ncutils.PrintLog("error checking in for "+network+" network: "+err.Error(), 1)
}
} else {
ncutils.PrintLog("checked in successfully for "+network, 1)
}
}
if len(networks) == 0 {
if ncutils.IsWindows() { // Windows specific - there are no netclients, so stop daemon process
daemon.StopWindowsDaemon()
}
}
errN = err
err = nil
} else {
err = functions.CheckConfig(cfg)
}
if err == nil && errN != nil {
err = errN
}
*/
return err
}
// Leave - runs the leave command from cli
func Leave(cfg config.ClientConfig) error {
err := functions.LeaveNetwork(cfg.Network)
func Leave(cfg config.ClientConfig, force bool) error {
err := functions.LeaveNetwork(cfg.Network, force)
if err != nil {
ncutils.PrintLog("error attempting to leave network "+cfg.Network, 1)
} else {
ncutils.PrintLog("success", 0)
}
return err
}
// Push - runs push command
func Push(cfg config.ClientConfig) error {
var err error
if cfg.Network == "all" || ncutils.IsWindows() {
ncutils.PrintLog("pushing config to server for all networks.", 0)
networks, err := ncutils.GetSystemNetworks()
if err != nil {
ncutils.PrintLog("error retrieving networks, exiting.", 0)
return err
nets, err := ncutils.GetSystemNetworks()
if err == nil && len(nets) == 1 {
if nets[0] == cfg.Node.CommID {
ncutils.PrintLog("detected comms as remaining network, removing...", 1)
err = functions.LeaveNetwork(nets[0], true)
}
for _, network := range networks {
err = functions.Push(network)
if err != nil {
ncutils.PrintLog("error pushing network configs for network: "+network+"\n"+err.Error(), 1)
} else {
ncutils.PrintLog("pushed network config for "+network, 1)
}
}
err = nil
} else {
err = functions.Push(cfg.Network)
}
if err == nil {
ncutils.PrintLog("completed pushing network configs to remote server", 1)
ncutils.PrintLog("success", 1)
} else {
ncutils.PrintLog("error occurred pushing configs", 1)
}
return err
}
@ -226,6 +151,7 @@ func Uninstall() error {
return err
}
// Daemon - runs the daemon
func Daemon() error {
err := functions.Daemon()
return err

View file

@ -15,12 +15,6 @@ import (
"gopkg.in/yaml.v3"
)
// GlobalConfig - struct for handling IntClients currently
type GlobalConfig struct {
GRPCWireGuard string `yaml:"grpcwg"`
Client models.IntClient
}
// ClientConfig - struct for dealing with client configuration
type ClientConfig struct {
Server ServerConfig `yaml:"server"`
@ -34,13 +28,11 @@ type ClientConfig struct {
// ServerConfig - struct for dealing with the server information for a netclient
type ServerConfig struct {
CoreDNSAddr string `yaml:"corednsaddr"`
GRPCAddress string `yaml:"grpcaddress"`
APIAddress string `yaml:"apiaddress"`
AccessKey string `yaml:"accesskey"`
GRPCSSL string `yaml:"grpcssl"`
GRPCWireGuard string `yaml:"grpcwg"`
CheckinInterval string `yaml:"checkininterval"`
CoreDNSAddr string `yaml:"corednsaddr"`
GRPCAddress string `yaml:"grpcaddress"`
AccessKey string `yaml:"accesskey"`
GRPCSSL string `yaml:"grpcssl"`
CommsNetwork string `yaml:"commsnetwork"`
}
// Write - writes the config of a client to disk
@ -71,6 +63,18 @@ func Write(config *ClientConfig, network string) error {
return f.Sync()
}
// ConfigFileExists - return true if config file exists
func (config *ClientConfig) ConfigFileExists() bool {
home := ncutils.GetNetclientPathSpecific()
file := fmt.Sprintf(home + "netconfig-" + config.Network)
info, err := os.Stat(file)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
// ClientConfig.ReadConfig - used to read config from client disk into memory
func (config *ClientConfig) ReadConfig() {
@ -82,8 +86,7 @@ func (config *ClientConfig) ReadConfig() {
//f, err := os.Open(file)
f, err := os.OpenFile(file, os.O_RDONLY, 0600)
if err != nil {
fmt.Println("trouble opening file")
fmt.Println(err)
ncutils.PrintLog("trouble opening file: "+err.Error(), 1)
nofile = true
//fmt.Println("Could not access " + home + "/.netconfig, proceeding...")
}
@ -175,21 +178,8 @@ func GetCLIConfig(c *cli.Context) (ClientConfig, string, error) {
return cfg, "", err
}
if accesstoken.ServerConfig.APIConnString != "" {
cfg.Server.APIAddress = accesstoken.ServerConfig.APIConnString
} else {
cfg.Server.APIAddress = accesstoken.ServerConfig.APIHost
if accesstoken.ServerConfig.APIPort != "" {
cfg.Server.APIAddress = cfg.Server.APIAddress + ":" + accesstoken.ServerConfig.APIPort
}
}
if accesstoken.ServerConfig.GRPCConnString != "" {
cfg.Server.GRPCAddress = accesstoken.ServerConfig.GRPCConnString
} else {
cfg.Server.GRPCAddress = accesstoken.ServerConfig.GRPCHost
if accesstoken.ServerConfig.GRPCPort != "" {
cfg.Server.GRPCAddress = cfg.Server.GRPCAddress + ":" + accesstoken.ServerConfig.GRPCPort
}
}
cfg.Network = accesstoken.ClientConfig.Network
@ -197,15 +187,10 @@ func GetCLIConfig(c *cli.Context) (ClientConfig, string, error) {
cfg.Server.AccessKey = accesstoken.ClientConfig.Key
cfg.Node.LocalRange = accesstoken.ClientConfig.LocalRange
cfg.Server.GRPCSSL = accesstoken.ServerConfig.GRPCSSL
cfg.Server.CheckinInterval = accesstoken.ServerConfig.CheckinInterval
cfg.Server.GRPCWireGuard = accesstoken.WG.GRPCWireGuard
cfg.Server.CoreDNSAddr = accesstoken.ServerConfig.CoreDNSAddr
cfg.Server.CommsNetwork = accesstoken.ServerConfig.CommsNetwork
if c.String("grpcserver") != "" {
cfg.Server.GRPCAddress = c.String("grpcserver")
}
if c.String("apiserver") != "" {
cfg.Server.APIAddress = c.String("apiserver")
}
if c.String("key") != "" {
cfg.Server.AccessKey = c.String("key")
}
@ -222,24 +207,15 @@ func GetCLIConfig(c *cli.Context) (ClientConfig, string, error) {
if c.String("corednsaddr") != "" {
cfg.Server.CoreDNSAddr = c.String("corednsaddr")
}
if c.String("grpcwg") != "" {
cfg.Server.GRPCWireGuard = c.String("grpcwg")
}
if c.String("checkininterval") != "" {
cfg.Server.CheckinInterval = c.String("checkininterval")
}
} else {
cfg.Server.GRPCAddress = c.String("grpcserver")
cfg.Server.APIAddress = c.String("apiserver")
cfg.Server.AccessKey = c.String("key")
cfg.Network = c.String("network")
cfg.Node.Network = c.String("network")
cfg.Node.LocalRange = c.String("localrange")
cfg.Server.GRPCWireGuard = c.String("grpcwg")
cfg.Server.GRPCSSL = c.String("grpcssl")
cfg.Server.CoreDNSAddr = c.String("corednsaddr")
cfg.Server.CheckinInterval = c.String("checkininterval")
}
cfg.Node.Name = c.String("name")
cfg.Node.Interface = c.String("interface")
@ -248,7 +224,7 @@ func GetCLIConfig(c *cli.Context) (ClientConfig, string, error) {
cfg.Node.LocalAddress = c.String("localaddress")
cfg.Node.Address = c.String("address")
cfg.Node.Address6 = c.String("addressIPV6")
cfg.Node.Roaming = c.String("roaming")
//cfg.Node.Roaming = c.String("roaming")
cfg.Node.DNSOn = c.String("dnson")
cfg.Node.IsLocal = c.String("islocal")
cfg.Node.IsStatic = c.String("isstatic")
@ -266,10 +242,6 @@ func GetCLIConfig(c *cli.Context) (ClientConfig, string, error) {
cfg.Node.UDPHolePunch = c.String("udpholepunch")
cfg.Node.MTU = int32(c.Int("mtu"))
if cfg.Server.CheckinInterval == "" {
cfg.Server.CheckinInterval = "15"
}
return cfg, privateKey, nil
}

View file

@ -13,18 +13,13 @@ func InstallDaemon(cfg config.ClientConfig) error {
os := runtime.GOOS
var err error
interval := "15"
if cfg.Server.CheckinInterval != "" {
interval = cfg.Server.CheckinInterval
}
switch os {
case "windows":
err = SetupWindowsDaemon()
case "darwin":
err = SetupMacDaemon(interval)
err = SetupMacDaemon()
case "linux":
err = SetupSystemDDaemon(interval)
err = SetupSystemDDaemon()
case "freebsd":
err = SetupFreebsdDaemon()
default:

View file

@ -1,7 +1,6 @@
package daemon
import (
"fmt"
"log"
"os"
"path/filepath"
@ -14,7 +13,7 @@ const MAC_SERVICE_NAME = "com.gravitl.netclient"
const MAC_EXEC_DIR = "/usr/local/bin/"
// SetupMacDaemon - Creates a daemon service from the netclient under LaunchAgents for MacOS
func SetupMacDaemon(interval string) error {
func SetupMacDaemon() error {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
@ -34,7 +33,7 @@ func SetupMacDaemon(interval string) error {
if os.IsNotExist(errN) {
os.Mkdir("~/Library/LaunchAgents", 0755)
}
err = CreateMacService(MAC_SERVICE_NAME, interval)
err = CreateMacService(MAC_SERVICE_NAME)
if err != nil {
return err
}
@ -69,7 +68,7 @@ func StopLaunchD() {
}
// CreateMacService - Creates the mac service file for LaunchDaemons
func CreateMacService(servicename string, interval string) error {
func CreateMacService(servicename string) error {
_, err := os.Stat("/Library/LaunchDaemons")
if os.IsNotExist(err) {
os.Mkdir("/Library/LaunchDaemons", 0755)
@ -77,7 +76,7 @@ func CreateMacService(servicename string, interval string) error {
log.Println("couldnt find or create /Library/LaunchDaemons")
return err
}
daemonstring := MacDaemonString(interval)
daemonstring := MacDaemonString()
daemonbytes := []byte(daemonstring)
if !ncutils.FileExists("/Library/LaunchDaemons/com.gravitl.netclient.plist") {
@ -87,8 +86,8 @@ func CreateMacService(servicename string, interval string) error {
}
// MacDaemonString - the file contents for the mac netclient daemon service (launchdaemon)
func MacDaemonString(interval string) string {
return fmt.Sprintf(`<?xml version='1.0' encoding='UTF-8'?>
func MacDaemonString() string {
return `<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\" >
<plist version='1.0'>
<dict>
@ -100,9 +99,11 @@ func MacDaemonString(interval string) string {
</array>
<key>StandardOutPath</key><string>/etc/netclient/com.gravitl.netclient.log</string>
<key>StandardErrorPath</key><string>/etc/netclient/com.gravitl.netclient.log</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>AbandonProcessGroup</key><true/>
<key>StartInterval</key>
<integer>%s</integer>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
@ -110,7 +111,7 @@ func MacDaemonString(interval string) string {
</dict>
</dict>
</plist>
`, interval)
`
}
// MacTemplateData - struct to represent the mac service

View file

@ -14,7 +14,7 @@ import (
const EXEC_DIR = "/sbin/"
// SetupSystemDDaemon - sets system daemon for supported machines
func SetupSystemDDaemon(interval string) error {
func SetupSystemDDaemon() error {
if ncutils.IsWindows() {
return nil

View file

@ -1,320 +0,0 @@
package functions
import (
"context"
"encoding/json"
"errors"
"os"
"runtime"
"strings"
nodepb "github.com/gravitl/netmaker/grpc"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/auth"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/local"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/wireguard"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
//homedir "github.com/mitchellh/go-homedir"
)
func isDeleteError(err error) bool {
return err != nil && strings.Contains(err.Error(), models.NODE_DELETE)
}
func checkIP(node *models.Node, servercfg config.ServerConfig, cliconf config.ClientConfig, network string) bool {
ipchange := false
var err error
if node.Roaming == "yes" && node.IsStatic != "yes" {
if node.IsLocal == "no" {
extIP, err := ncutils.GetPublicIP()
if err != nil {
ncutils.PrintLog("error encountered checking ip addresses: "+err.Error(), 1)
}
if node.Endpoint != extIP && extIP != "" {
ncutils.PrintLog("endpoint has changed from "+
node.Endpoint+" to "+extIP, 1)
ncutils.PrintLog("updating address", 1)
node.Endpoint = extIP
ipchange = true
}
intIP, err := getPrivateAddr()
if err != nil {
ncutils.PrintLog("error encountered checking ip addresses: "+err.Error(), 1)
}
if node.LocalAddress != intIP && intIP != "" {
ncutils.PrintLog("local Address has changed from "+
node.LocalAddress+" to "+intIP, 1)
ncutils.PrintLog("updating address", 1)
node.LocalAddress = intIP
ipchange = true
}
} else {
localIP, err := ncutils.GetLocalIP(node.LocalRange)
if err != nil {
ncutils.PrintLog("error encountered checking ip addresses: "+err.Error(), 1)
}
if node.Endpoint != localIP && localIP != "" {
ncutils.PrintLog("endpoint has changed from "+
node.Endpoint+" to "+localIP, 1)
ncutils.PrintLog("updating address", 1)
node.Endpoint = localIP
node.LocalAddress = localIP
ipchange = true
}
}
}
if ipchange {
err = config.ModConfig(node)
if err != nil {
ncutils.PrintLog("error modifying config file: "+err.Error(), 1)
return false
}
err = wireguard.SetWGConfig(network, false)
if err != nil {
ncutils.PrintLog("error setting wireguard config: "+err.Error(), 1)
return false
}
}
return ipchange && err == nil
}
// DEPRECATED
// func setDNS(node *models.Node, servercfg config.ServerConfig, nodecfg *models.Node) {
// if nodecfg.DNSOn == "yes" {
// ifacename := node.Interface
// nameserver := servercfg.CoreDNSAddr
// network := node.Network
// local.UpdateDNS(ifacename, network, nameserver)
// }
// }
func checkNodeActions(node *models.Node, networkName string, servercfg config.ServerConfig, localNode *models.Node, cfg *config.ClientConfig) string {
if (node.Action == models.NODE_UPDATE_KEY || localNode.Action == models.NODE_UPDATE_KEY) &&
node.IsStatic != "yes" {
err := wireguard.SetWGKeyConfig(networkName, servercfg.GRPCAddress)
if err != nil {
ncutils.PrintLog("unable to process reset keys request: "+err.Error(), 1)
return ""
}
}
if node.Action == models.NODE_DELETE || localNode.Action == models.NODE_DELETE {
err := RemoveLocalInstance(cfg, networkName)
if err != nil {
ncutils.PrintLog("error deleting locally: "+err.Error(), 1)
}
return models.NODE_DELETE
}
return ""
}
// CheckConfig - checks if current config of client needs update, see flow below
/**
* Pull changes if any (interface refresh)
* - Save it
* Check local changes for (ipAddress, publickey, configfile changes) (interface refresh)
* - Save it
* - Push it
* Pull Peers (sync)
*/
func CheckConfig(cliconf config.ClientConfig) error {
network := cliconf.Network
cfg, err := config.ReadConfig(network)
if err != nil {
return err
}
servercfg := cfg.Server
currentNode := cfg.Node
newNode, err := Pull(network, false)
if isDeleteError(err) {
return RemoveLocalInstance(cfg, network)
}
if err != nil {
return err
}
if newNode.IsPending == "yes" {
return errors.New("node is pending")
}
actionCompleted := checkNodeActions(newNode, network, servercfg, &currentNode, cfg)
if actionCompleted == models.NODE_DELETE {
return errors.New("node has been removed")
}
// Check if ip changed and push if so
checkIP(newNode, servercfg, cliconf, network)
return Push(network)
}
// Pull - pulls the latest config from the server, if manual it will overwrite
func Pull(network string, manual bool) (*models.Node, error) {
cfg, err := config.ReadConfig(network)
if err != nil {
return nil, err
}
node := cfg.Node
//servercfg := cfg.Server
if cfg.Node.IPForwarding == "yes" && !ncutils.IsWindows() {
if err = local.SetIPForwarding(); err != nil {
return nil, err
}
}
var resNode models.Node // just need to fill this with either server calls or client calls
var header metadata.MD
var wcclient nodepb.NodeServiceClient
var ctx context.Context
if cfg.Node.IsServer != "yes" {
conn, err := grpc.Dial(cfg.Server.GRPCAddress,
ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
if err != nil {
ncutils.PrintLog("Cant dial GRPC server: "+err.Error(), 1)
return nil, err
}
defer conn.Close()
wcclient = nodepb.NewNodeServiceClient(conn)
ctx, err = auth.SetJWT(wcclient, network)
if err != nil {
ncutils.PrintLog("Failed to authenticate: "+err.Error(), 1)
return nil, err
}
data, err := json.Marshal(&node)
if err != nil {
ncutils.PrintLog("Failed to parse node config: "+err.Error(), 1)
return nil, err
}
req := &nodepb.Object{
Data: string(data),
Type: nodepb.NODE_TYPE,
}
readres, err := wcclient.ReadNode(ctx, req, grpc.Header(&header))
if err != nil {
return nil, err
}
if err = json.Unmarshal([]byte(readres.Data), &resNode); err != nil {
return nil, err
}
}
// ensure that the OS never changes
resNode.OS = runtime.GOOS
if resNode.PullChanges == "yes" || manual {
// check for interface change
if cfg.Node.Interface != resNode.Interface {
if err = DeleteInterface(cfg.Node.Interface, cfg.Node.PostDown); err != nil {
ncutils.PrintLog("could not delete old interface "+cfg.Node.Interface, 1)
}
}
resNode.PullChanges = "no"
if err = config.ModConfig(&resNode); err != nil {
return nil, err
}
if err = wireguard.SetWGConfig(network, false); err != nil {
return nil, err
}
nodeData, err := json.Marshal(&resNode)
if err != nil {
return &resNode, err
}
if resNode.IsServer != "yes" {
if wcclient == nil || ctx == nil {
return &cfg.Node, errors.New("issue initializing gRPC client")
}
req := &nodepb.Object{
Data: string(nodeData),
Type: nodepb.NODE_TYPE,
Metadata: "",
}
_, err = wcclient.UpdateNode(ctx, req, grpc.Header(&header))
if err != nil {
return &resNode, err
}
}
} else {
if err = wireguard.SetWGConfig(network, true); err != nil {
if errors.Is(err, os.ErrNotExist) && !ncutils.IsFreeBSD() {
return Pull(network, true)
} else {
return nil, err
}
}
}
var bkupErr = config.SaveBackup(network)
if bkupErr != nil {
ncutils.Log("unable to update backup file")
}
return &resNode, err
}
// Push - pushes current client configuration to server
func Push(network string) error {
cfg, err := config.ReadConfig(network)
if err != nil {
return err
}
postnode := cfg.Node
// always set the OS on client
postnode.OS = runtime.GOOS
postnode.SetLastCheckIn()
var header metadata.MD
var wcclient nodepb.NodeServiceClient
conn, err := grpc.Dial(cfg.Server.GRPCAddress,
ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
if err != nil {
ncutils.PrintLog("Cant dial GRPC server: "+err.Error(), 1)
return err
}
defer conn.Close()
wcclient = nodepb.NewNodeServiceClient(conn)
ctx, err := auth.SetJWT(wcclient, network)
if err != nil {
ncutils.PrintLog("Failed to authenticate with server: "+err.Error(), 1)
return err
}
if postnode.IsPending != "yes" {
privateKey, err := wireguard.RetrievePrivKey(network)
if err != nil {
return err
}
privateKeyWG, err := wgtypes.ParseKey(privateKey)
if err != nil {
return err
}
if postnode.PublicKey != privateKeyWG.PublicKey().String() {
postnode.PublicKey = privateKeyWG.PublicKey().String()
}
}
nodeData, err := json.Marshal(&postnode)
if err != nil {
return err
}
req := &nodepb.Object{
Data: string(nodeData),
Type: nodepb.NODE_TYPE,
Metadata: "",
}
data, err := wcclient.UpdateNode(ctx, req, grpc.Header(&header))
if err != nil {
return err
}
err = json.Unmarshal([]byte(data.Data), &postnode)
if err != nil {
return err
}
err = config.ModConfig(&postnode)
return err
}

View file

@ -124,7 +124,7 @@ func Uninstall() error {
ncutils.PrintLog("continuing uninstall without leaving networks", 1)
} else {
for _, network := range networks {
err = LeaveNetwork(network)
err = LeaveNetwork(network, true)
if err != nil {
ncutils.PrintLog("Encounter issue leaving network "+network+": "+err.Error(), 1)
}
@ -147,13 +147,16 @@ func Uninstall() error {
}
// LeaveNetwork - client exits a network
func LeaveNetwork(network string) error {
func LeaveNetwork(network string, force bool) error {
cfg, err := config.ReadConfig(network)
if err != nil {
return err
}
servercfg := cfg.Server
node := cfg.Node
if node.NetworkSettings.IsComms == "yes" && !force {
return errors.New("COMMS_NET - You are trying to leave the comms network. This will break network updates. Unless you re-join. If you really want to leave, run with --force=yes.")
}
if node.IsServer != "yes" {
var wcclient nodepb.NodeServiceClient
@ -317,58 +320,3 @@ func WipeLocal(network string) error {
}
return err
}
func getLocalIP(node models.Node) string {
var local string
ifaces, err := net.Interfaces()
if err != nil {
return local
}
_, localrange, err := net.ParseCIDR(node.LocalRange)
if err != nil {
return local
}
found := false
for _, i := range ifaces {
if i.Flags&net.FlagUp == 0 {
continue // interface down
}
if i.Flags&net.FlagLoopback != 0 {
continue // loopback interface
}
addrs, err := i.Addrs()
if err != nil {
return local
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
if !found {
ip = v.IP
local = ip.String()
if node.IsLocal == "yes" {
found = localrange.Contains(ip)
} else {
found = true
}
}
case *net.IPAddr:
if !found {
ip = v.IP
local = ip.String()
if node.IsLocal == "yes" {
found = localrange.Contains(ip)
} else {
found = true
}
}
}
}
}
return local
}

View file

@ -2,12 +2,10 @@ package functions
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"os/signal"
"runtime"
"strings"
"sync"
"syscall"
@ -19,14 +17,11 @@ import (
"github.com/gravitl/netmaker/netclient/auth"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/daemon"
"github.com/gravitl/netmaker/netclient/local"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/wireguard"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// == Message Caches ==
// var keepalive = new(sync.Map)
var messageCache = new(sync.Map)
var networkcontext = new(sync.Map)
@ -38,63 +33,159 @@ type cachedMessage struct {
LastSeen time.Time
}
func insert(network, which, cache string) {
var newMessage = cachedMessage{
Message: cache,
LastSeen: time.Now(),
}
messageCache.Store(fmt.Sprintf("%s%s", network, which), newMessage)
}
func read(network, which string) string {
val, isok := messageCache.Load(fmt.Sprintf("%s%s", network, which))
if isok {
var readMessage = val.(cachedMessage) // fetch current cached message
if readMessage.LastSeen.IsZero() {
return ""
}
if time.Now().After(readMessage.LastSeen.Add(time.Minute * 10)) { // check if message has been there over a minute
messageCache.Delete(fmt.Sprintf("%s%s", network, which)) // remove old message if expired
return ""
}
return readMessage.Message // return current message if not expired
}
return ""
}
// == End Message Caches ==
// Daemon runs netclient daemon from command line
func Daemon() error {
networks, err := ncutils.GetSystemNetworks()
// == initial pull of all networks ==
networks, _ := ncutils.GetSystemNetworks()
for _, network := range networks {
var cfg config.ClientConfig
cfg.Network = network
cfg.ReadConfig()
initialPull(cfg.Network)
}
// == get all the comms networks on machine ==
commsNetworks, err := getCommsNetworks(networks[:])
if err != nil {
return err
return errors.New("no comm networks exist")
}
for _, network := range networks {
// == subscribe to all nodes on each comms network on machine ==
for currCommsNet := range commsNetworks {
ncutils.PrintLog("started comms network daemon, "+currCommsNet, 1)
ctx, cancel := context.WithCancel(context.Background())
networkcontext.Store(network, cancel)
go MessageQueue(ctx, network)
networkcontext.Store(currCommsNet, cancel)
go messageQueue(ctx, currCommsNet)
}
// == add waitgroup and cancel for checkin routine ==
wg := sync.WaitGroup{}
ctx, cancel := context.WithCancel(context.Background())
wg.Add(1)
go Checkin(ctx, &wg, commsNetworks)
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGTERM, os.Interrupt)
signal.Notify(quit, syscall.SIGTERM, os.Interrupt, os.Kill)
<-quit
for _, network := range networks {
if cancel, ok := networkcontext.Load(network); ok {
for currCommsNet := range commsNetworks {
if cancel, ok := networkcontext.Load(currCommsNet); ok {
cancel.(context.CancelFunc)()
}
}
ncutils.Log("all done")
cancel()
ncutils.Log("shutting down netclient daemon")
wg.Wait()
ncutils.Log("shutdown complete")
return nil
}
// SetupMQTT creates a connection to broker and return client
func SetupMQTT(cfg *config.ClientConfig, publish bool) mqtt.Client {
// UpdateKeys -- updates private key and returns new publickey
func UpdateKeys(nodeCfg *config.ClientConfig, client mqtt.Client) error {
ncutils.Log("received message to update wireguard keys for network " + nodeCfg.Network)
key, err := wgtypes.GeneratePrivateKey()
if err != nil {
ncutils.Log("error generating privatekey " + err.Error())
return err
}
file := ncutils.GetNetclientPathSpecific() + nodeCfg.Node.Interface + ".conf"
if err := wireguard.UpdatePrivateKey(file, key.String()); err != nil {
ncutils.Log("error updating wireguard key " + err.Error())
return err
}
if storeErr := wireguard.StorePrivKey(key.String(), nodeCfg.Network); storeErr != nil {
ncutils.Log("failed to save private key" + storeErr.Error())
return storeErr
}
nodeCfg.Node.PublicKey = key.PublicKey().String()
var commsCfg = getCommsCfgByNode(&nodeCfg.Node)
PublishNodeUpdate(&commsCfg, nodeCfg)
return nil
}
// PingServer -- checks if server is reachable
// use commsCfg only*
func PingServer(commsCfg *config.ClientConfig) error {
node := getServerAddress(commsCfg)
pinger, err := ping.NewPinger(node)
if err != nil {
return err
}
pinger.Timeout = 2 * time.Second
pinger.Run()
stats := pinger.Statistics()
if stats.PacketLoss == 100 {
return errors.New("ping error")
}
return nil
}
// == Private ==
// sets MQ client subscriptions for a specific node config
// should be called for each node belonging to a given comms network
func setSubscriptions(client mqtt.Client, nodeCfg *config.ClientConfig) {
if nodeCfg.DebugOn {
if token := client.Subscribe("#", 0, nil); token.Wait() && token.Error() != nil {
ncutils.Log(token.Error().Error())
return
}
ncutils.Log("subscribed to all topics for debugging purposes")
}
if token := client.Subscribe(fmt.Sprintf("update/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID), 0, mqtt.MessageHandler(NodeUpdate)); token.Wait() && token.Error() != nil {
ncutils.Log(token.Error().Error())
return
}
if nodeCfg.DebugOn {
ncutils.Log(fmt.Sprintf("subscribed to node updates for node %s update/%s/%s", nodeCfg.Node.Name, nodeCfg.Node.Network, nodeCfg.Node.ID))
}
if token := client.Subscribe(fmt.Sprintf("peers/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID), 0, mqtt.MessageHandler(UpdatePeers)); token.Wait() && token.Error() != nil {
ncutils.Log(token.Error().Error())
return
}
if nodeCfg.DebugOn {
ncutils.Log(fmt.Sprintf("subscribed to peer updates for node %s peers/%s/%s", nodeCfg.Node.Name, nodeCfg.Node.Network, nodeCfg.Node.ID))
}
}
// on a delete usually, pass in the nodecfg to unsubscribe client broker communications
// for the node in nodeCfg
func unsubscribeNode(client mqtt.Client, nodeCfg *config.ClientConfig) {
client.Unsubscribe(fmt.Sprintf("update/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID))
var ok = true
if token := client.Unsubscribe(fmt.Sprintf("update/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID)); token.Wait() && token.Error() != nil {
ncutils.PrintLog("unable to unsubscribe from updates for node "+nodeCfg.Node.Name+"\n"+token.Error().Error(), 1)
ok = false
}
if token := client.Unsubscribe(fmt.Sprintf("peers/%s/%s", nodeCfg.Node.Network, nodeCfg.Node.ID)); token.Wait() && token.Error() != nil {
ncutils.PrintLog("unable to unsubscribe from peer updates for node "+nodeCfg.Node.Name+"\n"+token.Error().Error(), 1)
ok = false
}
if ok {
ncutils.PrintLog("successfully unsubscribed node "+nodeCfg.Node.ID+" : "+nodeCfg.Node.Name, 0)
}
}
// sets up Message Queue and subsribes/publishes updates to/from server
// the client should subscribe to ALL nodes that exist on unique comms network locally
func messageQueue(ctx context.Context, commsNet string) {
var commsCfg config.ClientConfig
commsCfg.Network = commsNet
commsCfg.ReadConfig()
ncutils.Log("netclient daemon started for network: " + commsNet)
client := setupMQTT(&commsCfg, false)
defer client.Disconnect(250)
<-ctx.Done()
ncutils.Log("shutting down daemon for comms network " + commsNet)
}
// setupMQTT creates a connection to broker and return client
// utilizes comms client configs to setup connections
func setupMQTT(commsCfg *config.ClientConfig, publish bool) mqtt.Client {
opts := mqtt.NewClientOptions()
server := getServerAddress(cfg)
opts.AddBroker(server + ":1883")
id := ncutils.MakeRandomString(23)
opts.ClientID = id
server := getServerAddress(commsCfg)
opts.AddBroker(server + ":1883") // TODO get the appropriate port of the comms mq server
opts.ClientID = ncutils.MakeRandomString(23) // helps avoid id duplication on broker
opts.SetDefaultPublishHandler(All)
opts.SetAutoReconnect(true)
opts.SetConnectRetry(true)
@ -103,43 +194,26 @@ func SetupMQTT(cfg *config.ClientConfig, publish bool) mqtt.Client {
opts.SetWriteTimeout(time.Minute)
opts.SetOnConnectHandler(func(client mqtt.Client) {
if !publish {
if cfg.DebugOn {
if token := client.Subscribe("#", 0, nil); token.Wait() && token.Error() != nil {
ncutils.Log(token.Error().Error())
return
}
ncutils.Log("subscribed to all topics for debugging purposes")
networks, err := ncutils.GetSystemNetworks()
if err != nil {
ncutils.Log("error retriving networks " + err.Error())
}
if token := client.Subscribe(fmt.Sprintf("update/%s/%s", cfg.Node.Network, cfg.Node.ID), 0, mqtt.MessageHandler(NodeUpdate)); token.Wait() && token.Error() != nil {
ncutils.Log(token.Error().Error())
return
for _, network := range networks {
var currNodeCfg config.ClientConfig
currNodeCfg.Network = network
currNodeCfg.ReadConfig()
setSubscriptions(client, &currNodeCfg)
}
if cfg.DebugOn {
ncutils.Log(fmt.Sprintf("subscribed to node updates for node %s update/%s/%s", cfg.Node.Name, cfg.Node.Network, cfg.Node.ID))
}
if token := client.Subscribe(fmt.Sprintf("peers/%s/%s", cfg.Node.Network, cfg.Node.ID), 0, mqtt.MessageHandler(UpdatePeers)); token.Wait() && token.Error() != nil {
ncutils.Log(token.Error().Error())
return
}
if cfg.DebugOn {
ncutils.Log(fmt.Sprintf("subscribed to peer updates for node %s peers/%s/%s", cfg.Node.Name, cfg.Node.Network, cfg.Node.ID))
}
opts.SetOrderMatters(true)
opts.SetResumeSubs(true)
}
})
opts.SetOrderMatters(true)
opts.SetResumeSubs(true)
opts.SetConnectionLostHandler(func(c mqtt.Client, e error) {
ncutils.Log("detected broker connection lost, running pull for " + cfg.Node.Network)
_, err := Pull(cfg.Node.Network, true)
ncutils.Log("detected broker connection lost, running pull for " + commsCfg.Node.Network)
_, err := Pull(commsCfg.Node.Network, true)
if err != nil {
ncutils.Log("could not run pull, server unreachable: " + err.Error())
ncutils.Log("waiting to retry...")
/*
//Consider putting in logic to restart - daemon may take long time to refresh
time.Sleep(time.Minute * 5)
ncutils.Log("restarting netclient")
daemon.Restart()
*/
}
ncutils.Log("connection re-established with mqtt server")
})
@ -149,10 +223,10 @@ func SetupMQTT(cfg *config.ClientConfig, publish bool) mqtt.Client {
for {
//if after 12 seconds, try a gRPC pull on the last try
if time.Now().After(tperiod) {
ncutils.Log("running pull for " + cfg.Node.Network)
_, err := Pull(cfg.Node.Network, true)
ncutils.Log("running pull for " + commsCfg.Node.Network)
_, err := Pull(commsCfg.Node.Network, true)
if err != nil {
ncutils.Log("could not run pull, exiting " + cfg.Node.Network + " setup: " + err.Error())
ncutils.Log("could not run pull, exiting " + commsCfg.Node.Network + " setup: " + err.Error())
return client
}
time.Sleep(time.Second)
@ -160,10 +234,10 @@ func SetupMQTT(cfg *config.ClientConfig, publish bool) mqtt.Client {
if token := client.Connect(); token.Wait() && token.Error() != nil {
ncutils.Log("unable to connect to broker, retrying ...")
if time.Now().After(tperiod) {
ncutils.Log("could not connect to broker, exiting " + cfg.Node.Network + " setup: " + token.Error().Error())
ncutils.Log("could not connect to broker, exiting " + commsCfg.Node.Network + " setup: " + token.Error().Error())
if strings.Contains(token.Error().Error(), "connectex") || strings.Contains(token.Error().Error(), "i/o timeout") {
ncutils.PrintLog("connection issue detected.. pulling and restarting daemon", 0)
Pull(cfg.Node.Network, true)
Pull(commsCfg.Node.Network, true)
daemon.Restart()
}
return client
@ -176,341 +250,14 @@ func SetupMQTT(cfg *config.ClientConfig, publish bool) mqtt.Client {
return client
}
// MessageQueue sets up Message Queue and subsribes/publishes updates to/from server
func MessageQueue(ctx context.Context, network string) {
ncutils.Log("netclient go routine started for " + network)
var cfg config.ClientConfig
cfg.Network = network
initialPull(cfg.Network)
cfg.ReadConfig()
ncutils.Log("daemon started for network: " + network)
client := SetupMQTT(&cfg, false)
defer client.Disconnect(250)
wg := &sync.WaitGroup{}
wg.Add(2)
checkinctx, checkincancel := context.WithCancel(context.Background())
go Checkin(checkinctx, wg, &cfg, network)
<-ctx.Done()
checkincancel()
ncutils.Log("shutting down message queue for network " + network)
wg.Wait()
ncutils.Log("shutdown complete")
}
// All -- mqtt message hander for all ('#') topics
var All mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
ncutils.Log("default message handler -- received message but not handling")
ncutils.Log("Topic: " + string(msg.Topic()))
//ncutils.Log("Message: " + string(msg.Payload()))
}
// NodeUpdate -- mqtt message handler for /update/<NodeID> topic
func NodeUpdate(client mqtt.Client, msg mqtt.Message) {
var newNode models.Node
var cfg config.ClientConfig
var network = parseNetworkFromTopic(msg.Topic())
cfg.Network = network
cfg.ReadConfig()
data, dataErr := decryptMsg(&cfg, msg.Payload())
if dataErr != nil {
return
}
err := json.Unmarshal([]byte(data), &newNode)
if err != nil {
ncutils.Log("error unmarshalling node update data" + err.Error())
return
}
ncutils.Log("received message to update node " + newNode.Name)
// see if cache hit, if so skip
var currentMessage = read(newNode.Network, lastNodeUpdate)
if currentMessage == string(data) {
return
}
insert(newNode.Network, lastNodeUpdate, string(data)) // store new message in cache
//check if interface name has changed if so delete.
if cfg.Node.Interface != newNode.Interface {
if err = wireguard.RemoveConf(cfg.Node.Interface, true); err != nil {
ncutils.PrintLog("could not delete old interface "+cfg.Node.Interface+": "+err.Error(), 1)
}
}
newNode.PullChanges = "no"
//ensure that OS never changes
newNode.OS = runtime.GOOS
// check if interface needs to delta
ifaceDelta := ncutils.IfaceDelta(&cfg.Node, &newNode)
shouldDNSChange := cfg.Node.DNSOn != newNode.DNSOn
cfg.Node = newNode
switch newNode.Action {
case models.NODE_DELETE:
if cancel, ok := networkcontext.Load(newNode.Network); ok {
ncutils.Log("cancelling message queue context for " + newNode.Network)
cancel.(context.CancelFunc)()
} else {
ncutils.Log("failed to kill go routines for network " + newNode.Network)
}
ncutils.PrintLog(fmt.Sprintf("received delete request for %s", cfg.Node.Name), 1)
if err = LeaveNetwork(cfg.Node.Network); err != nil {
if !strings.Contains("rpc error", err.Error()) {
ncutils.PrintLog(fmt.Sprintf("failed to leave, please check that local files for network %s were removed", cfg.Node.Network), 1)
}
ncutils.PrintLog(fmt.Sprintf("%s was removed", cfg.Node.Name), 1)
return
}
ncutils.PrintLog(fmt.Sprintf("%s was removed", cfg.Node.Name), 1)
return
case models.NODE_UPDATE_KEY:
if err := UpdateKeys(&cfg, client); err != nil {
ncutils.PrintLog("err updating wireguard keys: "+err.Error(), 1)
}
case models.NODE_NOOP:
default:
}
// Save new config
cfg.Node.Action = models.NODE_NOOP
if err := config.Write(&cfg, cfg.Network); err != nil {
ncutils.PrintLog("error updating node configuration: "+err.Error(), 1)
}
nameserver := cfg.Server.CoreDNSAddr
privateKey, err := wireguard.RetrievePrivKey(newNode.Network)
if err != nil {
ncutils.Log("error reading PrivateKey " + err.Error())
return
}
file := ncutils.GetNetclientPathSpecific() + cfg.Node.Interface + ".conf"
if err := wireguard.UpdateWgInterface(file, privateKey, nameserver, newNode); err != nil {
ncutils.Log("error updating wireguard config " + err.Error())
return
}
if ifaceDelta {
ncutils.Log("applying WG conf to " + file)
err = wireguard.ApplyConf(&cfg.Node, cfg.Node.Interface, file)
if err != nil {
ncutils.Log("error restarting wg after node update " + err.Error())
return
}
time.Sleep(time.Second >> 1)
// if err = Resubscribe(client, &cfg); err != nil {
// ncutils.Log("error resubscribing after interface change " + err.Error())
// return
// }
if newNode.DNSOn == "yes" {
for _, server := range newNode.NetworkSettings.DefaultServerAddrs {
if server.IsLeader {
go local.SetDNSWithRetry(newNode, server.Address)
break
}
}
}
}
//deal with DNS
if newNode.DNSOn != "yes" && shouldDNSChange && cfg.Node.Interface != "" {
ncutils.Log("settng DNS off")
_, err := ncutils.RunCmd("/usr/bin/resolvectl revert "+cfg.Node.Interface, true)
if err != nil {
ncutils.Log("error applying dns" + err.Error())
}
}
}
// UpdatePeers -- mqtt message handler for peers/<Network>/<NodeID> topic
func UpdatePeers(client mqtt.Client, msg mqtt.Message) {
var peerUpdate models.PeerUpdate
var network = parseNetworkFromTopic(msg.Topic())
var cfg = config.ClientConfig{}
cfg.Network = network
cfg.ReadConfig()
data, dataErr := decryptMsg(&cfg, msg.Payload())
if dataErr != nil {
return
}
err := json.Unmarshal([]byte(data), &peerUpdate)
if err != nil {
ncutils.Log("error unmarshalling peer data")
return
}
// see if cached hit, if so skip
var currentMessage = read(peerUpdate.Network, lastPeerUpdate)
if currentMessage == string(data) {
return
}
insert(peerUpdate.Network, lastPeerUpdate, string(data))
file := ncutils.GetNetclientPathSpecific() + cfg.Node.Interface + ".conf"
err = wireguard.UpdateWgPeers(file, peerUpdate.Peers)
if err != nil {
ncutils.Log("error updating wireguard peers" + err.Error())
return
}
//err = wireguard.SyncWGQuickConf(cfg.Node.Interface, file)
var iface = cfg.Node.Interface
if ncutils.IsMac() {
iface, err = local.GetMacIface(cfg.Node.Address)
if err != nil {
ncutils.Log("error retrieving mac iface: " + err.Error())
return
}
}
err = wireguard.SetPeers(iface, cfg.Node.Address, cfg.Node.PersistentKeepalive, peerUpdate.Peers)
if err != nil {
ncutils.Log("error syncing wg after peer update: " + err.Error())
return
}
}
// MonitorKeepalive - checks time last server keepalive received. If more than 3+ minutes, notify and resubscribe
// func MonitorKeepalive(ctx context.Context, wg *sync.WaitGroup, client mqtt.Client, cfg *config.ClientConfig) {
// defer wg.Done()
// for {
// select {
// case <-ctx.Done():
// ncutils.Log("cancel recieved, monitor keepalive exiting")
// return
// case <-time.After(time.Second * 150):
// var keepalivetime time.Time
// keepaliveval, ok := keepalive.Load(cfg.Node.Network)
// if ok {
// keepalivetime = keepaliveval.(time.Time)
// if !keepalivetime.IsZero() && time.Since(keepalivetime) > time.Second*120 { // more than 2+ minutes
// // ncutils.Log("server keepalive not recieved recently, resubscribe to message queue")
// // err := Resubscribe(client, cfg)
// // if err != nil {
// // ncutils.Log("closing " + err.Error())
// // }
// ncutils.Log("maybe wanna call something")
// }
// }
// }
// }
// }
// ServerKeepAlive -- handler to react to keepalive messages published by server
// func ServerKeepAlive(client mqtt.Client, msg mqtt.Message) {
// var currentTime = time.Now()
// keepalive.Store(parseNetworkFromTopic(msg.Topic()), currentTime)
// ncutils.PrintLog("received server keepalive at "+currentTime.String(), 2)
// }
// UpdateKeys -- updates private key and returns new publickey
func UpdateKeys(cfg *config.ClientConfig, client mqtt.Client) error {
ncutils.Log("received message to update keys")
key, err := wgtypes.GeneratePrivateKey()
if err != nil {
ncutils.Log("error generating privatekey " + err.Error())
return err
}
file := ncutils.GetNetclientPathSpecific() + cfg.Node.Interface + ".conf"
if err := wireguard.UpdatePrivateKey(file, key.String()); err != nil {
ncutils.Log("error updating wireguard key " + err.Error())
return err
}
cfg.Node.PublicKey = key.PublicKey().String()
if err := config.ModConfig(&cfg.Node); err != nil {
ncutils.Log("error updating local config " + err.Error())
}
PublishNodeUpdate(cfg)
if err = wireguard.ApplyConf(&cfg.Node, cfg.Node.Interface, file); err != nil {
ncutils.Log("error applying new config " + err.Error())
// publishes a message to server to update peers on this peer's behalf
func publishSignal(commsCfg, nodeCfg *config.ClientConfig, signal byte) error {
if err := publish(commsCfg, nodeCfg, fmt.Sprintf("signal/%s", nodeCfg.Node.ID), []byte{signal}, 1); err != nil {
return err
}
return nil
}
// Checkin -- go routine that checks for public or local ip changes, publishes changes
// if there are no updates, simply "pings" the server as a checkin
func Checkin(ctx context.Context, wg *sync.WaitGroup, cfg *config.ClientConfig, network string) {
defer wg.Done()
for {
select {
case <-ctx.Done():
ncutils.Log("Checkin cancelled")
return
//delay should be configuraable -> use cfg.Node.NetworkSettings.DefaultCheckInInterval ??
case <-time.After(time.Second * 60):
// ncutils.Log("Checkin running")
//read latest config
cfg.ReadConfig()
if cfg.Node.Roaming == "yes" && cfg.Node.IsStatic != "yes" {
extIP, err := ncutils.GetPublicIP()
if err != nil {
ncutils.PrintLog("error encountered checking ip addresses: "+err.Error(), 1)
}
if cfg.Node.Endpoint != extIP && extIP != "" {
ncutils.PrintLog("endpoint has changed from "+cfg.Node.Endpoint+" to "+extIP, 1)
cfg.Node.Endpoint = extIP
if err := PublishNodeUpdate(cfg); err != nil {
ncutils.Log("could not publish endpoint change")
}
}
intIP, err := getPrivateAddr()
if err != nil {
ncutils.PrintLog("error encountered checking ip addresses: "+err.Error(), 1)
}
if cfg.Node.LocalAddress != intIP && intIP != "" {
ncutils.PrintLog("local Address has changed from "+cfg.Node.LocalAddress+" to "+intIP, 1)
cfg.Node.LocalAddress = intIP
if err := PublishNodeUpdate(cfg); err != nil {
ncutils.Log("could not publish local address change")
}
}
} else {
localIP, err := ncutils.GetLocalIP(cfg.Node.LocalRange)
if err != nil {
ncutils.PrintLog("error encountered checking ip addresses: "+err.Error(), 1)
}
if cfg.Node.Endpoint != localIP && localIP != "" {
ncutils.PrintLog("endpoint has changed from "+cfg.Node.Endpoint+" to "+localIP, 1)
cfg.Node.Endpoint = localIP
if err := PublishNodeUpdate(cfg); err != nil {
ncutils.Log("could not publish localip change")
}
}
}
if err := pingServer(cfg); err != nil {
ncutils.PrintLog("could not ping server "+err.Error(), 0)
}
Hello(cfg, network)
// ncutils.Log("Checkin complete")
}
}
}
// PublishNodeUpdates -- saves node and pushes changes to broker
func PublishNodeUpdate(cfg *config.ClientConfig) error {
if err := config.Write(cfg, cfg.Network); err != nil {
return err
}
data, err := json.Marshal(cfg.Node)
if err != nil {
return err
}
if err = publish(cfg, fmt.Sprintf("update/%s", cfg.Node.ID), data); err != nil {
return err
}
return nil
}
// Hello -- ping the broker to let server know node is alive and doing fine
func Hello(cfg *config.ClientConfig, network string) {
if err := publish(cfg, fmt.Sprintf("ping/%s", cfg.Node.ID), []byte(ncutils.Version)); err != nil {
ncutils.Log(fmt.Sprintf("error publishing ping, %v", err))
ncutils.Log("running pull on " + cfg.Node.Network + " to reconnect")
_, err := Pull(cfg.Node.Network, true)
if err != nil {
ncutils.Log("could not run pull on " + cfg.Node.Network + ", error: " + err.Error())
}
}
}
// == Private ==
func initialPull(network string) {
ncutils.Log("pulling latest config for " + network)
var configPath = fmt.Sprintf("%snetconfig-%s", ncutils.GetNetclientPathSpecific(), network)
@ -539,67 +286,28 @@ func initialPull(network string) {
}
}
func publish(cfg *config.ClientConfig, dest string, msg []byte) error {
// setup the keys
trafficPrivKey, err := auth.RetrieveTrafficKey(cfg.Node.Network)
if err != nil {
return err
}
serverPubKey, err := ncutils.ConvertBytesToKey(cfg.Node.TrafficKeys.Server)
if err != nil {
return err
}
client := SetupMQTT(cfg, true)
defer client.Disconnect(250)
encrypted, err := ncutils.BoxEncrypt(msg, serverPubKey, trafficPrivKey)
if err != nil {
return err
}
if token := client.Publish(dest, 0, false, encrypted); token.Wait() && token.Error() != nil {
return token.Error()
}
return nil
}
func parseNetworkFromTopic(topic string) string {
return strings.Split(topic, "/")[1]
}
func decryptMsg(cfg *config.ClientConfig, msg []byte) ([]byte, error) {
// should only ever use node client configs
func decryptMsg(nodeCfg *config.ClientConfig, msg []byte) ([]byte, error) {
if len(msg) <= 24 { // make sure message is of appropriate length
return nil, fmt.Errorf("recieved invalid message from broker %s", string(msg))
return nil, fmt.Errorf("recieved invalid message from broker %v", msg)
}
// setup the keys
diskKey, keyErr := auth.RetrieveTrafficKey(cfg.Node.Network)
diskKey, keyErr := auth.RetrieveTrafficKey(nodeCfg.Node.Network)
if keyErr != nil {
return nil, keyErr
}
serverPubKey, err := ncutils.ConvertBytesToKey(cfg.Node.TrafficKeys.Server)
serverPubKey, err := ncutils.ConvertBytesToKey(nodeCfg.Node.TrafficKeys.Server)
if err != nil {
return nil, err
}
return ncutils.BoxDecrypt(msg, serverPubKey, diskKey)
}
func pingServer(cfg *config.ClientConfig) error {
node := getServerAddress(cfg)
pinger, err := ping.NewPinger(node)
if err != nil {
return err
}
pinger.Timeout = 2 * time.Second
pinger.Run()
stats := pinger.Statistics()
if stats.PacketLoss == 100 {
return errors.New("ping error")
}
return nil
return ncutils.DeChunk(msg, serverPubKey, diskKey)
}
func getServerAddress(cfg *config.ClientConfig) string {
@ -611,3 +319,49 @@ func getServerAddress(cfg *config.ClientConfig) string {
}
return server.Address
}
func getCommsNetworks(networks []string) (map[string]bool, error) {
var cfg config.ClientConfig
var response = make(map[string]bool, 1)
for _, network := range networks {
cfg.Network = network
cfg.ReadConfig()
response[cfg.Node.CommID] = true
}
return response, nil
}
func getCommsCfgByNode(node *models.Node) config.ClientConfig {
var commsCfg config.ClientConfig
commsCfg.Network = node.Network
commsCfg.ReadConfig()
return commsCfg
}
// == Message Caches ==
func insert(network, which, cache string) {
var newMessage = cachedMessage{
Message: cache,
LastSeen: time.Now(),
}
messageCache.Store(fmt.Sprintf("%s%s", network, which), newMessage)
}
func read(network, which string) string {
val, isok := messageCache.Load(fmt.Sprintf("%s%s", network, which))
if isok {
var readMessage = val.(cachedMessage) // fetch current cached message
if readMessage.LastSeen.IsZero() {
return ""
}
if time.Now().After(readMessage.LastSeen.Add(time.Minute * 10)) { // check if message has been there over a minute
messageCache.Delete(fmt.Sprintf("%s%s", network, which)) // remove old message if expired
return ""
}
return readMessage.Message // return current message if not expired
}
return ""
}
// == End Message Caches ==

View file

@ -25,7 +25,7 @@ import (
)
// JoinNetwork - helps a client join a network
func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
func JoinNetwork(cfg config.ClientConfig, privateKey string, iscomms bool) error {
if cfg.Node.Network == "" {
return errors.New("no network provided")
}
@ -103,7 +103,8 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
if cfg.Node.MacAddress == "" {
macs, err := ncutils.GetMacAddr()
if err != nil {
return err
//if macaddress can't be found set to random string
cfg.Node.MacAddress = ncutils.MakeRandomString(18)
} else {
cfg.Node.MacAddress = macs[0]
}
@ -124,14 +125,14 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
cfg.Node.Name = formatName(cfg.Node)
// differentiate between client/server here
var node = models.Node{
Password: cfg.Node.Password,
Address: cfg.Node.Address,
Address6: cfg.Node.Address6,
ID: cfg.Node.ID,
MacAddress: cfg.Node.MacAddress,
AccessKey: cfg.Server.AccessKey,
IsStatic: cfg.Node.IsStatic,
Roaming: cfg.Node.Roaming,
Password: cfg.Node.Password,
Address: cfg.Node.Address,
Address6: cfg.Node.Address6,
ID: cfg.Node.ID,
MacAddress: cfg.Node.MacAddress,
AccessKey: cfg.Server.AccessKey,
IsStatic: cfg.Node.IsStatic,
//Roaming: cfg.Node.Roaming,
Network: cfg.Network,
ListenPort: cfg.Node.ListenPort,
PostUp: cfg.Node.PostUp,
@ -143,7 +144,6 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
DNSOn: cfg.Node.DNSOn,
Name: cfg.Node.Name,
Endpoint: cfg.Node.Endpoint,
SaveConfig: cfg.Node.SaveConfig,
UDPHolePunch: cfg.Node.UDPHolePunch,
TrafficKeys: cfg.Node.TrafficKeys,
OS: runtime.GOOS,
@ -242,7 +242,8 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
go func() {
if !local.SetDNSWithRetry(node, server.Address) {
cfg.Node.DNSOn = "no"
PublishNodeUpdate(&cfg)
var currentCommsCfg = getCommsCfgByNode(&cfg.Node)
PublishNodeUpdate(&currentCommsCfg, &cfg)
}
}()
break
@ -250,16 +251,18 @@ func JoinNetwork(cfg config.ClientConfig, privateKey string) error {
}
}
if cfg.Daemon != "off" {
err = daemon.InstallDaemon(cfg)
}
if err != nil {
return err
} else {
daemon.Restart()
if !iscomms {
if cfg.Daemon != "off" {
err = daemon.InstallDaemon(cfg)
}
if err != nil {
return err
} else {
daemon.Restart()
}
}
return err
return nil
}
// format name appropriately. Set to blank on failure

View file

@ -0,0 +1,192 @@
package functions
import (
"encoding/json"
"fmt"
"runtime"
"strings"
"time"
mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/local"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/wireguard"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
// All -- mqtt message hander for all ('#') topics
var All mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
ncutils.Log("default message handler -- received message but not handling")
ncutils.Log("Topic: " + string(msg.Topic()))
//ncutils.Log("Message: " + string(msg.Payload()))
}
// NodeUpdate -- mqtt message handler for /update/<NodeID> topic
func NodeUpdate(client mqtt.Client, msg mqtt.Message) {
var newNode models.Node
var nodeCfg config.ClientConfig
var network = parseNetworkFromTopic(msg.Topic())
nodeCfg.Network = network
nodeCfg.ReadConfig()
var commsCfg = getCommsCfgByNode(&nodeCfg.Node)
data, dataErr := decryptMsg(&nodeCfg, msg.Payload())
if dataErr != nil {
return
}
err := json.Unmarshal([]byte(data), &newNode)
if err != nil {
ncutils.Log("error unmarshalling node update data" + err.Error())
return
}
ncutils.Log("received message to update node " + newNode.Name)
// see if cache hit, if so skip
var currentMessage = read(newNode.Network, lastNodeUpdate)
if currentMessage == string(data) {
return
}
insert(newNode.Network, lastNodeUpdate, string(data)) // store new message in cache
// ensure that OS never changes
newNode.OS = runtime.GOOS
// check if interface needs to delta
ifaceDelta := ncutils.IfaceDelta(&nodeCfg.Node, &newNode)
shouldDNSChange := nodeCfg.Node.DNSOn != newNode.DNSOn
hubChange := nodeCfg.Node.IsHub != newNode.IsHub
nodeCfg.Node = newNode
switch newNode.Action {
case models.NODE_DELETE:
ncutils.PrintLog(fmt.Sprintf("received delete request for %s", nodeCfg.Node.Name), 0)
unsubscribeNode(client, &nodeCfg)
if err = LeaveNetwork(nodeCfg.Node.Network, true); err != nil {
if !strings.Contains("rpc error", err.Error()) {
ncutils.PrintLog(fmt.Sprintf("failed to leave, please check that local files for network %s were removed", nodeCfg.Node.Network), 0)
return
}
}
ncutils.PrintLog(fmt.Sprintf("%s was removed", nodeCfg.Node.Name), 0)
return
case models.NODE_UPDATE_KEY:
// == get the current key for node ==
oldPrivateKey, retErr := wireguard.RetrievePrivKey(nodeCfg.Network)
if retErr != nil {
break
}
if err := UpdateKeys(&nodeCfg, client); err != nil {
ncutils.PrintLog("err updating wireguard keys, reusing last key\n"+err.Error(), 0)
if key, parseErr := wgtypes.ParseKey(oldPrivateKey); parseErr == nil {
wireguard.StorePrivKey(key.String(), nodeCfg.Network)
nodeCfg.Node.PublicKey = key.PublicKey().String()
}
}
ifaceDelta = true
case models.NODE_NOOP:
default:
}
// Save new config
nodeCfg.Node.Action = models.NODE_NOOP
if err := config.Write(&nodeCfg, nodeCfg.Network); err != nil {
ncutils.PrintLog("error updating node configuration: "+err.Error(), 0)
}
nameserver := nodeCfg.Server.CoreDNSAddr
privateKey, err := wireguard.RetrievePrivKey(newNode.Network)
if err != nil {
ncutils.Log("error reading PrivateKey " + err.Error())
return
}
file := ncutils.GetNetclientPathSpecific() + nodeCfg.Node.Interface + ".conf"
if err := wireguard.UpdateWgInterface(file, privateKey, nameserver, newNode); err != nil {
ncutils.Log("error updating wireguard config " + err.Error())
return
}
if ifaceDelta { // if a change caused an ifacedelta we need to notify the server to update the peers
ncutils.Log("applying WG conf to " + file)
err = wireguard.ApplyConf(&nodeCfg.Node, nodeCfg.Node.Interface, file)
if err != nil {
ncutils.Log("error restarting wg after node update " + err.Error())
return
}
time.Sleep(time.Second >> 0)
if newNode.DNSOn == "yes" {
for _, server := range newNode.NetworkSettings.DefaultServerAddrs {
if server.IsLeader {
go local.SetDNSWithRetry(newNode, server.Address)
break
}
}
}
doneErr := publishSignal(&commsCfg, &nodeCfg, ncutils.DONE)
if doneErr != nil {
ncutils.Log("could not notify server to update peers after interface change")
} else {
ncutils.Log("signalled finished interface update to server")
}
} else if hubChange {
doneErr := publishSignal(&commsCfg, &nodeCfg, ncutils.DONE)
if doneErr != nil {
ncutils.Log("could not notify server to update peers after hub change")
} else {
ncutils.Log("signalled finished hub update to server")
}
}
//deal with DNS
if newNode.DNSOn != "yes" && shouldDNSChange && nodeCfg.Node.Interface != "" {
ncutils.Log("settng DNS off")
_, err := ncutils.RunCmd("/usr/bin/resolvectl revert "+nodeCfg.Node.Interface, true)
if err != nil {
ncutils.Log("error applying dns" + err.Error())
}
}
}
// UpdatePeers -- mqtt message handler for peers/<Network>/<NodeID> topic
func UpdatePeers(client mqtt.Client, msg mqtt.Message) {
var peerUpdate models.PeerUpdate
var network = parseNetworkFromTopic(msg.Topic())
var cfg = config.ClientConfig{}
cfg.Network = network
cfg.ReadConfig()
data, dataErr := decryptMsg(&cfg, msg.Payload())
if dataErr != nil {
return
}
err := json.Unmarshal([]byte(data), &peerUpdate)
if err != nil {
ncutils.Log("error unmarshalling peer data")
return
}
// see if cached hit, if so skip
var currentMessage = read(peerUpdate.Network, lastPeerUpdate)
if currentMessage == string(data) {
return
}
insert(peerUpdate.Network, lastPeerUpdate, string(data))
file := ncutils.GetNetclientPathSpecific() + cfg.Node.Interface + ".conf"
err = wireguard.UpdateWgPeers(file, peerUpdate.Peers)
if err != nil {
ncutils.Log("error updating wireguard peers" + err.Error())
return
}
//err = wireguard.SyncWGQuickConf(cfg.Node.Interface, file)
var iface = cfg.Node.Interface
if ncutils.IsMac() {
iface, err = local.GetMacIface(cfg.Node.Address)
if err != nil {
ncutils.Log("error retrieving mac iface: " + err.Error())
return
}
}
err = wireguard.SetPeers(iface, &cfg.Node, peerUpdate.Peers)
if err != nil {
ncutils.Log("error syncing wg after peer update: " + err.Error())
return
}
}

View file

@ -0,0 +1,143 @@
package functions
import (
"context"
"encoding/json"
"fmt"
"sync"
"time"
"github.com/gravitl/netmaker/netclient/auth"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// Checkin -- go routine that checks for public or local ip changes, publishes changes
// if there are no updates, simply "pings" the server as a checkin
func Checkin(ctx context.Context, wg *sync.WaitGroup, currentComms map[string]bool) {
defer wg.Done()
for {
select {
case <-ctx.Done():
ncutils.Log("checkin routine closed")
return
//delay should be configuraable -> use cfg.Node.NetworkSettings.DefaultCheckInInterval ??
case <-time.After(time.Second * 60):
// ncutils.Log("Checkin running")
//read latest config
networks, err := ncutils.GetSystemNetworks()
if err != nil {
return
}
for commsNet := range currentComms {
var currCommsCfg config.ClientConfig
currCommsCfg.Network = commsNet
currCommsCfg.ReadConfig()
for _, network := range networks {
var nodeCfg config.ClientConfig
nodeCfg.Network = network
nodeCfg.ReadConfig()
if nodeCfg.Node.CommID != commsNet {
continue // skip if not on current comms network
}
if nodeCfg.Node.IsStatic != "yes" {
extIP, err := ncutils.GetPublicIP()
if err != nil {
ncutils.PrintLog("error encountered checking public ip addresses: "+err.Error(), 1)
}
if nodeCfg.Node.Endpoint != extIP && extIP != "" {
ncutils.PrintLog("endpoint has changed from "+nodeCfg.Node.Endpoint+" to "+extIP, 1)
nodeCfg.Node.Endpoint = extIP
if err := PublishNodeUpdate(&currCommsCfg, &nodeCfg); err != nil {
ncutils.Log("could not publish endpoint change")
}
}
intIP, err := getPrivateAddr()
if err != nil {
ncutils.PrintLog("error encountered checking private ip addresses: "+err.Error(), 1)
}
if nodeCfg.Node.LocalAddress != intIP && intIP != "" {
ncutils.PrintLog("local Address has changed from "+nodeCfg.Node.LocalAddress+" to "+intIP, 1)
nodeCfg.Node.LocalAddress = intIP
if err := PublishNodeUpdate(&currCommsCfg, &nodeCfg); err != nil {
ncutils.Log("could not publish local address change")
}
}
} else if nodeCfg.Node.IsLocal == "yes" && nodeCfg.Node.LocalRange != "" {
localIP, err := ncutils.GetLocalIP(nodeCfg.Node.LocalRange)
if err != nil {
ncutils.PrintLog("error encountered checking local ip addresses: "+err.Error(), 1)
}
if nodeCfg.Node.Endpoint != localIP && localIP != "" {
ncutils.PrintLog("endpoint has changed from "+nodeCfg.Node.Endpoint+" to "+localIP, 1)
nodeCfg.Node.Endpoint = localIP
if err := PublishNodeUpdate(&currCommsCfg, &nodeCfg); err != nil {
ncutils.Log("could not publish localip change")
}
}
}
if err := PingServer(&currCommsCfg); err != nil {
ncutils.PrintLog("could not ping server on comms net, "+currCommsCfg.Network+"\n"+err.Error(), 0)
} else {
Hello(&currCommsCfg, &nodeCfg)
}
}
}
}
}
}
// PublishNodeUpdates -- saves node and pushes changes to broker
func PublishNodeUpdate(commsCfg, nodeCfg *config.ClientConfig) error {
if err := config.Write(nodeCfg, nodeCfg.Network); err != nil {
return err
}
data, err := json.Marshal(nodeCfg.Node)
if err != nil {
return err
}
if err = publish(commsCfg, nodeCfg, fmt.Sprintf("update/%s", nodeCfg.Node.ID), data, 1); err != nil {
return err
}
ncutils.PrintLog("sent a node update to server for node"+nodeCfg.Node.Name+", "+nodeCfg.Node.ID, 1)
return nil
}
// Hello -- ping the broker to let server know node it's alive and well
func Hello(commsCfg, nodeCfg *config.ClientConfig) {
if err := publish(commsCfg, nodeCfg, fmt.Sprintf("ping/%s", nodeCfg.Node.ID), []byte(ncutils.Version), 0); err != nil {
ncutils.Log(fmt.Sprintf("error publishing ping, %v", err))
ncutils.Log("running pull on " + commsCfg.Node.Network + " to reconnect")
_, err := Pull(commsCfg.Node.Network, true)
if err != nil {
ncutils.Log("could not run pull on " + commsCfg.Node.Network + ", error: " + err.Error())
}
}
}
// requires the commscfg in which to send traffic over and nodecfg of node that is publish the message
// node cfg is so that the traffic keys of that node may be fetched for encryption
func publish(commsCfg, nodeCfg *config.ClientConfig, dest string, msg []byte, qos byte) error {
// setup the keys
trafficPrivKey, err := auth.RetrieveTrafficKey(nodeCfg.Node.Network)
if err != nil {
return err
}
serverPubKey, err := ncutils.ConvertBytesToKey(nodeCfg.Node.TrafficKeys.Server)
if err != nil {
return err
}
client := setupMQTT(commsCfg, true)
defer client.Disconnect(250)
encrypted, err := ncutils.Chunk(msg, serverPubKey, trafficPrivKey)
if err != nil {
return err
}
if token := client.Publish(dest, qos, false, encrypted); token.Wait() && token.Error() != nil {
return token.Error()
}
return nil
}

126
netclient/functions/pull.go Normal file
View file

@ -0,0 +1,126 @@
package functions
import (
"context"
"encoding/json"
"errors"
"os"
"runtime"
nodepb "github.com/gravitl/netmaker/grpc"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/auth"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/local"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/wireguard"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
//homedir "github.com/mitchellh/go-homedir"
)
// Pull - pulls the latest config from the server, if manual it will overwrite
func Pull(network string, manual bool) (*models.Node, error) {
cfg, err := config.ReadConfig(network)
if err != nil {
return nil, err
}
node := cfg.Node
//servercfg := cfg.Server
if cfg.Node.IPForwarding == "yes" && !ncutils.IsWindows() {
if err = local.SetIPForwarding(); err != nil {
return nil, err
}
}
var resNode models.Node // just need to fill this with either server calls or client calls
var header metadata.MD
var wcclient nodepb.NodeServiceClient
var ctx context.Context
if cfg.Node.IsServer != "yes" {
conn, err := grpc.Dial(cfg.Server.GRPCAddress,
ncutils.GRPCRequestOpts(cfg.Server.GRPCSSL))
if err != nil {
ncutils.PrintLog("Cant dial GRPC server: "+err.Error(), 1)
return nil, err
}
defer conn.Close()
wcclient = nodepb.NewNodeServiceClient(conn)
ctx, err = auth.SetJWT(wcclient, network)
if err != nil {
ncutils.PrintLog("Failed to authenticate: "+err.Error(), 1)
return nil, err
}
data, err := json.Marshal(&node)
if err != nil {
ncutils.PrintLog("Failed to parse node config: "+err.Error(), 1)
return nil, err
}
req := &nodepb.Object{
Data: string(data),
Type: nodepb.NODE_TYPE,
}
readres, err := wcclient.ReadNode(ctx, req, grpc.Header(&header))
if err != nil {
return nil, err
}
if err = json.Unmarshal([]byte(readres.Data), &resNode); err != nil {
return nil, err
}
}
// ensure that the OS never changes
resNode.OS = runtime.GOOS
if manual {
// check for interface change
if cfg.Node.Interface != resNode.Interface {
if err = DeleteInterface(cfg.Node.Interface, cfg.Node.PostDown); err != nil {
ncutils.PrintLog("could not delete old interface "+cfg.Node.Interface, 1)
}
}
if err = config.ModConfig(&resNode); err != nil {
return nil, err
}
if err = wireguard.SetWGConfig(network, false); err != nil {
return nil, err
}
nodeData, err := json.Marshal(&resNode)
if err != nil {
return &resNode, err
}
if resNode.IsServer != "yes" {
if wcclient == nil || ctx == nil {
return &cfg.Node, errors.New("issue initializing gRPC client")
}
req := &nodepb.Object{
Data: string(nodeData),
Type: nodepb.NODE_TYPE,
Metadata: "",
}
_, err = wcclient.UpdateNode(ctx, req, grpc.Header(&header))
if err != nil {
return &resNode, err
}
}
} else {
if err = wireguard.SetWGConfig(network, true); err != nil {
if errors.Is(err, os.ErrNotExist) && !ncutils.IsFreeBSD() {
return Pull(network, true)
} else {
return nil, err
}
}
}
var bkupErr = config.SaveBackup(network)
if bkupErr != nil {
ncutils.Log("unable to update backup file")
}
return &resNode, err
}

View file

@ -0,0 +1,8 @@
package ncutils
const (
// ACK - acknowledgement signal for MQ
ACK = 1
// DONE - done signal for MQ
DONE = 2
)

View file

@ -0,0 +1,105 @@
package ncutils
import (
"bytes"
"crypto/rand"
"fmt"
"io"
"golang.org/x/crypto/nacl/box"
)
const (
chunkSize = 16000 // 16000 bytes max message size
)
// BoxEncrypt - encrypts traffic box
func BoxEncrypt(message []byte, recipientPubKey *[32]byte, senderPrivateKey *[32]byte) ([]byte, error) {
var nonce [24]byte // 192 bits of randomization
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
return nil, err
}
encrypted := box.Seal(nonce[:], message, &nonce, recipientPubKey, senderPrivateKey)
return encrypted, nil
}
// BoxDecrypt - decrypts traffic box
func BoxDecrypt(encrypted []byte, senderPublicKey *[32]byte, recipientPrivateKey *[32]byte) ([]byte, error) {
var decryptNonce [24]byte
copy(decryptNonce[:], encrypted[:24])
decrypted, ok := box.Open(nil, encrypted[24:], &decryptNonce, senderPublicKey, recipientPrivateKey)
if !ok {
return nil, fmt.Errorf("could not decrypt message, %v", encrypted)
}
return decrypted, nil
}
// Chunk - chunks a message and encrypts each chunk
func Chunk(message []byte, recipientPubKey *[32]byte, senderPrivateKey *[32]byte) ([]byte, error) {
var chunks [][]byte
for i := 0; i < len(message); i += chunkSize {
end := i + chunkSize
if end > len(message) {
end = len(message)
}
encryptedMsgSlice, err := BoxEncrypt(message[i:end], recipientPubKey, senderPrivateKey)
if err != nil {
return nil, err
}
chunks = append(chunks, encryptedMsgSlice)
}
chunkedMsg, err := convertBytesToMsg(chunks) // encode the array into some bytes to decode on receiving end
if err != nil {
return nil, err
}
return chunkedMsg, nil
}
// DeChunk - "de" chunks and decrypts a message
func DeChunk(chunkedMsg []byte, senderPublicKey *[32]byte, recipientPrivateKey *[32]byte) ([]byte, error) {
chunks, err := convertMsgToBytes(chunkedMsg) // convert the message to it's original chunks form
if err != nil {
return nil, err
}
var totalMsg []byte
for i := range chunks {
decodedMsg, err := BoxDecrypt(chunks[i], senderPublicKey, recipientPrivateKey)
if err != nil {
return nil, err
}
totalMsg = append(totalMsg, decodedMsg...)
}
return totalMsg, nil
}
// == private ==
var splitKey = []byte("|(,)(,)|")
// ConvertMsgToBytes - converts a message (MQ) to it's chunked version
// decode action
func convertMsgToBytes(msg []byte) ([][]byte, error) {
splitMsg := bytes.Split(msg, splitKey)
return splitMsg, nil
}
// ConvertBytesToMsg - converts the chunked message into a MQ message
// encode action
func convertBytesToMsg(b [][]byte) ([]byte, error) {
var buffer []byte // allocate a buffer with adequate sizing
for i := range b { // append bytes to it with key
buffer = append(buffer, b[i]...)
if i != len(b)-1 {
buffer = append(buffer, splitKey...)
}
}
return buffer, nil
}

View file

@ -6,6 +6,7 @@ import (
"github.com/gravitl/netmaker/models"
)
// IfaceDelta - checks if the new node causes an interface change
func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
// single comparison statements
if newNode.Endpoint != currentNode.Endpoint ||
@ -15,11 +16,12 @@ func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
newNode.IsEgressGateway != currentNode.IsEgressGateway ||
newNode.IsIngressGateway != currentNode.IsIngressGateway ||
newNode.IsRelay != currentNode.IsRelay ||
newNode.ListenPort != currentNode.ListenPort ||
newNode.UDPHolePunch != currentNode.UDPHolePunch ||
newNode.MTU != currentNode.MTU ||
newNode.IsPending != currentNode.IsPending ||
newNode.PersistentKeepalive != currentNode.PersistentKeepalive ||
newNode.DNSOn != currentNode.DNSOn ||
len(newNode.ExcludedAddrs) != len(currentNode.ExcludedAddrs) ||
len(newNode.AllowedIPs) != len(currentNode.AllowedIPs) {
return true
}

View file

@ -2,7 +2,6 @@ package ncutils
import (
"bytes"
crand "crypto/rand"
"crypto/tls"
"encoding/gob"
"errors"
@ -22,7 +21,6 @@ import (
"time"
"github.com/gravitl/netmaker/models"
"golang.org/x/crypto/nacl/box"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"google.golang.org/grpc"
@ -51,7 +49,7 @@ const LINUX_APP_DATA_PATH = "/etc/netclient"
const WINDOWS_APP_DATA_PATH = "C:\\ProgramData\\Netclient"
// WINDOWS_APP_DATA_PATH - windows path
const WINDOWS_WG_DPAPI_PATH = "C:\\Program Files\\WireGuard\\Data\\Configurations"
//const WINDOWS_WG_DPAPI_PATH = "C:\\Program Files\\WireGuard\\Data\\Configurations"
// WINDOWS_SVC_NAME - service name
const WINDOWS_SVC_NAME = "netclient"
@ -104,6 +102,12 @@ func IsFreeBSD() bool {
return runtime.GOOS == "freebsd"
}
// HasWGQuick - checks if WGQuick command is present
func HasWgQuick() bool {
cmd, err := exec.LookPath("wg-quick")
return err == nil && cmd != ""
}
// GetWireGuard - checks if wg is installed
func GetWireGuard() string {
userspace := os.Getenv("WG_QUICK_USERSPACE_IMPLEMENTATION")
@ -613,28 +617,6 @@ func ServerAddrSliceContains(slice []models.ServerAddr, item models.ServerAddr)
return false
}
// BoxEncrypt - encrypts traffic box
func BoxEncrypt(message []byte, recipientPubKey *[32]byte, senderPrivateKey *[32]byte) ([]byte, error) {
var nonce [24]byte // 192 bits of randomization
if _, err := io.ReadFull(crand.Reader, nonce[:]); err != nil {
return nil, err
}
encrypted := box.Seal(nonce[:], message, &nonce, recipientPubKey, senderPrivateKey)
return encrypted, nil
}
// BoxDecrypt - decrypts traffic box
func BoxDecrypt(encrypted []byte, senderPublicKey *[32]byte, recipientPrivateKey *[32]byte) ([]byte, error) {
var decryptNonce [24]byte
copy(decryptNonce[:], encrypted[:24])
decrypted, ok := box.Open(nil, encrypted[24:], &decryptNonce, senderPublicKey, recipientPrivateKey)
if !ok {
return nil, fmt.Errorf("could not decrypt message")
}
return decrypted, nil
}
// MakeRandomString - generates a random string of len n
func MakeRandomString(n int) string {
sb := strings.Builder{}

View file

@ -28,5 +28,3 @@ func RunCmdFormatted(command string, printerr bool) (string, error) {
func GetEmbedded() error {
return nil
}

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="0.10.0.0"
version="0.11.0.0"
processorArchitecture="*"
name="netclient.exe"
type="win32"

BIN
netclient/netclient.syso Normal file

Binary file not shown.

View file

@ -29,7 +29,7 @@
"OriginalFilename": "",
"PrivateBuild": "",
"ProductName": "Netclient",
"ProductVersion": "v0.10.0.0",
"ProductVersion": "v0.11.0.0",
"SpecialBuild": ""
},
"VarFileInfo": {

View file

@ -25,8 +25,10 @@ const (
)
// SetPeers - sets peers on a given WireGuard interface
func SetPeers(iface, currentNodeAddr string, keepalive int32, peers []wgtypes.PeerConfig) error {
func SetPeers(iface string, node *models.Node, peers []wgtypes.PeerConfig) error {
var devicePeers []wgtypes.Peer
var currentNodeAddr = node.Address
var keepalive = node.PersistentKeepalive
var oldPeerAllowedIps = make(map[string][]net.IPNet, len(peers))
var err error
if ncutils.IsFreeBSD() {
@ -73,13 +75,14 @@ func SetPeers(iface, currentNodeAddr string, keepalive int32, peers []wgtypes.Pe
if keepAliveString == "0" {
keepAliveString = "15"
}
if peer.Endpoint != nil {
if node.IsHub == "yes" || peer.Endpoint == nil {
_, err = ncutils.RunCmd("wg set "+iface+" peer "+peer.PublicKey.String()+
" endpoint "+udpendpoint+
" persistent-keepalive "+keepAliveString+
" allowed-ips "+allowedips, true)
} else {
_, err = ncutils.RunCmd("wg set "+iface+" peer "+peer.PublicKey.String()+
" endpoint "+udpendpoint+
" persistent-keepalive "+keepAliveString+
" allowed-ips "+allowedips, true)
}
@ -94,6 +97,10 @@ func SetPeers(iface, currentNodeAddr string, keepalive int32, peers []wgtypes.Pe
if peer.AllowedIPs[0].String() == currentPeer.AllowedIPs[0].String() {
shouldDelete = false
}
// re-check this if logic is not working, added in case of allowedips not working
if peer.PublicKey.String() == currentPeer.PublicKey.String() {
shouldDelete = false
}
}
if shouldDelete {
output, err := ncutils.RunCmd("wg set "+iface+" peer "+currentPeer.PublicKey.String()+" remove", true)
@ -131,10 +138,6 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
return err
}
nodecfg := modcfg.Node
if err != nil {
log.Fatalf("failed to open client: %v", err)
}
var ifacename string
if nodecfg.Interface != "" {
ifacename = nodecfg.Interface
@ -206,7 +209,13 @@ func InitWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig
if syncconf { // should never be called really.
err = SyncWGQuickConf(ifacename, confPath)
}
if !ncutils.HasWgQuick() && ncutils.IsLinux() {
err = SetPeers(ifacename, node, peers)
if err != nil {
ncutils.PrintLog("error setting peers: "+err.Error(), 1)
}
time.Sleep(time.Second)
}
_, cidr, cidrErr := net.ParseCIDR(modcfg.NetworkSettings.AddressRange)
if cidrErr == nil {
local.SetCIDRRoute(ifacename, node.Address, cidr)
@ -245,7 +254,7 @@ func SetWGConfig(network string, peerupdate bool) error {
return err
}
}
err = SetPeers(iface, nodecfg.Address, nodecfg.PersistentKeepalive, peers)
err = SetPeers(iface, &nodecfg, peers)
} else if peerupdate {
err = InitWireguard(&nodecfg, privkey, peers, hasGateway, gateways, true)
} else {
@ -260,8 +269,13 @@ func SetWGConfig(network string, peerupdate bool) error {
// RemoveConf - removes a configuration for a given WireGuard interface
func RemoveConf(iface string, printlog bool) error {
os := runtime.GOOS
if !ncutils.HasWgQuick() {
os = "nowgquick"
}
var err error
switch os {
case "nowgquick":
err = RemoveWithoutWGQuick(iface)
case "windows":
err = RemoveWindowsConf(iface, printlog)
case "darwin":
@ -276,15 +290,29 @@ func RemoveConf(iface string, printlog bool) error {
// ApplyConf - applys a conf on disk to WireGuard interface
func ApplyConf(node *models.Node, ifacename string, confPath string) error {
os := runtime.GOOS
if ncutils.IsLinux() && !ncutils.HasWgQuick() {
os = "nowgquick"
}
var err error
switch os {
case "nowgquick":
ApplyWithoutWGQuick(node, ifacename, confPath)
case "windows":
_ = ApplyWindowsConf(confPath)
ApplyWindowsConf(confPath)
case "darwin":
_ = ApplyMacOSConf(node, ifacename, confPath)
ApplyMacOSConf(node, ifacename, confPath)
default:
err = ApplyWGQuickConf(confPath, ifacename)
ApplyWGQuickConf(confPath, ifacename)
}
var nodeCfg config.ClientConfig
nodeCfg.Network = node.Network
nodeCfg.ReadConfig()
ip, cidr, err := net.ParseCIDR(nodeCfg.NetworkSettings.AddressRange)
if err == nil {
local.SetCIDRRoute(node.Interface, ip.String(), cidr)
}
return err
}

View file

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

View file

@ -65,6 +65,7 @@ func ApplyWGQuickConf(confPath string, ifacename string) error {
ncutils.RunCmd("wg-quick down "+confPath, true)
}
_, err = ncutils.RunCmd("wg-quick up "+confPath, true)
return err
}
}

View file

@ -2,23 +2,22 @@ package wireguard
import (
"fmt"
"os"
"strings"
"time"
"github.com/gravitl/netmaker/netclient/ncutils"
)
// ApplyWindowsConf - applies the WireGuard configuration file on Windows
func ApplyWindowsConf(confPath string) error {
pathStrings := strings.Split(confPath, ncutils.GetWGPathSpecific())
if len(pathStrings) == 2 {
copyConfPath := fmt.Sprintf("%s\\%s", ncutils.WINDOWS_WG_DPAPI_PATH, pathStrings[1])
err := ncutils.Copy(confPath, copyConfPath)
if err != nil {
ncutils.PrintLog(err.Error(), 1)
/*
pathStrings := strings.Split(confPath, ncutils.GetWGPathSpecific())
if len(pathStrings) == 2 {
copyConfPath := fmt.Sprintf("%s\\%s", ncutils.WINDOWS_WG_DPAPI_PATH, pathStrings[1])
err := ncutils.Copy(confPath, copyConfPath)
if err != nil {
ncutils.PrintLog(err.Error(), 1)
}
}
}
*/
var commandLine = fmt.Sprintf(`wireguard.exe /installtunnelservice "%s"`, confPath)
if _, err := ncutils.RunCmdFormatted(commandLine, false); err != nil {
return err
@ -31,20 +30,22 @@ func RemoveWindowsConf(ifacename string, printlog bool) error {
if _, err := ncutils.RunCmd("wireguard.exe /uninstalltunnelservice "+ifacename, printlog); err != nil {
ncutils.PrintLog(err.Error(), 1)
}
dpapipath := fmt.Sprintf("%s\\%s.conf.dpapi", ncutils.WINDOWS_WG_DPAPI_PATH, ifacename)
confpath := fmt.Sprintf("%s\\%s.conf", ncutils.WINDOWS_WG_DPAPI_PATH, ifacename)
if ncutils.FileExists(confpath) {
err := os.Remove(confpath)
if err != nil {
ncutils.PrintLog(err.Error(), 1)
/*
dpapipath := fmt.Sprintf("%s\\%s.conf.dpapi", ncutils.WINDOWS_WG_DPAPI_PATH, ifacename)
confpath := fmt.Sprintf("%s\\%s.conf", ncutils.WINDOWS_WG_DPAPI_PATH, ifacename)
if ncutils.FileExists(confpath) {
err := os.Remove(confpath)
if err != nil {
ncutils.PrintLog(err.Error(), 1)
}
}
}
time.Sleep(time.Second >> 2)
if ncutils.FileExists(dpapipath) {
err := os.Remove(dpapipath)
if err != nil {
ncutils.PrintLog(err.Error(), 1)
time.Sleep(time.Second >> 2)
if ncutils.FileExists(dpapipath) {
err := os.Remove(dpapipath)
if err != nil {
ncutils.PrintLog(err.Error(), 1)
}
}
}
*/
return nil
}

View file

@ -18,3 +18,4 @@ env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=6 go build -ldflags="-X 'main.ve
env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm GOARM=7 go build -ldflags="-X 'main.version=$VERSION'" -o build/netclient-freebsd-arm7 main.go
env CGO_ENABLED=0 GOOS=freebsd GOARCH=arm64 go build -ldflags="-X 'main.version=$VERSION'" -o build/netclient-freebsd-arm64 main.go
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="-X 'main.version=$VERSION'" -o build/netclient-darwin main.go
env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="-X 'main.version=${VERSION}'" -o build/netclient-darwin-arm64 main.go

View file

@ -12,7 +12,10 @@ import (
"github.com/gravitl/netmaker/config"
)
var Version = "dev"
var (
Version = "dev"
commsID = ""
)
// SetHost - sets the host ip
func SetHost() error {
@ -31,16 +34,17 @@ func GetServerConfig() config.ServerConfig {
cfg.CoreDNSAddr = GetCoreDNSAddr()
cfg.APIHost = GetAPIHost()
cfg.APIPort = GetAPIPort()
cfg.GRPCConnString = GetGRPCConnString()
cfg.APIPort = GetAPIPort()
cfg.MQPort = GetMQPort()
cfg.GRPCHost = GetGRPCHost()
cfg.GRPCPort = GetGRPCPort()
cfg.GRPCConnString = GetGRPCConnString()
cfg.MasterKey = "(hidden)"
cfg.DNSKey = "(hidden)"
cfg.AllowedOrigin = GetAllowedOrigin()
cfg.RestBackend = "off"
cfg.NodeID = GetNodeID()
cfg.CheckinInterval = GetCheckinInterval()
cfg.ServerCheckinInterval = GetServerCheckinInterval()
cfg.MQPort = GetMQPort()
if IsRestBackend() {
cfg.RestBackend = "on"
}
@ -68,10 +72,6 @@ func GetServerConfig() config.ServerConfig {
if DisableRemoteIPCheck() {
cfg.DisableRemoteIPCheck = "on"
}
cfg.DisableDefaultNet = "off"
if DisableDefaultNet() {
cfg.DisableRemoteIPCheck = "on"
}
cfg.Database = GetDB()
cfg.Platform = GetPlatform()
cfg.Version = GetVersion()
@ -90,8 +90,10 @@ func GetServerConfig() config.ServerConfig {
cfg.Debug = GetDebug()
cfg.Telemetry = Telemetry()
cfg.ManageIPTables = ManageIPTables()
cfg.CommsCIDR = GetCommsCIDR()
services := strings.Join(GetPortForwardServiceList(), ",")
cfg.PortForwardServices = services
cfg.CommsID = GetCommsCIDR()
return cfg
}
@ -177,17 +179,6 @@ func GetAPIPort() string {
return apiport
}
// GetCheckinInterval - get check in interval for nodes
func GetCheckinInterval() string {
seconds := "15"
if os.Getenv("CHECKIN_INTERVAL") != "" {
seconds = os.Getenv("CHECKIN_INTERVAL")
} else if config.Config.Server.CheckinInterval != "" {
seconds = config.Config.Server.CheckinInterval
}
return seconds
}
// GetDefaultNodeLimit - get node limit if one is set
func GetDefaultNodeLimit() int32 {
var limit int32
@ -208,6 +199,8 @@ func GetGRPCConnString() string {
conn = os.Getenv("SERVER_GRPC_CONN_STRING")
} else if config.Config.Server.GRPCConnString != "" {
conn = config.Config.Server.GRPCConnString
} else {
conn = GetGRPCHost() + ":" + GetGRPCPort()
}
return conn
}
@ -252,6 +245,42 @@ func GetGRPCPort() string {
return grpcport
}
// GetMQPort - gets the mq port
func GetMQPort() string {
mqport := "1883"
if os.Getenv("MQ_PORT") != "" {
mqport = os.Getenv("MQ_PORT")
} else if config.Config.Server.MQPort != "" {
mqport = config.Config.Server.MQPort
}
return mqport
}
// GetGRPCPort - gets the grpc port
func GetCommsCIDR() string {
netrange := "172.242.0.0/16"
if os.Getenv("COMMS_CIDR") != "" {
netrange = os.Getenv("COMMS_CIDR")
} else if config.Config.Server.CommsCIDR != "" {
netrange = config.Config.Server.CommsCIDR
}
_, _, err := net.ParseCIDR(netrange)
if err == nil {
return netrange
}
return "172.242.0.0/16"
}
// GetCommsID - gets the grpc port
func GetCommsID() string {
return commsID
}
// SetCommsID - sets the commsID
func SetCommsID(newCommsID string) {
commsID = newCommsID
}
// GetMessageQueueEndpoint - gets the message queue endpoint
func GetMessageQueueEndpoint() string {
host, _ := GetPublicIP()
@ -266,7 +295,7 @@ func GetMessageQueueEndpoint() string {
// GetMasterKey - gets the configured master key of server
func GetMasterKey() string {
key := "secretkey"
key := ""
if os.Getenv("MASTER_KEY") != "" {
key = os.Getenv("MASTER_KEY")
} else if config.Config.Server.MasterKey != "" {
@ -415,8 +444,8 @@ func IsGRPCSSL() bool {
if os.Getenv("GRPC_SSL") == "on" {
isssl = true
}
} else if config.Config.Server.DNSMode != "" {
if config.Config.Server.DNSMode == "on" {
} else if config.Config.Server.GRPCSSL != "" {
if config.Config.Server.GRPCSSL == "on" {
isssl = true
}
}
@ -438,21 +467,6 @@ func DisableRemoteIPCheck() bool {
return disabled
}
// DisableDefaultNet - disable default net
func DisableDefaultNet() bool {
disabled := false
if os.Getenv("DISABLE_DEFAULT_NET") != "" {
if os.Getenv("DISABLE_DEFAULT_NET") == "on" {
disabled = true
}
} else if config.Config.Server.DisableDefaultNet != "" {
if config.Config.Server.DisableDefaultNet == "on" {
disabled = true
}
}
return disabled
}
// GetPublicIP - gets public ip
func GetPublicIP() (string, error) {
@ -516,18 +530,7 @@ func GetSQLConn() string {
return sqlconn
}
// IsSplitDNS - checks if split dns is on
func IsSplitDNS() bool {
issplit := false
if os.Getenv("IS_SPLIT_DNS") == "yes" {
issplit = true
} else if config.Config.Server.SplitDNS == "yes" {
issplit = true
}
return issplit
}
// IsSplitDNS - checks if split dns is on
// IsHostNetwork - checks if running on host network
func IsHostNetwork() bool {
ishost := false
if os.Getenv("HOST_NETWORK") == "on" {
@ -541,15 +544,25 @@ func IsHostNetwork() bool {
// GetNodeID - gets the node id
func GetNodeID() string {
var id string
var err error
// id = getMacAddr()
if os.Getenv("NODE_ID") != "" {
id = os.Getenv("NODE_ID")
} else if config.Config.Server.NodeID != "" {
id = config.Config.Server.NodeID
} else {
id, err = os.Hostname()
if err != nil {
return ""
}
}
return id
}
func SetNodeID(id string) {
config.Config.Server.NodeID = id
}
// GetServerCheckinInterval - gets the server check-in time
func GetServerCheckinInterval() int64 {
var t = int64(5)
@ -592,22 +605,6 @@ func GetAzureTenant() string {
return azureTenant
}
// GetMacAddr - get's mac address
func getMacAddr() string {
ifas, err := net.Interfaces()
if err != nil {
return ""
}
var as []string
for _, ifa := range ifas {
a := ifa.HardwareAddr.String()
if a != "" {
as = append(as, a)
}
}
return as[0]
}
// GetRce - sees if Rce is enabled, off by default
func GetRce() bool {
return os.Getenv("RCE") == "on" || config.Config.Server.RCE == "on"

View file

@ -50,10 +50,12 @@ func portForwardServices() error {
case "dns":
err = iptablesPortForward("coredns", "53", "53", false)
case "ssh":
err = iptablesPortForward("127.0.0.1", "22", "22", true)
err = iptablesPortForward("netmaker", "22", "22", false)
default:
params := strings.Split(service, ":")
err = iptablesPortForward(params[0], params[1], params[2], true)
if len(params) == 3 {
err = iptablesPortForward(params[0], params[1], params[2], true)
}
}
if err != nil {
return err

View file

@ -2,7 +2,6 @@ package serverctl
import (
"errors"
"fmt"
"net"
"os"
"strings"
@ -10,10 +9,55 @@ import (
"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"
)
const NETMAKER_BINARY_NAME = "netmaker"
// COMMS_NETID - name of the comms network
var COMMS_NETID string
const (
// NETMAKER_BINARY_NAME - name of netmaker binary
NETMAKER_BINARY_NAME = "netmaker"
)
// InitializeCommsNetwork - Check if comms network exists (for MQ, DNS, SSH traffic), if not, create
func InitializeCommsNetwork() error {
setCommsID()
_, err := logic.GetNetwork(COMMS_NETID)
if err != nil {
logger.Log(1, "comms net does not exist, creating")
var network models.Network
network.NetID = COMMS_NETID
network.AddressRange = servercfg.GetCommsCIDR()
network.IsPointToSite = "yes"
network.DefaultUDPHolePunch = "yes"
network.IsComms = "yes"
return logic.CreateNetwork(network)
}
SyncServerNetwork(COMMS_NETID)
return nil
}
// SetJWTSecret - sets the jwt secret on server startup
func setCommsID() {
currentid, idErr := logic.FetchCommsNetID()
if idErr != nil {
commsid := logic.RandomString(8)
if err := logic.StoreCommsNetID(commsid); err != nil {
logger.FatalLog("something went wrong when configuring comms id")
}
COMMS_NETID = commsid
servercfg.SetCommsID(COMMS_NETID)
return
}
COMMS_NETID = currentid
servercfg.SetCommsID(COMMS_NETID)
}
// InitServerNetclient - intializes the server netclient
// 1. Check if config directory exists, if not attempt to make
@ -34,9 +78,7 @@ func InitServerNetclient() error {
var currentServerNode, nodeErr = logic.GetNetworkServerLocal(network.NetID)
if nodeErr == nil {
if err = logic.ServerPull(&currentServerNode, true); err != nil {
logger.Log(1, fmt.Sprintf("failed pull for network %s, on server node %s",
network.NetID,
currentServerNode.ID))
logger.Log(1, "failed pull for network", network.NetID, ", on server node", currentServerNode.ID)
}
}
}