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

add annotation to enable shield advanced protection for nlb #3869

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
53 changes: 53 additions & 0 deletions docs/guide/service/annotations.md
Original file line number Diff line number Diff line change
@@ -51,6 +51,45 @@
| [service.beta.kubernetes.io/aws-load-balancer-attributes](#load-balancer-attributes) | stringMap | | |
| [service.beta.kubernetes.io/aws-load-balancer-security-groups](#security-groups) | stringList | | |
| [service.beta.kubernetes.io/aws-load-balancer-manage-backend-security-group-rules](#manage-backend-sg-rules) | boolean | true | If `service.beta.kubernetes.io/aws-load-balancer-security-groups` is specified, this must also be explicitly specified otherwise it defaults to `false`. |
| Name | Type | Default | Notes |
|--------------------------------------------------------------------------------------------------|-------------------------|---------------------------|--------------------------------------------------------|
| [service.beta.kubernetes.io/load-balancer-source-ranges](#lb-source-ranges) | stringList | | |
| [service.beta.kubernetes.io/aws-load-balancer-nlb-shield-advanced-protection](#shield-advanced-protection) | boolean | false | |
| [service.beta.kubernetes.io/aws-load-balancer-security-group-prefix-lists](#lb-security-group-prefix-lists) | stringList | | |
| [service.beta.kubernetes.io/aws-load-balancer-type](#lb-type) | string | | |
| [service.beta.kubernetes.io/aws-load-balancer-nlb-target-type](#nlb-target-type) | string | | default `instance` in case of LoadBalancerClass |
| [service.beta.kubernetes.io/aws-load-balancer-name](#load-balancer-name) | string | | |
| [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-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)|
| [service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix](#deprecated-attributes)| string | | deprecated, in favor of [aws-load-balancer-attributes](#load-balancer-attributes)|
| [service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled](#deprecated-attributes)| boolean | false | deprecated, in favor of [aws-load-balancer-attributes](#load-balancer-attributes)|
| [service.beta.kubernetes.io/aws-load-balancer-ssl-cert](#ssl-cert) | stringList | | |
| [service.beta.kubernetes.io/aws-load-balancer-ssl-ports](#ssl-ports) | stringList | | |
| [service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy](#ssl-negotiation-policy) | string | ELBSecurityPolicy-2016-08 | |
| [service.beta.kubernetes.io/aws-load-balancer-backend-protocol](#backend-protocol) | string | | |
| [service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags](#additional-resource-tags) | stringMap | | |
| [service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol](#healthcheck-protocol) | string | TCP | |
| [service.beta.kubernetes.io/aws-load-balancer-healthcheck-port ](#healthcheck-port) | integer \| traffic-port | traffic-port | |
| [service.beta.kubernetes.io/aws-load-balancer-healthcheck-path](#healthcheck-path) | string | "/" for HTTP(S) protocols | |
| [service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold](#healthcheck-healthy-threshold) | integer | 3 | |
| [service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold](#healthcheck-unhealthy-threshold) | integer | 3 | |
| [service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout](#healthcheck-timeout) | integer | 10 | |
| [service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval](#healthcheck-interval) | integer | 10 | |
| [service.beta.kubernetes.io/aws-load-balancer-healthcheck-success-codes](#healthcheck-success-codes) | string | 200-399 | |
| [service.beta.kubernetes.io/aws-load-balancer-eip-allocations](#eip-allocations) | stringList | | internet-facing lb only. Length must match the number of subnets|
| [service.beta.kubernetes.io/aws-load-balancer-private-ipv4-addresses](#private-ipv4-addresses) | stringList | | internal lb only. Length must match the number of subnets |
| [service.beta.kubernetes.io/aws-load-balancer-ipv6-addresses](#ipv6-addresses) | stringList | | dualstack lb only. Length must match the number of subnets |
| [service.beta.kubernetes.io/aws-load-balancer-target-group-attributes](#target-group-attributes) | stringMap | | |
| [service.beta.kubernetes.io/aws-load-balancer-subnets](#subnets) | stringList | | |
| [service.beta.kubernetes.io/aws-load-balancer-alpn-policy](#alpn-policy) | string | | |
| [service.beta.kubernetes.io/aws-load-balancer-target-node-labels](#target-node-labels) | stringMap | | |
| [service.beta.kubernetes.io/aws-load-balancer-attributes](#load-balancer-attributes) | stringMap | | |
| [service.beta.kubernetes.io/aws-load-balancer-security-groups](#security-groups) | stringList | | |
| [service.beta.kubernetes.io/aws-load-balancer-manage-backend-security-group-rules](#manage-backend-sg-rules) | boolean | true | If `service.beta.kubernetes.io/aws-load-balancer-security-groups` is specified, this must also be explicitly specified otherwise it defaults to `false`. |
| [service.beta.kubernetes.io/aws-load-balancer-inbound-sg-rules-on-private-link-traffic](#update-security-settings) | string | |
| [service.beta.kubernetes.io/aws-load-balancer-listener-attributes.${Protocol}-${Port}](#listener-attributes) | stringMap | |
| [service.beta.kubernetes.io/aws-load-balancer-multi-cluster-target-group](#multi-cluster-target-group) | boolean | false | If specified, the controller will only operate on targets that exist within the cluster, ignoring targets from other sources. |
@@ -580,6 +619,20 @@ Load balancer access can be controlled via following annotations:
```


- <a name="shield-advanced-protection">`service.beta.kubernetes.io/aws-load-balancer-nlb-shield-advanced-protection`</a> turns on / off the AWS Shield Advanced protection for the network load balancer.

!!!note ""
When this annotation is absent, the controller will keep LoadBalancer shield protection settings unchanged.
To disable shield protection, explicitly set the annotation value to 'false'.

!!!example
- enable shield protection
```service.beta.kubernetes.io/aws-load-balancer-nlb-shield-advanced-protection: 'true'
```
- disable shield protection
```service.beta.kubernetes.io/aws-load-balancer-nlb-shield-advanced-protection: 'false'
```

## Legacy Cloud Provider
The AWS Load Balancer Controller manages Kubernetes Services in a compatible way with the AWS cloud provider's legacy service controller.

1 change: 1 addition & 0 deletions pkg/annotations/constants.go
Original file line number Diff line number Diff line change
@@ -61,6 +61,7 @@ const (
// NLB annotation suffixes
// prefixes service.beta.kubernetes.io, service.kubernetes.io
SvcLBSuffixSourceRanges = "load-balancer-source-ranges"
SvcLBSuffixShieldAdvancedProtection = "aws-load-balancer-nlb-shield-advanced-protection"
SvcLBSuffixLoadBalancerType = "aws-load-balancer-type"
SvcLBSuffixTargetType = "aws-load-balancer-nlb-target-type"
SvcLBSuffixLoadBalancerName = "aws-load-balancer-name"
37 changes: 37 additions & 0 deletions pkg/service/model_build_load_balancer_addons.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package service

import (
"context"

"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
"sigs.k8s.io/aws-load-balancer-controller/pkg/model/core"
shieldmodel "sigs.k8s.io/aws-load-balancer-controller/pkg/model/shield"
)

func (t *defaultModelBuildTask) buildLoadBalancerAddOns(ctx context.Context, lbARN core.StringToken) error {
if _, err := t.buildShieldProtection(ctx, lbARN); err != nil {
return err
}
return nil
}

func (t *defaultModelBuildTask) buildShieldProtection(_ context.Context, lbARN core.StringToken) (*shieldmodel.Protection, error) {
explicitEnableProtections := make(map[bool]struct{})
rawEnableProtection := false
exists, err := t.annotationParser.ParseBoolAnnotation(annotations.SvcLBSuffixShieldAdvancedProtection, &rawEnableProtection, t.service.Annotations)
if err != nil {
return nil, err
}
if exists {
explicitEnableProtections[rawEnableProtection] = struct{}{}
}
if len(explicitEnableProtections) == 0 {
return nil, nil
}
_, enableProtection := explicitEnableProtections[true]
protection := shieldmodel.NewProtection(t.stack, resourceIDLoadBalancer, shieldmodel.ProtectionSpec{
Enabled: enableProtection,
ResourceARN: lbARN,
})
return protection, nil
}
114 changes: 114 additions & 0 deletions pkg/service/model_build_load_balancer_addons_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package service

import (
"context"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
"sigs.k8s.io/aws-load-balancer-controller/pkg/model/core"
shieldmodel "sigs.k8s.io/aws-load-balancer-controller/pkg/model/shield"
)

func Test_defaultModelBuildTask_buildShieldProtection(t *testing.T) {
type args struct {
lbARN core.StringToken
}
tests := []struct {
testName string
svc *corev1.Service
args args
want *shieldmodel.Protection
wantError bool
}{
{
testName: "when shield-advanced-protection annotation is not specified",
svc: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{},
},
},
args: args{
lbARN: core.LiteralStringToken("awesome-lb-arn"),
},
want: nil,
wantError: false,
},
{
testName: "when shield-advanced-protection annotation set to true",
svc: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"service.beta.kubernetes.io/aws-load-balancer-nlb-shield-advanced-protection": "true",
},
},
},
args: args{
lbARN: core.LiteralStringToken("awesome-lb-arn"),
},
want: &shieldmodel.Protection{
Spec: shieldmodel.ProtectionSpec{
Enabled: true,
ResourceARN: core.LiteralStringToken("awesome-lb-arn"),
},
},
wantError: false,
},
{
testName: "when shield-advanced-protection annotation set to false",
svc: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"service.beta.kubernetes.io/aws-load-balancer-nlb-shield-advanced-protection": "false",
},
},
},
args: args{
lbARN: core.LiteralStringToken("awesome-lb-arn"),
},
want: &shieldmodel.Protection{
Spec: shieldmodel.ProtectionSpec{
Enabled: false,
ResourceARN: core.LiteralStringToken("awesome-lb-arn"),
},
},
wantError: false,
},
{
testName: "when shield-advanced-protection annotation has non boolean value",
svc: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"service.beta.kubernetes.io/aws-load-balancer-nlb-shield-advanced-protection": "FalSe1",
},
},
},
args: args{
lbARN: core.LiteralStringToken("awesome-lb-arn"),
},
wantError: true,
},
}
for _, tt := range tests {
t.Run(tt.testName, func(t *testing.T) {
stack := core.NewDefaultStack(core.StackID{Name: "awesome-stack"})
annotationParser := annotations.NewSuffixAnnotationParser("service.beta.kubernetes.io")
task := &defaultModelBuildTask{
service: tt.svc,
annotationParser: annotationParser,
stack: stack,
}
got, err := task.buildShieldProtection(context.Background(), tt.args.lbARN)
if tt.wantError {
assert.Error(t, err)
} else {
opts := cmpopts.IgnoreTypes(core.ResourceMeta{})
assert.True(t, cmp.Equal(tt.want, got, opts), "diff", cmp.Diff(tt.want, got, opts))
}
})
}
}
6 changes: 5 additions & 1 deletion pkg/service/model_builder.go
Original file line number Diff line number Diff line change
@@ -2,10 +2,11 @@ package service

import (
"context"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"strconv"
"sync"

ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"

"github.com/go-logr/logr"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
@@ -249,6 +250,9 @@ func (t *defaultModelBuildTask) buildModel(ctx context.Context) error {
if err != nil {
return err
}
if err := t.buildLoadBalancerAddOns(ctx, t.loadBalancer.LoadBalancerARN()); err != nil {
return err
}
return nil
}