k8s.io/kubernetes@v1.29.3/pkg/kubelet/sysctl/allowlist.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package sysctl 18 19 import ( 20 "fmt" 21 "strings" 22 23 utilsysctl "k8s.io/component-helpers/node/util/sysctl" 24 "k8s.io/kubernetes/pkg/apis/core/validation" 25 policyvalidation "k8s.io/kubernetes/pkg/apis/policy/validation" 26 "k8s.io/kubernetes/pkg/kubelet/lifecycle" 27 ) 28 29 const ( 30 ForbiddenReason = "SysctlForbidden" 31 ) 32 33 // patternAllowlist takes a list of sysctls or sysctl patterns (ending in *) and 34 // checks validity via a sysctl and prefix map, rejecting those which are not known 35 // to be namespaced. 36 type patternAllowlist struct { 37 sysctls map[string]utilsysctl.Namespace 38 prefixes map[string]utilsysctl.Namespace 39 } 40 41 var _ lifecycle.PodAdmitHandler = &patternAllowlist{} 42 43 // NewAllowlist creates a new Allowlist from a list of sysctls and sysctl pattern (ending in *). 44 func NewAllowlist(patterns []string) (*patternAllowlist, error) { 45 w := &patternAllowlist{ 46 sysctls: map[string]utilsysctl.Namespace{}, 47 prefixes: map[string]utilsysctl.Namespace{}, 48 } 49 50 for _, s := range patterns { 51 if !policyvalidation.IsValidSysctlPattern(s) { 52 return nil, fmt.Errorf("sysctl %q must have at most %d characters and match regex %s", 53 s, 54 validation.SysctlMaxLength, 55 policyvalidation.SysctlContainSlashPatternFmt, 56 ) 57 } 58 ns, sysctlOrPrefix, prefixed := utilsysctl.GetNamespace(s) 59 if ns == utilsysctl.UnknownNamespace { 60 return nil, fmt.Errorf("the sysctls %q are not known to be namespaced", sysctlOrPrefix) 61 } 62 if prefixed { 63 w.prefixes[sysctlOrPrefix] = ns 64 } else { 65 w.sysctls[sysctlOrPrefix] = ns 66 } 67 } 68 return w, nil 69 } 70 71 // validateSysctl checks that a sysctl is allowlisted because it is known 72 // to be namespaced by the Linux kernel. Note that being allowlisted is required, but not 73 // sufficient: the container runtime might have a stricter check and refuse to launch a pod. 74 // 75 // The parameters hostNet and hostIPC are used to forbid sysctls for pod sharing the 76 // respective namespaces with the host. This check is only possible for sysctls on 77 // the static default allowlist, not those on the custom allowlist provided by the admin. 78 func (w *patternAllowlist) validateSysctl(sysctl string, hostNet, hostIPC bool) error { 79 sysctl = utilsysctl.NormalizeName(sysctl) 80 nsErrorFmt := "%q not allowed with host %s enabled" 81 if ns, found := w.sysctls[sysctl]; found { 82 if ns == utilsysctl.IPCNamespace && hostIPC { 83 return fmt.Errorf(nsErrorFmt, sysctl, ns) 84 } 85 if ns == utilsysctl.NetNamespace && hostNet { 86 return fmt.Errorf(nsErrorFmt, sysctl, ns) 87 } 88 return nil 89 } 90 for p, ns := range w.prefixes { 91 if strings.HasPrefix(sysctl, p) { 92 if ns == utilsysctl.IPCNamespace && hostIPC { 93 return fmt.Errorf(nsErrorFmt, sysctl, ns) 94 } 95 if ns == utilsysctl.NetNamespace && hostNet { 96 return fmt.Errorf(nsErrorFmt, sysctl, ns) 97 } 98 return nil 99 } 100 } 101 return fmt.Errorf("%q not allowlisted", sysctl) 102 } 103 104 // Admit checks that all sysctls given in pod's security context 105 // are valid according to the allowlist. 106 func (w *patternAllowlist) Admit(attrs *lifecycle.PodAdmitAttributes) lifecycle.PodAdmitResult { 107 pod := attrs.Pod 108 if pod.Spec.SecurityContext == nil || len(pod.Spec.SecurityContext.Sysctls) == 0 { 109 return lifecycle.PodAdmitResult{ 110 Admit: true, 111 } 112 } 113 114 for _, s := range pod.Spec.SecurityContext.Sysctls { 115 if err := w.validateSysctl(s.Name, pod.Spec.HostNetwork, pod.Spec.HostIPC); err != nil { 116 return lifecycle.PodAdmitResult{ 117 Admit: false, 118 Reason: ForbiddenReason, 119 Message: fmt.Sprintf("forbidden sysctl: %v", err), 120 } 121 } 122 } 123 124 return lifecycle.PodAdmitResult{ 125 Admit: true, 126 } 127 }