Skip to content

Commit 42f4ede

Browse files
authored
Manage Security Groups for Octavia (kubernetes#1984)
This patch makes it possible to use the manage-security-groups with octavia load balancer. It creates a new function `ensureAndUpdateOctaviaSecurityGroup` which is called on `EnsureLoadBalancer` and `UpdateLoadBalancer` and handles the security group and security rule creation for Octavia Load Balancer. To make the changes not bigger than needed, it hooks into the `updateSecurityGroup` and `ensureSecurityGroup` function first, checks if Octavia is used and uses the new code. Otherwise the legacy code is called. So the removal of the legacy code should be quite easy. Also it adds a missing security rule for the HealthCheckNodePort. Signed-off-by: Sven Haardiek <[email protected]> Signed-off-by: Sven Haardiek <[email protected]>
1 parent 80dffd2 commit 42f4ede

File tree

2 files changed

+166
-5
lines changed

2 files changed

+166
-5
lines changed

docs/openstack-cloud-controller-manager/using-openstack-cloud-controller-manager.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,6 @@ Although the openstack-cloud-controller-manager was initially implemented with N
211211
* `manage-security-groups`
212212
If the Neutron security groups should be managed separately. Default: false
213213
214-
This option is not supported for Octavia. The worker nodes and the Octavia amphorae are usually in the same subnet, so it's sufficient to config the port security group rules manually for worker nodes, to allow the traffic coming from the the subnet IP range to the node port range(i.e. 30000-32767).
215-
216214
* `create-monitor`
217215
Indicates whether or not to create a health monitor for the service load balancer. A health monitor required for services that declare `externalTrafficPolicy: Local`. Default: false
218216

pkg/openstack/loadbalancer.go

Lines changed: 166 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,20 @@ func applyNodeSecurityGroupIDForLB(compute *gophercloud.ServiceClient, network *
862862
}
863863

864864
for _, port := range allPorts {
865+
// If the Security Group is already present on the port, skip it.
866+
// As soon as this only supports Go 1.18, this can be replaces by
867+
// slices.Contains.
868+
if func() bool {
869+
for _, currentSG := range port.SecurityGroups {
870+
if currentSG == sg {
871+
return true
872+
}
873+
}
874+
return false
875+
}() {
876+
continue
877+
}
878+
865879
newSGs := append(port.SecurityGroups, sg)
866880
updateOpts := neutronports.UpdateOpts{SecurityGroups: &newSGs}
867881
mc := metrics.NewMetricContext("port", "update")
@@ -2633,10 +2647,64 @@ func (lbaas *LbaasV2) getSubnet(subnet string) (*subnets.Subnet, error) {
26332647
return nil, fmt.Errorf("found multiple subnets with name %s", subnet)
26342648
}
26352649

2650+
// ensureSecurityRule ensures to create a security rule for a defined security
2651+
// group, if it not present.
2652+
func (lbaas *LbaasV2) ensureSecurityRule(
2653+
direction rules.RuleDirection,
2654+
protocol rules.RuleProtocol,
2655+
etherType rules.RuleEtherType,
2656+
remoteIPPrefix, secGroupID string,
2657+
portRangeMin, portRangeMax int,
2658+
) error {
2659+
sgListopts := rules.ListOpts{
2660+
Direction: string(direction),
2661+
Protocol: string(protocol),
2662+
PortRangeMax: portRangeMin,
2663+
PortRangeMin: portRangeMax,
2664+
RemoteIPPrefix: remoteIPPrefix,
2665+
SecGroupID: secGroupID,
2666+
}
2667+
sgRules, err := getSecurityGroupRules(lbaas.network, sgListopts)
2668+
if err != nil && !cpoerrors.IsNotFound(err) {
2669+
return fmt.Errorf(
2670+
"failed to find security group rules in %s: %v", secGroupID, err)
2671+
}
2672+
if len(sgRules) != 0 {
2673+
return nil
2674+
}
2675+
2676+
sgRuleCreateOpts := rules.CreateOpts{
2677+
Direction: direction,
2678+
Protocol: protocol,
2679+
PortRangeMax: portRangeMin,
2680+
PortRangeMin: portRangeMax,
2681+
RemoteIPPrefix: remoteIPPrefix,
2682+
SecGroupID: secGroupID,
2683+
EtherType: etherType,
2684+
}
2685+
2686+
mc := metrics.NewMetricContext("security_group_rule", "create")
2687+
_, err = rules.Create(lbaas.network, sgRuleCreateOpts).Extract()
2688+
if mc.ObserveRequest(err) != nil {
2689+
return fmt.Errorf(
2690+
"failed to create rule for security group %s: %v",
2691+
secGroupID, err)
2692+
}
2693+
return nil
2694+
}
2695+
26362696
// ensureSecurityGroup ensures security group exist for specific loadbalancer service.
26372697
// Creating security group for specific loadbalancer service when it does not exist.
26382698
func (lbaas *LbaasV2) ensureSecurityGroup(clusterName string, apiService *corev1.Service, nodes []*corev1.Node,
26392699
loadbalancer *loadbalancers.LoadBalancer, preferredIPFamily corev1.IPFamily, memberSubnetID string) error {
2700+
2701+
if lbaas.opts.UseOctavia {
2702+
return lbaas.ensureAndUpdateOctaviaSecurityGroup(clusterName, apiService, nodes, memberSubnetID)
2703+
}
2704+
2705+
// Following code is just for legacy Neutron-LBaaS support which has been deprecated since OpenStack stable/queens
2706+
// and not recommended using in production. No new features should be added.
2707+
26402708
// find node-security-group for service
26412709
var err error
26422710
if len(lbaas.opts.NodeSecurityGroupIDs) == 0 && !lbaas.opts.UseOctavia {
@@ -2928,7 +2996,7 @@ func (lbaas *LbaasV2) updateOctaviaLoadBalancer(ctx context.Context, clusterName
29282996
}
29292997

29302998
if lbaas.opts.ManageSecurityGroups {
2931-
err := lbaas.updateSecurityGroup(clusterName, service, nodes)
2999+
err := lbaas.updateSecurityGroup(clusterName, service, nodes, svcConf.lbMemberSubnetID)
29323000
if err != nil {
29333001
return fmt.Errorf("failed to update Security Group for loadbalancer service %s: %v", serviceName, err)
29343002
}
@@ -3092,7 +3160,8 @@ func (lbaas *LbaasV2) updateLoadBalancer(ctx context.Context, clusterName string
30923160
}
30933161

30943162
if lbaas.opts.ManageSecurityGroups {
3095-
err := lbaas.updateSecurityGroup(clusterName, service, nodes)
3163+
// MemberSubnetID is not needed, since this is the legacy call
3164+
err := lbaas.updateSecurityGroup(clusterName, service, nodes, "")
30963165
if err != nil {
30973166
return fmt.Errorf("failed to update Security Group for loadbalancer service %s: %v", serviceName, err)
30983167
}
@@ -3101,8 +3170,102 @@ func (lbaas *LbaasV2) updateLoadBalancer(ctx context.Context, clusterName string
31013170
return nil
31023171
}
31033172

3173+
// ensureAndUpdateOctaviaSecurityGroup handles the creation and update of the security group and the securiry rules for the octavia load balancer
3174+
func (lbaas *LbaasV2) ensureAndUpdateOctaviaSecurityGroup(clusterName string, apiService *corev1.Service, nodes []*corev1.Node, memberSubnetID string) error {
3175+
// get service ports
3176+
ports := apiService.Spec.Ports
3177+
if len(ports) == 0 {
3178+
return fmt.Errorf("no ports provided to openstack load balancer")
3179+
}
3180+
3181+
// ensure security group for LB
3182+
lbSecGroupName := getSecurityGroupName(apiService)
3183+
lbSecGroupID, err := secgroups.IDFromName(lbaas.network, lbSecGroupName)
3184+
if err != nil {
3185+
// If the security group of LB not exist, create it later
3186+
if isSecurityGroupNotFound(err) {
3187+
lbSecGroupID = ""
3188+
} else {
3189+
return fmt.Errorf("error occurred finding security group: %s: %v", lbSecGroupName, err)
3190+
}
3191+
}
3192+
if len(lbSecGroupID) == 0 {
3193+
// create security group
3194+
lbSecGroupCreateOpts := groups.CreateOpts{
3195+
Name: lbSecGroupName,
3196+
Description: fmt.Sprintf("Security Group for %s/%s Service LoadBalancer in cluster %s", apiService.Namespace, apiService.Name, clusterName),
3197+
}
3198+
3199+
mc := metrics.NewMetricContext("security_group", "create")
3200+
lbSecGroup, err := groups.Create(lbaas.network, lbSecGroupCreateOpts).Extract()
3201+
if mc.ObserveRequest(err) != nil {
3202+
return fmt.Errorf("failed to create Security Group for loadbalancer service %s/%s: %v", apiService.Namespace, apiService.Name, err)
3203+
}
3204+
lbSecGroupID = lbSecGroup.ID
3205+
}
3206+
3207+
mc := metrics.NewMetricContext("subnet", "get")
3208+
subnet, err := subnets.Get(lbaas.network, memberSubnetID).Extract()
3209+
if mc.ObserveRequest(err) != nil {
3210+
return fmt.Errorf(
3211+
"failed to find subnet %s from openstack: %v", memberSubnetID, err)
3212+
}
3213+
3214+
etherType := rules.EtherType4
3215+
if netutils.IsIPv6CIDRString(subnet.CIDR) {
3216+
etherType = rules.EtherType6
3217+
}
3218+
3219+
if apiService.Spec.HealthCheckNodePort != 0 {
3220+
err = lbaas.ensureSecurityRule(
3221+
rules.DirIngress,
3222+
rules.ProtocolTCP,
3223+
etherType,
3224+
subnet.CIDR,
3225+
lbSecGroupID,
3226+
int(apiService.Spec.HealthCheckNodePort),
3227+
int(apiService.Spec.HealthCheckNodePort),
3228+
)
3229+
if err != nil {
3230+
return fmt.Errorf(
3231+
"failed to apply security rule for health check node port, %w",
3232+
err)
3233+
}
3234+
}
3235+
3236+
// ensure rules for node security group
3237+
for _, port := range ports {
3238+
err = lbaas.ensureSecurityRule(
3239+
rules.DirIngress,
3240+
rules.RuleProtocol(port.Protocol),
3241+
etherType,
3242+
subnet.CIDR,
3243+
lbSecGroupID,
3244+
int(port.NodePort),
3245+
int(port.NodePort),
3246+
)
3247+
if err != nil {
3248+
return fmt.Errorf(
3249+
"failed to apply security rule for port %d, %w",
3250+
port.NodePort, err)
3251+
}
3252+
3253+
if err := applyNodeSecurityGroupIDForLB(lbaas.compute, lbaas.network, nodes, lbSecGroupID); err != nil {
3254+
return err
3255+
}
3256+
}
3257+
return nil
3258+
}
3259+
31043260
// updateSecurityGroup updating security group for specific loadbalancer service.
3105-
func (lbaas *LbaasV2) updateSecurityGroup(_ string, apiService *corev1.Service, nodes []*corev1.Node) error {
3261+
func (lbaas *LbaasV2) updateSecurityGroup(clusterName string, apiService *corev1.Service, nodes []*corev1.Node, memberSubnetID string) error {
3262+
if lbaas.opts.UseOctavia {
3263+
return lbaas.ensureAndUpdateOctaviaSecurityGroup(clusterName, apiService, nodes, memberSubnetID)
3264+
}
3265+
3266+
// Following code is just for legacy Neutron-LBaaS support which has been deprecated since OpenStack stable/queens
3267+
// and not recommended using in production. No new features should be added.
3268+
31063269
originalNodeSecurityGroupIDs := lbaas.opts.NodeSecurityGroupIDs
31073270

31083271
var err error

0 commit comments

Comments
 (0)