checker-caa/README.md
Pierre-Olivier Mercier c6400c7773
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
feat: publish tls.endpoint.v1 discovery entry to enable GetRelated
2026-05-15 18:44:35 +08:00

90 lines
3.7 KiB
Markdown

# checker-caa
CAA posture checker for happyDomain.
Validates that certificates observed by `checker-tls` were issued by a
CA actually authorized by the domain's `CAA` records. This checker
runs no network probes of its own: it reads the `svcs.CAAPolicy`
service body (already parsed by happyDomain from the zone's `CAA`
resource records) and the `tls_probes` observations published by
`checker-tls`, and cross-references them via the CCADB "CAA
Identifiers" mapping.
## How it works
1. The host runs this checker on a `svcs.CAAPolicy` service.
2. `Collect` unmarshals the service body into a list of
`(flag, tag, value)` entries. No network.
3. The `caa_compliance` rule:
- calls `obs.Get("caa_policy", …)` to load its own payload;
- calls `obs.GetRelated("tls_probes")` to pick up every TLS probe
produced on the target;
- resolves each observed issuer (keyed by `IssuerAKI` with an
`IssuerDN` fallback) against the embedded CCADB CSV to find the
CA's published CAA identifier domain(s);
- compares the observed identifiers against the `issue` /
`issuewild` allow list (or flags a `DisallowIssue` violation).
## Rules
| Code | Description | Severity |
|--------------------|----------------------------------------------------------------------------------------------------------------------|----------|
| `caa_compliance` | Cross-references TLS certificates observed on the domain against its CAA `issue`/`issuewild` policy, mapping each observed issuer to its CCADB-published CAA identifier. | CRITICAL |
## Rule outcomes
- `caa_ok`: every observed issuer is authorized by the zone's CAA
policy.
- `caa_no_tls`: no TLS probes related to this target have been
published yet. Reported as `UNKNOWN` (the same "eventual
consistency" steady state used by `checker-tls` when it has no
endpoints yet).
- `caa_not_authorized`: CCADB mapped an observed issuer to a domain
the CAA policy does not list. Reported `CRIT`.
- `caa_issuance_disallowed`: the policy contains `CAA 0 issue ";"`
(explicitly disallowing issuance) but a TLS cert was still observed.
Reported `CRIT`.
- `caa_issuer_unknown`: CCADB has no mapping for the observed issuer
(AKI + DN). Reported `INFO`; action is to file a CCADB update.
## Issuer -> CAA domain mapping (CCADB)
The file `checker/AllCAAIdentifiersReport.csv` is an unmodified
snapshot of the "CAA Identifiers (V2)" report from the Common CA
Database (https://www.ccadb.org/resources). It is embedded into the
binary via `//go:embed` but is **not committed to the repository**.
To fetch or refresh it, run:
```bash
go generate ./checker/
```
This downloads the current CSV from CCADB. No code changes are needed
to pick up a new snapshot: only a re-embed (recompile) is required
after the file is refreshed. Note that the download depends on CCADB
being reachable; `go build` itself has no network dependency.
The lookup key is:
1. `IssuerAKI` (uppercase hex of `leaf.AuthorityKeyId`), matched
against CCADB's `"Subject Key Identifier (Hex)"` column.
2. `IssuerDN` (Go's `leaf.Issuer.String()`), matched against CCADB's
`"Subject"` column after normalization (RDNs sorted by type,
whitespace trimmed, comma/semicolon separators collapsed).
## Options
| Id | Type | Default | Description |
|-----------|--------|---------|------------------------------------|
| `domain` | string | (auto) | Domain being checked (`AutoFill`). |
| `service` | (n/a) | (auto) | `svcs.CAAPolicy` service body. |
## Running
```bash
# Plugin (loaded by happyDomain at startup)
make plugin
# Standalone HTTP server
make && ./checker-caa -listen :8080
```