@marcel91200 / caddy Mirror · read-only / commits / 1e92258d
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

httpcaddyfile: Add `preferred_chains` global option and issuer subdirective (#4192)

* Added preferred_chains option to Caddyfile * Caddyfile adapt tests for preferred_chains

Klooven committed Jun 8, 2021 at 23:10 UTC 1e92258dd670dc62a55b100d1e68e7f482da14a1
5 files changed +187 -1
caddyconfig/httpcaddyfile/options.go
+6
@@ -49,6 +49,7 @@ func init() {
49 RegisterGlobalOption("servers", parseServerOptions)
50 RegisterGlobalOption("ocsp_stapling", parseOCSPStaplingOptions)
51 RegisterGlobalOption("log", parseLogOptions)
52 + RegisterGlobalOption("preferred_chains", parseOptPreferredChains)
53 }
54
55 func parseOptTrue(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) { return true, nil }
@@ -452,3 +453,8 @@ func parseLogOptions(d *caddyfile.Dispenser, existingVal interface{}) (interface
453
454 return configValues, nil
455 }
456 +
457 +func parseOptPreferredChains(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
458 + d.Next()
459 + return caddytls.ParseCaddyfilePreferredChainsOptions(d)
460 +}
caddyconfig/httpcaddyfile/tlsapp.go
+6 -1
@@ -321,7 +321,8 @@ func (st ServerType) buildTLSApp(
321 globalACMECARoot := options["acme_ca_root"]
322 globalACMEDNS := options["acme_dns"]
323 globalACMEEAB := options["acme_eab"]
324 - hasGlobalACMEDefaults := globalEmail != nil || globalACMECA != nil || globalACMECARoot != nil || globalACMEDNS != nil || globalACMEEAB != nil
324 + globalPreferredChains := options["preferred_chains"]
325 + hasGlobalACMEDefaults := globalEmail != nil || globalACMECA != nil || globalACMECARoot != nil || globalACMEDNS != nil || globalACMEEAB != nil || globalPreferredChains != nil
326 if hasGlobalACMEDefaults {
327 for _, ap := range tlsApp.Automation.Policies {
328 if len(ap.Issuers) == 0 {
@@ -405,6 +406,7 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]interf
406 globalACMECARoot := options["acme_ca_root"]
407 globalACMEDNS := options["acme_dns"]
408 globalACMEEAB := options["acme_eab"]
409 + globalPreferredChains := options["preferred_chains"]
410
411 if globalEmail != nil && acmeIssuer.Email == "" {
412 acmeIssuer.Email = globalEmail.(string)
@@ -425,6 +427,9 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]interf
427 if globalACMEEAB != nil && acmeIssuer.ExternalAccount == nil {
428 acmeIssuer.ExternalAccount = globalACMEEAB.(*acme.EAB)
429 }
430 + if globalPreferredChains != nil && acmeIssuer.PreferredChains == nil {
431 + acmeIssuer.PreferredChains = globalPreferredChains.(*caddytls.ChainPreference)
432 + }
433 return nil
434 }
435
caddytest/integration/caddyfile_adapt/global_options_preferred_chains.txt new
+56
@@ -0,0 +1,56 @@
1 +{
2 + preferred_chains smallest
3 +}
4 +
5 +localhost
6 +----------
7 +{
8 + "apps": {
9 + "http": {
10 + "servers": {
11 + "srv0": {
12 + "listen": [
13 + ":443"
14 + ],
15 + "routes": [
16 + {
17 + "match": [
18 + {
19 + "host": [
20 + "localhost"
21 + ]
22 + }
23 + ],
24 + "terminal": true
25 + }
26 + ]
27 + }
28 + }
29 + },
30 + "tls": {
31 + "automation": {
32 + "policies": [
33 + {
34 + "subjects": [
35 + "localhost"
36 + ],
37 + "issuers": [
38 + {
39 + "module": "acme",
40 + "preferred_chains": {
41 + "smallest": true
42 + }
43 + },
44 + {
45 + "module": "zerossl",
46 + "preferred_chains": {
47 + "smallest": true
48 + }
49 + }
50 + ]
51 + }
52 + ]
53 + }
54 + }
55 + }
56 +}
caddytest/integration/caddyfile_adapt/tls_acme_preferred_chains.txt new
+57
@@ -0,0 +1,57 @@
1 +localhost
2 +
3 +tls {
4 + issuer acme {
5 + preferred_chains {
6 + any_common_name "Generic CA 1" "Generic CA 2"
7 + }
8 + }
9 +}
10 +----------
11 +{
12 + "apps": {
13 + "http": {
14 + "servers": {
15 + "srv0": {
16 + "listen": [
17 + ":443"
18 + ],
19 + "routes": [
20 + {
21 + "match": [
22 + {
23 + "host": [
24 + "localhost"
25 + ]
26 + }
27 + ],
28 + "terminal": true
29 + }
30 + ]
31 + }
32 + }
33 + },
34 + "tls": {
35 + "automation": {
36 + "policies": [
37 + {
38 + "subjects": [
39 + "localhost"
40 + ],
41 + "issuers": [
42 + {
43 + "module": "acme",
44 + "preferred_chains": {
45 + "any_common_name": [
46 + "Generic CA 1",
47 + "Generic CA 2"
48 + ]
49 + }
50 + }
51 + ]
52 + }
53 + ]
54 + }
55 + }
56 + }
57 +}
modules/caddytls/acmeissuer.go
+62
@@ -265,6 +265,10 @@ func (iss *ACMEIssuer) GetACMEIssuer() *ACMEIssuer { return iss }
265 // trusted_roots <pem_files...>
266 // dns <provider_name> [<options>]
267 // resolvers <dns_servers...>
268 +// preferred_chains [smallest] {
269 +// root_common_name <common_names...>
270 +// any_common_name <common_names...>
271 +// }
272 // }
273 //
274 func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
@@ -416,6 +420,13 @@ func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
420 return d.ArgErr()
421 }
422
423 + case "preferred_chains":
424 + chainPref, err := ParseCaddyfilePreferredChainsOptions(d)
425 + if err != nil {
426 + return err
427 + }
428 + iss.PreferredChains = chainPref
429 +
430 default:
431 return d.Errf("unrecognized ACME issuer property: %s", d.Val())
432 }
@@ -452,6 +463,57 @@ func onDemandAskRequest(ask string, name string) error {
463 return nil
464 }
465
466 +func ParseCaddyfilePreferredChainsOptions(d *caddyfile.Dispenser) (*ChainPreference, error) {
467 + chainPref := new(ChainPreference)
468 + if d.NextArg() {
469 + smallestOpt := d.Val()
470 + if smallestOpt == "smallest" {
471 + trueBool := true
472 + chainPref.Smallest = &trueBool
473 + if d.NextArg() { // Only one argument allowed
474 + return nil, d.ArgErr()
475 + }
476 + if d.NextBlock(d.Nesting()) { // Don't allow other options when smallest == true
477 + return nil, d.Err("No more options are accepted when using the 'smallest' option")
478 + }
479 + } else { // Smallest option should always be 'smallest' or unset
480 + return nil, d.Errf("Invalid argument '%s'", smallestOpt)
481 + }
482 + }
483 + for nesting := d.Nesting(); d.NextBlock(nesting); {
484 + switch d.Val() {
485 + case "root_common_name":
486 + rootCommonNameOpt := d.RemainingArgs()
487 + chainPref.RootCommonName = rootCommonNameOpt
488 + if rootCommonNameOpt == nil {
489 + return nil, d.ArgErr()
490 + }
491 + if chainPref.AnyCommonName != nil {
492 + return nil, d.Err("Can't set root_common_name when any_common_name is already set")
493 + }
494 +
495 + case "any_common_name":
496 + anyCommonNameOpt := d.RemainingArgs()
497 + chainPref.AnyCommonName = anyCommonNameOpt
498 + if anyCommonNameOpt == nil {
499 + return nil, d.ArgErr()
500 + }
501 + if chainPref.RootCommonName != nil {
502 + return nil, d.Err("Can't set any_common_name when root_common_name is already set")
503 + }
504 +
505 + default:
506 + return nil, d.Errf("Received unrecognized parameter '%s'", d.Val())
507 + }
508 + }
509 +
510 + if chainPref.Smallest == nil && chainPref.RootCommonName == nil && chainPref.AnyCommonName == nil {
511 + return nil, d.Err("No options for preferred_chains received")
512 + }
513 +
514 + return chainPref, nil
515 +}
516 +
517 // ChainPreference describes the client's preferred certificate chain,
518 // useful if the CA offers alternate chains. The first matching chain
519 // will be selected.