summaryrefslogtreecommitdiff
path: root/caddyconfig/httpcaddyfile/httptype.go
diff options
context:
space:
mode:
Diffstat (limited to 'caddyconfig/httpcaddyfile/httptype.go')
-rw-r--r--caddyconfig/httpcaddyfile/httptype.go231
1 files changed, 62 insertions, 169 deletions
diff --git a/caddyconfig/httpcaddyfile/httptype.go b/caddyconfig/httpcaddyfile/httptype.go
index 15cfe1e..18dd0a0 100644
--- a/caddyconfig/httpcaddyfile/httptype.go
+++ b/caddyconfig/httpcaddyfile/httptype.go
@@ -26,7 +26,6 @@ import (
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/caddyserver/caddy/v2/modules/caddytls"
- "github.com/caddyserver/certmagic"
)
func init() {
@@ -177,105 +176,10 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
Servers: servers,
}
- // now for the TLS app! (TODO: refactor into own func)
- tlsApp := caddytls.TLS{CertificatesRaw: make(caddy.ModuleMap)}
- var certLoaders []caddytls.CertificateLoader
- for _, p := range pairings {
- for i, sblock := range p.serverBlocks {
- // tls automation policies
- if issuerVals, ok := sblock.pile["tls.cert_issuer"]; ok {
- for _, issuerVal := range issuerVals {
- issuer := issuerVal.Value.(certmagic.Issuer)
- sblockHosts, err := st.hostsFromServerBlockKeys(sblock.block)
- if err != nil {
- return nil, warnings, err
- }
- if len(sblockHosts) > 0 {
- if tlsApp.Automation == nil {
- tlsApp.Automation = new(caddytls.AutomationConfig)
- }
- tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, &caddytls.AutomationPolicy{
- Subjects: sblockHosts,
- IssuerRaw: caddyconfig.JSONModuleObject(issuer, "module", issuer.(caddy.Module).CaddyModule().ID.Name(), &warnings),
- })
- } else {
- warnings = append(warnings, caddyconfig.Warning{
- Message: fmt.Sprintf("Server block %d %v has no names that qualify for automatic HTTPS, so no TLS automation policy will be added.", i, sblock.block.Keys),
- })
- }
- }
- }
- // tls certificate loaders
- if clVals, ok := sblock.pile["tls.certificate_loader"]; ok {
- for _, clVal := range clVals {
- certLoaders = append(certLoaders, clVal.Value.(caddytls.CertificateLoader))
- }
- }
- }
- }
- // group certificate loaders by module name, then add to config
- if len(certLoaders) > 0 {
- loadersByName := make(map[string]caddytls.CertificateLoader)
- for _, cl := range certLoaders {
- name := caddy.GetModuleName(cl)
- // ugh... technically, we may have multiple FileLoader and FolderLoader
- // modules (because the tls directive returns one per occurrence), but
- // the config structure expects only one instance of each kind of loader
- // module, so we have to combine them... instead of enumerating each
- // possible cert loader module in a type switch, we can use reflection,
- // which works on any cert loaders that are slice types
- if reflect.TypeOf(cl).Kind() == reflect.Slice {
- combined := reflect.ValueOf(loadersByName[name])
- if !combined.IsValid() {
- combined = reflect.New(reflect.TypeOf(cl)).Elem()
- }
- clVal := reflect.ValueOf(cl)
- for i := 0; i < clVal.Len(); i++ {
- combined = reflect.Append(reflect.Value(combined), clVal.Index(i))
- }
- loadersByName[name] = combined.Interface().(caddytls.CertificateLoader)
- }
- }
- for certLoaderName, loaders := range loadersByName {
- tlsApp.CertificatesRaw[certLoaderName] = caddyconfig.JSON(loaders, &warnings)
- }
- }
- // if global ACME CA, DNS, or email were set, append a catch-all automation
- // policy that ensures they will be used if no tls directive was used
- acmeCA, hasACMECA := options["acme_ca"]
- acmeDNS, hasACMEDNS := options["acme_dns"]
- email, hasEmail := options["email"]
- if hasACMECA || hasACMEDNS || hasEmail {
- if tlsApp.Automation == nil {
- tlsApp.Automation = new(caddytls.AutomationConfig)
- }
- if !hasACMECA {
- acmeCA = ""
- }
- if !hasEmail {
- email = ""
- }
- mgr := caddytls.ACMEIssuer{
- CA: acmeCA.(string),
- Email: email.(string),
- }
- if hasACMEDNS {
- provName := acmeDNS.(string)
- dnsProvModule, err := caddy.GetModule("tls.dns." + provName)
- if err != nil {
- return nil, warnings, fmt.Errorf("getting DNS provider module named '%s': %v", provName, err)
- }
- mgr.Challenges = &caddytls.ChallengesConfig{
- DNSRaw: caddyconfig.JSONModuleObject(dnsProvModule.New(), "provider", provName, &warnings),
- }
- }
- tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, &caddytls.AutomationPolicy{
- IssuerRaw: caddyconfig.JSONModuleObject(mgr, "module", "acme", &warnings),
- })
- }
- if tlsApp.Automation != nil {
- // consolidate automation policies that are the exact same
- tlsApp.Automation.Policies = consolidateAutomationPolicies(tlsApp.Automation.Policies)
+ // then make the TLS app
+ tlsApp, warnings, err := st.buildTLSApp(pairings, options, warnings)
+ if err != nil {
+ return nil, warnings, err
}
// if experimental HTTP/3 is enabled, enable it on each server
@@ -316,10 +220,10 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
// annnd the top-level config, then we're done!
cfg := &caddy.Config{AppsRaw: make(caddy.ModuleMap)}
- if !reflect.DeepEqual(httpApp, caddyhttp.App{}) {
+ if len(httpApp.Servers) > 0 {
cfg.AppsRaw["http"] = caddyconfig.JSON(httpApp, &warnings)
}
- if !reflect.DeepEqual(tlsApp, caddytls.TLS{CertificatesRaw: make(caddy.ModuleMap)}) {
+ if !reflect.DeepEqual(tlsApp, &caddytls.TLS{CertificatesRaw: make(caddy.ModuleMap)}) {
cfg.AppsRaw["tls"] = caddyconfig.JSON(tlsApp, &warnings)
}
if storageCvtr, ok := options["storage"].(caddy.StorageConverter); ok {
@@ -377,7 +281,6 @@ func (ServerType) evaluateGlobalOptionsBlock(serverBlocks []serverBlock, options
var val interface{}
var err error
disp := caddyfile.NewDispenser(segment)
- // TODO: make this switch into a map
switch dir {
case "http_port":
val, err = parseOptHTTPPort(disp)
@@ -399,6 +302,10 @@ func (ServerType) evaluateGlobalOptionsBlock(serverBlocks []serverBlock, options
val, err = parseOptAdmin(disp)
case "debug":
options["debug"] = true
+ case "on_demand_tls":
+ val, err = parseOptOnDemand(disp)
+ case "local_certs":
+ val = true
default:
return nil, fmt.Errorf("unrecognized parameter name: %s", dir)
}
@@ -411,8 +318,10 @@ func (ServerType) evaluateGlobalOptionsBlock(serverBlocks []serverBlock, options
return serverBlocks[1:], nil
}
-// hostsFromServerBlockKeys returns a list of all the
-// hostnames found in the keys of the server block sb.
+// hostsFromServerBlockKeys returns a list of all the non-empty hostnames
+// found in the keys of the server block sb. If sb has a key that omits
+// the hostname (i.e. is a catch-all/empty host), then the returned list
+// is empty, because the server block effectively matches ALL hosts.
// The list may not be in a consistent order.
func (st *ServerType) hostsFromServerBlockKeys(sb caddyfile.ServerBlock) ([]string, error) {
// first get each unique hostname
@@ -424,7 +333,9 @@ func (st *ServerType) hostsFromServerBlockKeys(sb caddyfile.ServerBlock) ([]stri
}
addr = addr.Normalize()
if addr.Host == "" {
- continue
+ // server block contains a key like ":443", i.e. the host portion
+ // is empty / catch-all, which means to match all hosts
+ return []string{}, nil
}
hostMap[addr.Host] = struct{}{}
}
@@ -497,25 +408,18 @@ func (st *ServerType) serversFromPairings(
return nil, fmt.Errorf("server block %v: compiling matcher sets: %v", sblock.block.Keys, err)
}
- // tls: connection policies and toggle auto HTTPS
- if _, ok := sblock.pile["tls.off"]; ok {
- // TODO: right now, no directives yield any tls.off value...
- // tls off: disable TLS (and automatic HTTPS) for server block's names
- if srv.AutoHTTPS == nil {
- srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
- }
- srv.AutoHTTPS.Disabled = true
- } else if cpVals, ok := sblock.pile["tls.connection_policy"]; ok {
- // tls connection policies
+ hosts, err := st.hostsFromServerBlockKeys(sblock.block)
+ if err != nil {
+ return nil, err
+ }
+ // tls: connection policies
+ if cpVals, ok := sblock.pile["tls.connection_policy"]; ok {
+ // tls connection policies
for _, cpVal := range cpVals {
cp := cpVal.Value.(*caddytls.ConnectionPolicy)
// make sure the policy covers all hostnames from the block
- hosts, err := st.hostsFromServerBlockKeys(sblock.block)
- if err != nil {
- return nil, err
- }
for _, h := range hosts {
if h == defaultSNI {
hosts = append(hosts, "")
@@ -524,7 +428,6 @@ func (st *ServerType) serversFromPairings(
}
}
- // TODO: are matchers needed if every hostname of the resulting config is matched?
if len(hosts) > 0 {
cp.MatchersRaw = caddy.ModuleMap{
"sni": caddyconfig.JSON(hosts, warnings), // make sure to match all hosts, not just auto-HTTPS-qualified ones
@@ -536,7 +439,6 @@ func (st *ServerType) serversFromPairings(
srv.TLSConnPolicies = append(srv.TLSConnPolicies, cp)
}
- // TODO: consolidate equal conn policies?
}
// exclude any hosts that were defined explicitly with
@@ -547,7 +449,7 @@ func (st *ServerType) serversFromPairings(
return nil, err
}
addr = addr.Normalize()
- if addr.Scheme == "http" {
+ if addr.Scheme == "http" && addr.Host != "" {
if srv.AutoHTTPS == nil {
srv.AutoHTTPS = new(caddyhttp.AutoHTTPSConfig)
}
@@ -607,10 +509,15 @@ func (st *ServerType) serversFromPairings(
// catch-all/default policy if there isn't one already (it's
// important that it goes at the end) - see issue #3004:
// https://github.com/caddyserver/caddy/issues/3004
+ // TODO: maybe a smarter way to handle this might be to just make the
+ // auto-HTTPS logic at provision-time detect if there is any connection
+ // policy missing for any HTTPS-enabled hosts, if so, add it... maybe?
if !hasCatchAllTLSConnPolicy && (len(srv.TLSConnPolicies) > 0 || defaultSNI != "") {
srv.TLSConnPolicies = append(srv.TLSConnPolicies, &caddytls.ConnectionPolicy{DefaultSNI: defaultSNI})
}
+ // tidy things up a bit
+ srv.TLSConnPolicies = consolidateConnPolicies(srv.TLSConnPolicies)
srv.Routes = consolidateRoutes(srv.Routes)
servers[fmt.Sprintf("srv%d", i)] = srv
@@ -619,6 +526,26 @@ func (st *ServerType) serversFromPairings(
return servers, nil
}
+// consolidateConnPolicies combines TLS connection policies that are the same,
+// for a cleaner overall output.
+func consolidateConnPolicies(cps caddytls.ConnectionPolicies) caddytls.ConnectionPolicies {
+ for i := 0; i < len(cps); i++ {
+ for j := 0; j < len(cps); j++ {
+ if j == i {
+ continue
+ }
+
+ // if they're exactly equal in every way, just keep one of them
+ if reflect.DeepEqual(cps[i], cps[j]) {
+ cps = append(cps[:j], cps[j+1:]...)
+ i--
+ break
+ }
+ }
+ }
+ return cps
+}
+
// appendSubrouteToRouteList appends the routes in subroute
// to the routeList, optionally qualified by matchers.
func appendSubrouteToRouteList(routeList caddyhttp.RouteList,
@@ -750,52 +677,6 @@ func consolidateRoutes(routes caddyhttp.RouteList) caddyhttp.RouteList {
return routes
}
-// consolidateAutomationPolicies combines automation policies that are the same,
-// for a cleaner overall output.
-func consolidateAutomationPolicies(aps []*caddytls.AutomationPolicy) []*caddytls.AutomationPolicy {
- for i := 0; i < len(aps); i++ {
- for j := 0; j < len(aps); j++ {
- if j == i {
- continue
- }
-
- // if they're exactly equal in every way, just keep one of them
- if reflect.DeepEqual(aps[i], aps[j]) {
- aps = append(aps[:j], aps[j+1:]...)
- i--
- break
- }
-
- // if the policy is the same, we can keep just one, but we have
- // to be careful which one we keep; if only one has any hostnames
- // defined, then we need to keep the one without any hostnames,
- // otherwise the one without any subjects (a catch-all) would be
- // eaten up by the one with subjects; and if both have subjects, we
- // need to combine their lists
- if reflect.DeepEqual(aps[i].IssuerRaw, aps[j].IssuerRaw) &&
- aps[i].ManageSync == aps[j].ManageSync {
- if len(aps[i].Subjects) == 0 && len(aps[j].Subjects) > 0 {
- aps = append(aps[:j], aps[j+1:]...)
- } else if len(aps[i].Subjects) > 0 && len(aps[j].Subjects) == 0 {
- aps = append(aps[:i], aps[i+1:]...)
- } else {
- aps[i].Subjects = append(aps[i].Subjects, aps[j].Subjects...)
- aps = append(aps[:j], aps[j+1:]...)
- }
- i--
- break
- }
- }
- }
-
- // ensure any catch-all policies go last
- sort.SliceStable(aps, func(i, j int) bool {
- return len(aps[i].Subjects) > len(aps[j].Subjects)
- })
-
- return aps
-}
-
func matcherSetFromMatcherToken(
tkn caddyfile.Token,
matcherDefs map[string]caddy.ModuleMap,
@@ -831,6 +712,7 @@ func (st *ServerType) compileEncodedMatcherSets(sblock caddyfile.ServerBlock) ([
// keep routes with common host and path matchers together
var matcherPairs []*hostPathPair
+ var catchAllHosts bool
for _, key := range sblock.Keys {
addr, err := ParseAddress(key)
if err != nil {
@@ -856,6 +738,17 @@ func (st *ServerType) compileEncodedMatcherSets(sblock caddyfile.ServerBlock) ([
matcherPairs = append(matcherPairs, chosenMatcherPair)
}
+ // if one of the keys has no host (i.e. is a catch-all for
+ // any hostname), then we need to null out the host matcher
+ // entirely so that it matches all hosts
+ if addr.Host == "" && !catchAllHosts {
+ chosenMatcherPair.hostm = nil
+ catchAllHosts = true
+ }
+ if catchAllHosts {
+ continue
+ }
+
// add this server block's keys to the matcher
// pair if it doesn't already exist
if addr.Host != "" {