Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collapse not shared NSG rules with multiple source ranges #71484

Merged
merged 1 commit into from
Dec 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
104 changes: 78 additions & 26 deletions pkg/cloudprovider/providers/azure/azure_loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,7 @@ func (az *Cloud) reconcileSecurityGroup(clusterName string, service *v1.Service,
if err != nil {
return nil, err
}
var sourceAddressPrefixes []string
sourceAddressPrefixes := []string{}
if (sourceRanges == nil || serviceapi.IsAllowAll(sourceRanges)) && len(serviceTags) == 0 {
if !requiresInternalLoadBalancer(service) {
sourceAddressPrefixes = []string{"Internet"}
Expand All @@ -1006,35 +1006,51 @@ func (az *Cloud) reconcileSecurityGroup(clusterName string, service *v1.Service,
}
expectedSecurityRules := []network.SecurityRule{}

if wantLb {
expectedSecurityRules = make([]network.SecurityRule, len(ports)*len(sourceAddressPrefixes))

for i, port := range ports {
if wantLb && len(sourceAddressPrefixes) > 0 {
for _, port := range ports {
_, securityProto, _, err := getProtocolsFromKubernetesProtocol(port.Protocol)
if err != nil {
return nil, err
}
for j := range sourceAddressPrefixes {
ix := i*len(sourceAddressPrefixes) + j
securityRuleName := az.getSecurityRuleName(service, port, sourceAddressPrefixes[j])
expectedSecurityRules[ix] = network.SecurityRule{
if useSharedSecurityRule(service) {
// When service is shared, a separate rule is created for each element in sourceAddressPrefixes
for j := range sourceAddressPrefixes {
securityRuleName := az.getSecurityRuleName(service, port, sourceAddressPrefixes[j])
expectedSecurityRules = append(expectedSecurityRules, network.SecurityRule{
Name: to.StringPtr(securityRuleName),
SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
Protocol: *securityProto,
SourcePortRange: to.StringPtr("*"),
DestinationPortRange: to.StringPtr(strconv.Itoa(int(port.Port))),
SourceAddressPrefix: to.StringPtr(sourceAddressPrefixes[j]),
DestinationAddressPrefix: to.StringPtr(destinationIPAddress),
Access: network.SecurityRuleAccessAllow,
Direction: network.SecurityRuleDirectionInbound,
},
})
}
} else {
// When service is not shared, a single rule UUID-PROTOCOL-PORT is created
securityRuleName := az.getSecurityRuleName(service, port, "")
expectedSecurityRules = append(expectedSecurityRules, network.SecurityRule{
Name: to.StringPtr(securityRuleName),
SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{
Protocol: *securityProto,
SourcePortRange: to.StringPtr("*"),
DestinationPortRange: to.StringPtr(strconv.Itoa(int(port.Port))),
SourceAddressPrefix: to.StringPtr(sourceAddressPrefixes[j]),
SourceAddressPrefixes: &sourceAddressPrefixes,
DestinationAddressPrefix: to.StringPtr(destinationIPAddress),
Access: network.SecurityRuleAccessAllow,
Direction: network.SecurityRuleDirectionInbound,
},
}
})
}

}
}

for _, r := range expectedSecurityRules {
klog.V(10).Infof("Expecting security rule for %s: %s:%s -> %s:%s", service.Name, *r.SourceAddressPrefix, *r.SourcePortRange, *r.DestinationAddressPrefix, *r.DestinationPortRange)
klog.V(10).Infof("Expecting security rule for %s: %s:%s -> %s:%s", service.Name, logSafeCollection(r.SourceAddressPrefix, r.SourceAddressPrefixes), *r.SourcePortRange, *r.DestinationAddressPrefix, *r.DestinationPortRange)
}

// update security rules
Expand All @@ -1045,7 +1061,7 @@ func (az *Cloud) reconcileSecurityGroup(clusterName string, service *v1.Service,
}

for _, r := range updatedRules {
klog.V(10).Infof("Existing security rule while processing %s: %s:%s -> %s:%s", service.Name, logSafe(r.SourceAddressPrefix), logSafe(r.SourcePortRange), logSafeCollection(r.DestinationAddressPrefix, r.DestinationAddressPrefixes), logSafe(r.DestinationPortRange))
klog.V(10).Infof("Existing security rule while processing %s: %s:%s -> %s:%s", service.Name, logSafeCollection(r.SourceAddressPrefix, r.SourceAddressPrefixes), logSafe(r.SourcePortRange), logSafeCollection(r.DestinationAddressPrefix, r.DestinationAddressPrefixes), logSafe(r.DestinationPortRange))
}

// update security rules: remove unwanted rules that belong privately
Expand Down Expand Up @@ -1074,17 +1090,14 @@ func (az *Cloud) reconcileSecurityGroup(clusterName string, service *v1.Service,
sharedRuleName := az.getSecurityRuleName(service, port, sourceAddressPrefix)
sharedIndex, sharedRule, sharedRuleFound := findSecurityRuleByName(updatedRules, sharedRuleName)
if !sharedRuleFound {
klog.V(4).Infof("Expected to find shared rule %s for service %s being deleted, but did not", sharedRuleName, service.Name)
return nil, fmt.Errorf("Expected to find shared rule %s for service %s being deleted, but did not", sharedRuleName, service.Name)
}
if sharedRule.DestinationAddressPrefixes == nil {
klog.V(4).Infof("Expected to have array of destinations in shared rule for service %s being deleted, but did not", service.Name)
return nil, fmt.Errorf("Expected to have array of destinations in shared rule for service %s being deleted, but did not", service.Name)
}
existingPrefixes := *sharedRule.DestinationAddressPrefixes
addressIndex, found := findIndex(existingPrefixes, destinationIPAddress)
if !found {
klog.V(4).Infof("Expected to find destination address %s in shared rule %s for service %s being deleted, but did not", destinationIPAddress, sharedRuleName, service.Name)
return nil, fmt.Errorf("Expected to find destination address %s in shared rule %s for service %s being deleted, but did not", destinationIPAddress, sharedRuleName, service.Name)
}
if len(existingPrefixes) == 1 {
Expand Down Expand Up @@ -1118,10 +1131,26 @@ func (az *Cloud) reconcileSecurityGroup(clusterName string, service *v1.Service,
foundRule = true
}
if foundRule && allowsConsolidation(expectedRule) {
klog.V(2).Infof("reconcile(%s)(%t): sg rule(%s) - allowConsolidation", serviceName, wantLb, *expectedRule.Name)
index, _ := findConsolidationCandidate(updatedRules, expectedRule)
updatedRules[index] = consolidate(updatedRules[index], expectedRule)
dirtySg = true
}
if foundRule && !allowsConsolidation(expectedRule) {
// For backward compatibility, for not shared rules,
// if existing rule(s)'s name contains or equals to UUID-PROTOCOL-PORT,
// then replace the first matching rule with the new rule with the new naming convention.
// Keep the same priority as the existing rule.
// Remove rest of the matching rules as the new rule already includes all the SourceAddressPrefixes.
// This also takes care of service updates.
klog.V(2).Infof("reconcile(%s)(%t): sg rule(%s) - not allowConsolidation", serviceName, wantLb, *expectedRule.Name)
updated := false
updatedRules, updated = updateMatchingRules(updatedRules, expectedRule)
if !updated {
return nil, fmt.Errorf("Expected to update rules containing %s for service %s, but did not", *expectedRule.Name, service.Name)
}
dirtySg = true
}
if !foundRule {
klog.V(10).Infof("reconcile(%s)(%t): sg rule(%s) - adding", serviceName, wantLb, *expectedRule.Name)

Expand All @@ -1137,7 +1166,7 @@ func (az *Cloud) reconcileSecurityGroup(clusterName string, service *v1.Service,
}

for _, r := range updatedRules {
klog.V(10).Infof("Updated security rule while processing %s: %s:%s -> %s:%s", service.Name, logSafe(r.SourceAddressPrefix), logSafe(r.SourcePortRange), logSafeCollection(r.DestinationAddressPrefix, r.DestinationAddressPrefixes), logSafe(r.DestinationPortRange))
klog.V(10).Infof("Updated security rule while processing %s: %s:%s -> %s:%s", service.Name, logSafeCollection(r.SourceAddressPrefix, r.SourceAddressPrefixes), logSafe(r.SourcePortRange), logSafeCollection(r.DestinationAddressPrefix, r.DestinationAddressPrefixes), logSafe(r.DestinationPortRange))
}

if dirtySg {
Expand Down Expand Up @@ -1213,6 +1242,25 @@ func findConsolidationCandidate(rules []network.SecurityRule, rule network.Secur
return 0, false
}

func updateMatchingRules(existingRules []network.SecurityRule, updatedRule network.SecurityRule) ([]network.SecurityRule, bool) {
updatedRules := []network.SecurityRule{}
updated := false
for _, existingRule := range existingRules {
if strings.Contains(to.String(existingRule.Name), to.String(updatedRule.Name)) {
if !updated {
updated = true
updatedRule.Priority = existingRule.Priority
updatedRules = append(updatedRules, updatedRule)
}
// skip if another matching rule is found after an existing rule has been updated
} else {
updatedRules = append(updatedRules, existingRule)
}
}

return updatedRules, updated
}

func makeConsolidatable(rule network.SecurityRule) network.SecurityRule {
return network.SecurityRule{
Name: rule.Name,
Expand Down Expand Up @@ -1245,8 +1293,8 @@ func consolidate(existingRule network.SecurityRule, newRule network.SecurityRule
SourcePortRanges: existingRule.SourcePortRanges,
DestinationPortRange: existingRule.DestinationPortRange,
DestinationPortRanges: existingRule.DestinationPortRanges,
SourceAddressPrefix: existingRule.SourceAddressPrefix,
SourceAddressPrefixes: existingRule.SourceAddressPrefixes,
SourceAddressPrefix: newRule.SourceAddressPrefix,
SourceAddressPrefixes: newRule.SourceAddressPrefixes,
DestinationAddressPrefixes: destinations,
Access: existingRule.Access,
Direction: existingRule.Direction,
Expand Down Expand Up @@ -1473,12 +1521,19 @@ func equalLoadBalancingRulePropertiesFormat(s, t *network.LoadBalancingRulePrope

// This compares rule's Name, Protocol, SourcePortRange, DestinationPortRange, SourceAddressPrefix, Access, and Direction.
// Note that it compares rule's DestinationAddressPrefix only when it's not consolidated rule as such rule does not have DestinationAddressPrefix defined.
// We intentionally do not compare DestinationAddressPrefixes in consolidated case because reconcileSecurityRule has to consider the two rules equal,
// despite different DestinationAddressPrefixes, in order to give it a chance to consolidate the two rules.
// We intentionally do not compare SourceAddressPrefixes and DestinationAddressPrefixes in consolidated case because reconcileSecurityRule has to consider the two rules equal,
// despite different SourceAddressPrefixes and DestinationAddressPrefixes, in order to give it a chance to consolidate the two rules.
func findSecurityRule(rules []network.SecurityRule, rule network.SecurityRule) bool {
for _, existingRule := range rules {
if !strings.EqualFold(to.String(existingRule.Name), to.String(rule.Name)) {
continue
if allowsConsolidation(existingRule) && allowsConsolidation(rule) {
if !strings.EqualFold(to.String(existingRule.Name), to.String(rule.Name)) {
continue
}
} else {
// for backward compatibiiity as existing rule name UUID-PROTOCOL-PORT-SOURCEIP should contain new naming pattern UUID-PROTOCOL-PORT
if !strings.Contains(to.String(existingRule.Name), to.String(rule.Name)) {
continue
}
}
if existingRule.Protocol != rule.Protocol {
continue
Expand All @@ -1489,9 +1544,6 @@ func findSecurityRule(rules []network.SecurityRule, rule network.SecurityRule) b
if !strings.EqualFold(to.String(existingRule.DestinationPortRange), to.String(rule.DestinationPortRange)) {
continue
}
if !strings.EqualFold(to.String(existingRule.SourceAddressPrefix), to.String(rule.SourceAddressPrefix)) {
continue
}
if !allowsConsolidation(existingRule) && !allowsConsolidation(rule) {
if !strings.EqualFold(to.String(existingRule.DestinationAddressPrefix), to.String(rule.DestinationAddressPrefix)) {
continue
Expand Down
5 changes: 5 additions & 0 deletions pkg/cloudprovider/providers/azure/azure_standard.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ func (az *Cloud) getSecurityRuleName(service *v1.Service, port v1.ServicePort, s
safePrefix := strings.Replace(sourceAddrPrefix, "/", "_", -1)
return fmt.Sprintf("shared-%s-%d-%s", port.Protocol, port.Port, safePrefix)
}
if sourceAddrPrefix == "" {
feiskyer marked this conversation as resolved.
Show resolved Hide resolved
rulePrefix := az.getRulePrefix(service)
return fmt.Sprintf("%s-%s-%d", rulePrefix, port.Protocol, port.Port)
}
// ensure backward compatibility
safePrefix := strings.Replace(sourceAddrPrefix, "/", "_", -1)
rulePrefix := az.getRulePrefix(service)
return fmt.Sprintf("%s-%s-%d-%s", rulePrefix, port.Protocol, port.Port, safePrefix)
Expand Down