diff --git a/pkg/securitycontextconstraints/sysctl/mustmatchpatterns.go b/pkg/securitycontextconstraints/sysctl/mustmatchpatterns.go index bd307b424..576ec7566 100644 --- a/pkg/securitycontextconstraints/sysctl/mustmatchpatterns.go +++ b/pkg/securitycontextconstraints/sysctl/mustmatchpatterns.go @@ -17,12 +17,17 @@ limitations under the License. package sysctl import ( + "context" "fmt" "slices" "strings" + "time" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/version" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" "k8s.io/klog/v2" api "k8s.io/kubernetes/pkg/apis/core" utilkernel "k8s.io/kubernetes/pkg/util/kernel" @@ -71,7 +76,7 @@ var newerSysctls = []sysctl{ // - 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 { - return getSafeSysctlAllowlist(utilkernel.GetVersion) + return getSafeSysctlAllowlist(getWorkerNodesMinKernelVersion) } // getSafeSysctlAllowlist returns the list of safe sysctls that can be used. @@ -79,12 +84,14 @@ func SafeSysctlAllowlist() []string { // 1. Always return the legacy list (known safe sysctls from previous releases). // 2. Conditionally add newer sysctls only if the detected kernel version // is at least as new as required. +// +// Use worker node kernel versions since sysctls affect pods running there, not the API server func getSafeSysctlAllowlist(getVersion func() (*version.Version, error)) []string { safeSysctlAllowlist := slices.Clone(legacySafeSysctls) kernelVersion, err := getVersion() if err != nil { - klog.Error(err, "failed to get kernel version, falling back to legacy safe sysctl list") + klog.Error(err, "failed to get the worker node kernel version, falling back to legacy safe sysctl list") return safeSysctlAllowlist } @@ -183,3 +190,47 @@ func (s *mustMatchPatterns) Validate(pod *api.Pod) field.ErrorList { return allErrs } + +// getWorkerNodesMinKernelVersion returns the minimum kernel version across all the +// worker nodes +func getWorkerNodesMinKernelVersion() (*version.Version, error) { + clientConfig, err := rest.InClusterConfig() + if err != nil { + return nil, fmt.Errorf("failed to get in-cluster config: %v", err) + } + + kubeClient, err := kubernetes.NewForConfig(clientConfig) + if err != nil { + return nil, fmt.Errorf("failed to create kubernetes client: %v", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + nodes, err := kubeClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to list nodes: %v", err) + } + + var minVersion *version.Version + for _, node := range nodes.Items { + if _, hasWorkerRole := node.Labels["node-role.kubernetes.io/worker"]; !hasWorkerRole { + continue + } + + nodeVersion, err := version.ParseGeneric(node.Status.NodeInfo.KernelVersion) + if err != nil { + continue + } + + if minVersion == nil || nodeVersion.LessThan(minVersion) { + minVersion = nodeVersion + } + } + + if minVersion == nil { + return nil, fmt.Errorf("no worker nodes found") + } + + return minVersion, nil +}