OIDC Authentication¶
Jumpstarter authenticates clients and exporters with internally issued JWT tokens by default, it can also be configured to use external OpenID Connect (OIDC) providers.
To use OIDC with you Jumpstarter installation, set the helm value jumpstarter-controller.authenticationConfiguration
to a valid AuthenticationConfiguration
yaml configuration.
Examples¶
Keycloak¶
Create a new keycloak client for the jumpstarter cli, set Client ID
to jumpstarter-cli
, Valid redirect URIs
to http://localhost/callback
and leave the remaining fields as default. Use the following snippet as jumpstarter-controller.authenticationConfiguration
during Jumpstarter installation.
apiVersion: jumpstarter.dev/v1alpha1
kind: AuthenticationConfiguration
jwt:
- issuer:
url: https://<keycloak domain>/realms/<realm name> # https is mandatory
certificateAuthority: <PEM encoded CA certificates> # if using self-signed certificate
audiences:
- jumpstarter-cli
claimMappings:
# use user ID prefixed with "keycloak:" as username
# e.g. keycloak:example-user
username:
claim: preferred_username
prefix: "keycloak:"
Then proceed to create clients and exporters with the jmp admin create
commands, set their corresponding OIDC username with the --oidc-username
flag, e.g. jmp admin create client test-client --oidc-username keycloak:developer-1
. Be sure to prefix usernames with “keycloak:”, as previously configured.
Finally, instruct the users to login with the following commands
# for clients
jmp login --client <client alias> --endpoint <jumpstarter controller endpoint> \
--namespace <namespace> --name <client name> \
--issuer https://<keycloak domain>/realms/<realm name>
# without additional options, the users would be directed to login with the web browser
# or the username and password can be directly specified for non-interactive login
--username <username> --password <password>
# or a token for machine to machine authentication, useful in CI environments
--token <token>
# for exporters
jmp login --exporter <exporter alias> --endpoint <jumpstarter controller endpoint> \
--namespace <namespace> --name <exporter name> \
--issuer https://<keycloak domain>/realms/<realm name>
# --username, --password and --token are also accepted
Dex (for authenticating with kubernetes Service Accounts)¶
Initialize a self-signed CA and sign certificate for dex
easyrsa init-pki
easyrsa --no-pass build-ca
easyrsa --no-pass build-server-full dex.dex.svc.cluster.local
# import certificate into secret
kubectl create namespace dex
kubectl -n dex create secret tls dex-tls \
--cert=pki/issued/dex.dex.svc.cluster.local.crt \
--key=pki/private/dex.dex.svc.cluster.local.key
Install dex with helm
# dex.values.yaml
https:
enabled: true
config:
issuer: https://dex.dex.svc.cluster.local:5556
web:
tlsCert: /etc/dex/tls/tls.crt
tlsKey: /etc/dex/tls/tls.key
storage:
type: kubernetes
config:
inCluster: true
staticClients:
- id: jumpstarter-cli
name: Jumpstarter CLI
public: true
connectors:
- name: kubernetes
type: oidc
id: kubernetes
config:
# kubectl get --raw /.well-known/openid-configuration | jq -r '.issuer'
issuer: "https://kubernetes.default.svc.cluster.local"
rootCAs:
- /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
userNameKey: sub
scopes:
- profile
volumes:
- name: tls
secret:
secretName: dex-tls
volumeMounts:
- name: tls
mountPath: /etc/dex/tls
service:
type: ClusterIP
ports:
http:
port: 5554
https:
port: 5556
# Ensure OIDC discovery URLs do not require authentication
kubectl create clusterrolebinding oidc-reviewer \
--clusterrole=system:service-account-issuer-discovery \
--group=system:unauthenticated
helm repo add dex https://charts.dexidp.io
helm install --namespace dex --wait -f dex.values.yaml dex dex/dex
Configure Jumpstarter to trust dex by using the following snippet as jumpstarter-controller.authenticationConfiguration
during Jumpstarter installation.
apiVersion: jumpstarter.dev/v1alpha1
kind: AuthenticationConfiguration
jwt:
- issuer:
url: https://dex.dex.svc.cluster.local:5556
audiences:
- jumpstarter-cli
audienceMatchPolicy: MatchAny
certificateAuthority: |
<content of pki/ca.crt>
claimMappings:
username:
claim: "name"
prefix: "dex:"
Then proceed to create clients and exporters with the jmp admin create
commands, set their corresponding OIDC username with the --oidc-username
flag, e.g. jmp admin create exporter test-exporter --oidc-username dex:system:serviceaccount:default:test-service-account
. Just prefix the full service account name with “dex:”, as previously configured.
Finally, instruct the users to login with the following commands in pods configured with proper service accounts.
# for clients
jmp login --client <client alias> --endpoint <jumpstarter controller endpoint> \
--namespace <namespace> --name <client name> \
--issuer https://dex.dex.svc.cluster.local:5556 \
--connector-id kubernetes \
--token $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
# for exporters
jmp login --exporter <exporter alias> --endpoint <jumpstarter controller endpoint> \
--namespace <namespace> --name <exporter name> \
--issuer https://dex.dex.svc.cluster.local:5556 \
--connector-id kubernetes \
--token $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
Reference¶
#
# CAUTION: this is an example configuration.
# Do not use this for your own cluster!
#
apiVersion: jumpstarter.dev/v1alpha1
kind: AuthenticationConfiguration
# list of authenticators to authenticate Jumpstarter users using OIDC issued JWT tokens.
jwt:
- issuer:
# URL of the provider that allows the API server to discover public signing keys.
# Only URLs that use the https:// scheme are accepted.
url: https://example.com
# discoveryURL, if specified, overrides the URL used to fetch discovery
# information instead of using "{url}/.well-known/openid-configuration".
# The exact value specified is used, so "/.well-known/openid-configuration"
# must be included in discoveryURL if needed.
#
# The "issuer" field in the fetched discovery information must match the "issuer.url" field
# in the AuthenticationConfiguration and will be used to validate the "iss" claim in the presented JWT.
# This is for scenarios where the well-known and jwks endpoints are hosted at a different
# location than the issuer (such as locally in the cluster).
# discoveryURL must be different from url if specified and must be unique across all authenticators.
discoveryURL: https://discovery.example.com/.well-known/openid-configuration
# PEM encoded CA certificates used to validate the connection when fetching
# discovery information. If not set, the system verifier will be used.
certificateAuthority: <PEM encoded CA certificates>
# audiences is the set of acceptable audiences the JWT must be issued to.
# At least one of the entries must match the "aud" claim in presented JWTs.
audiences:
- my-app
- my-other-app
# this is required to be set to "MatchAny" when multiple audiences are specified.
audienceMatchPolicy: MatchAny
# rules applied to validate token claims to authenticate users.
claimValidationRules:
# A key=value pair that describes a required claim in the JWT Token.
# If set, the claim is verified to be present in the JWT Token with a matching value.
- claim: hd
requiredValue: example.com
# Instead of claim and requiredValue, you can use expression to validate the claim.
# expression is a CEL expression that evaluates to a boolean.
# all the expressions must evaluate to true for validation to succeed.
- expression: 'claims.hd == "example.com"'
# Message customizes the error message seen in the API server logs when the validation fails.
message: the hd claim must be set to example.com
- expression: 'claims.exp - claims.nbf <= 86400'
message: total token lifetime must not exceed 24 hours
claimMappings:
# username represents an option for the username attribute.
# This is the only required attribute.
username:
# JWT claim to use as the user name.
# By default sub, which is expected to be a unique identifier of the end user.
# Admins can choose other claims, such as email or name, depending on their provider.
# However, claims other than email should be prefixed to prevent naming clashes with other authenticators.
# Mutually exclusive with username.expression.
claim: "sub"
# Prefix prepended to username claims to prevent clashes with existing names (such as internal:users).
# For example, the value oidc: will create usernames like oidc:jane.doe.
# if username.claim is set, username.prefix is required.
# Explicitly set it to "" if no prefix is desired.
# Mutually exclusive with username.expression.
prefix: ""
# expression is a CEL expression that evaluates to a string.
#
# 1. If username.expression uses 'claims.email', then 'claims.email_verified' must be used in
# username.expression or extra[*].valueExpression or claimValidationRules[*].expression.
# An example claim validation rule expression that matches the validation automatically
# applied when username.claim is set to 'email' is 'claims.?email_verified.orValue(true)'.
# 2. If the username asserted based on username.expression is the empty string, the authentication
# request will fail.
# Mutually exclusive with username.claim and username.prefix.
expression: 'claims.username + ":external-user"'
# groups represents an option for the groups attribute.
groups:
# JWT claim to use as the user's group. If the claim is present it must be an array of strings.
# Mutually exclusive with groups.expression.
claim: "sub"
# Prefix prepended to group claims to prevent clashes with existing names (such as system:groups).
# For example, the value oidc: will create group names like oidc:engineering and oidc:infra.
# if groups.claim is set, groups.prefix is required.
# Explicitly set it to "" if no prefix is desired.
# Mutually exclusive with groups.expression.
prefix: ""
# expression is a CEL expression that evaluates to a string or a list of strings.
# Mutually exclusive with groups.claim and groups.prefix.
expression: 'claims.roles.split(",")'
# uid represents an option for the uid attribute.
uid:
# Mutually exclusive with uid.expression.
claim: 'sub'
# Mutually exclusive with uid.claim
# expression is a CEL expression that evaluates to a string.
expression: 'claims.sub'
# extra attributes to be added to the UserInfo object. Keys must be domain-prefix path and must be unique.
extra:
# key is a string to use as the extra attribute key.
# key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
# subdomain as defined by RFC 1123. All characters trailing the first "/" must
# be valid HTTP Path characters as defined by RFC 3986.
# k8s.io, kubernetes.io and their subdomains are reserved for Kubernetes use and cannot be used.
# key must be lowercase and unique across all extra attributes.
- key: 'example.com/tenant'
# valueExpression is a CEL expression that evaluates to a string or a list of strings.
valueExpression: 'claims.tenant'
# validation rules applied to the final user object.
userValidationRules:
# expression is a CEL expression that evaluates to a boolean.
# all the expressions must evaluate to true for the user to be valid.
- expression: "!user.username.startsWith('system:')"
# Message customizes the error message seen in the API server logs when the validation fails.
message: 'username cannot used reserved system: prefix'
- expression: "user.groups.all(group, !group.startsWith('system:'))"
message: 'groups cannot used reserved system: prefix'