122 lines
7.5 KiB
Markdown
122 lines
7.5 KiB
Markdown
# checker-dnssec
|
|
|
|
DNSSEC operational hygiene checker for [happyDomain](https://www.happydomain.org/).
|
|
|
|
Cryptographic chain validation is delegated to `checker-dnsviz`. This
|
|
checker focuses on **policy and operational hygiene**:
|
|
|
|
- NSEC vs NSEC3 zone walking exposure
|
|
- RFC 9276 NSEC3 parameter compliance (iterations, salt)
|
|
- Algorithm policy and key sizes (allowed / forbidden / modern)
|
|
- RRSIG presence, validity windows and freshness
|
|
- TTL recommendations for DNSKEY / RRSIG
|
|
- Per-name-server consistency of the DNSKEY RRset and denial scheme
|
|
|
|
The HTML report is laid out so the most common operator-facing failure
|
|
scenarios appear first, with a fix line citing the relevant RFC.
|
|
|
|
## Usage
|
|
|
|
### Standalone HTTP server
|
|
|
|
```bash
|
|
# Build and run
|
|
make
|
|
./checker-dnssec -listen :8080
|
|
```
|
|
|
|
The server exposes:
|
|
|
|
- `GET /health`: health check
|
|
- `POST /collect`: collect DNSSEC observations (happyDomain external checker protocol)
|
|
|
|
### Docker
|
|
|
|
```bash
|
|
make docker
|
|
docker run -p 8080:8080 happydomain/checker-dnssec
|
|
```
|
|
|
|
### happyDomain plugin
|
|
|
|
```bash
|
|
make plugin
|
|
# produces checker-dnssec.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 DNSSEC checker to the URL of the
|
|
running checker-dnssec server (e.g., `http://checker-dnssec:8080`).
|
|
happyDomain will delegate observation collection to this endpoint.
|
|
|
|
## Build
|
|
|
|
```sh
|
|
make # standalone binary
|
|
make docker # FROM scratch image
|
|
make plugin # Go plugin (.so)
|
|
make test # tests
|
|
```
|
|
|
|
## Options
|
|
|
|
### Admin options
|
|
|
|
| Id | Type | Default | Description |
|
|
|------------|--------|----------------------|--------------------------------------------------------------------------------------------------------------|
|
|
| `resolver` | string | `/etc/resolv.conf` | Bootstrap recursive resolver (`host:port`) used to discover the apex name servers and look up the parent DS. |
|
|
|
|
### User options
|
|
|
|
| Id | Type | Default | Description |
|
|
|---------------------------|--------|---------|------------------------------------------------------------------------------------------------------------------------------|
|
|
| `nsec3IterationsMax` | uint | `0` | RFC 9276 §3.1 ceiling on `NSEC3PARAM.Iterations`. Increase only if your signer cannot publish 0 yet. |
|
|
| `nsec3IterationsSeverity` | choice | `warn` | Severity when iterations exceed the ceiling. Use `crit` to enforce RFC 9276 strictly. |
|
|
| `signatureFreshness` | uint | `7` | Warn when the closest RRSIG expires in fewer than this many days. |
|
|
| `signatureFreshnessCrit` | uint | `1` | Critical when the closest RRSIG expires in fewer than this many days. |
|
|
| `minRSAKeySize` | uint | `2048` | Minimum acceptable RSA modulus size, in bits. |
|
|
| `requireSEP` | bool | `true` | Require at least one DNSKEY with the SEP bit (KSK). |
|
|
| `dnskeyTTLMin` | uint | `3600` | Minimum DNSKEY TTL, in seconds; shorter TTLs hurt cacheability. |
|
|
|
|
## Rules
|
|
|
|
| Code | Description | Severity |
|
|
|------------------------------------------------------|---------------------------------------------------------------------------------------------------|---------------------|
|
|
| `dnssec_zone_signed` | Detects a zone advertised as signed at the parent (DS) but no DNSKEY served at the apex. | CRITICAL |
|
|
| `dnssec_dnskey_consistent` | Verifies that every authoritative server returns the same DNSKEY RRset. | CRITICAL |
|
|
| `dnssec_dnskey_query_ok` | Verifies that every authoritative server answered the DNSKEY query. | CRITICAL |
|
|
| `dnssec_algorithm_allowed` | Rejects DNSKEYs that use a forbidden algorithm or are not in the allowed list. | CRITICAL |
|
|
| `dnssec_algorithm_modern` | Recommends ECDSAP256SHA256 (13) or Ed25519 (15) over RSA. | WARNING |
|
|
| `dnssec_rsa_keysize` | Verifies RSA DNSKEYs reach a minimum modulus size (default 2048 bits). | CRITICAL |
|
|
| `dnssec_ksk_present` | Verifies at least one DNSKEY has the SEP bit (KSK). | CRITICAL |
|
|
| `dnssec_dnskey_count` | Warns when too many DNSKEYs are published, inflating responses and amplification potential. | WARNING |
|
|
| `dnssec_rrsig_present_dnskey` | Ensures the DNSKEY RRset is signed. | CRITICAL |
|
|
| `dnssec_rrsig_present_soa` | Ensures the SOA RRset is signed. | CRITICAL |
|
|
| `dnssec_rrsig_validity_window` | Verifies that every observed RRSIG is currently within [Inception, Expiration]. | CRITICAL |
|
|
| `dnssec_rrsig_freshness` | Warns when RRSIGs are close to expiring; preemptive alert for stuck signers. | CRITICAL |
|
|
| `dnssec_denial_uses_nsec3` | Warns when the zone uses NSEC for negative answers, which makes the zone walkable (RFC 5155 / RFC 7129). | WARNING |
|
|
| `dnssec_nsec3_iterations` | Verifies that NSEC3PARAM.Iterations is at most nsec3IterationsMax (default 0, per RFC 9276 §3.1). | CRITICAL |
|
|
| `dnssec_nsec3_salt_empty` | Verifies that NSEC3PARAM.SaltLength is 0 (RFC 9276 §3.1: a salt buys no measurable protection). | WARNING |
|
|
| `dnssec_nsec3_optout_only_when_signed_delegations` | Reports informational note when the OPT-OUT flag is set on NSEC3PARAM in a leaf zone. | INFO |
|
|
| `dnssec_denial_consistent` | Verifies that every authoritative server uses the same denial-of-existence scheme. | WARNING |
|
|
| `dnssec_dnskey_ttl_min` | Warns when the DNSKEY TTL is too short to be useful for caching. | WARNING |
|
|
|
|
## License
|
|
|
|
Licensed under the **MIT License** (see `LICENSE`).
|