123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- package logic
- import (
- "errors"
- "fmt"
- "net"
- "os"
- "os/exec"
- "strconv"
- "strings"
- "time"
- "github.com/gravitl/netmaker/logger"
- "github.com/gravitl/netmaker/models"
- "github.com/gravitl/netmaker/netclient/local"
- "github.com/gravitl/netmaker/netclient/ncutils"
- "github.com/gravitl/netmaker/netclient/wireguard"
- "golang.zx2c4.com/wireguard/wgctrl"
- "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
- )
- // RemoveConf - removes a configuration for a given WireGuard interface
- func RemoveConf(iface string, printlog bool) error {
- var err error
- confPath := ncutils.GetNetclientPathSpecific() + iface + ".conf"
- err = removeWGQuickConf(confPath, printlog)
- return err
- }
- // HasPeerConnected - checks if a client node has connected over WG
- func HasPeerConnected(node *models.Node) bool {
- client, err := wgctrl.New()
- if err != nil {
- return false
- }
- defer client.Close()
- device, err := client.Device(node.Interface)
- if err != nil {
- return false
- }
- for _, peer := range device.Peers {
- if peer.PublicKey.String() == node.PublicKey {
- if peer.Endpoint != nil {
- return true
- }
- }
- }
- return false
- }
- // IfaceDelta - checks if the new node causes an interface change
- func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
- // single comparison statements
- if newNode.Endpoint != currentNode.Endpoint ||
- newNode.PublicKey != currentNode.PublicKey ||
- newNode.Address != currentNode.Address ||
- newNode.Address6 != currentNode.Address6 ||
- 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.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
- func getSystemPeers(node *models.Node) (map[string]string, error) {
- peers := make(map[string]string)
- client, err := wgctrl.New()
- if err != nil {
- return peers, err
- }
- defer client.Close()
- device, err := client.Device(node.Interface)
- if err != nil {
- return nil, err
- }
- if device.Peers != nil && len(device.Peers) > 0 {
- for _, peer := range device.Peers {
- if IsBase64(peer.PublicKey.String()) && peer.Endpoint != nil && CheckEndpoint(peer.Endpoint.String()) {
- peers[peer.PublicKey.String()] = peer.Endpoint.String()
- }
- }
- }
- return peers, nil
- }
- func initWireguard(node *models.Node, privkey string, peers []wgtypes.PeerConfig, hasGateway bool, gateways []string) error {
- key, err := wgtypes.ParseKey(privkey)
- if err != nil {
- return err
- }
- wgclient, err := wgctrl.New()
- if err != nil {
- return err
- }
- defer wgclient.Close()
- var ifacename string
- if node.Interface != "" {
- ifacename = node.Interface
- } else {
- logger.Log(2, "no server interface provided to configure")
- }
- if node.Address == "" {
- logger.Log(2, "no server address to provided configure")
- }
- if ncutils.IsKernel() {
- logger.Log(2, "setting kernel device", ifacename)
- network, err := GetNetwork(node.Network)
- if err != nil {
- logger.Log(0, "failed to get network"+err.Error())
- return err
- }
- var address4 string
- var address6 string
- var mask4 string
- var mask6 string
- if network.AddressRange != "" {
- net := strings.Split(network.AddressRange, "/")
- mask4 = net[len(net)-1]
- address4 = node.Address
- }
- if network.AddressRange6 != "" {
- net := strings.Split(network.AddressRange6, "/")
- mask6 = net[len(net)-1]
- address6 = node.Address6
- }
- setKernelDevice(ifacename, address4, mask4, address6, mask6)
- }
- nodeport := int(node.ListenPort)
- var conf = wgtypes.Config{
- PrivateKey: &key,
- ListenPort: &nodeport,
- ReplacePeers: true,
- Peers: peers,
- }
- if !ncutils.IsKernel() {
- if err := wireguard.WriteWgConfig(node, key.String(), peers); err != nil {
- logger.Log(1, "error writing wg conf file: ", err.Error())
- return err
- }
- // spin up userspace + apply the conf file
- var deviceiface = ifacename
- confPath := ncutils.GetNetclientPathSpecific() + ifacename + ".conf"
- d, _ := wgclient.Device(deviceiface)
- for d != nil && d.Name == deviceiface {
- _ = RemoveConf(ifacename, false) // remove interface first
- time.Sleep(time.Second >> 2)
- d, _ = wgclient.Device(deviceiface)
- }
- time.Sleep(time.Second >> 2)
- err = applyWGQuickConf(confPath)
- if err != nil {
- logger.Log(1, "failed to create wireguard interface")
- return err
- }
- } else {
- ipExec, err := exec.LookPath("ip")
- if err != nil {
- return err
- }
- _, err = wgclient.Device(ifacename)
- if err != nil {
- if os.IsNotExist(err) {
- fmt.Println("Device does not exist: ")
- fmt.Println(err)
- } else {
- return errors.New("Unknown config error: " + err.Error())
- }
- }
- err = wgclient.ConfigureDevice(ifacename, conf)
- if err != nil {
- if os.IsNotExist(err) {
- fmt.Println("Device does not exist: ")
- fmt.Println(err)
- } else {
- fmt.Printf("This is inconvenient: %v", err)
- }
- }
- 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 hasGateway {
- for _, gateway := range gateways {
- _, _ = ncutils.RunCmd(ipExec+" -4 route add "+gateway+" dev "+ifacename, true)
- }
- }
- if node.Address != "" {
- logger.Log(1, "adding address:", node.Address)
- _, _ = ncutils.RunCmd(ipExec+" address add dev "+ifacename+" "+node.Address+"/32", true)
- }
- if node.Address6 != "" {
- logger.Log(1, "adding address6:", node.Address6)
- _, _ = ncutils.RunCmd(ipExec+" address add dev "+ifacename+" "+node.Address6+"/128", true)
- }
- wireguard.SetPeers(ifacename, node, peers)
- }
- if node.IsServer == "yes" {
- setServerRoutes(node.Interface, node.Network)
- }
- return err
- }
- func setKernelDevice(ifacename, address4, mask4, address6, mask6 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)
- if address4 != "" {
- ncutils.RunCmd(ipExec+" address add dev "+ifacename+" "+address4+"/"+mask4, true)
- }
- if address6 != "" {
- ncutils.RunCmd(ipExec+" address add dev "+ifacename+" "+address6+"/"+mask6, true)
- }
- return nil
- }
- func applyWGQuickConf(confPath string) error {
- if _, err := ncutils.RunCmd("wg-quick up "+confPath, true); err != nil {
- return err
- }
- return nil
- }
- func removeWGQuickConf(confPath string, printlog bool) error {
- if _, err := ncutils.RunCmd("wg-quick down "+confPath, printlog); err != nil {
- return err
- }
- return nil
- }
- func setWGConfig(node *models.Node, peerupdate bool) error {
- peers, hasGateway, gateways, err := GetServerPeers(node)
- if err != nil {
- return err
- }
- privkey, err := FetchPrivKey(node.ID)
- if err != nil {
- return err
- }
- if peerupdate {
- if err := wireguard.SetPeers(node.Interface, node, peers); err != nil {
- logger.Log(0, "error updating peers", err.Error())
- }
- logger.Log(2, "updated peers on server", node.Name)
- } else {
- err = initWireguard(node, privkey, peers[:], hasGateway, gateways[:])
- logger.Log(3, "finished setting wg config on server", node.Name)
- }
- peers = nil
- return err
- }
- func setWGKeyConfig(node *models.Node) error {
- privatekey, err := wgtypes.GeneratePrivateKey()
- if err != nil {
- return err
- }
- privkeystring := privatekey.String()
- publickey := privatekey.PublicKey()
- node.PublicKey = publickey.String()
- err = StorePrivKey(node.ID, privkeystring)
- if err != nil {
- return err
- }
- if node.Action == models.NODE_UPDATE_KEY {
- node.Action = models.NODE_NOOP
- }
- return setWGConfig(node, false)
- }
- func removeLocalServer(node *models.Node) error {
- var err error
- var ifacename = node.Interface
- if err = RemovePrivKey(node.ID); err != nil {
- logger.Log(1, "failed to remove server conf from db", node.ID)
- }
- if ifacename != "" {
- if !ncutils.IsKernel() {
- if err = RemoveConf(ifacename, true); err == nil {
- logger.Log(1, "removed WireGuard interface:", ifacename)
- }
- } else {
- ipExec, err := exec.LookPath("ip")
- if err != nil {
- return err
- }
- out, err := ncutils.RunCmd(ipExec+" link del "+ifacename, false)
- dontprint := strings.Contains(out, "does not exist") || strings.Contains(out, "Cannot find device")
- if err != nil && !dontprint {
- logger.Log(1, "error running command:", ipExec, "link del", ifacename)
- logger.Log(1, out)
- }
- if node.PostDown != "" {
- runcmds := strings.Split(node.PostDown, "; ")
- _ = ncutils.RunCmds(runcmds, false)
- }
- }
- }
- home := ncutils.GetNetclientPathSpecific()
- if ncutils.FileExists(home + "netconfig-" + node.Network) {
- _ = os.Remove(home + "netconfig-" + node.Network)
- }
- if ncutils.FileExists(home + "nettoken-" + node.Network) {
- _ = os.Remove(home + "nettoken-" + node.Network)
- }
- if ncutils.FileExists(home + "secret-" + node.Network) {
- _ = os.Remove(home + "secret-" + node.Network)
- }
- if ncutils.FileExists(home + "wgkey-" + node.Network) {
- _ = os.Remove(home + "wgkey-" + node.Network)
- }
- if ncutils.FileExists(home + "nm-" + node.Network + ".conf") {
- _ = os.Remove(home + "nm-" + node.Network + ".conf")
- }
- return err
- }
- func setServerRoutes(iface, network string) {
- parentNetwork, err := GetParentNetwork(network)
- if err == nil {
- if parentNetwork.AddressRange != "" {
- ip, cidr, err := net.ParseCIDR(parentNetwork.AddressRange)
- if err == nil {
- local.SetCIDRRoute(iface, ip.String(), cidr)
- }
- }
- if parentNetwork.AddressRange6 != "" {
- ip, cidr, err := net.ParseCIDR(parentNetwork.AddressRange6)
- if err == nil {
- local.SetCIDRRoute(iface, ip.String(), cidr)
- }
- }
- }
- }
|