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-04 18:00:29 -05:00
|
|
|
package caddytls
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
|
|
|
"crypto/x509"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"time"
|
|
|
|
|
2016-06-20 12:44:20 -05:00
|
|
|
"github.com/mholt/caddy"
|
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-04 18:00:29 -05:00
|
|
|
"github.com/xenolf/lego/acme"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Config describes how TLS should be configured and used.
|
|
|
|
type Config struct {
|
|
|
|
// The hostname or class of hostnames this config is
|
|
|
|
// designated for; can contain wildcard characters
|
|
|
|
// according to RFC 6125 §6.4.3 - this field MUST
|
2016-06-09 20:12:11 -05:00
|
|
|
// be set in order for things to work as expected
|
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-04 18:00:29 -05:00
|
|
|
Hostname string
|
|
|
|
|
|
|
|
// Whether TLS is enabled
|
|
|
|
Enabled bool
|
|
|
|
|
|
|
|
// Minimum and maximum protocol versions to allow
|
|
|
|
ProtocolMinVersion uint16
|
|
|
|
ProtocolMaxVersion uint16
|
|
|
|
|
|
|
|
// The list of cipher suites; first should be
|
|
|
|
// TLS_FALLBACK_SCSV to prevent degrade attacks
|
|
|
|
Ciphers []uint16
|
|
|
|
|
|
|
|
// Whether to prefer server cipher suites
|
|
|
|
PreferServerCipherSuites bool
|
|
|
|
|
|
|
|
// Client authentication policy
|
|
|
|
ClientAuth tls.ClientAuthType
|
|
|
|
|
|
|
|
// List of client CA certificates to allow, if
|
|
|
|
// client authentication is enabled
|
|
|
|
ClientCerts []string
|
|
|
|
|
|
|
|
// Manual means user provides own certs and keys
|
|
|
|
Manual bool
|
|
|
|
|
|
|
|
// Managed means config qualifies for implicit,
|
|
|
|
// automatic, managed TLS; as opposed to the user
|
|
|
|
// providing and managing the certificate manually
|
|
|
|
Managed bool
|
|
|
|
|
|
|
|
// OnDemand means the class of hostnames this
|
|
|
|
// config applies to may obtain and manage
|
|
|
|
// certificates at handshake-time (as opposed
|
|
|
|
// to pre-loaded at startup); OnDemand certs
|
|
|
|
// will be managed the same way as preloaded
|
|
|
|
// ones, however, if an OnDemand cert fails to
|
|
|
|
// renew, it is removed from the in-memory
|
|
|
|
// cache; if this is true, Managed must
|
|
|
|
// necessarily be true
|
|
|
|
OnDemand bool
|
|
|
|
|
|
|
|
// SelfSigned means that this hostname is
|
|
|
|
// served with a self-signed certificate
|
|
|
|
// that we generated in memory for convenience
|
|
|
|
SelfSigned bool
|
|
|
|
|
|
|
|
// The endpoint of the directory for the ACME
|
|
|
|
// CA we are to use
|
|
|
|
CAUrl string
|
|
|
|
|
|
|
|
// The host (ONLY the host, not port) to listen
|
|
|
|
//on if necessary to start a a listener to solve
|
|
|
|
// an ACME challenge
|
|
|
|
ListenHost string
|
|
|
|
|
|
|
|
// The alternate port (ONLY port, not host)
|
|
|
|
// to use for the ACME HTTP challenge; this
|
|
|
|
// port will be used if we proxy challenges
|
|
|
|
// coming in on port 80 to this alternate port
|
|
|
|
AltHTTPPort string
|
|
|
|
|
|
|
|
// The string identifier of the DNS provider
|
|
|
|
// to use when solving the ACME DNS challenge
|
|
|
|
DNSProvider string
|
|
|
|
|
|
|
|
// The email address to use when creating or
|
|
|
|
// using an ACME account (fun fact: if this
|
|
|
|
// is set to "off" then this config will not
|
|
|
|
// qualify for managed TLS)
|
|
|
|
ACMEEmail string
|
|
|
|
|
|
|
|
// The type of key to use when generating
|
|
|
|
// certificates
|
|
|
|
KeyType acme.KeyType
|
|
|
|
}
|
|
|
|
|
|
|
|
// ObtainCert obtains a certificate for c.Hostname, as long as a certificate
|
|
|
|
// does not already exist in storage on disk. It only obtains and stores
|
|
|
|
// certificates (and their keys) to disk, it does not load them into memory.
|
|
|
|
// If allowPrompts is true, the user may be shown a prompt. If proxyACME is
|
|
|
|
// true, the relevant ACME challenges will be proxied to the alternate port.
|
|
|
|
func (c *Config) ObtainCert(allowPrompts bool) error {
|
|
|
|
return c.obtainCertName(c.Hostname, allowPrompts)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Config) obtainCertName(name string, allowPrompts bool) error {
|
|
|
|
storage, err := StorageFor(c.CAUrl)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !c.Managed || !HostQualifies(name) || existingCertAndKey(storage, name) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.ACMEEmail == "" {
|
|
|
|
c.ACMEEmail = getEmail(storage, allowPrompts)
|
|
|
|
}
|
|
|
|
|
|
|
|
client, err := newACMEClient(c, allowPrompts)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return client.Obtain([]string{name})
|
|
|
|
}
|
|
|
|
|
|
|
|
// RenewCert renews the certificate for c.Hostname.
|
|
|
|
func (c *Config) RenewCert(allowPrompts bool) error {
|
|
|
|
return c.renewCertName(c.Hostname, allowPrompts)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Config) renewCertName(name string, allowPrompts bool) error {
|
|
|
|
storage, err := StorageFor(c.CAUrl)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prepare for renewal (load PEM cert, key, and meta)
|
|
|
|
certBytes, err := ioutil.ReadFile(storage.SiteCertFile(c.Hostname))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
keyBytes, err := ioutil.ReadFile(storage.SiteKeyFile(c.Hostname))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
metaBytes, err := ioutil.ReadFile(storage.SiteMetaFile(c.Hostname))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var certMeta acme.CertificateResource
|
|
|
|
err = json.Unmarshal(metaBytes, &certMeta)
|
|
|
|
certMeta.Certificate = certBytes
|
|
|
|
certMeta.PrivateKey = keyBytes
|
|
|
|
|
|
|
|
client, err := newACMEClient(c, allowPrompts)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform renewal and retry if necessary, but not too many times.
|
|
|
|
var newCertMeta acme.CertificateResource
|
|
|
|
var success bool
|
|
|
|
for attempts := 0; attempts < 2; attempts++ {
|
|
|
|
acmeMu.Lock()
|
|
|
|
newCertMeta, err = client.RenewCertificate(certMeta, true)
|
|
|
|
acmeMu.Unlock()
|
|
|
|
if err == nil {
|
|
|
|
success = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the legal terms were updated and need to be
|
|
|
|
// agreed to again, we can handle that.
|
|
|
|
if _, ok := err.(acme.TOSError); ok {
|
|
|
|
err := client.AgreeToTOS()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// For any other kind of error, wait 10s and try again.
|
|
|
|
time.Sleep(10 * time.Second)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !success {
|
|
|
|
return errors.New("too many renewal attempts; last error: " + err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return saveCertResource(storage, newCertMeta)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MakeTLSConfig reduces configs into a single tls.Config.
|
|
|
|
// If TLS is to be disabled, a nil tls.Config will be returned.
|
|
|
|
func MakeTLSConfig(configs []*Config) (*tls.Config, error) {
|
|
|
|
if configs == nil || len(configs) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
config := new(tls.Config)
|
|
|
|
ciphersAdded := make(map[uint16]struct{})
|
|
|
|
configMap := make(configGroup)
|
|
|
|
|
|
|
|
for i, cfg := range configs {
|
|
|
|
if cfg == nil {
|
|
|
|
// avoid nil pointer dereference below
|
|
|
|
configs[i] = new(Config)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Key this config by its hostname; this
|
|
|
|
// overwrites configs with the same hostname
|
|
|
|
configMap[cfg.Hostname] = cfg
|
|
|
|
|
|
|
|
// Can't serve TLS and not-TLS on same port
|
|
|
|
if i > 0 && cfg.Enabled != configs[i-1].Enabled {
|
|
|
|
thisConfProto, lastConfProto := "not TLS", "not TLS"
|
|
|
|
if cfg.Enabled {
|
|
|
|
thisConfProto = "TLS"
|
|
|
|
}
|
|
|
|
if configs[i-1].Enabled {
|
|
|
|
lastConfProto = "TLS"
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("cannot multiplex %s (%s) and %s (%s) on same listener",
|
|
|
|
configs[i-1].Hostname, lastConfProto, cfg.Hostname, thisConfProto)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Union cipher suites
|
|
|
|
for _, ciph := range cfg.Ciphers {
|
|
|
|
if _, ok := ciphersAdded[ciph]; !ok {
|
|
|
|
ciphersAdded[ciph] = struct{}{}
|
|
|
|
config.CipherSuites = append(config.CipherSuites, ciph)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Can't resolve conflicting PreferServerCipherSuites settings
|
|
|
|
if i > 0 && cfg.PreferServerCipherSuites != configs[i-1].PreferServerCipherSuites {
|
|
|
|
return nil, fmt.Errorf("cannot both use PreferServerCipherSuites and not use it")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Go with the widest range of protocol versions
|
|
|
|
if cfg.ProtocolMinVersion < config.MinVersion {
|
|
|
|
config.MinVersion = cfg.ProtocolMinVersion
|
|
|
|
}
|
|
|
|
if cfg.ProtocolMaxVersion < config.MaxVersion {
|
|
|
|
config.MaxVersion = cfg.ProtocolMaxVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
// Go with the strictest ClientAuth type
|
|
|
|
if cfg.ClientAuth > config.ClientAuth {
|
|
|
|
config.ClientAuth = cfg.ClientAuth
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Is TLS disabled? If so, we're done here.
|
|
|
|
// By now, we know that all configs agree
|
|
|
|
// whether it is or not, so we can just look
|
|
|
|
// at the first one.
|
|
|
|
if len(configs) == 0 || !configs[0].Enabled {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default cipher suites
|
|
|
|
if len(config.CipherSuites) == 0 {
|
|
|
|
config.CipherSuites = defaultCiphers
|
|
|
|
}
|
|
|
|
|
|
|
|
// For security, ensure TLS_FALLBACK_SCSV is always included
|
|
|
|
if config.CipherSuites[0] != tls.TLS_FALLBACK_SCSV {
|
|
|
|
config.CipherSuites = append([]uint16{tls.TLS_FALLBACK_SCSV}, config.CipherSuites...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up client authentication if enabled
|
|
|
|
if config.ClientAuth != tls.NoClientCert {
|
|
|
|
pool := x509.NewCertPool()
|
|
|
|
clientCertsAdded := make(map[string]struct{})
|
|
|
|
for _, cfg := range configs {
|
|
|
|
for _, caFile := range cfg.ClientCerts {
|
|
|
|
// don't add cert to pool more than once
|
|
|
|
if _, ok := clientCertsAdded[caFile]; ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
clientCertsAdded[caFile] = struct{}{}
|
|
|
|
|
|
|
|
// Any client with a certificate from this CA will be allowed to connect
|
|
|
|
caCrt, err := ioutil.ReadFile(caFile)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !pool.AppendCertsFromPEM(caCrt) {
|
|
|
|
return nil, fmt.Errorf("error loading client certificate '%s': no certificates were successfully parsed", caFile)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
config.ClientCAs = pool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Associate the GetCertificate callback, or almost nothing we just did will work
|
|
|
|
config.GetCertificate = configMap.GetCertificate
|
|
|
|
|
|
|
|
return config, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ConfigGetter gets a Config keyed by key.
|
2016-06-20 12:44:20 -05:00
|
|
|
type ConfigGetter func(c *caddy.Controller) *Config
|
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-04 18:00:29 -05:00
|
|
|
|
|
|
|
var configGetters = make(map[string]ConfigGetter)
|
|
|
|
|
|
|
|
// RegisterConfigGetter registers fn as the way to get a
|
|
|
|
// Config for server type serverType.
|
|
|
|
func RegisterConfigGetter(serverType string, fn ConfigGetter) {
|
|
|
|
configGetters[serverType] = fn
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetDefaultTLSParams sets the default TLS cipher suites, protocol versions,
|
|
|
|
// and server preferences of a server.Config if they were not previously set
|
|
|
|
// (it does not overwrite; only fills in missing values).
|
|
|
|
func SetDefaultTLSParams(config *Config) {
|
|
|
|
// If no ciphers provided, use default list
|
|
|
|
if len(config.Ciphers) == 0 {
|
|
|
|
config.Ciphers = defaultCiphers
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not a cipher suite, but still important for mitigating protocol downgrade attacks
|
|
|
|
// (prepend since having it at end breaks http2 due to non-h2-approved suites before it)
|
|
|
|
config.Ciphers = append([]uint16{tls.TLS_FALLBACK_SCSV}, config.Ciphers...)
|
|
|
|
|
|
|
|
// Set default protocol min and max versions - must balance compatibility and security
|
|
|
|
if config.ProtocolMinVersion == 0 {
|
|
|
|
config.ProtocolMinVersion = tls.VersionTLS11
|
|
|
|
}
|
|
|
|
if config.ProtocolMaxVersion == 0 {
|
|
|
|
config.ProtocolMaxVersion = tls.VersionTLS12
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prefer server cipher suites
|
|
|
|
config.PreferServerCipherSuites = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Map of supported key types
|
|
|
|
var supportedKeyTypes = map[string]acme.KeyType{
|
|
|
|
"P384": acme.EC384,
|
|
|
|
"P256": acme.EC256,
|
|
|
|
"RSA8192": acme.RSA8192,
|
|
|
|
"RSA4096": acme.RSA4096,
|
|
|
|
"RSA2048": acme.RSA2048,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Map of supported protocols.
|
|
|
|
// HTTP/2 only supports TLS 1.2 and higher.
|
|
|
|
var supportedProtocols = map[string]uint16{
|
|
|
|
"tls1.0": tls.VersionTLS10,
|
|
|
|
"tls1.1": tls.VersionTLS11,
|
|
|
|
"tls1.2": tls.VersionTLS12,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Map of supported ciphers, used only for parsing config.
|
|
|
|
//
|
|
|
|
// Note that, at time of writing, HTTP/2 blacklists 276 cipher suites,
|
|
|
|
// including all but four of the suites below (the four GCM suites).
|
|
|
|
// See https://http2.github.io/http2-spec/#BadCipherSuites
|
|
|
|
//
|
|
|
|
// TLS_FALLBACK_SCSV is not in this list because we manually ensure
|
|
|
|
// it is always added (even though it is not technically a cipher suite).
|
|
|
|
//
|
|
|
|
// This map, like any map, is NOT ORDERED. Do not range over this map.
|
|
|
|
var supportedCiphersMap = map[string]uint16{
|
|
|
|
"ECDHE-RSA-AES256-GCM-SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
|
|
"ECDHE-ECDSA-AES256-GCM-SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
|
|
"ECDHE-RSA-AES128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
|
|
"ECDHE-ECDSA-AES128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
|
|
"ECDHE-RSA-AES128-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
|
|
"ECDHE-RSA-AES256-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
|
|
|
"ECDHE-ECDSA-AES256-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
|
|
|
"ECDHE-ECDSA-AES128-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
|
|
|
"RSA-AES128-CBC-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
|
|
"RSA-AES256-CBC-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
|
|
"ECDHE-RSA-3DES-EDE-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
|
|
"RSA-3DES-EDE-CBC-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
|
|
}
|
|
|
|
|
|
|
|
// List of supported cipher suites in descending order of preference.
|
|
|
|
// Ordering is very important! Getting the wrong order will break
|
|
|
|
// mainstream clients, especially with HTTP/2.
|
|
|
|
//
|
|
|
|
// Note that TLS_FALLBACK_SCSV is not in this list since it is always
|
|
|
|
// added manually.
|
|
|
|
var supportedCiphers = []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_RSA_WITH_AES_256_CBC_SHA,
|
|
|
|
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
|
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
|
|
|
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
|
|
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
|
|
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
|
|
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
|
|
}
|
|
|
|
|
|
|
|
// List of all the ciphers we want to use by default
|
|
|
|
var defaultCiphers = []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_RSA_WITH_AES_256_CBC_SHA,
|
|
|
|
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
|
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
|
|
|
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
|
|
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
// HTTPChallengePort is the officially designated port for
|
|
|
|
// the HTTP challenge.
|
|
|
|
HTTPChallengePort = "80"
|
|
|
|
|
|
|
|
// TLSSNIChallengePort is the officially designated port for
|
|
|
|
// the TLS-SNI challenge.
|
|
|
|
TLSSNIChallengePort = "443"
|
|
|
|
|
|
|
|
// DefaultHTTPAlternatePort is the port on which the ACME
|
|
|
|
// client will open a listener and solve the HTTP challenge.
|
|
|
|
// If this alternate port is used instead of the default
|
|
|
|
// port, then whatever is listening on the default port must
|
|
|
|
// be capable of proxying or forwarding the request to this
|
|
|
|
// alternate port.
|
|
|
|
DefaultHTTPAlternatePort = "5033"
|
|
|
|
)
|