177 lines
8.1 KiB
Markdown
177 lines
8.1 KiB
Markdown
# checker-ssh
|
|
|
|
Deep SSH security checker for [happyDomain](https://www.happydomain.org/).
|
|
|
|
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](https://github.com/jtesta/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.SSHFP` records declared in
|
|
the zone,
|
|
- flags `sshfp_missing`, `sshfp_not_covered`, `sshfp_only_sha1` or
|
|
`sshfp_mismatch` as 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":
|
|
|
|
1. **Overall status** banner + SSHFP verdict chips.
|
|
2. **What to fix**: top issues (crit -> warn), each with a
|
|
copy-pasteable remediation snippet (sshd_config lines, SSHFP DNS
|
|
records, ssh-keygen invocations).
|
|
3. **SSHFP table** with per-record match status.
|
|
4. **Per-endpoint details**: expandable sections with host-key
|
|
fingerprints, algorithm tables (broken entries highlighted), and
|
|
advertised auth methods.
|
|
|
|
## Usage
|
|
|
|
### Standalone HTTP server
|
|
```bash
|
|
make
|
|
./checker-ssh -listen :8080
|
|
```
|
|
Endpoints:
|
|
- `GET /health`
|
|
- `GET /definition`
|
|
- `POST /collect`
|
|
- `POST /evaluate`
|
|
- `POST /report`
|
|
|
|
### Docker
|
|
```bash
|
|
make docker
|
|
docker run -p 8080:8080 happydomain/checker-ssh
|
|
```
|
|
|
|
### happyDomain plugin
|
|
```bash
|
|
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`:
|
|
```json
|
|
{
|
|
"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`](https://git.happydns.org/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.
|