forked from gitea/gitea
Upgrade certmagic from v0.14.1 to v0.15.2 (#18138)
This commit is contained in:
parent
385dc6a992
commit
e9c9a35a61
|
@ -9,6 +9,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler)
|
||||||
magic.Issuers = []certmagic.Issuer{myACME}
|
magic.Issuers = []certmagic.Issuer{myACME}
|
||||||
|
|
||||||
// this obtains certificates or renews them if necessary
|
// this obtains certificates or renews them if necessary
|
||||||
err := magic.ManageSync([]string{domain})
|
err := magic.ManageSync(graceful.GetManager().HammerContext(), []string{domain})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
3
go.mod
3
go.mod
|
@ -22,7 +22,7 @@ require (
|
||||||
github.com/blevesearch/bleve/v2 v2.3.0
|
github.com/blevesearch/bleve/v2 v2.3.0
|
||||||
github.com/boombuler/barcode v1.0.1 // indirect
|
github.com/boombuler/barcode v1.0.1 // indirect
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
|
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
|
||||||
github.com/caddyserver/certmagic v0.14.1
|
github.com/caddyserver/certmagic v0.15.2
|
||||||
github.com/chi-middleware/proxy v1.1.1
|
github.com/chi-middleware/proxy v1.1.1
|
||||||
github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect
|
github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect
|
||||||
github.com/couchbase/gomemcached v0.1.2 // indirect
|
github.com/couchbase/gomemcached v0.1.2 // indirect
|
||||||
|
@ -78,7 +78,6 @@ require (
|
||||||
github.com/mattn/go-sqlite3 v1.14.8
|
github.com/mattn/go-sqlite3 v1.14.8
|
||||||
github.com/mholt/archiver/v3 v3.5.0
|
github.com/mholt/archiver/v3 v3.5.0
|
||||||
github.com/microcosm-cc/bluemonday v1.0.16
|
github.com/microcosm-cc/bluemonday v1.0.16
|
||||||
github.com/miekg/dns v1.1.43 // indirect
|
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
github.com/minio/minio-go/v7 v7.0.12
|
github.com/minio/minio-go/v7 v7.0.12
|
||||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||||
|
|
10
go.sum
10
go.sum
|
@ -193,8 +193,8 @@ github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
|
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||||
github.com/caddyserver/certmagic v0.14.1 h1:8RIFS/LbGne/I7Op56Kkm2annnei7io9VW/IWDttE9U=
|
github.com/caddyserver/certmagic v0.15.2 h1:OMTakTsLM1ZfzMDjwvYprfUgFzpVPh3u87oxMPwmeBc=
|
||||||
github.com/caddyserver/certmagic v0.14.1/go.mod h1:oRQOZmUVKwlpgNidslysHt05osM9uMrJ4YMk+Ot4P4Q=
|
github.com/caddyserver/certmagic v0.15.2/go.mod h1:qhkAOthf72ufAcp3Y5jF2RaGE96oip3UbEQRIzwe3/8=
|
||||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
@ -767,7 +767,6 @@ github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s
|
||||||
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
|
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||||
|
@ -848,14 +847,13 @@ github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxz
|
||||||
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/mholt/acmez v0.1.3 h1:J7MmNIk4Qf9b8mAGqAh4XkNeowv3f1zW816yf4zt7Qk=
|
github.com/mholt/acmez v1.0.1 h1:J7uquHOKEmo71UDnVApy1sSLA0oF/r+NtVrNzMKKA9I=
|
||||||
github.com/mholt/acmez v0.1.3/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
|
github.com/mholt/acmez v1.0.1/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
|
||||||
github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE=
|
github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE=
|
||||||
github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc=
|
github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXdQbCmeMxwc=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.16 h1:kHmAq2t7WPWLjiGvzKa5o3HzSfahUKiOq7fAPUiMNIc=
|
github.com/microcosm-cc/bluemonday v1.0.16 h1:kHmAq2t7WPWLjiGvzKa5o3HzSfahUKiOq7fAPUiMNIc=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.16/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM=
|
github.com/microcosm-cc/bluemonday v1.0.16/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
|
||||||
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||||
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
|
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
Caddy's automagic TLS features—now for your own Go programs—in one powerful and easy-to-use library!
|
Caddy's [automagic TLS features](https://caddyserver.com/docs/automatic-https)—now for your own Go programs—in one powerful and easy-to-use library!
|
||||||
|
|
||||||
CertMagic is the most mature, robust, and capable ACME client integration for Go... and perhaps ever.
|
CertMagic is the most mature, robust, and powerful ACME client integration for Go... and perhaps ever.
|
||||||
|
|
||||||
With CertMagic, you can add one line to your Go application to serve securely over TLS, without ever having to touch certificates.
|
With CertMagic, you can add one line to your Go application to serve securely over TLS, without ever having to touch certificates.
|
||||||
|
|
||||||
|
@ -40,11 +40,6 @@ Compared to other ACME client libraries for Go, only CertMagic supports the full
|
||||||
CertMagic - Automatic HTTPS using Let's Encrypt
|
CertMagic - Automatic HTTPS using Let's Encrypt
|
||||||
===============================================
|
===============================================
|
||||||
|
|
||||||
**Sponsored by Relica - Cross-platform local and cloud file backup:**
|
|
||||||
|
|
||||||
<a href="https://relicabackup.com"><img src="https://caddyserver.com/resources/images/sponsors/relica.png" width="220" alt="Relica - Cross-platform file backup to the cloud, local disks, or other computers"></a>
|
|
||||||
|
|
||||||
|
|
||||||
## Menu
|
## Menu
|
||||||
|
|
||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
|
@ -116,6 +111,7 @@ CertMagic - Automatic HTTPS using Let's Encrypt
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
|
0. ACME server (can be a publicly-trusted CA, or your own)
|
||||||
1. Public DNS name(s) you control
|
1. Public DNS name(s) you control
|
||||||
2. Server reachable from public Internet
|
2. Server reachable from public Internet
|
||||||
- Or use the DNS challenge to waive this requirement
|
- Or use the DNS challenge to waive this requirement
|
||||||
|
@ -270,7 +266,7 @@ myACME := certmagic.NewACMEManager(magic, certmagic.ACMEManager{
|
||||||
magic.Issuer = myACME
|
magic.Issuer = myACME
|
||||||
|
|
||||||
// this obtains certificates or renews them if necessary
|
// this obtains certificates or renews them if necessary
|
||||||
err := magic.ManageSync([]string{"example.com", "sub.example.com"})
|
err := magic.ManageSync(context.TODO(), []string{"example.com", "sub.example.com"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -279,6 +275,10 @@ if err != nil {
|
||||||
// you can get a TLS config to use in a TLS listener!
|
// you can get a TLS config to use in a TLS listener!
|
||||||
tlsConfig := magic.TLSConfig()
|
tlsConfig := magic.TLSConfig()
|
||||||
|
|
||||||
|
// be sure to customize NextProtos if serving a specific
|
||||||
|
// application protocol after the TLS handshake, for example:
|
||||||
|
tlsConfig.NextProtos = append([]string{"h2", "http/1.1"}, tlsConfig.NextProtos...)
|
||||||
|
|
||||||
//// OR ////
|
//// OR ////
|
||||||
|
|
||||||
// if you already have a TLS config you don't want to replace,
|
// if you already have a TLS config you don't want to replace,
|
||||||
|
|
|
@ -405,10 +405,6 @@ var (
|
||||||
discoveredEmailMu sync.Mutex
|
discoveredEmailMu sync.Mutex
|
||||||
)
|
)
|
||||||
|
|
||||||
// agreementTestURL is set during tests to skip requiring
|
|
||||||
// setting up an entire ACME CA endpoint.
|
|
||||||
var agreementTestURL string
|
|
||||||
|
|
||||||
// stdin is used to read the user's input if prompted;
|
// stdin is used to read the user's input if prompted;
|
||||||
// this is changed by tests during tests.
|
// this is changed by tests during tests.
|
||||||
var stdin = io.ReadWriter(os.Stdin)
|
var stdin = io.ReadWriter(os.Stdin)
|
||||||
|
|
|
@ -370,11 +370,11 @@ var (
|
||||||
|
|
||||||
// RateLimitEvents is how many new events can be allowed
|
// RateLimitEvents is how many new events can be allowed
|
||||||
// in RateLimitEventsWindow.
|
// in RateLimitEventsWindow.
|
||||||
RateLimitEvents = 20
|
RateLimitEvents = 10
|
||||||
|
|
||||||
// RateLimitEventsWindow is the size of the sliding
|
// RateLimitEventsWindow is the size of the sliding
|
||||||
// window that throttles events.
|
// window that throttles events.
|
||||||
RateLimitEventsWindow = 1 * time.Minute
|
RateLimitEventsWindow = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
// Some default values passed down to the underlying ACME client.
|
// Some default values passed down to the underlying ACME client.
|
||||||
|
|
|
@ -194,6 +194,14 @@ func (certCache *Cache) cacheCertificate(cert Certificate) {
|
||||||
func (certCache *Cache) unsyncedCacheCertificate(cert Certificate) {
|
func (certCache *Cache) unsyncedCacheCertificate(cert Certificate) {
|
||||||
// no-op if this certificate already exists in the cache
|
// no-op if this certificate already exists in the cache
|
||||||
if _, ok := certCache.cache[cert.hash]; ok {
|
if _, ok := certCache.cache[cert.hash]; ok {
|
||||||
|
if certCache.logger != nil {
|
||||||
|
certCache.logger.Debug("certificate already cached",
|
||||||
|
zap.Strings("subjects", cert.Names),
|
||||||
|
zap.Time("expiration", cert.Leaf.NotAfter),
|
||||||
|
zap.Bool("managed", cert.managed),
|
||||||
|
zap.String("issuer_key", cert.issuerKey),
|
||||||
|
zap.String("hash", cert.hash))
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,6 +217,13 @@ func (certCache *Cache) unsyncedCacheCertificate(cert Certificate) {
|
||||||
i := 0
|
i := 0
|
||||||
for _, randomCert := range certCache.cache {
|
for _, randomCert := range certCache.cache {
|
||||||
if i == rnd {
|
if i == rnd {
|
||||||
|
if certCache.logger != nil {
|
||||||
|
certCache.logger.Debug("cache full; evicting random certificate",
|
||||||
|
zap.Strings("removing_subjects", randomCert.Names),
|
||||||
|
zap.String("removing_hash", randomCert.hash),
|
||||||
|
zap.Strings("inserting_subjects", cert.Names),
|
||||||
|
zap.String("inserting_hash", cert.hash))
|
||||||
|
}
|
||||||
certCache.removeCertificate(randomCert)
|
certCache.removeCertificate(randomCert)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -223,6 +238,17 @@ func (certCache *Cache) unsyncedCacheCertificate(cert Certificate) {
|
||||||
for _, name := range cert.Names {
|
for _, name := range cert.Names {
|
||||||
certCache.cacheIndex[name] = append(certCache.cacheIndex[name], cert.hash)
|
certCache.cacheIndex[name] = append(certCache.cacheIndex[name], cert.hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if certCache.logger != nil {
|
||||||
|
certCache.logger.Debug("added certificate to cache",
|
||||||
|
zap.Strings("subjects", cert.Names),
|
||||||
|
zap.Time("expiration", cert.Leaf.NotAfter),
|
||||||
|
zap.Bool("managed", cert.managed),
|
||||||
|
zap.String("issuer_key", cert.issuerKey),
|
||||||
|
zap.String("hash", cert.hash),
|
||||||
|
zap.Int("cache_size", len(certCache.cache)),
|
||||||
|
zap.Int("cache_capacity", certCache.options.Capacity))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeCertificate removes cert from the cache.
|
// removeCertificate removes cert from the cache.
|
||||||
|
@ -233,9 +259,10 @@ func (certCache *Cache) removeCertificate(cert Certificate) {
|
||||||
// delete all mentions of this cert from the name index
|
// delete all mentions of this cert from the name index
|
||||||
for _, name := range cert.Names {
|
for _, name := range cert.Names {
|
||||||
keyList := certCache.cacheIndex[name]
|
keyList := certCache.cacheIndex[name]
|
||||||
for i, cacheKey := range keyList {
|
for i := 0; i < len(keyList); i++ {
|
||||||
if cacheKey == cert.hash {
|
if keyList[i] == cert.hash {
|
||||||
keyList = append(keyList[:i], keyList[i+1:]...)
|
keyList = append(keyList[:i], keyList[i+1:]...)
|
||||||
|
i--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(keyList) == 0 {
|
if len(keyList) == 0 {
|
||||||
|
@ -247,6 +274,17 @@ func (certCache *Cache) removeCertificate(cert Certificate) {
|
||||||
|
|
||||||
// delete the actual cert from the cache
|
// delete the actual cert from the cache
|
||||||
delete(certCache.cache, cert.hash)
|
delete(certCache.cache, cert.hash)
|
||||||
|
|
||||||
|
if certCache.logger != nil {
|
||||||
|
certCache.logger.Debug("removed certificate from cache",
|
||||||
|
zap.Strings("subjects", cert.Names),
|
||||||
|
zap.Time("expiration", cert.Leaf.NotAfter),
|
||||||
|
zap.Bool("managed", cert.managed),
|
||||||
|
zap.String("issuer_key", cert.issuerKey),
|
||||||
|
zap.String("hash", cert.hash),
|
||||||
|
zap.Int("cache_size", len(certCache.cache)),
|
||||||
|
zap.Int("cache_capacity", certCache.options.Capacity))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// replaceCertificate atomically replaces oldCert with newCert in
|
// replaceCertificate atomically replaces oldCert with newCert in
|
||||||
|
@ -260,7 +298,7 @@ func (certCache *Cache) replaceCertificate(oldCert, newCert Certificate) {
|
||||||
certCache.mu.Unlock()
|
certCache.mu.Unlock()
|
||||||
if certCache.logger != nil {
|
if certCache.logger != nil {
|
||||||
certCache.logger.Info("replaced certificate in cache",
|
certCache.logger.Info("replaced certificate in cache",
|
||||||
zap.Strings("identifiers", newCert.Names),
|
zap.Strings("subjects", newCert.Names),
|
||||||
zap.Time("new_expiration", newCert.Leaf.NotAfter))
|
zap.Time("new_expiration", newCert.Leaf.NotAfter))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -283,8 +283,6 @@ func fillCertFromLeaf(cert *Certificate, tlsCert tls.Certificate) error {
|
||||||
return fmt.Errorf("certificate has no names")
|
return fmt.Errorf("certificate has no names")
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the hash of this certificate (chain) and
|
|
||||||
// expiration date, for necessity and efficiency
|
|
||||||
cert.hash = hashCertificateChain(cert.Certificate.Certificate)
|
cert.hash = hashCertificateChain(cert.Certificate.Certificate)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -73,7 +73,7 @@ func HTTPS(domainNames []string, mux http.Handler) error {
|
||||||
DefaultACME.Agreed = true
|
DefaultACME.Agreed = true
|
||||||
cfg := NewDefault()
|
cfg := NewDefault()
|
||||||
|
|
||||||
err := cfg.ManageSync(domainNames)
|
err := cfg.ManageSync(context.Background(), domainNames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ func TLS(domainNames []string) (*tls.Config, error) {
|
||||||
DefaultACME.Agreed = true
|
DefaultACME.Agreed = true
|
||||||
DefaultACME.DisableHTTPChallenge = true
|
DefaultACME.DisableHTTPChallenge = true
|
||||||
cfg := NewDefault()
|
cfg := NewDefault()
|
||||||
return cfg.TLSConfig(), cfg.ManageSync(domainNames)
|
return cfg.TLSConfig(), cfg.ManageSync(context.Background(), domainNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen manages certificates for domainName and returns a
|
// Listen manages certificates for domainName and returns a
|
||||||
|
@ -195,7 +195,7 @@ func Listen(domainNames []string) (net.Listener, error) {
|
||||||
DefaultACME.Agreed = true
|
DefaultACME.Agreed = true
|
||||||
DefaultACME.DisableHTTPChallenge = true
|
DefaultACME.DisableHTTPChallenge = true
|
||||||
cfg := NewDefault()
|
cfg := NewDefault()
|
||||||
err := cfg.ManageSync(domainNames)
|
err := cfg.ManageSync(context.Background(), domainNames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -223,9 +223,9 @@ func Listen(domainNames []string) (net.Listener, error) {
|
||||||
//
|
//
|
||||||
// Calling this function signifies your acceptance to
|
// Calling this function signifies your acceptance to
|
||||||
// the CA's Subscriber Agreement and/or Terms of Service.
|
// the CA's Subscriber Agreement and/or Terms of Service.
|
||||||
func ManageSync(domainNames []string) error {
|
func ManageSync(ctx context.Context, domainNames []string) error {
|
||||||
DefaultACME.Agreed = true
|
DefaultACME.Agreed = true
|
||||||
return NewDefault().ManageSync(domainNames)
|
return NewDefault().ManageSync(ctx, domainNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManageAsync is the same as ManageSync, except that
|
// ManageAsync is the same as ManageSync, except that
|
||||||
|
|
|
@ -247,8 +247,28 @@ func newWithCache(certCache *Cache, cfg Config) *Config {
|
||||||
// of the given domainNames. This behavior is recommended for
|
// of the given domainNames. This behavior is recommended for
|
||||||
// interactive use (i.e. when an administrator is present) so
|
// interactive use (i.e. when an administrator is present) so
|
||||||
// that errors can be reported and fixed immediately.
|
// that errors can be reported and fixed immediately.
|
||||||
func (cfg *Config) ManageSync(domainNames []string) error {
|
func (cfg *Config) ManageSync(ctx context.Context, domainNames []string) error {
|
||||||
return cfg.manageAll(context.Background(), domainNames, false)
|
return cfg.manageAll(ctx, domainNames, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ManageAsync is the same as ManageSync, except that ACME
|
||||||
|
// operations are performed asynchronously (in the background).
|
||||||
|
// This method returns before certificates are ready. It is
|
||||||
|
// crucial that the administrator monitors the logs and is
|
||||||
|
// notified of any errors so that corrective action can be
|
||||||
|
// taken as soon as possible. Any errors returned from this
|
||||||
|
// method occurred before ACME transactions started.
|
||||||
|
//
|
||||||
|
// As long as logs are monitored, this method is typically
|
||||||
|
// recommended for non-interactive environments.
|
||||||
|
//
|
||||||
|
// If there are failures loading, obtaining, or renewing a
|
||||||
|
// certificate, it will be retried with exponential backoff
|
||||||
|
// for up to about 30 days, with a maximum interval of about
|
||||||
|
// 24 hours. Cancelling ctx will cancel retries and shut down
|
||||||
|
// any goroutines spawned by ManageAsync.
|
||||||
|
func (cfg *Config) ManageAsync(ctx context.Context, domainNames []string) error {
|
||||||
|
return cfg.manageAll(ctx, domainNames, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientCredentials returns a list of TLS client certificate chains for the given identifiers.
|
// ClientCredentials returns a list of TLS client certificate chains for the given identifiers.
|
||||||
|
@ -274,26 +294,6 @@ func (cfg *Config) ClientCredentials(ctx context.Context, identifiers []string)
|
||||||
return chains, nil
|
return chains, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManageAsync is the same as ManageSync, except that ACME
|
|
||||||
// operations are performed asynchronously (in the background).
|
|
||||||
// This method returns before certificates are ready. It is
|
|
||||||
// crucial that the administrator monitors the logs and is
|
|
||||||
// notified of any errors so that corrective action can be
|
|
||||||
// taken as soon as possible. Any errors returned from this
|
|
||||||
// method occurred before ACME transactions started.
|
|
||||||
//
|
|
||||||
// As long as logs are monitored, this method is typically
|
|
||||||
// recommended for non-interactive environments.
|
|
||||||
//
|
|
||||||
// If there are failures loading, obtaining, or renewing a
|
|
||||||
// certificate, it will be retried with exponential backoff
|
|
||||||
// for up to about 30 days, with a maximum interval of about
|
|
||||||
// 24 hours. Cancelling ctx will cancel retries and shut down
|
|
||||||
// any goroutines spawned by ManageAsync.
|
|
||||||
func (cfg *Config) ManageAsync(ctx context.Context, domainNames []string) error {
|
|
||||||
return cfg.manageAll(ctx, domainNames, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *Config) manageAll(ctx context.Context, domainNames []string, async bool) error {
|
func (cfg *Config) manageAll(ctx context.Context, domainNames []string, async bool) error {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
|
@ -863,20 +863,28 @@ func (cfg *Config) RevokeCert(ctx context.Context, domain string, reason int, in
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSConfig is an opinionated method that returns a
|
// TLSConfig is an opinionated method that returns a recommended, modern
|
||||||
// recommended, modern TLS configuration that can be
|
// TLS configuration that can be used to configure TLS listeners. Aside
|
||||||
// used to configure TLS listeners, which also supports
|
// from safe, modern defaults, this method sets two critical fields on the
|
||||||
// the TLS-ALPN challenge and serves up certificates
|
// TLS config which are required to enable automatic certificate
|
||||||
// managed by cfg.
|
// management: GetCertificate and NextProtos.
|
||||||
//
|
//
|
||||||
// Unlike the package TLS() function, this method does
|
// The GetCertificate field is necessary to get certificates from memory
|
||||||
// not, by itself, enable certificate management for
|
// or storage, including both manual and automated certificates. You
|
||||||
// any domain names.
|
// should only change this field if you know what you are doing.
|
||||||
//
|
//
|
||||||
// Feel free to further customize the returned tls.Config,
|
// The NextProtos field is pre-populated with a special value to enable
|
||||||
// but do not mess with the GetCertificate or NextProtos
|
// solving the TLS-ALPN ACME challenge. Because this method does not
|
||||||
// fields unless you know what you're doing, as they're
|
// assume any particular protocols after the TLS handshake is completed,
|
||||||
// necessary to solve the TLS-ALPN challenge.
|
// you will likely need to customize the NextProtos field by prepending
|
||||||
|
// your application's protocols to the slice. For example, to serve
|
||||||
|
// HTTP, you will need to prepend "h2" and "http/1.1" values. Be sure to
|
||||||
|
// leave the acmez.ACMETLS1Protocol value intact, however, or TLS-ALPN
|
||||||
|
// challenges will fail (which may be acceptable if you are not using
|
||||||
|
// ACME, or specifically, the TLS-ALPN challenge).
|
||||||
|
//
|
||||||
|
// Unlike the package TLS() function, this method does not, by itself,
|
||||||
|
// enable certificate management for any domain names.
|
||||||
func (cfg *Config) TLSConfig() *tls.Config {
|
func (cfg *Config) TLSConfig() *tls.Config {
|
||||||
return &tls.Config{
|
return &tls.Config{
|
||||||
// these two fields necessary for TLS-ALPN challenge
|
// these two fields necessary for TLS-ALPN challenge
|
||||||
|
|
|
@ -72,6 +72,10 @@ func encodePrivateKey(key crypto.PrivateKey) ([]byte, error) {
|
||||||
func decodePrivateKey(keyPEMBytes []byte) (crypto.Signer, error) {
|
func decodePrivateKey(keyPEMBytes []byte) (crypto.Signer, error) {
|
||||||
keyBlockDER, _ := pem.Decode(keyPEMBytes)
|
keyBlockDER, _ := pem.Decode(keyPEMBytes)
|
||||||
|
|
||||||
|
if keyBlockDER == nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode PEM block containing private key")
|
||||||
|
}
|
||||||
|
|
||||||
if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") {
|
if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") {
|
||||||
return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type)
|
return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type)
|
||||||
}
|
}
|
||||||
|
@ -142,14 +146,14 @@ func (cfg *Config) saveCertResource(issuer Issuer, cert CertificateResource) err
|
||||||
certKey := cert.NamesKey()
|
certKey := cert.NamesKey()
|
||||||
|
|
||||||
all := []keyValue{
|
all := []keyValue{
|
||||||
{
|
|
||||||
key: StorageKeys.SiteCert(issuerKey, certKey),
|
|
||||||
value: cert.CertificatePEM,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: StorageKeys.SitePrivateKey(issuerKey, certKey),
|
key: StorageKeys.SitePrivateKey(issuerKey, certKey),
|
||||||
value: cert.PrivateKeyPEM,
|
value: cert.PrivateKeyPEM,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: StorageKeys.SiteCert(issuerKey, certKey),
|
||||||
|
value: cert.CertificatePEM,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: StorageKeys.SiteMeta(issuerKey, certKey),
|
key: StorageKeys.SiteMeta(issuerKey, certKey),
|
||||||
value: metaBytes,
|
value: metaBytes,
|
||||||
|
|
|
@ -3,10 +3,10 @@ module github.com/caddyserver/certmagic
|
||||||
go 1.14
|
go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/cpuid/v2 v2.0.6
|
github.com/klauspost/cpuid/v2 v2.0.9
|
||||||
github.com/libdns/libdns v0.2.1
|
github.com/libdns/libdns v0.2.1
|
||||||
github.com/mholt/acmez v0.1.3
|
github.com/mholt/acmez v1.0.1
|
||||||
github.com/miekg/dns v1.1.42
|
github.com/miekg/dns v1.1.43
|
||||||
go.uber.org/zap v1.17.0
|
go.uber.org/zap v1.17.0
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5
|
||||||
|
|
|
@ -4,8 +4,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI=
|
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
@ -13,10 +13,10 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
||||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||||
github.com/mholt/acmez v0.1.3 h1:J7MmNIk4Qf9b8mAGqAh4XkNeowv3f1zW816yf4zt7Qk=
|
github.com/mholt/acmez v1.0.1 h1:J7uquHOKEmo71UDnVApy1sSLA0oF/r+NtVrNzMKKA9I=
|
||||||
github.com/mholt/acmez v0.1.3/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
|
github.com/mholt/acmez v1.0.1/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
|
||||||
github.com/miekg/dns v1.1.42 h1:gWGe42RGaIqXQZ+r3WUGEKBEtvPHY2SXo4dqixDNxuY=
|
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||||
github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|
|
@ -125,23 +125,6 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the certCache directly to see if the SNI name is
|
|
||||||
// already the key of the certificate it wants; this implies
|
|
||||||
// that the SNI can contain the hash of a specific cert
|
|
||||||
// (chain) it wants and we will still be able to serve it up
|
|
||||||
// (this behavior, by the way, could be controversial as to
|
|
||||||
// whether it complies with RFC 6066 about SNI, but I think
|
|
||||||
// it does, soooo...)
|
|
||||||
// (this is how we solved the former ACME TLS-SNI challenge)
|
|
||||||
cfg.certCache.mu.RLock()
|
|
||||||
directCert, ok := cfg.certCache.cache[name]
|
|
||||||
cfg.certCache.mu.RUnlock()
|
|
||||||
if ok {
|
|
||||||
cert = directCert
|
|
||||||
matched = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, we're bingo on ammo; see issues
|
// otherwise, we're bingo on ammo; see issues
|
||||||
|
@ -162,18 +145,48 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate,
|
||||||
// then all certificates in the cache will be passed in
|
// then all certificates in the cache will be passed in
|
||||||
// for the cfg.CertSelection to make the final decision.
|
// for the cfg.CertSelection to make the final decision.
|
||||||
func (cfg *Config) selectCert(hello *tls.ClientHelloInfo, name string) (Certificate, bool) {
|
func (cfg *Config) selectCert(hello *tls.ClientHelloInfo, name string) (Certificate, bool) {
|
||||||
|
logger := loggerNamed(cfg.Logger, "handshake")
|
||||||
choices := cfg.certCache.getAllMatchingCerts(name)
|
choices := cfg.certCache.getAllMatchingCerts(name)
|
||||||
if len(choices) == 0 {
|
if len(choices) == 0 {
|
||||||
if cfg.CertSelection == nil {
|
if cfg.CertSelection == nil {
|
||||||
|
if logger != nil {
|
||||||
|
logger.Debug("no matching certificates and no custom selection logic", zap.String("identifier", name))
|
||||||
|
}
|
||||||
return Certificate{}, false
|
return Certificate{}, false
|
||||||
}
|
}
|
||||||
|
if logger != nil {
|
||||||
|
logger.Debug("no matching certificate; will choose from all certificates", zap.String("identifier", name))
|
||||||
|
}
|
||||||
choices = cfg.certCache.getAllCerts()
|
choices = cfg.certCache.getAllCerts()
|
||||||
}
|
}
|
||||||
|
if logger != nil {
|
||||||
|
logger.Debug("choosing certificate",
|
||||||
|
zap.String("identifier", name),
|
||||||
|
zap.Int("num_choices", len(choices)))
|
||||||
|
}
|
||||||
if cfg.CertSelection == nil {
|
if cfg.CertSelection == nil {
|
||||||
cert, err := DefaultCertificateSelector(hello, choices)
|
cert, err := DefaultCertificateSelector(hello, choices)
|
||||||
|
if logger != nil {
|
||||||
|
logger.Debug("default certificate selection results",
|
||||||
|
zap.Error(err),
|
||||||
|
zap.String("identifier", name),
|
||||||
|
zap.Strings("subjects", cert.Names),
|
||||||
|
zap.Bool("managed", cert.managed),
|
||||||
|
zap.String("issuer_key", cert.issuerKey),
|
||||||
|
zap.String("hash", cert.hash))
|
||||||
|
}
|
||||||
return cert, err == nil
|
return cert, err == nil
|
||||||
}
|
}
|
||||||
cert, err := cfg.CertSelection.SelectCertificate(hello, choices)
|
cert, err := cfg.CertSelection.SelectCertificate(hello, choices)
|
||||||
|
if logger != nil {
|
||||||
|
logger.Debug("custom certificate selection results",
|
||||||
|
zap.Error(err),
|
||||||
|
zap.String("identifier", name),
|
||||||
|
zap.Strings("subjects", cert.Names),
|
||||||
|
zap.Bool("managed", cert.managed),
|
||||||
|
zap.String("issuer_key", cert.issuerKey),
|
||||||
|
zap.String("hash", cert.hash))
|
||||||
|
}
|
||||||
return cert, err == nil
|
return cert, err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,28 +226,54 @@ func DefaultCertificateSelector(hello *tls.ClientHelloInfo, choices []Certificat
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent use.
|
// This function is safe for concurrent use.
|
||||||
func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNecessary, obtainIfNecessary bool) (Certificate, error) {
|
func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNecessary, obtainIfNecessary bool) (Certificate, error) {
|
||||||
log := loggerNamed(cfg.Logger, "on_demand")
|
log := loggerNamed(cfg.Logger, "handshake")
|
||||||
|
|
||||||
// First check our in-memory cache to see if we've already loaded it
|
// First check our in-memory cache to see if we've already loaded it
|
||||||
cert, matched, defaulted := cfg.getCertificate(hello)
|
cert, matched, defaulted := cfg.getCertificate(hello)
|
||||||
if matched {
|
if matched {
|
||||||
|
if log != nil {
|
||||||
|
log.Debug("matched certificate in cache",
|
||||||
|
zap.Strings("subjects", cert.Names),
|
||||||
|
zap.Bool("managed", cert.managed),
|
||||||
|
zap.Time("expiration", cert.Leaf.NotAfter),
|
||||||
|
zap.String("hash", cert.hash))
|
||||||
|
}
|
||||||
if cert.managed && cfg.OnDemand != nil && obtainIfNecessary {
|
if cert.managed && cfg.OnDemand != nil && obtainIfNecessary {
|
||||||
// It's been reported before that if the machine goes to sleep (or
|
// It's been reported before that if the machine goes to sleep (or
|
||||||
// suspends the process) that certs which are already loaded into
|
// suspends the process) that certs which are already loaded into
|
||||||
// memory won't get renewed in the background, so we need to check
|
// memory won't get renewed in the background, so we need to check
|
||||||
// expiry on each handshake too, sigh:
|
// expiry on each handshake too, sigh:
|
||||||
// https://caddy.community/t/local-certificates-not-renewing-on-demand/9482
|
// https://caddy.community/t/local-certificates-not-renewing-on-demand/9482
|
||||||
return cfg.optionalMaintenance(log, cert, hello)
|
return cfg.optionalMaintenance(loggerNamed(cfg.Logger, "on_demand"), cert, hello)
|
||||||
}
|
}
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
name := cfg.getNameFromClientHello(hello)
|
name := cfg.getNameFromClientHello(hello)
|
||||||
|
|
||||||
// If OnDemand is enabled, then we might be able to load or
|
// We might be able to load or obtain a needed certificate. Load from
|
||||||
// obtain a needed certificate
|
// storage if OnDemand is enabled, or if there is the possibility that
|
||||||
if cfg.OnDemand != nil && loadIfNecessary {
|
// a statically-managed cert was evicted from a full cache.
|
||||||
|
cfg.certCache.mu.RLock()
|
||||||
|
cacheSize := len(cfg.certCache.cache)
|
||||||
|
cfg.certCache.mu.RUnlock()
|
||||||
|
|
||||||
|
// A cert might have still been evicted from the cache even if the cache
|
||||||
|
// is no longer completely full; this happens if the newly-loaded cert is
|
||||||
|
// itself evicted (perhaps due to being expired or unmanaged at this point).
|
||||||
|
// Hence, we use an "almost full" metric to allow for the cache to not be
|
||||||
|
// perfectly full while still being able to load needed certs from storage.
|
||||||
|
// See https://caddy.community/t/error-tls-alert-internal-error-592-again/13272
|
||||||
|
// and caddyserver/caddy#4320.
|
||||||
|
cacheAlmostFull := float64(cacheSize) >= (float64(cfg.certCache.options.Capacity) * .9)
|
||||||
|
loadDynamically := cfg.OnDemand != nil || cacheAlmostFull
|
||||||
|
|
||||||
|
if loadDynamically && loadIfNecessary {
|
||||||
// Then check to see if we have one on disk
|
// Then check to see if we have one on disk
|
||||||
|
// TODO: As suggested here, https://caddy.community/t/error-tls-alert-internal-error-592-again/13272/30?u=matt,
|
||||||
|
// it might be a good idea to check with the DecisionFunc or allowlist first before even loading the certificate
|
||||||
|
// from storage, since if we can't renew it, why should we even try serving it (it will just get evicted after
|
||||||
|
// we get a return value of false anyway)?
|
||||||
loadedCert, err := cfg.CacheManagedCertificate(name)
|
loadedCert, err := cfg.CacheManagedCertificate(name)
|
||||||
if _, ok := err.(ErrNotExist); ok {
|
if _, ok := err.(ErrNotExist); ok {
|
||||||
// If no exact match, try a wildcard variant, which is something we can still use
|
// If no exact match, try a wildcard variant, which is something we can still use
|
||||||
|
@ -243,6 +282,13 @@ func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNece
|
||||||
loadedCert, err = cfg.CacheManagedCertificate(strings.Join(labels, "."))
|
loadedCert, err = cfg.CacheManagedCertificate(strings.Join(labels, "."))
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
if log != nil {
|
||||||
|
log.Debug("loaded certificate from storage",
|
||||||
|
zap.Strings("subjects", loadedCert.Names),
|
||||||
|
zap.Bool("managed", loadedCert.managed),
|
||||||
|
zap.Time("expiration", loadedCert.Leaf.NotAfter),
|
||||||
|
zap.String("hash", loadedCert.hash))
|
||||||
|
}
|
||||||
loadedCert, err = cfg.handshakeMaintenance(hello, loadedCert)
|
loadedCert, err = cfg.handshakeMaintenance(hello, loadedCert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if log != nil {
|
if log != nil {
|
||||||
|
@ -253,7 +299,7 @@ func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNece
|
||||||
}
|
}
|
||||||
return loadedCert, nil
|
return loadedCert, nil
|
||||||
}
|
}
|
||||||
if obtainIfNecessary {
|
if cfg.OnDemand != nil && obtainIfNecessary {
|
||||||
// By this point, we need to ask the CA for a certificate
|
// By this point, we need to ask the CA for a certificate
|
||||||
return cfg.obtainOnDemandCertificate(hello)
|
return cfg.obtainOnDemandCertificate(hello)
|
||||||
}
|
}
|
||||||
|
@ -261,9 +307,28 @@ func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNece
|
||||||
|
|
||||||
// Fall back to the default certificate if there is one
|
// Fall back to the default certificate if there is one
|
||||||
if defaulted {
|
if defaulted {
|
||||||
|
if log != nil {
|
||||||
|
log.Debug("fell back to default certificate",
|
||||||
|
zap.Strings("subjects", cert.Names),
|
||||||
|
zap.Bool("managed", cert.managed),
|
||||||
|
zap.Time("expiration", cert.Leaf.NotAfter),
|
||||||
|
zap.String("hash", cert.hash))
|
||||||
|
}
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if log != nil {
|
||||||
|
log.Debug("no certificate matching TLS ClientHello",
|
||||||
|
zap.String("server_name", hello.ServerName),
|
||||||
|
zap.String("remote", hello.Conn.RemoteAddr().String()),
|
||||||
|
zap.String("identifier", name),
|
||||||
|
zap.Uint16s("cipher_suites", hello.CipherSuites),
|
||||||
|
zap.Float64("cert_cache_fill", float64(cacheSize)/float64(cfg.certCache.options.Capacity)), // may be approximate! because we are not within the lock
|
||||||
|
zap.Bool("load_if_necessary", loadIfNecessary),
|
||||||
|
zap.Bool("obtain_if_necessary", obtainIfNecessary),
|
||||||
|
zap.Bool("on_demand", cfg.OnDemand != nil))
|
||||||
|
}
|
||||||
|
|
||||||
return Certificate{}, fmt.Errorf("no certificate available for '%s'", name)
|
return Certificate{}, fmt.Errorf("no certificate available for '%s'", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,7 +436,8 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use a proper context; we use one with timeout because retries are enabled because interactive is false
|
// TODO: use a proper context; we use one with timeout because retries are enabled because interactive is false
|
||||||
ctx, cancel := context.WithTimeout(context.TODO(), 90*time.Second)
|
// (timeout duration is based on https://caddy.community/t/zerossl-dns-challenge-failing-often-route53-plugin/13822/24?u=matt)
|
||||||
|
ctx, cancel := context.WithTimeout(context.TODO(), 180*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// Obtain the certificate
|
// Obtain the certificate
|
||||||
|
@ -459,7 +525,7 @@ func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCe
|
||||||
// renewing it, so we might as well serve what we have without blocking
|
// renewing it, so we might as well serve what we have without blocking
|
||||||
if log != nil {
|
if log != nil {
|
||||||
log.Debug("certificate expires soon but is already being renewed; serving current certificate",
|
log.Debug("certificate expires soon but is already being renewed; serving current certificate",
|
||||||
zap.Strings("identifiers", currentCert.Names),
|
zap.Strings("subjects", currentCert.Names),
|
||||||
zap.Duration("remaining", timeLeft))
|
zap.Duration("remaining", timeLeft))
|
||||||
}
|
}
|
||||||
return currentCert, nil
|
return currentCert, nil
|
||||||
|
@ -470,7 +536,7 @@ func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCe
|
||||||
|
|
||||||
if log != nil {
|
if log != nil {
|
||||||
log.Debug("certificate has expired, but is already being renewed; waiting for renewal to complete",
|
log.Debug("certificate has expired, but is already being renewed; waiting for renewal to complete",
|
||||||
zap.Strings("identifiers", currentCert.Names),
|
zap.Strings("subjects", currentCert.Names),
|
||||||
zap.Time("expired", currentCert.Leaf.NotAfter))
|
zap.Time("expired", currentCert.Leaf.NotAfter))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,7 +567,7 @@ func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCe
|
||||||
if log != nil {
|
if log != nil {
|
||||||
log.Info("attempting certificate renewal",
|
log.Info("attempting certificate renewal",
|
||||||
zap.String("server_name", name),
|
zap.String("server_name", name),
|
||||||
zap.Strings("identifiers", currentCert.Names),
|
zap.Strings("subjects", currentCert.Names),
|
||||||
zap.Time("expiration", currentCert.Leaf.NotAfter),
|
zap.Time("expiration", currentCert.Leaf.NotAfter),
|
||||||
zap.Duration("remaining", timeLeft))
|
zap.Duration("remaining", timeLeft))
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/libdns/libdns"
|
"github.com/libdns/libdns"
|
||||||
"github.com/mholt/acmez"
|
"github.com/mholt/acmez"
|
||||||
"github.com/mholt/acmez/acme"
|
"github.com/mholt/acmez/acme"
|
||||||
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// httpSolver solves the HTTP challenge. It must be
|
// httpSolver solves the HTTP challenge. It must be
|
||||||
|
@ -131,10 +132,12 @@ func (s *tlsALPNSolver) Present(ctx context.Context, chal acme.Challenge) error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
key := challengeKey(chal)
|
||||||
activeChallengesMu.Lock()
|
activeChallengesMu.Lock()
|
||||||
chalData := activeChallenges[chal.Identifier.Value]
|
chalData := activeChallenges[key]
|
||||||
chalData.data = cert
|
chalData.data = cert
|
||||||
activeChallenges[chal.Identifier.Value] = chalData
|
activeChallenges[key] = chalData
|
||||||
activeChallengesMu.Unlock()
|
activeChallengesMu.Unlock()
|
||||||
|
|
||||||
// the rest of this function increments the
|
// the rest of this function increments the
|
||||||
|
@ -215,10 +218,6 @@ func (*tlsALPNSolver) handleConn(conn net.Conn) {
|
||||||
// CleanUp removes the challenge certificate from the cache, and if
|
// CleanUp removes the challenge certificate from the cache, and if
|
||||||
// it is the last one to finish, stops the TLS server.
|
// it is the last one to finish, stops the TLS server.
|
||||||
func (s *tlsALPNSolver) CleanUp(ctx context.Context, chal acme.Challenge) error {
|
func (s *tlsALPNSolver) CleanUp(ctx context.Context, chal acme.Challenge) error {
|
||||||
s.config.certCache.mu.Lock()
|
|
||||||
delete(s.config.certCache.cache, tlsALPNCertKeyName(chal.Identifier.Value))
|
|
||||||
s.config.certCache.mu.Unlock()
|
|
||||||
|
|
||||||
solversMu.Lock()
|
solversMu.Lock()
|
||||||
defer solversMu.Unlock()
|
defer solversMu.Unlock()
|
||||||
si := getSolverInfo(s.address)
|
si := getSolverInfo(s.address)
|
||||||
|
@ -236,14 +235,6 @@ func (s *tlsALPNSolver) CleanUp(ctx context.Context, chal acme.Challenge) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// tlsALPNCertKeyName returns the key to use when caching a cert
|
|
||||||
// for use with the TLS-ALPN ACME challenge. It is simply to help
|
|
||||||
// avoid conflicts (although at time of writing, there shouldn't
|
|
||||||
// be, since the cert cache is keyed by hash of certificate chain).
|
|
||||||
func tlsALPNCertKeyName(sniName string) string {
|
|
||||||
return sniName + ":acme-tls-alpn"
|
|
||||||
}
|
|
||||||
|
|
||||||
// DNS01Solver is a type that makes libdns providers usable
|
// DNS01Solver is a type that makes libdns providers usable
|
||||||
// as ACME dns-01 challenge solvers.
|
// as ACME dns-01 challenge solvers.
|
||||||
// See https://github.com/libdns/libdns
|
// See https://github.com/libdns/libdns
|
||||||
|
@ -478,7 +469,7 @@ func (dhs distributedSolver) Present(ctx context.Context, chal acme.Challenge) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = dhs.storage.Store(dhs.challengeTokensKey(chal.Identifier.Value), infoBytes)
|
err = dhs.storage.Store(dhs.challengeTokensKey(challengeKey(chal)), infoBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -501,7 +492,7 @@ func (dhs distributedSolver) Wait(ctx context.Context, challenge acme.Challenge)
|
||||||
// CleanUp invokes the underlying solver's CleanUp method
|
// CleanUp invokes the underlying solver's CleanUp method
|
||||||
// and also cleans up any assets saved to storage.
|
// and also cleans up any assets saved to storage.
|
||||||
func (dhs distributedSolver) CleanUp(ctx context.Context, chal acme.Challenge) error {
|
func (dhs distributedSolver) CleanUp(ctx context.Context, chal acme.Challenge) error {
|
||||||
err := dhs.storage.Delete(dhs.challengeTokensKey(chal.Identifier.Value))
|
err := dhs.storage.Delete(dhs.challengeTokensKey(challengeKey(chal)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -648,6 +639,18 @@ type Challenge struct {
|
||||||
data interface{}
|
data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// challengeKey returns the map key for a given challenge; it is the identifier
|
||||||
|
// unless it is an IP address using the TLS-ALPN challenge.
|
||||||
|
func challengeKey(chal acme.Challenge) string {
|
||||||
|
if chal.Type == acme.ChallengeTypeTLSALPN01 && chal.Identifier.Type == "ip" {
|
||||||
|
reversed, err := dns.ReverseAddr(chal.Identifier.Value)
|
||||||
|
if err == nil {
|
||||||
|
return reversed[:len(reversed)-1] // strip off '.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chal.Identifier.Value
|
||||||
|
}
|
||||||
|
|
||||||
// solverWrapper should be used to wrap all challenge solvers so that
|
// solverWrapper should be used to wrap all challenge solvers so that
|
||||||
// we can add the challenge info to memory; this makes challenges globally
|
// 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
|
// solvable by a single HTTP or TLS server even if multiple servers with
|
||||||
|
@ -656,7 +659,7 @@ type solverWrapper struct{ acmez.Solver }
|
||||||
|
|
||||||
func (sw solverWrapper) Present(ctx context.Context, chal acme.Challenge) error {
|
func (sw solverWrapper) Present(ctx context.Context, chal acme.Challenge) error {
|
||||||
activeChallengesMu.Lock()
|
activeChallengesMu.Lock()
|
||||||
activeChallenges[chal.Identifier.Value] = Challenge{Challenge: chal}
|
activeChallenges[challengeKey(chal)] = Challenge{Challenge: chal}
|
||||||
activeChallengesMu.Unlock()
|
activeChallengesMu.Unlock()
|
||||||
return sw.Solver.Present(ctx, chal)
|
return sw.Solver.Present(ctx, chal)
|
||||||
}
|
}
|
||||||
|
@ -670,7 +673,7 @@ func (sw solverWrapper) Wait(ctx context.Context, chal acme.Challenge) error {
|
||||||
|
|
||||||
func (sw solverWrapper) CleanUp(ctx context.Context, chal acme.Challenge) error {
|
func (sw solverWrapper) CleanUp(ctx context.Context, chal acme.Challenge) error {
|
||||||
activeChallengesMu.Lock()
|
activeChallengesMu.Lock()
|
||||||
delete(activeChallenges, chal.Identifier.Value)
|
delete(activeChallenges, challengeKey(chal))
|
||||||
activeChallengesMu.Unlock()
|
activeChallengesMu.Unlock()
|
||||||
return sw.Solver.CleanUp(ctx, chal)
|
return sw.Solver.CleanUp(ctx, chal)
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ func (c *Client) GetCertificateChain(ctx context.Context, account Account, certU
|
||||||
// heuristics to decide which is optimal." §7.4.2
|
// heuristics to decide which is optimal." §7.4.2
|
||||||
alternates := extractLinks(resp, "alternate")
|
alternates := extractLinks(resp, "alternate")
|
||||||
for _, altURL := range alternates {
|
for _, altURL := range alternates {
|
||||||
resp, err = addChain(altURL)
|
_, err = addChain(altURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("retrieving alternate certificate chain at %s: %w", altURL, err)
|
return nil, fmt.Errorf("retrieving alternate certificate chain at %s: %w", altURL, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,8 +117,7 @@ func (c *Client) httpPostJWS(ctx context.Context, privateKey crypto.Signer,
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, fmt.Errorf("request to %s failed after %d attempts: %w",
|
return resp, fmt.Errorf("attempt %d: %s: %w", attempts, endpoint, err)
|
||||||
endpoint, attempts, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// httpReq robustly performs an HTTP request using the given method to the given endpoint, honoring
|
// httpReq robustly performs an HTTP request using the given method to the given endpoint, honoring
|
||||||
|
@ -272,8 +271,8 @@ func (c *Client) doHTTPRequest(req *http.Request, buf *bytes.Buffer) (resp *http
|
||||||
zap.String("method", req.Method),
|
zap.String("method", req.Method),
|
||||||
zap.String("url", req.URL.String()),
|
zap.String("url", req.URL.String()),
|
||||||
zap.Reflect("headers", req.Header),
|
zap.Reflect("headers", req.Header),
|
||||||
zap.Int("status_code", resp.StatusCode),
|
zap.Reflect("response_headers", resp.Header),
|
||||||
zap.Reflect("response_headers", resp.Header))
|
zap.Int("status_code", resp.StatusCode))
|
||||||
}
|
}
|
||||||
|
|
||||||
// "The server MUST include a Replay-Nonce header field
|
// "The server MUST include a Replay-Nonce header field
|
||||||
|
|
|
@ -14,7 +14,11 @@
|
||||||
|
|
||||||
package acme
|
package acme
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
)
|
||||||
|
|
||||||
// Problem carries the details of an error from HTTP APIs as
|
// Problem carries the details of an error from HTTP APIs as
|
||||||
// defined in RFC 7807: https://tools.ietf.org/html/rfc7807
|
// defined in RFC 7807: https://tools.ietf.org/html/rfc7807
|
||||||
|
@ -77,6 +81,9 @@ func (p Problem) Error() string {
|
||||||
if len(p.Subproblems) > 0 {
|
if len(p.Subproblems) > 0 {
|
||||||
for _, v := range p.Subproblems {
|
for _, v := range p.Subproblems {
|
||||||
s += fmt.Sprintf(", problem %q: %s", v.Type, v.Detail)
|
s += fmt.Sprintf(", problem %q: %s", v.Type, v.Detail)
|
||||||
|
if v.Identifier.Type != "" || v.Identifier.Value != "" {
|
||||||
|
s += fmt.Sprintf(" (%s_identifier=%s)", v.Identifier.Type, v.Identifier.Value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if p.Instance != "" {
|
if p.Instance != "" {
|
||||||
|
@ -85,6 +92,17 @@ func (p Problem) Error() string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalLogObject satisfies the zapcore.ObjectMarshaler interface.
|
||||||
|
// This allows problems to be serialized by the zap logger.
|
||||||
|
func (p Problem) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||||
|
enc.AddString("type", p.Type)
|
||||||
|
enc.AddString("title", p.Title)
|
||||||
|
enc.AddString("detail", p.Detail)
|
||||||
|
enc.AddString("instance", p.Instance)
|
||||||
|
enc.AddArray("subproblems", loggableSubproblems(p.Subproblems))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Subproblem describes a more specific error in a problem according to
|
// Subproblem describes a more specific error in a problem according to
|
||||||
// RFC 8555 §6.7.1: "An ACME problem document MAY contain the
|
// RFC 8555 §6.7.1: "An ACME problem document MAY contain the
|
||||||
// 'subproblems' field, containing a JSON array of problem documents,
|
// 'subproblems' field, containing a JSON array of problem documents,
|
||||||
|
@ -97,6 +115,26 @@ type Subproblem struct {
|
||||||
Identifier Identifier `json:"identifier,omitempty"`
|
Identifier Identifier `json:"identifier,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalLogObject satisfies the zapcore.ObjectMarshaler interface.
|
||||||
|
// This allows subproblems to be serialized by the zap logger.
|
||||||
|
func (sp Subproblem) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
||||||
|
enc.AddString("identifier_type", sp.Identifier.Type)
|
||||||
|
enc.AddString("identifier", sp.Identifier.Value)
|
||||||
|
enc.AddObject("subproblem", sp.Problem)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type loggableSubproblems []Subproblem
|
||||||
|
|
||||||
|
// MarshalLogArray satisfies the zapcore.ArrayMarshaler interface.
|
||||||
|
// This allows a list of subproblems to be serialized by the zap logger.
|
||||||
|
func (ls loggableSubproblems) MarshalLogArray(enc zapcore.ArrayEncoder) error {
|
||||||
|
for _, sp := range ls {
|
||||||
|
enc.AppendObject(sp)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Standard token values for the "type" field of problems, as defined
|
// Standard token values for the "type" field of problems, as defined
|
||||||
// in RFC 8555 §6.7: https://tools.ietf.org/html/rfc8555#section-6.7
|
// in RFC 8555 §6.7: https://tools.ietf.org/html/rfc8555#section-6.7
|
||||||
//
|
//
|
||||||
|
|
|
@ -134,16 +134,23 @@ func (c *Client) ObtainCertificateUsingCSR(ctx context.Context, account acme.Acc
|
||||||
// for some errors, we can retry with different challenge types
|
// for some errors, we can retry with different challenge types
|
||||||
var problem acme.Problem
|
var problem acme.Problem
|
||||||
if errors.As(err, &problem) {
|
if errors.As(err, &problem) {
|
||||||
authz := problem.Resource.(acme.Authorization)
|
authz, haveAuthz := problem.Resource.(acme.Authorization)
|
||||||
if c.Logger != nil {
|
if c.Logger != nil {
|
||||||
c.Logger.Error("validating authorization",
|
l := c.Logger
|
||||||
zap.String("identifier", authz.IdentifierValue()),
|
if haveAuthz {
|
||||||
zap.Error(err),
|
l = l.With(zap.String("identifier", authz.IdentifierValue()))
|
||||||
|
}
|
||||||
|
l.Error("validating authorization",
|
||||||
|
zap.Object("problem", problem),
|
||||||
zap.String("order", order.Location),
|
zap.String("order", order.Location),
|
||||||
zap.Int("attempt", attempt),
|
zap.Int("attempt", attempt),
|
||||||
zap.Int("max_attempts", maxAttempts))
|
zap.Int("max_attempts", maxAttempts))
|
||||||
}
|
}
|
||||||
err = fmt.Errorf("solving challenge: %s: %w", authz.IdentifierValue(), err)
|
errStr := "solving challenge"
|
||||||
|
if haveAuthz {
|
||||||
|
errStr += ": " + authz.IdentifierValue()
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("%s: %w", errStr, err)
|
||||||
if errors.As(err, &retryableErr{}) {
|
if errors.As(err, &retryableErr{}) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -505,9 +512,7 @@ func (c *Client) pollAuthorization(ctx context.Context, account acme.Account, au
|
||||||
c.Logger.Error("challenge failed",
|
c.Logger.Error("challenge failed",
|
||||||
zap.String("identifier", authz.IdentifierValue()),
|
zap.String("identifier", authz.IdentifierValue()),
|
||||||
zap.String("challenge_type", authz.currentChallenge.Type),
|
zap.String("challenge_type", authz.currentChallenge.Type),
|
||||||
zap.Int("status_code", problem.Status),
|
zap.Object("problem", problem))
|
||||||
zap.String("problem_type", problem.Type),
|
|
||||||
zap.String("error", problem.Detail))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
failedChallengeTypes.rememberFailedChallenge(authz)
|
failedChallengeTypes.rememberFailedChallenge(authz)
|
||||||
|
|
|
@ -197,7 +197,7 @@ github.com/boombuler/barcode/utils
|
||||||
# github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
|
# github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
|
||||||
## explicit
|
## explicit
|
||||||
github.com/bradfitz/gomemcache/memcache
|
github.com/bradfitz/gomemcache/memcache
|
||||||
# github.com/caddyserver/certmagic v0.14.1
|
# github.com/caddyserver/certmagic v0.15.2
|
||||||
## explicit
|
## explicit
|
||||||
github.com/caddyserver/certmagic
|
github.com/caddyserver/certmagic
|
||||||
# github.com/cespare/xxhash/v2 v2.1.1
|
# github.com/cespare/xxhash/v2 v2.1.1
|
||||||
|
@ -606,7 +606,7 @@ github.com/mattn/go-runewidth
|
||||||
github.com/mattn/go-sqlite3
|
github.com/mattn/go-sqlite3
|
||||||
# github.com/matttproud/golang_protobuf_extensions v1.0.1
|
# github.com/matttproud/golang_protobuf_extensions v1.0.1
|
||||||
github.com/matttproud/golang_protobuf_extensions/pbutil
|
github.com/matttproud/golang_protobuf_extensions/pbutil
|
||||||
# github.com/mholt/acmez v0.1.3
|
# github.com/mholt/acmez v1.0.1
|
||||||
github.com/mholt/acmez
|
github.com/mholt/acmez
|
||||||
github.com/mholt/acmez/acme
|
github.com/mholt/acmez/acme
|
||||||
# github.com/mholt/archiver/v3 v3.5.0
|
# github.com/mholt/archiver/v3 v3.5.0
|
||||||
|
@ -617,7 +617,6 @@ github.com/mholt/archiver/v3
|
||||||
github.com/microcosm-cc/bluemonday
|
github.com/microcosm-cc/bluemonday
|
||||||
github.com/microcosm-cc/bluemonday/css
|
github.com/microcosm-cc/bluemonday/css
|
||||||
# github.com/miekg/dns v1.1.43
|
# github.com/miekg/dns v1.1.43
|
||||||
## explicit
|
|
||||||
github.com/miekg/dns
|
github.com/miekg/dns
|
||||||
# github.com/minio/md5-simd v1.1.2
|
# github.com/minio/md5-simd v1.1.2
|
||||||
## explicit
|
## explicit
|
||||||
|
|
Loading…
Reference in New Issue