Skip to content

Feature: configurable IP family (dual-stack / IPv6-only) for the Tenant Control Plane Service #1245

Description

@Jakob3xD

Feature: configurable IP family (dual-stack / IPv6-only) for the Tenant Control Plane Service

Summary

Allow TenantControlPlane.spec.controlPlane.service to configure the IP family of the
generated control-plane Service, so a Tenant Control Plane can be exposed as dual-stack,
IPv4-only, or IPv6-only. Today the exposing Service always inherits the management
cluster's default IP-family behaviour, with no per-TCP override.

Motivation

On dual-stack or IPv6-only management clusters there is currently no way to control how
the Tenant Control Plane endpoint Service is exposed. Operators who want an IPv6-only
control-plane endpoint, or an explicit dual-stack one with a chosen primary family, have
no knob for it. The tenant-internal networking already supports dual-stack via
networkProfile.serviceCidrs / podCidrs, but the exposing Service does not.

Current behaviour

internal/resources/k8s_service_resource.go builds the control-plane Service but never
sets spec.ipFamilies or spec.ipFamilyPolicy. The Service therefore always falls back
to the management cluster default (typically SingleStack).

The Konnectivity Service (internal/resources/konnectivity/service_resource.go) is not a
separate Service — it only appends a konnectivity-server port to the same Service named
after the TCP, so it is covered automatically.

CoreDNS (internal/resources/addons/coredns.go) already derives its IP families from
networkProfile.dnsServiceIPs (defaulted from serviceCidrs) and correctly handles both
dual-stack and IPv6-only; it is intentionally not part of this request.

Proposed change

Add two optional fields to ServiceSpec, mirroring the native Kubernetes Service fields
1:1 (pure passthrough):

spec:
  controlPlane:
    service:
      serviceType: LoadBalancer
      # IPv6-only:
      ipFamilyPolicy: SingleStack
      ipFamilies: [IPv6]
      # or dual-stack, IPv6 primary:
      # ipFamilyPolicy: RequireDualStack
      # ipFamilies: [IPv6, IPv4]
  • ipFamilyPolicy (*corev1.IPFamilyPolicy, enum SingleStack|PreferDualStack|RequireDualStack)
  • ipFamilies ([]corev1.IPFamily, enum IPv4|IPv6, max 2 items)

When both are unset, behaviour is unchanged (the management cluster default applies).

Validation

Cross-field CEL on TenantControlPlaneSpec (consistent with the existing
allocateLoadBalancerNodePorts validation):

  • ipFamilyPolicy: SingleStack allows at most one entry in ipFamilies.
  • ipFamilies entries must be unique.

No immutability constraint, so the Kubernetes-supported single-stack -> dual-stack upgrade
keeps working; API-server-forbidden transitions (dual -> single, v4 <-> v6) surface as a
reconcile error rather than being blocked at admission.

Alternatives considered

  • A single high-level enum (e.g. ipFamily: SingleStackIPv6|DualStack): simpler, but
    cannot express PreferDualStack vs RequireDualStack or family ordering. Rejected in
    favour of faithful, familiar Kubernetes semantics.
  • Delete/recreate the Service to force forbidden IP-family transitions: rejected as an
    implicit behaviour because it churns the control-plane endpoint IP, can drop a
    LoadBalancer IP, breaks Konnectivity and cached kubeconfigs/certs. Could be a separate
    explicit opt-in feature if ever needed.

Scope

  • In scope: control-plane Service (and Konnectivity, implicitly).
  • Out of scope: CoreDNS changes, RequireDualStack for CoreDNS, delete/recreate flows.

I'm happy to open a PR for this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions