Skip to content

Commit bee5f8c

Browse files
authored
Merge pull request #4079 from pthak94/proxy-protocol-per-target-group
Added support for setting Proxy protocol per target group based on ServicePort
2 parents 879e715 + 497a366 commit bee5f8c

File tree

4 files changed

+104
-38
lines changed

4 files changed

+104
-38
lines changed

docs/guide/service/annotations.md

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
| [service.beta.kubernetes.io/aws-load-balancer-internal](#lb-internal) | boolean | false | deprecated, in favor of [aws-load-balancer-scheme](#lb-scheme) |
2424
| [service.beta.kubernetes.io/aws-load-balancer-scheme](#lb-scheme) | string | internal | |
2525
| [service.beta.kubernetes.io/aws-load-balancer-proxy-protocol](#proxy-protocol-v2) | string | | Set to `"*"` to enable |
26+
| [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"` |
2627
| [service.beta.kubernetes.io/aws-load-balancer-ip-address-type](#ip-address-type) | string | ipv4 | ipv4 \| dualstack |
2728
| [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) |
2829
| [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) |

pkg/annotations/constants.go

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ const (
6969
SvcLBSuffixScheme = "aws-load-balancer-scheme"
7070
SvcLBSuffixInternal = "aws-load-balancer-internal"
7171
SvcLBSuffixProxyProtocol = "aws-load-balancer-proxy-protocol"
72+
SvcLBSuffixProxyProtocolPerTargetGroup = "aws-load-balancer-proxy-protocol-per-target-group"
7273
SvcLBSuffixIPAddressType = "aws-load-balancer-ip-address-type"
7374
SvcLBSuffixAccessLogEnabled = "aws-load-balancer-access-log-enabled"
7475
SvcLBSuffixAccessLogS3BucketName = "aws-load-balancer-access-log-s3-bucket-name"

pkg/service/model_build_target_group.go

+62-37
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (t *defaultModelBuildTask) buildTargetGroup(ctx context.Context, port corev
4444
if err != nil {
4545
return nil, err
4646
}
47-
tgAttrs, err := t.buildTargetGroupAttributes(ctx)
47+
tgAttrs, err := t.buildTargetGroupAttributes(ctx, port)
4848
if err != nil {
4949
return nil, err
5050
}
@@ -204,41 +204,66 @@ func (t *defaultModelBuildTask) buildTargetGroupName(_ context.Context, svcPort
204204
return fmt.Sprintf("k8s-%.8s-%.8s-%.10s", sanitizedNamespace, sanitizedName, uuid)
205205
}
206206

207-
func (t *defaultModelBuildTask) buildTargetGroupAttributes(_ context.Context) ([]elbv2model.TargetGroupAttribute, error) {
208-
var rawAttributes map[string]string
209-
if _, err := t.annotationParser.ParseStringMapAnnotation(annotations.SvcLBSuffixTargetGroupAttributes, &rawAttributes, t.service.Annotations); err != nil {
210-
return nil, err
211-
}
212-
if rawAttributes == nil {
213-
rawAttributes = make(map[string]string)
214-
}
215-
if _, ok := rawAttributes[tgAttrsProxyProtocolV2Enabled]; !ok {
216-
rawAttributes[tgAttrsProxyProtocolV2Enabled] = strconv.FormatBool(t.defaultProxyProtocolV2Enabled)
217-
}
218-
proxyV2Annotation := ""
219-
if exists := t.annotationParser.ParseStringAnnotation(annotations.SvcLBSuffixProxyProtocol, &proxyV2Annotation, t.service.Annotations); exists {
220-
if proxyV2Annotation != "*" {
221-
return []elbv2model.TargetGroupAttribute{}, errors.Errorf("invalid value %v for Load Balancer proxy protocol v2 annotation, only value currently supported is *", proxyV2Annotation)
222-
}
223-
rawAttributes[tgAttrsProxyProtocolV2Enabled] = "true"
224-
}
225-
if rawPreserveIPEnabled, ok := rawAttributes[tgAttrsPreserveClientIPEnabled]; ok {
226-
_, err := strconv.ParseBool(rawPreserveIPEnabled)
227-
if err != nil {
228-
return nil, errors.Wrapf(err, "failed to parse attribute %v=%v", tgAttrsPreserveClientIPEnabled, rawPreserveIPEnabled)
229-
}
230-
}
231-
attributes := make([]elbv2model.TargetGroupAttribute, 0, len(rawAttributes))
232-
for attrKey, attrValue := range rawAttributes {
233-
attributes = append(attributes, elbv2model.TargetGroupAttribute{
234-
Key: attrKey,
235-
Value: attrValue,
236-
})
237-
}
238-
sort.Slice(attributes, func(i, j int) bool {
239-
return attributes[i].Key < attributes[j].Key
240-
})
241-
return attributes, nil
207+
func (t *defaultModelBuildTask) buildTargetGroupAttributes(_ context.Context, port corev1.ServicePort) ([]elbv2model.TargetGroupAttribute, error) {
208+
var rawAttributes map[string]string
209+
if _, err := t.annotationParser.ParseStringMapAnnotation(annotations.SvcLBSuffixTargetGroupAttributes, &rawAttributes, t.service.Annotations); err != nil {
210+
return nil, err
211+
}
212+
if rawAttributes == nil {
213+
rawAttributes = make(map[string]string)
214+
}
215+
if _, ok := rawAttributes[tgAttrsProxyProtocolV2Enabled]; !ok {
216+
rawAttributes[tgAttrsProxyProtocolV2Enabled] = strconv.FormatBool(t.defaultProxyProtocolV2Enabled)
217+
}
218+
219+
var proxyProtocolPerTG string
220+
if t.annotationParser.ParseStringAnnotation(annotations.SvcLBSuffixProxyProtocolPerTargetGroup, &proxyProtocolPerTG, t.service.Annotations) {
221+
ports := strings.Split(proxyProtocolPerTG, ",")
222+
enabledPorts := make(map[string]struct{})
223+
for _, p := range ports {
224+
trimmedPort := strings.TrimSpace(p)
225+
if trimmedPort != "" {
226+
if _, err := strconv.Atoi(trimmedPort); err != nil {
227+
return nil, errors.Errorf("invalid port number in proxy-protocol-per-target-group: %v", trimmedPort)
228+
}
229+
enabledPorts[trimmedPort] = struct{}{}
230+
}
231+
}
232+
233+
currentPortStr := strconv.FormatInt(int64(port.Port), 10)
234+
if _, enabled := enabledPorts[currentPortStr]; enabled {
235+
rawAttributes[tgAttrsProxyProtocolV2Enabled] = "true"
236+
} else {
237+
rawAttributes[tgAttrsProxyProtocolV2Enabled] = "false"
238+
}
239+
}
240+
241+
proxyV2Annotation := ""
242+
if exists := t.annotationParser.ParseStringAnnotation(annotations.SvcLBSuffixProxyProtocol, &proxyV2Annotation, t.service.Annotations); exists {
243+
if proxyV2Annotation != "*" {
244+
return []elbv2model.TargetGroupAttribute{}, errors.Errorf("invalid value %v for Load Balancer proxy protocol v2 annotation, only value currently supported is *", proxyV2Annotation)
245+
}
246+
rawAttributes[tgAttrsProxyProtocolV2Enabled] = "true"
247+
}
248+
249+
if rawPreserveIPEnabled, ok := rawAttributes[tgAttrsPreserveClientIPEnabled]; ok {
250+
_, err := strconv.ParseBool(rawPreserveIPEnabled)
251+
if err != nil {
252+
return nil, errors.Wrapf(err, "failed to parse attribute %v=%v", tgAttrsPreserveClientIPEnabled, rawPreserveIPEnabled)
253+
}
254+
}
255+
256+
attributes := make([]elbv2model.TargetGroupAttribute, 0, len(rawAttributes))
257+
for attrKey, attrValue := range rawAttributes {
258+
attributes = append(attributes, elbv2model.TargetGroupAttribute{
259+
Key: attrKey,
260+
Value: attrValue,
261+
})
262+
}
263+
sort.Slice(attributes, func(i, j int) bool {
264+
return attributes[i].Key < attributes[j].Key
265+
})
266+
return attributes, nil
242267
}
243268

244269
func (t *defaultModelBuildTask) buildPreserveClientIPFlag(_ context.Context, targetType elbv2model.TargetType, tgAttrs []elbv2model.TargetGroupAttribute) (bool, error) {
@@ -711,4 +736,4 @@ func (t *defaultModelBuildTask) buildTargetGroupBindingMultiClusterFlag(svc *cor
711736
return rawEnabled, nil
712737
}
713738
return false, nil
714-
}
739+
}

pkg/service/model_build_target_group_test.go

+40-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func Test_defaultModelBuilderTask_targetGroupAttrs(t *testing.T) {
2626
tests := []struct {
2727
testName string
2828
svc *corev1.Service
29+
port corev1.ServicePort
2930
wantError bool
3031
wantValue []elbv2.TargetGroupAttribute
3132
}{
@@ -140,6 +141,44 @@ func Test_defaultModelBuilderTask_targetGroupAttrs(t *testing.T) {
140141
},
141142
wantError: true,
142143
},
144+
{
145+
testName: "proxy protocol per target group port 80",
146+
svc: &corev1.Service{
147+
ObjectMeta: metav1.ObjectMeta{
148+
Annotations: map[string]string{
149+
"service.beta.kubernetes.io/aws-load-balancer-proxy-protocol-per-target-group": "80",
150+
},
151+
},
152+
},
153+
port: corev1.ServicePort{Port: 80},
154+
wantError: false,
155+
wantValue: []elbv2.TargetGroupAttribute{
156+
{
157+
Key: tgAttrsProxyProtocolV2Enabled,
158+
Value: "true",
159+
},
160+
},
161+
},
162+
{
163+
testName: "proxy protocol per target group port 80 proxy v2 override",
164+
svc: &corev1.Service{
165+
ObjectMeta: metav1.ObjectMeta{
166+
Annotations: map[string]string{
167+
"service.beta.kubernetes.io/aws-load-balancer-proxy-protocol-per-target-group": "443, 22",
168+
"service.beta.kubernetes.io/aws-load-balancer-proxy-protocol": "*",
169+
170+
},
171+
},
172+
},
173+
port: corev1.ServicePort{Port: 80},
174+
wantError: false,
175+
wantValue: []elbv2.TargetGroupAttribute{
176+
{
177+
Key: tgAttrsProxyProtocolV2Enabled,
178+
Value: "true",
179+
},
180+
},
181+
},
143182
}
144183
for _, tt := range tests {
145184
t.Run(tt.testName, func(t *testing.T) {
@@ -148,7 +187,7 @@ func Test_defaultModelBuilderTask_targetGroupAttrs(t *testing.T) {
148187
service: tt.svc,
149188
annotationParser: parser,
150189
}
151-
tgAttrs, err := builder.buildTargetGroupAttributes(context.Background())
190+
tgAttrs, err := builder.buildTargetGroupAttributes(context.Background(), tt.port)
152191
if tt.wantError {
153192
assert.Error(t, err)
154193
} else {

0 commit comments

Comments
 (0)