k8s.io/apiserver@v0.31.1/pkg/apis/apiserver/validation/validation_encryption_test.go (about) 1 /* 2 Copyright 2019 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 "fmt" 21 "testing" 22 "time" 23 24 "github.com/google/go-cmp/cmp" 25 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/util/sets" 28 "k8s.io/apimachinery/pkg/util/validation/field" 29 "k8s.io/apiserver/pkg/apis/apiserver" 30 ) 31 32 func TestStructure(t *testing.T) { 33 root := field.NewPath("resources") 34 firstResourcePath := root.Index(0) 35 cacheSize := int32(1) 36 testCases := []struct { 37 desc string 38 in *apiserver.EncryptionConfiguration 39 reload bool 40 want field.ErrorList 41 }{{ 42 desc: "nil encryption config", 43 in: nil, 44 want: field.ErrorList{ 45 field.Required(root, encryptionConfigNilErr), 46 }, 47 }, { 48 desc: "empty encryption config", 49 in: &apiserver.EncryptionConfiguration{}, 50 want: field.ErrorList{ 51 field.Required(root, fmt.Sprintf(atLeastOneRequiredErrFmt, root)), 52 }, 53 }, { 54 desc: "no k8s resources", 55 in: &apiserver.EncryptionConfiguration{ 56 Resources: []apiserver.ResourceConfiguration{{ 57 Providers: []apiserver.ProviderConfiguration{{ 58 AESCBC: &apiserver.AESConfiguration{ 59 Keys: []apiserver.Key{{ 60 Name: "foo", 61 Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=", 62 }}, 63 }, 64 }}, 65 }}, 66 }, 67 want: field.ErrorList{ 68 field.Required(firstResourcePath.Child("resources"), fmt.Sprintf(atLeastOneRequiredErrFmt, root.Index(0).Child("resources"))), 69 }, 70 }, { 71 desc: "no providers", 72 in: &apiserver.EncryptionConfiguration{ 73 Resources: []apiserver.ResourceConfiguration{{ 74 Resources: []string{"secrets"}, 75 }}, 76 }, 77 want: field.ErrorList{ 78 field.Required(firstResourcePath.Child("providers"), fmt.Sprintf(atLeastOneRequiredErrFmt, root.Index(0).Child("providers"))), 79 }, 80 }, { 81 desc: "multiple providers", 82 in: &apiserver.EncryptionConfiguration{ 83 Resources: []apiserver.ResourceConfiguration{{ 84 Resources: []string{"secrets"}, 85 Providers: []apiserver.ProviderConfiguration{{ 86 AESGCM: &apiserver.AESConfiguration{ 87 Keys: []apiserver.Key{{ 88 Name: "foo", 89 Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=", 90 }}, 91 }, 92 AESCBC: &apiserver.AESConfiguration{ 93 Keys: []apiserver.Key{{ 94 Name: "foo", 95 Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=", 96 }}, 97 }, 98 }}, 99 }}, 100 }, 101 want: field.ErrorList{ 102 field.Invalid( 103 firstResourcePath.Child("providers").Index(0), 104 apiserver.ProviderConfiguration{ 105 AESGCM: &apiserver.AESConfiguration{ 106 Keys: []apiserver.Key{{ 107 Name: "foo", 108 Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=", 109 }}, 110 }, 111 AESCBC: &apiserver.AESConfiguration{ 112 Keys: []apiserver.Key{{ 113 Name: "foo", 114 Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=", 115 }}, 116 }, 117 }, 118 moreThanOneElementErr), 119 }, 120 }, { 121 desc: "valid config", 122 in: &apiserver.EncryptionConfiguration{ 123 Resources: []apiserver.ResourceConfiguration{{ 124 Resources: []string{"secrets"}, 125 Providers: []apiserver.ProviderConfiguration{{ 126 AESGCM: &apiserver.AESConfiguration{ 127 Keys: []apiserver.Key{{ 128 Name: "foo", 129 Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=", 130 }}, 131 }, 132 }}, 133 }}, 134 }, 135 want: field.ErrorList{}, 136 }, { 137 desc: "duplicate kms v2 config name with kms v1 config", 138 in: &apiserver.EncryptionConfiguration{ 139 Resources: []apiserver.ResourceConfiguration{{ 140 Resources: []string{"secrets"}, 141 Providers: []apiserver.ProviderConfiguration{{ 142 KMS: &apiserver.KMSConfiguration{ 143 Name: "foo", 144 Endpoint: "unix:///tmp/kms-provider-1.socket", 145 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 146 CacheSize: &cacheSize, 147 APIVersion: "v1", 148 }, 149 }, { 150 KMS: &apiserver.KMSConfiguration{ 151 Name: "foo", 152 Endpoint: "unix:///tmp/kms-provider-2.socket", 153 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 154 APIVersion: "v2", 155 }, 156 }}, 157 }}, 158 }, 159 want: field.ErrorList{ 160 field.Invalid(firstResourcePath.Child("providers").Index(1).Child("kms").Child("name"), 161 "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")), 162 }, 163 }, { 164 desc: "duplicate kms v2 config names", 165 in: &apiserver.EncryptionConfiguration{ 166 Resources: []apiserver.ResourceConfiguration{{ 167 Resources: []string{"secrets"}, 168 Providers: []apiserver.ProviderConfiguration{{ 169 KMS: &apiserver.KMSConfiguration{ 170 Name: "foo", 171 Endpoint: "unix:///tmp/kms-provider-1.socket", 172 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 173 APIVersion: "v2", 174 }, 175 }, { 176 KMS: &apiserver.KMSConfiguration{ 177 Name: "foo", 178 Endpoint: "unix:///tmp/kms-provider-2.socket", 179 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 180 APIVersion: "v2", 181 }, 182 }}, 183 }}, 184 }, 185 want: field.ErrorList{ 186 field.Invalid(firstResourcePath.Child("providers").Index(1).Child("kms").Child("name"), 187 "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")), 188 }, 189 }, { 190 desc: "duplicate kms v2 config name across providers", 191 in: &apiserver.EncryptionConfiguration{ 192 Resources: []apiserver.ResourceConfiguration{{ 193 Resources: []string{"secrets"}, 194 Providers: []apiserver.ProviderConfiguration{{ 195 KMS: &apiserver.KMSConfiguration{ 196 Name: "foo", 197 Endpoint: "unix:///tmp/kms-provider-1.socket", 198 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 199 APIVersion: "v2", 200 }, 201 }}, 202 }, { 203 Resources: []string{"secrets"}, 204 Providers: []apiserver.ProviderConfiguration{{ 205 KMS: &apiserver.KMSConfiguration{ 206 Name: "foo", 207 Endpoint: "unix:///tmp/kms-provider-2.socket", 208 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 209 APIVersion: "v2", 210 }, 211 }}, 212 }}, 213 }, 214 want: field.ErrorList{ 215 field.Invalid(root.Index(1).Child("providers").Index(0).Child("kms").Child("name"), 216 "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")), 217 }, 218 }, { 219 desc: "duplicate kms config name with v1 and v2 across providers", 220 in: &apiserver.EncryptionConfiguration{ 221 Resources: []apiserver.ResourceConfiguration{{ 222 Resources: []string{"secrets"}, 223 Providers: []apiserver.ProviderConfiguration{{ 224 KMS: &apiserver.KMSConfiguration{ 225 Name: "foo", 226 Endpoint: "unix:///tmp/kms-provider-1.socket", 227 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 228 CacheSize: &cacheSize, 229 APIVersion: "v1", 230 }, 231 }}, 232 }, { 233 Resources: []string{"secrets"}, 234 Providers: []apiserver.ProviderConfiguration{{ 235 KMS: &apiserver.KMSConfiguration{ 236 Name: "foo", 237 Endpoint: "unix:///tmp/kms-provider-2.socket", 238 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 239 APIVersion: "v2", 240 }, 241 }}, 242 }}, 243 }, 244 want: field.ErrorList{ 245 field.Invalid(root.Index(1).Child("providers").Index(0).Child("kms").Child("name"), 246 "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")), 247 }, 248 }, { 249 desc: "duplicate kms v1 config names shouldn't error", 250 in: &apiserver.EncryptionConfiguration{ 251 Resources: []apiserver.ResourceConfiguration{{ 252 Resources: []string{"secrets"}, 253 Providers: []apiserver.ProviderConfiguration{{ 254 KMS: &apiserver.KMSConfiguration{ 255 Name: "foo", 256 Endpoint: "unix:///tmp/kms-provider-1.socket", 257 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 258 CacheSize: &cacheSize, 259 APIVersion: "v1", 260 }, 261 }, { 262 KMS: &apiserver.KMSConfiguration{ 263 Name: "foo", 264 Endpoint: "unix:///tmp/kms-provider-2.socket", 265 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 266 CacheSize: &cacheSize, 267 APIVersion: "v1", 268 }, 269 }}, 270 }}, 271 }, 272 want: field.ErrorList{}, 273 }, { 274 desc: "duplicate kms v1 config names should error when reload=true", 275 in: &apiserver.EncryptionConfiguration{ 276 Resources: []apiserver.ResourceConfiguration{{ 277 Resources: []string{"secrets"}, 278 Providers: []apiserver.ProviderConfiguration{{ 279 KMS: &apiserver.KMSConfiguration{ 280 Name: "foo", 281 Endpoint: "unix:///tmp/kms-provider-1.socket", 282 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 283 CacheSize: &cacheSize, 284 APIVersion: "v1", 285 }, 286 }, { 287 KMS: &apiserver.KMSConfiguration{ 288 Name: "foo", 289 Endpoint: "unix:///tmp/kms-provider-2.socket", 290 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 291 CacheSize: &cacheSize, 292 APIVersion: "v1", 293 }, 294 }}, 295 }}, 296 }, 297 reload: true, 298 want: field.ErrorList{ 299 field.Invalid(root.Index(0).Child("providers").Index(1).Child("kms").Child("name"), 300 "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")), 301 }, 302 }, { 303 desc: "config should error when events.k8s.io group is used", 304 in: &apiserver.EncryptionConfiguration{ 305 Resources: []apiserver.ResourceConfiguration{{ 306 Resources: []string{ 307 "events.events.k8s.io", 308 }, 309 Providers: []apiserver.ProviderConfiguration{{ 310 KMS: &apiserver.KMSConfiguration{ 311 Name: "foo", 312 Endpoint: "unix:///tmp/kms-provider.socket", 313 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 314 CacheSize: &cacheSize, 315 APIVersion: "v1", 316 }, 317 }}, 318 }}, 319 }, 320 reload: false, 321 want: field.ErrorList{ 322 field.Invalid( 323 root.Index(0).Child("resources").Index(0), 324 "events.events.k8s.io", 325 eventsGroupErr, 326 ), 327 }, 328 }, { 329 desc: "config should error when events.k8s.io group is used later in the list", 330 in: &apiserver.EncryptionConfiguration{ 331 Resources: []apiserver.ResourceConfiguration{{ 332 Resources: []string{ 333 "secrets", 334 }, 335 Providers: []apiserver.ProviderConfiguration{{ 336 KMS: &apiserver.KMSConfiguration{ 337 Name: "foo", 338 Endpoint: "unix:///tmp/kms-provider.socket", 339 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 340 CacheSize: &cacheSize, 341 APIVersion: "v1", 342 }, 343 }}, 344 }, { 345 Resources: []string{ 346 "secret", 347 "events.events.k8s.io", 348 }, 349 Providers: []apiserver.ProviderConfiguration{{ 350 KMS: &apiserver.KMSConfiguration{ 351 Name: "foo", 352 Endpoint: "unix:///tmp/kms-provider.socket", 353 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 354 CacheSize: &cacheSize, 355 APIVersion: "v1", 356 }, 357 }}, 358 }}, 359 }, 360 reload: false, 361 want: field.ErrorList{ 362 field.Invalid( 363 root.Index(1).Child("resources").Index(1), 364 "events.events.k8s.io", 365 eventsGroupErr, 366 ), 367 }, 368 }, { 369 desc: "config should error when *.events.k8s.io group is used", 370 in: &apiserver.EncryptionConfiguration{ 371 Resources: []apiserver.ResourceConfiguration{{ 372 Resources: []string{ 373 "*.events.k8s.io", 374 }, 375 Providers: []apiserver.ProviderConfiguration{{ 376 KMS: &apiserver.KMSConfiguration{ 377 Name: "foo", 378 Endpoint: "unix:///tmp/kms-provider.socket", 379 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 380 CacheSize: &cacheSize, 381 APIVersion: "v1", 382 }, 383 }}, 384 }}, 385 }, 386 reload: false, 387 want: field.ErrorList{ 388 field.Invalid( 389 root.Index(0).Child("resources").Index(0), 390 "*.events.k8s.io", 391 eventsGroupErr, 392 ), 393 }, 394 }, { 395 desc: "config should error when extensions group is used", 396 in: &apiserver.EncryptionConfiguration{ 397 Resources: []apiserver.ResourceConfiguration{{ 398 Resources: []string{ 399 "*.extensions", 400 }, 401 Providers: []apiserver.ProviderConfiguration{{ 402 KMS: &apiserver.KMSConfiguration{ 403 Name: "foo", 404 Endpoint: "unix:///tmp/kms-provider.socket", 405 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 406 CacheSize: &cacheSize, 407 APIVersion: "v1", 408 }, 409 }}, 410 }}, 411 }, 412 reload: false, 413 want: field.ErrorList{ 414 field.Invalid( 415 root.Index(0).Child("resources").Index(0), 416 "*.extensions", 417 extensionsGroupErr, 418 ), 419 }, 420 }, { 421 desc: "config should error when foo.extensions group is used", 422 in: &apiserver.EncryptionConfiguration{ 423 Resources: []apiserver.ResourceConfiguration{{ 424 Resources: []string{ 425 "foo.extensions", 426 }, 427 Providers: []apiserver.ProviderConfiguration{{ 428 KMS: &apiserver.KMSConfiguration{ 429 Name: "foo", 430 Endpoint: "unix:///tmp/kms-provider.socket", 431 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 432 CacheSize: &cacheSize, 433 APIVersion: "v1", 434 }, 435 }}, 436 }}, 437 }, 438 reload: false, 439 want: field.ErrorList{ 440 field.Invalid( 441 root.Index(0).Child("resources").Index(0), 442 "foo.extensions", 443 extensionsGroupErr, 444 ), 445 }, 446 }, { 447 desc: "config should error when '*' resource is used", 448 in: &apiserver.EncryptionConfiguration{ 449 Resources: []apiserver.ResourceConfiguration{{ 450 Resources: []string{ 451 "*", 452 }, 453 Providers: []apiserver.ProviderConfiguration{{ 454 KMS: &apiserver.KMSConfiguration{ 455 Name: "foo", 456 Endpoint: "unix:///tmp/kms-provider.socket", 457 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 458 CacheSize: &cacheSize, 459 APIVersion: "v1", 460 }, 461 }}, 462 }}, 463 }, 464 reload: false, 465 want: field.ErrorList{ 466 field.Invalid( 467 root.Index(0).Child("resources").Index(0), 468 "*", 469 starResourceErr, 470 ), 471 }, 472 }, { 473 desc: "should error when resource name has capital letters", 474 in: &apiserver.EncryptionConfiguration{ 475 Resources: []apiserver.ResourceConfiguration{{ 476 Resources: []string{ 477 "apiServerIPInfo", 478 }, 479 Providers: []apiserver.ProviderConfiguration{{ 480 KMS: &apiserver.KMSConfiguration{ 481 Name: "foo", 482 Endpoint: "unix:///tmp/kms-provider.socket", 483 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 484 CacheSize: &cacheSize, 485 APIVersion: "v1", 486 }, 487 }}, 488 }}, 489 }, 490 reload: false, 491 want: field.ErrorList{ 492 field.Invalid( 493 root.Index(0).Child("resources").Index(0), 494 "apiServerIPInfo", 495 resourceNameErr, 496 ), 497 }, 498 }, { 499 desc: "should error when resource name is apiserveripinfo", 500 in: &apiserver.EncryptionConfiguration{ 501 Resources: []apiserver.ResourceConfiguration{{ 502 Resources: []string{ 503 "apiserveripinfo", 504 }, 505 Providers: []apiserver.ProviderConfiguration{{ 506 KMS: &apiserver.KMSConfiguration{ 507 Name: "foo", 508 Endpoint: "unix:///tmp/kms-provider.socket", 509 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 510 CacheSize: &cacheSize, 511 APIVersion: "v1", 512 }, 513 }}, 514 }}, 515 }, 516 reload: false, 517 want: field.ErrorList{ 518 field.Invalid( 519 root.Index(0).Child("resources").Index(0), 520 "apiserveripinfo", 521 nonRESTAPIResourceErr, 522 ), 523 }, 524 }, { 525 desc: "should error when resource name is serviceipallocations", 526 in: &apiserver.EncryptionConfiguration{ 527 Resources: []apiserver.ResourceConfiguration{{ 528 Resources: []string{ 529 "serviceipallocations", 530 }, 531 Providers: []apiserver.ProviderConfiguration{{ 532 KMS: &apiserver.KMSConfiguration{ 533 Name: "foo", 534 Endpoint: "unix:///tmp/kms-provider.socket", 535 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 536 CacheSize: &cacheSize, 537 APIVersion: "v1", 538 }, 539 }}, 540 }}, 541 }, 542 reload: false, 543 want: field.ErrorList{ 544 field.Invalid( 545 root.Index(0).Child("resources").Index(0), 546 "serviceipallocations", 547 nonRESTAPIResourceErr, 548 ), 549 }, 550 }, { 551 desc: "should error when resource name is servicenodeportallocations", 552 in: &apiserver.EncryptionConfiguration{ 553 Resources: []apiserver.ResourceConfiguration{{ 554 Resources: []string{ 555 "servicenodeportallocations", 556 }, 557 Providers: []apiserver.ProviderConfiguration{{ 558 KMS: &apiserver.KMSConfiguration{ 559 Name: "foo", 560 Endpoint: "unix:///tmp/kms-provider.socket", 561 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 562 CacheSize: &cacheSize, 563 APIVersion: "v1", 564 }, 565 }}, 566 }}, 567 }, 568 reload: false, 569 want: field.ErrorList{ 570 field.Invalid( 571 root.Index(0).Child("resources").Index(0), 572 "servicenodeportallocations", 573 nonRESTAPIResourceErr, 574 ), 575 }, 576 }, { 577 desc: "should not error when '*.apps' and '*.' are used within the same resource list", 578 in: &apiserver.EncryptionConfiguration{ 579 Resources: []apiserver.ResourceConfiguration{{ 580 Resources: []string{ 581 "*.apps", 582 "*.", 583 }, 584 Providers: []apiserver.ProviderConfiguration{{ 585 KMS: &apiserver.KMSConfiguration{ 586 Name: "foo", 587 Endpoint: "unix:///tmp/kms-provider.socket", 588 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 589 CacheSize: &cacheSize, 590 APIVersion: "v1", 591 }, 592 }}, 593 }}, 594 }, 595 reload: false, 596 want: field.ErrorList{}, 597 }, { 598 desc: "should error when the same resource across groups is encrypted", 599 in: &apiserver.EncryptionConfiguration{ 600 Resources: []apiserver.ResourceConfiguration{{ 601 Resources: []string{ 602 "*.", 603 "foos.*", 604 }, 605 Providers: []apiserver.ProviderConfiguration{{ 606 KMS: &apiserver.KMSConfiguration{ 607 Name: "foo", 608 Endpoint: "unix:///tmp/kms-provider.socket", 609 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 610 CacheSize: &cacheSize, 611 APIVersion: "v1", 612 }, 613 }}, 614 }}, 615 }, 616 reload: false, 617 want: field.ErrorList{ 618 field.Invalid( 619 root.Index(0).Child("resources").Index(1), 620 "foos.*", 621 resourceAcrossGroupErr, 622 ), 623 }, 624 }, { 625 desc: "should error when secrets are specified twice within the same resource list", 626 in: &apiserver.EncryptionConfiguration{ 627 Resources: []apiserver.ResourceConfiguration{{ 628 Resources: []string{ 629 "secrets", 630 "secrets", 631 }, 632 Providers: []apiserver.ProviderConfiguration{{ 633 KMS: &apiserver.KMSConfiguration{ 634 Name: "foo", 635 Endpoint: "unix:///tmp/kms-provider.socket", 636 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 637 CacheSize: &cacheSize, 638 APIVersion: "v1", 639 }, 640 }}, 641 }}, 642 }, 643 reload: false, 644 want: field.ErrorList{ 645 field.Invalid( 646 root.Index(0).Child("resources"), 647 []string{ 648 "secrets", 649 "secrets", 650 }, 651 duplicateResourceErr, 652 ), 653 }, 654 }, { 655 desc: "should error once when secrets are specified many times within the same resource list", 656 in: &apiserver.EncryptionConfiguration{ 657 Resources: []apiserver.ResourceConfiguration{{ 658 Resources: []string{ 659 "secrets", 660 "secrets", 661 "secrets", 662 "secrets", 663 }, 664 Providers: []apiserver.ProviderConfiguration{{ 665 KMS: &apiserver.KMSConfiguration{ 666 Name: "foo", 667 Endpoint: "unix:///tmp/kms-provider.socket", 668 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 669 CacheSize: &cacheSize, 670 APIVersion: "v1", 671 }, 672 }}, 673 }}, 674 }, 675 reload: false, 676 want: field.ErrorList{ 677 field.Invalid( 678 root.Index(0).Child("resources"), 679 []string{ 680 "secrets", 681 "secrets", 682 "secrets", 683 "secrets", 684 }, 685 duplicateResourceErr, 686 ), 687 }, 688 }, { 689 desc: "should error when secrets are specified twice within the same resource list, via dot", 690 in: &apiserver.EncryptionConfiguration{ 691 Resources: []apiserver.ResourceConfiguration{{ 692 Resources: []string{ 693 "secrets", 694 "secrets.", 695 }, 696 Providers: []apiserver.ProviderConfiguration{{ 697 KMS: &apiserver.KMSConfiguration{ 698 Name: "foo", 699 Endpoint: "unix:///tmp/kms-provider.socket", 700 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 701 CacheSize: &cacheSize, 702 APIVersion: "v1", 703 }, 704 }}, 705 }}, 706 }, 707 reload: false, 708 want: field.ErrorList{ 709 field.Invalid( 710 root.Index(0).Child("resources"), 711 []string{ 712 "secrets", 713 "secrets.", 714 }, 715 duplicateResourceErr, 716 ), 717 }, 718 }, { 719 desc: "should error when '*.apps' and '*.' and '*.*' are used within the same resource list", 720 in: &apiserver.EncryptionConfiguration{ 721 Resources: []apiserver.ResourceConfiguration{{ 722 Resources: []string{ 723 "*.apps", 724 "*.", 725 "*.*", 726 }, 727 Providers: []apiserver.ProviderConfiguration{{ 728 KMS: &apiserver.KMSConfiguration{ 729 Name: "foo", 730 Endpoint: "unix:///tmp/kms-provider.socket", 731 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 732 CacheSize: &cacheSize, 733 APIVersion: "v1", 734 }, 735 }}, 736 }}, 737 }, 738 reload: false, 739 want: field.ErrorList{ 740 field.Invalid( 741 root.Index(0).Child("resources"), 742 []string{ 743 "*.apps", 744 "*.", 745 "*.*", 746 }, 747 overlapErr, 748 ), 749 }, 750 }, { 751 desc: "should not error when deployments.apps are specified with '*.' within the same resource list", 752 in: &apiserver.EncryptionConfiguration{ 753 Resources: []apiserver.ResourceConfiguration{{ 754 Resources: []string{ 755 "deployments.apps", 756 "*.", 757 }, 758 Providers: []apiserver.ProviderConfiguration{{ 759 KMS: &apiserver.KMSConfiguration{ 760 Name: "foo", 761 Endpoint: "unix:///tmp/kms-provider.socket", 762 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 763 CacheSize: &cacheSize, 764 APIVersion: "v1", 765 }, 766 }}, 767 }}, 768 }, 769 reload: false, 770 want: field.ErrorList{}, 771 }, { 772 desc: "should error when deployments.apps are specified with '*.apps' within the same resource list", 773 in: &apiserver.EncryptionConfiguration{ 774 Resources: []apiserver.ResourceConfiguration{{ 775 Resources: []string{ 776 "deployments.apps", 777 "*.apps", 778 }, 779 Providers: []apiserver.ProviderConfiguration{{ 780 KMS: &apiserver.KMSConfiguration{ 781 Name: "foo", 782 Endpoint: "unix:///tmp/kms-provider.socket", 783 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 784 CacheSize: &cacheSize, 785 APIVersion: "v1", 786 }, 787 }}, 788 }}, 789 }, 790 reload: false, 791 want: field.ErrorList{ 792 field.Invalid( 793 root.Index(0).Child("resources"), 794 []string{ 795 "deployments.apps", 796 "*.apps", 797 }, 798 overlapErr, 799 ), 800 }, 801 }, { 802 desc: "should error when secrets are specified with '*.' within the same resource list", 803 in: &apiserver.EncryptionConfiguration{ 804 Resources: []apiserver.ResourceConfiguration{{ 805 Resources: []string{ 806 "secrets", 807 "*.", 808 }, 809 Providers: []apiserver.ProviderConfiguration{{ 810 KMS: &apiserver.KMSConfiguration{ 811 Name: "foo", 812 Endpoint: "unix:///tmp/kms-provider.socket", 813 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 814 CacheSize: &cacheSize, 815 APIVersion: "v1", 816 }, 817 }}, 818 }}, 819 }, 820 reload: false, 821 want: field.ErrorList{ 822 field.Invalid( 823 root.Index(0).Child("resources"), 824 []string{ 825 "secrets", 826 "*.", 827 }, 828 overlapErr, 829 ), 830 }, 831 }, { 832 desc: "should error when pods are specified with '*.' within the same resource list", 833 in: &apiserver.EncryptionConfiguration{ 834 Resources: []apiserver.ResourceConfiguration{{ 835 Resources: []string{ 836 "pods", 837 "*.", 838 }, 839 Providers: []apiserver.ProviderConfiguration{{ 840 KMS: &apiserver.KMSConfiguration{ 841 Name: "foo", 842 Endpoint: "unix:///tmp/kms-provider.socket", 843 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 844 CacheSize: &cacheSize, 845 APIVersion: "v1", 846 }, 847 }}, 848 }}, 849 }, 850 reload: false, 851 want: field.ErrorList{ 852 field.Invalid( 853 root.Index(0).Child("resources"), 854 []string{ 855 "pods", 856 "*.", 857 }, 858 overlapErr, 859 ), 860 }, 861 }, { 862 desc: "should error when other resources are specified with '*.*' within the same resource list", 863 in: &apiserver.EncryptionConfiguration{ 864 Resources: []apiserver.ResourceConfiguration{{ 865 Resources: []string{ 866 "secrets", 867 "*.*", 868 }, 869 Providers: []apiserver.ProviderConfiguration{{ 870 KMS: &apiserver.KMSConfiguration{ 871 Name: "foo", 872 Endpoint: "unix:///tmp/kms-provider.socket", 873 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 874 CacheSize: &cacheSize, 875 APIVersion: "v1", 876 }, 877 }}, 878 }}, 879 }, 880 reload: false, 881 want: field.ErrorList{ 882 field.Invalid( 883 root.Index(0).Child("resources"), 884 []string{ 885 "secrets", 886 "*.*", 887 }, 888 overlapErr, 889 ), 890 }, 891 }, { 892 desc: "should error when both '*.' and '*.*' are used within the same resource list", 893 in: &apiserver.EncryptionConfiguration{ 894 Resources: []apiserver.ResourceConfiguration{{ 895 Resources: []string{ 896 "*.", 897 "*.*", 898 }, 899 Providers: []apiserver.ProviderConfiguration{{ 900 KMS: &apiserver.KMSConfiguration{ 901 Name: "foo", 902 Endpoint: "unix:///tmp/kms-provider.socket", 903 Timeout: &metav1.Duration{Duration: 3 * time.Second}, 904 CacheSize: &cacheSize, 905 APIVersion: "v1", 906 }, 907 }}, 908 }}, 909 }, 910 reload: false, 911 want: field.ErrorList{ 912 field.Invalid( 913 root.Index(0).Child("resources"), 914 []string{ 915 "*.", 916 "*.*", 917 }, 918 overlapErr, 919 ), 920 }, 921 }} 922 923 for _, tt := range testCases { 924 t.Run(tt.desc, func(t *testing.T) { 925 got := ValidateEncryptionConfiguration(tt.in, tt.reload) 926 if d := cmp.Diff(tt.want, got); d != "" { 927 t.Fatalf("EncryptionConfiguration validation results mismatch (-want +got):\n%s", d) 928 } 929 }) 930 } 931 } 932 933 func TestKey(t *testing.T) { 934 root := field.NewPath("resources") 935 path := root.Index(0).Child("provider").Index(0).Child("key").Index(0) 936 testCases := []struct { 937 desc string 938 in apiserver.Key 939 want field.ErrorList 940 }{{ 941 desc: "valid key", 942 in: apiserver.Key{Name: "foo", Secret: "c2VjcmV0IGlzIHNlY3VyZQ=="}, 943 want: field.ErrorList{}, 944 }, { 945 desc: "key without name", 946 in: apiserver.Key{Secret: "c2VjcmV0IGlzIHNlY3VyZQ=="}, 947 want: field.ErrorList{ 948 field.Required(path.Child("name"), fmt.Sprintf(mandatoryFieldErrFmt, "name", "key")), 949 }, 950 }, { 951 desc: "key without secret", 952 in: apiserver.Key{Name: "foo"}, 953 want: field.ErrorList{ 954 field.Required(path.Child("secret"), fmt.Sprintf(mandatoryFieldErrFmt, "secret", "key")), 955 }, 956 }, { 957 desc: "key is not base64 encoded", 958 in: apiserver.Key{Name: "foo", Secret: "P@ssword"}, 959 want: field.ErrorList{ 960 field.Invalid(path.Child("secret"), "REDACTED", base64EncodingErr), 961 }, 962 }, { 963 desc: "key is not of expected length", 964 in: apiserver.Key{Name: "foo", Secret: "cGFzc3dvcmQK"}, 965 want: field.ErrorList{ 966 field.Invalid(path.Child("secret"), "REDACTED", fmt.Sprintf(keyLenErrFmt, 9, aesKeySizes)), 967 }, 968 }} 969 970 for _, tt := range testCases { 971 t.Run(tt.desc, func(t *testing.T) { 972 got := validateKey(tt.in, path, aesKeySizes) 973 if d := cmp.Diff(tt.want, got); d != "" { 974 t.Fatalf("Key validation results mismatch (-want +got):\n%s", d) 975 } 976 }) 977 } 978 } 979 980 func TestKMSProviderTimeout(t *testing.T) { 981 timeoutField := field.NewPath("Resource").Index(0).Child("Provider").Index(0).Child("KMS").Child("Timeout") 982 negativeTimeout := &metav1.Duration{Duration: -1 * time.Minute} 983 zeroTimeout := &metav1.Duration{Duration: 0 * time.Minute} 984 985 testCases := []struct { 986 desc string 987 in *apiserver.KMSConfiguration 988 want field.ErrorList 989 }{{ 990 desc: "valid timeout", 991 in: &apiserver.KMSConfiguration{Timeout: &metav1.Duration{Duration: 1 * time.Minute}}, 992 want: field.ErrorList{}, 993 }, { 994 desc: "negative timeout", 995 in: &apiserver.KMSConfiguration{Timeout: negativeTimeout}, 996 want: field.ErrorList{ 997 field.Invalid(timeoutField, negativeTimeout, fmt.Sprintf(zeroOrNegativeErrFmt, "timeout")), 998 }, 999 }, { 1000 desc: "zero timeout", 1001 in: &apiserver.KMSConfiguration{Timeout: zeroTimeout}, 1002 want: field.ErrorList{ 1003 field.Invalid(timeoutField, zeroTimeout, fmt.Sprintf(zeroOrNegativeErrFmt, "timeout")), 1004 }, 1005 }} 1006 1007 for _, tt := range testCases { 1008 t.Run(tt.desc, func(t *testing.T) { 1009 got := validateKMSTimeout(tt.in, timeoutField) 1010 if d := cmp.Diff(tt.want, got); d != "" { 1011 t.Fatalf("KMS Provider validation mismatch (-want +got):\n%s", d) 1012 } 1013 }) 1014 } 1015 } 1016 1017 func TestKMSEndpoint(t *testing.T) { 1018 endpointField := field.NewPath("Resource").Index(0).Child("Provider").Index(0).Child("kms").Child("endpoint") 1019 testCases := []struct { 1020 desc string 1021 in *apiserver.KMSConfiguration 1022 want field.ErrorList 1023 }{{ 1024 desc: "valid endpoint", 1025 in: &apiserver.KMSConfiguration{Endpoint: "unix:///socket.sock"}, 1026 want: field.ErrorList{}, 1027 }, { 1028 desc: "empty endpoint", 1029 in: &apiserver.KMSConfiguration{}, 1030 want: field.ErrorList{ 1031 field.Invalid(endpointField, "", fmt.Sprintf(mandatoryFieldErrFmt, "endpoint", "kms")), 1032 }, 1033 }, { 1034 desc: "non unix endpoint", 1035 in: &apiserver.KMSConfiguration{Endpoint: "https://www.foo.com"}, 1036 want: field.ErrorList{ 1037 field.Invalid(endpointField, "https://www.foo.com", fmt.Sprintf(unsupportedSchemeErrFmt, "https")), 1038 }, 1039 }, { 1040 desc: "invalid url", 1041 in: &apiserver.KMSConfiguration{Endpoint: "unix:///foo\n.socket"}, 1042 want: field.ErrorList{ 1043 field.Invalid(endpointField, "unix:///foo\n.socket", fmt.Sprintf(invalidURLErrFmt, `parse "unix:///foo\n.socket": net/url: invalid control character in URL`)), 1044 }, 1045 }} 1046 1047 for _, tt := range testCases { 1048 t.Run(tt.desc, func(t *testing.T) { 1049 got := validateKMSEndpoint(tt.in, endpointField) 1050 if d := cmp.Diff(tt.want, got); d != "" { 1051 t.Fatalf("KMS Provider validation mismatch (-want +got):\n%s", d) 1052 } 1053 }) 1054 } 1055 } 1056 1057 func TestKMSProviderCacheSize(t *testing.T) { 1058 root := field.NewPath("resources") 1059 cacheField := root.Index(0).Child("kms").Child("cachesize") 1060 negativeCacheSize := int32(-1) 1061 positiveCacheSize := int32(10) 1062 zeroCacheSize := int32(0) 1063 1064 testCases := []struct { 1065 desc string 1066 in *apiserver.KMSConfiguration 1067 want field.ErrorList 1068 }{{ 1069 desc: "valid positive cache size", 1070 in: &apiserver.KMSConfiguration{APIVersion: "v1", CacheSize: &positiveCacheSize}, 1071 want: field.ErrorList{}, 1072 }, { 1073 desc: "invalid zero cache size", 1074 in: &apiserver.KMSConfiguration{APIVersion: "v1", CacheSize: &zeroCacheSize}, 1075 want: field.ErrorList{ 1076 field.Invalid(cacheField, int32(0), fmt.Sprintf(nonZeroErrFmt, "cachesize")), 1077 }, 1078 }, { 1079 desc: "valid negative caches size", 1080 in: &apiserver.KMSConfiguration{APIVersion: "v1", CacheSize: &negativeCacheSize}, 1081 want: field.ErrorList{}, 1082 }, { 1083 desc: "cache size set with v2 provider", 1084 in: &apiserver.KMSConfiguration{CacheSize: &positiveCacheSize, APIVersion: "v2"}, 1085 want: field.ErrorList{ 1086 field.Invalid(cacheField, positiveCacheSize, "cachesize is not supported in v2"), 1087 }, 1088 }} 1089 1090 for _, tt := range testCases { 1091 t.Run(tt.desc, func(t *testing.T) { 1092 got := validateKMSCacheSize(tt.in, cacheField) 1093 if d := cmp.Diff(tt.want, got); d != "" { 1094 t.Fatalf("KMS Provider validation mismatch (-want +got):\n%s", d) 1095 } 1096 }) 1097 } 1098 } 1099 1100 func TestKMSProviderAPIVersion(t *testing.T) { 1101 apiVersionField := field.NewPath("Resource").Index(0).Child("Provider").Index(0).Child("KMS").Child("APIVersion") 1102 1103 testCases := []struct { 1104 desc string 1105 in *apiserver.KMSConfiguration 1106 want field.ErrorList 1107 }{{ 1108 desc: "valid v1 api version", 1109 in: &apiserver.KMSConfiguration{APIVersion: "v1"}, 1110 want: field.ErrorList{}, 1111 }, { 1112 desc: "valid v2 api version", 1113 in: &apiserver.KMSConfiguration{APIVersion: "v2"}, 1114 want: field.ErrorList{}, 1115 }, { 1116 desc: "invalid api version", 1117 in: &apiserver.KMSConfiguration{APIVersion: "v3"}, 1118 want: field.ErrorList{ 1119 field.Invalid(apiVersionField, "v3", fmt.Sprintf(unsupportedKMSAPIVersionErrFmt, "apiVersion")), 1120 }, 1121 }} 1122 1123 for _, tt := range testCases { 1124 t.Run(tt.desc, func(t *testing.T) { 1125 got := validateKMSAPIVersion(tt.in, apiVersionField) 1126 if d := cmp.Diff(tt.want, got); d != "" { 1127 t.Fatalf("KMS Provider validation mismatch (-want +got):\n%s", d) 1128 } 1129 }) 1130 } 1131 } 1132 1133 func TestKMSProviderName(t *testing.T) { 1134 nameField := field.NewPath("Resource").Index(0).Child("Provider").Index(0).Child("KMS").Child("name") 1135 1136 testCases := []struct { 1137 desc string 1138 in *apiserver.KMSConfiguration 1139 reload bool 1140 kmsProviderNames sets.Set[string] 1141 want field.ErrorList 1142 }{{ 1143 desc: "valid name", 1144 in: &apiserver.KMSConfiguration{Name: "foo"}, 1145 want: field.ErrorList{}, 1146 }, { 1147 desc: "empty name", 1148 in: &apiserver.KMSConfiguration{}, 1149 want: field.ErrorList{ 1150 field.Required(nameField, fmt.Sprintf(mandatoryFieldErrFmt, "name", "provider")), 1151 }, 1152 }, { 1153 desc: "invalid name with :", 1154 in: &apiserver.KMSConfiguration{Name: "foo:bar"}, 1155 want: field.ErrorList{ 1156 field.Invalid(nameField, "foo:bar", fmt.Sprintf(invalidKMSConfigNameErrFmt, "foo:bar")), 1157 }, 1158 }, { 1159 desc: "invalid name with : but api version is v1", 1160 in: &apiserver.KMSConfiguration{Name: "foo:bar", APIVersion: "v1"}, 1161 want: field.ErrorList{}, 1162 }, { 1163 desc: "duplicate name, kms v2, reload=false", 1164 in: &apiserver.KMSConfiguration{APIVersion: "v2", Name: "foo"}, 1165 kmsProviderNames: sets.New("foo"), 1166 want: field.ErrorList{ 1167 field.Invalid(nameField, "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")), 1168 }, 1169 }, { 1170 desc: "duplicate name, kms v2, reload=true", 1171 in: &apiserver.KMSConfiguration{APIVersion: "v2", Name: "foo"}, 1172 reload: true, 1173 kmsProviderNames: sets.New("foo"), 1174 want: field.ErrorList{ 1175 field.Invalid(nameField, "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")), 1176 }, 1177 }, { 1178 desc: "duplicate name, kms v1, reload=false", 1179 in: &apiserver.KMSConfiguration{APIVersion: "v1", Name: "foo"}, 1180 kmsProviderNames: sets.New("foo"), 1181 want: field.ErrorList{}, 1182 }, { 1183 desc: "duplicate name, kms v1, reload=true", 1184 in: &apiserver.KMSConfiguration{APIVersion: "v1", Name: "foo"}, 1185 reload: true, 1186 kmsProviderNames: sets.New("foo"), 1187 want: field.ErrorList{ 1188 field.Invalid(nameField, "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")), 1189 }, 1190 }} 1191 1192 for _, tt := range testCases { 1193 t.Run(tt.desc, func(t *testing.T) { 1194 got := validateKMSConfigName(tt.in, nameField, tt.kmsProviderNames, tt.reload) 1195 if d := cmp.Diff(tt.want, got); d != "" { 1196 t.Fatalf("KMS Provider validation mismatch (-want +got):\n%s", d) 1197 } 1198 }) 1199 } 1200 }