Skip to main content

audit-ssh-keys

CI codecov

Audit every SSH key on a Linux server in one pass: the server's own host keys, every account's authorized_keys, and any private keys sitting in ~/.ssh.

It reads the effective sshd configuration (via sshd -T) so it checks the files sshd will actually consult — including custom AuthorizedKeysFile locations and home directories outside /home — and tells you when sshd is sourcing keys from somewhere a file audit cannot see.

$ sudo python3 audit-ssh-keys.py
sshd config source: sshd -T

=== Server configuration ===
  [MEDIUM] PubkeyAcceptedAlgorithms accepts ssh-rsa (RSA with SHA-1 signatures)

=== Host keys (3) ===

/etc/ssh/ssh_host_rsa_key (last modified 2023-11-04)
  RSA 2048-bit  SHA256:/EqCt23YD1l/qoc+0D2kBP6H6jCo3An+wBUshL+LFPY
  [MEDIUM] RSA 2048-bit is below policy minimum of 3072
...
=== authorized_keys (3 file(s), 6 key(s)) ===

svc-backup: /var/lib/svc-backup/.ssh/authorized_keys (2 key(s), last modified 2026-02-17)
  [HIGH] /var/lib/svc-backup/.ssh/authorized_keys is owned by mallory, not svc-backup or root
...
Totals: CRITICAL: 1  HIGH: 3  MEDIUM: 5  LOW: 1  INFO: 2

Run it

The tool is one Python file. It needs nothing but Python 3.10+ and ssh-keygen (package openssh-client / openssh-clients) — no third-party dependencies, and no package to install. Copy it to the server and run it as root. No package is installed and no key file or configuration is touched; the only thing a run writes is a temporary directory holding a single symlink, created while fingerprinting a legacy PEM or PKCS#8 private key and removed immediately afterwards.

curl -fsSLO https://github.com/seanthegeek/audit-ssh-keys/releases/latest/download/audit-ssh-keys.py
scp audit-ssh-keys.py server:
ssh server sudo python3 audit-ssh-keys.py

releases/latest/download/ always fetches the newest release that is not a prerelease (the workflow marks any tag containing a letter as a prerelease, so latest never hands out one of those). To pin a specific version, use https://github.com/seanthegeek/audit-ssh-keys/releases/download/vX.Y.Z/audit-ssh-keys.py instead. Either way, python3 audit-ssh-keys.py --version tells you which version you have — worth recording alongside any report you keep.

Run it as root — other accounts' key files and the host private keys are not readable otherwise, and sshd -T needs root.

sudo python3 audit-ssh-keys.py            # human-readable report
sudo python3 audit-ssh-keys.py -v         # list every key, not just those with findings
sudo python3 audit-ssh-keys.py --json     # machine-readable, for pipelines and fleet rollups
sudo python3 audit-ssh-keys.py --authorized-keys-changed-within 7   # flag authorized_keys files modified in the last 7 days

Install

For a machine you administer, rather than one you're investigating, installing from PyPI gets you an audit-ssh-keys command that takes exactly the same options.

pipx install audit-ssh-keys
# or, from a checkout
pipx install .

Then run it as audit-ssh-keys, with the same options:

sudo audit-ssh-keys

What it checks

Section Checks
Server configuration Weak signature algorithms still accepted (ssh-dss, SHA-1 ssh-rsa, and their certificate forms); StrictModes no; PermitRootLogin yes; password auth enabled
Host keys Algorithm and size; ownership and mode (sshd refuses a root-owned key with group/other permission bits; a key owned by anyone else loads regardless, which is its own problem); a .pub file next to the key checked against the key itself; configured-but-missing keys; passphrase-protected keys; missing Ed25519 key; optionally, keys whose file changed inside a window you name (--host-keys-changed-within)
authorized_keys Every account, every configured path; algorithm and size; what StrictModes would reject (the file and every directory above it, up to $HOME for a file inside the home directory and otherwise up to /, must be owned by the user or root and not group/world-writable); the same key reused across accounts; unrestricted keys on uid-0 accounts; malformed lines; optionally, files changed inside, or untouched for longer than, a window you name (--authorized-keys-changed-within, --authorized-keys-unchanged-for)
Private keys in ~/.ssh Algorithm and size; ownership and mode; whether a passphrase is set; a .pub file next to the key checked against the key itself

Every host key, authorized_keys file and private key is also reported with the date it was last modified. The three DAYS options in the table turn that date into a finding; they are off unless you ask for them.

Every finding has a severity (CRITICAL, HIGH, MEDIUM, LOW, INFO). See docs/findings.md for what each one means and why it has the severity it does.

Documentation

  • Usage — options, exit codes, JSON schema, running without installing, fleet usage
  • Findings reference — every finding, its severity, and remediation
  • How it works — what is read, what is not, known gaps
  • Development — running tests, linting, releasing

Disclaimer

This tool was developed with the assistance of AI coding agents. All code has been reviewed and tested by a human, but you should review it yourself before running it on systems you care about. It only reads files and runs ssh-keygen and sshd -T; it never modifies keys or configuration.

License

MIT — see LICENSE.

Metadata

Release files for audit-ssh-keys 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for audit-ssh-keys 0.1.0
File Size Uploaded
audit_ssh_keys-0.1.0.tar.gz 138.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for audit-ssh-keys 0.1.0
File Interpreter ABI Platform
audit_ssh_keys-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 190.8 kB

Release files / audit_ssh_keys-0.1.0.tar.gz

Download URL audit_ssh_keys-0.1.0.tar.gz
Size 138.1 kB
Tags Source
SHA-256 checksum
How to use checksums
2f1c8617809c16b4dfe4bf9e2bcc48a928040c4c249eb36ee640b646b040a14a
BLAKE2b-256 checksum
How to use checksums
1668c47e40702bc8a02ccd03d6bdc9bec8789dfe45635b045457d4228c769e9d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

Release files / audit_ssh_keys-0.1.0-py3-none-any.whl

Download URL audit_ssh_keys-0.1.0-py3-none-any.whl
Size 52.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6629c4396a7af0eeb21de7c56fd9e51ca100acd75bc53c67da6dddc1d878b544
BLAKE2b-256 checksum
How to use checksums
9741a6084c12d3b70bb863a1c7f58fff50c7419d111764849792320ed25703c3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page