diff options
author | Matthew Holt <mholt@users.noreply.github.com> | 2019-03-04 12:14:25 -0700 |
---|---|---|
committer | Matthew Holt <mholt@users.noreply.github.com> | 2019-03-04 12:14:25 -0700 |
commit | 80dfb8b2a7f89b120a627bc4d866a1dc5ed3d92f (patch) | |
tree | 089d8a001256988f299277d2cb54d93c9efed5e5 | |
parent | 98f160e39cadeb737cd631199b9f5e1e30682e94 (diff) |
vendor: Update lego; notes for v0.11.5v0.11.5
19 files changed, 651 insertions, 55 deletions
@@ -76,7 +76,7 @@ Caddy binaries have no dependencies and are available for every platform. Get Ca ## Build -To build from source you need **[Git](https://git-scm.com/downloads)** and **[Go](https://golang.org/doc/install)** (1.10 or newer). Follow these instruction for fast building: +To build from source you need **[Git](https://git-scm.com/downloads)** and **[Go](https://golang.org/doc/install)** (1.12 or newer). Follow these instruction for fast building: - Get the source with `go get github.com/mholt/caddy/caddy` and then run `go get github.com/caddyserver/builds` - Now `cd $GOPATH/src/github.com/mholt/caddy/caddy` and run `go run build.go` diff --git a/dist/CHANGES.txt b/dist/CHANGES.txt index 47383a9b..eed33276 100644 --- a/dist/CHANGES.txt +++ b/dist/CHANGES.txt @@ -1,6 +1,18 @@ CHANGES +0.11.5 (March 4, 2019) +- TLS 1.3 +- Built with Go 1.12 +- More organized output at startup +- internal: Paths are now hidden from other middlewares +- staticfiles: Allow HEAD method +- tls: TLS 1.3 default max version (1.2 still default min) +- tls: Better handling of empty ACME email addresses +- tls: Removed CBC ciphers from defaults +- Other minor improvements and bug fixes + + 0.11.4 (February 15, 2019) - New -json-to-caddyfile and -caddyfile-to-json flags - Fix leaking logging goroutine on SIGUSR1 diff --git a/dist/README.txt b/dist/README.txt index 0b6ce04e..b74a1c0b 100644 --- a/dist/README.txt +++ b/dist/README.txt @@ -1,4 +1,4 @@ -CADDY 0.11.4 +CADDY 0.11.5 Website https://caddyserver.com diff --git a/vendor/github.com/cenkalti/backoff/LICENSE b/vendor/github.com/cenkalti/backoff/LICENSE new file mode 100644 index 00000000..89b81799 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Cenk Altı + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/cenkalti/backoff/backoff.go b/vendor/github.com/cenkalti/backoff/backoff.go new file mode 100644 index 00000000..3676ee40 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/backoff.go @@ -0,0 +1,66 @@ +// Package backoff implements backoff algorithms for retrying operations. +// +// Use Retry function for retrying operations that may fail. +// If Retry does not meet your needs, +// copy/paste the function into your project and modify as you wish. +// +// There is also Ticker type similar to time.Ticker. +// You can use it if you need to work with channels. +// +// See Examples section below for usage examples. +package backoff + +import "time" + +// BackOff is a backoff policy for retrying an operation. +type BackOff interface { + // NextBackOff returns the duration to wait before retrying the operation, + // or backoff. Stop to indicate that no more retries should be made. + // + // Example usage: + // + // duration := backoff.NextBackOff(); + // if (duration == backoff.Stop) { + // // Do not retry operation. + // } else { + // // Sleep for duration and retry operation. + // } + // + NextBackOff() time.Duration + + // Reset to initial state. + Reset() +} + +// Stop indicates that no more retries should be made for use in NextBackOff(). +const Stop time.Duration = -1 + +// ZeroBackOff is a fixed backoff policy whose backoff time is always zero, +// meaning that the operation is retried immediately without waiting, indefinitely. +type ZeroBackOff struct{} + +func (b *ZeroBackOff) Reset() {} + +func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 } + +// StopBackOff is a fixed backoff policy that always returns backoff.Stop for +// NextBackOff(), meaning that the operation should never be retried. +type StopBackOff struct{} + +func (b *StopBackOff) Reset() {} + +func (b *StopBackOff) NextBackOff() time.Duration { return Stop } + +// ConstantBackOff is a backoff policy that always returns the same backoff delay. +// This is in contrast to an exponential backoff policy, +// which returns a delay that grows longer as you call NextBackOff() over and over again. +type ConstantBackOff struct { + Interval time.Duration +} + +func (b *ConstantBackOff) Reset() {} +func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval } + +func NewConstantBackOff(d time.Duration) *ConstantBackOff { + return &ConstantBackOff{Interval: d} +} diff --git a/vendor/github.com/cenkalti/backoff/context.go b/vendor/github.com/cenkalti/backoff/context.go new file mode 100644 index 00000000..7706faa2 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/context.go @@ -0,0 +1,63 @@ +package backoff + +import ( + "context" + "time" +) + +// BackOffContext is a backoff policy that stops retrying after the context +// is canceled. +type BackOffContext interface { + BackOff + Context() context.Context +} + +type backOffContext struct { + BackOff + ctx context.Context +} + +// WithContext returns a BackOffContext with context ctx +// +// ctx must not be nil +func WithContext(b BackOff, ctx context.Context) BackOffContext { + if ctx == nil { + panic("nil context") + } + + if b, ok := b.(*backOffContext); ok { + return &backOffContext{ + BackOff: b.BackOff, + ctx: ctx, + } + } + + return &backOffContext{ + BackOff: b, + ctx: ctx, + } +} + +func ensureContext(b BackOff) BackOffContext { + if cb, ok := b.(BackOffContext); ok { + return cb + } + return WithContext(b, context.Background()) +} + +func (b *backOffContext) Context() context.Context { + return b.ctx +} + +func (b *backOffContext) NextBackOff() time.Duration { + select { + case <-b.ctx.Done(): + return Stop + default: + } + next := b.BackOff.NextBackOff() + if deadline, ok := b.ctx.Deadline(); ok && deadline.Sub(time.Now()) < next { + return Stop + } + return next +} diff --git a/vendor/github.com/cenkalti/backoff/exponential.go b/vendor/github.com/cenkalti/backoff/exponential.go new file mode 100644 index 00000000..a031a659 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/exponential.go @@ -0,0 +1,153 @@ +package backoff + +import ( + "math/rand" + "time" +) + +/* +ExponentialBackOff is a backoff implementation that increases the backoff +period for each retry attempt using a randomization function that grows exponentially. + +NextBackOff() is calculated using the following formula: + + randomized interval = + RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor]) + +In other words NextBackOff() will range between the randomization factor +percentage below and above the retry interval. + +For example, given the following parameters: + + RetryInterval = 2 + RandomizationFactor = 0.5 + Multiplier = 2 + +the actual backoff period used in the next retry attempt will range between 1 and 3 seconds, +multiplied by the exponential, that is, between 2 and 6 seconds. + +Note: MaxInterval caps the RetryInterval and not the randomized interval. + +If the time elapsed since an ExponentialBackOff instance is created goes past the +MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop. + +The elapsed time can be reset by calling Reset(). + +Example: Given the following default arguments, for 10 tries the sequence will be, +and assuming we go over the MaxElapsedTime on the 10th try: + + Request # RetryInterval (seconds) Randomized Interval (seconds) + + 1 0.5 [0.25, 0.75] + 2 0.75 [0.375, 1.125] + 3 1.125 [0.562, 1.687] + 4 1.687 [0.8435, 2.53] + 5 2.53 [1.265, 3.795] + 6 3.795 [1.897, 5.692] + 7 5.692 [2.846, 8.538] + 8 8.538 [4.269, 12.807] + 9 12.807 [6.403, 19.210] + 10 19.210 backoff.Stop + +Note: Implementation is not thread-safe. +*/ +type ExponentialBackOff struct { + InitialInterval time.Duration + RandomizationFactor float64 + Multiplier float64 + MaxInterval time.Duration + // After MaxElapsedTime the ExponentialBackOff stops. + // It never stops if MaxElapsedTime == 0. + MaxElapsedTime time.Duration + Clock Clock + + currentInterval time.Duration + startTime time.Time +} + +// Clock is an interface that returns current time for BackOff. +type Clock interface { + Now() time.Time +} + +// Default values for ExponentialBackOff. +const ( + DefaultInitialInterval = 500 * time.Millisecond + DefaultRandomizationFactor = 0.5 + DefaultMultiplier = 1.5 + DefaultMaxInterval = 60 * time.Second + DefaultMaxElapsedTime = 15 * time.Minute +) + +// NewExponentialBackOff creates an instance of ExponentialBackOff using default values. +func NewExponentialBackOff() *ExponentialBackOff { + b := &ExponentialBackOff{ + InitialInterval: DefaultInitialInterval, + RandomizationFactor: DefaultRandomizationFactor, + Multiplier: DefaultMultiplier, + MaxInterval: DefaultMaxInterval, + MaxElapsedTime: DefaultMaxElapsedTime, + Clock: SystemClock, + } + b.Reset() + return b +} + +type systemClock struct{} + +func (t systemClock) Now() time.Time { + return time.Now() +} + +// SystemClock implements Clock interface that uses time.Now(). +var SystemClock = systemClock{} + +// Reset the interval back to the initial retry interval and restarts the timer. +func (b *ExponentialBackOff) Reset() { + b.currentInterval = b.InitialInterval + b.startTime = b.Clock.Now() +} + +// NextBackOff calculates the next backoff interval using the formula: +// Randomized interval = RetryInterval +/- (RandomizationFactor * RetryInterval) +func (b *ExponentialBackOff) NextBackOff() time.Duration { + // Make sure we have not gone over the maximum elapsed time. + if b.MaxElapsedTime != 0 && b.GetElapsedTime() > b.MaxElapsedTime { + return Stop + } + defer b.incrementCurrentInterval() + return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval) +} + +// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance +// is created and is reset when Reset() is called. +// +// The elapsed time is computed using time.Now().UnixNano(). It is +// safe to call even while the backoff policy is used by a running +// ticker. +func (b *ExponentialBackOff) GetElapsedTime() time.Duration { + return b.Clock.Now().Sub(b.startTime) +} + +// Increments the current interval by multiplying it with the multiplier. +func (b *ExponentialBackOff) incrementCurrentInterval() { + // Check for overflow, if overflow is detected set the current interval to the max interval. + if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier { + b.currentInterval = b.MaxInterval + } else { + b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier) + } +} + +// Returns a random value from the following interval: +// [randomizationFactor * currentInterval, randomizationFactor * currentInterval]. +func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration { + var delta = randomizationFactor * float64(currentInterval) + var minInterval = float64(currentInterval) - delta + var maxInterval = float64(currentInterval) + delta + + // Get a random value from the range [minInterval, maxInterval]. + // The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then + // we want a 33% chance for selecting either 1, 2 or 3. + return time.Duration(minInterval + (random * (maxInterval - minInterval + 1))) +} diff --git a/vendor/github.com/cenkalti/backoff/retry.go b/vendor/github.com/cenkalti/backoff/retry.go new file mode 100644 index 00000000..e936a506 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/retry.go @@ -0,0 +1,82 @@ +package backoff + +import "time" + +// An Operation is executing by Retry() or RetryNotify(). +// The operation will be retried using a backoff policy if it returns an error. +type Operation func() error + +// Notify is a notify-on-error function. It receives an operation error and +// backoff delay if the operation failed (with an error). +// +// NOTE that if the backoff policy stated to stop retrying, +// the notify function isn't called. +type Notify func(error, time.Duration) + +// Retry the operation o until it does not return error or BackOff stops. +// o is guaranteed to be run at least once. +// +// If o returns a *PermanentError, the operation is not retried, and the +// wrapped error is returned. +// +// Retry sleeps the goroutine for the duration returned by BackOff after a +// failed operation returns. +func Retry(o Operation, b BackOff) error { return RetryNotify(o, b, nil) } + +// RetryNotify calls notify function with the error and wait duration +// for each failed attempt before sleep. +func RetryNotify(operation Operation, b BackOff, notify Notify) error { + var err error + var next time.Duration + var t *time.Timer + + cb := ensureContext(b) + + b.Reset() + for { + if err = operation(); err == nil { + return nil + } + + if permanent, ok := err.(*PermanentError); ok { + return permanent.Err + } + + if next = cb.NextBackOff(); next == Stop { + return err + } + + if notify != nil { + notify(err, next) + } + + if t == nil { + t = time.NewTimer(next) + defer t.Stop() + } else { + t.Reset(next) + } + + select { + case <-cb.Context().Done(): + return err + case <-t.C: + } + } +} + +// PermanentError signals that the operation should not be retried. +type PermanentError struct { + Err error +} + +func (e *PermanentError) Error() string { + return e.Err.Error() +} + +// Permanent wraps the given err in a *PermanentError. +func Permanent(err error) *PermanentError { + return &PermanentError{ + Err: err, + } +} diff --git a/vendor/github.com/cenkalti/backoff/ticker.go b/vendor/github.com/cenkalti/backoff/ticker.go new file mode 100644 index 00000000..e41084b0 --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/ticker.go @@ -0,0 +1,82 @@ +package backoff + +import ( + "sync" + "time" +) + +// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff. +// +// Ticks will continue to arrive when the previous operation is still running, +// so operations that take a while to fail could run in quick succession. +type Ticker struct { + C <-chan time.Time + c chan time.Time + b BackOffContext + stop chan struct{} + stopOnce sync.Once +} + +// NewTicker returns a new Ticker containing a channel that will send +// the time at times specified by the BackOff argument. Ticker is +// guaranteed to tick at least once. The channel is closed when Stop +// method is called or BackOff stops. It is not safe to manipulate the +// provided backoff policy (notably calling NextBackOff or Reset) +// while the ticker is running. +func NewTicker(b BackOff) *Ticker { + c := make(chan time.Time) + t := &Ticker{ + C: c, + c: c, + b: ensureContext(b), + stop: make(chan struct{}), + } + t.b.Reset() + go t.run() + return t +} + +// Stop turns off a ticker. After Stop, no more ticks will be sent. +func (t *Ticker) Stop() { + t.stopOnce.Do(func() { close(t.stop) }) +} + +func (t *Ticker) run() { + c := t.c + defer close(c) + + // Ticker is guaranteed to tick at least once. + afterC := t.send(time.Now()) + + for { + if afterC == nil { + return + } + + select { + case tick := <-afterC: + afterC = t.send(tick) + case <-t.stop: + t.c = nil // Prevent future ticks from being sent to the channel. + return + case <-t.b.Context().Done(): + return + } + } +} + +func (t *Ticker) send(tick time.Time) <-chan time.Time { + select { + case t.c <- tick: + case <-t.stop: + return nil + } + + next := t.b.NextBackOff() + if next == Stop { + t.Stop() + return nil + } + + return time.After(next) +} diff --git a/vendor/github.com/cenkalti/backoff/tries.go b/vendor/github.com/cenkalti/backoff/tries.go new file mode 100644 index 00000000..cfeefd9b --- /dev/null +++ b/vendor/github.com/cenkalti/backoff/tries.go @@ -0,0 +1,35 @@ +package backoff + +import "time" + +/* +WithMaxRetries creates a wrapper around another BackOff, which will +return Stop if NextBackOff() has been called too many times since +the last time Reset() was called + +Note: Implementation is not thread-safe. +*/ +func WithMaxRetries(b BackOff, max uint64) BackOff { + return &backOffTries{delegate: b, maxTries: max} +} + +type backOffTries struct { + delegate BackOff + maxTries uint64 + numTries uint64 +} + +func (b *backOffTries) NextBackOff() time.Duration { + if b.maxTries > 0 { + if b.maxTries <= b.numTries { + return Stop + } + b.numTries++ + } + return b.delegate.NextBackOff() +} + +func (b *backOffTries) Reset() { + b.numTries = 0 + b.delegate.Reset() +} diff --git a/vendor/github.com/xenolf/lego/acme/api/api.go b/vendor/github.com/xenolf/lego/acme/api/api.go index 14b18f52..a62e49e4 100644 --- a/vendor/github.com/xenolf/lego/acme/api/api.go +++ b/vendor/github.com/xenolf/lego/acme/api/api.go @@ -2,12 +2,15 @@ package api import ( "bytes" + "context" "crypto" "encoding/json" "errors" "fmt" "net/http" + "time" + "github.com/cenkalti/backoff" "github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme/api/internal/nonces" "github.com/xenolf/lego/acme/api/internal/secure" @@ -64,34 +67,46 @@ func (a *Core) post(uri string, reqBody, response interface{}) (*http.Response, return nil, errors.New("failed to marshal message") } - return a.retrievablePost(uri, content, response, 0) + return a.retrievablePost(uri, content, response) } // postAsGet performs an HTTP POST ("POST-as-GET") request. // https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-6.3 func (a *Core) postAsGet(uri string, response interface{}) (*http.Response, error) { - return a.retrievablePost(uri, []byte{}, response, 0) + return a.retrievablePost(uri, []byte{}, response) } -func (a *Core) retrievablePost(uri string, content []byte, response interface{}, retry int) (*http.Response, error) { - resp, err := a.signedPost(uri, content, response) - if err != nil { - // during tests, 5 retries allow to support ~50% of bad nonce. - if retry >= 5 { - log.Infof("too many retry on a nonce error, retry count: %d", retry) - return resp, err - } - switch err.(type) { - // Retry once if the nonce was invalidated - case *acme.NonceError: - log.Infof("nonce error retry: %s", err) - resp, err = a.retrievablePost(uri, content, response, retry+1) - if err != nil { - return resp, err +func (a *Core) retrievablePost(uri string, content []byte, response interface{}) (*http.Response, error) { + // during tests, allow to support ~90% of bad nonce with a minimum of attempts. + bo := backoff.NewExponentialBackOff() + bo.InitialInterval = 200 * time.Millisecond + bo.MaxInterval = 5 * time.Second + bo.MaxElapsedTime = 20 * time.Second + + ctx, cancel := context.WithCancel(context.Background()) + + var resp *http.Response + operation := func() error { + var err error + resp, err = a.signedPost(uri, content, response) + if err != nil { + switch err.(type) { + // Retry if the nonce was invalidated + case *acme.NonceError: + log.Infof("nonce error retry: %s", err) + return err + default: + cancel() + return err } - default: - return resp, err } + + return nil + } + + err := backoff.Retry(operation, backoff.WithContext(bo, ctx)) + if err != nil { + return nil, err } return resp, nil diff --git a/vendor/github.com/xenolf/lego/acme/api/internal/sender/useragent.go b/vendor/github.com/xenolf/lego/acme/api/internal/sender/useragent.go index b2096407..9806fd1f 100644 --- a/vendor/github.com/xenolf/lego/acme/api/internal/sender/useragent.go +++ b/vendor/github.com/xenolf/lego/acme/api/internal/sender/useragent.go @@ -5,7 +5,7 @@ package sender const ( // ourUserAgent is the User-Agent of this underlying library package. - ourUserAgent = "xenolf-acme/2.0.1" + ourUserAgent = "xenolf-acme/2.2.0" // ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package. // values: detach|release diff --git a/vendor/github.com/xenolf/lego/certcrypto/crypto.go b/vendor/github.com/xenolf/lego/certcrypto/crypto.go index bb99d7d2..c9d0c109 100644 --- a/vendor/github.com/xenolf/lego/certcrypto/crypto.go +++ b/vendor/github.com/xenolf/lego/certcrypto/crypto.go @@ -124,6 +124,10 @@ func GenerateCSR(privateKey crypto.PrivateKey, domain string, san []string, must } func PEMEncode(data interface{}) []byte { + return pem.EncodeToMemory(PEMBlock(data)) +} + +func PEMBlock(data interface{}) *pem.Block { var pemBlock *pem.Block switch key := data.(type) { case *ecdsa.PrivateKey: @@ -137,7 +141,7 @@ func PEMEncode(data interface{}) []byte { pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.(DERCertificateBytes))} } - return pem.EncodeToMemory(pemBlock) + return pemBlock } func pemDecode(data []byte) (*pem.Block, error) { diff --git a/vendor/github.com/xenolf/lego/challenge/dns01/cname.go b/vendor/github.com/xenolf/lego/challenge/dns01/cname.go new file mode 100644 index 00000000..619c8476 --- /dev/null +++ b/vendor/github.com/xenolf/lego/challenge/dns01/cname.go @@ -0,0 +1,16 @@ +package dns01 + +import "github.com/miekg/dns" + +// Update FQDN with CNAME if any +func updateDomainWithCName(r *dns.Msg, fqdn string) string { + for _, rr := range r.Answer { + if cn, ok := rr.(*dns.CNAME); ok { + if cn.Hdr.Name == fqdn { + return cn.Target + } + } + } + + return fqdn +} diff --git a/vendor/github.com/xenolf/lego/challenge/dns01/dns_challenge.go b/vendor/github.com/xenolf/lego/challenge/dns01/dns_challenge.go index c9ef2ee3..ace01378 100644 --- a/vendor/github.com/xenolf/lego/challenge/dns01/dns_challenge.go +++ b/vendor/github.com/xenolf/lego/challenge/dns01/dns_challenge.go @@ -4,8 +4,11 @@ import ( "crypto/sha256" "encoding/base64" "fmt" + "os" + "strconv" "time" + "github.com/miekg/dns" "github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme/api" "github.com/xenolf/lego/challenge" @@ -124,7 +127,7 @@ func (c *Challenge) Solve(authz acme.Authorization) error { log.Infof("[%s] acme: Checking DNS record propagation using %+v", domain, recursiveNameservers) err = wait.For("propagation", timeout, interval, func() (bool, error) { - stop, errP := c.preCheck.call(fqdn, value) + stop, errP := c.preCheck.call(domain, fqdn, value) if !stop || errP != nil { log.Infof("[%s] acme: Waiting for DNS record propagation.", domain) } @@ -135,7 +138,7 @@ func (c *Challenge) Solve(authz acme.Authorization) error { } chlng.KeyAuthorization = keyAuth - return c.validate(c.core, authz.Identifier.Value, chlng) + return c.validate(c.core, domain, chlng) } // CleanUp cleans the challenge. @@ -172,5 +175,14 @@ func GetRecord(domain, keyAuth string) (fqdn string, value string) { // base64URL encoding without padding value = base64.RawURLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size]) fqdn = fmt.Sprintf("_acme-challenge.%s.", domain) + + if ok, _ := strconv.ParseBool(os.Getenv("LEGO_EXPERIMENTAL_CNAME_SUPPORT")); ok { + r, err := dnsQuery(fqdn, dns.TypeCNAME, recursiveNameservers, true) + // Check if the domain has CNAME then return that + if err == nil && r.Rcode == dns.RcodeSuccess { + fqdn = updateDomainWithCName(r, fqdn) + } + } + return } diff --git a/vendor/github.com/xenolf/lego/challenge/dns01/precheck.go b/vendor/github.com/xenolf/lego/challenge/dns01/precheck.go index 63b72cef..00e09854 100644 --- a/vendor/github.com/xenolf/lego/challenge/dns01/precheck.go +++ b/vendor/github.com/xenolf/lego/challenge/dns01/precheck.go @@ -1,6 +1,7 @@ package dns01 import ( + "errors" "fmt" "net" "strings" @@ -11,11 +12,30 @@ import ( // PreCheckFunc checks DNS propagation before notifying ACME that the DNS challenge is ready. type PreCheckFunc func(fqdn, value string) (bool, error) +// WrapPreCheckFunc wraps a PreCheckFunc in order to do extra operations before or after +// the main check, put it in a loop, etc. +type WrapPreCheckFunc func(domain, fqdn, value string, check PreCheckFunc) (bool, error) + +// WrapPreCheck Allow to define checks before notifying ACME that the DNS challenge is ready. +func WrapPreCheck(wrap WrapPreCheckFunc) ChallengeOption { + return func(chlg *Challenge) error { + chlg.preCheck.checkFunc = wrap + return nil + } +} + +// AddPreCheck Allow to define checks before notifying ACME that the DNS challenge is ready. +// Deprecated: use WrapPreCheck instead. func AddPreCheck(preCheck PreCheckFunc) ChallengeOption { // Prevent race condition check := preCheck return func(chlg *Challenge) error { - chlg.preCheck.checkFunc = check + chlg.preCheck.checkFunc = func(_, fqdn, value string, _ PreCheckFunc) (bool, error) { + if check == nil { + return false, errors.New("invalid preCheck: preCheck is nil") + } + return check(fqdn, value) + } return nil } } @@ -29,7 +49,7 @@ func DisableCompletePropagationRequirement() ChallengeOption { type preCheck struct { // checks DNS propagation before notifying ACME that the DNS challenge is ready. - checkFunc PreCheckFunc + checkFunc WrapPreCheckFunc // require the TXT record to be propagated to all authoritative name servers requireCompletePropagation bool } @@ -40,11 +60,12 @@ func newPreCheck() preCheck { } } -func (p preCheck) call(fqdn, value string) (bool, error) { +func (p preCheck) call(domain, fqdn, value string) (bool, error) { if p.checkFunc == nil { return p.checkDNSPropagation(fqdn, value) } - return p.checkFunc(fqdn, value) + + return p.checkFunc(domain, fqdn, value, p.checkDNSPropagation) } // checkDNSPropagation checks if the expected TXT record has been propagated to all authoritative nameservers. @@ -60,15 +81,7 @@ func (p preCheck) checkDNSPropagation(fqdn, value string) (bool, error) { } if r.Rcode == dns.RcodeSuccess { - // If we see a CNAME here then use the alias - for _, rr := range r.Answer { - if cn, ok := rr.(*dns.CNAME); ok { - if cn.Hdr.Name == fqdn { - fqdn = cn.Target - break - } - } - } + fqdn = updateDomainWithCName(r, fqdn) } authoritativeNss, err := lookupNameservers(fqdn) diff --git a/vendor/github.com/xenolf/lego/challenge/http01/http_challenge.go b/vendor/github.com/xenolf/lego/challenge/http01/http_challenge.go index 4176ecae..861bdbed 100644 --- a/vendor/github.com/xenolf/lego/challenge/http01/http_challenge.go +++ b/vendor/github.com/xenolf/lego/challenge/http01/http_challenge.go @@ -61,5 +61,5 @@ func (c *Challenge) Solve(authz acme.Authorization) error { }() chlng.KeyAuthorization = keyAuth - return c.validate(c.core, authz.Identifier.Value, chlng) + return c.validate(c.core, domain, chlng) } diff --git a/vendor/github.com/xenolf/lego/challenge/resolver/solver_manager.go b/vendor/github.com/xenolf/lego/challenge/resolver/solver_manager.go index 55faf77a..840e19d8 100644 --- a/vendor/github.com/xenolf/lego/challenge/resolver/solver_manager.go +++ b/vendor/github.com/xenolf/lego/challenge/resolver/solver_manager.go @@ -1,12 +1,14 @@ package resolver import ( + "context" "errors" "fmt" "sort" "strconv" "time" + "github.com/cenkalti/backoff" "github.com/xenolf/lego/acme" "github.com/xenolf/lego/acme/api" "github.com/xenolf/lego/challenge" @@ -90,16 +92,35 @@ func validate(core *api.Core, domain string, chlg acme.Challenge) error { return nil } + ra, err := strconv.Atoi(chlng.RetryAfter) + if err != nil { + // The ACME server MUST return a Retry-After. + // If it doesn't, we'll just poll hard. + // Boulder does not implement the ability to retry challenges or the Retry-After header. + // https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md#section-82 + ra = 5 + } + initialInterval := time.Duration(ra) * time.Second + + bo := backoff.NewExponentialBackOff() + bo.InitialInterval = initialInterval + bo.MaxInterval = 10 * initialInterval + bo.MaxElapsedTime = 100 * initialInterval + + ctx, cancel := context.WithCancel(context.Background()) + // After the path is sent, the ACME server will access our server. // Repeatedly check the server for an updated status on our request. - for { + operation := func() error { authz, err := core.Authorizations.Get(chlng.AuthorizationURL) if err != nil { + cancel() return err } valid, err := checkAuthorizationStatus(authz) if err != nil { + cancel() return err } @@ -108,16 +129,10 @@ func validate(core *api.Core, domain string, chlg acme.Challenge) error { return nil } - ra, err := strconv.Atoi(chlng.RetryAfter) - if err != nil { - // The ACME server MUST return a Retry-After. - // If it doesn't, we'll just poll hard. - // Boulder does not implement the ability to retry challenges or the Retry-After header. - // https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md#section-82 - ra = 5 - } - time.Sleep(time.Duration(ra) * time.Second) + return errors.New("the server didn't respond to our request") } + + return backoff.Retry(operation, backoff.WithContext(bo, ctx)) } func checkChallengeStatus(chlng acme.ExtendedChallenge) (bool, error) { diff --git a/vendor/manifest b/vendor/manifest index d00333f1..feb0466f 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -35,6 +35,14 @@ "notests": true }, { + "importpath": "github.com/cenkalti/backoff", + "repository": "https://github.com/cenkalti/backoff", + "vcs": "git", + "revision": "1e4cf3da559842a91afcb6ea6141451e6c30c618", + "branch": "master", + "notests": true + }, + { "importpath": "github.com/codahale/aesnicheck", "repository": "https://github.com/codahale/aesnicheck", "vcs": "git", @@ -178,7 +186,7 @@ "importpath": "github.com/xenolf/lego/acme", "repository": "https://github.com/xenolf/lego", "vcs": "git", - "revision": "a43ec709e8034f388aab28d14b97aeed0e7aa98c", + "revision": "67b329e3e370f481da545a4c95577f60c9be08b6", "branch": "master", "path": "acme", "notests": true @@ -187,7 +195,7 @@ "importpath": "github.com/xenolf/lego/certcrypto", "repository": "https://github.com/xenolf/lego", "vcs": "git", - "revision": "a43ec709e8034f388aab28d14b97aeed0e7aa98c", + "revision": "67b329e3e370f481da545a4c95577f60c9be08b6", "branch": "master", "path": "certcrypto", "notests": true @@ -196,7 +204,7 @@ "importpath": "github.com/xenolf/lego/certificate", "repository": "https://github.com/xenolf/lego", "vcs": "git", - "revision": "a43ec709e8034f388aab28d14b97aeed0e7aa98c", + "revision": "67b329e3e370f481da545a4c95577f60c9be08b6", "branch": "master", "path": "certificate", "notests": true @@ -205,7 +213,7 @@ "importpath": "github.com/xenolf/lego/challenge", "repository": "https://github.com/xenolf/lego", "vcs": "git", - "revision": "a43ec709e8034f388aab28d14b97aeed0e7aa98c", + "revision": "67b329e3e370f481da545a4c95577f60c9be08b6", "branch": "master", "path": "/challenge", "notests": true @@ -214,7 +222,7 @@ "importpath": "github.com/xenolf/lego/lego", "repository": "https://github.com/xenolf/lego", "vcs": "git", - "revision": "f05aa4c241fd8b43da9dc8cab8c8965e5b3c1b55", + "revision": "67b329e3e370f481da545a4c95577f60c9be08b6", "branch": "master", "path": "/lego", "notests": true @@ -241,7 +249,7 @@ "importpath": "github.com/xenolf/lego/registration", "repository": "https://github.com/xenolf/lego", "vcs": "git", - "revision": "a43ec709e8034f388aab28d14b97aeed0e7aa98c", + "revision": "67b329e3e370f481da545a4c95577f60c9be08b6", "branch": "master", "path": "registration", "notests": true |