@@ -3,6 +3,7 @@ package ls
3
3
import (
4
4
"context"
5
5
"fmt"
6
+ "strings"
6
7
7
8
"k8s.io/apimachinery/pkg/util/sets"
8
9
@@ -12,13 +13,15 @@ import (
12
13
"github.com/kubernetes-sigs/aws-alb-ingress-controller/internal/ingress/auth"
13
14
14
15
"github.com/aws/aws-sdk-go/aws/awsutil"
16
+ "github.com/aws/aws-sdk-go/service/acm"
15
17
"github.com/aws/aws-sdk-go/service/elbv2"
16
18
"github.com/kubernetes-sigs/aws-alb-ingress-controller/internal/alb/tg"
17
19
"github.com/kubernetes-sigs/aws-alb-ingress-controller/internal/albctx"
18
20
"github.com/kubernetes-sigs/aws-alb-ingress-controller/internal/aws"
19
21
"github.com/kubernetes-sigs/aws-alb-ingress-controller/internal/ingress/annotations"
20
22
"github.com/kubernetes-sigs/aws-alb-ingress-controller/internal/ingress/annotations/action"
21
23
"github.com/kubernetes-sigs/aws-alb-ingress-controller/internal/ingress/annotations/loadbalancer"
24
+ "github.com/kubernetes-sigs/aws-alb-ingress-controller/pkg/util/log"
22
25
util "github.com/kubernetes-sigs/aws-alb-ingress-controller/pkg/util/types"
23
26
extensions "k8s.io/api/extensions/v1beta1"
24
27
)
@@ -220,7 +223,18 @@ func (controller *defaultController) buildListenerConfig(ctx context.Context, op
220
223
var certificateARNs []string
221
224
_ = annotations .LoadStringSliceAnnotation (AnnotationCertificateARN , & certificateARNs , options .Ingress .Annotations )
222
225
if len (certificateARNs ) == 0 {
223
- return config , errors .Errorf ("annotation %v must be specified for https listener" , parser .GetAnnotationWithPrefix (AnnotationCertificateARN ))
226
+ certs , err := inferCertARNs (controller .cloud , options .Ingress , albctx .GetLogger (ctx ))
227
+ if err != nil {
228
+ return config , errors .Errorf ("missing certificates annotation %v and could not auto-load certificates from ACM: %v" ,
229
+ parser .GetAnnotationWithPrefix (AnnotationCertificateARN ), err )
230
+ }
231
+ if len (certs ) == 0 {
232
+ return config , errors .Errorf ("missing certificates annotation %v could not find any matching certificates from ACM to auto-load" ,
233
+ parser .GetAnnotationWithPrefix (AnnotationCertificateARN ))
234
+ }
235
+
236
+ albctx .GetLogger (ctx ).Infof ("Auto-detected and added %d certificates to listener" , len (certs ))
237
+ certificateARNs = certs
224
238
}
225
239
config .DefaultCertificate = []* elbv2.Certificate {
226
240
{
@@ -250,3 +264,76 @@ func (controller *defaultController) buildDefaultActions(ctx context.Context, op
250
264
}
251
265
return buildActions (ctx , authCfg , options .IngressAnnos , backend , options .TGGroup )
252
266
}
267
+
268
+ // inferCertARNs retrieves a set of certificates from ACM that matches the ingress' hosts list
269
+ func inferCertARNs (acmsvc aws.ACMAPI , ingress * extensions.Ingress , logger * log.Logger ) ([]string , error ) {
270
+ var certArns []string
271
+ var seen = map [string ]bool {}
272
+ var ingressHosts = uniqueHosts (ingress )
273
+
274
+ certs , err := acmsvc .ListCertificates ([]string {acm .CertificateStatusIssued })
275
+ if err != nil {
276
+ return nil , err
277
+ }
278
+
279
+ for _ , c := range certs {
280
+ for _ , h := range ingressHosts {
281
+ if domainMatchesHost (aws .StringValue (c .DomainName ), h ) {
282
+ logger .Infof ("Domain name '%s', matches TLS host '%v', adding to Listener" , aws .StringValue (c .DomainName ), h )
283
+ if ! seen [aws .StringValue (c .CertificateArn )] {
284
+ certArns = append (certArns , aws .StringValue (c .CertificateArn ))
285
+ seen [aws .StringValue (c .CertificateArn )] = true
286
+ }
287
+ } else {
288
+ logger .Debugf ("Ignoring domain name '%s', doesn't match '%s'" , aws .StringValue (c .DomainName ), h )
289
+ }
290
+ }
291
+ }
292
+
293
+ return certArns , nil
294
+ }
295
+
296
+ func domainMatchesHost (domainName string , tlsHost string ) bool {
297
+ if strings .HasPrefix (domainName , "*." ) {
298
+ ds := strings .Split (domainName , "." )
299
+ hs := strings .Split (tlsHost , "." )
300
+
301
+ if len (ds ) != len (hs ) {
302
+ return false
303
+ }
304
+
305
+ for i , dp := range ds {
306
+ if i == 0 && dp == "*" {
307
+ continue
308
+ }
309
+ if dp != hs [i ] {
310
+ return false
311
+ }
312
+ }
313
+ return true
314
+ }
315
+
316
+ return domainName == tlsHost
317
+ }
318
+
319
+ func uniqueHosts (ingress * extensions.Ingress ) []string {
320
+ var result []string
321
+ seen := map [string ]bool {}
322
+
323
+ for _ , r := range ingress .Spec .Rules {
324
+ if ! seen [r .Host ] {
325
+ result = append (result , r .Host )
326
+ seen [r .Host ] = true
327
+ }
328
+ }
329
+ for _ , t := range ingress .Spec .TLS {
330
+ for _ , h := range t .Hosts {
331
+ if ! seen [h ] {
332
+ result = append (result , h )
333
+ seen [h ] = true
334
+ }
335
+ }
336
+ }
337
+
338
+ return result
339
+ }
0 commit comments