aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorFrancis Lavoie <[email protected]>2024-12-24 10:58:40 -0500
committerGitHub <[email protected]>2024-12-24 08:58:40 -0700
commitafa778ae05503f563af0d1015cdf7e5e78b1eeec (patch)
tree22475dee1f8000db62ee6bcdd4d82ba9a640dcaa
parent5ba1e06fd661aac2cbaab6d4a2ef63a9eb877a46 (diff)
downloadcaddy-afa778ae05503f563af0d1015cdf7e5e78b1eeec.tar.gz
caddy-afa778ae05503f563af0d1015cdf7e5e78b1eeec.zip
httpcaddyfile: Implement experimental `force_automate` option (#6712)HEADmaster
-rw-r--r--caddyconfig/httpcaddyfile/builtins.go17
-rw-r--r--caddyconfig/httpcaddyfile/httptype.go22
-rw-r--r--caddyconfig/httpcaddyfile/tlsapp.go16
-rw-r--r--caddytest/integration/caddyfile_adapt/tls_automation_wildcard_force_automate.caddyfiletest180
-rw-r--r--caddytest/integration/caddyfile_adapt/tls_automation_wildcard_shadowing.caddyfiletest102
5 files changed, 334 insertions, 3 deletions
diff --git a/caddyconfig/httpcaddyfile/builtins.go b/caddyconfig/httpcaddyfile/builtins.go
index 99a4d916a..45570d016 100644
--- a/caddyconfig/httpcaddyfile/builtins.go
+++ b/caddyconfig/httpcaddyfile/builtins.go
@@ -84,7 +84,7 @@ func parseBind(h Helper) ([]ConfigValue, error) {
// parseTLS parses the tls directive. Syntax:
//
-// tls [<email>|internal]|[<cert_file> <key_file>] {
+// tls [<email>|internal|force_automate]|[<cert_file> <key_file>] {
// protocols <min> [<max>]
// ciphers <cipher_suites...>
// curves <curves...>
@@ -107,6 +107,7 @@ func parseBind(h Helper) ([]ConfigValue, error) {
// dns_challenge_override_domain <domain>
// on_demand
// reuse_private_keys
+// force_automate
// eab <key_id> <mac_key>
// issuer <module_name> [...]
// get_certificate <module_name> [...]
@@ -126,6 +127,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
var certManagers []certmagic.Manager
var onDemand bool
var reusePrivateKeys bool
+ var forceAutomate bool
firstLine := h.RemainingArgs()
switch len(firstLine) {
@@ -133,8 +135,10 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
case 1:
if firstLine[0] == "internal" {
internalIssuer = new(caddytls.InternalIssuer)
+ } else if firstLine[0] == "force_automate" {
+ forceAutomate = true
} else if !strings.Contains(firstLine[0], "@") {
- return nil, h.Err("single argument must either be 'internal' or an email address")
+ return nil, h.Err("single argument must either be 'internal', 'force_automate', or an email address")
} else {
acmeIssuer = &caddytls.ACMEIssuer{
Email: firstLine[0],
@@ -569,6 +573,15 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
})
}
+ // if enabled, the names in the site addresses will be
+ // added to the automation policies
+ if forceAutomate {
+ configVals = append(configVals, ConfigValue{
+ Class: "tls.force_automate",
+ Value: true,
+ })
+ }
+
// custom certificate selection
if len(certSelector.AnyTag) > 0 {
cp.CertSelection = &certSelector
diff --git a/caddyconfig/httpcaddyfile/httptype.go b/caddyconfig/httpcaddyfile/httptype.go
index c169b92af..37a6f6b23 100644
--- a/caddyconfig/httpcaddyfile/httptype.go
+++ b/caddyconfig/httpcaddyfile/httptype.go
@@ -763,6 +763,14 @@ func (st *ServerType) serversFromPairings(
}
}
+ // collect hosts that are forced to be automated
+ forceAutomatedNames := make(map[string]struct{})
+ if _, ok := sblock.pile["tls.force_automate"]; ok {
+ for _, host := range hosts {
+ forceAutomatedNames[host] = struct{}{}
+ }
+ }
+
// tls: connection policies
if cpVals, ok := sblock.pile["tls.connection_policy"]; ok {
// tls connection policies
@@ -794,7 +802,7 @@ func (st *ServerType) serversFromPairings(
}
// only append this policy if it actually changes something
- if !cp.SettingsEmpty() {
+ if !cp.SettingsEmpty() || mapContains(forceAutomatedNames, hosts) {
srv.TLSConnPolicies = append(srv.TLSConnPolicies, cp)
hasCatchAllTLSConnPolicy = len(hosts) == 0
}
@@ -1661,6 +1669,18 @@ func listenersUseAnyPortOtherThan(addresses []string, otherPort string) bool {
return false
}
+func mapContains[K comparable, V any](m map[K]V, keys []K) bool {
+ if len(m) == 0 || len(keys) == 0 {
+ return false
+ }
+ for _, key := range keys {
+ if _, ok := m[key]; ok {
+ return true
+ }
+ }
+ return false
+}
+
// specificity returns len(s) minus any wildcards (*) and
// placeholders ({...}). Basically, it's a length count
// that penalizes the use of wildcards and placeholders.
diff --git a/caddyconfig/httpcaddyfile/tlsapp.go b/caddyconfig/httpcaddyfile/tlsapp.go
index 397323f71..09a862e76 100644
--- a/caddyconfig/httpcaddyfile/tlsapp.go
+++ b/caddyconfig/httpcaddyfile/tlsapp.go
@@ -94,6 +94,9 @@ func (st ServerType) buildTLSApp(
// collect all hosts that have a wildcard in them, and arent HTTP
wildcardHosts := []string{}
+ // hosts that have been explicitly marked to be automated,
+ // even if covered by another wildcard
+ forcedAutomatedNames := make(map[string]struct{})
for _, p := range pairings {
var addresses []string
for _, addressWithProtocols := range p.addressesWithProtocols {
@@ -150,6 +153,13 @@ func (st ServerType) buildTLSApp(
ap.OnDemand = true
}
+ // collect hosts that are forced to be automated
+ if _, ok := sblock.pile["tls.force_automate"]; ok {
+ for _, host := range sblockHosts {
+ forcedAutomatedNames[host] = struct{}{}
+ }
+ }
+
// reuse private keys tls
if _, ok := sblock.pile["tls.reuse_private_keys"]; ok {
ap.ReusePrivateKeys = true
@@ -407,6 +417,12 @@ func (st ServerType) buildTLSApp(
}
}
}
+ for name := range forcedAutomatedNames {
+ if slices.Contains(al, name) {
+ continue
+ }
+ al = append(al, name)
+ }
if len(al) > 0 {
tlsApp.CertificatesRaw["automate"] = caddyconfig.JSON(al, &warnings)
}
diff --git a/caddytest/integration/caddyfile_adapt/tls_automation_wildcard_force_automate.caddyfiletest b/caddytest/integration/caddyfile_adapt/tls_automation_wildcard_force_automate.caddyfiletest
new file mode 100644
index 000000000..4eb6c4f1c
--- /dev/null
+++ b/caddytest/integration/caddyfile_adapt/tls_automation_wildcard_force_automate.caddyfiletest
@@ -0,0 +1,180 @@
+automated1.example.com {
+ tls force_automate
+ respond "Automated!"
+}
+
+automated2.example.com {
+ tls force_automate
+ respond "Automated!"
+}
+
+shadowed.example.com {
+ respond "Shadowed!"
+}
+
+*.example.com {
+ tls cert.pem key.pem
+ respond "Wildcard!"
+}
+----------
+{
+ "apps": {
+ "http": {
+ "servers": {
+ "srv0": {
+ "listen": [
+ ":443"
+ ],
+ "routes": [
+ {
+ "match": [
+ {
+ "host": [
+ "automated1.example.com"
+ ]
+ }
+ ],
+ "handle": [
+ {
+ "handler": "subroute",
+ "routes": [
+ {
+ "handle": [
+ {
+ "body": "Automated!",
+ "handler": "static_response"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "terminal": true
+ },
+ {
+ "match": [
+ {
+ "host": [
+ "automated2.example.com"
+ ]
+ }
+ ],
+ "handle": [
+ {
+ "handler": "subroute",
+ "routes": [
+ {
+ "handle": [
+ {
+ "body": "Automated!",
+ "handler": "static_response"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "terminal": true
+ },
+ {
+ "match": [
+ {
+ "host": [
+ "shadowed.example.com"
+ ]
+ }
+ ],
+ "handle": [
+ {
+ "handler": "subroute",
+ "routes": [
+ {
+ "handle": [
+ {
+ "body": "Shadowed!",
+ "handler": "static_response"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "terminal": true
+ },
+ {
+ "match": [
+ {
+ "host": [
+ "*.example.com"
+ ]
+ }
+ ],
+ "handle": [
+ {
+ "handler": "subroute",
+ "routes": [
+ {
+ "handle": [
+ {
+ "body": "Wildcard!",
+ "handler": "static_response"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "terminal": true
+ }
+ ],
+ "tls_connection_policies": [
+ {
+ "match": {
+ "sni": [
+ "automated1.example.com"
+ ]
+ }
+ },
+ {
+ "match": {
+ "sni": [
+ "automated2.example.com"
+ ]
+ }
+ },
+ {
+ "match": {
+ "sni": [
+ "*.example.com"
+ ]
+ },
+ "certificate_selection": {
+ "any_tag": [
+ "cert0"
+ ]
+ }
+ },
+ {}
+ ]
+ }
+ }
+ },
+ "tls": {
+ "certificates": {
+ "automate": [
+ "automated1.example.com",
+ "automated2.example.com"
+ ],
+ "load_files": [
+ {
+ "certificate": "cert.pem",
+ "key": "key.pem",
+ "tags": [
+ "cert0"
+ ]
+ }
+ ]
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/caddytest/integration/caddyfile_adapt/tls_automation_wildcard_shadowing.caddyfiletest b/caddytest/integration/caddyfile_adapt/tls_automation_wildcard_shadowing.caddyfiletest
new file mode 100644
index 000000000..2be543779
--- /dev/null
+++ b/caddytest/integration/caddyfile_adapt/tls_automation_wildcard_shadowing.caddyfiletest
@@ -0,0 +1,102 @@
+subdomain.example.com {
+ respond "Subdomain!"
+}
+
+*.example.com {
+ tls cert.pem key.pem
+ respond "Wildcard!"
+}
+----------
+{
+ "apps": {
+ "http": {
+ "servers": {
+ "srv0": {
+ "listen": [
+ ":443"
+ ],
+ "routes": [
+ {
+ "match": [
+ {
+ "host": [
+ "subdomain.example.com"
+ ]
+ }
+ ],
+ "handle": [
+ {
+ "handler": "subroute",
+ "routes": [
+ {
+ "handle": [
+ {
+ "body": "Subdomain!",
+ "handler": "static_response"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "terminal": true
+ },
+ {
+ "match": [
+ {
+ "host": [
+ "*.example.com"
+ ]
+ }
+ ],
+ "handle": [
+ {
+ "handler": "subroute",
+ "routes": [
+ {
+ "handle": [
+ {
+ "body": "Wildcard!",
+ "handler": "static_response"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "terminal": true
+ }
+ ],
+ "tls_connection_policies": [
+ {
+ "match": {
+ "sni": [
+ "*.example.com"
+ ]
+ },
+ "certificate_selection": {
+ "any_tag": [
+ "cert0"
+ ]
+ }
+ },
+ {}
+ ]
+ }
+ }
+ },
+ "tls": {
+ "certificates": {
+ "load_files": [
+ {
+ "certificate": "cert.pem",
+ "key": "key.pem",
+ "tags": [
+ "cert0"
+ ]
+ }
+ ]
+ }
+ }
+ }
+} \ No newline at end of file