Skip to content

Commit d0c13bf

Browse files
authored
Implement mutual TLS authentication support for Ingress (#3532)
* Implement mutual TLS authentication support for Ingress * Addressing comments and adding policy updates * Adding dependency for maps * removing ingressClassParam support
1 parent 1a8bfc0 commit d0c13bf

16 files changed

+1264
-37
lines changed

.github/workflows/test.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
- name: Set up Go 1.x
1111
uses: actions/setup-go@v2
1212
with:
13-
go-version: ^1.19
13+
go-version: ^1.21
1414

1515
- name: Check out code into the Go module directory
1616
uses: actions/checkout@v2

controllers/ingress/group_controller.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func NewGroupReconciler(cloud aws.Cloud, k8sClient client.Client, eventRecorder
5656
trackingProvider := tracking.NewDefaultProvider(ingressTagPrefix, controllerConfig.ClusterName)
5757
elbv2TaggingManager := elbv2deploy.NewDefaultTaggingManager(cloud.ELBV2(), cloud.VpcID(), controllerConfig.FeatureGates, cloud.RGT(), logger)
5858
modelBuilder := ingress.NewDefaultModelBuilder(k8sClient, eventRecorder,
59-
cloud.EC2(), cloud.ACM(),
59+
cloud.EC2(), cloud.ELBV2(), cloud.ACM(),
6060
annotationParser, subnetsResolver,
6161
authConfigBuilder, enhancedBackendBuilder, trackingProvider, elbv2TaggingManager, controllerConfig.FeatureGates,
6262
cloud.VpcID(), controllerConfig.ClusterName, controllerConfig.DefaultTags, controllerConfig.ExternalManagedTags,

docs/install/iam_policy.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838
"elasticloadbalancing:DescribeTargetGroups",
3939
"elasticloadbalancing:DescribeTargetGroupAttributes",
4040
"elasticloadbalancing:DescribeTargetHealth",
41-
"elasticloadbalancing:DescribeTags"
41+
"elasticloadbalancing:DescribeTags",
42+
"elasticloadbalancing:DescribeTrustStores"
4243
],
4344
"Resource": "*"
4445
},

docs/install/iam_policy_us-gov.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838
"elasticloadbalancing:DescribeTargetGroups",
3939
"elasticloadbalancing:DescribeTargetGroupAttributes",
4040
"elasticloadbalancing:DescribeTargetHealth",
41-
"elasticloadbalancing:DescribeTags"
41+
"elasticloadbalancing:DescribeTags",
42+
"elasticloadbalancing:DescribeTrustStores"
4243
],
4344
"Resource": "*"
4445
},

go.mod

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
module sigs.k8s.io/aws-load-balancer-controller
22

3-
go 1.20
3+
go 1.21
44

