Skip to content

Commit fc8ccd9

Browse files
dulekshaardie
authored andcommitted
[occm] Delete unused SG rules with manage-security-groups (kubernetes#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 343acf9 commit fc8ccd9

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
@@ -431,6 +431,15 @@ func getSecurityGroupName(service *corev1.Service) string {
431431
return securityGroupName
432432
}
433433

434+
func getSecurityGroupRules(client *gophercloud.ServiceClient, opts rules.ListOpts) ([]rules.SecGroupRule, error) {
435+
mc := metrics.NewMetricContext("security_group_rule", "list")
436+
page, err := rules.List(client, opts).AllPages()
437+
if mc.ObserveRequest(err) != nil {
438+
return nil, err
439+
}
440+
return rules.ExtractRules(page)
441+
}
442+
434443
func getListenerProtocol(protocol corev1.Protocol, svcConf *serviceConfig) listeners.Protocol {
435444
// Make neutron-lbaas code work
436445
if svcConf != nil {
@@ -2091,30 +2100,16 @@ func (lbaas *LbaasV2) listSubnetsForNetwork(networkID string, tweak ...TweakSubN
20912100
}
20922101

20932102
// group, if it not present.
2094-
func (lbaas *LbaasV2) ensureSecurityRule(
2095-
direction rules.RuleDirection,
2096-
protocol rules.RuleProtocol,
2097-
etherType rules.RuleEtherType,
2098-
remoteIPPrefix, secGroupID string,
2099-
portRangeMin, portRangeMax int,
2100-
) error {
2101-
sgRuleCreateOpts := rules.CreateOpts{
2102-
Direction: direction,
2103-
Protocol: protocol,
2104-
PortRangeMax: portRangeMin,
2105-
PortRangeMin: portRangeMax,
2106-
RemoteIPPrefix: remoteIPPrefix,
2107-
SecGroupID: secGroupID,
2108-
EtherType: etherType,
2109-
}
2110-
2103+
func (lbaas *LbaasV2) ensureSecurityRule(sgRuleCreateOpts rules.CreateOpts) error {
21112104
mc := metrics.NewMetricContext("security_group_rule", "create")
21122105
_, err := rules.Create(lbaas.network, sgRuleCreateOpts).Extract()
21132106
if err != nil && cpoerrors.IsConflictError(err) {
21142107
// Conflict means the SG rule already exists, so ignoring that error.
2108+
klog.Warningf("Security group rule already found when trying to create it. This indicates concurrent "+
2109+
"updates to the SG %s and is unexpected", sgRuleCreateOpts.SecGroupID)
21152110
return mc.ObserveRequest(nil)
21162111
} else if mc.ObserveRequest(err) != nil {
2117-
return fmt.Errorf("failed to create rule for security group %s: %v", secGroupID, err)
2112+
return fmt.Errorf("failed to create rule for security group %s: %v", sgRuleCreateOpts.SecGroupID, err)
21182113
}
21192114
return nil
21202115
}
@@ -2210,6 +2205,50 @@ func (lbaas *LbaasV2) UpdateLoadBalancer(ctx context.Context, clusterName string
22102205
return mc.ObserveReconcile(err)
22112206
}
22122207

2208+
func compareSecurityGroupRuleAndCreateOpts(rule rules.SecGroupRule, opts rules.CreateOpts) bool {
2209+
return rule.Direction == string(opts.Direction) &&
2210+
rule.Protocol == string(opts.Protocol) &&
2211+
rule.EtherType == string(opts.EtherType) &&
2212+
rule.RemoteIPPrefix == opts.RemoteIPPrefix &&
2213+
rule.PortRangeMin == opts.PortRangeMin &&
2214+
rule.PortRangeMax == opts.PortRangeMax
2215+
}
2216+
2217+
func getRulesToCreateAndDelete(wantedRules []rules.CreateOpts, existingRules []rules.SecGroupRule) ([]rules.CreateOpts, []rules.SecGroupRule) {
2218+
toCreate := make([]rules.CreateOpts, 0, len(wantedRules)) // Max is all rules need creation
2219+
toDelete := make([]rules.SecGroupRule, 0, len(existingRules)) // Max will be all the existing rules to be deleted
2220+
// Surely this can be done in a more efficient way. Is it worth optimizing if most of
2221+
// the time we'll deal with just 1 or 2 elements in each array? I doubt it.
2222+
for _, existingRule := range existingRules {
2223+
found := false
2224+
for _, wantedRule := range wantedRules {
2225+
if compareSecurityGroupRuleAndCreateOpts(existingRule, wantedRule) {
2226+
found = true
2227+
break
2228+
}
2229+
}
2230+
if !found {
2231+
// in existingRules but not in wantedRules, delete
2232+
toDelete = append(toDelete, existingRule)
2233+
}
2234+
}
2235+
for _, wantedRule := range wantedRules {
2236+
found := false
2237+
for _, existingRule := range existingRules {
2238+
if compareSecurityGroupRuleAndCreateOpts(existingRule, wantedRule) {
2239+
found = true
2240+
break
2241+
}
2242+
}
2243+
if !found {
2244+
// in wantedRules but not in exisitngRules, create
2245+
toCreate = append(toCreate, wantedRule)
2246+
}
2247+
}
2248+
2249+
return toCreate, toDelete
2250+
}
2251+
22132252
// ensureAndUpdateOctaviaSecurityGroup handles the creation and update of the security group and the securiry rules for the octavia load balancer
22142253
func (lbaas *LbaasV2) ensureAndUpdateOctaviaSecurityGroup(clusterName string, apiService *corev1.Service, nodes []*corev1.Node, memberSubnetID string) error {
22152254
// get service ports
@@ -2256,47 +2295,75 @@ func (lbaas *LbaasV2) ensureAndUpdateOctaviaSecurityGroup(clusterName string, ap
22562295
etherType = rules.EtherType6
22572296
}
22582297

2298+
existingRules, err := getSecurityGroupRules(lbaas.network, rules.ListOpts{SecGroupID: lbSecGroupID})
2299+
if err != nil {
2300+
return fmt.Errorf(
2301+
"failed to find security group rules in %s: %v", lbSecGroupID, err)
2302+
}
2303+
2304+
// List of the security group rules wanted in the SG.
2305+
// Number of Ports plus the potential HealthCheckNodePort.
2306+
wantedRules := make([]rules.CreateOpts, 0, len(ports)+1)
2307+
22592308
if apiService.Spec.HealthCheckNodePort != 0 {
2260-
err = lbaas.ensureSecurityRule(
2261-
rules.DirIngress,
2262-
rules.ProtocolTCP,
2263-
etherType,
2264-
subnet.CIDR,
2265-
lbSecGroupID,
2266-
int(apiService.Spec.HealthCheckNodePort),
2267-
int(apiService.Spec.HealthCheckNodePort),
2309+
wantedRules = append(wantedRules,
2310+
rules.CreateOpts{
2311+
Direction: rules.DirIngress,
2312+
Protocol: rules.ProtocolTCP,
2313+
EtherType: etherType,
2314+
RemoteIPPrefix: subnet.CIDR,
2315+
SecGroupID: lbSecGroupID,
2316+
PortRangeMin: int(apiService.Spec.HealthCheckNodePort),
2317+
PortRangeMax: int(apiService.Spec.HealthCheckNodePort),
2318+
},
22682319
)
2269-
if err != nil {
2270-
return fmt.Errorf(
2271-
"failed to apply security rule for health check node port, %w",
2272-
err)
2273-
}
22742320
}
22752321

2276-
// ensure rules for node security group
22772322
for _, port := range ports {
22782323
if port.NodePort == 0 { // It's 0 when AllocateLoadBalancerNodePorts=False
22792324
continue
22802325
}
2281-
err = lbaas.ensureSecurityRule(
2282-
rules.DirIngress,
2283-
rules.RuleProtocol(port.Protocol),
2284-
etherType,
2285-
subnet.CIDR,
2286-
lbSecGroupID,
2287-
int(port.NodePort),
2288-
int(port.NodePort),
2326+
wantedRules = append(wantedRules,
2327+
rules.CreateOpts{
2328+
Direction: rules.DirIngress,
2329+
Protocol: rules.RuleProtocol(port.Protocol),
2330+
EtherType: etherType,
2331+
RemoteIPPrefix: subnet.CIDR,
2332+
SecGroupID: lbSecGroupID,
2333+
PortRangeMin: int(port.NodePort),
2334+
PortRangeMax: int(port.NodePort),
2335+
},
22892336
)
2337+
}
2338+
2339+
toCreate, toDelete := getRulesToCreateAndDelete(wantedRules, existingRules)
2340+
2341+
// create new rules
2342+
for _, opts := range toCreate {
2343+
err := lbaas.ensureSecurityRule(opts)
22902344
if err != nil {
2291-
return fmt.Errorf(
2292-
"failed to apply security rule for port %d, %w",
2293-
port.NodePort, err)
2345+
return fmt.Errorf("failed to apply security rule (%v), %w", opts, err)
22942346
}
2347+
}
22952348

2296-
if err := applyNodeSecurityGroupIDForLB(lbaas.compute, lbaas.network, nodes, lbSecGroupID); err != nil {
2297-
return err
2349+
// delete unneeded rules
2350+
for _, existingRule := range toDelete {
2351+
klog.Infof("Deleting rule %s from security group %s (%s)", existingRule.ID, existingRule.SecGroupID, lbSecGroupName)
2352+
mc := metrics.NewMetricContext("security_group_rule", "delete")
2353+
err := rules.Delete(lbaas.network, existingRule.ID).ExtractErr()
2354+
if err != nil && cpoerrors.IsNotFound(err) {
2355+
// ignore 404
2356+
klog.Warningf("Security group rule %s found missing when trying to delete it. This indicates concurrent "+
2357+
"updates to the SG %s and is unexpected", existingRule.ID, existingRule.SecGroupID)
2358+
return mc.ObserveRequest(nil)
2359+
} else if mc.ObserveRequest(err) != nil {
2360+
return fmt.Errorf("failed to delete security group rule %s: %w", existingRule.ID, err)
22982361
}
22992362
}
2363+
2364+
if err := applyNodeSecurityGroupIDForLB(lbaas.compute, lbaas.network, nodes, lbSecGroupID); err != nil {
2365+
return err
2366+
}
23002367
return nil
23012368
}
23022369

0 commit comments

Comments
 (0)