Skip to content

Commit

Permalink
Unify egress firewall implementation for both gateway modes,
Browse files Browse the repository at this point in the history
move all acls to the ClusterPortGroup.
Unify unit tests for both gateway modes.
Make sure egress firewall allows inbound connections.

Signed-off-by: Nadia Pinaeva <npinaeva@redhat.com>
  • Loading branch information
npinaeva committed Jan 11, 2023
1 parent b24f4f8 commit bd29f41
Show file tree
Hide file tree
Showing 3 changed files with 980 additions and 1,831 deletions.
93 changes: 26 additions & 67 deletions go-controller/pkg/ovn/egressfirewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/libovsdbops"
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb"
addressset "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/address_set"

"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"

kapi "k8s.io/api/core/v1"
Expand Down Expand Up @@ -83,20 +82,15 @@ func newEgressFirewallRule(rawEgressFirewallRule egressfirewallapi.EgressFirewal
return efr, nil
}

// This function is used to sync egress firewall setup. It does three "cleanups"

// - Cleanup the old implementation (using LRP) in local GW mode -> new implementation (using ACLs) local GW mode
// This function is used to sync egress firewall setup. Egress firewall implementation had many versions,
// the latest one makes no difference for gateway modes, and creates ACLs on types.ClusterPortGroupName.
// The following cleanups are needed from the previous versions:
// - Cleanup the old implementation (using LRP) in local GW mode
// For this it just deletes all LRP setup done for egress firewall
// And also convert all old ACLs which specifed from-lport to specifying to-lport

// - Cleanup the new local GW mode implementation (using ACLs on the node switch) -> shared GW mode implementation (using ACLs on the join switch)
// For this it just deletes all ACL setup done for egress firewall on the node switches

// - Cleanup the old implementation (using LRP) in local GW mode -> shared GW mode implementation (using ACLs on the join switch)
// For this it just deletes all LRP setup done for egress firewall

// - Cleanup for migration from shared GW mode -> local GW mode
// For this it just deletes all the ACLs on the distributed join switch
// - Cleanup the old implementation (using ACLs on the join and node switches)
// For this it deletes all the ACLs on the join and node switches, they will be created from scratch later.

// NOTE: Utilize the fact that we know that all egress firewall related setup must have a priority: types.MinimumReservedEgressFirewallPriority <= priority <= types.EgressFirewallStartPriority
// Upon failure, it may be invoked multiple times in order to avoid a pod restart.
Expand All @@ -110,25 +104,6 @@ func (oc *DefaultNetworkController) syncEgressFirewall(egressFirewalls []interfa
return fmt.Errorf("unable to list egress firewall ACLs, cannot cleanup old stale data, err: %v", err)
}

if len(egressFirewallACLs) != 0 {
var p func(item *nbdb.LogicalSwitch) bool
if config.Gateway.Mode == config.GatewayModeShared {
p = func(item *nbdb.LogicalSwitch) bool {
// Ignore external and Join switches(both legacy and current)
return !(strings.HasPrefix(item.Name, types.JoinSwitchPrefix) || item.Name == "join" || strings.HasPrefix(item.Name, types.ExternalSwitchPrefix))
}
} else if config.Gateway.Mode == config.GatewayModeLocal {
p = func(item *nbdb.LogicalSwitch) bool {
// Return only join switch (the per node ones if its old topology & distributed one if its new topology)
return (strings.HasPrefix(item.Name, types.JoinSwitchPrefix) || item.Name == "join")
}
}
err = libovsdbops.RemoveACLsFromLogicalSwitchesWithPredicate(oc.nbClient, p, egressFirewallACLs...)
if err != nil {
return fmt.Errorf("failed to remove reject acl from node logical switches: %v", err)
}
}

// Update the direction of each egressFirewallACL if needed.
// Update the egressFirewallACL name if needed.
namespaceToACLs := map[string][]*nbdb.ACL{}
Expand Down Expand Up @@ -161,7 +136,6 @@ func (oc *DefaultNetworkController) syncEgressFirewall(egressFirewalls []interfa
}

// In any gateway mode, make sure to delete all LRPs on ovn_cluster_router.
// This covers migration from LGW mode that used LRPs for EFW to using ACLs in SGW/LGW modes
p := func(item *nbdb.LogicalRouterPolicy) bool {
return item.Priority <= types.EgressFirewallStartPriority && item.Priority >= types.MinimumReservedEgressFirewallPriority
}
Expand All @@ -170,6 +144,15 @@ func (oc *DefaultNetworkController) syncEgressFirewall(egressFirewalls []interfa
return fmt.Errorf("error deleting egress firewall policies on router %s: %v", types.OVNClusterRouter, err)
}

// delete acls from all switches, they reside on the port group now
if len(egressFirewallACLs) != 0 {
err = libovsdbops.RemoveACLsFromLogicalSwitchesWithPredicate(oc.nbClient, func(item *nbdb.LogicalSwitch) bool { return true },
egressFirewallACLs...)
if err != nil {
return fmt.Errorf("failed to remove reject acl from node logical switches: %v", err)
}
}

