2018-12-10 13:15:26 +10:00
|
|
|
// Copyright 2015 Matthew Holt
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package certmagic
|
|
|
|
|
|
|
|
import (
|
2022-03-08 05:26:52 +10:00
|
|
|
"context"
|
2018-12-10 13:15:26 +10:00
|
|
|
"crypto"
|
|
|
|
"crypto/ecdsa"
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
"crypto/ed25519"
|
|
|
|
"crypto/elliptic"
|
|
|
|
"crypto/rand"
|
2018-12-10 13:15:26 +10:00
|
|
|
"crypto/rsa"
|
2018-12-18 05:22:46 +10:00
|
|
|
"crypto/tls"
|
2018-12-10 13:15:26 +10:00
|
|
|
"crypto/x509"
|
|
|
|
"encoding/json"
|
|
|
|
"encoding/pem"
|
2022-03-08 04:11:20 +10:00
|
|
|
"errors"
|
2018-12-10 13:15:26 +10:00
|
|
|
"fmt"
|
|
|
|
"hash/fnv"
|
2022-03-08 04:11:20 +10:00
|
|
|
"io/fs"
|
2020-11-17 03:53:41 +10:00
|
|
|
"sort"
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
"strings"
|
2018-12-10 13:15:26 +10:00
|
|
|
|
2021-04-06 07:05:24 +10:00
|
|
|
"github.com/klauspost/cpuid/v2"
|
2023-07-09 01:56:51 +10:00
|
|
|
"github.com/zeebo/blake3"
|
2020-11-17 03:53:41 +10:00
|
|
|
"go.uber.org/zap"
|
2021-01-05 08:42:49 +10:00
|
|
|
"golang.org/x/net/idna"
|
2018-12-10 13:15:26 +10:00
|
|
|
)
|
|
|
|
|
2022-02-18 07:37:50 +10:00
|
|
|
// PEMEncodePrivateKey marshals a private key into a PEM-encoded block.
|
|
|
|
// The private key must be one of *ecdsa.PrivateKey, *rsa.PrivateKey, or
|
|
|
|
// *ed25519.PrivateKey.
|
|
|
|
func PEMEncodePrivateKey(key crypto.PrivateKey) ([]byte, error) {
|
2018-12-10 13:15:26 +10:00
|
|
|
var pemType string
|
|
|
|
var keyBytes []byte
|
|
|
|
switch key := key.(type) {
|
|
|
|
case *ecdsa.PrivateKey:
|
|
|
|
var err error
|
|
|
|
pemType = "EC"
|
|
|
|
keyBytes, err = x509.MarshalECPrivateKey(key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
case *rsa.PrivateKey:
|
|
|
|
pemType = "RSA"
|
|
|
|
keyBytes = x509.MarshalPKCS1PrivateKey(key)
|
2020-07-28 08:50:41 +10:00
|
|
|
case ed25519.PrivateKey:
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
var err error
|
|
|
|
pemType = "ED25519"
|
|
|
|
keyBytes, err = x509.MarshalPKCS8PrivateKey(key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("unsupported key type: %T", key)
|
2018-12-10 13:15:26 +10:00
|
|
|
}
|
|
|
|
pemKey := pem.Block{Type: pemType + " PRIVATE KEY", Bytes: keyBytes}
|
|
|
|
return pem.EncodeToMemory(&pemKey), nil
|
|
|
|
}
|
|
|
|
|
2022-02-18 07:37:50 +10:00
|
|
|
// PEMDecodePrivateKey loads a PEM-encoded ECC/RSA private key from an array of bytes.
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
// Borrowed from Go standard library, to handle various private key and PEM block types.
|
2022-02-18 07:37:50 +10:00
|
|
|
func PEMDecodePrivateKey(keyPEMBytes []byte) (crypto.Signer, error) {
|
|
|
|
// Modified from original:
|
|
|
|
// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L291-L308
|
|
|
|
// https://github.com/golang/go/blob/693748e9fa385f1e2c3b91ca9acbb6c0ad2d133d/src/crypto/tls/tls.go#L238
|
|
|
|
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
keyBlockDER, _ := pem.Decode(keyPEMBytes)
|
|
|
|
|
Check keyBlockDER for nil (#147)
I got following panic while Caddy was running:
2021/10/26 08:06:34 panic: certificate worker: runtime error: invalid memory address or nil pointer dereference
goroutine 43 [running]:
github.com/caddyserver/certmagic.(*jobManager).worker.func1()
github.com/caddyserver/certmagic@v0.14.5/async.go:58 +0x65
panic({0x145d400, 0x23d6c50})
runtime/panic.go:1038 +0x215
github.com/caddyserver/certmagic.decodePrivateKey({0xc000738c00, 0x0, 0x0})
github.com/caddyserver/certmagic@v0.14.5/crypto.go:75 +0x2a
github.com/caddyserver/certmagic.(*Config).reusePrivateKey(0xc0003b77c0, {0xc0003b1640, 0x32})
github.com/caddyserver/certmagic@v0.14.5/config.go:602 +0x2b9
github.com/caddyserver/certmagic.(*Config).obtainCert.func2({0x190d3b8, 0xc000655920})
github.com/caddyserver/certmagic@v0.14.5/config.go:487 +0x1d6
github.com/caddyserver/certmagic.doWithRetry({0x190d310, 0xc0000b0440}, 0xc00003bd40, 0xc0007afba8)
github.com/caddyserver/certmagic@v0.14.5/async.go:106 +0x1cc
github.com/caddyserver/certmagic.(*Config).obtainCert(0xc0003b77c0, {0x190d310, 0xc0000b0440}, {0xc0003b1640, 0x32}, 0x0)
github.com/caddyserver/certmagic@v0.14.5/config.go:572 +0x58e
github.com/caddyserver/certmagic.(*Config).ObtainCertAsync(...)
github.com/caddyserver/certmagic@v0.14.5/config.go:427
github.com/caddyserver/certmagic.(*Config).manageOne.func1()
github.com/caddyserver/certmagic@v0.14.5/config.go:332 +0x6f
github.com/caddyserver/certmagic.(*jobManager).worker(0x23e0c60)
github.com/caddyserver/certmagic@v0.14.5/async.go:73 +0x112
created by github.com/caddyserver/certmagic.(*jobManager).Submit
github.com/caddyserver/certmagic@v0.14.5/async.go:50 +0x288
According to Go documentation: https://pkg.go.dev/encoding/pem#Decode
p can be nil (first parameter returned) and so it should be checked
before continuing as per this example:
https://pkg.go.dev/encoding/pem#example-Decode
I also added a test to verify that the fix works. Running the test
without the fix causes a panic.
Test: go test -count=1 './...'
2021-10-27 06:11:35 +10:00
|
|
|
if keyBlockDER == nil {
|
|
|
|
return nil, fmt.Errorf("failed to decode PEM block containing private key")
|
|
|
|
}
|
|
|
|
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") {
|
|
|
|
return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
if key, err := x509.ParsePKCS1PrivateKey(keyBlockDER.Bytes); err == nil {
|
|
|
|
return key, nil
|
2018-12-10 13:15:26 +10:00
|
|
|
}
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
|
|
|
|
if key, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil {
|
|
|
|
switch key := key.(type) {
|
|
|
|
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
|
2020-07-28 08:50:41 +10:00
|
|
|
return key.(crypto.Signer), nil
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if key, err := x509.ParseECPrivateKey(keyBlockDER.Bytes); err == nil {
|
|
|
|
return key, nil
|
|
|
|
}
|
|
|
|
|
2018-12-10 13:15:26 +10:00
|
|
|
return nil, fmt.Errorf("unknown private key type")
|
|
|
|
}
|
|
|
|
|
|
|
|
// parseCertsFromPEMBundle parses a certificate bundle from top to bottom and returns
|
|
|
|
// a slice of x509 certificates. This function will error if no certificates are found.
|
|
|
|
func parseCertsFromPEMBundle(bundle []byte) ([]*x509.Certificate, error) {
|
|
|
|
var certificates []*x509.Certificate
|
|
|
|
var certDERBlock *pem.Block
|
|
|
|
for {
|
|
|
|
certDERBlock, bundle = pem.Decode(bundle)
|
|
|
|
if certDERBlock == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if certDERBlock.Type == "CERTIFICATE" {
|
|
|
|
cert, err := x509.ParseCertificate(certDERBlock.Bytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
certificates = append(certificates, cert)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(certificates) == 0 {
|
|
|
|
return nil, fmt.Errorf("no certificates found in bundle")
|
|
|
|
}
|
|
|
|
return certificates, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// fastHash hashes input using a hashing algorithm that
|
|
|
|
// is fast, and returns the hash as a hex-encoded string.
|
|
|
|
// Do not use this for cryptographic purposes.
|
|
|
|
func fastHash(input []byte) string {
|
|
|
|
h := fnv.New32a()
|
|
|
|
h.Write(input)
|
|
|
|
return fmt.Sprintf("%x", h.Sum32())
|
|
|
|
}
|
|
|
|
|
|
|
|
// saveCertResource saves the certificate resource to disk. This
|
|
|
|
// includes the certificate file itself, the private key, and the
|
|
|
|
// metadata file.
|
2022-03-08 05:26:52 +10:00
|
|
|
func (cfg *Config) saveCertResource(ctx context.Context, issuer Issuer, cert CertificateResource) error {
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
metaBytes, err := json.MarshalIndent(cert, "", "\t")
|
2018-12-10 13:15:26 +10:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("encoding certificate metadata: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-11-17 03:53:41 +10:00
|
|
|
issuerKey := issuer.IssuerKey()
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
certKey := cert.NamesKey()
|
|
|
|
|
2018-12-10 13:15:26 +10:00
|
|
|
all := []keyValue{
|
|
|
|
{
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
key: StorageKeys.SitePrivateKey(issuerKey, certKey),
|
|
|
|
value: cert.PrivateKeyPEM,
|
2018-12-10 13:15:26 +10:00
|
|
|
},
|
2021-10-31 03:18:47 +10:00
|
|
|
{
|
|
|
|
key: StorageKeys.SiteCert(issuerKey, certKey),
|
|
|
|
value: cert.CertificatePEM,
|
|
|
|
},
|
2018-12-10 13:15:26 +10:00
|
|
|
{
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
key: StorageKeys.SiteMeta(issuerKey, certKey),
|
2018-12-10 13:15:26 +10:00
|
|
|
value: metaBytes,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-03-08 05:26:52 +10:00
|
|
|
return storeTx(ctx, cfg.Storage, all)
|
2018-12-10 13:15:26 +10:00
|
|
|
}
|
|
|
|
|
2020-11-17 03:53:41 +10:00
|
|
|
// loadCertResourceAnyIssuer loads and returns the certificate resource from any
|
|
|
|
// of the configured issuers. If multiple are found (e.g. if there are 3 issuers
|
|
|
|
// configured, and all 3 have a resource matching certNamesKey), then the newest
|
|
|
|
// (latest NotBefore date) resource will be chosen.
|
2022-03-08 05:26:52 +10:00
|
|
|
func (cfg *Config) loadCertResourceAnyIssuer(ctx context.Context, certNamesKey string) (CertificateResource, error) {
|
2020-11-17 03:53:41 +10:00
|
|
|
// we can save some extra decoding steps if there's only one issuer, since
|
|
|
|
// we don't need to compare potentially multiple available resources to
|
|
|
|
// select the best one, when there's only one choice anyway
|
|
|
|
if len(cfg.Issuers) == 1 {
|
2022-03-08 05:26:52 +10:00
|
|
|
return cfg.loadCertResource(ctx, cfg.Issuers[0], certNamesKey)
|
2020-11-17 03:53:41 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
type decodedCertResource struct {
|
|
|
|
CertificateResource
|
|
|
|
issuer Issuer
|
|
|
|
decoded *x509.Certificate
|
|
|
|
}
|
|
|
|
var certResources []decodedCertResource
|
|
|
|
var lastErr error
|
|
|
|
|
|
|
|
// load and decode all certificate resources found with the
|
|
|
|
// configured issuers so we can sort by newest
|
|
|
|
for _, issuer := range cfg.Issuers {
|
2022-03-08 05:26:52 +10:00
|
|
|
certRes, err := cfg.loadCertResource(ctx, issuer, certNamesKey)
|
2020-11-17 03:53:41 +10:00
|
|
|
if err != nil {
|
2022-03-08 04:11:20 +10:00
|
|
|
if errors.Is(err, fs.ErrNotExist) {
|
2020-11-17 03:53:41 +10:00
|
|
|
// not a problem, but we need to remember the error
|
|
|
|
// in case we end up not finding any cert resources
|
|
|
|
// since we'll need an error to return in that case
|
|
|
|
lastErr = err
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return CertificateResource{}, err
|
|
|
|
}
|
|
|
|
certs, err := parseCertsFromPEMBundle(certRes.CertificatePEM)
|
|
|
|
if err != nil {
|
|
|
|
return CertificateResource{}, err
|
|
|
|
}
|
|
|
|
certResources = append(certResources, decodedCertResource{
|
|
|
|
CertificateResource: certRes,
|
|
|
|
issuer: issuer,
|
|
|
|
decoded: certs[0],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if len(certResources) == 0 {
|
|
|
|
if lastErr == nil {
|
|
|
|
lastErr = fmt.Errorf("no certificate resources found") // just in case; e.g. no Issuers configured
|
|
|
|
}
|
|
|
|
return CertificateResource{}, lastErr
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort by date so the most recently issued comes first
|
|
|
|
sort.Slice(certResources, func(i, j int) bool {
|
|
|
|
return certResources[j].decoded.NotBefore.Before(certResources[i].decoded.NotBefore)
|
|
|
|
})
|
|
|
|
|
2022-09-27 02:19:28 +10:00
|
|
|
cfg.Logger.Debug("loading managed certificate",
|
|
|
|
zap.String("domain", certNamesKey),
|
|
|
|
zap.Time("expiration", expiresAt(certResources[0].decoded)),
|
|
|
|
zap.String("issuer_key", certResources[0].issuer.IssuerKey()),
|
|
|
|
zap.Any("storage", cfg.Storage),
|
|
|
|
)
|
2020-11-17 03:53:41 +10:00
|
|
|
|
|
|
|
return certResources[0].CertificateResource, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// loadCertResource loads a certificate resource from the given issuer's storage location.
|
2022-03-08 05:26:52 +10:00
|
|
|
func (cfg *Config) loadCertResource(ctx context.Context, issuer Issuer, certNamesKey string) (CertificateResource, error) {
|
2021-06-13 05:47:47 +10:00
|
|
|
certRes := CertificateResource{issuerKey: issuer.IssuerKey()}
|
2020-11-17 03:53:41 +10:00
|
|
|
|
2021-01-05 08:42:49 +10:00
|
|
|
normalizedName, err := idna.ToASCII(certNamesKey)
|
|
|
|
if err != nil {
|
2021-06-13 05:47:47 +10:00
|
|
|
return CertificateResource{}, fmt.Errorf("converting '%s' to ASCII: %v", certNamesKey, err)
|
2021-01-05 08:42:49 +10:00
|
|
|
}
|
|
|
|
|
2022-03-08 05:26:52 +10:00
|
|
|
keyBytes, err := cfg.Storage.Load(ctx, StorageKeys.SitePrivateKey(certRes.issuerKey, normalizedName))
|
2018-12-10 13:15:26 +10:00
|
|
|
if err != nil {
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
return CertificateResource{}, err
|
2018-12-10 13:15:26 +10:00
|
|
|
}
|
2022-02-16 05:23:49 +10:00
|
|
|
certRes.PrivateKeyPEM = keyBytes
|
2022-03-08 05:26:52 +10:00
|
|
|
certBytes, err := cfg.Storage.Load(ctx, StorageKeys.SiteCert(certRes.issuerKey, normalizedName))
|
2018-12-10 13:15:26 +10:00
|
|
|
if err != nil {
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
return CertificateResource{}, err
|
2018-12-10 13:15:26 +10:00
|
|
|
}
|
2022-02-16 05:23:49 +10:00
|
|
|
certRes.CertificatePEM = certBytes
|
2022-03-08 05:26:52 +10:00
|
|
|
metaBytes, err := cfg.Storage.Load(ctx, StorageKeys.SiteMeta(certRes.issuerKey, normalizedName))
|
2018-12-10 13:15:26 +10:00
|
|
|
if err != nil {
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
return CertificateResource{}, err
|
2018-12-10 13:15:26 +10:00
|
|
|
}
|
|
|
|
err = json.Unmarshal(metaBytes, &certRes)
|
|
|
|
if err != nil {
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
return CertificateResource{}, fmt.Errorf("decoding certificate metadata: %v", err)
|
2018-12-10 13:15:26 +10:00
|
|
|
}
|
2020-07-31 04:13:45 +10:00
|
|
|
|
2018-12-10 13:15:26 +10:00
|
|
|
return certRes, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// hashCertificateChain computes the unique hash of certChain,
|
|
|
|
// which is the chain of DER-encoded bytes. It returns the
|
|
|
|
// hex encoding of the hash.
|
|
|
|
func hashCertificateChain(certChain [][]byte) string {
|
2023-07-09 01:56:51 +10:00
|
|
|
h := blake3.New()
|
2018-12-10 13:15:26 +10:00
|
|
|
for _, certInChain := range certChain {
|
|
|
|
h.Write(certInChain)
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%x", h.Sum(nil))
|
|
|
|
}
|
2018-12-18 05:22:46 +10:00
|
|
|
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
func namesFromCSR(csr *x509.CertificateRequest) []string {
|
|
|
|
var nameSet []string
|
2024-04-09 02:59:55 +10:00
|
|
|
// TODO: CommonName should not be used (it has been deprecated for 25+ years,
|
2024-04-12 04:23:53 +10:00
|
|
|
// but ZeroSSL CA still requires it to be filled out and not overlap SANs...)
|
2024-04-09 02:59:55 +10:00
|
|
|
if csr.Subject.CommonName != "" {
|
|
|
|
nameSet = append(nameSet, csr.Subject.CommonName)
|
|
|
|
}
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
nameSet = append(nameSet, csr.DNSNames...)
|
|
|
|
nameSet = append(nameSet, csr.EmailAddresses...)
|
|
|
|
for _, v := range csr.IPAddresses {
|
|
|
|
nameSet = append(nameSet, v.String())
|
|
|
|
}
|
|
|
|
for _, v := range csr.URIs {
|
|
|
|
nameSet = append(nameSet, v.String())
|
|
|
|
}
|
|
|
|
return nameSet
|
|
|
|
}
|
|
|
|
|
2018-12-18 05:22:46 +10:00
|
|
|
// preferredDefaultCipherSuites returns an appropriate
|
|
|
|
// cipher suite to use depending on hardware support
|
|
|
|
// for AES-NI.
|
|
|
|
//
|
|
|
|
// See https://github.com/mholt/caddy/issues/1674
|
|
|
|
func preferredDefaultCipherSuites() []uint16 {
|
2021-04-06 07:05:24 +10:00
|
|
|
if cpuid.CPU.Supports(cpuid.AESNI) {
|
2018-12-18 05:22:46 +10:00
|
|
|
return defaultCiphersPreferAES
|
|
|
|
}
|
|
|
|
return defaultCiphersPreferChaCha
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
defaultCiphersPreferAES = []uint16{
|
|
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
|
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
|
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
|
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
|
|
|
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
|
|
|
}
|
|
|
|
defaultCiphersPreferChaCha = []uint16{
|
|
|
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
|
|
|
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
|
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
|
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
|
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
|
|
}
|
|
|
|
)
|
Major refactor to improve performance, correctness, and extensibility
Breaking changes; thank goodness we're not 1.0 yet 😅 - read on!
This change completely separates ACME-specific code from the rest of the
certificate management process, allowing pluggable sources for certs
that aren't ACME.
Notably, most of Config was spliced into ACMEManager. Similarly, there's
now Default and DefaultACME.
Storage structure had to be reconfigured. Certificates are no longer in
the acme/ subfolder since they can be obtained by ways other than ACME!
Certificates moved to a new certificates/ subfolder. The subfolders in
that folder use the path of the ACME endpoint instead of just the host,
so that also changed. Be aware that unless you move your certs over,
CertMagic will not find them and will attempt to get new ones. That is
usually fine for most users, but for extremely large deployments, you
will want to move them over first.
Old certs path:
acme/acme-staging-v02.api.letsencrypt.org/...
New certs path:
certificates/acme-staging-v02.api.letsencrypt.org-directory/...
That's all for significant storage changes!
But this refactor also vastly improves performance, especially at scale,
and makes CertMagic way more resilient to errors. Retries are done on
the staging endpoint by default, so they won't count against your rate
limit. If your hardware can handle it, I'm now pretty confident that you
can give CertMagic a million domain names and it will gracefully manage
them, as fast as it can within internal and external rate limits, even
in the presence of errors. Errors will of course slow some things down,
but you should be good to go if you're monitoring logs and can fix any
misconfigurations or other external errors!
Several other mostly-minor enhancements fix bugs, especially at scale.
For example, duplicated renewal tasks (that continuously fail) will not
pile up on each other: only one will operate, under exponential backoff.
Closes #50 and fixes #55
2020-02-22 07:32:57 +10:00
|
|
|
|
|
|
|
// StandardKeyGenerator is the standard, in-memory key source
|
|
|
|
// that uses crypto/rand.
|
|
|
|
type StandardKeyGenerator struct {
|
|
|
|
// The type of keys to generate.
|
|
|
|
KeyType KeyType
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenerateKey generates a new private key according to kg.KeyType.
|
|
|
|
func (kg StandardKeyGenerator) GenerateKey() (crypto.PrivateKey, error) {
|
|
|
|
switch kg.KeyType {
|
|
|
|
case ED25519:
|
|
|
|
_, priv, err := ed25519.GenerateKey(rand.Reader)
|
|
|
|
return priv, err
|
|
|
|
case "", P256:
|
|
|
|
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
|
|
case P384:
|
|
|
|
return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
|
|
|
case RSA2048:
|
|
|
|
return rsa.GenerateKey(rand.Reader, 2048)
|
|
|
|
case RSA4096:
|
|
|
|
return rsa.GenerateKey(rand.Reader, 4096)
|
|
|
|
case RSA8192:
|
|
|
|
return rsa.GenerateKey(rand.Reader, 8192)
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("unrecognized or unsupported key type: %s", kg.KeyType)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DefaultKeyGenerator is the default key source.
|
|
|
|
var DefaultKeyGenerator = StandardKeyGenerator{KeyType: P256}
|
|
|
|
|
|
|
|
// KeyType enumerates the known/supported key types.
|
|
|
|
type KeyType string
|
|
|
|
|
|
|
|
// Constants for all key types we support.
|
|
|
|
const (
|
|
|
|
ED25519 = KeyType("ed25519")
|
|
|
|
P256 = KeyType("p256")
|
|
|
|
P384 = KeyType("p384")
|
|
|
|
RSA2048 = KeyType("rsa2048")
|
|
|
|
RSA4096 = KeyType("rsa4096")
|
|
|
|
RSA8192 = KeyType("rsa8192")
|
|
|
|
)
|