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.toml

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 dustin

Groups

# 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 list

SSH 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.arpa

SSH 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.arpa

Home 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-unixd

NixOS 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 daemon

  • kanidm-unixd-tasks.service — home directory management

  • AuthorizedKeysCommand — SSH key lookup from Kanidm

  • NSS 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.pem

REST 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

SymptomFix

Permission denied (publickey) on SSH

Wrong SSH key — check which key your client offers (ssh -v) and ensure it’s uploaded to Kanidm for that user

getent passwd doesn’t resolve Kanidm users

Check systemctl status kanidm-unixd — verify it can reach the Kanidm server

status=226/NAMESPACE in kanidm container

Systemd sandboxing conflicts with nspawn — override with lib.mkForce (see blog post)

DynamicUser UID mismatch

Use a static system user instead of DynamicUser=true for services sharing files

UnknownIssuer TLS error

Root CA not trusted — check security.pki.certificateFiles includes root_ca.crt

Home dir not created on login

Check systemctl status kanidm-unixd-tasks — needs write access to /home

User resolves but can’t log in

User must be in a group listed in pam_allowed_login_groups

Key Paths

PathPurpose

/etc/kanidm/config

Kanidm client config (URI)

/etc/kanidm/unixd

kanidm-unixd config (PAM groups, cache timeout)

/var/cache/kanidm-unixd/

Credential cache and HSM pin

/run/kanidm-unixd/

Unix socket for PAM/NSS communication

/run/wrappers/bin/kanidm_ssh_authorizedkeys

suid wrapper for SSH key lookup

/etc/nixos/certs/root_ca.crt

Internal root CA certificate (trusted fleet-wide)

/var/lib/step-ca/

step-ca data (certs, keys, database)

/var/lib/kanidm/

Kanidm data (database, TLS certs, backups)