Connecting the gateway cluster to service clusters¶
Gateway pods live on a dedicated transit network (a vpn-transit-style
network, not on any service cluster's own fabric). Reaching a new
service cluster's Pod/Service network from a tunnel means giving that
cluster's router a leg on the transit network and adding one static route on
the gateway cluster's own router. This is one-time per service cluster,
not per tunnel or per exposure: once a service cluster is plumbed in, every
IPsecGateway on the gateway cluster can add exposures reaching into it
with no further fabric changes.
Validated end-to-end 2026-08-19
The fabric this page describes — a transit-network leg plus a gateway-
router route — has been validated live as part of v0.1.0's end-to-end
release testing; see the worked example at the bottom of this page. As
of v0.2.0, the ClusterLink CRD below is the reconciler-driven way to
set it up.
The automated way: ClusterLink¶
ClusterLink (ipsec.bnerd.net/v1alpha1, cluster-scoped — it manages
estate-level fabric, not tenant state) is the primary way to plumb in a
service cluster: one CR does what the manual method
below does by hand, and keeps doing it — it's a normal reconciler, so it
self-heals if a leg port or route is ever removed out-of-band.
apiVersion: ipsec.bnerd.net/v1alpha1
kind: ClusterLink
metadata:
name: svc01-link
spec:
serviceClusterRouter: shoot--services--svc01
serviceClusterCIDR: 10.251.0.0/16
Reconciling this:
- Allocates a transit IP for the leg port — starting at the transit
subnet's
.3(.1is conventionally the gateway cluster's own router leg,.2the network's DHCP port; both are skipped by construction, and any address already claimed by anotherClusterLinkor occupied on the network is skipped too), unlessspec.transitIPpins one explicitly. - Creates (or adopts — see below) the leg port on the operator's
transit network, tagged for this
ClusterLink. - Attaches that port to
spec.serviceClusterRouter(a router name or ID — resolved at reconcile time). - Adds the route on the gateway cluster's own router (the operator's
configured
routerID):spec.serviceClusterCIDRvia the allocated transit IP.
No extra route is needed for the return path — once step 3 has the transit-network leg, the service cluster gets a connected route back to the transit network for free.
The target-side security group is still your responsibility, exactly as
under the manual method: the service being exposed has to admit ingress
from the transit network's CIDR on the exposed ports — see the Networking
guide's "Exposure target reachability" section.
ClusterLink only plumbs the fabric; it never touches a target's security
group.
Adopting a pre-existing, hand-made leg¶
If a service cluster was already connected the manual way — a leg port
that exists but was never tagged by this operator — applying a
ClusterLink with the matching serviceClusterRouter and (if it isn't
already sitting at .3) the matching spec.transitIP adopts that port
instead of creating a duplicate or failing on an address conflict: an
untagged port already attached to the target router, at the candidate
transit IP, is re-tagged and taken over. Deleting the ClusterLink
afterwards deletes that port — an adopted-then-tagged port is this
ClusterLink's to delete, the same as one it created outright. See the
CRD Reference for the exact
adoption rule and every status field/condition.
Deleting a ClusterLink¶
Deleting the CR unwinds everything it owns, in reverse order: the gateway-
router route, then the router attachment, then the leg port itself. Do this
only once nothing still depends on reaching that service cluster — every
IPsecGateway exposure targeting it will break as soon as the route is
gone.
Verification recipe¶
Add (or reuse) an IPsecGateway exposure targeting a service in the newly
connected cluster, then reach its virtual IP through a live tunnel from the
client side. A successful check confirms a single gateway can reach
exposures on multiple service clusters simultaneously once each is plumbed
in, and that adding the exposure itself is a live reconcile with zero pod
restarts, consistent with the operator's live-reload behavior for
NAT/config changes (see Operations).
Ops note: Gardener admin kubeconfigs are time-boxed¶
If a service cluster's kubectl starts failing with remote error: tls:
expired certificate while you're setting up a verification target (a test
pod, a target-side security-group rule) in that cluster, the Gardener admin
kubeconfig has simply expired — this is expected, not a fault in the
cluster or the operator, and it has nothing to do with ClusterLink itself
(reconciling a ClusterLink never touches the service cluster's Kubernetes
API, only its OpenStack router). Refresh it via the Gardener virtual-garden
shoots/<name>/adminkubeconfig request rather than treating it as an
incident.
Manual method (background and troubleshooting)¶
This is the procedure ClusterLink automates. Useful for understanding
exactly what the reconciler is doing, or for plumbing in a service cluster
without applying a CR (e.g. a one-off, or before the operator is upgraded
to a version that has ClusterLink).
Replace <gateway-cluster> / <service-cluster> with your own cluster
names throughout.
-
Create a port on the transit network for the service cluster's router, with a fixed IP starting at
.3or higher..1is conventionally the gateway cluster's own router;.2is typically the network's DHCP port — pointing anything at.2collides with DHCP and should be avoided. -
Attach that port to the service cluster's router:
-
Add a static route on the gateway cluster's own router, pointing the service cluster's node CIDR at the leg's IP:
openstack router set --route destination=<service-cluster-node-CIDR>,gateway=<transit-leg-ip> \ shoot--services--<gateway-cluster>--routeappends, and it will happily point at the wrong portOn some OpenStack client versions,
--routeappends to the router's existing route list rather than replacing it — it will not warn you about a stale or duplicate entry. Always check the result withopenstack router show -c routes <router>afterward, and never point a route at the transit network's DHCP port. -
Open the target-side security group. The service being exposed — an internal LB VIP, a NodePort, whatever the exposure targets — has to admit ingress from the transit network's CIDR on the exposed ports; this is the same prerequisite the Networking guide's "Exposure target reachability" section already documents for same-cluster exposures, and it applies identically once the target lives in a different cluster.
-
No extra route is needed for the return path. Once the router in step 2 has the transit-network leg, the service cluster gets a connected route back to the transit network for free — return traffic from the exposed service to the gateway needs nothing further.
Worked example from our reference deployment (validated 2026-08-19)¶
Concrete values below are from b'nerd's own reference deployment — worked example only, not requirements for your own estate.
- Gateway cluster
vpn01, service clustersvc01. - Transit leg: a port on
vpn-transit, fixed IP10.254.0.3, attached tosvc01's router. - Route:
vpn01's router got10.251.0.0/16 via 10.254.0.3(svc01's node CIDR, routed at the transit leg's IP). - Both are permanent infrastructure — kept after verification, unlike the scaffolding security-group rule and test pod used only to prove the path.
- Verification: an
IPsecGatewayexposure onvpn01targeting100.64.0.11:8080was reached through a live tunnel and landed on a pod on ansvc01worker, through the same tunnel already carryingvpn01's own same-cluster exposure — confirming a single gateway can reach exposures on multiple service clusters simultaneously once each is plumbed in. - This is exactly the shape a
ClusterLinkCR (serviceClusterRouter: shoot--services--svc01,serviceClusterCIDR: 10.251.0.0/16,spec.transitIP: 10.254.0.3to match the existing leg) would take over via its adoption path, described above, without disturbing the tunnel or requiring the leg/route to be recreated.