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.