Skip to content

Commit

Permalink
feat(clientip): improve validation
Browse files Browse the repository at this point in the history
  • Loading branch information
tigerwill90 committed Dec 17, 2024
1 parent 49bf7a5 commit 05a6a33
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 78 deletions.
48 changes: 24 additions & 24 deletions clientip/clientip.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ var (
ErrRightmostTrustedRange = errors.New("rightmost trusted range resolver")
)

// Avoid allocating those errors each time since it may happen a lot on adversary header.
var (
errLeftmostNonPrivate = fmt.Errorf("%w: unable to find a valid or non-private IP", ErrLeftmostNonPrivate)
)

// TrustedIPRange returns a set of trusted IP ranges.
// Implementations of this interface must be thread-safe as it will be invoked
// whenever the client IP needs to be resolved, potentially from multiple goroutines.
Expand Down Expand Up @@ -61,18 +66,6 @@ const (
ForwardedKey
)

// Must is a helper that wraps a call to a function returning (R, error)
// and panics if the error is non-nil. It is intended for use in variable initializations
// such as:
//
// var r = clientip.Must(clientip.NewSingleIPHeader("True-Client-IP"))
func Must[R fox.ClientIPResolver](resolver R, err error) R {
if err != nil {
panic(err)
}
return resolver
}

// Chain attempts to use the given resolvers in order. If the first one returns an error, the second one is
// tried, and so on, until a good IP is found or the resolvers are exhausted. A common use for this is if a server is
// both directly connected to the internet and expecting a header to check. It might be called like:
Expand Down Expand Up @@ -180,6 +173,9 @@ func NewLeftmostNonPrivate(key HeaderKey, limit uint, opts ...BlacklistRangeOpti
if key > 1 {
return LeftmostNonPrivate{}, errors.New("invalid header key")
}
if limit == 0 {
return LeftmostNonPrivate{}, errors.New("invalid limit: expect greater than zero")
}

cfg := new(config)
for _, opt := range opts {
Expand All @@ -193,9 +189,6 @@ func NewLeftmostNonPrivate(key HeaderKey, limit uint, opts ...BlacklistRangeOpti
}, nil
}

// Avoid allocating this error since it may happen a lot on adversary header.
var errLeftmostNonPrivate = fmt.Errorf("%w: unable to find a valid or non-private IP", ErrLeftmostNonPrivate)

// ClientIP derives the client IP using the [LeftmostNonPrivate] resolver. The returned [net.IPAddr] may contain a
// zone identifier. If no valid IP can be derived, an error returned.
func (s LeftmostNonPrivate) ClientIP(c fox.Context) (*net.IPAddr, error) {
Expand Down Expand Up @@ -342,15 +335,6 @@ func (s RightmostTrustedRange) ClientIP(c fox.Context) (*net.IPAddr, error) {
return nil, fmt.Errorf("%w: unable to find a valid IP address", ErrRightmostTrustedRange)
}

// MustParseIPAddr panics if [ParseIPAddr] fails.
func MustParseIPAddr(ipStr string) *net.IPAddr {
ipAddr, err := ParseIPAddr(ipStr)
if err != nil {
panic(fmt.Sprintf("ParseIPAddr failed: %v", err))
}
return ipAddr
}

// ParseIPAddr safely parses the given string into a [net.IPAddr]. It also returns an error for unspecified (like "::")
// and zero-value addresses (like "0.0.0.0"). These are nominally valid IPs ([net.ParseIP] will accept them), but they
// are never valid "real" client IPs.
Expand Down Expand Up @@ -594,6 +578,15 @@ func parseForwardedListItem(fwd string) *net.IPAddr {
return ipAddr
}

// mustParseIPAddr panics if [ParseIPAddr] fails.
func mustParseIPAddr(ipStr string) *net.IPAddr {
ipAddr, err := ParseIPAddr(ipStr)
if err != nil {
panic(fmt.Sprintf("ParseIPAddr failed: %v", err))
}
return ipAddr
}

// mustParseCIDR panics if net.ParseCIDR fails
func mustParseCIDR(s string) net.IPNet {
_, ipNet, err := net.ParseCIDR(s)
Expand All @@ -603,6 +596,13 @@ func mustParseCIDR(s string) net.IPNet {
return *ipNet
}

func must[T fox.ClientIPResolver](s T, err error) T {
if err != nil {
panic(err)
}
return s
}

// privateAndLocalRanges net.IPNets that are loopback, private, link local, default unicast.
// Based on https://github.com/wader/filtertransport/blob/bdd9e61eee7804e94ceb927c896b59920345c6e4/filter.go#L36-L64
// which is based on https://github.com/letsencrypt/boulder/blob/master/bdns/dns.go
Expand Down
Loading

0 comments on commit 05a6a33

Please sign in to comment.