From 20a152261c6712c1ac7e2de8fbc24231be635b92 Mon Sep 17 00:00:00 2001 From: John Boxall Date: Sun, 30 Nov 2014 17:12:33 -0800 Subject: [PATCH 1/4] Adds failing test for using upstream Host header. --- oauthproxy.go | 6 +++++- oauthproxy_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 oauthproxy_test.go diff --git a/oauthproxy.go b/oauthproxy.go index 7951a2a..16c0dad 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -46,6 +46,10 @@ type OauthProxy struct { compiledRegex []*regexp.Regexp } +func NewReverseProxy(target *url.URL) (proxy *httputil.ReverseProxy) { + return httputil.NewSingleHostReverseProxy(target) +} + func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy { login, _ := url.Parse("https://accounts.google.com/o/oauth2/auth") redeem, _ := url.Parse("https://accounts.google.com/o/oauth2/token") @@ -54,7 +58,7 @@ func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy { path := u.Path u.Path = "" log.Printf("mapping path %q => upstream %q", path, u) - serveMux.Handle(path, httputil.NewSingleHostReverseProxy(u)) + serveMux.Handle(path, NewReverseProxy(u)) } for _, u := range opts.CompiledRegex { log.Printf("compiled skip-auth-regex => %q", u) diff --git a/oauthproxy_test.go b/oauthproxy_test.go new file mode 100644 index 0000000..c4e68b3 --- /dev/null +++ b/oauthproxy_test.go @@ -0,0 +1,36 @@ +package main + +import ( + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "net/url" + "testing" +) + +func TestNewReverseProxy(t *testing.T) { + backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + hostname, _, _ := net.SplitHostPort(r.Host) + w.Write([]byte(hostname)) + })) + defer backend.Close() + + backendURL, _ := url.Parse(backend.URL) + backendHostname := "upstream.127.0.0.1.xip.io" + _, backendPort, _ := net.SplitHostPort(backendURL.Host) + backendHost := net.JoinHostPort(backendHostname, backendPort) + proxyURL, _ := url.Parse(backendURL.Scheme + "://" + backendHost + "/") + + proxyHandler := NewReverseProxy(proxyURL) + frontend := httptest.NewServer(proxyHandler) + defer frontend.Close() + + getReq, _ := http.NewRequest("GET", frontend.URL, nil) + res, _ := http.DefaultClient.Do(getReq) + bodyBytes, _ := ioutil.ReadAll(res.Body) + if g, e := string(bodyBytes), backendHostname; g != e { + t.Errorf("got body %q; expected %q", g, e) + } +} From 24ef5555479802a9cebda04433dd7955562c4e23 Mon Sep 17 00:00:00 2001 From: John Boxall Date: Sun, 30 Nov 2014 17:13:45 -0800 Subject: [PATCH 2/4] Requests are proxied to the Host specified by the target. --- oauthproxy.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/oauthproxy.go b/oauthproxy.go index 16c0dad..0705b6d 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -47,7 +47,13 @@ type OauthProxy struct { } func NewReverseProxy(target *url.URL) (proxy *httputil.ReverseProxy) { - return httputil.NewSingleHostReverseProxy(target) + proxy = httputil.NewSingleHostReverseProxy(target) + director := proxy.Director + proxy.Director = func(req *http.Request) { + director(req) + req.Host = target.Host + } + return proxy } func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy { From 263e16eeeac3efadbfd7da44cdffda8eb39ae8ea Mon Sep 17 00:00:00 2001 From: Jehiah Czebotar Date: Tue, 17 Mar 2015 15:15:15 -0400 Subject: [PATCH 3/4] add --proxy-host-header option --- README.md | 1 + contrib/google_auth_proxy.cfg.example | 3 +++ main.go | 1 + oauthproxy.go | 21 +++++++++------ oauthproxy_test.go | 3 ++- options.go | 39 +++++++++++++++------------ options_test.go | 6 ++--- 7 files changed, 45 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index f9c752f..f7de4e8 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Usage of google_auth_proxy: -htpasswd-file="": additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -s" for SHA encryption -http-address="127.0.0.1:4180": [http://]: or unix:// to listen on for HTTP clients -pass-basic-auth=true: pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream + -pass-host-header=true: pass the request Host Header to upstream -redirect-url="": the OAuth Redirect URL. ie: "https://internalapp.yourcompany.com/oauth2/callback" -skip-auth-regex=: bypass authentication for requests path's that match (may be given multiple times) -upstream=: the http url(s) of the upstream endpoint. If multiple, routing is based on path diff --git a/contrib/google_auth_proxy.cfg.example b/contrib/google_auth_proxy.cfg.example index 7034a58..3158645 100644 --- a/contrib/google_auth_proxy.cfg.example +++ b/contrib/google_auth_proxy.cfg.example @@ -14,6 +14,9 @@ ## pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream # pass_basic_auth = true +## pass the request Host Header to upstream +## when disabled the upstream Host is used as the Host Header +# pass_host_header = true ## Google Apps Domains to allow authentication for # google_apps_domains = [ diff --git a/main.go b/main.go index 747b7f1..41a50e1 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,7 @@ func main() { flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"") flagSet.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint. If multiple, routing is based on path") flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream") + flagSet.Bool("pass-host-header", true, "pass the request Host Header to upstream") flagSet.Var(&skipAuthRegex, "skip-auth-regex", "bypass authentication for requests path's that match (may be given multiple times)") flagSet.Var(&googleAppsDomains, "google-apps-domain", "authenticate against the given Google apps domain (may be given multiple times)") diff --git a/oauthproxy.go b/oauthproxy.go index 0705b6d..1ead0ff 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -47,13 +47,14 @@ type OauthProxy struct { } func NewReverseProxy(target *url.URL) (proxy *httputil.ReverseProxy) { - proxy = httputil.NewSingleHostReverseProxy(target) - director := proxy.Director - proxy.Director = func(req *http.Request) { - director(req) - req.Host = target.Host - } - return proxy + return httputil.NewSingleHostReverseProxy(target) +} +func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) { + director := proxy.Director + proxy.Director = func(req *http.Request) { + director(req) + req.Host = target.Host + } } func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy { @@ -64,7 +65,11 @@ func NewOauthProxy(opts *Options, validator func(string) bool) *OauthProxy { path := u.Path u.Path = "" log.Printf("mapping path %q => upstream %q", path, u) - serveMux.Handle(path, NewReverseProxy(u)) + proxy := NewReverseProxy(u) + if !opts.PassHostHeader { + setProxyUpstreamHostHeader(proxy, u) + } + serveMux.Handle(path, proxy) } for _, u := range opts.CompiledRegex { log.Printf("compiled skip-auth-regex => %q", u) diff --git a/oauthproxy_test.go b/oauthproxy_test.go index c4e68b3..c4e3836 100644 --- a/oauthproxy_test.go +++ b/oauthproxy_test.go @@ -12,7 +12,7 @@ import ( func TestNewReverseProxy(t *testing.T) { backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) - hostname, _, _ := net.SplitHostPort(r.Host) + hostname, _, _ := net.SplitHostPort(r.Host) w.Write([]byte(hostname)) })) defer backend.Close() @@ -24,6 +24,7 @@ func TestNewReverseProxy(t *testing.T) { proxyURL, _ := url.Parse(backendURL.Scheme + "://" + backendHost + "/") proxyHandler := NewReverseProxy(proxyURL) + setProxyUpstreamHostHeader(proxyHandler, proxyURL) frontend := httptest.NewServer(proxyHandler) defer frontend.Close() diff --git a/options.go b/options.go index 7ef737a..df2b3ca 100644 --- a/options.go +++ b/options.go @@ -10,22 +10,26 @@ import ( // Configuration Options that can be set by Command Line Flag, or Config File type Options struct { - HttpAddress string `flag:"http-address" cfg:"http_address"` - RedirectUrl string `flag:"redirect-url" cfg:"redirect_url"` - ClientID string `flag:"client-id" cfg:"client_id" env:"GOOGLE_AUTH_PROXY_CLIENT_ID"` - ClientSecret string `flag:"client-secret" cfg:"client_secret" env:"GOOGLE_AUTH_PROXY_CLIENT_SECRET"` - PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth"` - HtpasswdFile string `flag:"htpasswd-file" cfg:"htpasswd_file"` - DisplayHtpasswdForm bool `flag:"display-htpasswd-form" cfg:"display_htpasswd_form"` - CookieSecret string `flag:"cookie-secret" cfg:"cookie_secret" env:"GOOGLE_AUTH_PROXY_COOKIE_SECRET"` - CookieDomain string `flag:"cookie-domain" cfg:"cookie_domain" env:"GOOGLE_AUTH_PROXY_COOKIE_DOMAIN"` - CookieExpire time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"GOOGLE_AUTH_PROXY_COOKIE_EXPIRE"` - CookieHttpsOnly bool `flag:"cookie-https-only" cfg:"cookie_https_only"` - CookieHttpOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly"` - AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"` - GoogleAppsDomains []string `flag:"google-apps-domain" cfg:"google_apps_domains"` - Upstreams []string `flag:"upstream" cfg:"upstreams"` - SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"` + HttpAddress string `flag:"http-address" cfg:"http_address"` + RedirectUrl string `flag:"redirect-url" cfg:"redirect_url"` + ClientID string `flag:"client-id" cfg:"client_id" env:"GOOGLE_AUTH_PROXY_CLIENT_ID"` + ClientSecret string `flag:"client-secret" cfg:"client_secret" env:"GOOGLE_AUTH_PROXY_CLIENT_SECRET"` + + AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"` + GoogleAppsDomains []string `flag:"google-apps-domain" cfg:"google_apps_domains"` + HtpasswdFile string `flag:"htpasswd-file" cfg:"htpasswd_file"` + DisplayHtpasswdForm bool `flag:"display-htpasswd-form" cfg:"display_htpasswd_form"` + + CookieSecret string `flag:"cookie-secret" cfg:"cookie_secret" env:"GOOGLE_AUTH_PROXY_COOKIE_SECRET"` + CookieDomain string `flag:"cookie-domain" cfg:"cookie_domain" env:"GOOGLE_AUTH_PROXY_COOKIE_DOMAIN"` + CookieExpire time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"GOOGLE_AUTH_PROXY_COOKIE_EXPIRE"` + CookieHttpsOnly bool `flag:"cookie-https-only" cfg:"cookie_https_only"` + CookieHttpOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly"` + + Upstreams []string `flag:"upstream" cfg:"upstreams"` + SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"` + PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth"` + PassHostHeader bool `flag:"pass-host-header" cfg:"pass_host_header"` // internal values that are set after config validation redirectUrl *url.URL @@ -39,8 +43,9 @@ func NewOptions() *Options { DisplayHtpasswdForm: true, CookieHttpsOnly: true, CookieHttpOnly: true, - PassBasicAuth: true, CookieExpire: time.Duration(168) * time.Hour, + PassBasicAuth: true, + PassHostHeader: true, } } diff --git a/options_test.go b/options_test.go index 37ab9d1..47c711a 100644 --- a/options_test.go +++ b/options_test.go @@ -1,14 +1,14 @@ package main import ( + "net/url" "strings" "testing" - "net/url" "github.com/bmizerany/assert" ) -func testOptions() (*Options) { +func testOptions() *Options { o := NewOptions() o.Upstreams = append(o.Upstreams, "http://127.0.0.1:8080/") o.CookieSecret = "foobar" @@ -17,7 +17,7 @@ func testOptions() (*Options) { return o } -func errorMsg(msgs []string)(string) { +func errorMsg(msgs []string) string { result := make([]string, 0) result = append(result, "Invalid configuration:") result = append(result, msgs...) From a162ee809eacbe54f3886d6b45cd3af084839218 Mon Sep 17 00:00:00 2001 From: Jehiah Czebotar Date: Tue, 17 Mar 2015 16:02:21 -0400 Subject: [PATCH 4/4] bump travis Go versions --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 09544e6..91cd4a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go go: - - 1.2.2 - 1.3.3 + - 1.4.2 script: - curl -s https://raw.githubusercontent.com/pote/gpm/v1.3.1/bin/gpm > gpm - chmod +x gpm