From cbc8e77b2a6232e5f76a11fa726eaf9d5b9374c5 Mon Sep 17 00:00:00 2001 From: Adrian Moisey Date: Thu, 21 May 2026 08:35:54 +0200 Subject: [PATCH] KEP-4427: Remove RelaxedDNSSearchValidation feature gate --- pkg/api/pod/util.go | 25 --- pkg/apis/core/validation/validation.go | 12 +- pkg/apis/core/validation/validation_test.go | 166 +++++------------- pkg/features/kube_features.go | 14 -- .../reference/feature_list.md | 1 - .../reference/versioned_feature_list.yaml | 14 -- test/integration/pods/pods_test.go | 81 +++------ 7 files changed, 68 insertions(+), 245 deletions(-) diff --git a/pkg/api/pod/util.go b/pkg/api/pod/util.go index a73dd072939..d8757abda57 100644 --- a/pkg/api/pod/util.go +++ b/pkg/api/pod/util.go @@ -438,7 +438,6 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po // If old spec uses relaxed validation or enabled the RelaxedEnvironmentVariableValidation feature gate, // we must allow it opts.AllowRelaxedEnvironmentVariableValidation = useRelaxedEnvironmentVariableValidation(podSpec, oldPodSpec) - opts.AllowRelaxedDNSSearchValidation = useRelaxedDNSSearchValidation(oldPodSpec) opts.AllowEnvFilesValidation = useAllowEnvFilesValidation(oldPodSpec) opts.AllowUserNamespacesHostNetworkSupport = useAllowUserNamespacesHostNetworkSupport(oldPodSpec) @@ -522,30 +521,6 @@ func useRelaxedEnvironmentVariableValidation(podSpec, oldPodSpec *api.PodSpec) b return false } -func useRelaxedDNSSearchValidation(oldPodSpec *api.PodSpec) bool { - // Return true early if feature gate is enabled - if utilfeature.DefaultFeatureGate.Enabled(features.RelaxedDNSSearchValidation) { - return true - } - - // Return false early if there is no DNSConfig or Searches. - if oldPodSpec == nil || oldPodSpec.DNSConfig == nil || oldPodSpec.DNSConfig.Searches == nil { - return false - } - - return hasDotOrUnderscore(oldPodSpec.DNSConfig.Searches) -} - -// Helper function to check if any domain is a dot or contains an underscore. -func hasDotOrUnderscore(searches []string) bool { - for _, domain := range searches { - if domain == "." || strings.Contains(domain, "_") { - return true - } - } - return false -} - func useAllowUserNamespacesHostNetworkSupport(oldPodSpec *api.PodSpec) bool { // Return true early if feature gate is enabled if utilfeature.DefaultFeatureGate.Enabled(features.UserNamespacesHostNetworkSupport) { diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index ce34fadfb4b..29c50949e0f 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -4186,16 +4186,10 @@ func validatePodDNSConfig(dnsConfig *core.PodDNSConfig, dnsPolicy *core.DNSPolic } for i, search := range dnsConfig.Searches { - if opts.AllowRelaxedDNSSearchValidation { - if search != "." { - search = strings.TrimSuffix(search, ".") - allErrs = append(allErrs, ValidateDNS1123SubdomainWithUnderScore(search, fldPath.Child("searches").Index(i))...) - } - } else { + if search != "." { search = strings.TrimSuffix(search, ".") - allErrs = append(allErrs, ValidateDNS1123Subdomain(search, fldPath.Child("searches").Index(i))...) + allErrs = append(allErrs, ValidateDNS1123SubdomainWithUnderScore(search, fldPath.Child("searches").Index(i))...) } - } // Validate options. for i, option := range dnsConfig.Options { @@ -4460,8 +4454,6 @@ type PodValidationOptions struct { ResourceIsPod bool // Allow relaxed validation of environment variable names AllowRelaxedEnvironmentVariableValidation bool - // Allow the use of a relaxed DNS search - AllowRelaxedDNSSearchValidation bool // Allows zero value for Pod Lifecycle Sleep Action AllowPodLifecycleSleepActionZeroValue bool // Allow only Recursive value of SELinuxChangePolicy. diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go index ab0bf5ab66d..85aa795460b 100644 --- a/pkg/apis/core/validation/validation_test.go +++ b/pkg/apis/core/validation/validation_test.go @@ -27870,162 +27870,76 @@ func TestValidateSleepAction(t *testing.T) { } } -// TODO: merge these test to TestValidatePodSpec after AllowRelaxedDNSSearchValidation feature graduates to GA func TestValidatePodDNSConfigWithRelaxedSearchDomain(t *testing.T) { testCases := []struct { - name string - expectError bool - featureEnabled bool - dnsConfig *core.PodDNSConfig + name string + expectError bool + dnsConfig *core.PodDNSConfig }{ { - name: "beginswith underscore, contains underscore, featuregate enabled", - expectError: false, - featureEnabled: true, - dnsConfig: &core.PodDNSConfig{Searches: []string{"_sip._tcp.abc_d.example.com"}}, + name: "beginswith underscore, contains underscore", + expectError: false, + dnsConfig: &core.PodDNSConfig{Searches: []string{"_sip._tcp.abc_d.example.com"}}, }, { - name: "contains underscore, featuregate enabled", - expectError: false, - featureEnabled: true, - dnsConfig: &core.PodDNSConfig{Searches: []string{"abc_d.example.com"}}, + name: "contains underscore", + expectError: false, + dnsConfig: &core.PodDNSConfig{Searches: []string{"abc_d.example.com"}}, }, { - name: "is dot, featuregate enabled", - expectError: false, - featureEnabled: true, - dnsConfig: &core.PodDNSConfig{Searches: []string{"."}}, + name: "is dot", + expectError: false, + dnsConfig: &core.PodDNSConfig{Searches: []string{"."}}, }, { - name: "two dots, featuregate enabled", - expectError: true, - featureEnabled: true, - dnsConfig: &core.PodDNSConfig{Searches: []string{".."}}, + name: "two dots", + expectError: true, + dnsConfig: &core.PodDNSConfig{Searches: []string{".."}}, }, { - name: "underscore and dot, featuregate enabled", - expectError: true, - featureEnabled: true, - dnsConfig: &core.PodDNSConfig{Searches: []string{"_."}}, + name: "underscore and dot", + expectError: true, + dnsConfig: &core.PodDNSConfig{Searches: []string{"_."}}, }, { - name: "dash and dot, featuregate enabled", - expectError: true, - featureEnabled: true, - dnsConfig: &core.PodDNSConfig{Searches: []string{"-."}}, + name: "dash and dot", + expectError: true, + dnsConfig: &core.PodDNSConfig{Searches: []string{"-."}}, }, { - name: "two underscore and dot, featuregate enabled", - expectError: true, - featureEnabled: true, - dnsConfig: &core.PodDNSConfig{Searches: []string{"__."}}, + name: "two underscore and dot", + expectError: true, + dnsConfig: &core.PodDNSConfig{Searches: []string{"__."}}, }, { - name: "dot and two underscore, featuregate enabled", - expectError: true, - featureEnabled: true, - dnsConfig: &core.PodDNSConfig{Searches: []string{".__"}}, + name: "dot and two underscore", + expectError: true, + dnsConfig: &core.PodDNSConfig{Searches: []string{".__"}}, }, { - name: "dot and underscore, featuregate enabled", - expectError: true, - featureEnabled: true, - dnsConfig: &core.PodDNSConfig{Searches: []string{"._"}}, + name: "dot and underscore", + expectError: true, + dnsConfig: &core.PodDNSConfig{Searches: []string{"._"}}, }, { - name: "lot of underscores, featuregate enabled", - expectError: true, - featureEnabled: true, - dnsConfig: &core.PodDNSConfig{Searches: []string{"____________"}}, + name: "lot of underscores", + expectError: true, + dnsConfig: &core.PodDNSConfig{Searches: []string{"____________"}}, }, { - name: "a regular name, featuregate enabled", - expectError: false, - featureEnabled: true, - dnsConfig: &core.PodDNSConfig{Searches: []string{"example.com"}}, + name: "a regular name", + expectError: false, + dnsConfig: &core.PodDNSConfig{Searches: []string{"example.com"}}, }, { - name: "unicode character, featuregate enabled", - expectError: true, - featureEnabled: true, - dnsConfig: &core.PodDNSConfig{Searches: []string{"☃.example.com"}}, - }, - { - name: "begins with underscore, contains underscore, featuregate disabled", - expectError: true, - featureEnabled: false, - dnsConfig: &core.PodDNSConfig{Searches: []string{"_sip._tcp.abc_d.example.com"}}, - }, - { - name: "contains underscore, featuregate disabled", - expectError: true, - featureEnabled: false, - dnsConfig: &core.PodDNSConfig{Searches: []string{"abc_d.example.com"}}, - }, - { - name: "is dot, featuregate disabled", - expectError: true, - featureEnabled: false, - dnsConfig: &core.PodDNSConfig{Searches: []string{"."}}, - }, - { - name: "two dots, featuregate disabled", - expectError: true, - featureEnabled: false, - dnsConfig: &core.PodDNSConfig{Searches: []string{".."}}, - }, - { - name: "underscore and dot, featuregate disabled", - expectError: true, - featureEnabled: false, - dnsConfig: &core.PodDNSConfig{Searches: []string{"_."}}, - }, - { - name: "dash and dot, featuregate disabled", - expectError: true, - featureEnabled: false, - dnsConfig: &core.PodDNSConfig{Searches: []string{"-."}}, - }, - { - name: "two underscore and dot, featuregate disabled", - expectError: true, - featureEnabled: false, - dnsConfig: &core.PodDNSConfig{Searches: []string{"__."}}, - }, - { - name: "dot and two underscore, featuregate disabled", - expectError: true, - featureEnabled: false, - dnsConfig: &core.PodDNSConfig{Searches: []string{".__"}}, - }, - { - name: "dot and underscore, featuregate disabled", - expectError: true, - featureEnabled: false, - dnsConfig: &core.PodDNSConfig{Searches: []string{"._"}}, - }, - { - name: "lot of underscores, featuregate disabled", - expectError: true, - featureEnabled: false, - dnsConfig: &core.PodDNSConfig{Searches: []string{"____________"}}, - }, - { - name: "a regular name, featuregate disabled", - expectError: false, - featureEnabled: false, - dnsConfig: &core.PodDNSConfig{Searches: []string{"example.com"}}, - }, - { - name: "unicode character, featuregate disabled", - expectError: true, - featureEnabled: false, - dnsConfig: &core.PodDNSConfig{Searches: []string{"☃.example.com"}}, + name: "unicode character", + expectError: true, + dnsConfig: &core.PodDNSConfig{Searches: []string{"☃.example.com"}}, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - errs := validatePodDNSConfig(testCase.dnsConfig, nil, nil, PodValidationOptions{AllowRelaxedDNSSearchValidation: testCase.featureEnabled}) + errs := validatePodDNSConfig(testCase.dnsConfig, nil, nil, PodValidationOptions{}) if testCase.expectError && len(errs) == 0 { t.Errorf("Unexpected success") } diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 45c68e38f92..a21bf20a8aa 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -838,12 +838,6 @@ const ( // containers (aka containers in CrashLoopBackOff) ReduceDefaultCrashLoopBackOffDecay featuregate.Feature = "ReduceDefaultCrashLoopBackOffDecay" - // owner: @adrianmoisey - // kep: https://kep.k8s.io/4427 - // - // Relaxed DNS search string validation. - RelaxedDNSSearchValidation featuregate.Feature = "RelaxedDNSSearchValidation" - // owner: @HirazawaUi // kep: https://kep.k8s.io/4369 // @@ -1783,12 +1777,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate {Version: version.MustParse("1.33"), Default: false, PreRelease: featuregate.Alpha}, }, - RelaxedDNSSearchValidation: { - {Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha}, - {Version: version.MustParse("1.33"), Default: true, PreRelease: featuregate.Beta}, - {Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.37 - }, - RelaxedEnvironmentVariableValidation: { {Version: version.MustParse("1.30"), Default: false, PreRelease: featuregate.Alpha}, {Version: version.MustParse("1.32"), Default: true, PreRelease: featuregate.Beta}, @@ -2480,8 +2468,6 @@ var defaultKubernetesFeatureGateDependencies = map[featuregate.Feature][]feature ReduceDefaultCrashLoopBackOffDecay: {}, - RelaxedDNSSearchValidation: {}, - RelaxedEnvironmentVariableValidation: {}, RelaxedServiceNameValidation: {}, diff --git a/test/compatibility_lifecycle/reference/feature_list.md b/test/compatibility_lifecycle/reference/feature_list.md index effe157b63b..536ef86a909 100644 --- a/test/compatibility_lifecycle/reference/feature_list.md +++ b/test/compatibility_lifecycle/reference/feature_list.md @@ -158,7 +158,6 @@ | RecoverVolumeExpansionFailure | :ballot_box_with_check: 1.32+ | :closed_lock_with_key: 1.34+ | 1.23–1.31 | 1.32–1.33 | 1.34– | | | [code](https://cs.k8s.io/?q=%5CbRecoverVolumeExpansionFailure%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbRecoverVolumeExpansionFailure%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | RecursiveReadOnlyMounts | :ballot_box_with_check: 1.31+ | :closed_lock_with_key: 1.33+ | 1.30 | 1.31–1.32 | 1.33– | | | [code](https://cs.k8s.io/?q=%5CbRecursiveReadOnlyMounts%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbRecursiveReadOnlyMounts%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | ReduceDefaultCrashLoopBackOffDecay | | | 1.33– | | | | | [code](https://cs.k8s.io/?q=%5CbReduceDefaultCrashLoopBackOffDecay%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbReduceDefaultCrashLoopBackOffDecay%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | -| RelaxedDNSSearchValidation | :ballot_box_with_check: 1.33+ | :closed_lock_with_key: 1.34+ | 1.32 | 1.33 | 1.34– | | | [code](https://cs.k8s.io/?q=%5CbRelaxedDNSSearchValidation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbRelaxedDNSSearchValidation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | RelaxedEnvironmentVariableValidation | :ballot_box_with_check: 1.32+ | :closed_lock_with_key: 1.34+ | 1.30–1.31 | 1.32–1.33 | 1.34– | | | [code](https://cs.k8s.io/?q=%5CbRelaxedEnvironmentVariableValidation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbRelaxedEnvironmentVariableValidation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | RelaxedServiceNameValidation | :ballot_box_with_check: 1.36+ | | 1.34–1.35 | 1.36– | | | | [code](https://cs.k8s.io/?q=%5CbRelaxedServiceNameValidation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbRelaxedServiceNameValidation%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | ReloadKubeletClientCAFile | :ballot_box_with_check: 1.36+ | | | 1.36– | | | | [code](https://cs.k8s.io/?q=%5CbReloadKubeletClientCAFile%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbReloadKubeletClientCAFile%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | diff --git a/test/compatibility_lifecycle/reference/versioned_feature_list.yaml b/test/compatibility_lifecycle/reference/versioned_feature_list.yaml index 38f7604a5ba..a4c03665dab 100644 --- a/test/compatibility_lifecycle/reference/versioned_feature_list.yaml +++ b/test/compatibility_lifecycle/reference/versioned_feature_list.yaml @@ -1477,20 +1477,6 @@ lockToDefault: false preRelease: Alpha version: "1.33" -- name: RelaxedDNSSearchValidation - versionedSpecs: - - default: false - lockToDefault: false - preRelease: Alpha - version: "1.32" - - default: true - lockToDefault: false - preRelease: Beta - version: "1.33" - - default: true - lockToDefault: true - preRelease: GA - version: "1.34" - name: RelaxedEnvironmentVariableValidation versionedSpecs: - default: false diff --git a/test/integration/pods/pods_test.go b/test/integration/pods/pods_test.go index 13e60f61724..2f79ae1413b 100644 --- a/test/integration/pods/pods_test.go +++ b/test/integration/pods/pods_test.go @@ -1403,11 +1403,9 @@ func TestMutablePodSchedulingDirectives(t *testing.T) { } } -// Test disabling of RelaxedDNSSearchValidation after a Pod has been created -func TestRelaxedDNSSearchValidation(t *testing.T) { +func TestDNSSearchValidation(t *testing.T) { // Disable ServiceAccount admission plugin as we don't have serviceaccount controller running. - server := kubeapiservertesting.StartTestServerOrDie(t, nil, - append(framework.DefaultTestServerFlags(), "--emulated-version=1.32"), framework.SharedEtcd()) + server := kubeapiservertesting.StartTestServerOrDie(t, nil, framework.DefaultTestServerFlags(), framework.SharedEtcd()) defer server.TearDownFn() client := clientset.NewForConfigOrDie(server.ClientConfig) @@ -1432,75 +1430,48 @@ func TestRelaxedDNSSearchValidation(t *testing.T) { } cases := []struct { - name string - original *v1.PodDNSConfig - valid bool - featureGateEnabled bool - update bool + name string + original *v1.PodDNSConfig + valid bool }{ { - name: "new pod with underscore - feature gate enabled", - original: &v1.PodDNSConfig{Searches: []string{"_sip._tcp.abc_d.example.com"}}, - valid: true, - featureGateEnabled: true, + name: "leading underscore", + original: &v1.PodDNSConfig{Searches: []string{"_sip._tcp.abc_d.example.com"}}, + valid: true, }, { - name: "new pod with dot - feature gate enabled", - original: &v1.PodDNSConfig{Searches: []string{"."}}, - valid: true, - featureGateEnabled: true, - }, - - { - name: "new pod without underscore - feature gate enabled", - original: &v1.PodDNSConfig{Searches: []string{"example.com"}}, - valid: true, - featureGateEnabled: true, + name: "single dot", + original: &v1.PodDNSConfig{Searches: []string{"."}}, + valid: true, }, { - name: "new pod with underscore - feature gate disabled", - original: &v1.PodDNSConfig{Searches: []string{"_sip._tcp.abc_d.example.com"}}, - valid: false, - featureGateEnabled: false, + name: "without underscore", + original: &v1.PodDNSConfig{Searches: []string{"example.com"}}, + valid: true, }, { - name: "new pod with dot - feature gate disabled", - original: &v1.PodDNSConfig{Searches: []string{"."}}, - valid: false, - featureGateEnabled: false, + name: "double dot", + original: &v1.PodDNSConfig{Searches: []string{".."}}, + valid: false, }, { - name: "new pod without underscore - feature gate disabled", - original: &v1.PodDNSConfig{Searches: []string{"example.com"}}, - valid: true, - featureGateEnabled: false, + name: "leading unicode", + original: &v1.PodDNSConfig{Searches: []string{"☃.example.com"}}, + valid: false, }, } for _, tc := range cases { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RelaxedDNSSearchValidation, tc.featureGateEnabled) pod := testPod("dns") pod.Spec.DNSConfig = tc.original - _, err := client.CoreV1().Pods(ns.Name).Create(context.TODO(), pod, metav1.CreateOptions{}) - if tc.valid && err != nil { - t.Errorf("%v: %v", tc.name, err) - } else if !tc.valid && err == nil { - t.Errorf("%v: unexpected allowed update to ephemeral containers", tc.name) - } - - // Disable gate and perform update - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RelaxedDNSSearchValidation, false) - pod.ObjectMeta.Labels = map[string]string{"label": "value"} - _, err = client.CoreV1().Pods(ns.Name).Update(context.TODO(), pod, metav1.UpdateOptions{}) - - if tc.valid && err != nil { - t.Errorf("%v: failed to update ephemeral containers: %v", tc.name, err) - } else if !tc.valid && err == nil { - t.Errorf("%v: unexpected allowed update to ephemeral containers", tc.name) - } - + _, err := client.CoreV1().Pods(ns.Name).Create(t.Context(), pod, metav1.CreateOptions{}) if tc.valid { + if err != nil { + t.Errorf("%v: %v", tc.name, err) + } integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name) + } else if err == nil { + t.Errorf("%v: unexpected allowed update to ephemeral containers", tc.name) } } }