@marcel91200 / caddy Mirror · read-only / commits / 9873ff99
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 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",