Farrier

SSH keys

Farrier keeps an inventory of SSH host keys and the user keys in authorized_keys, without installing anything on the hosts. The dashboard shows it under Inventory → SSH keys.

Two ways in

Logs inReads
Host scanNothe SSH banner, host keys, and the key exchange, cipher and MAC algorithms the server offers
Key collectionYes, with an access profileauthorized_keys for each user, with key type, size, fingerprint, comment and options

A host scan is like discovery for TLS: it only completes the start of an SSH handshake. Key collection needs credentials, so it uses an access profile.

Access profiles

An access profile says how Farrier logs in to a set of hosts.

FieldMeaning
usernamethe account Farrier logs in as
private_keya secret reference (vault:, env: or file:). Inline keys are refused
portdefaults to 22
hostshost names, IPs or CIDR ranges this profile applies to
sudoread every user's authorized_keys, not only the login user's
known_hosts_policytofu trusts a host key the first time and refuses a change after that; strict only connects to hosts already known

Test a profile before collecting with it. The dashboard stores the private key in the server's secret store when you paste it, so it is never saved in plain text.

Findings

Each key and host gets findings with a severity:

  • critical: a weak key, such as RSA under 2048 bits or DSA, or a weak host key.
  • warning: an unknown key, such as one with no comment that nobody can account for.
  • info: worth knowing, nothing to do yet.

The summary also counts hosts with weak host keys and hosts that offer a post-quantum key exchange.

Removing a key

Removal always starts with a dry run. Farrier shows the exact line it will take out of which authorized_keys file; nothing changes until you confirm. Only admins can remove keys.

Don't lock yourself out

Removing the key Farrier itself logs in with ends its access to that host. Check the dry run before you confirm.

API

GET    /api/ssh/summary                 # hosts, keys, findings by severity
GET    /api/ssh/keys
GET    /api/ssh/hosts
POST   /api/ssh/scan                    # {"targets": "10.0.4.0/24:22, bastion.example.com"}
POST   /api/ssh/collect                 # {"access": "linux-fleet"}  (omit to use every profile)
POST   /api/ssh/keys/{id}/remove        # {"dry_run": true} first, then {"dry_run": false}
GET    /api/ssh/access
PUT    /api/ssh/access/{name}
DELETE /api/ssh/access/{name}
POST   /api/ssh/access/{name}/test