use sysctl utils to do pod spec validation

Signed-off-by: Paco Xu <paco.xu@daocloud.io>
This commit is contained in:
Paco Xu 2023-06-16 15:50:36 +08:00
parent 11de9543ee
commit 9a8ccdebc5
4 changed files with 66 additions and 10 deletions

View File

@ -44,6 +44,7 @@ import (
"k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
utilsysctl "k8s.io/component-helpers/node/util/sysctl"
schedulinghelper "k8s.io/component-helpers/scheduling/corev1" schedulinghelper "k8s.io/component-helpers/scheduling/corev1"
kubeletapis "k8s.io/kubelet/pkg/apis" kubeletapis "k8s.io/kubelet/pkg/apis"
apiservice "k8s.io/kubernetes/pkg/api/service" 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 // The parameters hostNet and hostIPC are used to forbid sysctls for pod sharing the
// respective namespaces with the host. // respective namespaces with the host.
if hostNetwork && strings.HasPrefix(s.Name, "net") { if !isValidSysctlWithHostNetIPC(s.Name, hostNetwork, hostIPC) {
allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("name"), s.Name, "sysctl not allowed with host net enabled")) allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("name"), s.Name, "sysctl not allowed with host net/ipc 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"))
} }
names[s.Name] = struct{}{} names[s.Name] = struct{}{}
@ -4587,6 +4585,32 @@ func validateSysctls(sysctls []core.Sysctl, fldPath *field.Path, hostNetwork, ho
return allErrs 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, // validatePodSpecSecurityContext verifies the SecurityContext of a PodSpec,
// whether that is defined in a Pod or in an embedded PodSpec (e.g. a // whether that is defined in a Pod or in an embedded PodSpec (e.g. a
// Deployment's pod template). // Deployment's pod template).

View File

@ -21491,6 +21491,16 @@ func TestValidateSysctls(t *testing.T) {
"_invalid", "_invalid",
} }
invalidWithHostNet := []string{
"net.ipv4.conf.enp3s0/200.forwarding",
"net/ipv4/conf/enp3s0.200/forwarding",
}
invalidWithHostIPC := []string{
"kernel.shmmax",
"kernel.msgmax",
}
duplicates := []string{ duplicates := []string{
"kernel.shmmax", "kernel.shmmax",
"kernel.shmmax", "kernel.shmmax",
@ -21500,7 +21510,7 @@ func TestValidateSysctls(t *testing.T) {
for i, sysctl := range valid { for i, sysctl := range valid {
sysctls[i].Name = sysctl sysctls[i].Name = sysctl
} }
errs := validateSysctls(sysctls, field.NewPath("foo")) errs := validateSysctls(sysctls, field.NewPath("foo"), false, false)
if len(errs) != 0 { if len(errs) != 0 {
t.Errorf("unexpected validation errors: %v", errs) t.Errorf("unexpected validation errors: %v", errs)
} }
@ -21509,7 +21519,7 @@ func TestValidateSysctls(t *testing.T) {
for i, sysctl := range invalid { for i, sysctl := range invalid {
sysctls[i].Name = sysctl sysctls[i].Name = sysctl
} }
errs = validateSysctls(sysctls, field.NewPath("foo")) errs = validateSysctls(sysctls, field.NewPath("foo"), false, false)
if len(errs) != 2 { if len(errs) != 2 {
t.Errorf("expected 2 validation errors. Got: %v", errs) t.Errorf("expected 2 validation errors. Got: %v", errs)
} else { } else {
@ -21525,12 +21535,30 @@ func TestValidateSysctls(t *testing.T) {
for i, sysctl := range duplicates { for i, sysctl := range duplicates {
sysctls[i].Name = sysctl sysctls[i].Name = sysctl
} }
errs = validateSysctls(sysctls, field.NewPath("foo")) errs = validateSysctls(sysctls, field.NewPath("foo"), false, false)
if len(errs) != 1 { if len(errs) != 1 {
t.Errorf("unexpected validation errors: %v", errs) t.Errorf("unexpected validation errors: %v", errs)
} else if errs[0].Type != field.ErrorTypeDuplicate { } else if errs[0].Type != field.ErrorTypeDuplicate {
t.Errorf("expected error type %v, got %v", field.ErrorTypeDuplicate, errs[0].Type) 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 { func newNodeNameEndpoint(nodeName string) *core.Endpoints {

View File

@ -6,6 +6,7 @@ go 1.21.3
require ( require (
github.com/google/go-cmp v0.6.0 github.com/google/go-cmp v0.6.0
github.com/stretchr/testify v1.8.4
k8s.io/api v0.0.0 k8s.io/api v0.0.0
k8s.io/apimachinery v0.0.0 k8s.io/apimachinery v0.0.0
k8s.io/client-go 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/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // 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/net v0.17.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/sys v0.13.0 // indirect golang.org/x/sys v0.13.0 // indirect

View File

@ -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 See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package sysctl package sysctl
import ( import (
"github.com/stretchr/testify/assert"
"testing" "testing"
"github.com/stretchr/testify/assert"
) )
// TestConvertSysctlVariableToDotsSeparator tests whether the sysctl variable // TestConvertSysctlVariableToDotsSeparator tests whether the sysctl variable