Fix DNS resolution on Ubuntu 22 with systemd-resolved (#77)

On Ubuntu 22 (systemd 249), systemd-resolved applies interface-scoped
routing to per-link DNS servers. Configuring `resolvectl dns fips0
127.0.0.1:5354` caused resolved to attempt reaching 127.0.0.1 through
fips0 (a TUN with only fd00::/8 routes), silently failing. The DNS
responder never received queries. Newer systemd versions (250+) have
explicit handling for loopback servers on non-loopback interfaces.

Changes:

- DNS responder default bind_addr changed from "127.0.0.1" to "::"
  so it listens on all interfaces, including fips0. Bind logic in
  lifecycle.rs now parses bind_addr as IpAddr and constructs a
  SocketAddr, handling IPv6 literal formatting. Factored into
  Node::bind_dns_socket with explicit IPV6_V6ONLY=0 via socket2, so
  IPv4 clients on 127.0.0.1:5354 still reach the responder
  regardless of the kernel's net.ipv6.bindv6only sysctl.

- fips-dns-setup resolvectl backend now waits for fips0 to have a
  global IPv6 address, then configures resolved with
  [<fips0-addr>]:5354. That address is locally delivered by the
  kernel regardless of which interface resolved tries to route
  through. The dnsmasq and NetworkManager backends still use
  127.0.0.1 (they don't have the interface-scoping issue).

- Dropped hardcoded `bind_addr: "127.0.0.1"` from the packaged
  fips.yaml (Debian + OpenWrt). The shipped config was overriding
  the new default.

- DNS queries are only accepted from the localhost.

Verified end-to-end in a privileged Ubuntu 22.04 systemd container:
dig @127.0.0.53 AAAA <npub>.fips resolves cleanly through
systemd-resolved.

The dns-delegate backend (systemd 258+) still uses 127.0.0.1; it
has not been verified whether that backend has the same routing
issue.
This commit is contained in:
Johnathan Corgan
2026-04-21 18:17:56 -07:00
committed by GitHub
parent 03f6db58e8
commit ed312ac6f2
7 changed files with 538 additions and 60 deletions
+14 -23
View File
@@ -138,29 +138,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
updated. In-tree `fipstop` is adjusted to the new schema. The
control socket interface is still pre-1.0 and not covered by
stability guarantees
### Security
- Bloom filter poisoning defense. Reject inbound FilterAnnounce
messages whose false-positive rate exceeds a configurable cap
(`node.bloom.max_inbound_fpr`, default 0.05). Previously a peer
running a modified build could send an all-ones filter, causing
(1) lookup attraction / black-hole routing for unknown targets,
(2) aggregation contamination as the poisoned bits propagated one
hop per announce tick via strict-OR merging, and (3) mesh-size
estimate blowup to `f64::INFINITY`. Rejection is silent on the
wire; rejected announces log at WARN and increment a new
`bloom.fill_exceeded` counter. The peer's prior accepted filter
and sequence number are preserved on rejection so a single bad
announce cannot wipe a peer's contribution to aggregation.
An independent self-plausibility WARN fires (rate-limited to once
per 60s) if our own outgoing filter ever exceeds the cap,
surfacing aggregation drift or ingress-check bypasses.
`BloomFilter::estimated_count` now returns `Option<f64>` and
returns `None` for saturated filters, preventing `f64::INFINITY`
from propagating into mesh-size estimates. The node-level
`estimated_mesh_size` field (already `Option<u64>`) propagates
`None` when any contributing filter is above cap.
- Validate bloom filter fill ratio on FilterAnnounce ingress.
Inbound FilterAnnounce messages whose derived false-positive
rate exceeds `node.bloom.max_inbound_fpr` (new config field,
default 0.05) are rejected silently on the wire, logged at WARN,
and counted in a new `bloom.fill_exceeded` counter. A
rate-limited WARN also fires if our own outgoing filter's FPR
exceeds the cap. `BloomFilter::estimated_count` now takes
`max_fpr` and returns `Option<f64>`, returning `None` for
saturated filters; this propagates through `compute_mesh_size`
into `estimated_mesh_size` (already `Option<u64>`)
### Fixed
@@ -209,6 +196,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
tree specification. The receive path now verifies that the
ancestry is structurally consistent with the signed parent
declaration before mutating tree state.
- Fix DNS resolution on Ubuntu 22 with systemd-resolved. The DNS
responder now binds `::` (dual-stack) instead of `127.0.0.1` so
systemd-resolved's interface-scoped routing via fips0 reaches
it. DNS queries are accepted only from the localhost.
## [0.2.0] - 2026-03-22