2017-09-23 13:56:58 +08:00
|
|
|
// Copyright 2015 Light Code Labs, LLC
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
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"
|
2018-12-06 08:33:23 +08:00
|
|
|
"encoding/json"
|
2016-02-11 15:06:05 +08:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
2017-11-04 13:01:30 +08:00
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2018-12-06 08:33:23 +08:00
|
|
|
"os"
|
2016-02-12 04:48:52 +08:00
|
|
|
"strings"
|
2016-02-11 15:06:05 +08:00
|
|
|
"sync"
|
2016-02-12 04:48:52 +08:00
|
|
|
"sync/atomic"
|
2016-02-11 15:06:05 +08:00
|
|
|
"time"
|
2018-02-11 03:59:23 +08:00
|
|
|
|
2018-03-23 08:05:31 +08:00
|
|
|
"github.com/mholt/caddy/telemetry"
|
2018-12-06 08:33:23 +08:00
|
|
|
"github.com/xenolf/lego/acme"
|
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
|
|
|
// configGroup is a type that keys configs by their hostname
|
|
|
|
// (hostnames can have wildcard characters; use the getConfig
|
2017-02-22 00:49:22 +08:00
|
|
|
// method to get a config by matching its hostname).
|
|
|
|
type configGroup map[string]*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-05 07:00:29 +08:00
|
|
|
|
|
|
|
// getConfig gets the config by the first key match for name.
|
|
|
|
// In other words, "sub.foo.bar" will get the config for "*.foo.bar"
|
2017-02-22 00:49:22 +08:00
|
|
|
// if that is the closest match. If no match is found, the first
|
|
|
|
// (random) config will be loaded, which will defer any TLS alerts
|
|
|
|
// to the certificate validation (this may or may not be ideal;
|
|
|
|
// let's talk about it if this becomes problematic).
|
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
|
|
|
// This function follows nearly the same logic to lookup
|
|
|
|
// a hostname as the getCertificate function uses.
|
2017-02-22 00:49:22 +08:00
|
|
|
func (cg configGroup) getConfig(name string) *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-05 07:00:29 +08:00
|
|
|
name = strings.ToLower(name)
|
|
|
|
|
|
|
|
// exact match? great, let's use it
|
|
|
|
if config, ok := cg[name]; ok {
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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, ".")
|
|
|
|
if config, ok := cg[candidate]; ok {
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-18 07:03:12 +08:00
|
|
|
// try a config that serves all names (the above
|
|
|
|
// loop doesn't try empty string; for hosts defined
|
2018-11-13 05:24:07 +08:00
|
|
|
// with only a port, for instance, like ":443") -
|
|
|
|
// also known as the default 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-05 07:00:29 +08:00
|
|
|
if config, ok := cg[""]; ok {
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
|
2017-02-22 00:49:22 +08:00
|
|
|
// GetConfigForClient gets a TLS configuration satisfying clientHello.
|
|
|
|
// In getting the configuration, it abides the rules and settings
|
|
|
|
// defined in the Config that matches clientHello.ServerName. If no
|
|
|
|
// tls.Config is set on the matching Config, a nil value is returned.
|
|
|
|
//
|
|
|
|
// This method is safe for use as a tls.Config.GetConfigForClient callback.
|
|
|
|
func (cg configGroup) GetConfigForClient(clientHello *tls.ClientHelloInfo) (*tls.Config, error) {
|
|
|
|
config := cg.getConfig(clientHello.ServerName)
|
|
|
|
if config != nil {
|
|
|
|
return config.tlsConfig, nil
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
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
|
|
|
// GetCertificate gets a certificate to satisfy clientHello. In getting
|
|
|
|
// the certificate, it abides the rules and settings defined in the
|
|
|
|
// Config that matches clientHello.ServerName. It first checks the in-
|
2016-06-21 08:25:31 +08:00
|
|
|
// memory cache, then, if the config enables "OnDemand", it accesses
|
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
|
|
|
// disk, then accesses the network if it must obtain a new certificate
|
|
|
|
// via ACME.
|
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
|
|
|
// This method is safe for use as a tls.Config.GetCertificate callback.
|
2017-02-22 00:49:22 +08:00
|
|
|
func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
2018-05-08 07:04:39 +08:00
|
|
|
if ClientHelloTelemetry && len(clientHello.SupportedVersions) > 0 {
|
2018-05-08 06:09:39 +08:00
|
|
|
// If no other plugin (such as the HTTP server type) is implementing ClientHello telemetry, we do it.
|
|
|
|
// NOTE: The values in the Go standard lib's ClientHelloInfo aren't guaranteed to be in order.
|
|
|
|
info := ClientHelloInfo{
|
|
|
|
Version: clientHello.SupportedVersions[0], // report the highest
|
|
|
|
CipherSuites: clientHello.CipherSuites,
|
|
|
|
ExtensionsUnknown: true, // no extension info... :(
|
|
|
|
CompressionMethodsUnknown: true, // no compression methods... :(
|
2018-08-25 06:43:56 +08:00
|
|
|
Curves: clientHello.SupportedCurves,
|
|
|
|
Points: clientHello.SupportedPoints,
|
2018-05-08 06:09:39 +08:00
|
|
|
// We also have, but do not yet use: SignatureSchemes, ServerName, and SupportedProtos (ALPN)
|
|
|
|
// because the standard lib parses some extensions, but our MITM detector generally doesn't.
|
|
|
|
}
|
|
|
|
go telemetry.SetNested("tls_client_hello", info.Key(), info)
|
|
|
|
}
|
|
|
|
|
2018-12-06 08:33:23 +08:00
|
|
|
// special case: serve up the certificate for a TLS-ALPN ACME challenge
|
|
|
|
// (https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05)
|
|
|
|
for _, proto := range clientHello.SupportedProtos {
|
|
|
|
if proto == acme.ACMETLS1Protocol {
|
|
|
|
cfg.certCache.RLock()
|
|
|
|
challengeCert, ok := cfg.certCache.cache[tlsALPNCertKeyName(clientHello.ServerName)]
|
|
|
|
cfg.certCache.RUnlock()
|
|
|
|
if !ok {
|
|
|
|
// see if this challenge was started in a cluster; try distributed challenge solver
|
|
|
|
// (note that the tls.Config's ALPN settings must include the ACME TLS-ALPN challenge
|
|
|
|
// protocol string, otherwise a valid certificate will not solve the challenge; we
|
|
|
|
// should already have taken care of that when we made the tls.Config)
|
|
|
|
challengeCert, ok, err := cfg.tryDistributedChallengeSolver(clientHello)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("[ERROR][%s] TLS-ALPN: %v", clientHello.ServerName, err)
|
|
|
|
}
|
|
|
|
if ok {
|
|
|
|
return &challengeCert.Certificate, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("no certificate to complete TLS-ALPN challenge for SNI name: %s", clientHello.ServerName)
|
|
|
|
}
|
|
|
|
return &challengeCert.Certificate, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-08 06:09:39 +08:00
|
|
|
// get the certificate and serve it up
|
2017-02-22 00:49:22 +08:00
|
|
|
cert, err := cfg.getCertDuringHandshake(strings.ToLower(clientHello.ServerName), true, true)
|
2018-03-22 07:01:14 +08:00
|
|
|
if err == nil {
|
2018-03-26 11:50:07 +08:00
|
|
|
go telemetry.Increment("tls_handshake_count") // TODO: This is a "best guess" for now, we need something listener-level
|
2018-03-22 07:01:14 +08:00
|
|
|
}
|
2016-02-16 14:39:04 +08:00
|
|
|
return &cert.Certificate, err
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
|
tls: Restructure and improve certificate management
- Expose the list of Caddy instances through caddy.Instances()
- Added arbitrary storage to caddy.Instance
- The cache of loaded certificates is no longer global; now scoped
per-instance, meaning upon reload (like SIGUSR1) the old cert cache
will be discarded entirely, whereas before, aggressively reloading
config that added and removed lots of sites would cause unnecessary
build-up in the cache over time.
- Key certificates in the cache by their SHA-256 hash instead of
by their names. This means certificates will not be duplicated in
memory (within each instance), making Caddy much more memory-efficient
for large-scale deployments with thousands of sites sharing certs.
- Perform name-to-certificate lookups scoped per caddytls.Config instead
of a single global lookup. This prevents certificates from stepping on
each other when they overlap in their names.
- Do not allow TLS configurations keyed by the same hostname to be
different; this now throws an error.
- Updated relevant tests, with a stark awareness that more tests are
needed.
- Change the NewContext function signature to include an *Instance.
- Strongly recommend (basically require) use of caddytls.NewConfig()
to create a new *caddytls.Config, to ensure pointers to the instance
certificate cache are initialized properly.
- Update the TLS-SNI challenge solver (even though TLS-SNI is disabled
currently on the CA side). Store temporary challenge cert in instance
cache, but do so directly by the ACME challenge name, not the hash.
Modified the getCertificate function to check the cache directly for
a name match if one isn't found otherwise. This will allow any
caddytls.Config to be able to help solve a TLS-SNI challenge, with one
extra side-effect that might actually be kind of interesting (and
useless): clients could send a certificate's hash as the SNI and
Caddy would be able to serve that certificate for the handshake.
- Do not attempt to match a "default" (random) certificate when SNI
is present but unrecognized; return no certificate so a TLS alert
happens instead.
- Store an Instance in the list of instances even while the instance
is still starting up (this allows access to the cert cache for
performing renewals at startup, etc). Will be removed from list again
if instance startup fails.
- Laid groundwork for ACMEv2 and Let's Encrypt wildcard support.
Server type plugins will need to be updated slightly to accommodate
minor adjustments to their API (like passing in an Instance). This
commit includes the changes for the HTTP server.
Certain Caddyfile configurations might error out with this change, if
they configured different TLS settings for the same hostname.
This change trades some complexity for other complexity, but ultimately
this new complexity is more correct and robust than earlier logic.
Fixes #1991
Fixes #1994
Fixes #1303
2018-02-04 15:58:27 +08:00
|
|
|
// getCertificate gets a certificate that matches name (a server name)
|
|
|
|
// from the in-memory cache, according to the lookup table associated with
|
|
|
|
// cfg. The lookup then points to a certificate in the Instance certificate
|
|
|
|
// 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 defaulted is false, then no certificates were available.
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
func (cfg *Config) getCertificate(name string) (cert Certificate, matched, defaulted bool) {
|
|
|
|
var certKey string
|
|
|
|
var ok bool
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
|
|
|
cfg.certCache.RLock()
|
|
|
|
defer cfg.certCache.RUnlock()
|
|
|
|
|
|
|
|
// exact match? great, let's use it
|
|
|
|
if certKey, ok = cfg.Certificates[name]; ok {
|
|
|
|
cert = cfg.certCache.cache[certKey]
|
|
|
|
matched = true
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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, ".")
|
|
|
|
if certKey, ok = cfg.Certificates[candidate]; ok {
|
|
|
|
cert = cfg.certCache.cache[certKey]
|
|
|
|
matched = true
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check the certCache directly to see if the SNI name is
|
2018-12-06 08:33:23 +08:00
|
|
|
// already the key of the certificate it wants; this implies
|
|
|
|
// that the SNI can contain the hash of a specific cert
|
|
|
|
// (chain) it wants and we will still be able to serveit up
|
tls: Restructure and improve certificate management
- Expose the list of Caddy instances through caddy.Instances()
- Added arbitrary storage to caddy.Instance
- The cache of loaded certificates is no longer global; now scoped
per-instance, meaning upon reload (like SIGUSR1) the old cert cache
will be discarded entirely, whereas before, aggressively reloading
config that added and removed lots of sites would cause unnecessary
build-up in the cache over time.
- Key certificates in the cache by their SHA-256 hash instead of
by their names. This means certificates will not be duplicated in
memory (within each instance), making Caddy much more memory-efficient
for large-scale deployments with thousands of sites sharing certs.
- Perform name-to-certificate lookups scoped per caddytls.Config instead
of a single global lookup. This prevents certificates from stepping on
each other when they overlap in their names.
- Do not allow TLS configurations keyed by the same hostname to be
different; this now throws an error.
- Updated relevant tests, with a stark awareness that more tests are
needed.
- Change the NewContext function signature to include an *Instance.
- Strongly recommend (basically require) use of caddytls.NewConfig()
to create a new *caddytls.Config, to ensure pointers to the instance
certificate cache are initialized properly.
- Update the TLS-SNI challenge solver (even though TLS-SNI is disabled
currently on the CA side). Store temporary challenge cert in instance
cache, but do so directly by the ACME challenge name, not the hash.
Modified the getCertificate function to check the cache directly for
a name match if one isn't found otherwise. This will allow any
caddytls.Config to be able to help solve a TLS-SNI challenge, with one
extra side-effect that might actually be kind of interesting (and
useless): clients could send a certificate's hash as the SNI and
Caddy would be able to serve that certificate for the handshake.
- Do not attempt to match a "default" (random) certificate when SNI
is present but unrecognized; return no certificate so a TLS alert
happens instead.
- Store an Instance in the list of instances even while the instance
is still starting up (this allows access to the cert cache for
performing renewals at startup, etc). Will be removed from list again
if instance startup fails.
- Laid groundwork for ACMEv2 and Let's Encrypt wildcard support.
Server type plugins will need to be updated slightly to accommodate
minor adjustments to their API (like passing in an Instance). This
commit includes the changes for the HTTP server.
Certain Caddyfile configurations might error out with this change, if
they configured different TLS settings for the same hostname.
This change trades some complexity for other complexity, but ultimately
this new complexity is more correct and robust than earlier logic.
Fixes #1991
Fixes #1994
Fixes #1303
2018-02-04 15:58:27 +08:00
|
|
|
// (this behavior, by the way, could be controversial as to
|
|
|
|
// whether it complies with RFC 6066 about SNI, but I think
|
2018-12-06 08:33:23 +08:00
|
|
|
// it does, soooo...)
|
tls: Restructure and improve certificate management
- Expose the list of Caddy instances through caddy.Instances()
- Added arbitrary storage to caddy.Instance
- The cache of loaded certificates is no longer global; now scoped
per-instance, meaning upon reload (like SIGUSR1) the old cert cache
will be discarded entirely, whereas before, aggressively reloading
config that added and removed lots of sites would cause unnecessary
build-up in the cache over time.
- Key certificates in the cache by their SHA-256 hash instead of
by their names. This means certificates will not be duplicated in
memory (within each instance), making Caddy much more memory-efficient
for large-scale deployments with thousands of sites sharing certs.
- Perform name-to-certificate lookups scoped per caddytls.Config instead
of a single global lookup. This prevents certificates from stepping on
each other when they overlap in their names.
- Do not allow TLS configurations keyed by the same hostname to be
different; this now throws an error.
- Updated relevant tests, with a stark awareness that more tests are
needed.
- Change the NewContext function signature to include an *Instance.
- Strongly recommend (basically require) use of caddytls.NewConfig()
to create a new *caddytls.Config, to ensure pointers to the instance
certificate cache are initialized properly.
- Update the TLS-SNI challenge solver (even though TLS-SNI is disabled
currently on the CA side). Store temporary challenge cert in instance
cache, but do so directly by the ACME challenge name, not the hash.
Modified the getCertificate function to check the cache directly for
a name match if one isn't found otherwise. This will allow any
caddytls.Config to be able to help solve a TLS-SNI challenge, with one
extra side-effect that might actually be kind of interesting (and
useless): clients could send a certificate's hash as the SNI and
Caddy would be able to serve that certificate for the handshake.
- Do not attempt to match a "default" (random) certificate when SNI
is present but unrecognized; return no certificate so a TLS alert
happens instead.
- Store an Instance in the list of instances even while the instance
is still starting up (this allows access to the cert cache for
performing renewals at startup, etc). Will be removed from list again
if instance startup fails.
- Laid groundwork for ACMEv2 and Let's Encrypt wildcard support.
Server type plugins will need to be updated slightly to accommodate
minor adjustments to their API (like passing in an Instance). This
commit includes the changes for the HTTP server.
Certain Caddyfile configurations might error out with this change, if
they configured different TLS settings for the same hostname.
This change trades some complexity for other complexity, but ultimately
this new complexity is more correct and robust than earlier logic.
Fixes #1991
Fixes #1994
Fixes #1303
2018-02-04 15:58:27 +08:00
|
|
|
if directCert, ok := cfg.certCache.cache[name]; ok {
|
|
|
|
cert = directCert
|
|
|
|
matched = true
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-11-13 05:24:07 +08:00
|
|
|
// if nothing matches, use a "default" certificate
|
|
|
|
// (See issues 2035 and 1303; any change to this behavior
|
|
|
|
// must account for hosts defined like ":443" or
|
|
|
|
// "0.0.0.0:443" where the hostname is empty or a catch-all
|
|
|
|
// IP or something.)
|
|
|
|
if certKey, ok := cfg.Certificates[""]; ok {
|
2018-03-18 07:03:12 +08:00
|
|
|
cert = cfg.certCache.cache[certKey]
|
|
|
|
defaulted = true
|
|
|
|
return
|
tls: Restructure and improve certificate management
- Expose the list of Caddy instances through caddy.Instances()
- Added arbitrary storage to caddy.Instance
- The cache of loaded certificates is no longer global; now scoped
per-instance, meaning upon reload (like SIGUSR1) the old cert cache
will be discarded entirely, whereas before, aggressively reloading
config that added and removed lots of sites would cause unnecessary
build-up in the cache over time.
- Key certificates in the cache by their SHA-256 hash instead of
by their names. This means certificates will not be duplicated in
memory (within each instance), making Caddy much more memory-efficient
for large-scale deployments with thousands of sites sharing certs.
- Perform name-to-certificate lookups scoped per caddytls.Config instead
of a single global lookup. This prevents certificates from stepping on
each other when they overlap in their names.
- Do not allow TLS configurations keyed by the same hostname to be
different; this now throws an error.
- Updated relevant tests, with a stark awareness that more tests are
needed.
- Change the NewContext function signature to include an *Instance.
- Strongly recommend (basically require) use of caddytls.NewConfig()
to create a new *caddytls.Config, to ensure pointers to the instance
certificate cache are initialized properly.
- Update the TLS-SNI challenge solver (even though TLS-SNI is disabled
currently on the CA side). Store temporary challenge cert in instance
cache, but do so directly by the ACME challenge name, not the hash.
Modified the getCertificate function to check the cache directly for
a name match if one isn't found otherwise. This will allow any
caddytls.Config to be able to help solve a TLS-SNI challenge, with one
extra side-effect that might actually be kind of interesting (and
useless): clients could send a certificate's hash as the SNI and
Caddy would be able to serve that certificate for the handshake.
- Do not attempt to match a "default" (random) certificate when SNI
is present but unrecognized; return no certificate so a TLS alert
happens instead.
- Store an Instance in the list of instances even while the instance
is still starting up (this allows access to the cert cache for
performing renewals at startup, etc). Will be removed from list again
if instance startup fails.
- Laid groundwork for ACMEv2 and Let's Encrypt wildcard support.
Server type plugins will need to be updated slightly to accommodate
minor adjustments to their API (like passing in an Instance). This
commit includes the changes for the HTTP server.
Certain Caddyfile configurations might error out with this change, if
they configured different TLS settings for the same hostname.
This change trades some complexity for other complexity, but ultimately
this new complexity is more correct and robust than earlier logic.
Fixes #1991
Fixes #1994
Fixes #1303
2018-02-04 15:58:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-02-11 15:06:05 +08:00
|
|
|
// getCertDuringHandshake will get a certificate for name. It first tries
|
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
|
|
|
// the in-memory cache. If no certificate for name is in the cache, the
|
|
|
|
// config most closely corresponding to name will be loaded. If that config
|
|
|
|
// allows it (OnDemand==true) and if loadIfNecessary == true, it goes to disk
|
|
|
|
// to load it into the cache and serve it. If it's not on disk and if
|
|
|
|
// obtainIfNecessary == true, the certificate will be obtained from the CA,
|
|
|
|
// cached, and served. If obtainIfNecessary is true, then loadIfNecessary
|
|
|
|
// must also be set to true. An error will be returned if and only if no
|
|
|
|
// certificate is available.
|
2016-02-11 15:06:05 +08:00
|
|
|
//
|
|
|
|
// This function is safe for concurrent use.
|
2017-02-22 00:49:22 +08:00
|
|
|
func (cfg *Config) getCertDuringHandshake(name string, loadIfNecessary, obtainIfNecessary bool) (Certificate, error) {
|
2016-02-11 15:06:05 +08:00
|
|
|
// First check our in-memory cache to see if we've already loaded it
|
tls: Restructure and improve certificate management
- Expose the list of Caddy instances through caddy.Instances()
- Added arbitrary storage to caddy.Instance
- The cache of loaded certificates is no longer global; now scoped
per-instance, meaning upon reload (like SIGUSR1) the old cert cache
will be discarded entirely, whereas before, aggressively reloading
config that added and removed lots of sites would cause unnecessary
build-up in the cache over time.
- Key certificates in the cache by their SHA-256 hash instead of
by their names. This means certificates will not be duplicated in
memory (within each instance), making Caddy much more memory-efficient
for large-scale deployments with thousands of sites sharing certs.
- Perform name-to-certificate lookups scoped per caddytls.Config instead
of a single global lookup. This prevents certificates from stepping on
each other when they overlap in their names.
- Do not allow TLS configurations keyed by the same hostname to be
different; this now throws an error.
- Updated relevant tests, with a stark awareness that more tests are
needed.
- Change the NewContext function signature to include an *Instance.
- Strongly recommend (basically require) use of caddytls.NewConfig()
to create a new *caddytls.Config, to ensure pointers to the instance
certificate cache are initialized properly.
- Update the TLS-SNI challenge solver (even though TLS-SNI is disabled
currently on the CA side). Store temporary challenge cert in instance
cache, but do so directly by the ACME challenge name, not the hash.
Modified the getCertificate function to check the cache directly for
a name match if one isn't found otherwise. This will allow any
caddytls.Config to be able to help solve a TLS-SNI challenge, with one
extra side-effect that might actually be kind of interesting (and
useless): clients could send a certificate's hash as the SNI and
Caddy would be able to serve that certificate for the handshake.
- Do not attempt to match a "default" (random) certificate when SNI
is present but unrecognized; return no certificate so a TLS alert
happens instead.
- Store an Instance in the list of instances even while the instance
is still starting up (this allows access to the cert cache for
performing renewals at startup, etc). Will be removed from list again
if instance startup fails.
- Laid groundwork for ACMEv2 and Let's Encrypt wildcard support.
Server type plugins will need to be updated slightly to accommodate
minor adjustments to their API (like passing in an Instance). This
commit includes the changes for the HTTP server.
Certain Caddyfile configurations might error out with this change, if
they configured different TLS settings for the same hostname.
This change trades some complexity for other complexity, but ultimately
this new complexity is more correct and robust than earlier logic.
Fixes #1991
Fixes #1994
Fixes #1303
2018-02-04 15:58:27 +08:00
|
|
|
cert, matched, defaulted := cfg.getCertificate(name)
|
2016-02-19 11:33:15 +08:00
|
|
|
if matched {
|
2016-02-11 15:06:05 +08:00
|
|
|
return cert, nil
|
|
|
|
}
|
|
|
|
|
2017-02-22 00:49:22 +08:00
|
|
|
// If OnDemand is enabled, then we might be able to load or
|
|
|
|
// obtain a needed certificate
|
|
|
|
if cfg.OnDemand && loadIfNecessary {
|
2016-02-11 15:06:05 +08:00
|
|
|
// Then check to see if we have one on disk
|
2017-02-22 00:49:22 +08:00
|
|
|
loadedCert, err := cfg.CacheManagedCertificate(name)
|
2016-02-12 04:48:52 +08:00
|
|
|
if err == nil {
|
2017-02-22 00:49:22 +08:00
|
|
|
loadedCert, err = cfg.handshakeMaintenance(name, loadedCert)
|
2016-02-11 15:06:05 +08:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("[ERROR] Maintaining newly-loaded certificate for %s: %v", name, err)
|
|
|
|
}
|
2016-02-19 11:33:15 +08:00
|
|
|
return loadedCert, nil
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
2016-02-12 04:48:52 +08:00
|
|
|
if obtainIfNecessary {
|
2016-02-12 05:27:57 +08:00
|
|
|
// By this point, we need to ask the CA for a certificate
|
|
|
|
|
2016-02-12 04:48:52 +08:00
|
|
|
name = strings.ToLower(name)
|
|
|
|
|
2017-11-04 13:01:30 +08:00
|
|
|
// Make sure the certificate should be obtained based on config
|
|
|
|
err := cfg.checkIfCertShouldBeObtained(name)
|
2016-02-12 05:27:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return Certificate{}, err
|
2016-02-12 04:48:52 +08:00
|
|
|
}
|
|
|
|
|
2016-02-12 05:27:57 +08:00
|
|
|
// Name has to qualify for a certificate
|
2016-02-12 04:48:52 +08:00
|
|
|
if !HostQualifies(name) {
|
|
|
|
return cert, errors.New("hostname '" + name + "' does not qualify for certificate")
|
|
|
|
}
|
2016-02-11 15:06:05 +08:00
|
|
|
|
2016-02-12 05:27:57 +08:00
|
|
|
// Obtain certificate from the CA
|
2017-02-22 00:49:22 +08:00
|
|
|
return cfg.obtainOnDemandCertificate(name)
|
2016-02-12 04:48:52 +08:00
|
|
|
}
|
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
|
|
|
// Fall back to the default certificate if there is one
|
2016-02-19 11:33:15 +08:00
|
|
|
if defaulted {
|
|
|
|
return cert, nil
|
|
|
|
}
|
|
|
|
|
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
|
|
|
return Certificate{}, fmt.Errorf("no certificate available for %s", name)
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
|
2017-11-04 13:01:30 +08:00
|
|
|
// checkIfCertShouldBeObtained checks to see if an on-demand tls certificate
|
|
|
|
// should be obtained for a given domain based upon the config settings. If
|
|
|
|
// a non-nil error is returned, do not issue a new certificate for name.
|
|
|
|
func (cfg *Config) checkIfCertShouldBeObtained(name string) error {
|
|
|
|
// If the "ask" URL is defined in the config, use to determine if a
|
|
|
|
// cert should obtained
|
|
|
|
if cfg.OnDemandState.AskURL != nil {
|
|
|
|
return cfg.checkURLForObtainingNewCerts(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise use the limit defined by the "max_certs" setting
|
|
|
|
return cfg.checkLimitsForObtainingNewCerts(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cfg *Config) checkURLForObtainingNewCerts(name string) error {
|
|
|
|
client := http.Client{
|
|
|
|
Timeout: 10 * time.Second,
|
|
|
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
|
|
|
return errors.New("following http redirects is not allowed")
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy the URL from the config in order to modify it for this request
|
|
|
|
askURL := new(url.URL)
|
|
|
|
*askURL = *cfg.OnDemandState.AskURL
|
|
|
|
|
|
|
|
query := askURL.Query()
|
|
|
|
query.Set("domain", name)
|
|
|
|
askURL.RawQuery = query.Encode()
|
|
|
|
|
|
|
|
resp, err := client.Get(askURL.String())
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error checking %v to deterine if certificate for hostname '%s' should be allowed: %v", cfg.OnDemandState.AskURL, name, err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
|
|
|
return fmt.Errorf("certificate for hostname '%s' not allowed, non-2xx status code %d returned from %v", name, resp.StatusCode, cfg.OnDemandState.AskURL)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-02-12 05:27:57 +08:00
|
|
|
// checkLimitsForObtainingNewCerts checks to see if name can be issued right
|
2017-11-04 13:01:30 +08:00
|
|
|
// now according the maximum count defined in the configuration. If a non-nil
|
|
|
|
// error is returned, do not issue a new certificate for name.
|
2017-02-22 00:49:22 +08:00
|
|
|
func (cfg *Config) checkLimitsForObtainingNewCerts(name string) error {
|
2016-02-12 05:27:57 +08:00
|
|
|
// User can set hard limit for number of certs for the process to issue
|
2016-07-29 01:08:18 +08:00
|
|
|
if cfg.OnDemandState.MaxObtain > 0 &&
|
|
|
|
atomic.LoadInt32(&cfg.OnDemandState.ObtainedCount) >= cfg.OnDemandState.MaxObtain {
|
|
|
|
return fmt.Errorf("%s: maximum certificates issued (%d)", name, cfg.OnDemandState.MaxObtain)
|
2016-02-12 05:27:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure name hasn't failed a challenge recently
|
|
|
|
failedIssuanceMu.RLock()
|
|
|
|
when, ok := failedIssuance[name]
|
|
|
|
failedIssuanceMu.RUnlock()
|
|
|
|
if ok {
|
|
|
|
return fmt.Errorf("%s: throttled; refusing to issue cert since last attempt on %s failed", name, when.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure, if we've issued a few certificates already, that we haven't
|
|
|
|
// issued any recently
|
|
|
|
lastIssueTimeMu.Lock()
|
|
|
|
since := time.Since(lastIssueTime)
|
|
|
|
lastIssueTimeMu.Unlock()
|
2016-07-29 01:08:18 +08:00
|
|
|
if atomic.LoadInt32(&cfg.OnDemandState.ObtainedCount) >= 10 && since < 10*time.Minute {
|
2016-02-12 05:27:57 +08:00
|
|
|
return fmt.Errorf("%s: throttled; last certificate was obtained %v ago", name, since)
|
|
|
|
}
|
|
|
|
|
2017-02-22 00:49:22 +08:00
|
|
|
// Good to go 👍
|
2016-02-12 05:27:57 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-02-11 15:06:05 +08:00
|
|
|
// obtainOnDemandCertificate obtains a certificate for name for the given
|
2016-02-16 14:39:04 +08:00
|
|
|
// name. If another goroutine has already started obtaining a cert for
|
|
|
|
// name, it will wait and use what the other goroutine obtained.
|
2016-02-11 15:06:05 +08:00
|
|
|
//
|
|
|
|
// This function is safe for use by multiple concurrent goroutines.
|
2017-02-22 00:49:22 +08:00
|
|
|
func (cfg *Config) obtainOnDemandCertificate(name string) (Certificate, error) {
|
2016-02-11 15:06:05 +08:00
|
|
|
// We must protect this process from happening concurrently, so synchronize.
|
|
|
|
obtainCertWaitChansMu.Lock()
|
|
|
|
wait, ok := obtainCertWaitChans[name]
|
|
|
|
if ok {
|
|
|
|
// lucky us -- another goroutine is already obtaining the certificate.
|
|
|
|
// wait for it to finish obtaining the cert and then we'll use it.
|
|
|
|
obtainCertWaitChansMu.Unlock()
|
|
|
|
<-wait
|
2017-02-22 00:49:22 +08:00
|
|
|
return cfg.getCertDuringHandshake(name, true, false)
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
|
2016-09-15 12:30:49 +08:00
|
|
|
// looks like it's up to us to do all the work and obtain the cert.
|
|
|
|
// make a chan others can wait on if needed
|
2016-02-11 15:06:05 +08:00
|
|
|
wait = make(chan struct{})
|
|
|
|
obtainCertWaitChans[name] = wait
|
|
|
|
obtainCertWaitChansMu.Unlock()
|
|
|
|
|
tls: Restructure and improve certificate management
- Expose the list of Caddy instances through caddy.Instances()
- Added arbitrary storage to caddy.Instance
- The cache of loaded certificates is no longer global; now scoped
per-instance, meaning upon reload (like SIGUSR1) the old cert cache
will be discarded entirely, whereas before, aggressively reloading
config that added and removed lots of sites would cause unnecessary
build-up in the cache over time.
- Key certificates in the cache by their SHA-256 hash instead of
by their names. This means certificates will not be duplicated in
memory (within each instance), making Caddy much more memory-efficient
for large-scale deployments with thousands of sites sharing certs.
- Perform name-to-certificate lookups scoped per caddytls.Config instead
of a single global lookup. This prevents certificates from stepping on
each other when they overlap in their names.
- Do not allow TLS configurations keyed by the same hostname to be
different; this now throws an error.
- Updated relevant tests, with a stark awareness that more tests are
needed.
- Change the NewContext function signature to include an *Instance.
- Strongly recommend (basically require) use of caddytls.NewConfig()
to create a new *caddytls.Config, to ensure pointers to the instance
certificate cache are initialized properly.
- Update the TLS-SNI challenge solver (even though TLS-SNI is disabled
currently on the CA side). Store temporary challenge cert in instance
cache, but do so directly by the ACME challenge name, not the hash.
Modified the getCertificate function to check the cache directly for
a name match if one isn't found otherwise. This will allow any
caddytls.Config to be able to help solve a TLS-SNI challenge, with one
extra side-effect that might actually be kind of interesting (and
useless): clients could send a certificate's hash as the SNI and
Caddy would be able to serve that certificate for the handshake.
- Do not attempt to match a "default" (random) certificate when SNI
is present but unrecognized; return no certificate so a TLS alert
happens instead.
- Store an Instance in the list of instances even while the instance
is still starting up (this allows access to the cert cache for
performing renewals at startup, etc). Will be removed from list again
if instance startup fails.
- Laid groundwork for ACMEv2 and Let's Encrypt wildcard support.
Server type plugins will need to be updated slightly to accommodate
minor adjustments to their API (like passing in an Instance). This
commit includes the changes for the HTTP server.
Certain Caddyfile configurations might error out with this change, if
they configured different TLS settings for the same hostname.
This change trades some complexity for other complexity, but ultimately
this new complexity is more correct and robust than earlier logic.
Fixes #1991
Fixes #1994
Fixes #1303
2018-02-04 15:58:27 +08:00
|
|
|
// obtain the certificate
|
2016-02-11 15:06:05 +08:00
|
|
|
log.Printf("[INFO] Obtaining new certificate for %s", name)
|
2016-09-15 12:30:49 +08:00
|
|
|
err := cfg.ObtainCert(name, false)
|
|
|
|
|
|
|
|
// immediately unblock anyone waiting for it; doing this in
|
|
|
|
// a defer would risk deadlock because of the recursive call
|
|
|
|
// to getCertDuringHandshake below when we return!
|
|
|
|
obtainCertWaitChansMu.Lock()
|
|
|
|
close(wait)
|
|
|
|
delete(obtainCertWaitChans, name)
|
|
|
|
obtainCertWaitChansMu.Unlock()
|
2016-02-11 15:06:05 +08:00
|
|
|
|
2016-09-15 12:30:49 +08:00
|
|
|
if err != nil {
|
2016-02-12 04:48:52 +08:00
|
|
|
// Failed to solve challenge, so don't allow another on-demand
|
|
|
|
// issue for this name to be attempted for a little while.
|
|
|
|
failedIssuanceMu.Lock()
|
|
|
|
failedIssuance[name] = time.Now()
|
|
|
|
go func(name string) {
|
|
|
|
time.Sleep(5 * time.Minute)
|
|
|
|
failedIssuanceMu.Lock()
|
|
|
|
delete(failedIssuance, name)
|
|
|
|
failedIssuanceMu.Unlock()
|
|
|
|
}(name)
|
|
|
|
failedIssuanceMu.Unlock()
|
2016-02-11 15:06:05 +08:00
|
|
|
return Certificate{}, err
|
|
|
|
}
|
|
|
|
|
2016-02-12 05:27:57 +08:00
|
|
|
// Success - update counters and stuff
|
2016-07-29 01:08:18 +08:00
|
|
|
atomic.AddInt32(&cfg.OnDemandState.ObtainedCount, 1)
|
2016-02-12 05:27:57 +08:00
|
|
|
lastIssueTimeMu.Lock()
|
|
|
|
lastIssueTime = time.Now()
|
|
|
|
lastIssueTimeMu.Unlock()
|
2016-02-12 04:48:52 +08:00
|
|
|
|
2016-09-15 12:30:49 +08:00
|
|
|
// certificate is already on disk; now just start over to load it and serve it
|
2017-02-22 00:49:22 +08:00
|
|
|
return cfg.getCertDuringHandshake(name, true, false)
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// handshakeMaintenance performs a check on cert for expiration and OCSP
|
|
|
|
// validity.
|
|
|
|
//
|
|
|
|
// This function is safe for use by multiple concurrent goroutines.
|
2017-02-22 00:49:22 +08:00
|
|
|
func (cfg *Config) handshakeMaintenance(name string, cert Certificate) (Certificate, error) {
|
2016-02-11 15:06:05 +08:00
|
|
|
// Check cert expiration
|
|
|
|
timeLeft := cert.NotAfter.Sub(time.Now().UTC())
|
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 timeLeft < RenewDurationBefore {
|
2016-02-11 15:06:05 +08:00
|
|
|
log.Printf("[INFO] Certificate for %v expires in %v; attempting renewal", cert.Names, timeLeft)
|
2017-09-12 02:37:42 +08:00
|
|
|
return cfg.renewDynamicCertificate(name, cert)
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check OCSP staple validity
|
|
|
|
if cert.OCSP != nil {
|
|
|
|
refreshTime := cert.OCSP.ThisUpdate.Add(cert.OCSP.NextUpdate.Sub(cert.OCSP.ThisUpdate) / 2)
|
|
|
|
if time.Now().After(refreshTime) {
|
|
|
|
err := stapleOCSP(&cert, nil)
|
|
|
|
if err != nil {
|
|
|
|
// An error with OCSP stapling is not the end of the world, and in fact, is
|
|
|
|
// quite common considering not all certs have issuer URLs that support it.
|
|
|
|
log.Printf("[ERROR] Getting OCSP for %s: %v", name, err)
|
|
|
|
}
|
tls: Restructure and improve certificate management
- Expose the list of Caddy instances through caddy.Instances()
- Added arbitrary storage to caddy.Instance
- The cache of loaded certificates is no longer global; now scoped
per-instance, meaning upon reload (like SIGUSR1) the old cert cache
will be discarded entirely, whereas before, aggressively reloading
config that added and removed lots of sites would cause unnecessary
build-up in the cache over time.
- Key certificates in the cache by their SHA-256 hash instead of
by their names. This means certificates will not be duplicated in
memory (within each instance), making Caddy much more memory-efficient
for large-scale deployments with thousands of sites sharing certs.
- Perform name-to-certificate lookups scoped per caddytls.Config instead
of a single global lookup. This prevents certificates from stepping on
each other when they overlap in their names.
- Do not allow TLS configurations keyed by the same hostname to be
different; this now throws an error.
- Updated relevant tests, with a stark awareness that more tests are
needed.
- Change the NewContext function signature to include an *Instance.
- Strongly recommend (basically require) use of caddytls.NewConfig()
to create a new *caddytls.Config, to ensure pointers to the instance
certificate cache are initialized properly.
- Update the TLS-SNI challenge solver (even though TLS-SNI is disabled
currently on the CA side). Store temporary challenge cert in instance
cache, but do so directly by the ACME challenge name, not the hash.
Modified the getCertificate function to check the cache directly for
a name match if one isn't found otherwise. This will allow any
caddytls.Config to be able to help solve a TLS-SNI challenge, with one
extra side-effect that might actually be kind of interesting (and
useless): clients could send a certificate's hash as the SNI and
Caddy would be able to serve that certificate for the handshake.
- Do not attempt to match a "default" (random) certificate when SNI
is present but unrecognized; return no certificate so a TLS alert
happens instead.
- Store an Instance in the list of instances even while the instance
is still starting up (this allows access to the cert cache for
performing renewals at startup, etc). Will be removed from list again
if instance startup fails.
- Laid groundwork for ACMEv2 and Let's Encrypt wildcard support.
Server type plugins will need to be updated slightly to accommodate
minor adjustments to their API (like passing in an Instance). This
commit includes the changes for the HTTP server.
Certain Caddyfile configurations might error out with this change, if
they configured different TLS settings for the same hostname.
This change trades some complexity for other complexity, but ultimately
this new complexity is more correct and robust than earlier logic.
Fixes #1991
Fixes #1994
Fixes #1303
2018-02-04 15:58:27 +08:00
|
|
|
cfg.certCache.Lock()
|
|
|
|
cfg.certCache.cache[cert.Hash] = cert
|
|
|
|
cfg.certCache.Unlock()
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cert, nil
|
|
|
|
}
|
|
|
|
|
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
|
|
|
// renewDynamicCertificate renews the certificate for name using cfg. It returns the
|
2017-09-12 02:37:42 +08:00
|
|
|
// certificate to use and an error, if any. name should already be lower-cased before
|
|
|
|
// calling this function. name is the name obtained directly from the handshake's
|
|
|
|
// ClientHello.
|
2016-02-11 15:06:05 +08:00
|
|
|
//
|
|
|
|
// This function is safe for use by multiple concurrent goroutines.
|
2017-09-12 02:37:42 +08:00
|
|
|
func (cfg *Config) renewDynamicCertificate(name string, currentCert Certificate) (Certificate, error) {
|
2016-02-11 15:06:05 +08:00
|
|
|
obtainCertWaitChansMu.Lock()
|
|
|
|
wait, ok := obtainCertWaitChans[name]
|
|
|
|
if ok {
|
|
|
|
// lucky us -- another goroutine is already renewing the certificate.
|
|
|
|
// wait for it to finish, then we'll use the new one.
|
|
|
|
obtainCertWaitChansMu.Unlock()
|
|
|
|
<-wait
|
2017-02-22 00:49:22 +08:00
|
|
|
return cfg.getCertDuringHandshake(name, true, false)
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// looks like it's up to us to do all the work and renew the cert
|
|
|
|
wait = make(chan struct{})
|
|
|
|
obtainCertWaitChans[name] = wait
|
|
|
|
obtainCertWaitChansMu.Unlock()
|
|
|
|
|
tls: Restructure and improve certificate management
- Expose the list of Caddy instances through caddy.Instances()
- Added arbitrary storage to caddy.Instance
- The cache of loaded certificates is no longer global; now scoped
per-instance, meaning upon reload (like SIGUSR1) the old cert cache
will be discarded entirely, whereas before, aggressively reloading
config that added and removed lots of sites would cause unnecessary
build-up in the cache over time.
- Key certificates in the cache by their SHA-256 hash instead of
by their names. This means certificates will not be duplicated in
memory (within each instance), making Caddy much more memory-efficient
for large-scale deployments with thousands of sites sharing certs.
- Perform name-to-certificate lookups scoped per caddytls.Config instead
of a single global lookup. This prevents certificates from stepping on
each other when they overlap in their names.
- Do not allow TLS configurations keyed by the same hostname to be
different; this now throws an error.
- Updated relevant tests, with a stark awareness that more tests are
needed.
- Change the NewContext function signature to include an *Instance.
- Strongly recommend (basically require) use of caddytls.NewConfig()
to create a new *caddytls.Config, to ensure pointers to the instance
certificate cache are initialized properly.
- Update the TLS-SNI challenge solver (even though TLS-SNI is disabled
currently on the CA side). Store temporary challenge cert in instance
cache, but do so directly by the ACME challenge name, not the hash.
Modified the getCertificate function to check the cache directly for
a name match if one isn't found otherwise. This will allow any
caddytls.Config to be able to help solve a TLS-SNI challenge, with one
extra side-effect that might actually be kind of interesting (and
useless): clients could send a certificate's hash as the SNI and
Caddy would be able to serve that certificate for the handshake.
- Do not attempt to match a "default" (random) certificate when SNI
is present but unrecognized; return no certificate so a TLS alert
happens instead.
- Store an Instance in the list of instances even while the instance
is still starting up (this allows access to the cert cache for
performing renewals at startup, etc). Will be removed from list again
if instance startup fails.
- Laid groundwork for ACMEv2 and Let's Encrypt wildcard support.
Server type plugins will need to be updated slightly to accommodate
minor adjustments to their API (like passing in an Instance). This
commit includes the changes for the HTTP server.
Certain Caddyfile configurations might error out with this change, if
they configured different TLS settings for the same hostname.
This change trades some complexity for other complexity, but ultimately
this new complexity is more correct and robust than earlier logic.
Fixes #1991
Fixes #1994
Fixes #1303
2018-02-04 15:58:27 +08:00
|
|
|
// renew and reload the certificate
|
2016-02-11 15:06:05 +08:00
|
|
|
log.Printf("[INFO] Renewing certificate for %s", name)
|
2016-09-15 12:30:49 +08:00
|
|
|
err := cfg.RenewCert(name, false)
|
2017-09-12 02:37:42 +08:00
|
|
|
if err == nil {
|
|
|
|
// even though the recursive nature of the dynamic cert loading
|
|
|
|
// would just call this function anyway, we do it here to
|
tls: Restructure and improve certificate management
- Expose the list of Caddy instances through caddy.Instances()
- Added arbitrary storage to caddy.Instance
- The cache of loaded certificates is no longer global; now scoped
per-instance, meaning upon reload (like SIGUSR1) the old cert cache
will be discarded entirely, whereas before, aggressively reloading
config that added and removed lots of sites would cause unnecessary
build-up in the cache over time.
- Key certificates in the cache by their SHA-256 hash instead of
by their names. This means certificates will not be duplicated in
memory (within each instance), making Caddy much more memory-efficient
for large-scale deployments with thousands of sites sharing certs.
- Perform name-to-certificate lookups scoped per caddytls.Config instead
of a single global lookup. This prevents certificates from stepping on
each other when they overlap in their names.
- Do not allow TLS configurations keyed by the same hostname to be
different; this now throws an error.
- Updated relevant tests, with a stark awareness that more tests are
needed.
- Change the NewContext function signature to include an *Instance.
- Strongly recommend (basically require) use of caddytls.NewConfig()
to create a new *caddytls.Config, to ensure pointers to the instance
certificate cache are initialized properly.
- Update the TLS-SNI challenge solver (even though TLS-SNI is disabled
currently on the CA side). Store temporary challenge cert in instance
cache, but do so directly by the ACME challenge name, not the hash.
Modified the getCertificate function to check the cache directly for
a name match if one isn't found otherwise. This will allow any
caddytls.Config to be able to help solve a TLS-SNI challenge, with one
extra side-effect that might actually be kind of interesting (and
useless): clients could send a certificate's hash as the SNI and
Caddy would be able to serve that certificate for the handshake.
- Do not attempt to match a "default" (random) certificate when SNI
is present but unrecognized; return no certificate so a TLS alert
happens instead.
- Store an Instance in the list of instances even while the instance
is still starting up (this allows access to the cert cache for
performing renewals at startup, etc). Will be removed from list again
if instance startup fails.
- Laid groundwork for ACMEv2 and Let's Encrypt wildcard support.
Server type plugins will need to be updated slightly to accommodate
minor adjustments to their API (like passing in an Instance). This
commit includes the changes for the HTTP server.
Certain Caddyfile configurations might error out with this change, if
they configured different TLS settings for the same hostname.
This change trades some complexity for other complexity, but ultimately
this new complexity is more correct and robust than earlier logic.
Fixes #1991
Fixes #1994
Fixes #1303
2018-02-04 15:58:27 +08:00
|
|
|
// make the replacement as atomic as possible.
|
|
|
|
newCert, err := currentCert.configs[0].CacheManagedCertificate(name)
|
2017-09-12 02:37:42 +08:00
|
|
|
if err != nil {
|
tls: Restructure and improve certificate management
- Expose the list of Caddy instances through caddy.Instances()
- Added arbitrary storage to caddy.Instance
- The cache of loaded certificates is no longer global; now scoped
per-instance, meaning upon reload (like SIGUSR1) the old cert cache
will be discarded entirely, whereas before, aggressively reloading
config that added and removed lots of sites would cause unnecessary
build-up in the cache over time.
- Key certificates in the cache by their SHA-256 hash instead of
by their names. This means certificates will not be duplicated in
memory (within each instance), making Caddy much more memory-efficient
for large-scale deployments with thousands of sites sharing certs.
- Perform name-to-certificate lookups scoped per caddytls.Config instead
of a single global lookup. This prevents certificates from stepping on
each other when they overlap in their names.
- Do not allow TLS configurations keyed by the same hostname to be
different; this now throws an error.
- Updated relevant tests, with a stark awareness that more tests are
needed.
- Change the NewContext function signature to include an *Instance.
- Strongly recommend (basically require) use of caddytls.NewConfig()
to create a new *caddytls.Config, to ensure pointers to the instance
certificate cache are initialized properly.
- Update the TLS-SNI challenge solver (even though TLS-SNI is disabled
currently on the CA side). Store temporary challenge cert in instance
cache, but do so directly by the ACME challenge name, not the hash.
Modified the getCertificate function to check the cache directly for
a name match if one isn't found otherwise. This will allow any
caddytls.Config to be able to help solve a TLS-SNI challenge, with one
extra side-effect that might actually be kind of interesting (and
useless): clients could send a certificate's hash as the SNI and
Caddy would be able to serve that certificate for the handshake.
- Do not attempt to match a "default" (random) certificate when SNI
is present but unrecognized; return no certificate so a TLS alert
happens instead.
- Store an Instance in the list of instances even while the instance
is still starting up (this allows access to the cert cache for
performing renewals at startup, etc). Will be removed from list again
if instance startup fails.
- Laid groundwork for ACMEv2 and Let's Encrypt wildcard support.
Server type plugins will need to be updated slightly to accommodate
minor adjustments to their API (like passing in an Instance). This
commit includes the changes for the HTTP server.
Certain Caddyfile configurations might error out with this change, if
they configured different TLS settings for the same hostname.
This change trades some complexity for other complexity, but ultimately
this new complexity is more correct and robust than earlier logic.
Fixes #1991
Fixes #1994
Fixes #1303
2018-02-04 15:58:27 +08:00
|
|
|
log.Printf("[ERROR] loading renewed certificate for %s: %v", name, err)
|
|
|
|
} else {
|
|
|
|
// replace the old certificate with the new one
|
|
|
|
err = cfg.certCache.replaceCertificate(currentCert, newCert)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("[ERROR] Replacing certificate for %s: %v", name, err)
|
|
|
|
}
|
2017-09-12 02:37:42 +08:00
|
|
|
}
|
|
|
|
}
|
2016-09-15 12:30:49 +08:00
|
|
|
|
|
|
|
// immediately unblock anyone waiting for it; doing this in
|
|
|
|
// a defer would risk deadlock because of the recursive call
|
|
|
|
// to getCertDuringHandshake below when we return!
|
|
|
|
obtainCertWaitChansMu.Lock()
|
|
|
|
close(wait)
|
|
|
|
delete(obtainCertWaitChans, name)
|
|
|
|
obtainCertWaitChansMu.Unlock()
|
2016-02-11 15:06:05 +08:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return Certificate{}, err
|
|
|
|
}
|
|
|
|
|
2017-02-22 00:49:22 +08:00
|
|
|
return cfg.getCertDuringHandshake(name, true, false)
|
2016-02-11 15:06:05 +08:00
|
|
|
}
|
|
|
|
|
2018-12-06 08:33:23 +08:00
|
|
|
// tryDistributedChallengeSolver is to be called when the clientHello pertains to
|
|
|
|
// a TLS-ALPN challenge and a certificate is required to solve it. This method
|
|
|
|
// checks the distributed store of challenge info files and, if a matching ServerName
|
|
|
|
// is present, it makes a certificate to solve this challenge and returns it.
|
|
|
|
// A boolean true is returned if a valid certificate is returned.
|
|
|
|
func (cfg *Config) tryDistributedChallengeSolver(clientHello *tls.ClientHelloInfo) (Certificate, bool, error) {
|
|
|
|
filePath := distributedSolver{}.challengeTokensPath(clientHello.ServerName)
|
|
|
|
f, err := os.Open(filePath)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return Certificate{}, false, nil
|
|
|
|
}
|
|
|
|
return Certificate{}, false, fmt.Errorf("opening distributed challenge token file %s: %v", filePath, err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
var chalInfo challengeInfo
|
|
|
|
err = json.NewDecoder(f).Decode(&chalInfo)
|
|
|
|
if err != nil {
|
|
|
|
return Certificate{}, false, fmt.Errorf("decoding challenge token file %s (corrupted?): %v", filePath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
cert, err := acme.TLSALPNChallengeCert(chalInfo.Domain, chalInfo.KeyAuth)
|
|
|
|
if err != nil {
|
|
|
|
return Certificate{}, false, fmt.Errorf("making TLS-ALPN challenge certificate: %v", err)
|
|
|
|
}
|
|
|
|
if cert == nil {
|
|
|
|
return Certificate{}, false, fmt.Errorf("got nil TLS-ALPN challenge certificate but no error")
|
|
|
|
}
|
|
|
|
|
|
|
|
return Certificate{Certificate: *cert}, true, nil
|
|
|
|
}
|
|
|
|
|
2018-05-08 06:09:39 +08:00
|
|
|
// ClientHelloInfo is our own version of the standard lib's
|
|
|
|
// tls.ClientHelloInfo. As of May 2018, any fields populated
|
|
|
|
// by the Go standard library are not guaranteed to have their
|
|
|
|
// values in the original order as on the wire.
|
|
|
|
type ClientHelloInfo struct {
|
|
|
|
Version uint16 `json:"version,omitempty"`
|
|
|
|
CipherSuites []uint16 `json:"cipher_suites,omitempty"`
|
|
|
|
Extensions []uint16 `json:"extensions,omitempty"`
|
|
|
|
CompressionMethods []byte `json:"compression,omitempty"`
|
|
|
|
Curves []tls.CurveID `json:"curves,omitempty"`
|
|
|
|
Points []uint8 `json:"points,omitempty"`
|
|
|
|
|
|
|
|
// Whether a couple of fields are unknown; if not, the key will encode
|
|
|
|
// differently to reflect that, as opposed to being known empty values.
|
|
|
|
// (some fields may be unknown depending on what package is being used;
|
|
|
|
// i.e. the Go standard lib doesn't expose some things)
|
|
|
|
// (very important to NOT encode these to JSON)
|
|
|
|
ExtensionsUnknown bool `json:"-"`
|
|
|
|
CompressionMethodsUnknown bool `json:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Key returns a standardized string form of the data in info,
|
|
|
|
// useful for identifying duplicates.
|
|
|
|
func (info ClientHelloInfo) Key() string {
|
|
|
|
extensions, compressionMethods := "?", "?"
|
|
|
|
if !info.ExtensionsUnknown {
|
|
|
|
extensions = fmt.Sprintf("%x", info.Extensions)
|
|
|
|
}
|
|
|
|
if !info.CompressionMethodsUnknown {
|
|
|
|
compressionMethods = fmt.Sprintf("%x", info.CompressionMethods)
|
|
|
|
}
|
2018-05-10 22:57:25 +08:00
|
|
|
return telemetry.FastHash([]byte(fmt.Sprintf("%x-%x-%s-%s-%x-%x",
|
2018-05-08 06:09:39 +08:00
|
|
|
info.Version, info.CipherSuites, extensions,
|
|
|
|
compressionMethods, info.Curves, info.Points)))
|
|
|
|
}
|
|
|
|
|
2016-02-11 15:06:05 +08:00
|
|
|
// obtainCertWaitChans is used to coordinate obtaining certs for each hostname.
|
|
|
|
var obtainCertWaitChans = make(map[string]chan struct{})
|
|
|
|
var obtainCertWaitChansMu sync.Mutex
|
2016-02-12 04:48:52 +08:00
|
|
|
|
|
|
|
// failedIssuance is a set of names that we recently failed to get a
|
|
|
|
// certificate for from the ACME CA. They are removed after some time.
|
2016-02-12 05:27:57 +08:00
|
|
|
// When a name is in this map, do not issue a certificate for it on-demand.
|
2016-02-12 04:48:52 +08:00
|
|
|
var failedIssuance = make(map[string]time.Time)
|
|
|
|
var failedIssuanceMu sync.RWMutex
|
2016-02-12 05:27:57 +08:00
|
|
|
|
|
|
|
// lastIssueTime records when we last obtained a certificate successfully.
|
|
|
|
// If this value is recent, do not make any on-demand certificate requests.
|
|
|
|
var lastIssueTime time.Time
|
|
|
|
var lastIssueTimeMu sync.Mutex
|
2018-05-08 06:09:39 +08:00
|
|
|
|
|
|
|
// ClientHelloTelemetry determines whether to report
|
|
|
|
// TLS ClientHellos to telemetry. Disable if doing
|
|
|
|
// it from a different package.
|
|
|
|
var ClientHelloTelemetry = true
|