1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-01-26 05:27:28 +02:00
oauth2-proxy/pkg/ip/realclientip.go

102 lines
3.3 KiB
Go
Raw Normal View History

2020-05-23 15:17:41 +01:00
package ip
import (
"fmt"
"net"
"net/http"
"strings"
ipapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/ip"
2020-05-23 15:17:41 +01:00
)
2020-05-23 15:17:41 +01:00
func GetRealClientIPParser(headerKey string) (ipapi.RealClientIPParser, error) {
headerKey = http.CanonicalHeaderKey(headerKey)
switch headerKey {
case http.CanonicalHeaderKey("X-Forwarded-For"), http.CanonicalHeaderKey("X-Real-IP"), http.CanonicalHeaderKey("X-ProxyUser-IP"):
return &xForwardedForClientIPParser{header: headerKey}, nil
}
// TODO: implement the more standardized but more complex `Forwarded` header.
return nil, fmt.Errorf("the http header key (%s) is either invalid or unsupported", headerKey)
}
type xForwardedForClientIPParser struct {
header string
}
// GetRealClientIP obtain the IP address of the end-user (not proxy).
// Parses headers sharing the format as specified by:
// * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For.
// Returns the `<client>` portion specified in the above document.
// Additionally, is capable of parsing IPs with the port included, for v4 in the format "<ip>:<port>" and for v6 in the
// format "[<ip>]:<port>". With-port and without-port formats are seamlessly supported concurrently.
func (p xForwardedForClientIPParser) GetRealClientIP(h http.Header) (net.IP, error) {
var ipStr string
if realIP := h.Get(p.header); realIP != "" {
ipStr = realIP
} else {
return nil, nil
}
// Each successive proxy may append itself, comma separated, to the end of the X-Forwarded-for header.
// Select only the first IP listed, as it is the client IP recorded by the first proxy.
if commaIndex := strings.IndexRune(ipStr, ','); commaIndex != -1 {
ipStr = ipStr[:commaIndex]
}
ipStr = strings.TrimSpace(ipStr)
if ipHost, _, err := net.SplitHostPort(ipStr); err == nil {
ipStr = ipHost
}
ip := net.ParseIP(ipStr)
if ip == nil {
return nil, fmt.Errorf("unable to parse ip (%s) from %s header", ipStr, http.CanonicalHeaderKey(p.header))
}
return ip, nil
}
Implements --trusted-ip option (#552) * Implements --ip-whitelist option * Included IPWhitelist option to allow one-or-more selected CIDR ranges to bypass OAuth2 authentication. * Adds IPWhitelist, a fast lookup table for multiple CIDR ranges. * Renamed IPWhitelist ipCIDRSet * Fixed unessesary pointer usage in ipCIDRSet * Update CHANGELOG.md * Update CHANGELOG.md * Updated to not use err.Error() in printf statements * Imrpoved language for --ip-whitelist descriptions. * Improve IP whitelist options error messages * Clarify options single-host normalization * Wrote a book about ipCIDRSet * Added comment to IsWhitelistedIP in oauthproxy.go * Rewrite oauthproxy test case as table driven * oops * Support whitelisting by low-level remote address * Added more test-cases, improved descriptions * Move ip_cidr_set.go to pkg/ip/net_set.go * Add more whitelist test use cases. * Oops * Use subtests for TestIPWhitelist * Add minimal tests for ip.NetSet * Use switch statment * Renamed ip-whitelist to whitelist-ip * Update documentation with a warning. * Update pkg/apis/options/options.go * Update CHANGELOG.md Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk> * Update pkg/ip/net_set_test.go Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk> * Update pkg/ip/net_set_test.go Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk> * Update pkg/ip/net_set_test.go Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk> * Apply suggestions from code review Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk> * fix fmt * Move ParseIPNet into abstraction * Add warning in case of --reverse-proxy * Update pkg/validation/options_test.go * Rename --whitelist-ip to --trusted-ip * Update oauthproxy.go Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk> * fix Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk>
2020-07-11 12:10:58 +02:00
// GetClientIP obtains the perceived end-user IP address from headers if p != nil else from req.RemoteAddr.
func GetClientIP(p ipapi.RealClientIPParser, req *http.Request) (net.IP, error) {
if p != nil {
return p.GetRealClientIP(req.Header)
}
return getRemoteIP(req)
}
// getRemoteIP obtains the IP of the low-level connected network host
func getRemoteIP(req *http.Request) (net.IP, error) {
if ipStr, _, err := net.SplitHostPort(req.RemoteAddr); err != nil {
return nil, fmt.Errorf("unable to get ip and port from http.RemoteAddr (%s)", req.RemoteAddr)
} else if ip := net.ParseIP(ipStr); ip != nil {
return ip, nil
} else {
return nil, fmt.Errorf("unable to parse ip (%s)", ipStr)
}
}
2020-04-13 13:50:34 +01:00
// GetClientString obtains the human readable string of the remote IP and optionally the real client IP if available
2020-05-23 15:17:41 +01:00
func GetClientString(p ipapi.RealClientIPParser, req *http.Request, full bool) (s string) {
var realClientIPStr string
if p != nil {
2020-04-13 13:50:34 +01:00
if realClientIP, err := p.GetRealClientIP(req.Header); err == nil && realClientIP != nil {
realClientIPStr = realClientIP.String()
}
}
var remoteIPStr string
if remoteIP, err := getRemoteIP(req); err == nil {
remoteIPStr = remoteIP.String()
}
if !full && realClientIPStr != "" {
return realClientIPStr
}
if full && realClientIPStr != "" {
return fmt.Sprintf("%s (%s)", remoteIPStr, realClientIPStr)
}
return remoteIPStr
}