summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--caddyconfig/httpcaddyfile/options.go6
-rw-r--r--caddyconfig/httpcaddyfile/tlsapp.go7
-rw-r--r--caddytest/integration/caddyfile_adapt/global_options_preferred_chains.txt56
-rw-r--r--caddytest/integration/caddyfile_adapt/tls_acme_preferred_chains.txt57
-rw-r--r--modules/caddytls/acmeissuer.go62
5 files changed, 187 insertions, 1 deletions
diff --git a/caddyconfig/httpcaddyfile/options.go b/caddyconfig/httpcaddyfile/options.go
index fe8e319..f693110 100644
--- a/caddyconfig/httpcaddyfile/options.go
+++ b/caddyconfig/httpcaddyfile/options.go
@@ -49,6 +49,7 @@ func init() {
RegisterGlobalOption("servers", parseServerOptions)
RegisterGlobalOption("ocsp_stapling", parseOCSPStaplingOptions)
RegisterGlobalOption("log", parseLogOptions)
+ RegisterGlobalOption("preferred_chains", parseOptPreferredChains)
}
func parseOptTrue(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) { return true, nil }
@@ -452,3 +453,8 @@ func parseLogOptions(d *caddyfile.Dispenser, existingVal interface{}) (interface
return configValues, nil
}
+
+func parseOptPreferredChains(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
+ d.Next()
+ return caddytls.ParseCaddyfilePreferredChainsOptions(d)
+}
diff --git a/caddyconfig/httpcaddyfile/tlsapp.go b/caddyconfig/httpcaddyfile/tlsapp.go
index 2510a9b..b7a8f02 100644
--- a/caddyconfig/httpcaddyfile/tlsapp.go
+++ b/caddyconfig/httpcaddyfile/tlsapp.go
@@ -321,7 +321,8 @@ func (st ServerType) buildTLSApp(
globalACMECARoot := options["acme_ca_root"]
globalACMEDNS := options["acme_dns"]
globalACMEEAB := options["acme_eab"]
- hasGlobalACMEDefaults := globalEmail != nil || globalACMECA != nil || globalACMECARoot != nil || globalACMEDNS != nil || globalACMEEAB != nil
+ globalPreferredChains := options["preferred_chains"]
+ hasGlobalACMEDefaults := globalEmail != nil || globalACMECA != nil || globalACMECARoot != nil || globalACMEDNS != nil || globalACMEEAB != nil || globalPreferredChains != nil
if hasGlobalACMEDefaults {
for _, ap := range tlsApp.Automation.Policies {
if len(ap.Issuers) == 0 {
@@ -405,6 +406,7 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]interf
globalACMECARoot := options["acme_ca_root"]
globalACMEDNS := options["acme_dns"]
globalACMEEAB := options["acme_eab"]
+ globalPreferredChains := options["preferred_chains"]
if globalEmail != nil && acmeIssuer.Email == "" {
acmeIssuer.Email = globalEmail.(string)
@@ -425,6 +427,9 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]interf
if globalACMEEAB != nil && acmeIssuer.ExternalAccount == nil {
acmeIssuer.ExternalAccount = globalACMEEAB.(*acme.EAB)
}
+ if globalPreferredChains != nil && acmeIssuer.PreferredChains == nil {
+ acmeIssuer.PreferredChains = globalPreferredChains.(*caddytls.ChainPreference)
+ }
return nil
}
diff --git a/caddytest/integration/caddyfile_adapt/global_options_preferred_chains.txt b/caddytest/integration/caddyfile_adapt/global_options_preferred_chains.txt
new file mode 100644
index 0000000..893b34b
--- /dev/null
+++ b/caddytest/integration/caddyfile_adapt/global_options_preferred_chains.txt
@@ -0,0 +1,56 @@
+{
+ preferred_chains smallest
+}
+
+localhost
+----------
+{
+ "apps": {
+ "http": {
+ "servers": {
+ "srv0": {
+ "listen": [
+ ":443"
+ ],
+ "routes": [
+ {
+ "match": [
+ {
+ "host": [
+ "localhost"
+ ]
+ }
+ ],
+ "terminal": true
+ }
+ ]
+ }
+ }
+ },
+ "tls": {
+ "automation": {
+ "policies": [
+ {
+ "subjects": [
+ "localhost"
+ ],
+ "issuers": [
+ {
+ "module": "acme",
+ "preferred_chains": {
+ "smallest": true
+ }
+ },
+ {
+ "module": "zerossl",
+ "preferred_chains": {
+ "smallest": true
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/caddytest/integration/caddyfile_adapt/tls_acme_preferred_chains.txt b/caddytest/integration/caddyfile_adapt/tls_acme_preferred_chains.txt
new file mode 100644
index 0000000..d6242d7
--- /dev/null
+++ b/caddytest/integration/caddyfile_adapt/tls_acme_preferred_chains.txt
@@ -0,0 +1,57 @@
+localhost
+
+tls {
+ issuer acme {
+ preferred_chains {
+ any_common_name "Generic CA 1" "Generic CA 2"
+ }
+ }
+}
+----------
+{
+ "apps": {
+ "http": {
+ "servers": {
+ "srv0": {
+ "listen": [
+ ":443"
+ ],
+ "routes": [
+ {
+ "match": [
+ {
+ "host": [
+ "localhost"
+ ]
+ }
+ ],
+ "terminal": true
+ }
+ ]
+ }
+ }
+ },
+ "tls": {
+ "automation": {
+ "policies": [
+ {
+ "subjects": [
+ "localhost"
+ ],
+ "issuers": [
+ {
+ "module": "acme",
+ "preferred_chains": {
+ "any_common_name": [
+ "Generic CA 1",
+ "Generic CA 2"
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/modules/caddytls/acmeissuer.go b/modules/caddytls/acmeissuer.go
index 6085044..b60e560 100644
--- a/modules/caddytls/acmeissuer.go
+++ b/modules/caddytls/acmeissuer.go
@@ -265,6 +265,10 @@ func (iss *ACMEIssuer) GetACMEIssuer() *ACMEIssuer { return iss }
// trusted_roots <pem_files...>
// dns <provider_name> [<options>]
// resolvers <dns_servers...>
+// preferred_chains [smallest] {
+// root_common_name <common_names...>
+// any_common_name <common_names...>
+// }
// }
//
func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
@@ -416,6 +420,13 @@ func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return d.ArgErr()
}
+ case "preferred_chains":
+ chainPref, err := ParseCaddyfilePreferredChainsOptions(d)
+ if err != nil {
+ return err
+ }
+ iss.PreferredChains = chainPref
+
default:
return d.Errf("unrecognized ACME issuer property: %s", d.Val())
}
@@ -452,6 +463,57 @@ func onDemandAskRequest(ask string, name string) error {
return nil
}
+func ParseCaddyfilePreferredChainsOptions(d *caddyfile.Dispenser) (*ChainPreference, error) {
+ chainPref := new(ChainPreference)
+ if d.NextArg() {
+ smallestOpt := d.Val()
+ if smallestOpt == "smallest" {
+ trueBool := true
+ chainPref.Smallest = &trueBool
+ if d.NextArg() { // Only one argument allowed
+ return nil, d.ArgErr()
+ }
+ if d.NextBlock(d.Nesting()) { // Don't allow other options when smallest == true
+ return nil, d.Err("No more options are accepted when using the 'smallest' option")
+ }
+ } else { // Smallest option should always be 'smallest' or unset
+ return nil, d.Errf("Invalid argument '%s'", smallestOpt)
+ }
+ }
+ for nesting := d.Nesting(); d.NextBlock(nesting); {
+ switch d.Val() {
+ case "root_common_name":
+ rootCommonNameOpt := d.RemainingArgs()
+ chainPref.RootCommonName = rootCommonNameOpt
+ if rootCommonNameOpt == nil {
+ return nil, d.ArgErr()
+ }
+ if chainPref.AnyCommonName != nil {
+ return nil, d.Err("Can't set root_common_name when any_common_name is already set")
+ }
+
+ case "any_common_name":
+ anyCommonNameOpt := d.RemainingArgs()
+ chainPref.AnyCommonName = anyCommonNameOpt
+ if anyCommonNameOpt == nil {
+ return nil, d.ArgErr()
+ }
+ if chainPref.RootCommonName != nil {
+ return nil, d.Err("Can't set any_common_name when root_common_name is already set")
+ }
+
+ default:
+ return nil, d.Errf("Received unrecognized parameter '%s'", d.Val())
+ }
+ }
+
+ if chainPref.Smallest == nil && chainPref.RootCommonName == nil && chainPref.AnyCommonName == nil {
+ return nil, d.Err("No options for preferred_chains received")
+ }
+
+ return chainPref, nil
+}
+
// ChainPreference describes the client's preferred certificate chain,
// useful if the CA offers alternate chains. The first matching chain
// will be selected.