TODO #91
133
TODO.md
Normal file
133
TODO.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Status
|
||||
|
||||
pre-1.0. No git tags. Core resolver work in flight on feature/resolver
|
||||
(dirty: internal/resolver/resolver_test.go). Local checkout has diverged
|
||||
from origin: origin/main is 8 commits ahead (watcher orchestrator,
|
||||
unified TARGETS) and origin/feature/resolver already contains the full
|
||||
iterative resolver implementation with hermetic mocked tests.
|
||||
|
||||
# Next Step
|
||||
|
||||
Policy scaffold commit: add LICENSE, REPO_POLICIES.md, .editorconfig,
|
||||
.dockerignore, and .gitea/workflows/check.yml, and add the missing
|
||||
fmt-check, docker, and hooks targets to the Makefile. One commit, then
|
||||
confirm make check still passes.
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-02-20: iterative DNS resolver implemented; tests made hermetic
|
||||
with mocked DNS (origin/feature/resolver, unmerged)
|
||||
- 2026-02-20: CI actions and go install refs pinned to commit SHAs;
|
||||
Gitea Actions workflow for make check (origin/ci/make-check, unmerged)
|
||||
- 2026-02-20: watcher monitoring orchestrator merged to main (#8)
|
||||
- 2026-02-20: DOMAINS/HOSTNAMES unified into single TARGETS config (#11)
|
||||
- 2026-02-19: TCP port connectivity checker, made concurrent with port
|
||||
validation; gosec G704 SSRF findings fixed without suppression
|
||||
(feature branches, unmerged)
|
||||
- 2026-02-19: TLS certificate inspector with no-peer-certificates error
|
||||
path and IP SANs (feature branch, unmerged)
|
||||
- 2026-02-19: gosec SSRF and formatting fixes on main
|
||||
- 2026-02-19: initial scaffold with per-nameserver DNS monitoring model
|
||||
|
||||
# Future Steps
|
||||
|
||||
Compliance:
|
||||
|
||||
- Add README sections required by policy (Description, Getting Started,
|
||||
Rationale, Design, TODO, License, Author) if any are missing
|
||||
- Pin Dockerfile base images by sha256 and ensure the Docker build runs
|
||||
make check
|
||||
|
||||
Branch reconciliation:
|
||||
|
||||
- Sync local checkout with origin: local main is 8 commits behind
|
||||
origin/main; local feature/resolver has diverged from
|
||||
origin/feature/resolver, which already implements the resolver
|
||||
- Merge in-flight branches to main once green: feature/resolver,
|
||||
ci/make-check, feature/portcheck-implementation,
|
||||
feature/tlscheck-implementation
|
||||
|
||||
Resolver (plan from untracked TODO.md; largely implemented on
|
||||
origin/feature/resolver, verify each item before closing):
|
||||
|
||||
- Add github.com/miekg/dns dependency
|
||||
- roots.go: hardcoded IANA root server list (a through m, IPv4/IPv6),
|
||||
rootServers() returning ip:53 strings
|
||||
- query.go: low-level query(ctx, server, name, qtype): UDP with TCP
|
||||
fallback on truncation, RD=0, context respected, 5s per-query timeout,
|
||||
returns raw *dns.Msg
|
||||
- trace.go: iterative delegation chasing from roots: referral detection
|
||||
(NOERROR, empty answer, NS in authority), glue extraction with
|
||||
bailiwick check, out-of-bailiwick NS resolved with recursion guard,
|
||||
delegation depth limit (20), retry across nameservers on failure, do
|
||||
not chase CNAMEs inside trace
|
||||
- FindAuthoritativeNameservers: NS set via trace, sorted, FQDN
|
||||
normalized, trailing dot handled; must pass its 9 tests
|
||||
- QueryNameserver: resolve NS host to IPs, query A/AAAA/CNAME/MX/TXT/
|
||||
SRV/CAA/NS, build NameserverResponse with status mapping (OK,
|
||||
NXDomain, NoData, Error), documented record formatting, sorted values,
|
||||
lame delegation detection; must pass its 16 tests
|
||||
- QueryAllNameservers: find NS set for parent domain (public suffix
|
||||
list), query all NS in parallel with bounded concurrency, return map
|
||||
even when all fail, context cancellation; must pass its 4 tests
|
||||
- LookupNS: thin wrapper over FindAuthoritativeNameservers, sorted,
|
||||
identical results; must pass its 3 tests
|
||||
- ResolveIPAddresses: collect A/AAAA from all NS, follow CNAME chains
|
||||
with MaxCNAMEDepth, dedupe, sort, NXDOMAIN returns empty slice with
|
||||
nil error; must pass its 9 tests
|
||||
- All 39 resolver tests pass, make check green, merge to main
|
||||
|
||||
Watcher (internal/watcher/watcher.go):
|
||||
|
||||
- Scheduling loop in Run(ctx): initial check on startup, separate
|
||||
tickers for DNS/port and TLS intervals, persist state via state.Save()
|
||||
after each cycle, clean shutdown on context cancel
|
||||
- Domain check: LookupNS, compare to stored state, store silently on
|
||||
first run, notify with old/new NS lists on change
|
||||
- Hostname check: QueryAllNameservers, compare per-NS records; notify on
|
||||
record changes, NS failure, NS recovery, inconsistency detected,
|
||||
inconsistency resolved, empty response; store silently on first run
|
||||
- Port check: ResolveIPAddresses, check ports 80 and 443 per IP, notify
|
||||
on open/closed transitions, handle new and disappeared IPs
|
||||
- TLS check: for each open IP:443, CheckCertificate; notify on expiry
|
||||
warning, certificate change (CN/issuer/SANs), TLS failure/recovery
|
||||
|
||||
Port checker (internal/portcheck/portcheck.go):
|
||||
|
||||
- Tests against known-open ports and RFC documentation IPs
|
||||
- CheckPort: net.DialTimeout (5s), context respected; (true, nil) open,
|
||||
(false, nil) closed/timeout/refused, error only for unexpected
|
||||
failures
|
||||
|
||||
TLS checker (internal/tlscheck/tlscheck.go):
|
||||
|
||||
- Tests against known public HTTPS servers, verify fields populated
|
||||
- CheckCertificate: tls.Dial to specific IP:443 with hostname as SNI;
|
||||
extract subject CN, issuer CN and org, NotAfter, SANs; error on
|
||||
handshake failure
|
||||
|
||||
Notification service (internal/notify/notify.go, Slack/Mattermost/ntfy
|
||||
backends exist):
|
||||
|
||||
- Structured notification types: DNS change, port change, TLS expiry,
|
||||
TLS change, NS failure, NS recovery, NS inconsistency
|
||||
- Per-backend formatting: Slack/Mattermost attachment colors (red
|
||||
failures/expiry, yellow warnings, green recoveries, blue info); ntfy
|
||||
priorities (urgent failures, high warnings, default changes, low
|
||||
recoveries); include hostname, nameserver, old/new values, timestamps
|
||||
|
||||
HTTP API handlers:
|
||||
|
||||
- Wire *state.State and *watcher.Watcher into handler params
|
||||
- GET /api/v1/status: full state snapshot as JSON
|
||||
- GET /api/v1/domains: domain states with NS records and last-checked
|
||||
- GET /api/v1/hostnames: hostname states with per-NS record data
|
||||
|
||||
Infrastructure notes (from untracked TODO.md):
|
||||
|
||||
- Module path sneak.berlin/go/dnswatcher differs from the git.eeqj.de
|
||||
remote intentionally; do not "fix" it
|
||||
- Dependencies: github.com/miekg/dns, golang.org/x/net/publicsuffix
|
||||
- Resolver tests originally used live DNS against *.dns.sneak.cloud
|
||||
(required records documented in the test file header); origin now has
|
||||
mocked hermetic tests, keep them hermetic
|
||||
Reference in New Issue
Block a user