diff --git a/controllers/ext_client.go b/controllers/ext_client.go index a0b9ac35..ed2a4a90 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -363,6 +363,13 @@ func createExtClient(w http.ResponseWriter, r *http.Request) { extclient.Enabled = parentNetwork.DefaultACL == "yes" } + if err := logic.SetClientDefaultACLs(&extclient); err != nil { + logger.Log(0, r.Header.Get("user"), + fmt.Sprintf("failed to assign ACLs to new ext client on network [%s]: %v", networkName, err)) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + if err = logic.CreateExtClient(&extclient); err != nil { logger.Log(0, r.Header.Get("user"), fmt.Sprintf("failed to create new ext client on network [%s]: %v", networkName, err)) @@ -384,7 +391,7 @@ func createExtClient(w http.ResponseWriter, r *http.Request) { logger.Log(0, "failed to associate client", extclient.ClientID, "to user", userID) } extclient.OwnerID = userID - if _, err := logic.UpdateExtClient(extclient.ClientID, extclient.Network, extclient.Enabled, &extclient); err != nil { + if _, err := logic.UpdateExtClient(extclient.ClientID, extclient.Network, extclient.Enabled, &extclient, extclient.ACLs); err != nil { logger.Log(0, "failed to add owner id", userID, "to client", extclient.ClientID) } } @@ -477,10 +484,11 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) { } // == END PRO == - var changedEnabled = newExtClient.Enabled != oldExtClient.Enabled // indicates there was a change in enablement + var changedEnabled = (newExtClient.Enabled != oldExtClient.Enabled) || // indicates there was a change in enablement + len(newExtClient.ACLs) != len(oldExtClient.ACLs) // extra var need as logic.Update changes oldExtClient currentClient := oldExtClient - newclient, err := logic.UpdateExtClient(newExtClient.ClientID, params["network"], newExtClient.Enabled, &oldExtClient) + newclient, err := logic.UpdateExtClient(newExtClient.ClientID, params["network"], newExtClient.Enabled, &oldExtClient, newExtClient.ACLs) if err != nil { logger.Log(0, r.Header.Get("user"), fmt.Sprintf("failed to update ext client [%s], network [%s]: %v", @@ -570,7 +578,7 @@ func deleteExtClient(w http.ResponseWriter, r *http.Request) { } go func() { - if err := mq.PublishPeerUpdate(); err != nil { + if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil { logger.Log(1, "error setting ext peers on "+ingressnode.ID.String()+": "+err.Error()) } if err = mq.PublishDeleteExtClientDNS(&extclient); err != nil { diff --git a/controllers/node.go b/controllers/node.go index 9afde637..16c2d81c 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -434,7 +434,7 @@ func getNode(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - hostPeerUpdate, err := logic.GetPeerUpdateForHost(context.Background(), node.Network, host, nil) + hostPeerUpdate, err := logic.GetPeerUpdateForHost(context.Background(), node.Network, host, nil, nil) if err != nil && !database.IsEmptyRecord(err) { logger.Log(0, r.Header.Get("user"), fmt.Sprintf("error fetching wg peers config for host [ %s ]: %v", host.ID.String(), err)) @@ -623,7 +623,7 @@ func createNode(w http.ResponseWriter, r *http.Request) { return } } - hostPeerUpdate, err := logic.GetPeerUpdateForHost(context.Background(), networkName, &data.Host, nil) + hostPeerUpdate, err := logic.GetPeerUpdateForHost(context.Background(), networkName, &data.Host, nil, nil) if err != nil && !database.IsEmptyRecord(err) { logger.Log(0, r.Header.Get("user"), fmt.Sprintf("error fetching wg peers config for host [ %s ]: %v", data.Host.ID.String(), err)) diff --git a/ee/initialize.go b/ee/initialize.go index ff577e7d..c0508246 100644 --- a/ee/initialize.go +++ b/ee/initialize.go @@ -40,6 +40,9 @@ func InitEE() { logic.EnterpriseFailoverFunc = eelogic.SetFailover logic.EnterpriseResetFailoverFunc = eelogic.ResetFailover logic.EnterpriseResetAllPeersFailovers = eelogic.WipeAffectedFailoversOnly + logic.DenyClientNodeAccess = eelogic.DenyClientNode + logic.IsClientNodeAllowed = eelogic.IsClientNodeAllowed + logic.AllowClientNodeAccess = eelogic.RemoveDeniedNodeFromClient } func setControllerLimits() { diff --git a/ee/logic/ext_acls.go b/ee/logic/ext_acls.go new file mode 100644 index 00000000..ec6dc054 --- /dev/null +++ b/ee/logic/ext_acls.go @@ -0,0 +1,41 @@ +package logic + +import "github.com/gravitl/netmaker/models" + +// DenyClientNode - add a denied node to an ext client's list +func DenyClientNode(ec *models.ExtClient, clientOrNodeID string) (ok bool) { + if ec == nil || len(clientOrNodeID) == 0 { + return + } + if ec.ACLs == nil { + ec.ACLs = map[string]struct{}{} + } + ok = true + ec.ACLs[clientOrNodeID] = struct{}{} + return +} + +// IsClientNodeAllowed - checks if given ext client and node are allowed to communicate +func IsClientNodeAllowed(ec *models.ExtClient, clientOrNodeID string) bool { + if ec == nil || len(clientOrNodeID) == 0 { + return false + } + if ec.ACLs == nil { + return true + } + _, ok := ec.ACLs[clientOrNodeID] + return ok +} + +// RemoveDeniedNodeFromClient - removes a node id from set of denied nodes +func RemoveDeniedNodeFromClient(ec *models.ExtClient, clientOrNodeID string) bool { + if ec.ACLs == nil { + return true + } + _, ok := ec.ACLs[clientOrNodeID] + if !ok { + return false + } + delete(ec.ACLs, clientOrNodeID) + return true +} diff --git a/logic/clients.go b/logic/clients.go new file mode 100644 index 00000000..01427e5f --- /dev/null +++ b/logic/clients.go @@ -0,0 +1,53 @@ +package logic + +import "github.com/gravitl/netmaker/models" + +// functions defined here, handle client ACLs, should be set on ee + +var ( + // DenyClientNodeAccess - function to handle adding a node to an ext client's denied node set + DenyClientNodeAccess = func(ec *models.ExtClient, clientOrNodeID string) bool { return true } + // IsClientNodeAllowed - function to check if an ext client's denied node set contains a node ID + IsClientNodeAllowed = func(ec *models.ExtClient, clientOrNodeID string) bool { return true } + // AllowClientNodeAccess - function to handle removing a node ID from ext client's denied nodes, thus allowing it + AllowClientNodeAccess = func(ec *models.ExtClient, clientOrNodeID string) bool { return true } +) + +// SetClientDefaultACLs - set's a client's default ACLs based on network and nodes in network +func SetClientDefaultACLs(ec *models.ExtClient) error { + if !isEE { + return nil + } + networkNodes, err := GetNetworkNodes(ec.Network) + if err != nil { + return err + } + network, err := GetNetwork(ec.Network) + if err != nil { + return err + } + for i := range networkNodes { + currNode := networkNodes[i] + if network.DefaultACL == "no" || currNode.DefaultACL == "no" { + DenyClientNodeAccess(ec, currNode.ID.String()) + } + } + return nil +} + +// SetClientACLs - overwrites an ext client's ACL +func SetClientACLs(ec *models.ExtClient, newACLs map[string]struct{}) { + if ec == nil || newACLs == nil || !isEE { + return + } + ec.ACLs = newACLs +} + +// IsClientNodeAllowedByID - checks if a given ext client ID + nodeID are allowed +func IsClientNodeAllowedByID(clientID, networkName, clientOrNodeID string) bool { + client, err := GetExtClient(clientID, networkName) + if err != nil { + return false + } + return IsClientNodeAllowed(&client, clientOrNodeID) +} diff --git a/logic/extpeers.go b/logic/extpeers.go index 20bc4793..a71a4337 100644 --- a/logic/extpeers.go +++ b/logic/extpeers.go @@ -2,6 +2,7 @@ package logic import ( "encoding/json" + "fmt" "time" "github.com/gravitl/netmaker/database" @@ -114,6 +115,22 @@ func GetExtClient(clientid string, network string) (models.ExtClient, error) { return extclient, err } +// GetExtClient - gets a single ext client on a network +func GetExtClientByPubKey(publicKey string, network string) (*models.ExtClient, error) { + netClients, err := GetNetworkExtClients(network) + if err != nil { + return nil, err + } + for i := range netClients { + ec := netClients[i] + if ec.PublicKey == publicKey { + return &ec, nil + } + } + + return nil, fmt.Errorf("no client found") +} + // CreateExtClient - creates an extclient func CreateExtClient(extclient *models.ExtClient) error { @@ -172,15 +189,17 @@ func CreateExtClient(extclient *models.ExtClient) error { } // UpdateExtClient - only supports name changes right now -func UpdateExtClient(newclientid string, network string, enabled bool, client *models.ExtClient) (*models.ExtClient, error) { - +func UpdateExtClient(newclientid string, network string, enabled bool, client *models.ExtClient, newACLs map[string]struct{}) (*models.ExtClient, error) { err := DeleteExtClient(network, client.ClientID) if err != nil { return client, err } client.ClientID = newclientid client.Enabled = enabled - CreateExtClient(client) + SetClientACLs(client, newACLs) + if err = CreateExtClient(client); err != nil { + return client, err + } return client, err } diff --git a/logic/peers.go b/logic/peers.go index 76a2b664..8ee9e2ab 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -47,7 +47,7 @@ func GetProxyUpdateForHost(ctx context.Context, host *models.Host) (models.Proxy relayPeersMap := make(map[string]models.RelayedConf) for _, relayedHost := range relayedHosts { relayedHost := relayedHost - payload, err := GetPeerUpdateForHost(ctx, "", &relayedHost, nil) + payload, err := GetPeerUpdateForHost(ctx, "", &relayedHost, nil, nil) if err == nil { relayedEndpoint, udpErr := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", relayedHost.EndpointIP, GetPeerListenPort(&relayedHost))) if udpErr == nil { @@ -133,7 +133,7 @@ func ResetPeerUpdateContext() { } // GetPeerUpdateForHost - gets the consolidated peer update for the host from all networks -func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host, deletedNode *models.Node) (models.HostPeerUpdate, error) { +func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host, deletedNode *models.Node, deletedClient *models.ExtClient) (models.HostPeerUpdate, error) { if host == nil { return models.HostPeerUpdate{}, errors.New("host is nil") } @@ -250,6 +250,7 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host }, PeerKey: extPeerIdAndAddr.ID, Allow: true, + ID: extPeerIdAndAddr.ID, } } } @@ -265,6 +266,7 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host }, PeerKey: peerHost.PublicKey.String(), Allow: true, + ID: peer.ID.String(), } } @@ -319,6 +321,7 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host }, PeerKey: extPeerIdAndAddr.ID, Allow: true, + ID: extPeerIdAndAddr.ID, } } hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...) @@ -331,6 +334,7 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host Name: extPeerIdAndAddr.Name, Network: node.Network, } + hostPeerUpdate.IngressInfo.ExtPeers[extPeerIdAndAddr.ID] = models.ExtClientInfo{ Masquerade: true, IngGwAddr: net.IPNet{ @@ -343,7 +347,7 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host Mask: getCIDRMaskFromAddr(extPeerIdAndAddr.Address), }, ExtPeerKey: extPeerIdAndAddr.ID, - Peers: nodePeerMap, + Peers: filterNodeMapForClientACLs(extPeerIdAndAddr.ID, node.Network, nodePeerMap), } if node.Network == network { hostPeerUpdate.PeerIDs[extPeerIdAndAddr.ID] = extPeerIdAndAddr @@ -386,6 +390,16 @@ func GetPeerUpdateForHost(ctx context.Context, network string, host *models.Host hostPeerUpdate.NodePeers[i] = peer } + if deletedClient != nil { + key, err := wgtypes.ParseKey(deletedClient.PublicKey) + if err == nil { + hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, wgtypes.PeerConfig{ + PublicKey: key, + Remove: true, + }) + } + } + return hostPeerUpdate, nil } @@ -414,6 +428,7 @@ func getExtPeers(node *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, e return peers, idsAndAddr, err } for _, extPeer := range extPeers { + extPeer := extPeer pubkey, err := wgtypes.ParseKey(extPeer.PublicKey) if err != nil { logger.Log(1, "error parsing ext pub key:", err.Error()) @@ -645,3 +660,29 @@ func getCIDRMaskFromAddr(addr string) net.IPMask { } return cidr } + +// accounts for ext client ACLs +func filterNodeMapForClientACLs(publicKey, network string, nodePeerMap map[string]models.PeerRouteInfo) map[string]models.PeerRouteInfo { + if !isEE { + return nodePeerMap + } + if nodePeerMap == nil { + return map[string]models.PeerRouteInfo{} + } + + if len(publicKey) == 0 || len(network) == 0 { + return nodePeerMap + } + + client, err := GetExtClientByPubKey(publicKey, network) + if err != nil { + return nodePeerMap + } + for k := range nodePeerMap { + currNodePeer := nodePeerMap[k] + if _, ok := client.ACLs[currNodePeer.ID]; ok { + delete(nodePeerMap, k) + } + } + return nodePeerMap +} diff --git a/models/extclient.go b/models/extclient.go index c984a538..366174f5 100644 --- a/models/extclient.go +++ b/models/extclient.go @@ -2,16 +2,17 @@ package models // ExtClient - struct for external clients type ExtClient struct { - ClientID string `json:"clientid" bson:"clientid"` - Description string `json:"description" bson:"description"` - PrivateKey string `json:"privatekey" bson:"privatekey"` - PublicKey string `json:"publickey" bson:"publickey"` - Network string `json:"network" bson:"network"` - Address string `json:"address" bson:"address"` - Address6 string `json:"address6" bson:"address6"` - IngressGatewayID string `json:"ingressgatewayid" bson:"ingressgatewayid"` - IngressGatewayEndpoint string `json:"ingressgatewayendpoint" bson:"ingressgatewayendpoint"` - LastModified int64 `json:"lastmodified" bson:"lastmodified"` - Enabled bool `json:"enabled" bson:"enabled"` - OwnerID string `json:"ownerid" bson:"ownerid"` + ClientID string `json:"clientid" bson:"clientid"` + Description string `json:"description" bson:"description"` + PrivateKey string `json:"privatekey" bson:"privatekey"` + PublicKey string `json:"publickey" bson:"publickey"` + Network string `json:"network" bson:"network"` + Address string `json:"address" bson:"address"` + Address6 string `json:"address6" bson:"address6"` + IngressGatewayID string `json:"ingressgatewayid" bson:"ingressgatewayid"` + IngressGatewayEndpoint string `json:"ingressgatewayendpoint" bson:"ingressgatewayendpoint"` + LastModified int64 `json:"lastmodified" bson:"lastmodified"` + Enabled bool `json:"enabled" bson:"enabled"` + OwnerID string `json:"ownerid" bson:"ownerid"` + ACLs map[string]struct{} `json:"acls,omitempty" bson:"acls,omitempty"` } diff --git a/models/mqtt.go b/models/mqtt.go index 316d3fe8..35e1e216 100644 --- a/models/mqtt.go +++ b/models/mqtt.go @@ -41,6 +41,7 @@ type PeerRouteInfo struct { PeerAddr net.IPNet `json:"peer_addr" yaml:"peer_addr"` PeerKey string `json:"peer_key" yaml:"peer_key"` Allow bool `json:"allow" yaml:"allow"` + ID string `json:"id,omitempty" yaml:"id,omitempty"` } // ExtClientInfo - struct for ext. client and it's peers diff --git a/mq/handlers.go b/mq/handlers.go index 3755b9aa..c97c168b 100644 --- a/mq/handlers.go +++ b/mq/handlers.go @@ -185,7 +185,7 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) { logger.Log(0, "failed to send new node to host", hostUpdate.Host.Name, currentHost.ID.String(), err.Error()) return } else { - if err = PublishSingleHostPeerUpdate(context.Background(), currentHost, nil); err != nil { + if err = PublishSingleHostPeerUpdate(context.Background(), currentHost, nil, nil); err != nil { logger.Log(0, "failed peers publish after join acknowledged", hostUpdate.Host.Name, currentHost.ID.String(), err.Error()) return } @@ -279,7 +279,7 @@ func UpdateMetrics(client mqtt.Client, msg mqtt.Message) { logger.Log(2, "updating peers after node", currentNode.ID.String(), currentNode.Network, "detected connectivity issues") host, err := logic.GetHost(currentNode.HostID.String()) if err == nil { - if err = PublishSingleHostPeerUpdate(context.Background(), host, nil); err != nil { + if err = PublishSingleHostPeerUpdate(context.Background(), host, nil, nil); err != nil { logger.Log(0, "failed to publish update after failover peer change for node", currentNode.ID.String(), currentNode.Network) } } diff --git a/mq/publishers.go b/mq/publishers.go index 9e2f164a..67b2239d 100644 --- a/mq/publishers.go +++ b/mq/publishers.go @@ -27,7 +27,7 @@ func PublishPeerUpdate() error { logic.ResetPeerUpdateContext() for _, host := range hosts { host := host - if err = PublishSingleHostPeerUpdate(logic.PeerUpdateCtx, &host, nil); err != nil { + if err = PublishSingleHostPeerUpdate(logic.PeerUpdateCtx, &host, nil, nil); err != nil { logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error()) } } @@ -49,7 +49,29 @@ func PublishDeletedNodePeerUpdate(delNode *models.Node) error { logic.ResetPeerUpdateContext() for _, host := range hosts { host := host - if err = PublishSingleHostPeerUpdate(logic.PeerUpdateCtx, &host, delNode); err != nil { + if err = PublishSingleHostPeerUpdate(logic.PeerUpdateCtx, &host, delNode, nil); err != nil { + logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error()) + } + } + return err +} + +// PublishDeletedClientPeerUpdate --- determines and publishes a peer update +// to all the hosts with a deleted ext client to account for +func PublishDeletedClientPeerUpdate(delClient *models.ExtClient) error { + if !servercfg.IsMessageQueueBackend() { + return nil + } + + hosts, err := logic.GetAllHosts() + if err != nil { + logger.Log(1, "err getting all hosts", err.Error()) + return err + } + logic.ResetPeerUpdateContext() + for _, host := range hosts { + host := host + if err = PublishSingleHostPeerUpdate(logic.PeerUpdateCtx, &host, nil, delClient); err != nil { logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error()) } } @@ -57,9 +79,9 @@ func PublishDeletedNodePeerUpdate(delNode *models.Node) error { } // PublishSingleHostPeerUpdate --- determines and publishes a peer update to one host -func PublishSingleHostPeerUpdate(ctx context.Context, host *models.Host, deletedNode *models.Node) error { +func PublishSingleHostPeerUpdate(ctx context.Context, host *models.Host, deletedNode *models.Node, deletedClient *models.ExtClient) error { - peerUpdate, err := logic.GetPeerUpdateForHost(ctx, "", host, deletedNode) + peerUpdate, err := logic.GetPeerUpdateForHost(ctx, "", host, deletedNode, deletedClient) if err != nil { return err } @@ -429,7 +451,7 @@ func sendPeers() { for _, host := range hosts { host := host logger.Log(2, "sending scheduled peer update (5 min)") - if err = PublishSingleHostPeerUpdate(logic.PeerUpdateCtx, &host, nil); err != nil { + if err = PublishSingleHostPeerUpdate(logic.PeerUpdateCtx, &host, nil, nil); err != nil { logger.Log(1, "error publishing peer updates for host: ", host.ID.String(), " Err: ", err.Error()) } }