fix(talos): route LoadBalancer replies through table 47 #1549

Merged
k.renner merged 1 commits from lb-replies-table-47 into main 2026-11-02 07:45:38 +00:00
Member

Replies from Cilium LoadBalancer VIPs now leave the node over ingress0, where the requests arrive as the BGP next hop. Before, they went out over mgmt0.

Closes #1547.

Why

  • A VIP reply reaches the kernel's routing decision with the VIP as its source.
  • The pod-source rules 1000 (fd10:244::/48) and 1001 (10.244.0.0/16) therefore miss it.
  • Main's default then sends it out mgmt0 (10.2.40.1).
  • Seen on k8s-3, 2026-11-02: replies from 10.2.99.90 (shelly-manager-firmware) to an IoT device appear only on mgmt0, while the device's requests and ACKs arrive on ingress0.

Change

  • talos/patches/site-homelab.yaml:
    • RoutingRuleConfig 1002 (src fd99::/120) and 1003 (src 10.2.99.0/24) → table 47.
    • The LoadBalancer pool as a site fact: inline manifest lb-pool-site (ConfigMap, flux-system) with LB_POOL_V4/LB_POOL_V6, next to the rules.
  • site-qemu.yaml, site-hcloud.yaml: the same lb-pool-site. Services pin their addresses from the production pool, so a test run carries the same pool.
  • infrastructure/post-crds/cilium-bgp.yaml: bgp-pool takes its blocks from ${LB_POOL_V4} and ${LB_POOL_V6} as CIDR blocks.
    • With Cilium 1.20 a CIDR block uses all its addresses (allowFirstLastIPs defaults to Yes), so the blocks cover exactly the former start/stop ranges.
  • clusters/homelab/infrastructure.yaml: infrastructure-crds substitutes from lb-pool-site as well as cluster-settings.
  • NETWORK-traffic-classes.md: the LoadBalancer reply rule.

Rollout

  • Talos creates inline manifests at bootstrap; on the running cluster lb-pool-site has to exist before this merges. Without it the substitution fails and infrastructure-crds stops reconciling. It is created once by hand with the same content, before the merge.
  • The routing rules reach the running nodes with talosctl patch machineconfig, k8s-3 first. Network config applies without a reboot.

🤖 Generated with Claude Code

https://claude.ai/code/session_0185Qb5YG28FfnVi1ujMEV5C

Replies from Cilium LoadBalancer VIPs now leave the node over `ingress0`, where the requests arrive as the BGP next hop. Before, they went out over `mgmt0`. Closes #1547. **Why** - A VIP reply reaches the kernel's routing decision with the VIP as its source. - The pod-source rules 1000 (`fd10:244::/48`) and 1001 (`10.244.0.0/16`) therefore miss it. - Main's default then sends it out `mgmt0` (`10.2.40.1`). - Seen on k8s-3, 2026-11-02: replies from `10.2.99.90` (`shelly-manager-firmware`) to an IoT device appear only on `mgmt0`, while the device's requests and ACKs arrive on `ingress0`. **Change** - `talos/patches/site-homelab.yaml`: - RoutingRuleConfig 1002 (`src fd99::/120`) and 1003 (`src 10.2.99.0/24`) → table 47. - The LoadBalancer pool as a site fact: inline manifest `lb-pool-site` (ConfigMap, `flux-system`) with `LB_POOL_V4`/`LB_POOL_V6`, next to the rules. - `site-qemu.yaml`, `site-hcloud.yaml`: the same `lb-pool-site`. Services pin their addresses from the production pool, so a test run carries the same pool. - `infrastructure/post-crds/cilium-bgp.yaml`: `bgp-pool` takes its blocks from `${LB_POOL_V4}` and `${LB_POOL_V6}` as CIDR blocks. - With Cilium 1.20 a CIDR block uses all its addresses (`allowFirstLastIPs` defaults to `Yes`), so the blocks cover exactly the former `start`/`stop` ranges. - `clusters/homelab/infrastructure.yaml`: `infrastructure-crds` substitutes from `lb-pool-site` as well as `cluster-settings`. - `NETWORK-traffic-classes.md`: the LoadBalancer reply rule. **Rollout** - Talos creates inline manifests at bootstrap; on the running cluster `lb-pool-site` has to exist before this merges. Without it the substitution fails and `infrastructure-crds` stops reconciling. It is created once by hand with the same content, before the merge. - The routing rules reach the running nodes with `talosctl patch machineconfig`, k8s-3 first. Network config applies without a reboot. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0185Qb5YG28FfnVi1ujMEV5C
claude-bot added 1 commit 2026-11-02 07:43:05 +00:00
fix(talos): route LoadBalancer replies through table 47
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
3501586841
A reply from a Cilium LoadBalancer VIP reaches the routing decision with
the VIP as its source, so the pod-source rules 1000/1001 missed it and
main sent it out mgmt0 while the request came in over ingress0, the BGP
next hop (k8s-3, 2026-11-02: replies from 10.2.99.90 to an IoT device
seen only on mgmt0). Rules 1002/1003 send the pool to table 47.

The pool becomes a site fact: each site patch carries lb-pool-site
(flux-system), and bgp-pool takes its blocks from it by Flux
substitution in infrastructure-crds. The CIDR blocks cover the same
addresses as the start/stop ranges did (allowFirstLastIPs defaults to
Yes). The qemu and hcloud sites carry the production pool, since
Services pin their addresses from it.

Refs #1547

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0185Qb5YG28FfnVi1ujMEV5C
claude-bot requested review from review-bot 2026-11-02 07:43:05 +00:00
k.renner merged commit 59c9cc29fe into main 2026-11-02 07:45:38 +00:00
Sign in to join this conversation.