Deployment guide

Kanidm

Overview

Kanidm is a complete identity management platform. It combines a directory that holds your people, groups and service accounts with an identity provider that lets applications authenticate against them. It is a single statically linked binary written in Rust, published under the Mozilla Public License 2.0.

This SecureAnyCloud image runs Kanidm 1.11.2 on Ubuntu 24.04 LTS. One server gives you a web interface for administration and self-service, a REST API, a read-only LDAP compatibility endpoint, an OAuth2 and OpenID Connect provider, RADIUS secrets for network authentication, and POSIX account attributes for Unix login.

There is no default password on this image, and none is generated for you. Kanidm ships two break-glass accounts, admin and idm_admin, and neither has any credential. They cannot authenticate over HTTPS, LDAPS, OAuth2 or RADIUS. There is no vendor default to change and no first-boot password to find, so there is no window between boot and securing the machine during which anyone could sign in. You create the first credential yourself, from a root shell on the machine, with a single command.

What you get:

  • Kanidm 1.11.2, released 11 September 2026, built from the upstream git tag v1.11.2 at commit 547933867e. Before compiling, the build verifies that the tag resolves to that exact commit and that the licence file at that tag is the Mozilla Public License 2.0
  • The kanidm command line client, from the same source tree and the same version as the server
  • A per-machine TLS certificate and certificate authority, generated on your own virtual machine at first boot
  • Ubuntu 24.04 LTS, fully patched at build time, with unattended security upgrades enabled
  • A hardened sshd configuration: no root login and no password authentication
  • Exposed ports 22, 443 and 636, and nothing else

The upstream project publishes no prebuilt binary for this release, so the image is compiled from source at build time. The Rust toolchain is not in the image. The compiler, linker, cargo and the whole build tree live on a scratch volume that is destroyed before the image is captured. The build then asserts twice, once against the filesystem and once against the package database, that no compiler, linker or cargo binary remains.

Port 636 carries the LDAP compatibility endpoint. Kanidm serves LDAP over TLS only and does not implement StartTLS. It rejects every write over LDAP, and a bind that uses a POSIX password is granted only anonymous-level read. If you have no LDAP applications, close 636 in your network security group (NSG) and nothing else changes.

There is no reverse proxy in this image. Kanidm terminates TLS itself on both ports. Its trusted proxy settings keep the default of trusting nothing, so nothing on this machine believes a client-supplied X-Forwarded-For header.

Before you deploy: choose your domain name

Kanidm is domain based. The domain setting builds every account's security principal name, forms the basis of the LDAP base DN, and serves as the WebAuthn relying party identifier. Upstream warns that changing it later breaks registered credentials, including WebAuthn tokens and issued OAuth2 tokens, and requires a kanidmd domain rename.

Set the domain before the first boot. Pass cloud-init user data when you create the machine, and first boot uses your name instead of the default:

#cloud-config write_files: - path: /etc/kanidm/sac-domain.conf permissions: '0644' content: | KANIDM_DOMAIN=idm.yourcompany.example

If you skip this, the server comes up as idm.example.com. That default suits evaluation and access by IP address, and you can change it later with the documented rename procedure.

Deploying the virtual machine

Create the machine from the Marketplace image. Standard_B2s is the recommended size, because Kanidm is a single binary with a small memory footprint.

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:kanidm-ubuntu-24-04:default:latest
az vm create \
  --resource-group my-identity-rg \
  --name kanidm01 \
  --image sac1647283583153:kanidm-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard \
  --custom-data cloud-init-kanidm.yaml
az vm open-port --resource-group my-identity-rg --name kanidm01 --port 443 --priority 1001
az vm open-port --resource-group my-identity-rg --name kanidm01 --port 636 --priority 1002

First boot does four things, then starts the server:

  • Writes your domain into the configuration.
  • Creates this machine's own certificate authority and TLS certificate.
  • Validates the resulting configuration and stops if it is rejected.
  • Writes an access note to /root/kanidm-credentials.txt.

The Kanidm service is gated behind this first-boot work. It does not start until the first-boot script has finished successfully, so the server never listens with an unvalidated configuration or missing certificate material.

1

Check the server is running

Connect over SSH and confirm the service is healthy. The status endpoint returns true when the server is ready to take requests.

sudo systemctl is-active kanidm-firstboot.service kanidmd.service curl -sk https://127.0.0.1/status echo
sudo kanidmd version

Read the access note, which records everything specific to this machine. It contains no password, because no credential exists anywhere on the server yet.

sudo cat /root/kanidm-credentials.txt
2

Create your first administrator credential

Run this command on the machine, as root or as the service account. It turns a locked server into one you can administer, and it prints a freshly generated random password exactly once.

Use idm_admin for day-to-day administration of people and groups. Recover admin only when you need to change server-level configuration such as the domain name or the LDAP base DN. Keeping the two accounts separate is the reason Kanidm has both.

sudo -u kanidmd kanidmd recover-account idm_admin INFO Running account recovery ... INFO | new_password: "<a fresh 48 character password, shown once>"

Record that password in your password manager now. It is not stored in clear text anywhere on the machine. Running the command again replaces it with a new one.

For an automated deployment, capture the password straight into a sign-in so it is never written to a file. This is the sequence used to verify the image:

PW=$(sudo -u kanidmd kanidmd recover-account idm_admin 2>&1 | sed -n 's/.*new_password: "\(.*\)".*/\1/p')
sudo KANIDM_PASSWORD="$PW" kanidm login -D idm_admin
sudo kanidm self whoami -D idm_admin

The kanidm client on this machine is preconfigured with the server's address and its certificate authority, so you need no --url or --ca flags here.

Recovering one break-glass account does not unlock the other. Until you recover admin, it stays refused.

3

Sign in to the web interface

Browse to https://<your-domain>/. The certificate is issued for the domain name rather than the IP address, so if DNS does not yet point at the machine, add a hosts file entry on your workstation first.

Sign in as idm_admin with the password you just recorded.

Your browser warns about the certificate on the first visit. This is expected. The machine issued its own certificate at first boot, and upstream labels it with the subject OU = Development and Evaluation - NOT FOR PRODUCTION. It lets the server start and lets you connect immediately. Replace it with your own certificate, as described in the section on replacing the TLS certificate. It takes two file copies.

The web interface is where your people manage their own credentials, passkeys and application access. It also has an administration panel for browsing persons and groups.

Administration in Kanidm 1.11 is done with the kanidm command line client, not the web interface. The admin panel lists and inspects persons and groups and lets you edit a group's membership. Creating accounts, setting POSIX attributes, issuing RADIUS secrets and registering OAuth2 clients are command line operations. This is upstream design, not a limitation of this image.

4

Create a person and a group

Create an account for a person, add them to a group, and give both POSIX attributes so Unix and LDAP clients can see them.

sudo kanidm person create alice "Alice Baker" -D idm_admin
sudo kanidm person posix set alice --shell /bin/bash -D idm_admin
sudo kanidm group create platform-engineers -D idm_admin
sudo kanidm group add-members platform-engineers alice -D idm_admin
sudo kanidm group posix set platform-engineers -D idm_admin
sudo kanidm person get alice -D idm_admin

Both objects appear immediately in the administration panel. Open the person to see their security principal name, their UUID and every group they belong to. The group's own page lists its members and lets you add and remove them.

To let Alice set her own password and passkeys, generate a credential reset link and send it to her. The link is single-use and time-limited, so you never need to know or transport her password:

sudo kanidm person credential create-reset-token alice -D idm_admin
5

Use the LDAP directory

Applications that only speak LDAP bind to port 636 over TLS. The base DN is derived from your domain name, so idm.example.com becomes dc=idm,dc=example,dc=com. The access note records it for you.

Anonymous search resolves POSIX-enabled people and groups. This is what most LDAP clients need to look up a user and their group membership:

D=$(sudo grep -m1 '^KANIDM_DOMAIN=' /root/kanidm-credentials.txt | cut -d= -f2-)
B=$(sudo grep -m1 '^KANIDM_LDAP_BASEDN=' /root/kanidm-credentials.txt | cut -d= -f2-) sudo LDAPTLS_CACERT=/var/lib/kanidmd/ca.pem \
  ldapsearch -LLL -o ldif-wrap=no -H "ldaps://$D:636" -b "$B" -x '(name=alice)' \
  name spn gidnumber loginshell memberof

Read the base DN with cut -d= -f2- rather than awk -F= '{print $2}'. The value contains equals signs of its own. Taking only the second field silently truncates dc=idm,dc=example,dc=com to dc, and every search then fails with a constraint violation that looks like a server problem.

For an application that needs more than anonymous read access, create a service account and issue it an API token. Bind with the reserved DN dn=token and the token as the password to get that service account's own read permissions:

D=$(sudo grep -m1 '^KANIDM_DOMAIN=' /root/kanidm-credentials.txt | cut -d= -f2-) sudo kanidm service-account create svc-ldap "Directory reader" idm_admin -D idm_admin
TOK=$(sudo kanidm service-account api-token generate svc-ldap readers -D idm_admin | tail -1) sudo LDAPTLS_CACERT=/var/lib/kanidmd/ca.pem \
  ldapwhoami -H "ldaps://$D:636" -x -D 'dn=token' -w "$TOK"

A person can also bind as themselves with a POSIX password, which is what Unix clients do. Set one interactively. The command prompts twice and never accepts the password on the command line, so it cannot end up in your shell history:

sudo kanidm person posix set-password alice -D idm_admin

Two behaviours to expect:

  • Writes over LDAP are always refused, because LDAP cannot express Kanidm's data model. Use the REST API or the kanidm client to make changes.
  • A failed bind soft-locks the account for a short period, so the next few attempts fail even with the correct password. This is brute-force protection working, not a fault.

If you have no LDAP applications, you can turn the POSIX password bind off and leave only token binds:

sudo kanidm system domain set-ldap-allow-unix-password-bind false -D admin
6

Connect an application over OpenID Connect

For applications that support modern federation, register them as OAuth2 clients instead of pointing them at LDAP. Kanidm is the provider, and you need no extra component.

sudo kanidm system oauth2 create grafana "Grafana" https://grafana.example.com -D idm_admin
sudo kanidm system oauth2 update-scope-map grafana platform-engineers openid profile email -D idm_admin
sudo kanidm system oauth2 show-basic-secret grafana -D idm_admin
sudo kanidm system oauth2 list -D idm_admin

The discovery document your application needs is published at https://<your-domain>/oauth2/openid/grafana/.well-known/openid-configuration. Only members of the groups you grant a scope map to can sign in, so access control lives in the directory instead of in each application.

7

Set up RADIUS and Unix login

Kanidm holds the per-account RADIUS secret and serves it to a FreeRADIUS instance running upstream's rlm_kanidm module. The RADIUS daemon is a separate deployment and is not installed on this image. This machine is the authority that issues and stores the secrets.

sudo kanidm person radius generate-secret alice -D idm_admin
sudo kanidm group create radius_access_allowed -D idm_admin
sudo kanidm group add-members radius_access_allowed alice -D idm_admin

For Unix login, the POSIX attributes you set in step 4 are consumed by kanidm-unixd, libnss-kanidm and libpam-kanidm. Install them on the client machines that people log in to. They are not installed on this appliance, because a third-party NSS and PAM module in the authentication path of the machine that holds your only recovery command could lock you out of it.

Replacing the TLS certificate

Upstream labels the certificate generated at first boot as not for production. Replace it with a certificate from your own certificate authority, or from a public one, once the machine has a real DNS name.

Copy your full chain and private key over the two files Kanidm reads. Keep the ownership and modes, then reload.

Ownership matters more than mode. At startup, Kanidm checks its own TLS files and warns that a key "may not be secure" whenever the service account owns it, at any mode, because an owner can always give itself write access again. So the key belongs to root, and the service reads it through the kanidmd group. The server can use the key but cannot replace it. Kanidm re-reads both files on SIGHUP and reloads them if they are valid, so the reload does not interrupt service:

sudo install -o root -g kanidmd -m 0644 fullchain.pem /var/lib/kanidmd/chain.pem
sudo install -o root -g kanidmd -m 0640 privkey.pem /var/lib/kanidmd/key.pem
sudo systemctl reload kanidmd

The same certificate serves both HTTPS and LDAPS. Your LDAP clients stop needing the private certificate authority at the same moment your browsers do.

Backups

Kanidm runs an online backup every night at 22:00 UTC and keeps seven copies in /var/lib/kanidmd/backups/. Copy that directory off the machine on your own schedule.

sudo ls -la /var/lib/kanidmd/backups/ 2>/dev/null || echo "no scheduled backup has run yet on this machine"
sudo grep -A3 '^\[online_backup\]' /etc/kanidm/server.toml

To take a backup immediately, or to restore one, stop the server first. Both are offline operations on the database file:

sudo systemctl stop kanidmd
sudo -u kanidmd kanidmd database backup /var/lib/kanidmd/backups/manual.json
sudo -u kanidmd kanidmd database restore /var/lib/kanidmd/backups/manual.json
sudo systemctl start kanidmd

Changing the domain name after first boot

To change the domain after first boot, edit both settings, then run the rename. Afterwards you must re-register WebAuthn credentials and re-issue OAuth2 tokens, and your LDAP base DN changes with the domain.

sudo sed -i 's/^domain = .*/domain = "idm.yourcompany.example"/' /etc/kanidm/server.toml
sudo sed -i 's|^origin = .*|origin = "https://idm.yourcompany.example"|' /etc/kanidm/server.toml
sudo systemctl stop kanidmd
sudo -u kanidmd kanidmd domain rename
sudo systemctl start kanidmd

Troubleshooting

The server will not start. The configuration is validated before the service is allowed to run. Start with the validator and the first-boot log:

sudo -u kanidmd kanidmd configtest 2>&1 | tail -5 sudo systemctl status kanidm-firstboot.service --no-pager | head -12

Sign-in is refused for admin or idm_admin. This is expected until you recover the account. An account that has never been recovered reports "denied":"invalid credential state", because there is no password to reject.

A browser cannot reach the server by IP address. The certificate and the configured origin are both bound to the domain name. Use the name, or change the domain to one that resolves to the machine.

An LDAP client reports "Can't contact LDAP server". The cause is almost always TLS. The message is the same whether the certificate is untrusted, the port is wrong or the scheme is ldap:// instead of ldaps://. Point the client at your certificate authority with LDAPTLS_CACERT=/var/lib/kanidmd/ca.pem and add -d 1 to see which it is.

sudo journalctl -u kanidmd --no-pager -n 20

Support

SecureAnyCloud maintains this image. Security updates are applied unattended, and the image is rebuilt and republished as new Kanidm releases appear.

For help with this image, contact SecureAnyCloud support. For questions about Kanidm itself, see the upstream documentation at kanidm.github.io/kanidm/stable/.

The Mozilla Public License 2.0 that covers Kanidm ships with the image at /usr/share/doc/kanidm/LICENSE.md. Confirm it with:

head -1 /usr/share/doc/kanidm/LICENSE.md

Deploy on Azure

Launch Kanidm on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure