mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-23 18:55:49 +03:00
letsencrypt: Use existing certs & keys if already in storage
This commit is contained in:
parent
9f9de389d5
commit
da8a4fafcc
1 changed files with 61 additions and 25 deletions
|
@ -1,3 +1,5 @@
|
||||||
|
// Package letsencrypt integrates Let's Encrypt with Caddy with first-class support.
|
||||||
|
// It is designed to configure sites for HTTPS by default.
|
||||||
package letsencrypt
|
package letsencrypt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -26,6 +28,15 @@ import (
|
||||||
// address from last time. If there isn't one, the user
|
// address from last time. If there isn't one, the user
|
||||||
// will be prompted. If the user leaves email blank, <TODO>.
|
// will be prompted. If the user leaves email blank, <TODO>.
|
||||||
func Activate(configs []server.Config) ([]server.Config, error) {
|
func Activate(configs []server.Config) ([]server.Config, error) {
|
||||||
|
// First identify and configure any elligible hosts for which
|
||||||
|
// we already have certs and keys in storage from last time.
|
||||||
|
configLen := len(configs) // avoid infinite loop since this loop appends to the slice
|
||||||
|
for i := 0; i < configLen; i++ {
|
||||||
|
if existingCertAndKey(configs[i].Host) {
|
||||||
|
configs = autoConfigure(&configs[i], configs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Group configs by LE email address; this will help us
|
// Group configs by LE email address; this will help us
|
||||||
// reduce round-trips when getting the certs.
|
// reduce round-trips when getting the certs.
|
||||||
initMap, err := groupConfigsByEmail(configs)
|
initMap, err := groupConfigsByEmail(configs)
|
||||||
|
@ -54,8 +65,10 @@ func Activate(configs []server.Config) ([]server.Config, error) {
|
||||||
return configs, err
|
return configs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// it all comes down to this: filling in the file path of a valid certificate automatically
|
// it all comes down to this: turning TLS on for all the configs
|
||||||
configs = autoConfigure(configs, serverConfigs)
|
for _, cfg := range serverConfigs {
|
||||||
|
configs = autoConfigure(cfg, configs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return configs, nil
|
return configs, nil
|
||||||
|
@ -64,10 +77,18 @@ func Activate(configs []server.Config) ([]server.Config, error) {
|
||||||
// groupConfigsByEmail groups configs by the Let's Encrypt email address
|
// groupConfigsByEmail groups configs by the Let's Encrypt email address
|
||||||
// associated to them or to the default Let's Encrypt email address. If the
|
// associated to them or to the default Let's Encrypt email address. If the
|
||||||
// default email is not available, the user will be prompted to provide one.
|
// default email is not available, the user will be prompted to provide one.
|
||||||
|
//
|
||||||
|
// This function also filters out configs that don't need extra TLS help.
|
||||||
|
// Configurations with a manual TLS configuration or one that is already
|
||||||
|
// found in storage will not be added to any group.
|
||||||
func groupConfigsByEmail(configs []server.Config) (map[string][]*server.Config, error) {
|
func groupConfigsByEmail(configs []server.Config) (map[string][]*server.Config, error) {
|
||||||
initMap := make(map[string][]*server.Config)
|
initMap := make(map[string][]*server.Config)
|
||||||
for i := 0; i < len(configs); i++ {
|
for i := 0; i < len(configs); i++ {
|
||||||
if configs[i].TLS.Certificate == "" && configs[i].TLS.Key == "" && configs[i].Port != "http" { // TODO: && !cfg.Host.IsLoopback()
|
if configs[i].TLS.Certificate == "" && configs[i].TLS.Key == "" && configs[i].Port != "http" { // TODO: && !cfg.Host.IsLoopback()
|
||||||
|
// make sure an HTTPS version of this config doesn't exist in the list already
|
||||||
|
if hostHasOtherScheme(configs[i].Host, "https", configs) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
leEmail := getEmail(configs[i])
|
leEmail := getEmail(configs[i])
|
||||||
if leEmail == "" {
|
if leEmail == "" {
|
||||||
return nil, errors.New("must have email address to serve HTTPS without existing certificate and key")
|
return nil, errors.New("must have email address to serve HTTPS without existing certificate and key")
|
||||||
|
@ -78,6 +99,20 @@ func groupConfigsByEmail(configs []server.Config) (map[string][]*server.Config,
|
||||||
return initMap, nil
|
return initMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// existingCertAndKey returns true if the host has a certificate
|
||||||
|
// and private key in storage already, false otherwise.
|
||||||
|
func existingCertAndKey(host string) bool {
|
||||||
|
_, err := os.Stat(storage.SiteCertFile(host))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, err = os.Stat(storage.SiteKeyFile(host))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// newClient creates a new ACME client to facilitate communication
|
// newClient creates a new ACME client to facilitate communication
|
||||||
// with the Let's Encrypt CA server on behalf of the user specified
|
// with the Let's Encrypt CA server on behalf of the user specified
|
||||||
// by leEmail. As part of this process, a user will be loaded from
|
// by leEmail. As part of this process, a user will be loaded from
|
||||||
|
@ -168,11 +203,9 @@ func saveCertsAndKeys(certificates []acme.CertificateResource) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// autoConfigure enables TLS on all the configs in serverConfigs
|
// autoConfigure enables TLS on cfg and appends, if necessary, a new config
|
||||||
// and appends, if necessary, new configs to allConfigs that redirect
|
// to allConfigs that redirects plaintext HTTP to its new HTTPS counterpart.
|
||||||
// plaintext HTTP to their HTTPS counterparts.
|
func autoConfigure(cfg *server.Config, allConfigs []server.Config) []server.Config {
|
||||||
func autoConfigure(allConfigs []server.Config, serverConfigs []*server.Config) []server.Config {
|
|
||||||
for _, cfg := range serverConfigs {
|
|
||||||
cfg.TLS.Certificate = storage.SiteCertFile(cfg.Host)
|
cfg.TLS.Certificate = storage.SiteCertFile(cfg.Host)
|
||||||
cfg.TLS.Key = storage.SiteKeyFile(cfg.Host)
|
cfg.TLS.Key = storage.SiteKeyFile(cfg.Host)
|
||||||
cfg.TLS.Enabled = true
|
cfg.TLS.Enabled = true
|
||||||
|
@ -180,23 +213,26 @@ func autoConfigure(allConfigs []server.Config, serverConfigs []*server.Config) [
|
||||||
|
|
||||||
// Is there a plaintext HTTP config for the same host? If not, make
|
// Is there a plaintext HTTP config for the same host? If not, make
|
||||||
// one and have it redirect all requests to this HTTPS host.
|
// one and have it redirect all requests to this HTTPS host.
|
||||||
var plaintextHostFound bool
|
if !hostHasOtherScheme(cfg.Host, "http", allConfigs) {
|
||||||
for _, otherCfg := range allConfigs {
|
|
||||||
if cfg.Host == otherCfg.Host && otherCfg.Port == "http" {
|
|
||||||
plaintextHostFound = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !plaintextHostFound {
|
|
||||||
// Make one that redirects to HTTPS for all requests
|
// Make one that redirects to HTTPS for all requests
|
||||||
allConfigs = append(allConfigs, redirPlaintextHost(*cfg))
|
allConfigs = append(allConfigs, redirPlaintextHost(*cfg))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return allConfigs
|
return allConfigs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hostHasOtherScheme tells you whether there is another config in the list
|
||||||
|
// for the same host but with the port equal to scheme. For example, to see
|
||||||
|
// if example.com has a https variant already, pass in example.com and
|
||||||
|
// "https" along with the list of configs.
|
||||||
|
func hostHasOtherScheme(host, scheme string, allConfigs []server.Config) bool {
|
||||||
|
for _, otherCfg := range allConfigs {
|
||||||
|
if otherCfg.Host == host && otherCfg.Port == scheme {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// redirPlaintextHost returns a new plaintext HTTP configuration for
|
// redirPlaintextHost returns a new plaintext HTTP configuration for
|
||||||
// a virtualHost that simply redirects to cfg, which is assumed to
|
// a virtualHost that simply redirects to cfg, which is assumed to
|
||||||
// be the HTTPS configuration. The returned configuration is set
|
// be the HTTPS configuration. The returned configuration is set
|
||||||
|
|
Loading…
Reference in a new issue