diff --git a/internal/federationdomain/oidc/oidc.go b/internal/federationdomain/oidc/oidc.go index db187acfc..7e99e1eb0 100644 --- a/internal/federationdomain/oidc/oidc.go +++ b/internal/federationdomain/oidc/oidc.go @@ -1,4 +1,4 @@ -// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // Package oidc contains common OIDC functionality needed by FederationDomains to implement @@ -110,11 +110,14 @@ type UpstreamStateParamData struct { // DefaultOIDCTimeoutsConfiguration returns the default timeouts for the Supervisor server. func DefaultOIDCTimeoutsConfiguration() timeouts.Configuration { // Note: The maximum time that users can access Kubernetes clusters without - // needing to do a Supervisor refresh is the sum of the access token lifetime, - // the ID token lifetime, and the Concierge's mTLS client cert lifetime. - // This is because a client can exchange the access token just before it expires - // for a new cluster-scoped ID token, and use that just before it expires to get - // a new mTLS client cert, which grants access to the cluster until it expires. + // needing to do a Supervisor refresh is the sum of the authorization code, + // the access token lifetime, the ID token lifetime, and the Concierge's mTLS + // client cert lifetime. + // This is because a client can exchange the authcode just before it expires, + // for an access token, then exchange the access token just before it expires + // for a new cluster-scoped ID token, and then use that just before it expires to + // get a new mTLS client cert, which grants access to the cluster until it expires. + // For a refresh, the lifetime of the authcode would not be not included. // // Note that the Concierge's mTLS client cert lifetime is 5 minutes, which can // be seen in its source at credentialrequest/rest.go. @@ -127,7 +130,9 @@ func DefaultOIDCTimeoutsConfiguration() timeouts.Configuration { // identity provider that should be noticed by the Supervisor during an upstream // refresh. // - // Given the timeouts specified below, this is: 2 + 2 + 5 = 9 minutes. + // Given the timeouts specified below, this is: + // 10 + 2 + 2 + 5 = 19 minutes since an initial login, or + // 2 + 2 + 5 = 9 minutes since a refresh. // Note that this may be different if an OIDCClient's configuration has changed // the lifetime of the ID tokens issued to that client, but usually will not be // different because that configuration does not change the lifetime of the diff --git a/site/content/docs/background/architecture.md b/site/content/docs/background/architecture.md index 7238b619f..f87144f60 100644 --- a/site/content/docs/background/architecture.md +++ b/site/content/docs/background/architecture.md @@ -14,7 +14,7 @@ The principal purpose of Pinniped is to allow users to access Kubernetes clusters. Pinniped hopes to enable this access across a wide range of Kubernetes environments with zero configuration. -Pinniped is composed of two parts. +Pinniped is composed of three parts. 1. The Pinniped Supervisor is an OIDC server which allows users to authenticate with external identity providers (IDP), and then issues its own federation ID tokens @@ -24,6 +24,8 @@ credential from an identity source (e.g., Pinniped Supervisor, proprietary IDP), authenticates the user via that credential, and returns another credential which is understood by the host Kubernetes cluster or by an impersonation proxy which acts on behalf of the user. +1. The Pinniped CLI help generate Pinniped-compatible kubeconfigs for clusters +and helps end-users authenticate using those kubeconfigs. ![Pinniped Architecture Sketch](/docs/img/pinniped_architecture_concierge_supervisor.svg) @@ -37,7 +39,7 @@ Administrators will configure the Pinniped Supervisor to use IDPs via Kubernetes custom resources allowing Pinniped to be managed using GitOps and standard Kubernetes tools. -Pinniped supports the following IDPs. +Pinniped currently supports the following external identity provider types. 1. Any [OIDC-compliant](https://openid.net/specs/openid-connect-core-1_0.html) identity provider (e.g., [Dex](https://github.com/dexidp/dex), @@ -47,12 +49,12 @@ Pinniped supports the following IDPs. 1. Any Active Directory identity provider (via LDAP). +1. Identities from GitHub or GitHub Enterprise. + The [`idp.supervisor.pinniped.dev`](https://github.com/vmware-tanzu/pinniped/blob/main/generated/latest/README.adoc#k8s-api-idp-supervisor-pinniped-dev-v1alpha1) API group contains the Kubernetes custom resources that configure the Pinniped -Supervisor's upstream IDPs. - -More IDP integrations are coming soon. +Supervisor's external IDPs. ## Authenticators @@ -70,7 +72,7 @@ Pinniped supports the following authenticator types. serve as an extension point for Pinniped by allowing for integration of arbitrary custom authenticators. While a custom implementation may be in any language or framework, this project provides a sample implementation in Golang. See the `ServeHTTP` method of - [cmd/local-user-authenticator/main.go](https://github.com/vmware-tanzu/pinniped/blob/main/cmd/local-user-authenticator/main.go). + [internal/localuserauthenticator/localuserauthenticator.go](https://github.com/vmware-tanzu/pinniped/blob/main/internal/localuserauthenticator/localuserauthenticator.go). 1. A JSON Web Token (JWT) authenticator, which will validate and parse claims from JWTs. This can be used to validate tokens that are issued by the @@ -89,20 +91,28 @@ Concierge's authenticators. ## Cluster Integration Strategies -Pinniped will issue a cluster credential by leveraging cluster-specific +The Pinniped Concierge will issue a cluster credential by leveraging cluster-specific functionality. In the longer term, Pinniped hopes to contribute and leverage upstream Kubernetes extension points that cleanly enable this integration. -Pinniped supports the following cluster integration strategies. +The Concierge hosts a credential exchange API endpoint via a Kubernetes aggregated API server. +This API returns a short-lived mTLS client certificate which can then be used to invoke the Kubernetes API. +The method used to sign the short-lived cluster certificates depends on the cluster integration strategy selected +for that cluster. -* Token Credential Request API: Pinniped hosts a credential exchange API endpoint via a Kubernetes aggregated API server. -This API returns a new cluster-specific credential using the cluster's signing keypair to -issue short-lived cluster certificates. (In the future, when the Kubernetes CSR API -provides a way to issue short-lived certificates, then the Pinniped credential exchange API -will use that instead of using the cluster's signing keypair.) -* Impersonation Proxy: Pinniped hosts an [impersonation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation) -proxy that sends requests to the Kubernetes API server with user information and permissions based on a token. +The Concierge supports the following cluster integration strategies. + +* Kube Cluster Signing Certificate: Pinniped signs the short-lived client certificates using the + Kubernetes API server's signing key. These can be used to make Kubernetes API requests directly + to the Kubernetes API server. +* Impersonation Proxy: Pinniped signs the short-lived client certificates using its own signing key. + Pinniped hosts an + [impersonation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation) + proxy that relays requests to the Kubernetes API server with the end user's identity and group memberships. + When Kubernetes API requests are made through the impersonation proxy, Pinniped validates that the client's + certificate was signed by its own key before submitting the API request to the Kubernetes API server on + behalf of the user via impersonation as that user. ## kubectl Integration @@ -114,11 +124,12 @@ built with the [Pinniped Go client library](https://github.com/vmware-tanzu/pinn ## Pinniped Deployment Strategies + Pinniped can be configured to authenticate users in a variety of scenarios. Depending on the use case, administrators can deploy the Supervisor, the Concierge, both, or neither. -### Full Integration-- Concierge, Supervisor, and CLI +### Full Integration -- Concierge, Supervisor, and CLI Users can authenticate with the help of the Supervisor, which will issue tokens that can be exchanged at the Concierge for a credential that can be used to authenticate to @@ -138,7 +149,7 @@ JWT authenticator via the Pinniped Concierge. ![concierge-with-supervisor-sequence-diagram](/docs/img/pinniped-concierge-supervisor-sequence.svg) -### Dynamic Cluster Authentication-- Concierge and CLI +### Dynamic Cluster Authentication -- Concierge and CLI Users can authenticate directly with their OIDC compliant external identity provider to get credentials which can be exchanged at the Concierge for a credential that can be used to authenticate to @@ -154,7 +165,7 @@ that obtains an external credential to be sent to a webhook authenticator via th ![concierge-with-webhook-sequence-diagram](/docs/img/pinniped-concierge-sequence.svg) -### Static Cluster Integration-- Supervisor and CLI +### Static Cluster Integration -- Supervisor and CLI Users can authenticate with the help of the Supervisor, which will issue tokens that can be given directly to a Kubernetes API Server that has been configured with @@ -162,7 +173,7 @@ can be given directly to a Kubernetes API Server that has been configured with The Supervisor enables users to log in to their external identity provider once per day and access each cluster in a domain with a distinct scoped-down token. -### Minimal-- CLI only +### Minimal -- CLI only Users can authenticate directly with their OIDC compliant external identity provider to get credentials that can be given directly to a Kubernetes API Server that has been configured with diff --git a/site/content/docs/reference/tokens-and-credentials.md b/site/content/docs/reference/tokens-and-credentials.md new file mode 100644 index 000000000..f9dd66ed8 --- /dev/null +++ b/site/content/docs/reference/tokens-and-credentials.md @@ -0,0 +1,107 @@ + +--- +title: Tokens and credentials +description: A description of every token and credential issued to clients by Pinniped. +cascade: + layout: docs +menu: + docs: + name: Tokens and credentials + weight: 70 + parent: reference +--- + +Pinniped issues several types of tokens and credentials to clients to help users access Kubernetes clusters. +This document will explain the tokens and credentials issued when the Pinniped Supervisor, Concierge, and CLI +are all configured to work together. + +All issued tokens and credentials are short-lived and therefore must be refreshed often. Forcing users to refresh +tokens and credentials often gives Pinniped an opportunity to revalidate the user's identity and group memberships. +If the administrator of the external identity provider has removed or locked the user's account, or has changed +the user's group memberships, then they typically would like that change to take effect within Kubernetes clusters +as quickly as possible. + +Note that none of the token or credential lifetimes described in this document are currently configurable. +(One exception is the lifetime of ID tokens issued to OAuth2 clients created as `OIDCClients` may be configured +by the administrator, but that does not apply when using the Pinniped CLI, +which always uses the OAuth2 client called `pinniped-cli`.) + +When an admin user generates a Pinniped-compatible kubeconfig, that kubeconfig will use the Pinniped CLI as a +[Kubernetes client-go credential plugin](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#client-go-credential-plugins). +Whenever kubectl (or any similar client) needs a credential to +access a cluster, it will defer to the Pinniped CLI to provide that credential. + +Logging in using the Pinniped Supervisor via the Pinniped CLI is done using the +[OIDC authorization code flow](https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth). + +This flow starts by the CLI making several discovery requests to learn about the Supervisor's configuration. +These are public endpoints that do not require authentication, and do not expose any sensitive information. + +Next, the CLI makes a request to the Supervisor's authorization endpoint. If the user is starting a browser-based +flow, then authenticating with their external identity provider will involve several browser redirects, eventually +redirecting back to the callback endpoint of the Supervisor, which will issue an authorization code to the CLI for +a successful authentication. On the other hand, if the user is starting a CLI-based flow where the CLI collects +the user's username and password, then the authorization endpoint itself will issue the authorization code for a +successful authentication. Either way, the authorization code is valid for 10 minutes. +This gives the user sufficient time to copy/paste the authorization code to the CLI for those browsers (like Safari) +which cannot send the code to the CLI automatically due to security restrictions. + +The authorization code can then be exchanged using the Supervisor's token endpoint for the following +Supervisor-issued tokens, which are cached by the CLI in a file in the user's home directory. +- An ID token, which is valid for 2 minutes. This initial ID token is typically not used. +- An opaque access token, which is valid for 2 minutes. This token will be used to fetch a new, cluster-scoped access token. + It will only be sent back to the Supervisor, and never to any other server. +- An opaque refresh token, which is valid for 9 hours from the time of the initial authentication. + This token will be used to perform an OIDC refresh grant to fetch a new set of ID, access, and refresh tokens. + It will only be sent back to the Supervisor, and never to any other server. + +The next step in the flow is typically another call to the Supervisor to send the access token and request a new +ID token which is scoped down for access to one particular cluster. This request is made to the Supervisor's token +endpoint as an RFC8693 token exchange. This new ID token is valid for 2 minutes. +This is the only token that will be sent to the cluster that the user is trying to access. This token has no +value on any other cluster. If the token is somehow intercepted by another user on that cluster through a security +compromise, they cannot use it to access any other cluster. + +Typically, the final step is to make a request to the Concierge on cluster which the user is attempting to access. +This request sends the cluster-scoped ID token to the Concierge's `TokenCredentialRequest` API, and receives an +mTLS client certificate in the response. This certificate is valid from 5 minutes ago until 5 minutes in the future. +The backdating is to allow for some small amount of clock skew between hosts and is the same amount of backdating +done by Kubernetes itself when it issues client certificates. This client certificate can be used to make +Kubernetes API requests. This certificate is cached by the CLI in a file in the user's home directory. + +The CLI will continue to use this client certificate for future Kubernetes API calls until it expires. By the time +the cert has expired, the access token will have also expired. This means that the user must perform an OIDC refresh flow +with the Supervisor before they can make any more Kubernetes API calls. The CLI automatically sends the user's refresh +token to the Supervisor's token endpoint. +The Supervisor performs checks with the external identity provider to decide if the user's session should +be allowed to continue, and to update the user's group memberships. A successful refresh results in a new ID token, +a new access token, and a new refresh token, all with the same lifetimes as before. The old refresh token is revoked. +This refresh process is automatic and does not require any user interaction. A failed refresh will cause the CLI +to start all over again with a new authentication attempt. + +Note that the user can use their session to access many clusters without needing to log in again. +Accessing the Kubernetes API of a different cluster will cause the CLI to fetch a new cluster-scoped ID token +for that cluster, and then fetch a new credential for that cluster. This process does not require any user interaction. + +In this flow, the maximum amount of time that a user's identity and group information +as seen by the Kubernetes API can be stale is the amount of time that they could delay between +authenticating with the external identity provider and the expiry of the cluster credential. +For an initial login, this is 10 + 2 + 2 + 5 = 19 minutes. This represents the lifetime of the authorization code, +the access token, the cluster-scoped ID token, and the cluster credential itself. +When refreshing a session, the authorization code does not apply, so the maximum amount of time that +a user's identity and group information as seen by the Kubernetes API can be stale after a refresh +is 2 + 2 + 5 = 9 minutes. +The CLI will perform the token exchanges immediately without delay, +so the more typical time is approximately 5 minutes. This means that when an identity provider administrator +revokes a user's access or updates their group information in a way that Pinniped can detect, +those changes are typically seen by all clusters within about 5 minutes, and worst case within about 19 minutes. + +The maximum amount of time that any user can continue to refresh their Supervisor session is 9 hours from +the initial login time. After that, the next refresh will fail and the user must perform a fresh login. +This ensures that the user's access privileges are updated at least once a day, even if the Supervisor +cannot detect an access privilege change made in the external identity provider during the day. + +A user's session my terminate quicker if the Supervisor determines from an external identity provider that +the session should end. One example of this is that for an `OIDCIdentityProvider`, the Supervisor will +typically receive a refresh token for a user from the external OIDC provider, and when this token expires +then the user's Supervisor session will also expire, which can be shorter than 9 hours.