diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index d62a74aaed7..a4d8aeba47a 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -44,6 +44,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" utilfeature "k8s.io/apiserver/pkg/util/feature" + utilsysctl "k8s.io/component-helpers/node/util/sysctl" schedulinghelper "k8s.io/component-helpers/scheduling/corev1" kubeletapis "k8s.io/kubelet/pkg/apis" apiservice "k8s.io/kubernetes/pkg/api/service" @@ -4575,11 +4576,8 @@ func validateSysctls(sysctls []core.Sysctl, fldPath *field.Path, hostNetwork, ho } // The parameters hostNet and hostIPC are used to forbid sysctls for pod sharing the // respective namespaces with the host. - if hostNetwork && strings.HasPrefix(s.Name, "net") { - allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("name"), s.Name, "sysctl not allowed with host net enabled")) - } - if hostIPC && strings.HasPrefix(s.Name, "ipc") { - allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("name"), s.Name, "sysctl not allowed with host ipc enabled")) + if !isValidSysctlWithHostNetIPC(s.Name, hostNetwork, hostIPC) { + allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("name"), s.Name, "sysctl not allowed with host net/ipc enabled")) } names[s.Name] = struct{}{} @@ -4587,6 +4585,32 @@ func validateSysctls(sysctls []core.Sysctl, fldPath *field.Path, hostNetwork, ho return allErrs } +func isValidSysctlWithHostNetIPC(sysctl string, hostNet, hostIPC bool) bool { + sysctl = utilsysctl.ConvertSysctlVariableToDotsSeparator(sysctl) + var ns utilsysctl.Namespace + if strings.HasSuffix(sysctl, "*") { + prefix := sysctl[:len(sysctl)-1] + ns = utilsysctl.NamespacedBy(prefix) + if ns == utilsysctl.UnknownNamespace { + // don't handle unknown namespace here + return true + } + } else { + ns = utilsysctl.NamespacedBy(sysctl) + if ns == utilsysctl.UnknownNamespace { + // don't handle unknown namespace here + return true + } + } + if ns == utilsysctl.IpcNamespace && hostIPC { + return false + } + if ns == utilsysctl.NetNamespace && hostNet { + return false + } + return true +} + // validatePodSpecSecurityContext verifies the SecurityContext of a PodSpec, // whether that is defined in a Pod or in an embedded PodSpec (e.g. a // Deployment's pod template). diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go index c9f4cb8af4e..70b22540e7f 100644 --- a/pkg/apis/core/validation/validation_test.go +++ b/pkg/apis/core/validation/validation_test.go @@ -21491,6 +21491,16 @@ func TestValidateSysctls(t *testing.T) { "_invalid", } + invalidWithHostNet := []string{ + "net.ipv4.conf.enp3s0/200.forwarding", + "net/ipv4/conf/enp3s0.200/forwarding", + } + + invalidWithHostIPC := []string{ + "kernel.shmmax", + "kernel.msgmax", + } + duplicates := []string{ "kernel.shmmax", "kernel.shmmax", @@ -21500,7 +21510,7 @@ func TestValidateSysctls(t *testing.T) { for i, sysctl := range valid { sysctls[i].Name = sysctl } - errs := validateSysctls(sysctls, field.NewPath("foo")) + errs := validateSysctls(sysctls, field.NewPath("foo"), false, false) if len(errs) != 0 { t.Errorf("unexpected validation errors: %v", errs) } @@ -21509,7 +21519,7 @@ func TestValidateSysctls(t *testing.T) { for i, sysctl := range invalid { sysctls[i].Name = sysctl } - errs = validateSysctls(sysctls, field.NewPath("foo")) + errs = validateSysctls(sysctls, field.NewPath("foo"), false, false) if len(errs) != 2 { t.Errorf("expected 2 validation errors. Got: %v", errs) } else { @@ -21525,12 +21535,30 @@ func TestValidateSysctls(t *testing.T) { for i, sysctl := range duplicates { sysctls[i].Name = sysctl } - errs = validateSysctls(sysctls, field.NewPath("foo")) + errs = validateSysctls(sysctls, field.NewPath("foo"), false, false) if len(errs) != 1 { t.Errorf("unexpected validation errors: %v", errs) } else if errs[0].Type != field.ErrorTypeDuplicate { t.Errorf("expected error type %v, got %v", field.ErrorTypeDuplicate, errs[0].Type) } + + sysctls = make([]core.Sysctl, len(invalidWithHostNet)) + for i, sysctl := range invalidWithHostNet { + sysctls[i].Name = sysctl + } + errs = validateSysctls(sysctls, field.NewPath("foo"), true, false) + if len(errs) != 2 { + t.Errorf("unexpected validation errors: %v", errs) + } + + sysctls = make([]core.Sysctl, len(invalidWithHostIPC)) + for i, sysctl := range invalidWithHostIPC { + sysctls[i].Name = sysctl + } + errs = validateSysctls(sysctls, field.NewPath("foo"), false, true) + if len(errs) != 2 { + t.Errorf("unexpected validation errors: %v", errs) + } } func newNodeNameEndpoint(nodeName string) *core.Endpoints { diff --git a/staging/src/k8s.io/component-helpers/go.mod b/staging/src/k8s.io/component-helpers/go.mod index 927e94259c4..b9ffe28ac75 100644 --- a/staging/src/k8s.io/component-helpers/go.mod +++ b/staging/src/k8s.io/component-helpers/go.mod @@ -6,6 +6,7 @@ go 1.21.3 require ( github.com/google/go-cmp v0.6.0 + github.com/stretchr/testify v1.8.4 k8s.io/api v0.0.0 k8s.io/apimachinery v0.0.0 k8s.io/client-go v0.0.0 @@ -33,6 +34,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sys v0.13.0 // indirect diff --git a/staging/src/k8s.io/component-helpers/node/util/sysctl/sysctl_test.go b/staging/src/k8s.io/component-helpers/node/util/sysctl/sysctl_test.go index 32e27cdf1f8..a4ea1ef9bc1 100644 --- a/staging/src/k8s.io/component-helpers/node/util/sysctl/sysctl_test.go +++ b/staging/src/k8s.io/component-helpers/node/util/sysctl/sysctl_test.go @@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -13,11 +13,13 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ + package sysctl import ( - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) // TestConvertSysctlVariableToDotsSeparator tests whether the sysctl variable