Rewrote Caddy from the ground up; initial commit of 0.9 branch
These changes span work from the last ~4 months in an effort to make
Caddy more extensible, reduce the coupling between its components, and
lay a more robust foundation of code going forward into 1.0. A bunch of
new features have been added, too, with even higher future potential.
The most significant design change is an overall inversion of
dependencies. Instead of the caddy package knowing about the server
and the notion of middleware and config, the caddy package exposes an
interface that other components plug into. This does introduce more
indirection when reading the code, but every piece is very modular and
pluggable. Even the HTTP server is pluggable.
The caddy package has been moved to the top level, and main has been
pushed into a subfolder called caddy. The actual logic of the main
file has been pushed even further into caddy/caddymain/run.go so that
custom builds of Caddy can be 'go get'able.
The HTTPS logic was surgically separated into two parts to divide the
TLS-specific code and the HTTPS-specific code. The caddytls package can
now be used by any type of server that needs TLS, not just HTTP. I also
added the ability to customize nearly every aspect of TLS at the site
level rather than all sites sharing the same TLS configuration. Not all
of this flexibility is exposed in the Caddyfile yet, but it may be in
the future. Caddy can also generate self-signed certificates in memory
for the convenience of a developer working on localhost who wants HTTPS.
And Caddy now supports the DNS challenge, assuming at least one DNS
provider is plugged in.
Dozens, if not hundreds, of other minor changes swept through the code
base as I literally started from an empty main function, copying over
functions or files as needed, then adjusting them to fit in the new
design. Most tests have been restored and adapted to the new API,
but more work is needed there.
A lot of what was "impossible" before is now possible, or can be made
possible with minimal disruption of the code. For example, it's fairly
easy to make plugins hook into another part of the code via callbacks.
Plugins can do more than just be directives; we now have plugins that
customize how the Caddyfile is loaded (useful when you need to get your
configuration from a remote store).
Site addresses no longer need be just a host and port. They can have a
path, allowing you to scope a configuration to a specific path. There is
no inheretance, however; each site configuration is distinct.
Thanks to amazing work by Lucas Clemente, this commit adds experimental
QUIC support. Turn it on using the -quic flag; your browser may have
to be configured to enable it.
Almost everything is here, but you will notice that most of the middle-
ware are missing. After those are transferred over, we'll be ready for
beta tests.
I'm very excited to get this out. Thanks for everyone's help and
patience these last few months. I hope you like it!!
2016-06-05 07:00:29 +08:00
|
|
|
package caddytls
|
2016-02-11 15:06:05 +08:00
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
|
|
|
"errors"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"golang.org/x/crypto/ocsp"
|
|
|
|
)
|
|
|
|
|
|
|
|
// certCache stores certificates in memory,
|
2017-08-12 14:12:22 +08:00
|
|
|
// keying certificates by name. Certificates
|
|
|
|
// should not overlap in the names they serve,
|
|
|
|
// because a name only maps to one certificate.
|
2016-02-11 15:06:05 +08:00
|
|
|
var certCache = make(map[string]Certificate)
|
|
|
|
var certCacheMu sync.RWMutex
|
|
|
|
|
|
|
|
// Certificate is a tls.Certificate with associated metadata tacked on.
|
|
|
|
// Even if the metadata can be obtained by parsing the certificate,
|
|
|
|
// we can be more efficient by extracting the metadata once so it's
|
|
|
|
// just there, ready to use.
|
|
|
|
type Certificate struct {
|
2016-02-16 14:39:04 +08:00
|
|
|
tls.Certificate
|
2016-02-11 15:06:05 +08:00
|
|
|
|
|
|
|
// Names is the list of names this certificate is written for.
|
|
|
|
// The first is the CommonName (if any), the rest are SAN.
|
2017-08-12 14:12:22 +08:00
|
|
|
// This should be the exact list of keys by which this cert
|
|
|
|
// is accessed in the cache, careful to avoid overlap.
|
2016-02-11 15:06:05 +08:00
|
|
|
Names []string
|
|
|
|
|
|
|
|
// NotAfter is when the certificate expires.
|
|
|
|
NotAfter time.Time
|
|
|
|
|
|
|
|
// OCSP contains the certificate's parsed OCSP response.
|
|
|
|
OCSP *ocsp.Response
|
Rewrote Caddy from the ground up; initial commit of 0.9 branch
These changes span work from the last ~4 months in an effort to make
Caddy more extensible, reduce the coupling between its components, and
lay a more robust foundation of code going forward into 1.0. A bunch of
new features have been added, too, with even higher future potential.
The most significant design change is an overall inversion of
dependencies. Instead of the caddy package knowing about the server
and the notion of middleware and config, the caddy package exposes an
interface that other components plug into. This does introduce more
indirection when reading the code, but every piece is very modular and
pluggable. Even the HTTP server is pluggable.
The caddy package has been moved to the top level, and main has been
pushed into a subfolder called caddy. The actual logic of the main
file has been pushed even further into caddy/caddymain/run.go so that
custom builds of Caddy can be 'go get'able.
The HTTPS logic was surgically separated into two parts to divide the
TLS-specific code and the HTTPS-specific code. The caddytls package can
now be used by any type of server that needs TLS, not just HTTP. I also
added the ability to customize nearly every aspect of TLS at the site
level rather than all sites sharing the same TLS configuration. Not all
of this flexibility is exposed in the Caddyfile yet, but it may be in
the future. Caddy can also generate self-signed certificates in memory
for the convenience of a developer working on localhost who wants HTTPS.
And Caddy now supports the DNS challenge, assuming at least one DNS
provider is plugged in.
Dozens, if not hundreds, of other minor changes swept through the code
base as I literally started from an empty main function, copying over
functions or files as needed, then adjusting them to fit in the new
design. Most tests have been restored and adapted to the new API,
but more work is needed there.
A lot of what was "impossible" before is now possible, or can be made
possible with minimal disruption of the code. For example, it's fairly
easy to make plugins hook into another part of the code via callbacks.
Plugins can do more than just be directives; we now have plugins that
customize how the Caddyfile is loaded (useful when you need to get your
configuration from a remote store).
Site addresses no longer need be just a host and port. They can have a
path, allowing you to scope a configuration to a specific path. There is
no inheretance, however; each site configuration is distinct.
Thanks to amazing work by Lucas Clemente, this commit adds experimental
QUIC support. Turn it on using the -quic flag; your browser may have
to be configured to enable it.
Almost everything is here, but you will notice that most of the middle-
ware are missing. After those are transferred over, we'll be ready for
beta tests.
I'm very excited to get this out. Thanks for everyone's help and
patience these last few months. I hope you like it!!
2016-06-05 07:00:29 +08:00
|
|
|
|
|
|
|
// Config is the configuration with which the certificate was
|
|
|
|
// loaded or obtained and with which it should be maintained.
|
|
|
|
Config *Config
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
|
2016-02-19 11:33:15 +08:00
|
|
|
// getCertificate gets a certificate that matches name (a server name)
|
|
|
|
// from the in-memory cache. If there is no exact match for name, it
|
|
|
|
// will be checked against names of the form '*.example.com' (wildcard
|
|
|
|
// certificates) according to RFC 6125. If a match is found, matched will
|
|
|
|
// be true. If no matches are found, matched will be false and a default
|
|
|
|
// certificate will be returned with defaulted set to true. If no default
|
|
|
|
// certificate is set, defaulted will be set to false.
|
2016-02-11 15:06:05 +08:00
|
|
|
//
|
|
|
|
// The logic in this function is adapted from the Go standard library,
|
|
|
|
// which is by the Go Authors.
|
|
|
|
//
|
|
|
|
// This function is safe for concurrent use.
|
2016-02-19 11:33:15 +08:00
|
|
|
func getCertificate(name string) (cert Certificate, matched, defaulted bool) {
|
|
|
|
var ok bool
|
|
|
|
|
2016-02-11 15:06:05 +08:00
|
|
|
// Not going to trim trailing dots here since RFC 3546 says,
|
|
|
|
// "The hostname is represented ... without a trailing dot."
|
|
|
|
// Just normalize to lowercase.
|
|
|
|
name = strings.ToLower(name)
|
|
|
|
|
|
|
|
certCacheMu.RLock()
|
|
|
|
defer certCacheMu.RUnlock()
|
|
|
|
|
|
|
|
// exact match? great, let's use it
|
2016-02-19 11:33:15 +08:00
|
|
|
if cert, ok = certCache[name]; ok {
|
|
|
|
matched = true
|
|
|
|
return
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// try replacing labels in the name with wildcards until we get a match
|
|
|
|
labels := strings.Split(name, ".")
|
|
|
|
for i := range labels {
|
|
|
|
labels[i] = "*"
|
|
|
|
candidate := strings.Join(labels, ".")
|
2016-02-19 11:33:15 +08:00
|
|
|
if cert, ok = certCache[candidate]; ok {
|
|
|
|
matched = true
|
|
|
|
return
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-19 11:33:15 +08:00
|
|
|
// if nothing matches, use the default certificate or bust
|
|
|
|
cert, defaulted = certCache[""]
|
|
|
|
return
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
|
Rewrote Caddy from the ground up; initial commit of 0.9 branch
These changes span work from the last ~4 months in an effort to make
Caddy more extensible, reduce the coupling between its components, and
lay a more robust foundation of code going forward into 1.0. A bunch of
new features have been added, too, with even higher future potential.
The most significant design change is an overall inversion of
dependencies. Instead of the caddy package knowing about the server
and the notion of middleware and config, the caddy package exposes an
interface that other components plug into. This does introduce more
indirection when reading the code, but every piece is very modular and
pluggable. Even the HTTP server is pluggable.
The caddy package has been moved to the top level, and main has been
pushed into a subfolder called caddy. The actual logic of the main
file has been pushed even further into caddy/caddymain/run.go so that
custom builds of Caddy can be 'go get'able.
The HTTPS logic was surgically separated into two parts to divide the
TLS-specific code and the HTTPS-specific code. The caddytls package can
now be used by any type of server that needs TLS, not just HTTP. I also
added the ability to customize nearly every aspect of TLS at the site
level rather than all sites sharing the same TLS configuration. Not all
of this flexibility is exposed in the Caddyfile yet, but it may be in
the future. Caddy can also generate self-signed certificates in memory
for the convenience of a developer working on localhost who wants HTTPS.
And Caddy now supports the DNS challenge, assuming at least one DNS
provider is plugged in.
Dozens, if not hundreds, of other minor changes swept through the code
base as I literally started from an empty main function, copying over
functions or files as needed, then adjusting them to fit in the new
design. Most tests have been restored and adapted to the new API,
but more work is needed there.
A lot of what was "impossible" before is now possible, or can be made
possible with minimal disruption of the code. For example, it's fairly
easy to make plugins hook into another part of the code via callbacks.
Plugins can do more than just be directives; we now have plugins that
customize how the Caddyfile is loaded (useful when you need to get your
configuration from a remote store).
Site addresses no longer need be just a host and port. They can have a
path, allowing you to scope a configuration to a specific path. There is
no inheretance, however; each site configuration is distinct.
Thanks to amazing work by Lucas Clemente, this commit adds experimental
QUIC support. Turn it on using the -quic flag; your browser may have
to be configured to enable it.
Almost everything is here, but you will notice that most of the middle-
ware are missing. After those are transferred over, we'll be ready for
beta tests.
I'm very excited to get this out. Thanks for everyone's help and
patience these last few months. I hope you like it!!
2016-06-05 07:00:29 +08:00
|
|
|
// CacheManagedCertificate loads the certificate for domain into the
|
|
|
|
// cache, flagging it as Managed and, if onDemand is true, as "OnDemand"
|
2016-02-11 15:06:05 +08:00
|
|
|
// (meaning that it was obtained or loaded during a TLS handshake).
|
|
|
|
//
|
2017-02-22 00:49:22 +08:00
|
|
|
// This method is safe for concurrent use.
|
|
|
|
func (cfg *Config) CacheManagedCertificate(domain string) (Certificate, error) {
|
2016-07-08 21:32:31 +08:00
|
|
|
storage, err := cfg.StorageFor(cfg.CAUrl)
|
Rewrote Caddy from the ground up; initial commit of 0.9 branch
These changes span work from the last ~4 months in an effort to make
Caddy more extensible, reduce the coupling between its components, and
lay a more robust foundation of code going forward into 1.0. A bunch of
new features have been added, too, with even higher future potential.
The most significant design change is an overall inversion of
dependencies. Instead of the caddy package knowing about the server
and the notion of middleware and config, the caddy package exposes an
interface that other components plug into. This does introduce more
indirection when reading the code, but every piece is very modular and
pluggable. Even the HTTP server is pluggable.
The caddy package has been moved to the top level, and main has been
pushed into a subfolder called caddy. The actual logic of the main
file has been pushed even further into caddy/caddymain/run.go so that
custom builds of Caddy can be 'go get'able.
The HTTPS logic was surgically separated into two parts to divide the
TLS-specific code and the HTTPS-specific code. The caddytls package can
now be used by any type of server that needs TLS, not just HTTP. I also
added the ability to customize nearly every aspect of TLS at the site
level rather than all sites sharing the same TLS configuration. Not all
of this flexibility is exposed in the Caddyfile yet, but it may be in
the future. Caddy can also generate self-signed certificates in memory
for the convenience of a developer working on localhost who wants HTTPS.
And Caddy now supports the DNS challenge, assuming at least one DNS
provider is plugged in.
Dozens, if not hundreds, of other minor changes swept through the code
base as I literally started from an empty main function, copying over
functions or files as needed, then adjusting them to fit in the new
design. Most tests have been restored and adapted to the new API,
but more work is needed there.
A lot of what was "impossible" before is now possible, or can be made
possible with minimal disruption of the code. For example, it's fairly
easy to make plugins hook into another part of the code via callbacks.
Plugins can do more than just be directives; we now have plugins that
customize how the Caddyfile is loaded (useful when you need to get your
configuration from a remote store).
Site addresses no longer need be just a host and port. They can have a
path, allowing you to scope a configuration to a specific path. There is
no inheretance, however; each site configuration is distinct.
Thanks to amazing work by Lucas Clemente, this commit adds experimental
QUIC support. Turn it on using the -quic flag; your browser may have
to be configured to enable it.
Almost everything is here, but you will notice that most of the middle-
ware are missing. After those are transferred over, we'll be ready for
beta tests.
I'm very excited to get this out. Thanks for everyone's help and
patience these last few months. I hope you like it!!
2016-06-05 07:00:29 +08:00
|
|
|
if err != nil {
|
|
|
|
return Certificate{}, err
|
|
|
|
}
|
2016-07-08 21:32:31 +08:00
|
|
|
siteData, err := storage.LoadSite(domain)
|
|
|
|
if err != nil {
|
|
|
|
return Certificate{}, err
|
|
|
|
}
|
|
|
|
cert, err := makeCertificate(siteData.Cert, siteData.Key)
|
2016-02-11 15:06:05 +08:00
|
|
|
if err != nil {
|
|
|
|
return cert, err
|
|
|
|
}
|
Rewrote Caddy from the ground up; initial commit of 0.9 branch
These changes span work from the last ~4 months in an effort to make
Caddy more extensible, reduce the coupling between its components, and
lay a more robust foundation of code going forward into 1.0. A bunch of
new features have been added, too, with even higher future potential.
The most significant design change is an overall inversion of
dependencies. Instead of the caddy package knowing about the server
and the notion of middleware and config, the caddy package exposes an
interface that other components plug into. This does introduce more
indirection when reading the code, but every piece is very modular and
pluggable. Even the HTTP server is pluggable.
The caddy package has been moved to the top level, and main has been
pushed into a subfolder called caddy. The actual logic of the main
file has been pushed even further into caddy/caddymain/run.go so that
custom builds of Caddy can be 'go get'able.
The HTTPS logic was surgically separated into two parts to divide the
TLS-specific code and the HTTPS-specific code. The caddytls package can
now be used by any type of server that needs TLS, not just HTTP. I also
added the ability to customize nearly every aspect of TLS at the site
level rather than all sites sharing the same TLS configuration. Not all
of this flexibility is exposed in the Caddyfile yet, but it may be in
the future. Caddy can also generate self-signed certificates in memory
for the convenience of a developer working on localhost who wants HTTPS.
And Caddy now supports the DNS challenge, assuming at least one DNS
provider is plugged in.
Dozens, if not hundreds, of other minor changes swept through the code
base as I literally started from an empty main function, copying over
functions or files as needed, then adjusting them to fit in the new
design. Most tests have been restored and adapted to the new API,
but more work is needed there.
A lot of what was "impossible" before is now possible, or can be made
possible with minimal disruption of the code. For example, it's fairly
easy to make plugins hook into another part of the code via callbacks.
Plugins can do more than just be directives; we now have plugins that
customize how the Caddyfile is loaded (useful when you need to get your
configuration from a remote store).
Site addresses no longer need be just a host and port. They can have a
path, allowing you to scope a configuration to a specific path. There is
no inheretance, however; each site configuration is distinct.
Thanks to amazing work by Lucas Clemente, this commit adds experimental
QUIC support. Turn it on using the -quic flag; your browser may have
to be configured to enable it.
Almost everything is here, but you will notice that most of the middle-
ware are missing. After those are transferred over, we'll be ready for
beta tests.
I'm very excited to get this out. Thanks for everyone's help and
patience these last few months. I hope you like it!!
2016-06-05 07:00:29 +08:00
|
|
|
cert.Config = cfg
|
2016-02-11 15:06:05 +08:00
|
|
|
cacheCertificate(cert)
|
|
|
|
return cert, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// cacheUnmanagedCertificatePEMFile loads a certificate for host using certFile
|
|
|
|
// and keyFile, which must be in PEM format. It stores the certificate in
|
|
|
|
// memory. The Managed and OnDemand flags of the certificate will be set to
|
|
|
|
// false.
|
|
|
|
//
|
|
|
|
// This function is safe for concurrent use.
|
|
|
|
func cacheUnmanagedCertificatePEMFile(certFile, keyFile string) error {
|
|
|
|
cert, err := makeCertificateFromDisk(certFile, keyFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
cacheCertificate(cert)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// cacheUnmanagedCertificatePEMBytes makes a certificate out of the PEM bytes
|
|
|
|
// of the certificate and key, then caches it in memory.
|
|
|
|
//
|
|
|
|
// This function is safe for concurrent use.
|
|
|
|
func cacheUnmanagedCertificatePEMBytes(certBytes, keyBytes []byte) error {
|
|
|
|
cert, err := makeCertificate(certBytes, keyBytes)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
cacheCertificate(cert)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// makeCertificateFromDisk makes a Certificate by loading the
|
|
|
|
// certificate and key files. It fills out all the fields in
|
|
|
|
// the certificate except for the Managed and OnDemand flags.
|
|
|
|
// (It is up to the caller to set those.)
|
|
|
|
func makeCertificateFromDisk(certFile, keyFile string) (Certificate, error) {
|
|
|
|
certPEMBlock, err := ioutil.ReadFile(certFile)
|
|
|
|
if err != nil {
|
|
|
|
return Certificate{}, err
|
|
|
|
}
|
|
|
|
keyPEMBlock, err := ioutil.ReadFile(keyFile)
|
|
|
|
if err != nil {
|
|
|
|
return Certificate{}, err
|
|
|
|
}
|
|
|
|
return makeCertificate(certPEMBlock, keyPEMBlock)
|
|
|
|
}
|
|
|
|
|
|
|
|
// makeCertificate turns a certificate PEM bundle and a key PEM block into
|
|
|
|
// a Certificate, with OCSP and other relevant metadata tagged with it,
|
|
|
|
// except for the OnDemand and Managed flags. It is up to the caller to
|
|
|
|
// set those properties.
|
|
|
|
func makeCertificate(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
|
|
|
|
var cert Certificate
|
|
|
|
|
|
|
|
// Convert to a tls.Certificate
|
|
|
|
tlsCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
|
|
|
|
if err != nil {
|
|
|
|
return cert, err
|
|
|
|
}
|
|
|
|
|
2017-08-12 14:12:22 +08:00
|
|
|
// Extract relevant metadata and staple OCSP
|
|
|
|
err = fillCertFromLeaf(&cert, tlsCert)
|
2016-08-20 03:42:48 +08:00
|
|
|
if err != nil {
|
|
|
|
return cert, err
|
|
|
|
}
|
|
|
|
err = stapleOCSP(&cert, certPEMBlock)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("[WARNING] Stapling OCSP: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return cert, nil
|
|
|
|
}
|
|
|
|
|
2017-08-12 14:12:22 +08:00
|
|
|
// fillCertFromLeaf populates metadata fields on cert from tlsCert.
|
|
|
|
func fillCertFromLeaf(cert *Certificate, tlsCert tls.Certificate) error {
|
|
|
|
if len(tlsCert.Certificate) == 0 {
|
|
|
|
return errors.New("certificate is empty")
|
|
|
|
}
|
|
|
|
cert.Certificate = tlsCert
|
|
|
|
|
|
|
|
// the leaf cert should be the one for the site; it has what we need
|
|
|
|
leaf, err := x509.ParseCertificate(tlsCert.Certificate[0])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-02-11 15:06:05 +08:00
|
|
|
if leaf.Subject.CommonName != "" {
|
|
|
|
cert.Names = []string{strings.ToLower(leaf.Subject.CommonName)}
|
|
|
|
}
|
|
|
|
for _, name := range leaf.DNSNames {
|
|
|
|
if name != leaf.Subject.CommonName {
|
|
|
|
cert.Names = append(cert.Names, strings.ToLower(name))
|
|
|
|
}
|
|
|
|
}
|
2016-08-20 03:42:48 +08:00
|
|
|
for _, ip := range leaf.IPAddresses {
|
|
|
|
if ipStr := ip.String(); ipStr != leaf.Subject.CommonName {
|
|
|
|
cert.Names = append(cert.Names, strings.ToLower(ipStr))
|
|
|
|
}
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
2016-08-20 03:42:48 +08:00
|
|
|
for _, email := range leaf.EmailAddresses {
|
|
|
|
if email != leaf.Subject.CommonName {
|
|
|
|
cert.Names = append(cert.Names, strings.ToLower(email))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(cert.Names) == 0 {
|
|
|
|
return errors.New("certificate has no names")
|
|
|
|
}
|
2017-08-12 14:12:22 +08:00
|
|
|
|
2016-08-20 03:42:48 +08:00
|
|
|
cert.NotAfter = leaf.NotAfter
|
2017-08-12 14:12:22 +08:00
|
|
|
|
2016-08-20 03:42:48 +08:00
|
|
|
return nil
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// cacheCertificate adds cert to the in-memory cache. If the cache is
|
|
|
|
// empty, cert will be used as the default certificate. If the cache is
|
|
|
|
// full, random entries are deleted until there is room to map all the
|
|
|
|
// names on the certificate.
|
|
|
|
//
|
|
|
|
// This certificate will be keyed to the names in cert.Names. Any name
|
|
|
|
// that is already a key in the cache will be replaced with this cert.
|
|
|
|
//
|
|
|
|
// This function is safe for concurrent use.
|
|
|
|
func cacheCertificate(cert Certificate) {
|
2016-06-10 09:12:11 +08:00
|
|
|
if cert.Config == nil {
|
|
|
|
cert.Config = new(Config)
|
|
|
|
}
|
2016-02-11 15:06:05 +08:00
|
|
|
certCacheMu.Lock()
|
|
|
|
if _, ok := certCache[""]; !ok {
|
tls: Fix background certificate renewals that use TLS-SNI challenge
The loop which performs renewals in the background obtains a read lock
on the certificate cache map, so that it can be safely iterated. Before
this fix, it would obtain the renewals in the read lock. This has been
fine, except that the TLS-SNI challenge, when invoked after Caddy has
already started, requires adding a certificate to the cache. Doing this
requires an exclusive write lock. But it cannot obtain a write lock
because a read lock is obtained higher in the stack, while the loop
iterates. In other words, it's a deadlock.
I was able to reproduce this issue consistently locally, after jumping
through many hoops to force a renewal in a short time that bypasses
Let's Encrypt's authz caching. I was also able to verify that by queuing
renewals (like we do deletions and OCSP updates), lock contention is
relieved and the deadlock is avoided.
This only affects background renewals where the TLS-SNI(-01) challenge
are used. Users report seeing strange errors in the logs after this
happens ("tls: client offered an unsupported, maximum protocol version
of 301"), but I was not able to reproduce these locally. I was also not
able to reproduce the leak of sockets which are left in CLOSE_WAIT.
I am not sure if those are symptoms of running in production on Linux
and are related to this bug, or not.
Either way, this is an important fix. I do not yet know the ripple
effects this will have on other symptoms we've been chasing. But it
definitely resolves a deadlock during renewals.
2017-01-22 05:39:36 +08:00
|
|
|
// use as default - must be *appended* to end of list, or bad things happen!
|
2016-02-16 14:39:04 +08:00
|
|
|
cert.Names = append(cert.Names, "")
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
for len(certCache)+len(cert.Names) > 10000 {
|
|
|
|
// for simplicity, just remove random elements
|
|
|
|
for key := range certCache {
|
|
|
|
if key == "" { // ... but not the default cert
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
delete(certCache, key)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2017-08-12 14:12:22 +08:00
|
|
|
for i := 0; i < len(cert.Names); i++ {
|
|
|
|
name := cert.Names[i]
|
|
|
|
if _, ok := certCache[name]; ok {
|
|
|
|
// do not allow certificates to overlap in the names they serve;
|
|
|
|
// this ambiguity causes problems because it is confusing while
|
|
|
|
// maintaining certificates; see OCSP maintenance code and
|
|
|
|
// https://caddy.community/t/random-ocsp-response-errors-for-random-clients/2473?u=matt.
|
|
|
|
log.Printf("[NOTICE] There is already a certificate loaded for %s, "+
|
|
|
|
"so certificate for %v will not service that name",
|
|
|
|
name, cert.Names)
|
|
|
|
cert.Names = append(cert.Names[:i], cert.Names[i+1:]...)
|
|
|
|
i--
|
|
|
|
continue
|
|
|
|
}
|
2016-02-11 15:06:05 +08:00
|
|
|
certCache[name] = cert
|
|
|
|
}
|
|
|
|
certCacheMu.Unlock()
|
|
|
|
}
|
Rewrote Caddy from the ground up; initial commit of 0.9 branch
These changes span work from the last ~4 months in an effort to make
Caddy more extensible, reduce the coupling between its components, and
lay a more robust foundation of code going forward into 1.0. A bunch of
new features have been added, too, with even higher future potential.
The most significant design change is an overall inversion of
dependencies. Instead of the caddy package knowing about the server
and the notion of middleware and config, the caddy package exposes an
interface that other components plug into. This does introduce more
indirection when reading the code, but every piece is very modular and
pluggable. Even the HTTP server is pluggable.
The caddy package has been moved to the top level, and main has been
pushed into a subfolder called caddy. The actual logic of the main
file has been pushed even further into caddy/caddymain/run.go so that
custom builds of Caddy can be 'go get'able.
The HTTPS logic was surgically separated into two parts to divide the
TLS-specific code and the HTTPS-specific code. The caddytls package can
now be used by any type of server that needs TLS, not just HTTP. I also
added the ability to customize nearly every aspect of TLS at the site
level rather than all sites sharing the same TLS configuration. Not all
of this flexibility is exposed in the Caddyfile yet, but it may be in
the future. Caddy can also generate self-signed certificates in memory
for the convenience of a developer working on localhost who wants HTTPS.
And Caddy now supports the DNS challenge, assuming at least one DNS
provider is plugged in.
Dozens, if not hundreds, of other minor changes swept through the code
base as I literally started from an empty main function, copying over
functions or files as needed, then adjusting them to fit in the new
design. Most tests have been restored and adapted to the new API,
but more work is needed there.
A lot of what was "impossible" before is now possible, or can be made
possible with minimal disruption of the code. For example, it's fairly
easy to make plugins hook into another part of the code via callbacks.
Plugins can do more than just be directives; we now have plugins that
customize how the Caddyfile is loaded (useful when you need to get your
configuration from a remote store).
Site addresses no longer need be just a host and port. They can have a
path, allowing you to scope a configuration to a specific path. There is
no inheretance, however; each site configuration is distinct.
Thanks to amazing work by Lucas Clemente, this commit adds experimental
QUIC support. Turn it on using the -quic flag; your browser may have
to be configured to enable it.
Almost everything is here, but you will notice that most of the middle-
ware are missing. After those are transferred over, we'll be ready for
beta tests.
I'm very excited to get this out. Thanks for everyone's help and
patience these last few months. I hope you like it!!
2016-06-05 07:00:29 +08:00
|
|
|
|
|
|
|
// uncacheCertificate deletes name's certificate from the
|
|
|
|
// cache. If name is not a key in the certificate cache,
|
|
|
|
// this function does nothing.
|
|
|
|
func uncacheCertificate(name string) {
|
|
|
|
certCacheMu.Lock()
|
|
|
|
delete(certCache, name)
|
|
|
|
certCacheMu.Unlock()
|
|
|
|
}
|