TechEarl

ssh_scan: Audit Your SSH Server Configuration

ssh_scan is Mozilla's SSH configuration and policy scanner: point it at a host, get a JSON report of the algorithms the daemon offers and where they fail a security policy.

Ishan Karunaratne⏱️ 9 min readUpdated
Share thisCopied
Use ssh_scan to audit an SSH server's key exchange, cipher and MAC algorithms against the Mozilla Modern policy and get a JSON compliance report. Note: the project is archived.

You can audit a remote SSH server's configuration in one command:

bash
ssh_scan -t ssh.example.com

ssh_scan is Mozilla's SSH configuration and policy scanner. It connects to a host, reads the algorithms the SSH daemon advertises in its key-exchange handshake (key exchange, host key, cipher, and MAC algorithms), and prints a JSON report of what it found and, by default, where that configuration fails Mozilla's "Modern" OpenSSH policy. It is a black-box check: it never logs in, it only inspects the negotiation the server is willing to do.

One thing up front, because it would be dishonest to leave it for the footnotes: Mozilla archived this project on 24 January 2022 and no longer maintains it. The last release was v0.0.44 in May 2021. The tool still runs and is still useful as a quick read of a server's offered algorithms, but the bundled policy is frozen at 2021's idea of "modern," and you should treat its pass/fail verdict as a starting point, not gospel. I cover what to use instead further down.

Install it

Two clean ways. The gem is the canonical install:

bash
gem install ssh_scan
ssh_scan -t ssh.example.com

If you would rather not put an unmaintained Ruby gem on your machine, the Docker image keeps it contained:

bash
docker run --rm -it mozilla/ssh_scan -t ssh.example.com

Both give you the same ssh_scan binary and the same flags. On a current Ruby (3.x) the gem can be fussy about its pinned dependencies; that is the cost of an archived project, and the Docker route sidesteps it.

Read the report

A scan against a host running an older OpenSSH looks like this. I have trimmed the long algorithm arrays for readability; the real output lists every offered algorithm in full:

json
{
  "ssh_scan_version": "0.0.44",
  "ip": "192.168.1.1",
  "hostname": "",
  "port": 22,
  "server_banner": "SSH-2.0-OpenSSH_7.1p2 Debian-2",
  "ssh_version": 2.0,
  "os": "debian",
  "ssh_lib": "openssh",
  "key_algorithms": [
    "curve25519-sha256@libssh.org",
    "diffie-hellman-group14-sha1"
  ],
  "encryption_algorithms_server_to_client": [
    "chacha20-poly1305@openssh.com",
    "aes256-gcm@openssh.com"
  ],
  "mac_algorithms_server_to_client": [
    "hmac-sha2-512-etm@openssh.com",
    "hmac-sha1"
  ],
  "compliance": {
    "policy": "Mozilla Modern",
    "compliant": false,
    "recommendations": [
      "Remove these Key Exchange Algos: diffie-hellman-group14-sha1",
      "Remove these MAC Algos: umac-64-etm@openssh.com, hmac-sha1-etm@openssh.com, umac-64@openssh.com, hmac-sha1"
    ]
  }
}

The two halves to read: the algorithm arrays are the raw facts (this is exactly what the daemon offered), and the compliance block is ssh_scan's opinion about them. Here the server offers modern primitives (ChaCha20-Poly1305, AES-256-GCM, Curve25519) but also still advertises diffie-hellman-group14-sha1 and hmac-sha1, so it fails the Modern policy. The recommendations array is the actionable part: those are the lines you act on.

Note the recommendations only ever say "remove these." ssh_scan flags weak algorithms that should not be on the menu; it does not flag the absence of strong ones. That is a deliberate limitation worth knowing.

An ssh_scan JSON report against a current OpenSSH 9.6p1 server, showing the compliance block with policy Mozilla Modern, compliant false, recommendations to remove several algorithms, and an overall grade of D
A real ssh_scan run against a current OpenSSH 9.6 scores grade D: the frozen 2021 policy even lists the post-quantum sntrup761x25519 key exchange under 'remove'. A vivid reminder the archived policy is a starting point, not gospel.

Translate a finding into an sshd_config change

