github.com/nginxinc/kubernetes-ingress@v1.12.5/pkg/apis/configuration/validation/policy_test.go (about) 1 package validation 2 3 import ( 4 "testing" 5 6 v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1" 7 "k8s.io/apimachinery/pkg/util/validation/field" 8 ) 9 10 func TestValidatePolicy(t *testing.T) { 11 tests := []struct { 12 policy *v1.Policy 13 isPlus bool 14 enablePreviewPolicies bool 15 enableAppProtect bool 16 msg string 17 }{ 18 { 19 policy: &v1.Policy{ 20 Spec: v1.PolicySpec{ 21 AccessControl: &v1.AccessControl{ 22 Allow: []string{"127.0.0.1"}, 23 }, 24 }, 25 }, 26 isPlus: false, 27 enablePreviewPolicies: false, 28 enableAppProtect: false, 29 }, 30 { 31 policy: &v1.Policy{ 32 Spec: v1.PolicySpec{ 33 JWTAuth: &v1.JWTAuth{ 34 Realm: "My Product API", 35 Secret: "my-jwk", 36 }, 37 }, 38 }, 39 isPlus: true, 40 enablePreviewPolicies: true, 41 enableAppProtect: false, 42 msg: "use jwt(plus only) policy", 43 }, 44 { 45 policy: &v1.Policy{ 46 Spec: v1.PolicySpec{ 47 OIDC: &v1.OIDC{ 48 AuthEndpoint: "https://foo.bar/auth", 49 TokenEndpoint: "https://foo.bar/token", 50 JWKSURI: "https://foo.bar/certs", 51 ClientID: "random-string", 52 ClientSecret: "random-secret", 53 Scope: "openid", 54 }, 55 }, 56 }, 57 isPlus: true, 58 enablePreviewPolicies: true, 59 msg: "use OIDC (plus only)", 60 }, 61 { 62 policy: &v1.Policy{ 63 Spec: v1.PolicySpec{ 64 WAF: &v1.WAF{ 65 Enable: true, 66 }, 67 }, 68 }, 69 isPlus: true, 70 enablePreviewPolicies: true, 71 enableAppProtect: true, 72 msg: "use WAF(plus only) policy", 73 }, 74 } 75 for _, test := range tests { 76 err := ValidatePolicy(test.policy, test.isPlus, test.enablePreviewPolicies, test.enableAppProtect) 77 if err != nil { 78 t.Errorf("ValidatePolicy() returned error %v for valid input for the case of %v", err, test.msg) 79 } 80 } 81 } 82 83 func TestValidatePolicyFails(t *testing.T) { 84 tests := []struct { 85 policy *v1.Policy 86 isPlus bool 87 enablePreviewPolicies bool 88 enableAppProtect bool 89 msg string 90 }{ 91 { 92 policy: &v1.Policy{ 93 Spec: v1.PolicySpec{}, 94 }, 95 isPlus: false, 96 enablePreviewPolicies: false, 97 enableAppProtect: false, 98 msg: "empty policy spec", 99 }, 100 { 101 policy: &v1.Policy{ 102 Spec: v1.PolicySpec{ 103 AccessControl: &v1.AccessControl{ 104 Allow: []string{"127.0.0.1"}, 105 }, 106 RateLimit: &v1.RateLimit{ 107 Key: "${uri}", 108 ZoneSize: "10M", 109 Rate: "10r/s", 110 }, 111 }, 112 }, 113 isPlus: true, 114 enablePreviewPolicies: true, 115 enableAppProtect: false, 116 msg: "multiple policies in spec", 117 }, 118 { 119 policy: &v1.Policy{ 120 Spec: v1.PolicySpec{ 121 JWTAuth: &v1.JWTAuth{ 122 Realm: "My Product API", 123 Secret: "my-jwk", 124 }, 125 }, 126 }, 127 isPlus: false, 128 enablePreviewPolicies: true, 129 enableAppProtect: false, 130 msg: "jwt(plus only) policy on OSS", 131 }, 132 { 133 policy: &v1.Policy{ 134 Spec: v1.PolicySpec{ 135 WAF: &v1.WAF{ 136 Enable: true, 137 }, 138 }, 139 }, 140 isPlus: false, 141 enablePreviewPolicies: true, 142 enableAppProtect: false, 143 msg: "WAF(plus only) policy on OSS", 144 }, 145 { 146 policy: &v1.Policy{ 147 Spec: v1.PolicySpec{ 148 RateLimit: &v1.RateLimit{ 149 Rate: "10r/s", 150 ZoneSize: "10M", 151 Key: "${request_uri}", 152 }, 153 }, 154 }, 155 isPlus: false, 156 enablePreviewPolicies: false, 157 enableAppProtect: false, 158 msg: "rateLimit policy with preview policies disabled", 159 }, 160 { 161 policy: &v1.Policy{ 162 Spec: v1.PolicySpec{ 163 JWTAuth: &v1.JWTAuth{ 164 Realm: "My Product API", 165 Secret: "my-jwk", 166 }, 167 }, 168 }, 169 isPlus: true, 170 enablePreviewPolicies: false, 171 enableAppProtect: false, 172 msg: "jwt policy with preview policies disabled", 173 }, 174 { 175 policy: &v1.Policy{ 176 Spec: v1.PolicySpec{ 177 IngressMTLS: &v1.IngressMTLS{ 178 ClientCertSecret: "mtls-secret", 179 }, 180 }, 181 }, 182 isPlus: false, 183 enablePreviewPolicies: false, 184 enableAppProtect: false, 185 msg: "ingressMTLS policy with preview policies disabled", 186 }, 187 { 188 policy: &v1.Policy{ 189 Spec: v1.PolicySpec{ 190 EgressMTLS: &v1.EgressMTLS{ 191 TLSSecret: "mtls-secret", 192 }, 193 }, 194 }, 195 isPlus: false, 196 enablePreviewPolicies: false, 197 enableAppProtect: false, 198 msg: "egressMTLS policy with preview policies disabled", 199 }, 200 { 201 policy: &v1.Policy{ 202 Spec: v1.PolicySpec{ 203 OIDC: &v1.OIDC{ 204 AuthEndpoint: "https://foo.bar/auth", 205 TokenEndpoint: "https://foo.bar/token", 206 JWKSURI: "https://foo.bar/certs", 207 ClientID: "random-string", 208 ClientSecret: "random-secret", 209 Scope: "openid", 210 }, 211 }, 212 }, 213 isPlus: true, 214 enablePreviewPolicies: false, 215 msg: "OIDC policy with preview policies disabled", 216 }, 217 { 218 policy: &v1.Policy{ 219 Spec: v1.PolicySpec{ 220 OIDC: &v1.OIDC{ 221 AuthEndpoint: "https://foo.bar/auth", 222 TokenEndpoint: "https://foo.bar/token", 223 JWKSURI: "https://foo.bar/certs", 224 ClientID: "random-string", 225 ClientSecret: "random-secret", 226 Scope: "openid", 227 }, 228 }, 229 }, 230 isPlus: false, 231 enablePreviewPolicies: true, 232 msg: "OIDC policy in OSS", 233 }, 234 { 235 policy: &v1.Policy{ 236 Spec: v1.PolicySpec{ 237 WAF: &v1.WAF{ 238 Enable: true, 239 }, 240 }, 241 }, 242 isPlus: true, 243 enablePreviewPolicies: false, 244 enableAppProtect: true, 245 msg: "WAF policy with preview policies disabled", 246 }, 247 { 248 policy: &v1.Policy{ 249 Spec: v1.PolicySpec{ 250 WAF: &v1.WAF{ 251 Enable: true, 252 }, 253 }, 254 }, 255 isPlus: true, 256 enablePreviewPolicies: true, 257 enableAppProtect: false, 258 msg: "WAF policy with AP disabled", 259 }, 260 } 261 for _, test := range tests { 262 err := ValidatePolicy(test.policy, test.isPlus, test.enablePreviewPolicies, test.enableAppProtect) 263 if err == nil { 264 t.Errorf("ValidatePolicy() returned no error for invalid input") 265 } 266 } 267 } 268 269 func TestValidateAccessControl(t *testing.T) { 270 validInput := []*v1.AccessControl{ 271 { 272 Allow: []string{}, 273 }, 274 { 275 Allow: []string{"127.0.0.1"}, 276 }, 277 { 278 Deny: []string{}, 279 }, 280 { 281 Deny: []string{"127.0.0.1"}, 282 }, 283 } 284 285 for _, input := range validInput { 286 allErrs := validateAccessControl(input, field.NewPath("accessControl")) 287 if len(allErrs) > 0 { 288 t.Errorf("validateAccessControl(%+v) returned errors %v for valid input", input, allErrs) 289 } 290 } 291 } 292 293 func TestValidateAccessControlFails(t *testing.T) { 294 tests := []struct { 295 accessControl *v1.AccessControl 296 msg string 297 }{ 298 { 299 accessControl: &v1.AccessControl{ 300 Allow: nil, 301 Deny: nil, 302 }, 303 msg: "neither allow nor deny is defined", 304 }, 305 { 306 accessControl: &v1.AccessControl{ 307 Allow: []string{}, 308 Deny: []string{}, 309 }, 310 msg: "both allow and deny are defined", 311 }, 312 { 313 accessControl: &v1.AccessControl{ 314 Allow: []string{"invalid"}, 315 }, 316 msg: "invalid allow", 317 }, 318 { 319 accessControl: &v1.AccessControl{ 320 Deny: []string{"invalid"}, 321 }, 322 msg: "invalid deny", 323 }, 324 } 325 326 for _, test := range tests { 327 allErrs := validateAccessControl(test.accessControl, field.NewPath("accessControl")) 328 if len(allErrs) == 0 { 329 t.Errorf("validateAccessControl() returned no errors for invalid input for the case of %s", test.msg) 330 } 331 } 332 } 333 334 func TestValidateRateLimit(t *testing.T) { 335 dryRun := true 336 noDelay := false 337 338 tests := []struct { 339 rateLimit *v1.RateLimit 340 msg string 341 }{ 342 { 343 rateLimit: &v1.RateLimit{ 344 Rate: "10r/s", 345 ZoneSize: "10M", 346 Key: "${request_uri}", 347 }, 348 msg: "only required fields are set", 349 }, 350 { 351 rateLimit: &v1.RateLimit{ 352 Rate: "30r/m", 353 Key: "${request_uri}", 354 Delay: createPointerFromInt(5), 355 NoDelay: &noDelay, 356 Burst: createPointerFromInt(10), 357 ZoneSize: "10M", 358 DryRun: &dryRun, 359 LogLevel: "info", 360 RejectCode: createPointerFromInt(505), 361 }, 362 msg: "ratelimit all fields set", 363 }, 364 } 365 366 isPlus := false 367 368 for _, test := range tests { 369 allErrs := validateRateLimit(test.rateLimit, field.NewPath("rateLimit"), isPlus) 370 if len(allErrs) > 0 { 371 t.Errorf("validateRateLimit() returned errors %v for valid input for the case of %v", allErrs, test.msg) 372 } 373 } 374 } 375 376 func createInvalidRateLimit(f func(r *v1.RateLimit)) *v1.RateLimit { 377 validRateLimit := &v1.RateLimit{ 378 Rate: "10r/s", 379 ZoneSize: "10M", 380 Key: "${request_uri}", 381 } 382 f(validRateLimit) 383 return validRateLimit 384 } 385 386 func TestValidateRateLimitFails(t *testing.T) { 387 tests := []struct { 388 rateLimit *v1.RateLimit 389 msg string 390 }{ 391 { 392 rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) { 393 r.Rate = "0r/s" 394 }), 395 msg: "invalid rateLimit rate", 396 }, 397 { 398 rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) { 399 r.Key = "${fail}" 400 }), 401 msg: "invalid rateLimit key variable use", 402 }, 403 { 404 rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) { 405 r.Delay = createPointerFromInt(0) 406 }), 407 msg: "invalid rateLimit delay", 408 }, 409 { 410 rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) { 411 r.Burst = createPointerFromInt(0) 412 }), 413 msg: "invalid rateLimit burst", 414 }, 415 { 416 rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) { 417 r.ZoneSize = "31k" 418 }), 419 msg: "invalid rateLimit zoneSize", 420 }, 421 { 422 rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) { 423 r.RejectCode = createPointerFromInt(600) 424 }), 425 msg: "invalid rateLimit rejectCode", 426 }, 427 { 428 rateLimit: createInvalidRateLimit(func(r *v1.RateLimit) { 429 r.LogLevel = "invalid" 430 }), 431 msg: "invalid rateLimit logLevel", 432 }, 433 } 434 435 isPlus := false 436 437 for _, test := range tests { 438 allErrs := validateRateLimit(test.rateLimit, field.NewPath("rateLimit"), isPlus) 439 if len(allErrs) == 0 { 440 t.Errorf("validateRateLimit() returned no errors for invalid input for the case of %v", test.msg) 441 } 442 } 443 } 444 445 func TestValidateJWT(t *testing.T) { 446 tests := []struct { 447 jwt *v1.JWTAuth 448 msg string 449 }{ 450 { 451 jwt: &v1.JWTAuth{ 452 Realm: "My Product API", 453 Secret: "my-jwk", 454 }, 455 msg: "basic", 456 }, 457 { 458 jwt: &v1.JWTAuth{ 459 Realm: "My Product API", 460 Secret: "my-jwk", 461 Token: "$cookie_auth_token", 462 }, 463 msg: "jwt with token", 464 }, 465 } 466 for _, test := range tests { 467 allErrs := validateJWT(test.jwt, field.NewPath("jwt")) 468 if len(allErrs) != 0 { 469 t.Errorf("validateJWT() returned errors %v for valid input for the case of %v", allErrs, test.msg) 470 } 471 } 472 } 473 474 func TestValidateJWTFails(t *testing.T) { 475 tests := []struct { 476 msg string 477 jwt *v1.JWTAuth 478 }{ 479 { 480 jwt: &v1.JWTAuth{ 481 Realm: "My Product API", 482 }, 483 msg: "missing secret", 484 }, 485 { 486 jwt: &v1.JWTAuth{ 487 Secret: "my-jwk", 488 }, 489 msg: "missing realm", 490 }, 491 { 492 jwt: &v1.JWTAuth{ 493 Realm: "My Product API", 494 Secret: "my-jwk", 495 Token: "$uri", 496 }, 497 msg: "invalid variable use in token", 498 }, 499 { 500 jwt: &v1.JWTAuth{ 501 Realm: "My Product API", 502 Secret: "my-\"jwk", 503 }, 504 msg: "invalid secret name", 505 }, 506 { 507 jwt: &v1.JWTAuth{ 508 Realm: "My \"Product API", 509 Secret: "my-jwk", 510 }, 511 msg: "invalid realm due to escaped string", 512 }, 513 { 514 jwt: &v1.JWTAuth{ 515 Realm: "My Product ${api}", 516 Secret: "my-jwk", 517 }, 518 msg: "invalid variable use in realm with curly braces", 519 }, 520 { 521 jwt: &v1.JWTAuth{ 522 Realm: "My Product $api", 523 Secret: "my-jwk", 524 }, 525 msg: "invalid variable use in realm without curly braces", 526 }, 527 } 528 for _, test := range tests { 529 allErrs := validateJWT(test.jwt, field.NewPath("jwt")) 530 if len(allErrs) == 0 { 531 t.Errorf("validateJWT() returned no errors for invalid input for the case of %v", test.msg) 532 } 533 } 534 } 535 536 func TestValidateIPorCIDR(t *testing.T) { 537 validInput := []string{ 538 "192.168.1.1", 539 "192.168.1.0/24", 540 "2001:0db8::1", 541 "2001:0db8::/32", 542 } 543 544 for _, input := range validInput { 545 allErrs := validateIPorCIDR(input, field.NewPath("ipOrCIDR")) 546 if len(allErrs) > 0 { 547 t.Errorf("validateIPorCIDR(%q) returned errors %v for valid input", input, allErrs) 548 } 549 } 550 551 invalidInput := []string{ 552 "localhost", 553 "192.168.1.0/", 554 "2001:0db8:::1", 555 "2001:0db8::/", 556 } 557 558 for _, input := range invalidInput { 559 allErrs := validateIPorCIDR(input, field.NewPath("ipOrCIDR")) 560 if len(allErrs) == 0 { 561 t.Errorf("validateIPorCIDR(%q) returned no errors for invalid input", input) 562 } 563 } 564 } 565 566 func TestValidateRate(t *testing.T) { 567 validInput := []string{ 568 "10r/s", 569 "100r/m", 570 "1r/s", 571 } 572 573 for _, input := range validInput { 574 allErrs := validateRate(input, field.NewPath("rate")) 575 if len(allErrs) > 0 { 576 t.Errorf("validateRate(%q) returned errors %v for valid input", input, allErrs) 577 } 578 } 579 580 invalidInput := []string{ 581 "10s", 582 "10r/", 583 "10r/ms", 584 "0r/s", 585 } 586 587 for _, input := range invalidInput { 588 allErrs := validateRate(input, field.NewPath("rate")) 589 if len(allErrs) == 0 { 590 t.Errorf("validateRate(%q) returned no errors for invalid input", input) 591 } 592 } 593 } 594 595 func TestValidatePositiveInt(t *testing.T) { 596 validInput := []int{1, 2} 597 598 for _, input := range validInput { 599 allErrs := validatePositiveInt(input, field.NewPath("int")) 600 if len(allErrs) > 0 { 601 t.Errorf("validatePositiveInt(%q) returned errors %v for valid input", input, allErrs) 602 } 603 } 604 605 invalidInput := []int{-1, 0} 606 607 for _, input := range invalidInput { 608 allErrs := validatePositiveInt(input, field.NewPath("int")) 609 if len(allErrs) == 0 { 610 t.Errorf("validatePositiveInt(%q) returned no errors for invalid input", input) 611 } 612 } 613 } 614 615 func TestValidateRateLimitZoneSize(t *testing.T) { 616 validInput := []string{"32", "32k", "32K", "10m"} 617 618 for _, test := range validInput { 619 allErrs := validateRateLimitZoneSize(test, field.NewPath("size")) 620 if len(allErrs) != 0 { 621 t.Errorf("validateRateLimitZoneSize(%q) returned an error for valid input", test) 622 } 623 } 624 625 invalidInput := []string{"", "31", "31k", "0", "0M"} 626 627 for _, test := range invalidInput { 628 allErrs := validateRateLimitZoneSize(test, field.NewPath("size")) 629 if len(allErrs) == 0 { 630 t.Errorf("validateRateLimitZoneSize(%q) didn't return error for invalid input", test) 631 } 632 } 633 } 634 635 func TestValidateRateLimitLogLevel(t *testing.T) { 636 validInput := []string{"error", "info", "warn", "notice"} 637 638 for _, test := range validInput { 639 allErrs := validateRateLimitLogLevel(test, field.NewPath("logLevel")) 640 if len(allErrs) != 0 { 641 t.Errorf("validateRateLimitLogLevel(%q) returned an error for valid input", test) 642 } 643 } 644 645 invalidInput := []string{"warn ", "info error", ""} 646 647 for _, test := range invalidInput { 648 allErrs := validateRateLimitLogLevel(test, field.NewPath("logLevel")) 649 if len(allErrs) == 0 { 650 t.Errorf("validateRateLimitLogLevel(%q) didn't return error for invalid input", test) 651 } 652 } 653 } 654 655 func TestValidateJWTToken(t *testing.T) { 656 validTests := []struct { 657 token string 658 msg string 659 }{ 660 { 661 token: "", 662 msg: "no token set", 663 }, 664 { 665 token: "$http_token", 666 msg: "http special variable usage", 667 }, 668 { 669 token: "$arg_token", 670 msg: "arg special variable usage", 671 }, 672 { 673 token: "$cookie_token", 674 msg: "cookie special variable usage", 675 }, 676 } 677 for _, test := range validTests { 678 allErrs := validateJWTToken(test.token, field.NewPath("token")) 679 if len(allErrs) != 0 { 680 t.Errorf("validateJWTToken(%v) returned an error for valid input for the case of %v", test.token, test.msg) 681 } 682 } 683 684 invalidTests := []struct { 685 token string 686 msg string 687 }{ 688 { 689 token: "http_token", 690 msg: "missing $ prefix", 691 }, 692 { 693 token: "${http_token}", 694 msg: "usage of $ and curly braces", 695 }, 696 { 697 token: "$http_token$http_token", 698 msg: "multi variable usage", 699 }, 700 { 701 token: "something$http_token", 702 msg: "non variable usage", 703 }, 704 { 705 token: "$uri", 706 msg: "non special variable usage", 707 }, 708 } 709 for _, test := range invalidTests { 710 allErrs := validateJWTToken(test.token, field.NewPath("token")) 711 if len(allErrs) == 0 { 712 t.Errorf("validateJWTToken(%v) didn't return error for invalid input for the case of %v", test.token, test.msg) 713 } 714 } 715 } 716 717 func TestValidateIngressMTLS(t *testing.T) { 718 tests := []struct { 719 ing *v1.IngressMTLS 720 msg string 721 }{ 722 { 723 ing: &v1.IngressMTLS{ 724 ClientCertSecret: "mtls-secret", 725 }, 726 msg: "default", 727 }, 728 { 729 ing: &v1.IngressMTLS{ 730 ClientCertSecret: "mtls-secret", 731 VerifyClient: "on", 732 VerifyDepth: createPointerFromInt(1), 733 }, 734 msg: "all parameters with default value", 735 }, 736 { 737 ing: &v1.IngressMTLS{ 738 ClientCertSecret: "ingress-mtls-secret", 739 VerifyClient: "optional", 740 VerifyDepth: createPointerFromInt(2), 741 }, 742 msg: "optional parameters", 743 }, 744 } 745 for _, test := range tests { 746 allErrs := validateIngressMTLS(test.ing, field.NewPath("ingressMTLS")) 747 if len(allErrs) != 0 { 748 t.Errorf("validateIngressMTLS() returned errors %v for valid input for the case of %v", allErrs, test.msg) 749 } 750 } 751 } 752 753 func TestValidateIngressMTLSInvalid(t *testing.T) { 754 tests := []struct { 755 ing *v1.IngressMTLS 756 msg string 757 }{ 758 { 759 ing: &v1.IngressMTLS{ 760 VerifyClient: "on", 761 }, 762 msg: "no secret", 763 }, 764 { 765 ing: &v1.IngressMTLS{ 766 ClientCertSecret: "-foo-", 767 }, 768 msg: "invalid secret name", 769 }, 770 { 771 ing: &v1.IngressMTLS{ 772 ClientCertSecret: "mtls-secret", 773 VerifyClient: "foo", 774 }, 775 msg: "invalid verify client", 776 }, 777 { 778 ing: &v1.IngressMTLS{ 779 ClientCertSecret: "ingress-mtls-secret", 780 VerifyClient: "on", 781 VerifyDepth: createPointerFromInt(-1), 782 }, 783 msg: "invalid depth", 784 }, 785 } 786 for _, test := range tests { 787 allErrs := validateIngressMTLS(test.ing, field.NewPath("ingressMTLS")) 788 if len(allErrs) == 0 { 789 t.Errorf("validateIngressMTLS() returned no errors for invalid input for the case of %v", test.msg) 790 } 791 } 792 } 793 794 func TestValidateIngressMTLSVerifyClient(t *testing.T) { 795 validInput := []string{"on", "off", "optional", "optional_no_ca"} 796 797 for _, test := range validInput { 798 allErrs := validateIngressMTLSVerifyClient(test, field.NewPath("verifyClient")) 799 if len(allErrs) != 0 { 800 t.Errorf("validateIngressMTLSVerifyClient(%q) returned errors %v for valid input", allErrs, test) 801 } 802 } 803 804 invalidInput := []string{"true", "false"} 805 806 for _, test := range invalidInput { 807 allErrs := validateIngressMTLSVerifyClient(test, field.NewPath("verifyClient")) 808 if len(allErrs) == 0 { 809 t.Errorf("validateIngressMTLSVerifyClient(%q) didn't return error for invalid input", test) 810 } 811 } 812 } 813 814 func TestValidateEgressMTLS(t *testing.T) { 815 tests := []struct { 816 eg *v1.EgressMTLS 817 msg string 818 }{ 819 { 820 eg: &v1.EgressMTLS{ 821 TLSSecret: "mtls-secret", 822 }, 823 msg: "tls secret", 824 }, 825 { 826 eg: &v1.EgressMTLS{ 827 TrustedCertSecret: "tls-secret", 828 VerifyServer: true, 829 VerifyDepth: createPointerFromInt(2), 830 ServerName: false, 831 }, 832 msg: "verify server set to true", 833 }, 834 { 835 eg: &v1.EgressMTLS{ 836 VerifyServer: false, 837 }, 838 msg: "verify server set to false", 839 }, 840 { 841 eg: &v1.EgressMTLS{ 842 SSLName: "foo.com", 843 }, 844 msg: "ssl name", 845 }, 846 } 847 for _, test := range tests { 848 allErrs := validateEgressMTLS(test.eg, field.NewPath("egressMTLS")) 849 if len(allErrs) != 0 { 850 t.Errorf("validateEgressMTLS() returned errors %v for valid input for the case of %v", allErrs, test.msg) 851 } 852 } 853 } 854 855 func TestValidateEgressMTLSInvalid(t *testing.T) { 856 tests := []struct { 857 eg *v1.EgressMTLS 858 msg string 859 }{ 860 { 861 eg: &v1.EgressMTLS{ 862 VerifyServer: true, 863 }, 864 msg: "verify server set to true", 865 }, 866 { 867 eg: &v1.EgressMTLS{ 868 TrustedCertSecret: "-foo-", 869 }, 870 msg: "invalid secret name", 871 }, 872 { 873 eg: &v1.EgressMTLS{ 874 TrustedCertSecret: "ingress-mtls-secret", 875 VerifyServer: true, 876 VerifyDepth: createPointerFromInt(-1), 877 }, 878 msg: "invalid depth", 879 }, 880 { 881 eg: &v1.EgressMTLS{ 882 SSLName: "foo.com;", 883 }, 884 msg: "invalid name", 885 }, 886 } 887 888 for _, test := range tests { 889 allErrs := validateEgressMTLS(test.eg, field.NewPath("egressMTLS")) 890 if len(allErrs) == 0 { 891 t.Errorf("validateEgressMTLS() returned no errors for invalid input for the case of %v", test.msg) 892 } 893 } 894 } 895 896 func TestValidateOIDCValid(t *testing.T) { 897 tests := []struct { 898 oidc *v1.OIDC 899 msg string 900 }{ 901 { 902 oidc: &v1.OIDC{ 903 AuthEndpoint: "https://accounts.google.com/o/oauth2/v2/auth", 904 TokenEndpoint: "https://oauth2.googleapis.com/token", 905 JWKSURI: "https://www.googleapis.com/oauth2/v3/certs", 906 ClientID: "random-string", 907 ClientSecret: "random-secret", 908 Scope: "openid", 909 RedirectURI: "/foo", 910 }, 911 msg: "verify full oidc", 912 }, 913 { 914 oidc: &v1.OIDC{ 915 AuthEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/authorize", 916 TokenEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/token", 917 JWKSURI: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/discovery/v2.0/keys", 918 ClientID: "ff", 919 ClientSecret: "ff", 920 Scope: "openid+profile", 921 RedirectURI: "/_codexe", 922 }, 923 msg: "verify azure endpoint", 924 }, 925 { 926 oidc: &v1.OIDC{ 927 AuthEndpoint: "http://keycloak.default.svc.cluster.local:8080/auth/realms/master/protocol/openid-connect/auth", 928 TokenEndpoint: "http://keycloak.default.svc.cluster.local:8080/auth/realms/master/protocol/openid-connect/token", 929 JWKSURI: "http://keycloak.default.svc.cluster.local:8080/auth/realms/master/protocol/openid-connect/certs", 930 ClientID: "bar", 931 ClientSecret: "foo", 932 Scope: "openid", 933 }, 934 msg: "domain with port number", 935 }, 936 { 937 oidc: &v1.OIDC{ 938 AuthEndpoint: "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/auth", 939 TokenEndpoint: "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/token", 940 JWKSURI: "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/certs", 941 ClientID: "client", 942 ClientSecret: "secret", 943 Scope: "openid", 944 }, 945 msg: "ip address", 946 }, 947 } 948 949 for _, test := range tests { 950 allErrs := validateOIDC(test.oidc, field.NewPath("oidc")) 951 if len(allErrs) != 0 { 952 t.Errorf("validateOIDC() returned errors %v for valid input for the case of %v", allErrs, test.msg) 953 } 954 } 955 } 956 957 func TestValidateOIDCInvalid(t *testing.T) { 958 tests := []struct { 959 oidc *v1.OIDC 960 msg string 961 }{ 962 { 963 oidc: &v1.OIDC{ 964 RedirectURI: "/foo", 965 }, 966 msg: "missing required field auth", 967 }, 968 { 969 oidc: &v1.OIDC{ 970 AuthEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/authorize", 971 JWKSURI: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/discovery/v2.0/keys", 972 ClientID: "ff", 973 ClientSecret: "ff", 974 Scope: "openid+profile", 975 }, 976 msg: "missing required field token", 977 }, 978 { 979 oidc: &v1.OIDC{ 980 AuthEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/authorize", 981 TokenEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/token", 982 ClientID: "ff", 983 ClientSecret: "ff", 984 Scope: "openid+profile", 985 }, 986 msg: "missing required field jwk", 987 }, 988 { 989 oidc: &v1.OIDC{ 990 AuthEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/authorize", 991 TokenEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/token", 992 JWKSURI: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/discovery/v2.0/keys", 993 ClientSecret: "ff", 994 Scope: "openid+profile", 995 }, 996 msg: "missing required field clientid", 997 }, 998 { 999 oidc: &v1.OIDC{ 1000 AuthEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/authorize", 1001 TokenEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/token", 1002 JWKSURI: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/discovery/v2.0/keys", 1003 ClientID: "ff", 1004 Scope: "openid+profile", 1005 }, 1006 msg: "missing required field client secret", 1007 }, 1008 1009 { 1010 oidc: &v1.OIDC{ 1011 AuthEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/authorize", 1012 TokenEndpoint: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/oauth2/v2.0/token", 1013 JWKSURI: "https://login.microsoftonline.com/dd-fff-eee-1234-9be/discovery/v2.0/keys", 1014 ClientID: "ff", 1015 ClientSecret: "-ff-", 1016 Scope: "openid+profile", 1017 }, 1018 msg: "invalid secret name", 1019 }, 1020 { 1021 oidc: &v1.OIDC{ 1022 AuthEndpoint: "http://foo.\bar.com", 1023 TokenEndpoint: "http://keycloak.default", 1024 JWKSURI: "http://keycloak.default", 1025 ClientID: "bar", 1026 ClientSecret: "foo", 1027 Scope: "openid", 1028 }, 1029 msg: "invalid URL", 1030 }, 1031 { 1032 oidc: &v1.OIDC{ 1033 AuthEndpoint: "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/auth", 1034 TokenEndpoint: "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/token", 1035 JWKSURI: "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/certs", 1036 ClientID: "$foo$bar", 1037 ClientSecret: "secret", 1038 Scope: "openid", 1039 }, 1040 msg: "invalid chars in clientID", 1041 }, 1042 } 1043 1044 for _, test := range tests { 1045 allErrs := validateOIDC(test.oidc, field.NewPath("oidc")) 1046 if len(allErrs) == 0 { 1047 t.Errorf("validateOIDC() returned no errors for invalid input for the case of %v", test.msg) 1048 } 1049 } 1050 } 1051 1052 func TestValidateClientID(t *testing.T) { 1053 validInput := []string{"myid", "your.id", "id-sf-sjfdj.com", "foo_bar~vni"} 1054 1055 for _, test := range validInput { 1056 allErrs := validateClientID(test, field.NewPath("clientID")) 1057 if len(allErrs) != 0 { 1058 t.Errorf("validateClientID(%q) returned errors %v for valid input", allErrs, test) 1059 } 1060 } 1061 1062 invalidInput := []string{"$boo", "foo$bar", `foo_bar"vni`, `client\`} 1063 1064 for _, test := range invalidInput { 1065 allErrs := validateClientID(test, field.NewPath("clientID")) 1066 if len(allErrs) == 0 { 1067 t.Errorf("validateClientID(%q) didn't return error for invalid input", test) 1068 } 1069 } 1070 } 1071 1072 func TestValidateOIDCScope(t *testing.T) { 1073 validInput := []string{"openid", "openid+profile", "openid+email", "openid+phone"} 1074 1075 for _, test := range validInput { 1076 allErrs := validateOIDCScope(test, field.NewPath("scope")) 1077 if len(allErrs) != 0 { 1078 t.Errorf("validateOIDCScope(%q) returned errors %v for valid input", allErrs, test) 1079 } 1080 } 1081 1082 invalidInput := []string{"profile", "openid+web", `openid+foobar.com`} 1083 1084 for _, test := range invalidInput { 1085 allErrs := validateOIDCScope(test, field.NewPath("scope")) 1086 if len(allErrs) == 0 { 1087 t.Errorf("validateOIDCScope(%q) didn't return error for invalid input", test) 1088 } 1089 } 1090 } 1091 1092 func TestValidateURL(t *testing.T) { 1093 validInput := []string{"http://google.com/auth", "https://foo.bar/baz", "http://127.0.0.1/bar", "http://openid.connect.com:8080/foo"} 1094 1095 for _, test := range validInput { 1096 allErrs := validateURL(test, field.NewPath("authEndpoint")) 1097 if len(allErrs) != 0 { 1098 t.Errorf("validateURL(%q) returned errors %v for valid input", allErrs, test) 1099 } 1100 } 1101 1102 invalidInput := []string{"www.google..foo.com", "http://{foo.bar", `https://google.foo\bar`, "http://foo.bar:8080", "http://foo.bar:812345/fooo"} 1103 1104 for _, test := range invalidInput { 1105 allErrs := validateURL(test, field.NewPath("authEndpoint")) 1106 if len(allErrs) == 0 { 1107 t.Errorf("validateURL(%q) didn't return error for invalid input", test) 1108 } 1109 } 1110 } 1111 1112 func TestValidateWAF(t *testing.T) { 1113 tests := []struct { 1114 waf *v1.WAF 1115 msg string 1116 }{ 1117 { 1118 waf: &v1.WAF{ 1119 Enable: true, 1120 }, 1121 msg: "waf enabled", 1122 }, 1123 { 1124 waf: &v1.WAF{ 1125 Enable: true, 1126 ApPolicy: "ns1/waf-pol", 1127 }, 1128 msg: "cross ns reference", 1129 }, 1130 { 1131 waf: &v1.WAF{ 1132 Enable: true, 1133 SecurityLog: &v1.SecurityLog{ 1134 Enable: true, 1135 LogDest: "syslog:server=8.7.7.7:517", 1136 }, 1137 }, 1138 msg: "custom logdest", 1139 }, 1140 } 1141 1142 for _, test := range tests { 1143 allErrs := validateWAF(test.waf, field.NewPath("waf")) 1144 if len(allErrs) != 0 { 1145 t.Errorf("validateWAF() returned errors %v for valid input for the case of %v", allErrs, test.msg) 1146 } 1147 } 1148 } 1149 1150 func TestValidateWAFInvalid(t *testing.T) { 1151 tests := []struct { 1152 waf *v1.WAF 1153 msg string 1154 }{ 1155 { 1156 waf: &v1.WAF{ 1157 Enable: true, 1158 ApPolicy: "ns1/ap-pol/ns2", 1159 }, 1160 msg: "invalid apPolicy format", 1161 }, 1162 { 1163 waf: &v1.WAF{ 1164 Enable: true, 1165 SecurityLog: &v1.SecurityLog{ 1166 Enable: true, 1167 LogDest: "stdout", 1168 }, 1169 }, 1170 msg: "invalid logdest", 1171 }, 1172 { 1173 waf: &v1.WAF{ 1174 Enable: true, 1175 SecurityLog: &v1.SecurityLog{ 1176 Enable: true, 1177 ApLogConf: "ns1/log-conf/ns2", 1178 }, 1179 }, 1180 msg: "invalid logConf format", 1181 }, 1182 } 1183 1184 for _, test := range tests { 1185 allErrs := validateWAF(test.waf, field.NewPath("waf")) 1186 if len(allErrs) == 0 { 1187 t.Errorf("validateWAF() returned no errors for invalid input for the case of %v", test.msg) 1188 } 1189 } 1190 }