Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issuer policies that can randomize issuer used #238

Merged
merged 1 commit into from Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 7 additions & 4 deletions README.md
Expand Up @@ -75,10 +75,11 @@ CertMagic - Automatic HTTPS using Let's Encrypt
## Features

- Fully automated certificate management including issuance and renewal
- One-liner, fully managed HTTPS servers
- One-line, fully managed HTTPS servers
- Full control over almost every aspect of the system
- HTTP->HTTPS redirects
- Solves all 3 ACME challenges: HTTP, TLS-ALPN, and DNS
- Multiple issuers supported: get certificates from multiple sources/CAs for redundancy and resiliency
- Solves all 3 common ACME challenges: HTTP, TLS-ALPN, and DNS (and capable of others)
- Most robust error handling of _any_ ACME client
- Challenges are randomized to avoid accidental dependence
- Challenges are rotated to overcome certain network blockages
Expand All @@ -88,7 +89,8 @@ CertMagic - Automatic HTTPS using Let's Encrypt
- Written in Go, a language with memory-safety guarantees
- Powered by [ACMEz](https://github.com/mholt/acmez), _the_ premier ACME client library for Go
- All [libdns](https://github.com/libdns) DNS providers work out-of-the-box
- Pluggable storage implementations (default: file system)
- Pluggable storage backends (default: file system)
- Pluggable key sources
- Wildcard certificates
- Automatic OCSP stapling ([done right](https://gist.github.com/sleevi/5efe9ef98961ecfb4da8#gistcomment-2336055)) [keeps your sites online!](https://twitter.com/caddyserver/status/1234874273724084226)
- Will [automatically attempt](https://twitter.com/mholt6/status/1235577699541762048) to replace [revoked certificates](https://community.letsencrypt.org/t/2020-02-29-caa-rechecking-bug/114591/3?u=mholt)!
Expand All @@ -101,7 +103,8 @@ CertMagic - Automatic HTTPS using Let's Encrypt
- Caddy / CertMagic pioneered this technology
- Custom decision functions to regulate and throttle on-demand behavior
- Optional event hooks for observation
- Works with any certificate authority (CA) compliant with the ACME specification
- One-time private keys by default (new key for each cert) to discourage pinning and reduce scope of key compromise
- Works with any certificate authority (CA) compliant with the ACME specification RFC 8555
- Certificate revocation (please, only if private key is compromised)
- Must-Staple (optional; not default)
- Cross-platform support! Mac, Windows, Linux, BSD, Android...
Expand Down
17 changes: 17 additions & 0 deletions certmagic.go
Expand Up @@ -411,6 +411,23 @@ type KeyGenerator interface {
GenerateKey() (crypto.PrivateKey, error)
}

// IssuerPolicy is a type that enumerates how to
// choose which issuer to use. EXPERIMENTAL and
// subject to change.
type IssuerPolicy string

// Supported issuer policies. These are subject to change.
const (
// UseFirstIssuer uses the first issuer that
// successfully returns a certificate.
UseFirstIssuer = "first"

// UseFirstRandomIssuer shuffles the list of
// configured issuers, then uses the first one
// that successfully returns a certificate.
UseFirstRandomIssuer = "first_random"
)

// IssuedCertificate represents a certificate that was just issued.
type IssuedCertificate struct {
// The PEM-encoding of DER-encoded ASN.1 data.
Expand Down
16 changes: 15 additions & 1 deletion config.go
Expand Up @@ -95,6 +95,10 @@ type Config struct {
// turn until one succeeds.
Issuers []Issuer

// How to select which issuer to use.
// Default: UseFirstIssuer (subject to change).
IssuerPolicy IssuerPolicy

// If true, private keys already existing in storage
// will be reused. Otherwise, a new key will be
// created for every new certificate to mitigate
Expand Down Expand Up @@ -533,7 +537,9 @@ func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool
return fmt.Errorf("obtaining certificate aborted by event handler: %w", err)
}

// if storage has a private key already, use it; otherwise we'll generate our own
// If storage has a private key already, use it; otherwise we'll generate our own.
// Also create the slice of issuers we will try using according to any issuer
// selection policy (it must be a copy of the slice so we don't mutate original).
var privKey crypto.PrivateKey
var privKeyPEM []byte
var issuers []Issuer
Expand All @@ -542,6 +548,14 @@ func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool
if err != nil {
return err
}
} else {
issuers = make([]Issuer, len(cfg.Issuers))
copy(issuers, cfg.Issuers)
}
if cfg.IssuerPolicy == UseFirstRandomIssuer {
weakrand.Shuffle(len(issuers), func(i, j int) {
issuers[i], issuers[j] = issuers[j], issuers[i]
})
}
if privKey == nil {
privKey, err = cfg.KeySource.GenerateKey()
Expand Down