k8s.io/apiserver@v0.31.1/pkg/apis/audit/validation/validation.go (about)

     1  /*
     2  Copyright 2017 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 validation
    18  
    19  import (
    20  	"strings"
    21  
    22  	"k8s.io/apimachinery/pkg/api/validation"
    23  	"k8s.io/apimachinery/pkg/util/validation/field"
    24  	"k8s.io/apiserver/pkg/apis/audit"
    25  )
    26  
    27  // ValidatePolicy validates the audit policy
    28  func ValidatePolicy(policy *audit.Policy) field.ErrorList {
    29  	var allErrs field.ErrorList
    30  	allErrs = append(allErrs, validateOmitStages(policy.OmitStages, field.NewPath("omitStages"))...)
    31  	rulePath := field.NewPath("rules")
    32  	for i, rule := range policy.Rules {
    33  		allErrs = append(allErrs, validatePolicyRule(rule, rulePath.Index(i))...)
    34  	}
    35  	return allErrs
    36  }
    37  
    38  func validatePolicyRule(rule audit.PolicyRule, fldPath *field.Path) field.ErrorList {
    39  	var allErrs field.ErrorList
    40  	allErrs = append(allErrs, validateLevel(rule.Level, fldPath.Child("level"))...)
    41  	allErrs = append(allErrs, validateNonResourceURLs(rule.NonResourceURLs, fldPath.Child("nonResourceURLs"))...)
    42  	allErrs = append(allErrs, validateResources(rule.Resources, fldPath.Child("resources"))...)
    43  	allErrs = append(allErrs, validateOmitStages(rule.OmitStages, fldPath.Child("omitStages"))...)
    44  
    45  	if len(rule.NonResourceURLs) > 0 {
    46  		if len(rule.Resources) > 0 || len(rule.Namespaces) > 0 {
    47  			allErrs = append(allErrs, field.Invalid(fldPath.Child("nonResourceURLs"), rule.NonResourceURLs, "rules cannot apply to both regular resources and non-resource URLs"))
    48  		}
    49  	}
    50  
    51  	return allErrs
    52  }
    53  
    54  var validLevels = []string{
    55  	string(audit.LevelNone),
    56  	string(audit.LevelMetadata),
    57  	string(audit.LevelRequest),
    58  	string(audit.LevelRequestResponse),
    59  }
    60  
    61  var validOmitStages = []string{
    62  	string(audit.StageRequestReceived),
    63  	string(audit.StageResponseStarted),
    64  	string(audit.StageResponseComplete),
    65  	string(audit.StagePanic),
    66  }
    67  
    68  func validateLevel(level audit.Level, fldPath *field.Path) field.ErrorList {
    69  	switch level {
    70  	case audit.LevelNone, audit.LevelMetadata, audit.LevelRequest, audit.LevelRequestResponse:
    71  		return nil
    72  	case "":
    73  		return field.ErrorList{field.Required(fldPath, "")}
    74  	default:
    75  		return field.ErrorList{field.NotSupported(fldPath, level, validLevels)}
    76  	}
    77  }
    78  
    79  func validateNonResourceURLs(urls []string, fldPath *field.Path) field.ErrorList {
    80  	var allErrs field.ErrorList
    81  	for i, url := range urls {
    82  		if url == "*" {
    83  			continue
    84  		}
    85  
    86  		if !strings.HasPrefix(url, "/") {
    87  			allErrs = append(allErrs, field.Invalid(fldPath.Index(i), url, "non-resource URL rules must begin with a '/' character"))
    88  		}
    89  
    90  		if url != "" && strings.ContainsRune(url[:len(url)-1], '*') {
    91  			allErrs = append(allErrs, field.Invalid(fldPath.Index(i), url, "non-resource URL wildcards '*' must be the final character of the rule"))
    92  		}
    93  	}
    94  	return allErrs
    95  }
    96  
    97  func validateResources(groupResources []audit.GroupResources, fldPath *field.Path) field.ErrorList {
    98  	var allErrs field.ErrorList
    99  	for _, groupResource := range groupResources {
   100  		// The empty string represents the core API group.
   101  		if len(groupResource.Group) != 0 {
   102  			// Group names must be lower case and be valid DNS subdomains.
   103  			// reference: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md
   104  			// an error is returned for group name like rbac.authorization.k8s.io/v1beta1
   105  			// rbac.authorization.k8s.io is the valid one
   106  			if msgs := validation.NameIsDNSSubdomain(groupResource.Group, false); len(msgs) != 0 {
   107  				allErrs = append(allErrs, field.Invalid(fldPath.Child("group"), groupResource.Group, strings.Join(msgs, ",")))
   108  			}
   109  		}
   110  
   111  		if len(groupResource.ResourceNames) > 0 && len(groupResource.Resources) == 0 {
   112  			allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceNames"), groupResource.ResourceNames, "using resourceNames requires at least one resource"))
   113  		}
   114  	}
   115  	return allErrs
   116  }
   117  
   118  func validateOmitStages(omitStages []audit.Stage, fldPath *field.Path) field.ErrorList {
   119  	var allErrs field.ErrorList
   120  	for i, stage := range omitStages {
   121  		valid := false
   122  		for _, validOmitStage := range validOmitStages {
   123  			if string(stage) == validOmitStage {
   124  				valid = true
   125  				break
   126  			}
   127  		}
   128  		if !valid {
   129  			allErrs = append(allErrs, field.Invalid(fldPath.Index(i), string(stage), "allowed stages are "+strings.Join(validOmitStages, ",")))
   130  		}
   131  	}
   132  	return allErrs
   133  }