@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 503 lines 14.8 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 "slices"
21 "strconv"
22
23 "github.com/dustin/go-humanize"
24
25 "github.com/caddyserver/caddy/v2"
26 "github.com/caddyserver/caddy/v2/caddyconfig"
27 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
28 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
29 )
30
31 // serverOptions collects server config overrides parsed from Caddyfile global options
32 type serverOptions struct {
33 // If set, will only apply these options to servers that contain a
34 // listener address that matches exactly. If empty, will apply to all
35 // servers that were not already matched by another serverOptions.
36 ListenerAddress string
37
38 // These will all map 1:1 to the caddyhttp.Server struct
39 Name string
40 ListenerWrappersRaw []json.RawMessage
41 PacketConnWrappersRaw []json.RawMessage
42 ReadTimeout caddy.Duration
43 ReadHeaderTimeout caddy.Duration
44 ReadIdleTimeout caddy.Duration
45 ReadMinRate int64
46 WriteTimeout caddy.Duration
47 WriteIdleTimeout caddy.Duration
48 WriteMinRate int64
49 MaxWriteChunk int
50 IdleTimeout caddy.Duration
51 KeepAliveInterval caddy.Duration
52 KeepAliveIdle caddy.Duration
53 KeepAliveCount int
54 MaxHeaderBytes int
55 EnableFullDuplex bool
56 WebTransport *caddyhttp.WebTransportConfig
57 ExpectedUnderscoreHeaders []string
58 ExpectedDotHeaders []string
59 Protocols []string
60 StrictSNIHost *bool
61 TrustedProxiesRaw json.RawMessage
62 TrustedProxiesStrict int
63 TrustedProxiesUnix bool
64 ClientIPHeaders []string
65 ShouldLogCredentials bool
66 Metrics *caddyhttp.Metrics
67 Trace bool // TODO: EXPERIMENTAL
68 // If set, overrides whether QUIC listeners allow 0-RTT (early data).
69 // If nil, the default behavior is used (currently allowed).
70 Allow0RTT *bool
71 }
72
73 func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
74 d.Next() // consume option name
75
76 serverOpts := serverOptions{}
77 if d.NextArg() {
78 serverOpts.ListenerAddress = d.Val()
79 if d.NextArg() {
80 return nil, d.ArgErr()
81 }
82 }
83 for d.NextBlock(0) {
84 switch d.Val() {
85 case "name":
86 if serverOpts.ListenerAddress == "" {
87 return nil, d.Errf("cannot set a name for a server without a listener address")
88 }
89 if !d.NextArg() {
90 return nil, d.ArgErr()
91 }
92 serverOpts.Name = d.Val()
93
94 case "listener_wrappers":
95 for nesting := d.Nesting(); d.NextBlock(nesting); {
96 modID := "caddy.listeners." + d.Val()
97 unm, err := caddyfile.UnmarshalModule(d, modID)
98 if err != nil {
99 return nil, err
100 }
101 listenerWrapper, ok := unm.(caddy.ListenerWrapper)
102 if !ok {
103 return nil, fmt.Errorf("module %s (%T) is not a listener wrapper", modID, unm)
104 }
105 jsonListenerWrapper := caddyconfig.JSONModuleObject(
106 listenerWrapper,
107 "wrapper",
108 listenerWrapper.(caddy.Module).CaddyModule().ID.Name(),
109 nil,
110 )
111 serverOpts.ListenerWrappersRaw = append(serverOpts.ListenerWrappersRaw, jsonListenerWrapper)
112 }
113
114 case "packet_conn_wrappers":
115 for nesting := d.Nesting(); d.NextBlock(nesting); {
116 modID := "caddy.packetconns." + d.Val()
117 unm, err := caddyfile.UnmarshalModule(d, modID)
118 if err != nil {
119 return nil, err
120 }
121 packetConnWrapper, ok := unm.(caddy.PacketConnWrapper)
122 if !ok {
123 return nil, fmt.Errorf("module %s (%T) is not a packet conn wrapper", modID, unm)
124 }
125 jsonPacketConnWrapper := caddyconfig.JSONModuleObject(
126 packetConnWrapper,
127 "wrapper",
128 packetConnWrapper.(caddy.Module).CaddyModule().ID.Name(),
129 nil,
130 )
131 serverOpts.PacketConnWrappersRaw = append(serverOpts.PacketConnWrappersRaw, jsonPacketConnWrapper)
132 }
133
134 case "timeouts":
135 for nesting := d.Nesting(); d.NextBlock(nesting); {
136 switch d.Val() {
137 case "read_body":
138 if !d.NextArg() {
139 return nil, d.ArgErr()
140 }
141 dur, err := caddy.ParseDuration(d.Val())
142 if err != nil {
143 return nil, d.Errf("parsing read_body timeout duration: %v", err)
144 }
145 serverOpts.ReadTimeout = caddy.Duration(dur)
146
147 case "read_body_idle":
148 args := d.RemainingArgs()
149 if len(args) < 1 || len(args) > 2 {
150 return nil, d.ArgErr()
151 }
152 dur, err := caddy.ParseDuration(args[0])
153 if err != nil {
154 return nil, d.Errf("parsing read_body_idle timeout duration: %v", err)
155 }
156 serverOpts.ReadIdleTimeout = caddy.Duration(dur)
157 if len(args) == 2 {
158 rate, err := strconv.ParseInt(args[1], 10, 64)
159 if err != nil {
160 return nil, d.Errf("parsing read_body_idle min_rate bytes/second: %v", err)
161 }
162 serverOpts.ReadMinRate = rate
163 }
164
165 case "read_header":
166 if !d.NextArg() {
167 return nil, d.ArgErr()
168 }
169 dur, err := caddy.ParseDuration(d.Val())
170 if err != nil {
171 return nil, d.Errf("parsing read_header timeout duration: %v", err)
172 }
173 serverOpts.ReadHeaderTimeout = caddy.Duration(dur)
174
175 case "write":
176 if !d.NextArg() {
177 return nil, d.ArgErr()
178 }
179 dur, err := caddy.ParseDuration(d.Val())
180 if err != nil {
181 return nil, d.Errf("parsing write timeout duration: %v", err)
182 }
183 serverOpts.WriteTimeout = caddy.Duration(dur)
184
185 case "write_idle":
186 args := d.RemainingArgs()
187 if len(args) < 1 || len(args) > 2 {
188 return nil, d.ArgErr()
189 }
190 dur, err := caddy.ParseDuration(args[0])
191 if err != nil {
192 return nil, d.Errf("parsing write_idle timeout duration: %v", err)
193 }
194 serverOpts.WriteIdleTimeout = caddy.Duration(dur)
195 if len(args) == 2 {
196 rate, err := strconv.ParseInt(args[1], 10, 64)
197 if err != nil {
198 return nil, d.Errf("parsing write_idle min_rate bytes/second: %v", err)
199 }
200 serverOpts.WriteMinRate = rate
201 }
202
203 case "write_max_chunk":
204 var sizeStr string
205 if !d.AllArgs(&sizeStr) {
206 return nil, d.ArgErr()
207 }
208 size, err := humanize.ParseBytes(sizeStr)
209 if err != nil {
210 return nil, d.Errf("parsing write_max_chunk: %v", err)
211 }
212 serverOpts.MaxWriteChunk = int(size)
213
214 case "idle":
215 if !d.NextArg() {
216 return nil, d.ArgErr()
217 }
218 dur, err := caddy.ParseDuration(d.Val())
219 if err != nil {
220 return nil, d.Errf("parsing idle timeout duration: %v", err)
221 }
222 serverOpts.IdleTimeout = caddy.Duration(dur)
223
224 default:
225 return nil, d.Errf("unrecognized timeouts option '%s'", d.Val())
226 }
227 }
228
229 case "keepalive_interval":
230 if !d.NextArg() {
231 return nil, d.ArgErr()
232 }
233 dur, err := caddy.ParseDuration(d.Val())
234 if err != nil {
235 return nil, d.Errf("parsing keepalive interval duration: %v", err)
236 }
237 serverOpts.KeepAliveInterval = caddy.Duration(dur)
238
239 case "keepalive_idle":
240 if !d.NextArg() {
241 return nil, d.ArgErr()
242 }
243 dur, err := caddy.ParseDuration(d.Val())
244 if err != nil {
245 return nil, d.Errf("parsing keepalive idle duration: %v", err)
246 }
247 serverOpts.KeepAliveIdle = caddy.Duration(dur)
248
249 case "keepalive_count":
250 if !d.NextArg() {
251 return nil, d.ArgErr()
252 }
253 cnt, err := strconv.ParseInt(d.Val(), 10, 32)
254 if err != nil {
255 return nil, d.Errf("parsing keepalive count int: %v", err)
256 }
257 serverOpts.KeepAliveCount = int(cnt)
258
259 case "max_header_size":
260 var sizeStr string
261 if !d.AllArgs(&sizeStr) {
262 return nil, d.ArgErr()
263 }
264 size, err := humanize.ParseBytes(sizeStr)
265 if err != nil {
266 return nil, d.Errf("parsing max_header_size: %v", err)
267 }
268 serverOpts.MaxHeaderBytes = int(size)
269
270 case "enable_full_duplex":
271 if d.NextArg() {
272 return nil, d.ArgErr()
273 }
274 serverOpts.EnableFullDuplex = true
275
276 case "webtransport":
277 if d.NextArg() {
278 return nil, d.ArgErr()
279 }
280 for nesting := d.Nesting(); d.NextBlock(nesting); {
281 return nil, d.Errf("unrecognized webtransport option: %s", d.Val())
282 }
283 serverOpts.WebTransport = new(caddyhttp.WebTransportConfig)
284
285 case "expected_underscore_headers":
286 args := d.RemainingArgs()
287 if len(args) == 0 {
288 return nil, d.ArgErr()
289 }
290 serverOpts.ExpectedUnderscoreHeaders = args
291
292 case "expected_dot_headers":
293 args := d.RemainingArgs()
294 if len(args) == 0 {
295 return nil, d.ArgErr()
296 }
297 serverOpts.ExpectedDotHeaders = args
298
299 case "log_credentials":
300 if d.NextArg() {
301 return nil, d.ArgErr()
302 }
303 serverOpts.ShouldLogCredentials = true
304
305 case "protocols":
306 protos := d.RemainingArgs()
307 for _, proto := range protos {
308 if proto != "h1" && proto != "h2" && proto != "h2c" && proto != "h3" {
309 return nil, d.Errf("unknown protocol '%s': expected h1, h2, h2c, or h3", proto)
310 }
311 if slices.Contains(serverOpts.Protocols, proto) {
312 return nil, d.Errf("protocol %s specified more than once", proto)
313 }
314 serverOpts.Protocols = append(serverOpts.Protocols, proto)
315 }
316 if nesting := d.Nesting(); d.NextBlock(nesting) {
317 return nil, d.ArgErr()
318 }
319
320 case "strict_sni_host":
321 if d.NextArg() && d.Val() != "insecure_off" && d.Val() != "on" {
322 return nil, d.Errf("strict_sni_host only supports 'on' or 'insecure_off', got '%s'", d.Val())
323 }
324 boolVal := true
325 if d.Val() == "insecure_off" {
326 boolVal = false
327 }
328 serverOpts.StrictSNIHost = &boolVal
329
330 case "trusted_proxies":
331 if !d.NextArg() {
332 return nil, d.Err("trusted_proxies expects an IP range source module name as its first argument")
333 }
334 modID := "http.ip_sources." + d.Val()
335 unm, err := caddyfile.UnmarshalModule(d, modID)
336 if err != nil {
337 return nil, err
338 }
339 source, ok := unm.(caddyhttp.IPRangeSource)
340 if !ok {
341 return nil, fmt.Errorf("module %s (%T) is not an IP range source", modID, unm)
342 }
343 jsonSource := caddyconfig.JSONModuleObject(
344 source,
345 "source",
346 source.(caddy.Module).CaddyModule().ID.Name(),
347 nil,
348 )
349 serverOpts.TrustedProxiesRaw = jsonSource
350
351 case "trusted_proxies_strict":
352 if d.NextArg() {
353 return nil, d.ArgErr()
354 }
355 serverOpts.TrustedProxiesStrict = 1
356
357 case "trusted_proxies_unix":
358 if d.NextArg() {
359 return nil, d.ArgErr()
360 }
361 serverOpts.TrustedProxiesUnix = true
362
363 case "client_ip_headers":
364 headers := d.RemainingArgs()
365 for _, header := range headers {
366 if slices.Contains(serverOpts.ClientIPHeaders, header) {
367 return nil, d.Errf("client IP header %s specified more than once", header)
368 }
369 serverOpts.ClientIPHeaders = append(serverOpts.ClientIPHeaders, header)
370 }
371 if nesting := d.Nesting(); d.NextBlock(nesting) {
372 return nil, d.ArgErr()
373 }
374
375 case "metrics":
376 caddy.Log().Warn("The nested 'metrics' option inside `servers` is deprecated and will be removed in the next major version. Use the global 'metrics' option instead.")
377 serverOpts.Metrics = new(caddyhttp.Metrics)
378 for nesting := d.Nesting(); d.NextBlock(nesting); {
379 switch d.Val() {
380 case "per_host":
381 serverOpts.Metrics.PerHost = true
382 default:
383 return nil, d.Errf("unrecognized metrics option '%s'", d.Val())
384 }
385 }
386
387 case "trace":
388 if d.NextArg() {
389 return nil, d.ArgErr()
390 }
391 serverOpts.Trace = true
392
393 case "0rtt":
394 // only supports "off" for now
395 if !d.NextArg() {
396 return nil, d.ArgErr()
397 }
398 if d.Val() != "off" {
399 return nil, d.Errf("unsupported 0rtt argument '%s' (only 'off' is supported)", d.Val())
400 }
401 boolVal := false
402 serverOpts.Allow0RTT = &boolVal
403
404 default:
405 return nil, d.Errf("unrecognized servers option '%s'", d.Val())
406 }
407 }
408 return serverOpts, nil
409 }
410
411 // applyServerOptions sets the server options on the appropriate servers
412 func applyServerOptions(
413 servers map[string]*caddyhttp.Server,
414 options map[string]any,
415 _ *[]caddyconfig.Warning,
416 ) error {
417 serverOpts, ok := options["servers"].([]serverOptions)
418 if !ok {
419 return nil
420 }
421
422 // check for duplicate names, which would clobber the config
423 existingNames := map[string]bool{}
424 for _, opts := range serverOpts {
425 if opts.Name == "" {
426 continue
427 }
428 if existingNames[opts.Name] {
429 return fmt.Errorf("cannot use duplicate server name '%s'", opts.Name)
430 }
431 existingNames[opts.Name] = true
432 }
433
434 // collect the server name overrides
435 nameReplacements := map[string]string{}
436
437 for key, server := range servers {
438 // find the options that apply to this server
439 optsIndex := slices.IndexFunc(serverOpts, func(s serverOptions) bool {
440 return s.ListenerAddress == "" || slices.Contains(server.Listen, s.ListenerAddress)
441 })
442
443 // if none apply, then move to the next server
444 if optsIndex == -1 {
445 continue
446 }
447 opts := serverOpts[optsIndex]
448
449 // set all the options
450 server.ListenerWrappersRaw = opts.ListenerWrappersRaw
451 server.PacketConnWrappersRaw = opts.PacketConnWrappersRaw
452 server.ReadTimeout = opts.ReadTimeout
453 server.ReadHeaderTimeout = opts.ReadHeaderTimeout
454 server.ReadIdleTimeout = opts.ReadIdleTimeout
455 server.ReadMinRate = opts.ReadMinRate
456 server.WriteTimeout = opts.WriteTimeout
457 server.WriteIdleTimeout = opts.WriteIdleTimeout
458 server.WriteMinRate = opts.WriteMinRate
459 server.MaxWriteChunk = opts.MaxWriteChunk
460 server.IdleTimeout = opts.IdleTimeout
461 server.KeepAliveInterval = opts.KeepAliveInterval
462 server.KeepAliveIdle = opts.KeepAliveIdle
463 server.KeepAliveCount = opts.KeepAliveCount
464 server.MaxHeaderBytes = opts.MaxHeaderBytes
465 server.EnableFullDuplex = opts.EnableFullDuplex
466 server.WebTransport = opts.WebTransport
467 server.ExpectedUnderscoreHeaders = opts.ExpectedUnderscoreHeaders
468 server.ExpectedDotHeaders = opts.ExpectedDotHeaders
469 server.Protocols = opts.Protocols
470 server.StrictSNIHost = opts.StrictSNIHost
471 server.TrustedProxiesRaw = opts.TrustedProxiesRaw
472 server.ClientIPHeaders = opts.ClientIPHeaders
473 server.TrustedProxiesStrict = opts.TrustedProxiesStrict
474 server.TrustedProxiesUnix = opts.TrustedProxiesUnix
475 server.Metrics = opts.Metrics
476 server.Allow0RTT = opts.Allow0RTT
477 if opts.ShouldLogCredentials {
478 if server.Logs == nil {
479 server.Logs = new(caddyhttp.ServerLogConfig)
480 }
481 server.Logs.ShouldLogCredentials = opts.ShouldLogCredentials
482 }
483 if opts.Trace {
484 // TODO: THIS IS EXPERIMENTAL (MAY 2024)
485 if server.Logs == nil {
486 server.Logs = new(caddyhttp.ServerLogConfig)
487 }
488 server.Logs.Trace = opts.Trace
489 }
490
491 if opts.Name != "" {
492 nameReplacements[key] = opts.Name
493 }
494 }
495
496 // rename the servers if marked to do so
497 for old, new := range nameReplacements {
498 servers[new] = servers[old]
499 delete(servers, old)
500 }
501
502 return nil
503 }