Change:
<!-- Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks! If the matter is security related, please disclose it privately via --What happened: When multiple securityGroup are specified for AWS ELB type service, a random one will be chosen to grant inbound access to worker node. What you expected to happen: Always first LoadBalancer securityGroup be chosen to access worker nodes. How to reproduce it (as minimally and precisely as possible): use or on aws elb. Anything else we need to know?: Environment: Kubernetes version (use ): <=1.18 Cloud provider or hardware configuration: aws OS (e.g: ): linux Kernel (e.g. ): linux Install tools: kops/eks Network plugin and version (if this is a network-related bug): Others:
/sig cloud-provider These SIGs are my best guesses for this issue. Please comment if I am incorrect about one. I am a bot run by vllry. ‍

Discussion:
return sgList, setupSg, nil } <ins> // sortELBSecurityGroupList returns a list of sorted securityGroupIDs based on the original order // from buildELBSecurityGroupList. The logic is: //  * securityGroups specified by ServiceAnnotationLoadBalancerSecurityGroups appears first in order //  * securityGroups specified by ServiceAnnotationLoadBalancerExtraSecurityGroups appears last in order func (c *Cloud) sortELBSecurityGroupList(securityGroupIDs []string, annotations map[string]string) { annotatedSGList := getSGListFromAnnotation(annotations[ServiceAnnotationLoadBalancerSecurityGroups]) annotatedExtraSGList := getSGListFromAnnotation(annotations[ServiceAnnotationLoadBalancerExtraSecurityGroups]) annotatedSGIndex := make(map[string]int, len(annotatedSGList)) annotatedExtraSGIndex := make(map[string]int, len(annotatedExtraSGList)) for i, sgID := range annotatedSGList { annotatedSGIndex[sgID] = i } for i, sgID := range annotatedExtraSGList { annotatedExtraSGIndex[sgID] = i } sgOrderMapping := make(map[string]int, len(securityGroupIDs)) for _, sgID := range securityGroupIDs { if i, ok := annotatedSGIndex[sgID]; ok { sgOrderMapping[sgID] = i } else if j, ok := annotatedExtraSGIndex[sgID]; ok { sgOrderMapping[sgID] = len(annotatedSGIndex) + 1 + j } else { sgOrderMapping[sgID] = len(annotatedSGIndex) } } sort.Slice(securityGroupIDs, func(i, j int) bool { return sgOrderMapping[securityGroupIDs[i]] < sgOrderMapping[securityGroupIDs[j]] }) } </ins> // buildListener creates a new listener from the given port, adding an SSL certificate // if indicated by the appropriate annotations. func buildListener(port v1.ServicePort, annotations map[string]string, sslPorts *portSets) (*elb.Listener, error) {

} } <del> err = c.updateInstanceSecurityGroupsForLoadBalancer(loadBalancer, instances) </del> <ins> err = c.updateInstanceSecurityGroupsForLoadBalancer(loadBalancer, instances, annotations) </ins> if err != nil { klog.Warningf("Error opening ingress rules for the load balancer to the instances: %q", err) return nil, err

// Open security group ingress rules on the instances so that the load balancer can talk to them // Will also remove any security groups ingress rules for the load balancer that are _not_ needed for allInstances <del> func (c *Cloud) updateInstanceSecurityGroupsForLoadBalancer(lb *elb.LoadBalancerDescription, instances map[InstanceID]*ec2.Instance) error { </del> <ins> func (c *Cloud) updateInstanceSecurityGroupsForLoadBalancer(lb *elb.LoadBalancerDescription, instances map[InstanceID]*ec2.Instance, annotations map[string]string) error { </ins> if c.cfg.Global.DisableSecurityGroupIngress { return nil } // Determine the load balancer security group id <del> loadBalancerSecurityGroupID := "" for _, securityGroup := range lb.SecurityGroups { if aws.StringValue(securityGroup) == "" { continue } if loadBalancerSecurityGroupID != "" { // We create LBs with one SG klog.Warningf("Multiple security groups for load balancer: %q", aws.StringValue(lb.LoadBalancerName)) } loadBalancerSecurityGroupID = *securityGroup } if loadBalancerSecurityGroupID == "" { </del> <ins> lbSecurityGroupIDs := aws.StringValueSlice(lb.SecurityGroups) if len(lbSecurityGroupIDs) == 0 { </ins> return fmt.Errorf("could not determine security group for load balancer: %s", aws.StringValue(lb.LoadBalancerName)) } <ins> c.sortELBSecurityGroupList(lbSecurityGroupIDs, annotations) loadBalancerSecurityGroupID := lbSecurityGroupIDs[0] </ins> // Get the actual list of groups that allow ingress from the load-balancer var actualGroups []*ec2.SecurityGroup

{ // De-authorize the load balancer security group from the instances security group <del> err = c.updateInstanceSecurityGroupsForLoadBalancer(lb, nil) </del> <ins> err = c.updateInstanceSecurityGroupsForLoadBalancer(lb, nil, service.Annotations) </ins> if err != nil { klog.Errorf("Error deregistering load balancer from instance security groups: %q", err) return err

return nil } <del> err = c.updateInstanceSecurityGroupsForLoadBalancer(lb, instances) </del> <ins> err = c.updateInstanceSecurityGroupsForLoadBalancer(lb, instances, service.Annotations) </ins> if err != nil { return err }

assert.Equal(t, testCase.region, result) } } <ins> func TestCloud_sortELBSecurityGroupList(t *testing.T) { type args struct { securityGroupIDs []string annotations      map[string]string } tests := []struct { name                 string args                 args wantSecurityGroupIDs []string }{ { name: "with no annotation", args: args{ securityGroupIDs: []string{"sg-1"}, annotations:      map[string]string{}, }, wantSecurityGroupIDs: []string{"sg-1"}, }, { name: "with service.beta.kubernetes.io/aws-load-balancer-security-groups", args: args{ securityGroupIDs: []string{"sg-2", "sg-1", "sg-3"}, annotations: map[string]string{ "service.beta.kubernetes.io/aws-load-balancer-security-groups": "sg-3,sg-2,sg-1", }, }, wantSecurityGroupIDs: []string{"sg-3", "sg-2", "sg-1"}, }, { name: "with service.beta.kubernetes.io/aws-load-balancer-extra-security-groups", args: args{ securityGroupIDs: []string{"sg-2", "sg-1", "sg-3", "sg-4"}, annotations: map[string]string{ "service.beta.kubernetes.io/aws-load-balancer-extra-security-groups": "sg-3,sg-2,sg-1", }, }, wantSecurityGroupIDs: []string{"sg-4", "sg-3", "sg-2", "sg-1"}, }, { name: "with both annotation", args: args{ securityGroupIDs: []string{"sg-2", "sg-1", "sg-3", "sg-4", "sg-5", "sg-6"}, annotations: map[string]string{ "service.beta.kubernetes.io/aws-load-balancer-security-groups":       "sg-3,sg-2,sg-1", "service.beta.kubernetes.io/aws-load-balancer-extra-security-groups": "sg-6,sg-5", }, }, wantSecurityGroupIDs: []string{"sg-3", "sg-2", "sg-1", "sg-4", "sg-6", "sg-5"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Cloud{} c.sortELBSecurityGroupList(tt.args.securityGroupIDs, tt.args.annotations) assert.Equal(t, tt.wantSecurityGroupIDs, tt.args.securityGroupIDs) }) } } </ins>