k8s.io/apiserver@v0.31.1/pkg/util/webhook/validation.go (about) 1 /* 2 Copyright 2018 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 webhook 18 19 import ( 20 "fmt" 21 "net/url" 22 "strings" 23 24 "k8s.io/apimachinery/pkg/util/validation" 25 "k8s.io/apimachinery/pkg/util/validation/field" 26 "k8s.io/client-go/transport" 27 ) 28 29 func ValidateCABundle(fldPath *field.Path, caBundle []byte) field.ErrorList { 30 var allErrors field.ErrorList 31 _, err := transport.TLSConfigFor(&transport.Config{TLS: transport.TLSConfig{CAData: caBundle}}) 32 if err != nil { 33 allErrors = append(allErrors, field.Invalid(fldPath, caBundle, err.Error())) 34 } 35 return allErrors 36 } 37 38 // ValidateWebhookURL validates webhook's URL. 39 func ValidateWebhookURL(fldPath *field.Path, URL string, forceHttps bool) field.ErrorList { 40 var allErrors field.ErrorList 41 const form = "; desired format: https://host[/path]" 42 if u, err := url.Parse(URL); err != nil { 43 allErrors = append(allErrors, field.Required(fldPath, "url must be a valid URL: "+err.Error()+form)) 44 } else { 45 if forceHttps && u.Scheme != "https" { 46 allErrors = append(allErrors, field.Invalid(fldPath, u.Scheme, "'https' is the only allowed URL scheme"+form)) 47 } 48 if len(u.Host) == 0 { 49 allErrors = append(allErrors, field.Invalid(fldPath, u.Host, "host must be specified"+form)) 50 } 51 if u.User != nil { 52 allErrors = append(allErrors, field.Invalid(fldPath, u.User.String(), "user information is not permitted in the URL")) 53 } 54 if len(u.Fragment) != 0 { 55 allErrors = append(allErrors, field.Invalid(fldPath, u.Fragment, "fragments are not permitted in the URL")) 56 } 57 if len(u.RawQuery) != 0 { 58 allErrors = append(allErrors, field.Invalid(fldPath, u.RawQuery, "query parameters are not permitted in the URL")) 59 } 60 } 61 return allErrors 62 } 63 64 func ValidateWebhookService(fldPath *field.Path, namespace, name string, path *string, port int32) field.ErrorList { 65 var allErrors field.ErrorList 66 67 if len(name) == 0 { 68 allErrors = append(allErrors, field.Required(fldPath.Child("name"), "service name is required")) 69 } 70 71 if len(namespace) == 0 { 72 allErrors = append(allErrors, field.Required(fldPath.Child("namespace"), "service namespace is required")) 73 } 74 75 if errs := validation.IsValidPortNum(int(port)); errs != nil { 76 allErrors = append(allErrors, field.Invalid(fldPath.Child("port"), port, "port is not valid: "+strings.Join(errs, ", "))) 77 } 78 79 if path == nil { 80 return allErrors 81 } 82 83 // TODO: replace below with url.Parse + verifying that host is empty? 84 85 urlPath := *path 86 if urlPath == "/" || len(urlPath) == 0 { 87 return allErrors 88 } 89 if urlPath == "//" { 90 allErrors = append(allErrors, field.Invalid(fldPath.Child("path"), urlPath, "segment[0] may not be empty")) 91 return allErrors 92 } 93 94 if !strings.HasPrefix(urlPath, "/") { 95 allErrors = append(allErrors, field.Invalid(fldPath.Child("path"), urlPath, "must start with a '/'")) 96 } 97 98 urlPathToCheck := urlPath[1:] 99 if strings.HasSuffix(urlPathToCheck, "/") { 100 urlPathToCheck = urlPathToCheck[:len(urlPathToCheck)-1] 101 } 102 steps := strings.Split(urlPathToCheck, "/") 103 for i, step := range steps { 104 if len(step) == 0 { 105 allErrors = append(allErrors, field.Invalid(fldPath.Child("path"), urlPath, fmt.Sprintf("segment[%d] may not be empty", i))) 106 continue 107 } 108 failures := validation.IsDNS1123Subdomain(step) 109 for _, failure := range failures { 110 allErrors = append(allErrors, field.Invalid(fldPath.Child("path"), urlPath, fmt.Sprintf("segment[%d]: %v", i, failure))) 111 } 112 } 113 114 return allErrors 115 }