diff --git a/pkg/kubelet/sysctl/safe_sysctls.go b/pkg/kubelet/sysctl/safe_sysctls.go index ea3fcd57d27..615d1176883 100644 --- a/pkg/kubelet/sysctl/safe_sysctls.go +++ b/pkg/kubelet/sysctl/safe_sysctls.go @@ -25,24 +25,33 @@ import ( "k8s.io/kubernetes/pkg/proxy/ipvs" ) -// refer to https://github.com/torvalds/linux/commit/122ff243f5f104194750ecbc76d5946dd1eec934. -const ipLocalReservedPortsMinNamespacedKernelVersion = "3.16" - -var safeSysctls = []string{ - "kernel.shm_rmid_forced", - "net.ipv4.ip_local_port_range", - "net.ipv4.tcp_syncookies", - "net.ipv4.ping_group_range", - "net.ipv4.ip_unprivileged_port_start", +type sysctl struct { + // the name of sysctl + name string + // the minimum kernel version where the sysctl is available + kernel string } -var safeSysctlsIncludeReservedPorts = []string{ - "kernel.shm_rmid_forced", - "net.ipv4.ip_local_port_range", - "net.ipv4.tcp_syncookies", - "net.ipv4.ping_group_range", - "net.ipv4.ip_unprivileged_port_start", - "net.ipv4.ip_local_reserved_ports", +var safeSysctls = []sysctl{ + { + name: "kernel.shm_rmid_forced", + }, { + name: "net.ipv4.ip_local_port_range", + }, { + name: "net.ipv4.tcp_syncookies", + }, { + name: "net.ipv4.ping_group_range", + }, { + name: "net.ipv4.ip_unprivileged_port_start", + }, { + name: "net.ipv4.ip_local_reserved_ports", + // refer to https://github.com/torvalds/linux/commit/122ff243f5f104194750ecbc76d5946dd1eec934. + kernel: "3.16", + }, { + name: "net.ipv4.tcp_keepalive_time", + // refer to https://github.com/torvalds/linux/commit/13b287e8d1cad951634389f85b8c9b816bd3bb1e. + kernel: "4.5", + }, } // SafeSysctlAllowlist returns the allowlist of safe sysctls and safe sysctl patterns (ending in *). @@ -51,19 +60,32 @@ var safeSysctlsIncludeReservedPorts = []string{ // - it is namespaced in the container or the pod // - it is isolated, i.e. has no influence on any other pod on the same node. func SafeSysctlAllowlist() []string { - if goruntime.GOOS == "linux" { - // make sure we're on a new enough kernel that the ip_local_reserved_ports sysctl is namespaced - kernelVersion, err := getKernelVersion() - if err != nil { - klog.ErrorS(err, "Failed to get kernel version, dropping net.ipv4.ip_local_reserved_ports from safe sysctl list") - return safeSysctls + if goruntime.GOOS != "linux" { + return nil + } + return getSafeSysctlAllowlist(getKernelVersion) +} + +func getSafeSysctlAllowlist(getVersion func() (*version.Version, error)) []string { + kernelVersion, err := getVersion() + if err != nil { + klog.ErrorS(err, "failed to get kernel version, unable to determine which sysctls are available") + } + + var safeSysctlAllowlist []string + for _, sc := range safeSysctls { + if sc.kernel == "" { + safeSysctlAllowlist = append(safeSysctlAllowlist, sc.name) + continue } - if kernelVersion.LessThan(version.MustParseGeneric(ipLocalReservedPortsMinNamespacedKernelVersion)) { - klog.ErrorS(nil, "Kernel version is too old, dropping net.ipv4.ip_local_reserved_ports from safe sysctl list", "kernelVersion", kernelVersion) - return safeSysctls + + if kernelVersion != nil && kernelVersion.AtLeast(version.MustParseGeneric(sc.kernel)) { + safeSysctlAllowlist = append(safeSysctlAllowlist, sc.name) + } else { + klog.ErrorS(nil, "kernel version is too small, dropping the sysctl from safe sysctl list", "kernelVersion", kernelVersion, "sysctl", sc.name) } } - return safeSysctlsIncludeReservedPorts + return safeSysctlAllowlist } func getKernelVersion() (*version.Version, error) { @@ -71,6 +93,7 @@ func getKernelVersion() (*version.Version, error) { if err != nil { return nil, fmt.Errorf("failed to get kernel version: %w", err) } + kernelVersion, err := version.ParseGeneric(kernelVersionStr) if err != nil { return nil, fmt.Errorf("failed to parse kernel version: %w", err) diff --git a/pkg/kubelet/sysctl/safe_sysctls_test.go b/pkg/kubelet/sysctl/safe_sysctls_test.go new file mode 100644 index 00000000000..77ab07a70eb --- /dev/null +++ b/pkg/kubelet/sysctl/safe_sysctls_test.go @@ -0,0 +1,85 @@ +/* +Copyright 2016 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 ( + "fmt" + "reflect" + "testing" + + "k8s.io/apimachinery/pkg/util/version" +) + +func Test_getSafeSysctlAllowlist(t *testing.T) { + tests := []struct { + name string + getVersion func() (*version.Version, error) + want []string + }{ + { + name: "failed to get kernelVersion, only return safeSysctls with no kernelVersion limit", + getVersion: func() (*version.Version, error) { + return nil, fmt.Errorf("fork error") + }, + want: []string{ + "kernel.shm_rmid_forced", + "net.ipv4.ip_local_port_range", + "net.ipv4.tcp_syncookies", + "net.ipv4.ping_group_range", + "net.ipv4.ip_unprivileged_port_start", + }, + }, + { + name: "kernelVersion is 3.18.0, return safeSysctls with no kernelVersion limit and net.ipv4.ip_local_reserved_ports", + getVersion: func() (*version.Version, error) { + kernelVersionStr := "3.18.0-957.27.2.el7.x86_64" + return version.ParseGeneric(kernelVersionStr) + }, + want: []string{ + "kernel.shm_rmid_forced", + "net.ipv4.ip_local_port_range", + "net.ipv4.tcp_syncookies", + "net.ipv4.ping_group_range", + "net.ipv4.ip_unprivileged_port_start", + "net.ipv4.ip_local_reserved_ports", + }, + }, + { + name: "kernelVersion is 5.15.0, return safeSysctls with no kernelVersion limit and net.ipv4.ip_local_reserved_ports and net.ipv4.tcp_keepalive_time", + getVersion: func() (*version.Version, error) { + kernelVersionStr := "5.15.0-75-generic" + return version.ParseGeneric(kernelVersionStr) + }, + want: []string{ + "kernel.shm_rmid_forced", + "net.ipv4.ip_local_port_range", + "net.ipv4.tcp_syncookies", + "net.ipv4.ping_group_range", + "net.ipv4.ip_unprivileged_port_start", + "net.ipv4.ip_local_reserved_ports", + "net.ipv4.tcp_keepalive_time", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getSafeSysctlAllowlist(tt.getVersion); !reflect.DeepEqual(got, tt.want) { + t.Errorf("getSafeSysctlAllowlist() = %v, want %v", got, tt.want) + } + }) + } +}