From 05e9974570a08df14b1162a1e98315d4ee9ec2ee Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Mon, 27 Mar 2023 16:22:59 -0400 Subject: 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 --- modules/caddyhttp/matchers.go | 178 ------------------------------------------ 1 file changed, 178 deletions(-) (limited to 'modules/caddyhttp/matchers.go') 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) -- cgit v1.2.3 From 66114cb155f2a975ecdc9f3d2d89a9df1142791a Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Sat, 8 Jul 2023 13:42:13 -0600 Subject: caddyhttp: Trim dot/space only on Windows (fix #5613) Follow-up to #2917. Path matcher needs to trim dots and spaces but only on Windows. --- modules/caddyhttp/matchers.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'modules/caddyhttp/matchers.go') diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index f5f9a0f..fb84875 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -25,6 +25,7 @@ import ( "path" "reflect" "regexp" + "runtime" "sort" "strconv" "strings" @@ -395,7 +396,9 @@ func (m MatchPath) Match(r *http.Request) bool { // security risk (cry) if PHP files end up being served // as static files, exposing the source code, instead of // being matched by *.php to be treated as PHP scripts. - reqPath = strings.TrimRight(reqPath, ". ") + if runtime.GOOS == "windows" { // issue #5613 + reqPath = strings.TrimRight(reqPath, ". ") + } repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) -- cgit v1.2.3 From b32f265ecad60404c3818cc9d42e367a8e4eb7d4 Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Tue, 8 Aug 2023 03:40:31 +0800 Subject: ci: Use gofumpt to format code (#5707) --- modules/caddyhttp/matchers.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'modules/caddyhttp/matchers.go') diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index fb84875..f40802f 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -1392,9 +1392,7 @@ func ParseCaddyfileNestedMatcherSet(d *caddyfile.Dispenser) (caddy.ModuleMap, er return matcherSet, nil } -var ( - wordRE = regexp.MustCompile(`\w+`) -) +var wordRE = regexp.MustCompile(`\w+`) const regexpPlaceholderPrefix = "http.regexp" -- cgit v1.2.3 From d6f86cccf5fa5b4eb30141da390cf2439746c5da Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Mon, 14 Aug 2023 23:41:15 +0800 Subject: ci: use gci linter (#5708) * use gofmput to format code * use gci to format imports * reconfigure gci * linter autofixes * rearrange imports a little * export GOOS=windows golangci-lint run ./... --fix --- modules/caddyhttp/matchers.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'modules/caddyhttp/matchers.go') diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index f40802f..b385979 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -30,11 +30,12 @@ import ( "strconv" "strings" - "github.com/caddyserver/caddy/v2" - "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/google/cel-go/cel" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" + + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" ) type ( -- cgit v1.2.3