@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 137 lines 4.33 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 caddyhttp
16
17 import (
18 "fmt"
19 "net/http"
20 "net/netip"
21 "strings"
22
23 "github.com/caddyserver/caddy/v2"
24 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
25 "github.com/caddyserver/caddy/v2/internal"
26 )
27
28 func init() {
29 caddy.RegisterModule(StaticIPRange{})
30 }
31
32 // IPRangeSource gets a list of IP ranges.
33 //
34 // The request is passed as an argument to allow plugin implementations
35 // to have more flexibility. But, a plugin MUST NOT modify the request.
36 // The caller will have read the `r.RemoteAddr` before getting IP ranges.
37 //
38 // This should be a very fast function -- instant if possible.
39 // The list of IP ranges should be sourced as soon as possible if loaded
40 // from an external source (i.e. initially loaded during Provisioning),
41 // so that it's ready to be used when requests start getting handled.
42 // A read lock should probably be used to get the cached value if the
43 // ranges can change at runtime (e.g. periodically refreshed).
44 // Using a `caddy.UsagePool` may be a good idea to avoid having refetch
45 // the values when a config reload occurs, which would waste time.
46 //
47 // If the list of IP ranges cannot be sourced, then provisioning SHOULD
48 // fail. Getting the IP ranges at runtime MUST NOT fail, because it would
49 // cancel incoming requests. If refreshing the list fails, then the
50 // previous list of IP ranges should continue to be returned so that the
51 // server can continue to operate normally.
52 type IPRangeSource interface {
53 GetIPRanges(*http.Request) []netip.Prefix
54 }
55
56 // StaticIPRange provides a static range of IP address prefixes (CIDRs).
57 type StaticIPRange struct {
58 // A static list of IP ranges (supports CIDR notation).
59 Ranges []string `json:"ranges,omitempty"`
60
61 // Holds the parsed CIDR ranges from Ranges.
62 ranges []netip.Prefix
63 }
64
65 // CaddyModule returns the Caddy module information.
66 func (StaticIPRange) CaddyModule() caddy.ModuleInfo {
67 return caddy.ModuleInfo{
68 ID: "http.ip_sources.static",
69 New: func() caddy.Module { return new(StaticIPRange) },
70 }
71 }
72
73 func (s *StaticIPRange) Provision(ctx caddy.Context) error {
74 for _, str := range s.Ranges {
75 prefix, err := CIDRExpressionToPrefix(str)
76 if err != nil {
77 return err
78 }
79 s.ranges = append(s.ranges, prefix)
80 }
81
82 return nil
83 }
84
85 func (s *StaticIPRange) GetIPRanges(_ *http.Request) []netip.Prefix {
86 return s.ranges
87 }
88
89 // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
90 func (m *StaticIPRange) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
91 if !d.Next() {
92 return nil
93 }
94 for d.NextArg() {
95 if d.Val() == "private_ranges" {
96 m.Ranges = append(m.Ranges, internal.PrivateRangesCIDR()...)
97 continue
98 }
99 m.Ranges = append(m.Ranges, d.Val())
100 }
101 return nil
102 }
103
104 // CIDRExpressionToPrefix takes a string which could be either a
105 // CIDR expression or a single IP address, and returns a netip.Prefix.
106 func CIDRExpressionToPrefix(expr string) (netip.Prefix, error) {
107 // Having a slash means it should be a CIDR expression
108 if strings.Contains(expr, "/") {
109 prefix, err := netip.ParsePrefix(expr)
110 if err != nil {
111 return netip.Prefix{}, fmt.Errorf("parsing CIDR expression: '%s': %v", expr, err)
112 }
113 return prefix, nil
114 }
115
116 // Otherwise it's likely a single IP address
117 parsed, err := netip.ParseAddr(expr)
118 if err != nil {
119 return netip.Prefix{}, fmt.Errorf("invalid IP address: '%s': %v", expr, err)
120 }
121 prefix := netip.PrefixFrom(parsed, parsed.BitLen())
122 return prefix, nil
123 }
124
125 // Interface guards
126 var (
127 _ caddy.Provisioner = (*StaticIPRange)(nil)
128 _ caddyfile.Unmarshaler = (*StaticIPRange)(nil)
129 _ IPRangeSource = (*StaticIPRange)(nil)
130 )
131
132 // PrivateRangesCIDR returns a list of private CIDR range
133 // strings, which can be used as a configuration shortcut.
134 // Note: this function is used at least by mholt/caddy-l4.
135 func PrivateRangesCIDR() []string {
136 return internal.PrivateRangesCIDR()
137 }