Merge pull request #1660 from vmware-tanzu/multiple_idps_and_transformations_docs
multiple idps and identity transformations docs
This commit is contained in:
commit
efb53d3190
@ -17,7 +17,7 @@ environments with zero configuration.
|
|||||||
Pinniped is composed of two parts.
|
Pinniped is composed of two parts.
|
||||||
|
|
||||||
1. The Pinniped Supervisor is an OIDC server which allows users to authenticate
|
1. The Pinniped Supervisor is an OIDC server which allows users to authenticate
|
||||||
with an external identity provider (IDP), and then issues its own federation ID tokens
|
with external identity providers (IDP), and then issues its own federation ID tokens
|
||||||
to be passed on to clusters based on the user information from the IDP.
|
to be passed on to clusters based on the user information from the IDP.
|
||||||
1. The Pinniped Concierge is a credential exchange API which takes as input a
|
1. The Pinniped Concierge is a credential exchange API which takes as input a
|
||||||
credential from an identity source (e.g., Pinniped Supervisor, proprietary IDP),
|
credential from an identity source (e.g., Pinniped Supervisor, proprietary IDP),
|
||||||
|
@ -46,6 +46,11 @@ framework (e.g. Spring, Rails, Django, etc.) to implement authentication. The Su
|
|||||||
- Clients must use `query` as the
|
- Clients must use `query` as the
|
||||||
[response_mode](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) at the authorization endpoint,
|
[response_mode](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) at the authorization endpoint,
|
||||||
or not specify the `response_mode` param, which defaults to `query`.
|
or not specify the `response_mode` param, which defaults to `query`.
|
||||||
|
- If the Supervisor's FederationDomain was configured with explicit `identityProviders` in its spec, then the
|
||||||
|
client must send an extra parameter on the initial authorization request to indicate which identity provider
|
||||||
|
the user would like to use when authenticating. This parameter is called `pinniped_idp_name` and the value
|
||||||
|
of the parameter should be set to the `displayName` of the identity provider as it was configured on the
|
||||||
|
FederationDomain.
|
||||||
|
|
||||||
Most web application frameworks offer all these capabilities in their OAuth2/OIDC libraries.
|
Most web application frameworks offer all these capabilities in their OAuth2/OIDC libraries.
|
||||||
|
|
||||||
|
@ -76,6 +76,11 @@ One flag of note is `--upstream-identity-provider-flow browser_authcode` to choo
|
|||||||
(the default for OIDCIdentityProviders), and `--upstream-identity-provider-flow cli_password` to choose end-user `kubectl`
|
(the default for OIDCIdentityProviders), and `--upstream-identity-provider-flow cli_password` to choose end-user `kubectl`
|
||||||
login via CLI username/password prompts (the default for LDAPIdentityProviders and ActiveDirectoryIdentityProviders).
|
login via CLI username/password prompts (the default for LDAPIdentityProviders and ActiveDirectoryIdentityProviders).
|
||||||
|
|
||||||
|
If the cluster is using a Pinniped Supervisor's FederationDomain to provide authentication services,
|
||||||
|
and if that FederationDomain allows multiple identity providers, then you will need to specify which identity provider
|
||||||
|
you would like to use in the resulting kubeconfig with the `--upstream-identity-provider-name` and/or `--upstream-identity-provider-type` flags.
|
||||||
|
You may call `pinniped get kubeconfig` multiple times to generate multiple kubeconfigs for the cluster.
|
||||||
|
|
||||||
## Use the generated kubeconfig with `kubectl` to access the cluster
|
## Use the generated kubeconfig with `kubectl` to access the cluster
|
||||||
|
|
||||||
A cluster user will typically be given a Pinniped-compatible kubeconfig by their cluster admin. They can use this kubeconfig
|
A cluster user will typically be given a Pinniped-compatible kubeconfig by their cluster admin. They can use this kubeconfig
|
||||||
|
@ -0,0 +1,414 @@
|
|||||||
|
---
|
||||||
|
title: Configure Identity Providers (IDPs) on a FederationDomain
|
||||||
|
description: Learn how to use one or more identity providers, and identity transformations and policies, on a FederationDomain.
|
||||||
|
cascade:
|
||||||
|
layout: docs
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
name: IDPs on FederationDomains
|
||||||
|
weight: 20
|
||||||
|
parent: howto-configure-supervisor
|
||||||
|
---
|
||||||
|
|
||||||
|
This guide explains how to associate one or more external identity providers (IDPs) with a FederationDomain.
|
||||||
|
It also details how to configure identity transformations and identity policies for those identity
|
||||||
|
providers.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
This how-to guide assumes that you have already [installed the Pinniped Supervisor]({{< ref "install-supervisor" >}})
|
||||||
|
and have already read the guide about how to [configure the Supervisor as an OIDC issuer]({{< ref "configure-supervisor" >}}).
|
||||||
|
|
||||||
|
This guide focuses on the use of the `spec.identityProviders` setting on the
|
||||||
|
[FederationDomain](https://github.com/vmware-tanzu/pinniped/blob/main/generated/{{< latestcodegenversion >}}/README.adoc#federationdomain)
|
||||||
|
CRD.
|
||||||
|
|
||||||
|
Note that the `spec.identityProviders` setting on the FederationDomain CRD was added in v0.26.0 of Pinniped.
|
||||||
|
This guide assumes that you are using at least that version.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
External identity providers may be configured in the Supervisor by creating OIDCIdentityProvider,
|
||||||
|
ActiveDirectoryIdentityProvider, or LDAPIdentityProvider resources in the same namespace as the Supervisor.
|
||||||
|
|
||||||
|
There are two ways to configure which of these external identity providers shall be used by a FederationDomain.
|
||||||
|
|
||||||
|
1. When there is no `spec.identityProviders` configured on a FederationDomain, then the FederationDomain will use
|
||||||
|
the one and only identity provider that is configured in the same namespace. This provides backwards compatibility
|
||||||
|
with older configurations of Supervisors from before the `spec.identityProviders` setting was added to the
|
||||||
|
FederationDomain CRD. There must be exactly one OIDCIdentityProvider,
|
||||||
|
ActiveDirectoryIdentityProvider, or LDAPIdentityProvider resource in the same namespace as the Supervisor.
|
||||||
|
If there are no identity provider resources, or if there are more than one, then the FederationDomain will
|
||||||
|
not allow any users to authenticate, and a error message will be shown in its `status`.
|
||||||
|
|
||||||
|
2. When `spec.identityProviders` is explicitly configured on a FederationDomain, then the FederationDomain will
|
||||||
|
allow clients to use any of those identity providers to authenticate. In this case, you may optionally also configure
|
||||||
|
identity transformations and policies that the FederationDomain should apply to each of these identity providers
|
||||||
|
(see below for details). When using the `pinniped get kubeconfig` CLI command, you will need to choose for
|
||||||
|
which identity provider you would like to generate a kubeconfig. A cluster may have multiple kubeconfigs,
|
||||||
|
e.g. one for each identity provider.
|
||||||
|
|
||||||
|
The remainder of this guide focuses on the second case, and describes the settings that may be used to explicitly
|
||||||
|
configure which identity providers are used, along with optional identity transformations and policies.
|
||||||
|
|
||||||
|
## Configuring a FederationDomain's identity providers
|
||||||
|
|
||||||
|
A user may authenticate to a FederationDomain using any of the IDPs configured in the FederationDomain's
|
||||||
|
`spec.identityProviders`. To add IDPs to this list, simply configure each as a reference to the type and name
|
||||||
|
of the resource. The identity provider resources must be in the same namespace where the Supervisor was
|
||||||
|
[installed]({{< ref "docs/howto/install-supervisor.md" >}}).
|
||||||
|
|
||||||
|
Here is an example FederationDomain with two IDPs configured.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: config.supervisor.pinniped.dev/v1alpha1
|
||||||
|
kind: FederationDomain
|
||||||
|
metadata:
|
||||||
|
name: my-provider
|
||||||
|
# Must be in the same namespace where the Supervisor is installed.
|
||||||
|
namespace: pinniped-supervisor
|
||||||
|
spec:
|
||||||
|
issuer: https://my-issuer.example.com/any/path
|
||||||
|
tls:
|
||||||
|
secretName: my-tls-cert-secret
|
||||||
|
# Available identity providers are selected here...
|
||||||
|
identityProviders:
|
||||||
|
- displayName: ActiveDirectory for Admins
|
||||||
|
objectRef:
|
||||||
|
apiGroup: idp.supervisor.pinniped.dev
|
||||||
|
kind: ActiveDirectoryIdentityProvider
|
||||||
|
name: ad-for-admins
|
||||||
|
- displayName: Okta for Developers
|
||||||
|
objectRef:
|
||||||
|
apiGroup: idp.supervisor.pinniped.dev
|
||||||
|
kind: OIDCIdentityProvider
|
||||||
|
name: okta-for-developers
|
||||||
|
```
|
||||||
|
|
||||||
|
Now users may use either of the above identity providers to authenticate. You can create
|
||||||
|
kubeconfigs for both IDPs for each cluster by using `pinniped get kubeconfig` twice for
|
||||||
|
each cluster.
|
||||||
|
|
||||||
|
## Important consideration when using multiple identity providers: conflicting usernames and group names
|
||||||
|
|
||||||
|
When multiple identity providers are configured onto a FederationDomain, then a user may use any of those
|
||||||
|
providers to authenticate to that FederationDomain. Since an identity in Kubernetes is simply a username string
|
||||||
|
and a list of group name strings, it is very important to consider what will happen if two users each authenticate
|
||||||
|
to a FederationDomain using different identity providers.
|
||||||
|
|
||||||
|
1. If two users are assigned the same username string as a result of authenticating, then those two users will be
|
||||||
|
considered the *same user* by Kubernetes, regardless of which IDP they came from.
|
||||||
|
2. If two users are assigned the same group name string as a result of authenticating, then those two users will be
|
||||||
|
considered to *both belong to same group* by Kubernetes, regardless of which IDP they came from.
|
||||||
|
|
||||||
|
This may or may not be desirable. If the two identity providers are intended to represent distinct, non-overlapping
|
||||||
|
sets of users, then it is not desirable for their usernames and group names to conflict by being identical.
|
||||||
|
On the other hand, if two identity providers contain the same sets of users, and when those users authenticate
|
||||||
|
then your intention is that they are the same identity, then it may be desirable.
|
||||||
|
|
||||||
|
Let's consider several example use cases.
|
||||||
|
- Imagine two IDPs in which usernames and group names are assigned to users by their administrators
|
||||||
|
separately, with no coordination between them. In this case, a user named "ryan" from one IDP is probably not
|
||||||
|
the same human as the user named "ryan" from the other IDP. The group named "admins" from one IDP may
|
||||||
|
have a totally different intended meaning then the group named "admins" from the other IDP.
|
||||||
|
In this case, it is not desirable for usernames and group names from one IDP to conflict with the usernames
|
||||||
|
and group names from the other IDP.
|
||||||
|
- Imagine two IDPs which both use corporate email addresses as usernames. These email addresses are
|
||||||
|
assigned by IT and are not adjustable by the individual users. In this case, it may be desirable for a human
|
||||||
|
user to be able to authenticate using either IDP and end up being assigned the same username in
|
||||||
|
Kubernetes clusters either way. However, it may or may not be the case that the user's group names from one IDP
|
||||||
|
are meant to represent the same Kubernetes groups as the same group names from the other IDP.
|
||||||
|
In this case, it is not a concern for usernames from one IDP to conflict with the usernames from the other IDP,
|
||||||
|
however it still might be a concern for group names from one IDP to conflict with the group names from the other IDP.
|
||||||
|
|
||||||
|
You can easily add configuration to the FederationDomain to handle username and group name conflicts.
|
||||||
|
1. When it is not desirable for usernames to conflict, then a simple solution is to use identity transformations
|
||||||
|
to change all usernames to have a prefix which is unique to each IDP within that FederationDomain. For example,
|
||||||
|
usernames `ldap:ryan` and `gitlab:ryan` will be considered two different users by Kubernetes.
|
||||||
|
2. Similarly, when it is not desirable for group names to conflict, then a simple solution is to use identity transformations
|
||||||
|
to change all group names to have a prefix which is unique to each IDP within that FederationDomain.
|
||||||
|
|
||||||
|
Refer to the next section to learn about how to configure identity transformations to add prefixes to usernames
|
||||||
|
and group names.
|
||||||
|
|
||||||
|
## Identity transformations and policies
|
||||||
|
|
||||||
|
When a user authenticates, the configuration of the OIDCIdentityProvider, ActiveDirectoryIdentityProvider, or
|
||||||
|
LDAPIdentityProvider resource determines how the user's username and group names will be extracted from the external
|
||||||
|
identity provider in a protocol-specific way (e.g. via OIDC ID token claims or LDAP record attributes).
|
||||||
|
|
||||||
|
Then, operating on the username and group names extracted from the external IDP:
|
||||||
|
- Identity **transformations** can change either the user's username or group names.
|
||||||
|
- Identity **policies** can reject the user's authentication based on their username and/or groups.
|
||||||
|
|
||||||
|
Identity transformations and policies are configured on the FederationDomain, so they are specific to that specific
|
||||||
|
FederationDomain's usage of the external identity provider.
|
||||||
|
|
||||||
|
Transformations and policies are configured using the Common Expression Language (CEL) programming language.
|
||||||
|
They are configured as a list, and they will be executed in the order specified. The output of each transformation
|
||||||
|
or policy expression may impact the input values for the next expression from the list.
|
||||||
|
|
||||||
|
Pinniped's implementation of CEL expressions includes the
|
||||||
|
[standard language features](https://github.com/google/cel-spec/blob/master/doc/langdef.md)
|
||||||
|
as well as [the string extensions](https://github.com/google/cel-go/tree/master/ext#strings).
|
||||||
|
|
||||||
|
### Pipelines of identity transformation and policy `expressions`
|
||||||
|
|
||||||
|
There are three types of transformation expressions:
|
||||||
|
- `username/v1` are expressions which may change the user's username. These expressions must return a string,
|
||||||
|
and the value of the string will be the user's username. Returning an empty string or a string that contains
|
||||||
|
only whitespace characters will cause an authentication error. Returning the value of the `username` variable
|
||||||
|
unmodified will leave the username unchanged.
|
||||||
|
- `groups/v1` are expressions which may change the group names of the groups to which the user belongs.
|
||||||
|
These expressions must return a list of strings. The returned list may be empty. The returned list will
|
||||||
|
be the names of the groups to which the user belongs. Returning the value of the `groups` variable
|
||||||
|
unmodified will leave the groups unchanged.
|
||||||
|
- `policy/v1` are expressions which may reject the user's authentication based on their username and/or groups.
|
||||||
|
These expressions must return a boolean. Returning true has no impact on the user's authentication
|
||||||
|
and will therefore allow the user's authentication to continue. Returning true also has no impact
|
||||||
|
on the username or group names. Returning false will the reject user's authentication
|
||||||
|
and the user will see the error message configured for that policy expression (or a default error message).
|
||||||
|
Rejecting a user's authentication prevents the user from authenticating into every cluster
|
||||||
|
which uses this FederationDomain for identity services. This happens before
|
||||||
|
Kubernetes RBAC policies are considered by the individual clusters. Therefore, this is a authentication-level
|
||||||
|
rejection, not an authorization check.
|
||||||
|
|
||||||
|
All three transformation expression types are written using CEL expressions. They are declared as a list of transformations and policies.
|
||||||
|
Each time a user attempts to authenticate, and each time a user's session is automatically refreshed periodically,
|
||||||
|
the list is evaluated in the order that it was declared.
|
||||||
|
`username/v1` expressions may change the username that is passed to the next expressions.
|
||||||
|
`groups/v1` expressions may change the group names that are passed to the next expressions.
|
||||||
|
`policy/v1` expressions may halt the processing of further expressions when they reject the authentication.
|
||||||
|
Because each expression in the list can pass information to the following expressions via its return values,
|
||||||
|
the list of expressions acts like a "pipeline".
|
||||||
|
Any unexpected runtime evaluation errors (e.g. division by zero) cause the authentication to fail.
|
||||||
|
|
||||||
|
The following variables are available to each expression, regardless of type:
|
||||||
|
- `username` is a string. The value will be the username of the user who is attempting to authenticate.
|
||||||
|
Its value will never be the empty string. The value of `username` may have been modified by the
|
||||||
|
previous `username/v1` transformations in the pipeline.
|
||||||
|
- `groups` is a list of strings. The value will be the list of group names to which
|
||||||
|
the user attempting to authenticate belongs. The list may be empty, meaning that the user does not belong to any groups.
|
||||||
|
The value of `groups` may have been modified by the previous `groups/v1` transformations in the pipeline.
|
||||||
|
- `strConst` contains the string constants declared for those transformations, and each string
|
||||||
|
constant can be referenced using its name e.g. a string constant called `x` can be referenced as `strConst.x`
|
||||||
|
- `strListConst` contains the list constants declared for those transformations, and each list
|
||||||
|
constant can be referenced using its name e.g. a list constant called `x` can be referenced as `strListConst.x`
|
||||||
|
|
||||||
|
Each identity provider selected for use in a FederationDomain may declare its own list of expressions.
|
||||||
|
The expressions will only be applied when that FederationDomain uses that identity provider.
|
||||||
|
|
||||||
|
### Transformation pipelines `constants`
|
||||||
|
|
||||||
|
Rather than repeating the same special strings across multiple expressions, you may optionally configure
|
||||||
|
string constants and string list constants for your transformation pipeline.
|
||||||
|
|
||||||
|
For example, if there is a special username or group name which will be used for comparisons in your expressions,
|
||||||
|
then you might like to declare it as a string constant. If there is a special list of usernames or group names
|
||||||
|
which will be used for comparisons then you might like to declare the list as a constant.
|
||||||
|
|
||||||
|
Constants are available in every expression of the pipeline.
|
||||||
|
|
||||||
|
### Transformation pipelines `examples`
|
||||||
|
|
||||||
|
Because the pipelines of expressions may behave differently based on their inputs, you may also optionally configure
|
||||||
|
`examples` to demonstrate how a pipeline is expected to behave for various possible input scenarios. These examples
|
||||||
|
act as living documentation for your fellow administrators, and also act as unit tests for your CEL expression code.
|
||||||
|
|
||||||
|
Each example declares inputs for the whole pipeline of expressions, and also declares the expected results of the
|
||||||
|
entire pipeline running on those inputs. The inputs are examples of the username and list of group names that might
|
||||||
|
be determined by the related OIDCIdentityProvider, ActiveDirectoryIdentityProvider, or LDAPIdentityProvider resource.
|
||||||
|
The expected outputs are the username and list of group names, or the authentication rejection, for which your pipeline
|
||||||
|
should result upon the given inputs.
|
||||||
|
|
||||||
|
If any example does not behave as expected, Pinniped will mark the whole FederationDomain with an error in
|
||||||
|
its `status` and users will not be allowed to use the FederationDomain to authenticate until the error is corrected.
|
||||||
|
|
||||||
|
### Putting it all together: an example of a transformation pipeline configuration
|
||||||
|
|
||||||
|
The following example is contrived to demonstrate every feature of the `transforms` configuration
|
||||||
|
(constants, expressions, and examples). It is likely more complex than a typical configuration.
|
||||||
|
|
||||||
|
Documentation for each of the fields shown below can be found in the API docs for the
|
||||||
|
[FederationDomain](https://github.com/vmware-tanzu/pinniped/blob/main/generated/{{< latestcodegenversion >}}/README.adoc#federationdomain)
|
||||||
|
CRD.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
kind: FederationDomain
|
||||||
|
apiVersion: config.supervisor.pinniped.dev/v1alpha1
|
||||||
|
metadata:
|
||||||
|
name: demo-federation-domain
|
||||||
|
namespace: pinniped-supervisor
|
||||||
|
spec:
|
||||||
|
issuer: https://issuer.example.com/demo-issuer
|
||||||
|
tls:
|
||||||
|
secretName: my-federation-domain-tls
|
||||||
|
identityProviders:
|
||||||
|
- displayName: ActiveDirectory for Admins
|
||||||
|
objectRef:
|
||||||
|
apiGroup: idp.supervisor.pinniped.dev
|
||||||
|
kind: ActiveDirectoryIdentityProvider
|
||||||
|
name: ad-for-admins
|
||||||
|
transforms:
|
||||||
|
constants:
|
||||||
|
- name: prefix
|
||||||
|
type: string
|
||||||
|
stringValue: "ad:"
|
||||||
|
- name: onlyIncludeGroupsWithThisPrefix
|
||||||
|
type: string
|
||||||
|
stringValue: "kube/"
|
||||||
|
- name: mustBelongToOneOfThese
|
||||||
|
type: stringList
|
||||||
|
stringListValue:
|
||||||
|
- "kube/admins"
|
||||||
|
- "kube/developers"
|
||||||
|
- "kube/auditors"
|
||||||
|
- name: additionalAdmins
|
||||||
|
type: stringList
|
||||||
|
stringListValue:
|
||||||
|
- "ryan@example.com"
|
||||||
|
- "ben@example.com"
|
||||||
|
- "josh@example.com"
|
||||||
|
expressions:
|
||||||
|
# This expression runs first, so it operates on the unmodified usernames
|
||||||
|
# and groups as extracted from AD by the ActiveDirectoryIdentityProvider.
|
||||||
|
# It rejects auth for any user who does not belong to certain groups.
|
||||||
|
# When it returns true, the pipeline continues. When it returns false,
|
||||||
|
# the pipeline stops and the auth is rejected.
|
||||||
|
- type: policy/v1
|
||||||
|
expression: 'groups.exists(g, g in strListConst.mustBelongToOneOfThese)'
|
||||||
|
message: "Only users in kube groups are allowed to authenticate"
|
||||||
|
# This expression runs second, and the previous expression was a policy
|
||||||
|
# (which cannot change username or groups), so this expression also
|
||||||
|
# operates on the unmodified usernames and groups as extracted from the
|
||||||
|
# IDP. For certain users, this adds a new group to their list of groups.
|
||||||
|
- type: groups/v1
|
||||||
|
expression: 'username in strListConst.additionalAdmins ? groups + ["kube/admins"] : groups'
|
||||||
|
# This expression runs next. Due to the expression above, this expression
|
||||||
|
# operates on the original username, and on a potentially changed list of
|
||||||
|
# groups. This drops all groups which do not start with a certain prefix.
|
||||||
|
- type: groups/v1
|
||||||
|
expression: 'groups.filter(group, group.startsWith(strConst.onlyIncludeGroupsWithThisPrefix))'
|
||||||
|
# Due to the expressions above, this expression operates on the original
|
||||||
|
# username, and on a potentially changed list of groups. This
|
||||||
|
# unconditionally prefixes the username.
|
||||||
|
- type: username/v1
|
||||||
|
expression: 'strConst.prefix + username'
|
||||||
|
# The expressions above have already changed the username and might have
|
||||||
|
# changed the groups before this expression runs. This unconditionally
|
||||||
|
# prefixes all group names.
|
||||||
|
- type: groups/v1
|
||||||
|
expression: 'groups.map(group, strConst.prefix + group)'
|
||||||
|
examples:
|
||||||
|
- username: "ryan@example.com"
|
||||||
|
groups: [ "kube/developers", "kube/auditors", "non-kube-group" ]
|
||||||
|
expects:
|
||||||
|
username: "ad:ryan@example.com"
|
||||||
|
groups: [ "ad:kube/developers", "ad:kube/auditors", "ad:kube/admins" ]
|
||||||
|
- username: "someone_else@example.com"
|
||||||
|
groups: [ "kube/developers", "kube/other", "non-kube-group" ]
|
||||||
|
expects:
|
||||||
|
username: "ad:someone_else@example.com"
|
||||||
|
groups: [ "ad:kube/developers", "ad:kube/other" ]
|
||||||
|
- username: "paul@example.com"
|
||||||
|
groups: [ "kube/other", "non-kube-group" ]
|
||||||
|
expects:
|
||||||
|
rejected: true
|
||||||
|
message: "Only users in kube groups are allowed to authenticate"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Some useful features of CEL
|
||||||
|
|
||||||
|
Pinniped uses the cel-go library to implement CEL expressions.
|
||||||
|
It includes the CEL [standard language features](https://github.com/google/cel-spec/blob/master/doc/langdef.md)
|
||||||
|
as well as the [string extensions](https://github.com/google/cel-go/tree/master/ext#strings).
|
||||||
|
This section will attempt to highlight some of the useful features of CEL, but is not intended to
|
||||||
|
be a comprehensive overview of everything that you can use in CEL expressions.
|
||||||
|
|
||||||
|
- CEL has several [built-in functions](https://github.com/google/cel-spec/blob/master/doc/langdef.md) which may be called on strings:
|
||||||
|
`contains`, `startsWith`, `endsWith`, and `matches` (for regex matching), e.g. `x.contains("some-substring")` for a string `x`
|
||||||
|
- The [string extensions](https://github.com/google/cel-go/tree/master/ext#strings)
|
||||||
|
include several additional functions which may be called on strings:
|
||||||
|
`charAt`, `indexOf`, `join`, `lastIndexOf`, `lowerAscii`, `quote`, `replace`, `split`, `substring`,
|
||||||
|
`trim`, `upperAscii`, and `reverse`
|
||||||
|
- CEL has [several useful functions which can be called on lists](https://github.com/google/cel-spec/blob/master/doc/langdef.md#macros):
|
||||||
|
- `map` and `filter` can be used to return a modified copy of a list
|
||||||
|
- `exists`, `exists_one`, and `all` can be used to perform boolean checks on the contents of a list
|
||||||
|
- Equality of strings and lists can be compared with the `==` and `!=` operators
|
||||||
|
- Lexicographic ordering of strings can be compared with `<`, `<=`, `>`, and `>=` operators
|
||||||
|
- Concatenation of two strings or two lists can be performed with the `+` operator
|
||||||
|
- List membership may be tested using the `in` operator, e.g. `"foo" in x` for a list `x`
|
||||||
|
- CEL does not have an `if` statement, but it does include a ternary operator to achieve the same result:
|
||||||
|
`boolean_expression ? when_true_expression : when_false_expression`. These may be nested,
|
||||||
|
e.g. the `when_true_expression` may itself be another ternary expression.
|
||||||
|
- Boolean operators include `&&` (and), `||` (or), `!` (not), `in` (inclusion in a list), and the ternary `?:`
|
||||||
|
- String literals can be quoted using single quotes `''` or double quotes `""` and [may contain quoted special characters](https://github.com/google/cel-spec/blob/master/doc/langdef.md#string-and-bytes-values)
|
||||||
|
- List literals can be written as a comma-seperated list of elements within enclosing `[]`
|
||||||
|
- `[]` may be used to index into a list, e.g. `x[4]` for a list `x`
|
||||||
|
- `size(x)` returns the length of a string `x` or the length of a list `x`
|
||||||
|
|
||||||
|
### Example expressions
|
||||||
|
|
||||||
|
Below are some examples of using expressions for identity transformations and policies.
|
||||||
|
|
||||||
|
Note that any of the string literals in these examples could be replaced by string
|
||||||
|
constants, i.e. `"prefix"` could instead refer to a constant like `strConst.prefix`.
|
||||||
|
Any literal list of strings could be replaced by a string list constant, e.g.
|
||||||
|
`["allowed1", "allowed2"]` could instead refer to a constant like `strListConst.allowedGroups`.
|
||||||
|
|
||||||
|
#### Example `username/v1` expressions
|
||||||
|
|
||||||
|
- Prefix the username:
|
||||||
|
- `"prefix" + username`
|
||||||
|
- Suffix the username:
|
||||||
|
- `username + "suffix"`
|
||||||
|
- Down-case the username (be careful that this will cause "Ryan" and "ryan" to become the same user in Kubernetes):
|
||||||
|
- `username.lowerAscii()`
|
||||||
|
|
||||||
|
#### Example `groups/v1` expressions
|
||||||
|
|
||||||
|
- Prefix all group names:
|
||||||
|
- `groups.map(g, "prefix" + g)`
|
||||||
|
- Suffix all group names:
|
||||||
|
- `groups.map(g, g + "suffix")`
|
||||||
|
- Filter groups to remove any group names that start with `system:` which is a prefix that has a special meaning to Kubernetes:
|
||||||
|
- `groups.filter(group, !group.startsWith("system:"))`
|
||||||
|
- Filter groups to keep only groups with a certain prefix:
|
||||||
|
- `groups.filter(group, group.startsWith("kube/"))`
|
||||||
|
- Down-case all group names (be careful that this will cause "Admins" and "admins" to become the same group in Kubernetes):
|
||||||
|
- `groups.map(g, g.lowerAscii())`
|
||||||
|
- Filter groups based on an allow list, dropping any group names except those included in the allow list:
|
||||||
|
- `groups.filter(g, g in ["allowed1", "allowed2"])`
|
||||||
|
- Filter groups based on a disallow list, dropping any group names included in the disallow list:
|
||||||
|
- `groups.filter(g, !(g in ["dropped1", "dropped2"]))`
|
||||||
|
- Filter groups based on a list of disallowed prefixes, dropping any groups which have one of the disallowed prefixes:
|
||||||
|
- `groups.filter(group, !(["disallowed-prefix1:", "disallowed-prefix2:"].exists(prefix, group.startsWith(prefix))))`
|
||||||
|
- Unconditionally add a group:
|
||||||
|
- `groups + ["new-group"]`
|
||||||
|
- Add a group, but only if the user already belongs to another specific group:
|
||||||
|
- `"other" in groups ? groups + ["new-group"] : groups`
|
||||||
|
- Rename a particular group if the user belongs to that group:
|
||||||
|
- `groups.map(g, g == "other" ? "other-renamed" : g)`
|
||||||
|
- Unconditionally drop all groups:
|
||||||
|
- `[]`
|
||||||
|
|
||||||
|
#### Example `policy/v1` expressions
|
||||||
|
|
||||||
|
- User must belong to a particular group:
|
||||||
|
- `"required-group" in groups`
|
||||||
|
- User must belong to at least one of the groups in a list:
|
||||||
|
- `groups.exists(g, g in ["foobar", "foobaz", "foobat"])`
|
||||||
|
- User must belong to all the groups in a list:
|
||||||
|
- `["foobar", "foobaz", "foobat"].all(g, g in groups)`
|
||||||
|
- User must not belong to any of the groups in a list:
|
||||||
|
- `!groups.exists(g, g in ["foobar", "foobaz"])`
|
||||||
|
- Certain users are allowed to authenticate and everyone else is rejected:
|
||||||
|
- `username in ["foobar", "foobaz"]`
|
||||||
|
- Certain users are not allowed to authenticate:
|
||||||
|
- `!(username in ["foobar", "foobaz"])`
|
||||||
|
|
||||||
|
## Next steps
|
||||||
|
|
||||||
|
Next,
|
||||||
|
[configure the Concierge to use the Supervisor for authentication]({{< ref "configure-concierge-supervisor-jwt" >}})
|
||||||
|
on each cluster.
|
@ -11,8 +11,8 @@ menu:
|
|||||||
aliases:
|
aliases:
|
||||||
- /docs/howto/configure-supervisor-with-activedirectory/
|
- /docs/howto/configure-supervisor-with-activedirectory/
|
||||||
---
|
---
|
||||||
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting a single
|
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting
|
||||||
"upstream" identity provider to many "downstream" cluster clients.
|
"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
|
This guide shows you how to configure the Supervisor so that users can authenticate to their Kubernetes
|
||||||
cluster using their identity from Active Directory.
|
cluster using their identity from Active Directory.
|
||||||
|
@ -11,8 +11,8 @@ menu:
|
|||||||
aliases:
|
aliases:
|
||||||
- /docs/howto/configure-supervisor-with-auth0/
|
- /docs/howto/configure-supervisor-with-auth0/
|
||||||
---
|
---
|
||||||
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting a single
|
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting
|
||||||
"upstream" identity provider to many "downstream" cluster clients.
|
"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
|
This guide shows you how to configure the Supervisor so that users can authenticate to their Kubernetes
|
||||||
cluster using their Auth0 credentials.
|
cluster using their Auth0 credentials.
|
||||||
|
@ -12,8 +12,8 @@ aliases:
|
|||||||
- /docs/howto/configure-supervisor-with-dex/
|
- /docs/howto/configure-supervisor-with-dex/
|
||||||
---
|
---
|
||||||
|
|
||||||
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting a single
|
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting
|
||||||
"upstream" identity provider to many "downstream" cluster clients.
|
"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
|
This guide shows you how to configure the Supervisor so that users can authenticate to their Kubernetes
|
||||||
cluster using Dex and Github.
|
cluster using Dex and Github.
|
||||||
|
@ -11,8 +11,8 @@ menu:
|
|||||||
aliases:
|
aliases:
|
||||||
- /docs/howto/configure-supervisor-with-gitlab/
|
- /docs/howto/configure-supervisor-with-gitlab/
|
||||||
---
|
---
|
||||||
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting a single
|
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting
|
||||||
"upstream" identity provider to many "downstream" cluster clients.
|
"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
|
This guide shows you how to configure the Supervisor so that users can authenticate to their Kubernetes
|
||||||
cluster using their GitLab credentials.
|
cluster using their GitLab credentials.
|
||||||
|
@ -11,8 +11,8 @@ menu:
|
|||||||
aliases:
|
aliases:
|
||||||
- /docs/howto/configure-supervisor-with-jumpcloudldap/
|
- /docs/howto/configure-supervisor-with-jumpcloudldap/
|
||||||
---
|
---
|
||||||
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting a single
|
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting
|
||||||
"upstream" identity provider to many "downstream" cluster clients.
|
"upstream" identity providers to many "downstream" cluster clients.
|
||||||
|
|
||||||
[JumpCloud](https://jumpcloud.com) is a cloud-based service which bills itself as
|
[JumpCloud](https://jumpcloud.com) is a cloud-based service which bills itself as
|
||||||
"a comprehensive and flexible cloud directory platform". It includes the capability to act as an LDAP identity provider.
|
"a comprehensive and flexible cloud directory platform". It includes the capability to act as an LDAP identity provider.
|
||||||
|
@ -11,8 +11,8 @@ menu:
|
|||||||
aliases:
|
aliases:
|
||||||
- /docs/howto/configure-supervisor-with-okta/
|
- /docs/howto/configure-supervisor-with-okta/
|
||||||
---
|
---
|
||||||
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting a single
|
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting
|
||||||
"upstream" identity provider to many "downstream" cluster clients.
|
"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
|
This guide shows you how to configure the Supervisor so that users can authenticate to their Kubernetes
|
||||||
cluster using their Okta credentials.
|
cluster using their Okta credentials.
|
||||||
|
@ -11,8 +11,8 @@ menu:
|
|||||||
aliases:
|
aliases:
|
||||||
- /docs/howto/configure-supervisor-with-openldap/
|
- /docs/howto/configure-supervisor-with-openldap/
|
||||||
---
|
---
|
||||||
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting a single
|
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting
|
||||||
"upstream" identity provider to many "downstream" cluster clients.
|
"upstream" identity providers to many "downstream" cluster clients.
|
||||||
|
|
||||||
[OpenLDAP](https://www.openldap.org) is a popular open source LDAP server for Linux/UNIX.
|
[OpenLDAP](https://www.openldap.org) is a popular open source LDAP server for Linux/UNIX.
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ menu:
|
|||||||
aliases:
|
aliases:
|
||||||
- /docs/howto/configure-supervisor-with-workspace_one_access/
|
- /docs/howto/configure-supervisor-with-workspace_one_access/
|
||||||
---
|
---
|
||||||
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting a single
|
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting
|
||||||
"upstream" identity provider to many "downstream" cluster clients.
|
"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
|
This guide shows you how to configure the Supervisor so that users can authenticate to their Kubernetes
|
||||||
cluster using their [Workspace ONE Access](https://www.vmware.com/products/workspace-one/access.html) credentials.
|
cluster using their [Workspace ONE Access](https://www.vmware.com/products/workspace-one/access.html) credentials.
|
||||||
|
@ -12,8 +12,8 @@ aliases:
|
|||||||
- /docs/howto/configure-supervisor/
|
- /docs/howto/configure-supervisor/
|
||||||
---
|
---
|
||||||
|
|
||||||
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting a single
|
The Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that supports connecting
|
||||||
"upstream" identity provider to many "downstream" cluster clients. When a user authenticates, the Supervisor can issue
|
"upstream" identity providers to many "downstream" cluster clients. When a user authenticates, the Supervisor can issue
|
||||||
[JSON Web Tokens (JWTs)](https://tools.ietf.org/html/rfc7519) that can be [validated by the Pinniped Concierge]({{< ref "configure-concierge-jwt" >}}).
|
[JSON Web Tokens (JWTs)](https://tools.ietf.org/html/rfc7519) that can be [validated by the Pinniped Concierge]({{< ref "configure-concierge-jwt" >}}).
|
||||||
|
|
||||||
This guide explains how to expose the Supervisor's REST endpoints to clients.
|
This guide explains how to expose the Supervisor's REST endpoints to clients.
|
||||||
@ -246,10 +246,54 @@ spec:
|
|||||||
# for the HTTPS endpoints served by this OIDC Provider.
|
# for the HTTPS endpoints served by this OIDC Provider.
|
||||||
tls:
|
tls:
|
||||||
secretName: my-tls-cert-secret
|
secretName: my-tls-cert-secret
|
||||||
|
# Configure which identity providers (OIDCIdentityProvider,
|
||||||
|
# ActiveDirectoryIdentityProvider, or LDAPIdentityProvider)
|
||||||
|
# shall be used by this FederationDomain.
|
||||||
|
identityProviders:
|
||||||
|
# See the "IDPs on FederationDomains" Supervisor configuration guide
|
||||||
|
# for details on how to configure this section of the FederationDomain's
|
||||||
|
# spec.
|
||||||
```
|
```
|
||||||
|
|
||||||
You can create multiple FederationDomains as long as each has a unique issuer string.
|
### How FederationDomains Work
|
||||||
|
|
||||||
Each FederationDomain can be used to provide access to a set of Kubernetes clusters for a set of user identities.
|
Each FederationDomain can be used to provide access to a set of Kubernetes clusters for a set of user identities.
|
||||||
|
A FederationDomain will allow the same set of users to authenticate into all Kubernetes clusters
|
||||||
|
which choose to trust that FederationDomain to provide authentication services. Authenticating to a FederationDomain
|
||||||
|
will only provide access to those clusters which choose to trust that FederationDomain, and will not provide access
|
||||||
|
to any clusters that are trusting other FederationDomains. Therefore, FederationDomains are a means of providing
|
||||||
|
authentication isolation.
|
||||||
|
|
||||||
|
You can create multiple FederationDomains in a single Pinniped Supervisor, as long as each has a unique issuer string.
|
||||||
|
|
||||||
|
When a user authenticates into any cluster that chooses to trust the FederationDomain for authentication,
|
||||||
|
then the user has started a single sign-on session with that FederationDomain which will last approximately 9 hours.
|
||||||
|
The user will not need to manually authenticate again into any cluster any cluster that chooses to trust the
|
||||||
|
FederationDomain for authentication until the session expires. Behind the scenes, the user's client (e.g. `kubectl`)
|
||||||
|
will only be given short-lived access to each cluster (approximately 5 minutes), but will automatically refresh those
|
||||||
|
credentials without the need for user interaction. During each refresh, the Supervisor will perform checks against
|
||||||
|
the external identity provider to determine if the user's session should be allowed to continue.
|
||||||
|
A user may have active sessions with multiple FederationDomains at the same time, but each will require authenticating
|
||||||
|
separately to start the single sign-on session with that FederationDomain.
|
||||||
|
|
||||||
|
Technically, each FederationDomain is a separate OIDC issuer which serves multiple REST endpoints to clients, such
|
||||||
|
as the Pinniped CLI. The tokens issued by each FederationDomain are signed by that FederationDomain and cannot
|
||||||
|
be used by any other FederationDomain, which provides the isolation properties of the FederationDomain concept.
|
||||||
|
|
||||||
|
Any Kubernetes cluster can choose to trust a FederationDomain to provide user authentication
|
||||||
|
for that cluster by using the FederationDomain its OIDC issuer for JSON Web Token (JWT) formatted ID tokens.
|
||||||
|
This can be done by installing the Pinniped Concierge on each cluster and
|
||||||
|
[configuring the Concierge to use the Supervisor for authentication]({{< ref "configure-concierge-supervisor-jwt" >}})
|
||||||
|
on each cluster. Alternatively, it can be done by [configuring the Kubernetes API server on each cluster to use
|
||||||
|
the FederationDomain as its OIDC issuer]({{< ref "../../tutorials/supervisor-without-concierge-demo" >}}).
|
||||||
|
|
||||||
|
When two FederationDomains use the same hostname in the `spec.issuer`, then:
|
||||||
|
1. They must use different paths in their `spec.issuer` URLs to differentiate them from each other.
|
||||||
|
2. They must use the same `tls.secretName` if they both configure a `tls.secretName` (see below for details of TLS configuration).
|
||||||
|
|
||||||
|
The Supervisor uses the hostname on the each incoming request to determine which TLS certificate to serve for that
|
||||||
|
request, and then it uses the path to determine which FederationDomain should serve the request if there are multiple
|
||||||
|
FederationDomains with the same hostname.
|
||||||
|
|
||||||
### Configuring TLS for the Supervisor OIDC endpoints
|
### Configuring TLS for the Supervisor OIDC endpoints
|
||||||
|
|
||||||
@ -295,6 +339,7 @@ should be signed by a certificate authority that is trusted by their browsers.
|
|||||||
## Next steps
|
## Next steps
|
||||||
|
|
||||||
Next, configure an OIDCIdentityProvider, ActiveDirectoryIdentityProvider, or an LDAPIdentityProvider for the Supervisor
|
Next, configure an OIDCIdentityProvider, ActiveDirectoryIdentityProvider, or an LDAPIdentityProvider for the Supervisor
|
||||||
(several examples are available in these guides). Then
|
(several examples are available in these guides). Then learn [how to configure a FederationDomain to use one or more
|
||||||
|
identity providers]({{< ref "configure-supervisor-federationdomain-idps" >}}). And finally,
|
||||||
[configure the Concierge to use the Supervisor for authentication]({{< ref "configure-concierge-supervisor-jwt" >}})
|
[configure the Concierge to use the Supervisor for authentication]({{< ref "configure-concierge-supervisor-jwt" >}})
|
||||||
on each cluster!
|
on each cluster.
|
||||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user