aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/caddyserver/certmagic/solvers.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/caddyserver/certmagic/solvers.go')
-rw-r--r--vendor/github.com/caddyserver/certmagic/solvers.go127
1 files changed, 94 insertions, 33 deletions
diff --git a/vendor/github.com/caddyserver/certmagic/solvers.go b/vendor/github.com/caddyserver/certmagic/solvers.go
index c0957da3e6..afd0fd28a2 100644
--- a/vendor/github.com/caddyserver/certmagic/solvers.go
+++ b/vendor/github.com/caddyserver/certmagic/solvers.go
@@ -123,22 +123,19 @@ type tlsALPNSolver struct {
// Present adds the certificate to the certificate cache and, if
// needed, starts a TLS server for answering TLS-ALPN challenges.
func (s *tlsALPNSolver) Present(ctx context.Context, chal acme.Challenge) error {
- // load the certificate into the cache; this isn't strictly necessary
- // if we're using the distributed solver since our GetCertificate
- // function will check storage for the keyAuth anyway, but it seems
- // like loading it into the cache is the right thing to do
+ // we pre-generate the certificate for efficiency with multi-perspective
+ // validation, so it only has to be done once (at least, by this instance;
+ // distributed solving does not have that luxury, oh well) - update the
+ // challenge data in memory to be the generated certificate
cert, err := acmez.TLSALPN01ChallengeCert(chal)
if err != nil {
return err
}
- certHash := hashCertificateChain(cert.Certificate)
- s.config.certCache.mu.Lock()
- s.config.certCache.cache[tlsALPNCertKeyName(chal.Identifier.Value)] = Certificate{
- Certificate: *cert,
- Names: []string{chal.Identifier.Value},
- hash: certHash, // perhaps not necesssary
- }
- s.config.certCache.mu.Unlock()
+ activeChallengesMu.Lock()
+ chalData := activeChallenges[chal.Identifier.Value]
+ chalData.data = cert
+ activeChallenges[chal.Identifier.Value] = chalData
+ activeChallengesMu.Unlock()
// the rest of this function increments the
// challenge count for the solver at this
@@ -273,13 +270,6 @@ func (s *DNS01Solver) Present(ctx context.Context, challenge acme.Challenge) err
dnsName := challenge.DNS01TXTRecordName()
keyAuth := challenge.DNS01KeyAuthorization()
- rec := libdns.Record{
- Type: "TXT",
- Name: dnsName,
- Value: keyAuth,
- TTL: s.TTL,
- }
-
// multiple identifiers can have the same ACME challenge
// domain (e.g. example.com and *.example.com) so we need
// to ensure that we don't solve those concurrently and
@@ -292,6 +282,13 @@ func (s *DNS01Solver) Present(ctx context.Context, challenge acme.Challenge) err
return fmt.Errorf("could not determine zone for domain %q: %v", dnsName, err)
}
+ rec := libdns.Record{
+ Type: "TXT",
+ Name: libdns.RelativeName(dnsName+".", zone),
+ Value: keyAuth,
+ TTL: s.TTL,
+ }
+
results, err := s.DNSProvider.AppendRecords(ctx, zone, []libdns.Record{rec})
if err != nil {
return fmt.Errorf("adding temporary record for zone %s: %w", zone, err)
@@ -458,20 +455,19 @@ func (mmu *mapMutex) locked(key interface{}) (ok bool) {
// sharing sync and storage, and using the facilities provided by
// this package for solving the challenges.
type distributedSolver struct {
- // The config with a certificate cache
- // with a reference to the storage to
- // use which is shared among all the
- // instances in the cluster - REQUIRED.
- acmeManager *ACMEManager
+ // The storage backing the distributed solver. It must be
+ // the same storage configuration as what is solving the
+ // challenge in order to be effective.
+ storage Storage
+
+ // The storage key prefix, associated with the issuer
+ // that is solving the challenge.
+ storageKeyIssuerPrefix string
// Since the distributedSolver is only a
// wrapper over an actual solver, place
// the actual solver here.
solver acmez.Solver
-
- // The CA endpoint URL associated with
- // this solver.
- caURL string
}
// Present invokes the underlying solver's Present method
@@ -483,7 +479,7 @@ func (dhs distributedSolver) Present(ctx context.Context, chal acme.Challenge) e
return err
}
- err = dhs.acmeManager.config.Storage.Store(dhs.challengeTokensKey(chal.Identifier.Value), infoBytes)
+ err = dhs.storage.Store(dhs.challengeTokensKey(chal.Identifier.Value), infoBytes)
if err != nil {
return err
}
@@ -495,10 +491,18 @@ func (dhs distributedSolver) Present(ctx context.Context, chal acme.Challenge) e
return nil
}
+// Wait wraps the underlying solver's Wait() method, if any. Implements acmez.Waiter.
+func (dhs distributedSolver) Wait(ctx context.Context, challenge acme.Challenge) error {
+ if waiter, ok := dhs.solver.(acmez.Waiter); ok {
+ return waiter.Wait(ctx, challenge)
+ }
+ return nil
+}
+
// CleanUp invokes the underlying solver's CleanUp method
// and also cleans up any assets saved to storage.
func (dhs distributedSolver) CleanUp(ctx context.Context, chal acme.Challenge) error {
- err := dhs.acmeManager.config.Storage.Delete(dhs.challengeTokensKey(chal.Identifier.Value))
+ err := dhs.storage.Delete(dhs.challengeTokensKey(chal.Identifier.Value))
if err != nil {
return err
}
@@ -511,7 +515,7 @@ func (dhs distributedSolver) CleanUp(ctx context.Context, chal acme.Challenge) e
// challengeTokensPrefix returns the key prefix for challenge info.
func (dhs distributedSolver) challengeTokensPrefix() string {
- return path.Join(dhs.acmeManager.storageKeyCAPrefix(dhs.caURL), "challenge_tokens")
+ return path.Join(dhs.storageKeyIssuerPrefix, "challenge_tokens")
}
// challengeTokensKey returns the key to use to store and access
@@ -607,6 +611,15 @@ func dialTCPSocket(addr string) error {
return err
}
+// GetACMEChallenge returns an active ACME challenge for the given identifier,
+// or false if no active challenge for that identifier is known.
+func GetACMEChallenge(identifier string) (Challenge, bool) {
+ activeChallengesMu.Lock()
+ chalData, ok := activeChallenges[identifier]
+ activeChallengesMu.Unlock()
+ return chalData, ok
+}
+
// The active challenge solvers, keyed by listener address,
// and protected by a mutex. Note that the creation of
// solver listeners and the incrementing of their counts
@@ -616,8 +629,56 @@ var (
solversMu sync.Mutex
)
+// activeChallenges holds information about all known, currently-active
+// ACME challenges, keyed by identifier. CertMagic guarantees that
+// challenges for the same identifier do not overlap, by its locking
+// mechanisms; thus if a challenge comes in for a certain identifier,
+// we can be confident that if this process initiated the challenge,
+// the correct information to solve it is in this map. (It may have
+// alternatively been initiated by another instance in a cluster, in
+// which case the distributed solver will take care of that.)
+var (
+ activeChallenges = make(map[string]Challenge)
+ activeChallengesMu sync.Mutex
+)
+
+// Challenge is an ACME challenge, but optionally paired with
+// data that can make it easier or more efficient to solve.
+type Challenge struct {
+ acme.Challenge
+ data interface{}
+}
+
+// solverWrapper should be used to wrap all challenge solvers so that
+// we can add the challenge info to memory; this makes challenges globally
+// solvable by a single HTTP or TLS server even if multiple servers with
+// different configurations/scopes need to get certificates.
+type solverWrapper struct{ acmez.Solver }
+
+func (sw solverWrapper) Present(ctx context.Context, chal acme.Challenge) error {
+ activeChallengesMu.Lock()
+ activeChallenges[chal.Identifier.Value] = Challenge{Challenge: chal}
+ activeChallengesMu.Unlock()
+ return sw.Solver.Present(ctx, chal)
+}
+
+func (sw solverWrapper) Wait(ctx context.Context, chal acme.Challenge) error {
+ if waiter, ok := sw.Solver.(acmez.Waiter); ok {
+ return waiter.Wait(ctx, chal)
+ }
+ return nil
+}
+
+func (sw solverWrapper) CleanUp(ctx context.Context, chal acme.Challenge) error {
+ activeChallengesMu.Lock()
+ delete(activeChallenges, chal.Identifier.Value)
+ activeChallengesMu.Unlock()
+ return sw.Solver.CleanUp(ctx, chal)
+}
+
// Interface guards
var (
- _ acmez.Solver = (*DNS01Solver)(nil)
- _ acmez.Waiter = (*DNS01Solver)(nil)
+ _ acmez.Solver = (*solverWrapper)(nil)
+ _ acmez.Waiter = (*solverWrapper)(nil)
+ _ acmez.Waiter = (*distributedSolver)(nil)
)