From c81a76c950e3081224176d226acf40e9b8bd2f33 Mon Sep 17 00:00:00 2001 From: Pierre Tardy Date: Mon, 25 Mar 2019 15:06:41 +0100 Subject: [PATCH] support SSLOffloading by load balancer if pages is run behind SSL load balancer (e.g. ELB), we cannot use the fact that we have an ssl context to decide we redirect to ssl We need to trust the X-Forwarded-Proto header if it does not exist or is malformed, we then use the ssl context heuristic to decide we are https --- acceptance_test.go | 66 +++++++++++++++++++++++++++++++++++++++++++ helpers_test.go | 7 +++++ internal/auth/auth.go | 21 ++++++++------ 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/acceptance_test.go b/acceptance_test.go index eaa32318f..476f474be 100644 --- a/acceptance_test.go +++ b/acceptance_test.go @@ -931,6 +931,72 @@ func TestAccessControlUnderCustomDomain(t *testing.T) { assert.Equal(t, http.StatusOK, authrsp.StatusCode) } +func TestAccessControlUnderCustomDomainWithSSLOffloading(t *testing.T) { + skipUnlessEnabled(t, "not-inplace-chroot") + + testServer := makeGitLabPagesAccessStub(t) + testServer.Start() + defer testServer.Close() + + teardown := RunPagesProcessWithAuthServer(t, *pagesBinary, listeners, "", testServer.URL) + defer teardown() + + rsp, err := GetRedirectPageWithCookieAndProto(t, httpListener, "private.domain.com", "/", "", "https") + require.NoError(t, err) + defer rsp.Body.Close() + + cookie := rsp.Header.Get("Set-Cookie") + + url, err := url.Parse(rsp.Header.Get("Location")) + require.NoError(t, err) + + state := url.Query().Get("state") + assert.Equal(t, url.Query().Get("domain"), "https://private.domain.com") + + pagesrsp, err := GetRedirectPageWithCookieAndProto(t, httpListener, url.Host, url.Path+"?"+url.RawQuery, "", "https") + require.NoError(t, err) + defer pagesrsp.Body.Close() + + pagescookie := pagesrsp.Header.Get("Set-Cookie") + + // Go to auth page with correct state will cause fetching the token + authrsp, err := GetRedirectPageWithCookieAndProto(t, httpListener, "projects.gitlab-example.com", "/auth?code=1&state="+ + state, pagescookie, "https") + + require.NoError(t, err) + defer authrsp.Body.Close() + + url, err = url.Parse(authrsp.Header.Get("Location")) + require.NoError(t, err) + + // Will redirect to custom domain + assert.Equal(t, "https", url.Scheme) + assert.Equal(t, "private.domain.com", url.Host) + assert.Equal(t, "1", url.Query().Get("code")) + assert.Equal(t, state, url.Query().Get("state")) + + // Run auth callback in custom domain + authrsp, err = GetRedirectPageWithCookieAndProto(t, httpListener, "private.domain.com", "/auth?code=1&state="+ + state, cookie, "https") + + require.NoError(t, err) + defer authrsp.Body.Close() + + // Will redirect to the page + cookie = authrsp.Header.Get("Set-Cookie") + assert.Equal(t, http.StatusFound, authrsp.StatusCode) + + url, err = url.Parse(authrsp.Header.Get("Location")) + require.NoError(t, err) + + // Will redirect to custom domain + assert.Equal(t, "https://private.domain.com/", url.String()) + + // Fetch page in custom domain + authrsp, err = GetRedirectPageWithCookieAndProto(t, httpListener, "private.domain.com", "/", cookie, "https") + assert.Equal(t, http.StatusOK, authrsp.StatusCode) +} + func TestAccessControlGroupDomain404RedirectsAuth(t *testing.T) { skipUnlessEnabled(t) teardown := RunPagesProcessWithAuth(t, *pagesBinary, listeners, "") diff --git a/helpers_test.go b/helpers_test.go index 61fa52792..c8954614f 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -315,11 +315,18 @@ func GetRedirectPage(t *testing.T, spec ListenSpec, host, urlsuffix string) (*ht } func GetRedirectPageWithCookie(t *testing.T, spec ListenSpec, host, urlsuffix string, cookie string) (*http.Response, error) { + return GetRedirectPageWithCookieAndProto(t, spec, host, urlsuffix, cookie, "") +} + +func GetRedirectPageWithCookieAndProto(t *testing.T, spec ListenSpec, host, urlsuffix string, cookie string, proto string) (*http.Response, error) { url := spec.URL(urlsuffix) req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } + if proto != "" { + req.Header.Set("X-Forwarded-Proto", proto) + } if cookie != "" { req.Header.Set("Cookie", cookie) } diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 02879568c..acd72227d 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -255,19 +255,24 @@ func (a *Auth) handleProxyingAuth(session *sessions.Session, w http.ResponseWrit return false } - -func getRequestAddress(r *http.Request) string { +func getRequestProto(r *http.Request) string { + if val, ok := r.Header["X-Forwarded-Proto"]; ok { + proto := val[0] + if proto == "http" || proto == "https" { + return proto + } + } if r.TLS != nil { - return "https://" + r.Host + r.RequestURI + return "https" } - return "http://" + r.Host + r.RequestURI + return "http" +} +func getRequestAddress(r *http.Request) string { + return getRequestProto(r) + "://" + r.Host + r.RequestURI } func getRequestDomain(r *http.Request) string { - if r.TLS != nil { - return "https://" + r.Host - } - return "http://" + r.Host + return getRequestProto(r) + "://" + r.Host } func shouldProxyAuth(r *http.Request) bool { -- GitLab