Skip to content

feat: NodeHostField — dial NodesKey peers by name, not raw IP - #71

Merged
benitogf merged 1 commit into
masterfrom
feat/node-host-field
Jun 16, 2026
Merged

feat: NodeHostField — dial NodesKey peers by name, not raw IP#71
benitogf merged 1 commit into
masterfrom
feat/node-host-field

Conversation

@Booyaka101

@Booyaka101 Booyaka101 commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Closes #72

Problem

When SSL is on, the leader dials each NodesKey peer over HTTPS for the health-check (/_pivot/version) and trigger-sync (/_pivot/synchronize/pivot). parseNodeAddr builds that URL from the entry's ip+port, so the dial is https://<ip>:<port>/…. A peer whose TLS leaf is name-only (no IP SAN — the common case for an internally-enrolled, DNS-named device) can't satisfy standard hostname verification against an IP literal, so the handshake fails and the peer floods its log with tls: bad certificate. The peer is perfectly reachable and verifiable by name — only the raw-IP dial is wrong.

Fix

Add an opt-in Config.NodeHostField string:

  • Empty (default): unchanged — parseNodeAddr returns ip:port exactly as before.
  • Set (e.g. "host"): the named JSON field of each node entry is used as the dial host (combined with the entry's port). Resolution is per-entry: an entry missing or blank in that field falls back to its ip, so a mixed fleet stays reachable.

The consumer writes a DNS name into that field; the leader then dials https://<name>:<port>/…, which resolves to the peer and validates against its name-only leaf under ordinary hostname verification. No InsecureSkipVerify, no mTLS plumbing — just dial by the name the cert actually bears.

Threaded through nodesCache and makeGetNodes (the only parseNodeAddr call sites).

Tests

New TestParseNodeAddr covers: default ip/IP extraction, field-ignored-when-unconfigured, override-when-present, per-entry fallback when blank/absent, quoted-string port, and the empty/invalid cases. Full suite green; go vet and gofmt clean.

Compatibility

Fully backward-compatible: callers that don't set NodeHostField see identical behavior.

🤖 Generated with Claude Code

Add an opt-in Config.NodeHostField: when set, parseNodeAddr uses that
JSON field of a node entry as the dial host (combined with the entry's
port) instead of "ip", falling back to ip per-entry when the field is
absent or blank. Empty (default) preserves the historical ip:port
behavior byte-for-byte.

This lets a leader dial nodes by a DNS name so a name-only TLS leaf (no
IP SAN) validates under standard hostname verification — the leader→node
health check and trigger-sync no longer fail with "tls: bad certificate"
when SSL is on. Threaded through nodesCache and makeGetNodes; new
TestParseNodeAddr covers override, per-entry fallback, and defaults.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@benitogf
benitogf merged commit 4ebaef5 into master Jun 16, 2026
6 checks passed
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.

Leader dials NodesKey peers by raw IP, breaking HTTPS to name-only TLS leaves

3 participants