Skip to content

pkg/endpoints: forward Impersonate headers in ProxyRequest #1416

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

Closed
Closed
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: 2 additions & 0 deletions cmd/dashboard/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ var (
webDir = flag.String("web-dir", "", "Dashboard web resources dir")
logoutUrl = flag.String("logout-url", "", "If set, enables logout on the frontend and binds the logout button to this url")
csrfSecureCookie = flag.Bool("csrf-secure-cookie", true, "Enable or disable Secure attribute on the CSRF cookie")
impersonate = flag.Bool("imporsonate", false, "Enable user impersonation")
)

func getCSRFAuthKey() []byte {
Expand Down Expand Up @@ -117,6 +118,7 @@ func main() {
ReadOnly: *readOnly,
WebDir: *webDir,
LogoutURL: *logoutUrl,
Impersonate: *impersonate,
}

resource := endpoints.Resource{
Expand Down
30 changes: 30 additions & 0 deletions pkg/endpoints/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,36 @@ func (r Resource) ProxyRequest(request *restful.Request, response *restful.Respo
uri := request.PathParameter("subpath") + "?" + parsedURL.RawQuery
forwardRequest := r.K8sClient.CoreV1().RESTClient().Verb(request.Request.Method).RequestURI(uri).Body(request.Request.Body)
forwardRequest.SetHeader("Content-Type", request.HeaderParameter("Content-Type"))
if r.Options.Impersonate {
// If the dashboard is running with Impersonate enabled, then proxy requests using the
// incoming requests Impersonate-* and Authorization headers. The Authorization header must
// correspond to a serviceaccount that has `impersonate` privileges, otherwise the apiserver
// will reject the request.
//
// In this mode, the intention is for the dashboard to be behind an authenticating reverse proxy
// that authenticates the user using an OIDC flow. The reverse proxy then attaches an
// Authorization header to the request containing its own authentication proof.
// It also adds Impersonate-* headers that list the identity claim (e.g., `sub`, `email`, etc.)
// and/or user group (i.e., `groups` claim) of the end-user.
//
// The apiserver receives the forwarded request, determines that the serviceaccount corresponding
// to the Authorization token has `impersonate` privileges (the reverse proxy must have
// `impersonate` privileges) and responds to the request as though it was performed by the
// account given by the Impersonate-User header and/or Impersonate-Group header.
//
// In this mode, the Authorization header must be set, it is important that the
// dashboard not perform this request using its own serviceaccount (the default behaviour), as
// that leads to a privilege escalation whereby a less-privileged user can access resources
// that the dashboard can access, but the user may not.
authz := request.Request.Header.Get("Authorization")
if authz == "" {
utils.RespondError(response, err, http.StatusForbidden)
return
}
forwardRequest.SetHeader("Authorization", authz)
forwardRequest.SetHeader("Impersonate-User", request.Request.Header.Get("Impersonate-User"))
forwardRequest.SetHeader("Impersonate-Group", request.Request.Header.Get("Impersonate-Group"))
}
forwardResponse := forwardRequest.Do()

if secretsURIPattern.Match([]byte(uri)) {
Expand Down
4 changes: 4 additions & 0 deletions pkg/endpoints/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ type Options struct {
ReadOnly bool
WebDir string
LogoutURL string
// Expect and forward Impersonate-* and Authorization headers
// so requests are performed using the client's privileges instead
// of the dashboard's privileges.
Impersonate bool
}

// GetPipelinesNamespace returns the PipelinesNamespace property if set
Expand Down