From 96e444218101dccabdc039d34e72d3ccc2050c1c Mon Sep 17 00:00:00 2001 From: Joshua Casey Date: Wed, 10 Apr 2024 07:08:08 -0500 Subject: [PATCH 1/2] Add docs to configure the Supervisor with a GitHub IDP. - Does not include docs for configuring GitHub Apps or GitHub OAuth Apps --- .../configure-supervisor-with-github.md | 167 ++++++++++++++++++ test/integration/kube_api_discovery_test.go | 2 +- 2 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 site/content/docs/howto/supervisor/configure-supervisor-with-github.md diff --git a/site/content/docs/howto/supervisor/configure-supervisor-with-github.md b/site/content/docs/howto/supervisor/configure-supervisor-with-github.md new file mode 100644 index 000000000..b28337a90 --- /dev/null +++ b/site/content/docs/howto/supervisor/configure-supervisor-with-github.md @@ -0,0 +1,167 @@ +--- +title: Configure the Pinniped Supervisor to use GitHub as an identity provider +description: Set up the Pinniped Supervisor to use GitHub as an identity provider. +cascade: + layout: docs +menu: + docs: + name: With GitHub + weight: 80 + parent: howto-configure-supervisor +aliases: + - /docs/howto/configure-supervisor-with-github/ +--- +The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting +"upstream" identity providers to many "downstream" cluster clients. + +This guide shows you how to configure the Supervisor so that users can authenticate to their Kubernetes +cluster using their credentials from [GitHub.com](https://github.com) or [GitHub enterprise server](https://docs.github.com/en/enterprise-server@latest/admin/overview/about-github-enterprise-server). + +Currently, Pinniped supports GitHub API version `2022-11-28` ([ref](https://docs.github.com/en/rest/about-the-rest-api/api-versions?apiVersion=2022-11-28)). +Future GitHub API versions may include breaking changes. + +## Prerequisites + +This how-to guide assumes that you have already [installed the Pinniped Supervisor]({{< ref "install-supervisor" >}}) with working ingress, +and that you have [configured a FederationDomain to issue tokens for your downstream clusters]({{< ref "configure-supervisor" >}}). + +## Create a GitHub App + +TODO: add text + +## Create a GitHub OAuth App + +TODO: add text + +## Configure the Supervisor + +Create a [GitHubIdentityProvider](https://github.com/vmware-tanzu/pinniped/blob/main/generated/{{< latestcodegenversion >}}/README.adoc#githubidentityprovider) in the same namespace as the Supervisor. + +The simplest example uses https://github.com as the source of identity, and will allow any user with a GitHub account to log in. +Note that you do not need to explicitly specify a GitHub host since `github.com` is the default. + +```yaml +apiVersion: idp.supervisor.pinniped.dev/v1alpha1 +kind: GitHubIdentityProvider +metadata: + namespace: pinniped-supervisor + name: github-dot-com +spec: + client: + secretName: github-dot-com-client-credentials + allowAuthentication: + organizations: + policy: AllGitHubUsers +--- +apiVersion: v1 +kind: Secret +type: secrets.pinniped.dev/github-client +metadata: + namespace: pinniped-supervisor + name: github-dot-com-client-credentials +stringData: + # The "Client ID" from the GitHub App or GitHub OAuth App. + clientID: "" + # The "Client secret" from the GitHub App or GitHub OAuth App. + clientSecret: "" +``` + +For another example, let's fill out all fields. +Here we will configure a host and certificate for a GitHub Enterprise Server installation. + +```yaml +apiVersion: idp.supervisor.pinniped.dev/v1alpha1 +kind: GitHubIdentityProvider +metadata: + namespace: pinniped-supervisor + name: github-enterprise +spec: + githubAPI: + # Only the hostname or IP address and optional port, without the protocol. + # Pinniped will always use HTTPS. + host: github.enterprise.tld + tls: + # Specify the CA certificate of the server as a + # base64-encoded PEM bundle. + certificateAuthorityData: LS0tLS1CRUdJTiBDRVJUSUZJQ0FU.... + + client: + secretName: github-enterprise-client-credentials + allowAuthentication: + + # "policy: OnlyUsersFromAllowedOrganizations" restricts authentication to only + # those users who belong in at least one of the "allowed" organizations. + # Additionally, their groups as presented to K8s will only reflect team + # membership within these organizations. + organizations: + policy: OnlyUsersFromAllowedOrganizations + allowed: + - my-enterprise-organization + - admin-organization + + claims: + + # Use the login attribute of a user as the username to present to K8s. + # This attribute is taken from GitHub API endpoint "Get the authenticated user". + # https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user. + username: login + + # Use the name attribute of a team as the group name to present to K8s for RBAC . + # This attribute is taken from GitHub API endpoint "List teams for the authenticated user". + # https://docs.github.com/en/rest/teams/teams?apiVersion=2022-11-28#list-teams-for-the-authenticated-user. + groups: name + +--- +apiVersion: v1 +kind: Secret +type: secrets.pinniped.dev/github-client +metadata: + namespace: pinniped-supervisor + name: github-enterprise-client-credentials +stringData: + # The "Client ID" from the GitHub App or GitHub OAuth App. + clientID: "" + # The "Client secret" from the GitHub App or GitHub OAuth App. + clientSecret: "" +``` + +Once your GitHubIdentityProvider has been created, you can validate your configuration by running: + +```shell +kubectl describe GitHubIdentityProvider -n pinniped-supervisor +``` + +Look at the `status` field. If it was configured correctly, you should see `status.phase: Ready`. +Otherwise, inspect the `status.conditions` array for more information. + +## Additional authentication restrictions + +The GitHubIdentityProvider specification permits restricting authentication based on organization membership. +It's possible to use CEL expressions as part of a [policy expression pipeline]({{< ref "configure-supervisor-federationdomain-idps" >}}) to further restrict authentication. + +For example, to restrict authentication to a set of users who do not have any shared organization membership, you can add a policy similar to the following: + +```yaml + transforms: + constants: + - name: allowedUsersByLogin + type: stringList + stringListValue: + - "cfryanr" + - "benjaminapetersen" + - "joshuatcasey" + expressions: + - type: policy/v1 + expression: 'username in strListConst.allowedUsersByLogin' + message: "Only specified users may authenticate" +``` + +In this case, you will need to set `spec.allowAuthentication.organizations.policy: AllGitHubUsers` so that organization membership will not be used to restrict authentication. + +Remember that Pinniped may not be able to see the user's teams in every organization unless the GitHub App is installed in that organization and given appropriate permissions, +or the user's organization membership is public and that organization has chosen to make its team membership public. + +## Next steps + +Next, [configure the Concierge to validate JWTs issued by the Supervisor]({{< ref "configure-concierge-supervisor-jwt" >}})! +Then you'll be able to log into those clusters as any of the users from GitHub. diff --git a/test/integration/kube_api_discovery_test.go b/test/integration/kube_api_discovery_test.go index e8fbd5cc2..101234c96 100644 --- a/test/integration/kube_api_discovery_test.go +++ b/test/integration/kube_api_discovery_test.go @@ -452,7 +452,7 @@ func TestGetAPIResourceList(t *testing.T) { //nolint:gocyclo // each t.Run is pr } // manually update this value whenever you add additional fields to an API resource and then run the generator - totalExpectedAPIFields := 263 + totalExpectedAPIFields := 289 // Because we are parsing text from `kubectl explain` and because the format of that text can change // over time, make a rudimentary assertion that this test exercised the whole tree of all fields of all From f377292ffe73cbe193a702a504b2fd81f4f9e5b5 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Thu, 9 May 2024 10:53:48 -0700 Subject: [PATCH 2/2] change some wording in github doc --- .../configure-supervisor-with-github.md | 96 ++++++++++++------- 1 file changed, 60 insertions(+), 36 deletions(-) diff --git a/site/content/docs/howto/supervisor/configure-supervisor-with-github.md b/site/content/docs/howto/supervisor/configure-supervisor-with-github.md index b28337a90..283f36185 100644 --- a/site/content/docs/howto/supervisor/configure-supervisor-with-github.md +++ b/site/content/docs/howto/supervisor/configure-supervisor-with-github.md @@ -17,9 +17,6 @@ The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer This guide shows you how to configure the Supervisor so that users can authenticate to their Kubernetes cluster using their credentials from [GitHub.com](https://github.com) or [GitHub enterprise server](https://docs.github.com/en/enterprise-server@latest/admin/overview/about-github-enterprise-server). -Currently, Pinniped supports GitHub API version `2022-11-28` ([ref](https://docs.github.com/en/rest/about-the-rest-api/api-versions?apiVersion=2022-11-28)). -Future GitHub API versions may include breaking changes. - ## Prerequisites This how-to guide assumes that you have already [installed the Pinniped Supervisor]({{< ref "install-supervisor" >}}) with working ingress, @@ -37,8 +34,11 @@ TODO: add text Create a [GitHubIdentityProvider](https://github.com/vmware-tanzu/pinniped/blob/main/generated/{{< latestcodegenversion >}}/README.adoc#githubidentityprovider) in the same namespace as the Supervisor. -The simplest example uses https://github.com as the source of identity, and will allow any user with a GitHub account to log in. +The simplest example uses https://github.com as the source of identity. Note that you do not need to explicitly specify a GitHub host since `github.com` is the default. +This example allows any user with a GitHub account to log in. +You may prefer to limit which users may authenticate by GitHub organization or team membership. +See the following examples for more information about limiting which users can authenticate. ```yaml apiVersion: idp.supervisor.pinniped.dev/v1alpha1 @@ -66,8 +66,7 @@ stringData: clientSecret: "" ``` -For another example, let's fill out all fields. -Here we will configure a host and certificate for a GitHub Enterprise Server installation. +For another example, let's fill out all available fields. ```yaml apiVersion: idp.supervisor.pinniped.dev/v1alpha1 @@ -77,39 +76,45 @@ metadata: name: github-enterprise spec: githubAPI: + # This field is only required when using GitHub Enterprise Server. # Only the hostname or IP address and optional port, without the protocol. # Pinniped will always use HTTPS. host: github.enterprise.tld tls: + # This field is usually only used for GitHub Enterprise Server. # Specify the CA certificate of the server as a # base64-encoded PEM bundle. certificateAuthorityData: LS0tLS1CRUdJTiBDRVJUSUZJQ0FU.... client: secretName: github-enterprise-client-credentials - allowAuthentication: + allowAuthentication: # "policy: OnlyUsersFromAllowedOrganizations" restricts authentication to only - # those users who belong in at least one of the "allowed" organizations. + # those users who belong to at least one of the "allowed" organizations. # Additionally, their groups as presented to K8s will only reflect team # membership within these organizations. organizations: policy: OnlyUsersFromAllowedOrganizations allowed: - my-enterprise-organization - - admin-organization + - my-other-organization claims: - - # Use the login attribute of a user as the username to present to K8s. - # This attribute is taken from GitHub API endpoint "Get the authenticated user". - # https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user. - username: login - - # Use the name attribute of a team as the group name to present to K8s for RBAC . - # This attribute is taken from GitHub API endpoint "List teams for the authenticated user". - # https://docs.github.com/en/rest/teams/teams?apiVersion=2022-11-28#list-teams-for-the-authenticated-user. - groups: name + # This field chooses how the username will be presented to K8s. + # Allowed values are "id", "login", or "login:id". The login and id attributes + # are taken from the results of the GitHub "/user" API endpoint. + # See https://docs.github.com/en/rest/users/users. + # Using "id" or "login:id" is recommended because GitHub users can change their + # own login name, but cannot change their numeric ID. + username: "login:id" + # This field chooses how the team names will be presented to K8s as group names. + # Allowed values are "name" or "slug". The name and slug attributes + # are taken from the results of the GitHub "/user/teams" API endpoint. + # See https://docs.github.com/en/rest/teams/teams. + # E.g. for a team named "Kube admins!", the name will be "Kube admins!" + # while the slugs will be "kube-admins". + groups: slug --- apiVersion: v1 @@ -134,32 +139,51 @@ kubectl describe GitHubIdentityProvider -n pinniped-supervisor Look at the `status` field. If it was configured correctly, you should see `status.phase: Ready`. Otherwise, inspect the `status.conditions` array for more information. +## Org and Team membership visibility + +Pinniped may not be able to see which organizations to which a user belongs, or which teams to which a user +belongs within an org. When Pinniped is configured to restrict authentication by org membership, it will reject a user's +authentication when it cannot see that the user belongs to one of the required orgs. +Furthermore, the user's team memberships will only be presented to Kubernetes as group names for those +teams that Pinniped is allowed to see. +Which orgs and teams are returned by the GitHub API is controlled by the GitHub App or GitHub OAuth App that you configure. + +In order for a GitHub App or GitHub OAuth App to see the team memberships with an org, the app must either: +1. Be owned (created) by that Org +2. Or, be approved by the owners of that Org for use with that Org + +Note that for a Github OAuth app, the owner of an org may also choose to implicitly approve +all GitHub OAuth Apps owned by members of the org. + ## Additional authentication restrictions The GitHubIdentityProvider specification permits restricting authentication based on organization membership. -It's possible to use CEL expressions as part of a [policy expression pipeline]({{< ref "configure-supervisor-federationdomain-idps" >}}) to further restrict authentication. +It's possible to use CEL expressions as part of a [policy expression pipeline]({{< ref "configure-supervisor-federationdomain-idps" >}}) +to further restrict authentication based on usernames and group names. -For example, to restrict authentication to a set of users who do not have any shared organization membership, you can add a policy similar to the following: +For example, when you use `spec.allowAuthentication.organizations.policy: AllGitHubUsers` then any GitHub user +can authenticate. A CEL expression could be used to further restrict authentication to a set of specific users +with a policy like this: ```yaml - transforms: - constants: - - name: allowedUsersByLogin - type: stringList - stringListValue: - - "cfryanr" - - "benjaminapetersen" - - "joshuatcasey" - expressions: - - type: policy/v1 - expression: 'username in strListConst.allowedUsersByLogin' - message: "Only specified users may authenticate" +transforms: + constants: + - name: allowedUsers + type: stringList + stringListValue: + - "cfryanr" + - "joshuatcasey" + expressions: + - type: policy/v1 + expression: 'username in strListConst.allowedUsers' + message: "Only certain GitHub users may authenticate" ``` -In this case, you will need to set `spec.allowAuthentication.organizations.policy: AllGitHubUsers` so that organization membership will not be used to restrict authentication. +You could also use similar CEL expressions to limit authentication by GitHub team membership. -Remember that Pinniped may not be able to see the user's teams in every organization unless the GitHub App is installed in that organization and given appropriate permissions, -or the user's organization membership is public and that organization has chosen to make its team membership public. +## Notes + +Currently, Pinniped supports GitHub API version `2022-11-28` ([ref](https://docs.github.com/en/rest/about-the-rest-api/api-versions?apiVersion=2022-11-28)). ## Next steps