PAM module (pam-ssh-agent-webauthn)
pam-ssh-agent-webauthn is the PAM module that lets a target host accept a WebAuthn passkey (forwarded over the SSH agent) as a sudo / login factor. For the guide, see Guides → PAM module. This page is the reference.
Source: github.com/rado0x54/pam-ssh-agent-webauthn. Standalone — no runtime dependency on ShellWatch. (The shared library still installs under the historical pam_ssh_webauthn.so filename so existing /etc/pam.d/ configs keep working.)
PAM arguments
Section titled “PAM arguments”auth sufficient pam_ssh_webauthn.so [args]| Argument | Default | Notes |
|---|---|---|
file=<path> | /etc/security/authorized_keys | Authorized public-keys file. WebAuthn keys only — other types are silently ignored. |
socket=<path> | $SSH_AUTH_SOCK | Override the SSH agent socket path. |
Requires Rust 1.70+ and PAM headers.
sudo apt install libpam0g-dev # Debian/Ubuntusudo dnf install pam-devel # Fedora/RHEL
git clone https://github.com/rado0x54/pam-ssh-agent-webauthn.gitcd pam-ssh-agent-webauthncargo build --releaseOutput:
| Platform | Path |
|---|---|
| Linux | target/release/libpam_ssh_webauthn.so |
| macOS | target/release/libpam_ssh_webauthn.dylib |
Crypto backend
Section titled “Crypto backend”Default: pure-Rust p256 crate.
For FIPS / OpenSSL-backed verification:
cargo build --release --features native-cryptoRequires libssl-dev / openssl-devel.
Wire formats
Section titled “Wire formats”Key blob (webauthn-sk-ecdsa-sha2-nistp256@openssh.com)
Section titled “Key blob (webauthn-sk-ecdsa-sha2-nistp256@openssh.com)”string "webauthn-sk-ecdsa-sha2-nistp256@openssh.com"string "nistp256"string ec_point (65 bytes: 0x04 || x || y, uncompressed P-256)string application (relying-party ID, e.g. "shellwatch.example.com")Signature blob (WebAuthn variant)
Section titled “Signature blob (WebAuthn variant)”string algorithm ("sk-ecdsa-..." or "webauthn-sk-ecdsa-...")string ecdsa_signature (mpint R || mpint S)byte flagsuint32 counterstring origin (WebAuthn origin URL)string clientDataJSON (contains base64url-encoded challenge)string extensions (CBOR, typically empty)Verification
Section titled “Verification”signed_data = SHA256(application) || flags || counter || extensions || SHA256(clientDataJSON)ECDSA P-256 signature is verified over signed_data using the public key from the authorized-keys file.
Validation checks
Section titled “Validation checks”Mirrors OpenSSH’s webauthn_check_prepare_hash:
| Check | Requirement |
|---|---|
| Algorithm | webauthn-sk-ecdsa-sha2-nistp256@openssh.com or sk-ecdsa-sha2-nistp256@openssh.com |
| User Presence (UP) | flag bit 0x01 set |
| Attested Credential Data (AD) | flag bit 0x40 NOT set (assertion, not registration) |
| Extension Data (ED) | flag bit 0x80 consistent with extensions presence |
| Origin | no quote characters; matches between signature blob and clientDataJSON |
clientDataJSON.type | "webauthn.get" |
clientDataJSON.challenge | base64url-decodes to the PAM-issued challenge |
clientDataJSON.origin | matches signature-blob origin |
| Trailing data | rejected — no bytes after extensions |
What it does not handle
Section titled “What it does not handle”- Standard SSH key types (RSA, Ed25519, ECDSA) — use pam-ssh-agent.
- Standard FIDO2 SK keys (
sk-ecdsa-...withapplication=ssh:). - Ed25519 SK keys.
- SSH certificates.
authorized_keys_command(file-only).
Non-WebAuthn entries in the authorized-keys file are silently ignored.
Limits
Section titled “Limits”- No counter / replay protection. The counter is integrity-protected but not checked for monotonic increase. A captured signature still can’t be replayed (each invocation uses a fresh challenge), but a cloned authenticator with a reset counter would not be detected.
- No User Verification (UV) enforcement. UP is required, UV is not.
- Agent timeout: 60 s. Enough for a real user to respond to the browser approval.
Trust model
Section titled “Trust model”| Layer | Trust |
|---|---|
| Authenticator hardware, browser origin check, this verifier | Trusted |
| The SSH agent forwarder (ShellWatch, OpenSSH, etc.) | Transport only — can deny / delay, cannot forge |
| Verification on the target | ECDSA P-256 over a fresh challenge against /etc/security/authorized_keys |
A compromised broker can decline to forward a sign request; it cannot manufacture one. The PAM verifier on the target is the authoritative gate.
Logging
Section titled “Logging”Syslog facility AUTH, identifier pam_ssh_webauthn. Set log level via PAM stack or syslog config.
Examples (examples/)
Section titled “Examples (examples/)”authenticator.rs— runs the full flow without PAM. Useful for sanity checks.list_keys.rs— dumps the agent’s exposed key blobs (matching them against your authorized-keys file).
SSH_AUTH_SOCK=/path/to/agent.sock cargo run --example authenticator -- /etc/security/authorized_keysSSH_AUTH_SOCK=/path/to/agent.sock cargo run --example list_keys