3.6 KiB
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
- The host runs this checker on a
svcs.CAAPolicyservice. Collectunmarshals the service body into a list of(flag, tag, value)entries. No network.- The
caa_compliancerule:- 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
IssuerAKIwith anIssuerDNfallback) against the embedded CCADB CSV to find the CA's published CAA identifier domain(s); - compares the observed identifiers against the
issue/issuewildallow list (or flags aDisallowIssueviolation).
- calls
Observation payload
This checker does not publish endpoints or add a new observation
schema. Under its own observation key caa_policy it returns a
pass-through view of the zone-side CAA records:
{
"domain": "example.net",
"records": [
{ "flag": 0, "tag": "issue", "value": "letsencrypt.org" },
{ "flag": 0, "tag": "issuewild", "value": ";" }
],
"run_at": "2026-04-22T12:34:56Z"
}
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 asUNKNOWN(the same "eventual consistency" steady state used bychecker-tlswhen it has no endpoints yet).caa_not_authorized: CCADB mapped an observed issuer to a domain the CAA policy does not list. ReportedCRIT.caa_issuance_disallowed: the policy containsCAA 0 issue ";"(explicitly disallowing issuance) but a TLS cert was still observed. ReportedCRIT.caa_issuer_unknown: CCADB has no mapping for the observed issuer (AKI + DN). ReportedINFO; 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:
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:
IssuerAKI(uppercase hex ofleaf.AuthorityKeyId), matched against CCADB's"Subject Key Identifier (Hex)"column.IssuerDN(Go'sleaf.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
# Plugin (loaded by happyDomain at startup)
make plugin
# Standalone HTTP server
make && ./checker-caa -listen :8080