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
}