added logic for ext client ACLs

This commit is contained in:
0xdcarns
2023-03-13 15:06:07 -04:00
parent bcbe355281
commit 0f3faceb93
11 changed files with 220 additions and 31 deletions

View File

@@ -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 {

View File

@@ -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))

View File

@@ -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() {

41
ee/logic/ext_acls.go Normal file
View File

@@ -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
}

53
logic/clients.go Normal file
View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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"`
}

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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())
}
}