k8s.io/kubernetes@v1.29.3/pkg/apis/certificates/validation/validation.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 validation 18 19 import ( 20 "bytes" 21 "crypto/x509" 22 "encoding/pem" 23 "fmt" 24 25 "github.com/google/go-cmp/cmp" 26 v1 "k8s.io/api/core/v1" 27 apiequality "k8s.io/apimachinery/pkg/api/equality" 28 "k8s.io/apimachinery/pkg/util/sets" 29 "k8s.io/apimachinery/pkg/util/validation/field" 30 utilcert "k8s.io/client-go/util/cert" 31 "k8s.io/kubernetes/pkg/apis/certificates" 32 apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" 33 ) 34 35 var ( 36 // trueConditionTypes is the set of condition types which may only have a status of True if present 37 trueConditionTypes = sets.NewString( 38 string(certificates.CertificateApproved), 39 string(certificates.CertificateDenied), 40 string(certificates.CertificateFailed), 41 ) 42 43 trueStatusOnly = sets.NewString(string(v1.ConditionTrue)) 44 allStatusValues = sets.NewString(string(v1.ConditionTrue), string(v1.ConditionFalse), string(v1.ConditionUnknown)) 45 ) 46 47 type certificateValidationOptions struct { 48 // The following allow modifications only permitted via certain update paths 49 50 // allow populating/modifying Approved/Denied conditions 51 allowSettingApprovalConditions bool 52 // allow populating status.certificate 53 allowSettingCertificate bool 54 55 // allow Approved and Denied conditions to be exist. 56 // we tolerate this when the problem is already present in the persisted object for compatibility. 57 allowBothApprovedAndDenied bool 58 59 // The following are bad things we tolerate for compatibility reasons: 60 // * in requests made via the v1beta1 API 61 // * in update requests where the problem is already present in the persisted object 62 63 // allow modifying status.certificate on an update where the old object has a different certificate 64 allowResettingCertificate bool 65 // allow the legacy-unknown signerName 66 allowLegacySignerName bool 67 // allow conditions with duplicate types 68 allowDuplicateConditionTypes bool 69 // allow conditions with "" types 70 allowEmptyConditionType bool 71 // allow arbitrary content in status.certificate 72 allowArbitraryCertificate bool 73 // allow usages values outside the known set 74 allowUnknownUsages bool 75 // allow duplicate usages values 76 allowDuplicateUsages bool 77 } 78 79 // validateCSR validates the signature and formatting of a base64-wrapped, 80 // PEM-encoded PKCS#10 certificate signing request. If this is invalid, we must 81 // not accept the CSR for further processing. 82 func validateCSR(obj *certificates.CertificateSigningRequest) error { 83 csr, err := certificates.ParseCSR(obj.Spec.Request) 84 if err != nil { 85 return err 86 } 87 // check that the signature is valid 88 return csr.CheckSignature() 89 } 90 91 func validateCertificate(pemData []byte) error { 92 if len(pemData) == 0 { 93 return nil 94 } 95 96 blocks := 0 97 for { 98 block, remainingData := pem.Decode(pemData) 99 if block == nil { 100 break 101 } 102 103 if block.Type != utilcert.CertificateBlockType { 104 return fmt.Errorf("only CERTIFICATE PEM blocks are allowed, found %q", block.Type) 105 } 106 if len(block.Headers) != 0 { 107 return fmt.Errorf("no PEM block headers are permitted") 108 } 109 blocks++ 110 111 certs, err := x509.ParseCertificates(block.Bytes) 112 if err != nil { 113 return err 114 } 115 if len(certs) == 0 { 116 return fmt.Errorf("found CERTIFICATE PEM block containing 0 certificates") 117 } 118 119 pemData = remainingData 120 } 121 122 if blocks == 0 { 123 return fmt.Errorf("must contain at least one CERTIFICATE PEM block") 124 } 125 126 return nil 127 } 128 129 // We don't care what you call your certificate requests. 130 func ValidateCertificateRequestName(name string, prefix bool) []string { 131 return nil 132 } 133 134 func ValidateCertificateSigningRequestCreate(csr *certificates.CertificateSigningRequest) field.ErrorList { 135 opts := getValidationOptions(csr, nil) 136 return validateCertificateSigningRequest(csr, opts) 137 } 138 139 var ( 140 allValidUsages = sets.NewString( 141 string(certificates.UsageSigning), 142 string(certificates.UsageDigitalSignature), 143 string(certificates.UsageContentCommitment), 144 string(certificates.UsageKeyEncipherment), 145 string(certificates.UsageKeyAgreement), 146 string(certificates.UsageDataEncipherment), 147 string(certificates.UsageCertSign), 148 string(certificates.UsageCRLSign), 149 string(certificates.UsageEncipherOnly), 150 string(certificates.UsageDecipherOnly), 151 string(certificates.UsageAny), 152 string(certificates.UsageServerAuth), 153 string(certificates.UsageClientAuth), 154 string(certificates.UsageCodeSigning), 155 string(certificates.UsageEmailProtection), 156 string(certificates.UsageSMIME), 157 string(certificates.UsageIPsecEndSystem), 158 string(certificates.UsageIPsecTunnel), 159 string(certificates.UsageIPsecUser), 160 string(certificates.UsageTimestamping), 161 string(certificates.UsageOCSPSigning), 162 string(certificates.UsageMicrosoftSGC), 163 string(certificates.UsageNetscapeSGC), 164 ) 165 ) 166 167 func validateCertificateSigningRequest(csr *certificates.CertificateSigningRequest, opts certificateValidationOptions) field.ErrorList { 168 isNamespaced := false 169 allErrs := apivalidation.ValidateObjectMeta(&csr.ObjectMeta, isNamespaced, ValidateCertificateRequestName, field.NewPath("metadata")) 170 171 specPath := field.NewPath("spec") 172 err := validateCSR(csr) 173 if err != nil { 174 allErrs = append(allErrs, field.Invalid(specPath.Child("request"), csr.Spec.Request, fmt.Sprintf("%v", err))) 175 } 176 if len(csr.Spec.Usages) == 0 { 177 allErrs = append(allErrs, field.Required(specPath.Child("usages"), "")) 178 } 179 if !opts.allowUnknownUsages { 180 for i, usage := range csr.Spec.Usages { 181 if !allValidUsages.Has(string(usage)) { 182 allErrs = append(allErrs, field.NotSupported(specPath.Child("usages").Index(i), usage, allValidUsages.List())) 183 } 184 } 185 } 186 if !opts.allowDuplicateUsages { 187 seen := make(map[certificates.KeyUsage]bool, len(csr.Spec.Usages)) 188 for i, usage := range csr.Spec.Usages { 189 if seen[usage] { 190 allErrs = append(allErrs, field.Duplicate(specPath.Child("usages").Index(i), usage)) 191 } 192 seen[usage] = true 193 } 194 } 195 if !opts.allowLegacySignerName && csr.Spec.SignerName == certificates.LegacyUnknownSignerName { 196 allErrs = append(allErrs, field.Invalid(specPath.Child("signerName"), csr.Spec.SignerName, "the legacy signerName is not allowed via this API version")) 197 } else { 198 allErrs = append(allErrs, apivalidation.ValidateSignerName(specPath.Child("signerName"), csr.Spec.SignerName)...) 199 } 200 if csr.Spec.ExpirationSeconds != nil && *csr.Spec.ExpirationSeconds < 600 { 201 allErrs = append(allErrs, field.Invalid(specPath.Child("expirationSeconds"), *csr.Spec.ExpirationSeconds, "may not specify a duration less than 600 seconds (10 minutes)")) 202 } 203 allErrs = append(allErrs, validateConditions(field.NewPath("status", "conditions"), csr, opts)...) 204 205 if !opts.allowArbitraryCertificate { 206 if err := validateCertificate(csr.Status.Certificate); err != nil { 207 allErrs = append(allErrs, field.Invalid(field.NewPath("status", "certificate"), "<certificate data>", err.Error())) 208 } 209 } 210 211 return allErrs 212 } 213 214 func validateConditions(fldPath *field.Path, csr *certificates.CertificateSigningRequest, opts certificateValidationOptions) field.ErrorList { 215 allErrs := field.ErrorList{} 216 217 seenTypes := map[certificates.RequestConditionType]bool{} 218 hasApproved := false 219 hasDenied := false 220 221 for i, c := range csr.Status.Conditions { 222 223 if !opts.allowEmptyConditionType { 224 if len(c.Type) == 0 { 225 allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("type"), "")) 226 } 227 } 228 229 allowedStatusValues := allStatusValues 230 if trueConditionTypes.Has(string(c.Type)) { 231 allowedStatusValues = trueStatusOnly 232 } 233 switch { 234 case c.Status == "": 235 allErrs = append(allErrs, field.Required(fldPath.Index(i).Child("status"), "")) 236 case !allowedStatusValues.Has(string(c.Status)): 237 allErrs = append(allErrs, field.NotSupported(fldPath.Index(i).Child("status"), c.Status, allowedStatusValues.List())) 238 } 239 240 if !opts.allowBothApprovedAndDenied { 241 switch c.Type { 242 case certificates.CertificateApproved: 243 hasApproved = true 244 if hasDenied { 245 allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("type"), c.Type, "Approved and Denied conditions are mutually exclusive")) 246 } 247 case certificates.CertificateDenied: 248 hasDenied = true 249 if hasApproved { 250 allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("type"), c.Type, "Approved and Denied conditions are mutually exclusive")) 251 } 252 } 253 } 254 255 if !opts.allowDuplicateConditionTypes { 256 if seenTypes[c.Type] { 257 allErrs = append(allErrs, field.Duplicate(fldPath.Index(i).Child("type"), c.Type)) 258 } 259 seenTypes[c.Type] = true 260 } 261 } 262 263 return allErrs 264 } 265 266 func ValidateCertificateSigningRequestUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest) field.ErrorList { 267 opts := getValidationOptions(newCSR, oldCSR) 268 return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts) 269 } 270 271 func ValidateCertificateSigningRequestStatusUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest) field.ErrorList { 272 opts := getValidationOptions(newCSR, oldCSR) 273 opts.allowSettingCertificate = true 274 return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts) 275 } 276 277 func ValidateCertificateSigningRequestApprovalUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest) field.ErrorList { 278 opts := getValidationOptions(newCSR, oldCSR) 279 opts.allowSettingApprovalConditions = true 280 return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts) 281 } 282 283 func validateCertificateSigningRequestUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest, opts certificateValidationOptions) field.ErrorList { 284 validationErrorList := validateCertificateSigningRequest(newCSR, opts) 285 metaUpdateErrorList := apivalidation.ValidateObjectMetaUpdate(&newCSR.ObjectMeta, &oldCSR.ObjectMeta, field.NewPath("metadata")) 286 287 // prevent removal of existing Approved/Denied/Failed conditions 288 for _, t := range []certificates.RequestConditionType{certificates.CertificateApproved, certificates.CertificateDenied, certificates.CertificateFailed} { 289 oldConditions := findConditions(oldCSR, t) 290 newConditions := findConditions(newCSR, t) 291 if len(newConditions) < len(oldConditions) { 292 validationErrorList = append(validationErrorList, field.Forbidden(field.NewPath("status", "conditions"), fmt.Sprintf("updates may not remove a condition of type %q", t))) 293 } 294 } 295 296 if !opts.allowSettingApprovalConditions { 297 // prevent addition/removal/modification of Approved/Denied conditions 298 for _, t := range []certificates.RequestConditionType{certificates.CertificateApproved, certificates.CertificateDenied} { 299 oldConditions := findConditions(oldCSR, t) 300 newConditions := findConditions(newCSR, t) 301 switch { 302 case len(newConditions) < len(oldConditions): 303 // removals are prevented above 304 case len(newConditions) > len(oldConditions): 305 validationErrorList = append(validationErrorList, field.Forbidden(field.NewPath("status", "conditions"), fmt.Sprintf("updates may not add a condition of type %q", t))) 306 case !apiequality.Semantic.DeepEqual(oldConditions, newConditions): 307 conditionDiff := cmp.Diff(oldConditions, newConditions) 308 validationErrorList = append(validationErrorList, field.Forbidden(field.NewPath("status", "conditions"), fmt.Sprintf("updates may not modify a condition of type %q\n%v", t, conditionDiff))) 309 } 310 } 311 } 312 313 if !bytes.Equal(newCSR.Status.Certificate, oldCSR.Status.Certificate) { 314 if !opts.allowSettingCertificate { 315 validationErrorList = append(validationErrorList, field.Forbidden(field.NewPath("status", "certificate"), "updates may not set certificate content")) 316 } else if !opts.allowResettingCertificate && len(oldCSR.Status.Certificate) > 0 { 317 validationErrorList = append(validationErrorList, field.Forbidden(field.NewPath("status", "certificate"), "updates may not modify existing certificate content")) 318 } 319 } 320 321 return append(validationErrorList, metaUpdateErrorList...) 322 } 323 324 // findConditions returns all instances of conditions of the specified type 325 func findConditions(csr *certificates.CertificateSigningRequest, conditionType certificates.RequestConditionType) []certificates.CertificateSigningRequestCondition { 326 var retval []certificates.CertificateSigningRequestCondition 327 for i, c := range csr.Status.Conditions { 328 if c.Type == conditionType { 329 retval = append(retval, csr.Status.Conditions[i]) 330 } 331 } 332 return retval 333 } 334 335 // getValidationOptions returns the validation options to be 336 // compatible with the specified version and existing CSR. 337 // oldCSR may be nil if this is a create request. 338 // validation options related to subresource-specific capabilities are set to false. 339 func getValidationOptions(newCSR, oldCSR *certificates.CertificateSigningRequest) certificateValidationOptions { 340 return certificateValidationOptions{ 341 allowResettingCertificate: false, 342 allowBothApprovedAndDenied: allowBothApprovedAndDenied(oldCSR), 343 allowLegacySignerName: allowLegacySignerName(oldCSR), 344 allowDuplicateConditionTypes: allowDuplicateConditionTypes(oldCSR), 345 allowEmptyConditionType: allowEmptyConditionType(oldCSR), 346 allowArbitraryCertificate: allowArbitraryCertificate(newCSR, oldCSR), 347 allowDuplicateUsages: allowDuplicateUsages(oldCSR), 348 allowUnknownUsages: allowUnknownUsages(oldCSR), 349 } 350 } 351 352 func allowBothApprovedAndDenied(oldCSR *certificates.CertificateSigningRequest) bool { 353 if oldCSR == nil { 354 return false 355 } 356 approved := false 357 denied := false 358 for _, c := range oldCSR.Status.Conditions { 359 if c.Type == certificates.CertificateApproved { 360 approved = true 361 } else if c.Type == certificates.CertificateDenied { 362 denied = true 363 } 364 } 365 // compatibility with existing data 366 return approved && denied 367 } 368 369 func allowLegacySignerName(oldCSR *certificates.CertificateSigningRequest) bool { 370 switch { 371 case oldCSR != nil && oldCSR.Spec.SignerName == certificates.LegacyUnknownSignerName: 372 return true // compatibility with existing data 373 default: 374 return false 375 } 376 } 377 378 func allowDuplicateConditionTypes(oldCSR *certificates.CertificateSigningRequest) bool { 379 switch { 380 case oldCSR != nil && hasDuplicateConditionTypes(oldCSR): 381 return true // compatibility with existing data 382 default: 383 return false 384 } 385 } 386 func hasDuplicateConditionTypes(csr *certificates.CertificateSigningRequest) bool { 387 seen := map[certificates.RequestConditionType]bool{} 388 for _, c := range csr.Status.Conditions { 389 if seen[c.Type] { 390 return true 391 } 392 seen[c.Type] = true 393 } 394 return false 395 } 396 397 func allowEmptyConditionType(oldCSR *certificates.CertificateSigningRequest) bool { 398 switch { 399 case oldCSR != nil && hasEmptyConditionType(oldCSR): 400 return true // compatibility with existing data 401 default: 402 return false 403 } 404 } 405 func hasEmptyConditionType(csr *certificates.CertificateSigningRequest) bool { 406 for _, c := range csr.Status.Conditions { 407 if len(c.Type) == 0 { 408 return true 409 } 410 } 411 return false 412 } 413 414 func allowArbitraryCertificate(newCSR, oldCSR *certificates.CertificateSigningRequest) bool { 415 switch { 416 case newCSR != nil && oldCSR != nil && bytes.Equal(newCSR.Status.Certificate, oldCSR.Status.Certificate): 417 return true // tolerate updates that don't touch status.certificate 418 case oldCSR != nil && validateCertificate(oldCSR.Status.Certificate) != nil: 419 return true // compatibility with existing data 420 default: 421 return false 422 } 423 } 424 425 func allowUnknownUsages(oldCSR *certificates.CertificateSigningRequest) bool { 426 switch { 427 case oldCSR != nil && hasUnknownUsage(oldCSR.Spec.Usages): 428 return true // compatibility with existing data 429 default: 430 return false 431 } 432 } 433 434 func hasUnknownUsage(usages []certificates.KeyUsage) bool { 435 for _, usage := range usages { 436 if !allValidUsages.Has(string(usage)) { 437 return true 438 } 439 } 440 return false 441 } 442 443 func allowDuplicateUsages(oldCSR *certificates.CertificateSigningRequest) bool { 444 switch { 445 case oldCSR != nil && hasDuplicateUsage(oldCSR.Spec.Usages): 446 return true // compatibility with existing data 447 default: 448 return false 449 } 450 } 451 452 func hasDuplicateUsage(usages []certificates.KeyUsage) bool { 453 seen := make(map[certificates.KeyUsage]bool, len(usages)) 454 for _, usage := range usages { 455 if seen[usage] { 456 return true 457 } 458 seen[usage] = true 459 } 460 return false 461 } 462 463 type ValidateClusterTrustBundleOptions struct { 464 SuppressBundleParsing bool 465 } 466 467 // ValidateClusterTrustBundle runs all validation checks on bundle. 468 func ValidateClusterTrustBundle(bundle *certificates.ClusterTrustBundle, opts ValidateClusterTrustBundleOptions) field.ErrorList { 469 var allErrors field.ErrorList 470 471 metaErrors := apivalidation.ValidateObjectMeta(&bundle.ObjectMeta, false, apivalidation.ValidateClusterTrustBundleName(bundle.Spec.SignerName), field.NewPath("metadata")) 472 allErrors = append(allErrors, metaErrors...) 473 474 if bundle.Spec.SignerName != "" { 475 signerNameErrors := apivalidation.ValidateSignerName(field.NewPath("spec", "signerName"), bundle.Spec.SignerName) 476 allErrors = append(allErrors, signerNameErrors...) 477 } 478 479 if !opts.SuppressBundleParsing { 480 pemErrors := validateTrustBundle(field.NewPath("spec", "trustBundle"), bundle.Spec.TrustBundle) 481 allErrors = append(allErrors, pemErrors...) 482 } 483 484 return allErrors 485 } 486 487 // ValidateClusterTrustBundleUpdate runs all update validation checks on an 488 // update. 489 func ValidateClusterTrustBundleUpdate(newBundle, oldBundle *certificates.ClusterTrustBundle) field.ErrorList { 490 // If the caller isn't changing the TrustBundle field, don't parse it. 491 // This helps smoothly handle changes in Go's PEM or X.509 parsing 492 // libraries. 493 opts := ValidateClusterTrustBundleOptions{} 494 if newBundle.Spec.TrustBundle == oldBundle.Spec.TrustBundle { 495 opts.SuppressBundleParsing = true 496 } 497 498 var allErrors field.ErrorList 499 allErrors = append(allErrors, ValidateClusterTrustBundle(newBundle, opts)...) 500 allErrors = append(allErrors, apivalidation.ValidateObjectMetaUpdate(&newBundle.ObjectMeta, &oldBundle.ObjectMeta, field.NewPath("metadata"))...) 501 allErrors = append(allErrors, apivalidation.ValidateImmutableField(newBundle.Spec.SignerName, oldBundle.Spec.SignerName, field.NewPath("spec", "signerName"))...) 502 return allErrors 503 } 504 505 // validateTrustBundle rejects intra-block headers, blocks 506 // that don't parse as X.509 CA certificates, and duplicate trust anchors. It 507 // requires that at least one trust anchor is provided. 508 func validateTrustBundle(path *field.Path, in string) field.ErrorList { 509 var allErrors field.ErrorList 510 511 if len(in) > certificates.MaxTrustBundleSize { 512 allErrors = append(allErrors, field.TooLong(path, fmt.Sprintf("<value omitted, len %d>", len(in)), certificates.MaxTrustBundleSize)) 513 return allErrors 514 } 515 516 blockDedupe := map[string][]int{} 517 518 rest := []byte(in) 519 var b *pem.Block 520 i := -1 521 for { 522 b, rest = pem.Decode(rest) 523 if b == nil { 524 break 525 } 526 i++ 527 528 if b.Type != "CERTIFICATE" { 529 allErrors = append(allErrors, field.Invalid(path, "<value omitted>", fmt.Sprintf("entry %d has bad block type: %v", i, b.Type))) 530 continue 531 } 532 533 if len(b.Headers) != 0 { 534 allErrors = append(allErrors, field.Invalid(path, "<value omitted>", fmt.Sprintf("entry %d has PEM block headers", i))) 535 continue 536 } 537 538 cert, err := x509.ParseCertificate(b.Bytes) 539 if err != nil { 540 allErrors = append(allErrors, field.Invalid(path, "<value omitted>", fmt.Sprintf("entry %d does not parse as X.509", i))) 541 continue 542 } 543 544 if !cert.IsCA { 545 allErrors = append(allErrors, field.Invalid(path, "<value omitted>", fmt.Sprintf("entry %d does not have the CA bit set", i))) 546 continue 547 } 548 549 if !cert.BasicConstraintsValid { 550 allErrors = append(allErrors, field.Invalid(path, "<value omitted>", fmt.Sprintf("entry %d has invalid basic constraints", i))) 551 continue 552 } 553 554 blockDedupe[string(b.Bytes)] = append(blockDedupe[string(b.Bytes)], i) 555 } 556 557 // If we had a malformed block, don't also output potentially-redundant 558 // errors about duplicate or missing trust anchors. 559 if len(allErrors) != 0 { 560 return allErrors 561 } 562 563 if len(blockDedupe) == 0 { 564 allErrors = append(allErrors, field.Invalid(path, "<value omitted>", "at least one trust anchor must be provided")) 565 } 566 567 for _, indices := range blockDedupe { 568 if len(indices) > 1 { 569 allErrors = append(allErrors, field.Invalid(path, "<value omitted>", fmt.Sprintf("duplicate trust anchor (indices %v)", indices))) 570 } 571 } 572 573 return allErrors 574 }