func()

in pkg/openstack/loadbalancer.go [2465:2688]


func (lbaas *LbaasV2) ensureSecurityGroup(clusterName string, apiService *corev1.Service, nodes []*corev1.Node, loadbalancer *loadbalancers.LoadBalancer) error {
	// find node-security-group for service
	var err error
	if len(lbaas.opts.NodeSecurityGroupIDs) == 0 && !lbaas.opts.UseOctavia {
		lbaas.opts.NodeSecurityGroupIDs, err = getNodeSecurityGroupIDForLB(lbaas.compute, lbaas.network, nodes)
		if err != nil {
			return fmt.Errorf("failed to find node-security-group for loadbalancer service %s/%s: %v", apiService.Namespace, apiService.Name, err)
		}

		klog.V(4).Infof("find node-security-group %v for loadbalancer service %s/%s", lbaas.opts.NodeSecurityGroupIDs, apiService.Namespace, apiService.Name)
	}

	// get service ports
	ports := apiService.Spec.Ports
	if len(ports) == 0 {
		return fmt.Errorf("no ports provided to openstack load balancer")
	}

	// get service source ranges
	sourceRanges, err := GetLoadBalancerSourceRanges(apiService)
	if err != nil {
		return fmt.Errorf("failed to get source ranges for loadbalancer service %s/%s: %v", apiService.Namespace, apiService.Name, err)
	}

	// ensure security group for LB
	lbSecGroupName := getSecurityGroupName(apiService)
	lbSecGroupID, err := secgroups.IDFromName(lbaas.network, lbSecGroupName)
	if err != nil {
		// If the security group of LB not exist, create it later
		if isSecurityGroupNotFound(err) {
			lbSecGroupID = ""
		} else {
			return fmt.Errorf("error occurred finding security group: %s: %v", lbSecGroupName, err)
		}
	}
	if len(lbSecGroupID) == 0 {
		// create security group
		lbSecGroupCreateOpts := groups.CreateOpts{
			Name:        lbSecGroupName,
			Description: fmt.Sprintf("Security Group for %s/%s Service LoadBalancer in cluster %s", apiService.Namespace, apiService.Name, clusterName),
		}

		mc := metrics.NewMetricContext("security_group", "create")
		lbSecGroup, err := groups.Create(lbaas.network, lbSecGroupCreateOpts).Extract()
		if mc.ObserveRequest(err) != nil {
			return fmt.Errorf("failed to create Security Group for loadbalancer service %s/%s: %v", apiService.Namespace, apiService.Name, err)
		}
		lbSecGroupID = lbSecGroup.ID

		if !lbaas.opts.UseOctavia {
			//add rule in security group
			for _, port := range ports {
				for _, sourceRange := range sourceRanges.StringSlice() {
					ethertype := rules.EtherType4
					network, _, err := net.ParseCIDR(sourceRange)

					if err != nil {
						return fmt.Errorf("error parsing source range %s as a CIDR: %v", sourceRange, err)
					}

					if network.To4() == nil {
						ethertype = rules.EtherType6
					}

					lbSecGroupRuleCreateOpts := rules.CreateOpts{
						Direction:      rules.DirIngress,
						PortRangeMax:   int(port.Port),
						PortRangeMin:   int(port.Port),
						Protocol:       toRuleProtocol(port.Protocol),
						RemoteIPPrefix: sourceRange,
						SecGroupID:     lbSecGroup.ID,
						EtherType:      ethertype,
					}

					mc := metrics.NewMetricContext("security_group_rule", "create")
					_, err = rules.Create(lbaas.network, lbSecGroupRuleCreateOpts).Extract()

					if mc.ObserveRequest(err) != nil {
						return fmt.Errorf("error occurred creating rule for SecGroup %s: %v", lbSecGroup.ID, err)
					}
				}
			}

			lbSecGroupRuleCreateOpts := rules.CreateOpts{
				Direction:      rules.DirIngress,
				PortRangeMax:   4, // ICMP: Code -  Values for ICMP  "Destination Unreachable: Fragmentation Needed and Don't Fragment was Set"
				PortRangeMin:   3, // ICMP: Type
				Protocol:       rules.ProtocolICMP,
				RemoteIPPrefix: "0.0.0.0/0", // The Fragmentation packet can come from anywhere along the path back to the sourceRange - we need to all this from all
				SecGroupID:     lbSecGroup.ID,
				EtherType:      rules.EtherType4,
			}

			mc := metrics.NewMetricContext("security_group_rule", "create")
			_, err = rules.Create(lbaas.network, lbSecGroupRuleCreateOpts).Extract()

			if mc.ObserveRequest(err) != nil {
				return fmt.Errorf("error occurred creating rule for SecGroup %s: %v", lbSecGroup.ID, err)
			}

			lbSecGroupRuleCreateOpts = rules.CreateOpts{
				Direction:      rules.DirIngress,
				PortRangeMax:   0, // ICMP: Code - Values for ICMP "Packet Too Big"
				PortRangeMin:   2, // ICMP: Type
				Protocol:       rules.ProtocolICMP,
				RemoteIPPrefix: "::/0", // The Fragmentation packet can come from anywhere along the path back to the sourceRange - we need to all this from all
				SecGroupID:     lbSecGroup.ID,
				EtherType:      rules.EtherType6,
			}

			mc = metrics.NewMetricContext("security_group_rule", "create")
			_, err = rules.Create(lbaas.network, lbSecGroupRuleCreateOpts).Extract()
			if mc.ObserveRequest(err) != nil {
				return fmt.Errorf("error occurred creating rule for SecGroup %s: %v", lbSecGroup.ID, err)
			}

			// get security groups of port
			portID := loadbalancer.VipPortID
			port, err := getPortByID(lbaas.network, portID)
			if err != nil {
				return err
			}

			// ensure the vip port has the security groups
			found := false
			for _, portSecurityGroups := range port.SecurityGroups {
				if portSecurityGroups == lbSecGroup.ID {
					found = true
					break
				}
			}

			// update loadbalancer vip port
			if !found {
				port.SecurityGroups = append(port.SecurityGroups, lbSecGroup.ID)
				updateOpts := neutronports.UpdateOpts{SecurityGroups: &port.SecurityGroups}
				mc := metrics.NewMetricContext("port", "update")
				res := neutronports.Update(lbaas.network, portID, updateOpts)
				if mc.ObserveRequest(res.Err) != nil {
					msg := fmt.Sprintf("Error occurred updating port %s for loadbalancer service %s/%s: %v", portID, apiService.Namespace, apiService.Name, res.Err)
					return fmt.Errorf(msg)
				}
			}
		}
	}

	// ensure rules for node security group
	for _, port := range ports {
		// If Octavia is used, the VIP port security group is already taken good care of, we only need to allow ingress
		// traffic from Octavia amphorae to the node port on the worker nodes.
		if lbaas.opts.UseOctavia {
			mc := metrics.NewMetricContext("subnet", "get")
			subnet, err := subnets.Get(lbaas.network, lbaas.opts.SubnetID).Extract()
			if mc.ObserveRequest(err) != nil {
				return fmt.Errorf("failed to find subnet %s from openstack: %v", lbaas.opts.SubnetID, err)
			}

			sgListopts := rules.ListOpts{
				Direction:      string(rules.DirIngress),
				Protocol:       string(port.Protocol),
				PortRangeMax:   int(port.NodePort),
				PortRangeMin:   int(port.NodePort),
				RemoteIPPrefix: subnet.CIDR,
				SecGroupID:     lbSecGroupID,
			}
			sgRules, err := getSecurityGroupRules(lbaas.network, sgListopts)
			if err != nil && !cpoerrors.IsNotFound(err) {
				return fmt.Errorf("failed to find security group rules in %s: %v", lbSecGroupID, err)
			}
			if len(sgRules) != 0 {
				continue
			}

			// The Octavia amphorae and worker nodes are supposed to be in the same subnet. We allow the ingress traffic
			// from the amphorae to the specific node port on the nodes.
			sgRuleCreateOpts := rules.CreateOpts{
				Direction:      rules.DirIngress,
				PortRangeMax:   int(port.NodePort),
				PortRangeMin:   int(port.NodePort),
				Protocol:       toRuleProtocol(port.Protocol),
				RemoteIPPrefix: subnet.CIDR,
				SecGroupID:     lbSecGroupID,
				EtherType:      rules.EtherType4,
			}
			mc = metrics.NewMetricContext("security_group_rule", "create")
			_, err = rules.Create(lbaas.network, sgRuleCreateOpts).Extract()
			if mc.ObserveRequest(err) != nil {
				return fmt.Errorf("failed to create rule for security group %s: %v", lbSecGroupID, err)
			}

			if err := applyNodeSecurityGroupIDForLB(lbaas.compute, lbaas.network, nodes, lbSecGroupID); err != nil {
				return err
			}
		} else {
			for _, nodeSecurityGroupID := range lbaas.opts.NodeSecurityGroupIDs {
				opts := rules.ListOpts{
					Direction:     string(rules.DirIngress),
					SecGroupID:    nodeSecurityGroupID,
					RemoteGroupID: lbSecGroupID,
					PortRangeMax:  int(port.NodePort),
					PortRangeMin:  int(port.NodePort),
					Protocol:      string(port.Protocol),
				}
				secGroupRules, err := getSecurityGroupRules(lbaas.network, opts)
				if err != nil && !cpoerrors.IsNotFound(err) {
					msg := fmt.Sprintf("Error finding rules for remote group id %s in security group id %s: %v", lbSecGroupID, nodeSecurityGroupID, err)
					return fmt.Errorf(msg)
				}
				if len(secGroupRules) != 0 {
					// Do not add rule when find rules for remote group in the Node Security Group
					continue
				}

				// Add the rules in the Node Security Group
				err = createNodeSecurityGroup(lbaas.network, nodeSecurityGroupID, int(port.NodePort), port.Protocol, lbSecGroupID)
				if err != nil {
					return fmt.Errorf("error occurred creating security group for loadbalancer service %s/%s: %v", apiService.Namespace, apiService.Name, err)
				}
			}
		}
	}

	return nil
}