Initial commit

This commit is contained in:
nemunaire 2026-04-21 21:50:49 +07:00
commit b27336a908
15 changed files with 1122 additions and 0 deletions

77
README.md Normal file
View file

@ -0,0 +1,77 @@
# checker-tls
TLS posture checker for happyDomain.
Consumes `DiscoveredEndpoint` entries published by service checkers (xmpp,
srv, caldav, carddav, …) via `AutoFill: discovered_endpoints`, performs a
real TCP dial, optional STARTTLS upgrade, and TLS handshake on each, and
exports per-endpoint posture under the observation key `tls_probes`.
## Supported endpoint types
| `DiscoveredEndpoint.Type` | Handshake |
| --------------------------- | ---------------------------------------------- |
| `tls` | Direct TLS on connect |
| `starttls-smtp` | ESMTP EHLO + STARTTLS (RFC 3207) |
| `starttls-submission` | Same as `starttls-smtp` |
| `starttls-imap` | IMAP CAPABILITY + STARTTLS (RFC 3501) |
| `starttls-pop3` | POP3 CAPA + STLS (RFC 2595) |
| `starttls-xmpp-client` | XMPP c2s stream + `<starttls/>` (RFC 6120) |
| `starttls-xmpp-server` | XMPP s2s stream + `<starttls/>` (RFC 6120) |
| other `starttls-*` | Rejected with a `handshake_failed` issue |
## Payload shape
Observation data under `tls_probes`:
```json
{
"probes": {
"<endpointId>": {
"host": "example.net",
"port": 5222,
"endpoint": "example.net:5222",
"type": "starttls-xmpp-client",
"sni": "example.net",
"tls_version": "TLS1.3",
"cipher_suite": "TLS_AES_128_GCM_SHA256",
"hostname_match": true,
"chain_valid": true,
"not_after": "2026-07-01T00:00:00Z",
"issuer": "Let's Encrypt",
"issues": []
}
},
"collected_at": "2026-04-21T12:34:56Z"
}
```
Consumers pick their own endpoint from the map via
`RelatedObservation.EndpointID`.
## Issues reported
- `tcp_unreachable` — dial failed.
- `handshake_failed` — TLS handshake or STARTTLS upgrade failed.
- `starttls_not_offered` — server didn't advertise STARTTLS (severity depends
on `Meta["starttls"]` = `"required"` vs `"opportunistic"`).
- `chain_invalid` — leaf does not chain to a system-trusted root.
- `hostname_mismatch` — cert SANs don't cover the SNI.
- `expired` / `expiring_soon` — cert expiry posture.
- `weak_tls_version` — negotiated TLS < 1.2.
## Options
| Id | Type | Default | Description |
| ----------------- | ------ | ------- | ------------------------------------------------ |
| `probeTimeoutMs` | number | 10000 | Per-endpoint dial + handshake timeout in ms. |
## Running
```bash
# Plugin (loaded by happyDomain at startup)
make plugin
# Standalone HTTP server
make && ./checker-tls -listen :8080
```