diff options
Diffstat (limited to 'vendor/github.com/caddyserver/certmagic/acmeclient.go')
-rw-r--r-- | vendor/github.com/caddyserver/certmagic/acmeclient.go | 193 |
1 files changed, 115 insertions, 78 deletions
diff --git a/vendor/github.com/caddyserver/certmagic/acmeclient.go b/vendor/github.com/caddyserver/certmagic/acmeclient.go index 342b222d66..8a61a08255 100644 --- a/vendor/github.com/caddyserver/certmagic/acmeclient.go +++ b/vendor/github.com/caddyserver/certmagic/acmeclient.go @@ -37,19 +37,104 @@ func init() { weakrand.Seed(time.Now().UnixNano()) } -// acmeClient holds state necessary for us to perform -// ACME operations for certificate management. Call -// ACMEManager.newACMEClient() to get a valid one to . +// acmeClient holds state necessary to perform ACME operations +// for certificate management with an ACME account. Call +// ACMEManager.newACMEClientWithAccount() to get a valid one. type acmeClient struct { mgr *ACMEManager acmeClient *acmez.Client account acme.Account } -// newACMEClient creates the underlying ACME library client type. -// If useTestCA is true, am.TestCA will be used if it is set; -// otherwise, the primary CA will still be used. -func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive bool) (*acmeClient, error) { +// newACMEClientWithAccount creates an ACME client ready to use with an account, including +// loading one from storage or registering a new account with the CA if necessary. If +// useTestCA is true, am.TestCA will be used if set; otherwise, the primary CA will be used. +func (am *ACMEManager) newACMEClientWithAccount(ctx context.Context, useTestCA, interactive bool) (*acmeClient, error) { + // first, get underlying ACME client + client, err := am.newACMEClient(useTestCA) + if err != nil { + return nil, err + } + + // look up or create the ACME account + var account acme.Account + if am.AccountKeyPEM != "" { + account, err = am.GetAccount(ctx, []byte(am.AccountKeyPEM)) + } else { + account, err = am.getAccount(client.Directory, am.Email) + } + if err != nil { + return nil, fmt.Errorf("getting ACME account: %v", err) + } + + // register account if it is new + if account.Status == "" { + if am.NewAccountFunc != nil { + account, err = am.NewAccountFunc(ctx, am, account) + if err != nil { + return nil, fmt.Errorf("account pre-registration callback: %v", err) + } + } + + // agree to terms + if interactive { + if !am.Agreed { + var termsURL string + dir, err := client.GetDirectory(ctx) + if err != nil { + return nil, fmt.Errorf("getting directory: %w", err) + } + if dir.Meta != nil { + termsURL = dir.Meta.TermsOfService + } + if termsURL != "" { + am.Agreed = am.askUserAgreement(termsURL) + if !am.Agreed { + return nil, fmt.Errorf("user must agree to CA terms") + } + } + } + } else { + // can't prompt a user who isn't there; they should + // have reviewed the terms beforehand + am.Agreed = true + } + account.TermsOfServiceAgreed = am.Agreed + + // associate account with external binding, if configured + if am.ExternalAccount != nil { + err := account.SetExternalAccountBinding(ctx, client.Client, *am.ExternalAccount) + if err != nil { + return nil, err + } + } + + // create account + account, err = client.NewAccount(ctx, account) + if err != nil { + return nil, fmt.Errorf("registering account %v with server: %w", account.Contact, err) + } + + // persist the account to storage + err = am.saveAccount(client.Directory, account) + if err != nil { + return nil, fmt.Errorf("could not save account %v: %v", account.Contact, err) + } + } + + c := &acmeClient{ + mgr: am, + acmeClient: client, + account: account, + } + + return c, nil +} + +// newACMEClient creates a new underlying ACME client using the settings in am, +// independent of any particular ACME account. If useTestCA is true, am.TestCA +// will be used if it is set; otherwise, the primary CA will be used. +func (am *ACMEManager) newACMEClient(useTestCA bool) (*acmez.Client, error) { // ensure defaults are filled in var caURL string if useTestCA { @@ -78,12 +163,6 @@ func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL) } - // look up or create the ACME account - account, err := am.getAccount(caURL, am.Email) - if err != nil { - return nil, fmt.Errorf("getting ACME account: %v", err) - } - // set up the dialers and resolver for the ACME client's HTTP client dialer := &net.Dialer{ Timeout: 30 * time.Second, @@ -153,12 +232,12 @@ func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive useHTTPPort = am.AltHTTPPort } client.ChallengeSolvers[acme.ChallengeTypeHTTP01] = distributedSolver{ - acmeManager: am, + storage: am.config.Storage, + storageKeyIssuerPrefix: am.storageKeyCAPrefix(client.Directory), solver: &httpSolver{ acmeManager: am, address: net.JoinHostPort(am.ListenHost, strconv.Itoa(useHTTPPort)), }, - caURL: client.Directory, } } @@ -172,12 +251,12 @@ func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive useTLSALPNPort = am.AltTLSALPNPort } client.ChallengeSolvers[acme.ChallengeTypeTLSALPN01] = distributedSolver{ - acmeManager: am, + storage: am.config.Storage, + storageKeyIssuerPrefix: am.storageKeyCAPrefix(client.Directory), solver: &tlsALPNSolver{ config: am.config, address: net.JoinHostPort(am.ListenHost, strconv.Itoa(useTLSALPNPort)), }, - caURL: client.Directory, } } } else { @@ -185,68 +264,26 @@ func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive client.ChallengeSolvers[acme.ChallengeTypeDNS01] = am.DNS01Solver } - // register account if it is new - if account.Status == "" { - if am.NewAccountFunc != nil { - err = am.NewAccountFunc(ctx, am, account) - if err != nil { - return nil, fmt.Errorf("account pre-registration callback: %v", err) - } - } - - // agree to terms - if interactive { - if !am.Agreed { - var termsURL string - dir, err := client.GetDirectory(ctx) - if err != nil { - return nil, fmt.Errorf("getting directory: %w", err) - } - if dir.Meta != nil { - termsURL = dir.Meta.TermsOfService - } - if termsURL != "" { - am.Agreed = am.askUserAgreement(termsURL) - if !am.Agreed { - return nil, fmt.Errorf("user must agree to CA terms") - } - } - } - } else { - // can't prompt a user who isn't there; they should - // have reviewed the terms beforehand - am.Agreed = true - } - account.TermsOfServiceAgreed = am.Agreed - - // associate account with external binding, if configured - if am.ExternalAccount != nil { - err := account.SetExternalAccountBinding(ctx, client.Client, *am.ExternalAccount) - if err != nil { - return nil, err - } - } - - // create account - account, err = client.NewAccount(ctx, account) - if err != nil { - return nil, fmt.Errorf("registering account with server: %w", err) - } - - // persist the account to storage - err = am.saveAccount(caURL, account) - if err != nil { - return nil, fmt.Errorf("could not save account: %v", err) - } - } - - c := &acmeClient{ - mgr: am, - acmeClient: client, - account: account, + // wrap solvers in our wrapper so that we can keep track of challenge + // info: this is useful for solving challenges globally as a process; + // for example, usually there is only one process that can solve the + // HTTP and TLS-ALPN challenges, and only one server in that process + // that can bind the necessary port(s), so if a server listening on + // a different port needed a certificate, it would have to know about + // the other server listening on that port, and somehow convey its + // challenge info or share its config, but this isn't always feasible; + // what the wrapper does is it accesses a global challenge memory so + // that unrelated servers in this process can all solve each others' + // challenges without having to know about each other - Caddy's admin + // endpoint uses this functionality since it and the HTTP/TLS modules + // do not know about each other + // (doing this here in a separate loop ensures that even if we expose + // solver config to users later, we will even wrap their own solvers) + for name, solver := range client.ChallengeSolvers { + client.ChallengeSolvers[name] = solverWrapper{solver} } - return c, nil + return client, nil } func (c *acmeClient) throttle(ctx context.Context, names []string) error { @@ -325,7 +362,7 @@ var ( // RateLimitEvents is how many new events can be allowed // in RateLimitEventsWindow. - RateLimitEvents = 10 + RateLimitEvents = 20 // RateLimitEventsWindow is the size of the sliding // window that throttles events. |