Skip to content

chore: bump to 0.3.10-alpha.10 for the dns check method - #46

Merged
lr00rl merged 12 commits into
integrationfrom
feature/20261025_release-0.3.10-alpha.10
Oct 25, 2026
Merged

lr00rl merged 12 commits into
integrationfrom
feature/20261025_release-0.3.10-alpha.10

Conversation

@lr00rl

@lr00rl lr00rl commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Version bump and CHANGELOG for v0.3.10-alpha.10: the dns check method of design 32 S5a (#45). cmd/lattice-agent/main.go version and main_test.go, as #44 did.

Stacked on #45: this branch starts from feature/20261025_s5a-dns-check, so until #45 merges the diff here shows both. Merge #45 first.

Before tagging: #45 pins the SDK to the S5a branch commit (v0.2.24-0.20261025003326-6485e4abe7b9, lattice-sdk #45). Re-pin to the merged lattice-sdk commit first; the CHANGELOG names lattice-sdk #45, not the pseudo-version, so it stays right. This PR does not tag and creates no release.

No compatibility floor moves. A server before S5a drops dns from probe methods and never hands the agent a dns check; a node on alpha.9 or older reads agent_too_old for a dns check until it updates through its own approval.

Test plan

  • gofmt clean; go test ./cmd/lattice-agent/; scripts/check-release-workflow.sh.
  • CI (go test -race -cover ./..., gosec guard, release workflow check).
  • After the operator tags: release is prerelease and not Latest, -version prints 0.3.10-alpha.10, -compat-json floors unchanged, binaries built with go1.26.10.

lr00rl added 12 commits October 24, 2026 17:41
A dns check (design 32 S5a) asks one resolver one question and judges the
answer. The target's scheme picks the transport: https posts
application/dns-message (RFC 8484, ID 0), tls frames the message behind a
two-byte length inside TLS (RFC 7858), and udp and tcp are plain DNS. The
resolver's host is resolved once and every address judged by the rule
http and tcp checks follow, so a resolver that resolves to a private
address is refused with destination_refused before anything is sent.

One query per run, with recursion desired and an EDNS(0) record
advertising 1232 bytes; a truncated UDP answer is asked again once over
TCP. The check's timeout bounds the whole exchange, a DoH body over
64 KiB is refused unread, DoH and DoT verify the certificate against the
host and record cert_not_after, and latency runs from the dial to the
decoded answer. A response code above 15 is read through the EDNS
record.

The judgement follows the spec: NOERROR with an answer of the asked type,
every expected value in the answer set in the SDK's canonical form, or
NXDOMAIN when the check expects it. A failure's error starts with
dns_rcode, dns_mismatch, dns_no_answer or dns_transport, and the result
carries the response code and at most 16 answers of at most 255 bytes.
The agent advertises dns in probe_methods, and a change to the question
restarts the check.

The SDK pin moves to the S5a branch commit (lattice-sdk #45); it is
re-pinned after that merges.

Tested: stub resolvers per transport covering pass, mismatch, NXDOMAIN
expected and received, SERVFAIL, truncated UDP to TCP, a DoH 500, a
certificate for another name, a slow resolver, a body over 64 KiB and a
private resolver; go test -race on prober and cmd; gosec and govulncheck.
The release carries the dns check of design 32 S5a (lattice-node-agent
#45): DoH, DoT, UDP and TCP resolvers, the destination rule on the
resolved address, the certificate expiry of DoH and DoT, and the
judgement and failure codes the control plane shows. No compatibility
floor moves; a server before S5a never hands the agent a dns check.

The SDK pin is the S5a branch commit; it moves to the merged lattice-sdk
commit before this is tagged.
…t failure

A review found that only the UDP path acted on the truncation bit: a
truncated answer over TCP, DoT or DoH, or a second one after the TCP
retry, reached the judgement with an empty response code and failed as
dns_rcode with no code. It now fails as dns_transport, "the response is
truncated".

The same pass tightens three smaller things. A response that echoes a
question must echo the one asked, so an answer carrying the right ID for
another name is refused. A DoH answer's ID is not judged, since HTTP
binds it to its request and RFC 8484 sends 0. The TCP retry goes to the
address the truncated UDP answer came from. A reported answer drops
invalid UTF-8 before it is cut, at a character boundary, so it never
grows past 255 bytes.

Tested: the new cases fail before the change; go test -race ./internal/prober/; gosec.
dialJudged's share of the deadline per address becomes addrContext, so
the UDP exchange can try addresses on the same terms, and exchangeDoH
builds its client through dohClient, the seam the DNS tests use to pin
that a DoH request dials only its target. No behavior changes.
The DoH and resolver protections were covered only through the http
check's tests, which would not notice the DNS probe stepping around the
shared transport. Four tests now pin them on the DNS path: a DoH 3xx to
loopback, a private range or the metadata service is reported and not
followed; a proxy in the environment is ignored (the transport is
checked too, since net/http reads the environment once per process); a
DoH request for any host but the target is refused before a dial; and a
resolver name with one private address among public ones is refused
before any query, in either order and over every transport.

Tested: each test fails with its protection removed (CheckRedirect
dropped, Proxy set to ProxyFromEnvironment, the host check disabled,
only the first address judged) and passes with it in place.
A DoH answer with the wrong content type echoed the whole header, so a
resolver could put up to 64 KiB of its own text into the result's error,
which every node ships to the control plane on each run and the server
stores. The error now names only the parsed media type, cut to 64 bytes.

The other places a resolver's text reached the error are bounded the
same way: a TLS failure names the host asked for and at most three of
the certificate's names, 64 bytes each, instead of the x509 error's full
list; a transport error, which can quote the resolver's bytes (a garbled
HTTP status line, HTTP/2 GOAWAY debug data), is cut to 160 bytes; and
the whole error is cut to 512 bytes, the server's bound on a stored
error, so a node never sends text the server would cut. Reason only ever
carries constants, decode errors are fixed strings, and answers already
pass through dnsReported's bounds.

Tested: TestDNSResolverTextInErrorsIsBounded failed before the fix (a
61513-byte error) and passes after.
…r silent

exchangeUDP dialed only the first judged address, so a healthy resolver
whose first address is in a family the node cannot reach, or simply
does not answer, read as a timeout. It now asks the addresses in turn,
each for its share of the run's time on the same terms as dialJudged
(addrContext), as TCP, DoT and DoH already fall through. An answer,
right or wrong, ends the exchange at that address; a wrong answer is
not a reason to ask another. A truncated answer is asked again over TCP
of the address that sent it, which is no longer always the first.

Tested: TestDNSUDPTriesTheNextAddress failed before the fix on the
unreachable, silent and truncated cases and passes after; the wrong
answer case pins that the second address is never asked.
The UDP, TCP and DoT exchanges set the connection's deadline from the
context but did not watch its cancellation, so a run cancelled at
shutdown sat in its read until the check's timeout, up to the full
timeout per check. bindConn replaces setDeadline: it keeps the deadline
and closes the connection from context.AfterFunc when the context is
cancelled. A deadline that passes is still left to the connection's own
timeout, so a slow resolver reads as a timeout rather than a closed
connection, and a cancelled run now says it was cancelled instead of
timed out. DoH already followed its request's context.

Tested: TestDNSCancelEndsTheRun failed before the fix (a TCP run
cancelled after 200 ms took 20 s) and passes after over UDP, TCP, DoT
and DoH; go test -race on internal/prober and cmd/lattice-agent, gosec
(67 files, 0 issues) and govulncheck pass.
…error bounds

The alpha.10 entry said one query per run and nothing about error size or
cancellation; agent #45's review fixes changed all three.
Over TCP, DoT and DoH only an address that cannot be connected to falls
through; a connected one that stays silent ends the run at its timeout.
@lr00rl
lr00rl merged commit 08d7404 into integration Oct 25, 2026
1 check passed
@lr00rl
lr00rl deleted the feature/20261025_release-0.3.10-alpha.10 branch October 25, 2026 01:58
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