fix(edge-host): restore IPv6 addressing on the VLAN 40 sub-interface #316

Merged
k.renner merged 1 commits from claude-bot/edge1-vlan40-ipv6 into master 2026-07-19 20:24:48 +00:00
Member

Fixes a regression I introduced in #315.

What was wrong

10-vlan40.network.j2 set IPv6AcceptRA=false on enp1s0.40, leaving the interface with only a link-local address. edge-1 was therefore unreachable over IPv6 on the service VLAN — the VLAN it exists on in order to inherit the NTP VIP after the Talos migration.

The concern behind it was real: a second default route from the VLAN 40 lease would make return traffic asymmetric across the two VLANs. But that only required suppressing the default route and DNS, not addressing itself. Disabling RAs wholesale traded away the primary address family to protect the legacy one.

Fix

[Network]
IPv6AcceptRA=true

[IPv6AcceptRA]
UseGateway=false
UseDNS=false

Mirrors what UseRoutes=false / UseDNS=false already do for DHCPv4. Both options verified present in systemd 261 against man systemd.network (UseGateway= added in v250), not assumed.

Verified on edge-1 after applying

  • enp1s0.40 now holds fd40::8eec:4bff:fe43:cebc/64 and 2003:d6:9717:a702:8eec:4bff:fe43:cebc/64 alongside 10.2.40.153/24
  • exactly one IPv6 default route, still via enp1s0 (VLAN 30)
  • exactly one IPv4 default route, still via 10.2.30.1 on enp1s0
  • no DNS servers on the VLAN 40 link
  • chronyd and keepalived both still active; VIP unaffected

Auto-revert timer armed before the change and disarmed after, as with #315.

Separate, larger issue found while verifying — NOT fixed here

All four NTP servers bind only 0.0.0.0:123. There is no IPv6 listener anywhere in the cluster.

jupiter  UNCONN 0 0 0.0.0.0:123 0.0.0.0:*
saturn   UNCONN 0 0 0.0.0.0:123 0.0.0.0:*
neptune  UNCONN 0 0 0.0.0.0:123 0.0.0.0:*
edge-1   UNCONN 0 0 0.0.0.0:123 0.0.0.0:*

This predates edge-1 and is not caused by this PR or #315 — the three PVE nodes have served the LAN this way for months. So IPv6 clients have never received NTP from this infrastructure, and the VIP design (10.2.40.4, DHCP option 42, the VRRP instance) is IPv4-only end to end. After the Talos migration, IPv6 clients would have no NTP source at all.

Cause not determined. Ruled out so far: no -4 flag in either unit (DAEMON_OPTS="-F 1" on Debian, no options on Arch), net.ipv6.conf.all.disable_ipv6=0 on both. The chrony documentation states allow is access-control only and does not restrict address family, and does not document a default that would explain this. Needs proper investigation rather than a guess.

Worth tracking as its own issue — it is the missing half of the NTP migration.

Fixes a regression I introduced in #315. ## What was wrong `10-vlan40.network.j2` set `IPv6AcceptRA=false` on `enp1s0.40`, leaving the interface with only a link-local address. edge-1 was therefore unreachable over IPv6 on the service VLAN — the VLAN it exists on in order to inherit the NTP VIP after the Talos migration. The concern behind it was real: a second default route from the VLAN 40 lease would make return traffic asymmetric across the two VLANs. But that only required suppressing the default route and DNS, not addressing itself. Disabling RAs wholesale traded away the primary address family to protect the legacy one. ## Fix ``` [Network] IPv6AcceptRA=true [IPv6AcceptRA] UseGateway=false UseDNS=false ``` Mirrors what `UseRoutes=false` / `UseDNS=false` already do for DHCPv4. Both options verified present in systemd 261 against `man systemd.network` (`UseGateway=` added in v250), not assumed. ## Verified on edge-1 after applying - `enp1s0.40` now holds `fd40::8eec:4bff:fe43:cebc/64` and `2003:d6:9717:a702:8eec:4bff:fe43:cebc/64` alongside `10.2.40.153/24` - exactly **one** IPv6 default route, still via `enp1s0` (VLAN 30) - exactly **one** IPv4 default route, still via `10.2.30.1` on `enp1s0` - no DNS servers on the VLAN 40 link - `chronyd` and `keepalived` both still active; VIP unaffected Auto-revert timer armed before the change and disarmed after, as with #315. ## Separate, larger issue found while verifying — NOT fixed here **All four NTP servers bind only `0.0.0.0:123`. There is no IPv6 listener anywhere in the cluster.** ``` jupiter UNCONN 0 0 0.0.0.0:123 0.0.0.0:* saturn UNCONN 0 0 0.0.0.0:123 0.0.0.0:* neptune UNCONN 0 0 0.0.0.0:123 0.0.0.0:* edge-1 UNCONN 0 0 0.0.0.0:123 0.0.0.0:* ``` This predates edge-1 and is not caused by this PR or #315 — the three PVE nodes have served the LAN this way for months. So IPv6 clients have never received NTP from this infrastructure, and the VIP design (`10.2.40.4`, DHCP option 42, the VRRP instance) is IPv4-only end to end. After the Talos migration, IPv6 clients would have no NTP source at all. Cause **not determined**. Ruled out so far: no `-4` flag in either unit (`DAEMON_OPTS="-F 1"` on Debian, no options on Arch), `net.ipv6.conf.all.disable_ipv6=0` on both. The chrony documentation states `allow` is access-control only and does not restrict address family, and does not document a default that would explain this. Needs proper investigation rather than a guess. Worth tracking as its own issue — it is the missing half of the NTP migration.
claude-bot added 1 commit 2026-07-19 20:24:18 +00:00
The previous config set IPv6AcceptRA=false on enp1s0.40, which left the
interface with only a link-local address and made edge-1 unreachable over IPv6
on the service VLAN. IPv6 is the primary address family on this network, so
suppressing it to guard against a routing problem in the legacy family was the
wrong trade.