The report above wants two things gone: a SHA-1 key-exchange algorithm and a SHA-1 MAC. You fix that by pinning explicit allow-lists in /etc/ssh/sshd_config, dropping the flagged entries:

code
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr

Validate the syntax before you reload, so a typo does not lock you out of the box:

bash
sudo sshd -t && sudo systemctl reload ssh

The unit name differs by distro: it is ssh on Debian and Ubuntu (as in the example above), but sshd on RHEL, Fedora, and most others, so use sudo systemctl reload sshd there. Then re-run ssh_scan -t ssh.example.com and confirm "compliant": true. This edit-scan-reload loop is the whole point of the tool: it gives you a concrete list to act on instead of "your SSH is probably fine."

Useful flags

ssh_scan has a small, sane set of options. These are the ones I actually reach for:

FlagWhat it does
-t, --targetThe IP, hostname, or CIDR range to scan. Repeatable.
-f, --fileRead targets from a file, one per line, for bulk scans.
-p, --portSSH port (default 22). Set this for daemons on a non-standard port.
-P, --policyPath to a custom policy file (YAML) instead of Mozilla Modern.
-o, --outputWrite the JSON report to a file instead of stdout.
-T, --timeoutPer-connection timeout in seconds.
--threadsWorker threads for multi-host scans (default 5).
-V, --verbosityLogging level (INFO, DEBUG, etc.).

A bulk scan of every host in a file, written to a report, looks like:

bash
ssh_scan -f hosts.txt -o ssh-audit-report.json --threads 10

Writing your own policy

The default "Mozilla Modern" policy is just a YAML file, and you can supply your own with -P. A policy declares the algorithms you consider acceptable in each category; anything the server offers that is not on your list shows up in recommendations. A minimal custom policy:

yaml
name: "House SSH policy"
kex:
  - curve25519-sha256@libssh.org
  - diffie-hellman-group-exchange-sha256
macs:
  - hmac-sha2-512-etm@openssh.com
  - hmac-sha2-256-etm@openssh.com
encryption:
  - chacha20-poly1305@openssh.com
  - aes256-gcm@openssh.com
compression:
  - none

Run it with ssh_scan -t ssh.example.com -P house-policy.yml. This is genuinely the most durable way to use the tool today: because the bundled Modern policy is frozen at 2021, maintaining your own short allow-list and scanning against that keeps the verdict meaningful even though the project is dead.

When not to reach for ssh_scan

Be honest about the archived status. For a one-off check of a server you can already log into, ssh -Q plus reading sshd -T output tells you what the daemon supports and what it is actually configured to negotiate, with no third-party tool at all (both live in the SSH cheat sheet alongside the rest of the everyday commands):

bash
ssh -Q kex          # key-exchange algorithms this OpenSSH knows
sudo sshd -T | grep -iE 'kex|cipher|macs'   # what THIS daemon is configured to offer

For an actively maintained external scanner, ssh-audit (jtesta's fork lineage, Python, still shipping releases) covers the same ground as ssh_scan and then some: it grades algorithms, flags known CVEs against the detected OpenSSH version, and notes missing strong algorithms, which ssh_scan never did. If I were standing this up fresh in 2026, that is what I would script into CI. ssh_scan remains handy because its JSON shape is simple and a lot of older audit pipelines already parse it, but I would not start a new project on it.

ToolMaintainedBest for
ssh_scanNo (archived 2022)Quick remote read of offered algorithms; legacy pipelines that already parse its JSON
ssh-auditYesModern remote audits: grades, CVE flags, missing-strong-algorithm warnings
ssh -Q + sshd -TYes (ships with OpenSSH)Local checks on a box you already control, zero dependencies

FAQ

See also

Sources

Authoritative references this article was fact-checked against.

Tagsssh_scanSSHOpenSSHSecurityAuditingMozillaLinuxHardening

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Software Systems Architect · Senior Software Engineer · Engineering Leadership

Software systems architect and senior software engineer with more than two decades designing, building, and running production software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Now a CTO, though what I write here is drawn from the full arc of that work, across architecture, engineering, and operations, not any single job.

Keep reading

Related posts