From 8fd519decab9c0bf5b4c00f2ab8cdb40efac5130 Mon Sep 17 00:00:00 2001 From: ngala Date: Mon, 23 Oct 2023 12:01:18 +0530 Subject: [PATCH 01/19] Add logs to investiage NGINX config Changelog: changed --- internal/auth/auth.go | 4 ++++ internal/auth/session.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index dcc81eee2..23c939c4e 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -91,10 +91,12 @@ func (a *Auth) TryAuthenticate(w http.ResponseWriter, r *http.Request, domains s return false } + logRequest(r).Debug(">>>Inside TryAuthenticate: before checkSession") session, err := a.checkSession(w, r) if err != nil { return true } + logRequest(r).WithField("Session state", session.Values["state"]).Debug(">>>Inside TryAuthenticate: after checkSession") // Request is for auth if r.URL.Path != callbackPath { @@ -334,6 +336,7 @@ func shouldProxyCallbackToCustomDomain(session *hostSession) bool { func validateState(r *http.Request, session *hostSession) bool { state := r.URL.Query().Get("state") + logRequest(r).WithField("Session state", session.Values["state"]).Debug(">>>Inside validateState") if state == "" { // No state param return false @@ -422,6 +425,7 @@ func (a *Auth) checkTokenExists(session *hostSession, w http.ResponseWriter, r * if session.Values["state"] == nil { //Generate state hash and store requested address session.Values["state"] = base64.URLEncoding.EncodeToString(securecookie.GenerateRandomKey(16)) + logRequest(r).WithField("Session state", session.Values["state"]).Debug(">>>Inside checkTokenExists session state nil") } session.Values["uri"] = getRequestAddress(r) diff --git a/internal/auth/session.go b/internal/auth/session.go index 5bfc8e034..e0f081399 100644 --- a/internal/auth/session.go +++ b/internal/auth/session.go @@ -17,6 +17,8 @@ type hostSession struct { const sessionHostKey = "_session_host" func (s *hostSession) Save(r *http.Request, w http.ResponseWriter) error { + logRequest(r).WithField("_session_host", s.Session.Values[sessionHostKey]).Debug(">>>Session save") + logRequest(r).WithField("request host", r.Host).Debug(">>>Session save") s.Session.Values[sessionHostKey] = r.Host return s.Session.Save(r, w) @@ -32,6 +34,8 @@ func (a *Auth) getSessionFromStore(r *http.Request) (*hostSession, error) { session.Options.Secure = request.IsHTTPS(r) session.Options.MaxAge = int(a.cookieSessionTimeout.Seconds()) + logRequest(r).WithField("_session_host", session.Values[sessionHostKey]).Debug(">>>Inside getSessionFromStore: before host compare") + logRequest(r).WithField("request host", r.Host).Debug(">>>Inside getSessionFromStore: before host compare") if session.Values[sessionHostKey] == nil || session.Values[sessionHostKey] != r.Host { session.Values = make(map[interface{}]interface{}) } -- GitLab From 6f64da9b39c6d45104b9d1dc9e1fcac213f08190 Mon Sep 17 00:00:00 2001 From: ngala Date: Wed, 25 Oct 2023 13:59:34 +0530 Subject: [PATCH 02/19] Edited session.values reset condition --- internal/auth/session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/auth/session.go b/internal/auth/session.go index e0f081399..fd4e1637c 100644 --- a/internal/auth/session.go +++ b/internal/auth/session.go @@ -36,7 +36,7 @@ func (a *Auth) getSessionFromStore(r *http.Request) (*hostSession, error) { logRequest(r).WithField("_session_host", session.Values[sessionHostKey]).Debug(">>>Inside getSessionFromStore: before host compare") logRequest(r).WithField("request host", r.Host).Debug(">>>Inside getSessionFromStore: before host compare") - if session.Values[sessionHostKey] == nil || session.Values[sessionHostKey] != r.Host { + if session.Values[sessionHostKey] == nil || (session.Values[sessionHostKey] != r.Host && session.Values[sessionHostKey] != "projects.34.41.14.22.nip.io") { session.Values = make(map[interface{}]interface{}) } } -- GitLab From 3eb4213617891c6505347f34325659f55faecbfa Mon Sep 17 00:00:00 2001 From: ngala Date: Wed, 25 Oct 2023 17:32:15 +0530 Subject: [PATCH 03/19] comment session value reset logic --- internal/auth/auth.go | 8 ++++---- internal/auth/session.go | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 23c939c4e..f411b9d55 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -91,12 +91,12 @@ func (a *Auth) TryAuthenticate(w http.ResponseWriter, r *http.Request, domains s return false } - logRequest(r).Debug(">>>Inside TryAuthenticate: before checkSession") + logRequest(r).Debug("> Inside TryAuthenticate: before checkSession") session, err := a.checkSession(w, r) if err != nil { return true } - logRequest(r).WithField("Session state", session.Values["state"]).Debug(">>>Inside TryAuthenticate: after checkSession") + logRequest(r).WithField("Session state", session.Values["state"]).Debug("> Inside TryAuthenticate: after checkSession") // Request is for auth if r.URL.Path != callbackPath { @@ -336,7 +336,7 @@ func shouldProxyCallbackToCustomDomain(session *hostSession) bool { func validateState(r *http.Request, session *hostSession) bool { state := r.URL.Query().Get("state") - logRequest(r).WithField("Session state", session.Values["state"]).Debug(">>>Inside validateState") + logRequest(r).WithField("Session state", session.Values["state"]).Debug("> Inside validateState") if state == "" { // No state param return false @@ -425,7 +425,7 @@ func (a *Auth) checkTokenExists(session *hostSession, w http.ResponseWriter, r * if session.Values["state"] == nil { //Generate state hash and store requested address session.Values["state"] = base64.URLEncoding.EncodeToString(securecookie.GenerateRandomKey(16)) - logRequest(r).WithField("Session state", session.Values["state"]).Debug(">>>Inside checkTokenExists session state nil") + logRequest(r).WithField("Session state", session.Values["state"]).Debug("> Inside checkTokenExists session state nil") } session.Values["uri"] = getRequestAddress(r) diff --git a/internal/auth/session.go b/internal/auth/session.go index fd4e1637c..c667f7105 100644 --- a/internal/auth/session.go +++ b/internal/auth/session.go @@ -17,8 +17,8 @@ type hostSession struct { const sessionHostKey = "_session_host" func (s *hostSession) Save(r *http.Request, w http.ResponseWriter) error { - logRequest(r).WithField("_session_host", s.Session.Values[sessionHostKey]).Debug(">>>Session save") - logRequest(r).WithField("request host", r.Host).Debug(">>>Session save") + logRequest(r).WithField("_session_host", s.Session.Values[sessionHostKey]).Debug("> Session save") + logRequest(r).WithField("request host", r.Host).Debug("> Session save") s.Session.Values[sessionHostKey] = r.Host return s.Session.Save(r, w) @@ -34,11 +34,11 @@ func (a *Auth) getSessionFromStore(r *http.Request) (*hostSession, error) { session.Options.Secure = request.IsHTTPS(r) session.Options.MaxAge = int(a.cookieSessionTimeout.Seconds()) - logRequest(r).WithField("_session_host", session.Values[sessionHostKey]).Debug(">>>Inside getSessionFromStore: before host compare") - logRequest(r).WithField("request host", r.Host).Debug(">>>Inside getSessionFromStore: before host compare") - if session.Values[sessionHostKey] == nil || (session.Values[sessionHostKey] != r.Host && session.Values[sessionHostKey] != "projects.34.41.14.22.nip.io") { + logRequest(r).WithField("_session_host", session.Values[sessionHostKey]).Debug("> Inside getSessionFromStore: before host compare") + logRequest(r).WithField("request host", r.Host).Debug("> Inside getSessionFromStore: before host compare") + /*if session.Values[sessionHostKey] == nil || session.Values[sessionHostKey] != r.Host { session.Values = make(map[interface{}]interface{}) - } + }*/ } return &hostSession{session}, err -- GitLab From 15dd0fe4973ddc65d7de6c150b8a372188a39da3 Mon Sep 17 00:00:00 2001 From: ngala Date: Fri, 27 Oct 2023 14:12:47 +0530 Subject: [PATCH 04/19] update cookie store name --- internal/auth/session.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/auth/session.go b/internal/auth/session.go index c667f7105..5b75fefeb 100644 --- a/internal/auth/session.go +++ b/internal/auth/session.go @@ -2,6 +2,7 @@ package auth import ( "net/http" + "strings" "github.com/gorilla/sessions" @@ -25,7 +26,8 @@ func (s *hostSession) Save(r *http.Request, w http.ResponseWriter) error { } func (a *Auth) getSessionFromStore(r *http.Request) (*hostSession, error) { - session, err := a.store.Get(r, "gitlab-pages") + namespace := "-" + r.Host[:strings.IndexByte(r.Host, '.')] + session, err := a.store.Get(r, "gitlab-pages"+namespace) if session != nil { // Cookie just for this domain @@ -36,9 +38,9 @@ func (a *Auth) getSessionFromStore(r *http.Request) (*hostSession, error) { logRequest(r).WithField("_session_host", session.Values[sessionHostKey]).Debug("> Inside getSessionFromStore: before host compare") logRequest(r).WithField("request host", r.Host).Debug("> Inside getSessionFromStore: before host compare") - /*if session.Values[sessionHostKey] == nil || session.Values[sessionHostKey] != r.Host { + if session.Values[sessionHostKey] == nil || session.Values[sessionHostKey] != r.Host { session.Values = make(map[interface{}]interface{}) - }*/ + } } return &hostSession{session}, err -- GitLab From d1c2e83cb9ea35bced4ab3c72e36e21c42b0b3dd Mon Sep 17 00:00:00 2001 From: ngala Date: Fri, 27 Oct 2023 14:20:19 +0530 Subject: [PATCH 05/19] update session Options Path to include namespace --- internal/auth/session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/auth/session.go b/internal/auth/session.go index 5b75fefeb..02d9ba697 100644 --- a/internal/auth/session.go +++ b/internal/auth/session.go @@ -31,7 +31,7 @@ func (a *Auth) getSessionFromStore(r *http.Request) (*hostSession, error) { if session != nil { // Cookie just for this domain - session.Options.Path = "/" + session.Options.Path = "/" + namespace session.Options.HttpOnly = true session.Options.Secure = request.IsHTTPS(r) session.Options.MaxAge = int(a.cookieSessionTimeout.Seconds()) -- GitLab From 39b67a87a1fee6e649798e1752ad1fc5246ce4a7 Mon Sep 17 00:00:00 2001 From: ngala Date: Fri, 27 Oct 2023 14:23:31 +0530 Subject: [PATCH 06/19] update --- internal/auth/session.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/auth/session.go b/internal/auth/session.go index 02d9ba697..60b96b4a1 100644 --- a/internal/auth/session.go +++ b/internal/auth/session.go @@ -26,8 +26,8 @@ func (s *hostSession) Save(r *http.Request, w http.ResponseWriter) error { } func (a *Auth) getSessionFromStore(r *http.Request) (*hostSession, error) { - namespace := "-" + r.Host[:strings.IndexByte(r.Host, '.')] - session, err := a.store.Get(r, "gitlab-pages"+namespace) + namespace := r.Host[:strings.IndexByte(r.Host, '.')] + session, err := a.store.Get(r, "gitlab-pages"+"-"+namespace) if session != nil { // Cookie just for this domain -- GitLab From b88d9ff2b027d41ee865bc5029927b0896b61ee2 Mon Sep 17 00:00:00 2001 From: ngala Date: Fri, 27 Oct 2023 14:38:33 +0530 Subject: [PATCH 07/19] fetch namespace from request header --- internal/auth/session.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/internal/auth/session.go b/internal/auth/session.go index 60b96b4a1..a5d0760e2 100644 --- a/internal/auth/session.go +++ b/internal/auth/session.go @@ -26,9 +26,14 @@ func (s *hostSession) Save(r *http.Request, w http.ResponseWriter) error { } func (a *Auth) getSessionFromStore(r *http.Request) (*hostSession, error) { - namespace := r.Host[:strings.IndexByte(r.Host, '.')] - session, err := a.store.Get(r, "gitlab-pages"+"-"+namespace) + session, err := a.store.Get(r, "gitlab-pages") + namespace := "" + namespaceInPathHeader := r.Header.Get("namespace-in-path") + logRequest(r).WithField("namespace-in-path", namespaceInPathHeader).Debug("> Inside getSessionFromStore") + if namespaceInPathHeader != "" { + namespace = r.Host[:strings.IndexByte(r.Host, '.')] + } if session != nil { // Cookie just for this domain session.Options.Path = "/" + namespace -- GitLab From ff8ac11aba290d472928ed513ff5318e05a02db6 Mon Sep 17 00:00:00 2001 From: ngala Date: Fri, 27 Oct 2023 14:51:57 +0530 Subject: [PATCH 08/19] update path with namespace --- internal/auth/session.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/internal/auth/session.go b/internal/auth/session.go index a5d0760e2..b6c410bb7 100644 --- a/internal/auth/session.go +++ b/internal/auth/session.go @@ -1,10 +1,8 @@ package auth import ( - "net/http" - "strings" - "github.com/gorilla/sessions" + "net/http" "gitlab.com/gitlab-org/gitlab-pages/internal/errortracking" "gitlab.com/gitlab-org/gitlab-pages/internal/httperrors" @@ -28,15 +26,12 @@ func (s *hostSession) Save(r *http.Request, w http.ResponseWriter) error { func (a *Auth) getSessionFromStore(r *http.Request) (*hostSession, error) { session, err := a.store.Get(r, "gitlab-pages") - namespace := "" - namespaceInPathHeader := r.Header.Get("namespace-in-path") - logRequest(r).WithField("namespace-in-path", namespaceInPathHeader).Debug("> Inside getSessionFromStore") - if namespaceInPathHeader != "" { - namespace = r.Host[:strings.IndexByte(r.Host, '.')] - } + namespaceInPath := r.Header.Get("namespace-in-path") + logRequest(r).WithField("namespace-in-path", namespaceInPath).Debug("> Inside getSessionFromStore") + if session != nil { // Cookie just for this domain - session.Options.Path = "/" + namespace + session.Options.Path = "/" + namespaceInPath session.Options.HttpOnly = true session.Options.Secure = request.IsHTTPS(r) session.Options.MaxAge = int(a.cookieSessionTimeout.Seconds()) -- GitLab From 21e45a6db5ae3c7fe8b9bfd5a09297abf9346887 Mon Sep 17 00:00:00 2001 From: ngala Date: Sat, 28 Oct 2023 19:33:57 +0530 Subject: [PATCH 09/19] update session url with namespace in path --- internal/auth/auth.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index f411b9d55..7ea139dd3 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -319,6 +319,16 @@ func getRequestAddress(r *http.Request) string { return "http://" + r.Host + r.RequestURI } +func getRequestAddressWithNamespaceInPath(r *http.Request) string { + indexOfFirstDot := strings.IndexByte(r.Host, '.') + namespace := r.Host[:indexOfFirstDot] + dns := r.Host[indexOfFirstDot+1:] + if request.IsHTTPS(r) { + return "https://" + dns + "/" + namespace + r.RequestURI + } + return "http://" + dns + "/" + namespace + r.RequestURI +} + func getRequestDomain(r *http.Request) string { if request.IsHTTPS(r) { return "https://" + r.Host @@ -428,7 +438,12 @@ func (a *Auth) checkTokenExists(session *hostSession, w http.ResponseWriter, r * logRequest(r).WithField("Session state", session.Values["state"]).Debug("> Inside checkTokenExists session state nil") } - session.Values["uri"] = getRequestAddress(r) + if session.Options.Path == "/" || session.Options.Path == "/projects" { + session.Values["uri"] = getRequestAddress(r) + } else { + // To be used for namespace in url path + session.Values["uri"] = getRequestAddressWithNamespaceInPath(r) + } // Clear possible proxying delete(session.Values, "proxy_auth_domain") -- GitLab From f4871a27a2eb5c63abc4afec341cb20728f6d535 Mon Sep 17 00:00:00 2001 From: ngala Date: Sat, 28 Oct 2023 23:37:49 +0530 Subject: [PATCH 10/19] update logs --- internal/auth/auth.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 7ea139dd3..92568a0db 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -258,6 +258,7 @@ func (a *Auth) handleProxyingAuth(session *hostSession, w http.ResponseWriter, r "domain_query": domain, }).Info("Redirecting user to gitlab for oauth") + logRequest(r).WithField("redirect url", url).Debug("> shouldProxyAuthToGitlab redirect") http.Redirect(w, r, url, http.StatusFound) return true @@ -305,6 +306,7 @@ func (a *Auth) handleProxyingAuth(session *hostSession, w http.ResponseWriter, r // Redirect pages to originating domain with code and state to finish // authentication process + logRequest(r).WithField("redirect url", proxyDomain+r.URL.Path+"?"+query.Encode()).Debug("> shouldProxyCallbackToCustomDomain redirect") http.Redirect(w, r, proxyDomain+r.URL.Path+"?"+query.Encode(), http.StatusFound) return true } -- GitLab From 8e512def4e718bfd47396747edf4edc8e3cffc59 Mon Sep 17 00:00:00 2001 From: ngala Date: Sun, 29 Oct 2023 00:04:53 +0530 Subject: [PATCH 11/19] update proxyDomain --- internal/auth/auth.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 92568a0db..ddff3470f 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -268,7 +268,7 @@ func (a *Auth) handleProxyingAuth(session *hostSession, w http.ResponseWriter, r // redirect to originating domain set in the cookie as proxy_auth_domain if shouldProxyCallbackToCustomDomain(session) { // Get domain started auth process - proxyDomain := session.Values["proxy_auth_domain"].(string) + proxyDomain := getProxyAuthDomain(session) logRequest(r).WithField("proxy_auth_domain", proxyDomain).Info("Redirecting auth callback to custom domain") @@ -346,6 +346,17 @@ func shouldProxyCallbackToCustomDomain(session *hostSession) bool { return session.Values["proxy_auth_domain"] != nil } +func getProxyAuthDomain(session *hostSession) string { + domain := session.Values["proxy_auth_domain"].(string) + if session.Options.Path == "/" { + return domain + } + httpStr := domain[:strings.IndexByte(domain, ':')] + namespace := domain[strings.IndexByte(domain, ':')+3 : strings.IndexByte(domain, '.')] + dns := domain[strings.IndexByte(domain, '.')+1:] + return httpStr + "://" + dns + "/" + namespace +} + func validateState(r *http.Request, session *hostSession) bool { state := r.URL.Query().Get("state") logRequest(r).WithField("Session state", session.Values["state"]).Debug("> Inside validateState") -- GitLab From 5f96ebfb8ceb46b699e6cdef5800ede319d0d002 Mon Sep 17 00:00:00 2001 From: ngala Date: Mon, 30 Oct 2023 16:41:49 +0530 Subject: [PATCH 12/19] update request domain --- internal/auth/auth.go | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index ddff3470f..140c9673a 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -268,7 +268,7 @@ func (a *Auth) handleProxyingAuth(session *hostSession, w http.ResponseWriter, r // redirect to originating domain set in the cookie as proxy_auth_domain if shouldProxyCallbackToCustomDomain(session) { // Get domain started auth process - proxyDomain := getProxyAuthDomain(session) + proxyDomain := session.Values["proxy_auth_domain"].(string) logRequest(r).WithField("proxy_auth_domain", proxyDomain).Info("Redirecting auth callback to custom domain") @@ -338,6 +338,16 @@ func getRequestDomain(r *http.Request) string { return "http://" + r.Host } +func getRequestDomainWithNamespaceInPath(r *http.Request) string { + indexOfFirstDot := strings.IndexByte(r.Host, '.') + namespace := r.Host[:indexOfFirstDot] + dns := r.Host[indexOfFirstDot+1:] + if request.IsHTTPS(r) { + return "https://" + dns + "/" + namespace + } + return "http://" + dns + "/" + namespace +} + func shouldProxyAuthToGitlab(r *http.Request) bool { return r.URL.Query().Get("domain") != "" && r.URL.Query().Get("state") != "" } @@ -346,17 +356,6 @@ func shouldProxyCallbackToCustomDomain(session *hostSession) bool { return session.Values["proxy_auth_domain"] != nil } -func getProxyAuthDomain(session *hostSession) string { - domain := session.Values["proxy_auth_domain"].(string) - if session.Options.Path == "/" { - return domain - } - httpStr := domain[:strings.IndexByte(domain, ':')] - namespace := domain[strings.IndexByte(domain, ':')+3 : strings.IndexByte(domain, '.')] - dns := domain[strings.IndexByte(domain, '.')+1:] - return httpStr + "://" + dns + "/" + namespace -} - func validateState(r *http.Request, session *hostSession) bool { state := r.URL.Query().Get("state") logRequest(r).WithField("Session state", session.Values["state"]).Debug("> Inside validateState") @@ -451,12 +450,13 @@ func (a *Auth) checkTokenExists(session *hostSession, w http.ResponseWriter, r * logRequest(r).WithField("Session state", session.Values["state"]).Debug("> Inside checkTokenExists session state nil") } - if session.Options.Path == "/" || session.Options.Path == "/projects" { + session.Values["uri"] = getRequestAddress(r) + /*if session.Options.Path == "/" || session.Options.Path == "/projects" { session.Values["uri"] = getRequestAddress(r) } else { // To be used for namespace in url path session.Values["uri"] = getRequestAddressWithNamespaceInPath(r) - } + }*/ // Clear possible proxying delete(session.Values, "proxy_auth_domain") @@ -472,15 +472,20 @@ func (a *Auth) checkTokenExists(session *hostSession, w http.ResponseWriter, r * // Because the pages domain might be in public suffix list, we have to // redirect to pages domain to trigger authorization flow - http.Redirect(w, r, a.getProxyAddress(r, session.Values["state"].(string)), http.StatusFound) + isNamespaceInPath := session.Options.Path != "/" + http.Redirect(w, r, a.getProxyAddress(r, session.Values["state"].(string), isNamespaceInPath), http.StatusFound) return true } return false } -func (a *Auth) getProxyAddress(r *http.Request, state string) string { - return fmt.Sprintf(authorizeProxyTemplate, a.redirectURI, getRequestDomain(r), state) +func (a *Auth) getProxyAddress(r *http.Request, state string, isNamespaceInPath bool) string { + requestDomain := getRequestDomain(r) + if isNamespaceInPath { + requestDomain = getRequestDomainWithNamespaceInPath(r) + } + return fmt.Sprintf(authorizeProxyTemplate, a.redirectURI, requestDomain, state) } func destroySession(session *hostSession, w http.ResponseWriter, r *http.Request) { -- GitLab From fedbf77a2dddd9c195a8c1fb05af913a6636e278 Mon Sep 17 00:00:00 2001 From: ngala Date: Mon, 30 Oct 2023 17:03:58 +0530 Subject: [PATCH 13/19] update request domain --- internal/auth/auth.go | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 140c9673a..5c6fda29a 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -142,7 +142,8 @@ func (a *Auth) checkAuthenticationResponse(session *hostSession, w http.Response return } - decryptedCode, err := a.DecryptCode(r.URL.Query().Get("code"), getRequestDomain(r)) + isNamespaceInPath := session.Options.Path != "/" + decryptedCode, err := a.DecryptCode(r.URL.Query().Get("code"), getRequestDomain(r, isNamespaceInPath)) if err != nil { logRequest(r).WithError(err).Error("failed to decrypt secure code") errortracking.CaptureErrWithReqAndStackTrace(err, r) @@ -331,21 +332,20 @@ func getRequestAddressWithNamespaceInPath(r *http.Request) string { return "http://" + dns + "/" + namespace + r.RequestURI } -func getRequestDomain(r *http.Request) string { - if request.IsHTTPS(r) { - return "https://" + r.Host +func getRequestDomain(r *http.Request, isNamespaceInPath bool) string { + requestDomain := r.Host + + if isNamespaceInPath { + indexOfFirstDot := strings.IndexByte(r.Host, '.') + namespace := r.Host[:indexOfFirstDot] + dns := r.Host[indexOfFirstDot+1:] + requestDomain = dns + "/" + namespace } - return "http://" + r.Host -} -func getRequestDomainWithNamespaceInPath(r *http.Request) string { - indexOfFirstDot := strings.IndexByte(r.Host, '.') - namespace := r.Host[:indexOfFirstDot] - dns := r.Host[indexOfFirstDot+1:] if request.IsHTTPS(r) { - return "https://" + dns + "/" + namespace + return "https://" + requestDomain } - return "http://" + dns + "/" + namespace + return "http://" + requestDomain } func shouldProxyAuthToGitlab(r *http.Request) bool { @@ -481,11 +481,7 @@ func (a *Auth) checkTokenExists(session *hostSession, w http.ResponseWriter, r * } func (a *Auth) getProxyAddress(r *http.Request, state string, isNamespaceInPath bool) string { - requestDomain := getRequestDomain(r) - if isNamespaceInPath { - requestDomain = getRequestDomainWithNamespaceInPath(r) - } - return fmt.Sprintf(authorizeProxyTemplate, a.redirectURI, requestDomain, state) + return fmt.Sprintf(authorizeProxyTemplate, a.redirectURI, getRequestDomain(r, isNamespaceInPath), state) } func destroySession(session *hostSession, w http.ResponseWriter, r *http.Request) { -- GitLab From 6ebb036b23d2d2d9553976dd97f133469d15ff22 Mon Sep 17 00:00:00 2001 From: ngala Date: Tue, 31 Oct 2023 11:11:03 +0530 Subject: [PATCH 14/19] Remove commented code --- internal/auth/auth.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 5c6fda29a..5f667f8cb 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -322,16 +322,6 @@ func getRequestAddress(r *http.Request) string { return "http://" + r.Host + r.RequestURI } -func getRequestAddressWithNamespaceInPath(r *http.Request) string { - indexOfFirstDot := strings.IndexByte(r.Host, '.') - namespace := r.Host[:indexOfFirstDot] - dns := r.Host[indexOfFirstDot+1:] - if request.IsHTTPS(r) { - return "https://" + dns + "/" + namespace + r.RequestURI - } - return "http://" + dns + "/" + namespace + r.RequestURI -} - func getRequestDomain(r *http.Request, isNamespaceInPath bool) string { requestDomain := r.Host @@ -451,12 +441,6 @@ func (a *Auth) checkTokenExists(session *hostSession, w http.ResponseWriter, r * } session.Values["uri"] = getRequestAddress(r) - /*if session.Options.Path == "/" || session.Options.Path == "/projects" { - session.Values["uri"] = getRequestAddress(r) - } else { - // To be used for namespace in url path - session.Values["uri"] = getRequestAddressWithNamespaceInPath(r) - }*/ // Clear possible proxying delete(session.Values, "proxy_auth_domain") -- GitLab From 8cce75dc61d6bdcbd9334d76940124e8846363c5 Mon Sep 17 00:00:00 2001 From: ngala Date: Wed, 1 Nov 2023 15:54:41 +0530 Subject: [PATCH 15/19] Get namspace from the session path --- internal/auth/auth.go | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 5f667f8cb..ad35c3e93 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -142,8 +142,7 @@ func (a *Auth) checkAuthenticationResponse(session *hostSession, w http.Response return } - isNamespaceInPath := session.Options.Path != "/" - decryptedCode, err := a.DecryptCode(r.URL.Query().Get("code"), getRequestDomain(r, isNamespaceInPath)) + decryptedCode, err := a.DecryptCode(r.URL.Query().Get("code"), getRequestDomain(r, getNamespaceInPath(session))) if err != nil { logRequest(r).WithError(err).Error("failed to decrypt secure code") errortracking.CaptureErrWithReqAndStackTrace(err, r) @@ -322,14 +321,10 @@ func getRequestAddress(r *http.Request) string { return "http://" + r.Host + r.RequestURI } -func getRequestDomain(r *http.Request, isNamespaceInPath bool) string { +func getRequestDomain(r *http.Request, namespace string) string { requestDomain := r.Host - - if isNamespaceInPath { - indexOfFirstDot := strings.IndexByte(r.Host, '.') - namespace := r.Host[:indexOfFirstDot] - dns := r.Host[indexOfFirstDot+1:] - requestDomain = dns + "/" + namespace + if len(namespace) > 0 && strings.HasPrefix(r.Host, namespace) { + requestDomain = strings.TrimPrefix(r.Host, namespace+".") + "/" + namespace } if request.IsHTTPS(r) { @@ -338,6 +333,14 @@ func getRequestDomain(r *http.Request, isNamespaceInPath bool) string { return "http://" + requestDomain } +func getNamespaceInPath(session *hostSession) string { + namespaceInPath := "" + if len(session.Options.Path) > 1 && session.Options.Path[0] == '/' { + namespaceInPath = session.Options.Path[1:] + } + return namespaceInPath +} + func shouldProxyAuthToGitlab(r *http.Request) bool { return r.URL.Query().Get("domain") != "" && r.URL.Query().Get("state") != "" } @@ -456,16 +459,15 @@ func (a *Auth) checkTokenExists(session *hostSession, w http.ResponseWriter, r * // Because the pages domain might be in public suffix list, we have to // redirect to pages domain to trigger authorization flow - isNamespaceInPath := session.Options.Path != "/" - http.Redirect(w, r, a.getProxyAddress(r, session.Values["state"].(string), isNamespaceInPath), http.StatusFound) + http.Redirect(w, r, a.getProxyAddress(r, session.Values["state"].(string), getNamespaceInPath(session)), http.StatusFound) return true } return false } -func (a *Auth) getProxyAddress(r *http.Request, state string, isNamespaceInPath bool) string { - return fmt.Sprintf(authorizeProxyTemplate, a.redirectURI, getRequestDomain(r, isNamespaceInPath), state) +func (a *Auth) getProxyAddress(r *http.Request, state string, namespace string) string { + return fmt.Sprintf(authorizeProxyTemplate, a.redirectURI, getRequestDomain(r, namespace), state) } func destroySession(session *hostSession, w http.ResponseWriter, r *http.Request) { -- GitLab From 2a90912fd2afa9997789f6efc6af2b94fc8cc64d Mon Sep 17 00:00:00 2001 From: ngala Date: Tue, 7 Nov 2023 11:32:01 +0530 Subject: [PATCH 16/19] Update namespace header --- internal/auth/session.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/auth/session.go b/internal/auth/session.go index b6c410bb7..b1b8bdb2c 100644 --- a/internal/auth/session.go +++ b/internal/auth/session.go @@ -26,8 +26,8 @@ func (s *hostSession) Save(r *http.Request, w http.ResponseWriter) error { func (a *Auth) getSessionFromStore(r *http.Request) (*hostSession, error) { session, err := a.store.Get(r, "gitlab-pages") - namespaceInPath := r.Header.Get("namespace-in-path") - logRequest(r).WithField("namespace-in-path", namespaceInPath).Debug("> Inside getSessionFromStore") + namespaceInPath := r.Header.Get("X-Gitlab-Namespace-In-Path") + logRequest(r).WithField("X-Gitlab-Namespace-In-Path", namespaceInPath).Debug("> Inside getSessionFromStore") if session != nil { // Cookie just for this domain -- GitLab From ceb6d345d860dc8c6cce1d92278858f154f14440 Mon Sep 17 00:00:00 2001 From: ngala Date: Thu, 9 Nov 2023 22:07:59 +0530 Subject: [PATCH 17/19] Prevent forged X-Gitlab-Namespace-In-Path custom header --- internal/auth/session.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/internal/auth/session.go b/internal/auth/session.go index b1b8bdb2c..9a14e7f05 100644 --- a/internal/auth/session.go +++ b/internal/auth/session.go @@ -3,6 +3,7 @@ package auth import ( "github.com/gorilla/sessions" "net/http" + "strings" "gitlab.com/gitlab-org/gitlab-pages/internal/errortracking" "gitlab.com/gitlab-org/gitlab-pages/internal/httperrors" @@ -26,10 +27,13 @@ func (s *hostSession) Save(r *http.Request, w http.ResponseWriter) error { func (a *Auth) getSessionFromStore(r *http.Request) (*hostSession, error) { session, err := a.store.Get(r, "gitlab-pages") - namespaceInPath := r.Header.Get("X-Gitlab-Namespace-In-Path") - logRequest(r).WithField("X-Gitlab-Namespace-In-Path", namespaceInPath).Debug("> Inside getSessionFromStore") - if session != nil { + namespaceInPath := r.Header.Get("X-Gitlab-Namespace-In-Path") + if namespaceInPath != "" && !strings.HasPrefix(r.Host, namespaceInPath+"."+a.pagesDomain) { + namespaceInPath = "" + } + logRequest(r).WithField("X-Gitlab-Namespace-In-Path", namespaceInPath).Debug("> Inside getSessionFromStore") + // Cookie just for this domain session.Options.Path = "/" + namespaceInPath session.Options.HttpOnly = true -- GitLab From 62c52f5cff34e307a0519a915afb55f8315deff9 Mon Sep 17 00:00:00 2001 From: ngala Date: Fri, 10 Nov 2023 12:20:05 +0530 Subject: [PATCH 18/19] Add test for X-Gitlab-Namespace-In-Path --- internal/auth/auth.go | 6 +++--- internal/auth/session.go | 9 ++------ internal/request/request.go | 13 +++++++++++ internal/request/request_test.go | 37 ++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index ad35c3e93..bbb8daa61 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -142,7 +142,7 @@ func (a *Auth) checkAuthenticationResponse(session *hostSession, w http.Response return } - decryptedCode, err := a.DecryptCode(r.URL.Query().Get("code"), getRequestDomain(r, getNamespaceInPath(session))) + decryptedCode, err := a.DecryptCode(r.URL.Query().Get("code"), getRequestDomain(r, getNamespaceInPathFromSession(session))) if err != nil { logRequest(r).WithError(err).Error("failed to decrypt secure code") errortracking.CaptureErrWithReqAndStackTrace(err, r) @@ -333,7 +333,7 @@ func getRequestDomain(r *http.Request, namespace string) string { return "http://" + requestDomain } -func getNamespaceInPath(session *hostSession) string { +func getNamespaceInPathFromSession(session *hostSession) string { namespaceInPath := "" if len(session.Options.Path) > 1 && session.Options.Path[0] == '/' { namespaceInPath = session.Options.Path[1:] @@ -459,7 +459,7 @@ func (a *Auth) checkTokenExists(session *hostSession, w http.ResponseWriter, r * // Because the pages domain might be in public suffix list, we have to // redirect to pages domain to trigger authorization flow - http.Redirect(w, r, a.getProxyAddress(r, session.Values["state"].(string), getNamespaceInPath(session)), http.StatusFound) + http.Redirect(w, r, a.getProxyAddress(r, session.Values["state"].(string), getNamespaceInPathFromSession(session)), http.StatusFound) return true } diff --git a/internal/auth/session.go b/internal/auth/session.go index 9a14e7f05..bbe83ba5c 100644 --- a/internal/auth/session.go +++ b/internal/auth/session.go @@ -2,12 +2,10 @@ package auth import ( "github.com/gorilla/sessions" - "net/http" - "strings" - "gitlab.com/gitlab-org/gitlab-pages/internal/errortracking" "gitlab.com/gitlab-org/gitlab-pages/internal/httperrors" "gitlab.com/gitlab-org/gitlab-pages/internal/request" + "net/http" ) type hostSession struct { @@ -28,10 +26,7 @@ func (a *Auth) getSessionFromStore(r *http.Request) (*hostSession, error) { session, err := a.store.Get(r, "gitlab-pages") if session != nil { - namespaceInPath := r.Header.Get("X-Gitlab-Namespace-In-Path") - if namespaceInPath != "" && !strings.HasPrefix(r.Host, namespaceInPath+"."+a.pagesDomain) { - namespaceInPath = "" - } + namespaceInPath := request.GetNamespaceInPathFromRequest(r, a.pagesDomain) logRequest(r).WithField("X-Gitlab-Namespace-In-Path", namespaceInPath).Debug("> Inside getSessionFromStore") // Cookie just for this domain diff --git a/internal/request/request.go b/internal/request/request.go index f98b08195..4a7e1e520 100644 --- a/internal/request/request.go +++ b/internal/request/request.go @@ -3,6 +3,7 @@ package request import ( "net" "net/http" + "strings" ) const ( @@ -38,3 +39,15 @@ func GetRemoteAddrWithoutPort(r *http.Request) string { return remoteAddr } + +// GetNamespaceInPathFromRequest GetNamespaceInPath fetches X-Gitlab-Namespace-In-Path from r.Header and validates against pagesDomain before returning +func GetNamespaceInPathFromRequest(r *http.Request, pagesDomain string) string { + namespaceInPath := "" + if pagesDomain != "" { + namespaceInPath = r.Header.Get("X-Gitlab-Namespace-In-Path") + if namespaceInPath != "" && !strings.HasPrefix(r.Host, namespaceInPath+"."+pagesDomain) { + namespaceInPath = "" + } + } + return namespaceInPath +} diff --git a/internal/request/request_test.go b/internal/request/request_test.go index 9e71db37e..8ee16f8c4 100644 --- a/internal/request/request_test.go +++ b/internal/request/request_test.go @@ -87,3 +87,40 @@ func TestGetRemoteAddrWithoutPort(t *testing.T) { }) } } + +func TestGetNamespaceInPathFromRequest(t *testing.T) { + tests := map[string]struct { + pagesDomain string + u string + namespace string + expected string + }{ + "when valid X-Gitlab-Namespace-In-Path is provided in request header": { + pagesDomain: "example.com", + u: "https://namespace.example.com/myProject", + namespace: "namespace", + expected: "namespace", + }, + "when valid X-Gitlab-Namespace-In-Path with '.' in between in request header": { + pagesDomain: "example.com", + u: "https://namespace.test.example.com/myProject", + namespace: "namespace.test", + expected: "namespace.test", + }, + "when forged X-Gitlab-Namespace-In-Path is provided in request header": { + pagesDomain: "example.com", + u: "https://namespace.example.com/myProject", + namespace: "namespace-forged", + expected: "", + }, + } + for name, test := range tests { + t.Run(name, func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, test.u, nil) + req.Header.Set("X-Gitlab-Namespace-In-Path", test.namespace) + + namespace := GetNamespaceInPathFromRequest(req, test.pagesDomain) + require.Equal(t, test.expected, namespace) + }) + } +} -- GitLab From c220d906e1b94f5c86625bbf277a10ffaf9786c8 Mon Sep 17 00:00:00 2001 From: ngala Date: Fri, 10 Nov 2023 17:34:56 +0530 Subject: [PATCH 19/19] Reorder imports to fix pipeline failure --- internal/auth/session.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/auth/session.go b/internal/auth/session.go index bbe83ba5c..e2a7c3c1c 100644 --- a/internal/auth/session.go +++ b/internal/auth/session.go @@ -1,11 +1,13 @@ package auth import ( + "net/http" + "github.com/gorilla/sessions" + "gitlab.com/gitlab-org/gitlab-pages/internal/errortracking" "gitlab.com/gitlab-org/gitlab-pages/internal/httperrors" "gitlab.com/gitlab-org/gitlab-pages/internal/request" - "net/http" ) type hostSession struct { -- GitLab