Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 13, 10:30 UTC
caddyhttp: Remote IP prefix placeholders
See https://github.com/mholt/caddy-ratelimit/issues/12
Matthew Holt committed
Sep 30, 2022 at 13:29 UTC
9873ff9918224f56604048ea0ed3d3ae3953939e
2 files changed
+48
-4
modules/caddyhttp/replacer.go
+32
@@ -31,6 +31,7 @@ import (
31
"io"
32
"net"
33
"net/http"
34
+ "net/netip"
35
"net/textproto"
36
"net/url"
37
"path"
@@ -196,6 +197,37 @@ func addHTTPVarsToReplacer(repl *caddy.Replacer, req *http.Request, w http.Respo
197
return or.URL.RawQuery, true
198
}
199
200
+ // remote IP range/prefix (e.g. keep top 24 bits of 1.2.3.4 => "1.2.3.0/24")
201
+ // syntax: "/V4,V6" where V4 = IPv4 bits, and V6 = IPv6 bits; if no comma, then same bit length used for both
202
+ // (EXPERIMENTAL)
203
+ if strings.HasPrefix(key, "http.request.remote.host/") {
204
+ host, _, err := net.SplitHostPort(req.RemoteAddr)
205
+ if err != nil {
206
+ host = req.RemoteAddr // assume no port, I guess?
207
+ }
208
+ addr, err := netip.ParseAddr(host)
209
+ if err != nil {
210
+ return host, true // not an IP address
211
+ }
212
+ // extract the bits from the end of the placeholder (start after "/") then split on ","
213
+ bitsBoth := key[strings.Index(key, "/")+1:]
214
+ ipv4BitsStr, ipv6BitsStr, cutOK := strings.Cut(bitsBoth, ",")
215
+ bitsStr := ipv4BitsStr
216
+ if addr.Is6() && cutOK {
217
+ bitsStr = ipv6BitsStr
218
+ }
219
+ // convert to integer then compute prefix
220
+ bits, err := strconv.Atoi(bitsStr)
221
+ if err != nil {
222
+ return "", true
223
+ }
224
+ prefix, err := addr.Prefix(bits)
225
+ if err != nil {
226
+ return "", true
227
+ }
228
+ return prefix.String(), true
229
+ }
230
+
231
// hostname labels
232
if strings.HasPrefix(key, reqHostLabelsReplPrefix) {
233
idxStr := key[len(reqHostLabelsReplPrefix):]
modules/caddyhttp/replacer_test.go
+16
-4
@@ -32,7 +32,7 @@ func TestHTTPVarReplacement(t *testing.T) {
32
ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
33
req = req.WithContext(ctx)
34
req.Host = "example.com:80"
35
- req.RemoteAddr = "localhost:1234"
35
+ req.RemoteAddr = "192.168.159.32:1234"
36
37
clientCert := []byte(`-----BEGIN CERTIFICATE-----
38
MIIB9jCCAV+gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1DYWRk
@@ -61,7 +61,7 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
61
req.TLS = &tls.ConnectionState{
62
Version: tls.VersionTLS13,
63
HandshakeComplete: true,
64
- ServerName: "foo.com",
64
+ ServerName: "example.com",
65
CipherSuite: tls.TLS_AES_256_GCM_SHA384,
66
PeerCertificates: []*x509.Certificate{cert},
67
NegotiatedProtocol: "h2",
@@ -97,7 +97,19 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
97
},
98
{
99
get: "http.request.remote.host",
100
- expect: "localhost",
100
+ expect: "192.168.159.32",
101
+ },
102
+ {
103
+ get: "http.request.remote.host/24",
104
+ expect: "192.168.159.0/24",
105
+ },
106
+ {
107
+ get: "http.request.remote.host/24,32",
108
+ expect: "192.168.159.0/24",
109
+ },
110
+ {
111
+ get: "http.request.remote.host/999",
112
+ expect: "",
113
},
114
{
115
get: "http.request.remote.port",
@@ -146,7 +158,7 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
158
},
159
{
160
get: "http.request.tls.server_name",
149
- expect: "foo.com",
161
+ expect: "example.com",
162
},
163
{
164
get: "http.request.tls.version",