SSH server hardening auditor in portable C11 with zero dependencies. Grades
sshd_config and authorized_keys files the way certwatch grades TLS:
one letter per file, one line per problem, CI-friendly exit codes.
It knows what the file doesn't say, too: OpenSSH defaults to
PasswordAuthentication yes, and a config that disables passwords but leaves
keyboard-interactive PAM at its default is still accepting them.
$ sshield examples/sshd_config examples/authorized_keys
sshd_config examples/sshd_config GRADE F
WARN line 3: PermitRootLogin prohibit-password - prefer 'no' unless root keys are required
CRIT line 4: PasswordAuthentication yes - passwords can be brute-forced; use keys only
WARN line 5: X11Forwarding yes - exposes the client X server; disable unless required
WARN line 6: MaxAuthTries 6 - allows many guesses per connection; 3-4 is plenty
CRIT line 7: Ciphers include a CBC-mode cipher - plaintext-recovery attacks
INFO ClientAliveInterval not set - idle sessions never time out
authorized_keys examples/authorized_keys GRADE F
CRIT line 2: 1024-bit RSA key - too small to be safe; replace with ed25519
WARN line 3: 2048-bit RSA key - below current 3072-bit guidance; rotate to ed25519
INFO line 4: ssh-ed25519 key without from=/command=/restrict options - usable from anywhere
WARN line 6: duplicate of the key on line 4
3 critical, 5 warning(s), 5 info
sshd_config (global section; Match blocks are skipped)
- Root login, password / empty-password / keyboard-interactive authentication
- Weak
Ciphers,MACs, andKexAlgorithmsentries (CBC, RC4, 3DES, MD5, SHA-1, group1/group14 key exchange) MaxAuthTries,LoginGraceTime,ClientAliveInterval,X11Forwarding,PermitUserEnvironment, legacy protocol 1- Dangerous defaults for directives the file never sets
authorized_keys (the base64 blob is actually decoded - RFC 4253 wire format)
- DSA keys (rejected by modern OpenSSH), RSA below 2048/3072 bits
- ECDSA NIST-curve advisories, with ed25519 as the recommendation
- Keys without
from=/command=/restrictoptions - Duplicate keys, unparseable entries sshd would silently ignore
- Group/world-writable file permissions
File type is detected from content, so you can just point it at both:
sshield /etc/ssh/sshd_config ~/.ssh/authorized_keysRequires CMake 3.16+ and any C11 compiler. No libraries beyond libc.
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
./build/sshield --versionusage: sshield [--json] <sshd_config | authorized_keys> ...
| Code | Meaning |
|---|---|
| 0 | clean, or informational findings only |
| 1 | warnings |
| 2 | critical findings, or a file could not be read |
Drop it in a cron job or CI step and alert on non-zero:
sshield --json /etc/ssh/sshd_config | jq '.files[0].grade'- Built with
-Wall -Wextra -Werror, C11, no VLAs, nosprintf. - Checks are table-driven: adding a directive check is one struct entry.
- The key parser implements base64 and the SSH public-key wire format directly (~100 lines) rather than depending on OpenSSL.
- Tests run with CTest against real ssh-keygen output embedded as fixtures.
ctest --test-dir build --output-on-failure