@marcel91200 / caddy Mirror · read-only / commits / 1e662262
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 acme_ca and email global options

Also add ability to access options from individual unmarshalers through the Helper values

Matthew Holt committed Sep 30, 2019 at 09:11 UTC 1e662262179d326586d2beb849f842b82b7324c4
5 files changed +112 -70
caddyconfig/httpcaddyfile/builtins.go
+10 -2
@@ -86,6 +86,14 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
86 var mgr caddytls.ACMEManagerMaker
87 var off bool
88
89 + // fill in global defaults, if configured
90 + if email := h.Option("email"); email != nil {
91 + mgr.Email = email.(string)
92 + }
93 + if acmeCA := h.Option("acme_ca"); acmeCA != nil {
94 + mgr.CA = acmeCA.(string)
95 + }
96 +
97 for h.Next() {
98 // file certificate loader
99 firstLine := h.RemainingArgs()
@@ -112,7 +120,6 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
120 hasBlock = true
121
122 switch h.Val() {
115 -
123 // connection policy
124 case "protocols":
125 args := h.RemainingArgs()
@@ -164,7 +171,8 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
171 }
172 mgr.CA = arg[0]
173
167 - // TODO: other properties for automation manager
174 + default:
175 + return nil, h.Errf("unknown subdirective: %s", h.Val())
176 }
177 }
178
caddyconfig/httpcaddyfile/directives.go
+6
@@ -80,11 +80,17 @@ func RegisterHandlerDirective(dir string, setupFunc UnmarshalHandlerFunc) {
80 // Caddyfile tokens.
81 type Helper struct {
82 *caddyfile.Dispenser
83 + options map[string]interface{}
84 warnings *[]caddyconfig.Warning
85 matcherDefs map[string]map[string]json.RawMessage
86 parentBlock caddyfile.ServerBlock
87 }
88
89 +// Option gets the option keyed by name.
90 +func (h Helper) Option(name string) interface{} {
91 + return h.options[name]
92 +}
93 +
94 // Caddyfiles returns the list of config files from
95 // which tokens in the current server block were loaded.
96 func (h Helper) Caddyfiles() []string {
caddyconfig/httpcaddyfile/handlers.go deleted
-56
@@ -1,56 +0,0 @@
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 -
21 - "github.com/caddyserver/caddy/v2"
22 - "github.com/caddyserver/caddy/v2/caddyconfig"
23 - "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
24 - "github.com/caddyserver/caddy/v2/modules/caddyhttp"
25 -)
26 -
27 -func (st *ServerType) parseMatcherDefinitions(d *caddyfile.Dispenser) (map[string]map[string]json.RawMessage, error) {
28 - matchers := make(map[string]map[string]json.RawMessage)
29 - for d.Next() {
30 - definitionName := d.Val()
31 - for nesting := d.Nesting(); d.NextBlock(nesting); {
32 - matcherName := d.Val()
33 - mod, err := caddy.GetModule("http.matchers." + matcherName)
34 - if err != nil {
35 - return nil, fmt.Errorf("getting matcher module '%s': %v", matcherName, err)
36 - }
37 - unm, ok := mod.New().(caddyfile.Unmarshaler)
38 - if !ok {
39 - return nil, fmt.Errorf("matcher module '%s' is not a Caddyfile unmarshaler", matcherName)
40 - }
41 - err = unm.UnmarshalCaddyfile(d.NewFromNextTokens())
42 - if err != nil {
43 - return nil, err
44 - }
45 - rm, ok := unm.(caddyhttp.RequestMatcher)
46 - if !ok {
47 - return nil, fmt.Errorf("matcher module '%s' is not a request matcher", matcherName)
48 - }
49 - if _, ok := matchers[definitionName]; !ok {
50 - matchers[definitionName] = make(map[string]json.RawMessage)
51 - }
52 - matchers[definitionName][matcherName] = caddyconfig.JSON(rm, nil)
53 - }
54 - }
55 - return matchers, nil
56 -}
caddyconfig/httpcaddyfile/httptype.go
+72 -12
@@ -58,6 +58,7 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
58 var val interface{}
59 var err error
60 disp := caddyfile.NewDispenser(segment)
61 + // TODO: make this switch into a map
62 switch dir {
63 case "http_port":
64 val, err = parseOptHTTPPort(disp)
@@ -69,6 +70,10 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
70 val, err = parseOptExperimentalHTTP3(disp)
71 case "storage":
72 val, err = parseOptStorage(disp)
73 + case "acme_ca":
74 + val, err = parseOptACMECA(disp)
75 + case "email":
76 + val, err = parseOptEmail(disp)
77 default:
78 return nil, warnings, fmt.Errorf("unrecognized parameter name: %s", dir)
79 }
@@ -108,7 +113,7 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
113
114 // extract matcher definitions
115 d := sb.block.DispenseDirective("matcher")
111 - matcherDefs, err := st.parseMatcherDefinitions(d)
116 + matcherDefs, err := parseMatcherDefinitions(d)
117 if err != nil {
118 return nil, warnings, err
119 }
@@ -122,6 +127,7 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
127 if dirFunc, ok := registeredDirectives[dir]; ok {
128 results, err := dirFunc(Helper{
129 Dispenser: caddyfile.NewDispenser(segment),
130 + options: options,
131 warnings: &warnings,
132 matcherDefs: matcherDefs,
133 parentBlock: sb.block,
@@ -166,7 +172,7 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
172 // now for the TLS app! (TODO: refactor into own func)
173 tlsApp := caddytls.TLS{Certificates: make(map[string]json.RawMessage)}
174 for _, p := range pairings {
169 - for _, sblock := range p.serverBlocks {
175 + for i, sblock := range p.serverBlocks {
176 // tls automation policies
177 if mmVals, ok := sblock.pile["tls.automation_manager"]; ok {
178 for _, mmVal := range mmVals {
@@ -175,10 +181,16 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
181 if err != nil {
182 return nil, warnings, err
183 }
178 - tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, caddytls.AutomationPolicy{
179 - Hosts: sblockHosts,
180 - ManagementRaw: caddyconfig.JSONModuleObject(mm, "module", mm.(caddy.Module).CaddyModule().ID(), &warnings),
181 - })
184 + if len(sblockHosts) > 0 {
185 + tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, caddytls.AutomationPolicy{
186 + Hosts: sblockHosts,
187 + ManagementRaw: caddyconfig.JSONModuleObject(mm, "module", mm.(caddy.Module).CaddyModule().ID(), &warnings),
188 + })
189 + } else {
190 + warnings = append(warnings, caddyconfig.Warning{
191 + Message: fmt.Sprintf("Server block %d %v has no names that qualify for automatic HTTPS, so no TLS automation policy will be added.", i, sblock.block.Keys),
192 + })
193 + }
194 }
195 }
196
@@ -192,8 +204,25 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
204 }
205 }
206 }
195 - // consolidate automation policies that are the exact same
196 - tlsApp.Automation.Policies = consolidateAutomationPolicies(tlsApp.Automation.Policies)
207 + // if global ACME CA or email were set, append a catch-all automation
208 + // policy that ensures they will be used if no tls directive was used
209 + acmeCA, hasACMECA := options["acme_ca"]
210 + email, hasEmail := options["email"]
211 + if hasACMECA || hasEmail {
212 + if tlsApp.Automation == nil {
213 + tlsApp.Automation = new(caddytls.AutomationConfig)
214 + }
215 + tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, caddytls.AutomationPolicy{
216 + ManagementRaw: caddyconfig.JSONModuleObject(caddytls.ACMEManagerMaker{
217 + CA: acmeCA.(string),
218 + Email: email.(string),
219 + }, "module", "acme", &warnings),
220 + })
221 + }
222 + if tlsApp.Automation != nil {
223 + // consolidate automation policies that are the exact same
224 + tlsApp.Automation.Policies = consolidateAutomationPolicies(tlsApp.Automation.Policies)
225 + }
226
227 // if experimental HTTP/3 is enabled, enable it on each server
228 if enableH3, ok := options["experimental_http3"].(bool); ok && enableH3 {
@@ -207,7 +236,7 @@ func (st ServerType) Setup(originalServerBlocks []caddyfile.ServerBlock,
236 if !reflect.DeepEqual(httpApp, caddyhttp.App{}) {
237 cfg.AppsRaw["http"] = caddyconfig.JSON(httpApp, &warnings)
238 }
210 - if !reflect.DeepEqual(tlsApp, caddytls.TLS{}) {
239 + if !reflect.DeepEqual(tlsApp, caddytls.TLS{Certificates: make(map[string]json.RawMessage)}) {
240 cfg.AppsRaw["tls"] = caddyconfig.JSON(tlsApp, &warnings)
241 }
242 if storageCvtr, ok := options["storage"].(caddy.StorageConverter); ok {
@@ -415,10 +444,10 @@ func consolidateAutomationPolicies(aps []caddytls.AutomationPolicy) []caddytls.A
444 }
445 if reflect.DeepEqual(aps[i].ManagementRaw, aps[j].ManagementRaw) {
446 aps[i].Hosts = append(aps[i].Hosts, aps[j].Hosts...)
447 + aps = append(aps[:j], aps[j+1:]...)
448 + i--
449 + break
450 }
419 - aps = append(aps[:j], aps[j+1:]...)
420 - i--
421 - break
451 }
452 }
453 return aps
@@ -531,6 +560,37 @@ func (st *ServerType) compileEncodedMatcherSets(sblock caddyfile.ServerBlock) ([
560 return matcherSetsEnc, nil
561 }
562
563 +func parseMatcherDefinitions(d *caddyfile.Dispenser) (map[string]map[string]json.RawMessage, error) {
564 + matchers := make(map[string]map[string]json.RawMessage)
565 + for d.Next() {
566 + definitionName := d.Val()
567 + for nesting := d.Nesting(); d.NextBlock(nesting); {
568 + matcherName := d.Val()
569 + mod, err := caddy.GetModule("http.matchers." + matcherName)
570 + if err != nil {
571 + return nil, fmt.Errorf("getting matcher module '%s': %v", matcherName, err)
572 + }
573 + unm, ok := mod.New().(caddyfile.Unmarshaler)
574 + if !ok {
575 + return nil, fmt.Errorf("matcher module '%s' is not a Caddyfile unmarshaler", matcherName)
576 + }
577 + err = unm.UnmarshalCaddyfile(d.NewFromNextTokens())
578 + if err != nil {
579 + return nil, err
580 + }
581 + rm, ok := unm.(caddyhttp.RequestMatcher)
582 + if !ok {
583 + return nil, fmt.Errorf("matcher module '%s' is not a request matcher", matcherName)
584 + }
585 + if _, ok := matchers[definitionName]; !ok {
586 + matchers[definitionName] = make(map[string]json.RawMessage)
587 + }
588 + matchers[definitionName][matcherName] = caddyconfig.JSON(rm, nil)
589 + }
590 + }
591 + return matchers, nil
592 +}
593 +
594 func encodeMatcherSet(matchers map[string]caddyhttp.RequestMatcher) (map[string]json.RawMessage, error) {
595 msEncoded := make(map[string]json.RawMessage)
596 for matcherName, val := range matchers {
caddyconfig/httpcaddyfile/options.go
+24
@@ -108,3 +108,27 @@ func parseOptStorage(d *caddyfile.Dispenser) (caddy.StorageConverter, error) {
108 }
109 return storage, nil
110 }
111 +
112 +func parseOptACMECA(d *caddyfile.Dispenser) (string, error) {
113 + d.Next() // consume parameter name
114 + if !d.Next() {
115 + return "", d.ArgErr()
116 + }
117 + val := d.Val()
118 + if d.Next() {
119 + return "", d.ArgErr()
120 + }
121 + return val, nil
122 +}
123 +
124 +func parseOptEmail(d *caddyfile.Dispenser) (string, error) {
125 + d.Next() // consume parameter name
126 + if !d.Next() {
127 + return "", d.ArgErr()
128 + }
129 + val := d.Val()
130 + if d.Next() {
131 + return "", d.ArgErr()
132 + }
133 + return val, nil
134 +}