# 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). ## 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: ```json { "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 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 ```