Skip to content

Commit

Permalink
manage-security-groups: Only add SGs to LB members (#2455)
Browse files Browse the repository at this point in the history
Previous code for manage-security-groups were adding the SGs to all the
ports attached to the Node. That doesn't make a lot of sense, we only
need this traffic opened to the LB members and we only add one port of
the Node as the LB member. Moreover we've even tried to add SGs to the
ports that had `port_security_enabled=false` which obviously failed.

This patch makes sure that we only add the SG opening NodePort traffic
to the port that is actually plugged into the LB. Moreover it adds a
check for `port_security_enabled` to make sure that ports with it
disabled are ignored.
  • Loading branch information
dulek committed Nov 3, 2023
1 parent 7659c8f commit fdba36b
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 9 deletions.
37 changes: 32 additions & 5 deletions pkg/openstack/loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -728,25 +728,52 @@ func getSubnetIDForLB(network *gophercloud.ServiceClient, node corev1.Node, pref
return "", cpoerrors.ErrNotFound
}

// applyNodeSecurityGroupIDForLB associates the security group with all the ports on the nodes.
func applyNodeSecurityGroupIDForLB(network *gophercloud.ServiceClient, nodes []*corev1.Node, sg string) error {
// isPortMember returns true if IP and subnetID are one of the FixedIPs on the port
func isPortMember(port PortWithPortSecurity, IP string, subnetID string) bool {
for _, fixedIP := range port.FixedIPs {
if (subnetID == "" || subnetID == fixedIP.SubnetID) && IP == fixedIP.IPAddress {
return true
}
}
return false
}

// applyNodeSecurityGroupIDForLB associates the security group with the ports being members of the LB on the nodes.
func applyNodeSecurityGroupIDForLB(network *gophercloud.ServiceClient, svcConf *serviceConfig, nodes []*corev1.Node, sg string) error {
for _, node := range nodes {
serverID, _, err := instanceIDFromProviderID(node.Spec.ProviderID)
if err != nil {
return fmt.Errorf("error getting server ID from the node: %w", err)
}

addr, _ := nodeAddressForLB(node, svcConf.preferredIPFamily)
if addr == "" {
// If node has no viable address let's ignore it.
continue
}

listOpts := neutronports.ListOpts{DeviceID: serverID}
allPorts, err := openstackutil.GetPorts(network, listOpts)
allPorts, err := openstackutil.GetPorts[PortWithPortSecurity](network, listOpts)
if err != nil {
return err
}

for _, port := range allPorts {
// You can't assign an SG to a port with port_security_enabled=false, skip them.
if !port.PortSecurityEnabled {
continue
}

// If the Security Group is already present on the port, skip it.
if slices.Contains(port.SecurityGroups, sg) {
continue
}

// Only add SGs to the port actually attached to the LB
if !isPortMember(port, addr, svcConf.lbMemberSubnetID) {
continue
}

// Add the SG to the port
// TODO(dulek): This isn't an atomic operation. In order to protect from lost update issues we should use
// `revision_number` handling to make sure our update to `security_groups` field wasn't preceded
Expand All @@ -768,7 +795,7 @@ func applyNodeSecurityGroupIDForLB(network *gophercloud.ServiceClient, nodes []*
func disassociateSecurityGroupForLB(network *gophercloud.ServiceClient, sg string) error {
// Find all the ports that have the security group associated.
listOpts := neutronports.ListOpts{SecurityGroups: []string{sg}}
allPorts, err := openstackutil.GetPorts(network, listOpts)
allPorts, err := openstackutil.GetPorts[neutronports.Port](network, listOpts)
if err != nil {
return err
}
Expand Down Expand Up @@ -2382,7 +2409,7 @@ func (lbaas *LbaasV2) ensureAndUpdateOctaviaSecurityGroup(clusterName string, ap
}
}

if err := applyNodeSecurityGroupIDForLB(lbaas.network, nodes, lbSecGroupID); err != nil {
if err := applyNodeSecurityGroupIDForLB(lbaas.network, svcConf, nodes, lbSecGroupID); err != nil {
return err
}
return nil
Expand Down
6 changes: 6 additions & 0 deletions pkg/openstack/openstack.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones"
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/trunk_details"
neutronports "github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
"github.com/spf13/pflag"
Expand Down Expand Up @@ -78,6 +79,11 @@ type PortWithTrunkDetails struct {
trunk_details.TrunkDetailsExt
}

type PortWithPortSecurity struct {
neutronports.Port
portsecurity.PortSecurityExt
}

// LoadBalancer is used for creating and maintaining load balancers
type LoadBalancer struct {
secret *gophercloud.ServiceClient
Expand Down
9 changes: 5 additions & 4 deletions pkg/util/openstack/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,16 @@ func getSubnet(networkSubnet string, subnetList []subnets.Subnet) *subnets.Subne
}

// GetPorts gets all the filtered ports.
func GetPorts(client *gophercloud.ServiceClient, listOpts neutronports.ListOpts) ([]neutronports.Port, error) {
func GetPorts[PortType interface{}](client *gophercloud.ServiceClient, listOpts neutronports.ListOpts) ([]PortType, error) {
mc := metrics.NewMetricContext("port", "list")
allPages, err := neutronports.List(client, listOpts).AllPages()
if mc.ObserveRequest(err) != nil {
return []neutronports.Port{}, err
return []PortType{}, err
}
allPorts, err := neutronports.ExtractPorts(allPages)
var allPorts []PortType
err = neutronports.ExtractPortsInto(allPages, &allPorts)
if err != nil {
return []neutronports.Port{}, err
return []PortType{}, err
}

return allPorts, nil
Expand Down

0 comments on commit fdba36b

Please sign in to comment.