summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/caddyhttp/autohttps.go344
-rw-r--r--modules/caddyhttp/caddyhttp.go275
-rw-r--r--modules/caddyhttp/routes.go29
-rw-r--r--modules/caddyhttp/server.go53
4 files changed, 404 insertions, 297 deletions
diff --git a/modules/caddyhttp/autohttps.go b/modules/caddyhttp/autohttps.go
new file mode 100644
index 0000000..6cb0492
--- /dev/null
+++ b/modules/caddyhttp/autohttps.go
@@ -0,0 +1,344 @@
+package caddyhttp
+
+import (
+ "fmt"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "github.com/caddyserver/caddy/v2"
+ "github.com/caddyserver/caddy/v2/modules/caddytls"
+ "github.com/mholt/certmagic"
+ "go.uber.org/zap"
+)
+
+// AutoHTTPSConfig is used to disable automatic HTTPS
+// or certain aspects of it for a specific server.
+// HTTPS is enabled automatically and by default when
+// qualifying hostnames are available from the config.
+type AutoHTTPSConfig struct {
+ // If true, automatic HTTPS will be entirely disabled.
+ Disabled bool `json:"disable,omitempty"`
+
+ // If true, only automatic HTTP->HTTPS redirects will
+ // be disabled.
+ DisableRedir bool `json:"disable_redirects,omitempty"`
+
+ // Hosts/domain names listed here will not be included
+ // in automatic HTTPS (they will not have certificates
+ // loaded nor redirects applied).
+ Skip []string `json:"skip,omitempty"`
+
+ // Hosts/domain names listed here will still be enabled
+ // for automatic HTTPS (unless in the Skip list), except
+ // that certificates will not be provisioned and managed
+ // for these names.
+ SkipCerts []string `json:"skip_certificates,omitempty"`
+
+ // By default, automatic HTTPS will obtain and renew
+ // certificates for qualifying hostnames. However, if
+ // a certificate with a matching SAN is already loaded
+ // into the cache, certificate management will not be
+ // enabled. To force automated certificate management
+ // regardless of loaded certificates, set this to true.
+ IgnoreLoadedCerts bool `json:"ignore_loaded_certificates,omitempty"`
+
+ domainSet map[string]struct{}
+}
+
+// Skipped returns true if name is in skipSlice, which
+// should be one of the Skip* fields on ahc.
+func (ahc AutoHTTPSConfig) Skipped(name string, skipSlice []string) bool {
+ for _, n := range skipSlice {
+ if name == n {
+ return true
+ }
+ }
+ return false
+}
+
+// automaticHTTPSPhase1 provisions all route matchers, determines
+// which domain names found in the routes qualify for automatic
+// HTTPS, and sets up HTTP->HTTPS redirects. This phase must occur
+// at the beginning of provisioning, because it may add routes and
+// even servers to the app, which still need to be set up with the
+// rest of them.
+func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) error {
+ // this map will store associations of HTTP listener
+ // addresses to the routes that do HTTP->HTTPS redirects
+ lnAddrRedirRoutes := make(map[string]Route)
+
+ for srvName, srv := range app.Servers {
+ // as a prerequisite, provision route matchers; this is
+ // required for all routes on all servers, and must be
+ // done before we attempt to do phase 1 of auto HTTPS,
+ // since we have to access the decoded host matchers the
+ // handlers will be provisioned later
+ if srv.Routes != nil {
+ err := srv.Routes.ProvisionMatchers(ctx)
+ if err != nil {
+ return fmt.Errorf("server %s: setting up route matchers: %v", srvName, err)
+ }
+ }
+
+ // prepare for automatic HTTPS
+ if srv.AutoHTTPS == nil {
+ srv.AutoHTTPS = new(AutoHTTPSConfig)
+ }
+ if srv.AutoHTTPS.Disabled {
+ continue
+ }
+
+ // skip if all listeners use the HTTP port
+ if !srv.listenersUseAnyPortOtherThan(app.httpPort()) {
+ app.logger.Info("server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server",
+ zap.String("server_name", srvName),
+ zap.Int("http_port", app.httpPort()),
+ )
+ srv.AutoHTTPS.Disabled = true
+ continue
+ }
+
+ defaultConnPolicies := caddytls.ConnectionPolicies{
+ &caddytls.ConnectionPolicy{ALPN: defaultALPN},
+ }
+
+ // if all listeners are on the HTTPS port, make sure
+ // there is at least one TLS connection policy; it
+ // should be obvious that they want to use TLS without
+ // needing to specify one empty policy to enable it
+ if srv.TLSConnPolicies == nil &&
+ !srv.listenersUseAnyPortOtherThan(app.httpsPort()) {
+ app.logger.Info("server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS",
+ zap.String("server_name", srvName),
+ zap.Int("https_port", app.httpsPort()),
+ )
+ srv.TLSConnPolicies = defaultConnPolicies
+ }
+
+ // find all qualifying domain names in this server
+ srv.AutoHTTPS.domainSet = make(map[string]struct{})
+ for routeIdx, route := range srv.Routes {
+ for matcherSetIdx, matcherSet := range route.MatcherSets {
+ for matcherIdx, m := range matcherSet {
+ if hm, ok := m.(*MatchHost); ok {
+ for hostMatcherIdx, d := range *hm {
+ var err error
+ d, err = repl.ReplaceOrErr(d, true, false)
+ if err != nil {
+ return fmt.Errorf("%s: route %d, matcher set %d, matcher %d, host matcher %d: %v",
+ srvName, routeIdx, matcherSetIdx, matcherIdx, hostMatcherIdx, err)
+ }
+ if certmagic.HostQualifies(d) &&
+ !srv.AutoHTTPS.Skipped(d, srv.AutoHTTPS.Skip) {
+ srv.AutoHTTPS.domainSet[d] = struct{}{}
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // nothing more to do here if there are no
+ // domains that qualify for automatic HTTPS
+ if len(srv.AutoHTTPS.domainSet) == 0 {
+ continue
+ }
+
+ // tell the server to use TLS if it is not already doing so
+ if srv.TLSConnPolicies == nil {
+ srv.TLSConnPolicies = defaultConnPolicies
+ }
+
+ // nothing left to do if auto redirects are disabled
+ if srv.AutoHTTPS.DisableRedir {
+ continue
+ }
+
+ app.logger.Info("enabling automatic HTTP->HTTPS redirects",
+ zap.String("server_name", srvName),
+ )
+
+ // create HTTP->HTTPS redirects
+ for _, addr := range srv.Listen {
+ netw, host, port, err := caddy.SplitNetworkAddress(addr)
+ if err != nil {
+ return fmt.Errorf("%s: invalid listener address: %v", srvName, addr)
+ }
+
+ if parts := strings.SplitN(port, "-", 2); len(parts) == 2 {
+ port = parts[0]
+ }
+ redirTo := "https://{http.request.host}"
+
+ if port != strconv.Itoa(app.httpsPort()) {
+ redirTo += ":" + port
+ }
+ redirTo += "{http.request.uri}"
+
+ // build the plaintext HTTP variant of this address
+ httpRedirLnAddr := caddy.JoinNetworkAddress(netw, host, strconv.Itoa(app.httpPort()))
+
+ // build the matcher set for this redirect route
+ // (note that we happen to bypass Provision and
+ // Validate steps for these matcher modules)
+ matcherSet := MatcherSet{MatchProtocol("http")}
+ if len(srv.AutoHTTPS.Skip) > 0 {
+ matcherSet = append(matcherSet, MatchNegate{
+ Matchers: MatcherSet{MatchHost(srv.AutoHTTPS.Skip)},
+ })
+ }
+
+ // create the route that does the redirect and associate
+ // it with the listener address it will be served from
+ // (note that we happen to bypass any Provision or Validate
+ // steps on the handler modules created here)
+ lnAddrRedirRoutes[httpRedirLnAddr] = Route{
+ MatcherSets: []MatcherSet{matcherSet},
+ Handlers: []MiddlewareHandler{
+ StaticResponse{
+ StatusCode: WeakString(strconv.Itoa(http.StatusPermanentRedirect)),
+ Headers: http.Header{
+ "Location": []string{redirTo},
+ "Connection": []string{"close"},
+ },
+ Close: true,
+ },
+ },
+ }
+ }
+ }
+
+ // if there are HTTP->HTTPS redirects to add, do so now
+ if len(lnAddrRedirRoutes) == 0 {
+ return nil
+ }
+
+ var redirServerAddrs []string
+ var redirRoutes RouteList
+
+ // for each redirect listener, see if there's already a
+ // server configured to listen on that exact address; if so,
+ // simply add the redirect route to the end of its route
+ // list; otherwise, we'll create a new server for all the
+ // listener addresses that are unused and serve the
+ // remaining redirects from it
+redirRoutesLoop:
+ for addr, redirRoute := range lnAddrRedirRoutes {
+ for srvName, srv := range app.Servers {
+ if srv.hasListenerAddress(addr) {
+ // user has configured a server for the same address
+ // that the redirect runs from; simply append our
+ // redirect route to the existing routes, with a
+ // caveat that their config might override ours
+ app.logger.Warn("server is listening on same interface as redirects, so automatic HTTP->HTTPS redirects might be overridden by your own configuration",
+ zap.String("server_name", srvName),
+ zap.String("interface", addr),
+ )
+ srv.Routes = append(srv.Routes, redirRoute)
+ continue redirRoutesLoop
+ }
+ }
+ // no server with this listener address exists;
+ // save this address and route for custom server
+ redirServerAddrs = append(redirServerAddrs, addr)
+ redirRoutes = append(redirRoutes, redirRoute)
+ }
+
+ // if there are routes remaining which do not belong
+ // in any existing server, make our own to serve the
+ // rest of the redirects
+ if len(redirServerAddrs) > 0 {
+ app.Servers["remaining_auto_https_redirects"] = &Server{
+ Listen: redirServerAddrs,
+ Routes: redirRoutes,
+ }
+ }
+
+ return nil
+}
+
+// automaticHTTPSPhase2 attaches a TLS app pointer to each
+// server and begins certificate management for all names
+// in the qualifying domain set for each server. This phase
+// must occur after provisioning, and at the beginning of
+// the app start, before starting each of the servers.
+func (app *App) automaticHTTPSPhase2() error {
+ tlsAppIface, err := app.ctx.App("tls")
+ if err != nil {
+ return fmt.Errorf("getting tls app: %v", err)
+ }
+ tlsApp := tlsAppIface.(*caddytls.TLS)
+
+ // set the tlsApp pointer before starting any
+ // challenges, since it is required to solve
+ // the ACME HTTP challenge
+ for _, srv := range app.Servers {
+ srv.tlsApp = tlsApp
+ }
+
+ // begin managing certificates for enabled servers
+ for srvName, srv := range app.Servers {
+ if srv.AutoHTTPS == nil ||
+ srv.AutoHTTPS.Disabled ||
+ len(srv.AutoHTTPS.domainSet) == 0 {
+ continue
+ }
+
+ // marshal the domains into a slice
+ var domains, domainsForCerts []string
+ for d := range srv.AutoHTTPS.domainSet {
+ domains = append(domains, d)
+ if !srv.AutoHTTPS.Skipped(d, srv.AutoHTTPS.SkipCerts) {
+ // if a certificate for this name is already loaded,
+ // don't obtain another one for it, unless we are
+ // supposed to ignore loaded certificates
+ if !srv.AutoHTTPS.IgnoreLoadedCerts &&
+ len(tlsApp.AllMatchingCertificates(d)) > 0 {
+ app.logger.Info("skipping automatic certificate management because one or more matching certificates are already loaded",
+ zap.String("domain", d),
+ zap.String("server_name", srvName),
+ )
+ continue
+ }
+ domainsForCerts = append(domainsForCerts, d)
+ }
+ }
+
+ // ensure that these certificates are managed properly;
+ // for example, it's implied that the HTTPPort should also
+ // be the port the HTTP challenge is solved on, and so
+ // for HTTPS port and TLS-ALPN challenge also - we need
+ // to tell the TLS app to manage these certs by honoring
+ // those port configurations
+ acmeManager := &caddytls.ACMEManagerMaker{
+ Challenges: &caddytls.ChallengesConfig{
+ HTTP: &caddytls.HTTPChallengeConfig{
+ AlternatePort: app.HTTPPort, // we specifically want the user-configured port, if any
+ },
+ TLSALPN: &caddytls.TLSALPNChallengeConfig{
+ AlternatePort: app.HTTPSPort, // we specifically want the user-configured port, if any
+ },
+ },
+ }
+ if tlsApp.Automation == nil {
+ tlsApp.Automation = new(caddytls.AutomationConfig)
+ }
+ tlsApp.Automation.Policies = append(tlsApp.Automation.Policies,
+ caddytls.AutomationPolicy{
+ Hosts: domainsForCerts,
+ Management: acmeManager,
+ })
+
+ // manage their certificates
+ app.logger.Info("enabling automatic TLS certificate management",
+ zap.Strings("domains", domainsForCerts),
+ )
+ err := tlsApp.Manage(domainsForCerts)
+ if err != nil {
+ return fmt.Errorf("%s: managing certificate for %s: %s", srvName, domains, err)
+ }
+ }
+
+ return nil
+}
diff --git a/modules/caddyhttp/caddyhttp.go b/modules/caddyhttp/caddyhttp.go
index 73c4863..37f9670 100644
--- a/modules/caddyhttp/caddyhttp.go
+++ b/modules/caddyhttp/caddyhttp.go
@@ -25,13 +25,10 @@ import (
"net"
"net/http"
"strconv"
- "strings"
"time"
"github.com/caddyserver/caddy/v2"
- "github.com/caddyserver/caddy/v2/modules/caddytls"
"github.com/lucas-clemente/quic-go/http3"
- "github.com/mholt/certmagic"
"go.uber.org/zap"
)
@@ -52,7 +49,7 @@ func init() {
// only on the HTTPS port but which do not have any TLS connection policies
// defined by adding a good, default TLS connection policy.
//
-// In HTTP routes, additional placeholders are available:
+// In HTTP routes, additional placeholders are available (replace any `*`):
//
// Placeholder | Description
// ------------|---------------
@@ -127,6 +124,14 @@ func (app *App) Provision(ctx caddy.Context) error {
repl := caddy.NewReplacer()
+ // this provisions the matchers for each route,
+ // and prepares auto HTTP->HTTP redirects, and
+ // is required before we provision each server
+ err := app.automaticHTTPSPhase1(ctx, repl)
+ if err != nil {
+ return err
+ }
+
for srvName, srv := range app.Servers {
srv.logger = app.logger.Named("log")
srv.errorLogger = app.logger.Named("log.error")
@@ -136,11 +141,6 @@ func (app *App) Provision(ctx caddy.Context) error {
srv.accessLogger = app.logger.Named("log.access")
}
- if srv.AutoHTTPS == nil {
- // avoid nil pointer dereferences
- srv.AutoHTTPS = new(AutoHTTPSConfig)
- }
-
// if not explicitly configured by the user, disallow TLS
// client auth bypass (domain fronting) which could
// otherwise be exploited by sending an unprotected SNI
@@ -151,6 +151,9 @@ func (app *App) Provision(ctx caddy.Context) error {
// domain fronting is desired and access is not restricted
// based on hostname
if srv.StrictSNIHost == nil && srv.hasTLSClientAuth() {
+ app.logger.Info("enabling strict SNI-Host matching because TLS client auth is configured",
+ zap.String("server_name", srvName),
+ )
trueBool := true
srv.StrictSNIHost = &trueBool
}
@@ -164,18 +167,19 @@ func (app *App) Provision(ctx caddy.Context) error {
srv.Listen[i] = lnOut
}
+ // pre-compile the primary handler chain, and be sure to wrap it in our
+ // route handler so that important security checks are done, etc.
primaryRoute := emptyHandler
if srv.Routes != nil {
- err := srv.Routes.Provision(ctx)
+ err := srv.Routes.ProvisionHandlers(ctx)
if err != nil {
- return fmt.Errorf("server %s: setting up server routes: %v", srvName, err)
+ return fmt.Errorf("server %s: setting up route handlers: %v", srvName, err)
}
- // pre-compile the handler chain, and be sure to wrap it in our
- // route handler so that important security checks are done, etc.
primaryRoute = srv.Routes.Compile(emptyHandler)
}
srv.primaryHandlerChain = srv.wrapPrimaryRoute(primaryRoute)
+ // pre-compile the error handler chain
if srv.Errors != nil {
err := srv.Errors.Routes.Provision(ctx)
if err != nil {
@@ -213,9 +217,12 @@ func (app *App) Validate() error {
return nil
}
-// Start runs the app. It sets up automatic HTTPS if enabled.
+// Start runs the app. It finishes automatic HTTPS if enabled,
+// including management of certificates.
func (app *App) Start() error {
- err := app.automaticHTTPS()
+ // finish setting up automatic HTTPS and manage certs;
+ // this must happen before each server is started
+ err := app.automaticHTTPSPhase2()
if err != nil {
return fmt.Errorf("enabling automatic HTTPS: %v", err)
}
@@ -235,8 +242,8 @@ func (app *App) Start() error {
if err != nil {
return fmt.Errorf("%s: parsing listen address '%s': %v", srvName, lnAddr, err)
}
- for i := uint(0); i < listenAddr.PortRangeSize(); i++ {
- hostport := listenAddr.JoinHostPort(i)
+ for portOffset := uint(0); portOffset < listenAddr.PortRangeSize(); portOffset++ {
+ hostport := listenAddr.JoinHostPort(portOffset)
ln, err := caddy.Listen(listenAddr.Network, hostport)
if err != nil {
return fmt.Errorf("%s: listening on %s: %v", listenAddr.Network, hostport, err)
@@ -249,8 +256,10 @@ func (app *App) Start() error {
}
}
- // enable TLS
- if len(srv.TLSConnPolicies) > 0 && int(i) != app.httpPort() {
+ // enable TLS if there is a policy and if this is not the HTTP port
+ if len(srv.TLSConnPolicies) > 0 &&
+ int(listenAddr.StartPort+portOffset) != app.httpPort() {
+ // create TLS listener
tlsCfg, err := srv.TLSConnPolicies.TLSConfig(app.ctx)
if err != nil {
return fmt.Errorf("%s/%s: making TLS configuration: %v", listenAddr.Network, hostport, err)
@@ -330,230 +339,6 @@ func (app *App) Stop() error {
return nil
}
-func (app *App) automaticHTTPS() error {
- tlsAppIface, err := app.ctx.App("tls")
- if err != nil {
- return fmt.Errorf("getting tls app: %v", err)
- }
- tlsApp := tlsAppIface.(*caddytls.TLS)
-
- // this map will store associations of HTTP listener
- // addresses to the routes that do HTTP->HTTPS redirects
- lnAddrRedirRoutes := make(map[string]Route)
-
- repl := caddy.NewReplacer()
-
- for srvName, srv := range app.Servers {
- srv.tlsApp = tlsApp
-
- if srv.AutoHTTPS.Disabled {
- continue
- }
-
- // skip if all listeners use the HTTP port
- if !srv.listenersUseAnyPortOtherThan(app.httpPort()) {
- app.logger.Info("server is only listening on the HTTP port, so no automatic HTTPS will be applied to this server",
- zap.String("server_name", srvName),
- zap.Int("http_port", app.httpPort()),
- )
- continue
- }
-
- // if all listeners are on the HTTPS port, make sure
- // there is at least one TLS connection policy; it
- // should be obvious that they want to use TLS without
- // needing to specify one empty policy to enable it
- if !srv.listenersUseAnyPortOtherThan(app.httpsPort()) && len(srv.TLSConnPolicies) == 0 {
- app.logger.Info("server is only listening on the HTTPS port but has no TLS connection policies; adding one to enable TLS",
- zap.String("server_name", srvName),
- zap.Int("https_port", app.httpsPort()),
- )
- srv.TLSConnPolicies = append(srv.TLSConnPolicies, new(caddytls.ConnectionPolicy))
- }
-
- // find all qualifying domain names, de-duplicated
- domainSet := make(map[string]struct{})
- for routeIdx, route := range srv.Routes {
- for matcherSetIdx, matcherSet := range route.MatcherSets {
- for matcherIdx, m := range matcherSet {
- if hm, ok := m.(*MatchHost); ok {
- for hostMatcherIdx, d := range *hm {
- d, err = repl.ReplaceOrErr(d, true, false)
- if err != nil {
- return fmt.Errorf("%s: route %d, matcher set %d, matcher %d, host matcher %d: %v",
- srvName, routeIdx, matcherSetIdx, matcherIdx, hostMatcherIdx, err)
- }
- if certmagic.HostQualifies(d) &&
- !srv.AutoHTTPS.Skipped(d, srv.AutoHTTPS.Skip) {
- domainSet[d] = struct{}{}
- }
- }
- }
- }
- }
- }
-
- if len(domainSet) > 0 {
- // marshal the domains into a slice
- var domains, domainsForCerts []string
- for d := range domainSet {
- domains = append(domains, d)
- if !srv.AutoHTTPS.Skipped(d, srv.AutoHTTPS.SkipCerts) {
- // if a certificate for this name is already loaded,
- // don't obtain another one for it, unless we are
- // supposed to ignore loaded certificates
- if !srv.AutoHTTPS.IgnoreLoadedCerts &&
- len(tlsApp.AllMatchingCertificates(d)) > 0 {
- app.logger.Info("skipping automatic certificate management because one or more matching certificates are already loaded",
- zap.String("domain", d),
- zap.String("server_name", srvName),
- )
- continue
- }
- domainsForCerts = append(domainsForCerts, d)
- }
- }
-
- // ensure that these certificates are managed properly;
- // for example, it's implied that the HTTPPort should also
- // be the port the HTTP challenge is solved on, and so
- // for HTTPS port and TLS-ALPN challenge also - we need
- // to tell the TLS app to manage these certs by honoring
- // those port configurations
- acmeManager := &caddytls.ACMEManagerMaker{
- Challenges: &caddytls.ChallengesConfig{
- HTTP: &caddytls.HTTPChallengeConfig{
- AlternatePort: app.HTTPPort, // we specifically want the user-configured port, if any
- },
- TLSALPN: &caddytls.TLSALPNChallengeConfig{
- AlternatePort: app.HTTPSPort, // we specifically want the user-configured port, if any
- },
- },
- }
- if tlsApp.Automation == nil {
- tlsApp.Automation = new(caddytls.AutomationConfig)
- }
- tlsApp.Automation.Policies = append(tlsApp.Automation.Policies,
- caddytls.AutomationPolicy{
- Hosts: domainsForCerts,
- Management: acmeManager,
- })
-
- // manage their certificates
- app.logger.Info("enabling automatic TLS certificate management",
- zap.Strings("domains", domainsForCerts),
- )
- err := tlsApp.Manage(domainsForCerts)
- if err != nil {
- return fmt.Errorf("%s: managing certificate for %s: %s", srvName, domains, err)
- }
-
- // tell the server to use TLS if it is not already doing so
- if srv.TLSConnPolicies == nil {
- srv.TLSConnPolicies = caddytls.ConnectionPolicies{
- &caddytls.ConnectionPolicy{ALPN: defaultALPN},
- }
- }
-
- if srv.AutoHTTPS.DisableRedir {
- continue
- }
-
- app.logger.Info("enabling automatic HTTP->HTTPS redirects",
- zap.Strings("domains", domains),
- )
-
- // create HTTP->HTTPS redirects
- for _, addr := range srv.Listen {
- netw, host, port, err := caddy.SplitNetworkAddress(addr)
- if err != nil {
- return fmt.Errorf("%s: invalid listener address: %v", srvName, addr)
- }
-
- if parts := strings.SplitN(port, "-", 2); len(parts) == 2 {
- port = parts[0]
- }
- redirTo := "https://{http.request.host}"
-
- if port != strconv.Itoa(app.httpsPort()) {
- redirTo += ":" + port
- }
- redirTo += "{http.request.uri}"
-
- // build the plaintext HTTP variant of this address
- httpRedirLnAddr := caddy.JoinNetworkAddress(netw, host, strconv.Itoa(app.httpPort()))
-
- // create the route that does the redirect and associate
- // it with the listener address it will be served from
- lnAddrRedirRoutes[httpRedirLnAddr] = Route{
- MatcherSets: []MatcherSet{{MatchProtocol("http")}},
- Handlers: []MiddlewareHandler{
- StaticResponse{
- StatusCode: WeakString(strconv.Itoa(http.StatusPermanentRedirect)),
- Headers: http.Header{
- "Location": []string{redirTo},
- "Connection": []string{"close"},
- },
- Close: true,
- },
- },
- }
-
- }
- }
- }
-
- // if there are HTTP->HTTPS redirects to add, do so now
- if len(lnAddrRedirRoutes) > 0 {
- var redirServerAddrs []string
- var redirRoutes RouteList
-
- // for each redirect listener, see if there's already a
- // server configured to listen on that exact address; if so,
- // simply add the redirect route to the end of its route
- // list; otherwise, we'll create a new server for all the
- // listener addresses that are unused and serve the
- // remaining redirects from it
- redirRoutesLoop:
- for addr, redirRoute := range lnAddrRedirRoutes {
- for srvName, srv := range app.Servers {
- if srv.hasListenerAddress(addr) {
- // user has configured a server for the same address
- // that the redirect runs from; simply append our
- // redirect route to the existing routes, with a
- // caveat that their config might override ours
- app.logger.Warn("server is listening on same interface as redirects, so automatic HTTP->HTTPS redirects might be overridden by your own configuration",
- zap.String("server_name", srvName),
- zap.String("interface", addr),
- )
- srv.Routes = append(srv.Routes, redirRoute)
- continue redirRoutesLoop
- }
- }
- // no server with this listener address exists;
- // save this address and route for custom server
- redirServerAddrs = append(redirServerAddrs, addr)
- redirRoutes = append(redirRoutes, redirRoute)
- }
-
- // if there are routes remaining which do not belong
- // in any existing server, make our own to serve the
- // rest of the redirects
- if len(redirServerAddrs) > 0 {
- app.Servers["remaining_auto_https_redirects"] = &Server{
- Listen: redirServerAddrs,
- Routes: redirRoutes,
- tlsApp: tlsApp, // required to solve HTTP challenge
- logger: app.logger.Named("log"),
- errorLogger: app.logger.Named("log.error"),
- primaryHandlerChain: redirRoutes.Compile(emptyHandler),
- }
- }
- }
-
- return nil
-}
-
func (app *App) httpPort() int {
if app.HTTPPort == 0 {
return DefaultHTTPPort
@@ -709,7 +494,9 @@ func StatusCodeMatches(actual, configured int) bool {
if actual == configured {
return true
}
- if configured < 100 && actual >= configured*100 && actual < (configured+1)*100 {
+ if configured < 100 &&
+ actual >= configured*100 &&
+ actual < (configured+1)*100 {
return true
}
return false
diff --git a/modules/caddyhttp/routes.go b/modules/caddyhttp/routes.go
index 431d1a5..d4ff02a 100644
--- a/modules/caddyhttp/routes.go
+++ b/modules/caddyhttp/routes.go
@@ -113,23 +113,43 @@ func (r Route) Empty() bool {
// create a middleware chain.
type RouteList []Route
-// Provision sets up all the routes by loading the modules.
+// Provision sets up both the matchers and handlers in the route.
func (routes RouteList) Provision(ctx caddy.Context) error {
+ err := routes.ProvisionMatchers(ctx)
+ if err != nil {
+ return err
+ }
+ return routes.ProvisionHandlers(ctx)
+}
+
+// ProvisionMatchers sets up all the matchers by loading the
+// matcher modules. Only call this method directly if you need
+// to set up matchers and handlers separately without having
+// to provision a second time; otherwise use Provision instead.
+func (routes RouteList) ProvisionMatchers(ctx caddy.Context) error {
for i := range routes {
// matchers
matchersIface, err := ctx.LoadModule(&routes[i], "MatcherSetsRaw")
if err != nil {
- return fmt.Errorf("loading matchers in route %d: %v", i, err)
+ return fmt.Errorf("route %d: loading matcher modules: %v", i, err)
}
err = routes[i].MatcherSets.FromInterface(matchersIface)
if err != nil {
return fmt.Errorf("route %d: %v", i, err)
}
+ }
+ return nil
+}
- // handlers
+// ProvisionHandlers sets up all the handlers by loading the
+// handler modules. Only call this method directly if you need
+// to set up matchers and handlers separately without having
+// to provision a second time; otherwise use Provision instead.
+func (routes RouteList) ProvisionHandlers(ctx caddy.Context) error {
+ for i := range routes {
handlersIface, err := ctx.LoadModule(&routes[i], "HandlersRaw")
if err != nil {
- return fmt.Errorf("loading handler modules in route %d: %v", i, err)
+ return fmt.Errorf("route %d: loading handler modules: %v", i, err)
}
for _, handler := range handlersIface.([]interface{}) {
routes[i].Handlers = append(routes[i].Handlers, handler.(MiddlewareHandler))
@@ -140,7 +160,6 @@ func (routes RouteList) Provision(ctx caddy.Context) error {
routes[i].middleware = append(routes[i].middleware, wrapMiddleware(midhandler))
}
}
-
return nil
}
diff --git a/modules/caddyhttp/server.go b/modules/caddyhttp/server.go
index ce61b13..1c896a4 100644
--- a/modules/caddyhttp/server.go
+++ b/modules/caddyhttp/server.go
@@ -173,7 +173,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
log("handled request",
- zap.String("common_log", repl.ReplaceAll(CommonLogFormat, "-")),
+ zap.String("common_log", repl.ReplaceAll(commonLogFormat, "-")),
zap.Duration("latency", latency),
zap.Int("size", wrec.Size()),
zap.Int("status", wrec.Status()),
@@ -317,49 +317,6 @@ func (s *Server) hasTLSClientAuth() bool {
return false
}
-// AutoHTTPSConfig is used to disable automatic HTTPS
-// or certain aspects of it for a specific server.
-// HTTPS is enabled automatically and by default when
-// qualifying hostnames are available from the config.
-type AutoHTTPSConfig struct {
- // If true, automatic HTTPS will be entirely disabled.
- Disabled bool `json:"disable,omitempty"`
-
- // If true, only automatic HTTP->HTTPS redirects will
- // be disabled.
- DisableRedir bool `json:"disable_redirects,omitempty"`
-
- // Hosts/domain names listed here will not be included
- // in automatic HTTPS (they will not have certificates
- // loaded nor redirects applied).
- Skip []string `json:"skip,omitempty"`
-
- // Hosts/domain names listed here will still be enabled
- // for automatic HTTPS (unless in the Skip list), except
- // that certificates will not be provisioned and managed
- // for these names.
- SkipCerts []string `json:"skip_certificates,omitempty"`
-
- // By default, automatic HTTPS will obtain and renew
- // certificates for qualifying hostnames. However, if
- // a certificate with a matching SAN is already loaded
- // into the cache, certificate management will not be
- // enabled. To force automated certificate management
- // regardless of loaded certificates, set this to true.
- IgnoreLoadedCerts bool `json:"ignore_loaded_certificates,omitempty"`
-}
-
-// Skipped returns true if name is in skipSlice, which
-// should be one of the Skip* fields on ahc.
-func (ahc AutoHTTPSConfig) Skipped(name string, skipSlice []string) bool {
- for _, n := range skipSlice {
- if name == n {
- return true
- }
- }
- return false
-}
-
// HTTPErrorConfig determines how to handle errors
// from the HTTP handlers.
type HTTPErrorConfig struct {
@@ -466,11 +423,11 @@ func cloneURL(from, to *url.URL) {
}
const (
- // CommonLogFormat is the common log format. https://en.wikipedia.org/wiki/Common_Log_Format
- CommonLogFormat = `{http.request.remote.host} ` + CommonLogEmptyValue + ` {http.authentication.user.id} [{time.now.common_log}] "{http.request.orig_method} {http.request.orig_uri} {http.request.proto}" {http.response.status} {http.response.size}`
+ // commonLogFormat is the common log format. https://en.wikipedia.org/wiki/Common_Log_Format
+ commonLogFormat = `{http.request.remote.host} ` + commonLogEmptyValue + ` {http.authentication.user.id} [{time.now.common_log}] "{http.request.orig_method} {http.request.orig_uri} {http.request.proto}" {http.response.status} {http.response.size}`
- // CommonLogEmptyValue is the common empty log value.
- CommonLogEmptyValue = "-"
+ // commonLogEmptyValue is the common empty log value.
+ commonLogEmptyValue = "-"
)
// Context keys for HTTP request context values.