move sysctl namespace and some funcs to component helpers util

Signed-off-by: Paco Xu <paco.xu@daocloud.io>
This commit is contained in:
Paco Xu 2023-06-16 15:50:18 +08:00
parent 4321652d13
commit 11de9543ee
7 changed files with 105 additions and 84 deletions

View File

@ -20,6 +20,7 @@ import (
"fmt"
"strings"
utilsysctl "k8s.io/component-helpers/node/util/sysctl"
"k8s.io/kubernetes/pkg/apis/core/validation"
policyvalidation "k8s.io/kubernetes/pkg/apis/policy/validation"
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
@ -33,8 +34,8 @@ const (
// checks validity via a sysctl and prefix map, rejecting those which are not known
// to be namespaced.
type patternAllowlist struct {
sysctls map[string]Namespace
prefixes map[string]Namespace
sysctls map[string]utilsysctl.Namespace
prefixes map[string]utilsysctl.Namespace
}
var _ lifecycle.PodAdmitHandler = &patternAllowlist{}
@ -42,8 +43,8 @@ var _ lifecycle.PodAdmitHandler = &patternAllowlist{}
// NewAllowlist creates a new Allowlist from a list of sysctls and sysctl pattern (ending in *).
func NewAllowlist(patterns []string) (*patternAllowlist, error) {
w := &patternAllowlist{
sysctls: map[string]Namespace{},
prefixes: map[string]Namespace{},
sysctls: map[string]utilsysctl.Namespace{},
prefixes: map[string]utilsysctl.Namespace{},
}
for _, s := range patterns {
@ -54,17 +55,17 @@ func NewAllowlist(patterns []string) (*patternAllowlist, error) {
policyvalidation.SysctlContainSlashPatternFmt,
)
}
s = convertSysctlVariableToDotsSeparator(s)
s = utilsysctl.ConvertSysctlVariableToDotsSeparator(s)
if strings.HasSuffix(s, "*") {
prefix := s[:len(s)-1]
ns := NamespacedBy(prefix)
if ns == unknownNamespace {
ns := utilsysctl.NamespacedBy(prefix)
if ns == utilsysctl.UnknownNamespace {
return nil, fmt.Errorf("the sysctls %q are not known to be namespaced", s)
}
w.prefixes[prefix] = ns
} else {
ns := NamespacedBy(s)
if ns == unknownNamespace {
ns := utilsysctl.NamespacedBy(s)
if ns == utilsysctl.UnknownNamespace {
return nil, fmt.Errorf("the sysctl %q are not known to be namespaced", s)
}
w.sysctls[s] = ns
@ -81,23 +82,23 @@ func NewAllowlist(patterns []string) (*patternAllowlist, error) {
// respective namespaces with the host. This check is only possible for sysctls on
// the static default allowlist, not those on the custom allowlist provided by the admin.
func (w *patternAllowlist) validateSysctl(sysctl string, hostNet, hostIPC bool) error {
sysctl = convertSysctlVariableToDotsSeparator(sysctl)
sysctl = utilsysctl.ConvertSysctlVariableToDotsSeparator(sysctl)
nsErrorFmt := "%q not allowed with host %s enabled"
if ns, found := w.sysctls[sysctl]; found {
if ns == ipcNamespace && hostIPC {
if ns == utilsysctl.IpcNamespace && hostIPC {
return fmt.Errorf(nsErrorFmt, sysctl, ns)
}
if ns == netNamespace && hostNet {
if ns == utilsysctl.NetNamespace && hostNet {
return fmt.Errorf(nsErrorFmt, sysctl, ns)
}
return nil
}
for p, ns := range w.prefixes {
if strings.HasPrefix(sysctl, p) {
if ns == ipcNamespace && hostIPC {
if ns == utilsysctl.IpcNamespace && hostIPC {
return fmt.Errorf(nsErrorFmt, sysctl, ns)
}
if ns == netNamespace && hostNet {
if ns == utilsysctl.NetNamespace && hostNet {
return fmt.Errorf(nsErrorFmt, sysctl, ns)
}
return nil

View File

@ -17,39 +17,10 @@ limitations under the License.
package sysctl
import (
"strings"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
utilsysctl "k8s.io/component-helpers/node/util/sysctl"
)
// convertSysctlVariableToDotsSeparator can return sysctl variables in dots separator format.
// The '/' separator is also accepted in place of a '.'.
// Convert the sysctl variables to dots separator format for validation.
// More info:
//
// https://man7.org/linux/man-pages/man8/sysctl.8.html
// https://man7.org/linux/man-pages/man5/sysctl.d.5.html
func convertSysctlVariableToDotsSeparator(val string) string {
if val == "" {
return val
}
firstSepIndex := strings.IndexAny(val, "./")
if firstSepIndex == -1 || val[firstSepIndex] == '.' {
return val
}
f := func(r rune) rune {
switch r {
case '.':
return '/'
case '/':
return '.'
}
return r
}
return strings.Map(f, val)
}
// ConvertPodSysctlsVariableToDotsSeparator converts sysctls variable in the Pod.Spec.SecurityContext.Sysctls slice into a dot as a separator
// according to the linux sysctl conversion rules.
// see https://man7.org/linux/man-pages/man5/sysctl.d.5.html for more details.
@ -58,7 +29,7 @@ func ConvertPodSysctlsVariableToDotsSeparator(securityContext *v1.PodSecurityCon
return
}
for i, sysctl := range securityContext.Sysctls {
securityContext.Sysctls[i].Name = convertSysctlVariableToDotsSeparator(sysctl.Name)
securityContext.Sysctls[i].Name = utilsysctl.ConvertSysctlVariableToDotsSeparator(sysctl.Name)
}
return
}

View File

@ -24,29 +24,6 @@ import (
"github.com/stretchr/testify/assert"
)
// TestConvertSysctlVariableToDotsSeparator tests whether the sysctl variable
// can be correctly converted to a dot as a separator.
func TestConvertSysctlVariableToDotsSeparator(t *testing.T) {
type testCase struct {
in string
out string
}
valid := []testCase{
{in: "kernel.shm_rmid_forced", out: "kernel.shm_rmid_forced"},
{in: "kernel/shm_rmid_forced", out: "kernel.shm_rmid_forced"},
{in: "net.ipv4.conf.eno2/100.rp_filter", out: "net.ipv4.conf.eno2/100.rp_filter"},
{in: "net/ipv4/conf/eno2.100/rp_filter", out: "net.ipv4.conf.eno2/100.rp_filter"},
{in: "net/ipv4/ip_local_port_range", out: "net.ipv4.ip_local_port_range"},
{in: "kernel/msgmax", out: "kernel.msgmax"},
{in: "kernel/sem", out: "kernel.sem"},
}
for _, test := range valid {
convertSysctlVal := convertSysctlVariableToDotsSeparator(test.in)
assert.Equalf(t, test.out, convertSysctlVal, "The sysctl variable was not converted correctly. got: %s, want: %s", convertSysctlVal, test.out)
}
}
// TestConvertPodSysctlsVariableToDotsSeparator tests whether the sysctls variable
// can be correctly converted to a dot as a separator.
func TestConvertPodSysctlsVariableToDotsSeparator(t *testing.T) {

View File

@ -1,5 +1,5 @@
/*
Copyright 2016 The Kubernetes Authors.
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -25,24 +25,24 @@ type Namespace string
const (
// the Linux IPC namespace
ipcNamespace = Namespace("ipc")
IpcNamespace = Namespace("ipc")
// the network namespace
netNamespace = Namespace("net")
NetNamespace = Namespace("net")
// the zero value if no namespace is known
unknownNamespace = Namespace("")
UnknownNamespace = Namespace("")
)
var namespaces = map[string]Namespace{
"kernel.sem": ipcNamespace,
"kernel.sem": IpcNamespace,
}
var prefixNamespaces = map[string]Namespace{
"kernel.shm": ipcNamespace,
"kernel.msg": ipcNamespace,
"fs.mqueue.": ipcNamespace,
"net.": netNamespace,
"kernel.shm": IpcNamespace,
"kernel.msg": IpcNamespace,
"fs.mqueue.": IpcNamespace,
"net.": NetNamespace,
}
// NamespacedBy returns the namespace of the Linux kernel for a sysctl, or
@ -56,5 +56,5 @@ func NamespacedBy(val string) Namespace {
return ns
}
}
return unknownNamespace
return UnknownNamespace
}

View File

@ -1,5 +1,5 @@
/*
Copyright 2016 The Kubernetes Authors.
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -22,10 +22,10 @@ import (
func TestNamespacedBy(t *testing.T) {
tests := map[string]Namespace{
"kernel.shm_rmid_forced": ipcNamespace,
"net.a.b.c": netNamespace,
"fs.mqueue.a.b.c": ipcNamespace,
"foo": unknownNamespace,
"kernel.shm_rmid_forced": IpcNamespace,
"net.a.b.c": NetNamespace,
"fs.mqueue.a.b.c": IpcNamespace,
"foo": UnknownNamespace,
}
for sysctl, ns := range tests {

View File

@ -98,3 +98,31 @@ func (*procSysctl) GetSysctl(sysctl string) (int, error) {
func (*procSysctl) SetSysctl(sysctl string, newVal int) error {
return os.WriteFile(path.Join(sysctlBase, sysctl), []byte(strconv.Itoa(newVal)), 0640)
}
// ConvertSysctlVariableToDotsSeparator can return sysctl variables in dots separator format.
// The '/' separator is also accepted in place of a '.'.
// Convert the sysctl variables to dots separator format for validation.
// More info:
//
// https://man7.org/linux/man-pages/man8/sysctl.8.html
// https://man7.org/linux/man-pages/man5/sysctl.d.5.html
func ConvertSysctlVariableToDotsSeparator(val string) string {
if val == "" {
return val
}
firstSepIndex := strings.IndexAny(val, "./")
if firstSepIndex == -1 || val[firstSepIndex] == '.' {
return val
}
f := func(r rune) rune {
switch r {
case '.':
return '/'
case '/':
return '.'
}
return r
}
return strings.Map(f, val)
}

View File

@ -0,0 +1,44 @@
/*
Copyright 2023 The Kubernetes Authors.
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
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
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"
)
// TestConvertSysctlVariableToDotsSeparator tests whether the sysctl variable
// can be correctly converted to a dot as a separator.
func TestConvertSysctlVariableToDotsSeparator(t *testing.T) {
type testCase struct {
in string
out string
}
valid := []testCase{
{in: "kernel.shm_rmid_forced", out: "kernel.shm_rmid_forced"},
{in: "kernel/shm_rmid_forced", out: "kernel.shm_rmid_forced"},
{in: "net.ipv4.conf.eno2/100.rp_filter", out: "net.ipv4.conf.eno2/100.rp_filter"},
{in: "net/ipv4/conf/eno2.100/rp_filter", out: "net.ipv4.conf.eno2/100.rp_filter"},
{in: "net/ipv4/ip_local_port_range", out: "net.ipv4.ip_local_port_range"},
{in: "kernel/msgmax", out: "kernel.msgmax"},
{in: "kernel/sem", out: "kernel.sem"},
}
for _, test := range valid {
convertSysctlVal := ConvertSysctlVariableToDotsSeparator(test.in)
assert.Equalf(t, test.out, convertSysctlVal, "The sysctl variable was not converted correctly. got: %s, want: %s", convertSysctlVal, test.out)
}
}