Okta SAML
Overview
This article describes how to set up SAML 2.0 single sign-on (SSO) for KubeSense using Okta as the identity provider (IdP).
Once configured, users sign in to KubeSense with their Okta credentials, inheriting your organization's Okta policies — MFA, conditional access, and lifecycle management.
info: KubeSense federates with one SAML IdP per deployment. Choose either Okta or Google SAML, not both. The provider setting selects which.
Prerequisites
- An Okta organization with super administrator access
- A KubeSense deployment reachable at a public HTTPS URL (e.g.
https://kubesense.example.com) - The ability to run
helm upgradeagainst the deployment
Step 1: Create the SAML app in Okta
- In the Okta Admin Console, go to Applications → Applications
- Click Create App Integration
- Select SAML 2.0 and click Next
- On General Settings, enter App name:
KubeSense, then click Next
info: Use Create App Integration, not the Okta Integration Network submission wizard. The OIN wizard is for publishing an app to Okta's public catalog for other organizations to install — it is not needed to connect your own tenant.
Step 2: Configure SAML settings
On the Configure SAML page, enter the following exactly:
| Field | Value |
|---|---|
| Single sign-on URL | https://<KUBESENSE_DOMAIN>/api/saml/acs |
| Audience URI (SP Entity ID) | https://<KUBESENSE_DOMAIN>/saml |
| Default RelayState | (leave blank) |
| Name ID format | EmailAddress |
| Application username | Email |
Replace <KUBESENSE_DOMAIN> with your actual domain, with no trailing slash.
warning: The Single sign-on URL includes the /api prefix. KubeSense mounts its SAML endpoints under /api/saml/*; a URL without /api fails assertion validation with a Destination mismatch.
Attribute statements (required)
Unlike Google Workspace, an Okta SAML app sends no attributes by default. KubeSense identifies users by email, so these must be added or every login is rejected.
Under Attribute Statements, add:
| Name | Name format | Value |
|---|---|---|
email | Unspecified | user.email |
firstName | Unspecified | user.firstName |
lastName | Unspecified | user.lastName |
Group attribute statements (optional)
Only needed if you want Okta groups to determine KubeSense roles — see Map Okta groups to roles.
Under Group Attribute Statements, add:
| Name | Filter | Value |
|---|---|---|
groups | Matches regex | .* |
Click Next, choose I'm an Okta customer adding an internal app, then Finish.
Leave request signing off
On the app's General tab, leave Signed Requests disabled. KubeSense signs its AuthnRequests with RSA-SHA1, which Okta rejects when request-signature validation is enabled. Okta does not require request signing, and assertion signing (which Okta always does) is what KubeSense verifies.
Step 3: Get the IdP metadata
- Open the app's Sign On tab
- Under SAML Signing Certificates → Actions → View IdP metadata, or copy the Metadata URL
- Save the XML — you will paste it into the KubeSense config in Step 5
The metadata URL looks like:
https://<your-org>.okta.com/app/<app-id>/sso/saml/metadataStep 4: Assign the app to users
A newly created Okta app is assigned to nobody. Until you assign it, users get an Okta error before ever reaching KubeSense.
- Open the app's Assignments tab
- Assign → Assign to People (or Assign to Groups)
- Select the users or groups who should have access, then Save
Step 5: Configure KubeSense via Helm values
api:
saml:
enabled: true
rootUrl: "https://<KUBESENSE_DOMAIN>"
entityId: "https://<KUBESENSE_DOMAIN>/saml"
# Paste the IdP metadata XML from Step 3.
idpMetadataXml: |
<?xml version="1.0" encoding="UTF-8"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://www.okta.com/...">
...full XML body...
</md:EntityDescriptor>
# Selects the Okta IdP flavour. Supplied via a Secret because the chart does
# not yet expose `api.saml.provider` as a first-class value.
extraEnvVarsSecret: kubesense-okta-saml
webapp:
extraEnv:
# Renders the "Sign In with Okta SAML" button.
ENABLE_SAML: "true"
SAML_PROVIDER: "okta"Create the Secret that carries the provider setting:
kubectl create secret generic kubesense-okta-saml \
--namespace <KUBESENSE_NAMESPACE> \
--from-literal=SAML_PROVIDER=oktaThen apply:
helm upgrade kubesense ./charts/kubesense-server \
-f values.yaml \
--set-file api.saml.idpMetadataXml=./OktaIDPMetadata.xmlinfo: webapp.extraEnv.SAML_PROVIDER only controls which logo and label the sign-in button shows. The API's SAML_PROVIDER is what actually selects the IdP behaviour. Keep the two identical, or users will see a Google button that performs an Okta login.
What the chart handles for you
kubesense-saml-secret— created on first install with a random 32-byte master key, preserved across upgrades vialookup- API ConfigMap — rendered with
SAML_ENABLED,SAML_ENTITY_ID,SAML_ROOT_URL,SAML_IDP_METADATA_XML - API Deployment —
envFrom-wired tokubesense-saml-secretsoSAML_KEY_ENCRYPTION_KEYis injected - SP signing key — the API generates a self-signed cert + RSA-2048 key on first SAML init, encrypts it with AES-256-GCM, and persists it in the
saml_sp_keystable. Restarts reuse the same key.
Configuration reference
api.saml
| Field | Required | Description |
|---|---|---|
enabled | yes | Registers the /api/saml/* routes |
rootUrl | yes | Public HTTPS URL of the deployment |
entityId | yes | SP Entity ID — must equal Okta's Audience URI, byte-for-byte |
idpMetadataXml | one of two | Inline IdP metadata XML (recommended) |
idpMetadataUrl | one of two | Metadata URL fetched at startup — adds a startup dependency on Okta |
API environment variables
| Variable | Default | Description |
|---|---|---|
SAML_PROVIDER | google | google or okta. Selects the requested NameID format, the auth_provider recorded on users, and audit wording. |
SAML_NAMEID_FORMAT | per provider | transient, emailaddress, persistent or unspecified. Empty means Google → transient, Okta → emailaddress. |
SAML_GROUPS_ATTRIBUTE | groups | Assertion attribute carrying group membership |
SAML_SYNC_ROLE_ON_LOGIN | false | Re-apply group→role mapping on every login |
SAML_KEY_ENCRYPTION_KEY | — | 32-byte AES key (hex or base64). Injected by the chart. |
Webapp environment variables
| Variable | Default | Description |
|---|---|---|
ENABLE_SAML | (unset) | true shows the SAML button. When unset, falls back to the legacy ENABLE_GOOGLE_SAML. |
SAML_PROVIDER | google | google or okta. Button label and icon only. |
Step 6: Verify SSO
- Open
https://<KUBESENSE_DOMAIN>/loginin a private browser window - Click Sign In with Okta SAML
- Authenticate with your Okta account
- You should land on the KubeSense dashboard
New users are provisioned automatically on first login with the User role.
Map Okta groups to roles
By default every SAML user is created with the User role, and administrators are promoted manually in KubeSense. To let Okta groups drive roles instead, configure a mapping.
warning: Group mapping is read from the API's config.yaml file. It cannot be supplied through an environment variable, because a map cannot be expressed as a single env value — so this requires mounting a config file into the API pod rather than using the chart's env-based configuration.
saml:
enabled: true
provider: okta
groups_attribute: "groups"
group_role_map:
kubesense-admins: Admin
kubesense-users: User
sync_role_on_login: falseBehaviour:
- Group names match case-insensitively
- A user in several mapped groups receives the highest-privilege match
- Groups with no mapping are ignored; the user falls back to User
- An unknown role type in the map fails at startup rather than silently downgrading users
sync_role_on_login: truere-applies the mapping on every login, so revoking an Okta group demotes the user at next sign-in. Leftfalse, a role edited in the KubeSense UI is preserved.
Valid role types are Admin, User and Internal.
Single Logout
KubeSense does not implement SAML Single Logout. Signing out of KubeSense ends the KubeSense session only; the Okta session remains active. Leave the app's Single Logout setting disabled.
Rotate the IdP certificate
Okta rotates SAML signing certificates when you generate a new one or when an app is re-created:
- Open the app's Sign On tab and view the IdP metadata
- Replace
api.saml.idpMetadataXmlin your Helm values - Run
helm upgrade— the chart re-renders the API ConfigMap and the Deployment rolls automatically
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
503 SAML is not available | SAML handler failed at startup | The real reason is logged. Run kubectl logs deployment/kubesense-api | grep "Failed to initialize SAML handler" |
503 and logs show saml_sp_keys ... doesn't exist | Table missing from the database | Apply migration 1.0.53_saml_sp_keys.sql |
503 and logs show SAML_KEY_ENCRYPTION_KEY env var is required | Secret not mounted | Confirm kubesense-saml-secret exists and the API Deployment references it |
403 InvalidNameIDPolicy from Okta | Requested NameID format disagrees with the app | Set the app's Name ID format to EmailAddress, or set SAML_NAMEID_FORMAT to match the app |
400 SAML assertion missing email attribute | Attribute statements not configured | Add the email attribute statement from Step 2 |
401 SAML assertion validation failed | Deliberately generic; real reason is logged | Check logs for SAML: failed to parse/validate assertion and its private_detail field |
private_detail: "Destination does not match" | Single sign-on URL ≠ <rootUrl>/api/saml/acs | Fix the URL in Okta — remember the /api prefix |
private_detail: "Audience does not match" | Entity ID mismatch | Make Okta's Audience URI equal api.saml.entityId exactly |
private_detail: "Signature could not be verified" | Stale IdP metadata after certificate rotation | Re-copy the metadata and helm upgrade |
| Okta error before reaching KubeSense | App not assigned | Complete Step 4 |
| Okta rejects the AuthnRequest signature | Request signature validation enabled | Disable Signed Requests on the app |
Login completes but bounces to /login | Auth cookies not stored | KubeSense must be served over HTTPS — the cookies are Secure — and rootUrl must match the URL the browser actually uses |
| Button says "Google SAML" but signs in with Okta | Webapp and API disagree | Set webapp.extraEnv.SAML_PROVIDER to okta to match the API |