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
+}