KEP-4427: Remove RelaxedDNSSearchValidation feature gate

This commit is contained in:
Adrian Moisey 2026-05-21 08:35:54 +02:00
parent e09c6f3997
commit cbc8e77b2a
No known key found for this signature in database
GPG key ID: 41AE4AE32747C7CF
7 changed files with 68 additions and 245 deletions

View file

@ -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) {

View file

@ -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.

View file

@ -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")
}

View file

@ -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: {},

View file

@ -158,7 +158,6 @@
| RecoverVolumeExpansionFailure | :ballot_box_with_check: 1.32+ | :closed_lock_with_key: 1.34+ | 1.231.31 | 1.321.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.311.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.301.31 | 1.321.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.341.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) |

View file

@ -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

View file

@ -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)
}
}
}