diff options
author | Matthew Holt <[email protected]> | 2020-12-10 16:09:30 -0700 |
---|---|---|
committer | Matthew Holt <[email protected]> | 2020-12-10 16:09:30 -0700 |
commit | deedf8abb036bdc096360bd6f06df17a6cff9799 (patch) | |
tree | 19505f3043a3d0764db1b5bcec5f0c4ffe6a820e /modules/caddyhttp/matchers.go | |
parent | 63bda6a0dc97e02d32865c31b5e46d2ead86ac7b (diff) | |
download | caddy-deedf8abb036bdc096360bd6f06df17a6cff9799.tar.gz caddy-deedf8abb036bdc096360bd6f06df17a6cff9799.zip |
caddyhttp: Optionally use forwarded IP for remote_ip matcherv2.3.0-rc.1
The remote_ip matcher was reading the X-Forwarded-For header by default, but this behavior was not documented in anything that was released. This is also a less secure default, as it is trivially easy to spoof request headers. Reading IPs from that header should be optional, and it should not be the default.
This is technically a breaking change, but anyone relying on the undocumented behavior was just doing so by coincidence/luck up to this point since it was never in any released documentation. We'll still add a mention in the release notes about this.
Diffstat (limited to 'modules/caddyhttp/matchers.go')
-rw-r--r-- | modules/caddyhttp/matchers.go | 35 |
1 files changed, 22 insertions, 13 deletions
diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index b9ddc3368..b77677bff 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -105,12 +105,16 @@ type ( MatchProtocol string // MatchRemoteIP matches requests by client IP (or CIDR range). - // If the X-Forwarded-For header is set, the first IP in that list - // is used as the reference IP; otherwise, the remote IP of the - // connection is the reference. 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 []*net.IPNet logger *zap.Logger } @@ -795,7 +799,16 @@ func (MatchRemoteIP) CaddyModule() caddy.ModuleInfo { // UnmarshalCaddyfile implements caddyfile.Unmarshaler. func (m *MatchRemoteIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { for d.Next() { - m.Ranges = append(m.Ranges, d.RemainingArgs()...) + 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 + } + m.Ranges = append(m.Ranges, d.Val()) + } if d.NextBlock(0) { return d.Err("malformed remote_ip matcher: blocks are not supported") } @@ -829,24 +842,20 @@ func (m *MatchRemoteIP) Provision(ctx caddy.Context) error { } func (m MatchRemoteIP) getClientIP(r *http.Request) (net.IP, error) { - var remote string - if fwdFor := r.Header.Get("X-Forwarded-For"); fwdFor != "" { - remote = strings.TrimSpace(strings.Split(fwdFor, ",")[0]) - } - if remote == "" { - remote = r.RemoteAddr + remote := r.RemoteAddr + 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 } - ip := net.ParseIP(ipStr) if ip == nil { return nil, fmt.Errorf("invalid client IP address: %s", ipStr) } - return ip, nil } |