| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 | package functionsimport (	"encoding/json"	"runtime"	"strings"	"time"	mqtt "github.com/eclipse/paho.mqtt.golang"	"github.com/gravitl/netmaker/logger"	"github.com/gravitl/netmaker/models"	"github.com/gravitl/netmaker/netclient/config"	"github.com/gravitl/netmaker/netclient/local"	"github.com/gravitl/netmaker/netclient/ncutils"	"github.com/gravitl/netmaker/netclient/wireguard"	"github.com/guumaster/hostctl/pkg/file"	"github.com/guumaster/hostctl/pkg/parser"	"github.com/guumaster/hostctl/pkg/types"	"golang.zx2c4.com/wireguard/wgctrl/wgtypes")// All -- mqtt message hander for all ('#') topicsvar All mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {	logger.Log(0, "default message handler -- received message but not handling")	logger.Log(0, "Topic: "+string(msg.Topic()))	//logger.Log(0, "Message: " + string(msg.Payload()))}// NodeUpdate -- mqtt message handler for /update/<NodeID> topicfunc 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()	data, dataErr := decryptMsg(&nodeCfg, msg.Payload())	if dataErr != nil {		return	}	err := json.Unmarshal([]byte(data), &newNode)	if err != nil {		logger.Log(0, "error unmarshalling node update data"+err.Error())		return	}	logger.Log(0, "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	keepaliveChange := nodeCfg.Node.PersistentKeepalive != newNode.PersistentKeepalive	nodeCfg.Node = newNode	switch newNode.Action {	case models.NODE_DELETE:		logger.Log(0, "received delete request for %s", nodeCfg.Node.Name)		unsubscribeNode(client, &nodeCfg)		if err = LeaveNetwork(nodeCfg.Node.Network, true); err != nil {			if !strings.Contains("rpc error", err.Error()) {				logger.Log(0, "failed to leave, please check that local files for network", nodeCfg.Node.Network, "were removed")				return			}		}		logger.Log(0, nodeCfg.Node.Name, " was removed")		return	case models.NODE_UPDATE_KEY:		// == get the current key for node ==		oldPrivateKey, retErr := wireguard.RetrievePrivKey(nodeCfg.Network)		if retErr != nil {			break		}		if err := UpdateKeys(&nodeCfg, client); err != nil {			logger.Log(0, "err updating wireguard keys, reusing last key\n", err.Error())			if key, parseErr := wgtypes.ParseKey(oldPrivateKey); parseErr == nil {				wireguard.StorePrivKey(key.String(), nodeCfg.Network)				nodeCfg.Node.PublicKey = key.PublicKey().String()			}		}		ifaceDelta = true	case models.NODE_FORCE_UPDATE:		ifaceDelta = true	case models.NODE_NOOP:	default:	}	// Save new config	nodeCfg.Node.Action = models.NODE_NOOP	if err := config.Write(&nodeCfg, nodeCfg.Network); err != nil {		logger.Log(0, "error updating node configuration: ", err.Error())	}	nameserver := nodeCfg.Server.CoreDNSAddr	privateKey, err := wireguard.RetrievePrivKey(newNode.Network)	if err != nil {		logger.Log(0, "error reading PrivateKey "+err.Error())		return	}	file := ncutils.GetNetclientPathSpecific() + nodeCfg.Node.Interface + ".conf"	if err := wireguard.UpdateWgInterface(file, privateKey, nameserver, newNode); err != nil {		logger.Log(0, "error updating wireguard config "+err.Error())		return	}	if keepaliveChange {		wireguard.UpdateKeepAlive(file, newNode.PersistentKeepalive)	}	if ifaceDelta { // if a change caused an ifacedelta we need to notify the server to update the peers		logger.Log(0, "applying WG conf to "+file)		if ncutils.IsWindows() {			wireguard.RemoveConfGraceful(nodeCfg.Node.Interface)		}		err = wireguard.ApplyConf(&nodeCfg.Node, nodeCfg.Node.Interface, file)		if err != nil {			logger.Log(0, "error restarting wg after node update "+err.Error())			return		}		time.Sleep(time.Second >> 0)		//	if newNode.DNSOn == "yes" {		//		for _, server := range newNode.NetworkSettings.DefaultServerAddrs {		//			if server.IsLeader {		//				go local.SetDNSWithRetry(newNode, server.Address)		//				break		//			}		//		}		//	}		doneErr := publishSignal(&nodeCfg, ncutils.DONE)		if doneErr != nil {			logger.Log(0, "could not notify server to update peers after interface change")		} else {			logger.Log(0, "signalled finished interface update to server")		}	} else if hubChange {		doneErr := publishSignal(&nodeCfg, ncutils.DONE)		if doneErr != nil {			logger.Log(0, "could not notify server to update peers after hub change")		} else {			logger.Log(0, "signalled finished hub update to server")		}	}	//deal with DNS	if newNode.DNSOn != "yes" && shouldDNSChange && nodeCfg.Node.Interface != "" {		logger.Log(0, "settng DNS off")		if err := removeHostDNS(nodeCfg.Node.Interface, ncutils.IsWindows()); err != nil {			logger.Log(0, "error removing netmaker profile from /etc/hosts "+err.Error())		}		//		_, err := ncutils.RunCmd("/usr/bin/resolvectl revert "+nodeCfg.Node.Interface, true)		//		if err != nil {		//			logger.Log(0, "error applying dns" + err.Error())		//		}	}	_ = UpdateLocalListenPort(&nodeCfg)}// UpdatePeers -- mqtt message handler for peers/<Network>/<NodeID> topicfunc UpdatePeers(client mqtt.Client, msg mqtt.Message) {	var peerUpdate models.PeerUpdate	var network = parseNetworkFromTopic(msg.Topic())	var cfg = config.ClientConfig{}	cfg.Network = network	cfg.ReadConfig()	data, dataErr := decryptMsg(&cfg, msg.Payload())	if dataErr != nil {		return	}	err := json.Unmarshal([]byte(data), &peerUpdate)	if err != nil {		logger.Log(0, "error unmarshalling peer data")		return	}	// see if cached hit, if so skip	var currentMessage = read(peerUpdate.Network, lastPeerUpdate)	if currentMessage == string(data) {		return	}	insert(peerUpdate.Network, lastPeerUpdate, string(data))	// check version	if peerUpdate.ServerVersion != ncutils.Version {		logger.Log(0, "server/client version mismatch server: ", peerUpdate.ServerVersion, " client: ", ncutils.Version)	}	if peerUpdate.ServerVersion != cfg.Server.Version {		logger.Log(1, "updating server version")		cfg.Server.Version = peerUpdate.ServerVersion		config.Write(&cfg, cfg.Network)	}	file := ncutils.GetNetclientPathSpecific() + cfg.Node.Interface + ".conf"	err = wireguard.UpdateWgPeers(file, peerUpdate.Peers)	if err != nil {		logger.Log(0, "error updating wireguard peers"+err.Error())		return	}	queryAddr := cfg.Node.PrimaryAddress()	//err = wireguard.SyncWGQuickConf(cfg.Node.Interface, file)	var iface = cfg.Node.Interface	if ncutils.IsMac() {		iface, err = local.GetMacIface(queryAddr)		if err != nil {			logger.Log(0, "error retrieving mac iface: "+err.Error())			return		}	}	err = wireguard.SetPeers(iface, &cfg.Node, peerUpdate.Peers)	if err != nil {		logger.Log(0, "error syncing wg after peer update: "+err.Error())		return	}	logger.Log(0, "received peer update for node "+cfg.Node.Name+" "+cfg.Node.Network)	if cfg.Node.DNSOn == "yes" {		if err := setHostDNS(peerUpdate.DNS, cfg.Node.Interface, ncutils.IsWindows()); err != nil {			logger.Log(0, "error updating /etc/hosts "+err.Error())			return		}	} else {		if err := removeHostDNS(cfg.Node.Interface, ncutils.IsWindows()); err != nil {			logger.Log(0, "error removing profile from /etc/hosts "+err.Error())			return		}	}	_ = UpdateLocalListenPort(&cfg)}func setHostDNS(dns, iface string, windows bool) error {	etchosts := "/etc/hosts"	if windows {		etchosts = "c:\\windows\\system32\\drivers\\etc\\hosts"	}	dnsdata := strings.NewReader(dns)	profile, err := parser.ParseProfile(dnsdata)	if err != nil {		return err	}	hosts, err := file.NewFile(etchosts)	if err != nil {		return err	}	profile.Name = strings.ToLower(iface)	profile.Status = types.Enabled	if err := hosts.ReplaceProfile(profile); err != nil {		return err	}	if err := hosts.Flush(); err != nil {		return err	}	return nil}func removeHostDNS(iface string, windows bool) error {	etchosts := "/etc/hosts"	if windows {		etchosts = "c:\\windows\\system32\\drivers\\etc\\hosts"	}	hosts, err := file.NewFile(etchosts)	if err != nil {		return err	}	if err := hosts.RemoveProfile(strings.ToLower(iface)); err != nil {		if err == types.ErrUnknownProfile {			return nil		}		return err	}	if err := hosts.Flush(); err != nil {		return err	}	return nil}
 |