Skip to content

Commit ef8cf3b

Browse files
authored
Merge pull request #4113 from u-kai/fix/issue-3070
Allow specifying the same certificate for both default and SNI
2 parents 171d681 + 402c61f commit ef8cf3b

File tree

3 files changed

+310
-3
lines changed

3 files changed

+310
-3
lines changed

docs/guide/ingress/annotations.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,9 @@ TLS support can be controlled with the following annotations:
800800
- <a name="certificate-arn">`alb.ingress.kubernetes.io/certificate-arn`</a> specifies the ARN of one or more certificate managed by [AWS Certificate Manager](https://aws.amazon.com/certificate-manager)
801801

802802
!!!tip ""
803-
The first certificate in the list will be added as default certificate. And remaining certificate will be added to the optional certificate list.
803+
The first certificate in the list will be added as the default certificate.
804+
The remaining certificates will be added to the optional SNI certificate list.
805+
If the same certificate as the default certificate is also listed again (either explicitly in the list or via annotations from other IngressGroup members), it will still be added to the SNI list as well.
804806
See [SSL Certificates](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html#https-listener-certificates) for more details.
805807

806808
!!!tip "Certificate Discovery"

pkg/ingress/model_builder.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,12 +332,24 @@ func (t *defaultModelBuildTask) mergeListenPortConfigs(_ context.Context, listen
332332
var mergedSSLPolicy *string
333333

334334
var mergedTLSCerts []string
335+
336+
// Set the default cert as the first cert
337+
// This process allows the same certificate to be specified for both the default certificate and the SNI certificate.
338+
var defaultCertMemberIndex int
339+
for i, cfg := range listenPortConfigs {
340+
if len(cfg.listenPortConfig.tlsCerts) > 0 {
341+
mergedTLSCerts = append(mergedTLSCerts, cfg.listenPortConfig.tlsCerts[0])
342+
defaultCertMemberIndex = i
343+
break
344+
}
345+
}
346+
335347
mergedTLSCertsSet := sets.NewString()
336348

337349
var mergedMtlsAttributesProvider *types.NamespacedName
338350
var mergedMtlsAttributes *elbv2model.MutualAuthenticationAttributes
339351

340-
for _, cfg := range listenPortConfigs {
352+
for i, cfg := range listenPortConfigs {
341353
if mergedProtocolProvider == nil {
342354
mergedProtocolProvider = &cfg.ingKey
343355
mergedProtocol = cfg.listenPortConfig.protocol
@@ -380,7 +392,11 @@ func (t *defaultModelBuildTask) mergeListenPortConfigs(_ context.Context, listen
380392
}
381393
}
382394

383-
for _, cert := range cfg.listenPortConfig.tlsCerts {
395+
for j, cert := range cfg.listenPortConfig.tlsCerts {
396+
// The first certificate is ignored as it is the default certificate, which has already been added to the mergedTLSCerts.
397+
if i == defaultCertMemberIndex && j == 0 {
398+
continue
399+
}
384400
if mergedTLSCertsSet.Has(cert) {
385401
continue
386402
}

pkg/ingress/model_builder_test.go

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,295 @@ func Test_defaultModelBuilder_Build(t *testing.T) {
10041004
}
10051005
}
10061006
}
1007+
}`,
1008+
},
1009+
{
1010+
name: "Ingress - using acm and internet-facing case with the same acm certificate for default and sni listener",
1011+
env: env{
1012+
svcs: []*corev1.Service{ns_1_svc_1, ns_1_svc_2, ns_1_svc_3},
1013+
},
1014+
fields: fields{
1015+
resolveViaDiscoveryCalls: []resolveViaDiscoveryCall{resolveViaDiscoveryCallForInternetFacingLB},
1016+
listLoadBalancersCalls: []listLoadBalancersCall{listLoadBalancerCallForEmptyLB},
1017+
enableBackendSG: true,
1018+
},
1019+
args: args{
1020+
ingGroup: Group{
1021+
ID: GroupID{Namespace: "ns-1", Name: "ing-1"},
1022+
Members: []ClassifiedIngress{
1023+
{
1024+
Ing: &networking.Ingress{ObjectMeta: metav1.ObjectMeta{
1025+
Namespace: "ns-1",
1026+
Name: "ing-1",
1027+
Annotations: map[string]string{
1028+
"alb.ingress.kubernetes.io/scheme": "internet-facing",
1029+
"alb.ingress.kubernetes.io/certificate-arn": "arn:aws:acm:us-east-1:9999999:certificate/11111111,arn:aws:acm:us-east-1:9999999:certificate/33333333,arn:aws:acm:us-east-1:9999999:certificate/22222222,,arn:aws:acm:us-east-1:9999999:certificate/11111111",
1030+
"alb.ingress.kubernetes.io/mutual-authentication": `[{"port":443,"mode":"off"}]`,
1031+
},
1032+
},
1033+
Spec: networking.IngressSpec{
1034+
Rules: []networking.IngressRule{
1035+
{
1036+
Host: "app-1.example.com",
1037+
IngressRuleValue: networking.IngressRuleValue{
1038+
HTTP: &networking.HTTPIngressRuleValue{
1039+
Paths: []networking.HTTPIngressPath{
1040+
{
1041+
Path: "/svc-1",
1042+
Backend: networking.IngressBackend{
1043+
Service: &networking.IngressServiceBackend{
1044+
Name: ns_1_svc_1.Name,
1045+
Port: networking.ServiceBackendPort{
1046+
Name: "http",
1047+
},
1048+
},
1049+
},
1050+
},
1051+
{
1052+
Path: "/svc-2",
1053+
Backend: networking.IngressBackend{
1054+
Service: &networking.IngressServiceBackend{
1055+
Name: ns_1_svc_2.Name,
1056+
Port: networking.ServiceBackendPort{
1057+
Name: "http",
1058+
},
1059+
},
1060+
},
1061+
},
1062+
},
1063+
},
1064+
},
1065+
},
1066+
{
1067+
Host: "app-2.example.com",
1068+
IngressRuleValue: networking.IngressRuleValue{
1069+
HTTP: &networking.HTTPIngressRuleValue{
1070+
Paths: []networking.HTTPIngressPath{
1071+
{
1072+
Path: "/svc-3",
1073+
Backend: networking.IngressBackend{
1074+
Service: &networking.IngressServiceBackend{
1075+
Name: ns_1_svc_3.Name,
1076+
Port: networking.ServiceBackendPort{
1077+
Name: "https",
1078+
},
1079+
},
1080+
},
1081+
},
1082+
},
1083+
},
1084+
},
1085+
},
1086+
},
1087+
},
1088+
},
1089+
},
1090+
},
1091+
},
1092+
},
1093+
wantStackPatch: `
1094+
{
1095+
"resources": {
1096+
"AWS::EC2::SecurityGroup": {
1097+
"ManagedLBSecurityGroup": {
1098+
"spec": {
1099+
"ingress": [
1100+
{
1101+
"fromPort": 443,
1102+
"ipProtocol": "tcp",
1103+
"ipRanges": [
1104+
{
1105+
"cidrIP": "0.0.0.0/0"
1106+
}
1107+
],
1108+
"toPort": 443
1109+
}
1110+
]
1111+
}
1112+
}
1113+
},
1114+
"AWS::ElasticLoadBalancingV2::Listener": {
1115+
"443": {
1116+
"spec": {
1117+
"certificates": [
1118+
{
1119+
"certificateARN": "arn:aws:acm:us-east-1:9999999:certificate/11111111"
1120+
},
1121+
{
1122+
"certificateARN": "arn:aws:acm:us-east-1:9999999:certificate/33333333"
1123+
},
1124+
{
1125+
"certificateARN": "arn:aws:acm:us-east-1:9999999:certificate/22222222"
1126+
},
1127+
{
1128+
"certificateARN": "arn:aws:acm:us-east-1:9999999:certificate/11111111"
1129+
}
1130+
],
1131+
"defaultActions": [
1132+
{
1133+
"fixedResponseConfig": {
1134+
"contentType": "text/plain",
1135+
"statusCode": "404"
1136+
},
1137+
"type": "fixed-response"
1138+
}
1139+
],
1140+
"loadBalancerARN": {
1141+
"$ref": "#/resources/AWS::ElasticLoadBalancingV2::LoadBalancer/LoadBalancer/status/loadBalancerARN"
1142+
},
1143+
"port": 443,
1144+
"protocol": "HTTPS",
1145+
"sslPolicy": "ELBSecurityPolicy-2016-08",
1146+
"mutualAuthentication" : {
1147+
"mode" : "off",
1148+
"trustStoreArn": ""
1149+
}
1150+
}
1151+
},
1152+
"80": null
1153+
},
1154+
"AWS::ElasticLoadBalancingV2::ListenerRule": {
1155+
"443:1": {
1156+
"spec": {
1157+
"actions": [
1158+
{
1159+
"forwardConfig": {
1160+
"targetGroups": [
1161+
{
1162+
"targetGroupARN": {
1163+
"$ref": "#/resources/AWS::ElasticLoadBalancingV2::TargetGroup/ns-1/ing-1-svc-1:http/status/targetGroupARN"
1164+
}
1165+
}
1166+
]
1167+
},
1168+
"type": "forward"
1169+
}
1170+
],
1171+
"conditions": [
1172+
{
1173+
"field": "host-header",
1174+
"hostHeaderConfig": {
1175+
"values": [
1176+
"app-1.example.com"
1177+
]
1178+
}
1179+
},
1180+
{
1181+
"field": "path-pattern",
1182+
"pathPatternConfig": {
1183+
"values": [
1184+
"/svc-1"
1185+
]
1186+
}
1187+
}
1188+
],
1189+
"listenerARN": {
1190+
"$ref": "#/resources/AWS::ElasticLoadBalancingV2::Listener/443/status/listenerARN"
1191+
},
1192+
"priority": 1
1193+
}
1194+
},
1195+
"443:2": {
1196+
"spec": {
1197+
"actions": [
1198+
{
1199+
"forwardConfig": {
1200+
"targetGroups": [
1201+
{
1202+
"targetGroupARN": {
1203+
"$ref": "#/resources/AWS::ElasticLoadBalancingV2::TargetGroup/ns-1/ing-1-svc-2:http/status/targetGroupARN"
1204+
}
1205+
}
1206+
]
1207+
},
1208+
"type": "forward"
1209+
}
1210+
],
1211+
"conditions": [
1212+
{
1213+
"field": "host-header",
1214+
"hostHeaderConfig": {
1215+
"values": [
1216+
"app-1.example.com"
1217+
]
1218+
}
1219+
},
1220+
{
1221+
"field": "path-pattern",
1222+
"pathPatternConfig": {
1223+
"values": [
1224+
"/svc-2"
1225+
]
1226+
}
1227+
}
1228+
],
1229+
"listenerARN": {
1230+
"$ref": "#/resources/AWS::ElasticLoadBalancingV2::Listener/443/status/listenerARN"
1231+
},
1232+
"priority": 2
1233+
}
1234+
},
1235+
"443:3": {
1236+
"spec": {
1237+
"actions": [
1238+
{
1239+
"forwardConfig": {
1240+
"targetGroups": [
1241+
{
1242+
"targetGroupARN": {
1243+
"$ref": "#/resources/AWS::ElasticLoadBalancingV2::TargetGroup/ns-1/ing-1-svc-3:https/status/targetGroupARN"
1244+
}
1245+
}
1246+
]
1247+
},
1248+
"type": "forward"
1249+
}
1250+
],
1251+
"conditions": [
1252+
{
1253+
"field": "host-header",
1254+
"hostHeaderConfig": {
1255+
"values": [
1256+
"app-2.example.com"
1257+
]
1258+
}
1259+
},
1260+
{
1261+
"field": "path-pattern",
1262+
"pathPatternConfig": {
1263+
"values": [
1264+
"/svc-3"
1265+
]
1266+
}
1267+
}
1268+
],
1269+
"listenerARN": {
1270+
"$ref": "#/resources/AWS::ElasticLoadBalancingV2::Listener/443/status/listenerARN"
1271+
},
1272+
"priority": 3
1273+
}
1274+
},
1275+
"80:1": null,
1276+
"80:2": null,
1277+
"80:3": null
1278+
},
1279+
"AWS::ElasticLoadBalancingV2::LoadBalancer": {
1280+
"LoadBalancer": {
1281+
"spec": {
1282+
"name": "k8s-ns1-ing1-159dd7a143",
1283+
"scheme": "internet-facing",
1284+
"subnetMapping": [
1285+
{
1286+
"subnetID": "subnet-c"
1287+
},
1288+
{
1289+
"subnetID": "subnet-d"
1290+
}
1291+
]
1292+
}
1293+
}
1294+
}
1295+
}
10071296
}`,
10081297
},
10091298
{

0 commit comments

Comments
 (0)