Skip to content
This repository was archived by the owner on Jun 6, 2026. It is now read-only.

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

PreferDualStack Admission Controller

Warning

This project is deprecated and the repository is archived.

Kubernetes now ships MutatingAdmissionPolicy, an in-process, CEL-based mutating admission mechanism that replaces this webhook entirely — no server, Deployment, Service, or TLS certificate management required. See Recommended replacement below.

MutatingAdmissionPolicy is GA in Kubernetes 1.36+ and works out of the box via admissionregistration.k8s.io/v1. On 1.34/1.35 it exists only as v1beta1, which is off by default — it requires the MutatingAdmissionPolicy feature gate and --runtime-config=admissionregistration.k8s.io/v1beta1=true on the API server, so on a stock 1.35 cluster it won't work. Upgrade to 1.36 (or enable those flags) to use it.

The published container images remain available for anyone still running the webhook, but the source, manifests, and CI have been removed and no further updates will be made.

Kubernetes services are created in single stack mode by default, defaulting to the primary address family of the cluster.

This was a mutating webhook admission controller that adds ipFamilyPolicy: PreferDualStack to all newly created services if the field is not explicitly specified, making dual-stack the default.

Recommended replacement

On any cluster running Kubernetes 1.34 or newer, the same behaviour can be achieved declaratively with a MutatingAdmissionPolicy and a binding — no webhook server to deploy or certificates to rotate.

apiVersion: admissionregistration.k8s.io/v1   # GA in 1.36+; on 1.34/1.35 use v1beta1 with the feature gate + runtime-config enabled
kind: MutatingAdmissionPolicy
metadata:
  name: prefer-dual-stack-services
spec:
  matchConstraints:
    resourceRules:
    - apiGroups:   [""]
      apiVersions: ["v1"]
      operations:  ["CREATE"]
      resources:   ["services"]
  matchConditions:
  - name: ipfamilypolicy-unset
    expression: "!has(object.spec.ipFamilyPolicy)"
  - name: not-externalname
    expression: 'object.spec.type != "ExternalName"'
  failurePolicy: Ignore
  reinvocationPolicy: Never
  mutations:
  - patchType: ApplyConfiguration
    applyConfiguration:
      expression: >
        Object{
          spec: Object.spec{
            ipFamilyPolicy: "PreferDualStack"
          }
        }
---
apiVersion: admissionregistration.k8s.io/v1   # GA in 1.36+; on 1.34/1.35 use v1beta1 with the feature gate + runtime-config enabled
kind: MutatingAdmissionPolicyBinding
metadata:
  name: prefer-dual-stack-services-binding
spec:
  policyName: prefer-dual-stack-services

How this maps to the old webhook:

  • The matchConditions reproduce the original guards: only apply when spec.ipFamilyPolicy is unset and the service is not of type ExternalName (the != "ExternalName" check holds whether type is empty or ClusterIP).
  • operations: ["CREATE"] matches the webhook's create-only behaviour.
  • ApplyConfiguration is used because ipFamilyPolicy is a plain scalar on a non-atomic struct. If you ever need to mutate atomic structs/maps/lists, use patchType: JSONPatch instead.
  • failurePolicy: Ignore mirrors the webhook's "never get in the way" posture; set it to Fail if you'd rather block service creation on policy errors.

Demo

Before After
> kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: dummy
spec:
  selector:
    app: dummy
  ports:
    - port: 80
      targetPort: 80
EOF
service/dummy created

> kubectl describe svc dummy
Name:              dummy
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=dummy
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.96.200.4
IPs:               10.96.200.4
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         <none>
Session Affinity:  None
Events:            <none>
> kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: dummy
spec:
  selector:
    app: dummy
  ports:
    - port: 80
      targetPort: 80
EOF
service/dummy created

> kubectl describe svc dummy
Name:              dummy
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=dummy
Type:              ClusterIP
IP Family Policy:  PreferDualStack
IP Families:       IPv4,IPv6
IP:                10.96.19.47
IPs:               10.96.19.47,fd00:10:96::f31
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         <none>
Session Affinity:  None
Events:            <none>

Acknowledgements

Admission controller webhook boilerplate adapted from douglasmakey/admissioncontroller by Douglas Makey Mendez Molero.

About

Admission controller that makes services dual-stack by default

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors