Kanidm & SSH Identity Management
Authentication
# Login to Kanidm CLI (interactive)
kanidm login --name idm_admin
# Login to Kanidm CLI (specify server)
KANIDM_URL=https://idm.infra.home.arpa:8443 kanidm login --name idm_admin
# Check who you're logged in as
kanidm whoami
# Recover admin passwords (run inside kanidm container)
kanidmd recover-account admin -c /nix/store/...-server.toml
kanidmd recover-account idm_admin -c /nix/store/...-server.tomlWeb UI: https://idm.infra.home.arpa:8443
Users
# Create a person
kanidm person create dustin "Dustin Lee"
# Get person details
kanidm person get dustin
# List all persons
kanidm person list
# Enable POSIX attributes (required for Linux login)
kanidm person posix set dustin --shell /run/current-system/sw/bin/zsh
# Set POSIX (unix) password via REST API
curl -sk -X PUT -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"value":"your-password"}' \
"https://idm.infra.home.arpa:8443/v1/person/dustin/_unix/_credential"
# Delete a person
kanidm person delete dustinGroups
# Create a group
kanidm group create shell_users
# Enable POSIX attributes on group
kanidm group posix set shell_users
# Add members
kanidm group add-members shell_users dustin
# Remove members
kanidm group remove-members shell_users dustin
# List group members
kanidm group list-members shell_users
# List all groups
kanidm group listSSH Keys
# Add SSH public key (label + key)
kanidm person ssh add-publickey dustin griffin \
"ssh-ed25519 AAAA... leed@griffin"
kanidm person ssh add-publickey dustin laptop \
"ssh-ed25519 AAAA... dustin@laptop"
# List SSH keys for a person
kanidm person ssh list-publickeys dustin
# Remove an SSH key by label
kanidm person ssh delete-publickey dustin griffin
# Test SSH key lookup (as sshd would)
/run/wrappers/bin/kanidm_ssh_authorizedkeys dustin@infra.home.arpaSSH Key Upload via REST API
# Body is a JSON tuple: ["label", "key"]
curl -sk -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '["griffin","ssh-ed25519 AAAA... leed@griffin"]' \
"https://idm.infra.home.arpa:8443/v1/person/dustin/_ssh_pubkeys"
# Verify
curl -sk -H "Authorization: Bearer $TOKEN" \
"https://idm.infra.home.arpa:8443/v1/person/dustin/_ssh_pubkeys"SSH Login
# Login as Kanidm user (note the @domain suffix)
ssh dustin@infra.home.arpa@wolfhound.infra.home.arpa
# SSH config shortcut (~/.ssh/config)
# Host wolfhound-dustin
# HostName wolfhound.infra.home.arpa
# User dustin@infra.home.arpaHome directory is created automatically at /home/dustin@infra.home.arpa (symlink to UUID-based path).
PAM/NSS Verification
# Check if user resolves via NSS
getent passwd dustin@infra.home.arpa
# Check if group resolves
getent group shell_users@infra.home.arpa
# Check user ID and groups
id dustin@infra.home.arpa
# Check kanidm-unixd service
systemctl status kanidm-unixd
# Check kanidm-unixd-tasks (home dir management)
systemctl status kanidm-unixd-tasks
# View kanidm-unixd logs
journalctl -u kanidm-unixd -f
# Restart after config changes
sudo systemctl restart kanidm-unixdNixOS Client Config
Deployed fleet-wide via common.nix:
services.kanidm = {
package = pkgs.kanidm_1_9;
client = {
enable = true;
settings.uri = "https://idm.infra.home.arpa:8443";
};
unix = {
enable = true; # PAM/NSS integration
sshIntegration = true; # AuthorizedKeysCommand
settings = {
kanidm.pam_allowed_login_groups = [ "shell_users" ];
cache_timeout = 900; # 15 min credential cache
};
};
};This enables:
kanidm-unixd.service— PAM/NSS daemonkanidm-unixd-tasks.service— home directory managementAuthorizedKeysCommand— SSH key lookup from KanidmNSS modules for passwd/group resolution
Certificates (step-ca)
# Check CA health
step ca health --ca-url https://ca.infra.home.arpa:8443 \
--root /etc/nixos/certs/root_ca.crt
# Request a certificate via ACME
step ca certificate "myservice.infra.home.arpa" cert.pem key.pem \
--ca-url https://ca.infra.home.arpa:8443 \
--root /etc/nixos/certs/root_ca.crt
# Issue a certificate directly (using intermediate CA key)
step certificate create "myservice.infra.home.arpa" cert.pem key.pem \
--profile leaf \
--ca /var/lib/step-ca/certs/intermediate_ca.crt \
--ca-key /var/lib/step-ca/secrets/intermediate_ca.key \
--ca-password-file /var/lib/step-ca/secrets/password \
--san "myservice.infra.home.arpa" \
--san "192.168.2.xx" \
--not-after 720h \
--no-password --insecure --force
# Inspect a certificate
step certificate inspect cert.pem --short
# Check certificate expiry
openssl x509 -enddate -noout -in cert.pem
# Verify certificate chain
openssl verify -CAfile /etc/nixos/certs/root_ca.crt cert.pemREST API Authentication
# Three-step cookie-based auth flow
KANIDM="https://idm.infra.home.arpa:8443"
COOKIE=$(mktemp)
# Step 1: Init
curl -sk -c "$COOKIE" -H "Content-Type: application/json" \
-d '{"step":{"init":"idm_admin"}}' "$KANIDM/v1/auth"
# Step 2: Begin
curl -sk -b "$COOKIE" -c "$COOKIE" -H "Content-Type: application/json" \
-d '{"step":{"begin":"password"}}' "$KANIDM/v1/auth"
# Step 3: Credential (returns JWT in state.success)
curl -sk -b "$COOKIE" -c "$COOKIE" -H "Content-Type: application/json" \
-d '{"step":{"cred":{"password":"your-password"}}}' "$KANIDM/v1/auth"
# Extract token from response and use as Bearer token
curl -sk -H "Authorization: Bearer $TOKEN" "$KANIDM/v1/person/dustin"Troubleshooting
| Symptom | Fix |
|---|---|
| Wrong SSH key — check which key your client offers ( |
| Check |
| Systemd sandboxing conflicts with nspawn — override with |
| Use a static system user instead of |
| Root CA not trusted — check |
Home dir not created on login | Check |
User resolves but can’t log in | User must be in a group listed in |
Key Paths
| Path | Purpose |
|---|---|
| Kanidm client config (URI) |
| kanidm-unixd config (PAM groups, cache timeout) |
| Credential cache and HSM pin |
| Unix socket for PAM/NSS communication |
| suid wrapper for SSH key lookup |
| Internal root CA certificate (trusted fleet-wide) |
| step-ca data (certs, keys, database) |
| Kanidm data (database, TLS certs, backups) |