feat(relay): localhost-only /metrics listener with bind-address validation (#60)#87
Merged
Conversation
…ation (#60) Adds a second http.Server bound to a loopback IP for /metrics scraping, separate from the internet-exposed public listener. Operators reach /metrics via SSH tunnel or sidecar; the listener defaults to 127.0.0.1:9090 and accepts an empty value as the opt-out. CheckLoopbackBind is the structural defence: bind address must be an IP literal (hostnames refused to close the DNS-time TOCTOU window) and that IP must satisfy net.IP.IsLoopback. Non-loopback addresses fail loudly at boot rather than silently publishing operational state. The metrics port joins both the expected and actual sets of #81's listener-port allowlist so the boot check stays consistent across the opt-out, insecure-mode, and autocert-mode paths.
Contributor
Author
Code Review: #60Decision: PASS Findings
SummaryImplementation faithfully matches the architect's spec. The load-bearing security choice (refuse hostnames to avoid DNS-time TOCTOU) is documented in the
|
) Adds the metrics-listener feature doc and the per-ticket codebase note; updates the metrics-registry and connection-count-gauges feature pages where they pointed at #60 as a pending follow-up. INDEX.md gains the new feature entry and drops the "Listener still pending (#60)" trailer on the metrics-registry line now that the wiring has landed. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Stands up a second
http.Serverfor/metrics, bound to a loopback IPliteral only, and refuses to boot on any non-loopback
--metrics-listenvalue. Pulled together with the registry and handler from #59 and the
collector from #61.
internal/relay/metrics_listen.go:CheckLoopbackBind(addr)— pure validator. Refuses hostnames(DNS-time TOCTOU), non-loopback IPs, port 0, and malformed inputs.
Loopback IPv4 (
127.0.0.0/8) and IPv6 (::1) are accepted.NewMetricsServer(addr, h)— opt-out aware.addr == ""returns(nil, nil); otherwise validates and returns an*http.Serverwithtimeouts matching the public listener (gosec G114 compliant).
cmd/pyrycode-relay/main.go:--metrics-listenflag, default127.0.0.1:9090.metricsReg, wiresNewConnectionsMetrics, builds adedicated mux, and launches the listener in a goroutine.
expectedandactualsets forCheckListenerPorts(relay: refuse to boot if listener ports exceed expected set #81) in both insecure and autocert modes; thelistener is skipped entirely when
metricsSrv == nil.Issue
Closes #60.
Testing
internal/relay/metrics_listen_test.go(new):TestCheckLoopbackBind_Matrix— every branch of the validator,including the hostname-refusal case (
localhost:9090).TestNewMetricsServer_Matrix— opt-out, rejection, and timeoutpinning.
TestMetricsServer_EndToEnd_HappyPath— realnet.Listen+ HTTPround-trip on an ephemeral loopback port, asserts 200 + Prometheus
text Content-Type + non-empty body.
make vet,make test(with-race), andmake buildall clean.Architecture compliance
Follows
docs/specs/architecture/60-metrics-listener-loopback-bind.md:to
metrics.go(the relay: adopt prometheus/client_golang and introduce metrics registry scaffolding #59 seam is fixed).main.go: flag declaration, constructionblock, and per-mode goroutine + port-set integration.
CheckLoopbackBinddocstring withthe DNS-rebinding /
/etc/hosts-race rationale so future contributorsdo not "fix" it by adding
net.LookupHost.ListenAndServereturn as process-fatal(
os.Exit(1)), matching the http-01 listener's shape — loud failureover silent correction.
🤖 Generated with Claude Code