@@ -35,6 +35,9 @@ import (
35
35
"golang.org/x/net/idna"
36
36
)
37
37
38
+ // DefaultACMEDirectory is the default ACME Directory URL used when the Manager's Client is nil.
39
+ const DefaultACMEDirectory = "https://acme-v02.api.letsencrypt.org/directory"
40
+
38
41
// createCertRetryAfter is how much time to wait before removing a failed state
39
42
// entry due to an unsuccessful createCert call.
40
43
// This is a variable instead of a const for testing.
@@ -135,9 +138,10 @@ type Manager struct {
135
138
// Client is used to perform low-level operations, such as account registration
136
139
// and requesting new certificates.
137
140
//
138
- // If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
139
- // as directory endpoint. If the Client.Key is nil, a new ECDSA P-256 key is
140
- // generated and, if Cache is not nil, stored in cache.
141
+ // If Client is nil, a zero-value acme.Client is used with DefaultACMEDirectory
142
+ // as the directory endpoint.
143
+ // If the Client.Key is nil, a new ECDSA P-256 key is generated and,
144
+ // if Cache is not nil, stored in cache.
141
145
//
142
146
// Mutating the field after the first call of GetCertificate method will have no effect.
143
147
Client * acme.Client
@@ -640,78 +644,72 @@ func (m *Manager) certState(ck certKey) (*certState, error) {
640
644
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
641
645
// The key argument is the certificate private key.
642
646
func (m * Manager ) authorizedCert (ctx context.Context , key crypto.Signer , ck certKey ) (der [][]byte , leaf * x509.Certificate , err error ) {
643
- client , err := m .acmeClient (ctx )
644
- if err != nil {
645
- return nil , nil , err
646
- }
647
-
648
- if err := m .verify (ctx , client , ck .domain ); err != nil {
649
- return nil , nil , err
650
- }
651
647
csr , err := certRequest (key , ck .domain , m .ExtraExtensions )
652
648
if err != nil {
653
649
return nil , nil , err
654
650
}
655
- der , _ , err = client .CreateCert (ctx , csr , 0 , true )
651
+
652
+ client , err := m .acmeClient (ctx )
656
653
if err != nil {
657
654
return nil , nil , err
658
655
}
659
- leaf , err = validCert ( ck , der , key , m . now () )
656
+ dir , err := client . Discover ( ctx )
660
657
if err != nil {
661
658
return nil , nil , err
662
659
}
663
- return der , leaf , nil
664
- }
665
660
666
- // revokePendingAuthz revokes all authorizations idenfied by the elements of uri slice.
667
- // It ignores revocation errors.
668
- func (m * Manager ) revokePendingAuthz (ctx context.Context , uri []string ) {
669
- client , err := m .acmeClient (ctx )
670
- if err != nil {
671
- return
661
+ var chain [][]byte
662
+ switch {
663
+ // Pre-RFC legacy CA.
664
+ case dir .OrderURL == "" :
665
+ if err := m .verify (ctx , client , ck .domain ); err != nil {
666
+ return nil , nil , err
667
+ }
668
+ der , _ , err := client .CreateCert (ctx , csr , 0 , true )
669
+ if err != nil {
670
+ return nil , nil , err
671
+ }
672
+ chain = der
673
+ // RFC 8555 compliant CA.
674
+ default :
675
+ o , err := m .verifyRFC (ctx , client , ck .domain )
676
+ if err != nil {
677
+ return nil , nil , err
678
+ }
679
+ der , _ , err := client .CreateOrderCert (ctx , o .FinalizeURL , csr , true )
680
+ if err != nil {
681
+ return nil , nil , err
682
+ }
683
+ chain = der
672
684
}
673
- for _ , u := range uri {
674
- client .RevokeAuthorization (ctx , u )
685
+ leaf , err = validCert (ck , chain , key , m .now ())
686
+ if err != nil {
687
+ return nil , nil , err
675
688
}
689
+ return chain , leaf , nil
676
690
}
677
691
678
- // verify runs the identifier (domain) authorization flow
692
+ // verify runs the identifier (domain) pre- authorization flow for legacy CAs
679
693
// using each applicable ACME challenge type.
680
694
func (m * Manager ) verify (ctx context.Context , client * acme.Client , domain string ) error {
681
- // The list of challenge types we'll try to fulfill
682
- // in this specific order.
683
- challengeTypes := []string {"tls-alpn-01" }
684
- m .tokensMu .RLock ()
685
- if m .tryHTTP01 {
686
- challengeTypes = append (challengeTypes , "http-01" )
687
- }
688
- m .tokensMu .RUnlock ()
689
-
690
- // Keep track of pending authzs and revoke the ones that did not validate.
691
- pendingAuthzs := make (map [string ]bool )
695
+ // Remove all hanging authorizations to reduce rate limit quotas
696
+ // after we're done.
697
+ var authzURLs []string
692
698
defer func () {
693
- var uri []string
694
- for k , pending := range pendingAuthzs {
695
- if pending {
696
- uri = append (uri , k )
697
- }
698
- }
699
- if len (uri ) > 0 {
700
- // Use "detached" background context.
701
- // The revocations need not happen in the current verification flow.
702
- go m .revokePendingAuthz (context .Background (), uri )
703
- }
699
+ go m .deactivatePendingAuthz (authzURLs )
704
700
}()
705
701
706
702
// errs accumulates challenge failure errors, printed if all fail
707
703
errs := make (map [* acme.Challenge ]error )
704
+ challengeTypes := m .supportedChallengeTypes ()
708
705
var nextTyp int // challengeType index of the next challenge type to try
709
706
for {
710
707
// Start domain authorization and get the challenge.
711
708
authz , err := client .Authorize (ctx , domain )
712
709
if err != nil {
713
710
return err
714
711
}
712
+ authzURLs = append (authzURLs , authz .URI )
715
713
// No point in accepting challenges if the authorization status
716
714
// is in a final state.
717
715
switch authz .Status {
@@ -721,8 +719,6 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
721
719
return fmt .Errorf ("acme/autocert: invalid authorization %q" , authz .URI )
722
720
}
723
721
724
- pendingAuthzs [authz .URI ] = true
725
-
726
722
// Pick the next preferred challenge.
727
723
var chal * acme.Challenge
728
724
for chal == nil && nextTyp < len (challengeTypes ) {
@@ -752,11 +748,126 @@ func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string
752
748
errs [chal ] = err
753
749
continue
754
750
}
755
- delete (pendingAuthzs , authz .URI )
756
751
return nil
757
752
}
758
753
}
759
754
755
+ // verifyRFC runs the identifier (domain) order-based authorization flow for RFC compliant CAs
756
+ // using each applicable ACME challenge type.
757
+ func (m * Manager ) verifyRFC (ctx context.Context , client * acme.Client , domain string ) (* acme.Order , error ) {
758
+ // Try each supported challenge type starting with a new order each time.
759
+ // The nextTyp index of the next challenge type to try is shared across
760
+ // all order authorizations: if we've tried a challenge type once and it didn't work,
761
+ // it will most likely not work on another order's authorization either.
762
+ challengeTypes := m .supportedChallengeTypes ()
763
+ nextTyp := 0 // challengeTypes index
764
+ AuthorizeOrderLoop:
765
+ for {
766
+ o , err := client .AuthorizeOrder (ctx , acme .DomainIDs (domain ))
767
+ if err != nil {
768
+ return nil , err
769
+ }
770
+ // Remove all hanging authorizations to reduce rate limit quotas
771
+ // after we're done.
772
+ defer func () {
773
+ go m .deactivatePendingAuthz (o .AuthzURLs )
774
+ }()
775
+
776
+ // Check if there's actually anything we need to do.
777
+ switch o .Status {
778
+ case acme .StatusReady :
779
+ // Already authorized.
780
+ return o , nil
781
+ case acme .StatusPending :
782
+ // Continue normal Order-based flow.
783
+ default :
784
+ return nil , fmt .Errorf ("acme/autocert: invalid new order status %q; order URL: %q" , o .Status , o .URI )
785
+ }
786
+
787
+ // Satisfy all pending authorizations.
788
+ for _ , zurl := range o .AuthzURLs {
789
+ z , err := client .GetAuthorization (ctx , zurl )
790
+ if err != nil {
791
+ return nil , err
792
+ }
793
+ if z .Status != acme .StatusPending {
794
+ // We are interested only in pending authorizations.
795
+ continue
796
+ }
797
+ // Pick the next preferred challenge.
798
+ var chal * acme.Challenge
799
+ for chal == nil && nextTyp < len (challengeTypes ) {
800
+ chal = pickChallenge (challengeTypes [nextTyp ], z .Challenges )
801
+ nextTyp ++
802
+ }
803
+ if chal == nil {
804
+ return nil , fmt .Errorf ("acme/autocert: unable to satisfy %q for domain %q: no viable challenge type found" , z .URI , domain )
805
+ }
806
+ // Respond to the challenge and wait for validation result.
807
+ cleanup , err := m .fulfill (ctx , client , chal , domain )
808
+ if err != nil {
809
+ continue AuthorizeOrderLoop
810
+ }
811
+ defer cleanup ()
812
+ if _ , err := client .Accept (ctx , chal ); err != nil {
813
+ continue AuthorizeOrderLoop
814
+ }
815
+ if _ , err := client .WaitAuthorization (ctx , z .URI ); err != nil {
816
+ continue AuthorizeOrderLoop
817
+ }
818
+ }
819
+
820
+ // All authorizations are satisfied.
821
+ // Wait for the CA to update the order status.
822
+ o , err = client .WaitOrder (ctx , o .URI )
823
+ if err != nil {
824
+ continue AuthorizeOrderLoop
825
+ }
826
+ return o , nil
827
+ }
828
+ }
829
+
830
+ func pickChallenge (typ string , chal []* acme.Challenge ) * acme.Challenge {
831
+ for _ , c := range chal {
832
+ if c .Type == typ {
833
+ return c
834
+ }
835
+ }
836
+ return nil
837
+ }
838
+
839
+ func (m * Manager ) supportedChallengeTypes () []string {
840
+ m .tokensMu .RLock ()
841
+ defer m .tokensMu .RUnlock ()
842
+ typ := []string {"tls-alpn-01" }
843
+ if m .tryHTTP01 {
844
+ typ = append (typ , "http-01" )
845
+ }
846
+ return typ
847
+ }
848
+
849
+ // deactivatePendingAuthz relinquishes all authorizations identified by the elements
850
+ // of the provided uri slice which are in "pending" state.
851
+ // It ignores revocation errors.
852
+ //
853
+ // deactivatePendingAuthz takes no context argument and instead runs with its own
854
+ // "detached" context because deactivations are done in a goroutine separate from
855
+ // that of the main issuance or renewal flow.
856
+ func (m * Manager ) deactivatePendingAuthz (uri []string ) {
857
+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Minute )
858
+ defer cancel ()
859
+ client , err := m .acmeClient (ctx )
860
+ if err != nil {
861
+ return
862
+ }
863
+ for _ , u := range uri {
864
+ z , err := client .GetAuthorization (ctx , u )
865
+ if err == nil && z .Status == acme .StatusPending {
866
+ client .RevokeAuthorization (ctx , u )
867
+ }
868
+ }
869
+ }
870
+
760
871
// fulfill provisions a response to the challenge chal.
761
872
// The cleanup is non-nil only if provisioning succeeded.
762
873
func (m * Manager ) fulfill (ctx context.Context , client * acme.Client , chal * acme.Challenge , domain string ) (cleanup func (), err error ) {
@@ -780,15 +891,6 @@ func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.C
780
891
return nil , fmt .Errorf ("acme/autocert: unknown challenge type %q" , chal .Type )
781
892
}
782
893
783
- func pickChallenge (typ string , chal []* acme.Challenge ) * acme.Challenge {
784
- for _ , c := range chal {
785
- if c .Type == typ {
786
- return c
787
- }
788
- }
789
- return nil
790
- }
791
-
792
894
// putCertToken stores the token certificate with the specified name
793
895
// in both m.certTokens map and m.Cache.
794
896
func (m * Manager ) putCertToken (ctx context.Context , name string , cert * tls.Certificate ) {
@@ -949,7 +1051,7 @@ func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
949
1051
950
1052
client := m .Client
951
1053
if client == nil {
952
- client = & acme.Client {DirectoryURL : acme . LetsEncryptURL }
1054
+ client = & acme.Client {DirectoryURL : DefaultACMEDirectory }
953
1055
}
954
1056
if client .Key == nil {
955
1057
var err error
@@ -967,14 +1069,23 @@ func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
967
1069
}
968
1070
a := & acme.Account {Contact : contact }
969
1071
_ , err := client .Register (ctx , a , m .Prompt )
970
- if ae , ok := err .(* acme.Error ); err == nil || ok && ae .StatusCode == http .StatusConflict {
971
- // conflict indicates the key is already registered
1072
+ if err == nil || isAccountAlreadyExist (err ) {
972
1073
m .client = client
973
1074
err = nil
974
1075
}
975
1076
return m .client , err
976
1077
}
977
1078
1079
+ // isAccountAlreadyExist reports whether the err, as returned from acme.Client.Register,
1080
+ // indicates the account has already been registered.
1081
+ func isAccountAlreadyExist (err error ) bool {
1082
+ if err == acme .ErrAccountAlreadyExists {
1083
+ return true
1084
+ }
1085
+ ae , ok := err .(* acme.Error )
1086
+ return ok && ae .StatusCode == http .StatusConflict
1087
+ }
1088
+
978
1089
func (m * Manager ) hostPolicy () HostPolicy {
979
1090
if m .HostPolicy != nil {
980
1091
return m .HostPolicy
0 commit comments