sshd_config Analyzer: Grade and Harden Your SSH Server Config
Paste your /etc/ssh/sshd_config and get a graded A to F review with a finding for every weak directive, the exact line it is on, and the line that fixes it. Handles Match blocks, Include drop-ins, and the first-value-wins rule that makes most SSH config checkers wrong. Nothing is uploaded.
Your config is analyzed entirely in this browser tab. Nothing is uploaded, logged, or sent anywhere. Never paste a private key into any website, including this one.
How to check your sshd_config
- On your server, print the config:
cat /etc/ssh/sshd_config. Copy the output. - Paste it into the box above. The review runs as you type, in your browser, and nothing is uploaded.
- Read the findings worst-first. Each one shows the directive, the line it is on, what it is set to now, and what it should be.
- If the tool warns that an
Includemay override your settings, runsudo sshd -Ton the server and paste that output instead. It resolves every included file and Match block, so the answer is definitive. - Switch to the Hardening patch tab, apply the safe lines, then verify with
sudo sshd -tbefore reloading.
The first-value-wins rule that breaks most SSH config checkers
Almost every configuration format on a Linux server uses the last value it reads. sshd_config does the opposite.The manual is explicit: "unless noted otherwise, for each keyword, the first obtained value will be used".
So a file containing PasswordAuthentication yes on line 20 and PasswordAuthentication no on line 60 accepts passwords. sshd prints no warning, the second line is simply dead, and reading the file top to bottom gives you the wrong answer unless you know the rule. This tool flags every duplicate and tells you which line actually won.
A few directives are exceptions and accumulate instead of overriding: HostKey, ListenAddress, Port, AllowUsers, AllowGroups, AcceptEnv, Include and Subsystem. Reporting those as duplicates is a false positive, so this tool does not.
Why your Ubuntu server still accepts passwords
This is the most common way a hardened-looking config is not hardened at all. Ubuntu 22.04 and later ship an sshd_config whose first line is Include /etc/ssh/sshd_config.d/*.conf.
Combine that with first-value-wins and the consequence is severe: anything in that directory is read first and therefore overrides the whole main file. On a cloud image, cloud-init writes /etc/ssh/sshd_config.d/50-cloud-init.conf containing PasswordAuthentication yes. You can set PasswordAuthentication no in the main config, restart sshd, and still be accepting passwords.
When this tool sees an Includeahead of a directive it marks that finding "may be overridden" rather than quietly passing it. To settle it, run sudo sshd -T and paste the output: that is the fully resolved configuration sshd is actually running, with every include and default filled in.
Disabling passwords without locking yourself out
PasswordAuthentication nois the single most valuable line in an SSH config and the single easiest way to lose a server. If key login is not already working, applying it ends your access the moment sshd reloads, and the only way back is your provider's console or a rescue boot.
That is why the hardening patch here is split in two. Everything that cannot end your session is in one block. PasswordAuthentication, PermitRootLogin and the allow-list directives are in a separate block behind a warning, because each of them can.
The safe order is always the same: open a second terminal, log in with your key, confirm it does not ask for a password, and keep your first session open the entire time. Then apply, run sudo sshd -t to check the syntax, and sudo systemctl reload ssh rather than restart so existing sessions survive.
One more trap: PasswordAuthentication no on its own does not disable password login. PAM can still prompt through keyboard-interactive, so you need KbdInteractiveAuthentication no as well. The patch includes it. The exception is a server using PAM-based MFA such as Google Authenticator, where that line would lock you out instead.
Match blocks, and the exception you forgot about
A Match block applies its directives only to connections meeting a condition, and everything after it stays inside that block until the next Match or the end of the file. That makes it easy to write a global policy and then quietly undo it:
PermitRootLogin no at the top, then Match User deploy followed by PermitRootLogin yes three hundred lines down. The global setting looks right in a review, and root login is still possible.
This tool evaluates Match blocks separately from global scope. A weak directive inside a Match block is never reported as your global setting, and a Match block that re-enables something the global scope disabled gets its own finding.
What a config file cannot tell you
This tool reads text. It is honest about the limits of that. It cannot tell you whether a key in authorized_keys still belongs to someone who works here, whether an account has a blank password, who is in the sudo group, or whether an old employee still has access to three of your servers. None of that is in sshd_config.
Those checks need the running machine. The SSH and Access checklist lists all eleven of them as plain shell commands you can paste into a terminal, and the fix pages cover what to do with each result. CtrlOps runs the whole set over SSH across every server you manage and hands back a hardening score and a PDF, but the checklist works perfectly well on its own.
The directives that matter most, and what they should be
| Directive | Default if unset | What you want | Why |
|---|---|---|---|
| PermitRootLogin | prohibit-password | no | The default still lets root in with a key. Use a normal account and sudo so actions are attributable. |
| PasswordAuthentication | yes | no | The default is the insecure value. Leaving it alone means your server accepts passwords. |
| KbdInteractiveAuthentication | yes | no | PAM can still prompt for a password through this even after PasswordAuthentication no. |
| PermitEmptyPasswords | no | no | Set to yes, any account with a blank password can log in over the network. |
| MaxAuthTries | 6 | 3 | Each connection gets this many guesses before it is dropped. |
| AllowGroups | unset | ssh-users | Without an allow list, every account on the host may attempt SSH. |
| ClientAliveInterval | 0 | 300 | Zero means an abandoned session stays open indefinitely. |
| LogLevel | INFO | VERBOSE | VERBOSE records the key fingerprint used for each login, which is what makes an audit possible. |
| X11Forwarding | no (yes on Ubuntu) | no | Debian and Ubuntu ship yes. On a headless server it is unused attack surface. |
Frequently asked questions
Related developer tools
Check one file here, audit every server there.
CtrlOps runs 25 security audits over SSH across your whole fleet, scores each server, and fixes findings with approval-gated AI. Your keys never leave your machine.
✓ Start instantly·✓ No credit card·✓ No sneaky autorenewals