...

Source file src/k8s.io/kubernetes/pkg/kubelet/sysctl/allowlist.go

Documentation: k8s.io/kubernetes/pkg/kubelet/sysctl

     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  }
   128  

View as plain text