@marcel91200 / caddy Mirror · read-only
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 11, 01:00 UTC

master
go 1,278 lines 36.3 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package httpcaddyfile
16
17 import (
18 "encoding/json"
19 "fmt"
20 "html"
21 "net/http"
22 "reflect"
23 "strconv"
24 "strings"
25 "time"
26
27 "github.com/caddyserver/certmagic"
28 "github.com/mholt/acmez/v3/acme"
29 "go.uber.org/zap/zapcore"
30
31 "github.com/caddyserver/caddy/v2"
32 "github.com/caddyserver/caddy/v2/caddyconfig"
33 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
34 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
35 "github.com/caddyserver/caddy/v2/modules/caddytls"
36 )
37
38 func init() {
39 RegisterDirective("bind", parseBind)
40 RegisterDirective("tls", parseTLS)
41 RegisterHandlerDirective("fs", parseFilesystem)
42 RegisterDirective("root", parseRoot)
43 RegisterHandlerDirective("vars", parseVars)
44 RegisterHandlerDirective("redir", parseRedir)
45 RegisterHandlerDirective("respond", parseRespond)
46 RegisterHandlerDirective("abort", parseAbort)
47 RegisterHandlerDirective("error", parseError)
48 RegisterHandlerDirective("route", parseRoute)
49 RegisterHandlerDirective("handle", parseHandle)
50 RegisterDirective("handle_errors", parseHandleErrors)
51 RegisterHandlerDirective("invoke", parseInvoke)
52 RegisterDirective("log", parseLog)
53 RegisterHandlerDirective("skip_log", parseLogSkip)
54 RegisterHandlerDirective("log_skip", parseLogSkip)
55 RegisterHandlerDirective("log_name", parseLogName)
56 RegisterHandlerDirective("log_level", parseLogLevel)
57 }
58
59 // parseBind parses the bind directive. Syntax:
60 //
61 // bind <addresses...> [{
62 // protocols [h1|h2|h2c|h3] [...]
63 // }]
64 func parseBind(h Helper) ([]ConfigValue, error) {
65 h.Next() // consume directive name
66 var addresses, protocols []string
67 addresses = h.RemainingArgs()
68
69 for h.NextBlock(0) {
70 switch h.Val() {
71 case "protocols":
72 protocols = h.RemainingArgs()
73 if len(protocols) == 0 {
74 return nil, h.Errf("protocols requires one or more arguments")
75 }
76 default:
77 return nil, h.Errf("unknown subdirective: %s", h.Val())
78 }
79 }
80
81 return []ConfigValue{{Class: "bind", Value: addressesWithProtocols{
82 addresses: addresses,
83 protocols: protocols,
84 }}}, nil
85 }
86
87 // parseTLS parses the tls directive. Syntax:
88 //
89 // tls [<email>|internal|force_automate]|[<cert_file> <key_file>] {
90 // protocols <min> [<max>]
91 // ciphers <cipher_suites...>
92 // curves <curves...>
93 // client_auth {
94 // mode [request|require|verify_if_given|require_and_verify]
95 // trust_pool <module_name> [...]
96 // trusted_leaf_cert <base64_der>
97 // trusted_leaf_cert_file <filename>
98 // }
99 // alpn <values...>
100 // load <paths...>
101 // ca <acme_ca_endpoint>
102 // ca_root <pem_file>
103 // key_type [ed25519|p256|p384|rsa2048|rsa4096]
104 // dns [<provider_name> [...]] (required, though, if DNS is not configured as global option)
105 // propagation_delay <duration>
106 // propagation_timeout <duration>
107 // resolvers <dns_servers...>
108 // dns_ttl <duration>
109 // dns_challenge_override_domain <domain>
110 // on_demand
111 // reuse_private_keys
112 // force_automate
113 // eab <key_id> <mac_key>
114 // issuer <module_name> [...]
115 // get_certificate <module_name> [...]
116 // insecure_secrets_log <log_file>
117 // renewal_window_ratio <ratio>
118 // }
119 func parseTLS(h Helper) ([]ConfigValue, error) {
120 h.Next() // consume directive name
121
122 cp := new(caddytls.ConnectionPolicy)
123 var fileLoader caddytls.FileLoader
124 var folderLoader caddytls.FolderLoader
125 var certSelector caddytls.CustomCertSelectionPolicy
126 var acmeIssuer *caddytls.ACMEIssuer
127 var keyType string
128 var internalIssuer *caddytls.InternalIssuer
129 var issuers []certmagic.Issuer
130 var certManagers []certmagic.Manager
131 var onDemand bool
132 var reusePrivateKeys bool
133 var forceAutomate bool
134 var renewalWindowRatio float64
135
136 // Track which DNS challenge options are set
137 var dnsOptionsSet []string
138
139 firstLine := h.RemainingArgs()
140 switch len(firstLine) {
141 case 0:
142 case 1:
143 if firstLine[0] == "internal" {
144 internalIssuer = new(caddytls.InternalIssuer)
145 } else if firstLine[0] == "force_automate" {
146 forceAutomate = true
147 } else if !strings.Contains(firstLine[0], "@") {
148 return nil, h.Err("single argument must either be 'internal', 'force_automate', or an email address")
149 } else {
150 acmeIssuer = &caddytls.ACMEIssuer{
151 Email: firstLine[0],
152 }
153 }
154
155 case 2:
156 // file certificate loader
157 certFilename := firstLine[0]
158 keyFilename := firstLine[1]
159
160 // tag this certificate so if multiple certs match, specifically
161 // this one that the user has provided will be used, see #2588:
162 // https://github.com/caddyserver/caddy/issues/2588 ... but we
163 // must be careful about how we do this; being careless will
164 // lead to failed handshakes
165 //
166 // we need to remember which cert files we've seen, since we
167 // must load each cert only once; otherwise, they each get a
168 // different tag... since a cert loaded twice has the same
169 // bytes, it will overwrite the first one in the cache, and
170 // only the last cert (and its tag) will survive, so any conn
171 // policy that is looking for any tag other than the last one
172 // to be loaded won't find it, and TLS handshakes will fail
173 // (see end of issue #3004)
174 //
175 // tlsCertTags maps certificate filenames to their tag.
176 // This is used to remember which tag is used for each
177 // certificate files, since we need to avoid loading
178 // the same certificate files more than once, overwriting
179 // previous tags
180 tlsCertTags, ok := h.State["tlsCertTags"].(map[string]string)
181 if !ok {
182 tlsCertTags = make(map[string]string)
183 h.State["tlsCertTags"] = tlsCertTags
184 }
185
186 tag, ok := tlsCertTags[certFilename]
187 if !ok {
188 // haven't seen this cert file yet, let's give it a tag
189 // and add a loader for it
190 tag = fmt.Sprintf("cert%d", len(tlsCertTags))
191 fileLoader = append(fileLoader, caddytls.CertKeyFilePair{
192 Certificate: certFilename,
193 Key: keyFilename,
194 Tags: []string{tag},
195 })
196 // remember this for next time we see this cert file
197 tlsCertTags[certFilename] = tag
198 }
199 certSelector.AnyTag = append(certSelector.AnyTag, tag)
200
201 default:
202 return nil, h.ArgErr()
203 }
204
205 var hasBlock bool
206 for h.NextBlock(0) {
207 hasBlock = true
208
209 switch h.Val() {
210 case "protocols":
211 args := h.RemainingArgs()
212 if len(args) == 0 {
213 return nil, h.Errf("protocols requires one or two arguments")
214 }
215 if len(args) > 0 {
216 if _, ok := caddytls.SupportedProtocols[args[0]]; !ok {
217 return nil, h.Errf("wrong protocol name or protocol not supported: '%s'", args[0])
218 }
219 cp.ProtocolMin = args[0]
220 }
221 if len(args) > 1 {
222 if _, ok := caddytls.SupportedProtocols[args[1]]; !ok {
223 return nil, h.Errf("wrong protocol name or protocol not supported: '%s'", args[1])
224 }
225 cp.ProtocolMax = args[1]
226 }
227
228 case "ciphers":
229 for h.NextArg() {
230 if !caddytls.CipherSuiteNameSupported(h.Val()) {
231 return nil, h.Errf("wrong cipher suite name or cipher suite not supported: '%s'", h.Val())
232 }
233 cp.CipherSuites = append(cp.CipherSuites, h.Val())
234 }
235
236 case "curves":
237 for h.NextArg() {
238 if _, ok := caddytls.SupportedCurves[h.Val()]; !ok {
239 return nil, h.Errf("Wrong curve name or curve not supported: '%s'", h.Val())
240 }
241 cp.Curves = append(cp.Curves, h.Val())
242 }
243
244 case "client_auth":
245 cp.ClientAuthentication = &caddytls.ClientAuthentication{}
246 if err := cp.ClientAuthentication.UnmarshalCaddyfile(h.NewFromNextSegment()); err != nil {
247 return nil, err
248 }
249 case "alpn":
250 args := h.RemainingArgs()
251 if len(args) == 0 {
252 return nil, h.ArgErr()
253 }
254 cp.ALPN = args
255
256 case "load":
257 folderLoader = append(folderLoader, h.RemainingArgs()...)
258
259 case "ca":
260 arg := h.RemainingArgs()
261 if len(arg) != 1 {
262 return nil, h.ArgErr()
263 }
264 if acmeIssuer == nil {
265 acmeIssuer = new(caddytls.ACMEIssuer)
266 }
267 acmeIssuer.CA = arg[0]
268
269 case "key_type":
270 arg := h.RemainingArgs()
271 if len(arg) != 1 {
272 return nil, h.ArgErr()
273 }
274 keyType = arg[0]
275
276 case "eab":
277 arg := h.RemainingArgs()
278 if len(arg) != 2 {
279 return nil, h.ArgErr()
280 }
281 if acmeIssuer == nil {
282 acmeIssuer = new(caddytls.ACMEIssuer)
283 }
284 acmeIssuer.ExternalAccount = &acme.EAB{
285 KeyID: arg[0],
286 MACKey: arg[1],
287 }
288
289 case "issuer":
290 if !h.NextArg() {
291 return nil, h.ArgErr()
292 }
293 modName := h.Val()
294 modID := "tls.issuance." + modName
295 unm, err := caddyfile.UnmarshalModule(h.Dispenser, modID)
296 if err != nil {
297 return nil, err
298 }
299 issuer, ok := unm.(certmagic.Issuer)
300 if !ok {
301 return nil, h.Errf("module %s (%T) is not a certmagic.Issuer", modID, unm)
302 }
303 issuers = append(issuers, issuer)
304
305 case "get_certificate":
306 if !h.NextArg() {
307 return nil, h.ArgErr()
308 }
309 modName := h.Val()
310 modID := "tls.get_certificate." + modName
311 unm, err := caddyfile.UnmarshalModule(h.Dispenser, modID)
312 if err != nil {
313 return nil, err
314 }
315 certManager, ok := unm.(certmagic.Manager)
316 if !ok {
317 return nil, h.Errf("module %s (%T) is not a certmagic.CertificateManager", modID, unm)
318 }
319 certManagers = append(certManagers, certManager)
320
321 case "dns":
322 if acmeIssuer == nil {
323 acmeIssuer = new(caddytls.ACMEIssuer)
324 }
325 if acmeIssuer.Challenges == nil {
326 acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
327 }
328 if acmeIssuer.Challenges.DNS == nil {
329 acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
330 }
331 // DNS provider configuration optional, since it may be configured globally via the TLS app with global options
332 if h.NextArg() {
333 provName := h.Val()
334 modID := "dns.providers." + provName
335 unm, err := caddyfile.UnmarshalModule(h.Dispenser, modID)
336 if err != nil {
337 return nil, err
338 }
339 acmeIssuer.Challenges.DNS.ProviderRaw = caddyconfig.JSONModuleObject(unm, "name", provName, h.warnings)
340 } else if h.Option("dns") == nil {
341 // if DNS is omitted locally, it needs to be configured globally
342 return nil, h.ArgErr()
343 }
344
345 case "resolvers":
346 args := h.RemainingArgs()
347 if len(args) == 0 {
348 return nil, h.ArgErr()
349 }
350 if acmeIssuer == nil {
351 acmeIssuer = new(caddytls.ACMEIssuer)
352 }
353 if acmeIssuer.Challenges == nil {
354 acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
355 }
356 if acmeIssuer.Challenges.DNS == nil {
357 acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
358 }
359 dnsOptionsSet = append(dnsOptionsSet, "resolvers")
360 acmeIssuer.Challenges.DNS.Resolvers = args
361
362 case "propagation_delay":
363 arg := h.RemainingArgs()
364 if len(arg) != 1 {
365 return nil, h.ArgErr()
366 }
367 delayStr := arg[0]
368 delay, err := caddy.ParseDuration(delayStr)
369 if err != nil {
370 return nil, h.Errf("invalid propagation_delay duration %s: %v", delayStr, err)
371 }
372 if acmeIssuer == nil {
373 acmeIssuer = new(caddytls.ACMEIssuer)
374 }
375 if acmeIssuer.Challenges == nil {
376 acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
377 }
378 if acmeIssuer.Challenges.DNS == nil {
379 acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
380 }
381 dnsOptionsSet = append(dnsOptionsSet, "propagation_delay")
382 acmeIssuer.Challenges.DNS.PropagationDelay = caddy.Duration(delay)
383
384 case "propagation_timeout":
385 arg := h.RemainingArgs()
386 if len(arg) != 1 {
387 return nil, h.ArgErr()
388 }
389 timeoutStr := arg[0]
390 var timeout time.Duration
391 if timeoutStr == "-1" {
392 timeout = time.Duration(-1)
393 } else {
394 var err error
395 timeout, err = caddy.ParseDuration(timeoutStr)
396 if err != nil {
397 return nil, h.Errf("invalid propagation_timeout duration %s: %v", timeoutStr, err)
398 }
399 }
400 if acmeIssuer == nil {
401 acmeIssuer = new(caddytls.ACMEIssuer)
402 }
403 if acmeIssuer.Challenges == nil {
404 acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
405 }
406 if acmeIssuer.Challenges.DNS == nil {
407 acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
408 }
409 dnsOptionsSet = append(dnsOptionsSet, "propagation_timeout")
410 acmeIssuer.Challenges.DNS.PropagationTimeout = caddy.Duration(timeout)
411
412 case "dns_ttl":
413 arg := h.RemainingArgs()
414 if len(arg) != 1 {
415 return nil, h.ArgErr()
416 }
417 ttlStr := arg[0]
418 ttl, err := caddy.ParseDuration(ttlStr)
419 if err != nil {
420 return nil, h.Errf("invalid dns_ttl duration %s: %v", ttlStr, err)
421 }
422 if acmeIssuer == nil {
423 acmeIssuer = new(caddytls.ACMEIssuer)
424 }
425 if acmeIssuer.Challenges == nil {
426 acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
427 }
428 if acmeIssuer.Challenges.DNS == nil {
429 acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
430 }
431 dnsOptionsSet = append(dnsOptionsSet, "dns_ttl")
432 acmeIssuer.Challenges.DNS.TTL = caddy.Duration(ttl)
433
434 case "dns_challenge_override_domain":
435 arg := h.RemainingArgs()
436 if len(arg) != 1 {
437 return nil, h.ArgErr()
438 }
439 if acmeIssuer == nil {
440 acmeIssuer = new(caddytls.ACMEIssuer)
441 }
442 if acmeIssuer.Challenges == nil {
443 acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
444 }
445 if acmeIssuer.Challenges.DNS == nil {
446 acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
447 }
448 dnsOptionsSet = append(dnsOptionsSet, "dns_challenge_override_domain")
449 acmeIssuer.Challenges.DNS.OverrideDomain = arg[0]
450
451 case "ca_root":
452 arg := h.RemainingArgs()
453 if len(arg) != 1 {
454 return nil, h.ArgErr()
455 }
456 if acmeIssuer == nil {
457 acmeIssuer = new(caddytls.ACMEIssuer)
458 }
459 acmeIssuer.TrustedRootsPEMFiles = append(acmeIssuer.TrustedRootsPEMFiles, arg[0])
460
461 case "on_demand":
462 if h.NextArg() {
463 return nil, h.ArgErr()
464 }
465 onDemand = true
466
467 case "reuse_private_keys":
468 if h.NextArg() {
469 return nil, h.ArgErr()
470 }
471 reusePrivateKeys = true
472
473 case "insecure_secrets_log":
474 if !h.NextArg() {
475 return nil, h.ArgErr()
476 }
477 cp.InsecureSecretsLog = h.Val()
478
479 case "renewal_window_ratio":
480 arg := h.RemainingArgs()
481 if len(arg) != 1 {
482 return nil, h.ArgErr()
483 }
484 ratio, err := strconv.ParseFloat(arg[0], 64)
485 if err != nil {
486 return nil, h.Errf("parsing renewal_window_ratio: %v", err)
487 }
488 if ratio <= 0 || ratio >= 1 {
489 return nil, h.Errf("renewal_window_ratio must be between 0 and 1 (exclusive)")
490 }
491 renewalWindowRatio = ratio
492
493 default:
494 return nil, h.Errf("unknown subdirective: %s", h.Val())
495 }
496 }
497
498 // Validate DNS challenge config: any DNS challenge option except "dns" requires a DNS provider
499 if acmeIssuer != nil && acmeIssuer.Challenges != nil && acmeIssuer.Challenges.DNS != nil {
500 dnsCfg := acmeIssuer.Challenges.DNS
501 providerSet := dnsCfg.ProviderRaw != nil || h.Option("dns") != nil || h.Option("acme_dns") != nil
502 if len(dnsOptionsSet) > 0 && !providerSet {
503 return nil, h.Errf(
504 "setting DNS challenge options [%s] requires a DNS provider (set with the 'dns' subdirective or 'acme_dns' global option)",
505 strings.Join(dnsOptionsSet, ", "),
506 )
507 }
508 }
509
510 // a naked tls directive is not allowed
511 if len(firstLine) == 0 && !hasBlock {
512 return nil, h.ArgErr()
513 }
514
515 // begin building the final config values
516 configVals := []ConfigValue{}
517
518 // certificate loaders
519 if len(fileLoader) > 0 {
520 configVals = append(configVals, ConfigValue{
521 Class: "tls.cert_loader",
522 Value: fileLoader,
523 })
524 }
525 if len(folderLoader) > 0 {
526 configVals = append(configVals, ConfigValue{
527 Class: "tls.cert_loader",
528 Value: folderLoader,
529 })
530 }
531
532 // some tls subdirectives are shortcuts that implicitly configure issuers, and the
533 // user can also configure issuers explicitly using the issuer subdirective; the
534 // logic to support both would likely be complex, or at least unintuitive
535 if len(issuers) > 0 && (acmeIssuer != nil || internalIssuer != nil) {
536 return nil, h.Err("cannot mix issuer subdirective (explicit issuers) with other issuer-specific subdirectives (implicit issuers)")
537 }
538 if acmeIssuer != nil && internalIssuer != nil {
539 return nil, h.Err("cannot create both ACME and internal certificate issuers")
540 }
541
542 // now we should either have: explicitly-created issuers, or an implicitly-created
543 // ACME or internal issuer, or no issuers at all
544 switch {
545 case len(issuers) > 0:
546 for _, issuer := range issuers {
547 configVals = append(configVals, ConfigValue{
548 Class: "tls.cert_issuer",
549 Value: issuer,
550 })
551 }
552
553 case acmeIssuer != nil:
554 // implicit ACME issuers (from various subdirectives) should inherit from
555 // any globally-configured ACME issuer templates, then apply the local
556 // shortcut settings as overrides.
557 defaultIssuers := implicitACMEIssuers(h, acmeIssuer)
558 for _, issuer := range defaultIssuers {
559 configVals = append(configVals, ConfigValue{
560 Class: "tls.cert_issuer",
561 Value: issuer,
562 })
563 }
564
565 case internalIssuer != nil:
566 configVals = append(configVals, ConfigValue{
567 Class: "tls.cert_issuer",
568 Value: internalIssuer,
569 })
570 }
571
572 // certificate key type
573 if keyType != "" {
574 configVals = append(configVals, ConfigValue{
575 Class: "tls.key_type",
576 Value: keyType,
577 })
578 }
579
580 // on-demand TLS
581 if onDemand {
582 configVals = append(configVals, ConfigValue{
583 Class: "tls.on_demand",
584 Value: true,
585 })
586 }
587 for _, certManager := range certManagers {
588 configVals = append(configVals, ConfigValue{
589 Class: "tls.cert_manager",
590 Value: certManager,
591 })
592 }
593
594 // reuse private keys TLS
595 if reusePrivateKeys {
596 configVals = append(configVals, ConfigValue{
597 Class: "tls.reuse_private_keys",
598 Value: true,
599 })
600 }
601
602 // renewal window ratio
603 if renewalWindowRatio > 0 {
604 configVals = append(configVals, ConfigValue{
605 Class: "tls.renewal_window_ratio",
606 Value: renewalWindowRatio,
607 })
608 }
609
610 // if enabled, the names in the site addresses will be
611 // added to the automation policies
612 if forceAutomate {
613 configVals = append(configVals, ConfigValue{
614 Class: "tls.force_automate",
615 Value: true,
616 })
617 }
618
619 // custom certificate selection
620 if len(certSelector.AnyTag) > 0 {
621 cp.CertSelection = &certSelector
622 }
623
624 // connection policy -- always add one, to ensure that TLS
625 // is enabled, because this directive was used (this is
626 // needed, for instance, when a site block has a key of
627 // just ":5000" - i.e. no hostname, and only on-demand TLS
628 // is enabled)
629 configVals = append(configVals, ConfigValue{
630 Class: "tls.connection_policy",
631 Value: cp,
632 })
633
634 return configVals, nil
635 }
636
637 // parseRoot parses the root directive. Syntax:
638 //
639 // root [<matcher>] <path>
640 func parseRoot(h Helper) ([]ConfigValue, error) {
641 h.Next() // consume directive name
642
643 // count the tokens to determine what to do
644 argsCount := h.CountRemainingArgs()
645 if argsCount == 0 {
646 return nil, h.Errf("too few arguments; must have at least a root path")
647 }
648 if argsCount > 2 {
649 return nil, h.Errf("too many arguments; should only be a matcher and a path")
650 }
651
652 // with only one arg, assume it's a root path with no matcher token
653 if argsCount == 1 {
654 if !h.NextArg() {
655 return nil, h.ArgErr()
656 }
657 // store the unmatched root in block state so sibling directives can access it
658 h.BlockState["root"] = h.Val()
659 return h.NewRoute(nil, caddyhttp.VarsMiddleware{"root": h.Val()}), nil
660 }
661
662 // parse the matcher token into a matcher set
663 userMatcherSet, err := h.ExtractMatcherSet()
664 if err != nil {
665 return nil, err
666 }
667 h.Next() // consume directive name again, matcher parsing does a reset
668
669 // advance to the root path
670 if !h.NextArg() {
671 return nil, h.ArgErr()
672 }
673 // store the unmatched root in state so sibling/child directives can access it
674 if userMatcherSet == nil {
675 h.BlockState["root"] = h.Val()
676 }
677 // make the route with the matcher
678 return h.NewRoute(userMatcherSet, caddyhttp.VarsMiddleware{"root": h.Val()}), nil
679 }
680
681 // parseFilesystem parses the fs directive. Syntax:
682 //
683 // fs <filesystem>
684 func parseFilesystem(h Helper) (caddyhttp.MiddlewareHandler, error) {
685 h.Next() // consume directive name
686 if !h.NextArg() {
687 return nil, h.ArgErr()
688 }
689 if h.NextArg() {
690 return nil, h.ArgErr()
691 }
692 return caddyhttp.VarsMiddleware{"fs": h.Val()}, nil
693 }
694
695 // parseVars parses the vars directive. See its UnmarshalCaddyfile method for syntax.
696 func parseVars(h Helper) (caddyhttp.MiddlewareHandler, error) {
697 v := new(caddyhttp.VarsMiddleware)
698 err := v.UnmarshalCaddyfile(h.Dispenser)
699 return v, err
700 }
701
702 // parseRedir parses the redir directive. Syntax:
703 //
704 // redir [<matcher>] <to> [<code>]
705 //
706 // <code> can be "permanent" for 301, "temporary" for 302 (default),
707 // a placeholder, or any number in the 3xx range or 401. The special
708 // code "html" can be used to redirect only browser clients (will
709 // respond with HTTP 200 and no Location header; redirect is performed
710 // with JS and a meta tag).
711 func parseRedir(h Helper) (caddyhttp.MiddlewareHandler, error) {
712 h.Next() // consume directive name
713 if !h.NextArg() {
714 return nil, h.ArgErr()
715 }
716 to := h.Val()
717
718 var code string
719 if h.NextArg() {
720 code = h.Val()
721 }
722
723 var body string
724 var hdr http.Header
725 switch code {
726 case "permanent":
727 code = "301"
728
729 case "temporary", "":
730 code = "302"
731
732 case "html":
733 // Script tag comes first since that will better imitate a redirect in the browser's
734 // history, but the meta tag is a fallback for most non-JS clients.
735 const metaRedir = `<!DOCTYPE html>
736 <html>
737 <head>
738 <title>Redirecting...</title>
739 <script>window.location.replace("%s");</script>
740 <meta http-equiv="refresh" content="0; URL='%s'">
741 </head>
742 <body>Redirecting to <a href="%s">%s</a>...</body>
743 </html>
744 `
745 safeTo := html.EscapeString(to)
746 body = fmt.Sprintf(metaRedir, safeTo, safeTo, safeTo, safeTo)
747 hdr = http.Header{"Content-Type": []string{"text/html; charset=utf-8"}}
748 code = "200" // don't redirect non-browser clients
749
750 default:
751 // Allow placeholders for the code
752 if strings.HasPrefix(code, "{") {
753 break
754 }
755 // Try to validate as an integer otherwise
756 codeInt, err := strconv.Atoi(code)
757 if err != nil {
758 return nil, h.Errf("Not a supported redir code type or not valid integer: '%s'", code)
759 }
760 // Sometimes, a 401 with Location header is desirable because
761 // requests made with XHR will "eat" the 3xx redirect; so if
762 // the intent was to redirect to an auth page, a 3xx won't
763 // work. Responding with 401 allows JS code to read the
764 // Location header and do a window.location redirect manually.
765 // see https://stackoverflow.com/a/2573589/846934
766 // see https://github.com/oauth2-proxy/oauth2-proxy/issues/1522
767 if codeInt < 300 || (codeInt > 399 && codeInt != 401) {
768 return nil, h.Errf("Redir code not in the 3xx range or 401: '%v'", codeInt)
769 }
770 }
771
772 // don't redirect non-browser clients
773 if code != "200" {
774 hdr = http.Header{"Location": []string{to}}
775 }
776
777 return caddyhttp.StaticResponse{
778 StatusCode: caddyhttp.WeakString(code),
779 Headers: hdr,
780 Body: body,
781 }, nil
782 }
783
784 // parseRespond parses the respond directive.
785 func parseRespond(h Helper) (caddyhttp.MiddlewareHandler, error) {
786 sr := new(caddyhttp.StaticResponse)
787 err := sr.UnmarshalCaddyfile(h.Dispenser)
788 return sr, err
789 }
790
791 // parseAbort parses the abort directive.
792 func parseAbort(h Helper) (caddyhttp.MiddlewareHandler, error) {
793 h.Next() // consume directive
794 for h.Next() || h.NextBlock(0) {
795 return nil, h.ArgErr()
796 }
797 return &caddyhttp.StaticResponse{Abort: true}, nil
798 }
799
800 // parseError parses the error directive.
801 func parseError(h Helper) (caddyhttp.MiddlewareHandler, error) {
802 se := new(caddyhttp.StaticError)
803 err := se.UnmarshalCaddyfile(h.Dispenser)
804 return se, err
805 }
806
807 // parseRoute parses the route directive.
808 func parseRoute(h Helper) (caddyhttp.MiddlewareHandler, error) {
809 allResults, err := parseSegmentAsConfig(h)
810 if err != nil {
811 return nil, err
812 }
813
814 for _, result := range allResults {
815 switch result.Value.(type) {
816 case caddyhttp.Route, caddyhttp.Subroute:
817 default:
818 return nil, h.Errf("%s directive returned something other than an HTTP route or subroute: %#v (only handler directives can be used in routes)", result.directive, result.Value)
819 }
820 }
821
822 return buildSubroute(allResults, h.groupCounter, nil)
823 }
824
825 func parseHandle(h Helper) (caddyhttp.MiddlewareHandler, error) {
826 return ParseSegmentAsSubroute(h)
827 }
828
829 func parseHandleErrors(h Helper) ([]ConfigValue, error) {
830 h.Next() // consume directive name
831
832 expression := ""
833 args := h.RemainingArgs()
834 if len(args) > 0 {
835 codes := []string{}
836 for _, val := range args {
837 if len(val) != 3 {
838 return nil, h.Errf("bad status value '%s'", val)
839 }
840 if strings.HasSuffix(val, "xx") {
841 val = val[:1]
842 _, err := strconv.Atoi(val)
843 if err != nil {
844 return nil, h.Errf("bad status value '%s': %v", val, err)
845 }
846 if expression != "" {
847 expression += " || "
848 }
849 expression += fmt.Sprintf("{http.error.status_code} >= %s00 && {http.error.status_code} <= %s99", val, val)
850 continue
851 }
852 _, err := strconv.Atoi(val)
853 if err != nil {
854 return nil, h.Errf("bad status value '%s': %v", val, err)
855 }
856 codes = append(codes, val)
857 }
858 if len(codes) > 0 {
859 if expression != "" {
860 expression += " || "
861 }
862 expression += "{http.error.status_code} in [" + strings.Join(codes, ", ") + "]"
863 }
864 // Reset cursor position to get ready for ParseSegmentAsSubroute
865 h.Reset()
866 h.Next()
867 h.RemainingArgs()
868 h.Prev()
869 } else {
870 // If no arguments present reset the cursor position to get ready for ParseSegmentAsSubroute
871 h.Prev()
872 }
873
874 handler, err := ParseSegmentAsSubroute(h)
875 if err != nil {
876 return nil, err
877 }
878 subroute, ok := handler.(*caddyhttp.Subroute)
879 if !ok {
880 return nil, h.Errf("segment was not parsed as a subroute")
881 }
882
883 // wrap the subroutes
884 wrappingRoute := caddyhttp.Route{
885 HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(subroute, "handler", "subroute", nil)},
886 }
887 subroute = &caddyhttp.Subroute{
888 Routes: []caddyhttp.Route{wrappingRoute},
889 }
890 if expression != "" {
891 statusMatcher := caddy.ModuleMap{
892 "expression": h.JSON(caddyhttp.MatchExpression{Expr: expression}),
893 }
894 subroute.Routes[0].MatcherSetsRaw = []caddy.ModuleMap{statusMatcher}
895 }
896 return []ConfigValue{
897 {
898 Class: "error_route",
899 Value: subroute,
900 },
901 }, nil
902 }
903
904 // parseInvoke parses the invoke directive.
905 func parseInvoke(h Helper) (caddyhttp.MiddlewareHandler, error) {
906 h.Next() // consume directive
907 if !h.NextArg() {
908 return nil, h.ArgErr()
909 }
910 for h.Next() || h.NextBlock(0) {
911 return nil, h.ArgErr()
912 }
913
914 // remember that we're invoking this name
915 // to populate the server with these named routes
916 if h.State[namedRouteKey] == nil {
917 h.State[namedRouteKey] = map[string]struct{}{}
918 }
919 h.State[namedRouteKey].(map[string]struct{})[h.Val()] = struct{}{}
920
921 // return the handler
922 return &caddyhttp.Invoke{Name: h.Val()}, nil
923 }
924
925 // parseLog parses the log directive. Syntax:
926 //
927 // log <logger_name> {
928 // hostnames <hostnames...>
929 // output <writer_module> ...
930 // core <core_module> ...
931 // format <encoder_module> ...
932 // level <level>
933 // }
934 func parseLog(h Helper) ([]ConfigValue, error) {
935 return parseLogHelper(h, nil)
936 }
937
938 // parseLogHelper is used both for the parseLog directive within Server Blocks,
939 // as well as the global "log" option for configuring loggers at the global
940 // level. The parseAsGlobalOption parameter is used to distinguish any differing logic
941 // between the two.
942 func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue, error) {
943 h.Next() // consume option name
944
945 // When the globalLogNames parameter is passed in, we make
946 // modifications to the parsing behavior.
947 parseAsGlobalOption := globalLogNames != nil
948
949 // nolint:prealloc
950 var configValues []ConfigValue
951
952 // Logic below expects that a name is always present when a
953 // global option is being parsed; or an optional override
954 // is supported for access logs.
955 var logName string
956
957 if parseAsGlobalOption {
958 if h.NextArg() {
959 logName = h.Val()
960
961 // Only a single argument is supported.
962 if h.NextArg() {
963 return nil, h.ArgErr()
964 }
965 } else {
966 // If there is no log name specified, we
967 // reference the default logger. See the
968 // setupNewDefault function in the logging
969 // package for where this is configured.
970 logName = caddy.DefaultLoggerName
971 }
972
973 // Verify this name is unused.
974 _, used := globalLogNames[logName]
975 if used {
976 return nil, h.Err("duplicate global log option for: " + logName)
977 }
978 globalLogNames[logName] = struct{}{}
979 } else {
980 // An optional override of the logger name can be provided;
981 // otherwise a default will be used, like "log0", "log1", etc.
982 if h.NextArg() {
983 logName = h.Val()
984
985 // Only a single argument is supported.
986 if h.NextArg() {
987 return nil, h.ArgErr()
988 }
989 }
990 }
991
992 cl := new(caddy.CustomLog)
993
994 // allow overriding the current site block's hostnames for this logger;
995 // this is useful for setting up loggers per subdomain in a site block
996 // with a wildcard domain
997 customHostnames := []string{}
998 noHostname := false
999 // track single-valued subdirectives so that repeating one is an error
1000 // rather than silently overriding the previous value; see
1001 // https://caddy.community/t/logs-not-getting-written-to-journal/33852
1002 seen := make(map[string]bool)
1003 assertUnique := func(subdir string) error {
1004 if seen[subdir] {
1005 return h.Errf("'%s' subdirective specified more than once in the same log block", subdir)
1006 }
1007 seen[subdir] = true
1008 return nil
1009 }
1010 for h.NextBlock(0) {
1011 switch h.Val() {
1012 case "hostnames":
1013 if parseAsGlobalOption {
1014 return nil, h.Err("hostnames is not allowed in the log global options")
1015 }
1016 args := h.RemainingArgs()
1017 if len(args) == 0 {
1018 return nil, h.ArgErr()
1019 }
1020 customHostnames = append(customHostnames, args...)
1021
1022 case "output":
1023 if err := assertUnique(h.Val()); err != nil {
1024 return nil, err
1025 }
1026 if !h.NextArg() {
1027 return nil, h.ArgErr()
1028 }
1029 moduleName := h.Val()
1030
1031 // can't use the usual caddyfile.Unmarshaler flow with the
1032 // standard writers because they are in the caddy package
1033 // (because they are the default) and implementing that
1034 // interface there would unfortunately create circular import
1035 var wo caddy.WriterOpener
1036 switch moduleName {
1037 case "stdout":
1038 wo = caddy.StdoutWriter{}
1039 case "stderr":
1040 wo = caddy.StderrWriter{}
1041 case "discard":
1042 wo = caddy.DiscardWriter{}
1043 default:
1044 modID := "caddy.logging.writers." + moduleName
1045 unm, err := caddyfile.UnmarshalModule(h.Dispenser, modID)
1046 if err != nil {
1047 return nil, err
1048 }
1049 var ok bool
1050 wo, ok = unm.(caddy.WriterOpener)
1051 if !ok {
1052 return nil, h.Errf("module %s (%T) is not a WriterOpener", modID, unm)
1053 }
1054 }
1055 cl.WriterRaw = caddyconfig.JSONModuleObject(wo, "output", moduleName, h.warnings)
1056
1057 case "sampling":
1058 if err := assertUnique(h.Val()); err != nil {
1059 return nil, err
1060 }
1061 d := h.Dispenser.NewFromNextSegment()
1062 for d.NextArg() {
1063 // consume any tokens on the same line, if any.
1064 }
1065
1066 sampling := &caddy.LogSampling{}
1067 samplingSeen := make(map[string]bool)
1068 for nesting := d.Nesting(); d.NextBlock(nesting); {
1069 subdir := d.Val()
1070 if samplingSeen[subdir] {
1071 return nil, d.Errf("'%s' specified more than once in the same sampling block", subdir)
1072 }
1073 samplingSeen[subdir] = true
1074 switch subdir {
1075 case "interval":
1076 if !d.NextArg() {
1077 return nil, d.ArgErr()
1078 }
1079 interval, err := caddy.ParseDuration(d.Val())
1080 if err != nil {
1081 return nil, d.Errf("failed to parse interval: %v", err)
1082 }
1083 sampling.Interval = interval
1084 case "first":
1085 if !d.NextArg() {
1086 return nil, d.ArgErr()
1087 }
1088 first, err := strconv.Atoi(d.Val())
1089 if err != nil {
1090 return nil, d.Errf("failed to parse first: %v", err)
1091 }
1092 sampling.First = first
1093 case "thereafter":
1094 if !d.NextArg() {
1095 return nil, d.ArgErr()
1096 }
1097 thereafter, err := strconv.Atoi(d.Val())
1098 if err != nil {
1099 return nil, d.Errf("failed to parse thereafter: %v", err)
1100 }
1101 sampling.Thereafter = thereafter
1102 default:
1103 return nil, d.Errf("unrecognized subdirective: %s", subdir)
1104 }
1105 }
1106
1107 cl.Sampling = sampling
1108
1109 case "core":
1110 if err := assertUnique(h.Val()); err != nil {
1111 return nil, err
1112 }
1113 if !h.NextArg() {
1114 return nil, h.ArgErr()
1115 }
1116 moduleName := h.Val()
1117 moduleID := "caddy.logging.cores." + moduleName
1118 unm, err := caddyfile.UnmarshalModule(h.Dispenser, moduleID)
1119 if err != nil {
1120 return nil, err
1121 }
1122 core, ok := unm.(zapcore.Core)
1123 if !ok {
1124 return nil, h.Errf("module %s (%T) is not a zapcore.Core", moduleID, unm)
1125 }
1126 cl.CoreRaw = caddyconfig.JSONModuleObject(core, "module", moduleName, h.warnings)
1127
1128 case "format":
1129 if err := assertUnique(h.Val()); err != nil {
1130 return nil, err
1131 }
1132 if !h.NextArg() {
1133 return nil, h.ArgErr()
1134 }
1135 moduleName := h.Val()
1136 moduleID := "caddy.logging.encoders." + moduleName
1137 unm, err := caddyfile.UnmarshalModule(h.Dispenser, moduleID)
1138 if err != nil {
1139 return nil, err
1140 }
1141 enc, ok := unm.(zapcore.Encoder)
1142 if !ok {
1143 return nil, h.Errf("module %s (%T) is not a zapcore.Encoder", moduleID, unm)
1144 }
1145 cl.EncoderRaw = caddyconfig.JSONModuleObject(enc, "format", moduleName, h.warnings)
1146
1147 case "level":
1148 if err := assertUnique(h.Val()); err != nil {
1149 return nil, err
1150 }
1151 if !h.NextArg() {
1152 return nil, h.ArgErr()
1153 }
1154 cl.Level = h.Val()
1155 if h.NextArg() {
1156 return nil, h.ArgErr()
1157 }
1158
1159 case "include":
1160 if !parseAsGlobalOption {
1161 return nil, h.Err("include is not allowed in the log directive")
1162 }
1163 for h.NextArg() {
1164 cl.Include = append(cl.Include, h.Val())
1165 }
1166
1167 case "exclude":
1168 if !parseAsGlobalOption {
1169 return nil, h.Err("exclude is not allowed in the log directive")
1170 }
1171 for h.NextArg() {
1172 cl.Exclude = append(cl.Exclude, h.Val())
1173 }
1174
1175 case "no_hostname":
1176 if err := assertUnique(h.Val()); err != nil {
1177 return nil, err
1178 }
1179 if h.NextArg() {
1180 return nil, h.ArgErr()
1181 }
1182 noHostname = true
1183
1184 default:
1185 return nil, h.Errf("unrecognized subdirective: %s", h.Val())
1186 }
1187 }
1188
1189 var val namedCustomLog
1190 val.hostnames = customHostnames
1191 val.noHostname = noHostname
1192 isEmptyConfig := reflect.DeepEqual(cl, new(caddy.CustomLog))
1193
1194 // Skip handling of empty logging configs
1195
1196 if parseAsGlobalOption {
1197 // Use indicated name for global log options
1198 val.name = logName
1199 } else {
1200 if logName != "" {
1201 val.name = logName
1202 } else if !isEmptyConfig {
1203 // Construct a log name for server log streams
1204 logCounter, ok := h.State["logCounter"].(int)
1205 if !ok {
1206 logCounter = 0
1207 }
1208 val.name = fmt.Sprintf("log%d", logCounter)
1209 logCounter++
1210 h.State["logCounter"] = logCounter
1211 }
1212 if val.name != "" {
1213 cl.Include = []string{"http.log.access." + val.name}
1214 }
1215 }
1216 if !isEmptyConfig {
1217 val.log = cl
1218 }
1219 configValues = append(configValues, ConfigValue{
1220 Class: "custom_log",
1221 Value: val,
1222 })
1223 return configValues, nil
1224 }
1225
1226 // parseLogSkip parses the log_skip directive. Syntax:
1227 //
1228 // log_skip [<matcher>]
1229 func parseLogSkip(h Helper) (caddyhttp.MiddlewareHandler, error) {
1230 h.Next() // consume directive name
1231
1232 // "skip_log" is deprecated, replaced by "log_skip"
1233 if h.Val() == "skip_log" {
1234 caddy.Log().Named("config.adapter.caddyfile").Warn("the 'skip_log' directive is deprecated, please use 'log_skip' instead!")
1235 }
1236
1237 if h.NextArg() {
1238 return nil, h.ArgErr()
1239 }
1240
1241 if h.NextBlock(0) {
1242 return nil, h.Err("log_skip directive does not accept blocks")
1243 }
1244
1245 return caddyhttp.VarsMiddleware{"log_skip": true}, nil
1246 }
1247
1248 // parseLogName parses the log_name directive. Syntax:
1249 //
1250 // log_name <names...>
1251 func parseLogName(h Helper) (caddyhttp.MiddlewareHandler, error) {
1252 h.Next() // consume directive name
1253 return caddyhttp.VarsMiddleware{
1254 caddyhttp.AccessLoggerNameVarKey: h.RemainingArgs(),
1255 }, nil
1256 }
1257
1258 // parseLogLevel parses the log_level directive. Syntax:
1259 //
1260 // log_level [<matcher>] <level>
1261 //
1262 // The level overrides the access log severity for matching requests.
1263 // Supported levels are DEBUG, INFO, WARN, and ERROR (case-insensitive).
1264 func parseLogLevel(h Helper) (caddyhttp.MiddlewareHandler, error) {
1265 h.Next() // consume directive name
1266 args := h.RemainingArgs()
1267 if len(args) != 1 {
1268 return nil, h.ArgErr()
1269 }
1270 if h.NextBlock(0) {
1271 return nil, h.Err("log_level directive does not accept blocks")
1272 }
1273 level, err := zapcore.ParseLevel(args[0])
1274 if err != nil || level < zapcore.DebugLevel || level > zapcore.ErrorLevel {
1275 return nil, h.Errf("invalid log level %q: expected DEBUG, INFO, WARN, or ERROR", args[0])
1276 }
1277 return caddyhttp.VarsMiddleware{caddyhttp.LogLevelVar: level.String()}, nil
1278 }