diff --git a/controllers/ext_client.go b/controllers/ext_client.go index 3035979e..7e7c88f7 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -481,6 +481,9 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) { sendPeerUpdate = true logic.SetClientACLs(&oldExtClient, update.DeniedACLs) } + if !logic.IsSlicesEqual(update.ExtraAllowedIPs, oldExtClient.ExtraAllowedIPs) { + sendPeerUpdate = true + } if update.Enabled != oldExtClient.Enabled { sendPeerUpdate = true diff --git a/logic/extpeers.go b/logic/extpeers.go index e19b2edc..aa6b715c 100644 --- a/logic/extpeers.go +++ b/logic/extpeers.go @@ -3,11 +3,13 @@ package logic import ( "encoding/json" "fmt" + "net" "reflect" "sync" "time" "github.com/gravitl/netmaker/database" + "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/models" "golang.org/x/exp/slog" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" @@ -184,6 +186,9 @@ func CreateExtClient(extclient *models.ExtClient) error { } else if len(extclient.PrivateKey) == 0 && len(extclient.PublicKey) > 0 { extclient.PrivateKey = "[ENTER PRIVATE KEY]" } + if extclient.ExtraAllowedIPs == nil { + extclient.ExtraAllowedIPs = []string{} + } parentNetwork, err := GetNetwork(extclient.Network) if err != nil { @@ -247,9 +252,7 @@ func UpdateExtClient(old *models.ExtClient, update *models.CustomExtClient) mode if update.Enabled != old.Enabled { new.Enabled = update.Enabled } - if update.ExtraAllowedIPs != nil && StringDifference(old.ExtraAllowedIPs, update.ExtraAllowedIPs) != nil { - new.ExtraAllowedIPs = update.ExtraAllowedIPs - } + new.ExtraAllowedIPs = update.ExtraAllowedIPs if update.DeniedACLs != nil && !reflect.DeepEqual(old.DeniedACLs, update.DeniedACLs) { new.DeniedACLs = update.DeniedACLs } @@ -318,3 +321,107 @@ func ToggleExtClientConnectivity(client *models.ExtClient, enable bool) (models. return newClient, nil } + +func getExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, []models.EgressNetworkRoutes, error) { + var peers []wgtypes.PeerConfig + var idsAndAddr []models.IDandAddr + var egressRoutes []models.EgressNetworkRoutes + extPeers, err := GetNetworkExtClients(node.Network) + if err != nil { + return peers, idsAndAddr, egressRoutes, err + } + host, err := GetHost(node.HostID.String()) + if err != nil { + return peers, idsAndAddr, egressRoutes, err + } + for _, extPeer := range extPeers { + extPeer := extPeer + if !IsClientNodeAllowed(&extPeer, peer.ID.String()) { + continue + } + pubkey, err := wgtypes.ParseKey(extPeer.PublicKey) + if err != nil { + logger.Log(1, "error parsing ext pub key:", err.Error()) + continue + } + + if host.PublicKey.String() == extPeer.PublicKey || + extPeer.IngressGatewayID != node.ID.String() || !extPeer.Enabled { + continue + } + + var allowedips []net.IPNet + var peer wgtypes.PeerConfig + if extPeer.Address != "" { + var peeraddr = net.IPNet{ + IP: net.ParseIP(extPeer.Address), + Mask: net.CIDRMask(32, 32), + } + if peeraddr.IP != nil && peeraddr.Mask != nil { + allowedips = append(allowedips, peeraddr) + } + } + + if extPeer.Address6 != "" { + var addr6 = net.IPNet{ + IP: net.ParseIP(extPeer.Address6), + Mask: net.CIDRMask(128, 128), + } + if addr6.IP != nil && addr6.Mask != nil { + allowedips = append(allowedips, addr6) + } + } + for _, extraAllowedIP := range extPeer.ExtraAllowedIPs { + _, cidr, err := net.ParseCIDR(extraAllowedIP) + if err == nil { + allowedips = append(allowedips, *cidr) + } + } + egressRoutes = append(egressRoutes, getExtPeerEgressRoute(extPeer)...) + primaryAddr := extPeer.Address + if primaryAddr == "" { + primaryAddr = extPeer.Address6 + } + peer = wgtypes.PeerConfig{ + PublicKey: pubkey, + ReplaceAllowedIPs: true, + AllowedIPs: allowedips, + } + peers = append(peers, peer) + idsAndAddr = append(idsAndAddr, models.IDandAddr{ + ID: peer.PublicKey.String(), + Name: extPeer.ClientID, + Address: primaryAddr, + IsExtClient: true, + }) + } + return peers, idsAndAddr, egressRoutes, nil + +} + +func getExtPeerEgressRoute(extPeer models.ExtClient) (egressRoutes []models.EgressNetworkRoutes) { + if extPeer.Address != "" { + egressRoutes = append(egressRoutes, models.EgressNetworkRoutes{ + NodeAddr: extPeer.AddressIPNet4(), + EgressRanges: extPeer.ExtraAllowedIPs, + }) + } + if extPeer.Address6 != "" { + egressRoutes = append(egressRoutes, models.EgressNetworkRoutes{ + NodeAddr: extPeer.AddressIPNet6(), + EgressRanges: extPeer.ExtraAllowedIPs, + }) + } + return +} + +func getExtpeersExtraRoutes(network string) (egressRoutes []models.EgressNetworkRoutes) { + extPeers, err := GetNetworkExtClients(network) + if err != nil { + return + } + for _, extPeer := range extPeers { + egressRoutes = append(egressRoutes, getExtPeerEgressRoute(extPeer)...) + } + return +} diff --git a/logic/peers.go b/logic/peers.go index 9f86fd2d..9e7e32ca 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -129,6 +129,9 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N EgressRanges: peer.EgressGatewayRanges, }) } + if peer.IsIngressGateway { + hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, getExtpeersExtraRoutes(peer.Network)...) + } if (node.IsRelayed && node.RelayedBy != peer.ID.String()) || (peer.IsRelayed && peer.RelayedBy != node.ID.String()) { // if node is relayed and peer is not the relay, set remove to true if _, ok := peerIndexMap[peerHost.PublicKey.String()]; ok { @@ -207,9 +210,11 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N } var extPeers []wgtypes.PeerConfig var extPeerIDAndAddrs []models.IDandAddr + var egressRoutes []models.EgressNetworkRoutes if node.IsIngressGateway { - extPeers, extPeerIDAndAddrs, err = getExtPeers(&node, &node) + extPeers, extPeerIDAndAddrs, egressRoutes, err = getExtPeers(&node, &node) if err == nil { + hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, egressRoutes...) hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...) for _, extPeerIdAndAddr := range extPeerIDAndAddrs { extPeerIdAndAddr := extPeerIdAndAddr @@ -290,76 +295,6 @@ func GetPeerListenPort(host *models.Host) int { return peerPort } -func getExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, error) { - var peers []wgtypes.PeerConfig - var idsAndAddr []models.IDandAddr - extPeers, err := GetNetworkExtClients(node.Network) - if err != nil { - return peers, idsAndAddr, err - } - host, err := GetHost(node.HostID.String()) - if err != nil { - return peers, idsAndAddr, err - } - for _, extPeer := range extPeers { - extPeer := extPeer - if !IsClientNodeAllowed(&extPeer, peer.ID.String()) { - continue - } - pubkey, err := wgtypes.ParseKey(extPeer.PublicKey) - if err != nil { - logger.Log(1, "error parsing ext pub key:", err.Error()) - continue - } - - if host.PublicKey.String() == extPeer.PublicKey || - extPeer.IngressGatewayID != node.ID.String() || !extPeer.Enabled { - continue - } - - var allowedips []net.IPNet - var peer wgtypes.PeerConfig - if extPeer.Address != "" { - var peeraddr = net.IPNet{ - IP: net.ParseIP(extPeer.Address), - Mask: net.CIDRMask(32, 32), - } - if peeraddr.IP != nil && peeraddr.Mask != nil { - allowedips = append(allowedips, peeraddr) - } - } - - if extPeer.Address6 != "" { - var addr6 = net.IPNet{ - IP: net.ParseIP(extPeer.Address6), - Mask: net.CIDRMask(128, 128), - } - if addr6.IP != nil && addr6.Mask != nil { - allowedips = append(allowedips, addr6) - } - } - - primaryAddr := extPeer.Address - if primaryAddr == "" { - primaryAddr = extPeer.Address6 - } - peer = wgtypes.PeerConfig{ - PublicKey: pubkey, - ReplaceAllowedIPs: true, - AllowedIPs: allowedips, - } - peers = append(peers, peer) - idsAndAddr = append(idsAndAddr, models.IDandAddr{ - ID: peer.PublicKey.String(), - Name: extPeer.ClientID, - Address: primaryAddr, - IsExtClient: true, - }) - } - return peers, idsAndAddr, nil - -} - // GetAllowedIPs - calculates the wireguard allowedip field for a peer of a node based on the peer and node settings func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet { var allowedips []net.IPNet @@ -367,7 +302,7 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet // handle ingress gateway peers if peer.IsIngressGateway { - extPeers, _, err := getExtPeers(peer, node) + extPeers, _, _, err := getExtPeers(peer, node) if err != nil { logger.Log(2, "could not retrieve ext peers for ", peer.ID.String(), err.Error()) } diff --git a/logic/util.go b/logic/util.go index 52ed902a..bb38a952 100644 --- a/logic/util.go +++ b/logic/util.go @@ -134,4 +134,18 @@ func RemoveStringSlice(slice []string, i int) []string { return append(slice[:i], slice[i+1:]...) } +// IsSlicesEqual tells whether a and b contain the same elements. +// A nil argument is equivalent to an empty slice. +func IsSlicesEqual(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true +} + // == private == diff --git a/models/node.go b/models/node.go index 777c6aaf..3e230488 100644 --- a/models/node.go +++ b/models/node.go @@ -192,6 +192,22 @@ func (node *Node) PrimaryAddress() string { return node.Address6.IP.String() } +// ExtClient.PrimaryAddress - returns ipv4 IPNet format +func (extPeer *ExtClient) AddressIPNet4() net.IPNet { + return net.IPNet{ + IP: net.ParseIP(extPeer.Address), + Mask: net.CIDRMask(32, 32), + } +} + +// ExtClient.AddressIPNet6 - return ipv6 IPNet format +func (extPeer *ExtClient) AddressIPNet6() net.IPNet { + return net.IPNet{ + IP: net.ParseIP(extPeer.Address), + Mask: net.CIDRMask(128, 128), + } +} + // Node.PrimaryNetworkRange - returns node's parent network, returns ipv4 address if present, else return ipv6 func (node *Node) PrimaryNetworkRange() net.IPNet { if node.NetworkRange.IP != nil {