Skip to content

Commit

Permalink
new option real-client-ip-header for alternatives to X-Real-IP
Browse files Browse the repository at this point in the history
also remove the recently-added --xheaders option (it was never in a release),
in favor of disabling trust of the X-Real-IP header by setting the new
real-client-ip-header option to a blank/empty string

inspired by oauth2-proxy/oauth2-proxy#503
  • Loading branch information
ploxiln committed May 24, 2020
1 parent 5fb7b7e commit a55f856
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 14 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ Usage of oauth2_proxy:
-provider string: OAuth provider (default "google")
-proxy-prefix string: the url root path that this proxy should be nested under (e.g. /<oauth2>/sign_in) (default "/oauth2")
-proxy-websockets: enables WebSocket proxying (default true)
-real-client-ip-header: HTTP header indicating the actual ip address of the client (blank to disable) (default "X-Real-IP")
-redeem-url string: Token redemption endpoint
-redirect-url string: the OAuth Redirect URL. ie: "https://internalapp.yourcompany.com/oauth2/callback"
-request-logging: Log requests to stdout (default true)
Expand Down
4 changes: 2 additions & 2 deletions contrib/oauth2_proxy.cfg.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
# tls_cert_file = ""
# tls_key_file = ""

## whether to trust the X-Real-IP request header for logging
## can be set to "X-Real-IP" (default), "X-Forwarded-For", "X-ProxyUser-IP", or "" (disabled)
## disable if not running oauth2_proxy behind another reverse-proxy or load-balancer
# xheaders = true
# real_client_ip_header = "X-Real-IP"

## the OAuth Redirect URL
## defaults to "https://" + requested host header + "/oauth2/callback"
Expand Down
14 changes: 14 additions & 0 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,17 @@ func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}

// extract client ip address from configured header
func extractClientIP(req *http.Request, header string) string {
if header != "" {
val := req.Header.Get(header)
if val != "" {
if header == "X-Forwarded-For" {
val = strings.TrimSpace(strings.SplitN(val, ",", 2)[0])
}
return val
}
}
return ""
}
11 changes: 6 additions & 5 deletions logging_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,16 @@ type loggingHandler struct {
writer io.Writer
handler http.Handler
enabled bool
xheaders bool
ipHeader string
logTemplate *template.Template
}

func LoggingHandler(out io.Writer, h http.Handler, enabled bool, xheaders bool, requestLoggingTpl string) http.Handler {
func LoggingHandler(out io.Writer, h http.Handler, enabled bool, ipHeader, requestLoggingTpl string) http.Handler {
return loggingHandler{
writer: out,
handler: h,
enabled: enabled,
xheaders: xheaders,
ipHeader: ipHeader,
logTemplate: template.Must(template.New("request-log").Parse(requestLoggingTpl + "\n")),
}
}
Expand Down Expand Up @@ -149,8 +149,9 @@ func (h loggingHandler) writeLogLine(username, upstream string, req *http.Reques
}

client := req.RemoteAddr
if h.xheaders && req.Header.Get("X-Real-IP") != "" {
client = req.Header.Get("X-Real-IP")
hval := extractClientIP(req, h.ipHeader)
if hval != "" {
client = hval
}

if c, _, err := net.SplitHostPort(client); err == nil {
Expand Down
3 changes: 2 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func mainFlagSet() *flag.FlagSet {
flagSet.Bool("xheaders", true, "Trust X-Real-IP request header (appropriate when behind a reverse proxy)")
flagSet.Bool("request-logging", true, "Log requests to stdout")
flagSet.String("request-logging-format", defaultRequestLoggingFormat, "Template for request log lines")
flagSet.String("real-client-ip-header", "X-Real-IP", "HTTP header indicating the actual ip address of the client (blank to disable)")

flagSet.String("provider", "google", "OAuth provider")
flagSet.String("oidc-issuer-url", "", "OpenID Connect issuer URL (e.g. https://accounts.google.com)")
Expand Down Expand Up @@ -150,7 +151,7 @@ func main() {
}

s := &Server{
Handler: LoggingHandler(os.Stdout, oauthproxy, opts.RequestLogging, opts.XHeaders, opts.RequestLoggingFormat),
Handler: LoggingHandler(os.Stdout, oauthproxy, opts.RequestLogging, opts.RealClientIPHeader, opts.RequestLoggingFormat),
Opts: opts,
}
s.ListenAndServe()
Expand Down
10 changes: 6 additions & 4 deletions oauthproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ type OAuthProxy struct {
PassUserHeaders bool
BasicAuthPassword string
PassAccessToken bool
XHeaders bool
ClientIPHeader string
CookieCipher *cookie.Cipher
skipAuthRegex []string
skipAuthPreflight bool
Expand Down Expand Up @@ -237,7 +237,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
BasicAuthPassword: opts.BasicAuthPassword,
PassAccessToken: opts.PassAccessToken,
SkipProviderButton: opts.SkipProviderButton,
XHeaders: opts.XHeaders,
ClientIPHeader: opts.RealClientIPHeader,
CookieCipher: cipher,
templates: loadTemplates(opts.CustomTemplatesDir),
Footer: opts.Footer,
Expand Down Expand Up @@ -521,8 +521,10 @@ func (p *OAuthProxy) IsWhitelistedPath(path string) (ok bool) {

func (p *OAuthProxy) getRemoteAddr(req *http.Request) (s string) {
s = req.RemoteAddr
if p.XHeaders && req.Header.Get("X-Real-IP") != "" {
s += fmt.Sprintf(" (%q)", req.Header.Get("X-Real-IP"))

hval := extractClientIP(req, p.ClientIPHeader)
if hval != "" {
s += fmt.Sprintf(" (%q)", hval)
}
return
}
Expand Down
21 changes: 19 additions & 2 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ type Options struct {
Scope string `flag:"scope" cfg:"scope"`
ApprovalPrompt string `flag:"approval-prompt" cfg:"approval_prompt"`

XHeaders bool `flag:"xheaders" cfg:"xheaders"`
RequestLogging bool `flag:"request-logging" cfg:"request_logging"`
RequestLoggingFormat string `flag:"request-logging-format" cfg:"request_logging_format"`
RealClientIPHeader string `flag:"real-client-ip-header" cfg:"real_client_ip_header"`

SignatureKey string `flag:"signature-key" cfg:"signature_key" env:"OAUTH2_PROXY_SIGNATURE_KEY"`

Expand Down Expand Up @@ -120,9 +120,9 @@ func NewOptions() *Options {
PassAccessToken: false,
PassHostHeader: true,
ApprovalPrompt: "force",
XHeaders: true,
RequestLogging: true,
RequestLoggingFormat: defaultRequestLoggingFormat,
RealClientIPHeader: "X-Real-IP",
}
}

Expand Down Expand Up @@ -223,6 +223,23 @@ func (o *Options) Validate() error {
msgs = parseSignatureKey(o, msgs)
msgs = validateCookieName(o, msgs)

if o.RealClientIPHeader != "" {
valid := false
realClientIPHeaders := []string{
"X-Real-IP",
"X-Forwarded-For",
"X-ProxyUser-IP",
}
for _, s := range realClientIPHeaders {
if o.RealClientIPHeader == s {
valid = true
}
}
if !valid {
msgs = append(msgs, fmt.Sprintf("unsupported real-client-ip-header %q", o.RealClientIPHeader))
}
}

if len(msgs) != 0 {
return fmt.Errorf("Invalid configuration:\n %s",
strings.Join(msgs, "\n "))
Expand Down

0 comments on commit a55f856

Please sign in to comment.