Skip to content

Commit 318b4c1

Browse files
dulekshaardie
andauthored
[occm] Delete unused SG rules with manage-security-groups (#2287)
* Apply LB SG to nodes only once Code applying SG to nodes was incorrectly put in a loop over Services ports. This commit fixes that. * Delete unused SG rules with manage-security-groups This commit makes sure unnecessary SG rules are removed when we're reconciling a Service. Co-Authored-By: Sven Haardiek <[email protected]> --------- Co-authored-by: Sven Haardiek <[email protected]>
1 parent 9ed6d96 commit 318b4c1

File tree

2 files changed

+411
-45
lines changed

2 files changed

+411
-45
lines changed

pkg/openstack/loadbalancer.go

Lines changed: 112 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,15 @@ func getSecurityGroupName(service *corev1.Service) string {
422422
return securityGroupName
423423
}
424424

425+
func getSecurityGroupRules(client *gophercloud.ServiceClient, opts rules.ListOpts) ([]rules.SecGroupRule, error) {
426+
mc := metrics.NewMetricContext("security_group_rule", "list")
427+
page, err := rules.List(client, opts).AllPages()
428+
if mc.ObserveRequest(err) != nil {
429+
return nil, err
430+
}
431+
return rules.ExtractRules(page)
432+
}
433+
425434
func getListenerProtocol(protocol corev1.Protocol, svcConf *serviceConfig) listeners.Protocol {
426435
// Make neutron-lbaas code work
427436
if svcConf != nil {
@@ -2060,30 +2069,16 @@ func (lbaas *LbaasV2) listSubnetsForNetwork(networkID string, tweak ...TweakSubN
20602069
}
20612070

20622071
// group, if it not present.
2063-
func (lbaas *LbaasV2) ensureSecurityRule(
2064-
direction rules.RuleDirection,
2065-
protocol rules.RuleProtocol,
2066-
etherType rules.RuleEtherType,
2067-
remoteIPPrefix, secGroupID string,
2068-
portRangeMin, portRangeMax int,
2069-
) error {
2070-
sgRuleCreateOpts := rules.CreateOpts{
2071-
Direction: direction,
2072-
Protocol: protocol,
2073-
PortRangeMax: portRangeMin,
2074-
PortRangeMin: portRangeMax,
2075-
RemoteIPPrefix: remoteIPPrefix,
2076-
SecGroupID: secGroupID,
2077-
EtherType: etherType,
2078-
}
2079-
2072+
func (lbaas *LbaasV2) ensureSecurityRule(sgRuleCreateOpts rules.CreateOpts) error {
20802073
mc := metrics.NewMetricContext("security_group_rule", "create")
20812074
_, err := rules.Create(lbaas.network, sgRuleCreateOpts).Extract()
20822075
if err != nil && cpoerrors.IsConflictError(err) {
20832076
// Conflict means the SG rule already exists, so ignoring that error.
2077+
klog.Warningf("Security group rule already found when trying to create it. This indicates concurrent "+
2078+
"updates to the SG %s and is unexpected", sgRuleCreateOpts.SecGroupID)
20842079
return mc.ObserveRequest(nil)
20852080
} else if mc.ObserveRequest(err) != nil {
2086-
return fmt.Errorf("failed to create rule for security group %s: %v", secGroupID, err)
2081+
return fmt.Errorf("failed to create rule for security group %s: %v", sgRuleCreateOpts.SecGroupID, err)
20872082
}
20882083
return nil
20892084
}
@@ -2174,6 +2169,50 @@ func (lbaas *LbaasV2) UpdateLoadBalancer(ctx context.Context, clusterName string
21742169
return mc.ObserveReconcile(err)
21752170
}
21762171

2172+
func compareSecurityGroupRuleAndCreateOpts(rule rules.SecGroupRule, opts rules.CreateOpts) bool {
2173+
return rule.Direction == string(opts.Direction) &&
2174+
rule.Protocol == string(opts.Protocol) &&
2175+
rule.EtherType == string(opts.EtherType) &&
2176+
rule.RemoteIPPrefix == opts.RemoteIPPrefix &&
2177+
rule.PortRangeMin == opts.PortRangeMin &&
2178+
rule.PortRangeMax == opts.PortRangeMax
2179+
}
2180+
2181+
func getRulesToCreateAndDelete(wantedRules []rules.CreateOpts, existingRules []rules.SecGroupRule) ([]rules.CreateOpts, []rules.SecGroupRule) {
2182+
toCreate := make([]rules.CreateOpts, 0, len(wantedRules)) // Max is all rules need creation
2183+
toDelete := make([]rules.SecGroupRule, 0, len(existingRules)) // Max will be all the existing rules to be deleted
2184+
// Surely this can be done in a more efficient way. Is it worth optimizing if most of
2185+
// the time we'll deal with just 1 or 2 elements in each array? I doubt it.
2186+
for _, existingRule := range existingRules {
2187+
found := false
2188+
for _, wantedRule := range wantedRules {
2189+
if compareSecurityGroupRuleAndCreateOpts(existingRule, wantedRule) {
2190+
found = true
2191+
break
2192+
}
2193+
}
2194+
if !found {
2195+
// in existingRules but not in wantedRules, delete
2196+
toDelete = append(toDelete, existingRule)
2197+
}
2198+
}
2199+
for _, wantedRule := range wantedRules {
2200+
found := false
2201+
for _, existingRule := range existingRules {
2202+
if compareSecurityGroupRuleAndCreateOpts(existingRule, wantedRule) {
2203+
found = true
2204+
break
2205+
}
2206+
}
2207+
if !found {
2208+
// in wantedRules but not in exisitngRules, create
2209+
toCreate = append(toCreate, wantedRule)
2210+
}
2211+
}
2212+
2213+
return toCreate, toDelete
2214+
}
2215+
21772216
// ensureAndUpdateOctaviaSecurityGroup handles the creation and update of the security group and the securiry rules for the octavia load balancer
21782217
func (lbaas *LbaasV2) ensureAndUpdateOctaviaSecurityGroup(clusterName string, apiService *corev1.Service, nodes []*corev1.Node, memberSubnetID string) error {
21792218
// get service ports
@@ -2220,47 +2259,75 @@ func (lbaas *LbaasV2) ensureAndUpdateOctaviaSecurityGroup(clusterName string, ap
22202259
etherType = rules.EtherType6
22212260
}
22222261

2262+
existingRules, err := getSecurityGroupRules(lbaas.network, rules.ListOpts{SecGroupID: lbSecGroupID})
2263+
if err != nil {
2264+
return fmt.Errorf(
2265+
"failed to find security group rules in %s: %v", lbSecGroupID, err)
2266+
}
2267+
2268+
// List of the security group rules wanted in the SG.
2269+
// Number of Ports plus the potential HealthCheckNodePort.
2270+
wantedRules := make([]rules.CreateOpts, 0, len(ports)+1)
2271+
22232272
if apiService.Spec.HealthCheckNodePort != 0 {
2224-
err = lbaas.ensureSecurityRule(
2225-
rules.DirIngress,
2226-
rules.ProtocolTCP,
2227-
etherType,
2228-
subnet.CIDR,
2229-
lbSecGroupID,
2230-
int(apiService.Spec.HealthCheckNodePort),
2231-
int(apiService.Spec.HealthCheckNodePort),
2273+
wantedRules = append(wantedRules,
2274+
rules.CreateOpts{
2275+
Direction: rules.DirIngress,
2276+
Protocol: rules.ProtocolTCP,
2277+
EtherType: etherType,
2278+
RemoteIPPrefix: subnet.CIDR,
2279+
SecGroupID: lbSecGroupID,
2280+
PortRangeMin: int(apiService.Spec.HealthCheckNodePort),
2281+
PortRangeMax: int(apiService.Spec.HealthCheckNodePort),
2282+
},
22322283
)
2233-
if err != nil {
2234-
return fmt.Errorf(
2235-
"failed to apply security rule for health check node port, %w",
2236-
err)
2237-
}
22382284
}
22392285

2240-
// ensure rules for node security group
22412286
for _, port := range ports {
22422287
if port.NodePort == 0 { // It's 0 when AllocateLoadBalancerNodePorts=False
22432288
continue
22442289
}
2245-
err = lbaas.ensureSecurityRule(
2246-
rules.DirIngress,
2247-
rules.RuleProtocol(port.Protocol),
2248-
etherType,
2249-
subnet.CIDR,
2250-
lbSecGroupID,
2251-
int(port.NodePort),
2252-
int(port.NodePort),
2290+
wantedRules = append(wantedRules,
2291+
rules.CreateOpts{
2292+
Direction: rules.DirIngress,
2293+
Protocol: rules.RuleProtocol(port.Protocol),
2294+
EtherType: etherType,
2295+
RemoteIPPrefix: subnet.CIDR,
2296+
SecGroupID: lbSecGroupID,
2297+
PortRangeMin: int(port.NodePort),
2298+
PortRangeMax: int(port.NodePort),
2299+
},
22532300
)
2301+
}
2302+
2303+
toCreate, toDelete := getRulesToCreateAndDelete(wantedRules, existingRules)
2304+
2305+
// create new rules
2306+
for _, opts := range toCreate {
2307+
err := lbaas.ensureSecurityRule(opts)
22542308
if err != nil {
2255-
return fmt.Errorf(
2256-
"failed to apply security rule for port %d, %w",
2257-
port.NodePort, err)
2309+
return fmt.Errorf("failed to apply security rule (%v), %w", opts, err)
22582310
}
2311+
}
22592312

2260-
if err := applyNodeSecurityGroupIDForLB(lbaas.compute, lbaas.network, nodes, lbSecGroupID); err != nil {
2261-
return err
2313+
// delete unneeded rules
2314+
for _, existingRule := range toDelete {
2315+
klog.Infof("Deleting rule %s from security group %s (%s)", existingRule.ID, existingRule.SecGroupID, lbSecGroupName)
2316+
mc := metrics.NewMetricContext("security_group_rule", "delete")
2317+
err := rules.Delete(lbaas.network, existingRule.ID).ExtractErr()
2318+
if err != nil && cpoerrors.IsNotFound(err) {
2319+
// ignore 404
2320+
klog.Warningf("Security group rule %s found missing when trying to delete it. This indicates concurrent "+
2321+
"updates to the SG %s and is unexpected", existingRule.ID, existingRule.SecGroupID)
2322+
return mc.ObserveRequest(nil)
2323+
} else if mc.ObserveRequest(err) != nil {
2324+
return fmt.Errorf("failed to delete security group rule %s: %w", existingRule.ID, err)
22622325
}
22632326
}
2327+
2328+
if err := applyNodeSecurityGroupIDForLB(lbaas.compute, lbaas.network, nodes, lbSecGroupID); err != nil {
2329+
return err
2330+
}
22642331
return nil
22652332
}
22662333

0 commit comments

Comments
 (0)