The concern that motivated it was real -- a second default route from the VLAN
40 lease would make return traffic asymmetric across the two VLANs -- but it
only required suppressing the default route and DNS, not addressing itself.
RAs are now accepted with [IPv6AcceptRA] UseGateway=false and UseDNS=false,
mirroring what UseRoutes=false and UseDNS=false already do for DHCPv4. Both
options verified present in systemd 261 (UseGateway added in v250).

Verified after applying to edge-1:
  - enp1s0.40 now holds fd40::/64 and 2003:d6:9717:a702::/64 alongside
    10.2.40.153/24
  - exactly one IPv6 default route, still via enp1s0 (VLAN 30)
  - exactly one IPv4 default route, still via 10.2.30.1 on enp1s0
  - no DNS servers on the VLAN 40 link
  - chronyd and keepalived unaffected

Separately discovered and NOT addressed here: all four NTP servers bind only
0.0.0.0:123, so the service has no IPv6 listener at all -- this predates edge-1
and affects jupiter, saturn and neptune equally. No -4 flag is set and IPv6 is
not disabled in the kernel; the cause is not yet determined.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
k.renner merged commit 756edc4060 into master 2026-07-19 20:24:48 +00:00
Author
Member

Second commit added: IPv6 allow rule — this is the actual reason the infrastructure has never served IPv6 clients.

chrony's allow directive is per address family and the default is that no clients are allowed access. The chrony.conf documentation lists allow 0/0 and allow ::/0 as separate examples for exactly this reason. Every allow rule in this repo was IPv4, so all four servers bound 0.0.0.0:123 and opened no IPv6 socket at all.

Adding an IPv6 rule brings up [::]:123. Verified with a real NTP transaction, not just a listening socket — chronyd -Q from jupiter against edge-1's ULA:

server fd40::8eec:4bff:fe43:cebc iburst
-> System clock wrong by -0.000023 seconds

ULA only, deliberately. The GUA prefix is ISP-delegated and rotates. A hardcoded 2003:...::/56 rule silently stops matching at the next delegation change, and since the default is deny, a stale rule fails closed and locks clients out. Keeping both the allow rule and any future service address on ULA makes them independent of the ISP entirely. Per RFC 6724 source selection a client reaching a ULA destination sources from its own ULA, so ULA-only suffices provided clients target a ULA address.

Scoped to edge_host only. jupiter, saturn and neptune still need the same rule before IPv6 clients can be pointed anywhere — left for a follow-up so this proves out on one host first.

This changes the shape of the anycast question in the parent discussion: with a working IPv6 listener, a ULA anycast address is now viable and is rotation-proof by construction. Still unverified: DAD suppression mechanics for a shared address in systemd-networkd, and whether the UDM can advertise an NTP server to IPv6 clients (DHCPv6 option 56 or an RA option) — without that, no client learns the address regardless of how it is held.

Second commit added: **IPv6 `allow` rule** — this is the actual reason the infrastructure has never served IPv6 clients. chrony's `allow` directive is per address family and **the default is that no clients are allowed access**. The chrony.conf documentation lists `allow 0/0` and `allow ::/0` as separate examples for exactly this reason. Every allow rule in this repo was IPv4, so all four servers bound `0.0.0.0:123` and opened no IPv6 socket at all. Adding an IPv6 rule brings up `[::]:123`. Verified with a real NTP transaction, not just a listening socket — `chronyd -Q` from jupiter against edge-1's ULA: ``` server fd40::8eec:4bff:fe43:cebc iburst -> System clock wrong by -0.000023 seconds ``` **ULA only, deliberately.** The GUA prefix is ISP-delegated and rotates. A hardcoded `2003:...::/56` rule silently stops matching at the next delegation change, and since the default is deny, a stale rule **fails closed** and locks clients out. Keeping both the allow rule and any future service address on ULA makes them independent of the ISP entirely. Per RFC 6724 source selection a client reaching a ULA destination sources from its own ULA, so ULA-only suffices provided clients target a ULA address. **Scoped to `edge_host` only.** jupiter, saturn and neptune still need the same rule before IPv6 clients can be pointed anywhere — left for a follow-up so this proves out on one host first. This changes the shape of the anycast question in the parent discussion: with a working IPv6 listener, a ULA anycast address is now viable and is rotation-proof by construction. Still unverified: DAD suppression mechanics for a shared address in systemd-networkd, and whether the UDM can advertise an NTP server to IPv6 clients (DHCPv6 option 56 or an RA option) — without that, no client learns the address regardless of how it is held.
Sign in to join this conversation.