// sync the ovn and k8s egressFirewall states
// represents the namespaces that have firewalls according to ovn
ovnEgressFirewalls := make(map[string]struct{})
Expand Down Expand Up @@ -338,26 +321,8 @@ func (oc *DefaultNetworkController) addEgressFirewallRules(ef *egressFirewall, h
}

// createEgressFirewallRules uses the previously generated elements and creates the
// logical_router_policy/join_switch_acl for a specific egressFirewallRouter
// acls for all node switches
func (oc *DefaultNetworkController) createEgressFirewallRules(priority int, match, action, externalID string, aclLogging *ACLLoggingLevels) error {
logicalSwitches := []string{}
if config.Gateway.Mode == config.GatewayModeLocal {
// Find all node switches
p := func(item *nbdb.LogicalSwitch) bool {
// Ignore external and Join switches(both legacy and current)
return !(strings.HasPrefix(item.Name, types.JoinSwitchPrefix) || item.Name == "join" || strings.HasPrefix(item.Name, types.ExternalSwitchPrefix))
}
nodeLocalSwitches, err := libovsdbops.FindLogicalSwitchesWithPredicate(oc.nbClient, p)
if err != nil {
return fmt.Errorf("unable to setup egress firewall ACLs on cluster nodes, err: %v", err)
}
for _, nodeLocalSwitch := range nodeLocalSwitches {
logicalSwitches = append(logicalSwitches, nodeLocalSwitch.Name)
}
} else {
logicalSwitches = append(logicalSwitches, types.OVNJoinSwitch)
}

// a name is needed for logging purposes - the name must be unique, so make it
// egressFirewall_<namespace name>_<priority>
aclName := buildEgressFwAclName(externalID, priority)
Expand All @@ -377,13 +342,13 @@ func (oc *DefaultNetworkController) createEgressFirewallRules(priority int, matc
return fmt.Errorf("failed to create egressFirewall ACL %v: %v", egressFirewallACL, err)
}

for _, logicalSwitchName := range logicalSwitches {
ops, err = libovsdbops.AddACLsToLogicalSwitchOps(oc.nbClient, ops, logicalSwitchName, egressFirewallACL)
if err != nil {
return fmt.Errorf("failed to add egressFirewall ACL %v to switch %s: %v", egressFirewallACL, logicalSwitchName, err)
}
// Applying ACLs on types.ClusterPortGroupName is equivalent to applying on every node switch, since
// types.ClusterPortGroupName contains management port from every switch.
ops, err = libovsdbops.AddACLsToPortGroupOps(oc.nbClient, ops, types.ClusterPortGroupName, egressFirewallACL)
if err != nil {
return fmt.Errorf("failed to add egressFirewall ACL %v to port group %s: %v",
egressFirewallACL, types.ClusterPortGroupName, err)
}

_, err = libovsdbops.TransactAndCheck(oc.nbClient, ops)
if err != nil {
return fmt.Errorf("failed to transact egressFirewall ACL: %v", err)
Expand All @@ -392,7 +357,7 @@ func (oc *DefaultNetworkController) createEgressFirewallRules(priority int, matc
return nil
}

// deleteEgressFirewallRules delete the specific logical router policy/join switch Acls
// deleteEgressFirewallRules delete egress firewall Acls
func (oc *DefaultNetworkController) deleteEgressFirewallRules(externalID string) error {
// Find ACLs for a given egressFirewall
pACL := func(item *nbdb.ACL) bool {
Expand All @@ -408,10 +373,7 @@ func (oc *DefaultNetworkController) deleteEgressFirewallRules(externalID string)
return nil
}

// delete egress firewall acls off any logical switch which has it,
// then manually remove the egressFirewall ACLs instead of relying on ovsdb garbage collection to do so
pSwitch := func(item *nbdb.LogicalSwitch) bool { return true }
err = libovsdbops.RemoveACLsFromLogicalSwitchesWithPredicate(oc.nbClient, pSwitch, egressFirewallACLs...)
err = libovsdbops.DeleteACLsFromPortGroups(oc.nbClient, []string{types.ClusterPortGroupName}, egressFirewallACLs...)
if err != nil {
return err
}
Expand Down Expand Up @@ -491,11 +453,8 @@ func generateMatch(ipv4Source, ipv6Source string, destinations []matchTarget, ds
match = fmt.Sprintf("%s && %s", match, egressGetL4Match(dstPorts))
}

if config.Gateway.Mode == config.GatewayModeLocal {
extraMatch = getClusterSubnetsExclusion()
} else {
extraMatch = fmt.Sprintf("inport == \"%s%s\"", types.JoinSwitchToGWRouterPrefix, types.OVNClusterRouter)
}
// TODO only add exclusion when egress firewall rule subnet intersects with cluster subnet
extraMatch = getClusterSubnetsExclusion()
return fmt.Sprintf("%s && %s", match, extraMatch)
}

Expand Down
Loading

0 comments on commit bd29f41

Please sign in to comment.