Skip to content

Kanidm

LDAP connection

For login with LDAP accounts your user has to have enabled the POSIX attributes and need to set a Unix password.

Modify ACP

Disallow displayname self modification:

cat << EOF> /tmp/modify.json
[
    { "removed": ["acp_modify_removedattr", "displayname"] },
    { "removed": ["acp_modify_presentattr", "displayname"] }
]
EOF
kanidm raw modify '{"eq": ["name", "idm_self_acp_write"]}'  /tmp/modify.json
kanidm raw search '{"eq": ["name", "idm_self_acp_write"]}'

Create user

kanidm person create demo-user "demo-user" -D idm_admin
kanidm person update demo-user --mail "demo-user@example.com" -D idm_admin
kanidm person credential create-reset-token demo-user -D idm_admin

kanidm group list -D idm_admin | rg name | rg users
kanidm group add-members ${GROUP_NAME} demo-user -D idm_admin

Add app to SSO (GitOps with Kaniop)

Preferred method. The Kaniop operator manages OAuth2 clients and groups declaratively via CRDs. ArgoCD syncs the resources and the operator creates the client in Kanidm plus a Kubernetes secret with the credentials.

1. Create the group

apps/<name>/templates/kanidm-group.yaml:

---
apiVersion: kaniop.rs/v1beta1
kind: KanidmGroup
metadata:
  name: <app>-users
spec:
  kanidmRef:
    name: kanidm
    namespace: kanidm

Optionally add an <app>-admins group if the app supports admin scope maps.

2. Create the OAuth2 client

apps/<name>/templates/kanidm-oauth2-client.yaml:

---
apiVersion: kaniop.rs/v1beta1
kind: KanidmOAuth2Client
metadata:
  name: <app>
spec:
  kanidmRef:
    name: kanidm
    namespace: kanidm

  displayname: <app>

  origin: https://<app>.grigri.cloud/

  redirectUrl:
    - https://<app>.grigri.cloud/<callback-path>

  scopeMap:
    - group: <app>-users
      scopes:
        - openid
        - profile
        - email

  # Optional: admin scope map
  # supScopeMap:
  #   - group: <app>-admins
  #     scopes:
  #       - admin

  preferShortUsername: true
  strictRedirectUrl: true

Common optional flags:

Flag When to use
allowInsecureClientDisablePkce: true App doesn't support PKCE
public: true Public client (no secret, e.g. SPA/native)
allowLocalhostRedirect: true CLI tools that redirect to localhost
jwtLegacyCryptoEnable: true App needs legacy JWT encryption

3. Consume the secret

The operator creates a secret named <app>-kanidm-oauth2-credentials with keys CLIENT_ID and CLIENT_SECRET. Reference it in your chart's env vars:

env:
  - name: OIDC_CLIENT_ID
    valueFrom:
      secretKeyRef:
        name: <app>-kanidm-oauth2-credentials
        key: CLIENT_ID
  - name: OIDC_CLIENT_SECRET
    valueFrom:
      secretKeyRef:
        name: <app>-kanidm-oauth2-credentials
        key: CLIENT_SECRET

The OIDC issuer URL follows this pattern:

https://idm.grigri.cloud/oauth2/openid/<app>

4. Add users to the group

Users must be added manually by an admin after the group is created:

kanidm group add-members <app>-users ${USER} -D idm_admin

5. Verify

After ArgoCD syncs, check the client is ready:

kubectl --context=grigri get kanidmoauth2client <app> -n <namespace>
kubectl --context=grigri get secret <app>-kanidm-oauth2-credentials -n <namespace>

The status should show ready: true and all conditions True.


Manual CLI method (legacy)

Example with Grafana:

kanidm system oauth2 create grafana grafana  https://grafana.grigri.cloud/login/generic_oauth -D admin
kanidm group create grafana-users -D admin
kanidm group add-members grafana-users ${USER} -D admin
kanidm system oauth2 update-scope-map grafana grafana-users openid profile email -D admin
kanidm system oauth2 show-basic-secret grafana -D admin
kanidm group create grafana-admins -D admin
kanidm group add-members grafana-admins ${USER} -D admin
kanidm system oauth2 update-sup-scope-map grafana grafana-admins admin -D admin
kanidm system oauth2 prefer-short-username grafana -D admin
kanidm system oauth2 set-landing-url grafana https://grafana.grigri.cloud/login/generic_oauth

If PKCE needs to be disabled:

kanidm system oauth2 warning-insecure-client-disable-pkce ${CLIENT}

Use groups in SSO

To pass groups in JWT you need to ask for openid groups scopes.

After next login, you will receive groups uuid and groups spn in the token:

    ...
  "scopes": [
    "email",
    "groups",
    "openid",
    "profile"
  ],
  "groups": [
    "idm_all_persons@idm.grigri.cloud",
    "00000000-0000-0000-0000-000000000035",
    "idm_all_accounts@idm.grigri.cloud",
    "00000000-0000-0000-0000-000000000036",
    "XXXXXX@idm.grigri.cloud",
    "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    ...
  ]
}

Note: When groups scope is activated your header size will be above 4k so you will need to add this annotation to your ingress:

nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"

Upgrade

Check if upgrade is possible:

kanidmd domain upgrade-check

Disable anonymous access

kanidm service-account validity expire-at anonymous '2024-11-14T00:00:00+01:00'

Increase expiration time

kanidm group account-policy auth-expiry idm_all_accounts 2592000

Service account permissions to validate credentials

kanidm group add-members idm_people_pii_read