Configure OIDC
This guide is only applicable for administrators who've deployed self-hosted Spaces. For general RBAC in Upbound, read Upbound RBAC.
Upbound uses the Kubernetes Structured Authentication Configuration to validate OIDC tokens sent to the API. Upbound stores this
configuration as a ConfigMap and authenticates with the Upbound router
component during installation with Helm.
This guide walks you through how to create and apply an authentication configuration to validate Upbound with an external identity provider. Each section focuses on a specific part of the configuration file.
Creating the AuthenticationConfiguration file
First, create a file called config.yaml with an AuthenticationConfiguration
kind. The AuthenticationConfiguration is the initial authentication structure
necessary for Upbound to communicate with your chosen identity provider.
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthenticationConfiguration
jwt:
- issuer:
url: oidc-issuer-url
audiences:
- oidc-client-id
claimMappings: # optional
username:
claim: oidc-username-claim
prefix: oidc-username-prefix
groups:
claim: oidc-groups-claim
prefix: oidc-groups-prefix
For detailed configuration options, including the CEL-based token validation, review the feature documentation.
The AuthenticationConfiguration allows you to configure multiple JWT
authenticators as separate issuers.
Configure an issuer
The jwt array requires an issuer specification and typically contains:
- A
usernameclaim mapping - A
groupsclaim mapping Optionally, the configuration may also include: - A set of claim validation rules
- A set of user validation rules
The issuer URL must be unique across all configured authenticators.
issuer:
url: https://example.com
discoveryUrl: https://discovery.example.com/.well-known/openid-configuration
certificateAuthority: |-
<Inline CA Certificate>
audiences:
- client-id-a
- client-id-b
audienceMatchPolicy: MatchAny
By default, the authenticator assumes the OIDC Discovery URL is
{issuer.url}/.well-known/openid-configuration. Most identity providers follow
this structure, and you can omit the discoveryUrl field. To use a separate
discovery service, specify the full path to the discovery endpoint in this
field.
If the CA for the Issuer isn't public, provide the PEM encoded CA for the Discovery URL.
At least one of the audiences entries must match the aud claim in the JWT.
For OIDC tokens, this is the Client ID of the application attempting to access
the Upbound API. Having multiple values set allows the same configuration to
apply to multiple client applications, for example the kubectl CLI and an
Internal Developer Portal.
If you specify multiple audiences , audienceMatchPolicy must equal MatchAny.
Configure claimMappings
Username claim mapping
By default, the authenticator uses the sub claim as the user name. To override this, either:
- specify both
claimandprefix.prefixmay be explicitly set to the empty string. or
- specify a CEL
expressionto calculate the user name.
claimMappings:
username:
claim: "sub"
prefix: "keycloak"
# <or>
expression: 'claims.username + ":external-user"'
Groups claim mapping
By default, this configuration doesn't map groups, unless you either:
- specify both
claimandprefix.prefixmay be explicitly set to the empty string. or
- specify a CEL
expressionthat returns a string or list of strings.
claimMappings:
groups:
claim: "groups"
prefix: ""
# <or>
expression: 'claims.roles.split(",")'
Validation rules
Validation rules are outside the scope of this document. Review the documentation for more information. Examples include using CEL expressions to validate authentication such as:
- Validating that a token claim has a specific value
- Validating that a token has a limited lifetime
- Ensuring usernames and groups don't contain reserved prefixes
Required claims
To interact with Space and ControlPlane APIs, users must have the upbound.io/aud claim set to one of the following:
| Upbound.io Audience | Notes |
|---|---|
[] | No Access to Space-level or ControlPlane APIs |
['upbound:spaces:api'] | This Identity is only for Space-level APIs |
['upbound:spaces:controlplanes'] | This Identity is only for ControlPlane APIs |
['upbound:spaces:api', 'upbound:spaces:controlplanes'] | This Identity is for both Space-level and ControlPlane APIs |
You can set this claim in two ways:
- In the identity provider mapped in the ID token.
- Inject in the authenticator with the
jwt.claimMappings.extraarray.
For example:
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthenticationConfiguration
jwt:
- issuer:
url: https://keycloak:8443/realms/master
certificateAuthority: |-
<ca bytes inline>
audiences:
- master-realm
audienceMatchPolicy: MatchAny
claimMappings:
username:
claim: "preferred_username"
prefix: "keycloak:"
groups:
claim: "groups"
prefix: ""
extra:
- key: 'upbound.io/aud'
valueExpression: "['upbound:spaces:controlplanes', 'upbound:spaces:api']"
Install the AuthenticationConfiguration
Once you create an AuthenticationConfiguration file, specify this file as a
ConfigMap in the host cluster for the Upbound Space.
kubectl create configmap <configmap name> -n upbound-system --from-file=config.yaml=./path/to/config.yaml
To enable OIDC authentication and disable Upbound IAM when installing the Space, reference the configuration and pass an empty value to the Upbound IAM issuer parameter:
up space init --token-file="${SPACES_TOKEN_PATH}" "v${SPACES_VERSION}" \
...
--set "authentication.structuredConfig=<configmap name>" \
--set "router.controlPlane.extraArgs[0]=--upbound-iam-issuer-url="
Configure RBAC
In this scenario, the external identity provider handles authentication, but permissions for Spaces and ControlPlane APIs use standard RBAC objects.
Spaces APIs
The Spaces APIs include:
- apiGroups:
- spaces.upbound.io
resources:
- controlplanes
- sharedexternalsecrets
- sharedsecretstores
- backups
- backupschedules
- sharedbackups
- sharedbackupconfigs
- sharedbackupschedules
- apiGroups:
- observability.spaces.upbound.io
resources:
- sharedtelemetryconfigs
ControlPlane APIs
Crossplane specifies three roles for a
ControlPlane: admin, editor, and viewer. These map to the verbs admin, edit,
and view on the controlplanes/k8s resource in the spaces.upbound.io API
group.
Control access
The groups claim in the AuthenticationConfiguration allows you to control
resource access when you create a ClusterRoleBinding. A ClusterRole defines
the role parameters and a ClusterRoleBinding subject.
The example below allows admin permissions for all ControlPlanes to members of
the ctp-admins group:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: allow-ctp-admin
rules:
- apiGroups:
- spaces.upbound.io
resources:
- controlplanes/k8s
verbs:
- admin
ctp-admins ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: allow-ctp-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: allow-ctp-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: ctp-admins