Skip to content

Commit 788ea08

Browse files
authored
Don't allow internal Services to share an LB (#2190)
Feature of sharing LBs seems to have some logic flaws. One of them is the problem of mixing internal and external Services on a single LB. As a FIP is tied to the LB and not individual listener, it means that if one Service attached the FIP to the Service, all the other Services will be available on that FIP. This may lead to accidental exposure of an internal Service which can potentially be pretty bad. This commits attempts to limit the number of cases when the user can shoot themselves in the foot and makes it impossible for the internal Services to be secondary on a share load balancer. Moreover a condition is added that prevents secondary Services to create FIPs at all, so that accidental exposure of primary internal service by a secondary external one is solved. There is no other way to reliably do that prevention as we only save truncated name into the tags of the LB, so there's no way to get list of all Services in a load balancer without listing all of them (in every namespace) and looking through their LB ID annotation. Even with that it's still really complicated to make decisions. Cases (P - primary, S - secondary, E - external, I - internal): P S E E - all good E I - prevented, all good I I - prevented, not great, because it's technically a working combination, but it's a cost I E - external one won't create FIP, good Updates (after #2168 merges): E E -> E I - we'll get error on the second Service, good E E -> I E - FIP gets detached, E won't be able to readd it, good.
1 parent c9b9801 commit 788ea08

File tree

1 file changed

+19
-6
lines changed

1 file changed

+19
-6
lines changed

pkg/openstack/loadbalancer.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,8 @@ func (lbaas *LbaasV2) createFloatingIP(msg string, floatIPOpts floatingips.Creat
912912
// 1. The floating IP that is already attached to the VIP port.
913913
// 2. Floating IP specified in Spec.LoadBalancerIP
914914
// 3. Create a new one
915-
func (lbaas *LbaasV2) getServiceAddress(clusterName string, service *corev1.Service, lb *loadbalancers.LoadBalancer, svcConf *serviceConfig) (string, error) {
915+
func (lbaas *LbaasV2) getServiceAddress(clusterName string, service *corev1.Service, lb *loadbalancers.LoadBalancer, svcConf *serviceConfig, isLBOwner bool) (string, error) {
916+
916917
if svcConf.internal {
917918
return lb.VipAddress, nil
918919
}
@@ -928,6 +929,13 @@ func (lbaas *LbaasV2) getServiceAddress(clusterName string, service *corev1.Serv
928929
}
929930
klog.V(4).Infof("Found floating ip %v by loadbalancer port id %q", floatIP, portID)
930931

932+
// we cannot add a FIP to a shared LB when we're a secondary Service or we risk adding it to an internal
933+
// Service and exposing it to the world unintentionally.
934+
if floatIP == nil && !isLBOwner {
935+
return "", fmt.Errorf("cannot attach a floating IP to a load balancer for a shared Service %s/%s, only owner Service can do that",
936+
service.Namespace, service.Name)
937+
}
938+
931939
// second attempt: fetch floating IP specified in service Spec.LoadBalancerIP
932940
// if found, associate floating IP with loadbalancer's VIP port
933941
loadBalancerIP := service.Spec.LoadBalancerIP
@@ -1875,8 +1883,8 @@ func (lbaas *LbaasV2) ensureOctaviaLoadBalancer(ctx context.Context, clusterName
18751883
return nil, fmt.Errorf("shared load balancer is only supported with the tag feature in the cloud load balancer service")
18761884
}
18771885

1878-
// The load balancer can only be shared with the configured number of Services.
18791886
if svcConf.supportLBTags {
1887+
// The load balancer can only be shared with the configured number of Services.
18801888
sharedCount := 0
18811889
for _, tag := range loadbalancer.Tags {
18821890
if strings.HasPrefix(tag, servicePrefix) {
@@ -1886,6 +1894,12 @@ func (lbaas *LbaasV2) ensureOctaviaLoadBalancer(ctx context.Context, clusterName
18861894
if !isLBOwner && !cpoutil.Contains(loadbalancer.Tags, lbName) && sharedCount+1 > lbaas.opts.MaxSharedLB {
18871895
return nil, fmt.Errorf("load balancer %s already shared with %d Services", loadbalancer.ID, sharedCount)
18881896
}
1897+
1898+
// Internal load balancer cannot be shared to prevent situations when we accidentally expose it because the
1899+
// owner Service becomes external.
1900+
if !isLBOwner && svcConf.internal {
1901+
return nil, fmt.Errorf("internal Service cannot share a load balancer")
1902+
}
18891903
}
18901904
} else {
18911905
legacyName := lbaas.getLoadBalancerLegacyName(ctx, clusterName, service)
@@ -1901,10 +1915,9 @@ func (lbaas *LbaasV2) ensureOctaviaLoadBalancer(ctx context.Context, clusterName
19011915
return nil, fmt.Errorf("error creating loadbalancer %s: %v", lbName, err)
19021916
}
19031917
createNewLB = true
1904-
} else {
1905-
// This is a Service created before shared LB is supported.
1906-
isLBOwner = true
19071918
}
1919+
// This is a Service created before shared LB is supported or a brand new LB.
1920+
isLBOwner = true
19081921
}
19091922

19101923
if loadbalancer.ProvisioningStatus != activeStatus {
@@ -1960,7 +1973,7 @@ func (lbaas *LbaasV2) ensureOctaviaLoadBalancer(ctx context.Context, clusterName
19601973
}
19611974
}
19621975

1963-
addr, err := lbaas.getServiceAddress(clusterName, service, loadbalancer, svcConf)
1976+
addr, err := lbaas.getServiceAddress(clusterName, service, loadbalancer, svcConf, isLBOwner)
19641977
if err != nil {
19651978
return nil, err
19661979
}

0 commit comments

Comments
 (0)