From 203a63a7d94458a7fdb7bddb5e97a98b5bc488b5 Mon Sep 17 00:00:00 2001 From: Archie To Date: Fri, 31 May 2024 22:35:04 +0000 Subject: [PATCH 1/6] Preserve 2 specific headers --- internal/handlers/server.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/handlers/server.go b/internal/handlers/server.go index d3f4cc1..9c2eab7 100644 --- a/internal/handlers/server.go +++ b/internal/handlers/server.go @@ -95,6 +95,8 @@ func (s *Server) RootHandler(w http.ResponseWriter, r *http.Request) { "X-Forwarded-Host": r.Header.Get("X-Forwarded-Host"), "X-Forwarded-Prefix": r.Header.Get("X-Forwarded-Prefix"), "X-Forwarded-Uri": r.Header.Get("X-Forwarded-Uri"), + "X-CSRFToken": r.Header.Get("X-CSRFToken"), + "X-Requested-With": r.Header.Get("X-Requested-With"), }) // Modify request @@ -228,6 +230,9 @@ func (s *Server) AuthHandler(rule string) http.HandlerFunc { w.Header().Add(s.config.ForwardTokenHeaderName, s.config.ForwardTokenPrefix+id.Token) } + w.Header().Add("X-CSRFToken", r.Header.Get("X-CSRFToken")); + w.Header().Add("X-Requested-With", r.Header.Get("X-Requested-With")); + w.WriteHeader(200) } } From 33619d502ff9f78c9ad2c2f9d688dc0db611a430 Mon Sep 17 00:00:00 2001 From: Archie To Date: Mon, 24 Jun 2024 17:37:45 +0000 Subject: [PATCH 2/6] Add a config to set headers to be forwarded --- internal/configuration/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/configuration/config.go b/internal/configuration/config.go index d4332bb..e1e6b45 100644 --- a/internal/configuration/config.go +++ b/internal/configuration/config.go @@ -60,6 +60,7 @@ type Config struct { GroupClaimPrefix string `long:"group-claim-prefix" env:"GROUP_CLAIM_PREFIX" default:"oidc:" description:"prefix oidc group claims with this value"` EncryptionKeyString string `long:"encryption-key" env:"ENCRYPTION_KEY" description:"Encryption key used to encrypt the cookie (required)" json:"-"` GroupsAttributeName string `long:"groups-attribute-name" env:"GROUPS_ATTRIBUTE_NAME" default:"groups" description:"Map the correct attribute that contain the user groups"` + ForwardedHeaders string `long:"forwarded-headers" env:"FORWARDED_HEADERS" default:"" description:"Headers to forward to the upstream, separated by a comma. For example: X-CSRF-Token,X-Requested-With"` // RBAC EnableRBAC bool `long:"enable-rbac" env:"ENABLE_RBAC" description:"Indicates that RBAC support should be enabled"` From fdea37daed880a4758487077178afa664098c5dc Mon Sep 17 00:00:00 2001 From: Archie To Date: Mon, 24 Jun 2024 17:38:01 +0000 Subject: [PATCH 3/6] Forward headers based on config --- internal/handlers/server.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/handlers/server.go b/internal/handlers/server.go index 9c2eab7..3d33199 100644 --- a/internal/handlers/server.go +++ b/internal/handlers/server.go @@ -95,8 +95,6 @@ func (s *Server) RootHandler(w http.ResponseWriter, r *http.Request) { "X-Forwarded-Host": r.Header.Get("X-Forwarded-Host"), "X-Forwarded-Prefix": r.Header.Get("X-Forwarded-Prefix"), "X-Forwarded-Uri": r.Header.Get("X-Forwarded-Uri"), - "X-CSRFToken": r.Header.Get("X-CSRFToken"), - "X-Requested-With": r.Header.Get("X-Requested-With"), }) // Modify request @@ -230,8 +228,14 @@ func (s *Server) AuthHandler(rule string) http.HandlerFunc { w.Header().Add(s.config.ForwardTokenHeaderName, s.config.ForwardTokenPrefix+id.Token) } - w.Header().Add("X-CSRFToken", r.Header.Get("X-CSRFToken")); - w.Header().Add("X-Requested-With", r.Header.Get("X-Requested-With")); + // Get all headers that we want to forward from the original request + // by reading the config + headers := strings.Split(s.config.ForwardedHeaders, ",") + + // Forward headers + for _, header := range headers { + w.Header().Add(header, r.Header.Get(header)) + } w.WriteHeader(200) } From 0da9ffd6dda6e9371cc9f2f78f59d00cc2accc58 Mon Sep 17 00:00:00 2001 From: Archie To Date: Mon, 24 Jun 2024 18:11:28 +0000 Subject: [PATCH 4/6] Change 'forwarded headers' to 'forward headers' as the headers are not forwarded yet --- internal/configuration/config.go | 2 +- internal/handlers/server.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/configuration/config.go b/internal/configuration/config.go index e1e6b45..3f99b80 100644 --- a/internal/configuration/config.go +++ b/internal/configuration/config.go @@ -60,7 +60,7 @@ type Config struct { GroupClaimPrefix string `long:"group-claim-prefix" env:"GROUP_CLAIM_PREFIX" default:"oidc:" description:"prefix oidc group claims with this value"` EncryptionKeyString string `long:"encryption-key" env:"ENCRYPTION_KEY" description:"Encryption key used to encrypt the cookie (required)" json:"-"` GroupsAttributeName string `long:"groups-attribute-name" env:"GROUPS_ATTRIBUTE_NAME" default:"groups" description:"Map the correct attribute that contain the user groups"` - ForwardedHeaders string `long:"forwarded-headers" env:"FORWARDED_HEADERS" default:"" description:"Headers to forward to the upstream, separated by a comma. For example: X-CSRF-Token,X-Requested-With"` + ForwardHeaders string `long:"forward-headers" env:"FORWARD_HEADERS" default:"" description:"Headers to forward to the upstream, separated by a comma. For example: X-CSRF-Token,X-Requested-With"` // RBAC EnableRBAC bool `long:"enable-rbac" env:"ENABLE_RBAC" description:"Indicates that RBAC support should be enabled"` diff --git a/internal/handlers/server.go b/internal/handlers/server.go index 3d33199..9f83c4a 100644 --- a/internal/handlers/server.go +++ b/internal/handlers/server.go @@ -230,7 +230,7 @@ func (s *Server) AuthHandler(rule string) http.HandlerFunc { // Get all headers that we want to forward from the original request // by reading the config - headers := strings.Split(s.config.ForwardedHeaders, ",") + headers := strings.Split(s.config.ForwardHeaders, ",") // Forward headers for _, header := range headers { From a76f1d111ad1d4f3ad5221a1178a97a51c7b248e Mon Sep 17 00:00:00 2001 From: Archie To Date: Mon, 24 Jun 2024 18:22:59 +0000 Subject: [PATCH 5/6] Format code using 'go fmt' --- internal/authorization/rbac/rbac.go | 2 +- internal/configuration/config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/authorization/rbac/rbac.go b/internal/authorization/rbac/rbac.go index 383273c..18187e5 100644 --- a/internal/authorization/rbac/rbac.go +++ b/internal/authorization/rbac/rbac.go @@ -22,7 +22,7 @@ const ( // This creates a higher guarantee that your informer’s store has a perfect picture of the resources it is watching. // There are situations where events can be missed entirely and resyncing every so often solves this. // Setting to 0 disables the resync and makes the informer subscribe to individual updates only. - defaultResyncDuration = time.Minute * 10 + defaultResyncDuration = time.Minute * 10 ) // Logger is an interface for basic log output diff --git a/internal/configuration/config.go b/internal/configuration/config.go index 3f99b80..e6505a2 100644 --- a/internal/configuration/config.go +++ b/internal/configuration/config.go @@ -60,7 +60,7 @@ type Config struct { GroupClaimPrefix string `long:"group-claim-prefix" env:"GROUP_CLAIM_PREFIX" default:"oidc:" description:"prefix oidc group claims with this value"` EncryptionKeyString string `long:"encryption-key" env:"ENCRYPTION_KEY" description:"Encryption key used to encrypt the cookie (required)" json:"-"` GroupsAttributeName string `long:"groups-attribute-name" env:"GROUPS_ATTRIBUTE_NAME" default:"groups" description:"Map the correct attribute that contain the user groups"` - ForwardHeaders string `long:"forward-headers" env:"FORWARD_HEADERS" default:"" description:"Headers to forward to the upstream, separated by a comma. For example: X-CSRF-Token,X-Requested-With"` + ForwardHeaders string `long:"forward-headers" env:"FORWARD_HEADERS" default:"" description:"Headers to forward to the upstream, separated by a comma. For example: X-CSRF-Token,X-Requested-With"` // RBAC EnableRBAC bool `long:"enable-rbac" env:"ENABLE_RBAC" description:"Indicates that RBAC support should be enabled"` From 05a0b152ce2ce2980a0771ca1c98f1d57e036c7a Mon Sep 17 00:00:00 2001 From: Archie To Date: Wed, 14 May 2025 20:00:55 +0000 Subject: [PATCH 6/6] Create an allow-with-auth mode This mode forwards authentication headers if user is authenticated. Otherwise, it doesn't forward anything --- internal/configuration/config.go | 6 +-- internal/handlers/server.go | 78 ++++++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/internal/configuration/config.go b/internal/configuration/config.go index e6505a2..a4aaf72 100644 --- a/internal/configuration/config.go +++ b/internal/configuration/config.go @@ -46,7 +46,7 @@ type Config struct { UserCookieName string `long:"user-cookie-name" env:"USER_COOKIE_NAME" default:"_forward_auth_name" description:"User Cookie Name"` CSRFCookieName string `long:"csrf-cookie-name" env:"CSRF_COOKIE_NAME" default:"_forward_auth_csrf" description:"CSRF Cookie Name"` ClaimsSessionName string `long:"claims-session-name" env:"CLAIMS_SESSION_NAME" default:"_forward_auth_claims" description:"Name of the claims session"` - DefaultAction string `long:"default-action" env:"DEFAULT_ACTION" default:"auth" choice:"auth" choice:"allow" description:"Default action"` + DefaultAction string `long:"default-action" env:"DEFAULT_ACTION" default:"auth" choice:"auth" choice:"allow" choice:"allow-with-auth" description:"Default action"` Domains CommaSeparatedList `long:"domain" env:"DOMAIN" description:"Only allow given email domains, can be set multiple times"` LifetimeString int `long:"lifetime" env:"LIFETIME" default:"43200" description:"Lifetime in seconds"` Path string `long:"url-path" env:"URL_PATH" default:"/_oauth" description:"Callback URL Path"` @@ -295,8 +295,8 @@ func (r *Rule) FormattedRule() string { // Validate validates the rule func (r *Rule) Validate() { - if r.Action != "auth" && r.Action != "allow" { - log.Fatal("invalid rule action, must be \"auth\" or \"allow\"") + if r.Action != "auth" && r.Action != "allow" && r.Action != "allow-with-auth" { + log.Fatal("invalid rule action, must be \"auth\", \"allow\" or \"allow-with-auth\"") } } diff --git a/internal/handlers/server.go b/internal/handlers/server.go index 9f83c4a..bad919c 100644 --- a/internal/handlers/server.go +++ b/internal/handlers/server.go @@ -66,9 +66,12 @@ func (s *Server) buildRoutes() { // Let's build a router for name, rule := range s.config.Rules { var err error - if rule.Action == "allow" { + switch rule.Action { + case "allow": err = s.router.AddRoute(rule.FormattedRule(), 1, s.AllowHandler(name)) - } else { + case "allow-with-auth": + err = s.router.AddRoute(rule.FormattedRule(), 1, s.AllowWithAuthHandler(name)) + default: err = s.router.AddRoute(rule.FormattedRule(), 1, s.AuthHandler(name)) } if err != nil { @@ -80,9 +83,12 @@ func (s *Server) buildRoutes() { s.router.Handle(s.config.Path, s.AuthCallbackHandler()) // Add a default handler - if s.config.DefaultAction == "allow" { + switch s.config.DefaultAction { + case "allow": s.router.NewRoute().Handler(s.AllowHandler("default")) - } else { + case "allow-with-auth": + s.router.NewRoute().Handler(s.AllowWithAuthHandler("default")) + default: s.router.NewRoute().Handler(s.AuthHandler("default")) } } @@ -524,3 +530,67 @@ func (s *Server) makeKubeUserInfo(email string, groups []string) authorization.U Groups: g, } } + +// AllowWithAuthHandler handles the request as "allow-with-auth", allowing the request through +// but forwarding authentication headers if the user is authenticated +func (s *Server) AllowWithAuthHandler(rule string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + logger := s.logger(r, rule, "Allow with auth request") + + // Get auth cookie + c, err := r.Cookie(s.config.CookieName) + if err != nil { + // Not authenticated, just return 200 without forwarding headers + w.WriteHeader(200) + return + } + + // Validate cookie + id, err := s.authenticator.ValidateCookie(r, c) + if err != nil { + // Invalid cookie, just return 200 without forwarding headers + logger.Info(fmt.Sprintf("cookie validation failure: %s", err.Error())) + w.WriteHeader(200) + return + } + + // Validate user + valid := s.authenticator.ValidateEmail(id.Email) + if !valid { + // Invalid user, just return 200 without forwarding headers + logger.WithFields(logrus.Fields{ + "email": id.Email, + }).Errorf("Invalid email") + w.WriteHeader(200) + return + } + + // User is authenticated, forward headers + for _, headerName := range s.config.EmailHeaderNames { + w.Header().Set(headerName, id.Email) + } + + if s.config.EnableImpersonation { + // Set impersonation headers + logger.Debug(fmt.Sprintf("setting authorization token and impersonation headers: email: %s", id.Email)) + w.Header().Set("Authorization", fmt.Sprintf("Bearer %s", s.config.ServiceAccountToken)) + w.Header().Set(impersonateUserHeader, id.Email) + w.Header().Set(impersonateGroupHeader, "system:authenticated") + w.Header().Set("Connection", cleanupConnectionHeader(w.Header().Get("Connection"))) + } + + if s.config.ForwardTokenHeaderName != "" && id.Token != "" { + w.Header().Add(s.config.ForwardTokenHeaderName, s.config.ForwardTokenPrefix+id.Token) + } + + // Get all headers that we want to forward from the original request + headers := strings.Split(s.config.ForwardHeaders, ",") + + // Forward headers + for _, header := range headers { + w.Header().Add(header, r.Header.Get(header)) + } + + w.WriteHeader(200) + } +}