- Go 99.2%
- Makefile 0.5%
- Dockerfile 0.3%
An SSH probe (reachability, banner, KEX/host-key algorithm posture, host keys) depends only on the set of addresses and ports dialed and the probe knobs, never on which domain name points at the server: SSH has no SNI, so the same daemon answers identically behind every name. Implement sdk.ObservationSharer so the host can probe an address set once and serve every target (of the same user) that points at it, instead of re-connecting per record. The share key sorts the resolved addresses and ports and folds in the probe timeout, the auth-probe flag, and the declared SSHFP fingerprints — the latter live in the observation and drive the SSHFP-match rule, so two services with the same endpoints but different SSHFP must not share a verdict. The host/Domain label is intentionally excluded, mirroring the ping checker's exclusion of which domain the addresses belong to: it does not change reachability, the negotiated algorithms, the host keys, or the SSHFP comparison. Inputs with no probable address yield "" so the host falls back to per-target caching. |
||
|---|---|---|
| checker | ||
| plugin | ||
| .drone-manifest.yml | ||
| .drone.yml | ||
| .gitignore | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| Makefile | ||
| NOTICE | ||
| README.md | ||
checker-ssh
Deep SSH security checker for happyDomain.
Given an abstract.Server service (A / AAAA / SSHFP records), the checker
connects to the advertised SSH port(s) and produces a comprehensive
audit: reachability, banner-to-CVE matches, full algorithm posture
(KEX / host-key / cipher / MAC / compression), observed host keys,
SSHFP fingerprint validation, and authentication method exposure.
What it checks
Reachability
- TCP connect to port 22 (and optionally extra ports) on every A/AAAA address of the service.
- SSH-2.0 protocol banner read.
Banner / CVE
The banner is parsed into an OpenSSH_X.Ypz tuple and matched against
a bundled subset of the ssh-audit
vulnerability database, including:
| CVE | Issue |
|---|---|
| CVE-2024-6387 | regreSSHion - unauth RCE as root (8.5p1 <= v < 9.8p1) |
| CVE-2023-38408 | ssh-agent PKCS#11 RCE (5.5 ≤ v < 9.3p2) |
| CVE-2023-48795 | Terrapin prefix-truncation (v < 9.6p1) |
| CVE-2021-41617 | AuthorizedKeysCommand privdrop (6.2 ≤ v < 8.8p1) |
| CVE-2020-15778 | scp command injection (v < 8.4p1) |
| CVE-2018-15473 | username enumeration (v < 7.8p1) |
Algorithm posture
A raw SSH_MSG_KEXINIT is exchanged with the server to enumerate every
algorithm it advertises. Each entry is graded against a curated table
inspired by ssh-audit:
- crit:
diffie-hellman-group1-sha1(Logjam),3des-cbc(Sweet32),arcfour*,hmac-md5*,hmac-sha1-96,ssh-dss,none. - warn:
ssh-rsa(RFC 8332 deprecated),diffie-hellman-group14-sha1, AES-CBC, non-ETM MACs,hmac-sha1, missing strict-KEX marker (CVE-2023-48795 mitigation). - ok:
curve25519-sha256,sntrup761x25519-sha512@openssh.com,mlkem768x25519-sha256,ssh-ed25519, AES-GCM/ChaCha20-Poly1305, SHA-2 ETM MACs.
SSHFP validation
For each observed host key, the checker:
- computes SHA-1 and SHA-256 fingerprints,
- matches them against the
abstract.Server.SSHFPrecords declared in the zone, - flags
sshfp_missing,sshfp_not_covered,sshfp_only_sha1orsshfp_mismatchas appropriate, with copy-pasteable fix snippets.
Authentication methods
A second connection is opened with a dummy user and no credentials. The
server replies with the auth-method list, which is surfaced as
password, publickey, keyboard-interactive chips. Password
authentication triggers a password_auth_enabled warning.
HTML report
The iframe report is structured for "fix me fast":
- Overall status banner + SSHFP verdict chips.
- What to fix: top issues (crit -> warn), each with a copy-pasteable remediation snippet (sshd_config lines, SSHFP DNS records, ssh-keygen invocations).
- SSHFP table with per-record match status.
- Per-endpoint details: expandable sections with host-key fingerprints, algorithm tables (broken entries highlighted), and advertised auth methods.
Usage
Standalone HTTP server
make
./checker-ssh -listen :8080
Endpoints:
GET /healthGET /definitionPOST /collectPOST /evaluatePOST /report
Docker
make docker
docker run -p 8080:8080 happydomain/checker-ssh
happyDomain plugin
make plugin
# produces checker-ssh.so, loadable as a Go plugin
Options
| Option | Type | Default | Description |
|---|---|---|---|
ports |
string | "" |
Comma-separated extra ports (port 22 is always probed). |
probeTimeoutMs |
number | 10000 |
Per-endpoint dial + handshake timeout. |
includeAuthProbe |
bool | true |
Open a second connection to enumerate auth methods. |
Rules
| Code | Description | Severity |
|---|---|---|
ssh.tcp_reachable |
Verifies that every probed (address, port) pair accepts a TCP connection. | CRITICAL |
ssh.handshake |
Verifies that the SSH banner exchange and KEXINIT parse succeed on every reachable endpoint. | CRITICAL |
ssh.protocol_version |
Verifies every endpoint advertises SSH-2 and rejects the legacy SSH-1 protocol. | CRITICAL |
ssh.banner_software |
Flags servers whose banner is not a recognised OpenSSH build. | INFO |
ssh.known_vulnerabilities |
Matches the advertised OpenSSH version against a curated catalog of remotely-observable CVEs. | CRITICAL |
ssh.host_key_strength |
Flags SSH host keys whose size is below the currently accepted minimum (e.g. RSA < 2048 bits). | CRITICAL |
ssh.kex_algorithms |
Flags key-exchange algorithms advertised by the server that are weak or broken. | CRITICAL |
ssh.host_key_algorithms |
Flags server host-key algorithms that are weak or deprecated (ssh-rsa/SHA-1, ssh-dss, ...). | CRITICAL |
ssh.cipher_algorithms |
Flags symmetric ciphers advertised by the server that are weak or broken (CBC, 3DES, RC4, ...). | CRITICAL |
ssh.mac_algorithms |
Flags MAC algorithms advertised by the server that are weak (SHA-1, non-ETM, ...). | CRITICAL |
ssh.strict_kex |
Verifies the server advertises the strict-KEX marker (CVE-2023-48795 Terrapin mitigation). | WARNING |
ssh.preauth_compression |
Flags servers that offer pre-authentication zlib compression (prefer zlib@openssh.com). | INFO |
ssh.auth_methods |
Reviews the advertised authentication methods (password exposure, public-key availability). | WARNING |
ssh.sshfp_alignment |
Compares published SSHFP records against the observed host keys (match, missing, mismatch). | CRITICAL |
ssh.sshfp_hash |
Flags SSHFP record sets that only publish SHA-1 (type 1) fingerprints instead of SHA-256. | WARNING |
Observation key
Writes a single observation under ssh:
{
"domain": "...",
"endpoints": [
{
"host": "...", "port": 22, "address": "...",
"banner": "SSH-2.0-OpenSSH_9.3p1",
"kex_algorithms": ["curve25519-sha256", "..."],
"host_keys": [{"type": "ssh-ed25519", "sha256": "abc..."}],
"auth_methods": ["publickey"],
"issues": [ { "code": "...", "severity": "warn", ... } ]
}
],
"sshfp": { "present": true, "records": [...] },
"collected_at": "..."
}
License & licensing roadmap
This project is currently licensed under the GNU Affero General Public
License v3.0 (see LICENSE), because it still imports
happydns.ServiceMessage and abstract.Server from the happyDomain
server module (git.happydns.org/happyDomain/model and
git.happydns.org/happyDomain/services/abstract), which are themselves
distributed under AGPL-3.0 and a commercial license.
The core checker types (CheckerOptions, CheckerDefinition,
ObservationProvider, CheckRule, …) have already been migrated to
checker-sdk-go; only the
service-message types remain on the AGPL side.
Planned relicensing: as soon as the remaining ServiceMessage /
abstract.Server dependency has been removed (moved into a dedicated
permissively licensed module), this project will be relicensed under the
MIT License, in line with the rest of the happyDomain checker
ecosystem (see checker-dummy for the target shape).
Contributors notice: by submitting a contribution to this repository, you accept that your contribution will be relicensed from AGPL-3.0 to MIT at the time of the relicensing described above. If you do not agree with this, please do not submit contributions until the relicensing has taken place.
The third-party Apache-2.0 attributions for checker-sdk-go are recorded
in NOTICE and must accompany any binary or source redistribution of this
project.