An IdentityProvider represents an identity provider (IdP) that is used by the Cluster to authenticate and re-authenticate the Users in order for them to obtain and keep a valid Session and consequently be able to interact with the Cluster and access its Services (read more about Sessions here). As will be discussed in detail below, an IdentityProvider has one of the following types: github
, oidc
, saml
and oidcIdentityToken
.
A User can have one or more explicit identities, each for a different IdentityProvider. For example, a single User can use both GitHub or Okta with SAML 2.0 to authenticate themselves. You can read more about User identities here.
Web Identity Providers
Whenever you create an OAuth2/OIDC/SAML 2.0 application from within your identity provider's dashboard, you need to set the redirection URL to https://<DOMAIN>/callback
in your application settings in order for the identity provider to know where to redirect the authentication flow back to Portal.
You should have a look at the reference here to see all provider-specific options.
In order for the Users to see a web IdP IdentityProvider in the login page and use it to log in to the Cluster, you have to explicitly add its name in the ClusterConfig's list of web IdentityProviders. You can read more here.
GitHub OAuth2
The GitHub IdentityProvider can be defined as in the following example:
1kind: IdentityProvider2metadata:3name: github4displayName: GitHub5spec:6github:7clientID: abcdef...8clientSecret:9fromSecret: <SECRET_NAME>
As you can see, the client secret is referenced by a Secret resource via the
field fromSecret
in very similar way to how secrets are referenced in
Kubernetes for example. That means you have to first create a Secret via the
command octeliumctl create secret
and then reference it by its name in your
IdentityProvider. You can read more about Secrets here.
To get the IdentityProvider client ID and secret, you will have to create an OAuth application as follows:
- Go to
Developer Settings
here. - Add a new OAuth app.
- Set the
Authorization callback URL
field tohttps://<DOMAIN>/callback
.
OpenID Connect
OpenID Connect is an identity layer built on top of the OAuth 2.0 protocol and is currently usually preferred over SAML in most cases. Octelium can use an OIDC client to authenticate the User from an OIDC IdentityProvider. Here is an example:
1kind: IdentityProvider2metadata:3name: okta-oidc-014displayName: Okta5spec:6oidc:7issuerURL: https://dev-<ID>.okta.com8clientID: <CLIENT_ID>9clientSecret:10fromSecret: <SECRET_NAME>11# Optional, by default the claim `email` is used as the identifier.12identifierClaim: custom-identifier13# Optional but RECOMMENDED, this checks whether the claim `email_verified` claim value is set to true.14checkEmailVerified: true15# Optional, this obtains the claims via the UserInfo endpoint instead of the ID token.16useUserInfoEndpoint: true17# Optional, by default the scopes `profile` and `email` are added in addition to the mandatory scope `openid`.18scopes: ["scope1", "scope2"]
SAML 2.0
You can define a SAML 2.0 IdentityProvider as in the following example:
1kind: IdentityProvider2metadata:3name: okta-saml-014displayName: Okta SAML5spec:6saml:7# Required8metadataURL: https://dev-12345678.okta.com/app/123456789abcde/sso/saml/metadata9# Optional, the entity ID by default is set to https://<DOMAIN>10# i.e. if the Cluster domain is example.com then the entity ID is https://example.com11entityID: urn:<DOMAIN>12# Optional. By default, the identifier attribute is assumed to be13# `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`.14identifierAttribute: custom-email-attr15# Optional, this forces a re-authentication even if the user has a SSO session at the IdP16forceAuthn: true
By default, the SAML 2.0 provider always checks for the user's identifier, typical the email, using the identifierAttribute
which is set by default to http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
. However, you can always override that value by setting identifierAttribute
.
User Email as an Identifier
By default, an authentication by a web IdP IdentityProvider (ie. GitHub OAuth2, OpenID Connect or SAML 2.0 IdentityProvider) at first tries to find the User via an explicitly set identity (read more here) with an identityProvider
field that matches the IdP IdentityProvider's name and identifier
that matches the identifier in the assertion returned by the provider.
If no explicit identity matches or if the list of identities is simply empty, then the email
field of the User (read more here) is then inspected against the identifier in the assertion returned to the Cluster by the identity provider. In other words, the email
field acts as a default identifier that can be used automatically for any IdP IdentityProvider to find a User. However, you can disable this default behavior in a certain IdentityProvider, for example, when you do not trust that the email has been verified by that identity provider, as follows:
1kind: IdentityProvider2metadata:3name: github4displayName: GitHub5spec:6disableEmailAsIdentity: true7github:8clientID: <CLIENT_ID>9clientSecret:10fromSecret: <SECRET_NAME>
Public Display Name
You can set the public display name that shows up on the button of the corresponding IdentityProvider when Users visit the login page. If not set, the resource metadata's display name will be used as a fallback. Here is an example:
1```yaml2kind: IdentityProvider3metadata:4name: okta-oidc5displayName: Okta OIDC6spec:7displayName: Login with Okta8oidc:9# The rest of your configuration
Workload Identity Providers
OIDC Assertion
Octelium currently also supports authentication for WORKLOAD
Users using OpenID Connect (OIDC) assertions where workloads can authenticate themselves to the Cluster using OIDC ID tokens issued by the identity provider hosting the workload instead of typically having to use authentication token Credentials. The main disadvantage of using authentication tokens is that they need to be issued by the Cluster and have to be stored securely at the User's side. This can be cumbersome at scale and may lead to a breach if a Credential leaks. This "secret-less" assertion-based authentication method is increasingly getting more popular recently for workloads running on major cloud providers such as Azure, CI/CD providers such as GitHub Actions and CircleCI and it can also work for Kubernetes clusters. There are 3 modes to define the OIDC assertion IdentityProvider:
Public OIDC Issuer
This mode is preferable when the OIDC issuer publicly publishes its endpoint. Many cloud providers, CI/CD providers and managed Kubernetes clusters already offer their OIDC endpoint publicly.
1kind: IdentityProvider2metadata:3name: github-actions-014spec:5oidcIdentityToken:6# this is the URL that will be appended with the path "/.well-known/openid-configuration"7# to get the discovery-document URL of the issuer8issuerURL: https://token.actions.githubusercontent.com9audience: https://example.com
JWKS URL
In some cases, you might want to set the JWKS url directly if you are not dealing with a fully compliant OIDC provider. In such case, you also have to explicitly set the issuer in order to be verified against the issuer claim in the OIDC ID token assertion.
1kind: IdentityProvider2metadata:3name: github-actions-014spec:5oidcIdentityToken:6jwksURL: https://token.actions.githubusercontent.com/.well-known/jwks7issuer: https://token.actions.githubusercontent.com8audience: https://example.com
JWKS Content
This mode can be useful for cases where the OIDC issuer is not publishing its endpoint publicly (e.g. Kubernetes clusters that do not publish their endpoints publicly). In this case you can just manually set the JWKS content manually. In such case, you also have to explicitly set the issuer in order to be verified against the issuer claim in the OIDC ID token assertion.
1kind: IdentityProvider2metadata:3name: k8s-014spec:5oidcIdentityToken:6jwksContent: |7{8"keys": [9{10"use": "sig",11"kty": "RSA",12"kid": "I4twhfbX-awEXmXezQS_i_e7L-a6EWRp8-fYdAhga6A",13"alg": "RS256",14"n": "oloso_qOKvQ8Fu9rkUVUHqYJsyQ_2p_G_pAp5....",15"e": "AQAB"16}17]18}19issuer: https://kubernetes.default.svc.cluster.local20audience: https://kubernetes.default.svc.cluster.local
Authenticator Assurance Level
One of the important goals of the zero trust model is to control access based on the User's identity whose degree of confidence and assurance depends on the strength of authentication methods used by SSO identity providers. For example, an IdP might only require a password, another might require MFA second-factor authentication method such as TOTP, and a third forces the User to authenticate with a hardware-based phishing resistant authenticators such as FIDO-2 security keys (e.g. Yubikey) that is capable of enforcing user consent and verification methods during authentication such as biometric (e.g. fingerprint) or PIN in order for the authenticator to proceed with the authentication process. NIST defines a technical guide of 3 authenticator assurance levels (AAL) of increasing strength AAL1
, AAL2
and AAL3
here where level-3 corresponds to the highest level of assurance. Octelium enables you to control such AAL level during authentication depending on the content of the assertion (e.g. ID token in the case of OpenID Connect IdentityProviders) returned by the IdP to the Cluster, and use the resulting AAL in your access control decisions (read more about access control and Policies here). For example, you might want to be sure that all or certain Users can access all Services or certain sensitive Services only if they authenticated via a phishing resistant hardware-based authenticator that satisfies AAL3
requirements. Here is an example:
1kind: Policy2metadata:3name: production-policy4spec:5rules:6- name: production7effect: ALLOW8condition:9all:10of:11- match: ctx.namespace.metadata.name == "production"12- match: "ops" in ctx.user.spec.groups13- match: ctx.user.spec.type == "HUMAN"14- match: ctx.session.status.authentication.info.aal == "AAL3"
To actually set the AAL value based on the assertion content upon authentication, Octelium enables you to define your own policy-as-code AAL rules, represented by the aalRules
list as shown below, where the value chosen corresponds to the first rule whose condition matches. This is similar to dynamic configuration rules (read more here). Octelium converts the assertion into a map in the case of oidc
, oidcIdentityToken
and github
IdentityProviders where you can define your conditions in the assertionMap
object. Here is an example:
1kind: IdentityProvider2metadata:3name: oidc-014displayName: My OIDC IdP5spec:6oidc:7issuerURL: https://oidc.example.com8# The rest of your configs9aalRules:10- aal: AAL311condition:12match: ctx.assertionMap.hasMFA
For the case of OpenID Connect, some IdPs use standard claims such as amr
and acr
to provide AAL information, others do use custom claims.
For the case of saml
IdentityProviders, the assertion is available as a string value in the assertion
field. Here is an example:
1kind: IdentityProvider2metadata:3name: oidc-014displayName: My OIDC IdP5spec:6saml:7# The rest of your configs8aalRules:9- aal: AAL210condition:11match: ctx.assertion.contains("mfa")
Disabling an IdentityProvider
You can disable/deactivate a IdentityProvider and immediately disable the Users ability to see that IdentityProvider in the log in page or log in with it. Here is an example:
1kind: IdentityProvider2metadata:3name: oidc4spec:5isDisabled: true6oidc:7# The rest of your configurations