summaryrefslogtreecommitdiff
path: root/modules/caddyhttp/matchers.go
diff options
context:
space:
mode:
authorFrancis Lavoie <lavofr@gmail.com>2023-03-27 16:22:59 -0400
committerGitHub <noreply@github.com>2023-03-27 20:22:59 +0000
commit05e9974570a08df14b1162a1e98315d4ee9ec2ee (patch)
treee11e345766dc53c25f901a7402ceb489b4416fe9 /modules/caddyhttp/matchers.go
parent330be2d8c793147d3914f944eecb96c18f2eabff (diff)
caddyhttp: Determine real client IP if trusted proxies configured (#5104)
* caddyhttp: Determine real client IP if trusted proxies configured * Support customizing client IP header * Implement client_ip matcher, deprecate remote_ip's forwarded option
Diffstat (limited to 'modules/caddyhttp/matchers.go')
-rw-r--r--modules/caddyhttp/matchers.go178
1 files changed, 0 insertions, 178 deletions
diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go
index 3064300..f5f9a0f 100644
--- a/modules/caddyhttp/matchers.go
+++ b/modules/caddyhttp/matchers.go
@@ -20,7 +20,6 @@ import (
"fmt"
"net"
"net/http"
- "net/netip"
"net/textproto"
"net/url"
"path"
@@ -35,7 +34,6 @@ import (
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
- "go.uber.org/zap"
)
type (
@@ -176,24 +174,6 @@ type (
// "http/2", "http/3", or minimum versions: "http/2+", etc.
MatchProtocol string
- // MatchRemoteIP matches requests by client IP (or CIDR range).
- MatchRemoteIP struct {
- // The IPs or CIDR ranges to match.
- Ranges []string `json:"ranges,omitempty"`
-
- // If true, prefer the first IP in the request's X-Forwarded-For
- // header, if present, rather than the immediate peer's IP, as
- // the reference IP against which to match. Note that it is easy
- // to spoof request headers. Default: false
- Forwarded bool `json:"forwarded,omitempty"`
-
- // cidrs and zones vars should aligned always in the same
- // length and indexes for matching later
- cidrs []*netip.Prefix
- zones []string
- logger *zap.Logger
- }
-
// MatchNot matches requests by negating the results of its matcher
// sets. A single "not" matcher takes one or more matcher sets. Each
// matcher set is OR'ed; in other words, if any matcher set returns
@@ -229,7 +209,6 @@ func init() {
caddy.RegisterModule(MatchHeader{})
caddy.RegisterModule(MatchHeaderRE{})
caddy.RegisterModule(new(MatchProtocol))
- caddy.RegisterModule(MatchRemoteIP{})
caddy.RegisterModule(MatchNot{})
}
@@ -1261,159 +1240,6 @@ func (m MatchNot) Match(r *http.Request) bool {
return true
}
-// CaddyModule returns the Caddy module information.
-func (MatchRemoteIP) CaddyModule() caddy.ModuleInfo {
- return caddy.ModuleInfo{
- ID: "http.matchers.remote_ip",
- New: func() caddy.Module { return new(MatchRemoteIP) },
- }
-}
-
-// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
-func (m *MatchRemoteIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
- for d.Next() {
- for d.NextArg() {
- if d.Val() == "forwarded" {
- if len(m.Ranges) > 0 {
- return d.Err("if used, 'forwarded' must be first argument")
- }
- m.Forwarded = true
- continue
- }
- if d.Val() == "private_ranges" {
- m.Ranges = append(m.Ranges, PrivateRangesCIDR()...)
- continue
- }
- m.Ranges = append(m.Ranges, d.Val())
- }
- if d.NextBlock(0) {
- return d.Err("malformed remote_ip matcher: blocks are not supported")
- }
- }
- return nil
-}
-
-// CELLibrary produces options that expose this matcher for use in CEL
-// expression matchers.
-//
-// Example:
-//
-// expression remote_ip('forwarded', '192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8')
-func (MatchRemoteIP) CELLibrary(ctx caddy.Context) (cel.Library, error) {
- return CELMatcherImpl(
- // name of the macro, this is the function name that users see when writing expressions.
- "remote_ip",
- // name of the function that the macro will be rewritten to call.
- "remote_ip_match_request_list",
- // internal data type of the MatchPath value.
- []*cel.Type{cel.ListType(cel.StringType)},
- // function to convert a constant list of strings to a MatchPath instance.
- func(data ref.Val) (RequestMatcher, error) {
- refStringList := reflect.TypeOf([]string{})
- strList, err := data.ConvertToNative(refStringList)
- if err != nil {
- return nil, err
- }
-
- m := MatchRemoteIP{}
-
- for _, input := range strList.([]string) {
- if input == "forwarded" {
- if len(m.Ranges) > 0 {
- return nil, errors.New("if used, 'forwarded' must be first argument")
- }
- m.Forwarded = true
- continue
- }
- m.Ranges = append(m.Ranges, input)
- }
-
- err = m.Provision(ctx)
- return m, err
- },
- )
-}
-
-// Provision parses m's IP ranges, either from IP or CIDR expressions.
-func (m *MatchRemoteIP) Provision(ctx caddy.Context) error {
- m.logger = ctx.Logger()
- for _, str := range m.Ranges {
- // Exclude the zone_id from the IP
- if strings.Contains(str, "%") {
- split := strings.Split(str, "%")
- str = split[0]
- // write zone identifiers in m.zones for matching later
- m.zones = append(m.zones, split[1])
- } else {
- m.zones = append(m.zones, "")
- }
- if strings.Contains(str, "/") {
- ipNet, err := netip.ParsePrefix(str)
- if err != nil {
- return fmt.Errorf("parsing CIDR expression '%s': %v", str, err)
- }
- m.cidrs = append(m.cidrs, &ipNet)
- } else {
- ipAddr, err := netip.ParseAddr(str)
- if err != nil {
- return fmt.Errorf("invalid IP address: '%s': %v", str, err)
- }
- ipNew := netip.PrefixFrom(ipAddr, ipAddr.BitLen())
- m.cidrs = append(m.cidrs, &ipNew)
- }
- }
- return nil
-}
-
-func (m MatchRemoteIP) getClientIP(r *http.Request) (netip.Addr, string, error) {
- remote := r.RemoteAddr
- zoneID := ""
- if m.Forwarded {
- if fwdFor := r.Header.Get("X-Forwarded-For"); fwdFor != "" {
- remote = strings.TrimSpace(strings.Split(fwdFor, ",")[0])
- }
- }
- ipStr, _, err := net.SplitHostPort(remote)
- if err != nil {
- ipStr = remote // OK; probably didn't have a port
- }
- // Some IPv6-Adresses can contain zone identifiers at the end,
- // which are separated with "%"
- if strings.Contains(ipStr, "%") {
- split := strings.Split(ipStr, "%")
- ipStr = split[0]
- zoneID = split[1]
- }
- ipAddr, err := netip.ParseAddr(ipStr)
- if err != nil {
- return netip.IPv4Unspecified(), "", err
- }
- return ipAddr, zoneID, nil
-}
-
-// Match returns true if r matches m.
-func (m MatchRemoteIP) Match(r *http.Request) bool {
- clientIP, zoneID, err := m.getClientIP(r)
- if err != nil {
- m.logger.Error("getting client IP", zap.Error(err))
- return false
- }
- zoneFilter := true
- for i, ipRange := range m.cidrs {
- if ipRange.Contains(clientIP) {
- // Check if there are zone filters assigned and if they match.
- if m.zones[i] == "" || zoneID == m.zones[i] {
- return true
- }
- zoneFilter = false
- }
- }
- if !zoneFilter {
- m.logger.Debug("zone ID from remote did not match", zap.String("zone", zoneID))
- }
- return false
-}
-
// MatchRegexp is an embedable type for matching
// using regular expressions. It adds placeholders
// to the request's replacer.
@@ -1588,8 +1414,6 @@ var (
_ RequestMatcher = (*MatchHeaderRE)(nil)
_ caddy.Provisioner = (*MatchHeaderRE)(nil)
_ RequestMatcher = (*MatchProtocol)(nil)
- _ RequestMatcher = (*MatchRemoteIP)(nil)
- _ caddy.Provisioner = (*MatchRemoteIP)(nil)
_ RequestMatcher = (*MatchNot)(nil)
_ caddy.Provisioner = (*MatchNot)(nil)
_ caddy.Provisioner = (*MatchRegexp)(nil)
@@ -1602,7 +1426,6 @@ var (
_ caddyfile.Unmarshaler = (*MatchHeader)(nil)
_ caddyfile.Unmarshaler = (*MatchHeaderRE)(nil)
_ caddyfile.Unmarshaler = (*MatchProtocol)(nil)
- _ caddyfile.Unmarshaler = (*MatchRemoteIP)(nil)
_ caddyfile.Unmarshaler = (*VarsMatcher)(nil)
_ caddyfile.Unmarshaler = (*MatchVarsRE)(nil)
@@ -1614,7 +1437,6 @@ var (
_ CELLibraryProducer = (*MatchHeader)(nil)
_ CELLibraryProducer = (*MatchHeaderRE)(nil)
_ CELLibraryProducer = (*MatchProtocol)(nil)
- _ CELLibraryProducer = (*MatchRemoteIP)(nil)
// _ CELLibraryProducer = (*VarsMatcher)(nil)
// _ CELLibraryProducer = (*MatchVarsRE)(nil)