Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/guide/service/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
| [service.beta.kubernetes.io/aws-load-balancer-internal](#lb-internal) | boolean | false | deprecated, in favor of [aws-load-balancer-scheme](#lb-scheme) |
| [service.beta.kubernetes.io/aws-load-balancer-scheme](#lb-scheme) | string | internal | |
| [service.beta.kubernetes.io/aws-load-balancer-proxy-protocol](#proxy-protocol-v2) | string | | Set to `"*"` to enable |
| [service.beta.kubernetes.io/aws-load-balancer-proxy-protocol-per-target-group](#proxy-protocol-v2) | string | | If specified,configures proxy protocol for the target groups corresponding to the ports mentioned and disables for the rest. For example, if you have services deployed on ports `"80, 443 and 22"`, the annotation value `"80, 443"` will enable proxy protocol for ports 80 and 443 only, and disable for port 22. This annotation is overriden by `"service.beta.kubernetes.io/aws-load-balancer-proxy-protocol"` |
| [service.beta.kubernetes.io/aws-load-balancer-ip-address-type](#ip-address-type) | string | ipv4 | ipv4 \| dualstack |
| [service.beta.kubernetes.io/aws-load-balancer-access-log-enabled](#deprecated-attributes) | boolean | false | deprecated, in favor of [aws-load-balancer-attributes](#load-balancer-attributes) |
| [service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name](#deprecated-attributes) | string | | deprecated, in favor of [aws-load-balancer-attributes](#load-balancer-attributes) |
Expand Down
1 change: 1 addition & 0 deletions pkg/annotations/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const (
SvcLBSuffixScheme = "aws-load-balancer-scheme"
SvcLBSuffixInternal = "aws-load-balancer-internal"
SvcLBSuffixProxyProtocol = "aws-load-balancer-proxy-protocol"
SvcLBSuffixProxyProtocolPerTargetGroup = "aws-load-balancer-proxy-protocol-per-target-group"
SvcLBSuffixIPAddressType = "aws-load-balancer-ip-address-type"
SvcLBSuffixAccessLogEnabled = "aws-load-balancer-access-log-enabled"
SvcLBSuffixAccessLogS3BucketName = "aws-load-balancer-access-log-s3-bucket-name"
Expand Down
99 changes: 62 additions & 37 deletions pkg/service/model_build_target_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (t *defaultModelBuildTask) buildTargetGroup(ctx context.Context, port corev
if err != nil {
return nil, err
}
tgAttrs, err := t.buildTargetGroupAttributes(ctx)
tgAttrs, err := t.buildTargetGroupAttributes(ctx, port)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -204,41 +204,66 @@ func (t *defaultModelBuildTask) buildTargetGroupName(_ context.Context, svcPort
return fmt.Sprintf("k8s-%.8s-%.8s-%.10s", sanitizedNamespace, sanitizedName, uuid)
}

func (t *defaultModelBuildTask) buildTargetGroupAttributes(_ context.Context) ([]elbv2model.TargetGroupAttribute, error) {
var rawAttributes map[string]string
if _, err := t.annotationParser.ParseStringMapAnnotation(annotations.SvcLBSuffixTargetGroupAttributes, &rawAttributes, t.service.Annotations); err != nil {
return nil, err
}
if rawAttributes == nil {
rawAttributes = make(map[string]string)
}
if _, ok := rawAttributes[tgAttrsProxyProtocolV2Enabled]; !ok {
rawAttributes[tgAttrsProxyProtocolV2Enabled] = strconv.FormatBool(t.defaultProxyProtocolV2Enabled)
}
proxyV2Annotation := ""
if exists := t.annotationParser.ParseStringAnnotation(annotations.SvcLBSuffixProxyProtocol, &proxyV2Annotation, t.service.Annotations); exists {
if proxyV2Annotation != "*" {
return []elbv2model.TargetGroupAttribute{}, errors.Errorf("invalid value %v for Load Balancer proxy protocol v2 annotation, only value currently supported is *", proxyV2Annotation)
}
rawAttributes[tgAttrsProxyProtocolV2Enabled] = "true"
}
if rawPreserveIPEnabled, ok := rawAttributes[tgAttrsPreserveClientIPEnabled]; ok {
_, err := strconv.ParseBool(rawPreserveIPEnabled)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse attribute %v=%v", tgAttrsPreserveClientIPEnabled, rawPreserveIPEnabled)
}
}
attributes := make([]elbv2model.TargetGroupAttribute, 0, len(rawAttributes))
for attrKey, attrValue := range rawAttributes {
attributes = append(attributes, elbv2model.TargetGroupAttribute{
Key: attrKey,
Value: attrValue,
})
}
sort.Slice(attributes, func(i, j int) bool {
return attributes[i].Key < attributes[j].Key
})
return attributes, nil
func (t *defaultModelBuildTask) buildTargetGroupAttributes(_ context.Context, port corev1.ServicePort) ([]elbv2model.TargetGroupAttribute, error) {
var rawAttributes map[string]string
if _, err := t.annotationParser.ParseStringMapAnnotation(annotations.SvcLBSuffixTargetGroupAttributes, &rawAttributes, t.service.Annotations); err != nil {
return nil, err
}
if rawAttributes == nil {
rawAttributes = make(map[string]string)
}
if _, ok := rawAttributes[tgAttrsProxyProtocolV2Enabled]; !ok {
rawAttributes[tgAttrsProxyProtocolV2Enabled] = strconv.FormatBool(t.defaultProxyProtocolV2Enabled)
}

var proxyProtocolPerTG string
if t.annotationParser.ParseStringAnnotation(annotations.SvcLBSuffixProxyProtocolPerTargetGroup, &proxyProtocolPerTG, t.service.Annotations) {
ports := strings.Split(proxyProtocolPerTG, ",")
enabledPorts := make(map[string]struct{})
for _, p := range ports {
trimmedPort := strings.TrimSpace(p)
if trimmedPort != "" {
if _, err := strconv.Atoi(trimmedPort); err != nil {
return nil, errors.Errorf("invalid port number in proxy-protocol-per-target-group: %v", trimmedPort)
}
enabledPorts[trimmedPort] = struct{}{}
}
}

currentPortStr := strconv.FormatInt(int64(port.Port), 10)
if _, enabled := enabledPorts[currentPortStr]; enabled {
rawAttributes[tgAttrsProxyProtocolV2Enabled] = "true"
} else {
rawAttributes[tgAttrsProxyProtocolV2Enabled] = "false"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to keep existing behavior, can you remove this line? the default for this attribute is false anyways.

Copy link
Contributor Author

@pthak94 pthak94 Mar 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zac-nixon I'm assuming you meant remove only the else block and not the whole line. I can't think of any scenario where this can break the existing behavior. Can you please help me understand?

The intention of having the else block is for a situation where these attributes are set:

"service.beta.kubernetes.io/aws-load-balancer-target-group-attributes": tgAttrsProxyProtocolV2Enabled + "=true"
"service.beta.kubernetes.io/aws-load-balancer-proxy-protocol-per-target-group": "80, 443"

in this case, our new annotation will not do what we say it does, that is disable proxy_protocol for unspecified port 22. It will just enable it for all.

"service.beta.kubernetes.io/aws-load-balancer-proxy-protocol": "*" still overrides everything as the documentation says.

Do let me know what you think?

Copy link
Collaborator

@zac-nixon zac-nixon Mar 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation. I was just thinking that it would be nice to not have the false value set when we didn't need it. But I'm happy with the implementation as-is and I appreciate the work you did here :)

}
}

proxyV2Annotation := ""
if exists := t.annotationParser.ParseStringAnnotation(annotations.SvcLBSuffixProxyProtocol, &proxyV2Annotation, t.service.Annotations); exists {
if proxyV2Annotation != "*" {
return []elbv2model.TargetGroupAttribute{}, errors.Errorf("invalid value %v for Load Balancer proxy protocol v2 annotation, only value currently supported is *", proxyV2Annotation)
}
rawAttributes[tgAttrsProxyProtocolV2Enabled] = "true"
}

if rawPreserveIPEnabled, ok := rawAttributes[tgAttrsPreserveClientIPEnabled]; ok {
_, err := strconv.ParseBool(rawPreserveIPEnabled)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse attribute %v=%v", tgAttrsPreserveClientIPEnabled, rawPreserveIPEnabled)
}
}

attributes := make([]elbv2model.TargetGroupAttribute, 0, len(rawAttributes))
for attrKey, attrValue := range rawAttributes {
attributes = append(attributes, elbv2model.TargetGroupAttribute{
Key: attrKey,
Value: attrValue,
})
}
sort.Slice(attributes, func(i, j int) bool {
return attributes[i].Key < attributes[j].Key
})
return attributes, nil
}

func (t *defaultModelBuildTask) buildPreserveClientIPFlag(_ context.Context, targetType elbv2model.TargetType, tgAttrs []elbv2model.TargetGroupAttribute) (bool, error) {
Expand Down Expand Up @@ -711,4 +736,4 @@ func (t *defaultModelBuildTask) buildTargetGroupBindingMultiClusterFlag(svc *cor
return rawEnabled, nil
}
return false, nil
}
}
41 changes: 40 additions & 1 deletion pkg/service/model_build_target_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func Test_defaultModelBuilderTask_targetGroupAttrs(t *testing.T) {
tests := []struct {
testName string
svc *corev1.Service
port corev1.ServicePort
wantError bool
wantValue []elbv2.TargetGroupAttribute
}{
Expand Down Expand Up @@ -140,6 +141,44 @@ func Test_defaultModelBuilderTask_targetGroupAttrs(t *testing.T) {
},
wantError: true,
},
{
testName: "proxy protocol per target group port 80",
svc: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"service.beta.kubernetes.io/aws-load-balancer-proxy-protocol-per-target-group": "80",
},
},
},
port: corev1.ServicePort{Port: 80},
wantError: false,
wantValue: []elbv2.TargetGroupAttribute{
{
Key: tgAttrsProxyProtocolV2Enabled,
Value: "true",
},
},
},
{
testName: "proxy protocol per target group port 80 proxy v2 override",
svc: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"service.beta.kubernetes.io/aws-load-balancer-proxy-protocol-per-target-group": "443, 22",
"service.beta.kubernetes.io/aws-load-balancer-proxy-protocol": "*",

},
},
},
port: corev1.ServicePort{Port: 80},
wantError: false,
wantValue: []elbv2.TargetGroupAttribute{
{
Key: tgAttrsProxyProtocolV2Enabled,
Value: "true",
},
},
},
}
for _, tt := range tests {
t.Run(tt.testName, func(t *testing.T) {
Expand All @@ -148,7 +187,7 @@ func Test_defaultModelBuilderTask_targetGroupAttrs(t *testing.T) {
service: tt.svc,
annotationParser: parser,
}
tgAttrs, err := builder.buildTargetGroupAttributes(context.Background())
tgAttrs, err := builder.buildTargetGroupAttributes(context.Background(), tt.port)
if tt.wantError {
assert.Error(t, err)
} else {
Expand Down
Loading