55
require (
6-
github.com/aws/aws-sdk-go v1.47.13
6+
github.com/aws/aws-sdk-go v1.48.10
77
github.com/evanphx/json-patch v5.6.0+incompatible
88
github.com/gavv/httpexpect/v2 v2.9.0
99
github.com/go-logr/logr v1.2.4
@@ -23,6 +23,7 @@ require (
2323
k8s.io/apimachinery v0.26.5
2424
k8s.io/cli-runtime v0.26.3
2525
k8s.io/client-go v0.26.5
26+
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448
2627
sigs.k8s.io/controller-runtime v0.14.6
2728
sigs.k8s.io/yaml v1.3.0
2829
)
@@ -156,7 +157,6 @@ require (
156157
k8s.io/klog/v2 v2.80.1 // indirect
157158
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
158159
k8s.io/kubectl v0.26.0 // indirect
159-
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect
160160
moul.io/http2curl/v2 v2.3.0 // indirect
161161
oras.land/oras-go v1.2.2 // indirect
162162
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0
7979
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
8080
github.com/aws/aws-sdk-go v1.47.13 h1:pJgCtldg5azDAFoEcE0fz6n+FnCc1/FY4krtUa5uvZQ=
8181
github.com/aws/aws-sdk-go v1.47.13/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
82+
github.com/aws/aws-sdk-go v1.48.10 h1:0LIFG3wp2Dt6PsxKWCg1Y1xRrn2vZnW5/gWdgaBalKg=
83+
github.com/aws/aws-sdk-go v1.48.10/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
8284
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
8385
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
8486
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=

pkg/annotations/constants.go

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const (
4646
IngressSuffixAuthSessionTimeout = "auth-session-timeout"
4747
IngressSuffixTargetNodeLabels = "target-node-labels"
4848
IngressSuffixManageSecurityGroupRules = "manage-backend-security-group-rules"
49+
IngressSuffixMutualAuthentication = "mutual-authentication"
4950

5051
// NLB annotation suffixes
5152
// prefixes service.beta.kubernetes.io, service.kubernetes.io

pkg/aws/services/ec2_mocks.go

+300
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/aws/services/elbv2_mocks.go

+599
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/deploy/elbv2/listener_manager.go

+23-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package elbv2
22

33
import (
44
"context"
5+
"reflect"
56
"time"
67

78
awssdk "github.com/aws/aws-sdk-go/aws"
@@ -134,7 +135,8 @@ func (m *defaultListenerManager) updateSDKListenerWithSettings(ctx context.Conte
134135
return err
135136
}
136137
desiredDefaultCerts, _ := buildSDKCertificates(resLS.Spec.Certificates)
137-
if !isSDKListenerSettingsDrifted(resLS.Spec, sdkLS, desiredDefaultActions, desiredDefaultCerts) {
138+
desiredDefaultMutualAuthentication := buildSDKMutualAuthenticationConfig(resLS.Spec.MutualAuthentication)
139+
if !isSDKListenerSettingsDrifted(resLS.Spec, sdkLS, desiredDefaultActions, desiredDefaultCerts, desiredDefaultMutualAuthentication) {
138140
return nil
139141
}
140142
req := buildSDKModifyListenerInput(resLS.Spec, desiredDefaultActions, desiredDefaultCerts)
@@ -246,7 +248,7 @@ func (m *defaultListenerManager) fetchSDKListenerExtraCertificateARNs(ctx contex
246248
}
247249

248250
func isSDKListenerSettingsDrifted(lsSpec elbv2model.ListenerSpec, sdkLS ListenerWithTags,
249-
desiredDefaultActions []*elbv2sdk.Action, desiredDefaultCerts []*elbv2sdk.Certificate) bool {
251+
desiredDefaultActions []*elbv2sdk.Action, desiredDefaultCerts []*elbv2sdk.Certificate, desiredDefaultMutualAuthentication *elbv2sdk.MutualAuthenticationAttributes) bool {
250252
if lsSpec.Port != awssdk.Int64Value(sdkLS.Listener.Port) {
251253
return true
252254
}
@@ -265,6 +267,9 @@ func isSDKListenerSettingsDrifted(lsSpec elbv2model.ListenerSpec, sdkLS Listener
265267
if len(lsSpec.ALPNPolicy) != 0 && !cmp.Equal(lsSpec.ALPNPolicy, awssdk.StringValueSlice(sdkLS.Listener.AlpnPolicy), cmpopts.EquateEmpty()) {
266268
return true
267269
}
270+
if !reflect.DeepEqual(desiredDefaultMutualAuthentication, sdkLS.Listener.MutualAuthentication) {
271+
return true
272+
}
268273

269274
return false
270275
}
@@ -289,6 +294,8 @@ func buildSDKCreateListenerInput(lsSpec elbv2model.ListenerSpec, featureGates co
289294
if len(lsSpec.ALPNPolicy) != 0 {
290295
sdkObj.AlpnPolicy = awssdk.StringSlice(lsSpec.ALPNPolicy)
291296
}
297+
sdkObj.MutualAuthentication = buildSDKMutualAuthenticationConfig(lsSpec.MutualAuthentication)
298+
292299
return sdkObj, nil
293300
}
294301

@@ -302,6 +309,8 @@ func buildSDKModifyListenerInput(lsSpec elbv2model.ListenerSpec, desiredDefaultA
302309
if len(lsSpec.ALPNPolicy) != 0 {
303310
sdkObj.AlpnPolicy = awssdk.StringSlice(lsSpec.ALPNPolicy)
304311
}
312+
sdkObj.MutualAuthentication = buildSDKMutualAuthenticationConfig(lsSpec.MutualAuthentication)
313+
305314
return sdkObj
306315
}
307316

@@ -327,6 +336,18 @@ func buildSDKCertificate(modelCert elbv2model.Certificate) *elbv2sdk.Certificate
327336
}
328337
}
329338

339+
// buildSDKMutualAuthenticationConfig builds the mutual TLS authentication config for listener
340+
func buildSDKMutualAuthenticationConfig(modelMutualAuthenticationCfg *elbv2model.MutualAuthenticationAttributes) *elbv2sdk.MutualAuthenticationAttributes {
341+
if modelMutualAuthenticationCfg == nil {
342+
return nil
343+
}
344+
return &elbv2sdk.MutualAuthenticationAttributes{
345+
IgnoreClientCertificateExpiry: modelMutualAuthenticationCfg.IgnoreClientCertificateExpiry,
346+
Mode: awssdk.String(modelMutualAuthenticationCfg.Mode),
347+
TrustStoreArn: modelMutualAuthenticationCfg.TrustStoreArn,
348+
}
349+
}
350+
330351
func buildResListenerStatus(sdkLS ListenerWithTags) elbv2model.ListenerStatus {
331352
return elbv2model.ListenerStatus{
332353
ListenerARN: awssdk.StringValue(sdkLS.Listener.ListenerArn),

pkg/deploy/elbv2/listener_manager_test.go

+92-5
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ import (
1010

1111
func Test_isSDKListenerSettingsDrifted(t *testing.T) {
1212
type args struct {
13-
lsSpec elbv2model.ListenerSpec
14-
sdkLS ListenerWithTags
15-
desiredDefaultActions []*elbv2sdk.Action
16-
desiredDefaultCerts []*elbv2sdk.Certificate
13+
lsSpec elbv2model.ListenerSpec
14+
sdkLS ListenerWithTags
15+
desiredDefaultActions []*elbv2sdk.Action
16+
desiredDefaultCerts []*elbv2sdk.Certificate
17+
desiredDefaultMutualAuthentication *elbv2sdk.MutualAuthenticationAttributes
1718
}
1819
tests := []struct {
1920
name string
@@ -49,6 +50,9 @@ func Test_isSDKListenerSettingsDrifted(t *testing.T) {
4950
},
5051
SslPolicy: awssdk.String("ELBSecurityPolicy-FS-1-2-Res-2019-08"),
5152
AlpnPolicy: awssdk.StringSlice([]string{"HTTP2Preferred"}),
53+
MutualAuthentication: &elbv2sdk.MutualAuthenticationAttributes{
54+
Mode: awssdk.String("off"),
55+
},
5256
},
5357
},
5458
desiredDefaultCerts: []*elbv2sdk.Certificate{
@@ -65,6 +69,9 @@ func Test_isSDKListenerSettingsDrifted(t *testing.T) {
6569
},
6670
},
6771
},
72+
desiredDefaultMutualAuthentication: &elbv2sdk.MutualAuthenticationAttributes{
73+
Mode: awssdk.String("off"),
74+
},
6875
},
6976
},
7077
{
@@ -104,6 +111,9 @@ func Test_isSDKListenerSettingsDrifted(t *testing.T) {
104111
},
105112
SslPolicy: awssdk.String("ELBSecurityPolicy-FS-1-2-Res-2019-08"),
106113
AlpnPolicy: awssdk.StringSlice([]string{"HTTP2Preferred"}),
114+
MutualAuthentication: &elbv2sdk.MutualAuthenticationAttributes{
115+
Mode: awssdk.String("off"),
116+
},
107117
},
108118
},
109119
desiredDefaultCerts: []*elbv2sdk.Certificate{
@@ -120,6 +130,9 @@ func Test_isSDKListenerSettingsDrifted(t *testing.T) {
120130
},
121131
},
122132
},
133+
desiredDefaultMutualAuthentication: &elbv2sdk.MutualAuthenticationAttributes{
134+
Mode: awssdk.String("off"),
135+
},
123136
},
124137
},
125138
{
@@ -154,6 +167,75 @@ func Test_isSDKListenerSettingsDrifted(t *testing.T) {
154167
},
155168
SslPolicy: awssdk.String("ELBSecurityPolicy-FS-1-2-Res-2019-08"),
156169
AlpnPolicy: awssdk.StringSlice([]string{"HTTP2Preferred"}),
170+
MutualAuthentication: &elbv2sdk.MutualAuthenticationAttributes{
171+
Mode: awssdk.String("off"),
172+
},
173+
},
174+
},
175+
desiredDefaultCerts: []*elbv2sdk.Certificate{
176+
{
177+
CertificateArn: awssdk.String("cert-arn1"),
178+
IsDefault: awssdk.Bool(true),
179+
},
180+
},
181+
desiredDefaultActions: []*elbv2sdk.Action{
182+
{
183+
Type: awssdk.String("forward-config"),
184+
ForwardConfig: &elbv2sdk.ForwardActionConfig{
185+
TargetGroups: []*elbv2sdk.TargetGroupTuple{
186+
{
187+
TargetGroupArn: awssdk.String("target-group"),
188+
},
189+
},
190+
},
191+
},
192+
},
193+
desiredDefaultMutualAuthentication: &elbv2sdk.MutualAuthenticationAttributes{
194+
Mode: awssdk.String("off"),
195+
},
196+
},
197+
},
198+
{
199+
name: "listener hasn't drifted if mutualAuthentication verify mode specified",
200+
args: args{
201+
lsSpec: elbv2model.ListenerSpec{
202+
Port: 80,
203+
Protocol: elbv2model.ProtocolHTTPS,
204+
SSLPolicy: awssdk.String("ELBSecurityPolicy-FS-1-2-Res-2019-08"),
205+
MutualAuthentication: &elbv2model.MutualAuthenticationAttributes{
206+
Mode: "verify",
207+
TrustStoreArn: awssdk.String("arn:aws:elasticloadbalancing:us-east-1:123456789123:truststore/ts-1/8786hghf"),
208+
},
209+
},
210+
sdkLS: ListenerWithTags{
211+
Listener: &elbv2sdk.Listener{
212+
Port: awssdk.Int64(80),
213+
Protocol: awssdk.String("HTTPS"),
214+
Certificates: []*elbv2sdk.Certificate{
215+
{
216+
CertificateArn: awssdk.String("cert-arn1"),
217+
IsDefault: awssdk.Bool(true),
218+
},
219+
},
220+
DefaultActions: []*elbv2sdk.Action{
221+
{
222+
Type: awssdk.String("forward-config"),
223+
ForwardConfig: &elbv2sdk.ForwardActionConfig{
224+
TargetGroups: []*elbv2sdk.TargetGroupTuple{
225+
{
226+
TargetGroupArn: awssdk.String("target-group"),
227+
},
228+
},
229+
},
230+
},
231+
},
232+
SslPolicy: awssdk.String("ELBSecurityPolicy-FS-1-2-Res-2019-08"),
233+
AlpnPolicy: awssdk.StringSlice([]string{"HTTP2Preferred"}),
234+
MutualAuthentication: &elbv2sdk.MutualAuthenticationAttributes{
235+
Mode: awssdk.String("verify"),
236+
TrustStoreArn: awssdk.String("arn:aws:elasticloadbalancing:us-east-1:123456789123:truststore/ts-1/8786hghf"),
237+
IgnoreClientCertificateExpiry: awssdk.Bool(false),
238+
},
157239
},
158240
},
159241
desiredDefaultCerts: []*elbv2sdk.Certificate{
@@ -174,12 +256,17 @@ func Test_isSDKListenerSettingsDrifted(t *testing.T) {
174256
},
175257
},
176258
},
259+
desiredDefaultMutualAuthentication: &elbv2sdk.MutualAuthenticationAttributes{
260+
Mode: awssdk.String("verify"),
261+
TrustStoreArn: awssdk.String("arn:aws:elasticloadbalancing:us-east-1:123456789123:truststore/ts-1/8786hghf"),
262+
IgnoreClientCertificateExpiry: awssdk.Bool(false),
263+
},
177264
},
178265
},
179266
}
180267
for _, tt := range tests {
181268
t.Run(tt.name, func(t *testing.T) {
182-
got := isSDKListenerSettingsDrifted(tt.args.lsSpec, tt.args.sdkLS, tt.args.desiredDefaultActions, tt.args.desiredDefaultCerts)
269+
got := isSDKListenerSettingsDrifted(tt.args.lsSpec, tt.args.sdkLS, tt.args.desiredDefaultActions, tt.args.desiredDefaultCerts, tt.args.desiredDefaultMutualAuthentication)
183270
assert.Equal(t, tt.want, got)
184271
})
185272
}

0 commit comments

Comments
 (0)