106 lines
5.9 KiB
Markdown
106 lines
5.9 KiB
Markdown
# checker-resolver-propagation
|
|
|
|
Worldwide DNS propagation checker for [happyDomain](https://www.happydomain.org/).
|
|
|
|
Probes a curated catalog of public recursive resolvers (Cloudflare,
|
|
Google, Quad9, OpenDNS, Yandex, regional ISPs, …) across multiple
|
|
transports (UDP, TCP, DoT, DoH) and regions, then compares their
|
|
answers to the zone's authoritative nameservers to detect propagation
|
|
gaps, regional splits, SOA serial drift, stale caches, DNSSEC
|
|
validation failures, SERVFAIL/NXDOMAIN inconsistencies, and resolver
|
|
filtering.
|
|
|
|
## Usage
|
|
|
|
### Standalone HTTP server
|
|
|
|
```bash
|
|
# Build and run
|
|
make
|
|
./checker-resolver-propagation -listen :8080
|
|
```
|
|
|
|
The server exposes:
|
|
|
|
- `GET /health`: health check
|
|
- `POST /collect`: collect propagation observations (happyDomain external checker protocol)
|
|
- `POST /evaluate`: run the evaluation rules against an observation
|
|
- `POST /report`: extract metrics / HTML report from an observation
|
|
|
|
### Docker
|
|
|
|
```bash
|
|
make docker
|
|
docker run -p 8080:8080 happydomain/checker-resolver-propagation
|
|
```
|
|
|
|
### happyDomain plugin
|
|
|
|
```bash
|
|
make plugin
|
|
# produces checker-resolver-propagation.so, loadable by happyDomain as a Go plugin
|
|
```
|
|
|
|
The plugin exposes a `NewCheckerPlugin` symbol returning the checker
|
|
definition and observation provider, which happyDomain registers in its
|
|
global registries at load time.
|
|
|
|
### Versioning
|
|
|
|
The binary, plugin, and Docker image embed a version string overridable
|
|
at build time:
|
|
|
|
```bash
|
|
make CHECKER_VERSION=1.2.3
|
|
make plugin CHECKER_VERSION=1.2.3
|
|
make docker CHECKER_VERSION=1.2.3
|
|
```
|
|
|
|
### happyDomain remote endpoint
|
|
|
|
Set the `endpoint` admin option for the resolver-propagation checker to
|
|
the URL of the running checker-resolver-propagation server (e.g.,
|
|
`http://checker-resolver-propagation:8080`). happyDomain will delegate
|
|
observation collection to this endpoint.
|
|
|
|
This checker applies to **service**-level checks and is restricted to
|
|
the `abstract.Origin` and `abstract.NSOnlyOrigin` services (the zone
|
|
apex / NS configuration).
|
|
|
|
## Options
|
|
|
|
| Id | Type | Default | Description |
|
|
|-----------------------|--------|-------------------------------|------------------------------------------------------------------------------------------------------------------------|
|
|
| `recordTypes` | string | `SOA,NS,A,AAAA,MX,TXT,CAA` | Comma-separated list of RR types to probe at the apex (and at each `subdomains` entry). |
|
|
| `subdomains` | string | `www` | Comma-separated list of owner names to probe in addition to the apex (e.g. `www,mail,@`). Empty = apex only. |
|
|
| `includeFiltered` | bool | `false` | Probe filtering resolvers (malware/family/adblock). Their answers routinely diverge by design. |
|
|
| `region` | string | `all` | Restrict to a region: `all`, `global`, `na`, `eu`, `asia`, `ru`, `me`. |
|
|
| `transports` | string | `udp` | Comma-separated transports to probe: `udp`, `tcp`, `dot`, `doh`. Encrypted transports are only used where published. |
|
|
| `resolverAllowlist` | string | | Comma-separated resolver IDs or IPs to probe exclusively (e.g. `cloudflare,google,9.9.9.9`). Empty = catalog selection.|
|
|
| `latencyThresholdMs` | uint | `500` | Resolvers averaging above this value emit an info finding. |
|
|
| `runTimeoutSeconds` | uint | `30` | Hard wall-clock budget for one propagation run. Slower resolvers report as unreachable. |
|
|
|
|
## Rules
|
|
|
|
Each rule emits a finding code. Severity can be affected by the options above.
|
|
|
|
| Code | Default severity | Condition |
|
|
|-------------------------------|------------------|-----------|
|
|
| `rprop_no_resolvers` | critical | The current option set selects no resolver from the catalog. |
|
|
| `rprop_all_resolvers_down` | critical | Every selected resolver failed to answer (likely no DNS connectivity from the checker host). |
|
|
| `rprop_resolver_unreachable` | warning | An individual resolver failed to answer within the run budget. |
|
|
| `rprop_resolver_high_latency` | info | A resolver's average response time exceeds `latencyThresholdMs`. |
|
|
| `rprop_resolver_filtered_hit` | info | A filtered resolver returned a different answer than the consensus (typical blocklist behaviour). Only when `includeFiltered` is enabled. |
|
|
| `rprop_partial_propagation` | warning | Public resolvers disagree on the answer for a probed RRset. |
|
|
| `rprop_answer_drift` | critical | The public consensus differs from the answer served by the zone's authoritative nameservers. |
|
|
| `rprop_unexpected_nxdomain` | critical | Some resolvers return NXDOMAIN while others return NOERROR for the same RRset. |
|
|
| `rprop_unexpected_servfail` | critical | A resolver returns SERVFAIL (usually a DNSSEC or reachability failure). |
|
|
| `rprop_regional_split` | warning | Every resolver of a region agrees on an answer that differs from the global consensus. |
|
|
| `rprop_serial_drift` | warning | Unfiltered resolvers disagree on the SOA serial. |
|
|
| `rprop_stale_cache` | info | A resolver still serves an SOA serial below the one last observed by happyDomain. |
|
|
| `rprop_dnssec_failure` | critical | A validating resolver fails to validate the zone's DNSSEC chain (returns SERVFAIL with AD/CD semantics). |
|
|
| `rprop_dnssec_not_validated` | info | A validating resolver answered without setting AD on a signed zone. |
|
|
|
|
## License
|
|
|
|
Licensed under the **MIT License** (see `LICENSE`).
|