Skip to content

feat: accept dns checks and definitions, gate them on the agent, and feed Expiring - #235

Merged
lr00rl merged 5 commits into
integrationfrom
feature/20261025_s5a-dns-check
Oct 25, 2026
Merged

lr00rl merged 5 commits into
integrationfrom
feature/20261025_s5a-dns-check

Conversation

@lr00rl

@lr00rl lr00rl commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Design 32 S5a, the DNS check method, server part.

  • Validation (internal/server/monitor_dns.go): a dns create, edit or definition address uses the SDK's ParseDNSResolver and the destination rule in monitor_checks.go (checkMonitorHost), so a private, loopback, link-local, metadata or numeric resolver host is refused, as is an unknown scheme or a DoH target without a path. The target is stored canonical (DNSResolver.String), so two spellings expand to one id. The question: name at most 253 bytes, type A/AAAA/CNAME/TXT/MX/NS (default A), at most 16 expected values stored in the form an agent compares, dns_expect_rcode NOERROR (stored empty) or NXDOMAIN, which takes no values. Every refusal carries R6's field (dns_expect[1], dns_name, ...). The dns fields are refused on every other kind; empty ones echoed by an editor are not.
  • Edits: MonitorUpdate changes the question in place; the resolver is the target and cannot change (another spelling of the same endpoint is the same target).
  • Definitions: method dns takes resolver endpoints as addresses (node targets wait for the dnsproxy engine), copies the question onto each expanded monitor, and an edit of the question alone rewrites every monitor. managedMonitorEqual already compares whole records; it now reads an empty dns_expect as missing.
  • Probe gate: generic; with the SDK knowing dns, a source whose agent does not report it reads agent_too_old and is not handed the check.
  • Ingestion: results keep dns_rcode (mnemonic shape) and at most 16 dns_answers of at most 255 bytes, cut rather than refused; other kinds and unknown runs keep neither. A DoH or DoT result keeps cert_not_after; any other agent claim of one is still dropped. The bolt row maps the two new fields, and the latest view exposes them.
  • Expiring: a DoH or DoT check is listed as a tls_certificate item, dated by the soonest of each running source's newest certificate reading.
  • Paths: a resolver whose host is a node's public address draws its path to that node.

SDK pin: v0.2.24-0.20261025003326-6485e4abe7b9 and sdk.ref 6485e4ab, the pushed branch commit of LatticeNet/lattice-sdk#45. Re-pin both after that merges.

Decisions a reviewer may question:

  • Expiring takes the soonest certificate among sources, not the newest reading: with anycast or several PoPs, one vantage point that still sees an unrenewed certificate is the one worth showing. A source no longer assigned stops counting.
  • Out-of-bound answers are truncated, not refused, because they are display data and the run's verdict was already made by the agent.
  • The create refusal for an unknown type now lists dns ("only tcp, http, icmp, beacon, dns and tls monitors are supported").

Left out: node targets for dns definitions (spec: wait for the dnsproxy engine); audit metadata does not list the question.

Test plan

  • go test ./internal/store/ ./internal/server/ (full packages, through the desk lock)
  • gosec on both packages, gofmt -l .
  • internal/server/monitor_dns_test.go: every create refusal of the spec's server list (private, loopback, metadata resolver, unknown scheme, name over 253 bytes, 17 expected values, rcode other than NOERROR or NXDOMAIN) plus shape and cross-kind refusals, each with its field; canonical storage; edits; the agent gate and agent_too_old; a dns definition with two addresses (three spellings) over two sources expanding to two monitors and four paths, a question-only edit reaching every monitor, the dry run; definition refusals; ingestion bounds and certificate handling; Expiring from two sources
  • CI race shards

…feed Expiring

A dns check (design 32 S5a) asks a resolver endpoint a question from
each source. A create or a definition validates the endpoint with the
SDK's rules (https DoH with its path, tls DoT, udp and tcp) and the
destination rule every check follows (checkMonitorHost: no private,
loopback, link-local, metadata or numeric host), stores it in canonical
form so two spellings are one target, and checks the question: a name of
at most 253 bytes, a type of A, AAAA, CNAME, TXT, MX or NS, at most 16
expected values in the form an agent compares them in, and NOERROR or
NXDOMAIN, the latter with no values. Every refusal names its field. The
dns fields are refused on every other kind, and an edit changes the
question in place.

A definition with method dns takes resolver endpoints as addresses and
copies the question onto every expanded monitor; managedMonitorEqual
already compares whole records and now reads an empty expected list as a
missing one. A source whose agent does not report dns reads
agent_too_old and is not handed the check, through the existing gate.

Results keep dns_rcode (a mnemonic's shape) and at most 16 answers of at
most 255 bytes, cut rather than refused; another kind keeps neither, and
an unknown run keeps no answer. A dns result over DoH or DoT keeps its
certificate expiry, and Expiring lists such a check by the soonest of
its running sources' newest readings, so a vantage point that still sees
an unrenewed certificate shows it. A dns resolver whose host is a node's
address draws its path to that node.

The SDK pin and sdk.ref move to the S5a branch commit (lattice-sdk #45);
they are re-pinned after that merges.

Tested: go test ./internal/store/ ./internal/server/ (full packages),
gosec on both.
A review found two ways a DoH or DoT check could show the wrong expiry.
A source that stopped reaching the resolver kept its last certificate
reading forever, and as the soonest of the sources' readings it held the
resolver in Expiring after the certificate was renewed. A reading more
than a week older than the newest one no longer counts. And an agent's
cert_not_after was kept as sent: it is now dropped on a run that
measured nothing and when it is not a date a certificate carries (before
2000, or 50 years or more ahead).

Tested: the new cases fail before the change; focused server and store
runs through the desk lock.
The SDK branch merged integration (lattice-sdk #46, the S5bc DNS
deployment fields), so go.mod and sdk.ref move to its head 6f7f1be
together, as the root refs test requires. Re-pinned again after
lattice-sdk #45 merges.
…25_s5a-dns-check

# Conflicts:
#	go.mod
#	go.sum
#	sdk.ref
@lr00rl
lr00rl merged commit 99611b1 into integration Oct 25, 2026
9 checks passed
@lr00rl
lr00rl deleted the feature/20261025_s5a-dns-check branch October 25, 2026 01:58
@lr00rl lr00rl mentioned this pull request Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant