Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new option real-client-ip-header for alternatives to X-Real-IP #55

Merged
merged 1 commit into from
May 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,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 All @@ -326,7 +327,6 @@ Usage of oauth2_proxy:
-validate-url string: Access token validation endpoint
-version: print version string
-whitelist-domain value: allowed domain for redirection after authentication, leading '.' allows subdomains (may be given multiple times)
-xheaders: Trust X-Real-IP request header (appropriate when behind a reverse proxy) (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
2 changes: 1 addition & 1 deletion logging_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestLoggingHandler_ServeHTTP(t *testing.T) {
w.Write([]byte("test"))
}

h := LoggingHandler(buf, http.HandlerFunc(handler), true, true, test.Format)
h := LoggingHandler(buf, http.HandlerFunc(handler), true, "", test.Format)
r, _ := http.NewRequest("GET", "/foo/bar", nil)
r.RemoteAddr = "127.0.0.1"
r.Host = "test-server"
Expand Down
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ func mainFlagSet() *flag.FlagSet {
flagSet.Bool("cookie-secure", true, "set secure (HTTPS) cookie flag")
flagSet.Bool("cookie-httponly", true, "set HttpOnly cookie flag")

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 @@ -151,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 @@ -83,9 +83,9 @@ type Options struct {
Prompt string `flag:"prompt" cfg:"prompt"`
ApprovalPrompt string `flag:"approval-prompt" cfg:"approval_prompt"` // Deprecated by OIDC 1.0

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 @@ -122,9 +122,9 @@ func NewOptions() *Options {
PassHostHeader: true,
Prompt: "", // Change to "login" when ApprovalPrompt deprecated/removed
ApprovalPrompt: "force",
XHeaders: true,
RequestLogging: true,
RequestLoggingFormat: defaultRequestLoggingFormat,
RealClientIPHeader: "X-Real-IP",
}
}

Expand Down Expand Up @@ -225,6 +225,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