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: Support repeated use of cert_issuer global option
This changes the signature of UnmarshalGlobalFunc but this is probably OK since it's only used by this repo as far as we know. We need this change in order to "remember" the previous value in case a global option appears more than once, which is now a possibility with the cert_issuer option since Caddy now supports multiple issuers in the order defined by the user. Bonus: the issuer subdirective of tls now supports one-liner for "acme" when all you need to set is the directory: issuer acme <dir>
Matthew Holt committed
Jan 7, 2021 at 11:01 UTC
c2b91dbd65173d256e6fa4ddd3fec6ed2f6c87ef
5 files changed
+51
-40
caddyconfig/httpcaddyfile/directives.go
+4
-3
@@ -498,9 +498,10 @@ type (
498
UnmarshalHandlerFunc func(h Helper) (caddyhttp.MiddlewareHandler, error)
499
500
// UnmarshalGlobalFunc is a function which can unmarshal Caddyfile
501
- // tokens into a global option config value using a Helper type.
502
- // These are passed in a call to RegisterGlobalOption.
503
- UnmarshalGlobalFunc func(d *caddyfile.Dispenser) (interface{}, error)
501
+ // tokens from a global option. It is passed the tokens to parse and
502
+ // existing value from the previous instance of this global option
503
+ // (if any). It returns the value to associate with this global option.
504
+ UnmarshalGlobalFunc func(d *caddyfile.Dispenser, existingVal interface{}) (interface{}, error)
505
)
506
507
var registeredDirectives = make(map[string]UnmarshalFunc)
caddyconfig/httpcaddyfile/httptype.go
+1
-1
@@ -315,7 +315,7 @@ func (ServerType) evaluateGlobalOptionsBlock(serverBlocks []serverBlock, options
315
return nil, fmt.Errorf("%s:%d: unrecognized global option: %s", tkn.File, tkn.Line, opt)
316
}
317
318
- val, err = optFunc(disp)
318
+ val, err = optFunc(disp, options[opt])
319
if err != nil {
320
return nil, fmt.Errorf("parsing caddyfile tokens for '%s': %v", opt, err)
321
}
caddyconfig/httpcaddyfile/options.go
+31
-30
@@ -30,7 +30,6 @@ func init() {
30
RegisterGlobalOption("https_port", parseOptHTTPSPort)
31
RegisterGlobalOption("default_sni", parseOptSingleString)
32
RegisterGlobalOption("order", parseOptOrder)
33
- RegisterGlobalOption("experimental_http3", parseOptTrue)
33
RegisterGlobalOption("storage", parseOptStorage)
34
RegisterGlobalOption("acme_ca", parseOptSingleString)
35
RegisterGlobalOption("acme_ca_root", parseOptSingleString)
@@ -46,11 +45,9 @@ func init() {
45
RegisterGlobalOption("servers", parseServerOptions)
46
}
47
49
-func parseOptTrue(d *caddyfile.Dispenser) (interface{}, error) {
50
- return true, nil
51
-}
48
+func parseOptTrue(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) { return true, nil }
49
53
-func parseOptHTTPPort(d *caddyfile.Dispenser) (interface{}, error) {
50
+func parseOptHTTPPort(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
51
var httpPort int
52
for d.Next() {
53
var httpPortStr string
@@ -66,7 +63,7 @@ func parseOptHTTPPort(d *caddyfile.Dispenser) (interface{}, error) {
63
return httpPort, nil
64
}
65
69
-func parseOptHTTPSPort(d *caddyfile.Dispenser) (interface{}, error) {
66
+func parseOptHTTPSPort(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
67
var httpsPort int
68
for d.Next() {
69
var httpsPortStr string
@@ -82,7 +79,7 @@ func parseOptHTTPSPort(d *caddyfile.Dispenser) (interface{}, error) {
79
return httpsPort, nil
80
}
81
85
-func parseOptOrder(d *caddyfile.Dispenser) (interface{}, error) {
82
+func parseOptOrder(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
83
newOrder := directiveOrder
84
85
for d.Next() {
@@ -158,7 +155,7 @@ func parseOptOrder(d *caddyfile.Dispenser) (interface{}, error) {
155
return newOrder, nil
156
}
157
161
-func parseOptStorage(d *caddyfile.Dispenser) (interface{}, error) {
158
+func parseOptStorage(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
159
if !d.Next() { // consume option name
160
return nil, d.ArgErr()
161
}
@@ -177,7 +174,7 @@ func parseOptStorage(d *caddyfile.Dispenser) (interface{}, error) {
174
return storage, nil
175
}
176
180
-func parseOptACMEDNS(d *caddyfile.Dispenser) (interface{}, error) {
177
+func parseOptACMEDNS(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
178
if !d.Next() { // consume option name
179
return nil, d.ArgErr()
180
}
@@ -196,7 +193,7 @@ func parseOptACMEDNS(d *caddyfile.Dispenser) (interface{}, error) {
193
return prov, nil
194
}
195
199
-func parseOptACMEEAB(d *caddyfile.Dispenser) (interface{}, error) {
196
+func parseOptACMEEAB(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
197
eab := new(acme.EAB)
198
for d.Next() {
199
if d.NextArg() {
@@ -224,26 +221,30 @@ func parseOptACMEEAB(d *caddyfile.Dispenser) (interface{}, error) {
221
return eab, nil
222
}
223
227
-func parseOptCertIssuer(d *caddyfile.Dispenser) (interface{}, error) {
228
- if !d.Next() { // consume option name
229
- return nil, d.ArgErr()
230
- }
231
- if !d.Next() { // get issuer module name
232
- return nil, d.ArgErr()
224
+func parseOptCertIssuer(d *caddyfile.Dispenser, existing interface{}) (interface{}, error) {
225
+ var issuers []certmagic.Issuer
226
+ if existing != nil {
227
+ issuers = existing.([]certmagic.Issuer)
228
}
234
- modID := "tls.issuance." + d.Val()
235
- unm, err := caddyfile.UnmarshalModule(d, modID)
236
- if err != nil {
237
- return nil, err
238
- }
239
- iss, ok := unm.(certmagic.Issuer)
240
- if !ok {
241
- return nil, d.Errf("module %s (%T) is not a certmagic.Issuer", modID, unm)
229
+ for d.Next() { // consume option name
230
+ if !d.Next() { // get issuer module name
231
+ return nil, d.ArgErr()
232
+ }
233
+ modID := "tls.issuance." + d.Val()
234
+ unm, err := caddyfile.UnmarshalModule(d, modID)
235
+ if err != nil {
236
+ return nil, err
237
+ }
238
+ iss, ok := unm.(certmagic.Issuer)
239
+ if !ok {
240
+ return nil, d.Errf("module %s (%T) is not a certmagic.Issuer", modID, unm)
241
+ }
242
+ issuers = append(issuers, iss)
243
}
243
- return iss, nil
244
+ return issuers, nil
245
}
246
246
-func parseOptSingleString(d *caddyfile.Dispenser) (interface{}, error) {
247
+func parseOptSingleString(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
248
d.Next() // consume parameter name
249
if !d.Next() {
250
return "", d.ArgErr()
@@ -255,7 +256,7 @@ func parseOptSingleString(d *caddyfile.Dispenser) (interface{}, error) {
256
return val, nil
257
}
258
258
-func parseOptAdmin(d *caddyfile.Dispenser) (interface{}, error) {
259
+func parseOptAdmin(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
260
adminCfg := new(caddy.AdminConfig)
261
for d.Next() {
262
if d.NextArg() {
@@ -291,7 +292,7 @@ func parseOptAdmin(d *caddyfile.Dispenser) (interface{}, error) {
292
return adminCfg, nil
293
}
294
294
-func parseOptOnDemand(d *caddyfile.Dispenser) (interface{}, error) {
295
+func parseOptOnDemand(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
296
var ond *caddytls.OnDemandConfig
297
for d.Next() {
298
if d.NextArg() {
@@ -351,7 +352,7 @@ func parseOptOnDemand(d *caddyfile.Dispenser) (interface{}, error) {
352
return ond, nil
353
}
354
354
-func parseOptAutoHTTPS(d *caddyfile.Dispenser) (interface{}, error) {
355
+func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
356
d.Next() // consume parameter name
357
if !d.Next() {
358
return "", d.ArgErr()
@@ -366,6 +367,6 @@ func parseOptAutoHTTPS(d *caddyfile.Dispenser) (interface{}, error) {
367
return val, nil
368
}
369
369
-func parseServerOptions(d *caddyfile.Dispenser) (interface{}, error) {
370
+func parseServerOptions(d *caddyfile.Dispenser, _ interface{}) (interface{}, error) {
371
return unmarshalCaddyfileServerOptions(d)
372
}
caddyconfig/httpcaddyfile/tlsapp.go
+5
-5
@@ -414,11 +414,11 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]interf
414
// returned if there are no default/global options. However, if always is
415
// true, a non-nil value will always be returned (unless there is an error).
416
func newBaseAutomationPolicy(options map[string]interface{}, warnings []caddyconfig.Warning, always bool) (*caddytls.AutomationPolicy, error) {
417
- issuer, hasIssuer := options["cert_issuer"]
417
+ issuers, hasIssuers := options["cert_issuer"]
418
_, hasLocalCerts := options["local_certs"]
419
keyType, hasKeyType := options["key_type"]
420
421
- hasGlobalAutomationOpts := hasIssuer || hasLocalCerts || hasKeyType
421
+ hasGlobalAutomationOpts := hasIssuers || hasLocalCerts || hasKeyType
422
423
// if there are no global options related to automation policies
424
// set, then we can just return right away
@@ -434,12 +434,12 @@ func newBaseAutomationPolicy(options map[string]interface{}, warnings []caddycon
434
ap.KeyType = keyType.(string)
435
}
436
437
- if hasIssuer && hasLocalCerts {
437
+ if hasIssuers && hasLocalCerts {
438
return nil, fmt.Errorf("global options are ambiguous: local_certs is confusing when combined with cert_issuer, because local_certs is also a specific kind of issuer")
439
}
440
441
- if hasIssuer {
442
- ap.Issuers = []certmagic.Issuer{issuer.(certmagic.Issuer)}
441
+ if hasIssuers {
442
+ ap.Issuers = issuers.([]certmagic.Issuer)
443
} else if hasLocalCerts {
444
ap.Issuers = []certmagic.Issuer{new(caddytls.InternalIssuer)}
445
}
modules/caddytls/acmeissuer.go
+10
-1
@@ -233,7 +233,7 @@ func (iss *ACMEIssuer) GetACMEIssuer() *ACMEIssuer { return iss }
233
234
// UnmarshalCaddyfile deserializes Caddyfile tokens into iss.
235
//
236
-// ... acme {
236
+// ... acme [<directory_url>] {
237
// dir <directory_url>
238
// test_dir <test_directory_url>
239
// email <email>
@@ -250,9 +250,18 @@ func (iss *ACMEIssuer) GetACMEIssuer() *ACMEIssuer { return iss }
250
//
251
func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
252
for d.Next() {
253
+ if d.NextArg() {
254
+ iss.CA = d.Val()
255
+ if d.NextArg() {
256
+ return d.ArgErr()
257
+ }
258
+ }
259
for nesting := d.Nesting(); d.NextBlock(nesting); {
260
switch d.Val() {
261
case "dir":
262
+ if iss.CA != "" {
263
+ return d.Errf("directory is already specified: %s", iss.CA)
264
+ }
265
if !d.AllArgs(&iss.CA) {
266
return d.ArgErr()
267
}