88 "io/ioutil"
99 "os"
1010 "strings"
11+ "sync"
1112 "time"
1213
1314 "github.com/apex/log"
@@ -318,35 +319,34 @@ func (p *Platform) getHostedZone() (zones []*route53.HostedZone, err error) {
318319func (p * Platform ) createCerts () error {
319320 s := session .New (aws .NewConfig ().WithRegion ("us-east-1" ))
320321 a := acm .New (s )
322+ var domains []string
321323
322324 // existing certs
323- res , err := a .ListCertificates (& acm.ListCertificatesInput {
324- MaxItems : aws .Int64 (1000 ),
325- })
326-
325+ log .Debug ("fetching existing certs" )
326+ certs , err := getCerts (a )
327327 if err != nil {
328- return errors .Wrap (err , "listing " )
328+ return errors .Wrap (err , "fetching certs " )
329329 }
330330
331- var domains []string
332-
333331 // request certs
334332 for _ , s := range p .config .Stages .List () {
335333 if s == nil {
336334 continue
337335 }
338336
337+ certDomains := util .CertDomainNames (s .Domain )
338+
339339 // see if the cert exists
340340 log .Debugf ("looking up cert for %s" , s .Domain )
341- arn := getCert (res . CertificateSummaryList , s .Domain )
341+ arn := getCert (certs , s .Domain )
342342 if arn != "" {
343343 log .Debugf ("found cert for %s: %s" , s .Domain , arn )
344344 s .Cert = arn
345345 continue
346346 }
347347
348348 option := acm.DomainValidationOption {
349- DomainName : & s . Domain ,
349+ DomainName : aws . String ( certDomains [ 0 ]) ,
350350 ValidationDomain : aws .String (util .Domain (s .Domain )),
351351 }
352352
@@ -356,15 +356,16 @@ func (p *Platform) createCerts() error {
356356
357357 // request the cert
358358 res , err := a .RequestCertificate (& acm.RequestCertificateInput {
359- DomainName : & s . Domain ,
359+ DomainName : aws . String ( certDomains [ 0 ]) ,
360360 DomainValidationOptions : options ,
361+ SubjectAlternativeNames : aws .StringSlice (certDomains [1 :]),
361362 })
362363
363364 if err != nil {
364- return errors .Wrapf (err , "requesting cert for %s " , s . Domain )
365+ return errors .Wrapf (err , "requesting cert for %v " , certDomains )
365366 }
366367
367- domains = append (domains , s . Domain )
368+ domains = append (domains , certDomains [ 0 ] )
368369 s .Cert = * res .CertificateArn
369370 }
370371
@@ -379,7 +380,7 @@ func (p *Platform) createCerts() error {
379380
380381 // wait for approval
381382 for range time .Tick (4 * time .Second ) {
382- res , err = a .ListCertificates (& acm.ListCertificatesInput {
383+ res , err : = a .ListCertificates (& acm.ListCertificatesInput {
383384 MaxItems : aws .Int64 (1000 ),
384385 CertificateStatuses : aws .StringSlice ([]string {acm .CertificateStatusPendingValidation }),
385386 })
@@ -787,12 +788,72 @@ func toEnv(env config.Environment, stage string) *lambda.Environment {
787788 }
788789}
789790
790- // getCert returns the ARN if the cert is present.
791- func getCert (certs []* acm.CertificateSummary , domain string ) string {
791+ // getCerts returns the certificates available.
792+ func getCerts (a * acm.ACM ) (certs []* acm.CertificateDetail , err error ) {
793+ var g errgroup.Group
794+ var mu sync.Mutex
795+
796+ res , err := a .ListCertificates (& acm.ListCertificatesInput {
797+ MaxItems : aws .Int64 (1000 ),
798+ })
799+
800+ if err != nil {
801+ return nil , errors .Wrap (err , "listing" )
802+ }
803+
804+ for _ , c := range res .CertificateSummaryList {
805+ c := c
806+ g .Go (func () error {
807+ res , err := a .DescribeCertificate (& acm.DescribeCertificateInput {
808+ CertificateArn : c .CertificateArn ,
809+ })
810+
811+ if err != nil {
812+ return errors .Wrap (err , "describing" )
813+ }
814+
815+ mu .Lock ()
816+ certs = append (certs , res .Certificate )
817+ mu .Unlock ()
818+ return nil
819+ })
820+ }
821+
822+ err = g .Wait ()
823+ return
824+ }
825+
826+ // getCert returns the ARN of a certificate with can satisfy domain,
827+ // favoring more specific certificates, then falling back on wildcards.
828+ func getCert (certs []* acm.CertificateDetail , domain string ) string {
829+ // exact domain
792830 for _ , c := range certs {
793831 if * c .DomainName == domain {
794832 return * c .CertificateArn
795833 }
796834 }
835+
836+ // exact alt
837+ for _ , c := range certs {
838+ for _ , a := range c .SubjectAlternativeNames {
839+ if * a == domain {
840+ return * c .CertificateArn
841+ }
842+ }
843+ }
844+
845+ // wildcards
846+ for _ , c := range certs {
847+ if util .WildcardMatches (* c .DomainName , domain ) {
848+ return * c .CertificateArn
849+ }
850+
851+ for _ , a := range c .SubjectAlternativeNames {
852+ if util .WildcardMatches (* a , domain ) {
853+ return * c .CertificateArn
854+ }
855+ }
856+ }
857+
797858 return ""
798859}
0 commit comments