istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/validation/validation_test.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package validation 16 17 import ( 18 "errors" 19 "fmt" 20 "strings" 21 "testing" 22 "time" 23 24 "google.golang.org/protobuf/proto" 25 "google.golang.org/protobuf/types/known/durationpb" 26 "google.golang.org/protobuf/types/known/wrapperspb" 27 28 extensions "istio.io/api/extensions/v1alpha1" 29 networking "istio.io/api/networking/v1alpha3" 30 networkingv1beta1 "istio.io/api/networking/v1beta1" 31 security_beta "istio.io/api/security/v1beta1" 32 telemetry "istio.io/api/telemetry/v1alpha1" 33 api "istio.io/api/type/v1beta1" 34 "istio.io/istio/pkg/config" 35 "istio.io/istio/pkg/config/constants" 36 "istio.io/istio/pkg/config/schema/gvk" 37 "istio.io/istio/pkg/test/util/assert" 38 ) 39 40 const ( 41 // Config name for testing 42 someName = "foo" 43 // Config namespace for testing. 44 someNamespace = "bar" 45 ) 46 47 func TestValidateMaxServerConnectionAge(t *testing.T) { 48 type durationCheck struct { 49 duration time.Duration 50 isValid bool 51 } 52 durMin, _ := time.ParseDuration("-30m") 53 durHr, _ := time.ParseDuration("-1.5h") 54 checks := []durationCheck{ 55 { 56 duration: 30 * time.Minute, 57 isValid: true, 58 }, 59 { 60 duration: durMin, 61 isValid: false, 62 }, 63 { 64 duration: durHr, 65 isValid: false, 66 }, 67 } 68 69 for _, check := range checks { 70 if got := ValidateMaxServerConnectionAge(check.duration); (got == nil) != check.isValid { 71 t.Errorf("Failed: got valid=%t but wanted valid=%t: %v for %v", got == nil, check.isValid, got, check.duration) 72 } 73 } 74 } 75 76 func TestValidateGateway(t *testing.T) { 77 tests := []struct { 78 name string 79 in proto.Message 80 out string 81 warning string 82 }{ 83 {"empty", &networking.Gateway{}, "server", ""}, 84 {"invalid message", &networking.Server{}, "cannot cast", ""}, 85 { 86 "happy domain", 87 &networking.Gateway{ 88 Servers: []*networking.Server{{ 89 Hosts: []string{"foo.bar.com"}, 90 Port: &networking.Port{Name: "name1", Number: 7, Protocol: "http"}, 91 }}, 92 }, 93 "", "", 94 }, 95 { 96 "happy multiple servers", 97 &networking.Gateway{ 98 Servers: []*networking.Server{ 99 { 100 Hosts: []string{"foo.bar.com"}, 101 Port: &networking.Port{Name: "name1", Number: 7, Protocol: "http"}, 102 }, 103 }, 104 }, 105 "", "", 106 }, 107 { 108 "happy k8s gateway-api server with no attached routes", 109 &networking.Gateway{ 110 Servers: []*networking.Server{{ 111 Hosts: []string{"~/foo.bar.com"}, 112 Port: &networking.Port{Name: "name1", Number: 7, Protocol: "http"}, 113 }}, 114 }, 115 "invalid namespace value", "", 116 }, 117 { 118 "invalid port", 119 &networking.Gateway{ 120 Servers: []*networking.Server{ 121 { 122 Hosts: []string{"foo.bar.com"}, 123 Port: &networking.Port{Name: "name1", Number: 66000, Protocol: "http"}, 124 }, 125 }, 126 }, 127 "port", "", 128 }, 129 { 130 "duplicate port names", 131 &networking.Gateway{ 132 Servers: []*networking.Server{ 133 { 134 Hosts: []string{"foo.bar.com"}, 135 Port: &networking.Port{Name: "foo", Number: 80, Protocol: "http"}, 136 }, 137 { 138 Hosts: []string{"scooby.doo.com"}, 139 Port: &networking.Port{Name: "foo", Number: 8080, Protocol: "http"}, 140 }, 141 }, 142 }, 143 "port names", "", 144 }, 145 { 146 "invalid domain", 147 &networking.Gateway{ 148 Servers: []*networking.Server{ 149 { 150 Hosts: []string{"foo.*.bar.com"}, 151 Port: &networking.Port{Number: 7, Protocol: "http"}, 152 }, 153 }, 154 }, 155 "domain", "", 156 }, 157 { 158 "valid httpsRedirect", 159 &networking.Gateway{ 160 Servers: []*networking.Server{ 161 { 162 Hosts: []string{"bar.com"}, 163 Port: &networking.Port{Name: "http", Number: 80, Protocol: "http"}, 164 Tls: &networking.ServerTLSSettings{HttpsRedirect: true}, 165 }, 166 }, 167 }, 168 "", "", 169 }, 170 { 171 "invalid https httpsRedirect", 172 &networking.Gateway{ 173 Servers: []*networking.Server{ 174 { 175 Hosts: []string{"bar.com"}, 176 Port: &networking.Port{Name: "https", Number: 80, Protocol: "https"}, 177 Tls: &networking.ServerTLSSettings{HttpsRedirect: true}, 178 }, 179 }, 180 }, 181 "", "tls.httpsRedirect should only be used with http servers", 182 }, 183 { 184 "invalid partial wildcard", 185 &networking.Gateway{ 186 Servers: []*networking.Server{ 187 { 188 Hosts: []string{"*bar.com"}, 189 Port: &networking.Port{Name: "tls", Number: 443, Protocol: "tls"}, 190 Tls: &networking.ServerTLSSettings{Mode: networking.ServerTLSSettings_ISTIO_MUTUAL}, 191 }, 192 }, 193 }, 194 "partial wildcard \"*bar.com\" not allowed", "", 195 }, 196 } 197 for _, tt := range tests { 198 t.Run(tt.name, func(t *testing.T) { 199 warn, err := ValidateGateway(config.Config{ 200 Meta: config.Meta{ 201 Name: someName, 202 Namespace: someNamespace, 203 }, 204 Spec: tt.in, 205 }) 206 checkValidationMessage(t, warn, err, tt.warning, tt.out) 207 }) 208 } 209 } 210 211 func TestValidateK8sGateway(t *testing.T) { 212 tests := []struct { 213 name string 214 in proto.Message 215 out string 216 warning string 217 }{ 218 { 219 "happy k8s gateway-api server with no attached routes", 220 &networking.Gateway{ 221 Servers: []*networking.Server{{ 222 Hosts: []string{"~/foo.bar.com"}, 223 Port: &networking.Port{Name: "name1", Number: 7, Protocol: "http"}, 224 }}, 225 }, 226 "", "", 227 }, 228 } 229 for _, tt := range tests { 230 t.Run(tt.name, func(t *testing.T) { 231 annotations := map[string]string{} 232 annotations[constants.InternalGatewaySemantics] = constants.GatewaySemanticsGateway 233 234 warn, err := ValidateGateway(config.Config{ 235 Meta: config.Meta{ 236 Name: someName, 237 Namespace: someNamespace, 238 Annotations: annotations, 239 }, 240 Spec: tt.in, 241 }) 242 checkValidationMessage(t, warn, err, tt.warning, tt.out) 243 }) 244 } 245 } 246 247 func TestValidateServer(t *testing.T) { 248 tests := []struct { 249 name string 250 in *networking.Server 251 out string 252 }{ 253 {"empty", &networking.Server{}, "host"}, 254 {"empty", &networking.Server{}, "port"}, 255 { 256 "happy", 257 &networking.Server{ 258 Hosts: []string{"foo.bar.com"}, 259 Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, 260 }, 261 "", 262 }, 263 { 264 "happy ip", 265 &networking.Server{ 266 Hosts: []string{"1.1.1.1"}, 267 Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, 268 }, 269 "", 270 }, 271 { 272 "happy ns/name", 273 &networking.Server{ 274 Hosts: []string{"ns1/foo.bar.com"}, 275 Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, 276 }, 277 "", 278 }, 279 { 280 "happy */name", 281 &networking.Server{ 282 Hosts: []string{"*/foo.bar.com"}, 283 Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, 284 }, 285 "", 286 }, 287 { 288 "happy ./name", 289 &networking.Server{ 290 Hosts: []string{"./foo.bar.com"}, 291 Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, 292 }, 293 "", 294 }, 295 { 296 "invalid ~/name", 297 &networking.Server{ 298 Hosts: []string{"~/foo.bar.com"}, 299 Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, 300 }, 301 "namespace", 302 }, 303 { 304 "invalid domain ns/name format", 305 &networking.Server{ 306 Hosts: []string{"ns1/foo.*.bar.com"}, 307 Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, 308 }, 309 "domain", 310 }, 311 { 312 "invalid domain", 313 &networking.Server{ 314 Hosts: []string{"foo.*.bar.com"}, 315 Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, 316 }, 317 "domain", 318 }, 319 { 320 "invalid short name host", 321 &networking.Server{ 322 Hosts: []string{"foo"}, 323 Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, 324 }, 325 "short names", 326 }, 327 { 328 "invalid port", 329 &networking.Server{ 330 Hosts: []string{"foo.bar.com"}, 331 Port: &networking.Port{Number: 66000, Name: "http", Protocol: "http"}, 332 }, 333 "port", 334 }, 335 { 336 "invalid tls options", 337 &networking.Server{ 338 Hosts: []string{"foo.bar.com"}, 339 Port: &networking.Port{Number: 1, Name: "http", Protocol: "http"}, 340 Tls: &networking.ServerTLSSettings{Mode: networking.ServerTLSSettings_SIMPLE}, 341 }, 342 "TLS", 343 }, 344 { 345 "no tls on HTTPS", 346 &networking.Server{ 347 Hosts: []string{"foo.bar.com"}, 348 Port: &networking.Port{Number: 10000, Name: "https", Protocol: "https"}, 349 }, 350 "must have TLS", 351 }, 352 { 353 "tls on HTTP", 354 &networking.Server{ 355 Hosts: []string{"foo.bar.com"}, 356 Port: &networking.Port{Number: 10000, Name: "http", Protocol: "http"}, 357 Tls: &networking.ServerTLSSettings{Mode: networking.ServerTLSSettings_SIMPLE}, 358 }, 359 "cannot have TLS", 360 }, 361 { 362 "tls redirect on HTTP", 363 &networking.Server{ 364 Hosts: []string{"foo.bar.com"}, 365 Port: &networking.Port{Number: 10000, Name: "http", Protocol: "http"}, 366 Tls: &networking.ServerTLSSettings{ 367 HttpsRedirect: true, 368 }, 369 }, 370 "", 371 }, 372 { 373 "bind ip", 374 &networking.Server{ 375 Hosts: []string{"foo.bar.com"}, 376 Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, 377 Bind: "127.0.0.1", 378 }, 379 "", 380 }, 381 { 382 "bind unix path with invalid port", 383 &networking.Server{ 384 Hosts: []string{"foo.bar.com"}, 385 Port: &networking.Port{Number: 7, Name: "http", Protocol: "http"}, 386 Bind: "unix://@foobar", 387 }, 388 "port number must be 0 for unix domain socket", 389 }, 390 { 391 "bind unix path", 392 &networking.Server{ 393 Hosts: []string{"foo.bar.com"}, 394 Port: &networking.Port{Number: 0, Name: "http", Protocol: "http"}, 395 Bind: "unix://@foobar", 396 }, 397 "", 398 }, 399 { 400 "bind bad ip", 401 &networking.Server{ 402 Hosts: []string{"foo.bar.com"}, 403 Port: &networking.Port{Number: 0, Name: "http", Protocol: "http"}, 404 Bind: "foo.bar", 405 }, 406 "foo.bar is not a valid IP", 407 }, 408 } 409 for _, tt := range tests { 410 t.Run(tt.name, func(t *testing.T) { 411 v := validateServer(tt.in, false) 412 warn, err := v.Unwrap() 413 checkValidationMessage(t, warn, err, "", tt.out) 414 }) 415 } 416 } 417 418 func TestValidateServerPort(t *testing.T) { 419 tests := []struct { 420 name string 421 in *networking.Port 422 bind string 423 out string 424 }{ 425 {"empty", &networking.Port{}, "", "invalid protocol"}, 426 {"empty", &networking.Port{}, "", "port name"}, 427 { 428 "happy", 429 &networking.Port{ 430 Protocol: "http", 431 Number: 1, 432 Name: "Henry", 433 }, 434 "", 435 "", 436 }, 437 { 438 "invalid protocol", 439 &networking.Port{ 440 Protocol: "kafka", 441 Number: 1, 442 Name: "Henry", 443 }, 444 "", 445 "invalid protocol", 446 }, 447 { 448 "invalid number", 449 &networking.Port{ 450 Protocol: "http", 451 Number: uint32(1 << 30), 452 Name: "http", 453 }, 454 "", 455 "port number", 456 }, 457 { 458 "name, no number", 459 &networking.Port{ 460 Protocol: "http", 461 Number: 0, 462 Name: "Henry", 463 }, 464 "", 465 "port number", 466 }, 467 { 468 "name, no number, uds", 469 &networking.Port{ 470 Protocol: "http", 471 Number: 0, 472 Name: "Henry", 473 }, 474 "uds:///tmp", 475 "port number", 476 }, 477 } 478 for _, tt := range tests { 479 t.Run(tt.name, func(t *testing.T) { 480 v := validateServerPort(tt.in, tt.bind) 481 _, err := v.Unwrap() 482 if err == nil && tt.out != "" { 483 t.Fatalf("validateServerPort(%v) = nil, wanted %q", tt.in, tt.out) 484 } else if err != nil && tt.out == "" { 485 t.Fatalf("validateServerPort(%v) = %v, wanted nil", tt.in, err) 486 } else if err != nil && !strings.Contains(err.Error(), tt.out) { 487 t.Fatalf("validateServerPort(%v) = %v, wanted %q", tt.in, err, tt.out) 488 } 489 }) 490 } 491 } 492 493 func TestValidateTlsOptions(t *testing.T) { 494 tests := []struct { 495 name string 496 in *networking.ServerTLSSettings 497 out string 498 warning string 499 }{ 500 {"empty", &networking.ServerTLSSettings{}, "", ""}, 501 { 502 "simple", 503 &networking.ServerTLSSettings{ 504 Mode: networking.ServerTLSSettings_SIMPLE, 505 ServerCertificate: "Captain Jean-Luc Picard", 506 PrivateKey: "Khan Noonien Singh", 507 }, 508 "", "", 509 }, 510 { 511 "simple with client bundle", 512 &networking.ServerTLSSettings{ 513 Mode: networking.ServerTLSSettings_SIMPLE, 514 ServerCertificate: "Captain Jean-Luc Picard", 515 PrivateKey: "Khan Noonien Singh", 516 CaCertificates: "Commander William T. Riker", 517 }, 518 "", "", 519 }, 520 { 521 "simple sds with client bundle", 522 &networking.ServerTLSSettings{ 523 Mode: networking.ServerTLSSettings_SIMPLE, 524 ServerCertificate: "Captain Jean-Luc Picard", 525 PrivateKey: "Khan Noonien Singh", 526 CaCertificates: "Commander William T. Riker", 527 CredentialName: "sds-name", 528 }, 529 "", "", 530 }, 531 { 532 "simple no server cert", 533 &networking.ServerTLSSettings{ 534 Mode: networking.ServerTLSSettings_SIMPLE, 535 ServerCertificate: "", 536 PrivateKey: "Khan Noonien Singh", 537 }, 538 "server certificate", "", 539 }, 540 { 541 "simple no private key", 542 &networking.ServerTLSSettings{ 543 Mode: networking.ServerTLSSettings_SIMPLE, 544 ServerCertificate: "Captain Jean-Luc Picard", 545 PrivateKey: "", 546 }, 547 "private key", "", 548 }, 549 { 550 "simple sds no server cert", 551 &networking.ServerTLSSettings{ 552 Mode: networking.ServerTLSSettings_SIMPLE, 553 ServerCertificate: "", 554 PrivateKey: "Khan Noonien Singh", 555 CredentialName: "sds-name", 556 }, 557 "", "", 558 }, 559 { 560 "simple sds no private key", 561 &networking.ServerTLSSettings{ 562 Mode: networking.ServerTLSSettings_SIMPLE, 563 ServerCertificate: "Captain Jean-Luc Picard", 564 PrivateKey: "", 565 CredentialName: "sds-name", 566 }, 567 "", "", 568 }, 569 { 570 "mutual", 571 &networking.ServerTLSSettings{ 572 Mode: networking.ServerTLSSettings_MUTUAL, 573 ServerCertificate: "Captain Jean-Luc Picard", 574 PrivateKey: "Khan Noonien Singh", 575 CaCertificates: "Commander William T. Riker", 576 }, 577 "", "", 578 }, 579 { 580 "mutual sds", 581 &networking.ServerTLSSettings{ 582 Mode: networking.ServerTLSSettings_MUTUAL, 583 ServerCertificate: "Captain Jean-Luc Picard", 584 PrivateKey: "Khan Noonien Singh", 585 CaCertificates: "Commander William T. Riker", 586 CredentialName: "sds-name", 587 }, 588 "", "", 589 }, 590 { 591 "mutual no server cert", 592 &networking.ServerTLSSettings{ 593 Mode: networking.ServerTLSSettings_MUTUAL, 594 ServerCertificate: "", 595 PrivateKey: "Khan Noonien Singh", 596 CaCertificates: "Commander William T. Riker", 597 }, 598 "server certificate", "", 599 }, 600 { 601 "mutual sds no server cert", 602 &networking.ServerTLSSettings{ 603 Mode: networking.ServerTLSSettings_MUTUAL, 604 ServerCertificate: "", 605 PrivateKey: "Khan Noonien Singh", 606 CaCertificates: "Commander William T. Riker", 607 CredentialName: "sds-name", 608 }, 609 "", "", 610 }, 611 { 612 "mutual no client CA bundle", 613 &networking.ServerTLSSettings{ 614 Mode: networking.ServerTLSSettings_MUTUAL, 615 ServerCertificate: "Captain Jean-Luc Picard", 616 PrivateKey: "Khan Noonien Singh", 617 CaCertificates: "", 618 }, 619 "client CA bundle", "", 620 }, 621 // this pair asserts we get errors about both client and server certs missing when in mutual mode 622 // and both are absent, but requires less rewriting of the testing harness than merging the cases 623 { 624 "mutual no certs", 625 &networking.ServerTLSSettings{ 626 Mode: networking.ServerTLSSettings_MUTUAL, 627 ServerCertificate: "", 628 PrivateKey: "", 629 CaCertificates: "", 630 }, 631 "server certificate", "", 632 }, 633 { 634 "mutual no certs", 635 &networking.ServerTLSSettings{ 636 Mode: networking.ServerTLSSettings_MUTUAL, 637 ServerCertificate: "", 638 PrivateKey: "", 639 CaCertificates: "", 640 }, 641 "private key", "", 642 }, 643 { 644 "mutual no certs", 645 &networking.ServerTLSSettings{ 646 Mode: networking.ServerTLSSettings_MUTUAL, 647 ServerCertificate: "", 648 PrivateKey: "", 649 CaCertificates: "", 650 }, 651 "client CA bundle", "", 652 }, 653 { 654 "optional mutual no certs", 655 &networking.ServerTLSSettings{ 656 Mode: networking.ServerTLSSettings_OPTIONAL_MUTUAL, 657 ServerCertificate: "", 658 PrivateKey: "", 659 CaCertificates: "", 660 }, 661 "server certificate", "", 662 }, 663 { 664 "optional mutual no certs", 665 &networking.ServerTLSSettings{ 666 Mode: networking.ServerTLSSettings_OPTIONAL_MUTUAL, 667 ServerCertificate: "", 668 PrivateKey: "", 669 CaCertificates: "", 670 }, 671 "private key", "", 672 }, 673 { 674 "optional mutual no certs", 675 &networking.ServerTLSSettings{ 676 Mode: networking.ServerTLSSettings_OPTIONAL_MUTUAL, 677 ServerCertificate: "", 678 PrivateKey: "", 679 CaCertificates: "", 680 }, 681 "client CA bundle", "", 682 }, 683 { 684 "pass through sds no certs", 685 &networking.ServerTLSSettings{ 686 Mode: networking.ServerTLSSettings_PASSTHROUGH, 687 ServerCertificate: "", 688 CaCertificates: "", 689 CredentialName: "sds-name", 690 }, 691 "", "PASSTHROUGH mode does not use certificates", 692 }, 693 { 694 "pass through sds crl", 695 &networking.ServerTLSSettings{ 696 Mode: networking.ServerTLSSettings_PASSTHROUGH, 697 ServerCertificate: "", 698 CaCertificates: "", 699 CaCrl: "scrl", 700 }, 701 "", "PASSTHROUGH mode does not use certificates", 702 }, 703 { 704 "istio_mutual no certs", 705 &networking.ServerTLSSettings{ 706 Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, 707 ServerCertificate: "", 708 PrivateKey: "", 709 CaCertificates: "", 710 }, 711 "", "", 712 }, 713 { 714 "istio_mutual with server cert", 715 &networking.ServerTLSSettings{ 716 Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, 717 ServerCertificate: "Captain Jean-Luc Picard", 718 }, 719 "cannot have associated server cert", "", 720 }, 721 { 722 "istio_mutual with client bundle", 723 &networking.ServerTLSSettings{ 724 Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, 725 ServerCertificate: "Captain Jean-Luc Picard", 726 PrivateKey: "Khan Noonien Singh", 727 CaCertificates: "Commander William T. Riker", 728 }, 729 "cannot have associated", "", 730 }, 731 { 732 "istio_mutual with private key", 733 &networking.ServerTLSSettings{ 734 Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, 735 PrivateKey: "Khan Noonien Singh", 736 }, 737 "cannot have associated private key", "", 738 }, 739 { 740 "istio_mutual with credential name", 741 &networking.ServerTLSSettings{ 742 Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, 743 CredentialName: "some-cred", 744 }, 745 "cannot have associated credentialName", "", 746 }, 747 { 748 "invalid cipher suites", 749 &networking.ServerTLSSettings{ 750 Mode: networking.ServerTLSSettings_SIMPLE, 751 CredentialName: "sds-name", 752 CipherSuites: []string{"not-a-cipher-suite"}, 753 }, 754 "", "not-a-cipher-suite", 755 }, 756 { 757 "valid cipher suites", 758 &networking.ServerTLSSettings{ 759 Mode: networking.ServerTLSSettings_SIMPLE, 760 CredentialName: "sds-name", 761 CipherSuites: []string{"ECDHE-ECDSA-AES128-SHA"}, 762 }, 763 "", "", 764 }, 765 { 766 "cipher suites operations", 767 &networking.ServerTLSSettings{ 768 Mode: networking.ServerTLSSettings_SIMPLE, 769 CredentialName: "sds-name", 770 CipherSuites: []string{"-ECDHE-ECDSA-AES128-SHA"}, 771 }, 772 "", "", 773 }, 774 { 775 "duplicate cipher suites", 776 &networking.ServerTLSSettings{ 777 Mode: networking.ServerTLSSettings_SIMPLE, 778 CredentialName: "sds-name", 779 CipherSuites: []string{"ECDHE-ECDSA-AES128-SHA", "ECDHE-ECDSA-AES128-SHA"}, 780 }, 781 "", "ECDHE-ECDSA-AES128-SHA", 782 }, 783 { 784 "invalid cipher suites with invalid config", 785 &networking.ServerTLSSettings{ 786 Mode: networking.ServerTLSSettings_SIMPLE, 787 CipherSuites: []string{"not-a-cipher-suite"}, 788 }, 789 "requires a private key", "not-a-cipher-suite", 790 }, 791 { 792 "crl specified for SIMPLE TLS", 793 &networking.ServerTLSSettings{ 794 Mode: networking.ServerTLSSettings_SIMPLE, 795 CaCrl: "crl", 796 }, 797 "CRL is not supported with SIMPLE TLS", "", 798 }, 799 { 800 "crl specified for CredentialName", 801 &networking.ServerTLSSettings{ 802 Mode: networking.ServerTLSSettings_SIMPLE, 803 CaCrl: "crl", 804 CredentialName: "credential", 805 }, 806 "", "", 807 }, 808 } 809 for _, tt := range tests { 810 t.Run(tt.name, func(t *testing.T) { 811 v := validateTLSOptions(tt.in) 812 warn, err := v.Unwrap() 813 checkValidationMessage(t, warn, err, tt.warning, tt.out) 814 }) 815 } 816 } 817 818 func TestValidateHTTPHeaderName(t *testing.T) { 819 testCases := []struct { 820 name string 821 valid bool 822 }{ 823 {name: "header1", valid: true}, 824 {name: "X-Requested-With", valid: true}, 825 {name: "", valid: false}, 826 } 827 828 for _, tc := range testCases { 829 if got := ValidateHTTPHeaderName(tc.name); (got == nil) != tc.valid { 830 t.Errorf("ValidateHTTPHeaderName(%q) => got valid=%v, want valid=%v", 831 tc.name, got == nil, tc.valid) 832 } 833 } 834 } 835 836 func TestValidateCORSPolicy(t *testing.T) { 837 testCases := []struct { 838 name string 839 in *networking.CorsPolicy 840 valid bool 841 }{ 842 {name: "valid", in: &networking.CorsPolicy{ 843 AllowMethods: []string{"GET", "POST"}, 844 AllowHeaders: []string{"header1", "header2"}, 845 ExposeHeaders: []string{"header3"}, 846 MaxAge: &durationpb.Duration{Seconds: 2}, 847 }, valid: true}, 848 {name: "bad method", in: &networking.CorsPolicy{ 849 AllowMethods: []string{"GET", "PUTT"}, 850 AllowHeaders: []string{"header1", "header2"}, 851 ExposeHeaders: []string{"header3"}, 852 MaxAge: &durationpb.Duration{Seconds: 2}, 853 }, valid: false}, 854 {name: "bad header", in: &networking.CorsPolicy{ 855 AllowMethods: []string{"GET", "POST"}, 856 AllowHeaders: []string{"header1", "header2"}, 857 ExposeHeaders: []string{""}, 858 MaxAge: &durationpb.Duration{Seconds: 2}, 859 }, valid: false}, 860 {name: "bad max age", in: &networking.CorsPolicy{ 861 AllowMethods: []string{"GET", "POST"}, 862 AllowHeaders: []string{"header1", "header2"}, 863 ExposeHeaders: []string{"header3"}, 864 MaxAge: &durationpb.Duration{Seconds: 2, Nanos: 42}, 865 }, valid: false}, 866 {name: "empty matchType AllowOrigins", in: &networking.CorsPolicy{ 867 AllowOrigins: []*networking.StringMatch{ 868 {MatchType: &networking.StringMatch_Exact{Exact: ""}}, 869 {MatchType: &networking.StringMatch_Prefix{Prefix: ""}}, 870 {MatchType: &networking.StringMatch_Regex{Regex: ""}}, 871 }, 872 AllowMethods: []string{"GET", "POST"}, 873 AllowHeaders: []string{"header1", "header2"}, 874 ExposeHeaders: []string{"header3"}, 875 MaxAge: &durationpb.Duration{Seconds: 2}, 876 }, valid: false}, 877 {name: "non empty matchType AllowOrigins", in: &networking.CorsPolicy{ 878 AllowOrigins: []*networking.StringMatch{ 879 {MatchType: &networking.StringMatch_Exact{Exact: "exact"}}, 880 {MatchType: &networking.StringMatch_Prefix{Prefix: "prefix"}}, 881 {MatchType: &networking.StringMatch_Regex{Regex: "regex"}}, 882 }, 883 AllowMethods: []string{"GET", "POST"}, 884 AllowHeaders: []string{"header1", "header2"}, 885 ExposeHeaders: []string{"header3"}, 886 MaxAge: &durationpb.Duration{Seconds: 2}, 887 }, valid: true}, 888 } 889 890 for _, tc := range testCases { 891 t.Run(tc.name, func(t *testing.T) { 892 if got := validateCORSPolicy(tc.in); (got == nil) != tc.valid { 893 t.Errorf("got valid=%v, want valid=%v: %v", 894 got == nil, tc.valid, got) 895 } 896 }) 897 } 898 } 899 900 func TestValidateHTTPStatus(t *testing.T) { 901 testCases := []struct { 902 in int32 903 valid bool 904 }{ 905 {-100, false}, 906 {0, false}, 907 {200, true}, 908 {600, true}, 909 {601, false}, 910 } 911 912 for _, tc := range testCases { 913 if got := validateHTTPStatus(tc.in); (got == nil) != tc.valid { 914 t.Errorf("validateHTTPStatus(%d) => got valid=%v, want valid=%v", 915 tc.in, got, tc.valid) 916 } 917 } 918 } 919 920 func TestValidateHTTPFaultInjectionAbort(t *testing.T) { 921 testCases := []struct { 922 name string 923 in *networking.HTTPFaultInjection_Abort 924 valid bool 925 }{ 926 {name: "nil", in: nil, valid: true}, 927 {name: "valid", in: &networking.HTTPFaultInjection_Abort{ 928 Percentage: &networking.Percent{ 929 Value: 20, 930 }, 931 ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{ 932 HttpStatus: 200, 933 }, 934 }, valid: true}, 935 {name: "valid default", in: &networking.HTTPFaultInjection_Abort{ 936 ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{ 937 HttpStatus: 200, 938 }, 939 }, valid: true}, 940 {name: "invalid http status", in: &networking.HTTPFaultInjection_Abort{ 941 Percentage: &networking.Percent{ 942 Value: 20, 943 }, 944 ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{ 945 HttpStatus: 9000, 946 }, 947 }, valid: false}, 948 {name: "invalid low http status", in: &networking.HTTPFaultInjection_Abort{ 949 Percentage: &networking.Percent{ 950 Value: 20, 951 }, 952 ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{ 953 HttpStatus: 100, 954 }, 955 }, valid: false}, 956 {name: "valid percentage", in: &networking.HTTPFaultInjection_Abort{ 957 Percentage: &networking.Percent{ 958 Value: 0.001, 959 }, 960 ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{ 961 HttpStatus: 200, 962 }, 963 }, valid: true}, 964 {name: "invalid fractional percent", in: &networking.HTTPFaultInjection_Abort{ 965 Percentage: &networking.Percent{ 966 Value: -10.0, 967 }, 968 ErrorType: &networking.HTTPFaultInjection_Abort_HttpStatus{ 969 HttpStatus: 200, 970 }, 971 }, valid: false}, 972 {name: "grpc: nil", in: nil, valid: true}, 973 {name: "grpc: valid", in: &networking.HTTPFaultInjection_Abort{ 974 Percentage: &networking.Percent{ 975 Value: 20, 976 }, 977 ErrorType: &networking.HTTPFaultInjection_Abort_GrpcStatus{ 978 GrpcStatus: "DEADLINE_EXCEEDED", 979 }, 980 }, valid: true}, 981 {name: "grpc: valid default percentage", in: &networking.HTTPFaultInjection_Abort{ 982 ErrorType: &networking.HTTPFaultInjection_Abort_GrpcStatus{ 983 GrpcStatus: "DEADLINE_EXCEEDED", 984 }, 985 }, valid: true}, 986 {name: "grpc: invalid status", in: &networking.HTTPFaultInjection_Abort{ 987 Percentage: &networking.Percent{ 988 Value: 20, 989 }, 990 ErrorType: &networking.HTTPFaultInjection_Abort_GrpcStatus{ 991 GrpcStatus: "BAD_STATUS", 992 }, 993 }, valid: false}, 994 {name: "grpc: valid percentage", in: &networking.HTTPFaultInjection_Abort{ 995 Percentage: &networking.Percent{ 996 Value: 0.001, 997 }, 998 ErrorType: &networking.HTTPFaultInjection_Abort_GrpcStatus{ 999 GrpcStatus: "INTERNAL", 1000 }, 1001 }, valid: true}, 1002 {name: "grpc: invalid fractional percent", in: &networking.HTTPFaultInjection_Abort{ 1003 Percentage: &networking.Percent{ 1004 Value: -10.0, 1005 }, 1006 ErrorType: &networking.HTTPFaultInjection_Abort_GrpcStatus{ 1007 GrpcStatus: "DEADLINE_EXCEEDED", 1008 }, 1009 }, valid: false}, 1010 } 1011 1012 for _, tc := range testCases { 1013 t.Run(tc.name, func(t *testing.T) { 1014 if got := validateHTTPFaultInjectionAbort(tc.in); (got.Err == nil) != tc.valid { 1015 t.Errorf("got valid=%v, want valid=%v: %v", 1016 got.Err == nil, tc.valid, got) 1017 } 1018 }) 1019 } 1020 } 1021 1022 func TestValidateHTTPFaultInjectionDelay(t *testing.T) { 1023 testCases := []struct { 1024 name string 1025 in *networking.HTTPFaultInjection_Delay 1026 valid bool 1027 }{ 1028 {name: "nil", in: nil, valid: true}, 1029 {name: "valid fixed", in: &networking.HTTPFaultInjection_Delay{ 1030 Percentage: &networking.Percent{ 1031 Value: 20, 1032 }, 1033 HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{ 1034 FixedDelay: &durationpb.Duration{Seconds: 3}, 1035 }, 1036 }, valid: true}, 1037 {name: "valid default", in: &networking.HTTPFaultInjection_Delay{ 1038 HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{ 1039 FixedDelay: &durationpb.Duration{Seconds: 3}, 1040 }, 1041 }, valid: true}, 1042 {name: "invalid percent", in: &networking.HTTPFaultInjection_Delay{ 1043 Percentage: &networking.Percent{ 1044 Value: 101, 1045 }, 1046 HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{ 1047 FixedDelay: &durationpb.Duration{Seconds: 3}, 1048 }, 1049 }, valid: false}, 1050 {name: "invalid delay", in: &networking.HTTPFaultInjection_Delay{ 1051 Percentage: &networking.Percent{ 1052 Value: 20, 1053 }, 1054 HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{ 1055 FixedDelay: &durationpb.Duration{Seconds: 3, Nanos: 42}, 1056 }, 1057 }, valid: false}, 1058 {name: "valid fractional percentage", in: &networking.HTTPFaultInjection_Delay{ 1059 Percentage: &networking.Percent{ 1060 Value: 0.001, 1061 }, 1062 HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{ 1063 FixedDelay: &durationpb.Duration{Seconds: 3}, 1064 }, 1065 }, valid: true}, 1066 {name: "invalid fractional percentage", in: &networking.HTTPFaultInjection_Delay{ 1067 Percentage: &networking.Percent{ 1068 Value: -10.0, 1069 }, 1070 HttpDelayType: &networking.HTTPFaultInjection_Delay_FixedDelay{ 1071 FixedDelay: &durationpb.Duration{Seconds: 3}, 1072 }, 1073 }, valid: false}, 1074 } 1075 1076 for _, tc := range testCases { 1077 t.Run(tc.name, func(t *testing.T) { 1078 if got := validateHTTPFaultInjectionDelay(tc.in); (got == nil) != tc.valid { 1079 t.Errorf("got valid=%v, want valid=%v: %v", 1080 got == nil, tc.valid, got) 1081 } 1082 }) 1083 } 1084 } 1085 1086 func TestValidateHTTPRetry(t *testing.T) { 1087 testCases := []struct { 1088 name string 1089 in *networking.HTTPRetry 1090 valid bool 1091 }{ 1092 {name: "valid", in: &networking.HTTPRetry{ 1093 Attempts: 10, 1094 PerTryTimeout: &durationpb.Duration{Seconds: 2}, 1095 RetryOn: "5xx,gateway-error", 1096 }, valid: true}, 1097 {name: "disable retries", in: &networking.HTTPRetry{ 1098 Attempts: 0, 1099 }, valid: true}, 1100 {name: "invalid, retry policy configured but attempts set to zero", in: &networking.HTTPRetry{ 1101 Attempts: 0, 1102 PerTryTimeout: &durationpb.Duration{Seconds: 2}, 1103 RetryOn: "5xx,gateway-error", 1104 }, valid: false}, 1105 {name: "valid default", in: &networking.HTTPRetry{ 1106 Attempts: 10, 1107 }, valid: true}, 1108 {name: "valid http status retryOn", in: &networking.HTTPRetry{ 1109 Attempts: 10, 1110 PerTryTimeout: &durationpb.Duration{Seconds: 2}, 1111 RetryOn: "503,connect-failure", 1112 }, valid: true}, 1113 {name: "invalid attempts", in: &networking.HTTPRetry{ 1114 Attempts: -1, 1115 PerTryTimeout: &durationpb.Duration{Seconds: 2}, 1116 }, valid: false}, 1117 {name: "invalid timeout", in: &networking.HTTPRetry{ 1118 Attempts: 10, 1119 PerTryTimeout: &durationpb.Duration{Seconds: 2, Nanos: 1}, 1120 }, valid: false}, 1121 {name: "timeout too small", in: &networking.HTTPRetry{ 1122 Attempts: 10, 1123 PerTryTimeout: &durationpb.Duration{Nanos: 999}, 1124 }, valid: false}, 1125 {name: "invalid policy retryOn", in: &networking.HTTPRetry{ 1126 Attempts: 10, 1127 PerTryTimeout: &durationpb.Duration{Seconds: 2}, 1128 RetryOn: "5xx,invalid policy", 1129 }, valid: false}, 1130 {name: "invalid http status retryOn", in: &networking.HTTPRetry{ 1131 Attempts: 10, 1132 PerTryTimeout: &durationpb.Duration{Seconds: 2}, 1133 RetryOn: "600,connect-failure", 1134 }, valid: false}, 1135 {name: "invalid, retryRemoteLocalities configured but attempts set to zero", in: &networking.HTTPRetry{ 1136 Attempts: 0, 1137 RetryRemoteLocalities: &wrapperspb.BoolValue{Value: false}, 1138 }, valid: false}, 1139 } 1140 1141 for _, tc := range testCases { 1142 t.Run(tc.name, func(t *testing.T) { 1143 if got := validateHTTPRetry(tc.in); (got == nil) != tc.valid { 1144 t.Errorf("got valid=%v, want valid=%v: %v", 1145 got == nil, tc.valid, got) 1146 } 1147 }) 1148 } 1149 } 1150 1151 func TestValidateHTTPRewrite(t *testing.T) { 1152 testCases := []struct { 1153 name string 1154 in *networking.HTTPRewrite 1155 valid bool 1156 }{ 1157 { 1158 name: "nil in", 1159 in: nil, 1160 valid: true, 1161 }, 1162 { 1163 name: "uri and authority", 1164 in: &networking.HTTPRewrite{ 1165 Uri: "/path/to/resource", 1166 Authority: "foobar.org", 1167 }, 1168 valid: true, 1169 }, 1170 { 1171 name: "uri", 1172 in: &networking.HTTPRewrite{ 1173 Uri: "/path/to/resource", 1174 }, 1175 valid: true, 1176 }, 1177 { 1178 name: "authority", 1179 in: &networking.HTTPRewrite{ 1180 Authority: "foobar.org", 1181 }, 1182 valid: true, 1183 }, 1184 { 1185 name: "uriRegexRewrite", 1186 in: &networking.HTTPRewrite{ 1187 UriRegexRewrite: &networking.RegexRewrite{ 1188 Match: "^/service/([^/]+)(/.*)$", 1189 Rewrite: `\2/instance/\1`, 1190 }, 1191 }, 1192 valid: true, 1193 }, 1194 { 1195 name: "uriRegexRewrite and authority", 1196 in: &networking.HTTPRewrite{ 1197 Authority: "foobar.org", 1198 UriRegexRewrite: &networking.RegexRewrite{ 1199 Match: "^/service/([^/]+)(/.*)$", 1200 Rewrite: `\2/instance/\1`, 1201 }, 1202 }, 1203 valid: true, 1204 }, 1205 { 1206 name: "uriRegexRewrite and uri", 1207 in: &networking.HTTPRewrite{ 1208 Uri: "/path/to/resource", 1209 UriRegexRewrite: &networking.RegexRewrite{ 1210 Match: "^/service/([^/]+)(/.*)$", 1211 Rewrite: `\2/instance/\1`, 1212 }, 1213 }, 1214 valid: false, 1215 }, 1216 { 1217 name: "no uri, uriRegexRewrite, or authority", 1218 in: &networking.HTTPRewrite{}, 1219 valid: false, 1220 }, 1221 } 1222 1223 for _, tc := range testCases { 1224 t.Run(tc.name, func(t *testing.T) { 1225 if got := validateHTTPRewrite(tc.in); (got == nil) != tc.valid { 1226 t.Errorf("got valid=%v, want valid=%v: %v", 1227 got == nil, tc.valid, got) 1228 } 1229 }) 1230 } 1231 } 1232 1233 func TestValidateUriRegexRewrite(t *testing.T) { 1234 testCases := []struct { 1235 name string 1236 in *networking.RegexRewrite 1237 valid bool 1238 }{ 1239 { 1240 name: "uriRegexRewrite nil", 1241 in: nil, 1242 valid: true, 1243 }, 1244 { 1245 name: "uriRegexRewrite happy path", 1246 in: &networking.RegexRewrite{ 1247 Match: "^/service/([^/]+)(/.*)$", 1248 Rewrite: `\2/instance/\1`, 1249 }, 1250 valid: true, 1251 }, 1252 { 1253 name: "uriRegexRewrite missing match", 1254 in: &networking.RegexRewrite{ 1255 Rewrite: `\2/instance/\1`, 1256 }, 1257 valid: false, 1258 }, 1259 { 1260 name: "uriRegexRewrite missing rewrite", 1261 in: &networking.RegexRewrite{ 1262 Match: "^/service/([^/]+)(/.*)$", 1263 }, 1264 valid: false, 1265 }, 1266 { 1267 name: "uriRegexRewrite invalid regex patterns", 1268 in: &networking.RegexRewrite{ 1269 Match: "[", 1270 Rewrite: "[", 1271 }, 1272 valid: false, 1273 }, 1274 } 1275 1276 for _, tc := range testCases { 1277 t.Run(tc.name, func(t *testing.T) { 1278 if got := validateURIRegexRewrite(tc.in); (got == nil) != tc.valid { 1279 t.Errorf("got valid=%v, want valid=%v: %v", 1280 got == nil, tc.valid, got) 1281 } 1282 }) 1283 } 1284 } 1285 1286 func TestValidatePortName(t *testing.T) { 1287 testCases := []struct { 1288 name string 1289 valid bool 1290 }{ 1291 { 1292 name: "", 1293 valid: false, 1294 }, 1295 { 1296 name: "simple", 1297 valid: true, 1298 }, 1299 { 1300 name: "full", 1301 valid: true, 1302 }, 1303 { 1304 name: "toolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolong", 1305 valid: false, 1306 }, 1307 } 1308 1309 for _, tc := range testCases { 1310 t.Run(tc.name, func(t *testing.T) { 1311 if err := ValidatePortName(tc.name); (err == nil) != tc.valid { 1312 t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err) 1313 } 1314 }) 1315 } 1316 } 1317 1318 func TestValidateHTTPRedirect(t *testing.T) { 1319 testCases := []struct { 1320 name string 1321 redirect *networking.HTTPRedirect 1322 valid bool 1323 }{ 1324 { 1325 name: "nil redirect", 1326 redirect: nil, 1327 valid: true, 1328 }, 1329 { 1330 name: "empty uri and authority", 1331 redirect: &networking.HTTPRedirect{ 1332 Uri: "", 1333 Authority: "", 1334 }, 1335 valid: false, 1336 }, 1337 { 1338 name: "too small redirect code", 1339 redirect: &networking.HTTPRedirect{ 1340 Uri: "t", 1341 Authority: "", 1342 RedirectCode: 299, 1343 }, 1344 valid: false, 1345 }, 1346 { 1347 name: "too large redirect code", 1348 redirect: &networking.HTTPRedirect{ 1349 Uri: "t", 1350 Authority: "", 1351 RedirectCode: 400, 1352 }, 1353 valid: false, 1354 }, 1355 { 1356 name: "empty authority", 1357 redirect: &networking.HTTPRedirect{ 1358 Uri: "t", 1359 Authority: "", 1360 }, 1361 valid: true, 1362 }, 1363 { 1364 name: "empty uri", 1365 redirect: &networking.HTTPRedirect{ 1366 Uri: "", 1367 Authority: "t", 1368 }, 1369 valid: true, 1370 }, 1371 { 1372 name: "empty redirect code", 1373 redirect: &networking.HTTPRedirect{ 1374 Uri: "t", 1375 Authority: "t", 1376 RedirectCode: 0, 1377 }, 1378 valid: true, 1379 }, 1380 { 1381 name: "normal redirect", 1382 redirect: &networking.HTTPRedirect{ 1383 Uri: "t", 1384 Authority: "t", 1385 RedirectCode: 308, 1386 }, 1387 valid: true, 1388 }, 1389 } 1390 1391 for _, tc := range testCases { 1392 t.Run(tc.name, func(t *testing.T) { 1393 if err := validateHTTPRedirect(tc.redirect); (err == nil) != tc.valid { 1394 t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err) 1395 } 1396 }) 1397 } 1398 } 1399 1400 func TestValidateHTTPDirectResponse(t *testing.T) { 1401 testCases := []struct { 1402 name string 1403 directResponse *networking.HTTPDirectResponse 1404 valid bool 1405 warning bool 1406 }{ 1407 { 1408 name: "nil redirect", 1409 directResponse: nil, 1410 valid: true, 1411 }, 1412 { 1413 name: "status 200", 1414 directResponse: &networking.HTTPDirectResponse{ 1415 Status: 200, 1416 }, 1417 valid: true, 1418 }, 1419 { 1420 name: "status 100", 1421 directResponse: &networking.HTTPDirectResponse{ 1422 Status: 199, 1423 }, 1424 valid: false, 1425 }, 1426 { 1427 name: "status 600", 1428 directResponse: &networking.HTTPDirectResponse{ 1429 Status: 601, 1430 }, 1431 valid: false, 1432 }, 1433 { 1434 name: "with string body", 1435 directResponse: &networking.HTTPDirectResponse{ 1436 Status: 200, 1437 Body: &networking.HTTPBody{ 1438 Specifier: &networking.HTTPBody_String_{String_: "hello"}, 1439 }, 1440 }, 1441 valid: true, 1442 }, 1443 { 1444 name: "with string body over 100kb", 1445 directResponse: &networking.HTTPDirectResponse{ 1446 Status: 200, 1447 Body: &networking.HTTPBody{ 1448 Specifier: &networking.HTTPBody_String_{String_: strings.Repeat("a", 101*kb)}, 1449 }, 1450 }, 1451 valid: true, 1452 warning: true, 1453 }, 1454 { 1455 name: "with string body over 1mb", 1456 directResponse: &networking.HTTPDirectResponse{ 1457 Status: 200, 1458 Body: &networking.HTTPBody{ 1459 Specifier: &networking.HTTPBody_String_{String_: strings.Repeat("a", 2*mb)}, 1460 }, 1461 }, 1462 valid: false, 1463 }, 1464 { 1465 name: "with bytes body", 1466 directResponse: &networking.HTTPDirectResponse{ 1467 Status: 200, 1468 Body: &networking.HTTPBody{ 1469 Specifier: &networking.HTTPBody_Bytes{Bytes: []byte("hello")}, 1470 }, 1471 }, 1472 valid: true, 1473 }, 1474 { 1475 name: "with bytes body over 100kb", 1476 directResponse: &networking.HTTPDirectResponse{ 1477 Status: 200, 1478 Body: &networking.HTTPBody{ 1479 Specifier: &networking.HTTPBody_Bytes{Bytes: []byte(strings.Repeat("a", (100*kb)+1))}, 1480 }, 1481 }, 1482 valid: true, 1483 warning: true, 1484 }, 1485 { 1486 name: "with bytes body over 1mb", 1487 directResponse: &networking.HTTPDirectResponse{ 1488 Status: 200, 1489 Body: &networking.HTTPBody{ 1490 Specifier: &networking.HTTPBody_Bytes{Bytes: []byte(strings.Repeat("a", (1*mb)+1))}, 1491 }, 1492 }, 1493 valid: false, 1494 }, 1495 } 1496 1497 for _, tc := range testCases { 1498 t.Run(tc.name, func(t *testing.T) { 1499 if err := validateHTTPDirectResponse(tc.directResponse); (err.Err == nil) != tc.valid { 1500 t.Fatalf("got valid=%v but wanted valid=%v: %v", err.Err == nil, tc.valid, err) 1501 } 1502 if err := validateHTTPDirectResponse(tc.directResponse); (err.Warning != nil) != tc.warning { 1503 t.Fatalf("got valid=%v but wanted valid=%v: %v", err.Warning != nil, tc.warning, err) 1504 } 1505 }) 1506 } 1507 } 1508 1509 func TestValidateDestination(t *testing.T) { 1510 testCases := []struct { 1511 name string 1512 destination *networking.Destination 1513 valid bool 1514 }{ 1515 { 1516 name: "empty", 1517 destination: &networking.Destination{}, // nothing 1518 valid: false, 1519 }, 1520 { 1521 name: "simple", 1522 destination: &networking.Destination{ 1523 Host: "foo.bar", 1524 }, 1525 valid: true, 1526 }, 1527 { 1528 name: "full", 1529 destination: &networking.Destination{ 1530 Host: "foo.bar", 1531 Subset: "shiny", 1532 Port: &networking.PortSelector{ 1533 Number: 5000, 1534 }, 1535 }, 1536 valid: true, 1537 }, 1538 { 1539 name: "unnumbered-selector", 1540 destination: &networking.Destination{ 1541 Host: "foo.bar", 1542 Subset: "shiny", 1543 Port: &networking.PortSelector{}, 1544 }, 1545 valid: false, 1546 }, 1547 } 1548 1549 for _, tc := range testCases { 1550 t.Run(tc.name, func(t *testing.T) { 1551 if err := validateDestination(tc.destination); (err == nil) != tc.valid { 1552 t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err) 1553 } 1554 }) 1555 } 1556 } 1557 1558 func TestValidateHTTPRoute(t *testing.T) { 1559 testCases := []struct { 1560 name string 1561 route *networking.HTTPRoute 1562 valid bool 1563 }{ 1564 {name: "empty", route: &networking.HTTPRoute{ // nothing 1565 }, valid: false}, 1566 {name: "simple", route: &networking.HTTPRoute{ 1567 Route: []*networking.HTTPRouteDestination{{ 1568 Destination: &networking.Destination{Host: "foo.baz"}, 1569 }}, 1570 }, valid: true}, 1571 {name: "no destination", route: &networking.HTTPRoute{ 1572 Route: []*networking.HTTPRouteDestination{{ 1573 Destination: nil, 1574 }}, 1575 }, valid: false}, 1576 {name: "weighted", route: &networking.HTTPRoute{ 1577 Route: []*networking.HTTPRouteDestination{{ 1578 Destination: &networking.Destination{Host: "foo.baz.south"}, 1579 Weight: 25, 1580 }, { 1581 Destination: &networking.Destination{Host: "foo.baz.east"}, 1582 Weight: 75, 1583 }}, 1584 }, valid: true}, 1585 {name: "total weight > 100", route: &networking.HTTPRoute{ 1586 Route: []*networking.HTTPRouteDestination{{ 1587 Destination: &networking.Destination{Host: "foo.baz.south"}, 1588 Weight: 550, 1589 }, { 1590 Destination: &networking.Destination{Host: "foo.baz.east"}, 1591 Weight: 500, 1592 }}, 1593 }, valid: true}, 1594 {name: "total weight < 100", route: &networking.HTTPRoute{ 1595 Route: []*networking.HTTPRouteDestination{{ 1596 Destination: &networking.Destination{Host: "foo.baz.south"}, 1597 Weight: 49, 1598 }, { 1599 Destination: &networking.Destination{Host: "foo.baz.east"}, 1600 Weight: 50, 1601 }}, 1602 }, valid: true}, 1603 {name: "simple redirect", route: &networking.HTTPRoute{ 1604 Redirect: &networking.HTTPRedirect{ 1605 Uri: "/lerp", 1606 Authority: "foo.biz", 1607 }, 1608 }, valid: true}, 1609 {name: "conflicting redirect and route", route: &networking.HTTPRoute{ 1610 Route: []*networking.HTTPRouteDestination{{ 1611 Destination: &networking.Destination{Host: "foo.baz"}, 1612 }}, 1613 Redirect: &networking.HTTPRedirect{ 1614 Uri: "/lerp", 1615 Authority: "foo.biz", 1616 }, 1617 }, valid: false}, 1618 {name: "request response headers", route: &networking.HTTPRoute{ 1619 Route: []*networking.HTTPRouteDestination{{ 1620 Destination: &networking.Destination{Host: "foo.baz"}, 1621 }}, 1622 }, valid: true}, 1623 {name: "valid headers", route: &networking.HTTPRoute{ 1624 Route: []*networking.HTTPRouteDestination{{ 1625 Destination: &networking.Destination{Host: "foo.baz"}, 1626 Headers: &networking.Headers{ 1627 Request: &networking.Headers_HeaderOperations{ 1628 Add: map[string]string{ 1629 "name": "", 1630 }, 1631 Set: map[string]string{ 1632 "name": "", 1633 }, 1634 Remove: []string{ 1635 "name", 1636 }, 1637 }, 1638 Response: &networking.Headers_HeaderOperations{ 1639 Add: map[string]string{ 1640 "name": "", 1641 }, 1642 Set: map[string]string{ 1643 "name": "", 1644 }, 1645 Remove: []string{ 1646 "name", 1647 }, 1648 }, 1649 }, 1650 }}, 1651 }, valid: true}, 1652 {name: "empty header name - request add", route: &networking.HTTPRoute{ 1653 Route: []*networking.HTTPRouteDestination{{ 1654 Destination: &networking.Destination{Host: "foo.baz"}, 1655 Headers: &networking.Headers{ 1656 Request: &networking.Headers_HeaderOperations{ 1657 Add: map[string]string{ 1658 "": "value", 1659 }, 1660 }, 1661 }, 1662 }}, 1663 }, valid: false}, 1664 {name: "empty header name - request set", route: &networking.HTTPRoute{ 1665 Route: []*networking.HTTPRouteDestination{{ 1666 Destination: &networking.Destination{Host: "foo.baz"}, 1667 Headers: &networking.Headers{ 1668 Request: &networking.Headers_HeaderOperations{ 1669 Set: map[string]string{ 1670 "": "value", 1671 }, 1672 }, 1673 }, 1674 }}, 1675 }, valid: false}, 1676 {name: "empty header name - request remove", route: &networking.HTTPRoute{ 1677 Route: []*networking.HTTPRouteDestination{{ 1678 Destination: &networking.Destination{Host: "foo.baz"}, 1679 Headers: &networking.Headers{ 1680 Request: &networking.Headers_HeaderOperations{ 1681 Remove: []string{ 1682 "", 1683 }, 1684 }, 1685 }, 1686 }}, 1687 }, valid: false}, 1688 {name: "empty header name - response add", route: &networking.HTTPRoute{ 1689 Route: []*networking.HTTPRouteDestination{{ 1690 Destination: &networking.Destination{Host: "foo.baz"}, 1691 Headers: &networking.Headers{ 1692 Response: &networking.Headers_HeaderOperations{ 1693 Add: map[string]string{ 1694 "": "value", 1695 }, 1696 }, 1697 }, 1698 }}, 1699 }, valid: false}, 1700 {name: "empty header name - response set", route: &networking.HTTPRoute{ 1701 Route: []*networking.HTTPRouteDestination{{ 1702 Destination: &networking.Destination{Host: "foo.baz"}, 1703 Headers: &networking.Headers{ 1704 Response: &networking.Headers_HeaderOperations{ 1705 Set: map[string]string{ 1706 "": "value", 1707 }, 1708 }, 1709 }, 1710 }}, 1711 }, valid: false}, 1712 {name: "empty header name - response remove", route: &networking.HTTPRoute{ 1713 Route: []*networking.HTTPRouteDestination{{ 1714 Destination: &networking.Destination{Host: "foo.baz"}, 1715 Headers: &networking.Headers{ 1716 Response: &networking.Headers_HeaderOperations{ 1717 Remove: []string{ 1718 "", 1719 }, 1720 }, 1721 }, 1722 }}, 1723 }, valid: false}, 1724 {name: "envoy escaped % set", route: &networking.HTTPRoute{ 1725 Route: []*networking.HTTPRouteDestination{{ 1726 Destination: &networking.Destination{Host: "foo.baz"}, 1727 Headers: &networking.Headers{ 1728 Response: &networking.Headers_HeaderOperations{ 1729 Set: map[string]string{ 1730 "i-love-istio": "100%%", 1731 }, 1732 }, 1733 }, 1734 }}, 1735 }, valid: true}, 1736 {name: "envoy variable set", route: &networking.HTTPRoute{ 1737 Route: []*networking.HTTPRouteDestination{{ 1738 Destination: &networking.Destination{Host: "foo.baz"}, 1739 Headers: &networking.Headers{ 1740 Response: &networking.Headers_HeaderOperations{ 1741 Set: map[string]string{ 1742 "name": "%HOSTNAME%", 1743 }, 1744 }, 1745 }, 1746 }}, 1747 }, valid: true}, 1748 {name: "envoy unescaped % set", route: &networking.HTTPRoute{ 1749 Route: []*networking.HTTPRouteDestination{{ 1750 Destination: &networking.Destination{Host: "foo.baz"}, 1751 Headers: &networking.Headers{ 1752 Response: &networking.Headers_HeaderOperations{ 1753 Set: map[string]string{ 1754 "name": "abcd%oijasodifj", 1755 }, 1756 }, 1757 }, 1758 }}, 1759 }, valid: false}, 1760 {name: "envoy escaped % add", route: &networking.HTTPRoute{ 1761 Route: []*networking.HTTPRouteDestination{{ 1762 Destination: &networking.Destination{Host: "foo.baz"}, 1763 Headers: &networking.Headers{ 1764 Response: &networking.Headers_HeaderOperations{ 1765 Add: map[string]string{ 1766 "i-love-istio": "100%% and more", 1767 }, 1768 }, 1769 }, 1770 }}, 1771 }, valid: true}, 1772 {name: "envoy variable add", route: &networking.HTTPRoute{ 1773 Route: []*networking.HTTPRouteDestination{{ 1774 Destination: &networking.Destination{Host: "foo.baz"}, 1775 Headers: &networking.Headers{ 1776 Response: &networking.Headers_HeaderOperations{ 1777 Add: map[string]string{ 1778 "name": "hello %HOSTNAME%", 1779 }, 1780 }, 1781 }, 1782 }}, 1783 }, valid: true}, 1784 {name: "envoy unescaped % add", route: &networking.HTTPRoute{ 1785 Route: []*networking.HTTPRouteDestination{{ 1786 Destination: &networking.Destination{Host: "foo.baz"}, 1787 Headers: &networking.Headers{ 1788 Response: &networking.Headers_HeaderOperations{ 1789 Add: map[string]string{ 1790 "name": "abcd%oijasodifj", 1791 }, 1792 }, 1793 }, 1794 }}, 1795 }, valid: false}, 1796 {name: "null header match", route: &networking.HTTPRoute{ 1797 Route: []*networking.HTTPRouteDestination{{ 1798 Destination: &networking.Destination{Host: "foo.bar"}, 1799 }}, 1800 Match: []*networking.HTTPMatchRequest{{ 1801 Headers: map[string]*networking.StringMatch{ 1802 "header": nil, 1803 }, 1804 }}, 1805 }, valid: false}, 1806 {name: "empty prefix header match", route: &networking.HTTPRoute{ 1807 Route: []*networking.HTTPRouteDestination{{ 1808 Destination: &networking.Destination{Host: "foo.bar"}, 1809 }}, 1810 Match: []*networking.HTTPMatchRequest{{ 1811 Headers: map[string]*networking.StringMatch{ 1812 "emptyprefix": {MatchType: &networking.StringMatch_Prefix{Prefix: ""}}, 1813 }, 1814 }}, 1815 }, valid: false}, 1816 {name: "nil match", route: &networking.HTTPRoute{ 1817 Route: []*networking.HTTPRouteDestination{{ 1818 Destination: &networking.Destination{Host: "foo.bar"}, 1819 }}, 1820 Match: nil, 1821 }, valid: true}, 1822 {name: "match with nil element", route: &networking.HTTPRoute{ 1823 Route: []*networking.HTTPRouteDestination{{ 1824 Destination: &networking.Destination{Host: "foo.bar"}, 1825 }}, 1826 Match: []*networking.HTTPMatchRequest{nil}, 1827 }, valid: true}, 1828 {name: "invalid mirror percent", route: &networking.HTTPRoute{ 1829 MirrorPercent: &wrapperspb.UInt32Value{Value: 101}, 1830 Route: []*networking.HTTPRouteDestination{{ 1831 Destination: &networking.Destination{Host: "foo.bar"}, 1832 }}, 1833 Match: []*networking.HTTPMatchRequest{nil}, 1834 }, valid: false}, 1835 {name: "invalid mirror percentage", route: &networking.HTTPRoute{ 1836 MirrorPercentage: &networking.Percent{ 1837 Value: 101, 1838 }, 1839 Route: []*networking.HTTPRouteDestination{{ 1840 Destination: &networking.Destination{Host: "foo.bar"}, 1841 }}, 1842 Match: []*networking.HTTPMatchRequest{nil}, 1843 }, valid: false}, 1844 {name: "valid mirror percentage", route: &networking.HTTPRoute{ 1845 MirrorPercentage: &networking.Percent{ 1846 Value: 1, 1847 }, 1848 Route: []*networking.HTTPRouteDestination{{ 1849 Destination: &networking.Destination{Host: "foo.bar"}, 1850 }}, 1851 Match: []*networking.HTTPMatchRequest{nil}, 1852 }, valid: true}, 1853 {name: "negative mirror percentage", route: &networking.HTTPRoute{ 1854 MirrorPercentage: &networking.Percent{ 1855 Value: -1, 1856 }, 1857 Route: []*networking.HTTPRouteDestination{{ 1858 Destination: &networking.Destination{Host: "foo.bar"}, 1859 }}, 1860 Match: []*networking.HTTPMatchRequest{nil}, 1861 }, valid: false}, 1862 {name: "mirrors without destination", route: &networking.HTTPRoute{ 1863 Mirrors: []*networking.HTTPMirrorPolicy{{ 1864 Destination: nil, 1865 }}, 1866 Route: []*networking.HTTPRouteDestination{{ 1867 Destination: &networking.Destination{Host: "foo.bar"}, 1868 }}, 1869 Match: []*networking.HTTPMatchRequest{nil}, 1870 }, valid: false}, 1871 {name: "mirrors invalid mirror percentage", route: &networking.HTTPRoute{ 1872 Mirrors: []*networking.HTTPMirrorPolicy{{ 1873 Destination: &networking.Destination{Host: "foo.baz"}, 1874 }, { 1875 Destination: &networking.Destination{Host: "foo.baz"}, 1876 Percentage: &networking.Percent{Value: 101}, 1877 }}, 1878 Route: []*networking.HTTPRouteDestination{{ 1879 Destination: &networking.Destination{Host: "foo.bar"}, 1880 }}, 1881 Match: []*networking.HTTPMatchRequest{nil}, 1882 }, valid: false}, 1883 {name: "mirrors valid mirror percentage", route: &networking.HTTPRoute{ 1884 Mirrors: []*networking.HTTPMirrorPolicy{{ 1885 Destination: &networking.Destination{Host: "foo.baz"}, 1886 Percentage: &networking.Percent{Value: 1}, 1887 }, { 1888 Destination: &networking.Destination{Host: "foo.baz"}, 1889 Percentage: &networking.Percent{Value: 50}, 1890 }}, 1891 Route: []*networking.HTTPRouteDestination{{ 1892 Destination: &networking.Destination{Host: "foo.bar"}, 1893 }}, 1894 Match: []*networking.HTTPMatchRequest{nil}, 1895 }, valid: true}, 1896 {name: "mirrors negative mirror percentage", route: &networking.HTTPRoute{ 1897 Mirrors: []*networking.HTTPMirrorPolicy{{ 1898 Destination: &networking.Destination{Host: "foo.baz"}, 1899 Percentage: &networking.Percent{Value: -1}, 1900 }}, 1901 Route: []*networking.HTTPRouteDestination{{ 1902 Destination: &networking.Destination{Host: "foo.bar"}, 1903 }}, 1904 Match: []*networking.HTTPMatchRequest{nil}, 1905 }, valid: false}, 1906 {name: "conflicting mirror and mirrors", route: &networking.HTTPRoute{ 1907 Mirror: &networking.Destination{Host: "foo.baz"}, 1908 Mirrors: []*networking.HTTPMirrorPolicy{{ 1909 Destination: &networking.Destination{Host: "foo.bar"}, 1910 }}, 1911 Route: []*networking.HTTPRouteDestination{{ 1912 Destination: &networking.Destination{Host: "foo.baz"}, 1913 }}, 1914 }, valid: false}, 1915 } 1916 1917 for _, tc := range testCases { 1918 t.Run(tc.name, func(t *testing.T) { 1919 if err := validateHTTPRoute(tc.route, false, false); (err.Err == nil) != tc.valid { 1920 t.Fatalf("got valid=%v but wanted valid=%v: %v", err.Err == nil, tc.valid, err) 1921 } 1922 }) 1923 } 1924 } 1925 1926 func TestValidateRouteDestination(t *testing.T) { 1927 testCases := []struct { 1928 name string 1929 routes []*networking.RouteDestination 1930 valid bool 1931 }{ 1932 {name: "simple", routes: []*networking.RouteDestination{{ 1933 Destination: &networking.Destination{Host: "foo.baz"}, 1934 }}, valid: true}, 1935 {name: "wildcard dash", routes: []*networking.RouteDestination{{ 1936 Destination: &networking.Destination{Host: "*-foo.baz"}, 1937 }}, valid: true}, 1938 {name: "wildcard prefix", routes: []*networking.RouteDestination{{ 1939 Destination: &networking.Destination{Host: "*foo.baz"}, 1940 }}, valid: true}, 1941 {name: "wildcard", routes: []*networking.RouteDestination{{ 1942 Destination: &networking.Destination{Host: "*"}, 1943 }}, valid: false}, 1944 {name: "bad wildcard", routes: []*networking.RouteDestination{{ 1945 Destination: &networking.Destination{Host: "foo.*"}, 1946 }}, valid: false}, 1947 {name: "bad fqdn", routes: []*networking.RouteDestination{{ 1948 Destination: &networking.Destination{Host: "default/baz"}, 1949 }}, valid: false}, 1950 {name: "no destination", routes: []*networking.RouteDestination{{ 1951 Destination: nil, 1952 }}, valid: false}, 1953 {name: "weighted", routes: []*networking.RouteDestination{{ 1954 Destination: &networking.Destination{Host: "foo.baz.south"}, 1955 Weight: 25, 1956 }, { 1957 Destination: &networking.Destination{Host: "foo.baz.east"}, 1958 Weight: 75, 1959 }}, valid: true}, 1960 {name: "weight < 0", routes: []*networking.RouteDestination{{ 1961 Destination: &networking.Destination{Host: "foo.baz.south"}, 1962 Weight: 5, 1963 }, { 1964 Destination: &networking.Destination{Host: "foo.baz.east"}, 1965 Weight: -1, 1966 }}, valid: false}, 1967 {name: "total weight > 100", routes: []*networking.RouteDestination{{ 1968 Destination: &networking.Destination{Host: "foo.baz.south"}, 1969 Weight: 550, 1970 }, { 1971 Destination: &networking.Destination{Host: "foo.baz.east"}, 1972 Weight: 500, 1973 }}, valid: true}, 1974 {name: "total weight < 100", routes: []*networking.RouteDestination{{ 1975 Destination: &networking.Destination{Host: "foo.baz.south"}, 1976 Weight: 49, 1977 }, { 1978 Destination: &networking.Destination{Host: "foo.baz.east"}, 1979 Weight: 50, 1980 }}, valid: true}, 1981 {name: "total weight = 100", routes: []*networking.RouteDestination{{ 1982 Destination: &networking.Destination{Host: "foo.baz.south"}, 1983 Weight: 100, 1984 }, { 1985 Destination: &networking.Destination{Host: "foo.baz.east"}, 1986 Weight: 0, 1987 }}, valid: true}, 1988 {name: "weight = 0", routes: []*networking.RouteDestination{{ 1989 Destination: &networking.Destination{Host: "foo.baz.south"}, 1990 Weight: 0, 1991 }}, valid: true}, 1992 {name: "total weight = 0 with multi RouteDestination", routes: []*networking.RouteDestination{{ 1993 Destination: &networking.Destination{Host: "foo.baz.south"}, 1994 Weight: 0, 1995 }, { 1996 Destination: &networking.Destination{Host: "foo.baz.east"}, 1997 Weight: 0, 1998 }}, valid: false}, 1999 } 2000 2001 for _, tc := range testCases { 2002 t.Run(tc.name, func(t *testing.T) { 2003 if err := validateRouteDestinations(tc.routes, false); (err == nil) != tc.valid { 2004 t.Fatalf("got valid=%v but wanted valid=%v: %v", err == nil, tc.valid, err) 2005 } 2006 }) 2007 } 2008 } 2009 2010 // TODO: add TCP test cases once it is implemented 2011 func TestValidateVirtualService(t *testing.T) { 2012 testCases := []struct { 2013 name string 2014 in proto.Message 2015 valid bool 2016 warning bool 2017 }{ 2018 {name: "simple", in: &networking.VirtualService{ 2019 Hosts: []string{"foo.bar"}, 2020 Http: []*networking.HTTPRoute{{ 2021 Route: []*networking.HTTPRouteDestination{{ 2022 Destination: &networking.Destination{Host: "foo.baz"}, 2023 }}, 2024 }}, 2025 }, valid: true}, 2026 {name: "duplicate hosts", in: &networking.VirtualService{ 2027 Hosts: []string{"*.foo.bar", "*.bar"}, 2028 Http: []*networking.HTTPRoute{{ 2029 Route: []*networking.HTTPRouteDestination{{ 2030 Destination: &networking.Destination{Host: "foo.baz"}, 2031 }}, 2032 }}, 2033 }, valid: false}, 2034 {name: "with no destination", in: &networking.VirtualService{ 2035 Hosts: []string{"*.foo.bar", "*.bar"}, 2036 Http: []*networking.HTTPRoute{{ 2037 Route: []*networking.HTTPRouteDestination{{}}, 2038 }}, 2039 }, valid: false}, 2040 {name: "destination with out hosts", in: &networking.VirtualService{ 2041 Hosts: []string{"*.foo.bar", "*.bar"}, 2042 Http: []*networking.HTTPRoute{{ 2043 Route: []*networking.HTTPRouteDestination{{ 2044 Destination: &networking.Destination{}, 2045 }}, 2046 }}, 2047 }, valid: false}, 2048 {name: "delegate with no hosts", in: &networking.VirtualService{ 2049 Hosts: nil, 2050 Http: []*networking.HTTPRoute{{ 2051 Route: []*networking.HTTPRouteDestination{{ 2052 Destination: &networking.Destination{Host: "foo.baz"}, 2053 }}, 2054 }}, 2055 }, valid: true}, 2056 {name: "bad host", in: &networking.VirtualService{ 2057 Hosts: []string{"foo.ba!r"}, 2058 Http: []*networking.HTTPRoute{{ 2059 Route: []*networking.HTTPRouteDestination{{ 2060 Destination: &networking.Destination{Host: "foo.baz"}, 2061 }}, 2062 }}, 2063 }, valid: false}, 2064 {name: "no tcp or http routing", in: &networking.VirtualService{ 2065 Hosts: []string{"foo.bar"}, 2066 }, valid: false}, 2067 {name: "bad gateway", in: &networking.VirtualService{ 2068 Hosts: []string{"foo.bar"}, 2069 Gateways: []string{"b@dgateway"}, 2070 Http: []*networking.HTTPRoute{{ 2071 Route: []*networking.HTTPRouteDestination{{ 2072 Destination: &networking.Destination{Host: "foo.baz"}, 2073 }}, 2074 }}, 2075 }, valid: false}, 2076 {name: "FQDN for gateway", in: &networking.VirtualService{ 2077 Hosts: []string{"foo.bar"}, 2078 Gateways: []string{"gateway.example.com"}, 2079 Http: []*networking.HTTPRoute{{ 2080 Route: []*networking.HTTPRouteDestination{{ 2081 Destination: &networking.Destination{Host: "foo.baz"}, 2082 }}, 2083 }}, 2084 }, valid: true, warning: true}, 2085 {name: "namespace/name for gateway", in: &networking.VirtualService{ 2086 Hosts: []string{"foo.bar"}, 2087 Gateways: []string{"ns1/gateway"}, 2088 Http: []*networking.HTTPRoute{{ 2089 Route: []*networking.HTTPRouteDestination{{ 2090 Destination: &networking.Destination{Host: "foo.baz"}, 2091 }}, 2092 }}, 2093 }, valid: true}, 2094 {name: "namespace/* for gateway", in: &networking.VirtualService{ 2095 Hosts: []string{"foo.bar"}, 2096 Gateways: []string{"ns1/*"}, 2097 Http: []*networking.HTTPRoute{{ 2098 Route: []*networking.HTTPRouteDestination{{ 2099 Destination: &networking.Destination{Host: "foo.baz"}, 2100 }}, 2101 }}, 2102 }, valid: false}, 2103 {name: "*/name for gateway", in: &networking.VirtualService{ 2104 Hosts: []string{"foo.bar"}, 2105 Gateways: []string{"*/gateway"}, 2106 Http: []*networking.HTTPRoute{{ 2107 Route: []*networking.HTTPRouteDestination{{ 2108 Destination: &networking.Destination{Host: "foo.baz"}, 2109 }}, 2110 }}, 2111 }, valid: false}, 2112 {name: "wildcard for mesh gateway", in: &networking.VirtualService{ 2113 Hosts: []string{"*"}, 2114 Http: []*networking.HTTPRoute{{ 2115 Route: []*networking.HTTPRouteDestination{{ 2116 Destination: &networking.Destination{Host: "foo.baz"}, 2117 }}, 2118 }}, 2119 }, valid: false}, 2120 {name: "wildcard for non-mesh gateway", in: &networking.VirtualService{ 2121 Hosts: []string{"*"}, 2122 Gateways: []string{"somegateway"}, 2123 Http: []*networking.HTTPRoute{{ 2124 Route: []*networking.HTTPRouteDestination{{ 2125 Destination: &networking.Destination{Host: "foo.baz"}, 2126 }}, 2127 }}, 2128 }, valid: true}, 2129 {name: "missing tcp route", in: &networking.VirtualService{ 2130 Hosts: []string{"foo.bar"}, 2131 Tcp: []*networking.TCPRoute{{ 2132 Match: []*networking.L4MatchAttributes{ 2133 {Port: 999}, 2134 }, 2135 }}, 2136 }, valid: false}, 2137 {name: "missing tls route", in: &networking.VirtualService{ 2138 Hosts: []string{"foo.bar"}, 2139 Tls: []*networking.TLSRoute{{ 2140 Match: []*networking.TLSMatchAttributes{ 2141 { 2142 Port: 999, 2143 SniHosts: []string{"foo.bar"}, 2144 }, 2145 }, 2146 }}, 2147 }, valid: false}, 2148 {name: "deprecated mirror", in: &networking.VirtualService{ 2149 Hosts: []string{"foo.bar"}, 2150 Gateways: []string{"ns1/gateway"}, 2151 Http: []*networking.HTTPRoute{{ 2152 MirrorPercent: &wrapperspb.UInt32Value{Value: 5}, 2153 Route: []*networking.HTTPRouteDestination{{ 2154 Destination: &networking.Destination{Host: "foo.baz"}, 2155 }}, 2156 }}, 2157 }, valid: true, warning: true}, 2158 {name: "set authority", in: &networking.VirtualService{ 2159 Hosts: []string{"foo.bar"}, 2160 Http: []*networking.HTTPRoute{{ 2161 Headers: &networking.Headers{ 2162 Request: &networking.Headers_HeaderOperations{Set: map[string]string{":authority": "foo"}}, 2163 }, 2164 Route: []*networking.HTTPRouteDestination{{ 2165 Destination: &networking.Destination{Host: "foo.baz"}, 2166 }}, 2167 }}, 2168 }, valid: true, warning: false}, 2169 {name: "set authority in destination", in: &networking.VirtualService{ 2170 Hosts: []string{"foo.bar"}, 2171 Http: []*networking.HTTPRoute{{ 2172 Route: []*networking.HTTPRouteDestination{{ 2173 Destination: &networking.Destination{Host: "foo.baz"}, 2174 Headers: &networking.Headers{ 2175 Request: &networking.Headers_HeaderOperations{Set: map[string]string{":authority": "foo"}}, 2176 }, 2177 }}, 2178 }}, 2179 }, valid: true, warning: false}, 2180 {name: "set authority in rewrite and header", in: &networking.VirtualService{ 2181 Hosts: []string{"foo.bar"}, 2182 Http: []*networking.HTTPRoute{{ 2183 Headers: &networking.Headers{ 2184 Request: &networking.Headers_HeaderOperations{Set: map[string]string{":authority": "foo"}}, 2185 }, 2186 Rewrite: &networking.HTTPRewrite{Authority: "bar"}, 2187 Route: []*networking.HTTPRouteDestination{{ 2188 Destination: &networking.Destination{Host: "foo.baz"}, 2189 }}, 2190 }}, 2191 }, valid: false, warning: false}, 2192 {name: "non-method-get", in: &networking.VirtualService{ 2193 Hosts: []string{"foo.bar"}, 2194 Http: []*networking.HTTPRoute{{ 2195 Route: []*networking.HTTPRouteDestination{{ 2196 Destination: &networking.Destination{Host: "foo.baz"}, 2197 }}, 2198 Match: []*networking.HTTPMatchRequest{ 2199 { 2200 Uri: &networking.StringMatch{ 2201 MatchType: &networking.StringMatch_Prefix{Prefix: "/api/v1/product"}, 2202 }, 2203 }, 2204 { 2205 Uri: &networking.StringMatch{ 2206 MatchType: &networking.StringMatch_Prefix{Prefix: "/api/v1/products"}, 2207 }, 2208 Method: &networking.StringMatch{ 2209 MatchType: &networking.StringMatch_Exact{Exact: "GET"}, 2210 }, 2211 }, 2212 }, 2213 }}, 2214 }, valid: true, warning: true}, 2215 {name: "uri-with-prefix-exact", in: &networking.VirtualService{ 2216 Hosts: []string{"foo.bar"}, 2217 Http: []*networking.HTTPRoute{{ 2218 Route: []*networking.HTTPRouteDestination{{ 2219 Destination: &networking.Destination{Host: "foo.baz"}, 2220 }}, 2221 Match: []*networking.HTTPMatchRequest{ 2222 { 2223 Uri: &networking.StringMatch{ 2224 MatchType: &networking.StringMatch_Prefix{Prefix: "/"}, 2225 }, 2226 }, 2227 { 2228 Uri: &networking.StringMatch{ 2229 MatchType: &networking.StringMatch_Exact{Exact: "/"}, 2230 }, 2231 Method: &networking.StringMatch{ 2232 MatchType: &networking.StringMatch_Exact{Exact: "GET"}, 2233 }, 2234 }, 2235 }, 2236 }}, 2237 }, valid: true, warning: false}, 2238 {name: "jwt claim route without gateway", in: &networking.VirtualService{ 2239 Hosts: []string{"foo.bar"}, 2240 Gateways: []string{"mesh"}, 2241 Http: []*networking.HTTPRoute{{ 2242 Route: []*networking.HTTPRouteDestination{{ 2243 Destination: &networking.Destination{Host: "foo.baz"}, 2244 }}, 2245 Match: []*networking.HTTPMatchRequest{ 2246 { 2247 Uri: &networking.StringMatch{ 2248 MatchType: &networking.StringMatch_Prefix{Prefix: "/"}, 2249 }, 2250 Headers: map[string]*networking.StringMatch{ 2251 "@request.auth.claims.foo": { 2252 MatchType: &networking.StringMatch_Exact{Exact: "bar"}, 2253 }, 2254 }, 2255 }, 2256 }, 2257 }}, 2258 }, valid: false, warning: false}, 2259 {name: "ip address as sni host", in: &networking.VirtualService{ 2260 Hosts: []string{"foo.bar"}, 2261 Tls: []*networking.TLSRoute{{ 2262 Route: []*networking.RouteDestination{{ 2263 Destination: &networking.Destination{Host: "foo.baz"}, 2264 }}, 2265 Match: []*networking.TLSMatchAttributes{ 2266 { 2267 Port: 999, 2268 SniHosts: []string{"1.1.1.1"}, 2269 }, 2270 }, 2271 }}, 2272 }, valid: true, warning: true}, 2273 {name: "invalid wildcard as sni host", in: &networking.VirtualService{ 2274 Hosts: []string{"foo.bar"}, 2275 Tls: []*networking.TLSRoute{{ 2276 Route: []*networking.RouteDestination{{ 2277 Destination: &networking.Destination{Host: "foo.baz"}, 2278 }}, 2279 Match: []*networking.TLSMatchAttributes{ 2280 { 2281 Port: 999, 2282 SniHosts: []string{"foo.*.com"}, 2283 }, 2284 }, 2285 }}, 2286 }, valid: false, warning: false}, 2287 } 2288 2289 for _, tc := range testCases { 2290 t.Run(tc.name, func(t *testing.T) { 2291 warn, err := ValidateVirtualService(config.Config{Spec: tc.in}) 2292 checkValidation(t, warn, err, tc.valid, tc.warning) 2293 }) 2294 } 2295 } 2296 2297 func TestValidateWorkloadEntry(t *testing.T) { 2298 testCases := []struct { 2299 name string 2300 in proto.Message 2301 valid bool 2302 warning bool 2303 }{ 2304 { 2305 name: "valid", 2306 in: &networking.WorkloadEntry{Address: "1.2.3.4"}, 2307 valid: true, 2308 }, 2309 { 2310 name: "missing address", 2311 in: &networking.WorkloadEntry{}, 2312 valid: false, 2313 }, 2314 { 2315 name: "missing address with network", 2316 in: &networking.WorkloadEntry{Network: "network-2"}, 2317 valid: true, 2318 warning: true, 2319 }, 2320 { 2321 name: "valid unix endpoint", 2322 in: &networking.WorkloadEntry{Address: "unix:///lon/google/com"}, 2323 valid: true, 2324 }, 2325 { 2326 name: "invalid unix endpoint", 2327 in: &networking.WorkloadEntry{Address: "unix:///lon/google/com", Ports: map[string]uint32{"7777": 7777}}, 2328 valid: false, 2329 }, 2330 { 2331 name: "valid FQDN", 2332 in: &networking.WorkloadEntry{Address: "validdns.com", Ports: map[string]uint32{"7777": 7777}}, 2333 valid: true, 2334 }, 2335 { 2336 name: "invalid FQDN", 2337 in: &networking.WorkloadEntry{Address: "invaliddns.com:9443", Ports: map[string]uint32{"7777": 7777}}, 2338 valid: false, 2339 }, 2340 { 2341 name: "valid IP", 2342 in: &networking.WorkloadEntry{Address: "172.16.1.1", Ports: map[string]uint32{"7777": 7777}}, 2343 valid: true, 2344 }, 2345 } 2346 for _, tc := range testCases { 2347 t.Run(tc.name, func(t *testing.T) { 2348 warn, err := ValidateWorkloadEntry(config.Config{Spec: tc.in}) 2349 checkValidation(t, warn, err, tc.valid, tc.warning) 2350 }) 2351 } 2352 } 2353 2354 func TestValidateWorkloadGroup(t *testing.T) { 2355 testCases := []struct { 2356 name string 2357 in proto.Message 2358 valid bool 2359 warning bool 2360 }{ 2361 { 2362 name: "valid", 2363 in: &networking.WorkloadGroup{Template: &networking.WorkloadEntry{}}, 2364 valid: true, 2365 }, 2366 { 2367 name: "invalid", 2368 in: &networking.WorkloadGroup{Template: &networking.WorkloadEntry{}, Metadata: &networking.WorkloadGroup_ObjectMeta{Labels: map[string]string{ 2369 ".": "~", 2370 }}}, 2371 valid: false, 2372 }, 2373 { 2374 name: "probe missing method", 2375 in: &networking.WorkloadGroup{ 2376 Template: &networking.WorkloadEntry{}, 2377 Probe: &networking.ReadinessProbe{}, 2378 }, 2379 valid: false, 2380 }, 2381 { 2382 name: "probe nil", 2383 in: &networking.WorkloadGroup{ 2384 Template: &networking.WorkloadEntry{}, 2385 Probe: &networking.ReadinessProbe{ 2386 HealthCheckMethod: &networking.ReadinessProbe_HttpGet{}, 2387 }, 2388 }, 2389 valid: false, 2390 }, 2391 { 2392 name: "probe http empty", 2393 in: &networking.WorkloadGroup{ 2394 Template: &networking.WorkloadEntry{}, 2395 Probe: &networking.ReadinessProbe{ 2396 HealthCheckMethod: &networking.ReadinessProbe_HttpGet{ 2397 HttpGet: &networking.HTTPHealthCheckConfig{}, 2398 }, 2399 }, 2400 }, 2401 valid: false, 2402 }, 2403 { 2404 name: "probe http valid", 2405 in: &networking.WorkloadGroup{ 2406 Template: &networking.WorkloadEntry{}, 2407 Probe: &networking.ReadinessProbe{ 2408 HealthCheckMethod: &networking.ReadinessProbe_HttpGet{ 2409 HttpGet: &networking.HTTPHealthCheckConfig{ 2410 Port: 5, 2411 }, 2412 }, 2413 }, 2414 }, 2415 valid: true, 2416 }, 2417 { 2418 name: "probe tcp invalid", 2419 in: &networking.WorkloadGroup{ 2420 Template: &networking.WorkloadEntry{}, 2421 Probe: &networking.ReadinessProbe{ 2422 HealthCheckMethod: &networking.ReadinessProbe_TcpSocket{ 2423 TcpSocket: &networking.TCPHealthCheckConfig{}, 2424 }, 2425 }, 2426 }, 2427 valid: false, 2428 }, 2429 { 2430 name: "probe tcp valid", 2431 in: &networking.WorkloadGroup{ 2432 Template: &networking.WorkloadEntry{}, 2433 Probe: &networking.ReadinessProbe{ 2434 HealthCheckMethod: &networking.ReadinessProbe_TcpSocket{ 2435 TcpSocket: &networking.TCPHealthCheckConfig{ 2436 Port: 5, 2437 }, 2438 }, 2439 }, 2440 }, 2441 valid: true, 2442 }, 2443 { 2444 name: "probe exec invalid", 2445 in: &networking.WorkloadGroup{ 2446 Template: &networking.WorkloadEntry{}, 2447 Probe: &networking.ReadinessProbe{ 2448 HealthCheckMethod: &networking.ReadinessProbe_Exec{ 2449 Exec: &networking.ExecHealthCheckConfig{}, 2450 }, 2451 }, 2452 }, 2453 valid: false, 2454 }, 2455 { 2456 name: "probe exec valid", 2457 in: &networking.WorkloadGroup{ 2458 Template: &networking.WorkloadEntry{}, 2459 Probe: &networking.ReadinessProbe{ 2460 HealthCheckMethod: &networking.ReadinessProbe_Exec{ 2461 Exec: &networking.ExecHealthCheckConfig{ 2462 Command: []string{"foo", "bar"}, 2463 }, 2464 }, 2465 }, 2466 }, 2467 valid: true, 2468 }, 2469 } 2470 for _, tc := range testCases { 2471 t.Run(tc.name, func(t *testing.T) { 2472 warn, err := ValidateWorkloadGroup(config.Config{Spec: tc.in}) 2473 checkValidation(t, warn, err, tc.valid, tc.warning) 2474 }) 2475 } 2476 } 2477 2478 func checkValidation(t *testing.T, gotWarning Warning, gotError error, valid bool, warning bool) { 2479 t.Helper() 2480 if (gotError == nil) != valid { 2481 t.Fatalf("got valid=%v but wanted valid=%v: %v", gotError == nil, valid, gotError) 2482 } 2483 if (gotWarning == nil) == warning { 2484 t.Fatalf("got warning=%v but wanted warning=%v", gotWarning, warning) 2485 } 2486 } 2487 2488 func stringOrEmpty(v error) string { 2489 if v == nil { 2490 return "" 2491 } 2492 return v.Error() 2493 } 2494 2495 func checkValidationMessage(t *testing.T, gotWarning Warning, gotError error, wantWarning string, wantError string) { 2496 t.Helper() 2497 if (gotError == nil) != (wantError == "") { 2498 t.Fatalf("got err=%v but wanted err=%v", gotError, wantError) 2499 } 2500 if !strings.Contains(stringOrEmpty(gotError), wantError) { 2501 t.Fatalf("got err=%v but wanted err=%v", gotError, wantError) 2502 } 2503 2504 if (gotWarning == nil) != (wantWarning == "") { 2505 t.Fatalf("got warning=%v but wanted warning=%v", gotWarning, wantWarning) 2506 } 2507 if !strings.Contains(stringOrEmpty(gotWarning), wantWarning) { 2508 t.Fatalf("got warning=%v but wanted warning=%v", gotWarning, wantWarning) 2509 } 2510 } 2511 2512 func TestValidateDestinationRule(t *testing.T) { 2513 cases := []struct { 2514 name string 2515 in proto.Message 2516 valid bool 2517 warning bool 2518 }{ 2519 {name: "simple destination rule", in: &networking.DestinationRule{ 2520 Host: "reviews", 2521 Subsets: []*networking.Subset{ 2522 {Name: "v1", Labels: map[string]string{"version": "v1"}}, 2523 {Name: "v2", Labels: map[string]string{"version": "v2"}}, 2524 }, 2525 }, valid: true}, 2526 2527 {name: "simple destination rule with empty selector labels", in: &networking.DestinationRule{ 2528 Host: "reviews", 2529 Subsets: []*networking.Subset{ 2530 {Name: "v1", Labels: map[string]string{"version": "v1"}}, 2531 {Name: "v2", Labels: map[string]string{"version": "v2"}}, 2532 }, 2533 WorkloadSelector: &api.WorkloadSelector{}, 2534 }, valid: true, warning: true}, 2535 2536 {name: "missing destination name", in: &networking.DestinationRule{ 2537 Host: "", 2538 Subsets: []*networking.Subset{ 2539 {Name: "v1", Labels: map[string]string{"version": "v1"}}, 2540 {Name: "v2", Labels: map[string]string{"version": "v2"}}, 2541 }, 2542 }, valid: false}, 2543 2544 {name: "missing subset name", in: &networking.DestinationRule{ 2545 Host: "reviews", 2546 Subsets: []*networking.Subset{ 2547 {Name: "", Labels: map[string]string{"version": "v1"}}, 2548 {Name: "v2", Labels: map[string]string{"version": "v2"}}, 2549 }, 2550 }, valid: false}, 2551 2552 {name: "duplicate subset names", in: &networking.DestinationRule{ 2553 Host: "reviews", 2554 Subsets: []*networking.Subset{ 2555 {Name: "foo", Labels: map[string]string{"version": "v1"}}, 2556 {Name: "foo", Labels: map[string]string{"version": "v2"}}, 2557 }, 2558 }, valid: false}, 2559 2560 {name: "valid traffic policy, top level", in: &networking.DestinationRule{ 2561 Host: "reviews", 2562 TrafficPolicy: &networking.TrafficPolicy{ 2563 LoadBalancer: &networking.LoadBalancerSettings{ 2564 LbPolicy: &networking.LoadBalancerSettings_Simple{ 2565 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 2566 }, 2567 }, 2568 ConnectionPool: &networking.ConnectionPoolSettings{ 2569 Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, 2570 Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, 2571 }, 2572 OutlierDetection: &networking.OutlierDetection{ 2573 MinHealthPercent: 20, 2574 }, 2575 }, 2576 Subsets: []*networking.Subset{ 2577 {Name: "v1", Labels: map[string]string{"version": "v1"}}, 2578 {Name: "v2", Labels: map[string]string{"version": "v2"}}, 2579 }, 2580 }, valid: true}, 2581 2582 {name: "invalid traffic policy, top level", in: &networking.DestinationRule{ 2583 Host: "reviews", 2584 TrafficPolicy: &networking.TrafficPolicy{ 2585 LoadBalancer: &networking.LoadBalancerSettings{ 2586 LbPolicy: &networking.LoadBalancerSettings_Simple{ 2587 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 2588 }, 2589 }, 2590 ConnectionPool: &networking.ConnectionPoolSettings{}, 2591 OutlierDetection: &networking.OutlierDetection{ 2592 MinHealthPercent: 20, 2593 }, 2594 }, 2595 Subsets: []*networking.Subset{ 2596 {Name: "v1", Labels: map[string]string{"version": "v1"}}, 2597 {Name: "v2", Labels: map[string]string{"version": "v2"}}, 2598 }, 2599 }, valid: false}, 2600 2601 {name: "valid traffic policy, subset level", in: &networking.DestinationRule{ 2602 Host: "reviews", 2603 Subsets: []*networking.Subset{ 2604 { 2605 Name: "v1", Labels: map[string]string{"version": "v1"}, 2606 TrafficPolicy: &networking.TrafficPolicy{ 2607 LoadBalancer: &networking.LoadBalancerSettings{ 2608 LbPolicy: &networking.LoadBalancerSettings_Simple{ 2609 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 2610 }, 2611 }, 2612 ConnectionPool: &networking.ConnectionPoolSettings{ 2613 Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, 2614 Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, 2615 }, 2616 OutlierDetection: &networking.OutlierDetection{ 2617 MinHealthPercent: 20, 2618 }, 2619 }, 2620 }, 2621 {Name: "v2", Labels: map[string]string{"version": "v2"}}, 2622 }, 2623 }, valid: true}, 2624 2625 {name: "invalid traffic policy, subset level", in: &networking.DestinationRule{ 2626 Host: "reviews", 2627 Subsets: []*networking.Subset{ 2628 { 2629 Name: "v1", Labels: map[string]string{"version": "v1"}, 2630 TrafficPolicy: &networking.TrafficPolicy{ 2631 LoadBalancer: &networking.LoadBalancerSettings{ 2632 LbPolicy: &networking.LoadBalancerSettings_Simple{ 2633 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 2634 }, 2635 }, 2636 ConnectionPool: &networking.ConnectionPoolSettings{}, 2637 OutlierDetection: &networking.OutlierDetection{ 2638 MinHealthPercent: 20, 2639 }, 2640 }, 2641 }, 2642 {Name: "v2", Labels: map[string]string{"version": "v2"}}, 2643 }, 2644 }, valid: false}, 2645 2646 {name: "valid traffic policy, both levels", in: &networking.DestinationRule{ 2647 Host: "reviews", 2648 TrafficPolicy: &networking.TrafficPolicy{ 2649 LoadBalancer: &networking.LoadBalancerSettings{ 2650 LbPolicy: &networking.LoadBalancerSettings_Simple{ 2651 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 2652 }, 2653 }, 2654 ConnectionPool: &networking.ConnectionPoolSettings{ 2655 Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, 2656 Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, 2657 }, 2658 OutlierDetection: &networking.OutlierDetection{ 2659 MinHealthPercent: 20, 2660 }, 2661 }, 2662 Subsets: []*networking.Subset{ 2663 { 2664 Name: "v1", Labels: map[string]string{"version": "v1"}, 2665 TrafficPolicy: &networking.TrafficPolicy{ 2666 LoadBalancer: &networking.LoadBalancerSettings{ 2667 LbPolicy: &networking.LoadBalancerSettings_Simple{ 2668 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 2669 }, 2670 }, 2671 ConnectionPool: &networking.ConnectionPoolSettings{ 2672 Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, 2673 Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, 2674 }, 2675 OutlierDetection: &networking.OutlierDetection{ 2676 MinHealthPercent: 30, 2677 }, 2678 }, 2679 }, 2680 {Name: "v2", Labels: map[string]string{"version": "v2"}}, 2681 }, 2682 }, valid: true}, 2683 2684 {name: "InsecureSkipVerify is not specified with tls mode simple, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{ 2685 Host: "reviews", 2686 TrafficPolicy: &networking.TrafficPolicy{ 2687 Tls: &networking.ClientTLSSettings{ 2688 Mode: networking.ClientTLSSettings_SIMPLE, 2689 CaCertificates: "test", 2690 SubjectAltNames: []string{"reviews.default.svc"}, 2691 }, 2692 }, 2693 }, valid: true}, 2694 2695 {name: "InsecureSkipVerify is not specified with tls mode simple, and the ca cert is specified by CredentialName", in: &networking.DestinationRule{ 2696 Host: "reviews", 2697 TrafficPolicy: &networking.TrafficPolicy{ 2698 Tls: &networking.ClientTLSSettings{ 2699 Mode: networking.ClientTLSSettings_SIMPLE, 2700 CredentialName: "test", 2701 SubjectAltNames: []string{"reviews.default.svc"}, 2702 }, 2703 }, 2704 }, valid: true}, 2705 2706 {name: "InsecureSkipVerify is set false with tls mode simple, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{ 2707 Host: "reviews", 2708 TrafficPolicy: &networking.TrafficPolicy{ 2709 Tls: &networking.ClientTLSSettings{ 2710 Mode: networking.ClientTLSSettings_SIMPLE, 2711 CaCertificates: "test", 2712 SubjectAltNames: []string{"reviews.default.svc"}, 2713 InsecureSkipVerify: &wrapperspb.BoolValue{ 2714 Value: false, 2715 }, 2716 }, 2717 }, 2718 }, valid: true}, 2719 2720 {name: "InsecureSkipVerify is set false with tls mode simple, and the ca cert is specified by CredentialName", in: &networking.DestinationRule{ 2721 Host: "reviews", 2722 TrafficPolicy: &networking.TrafficPolicy{ 2723 Tls: &networking.ClientTLSSettings{ 2724 Mode: networking.ClientTLSSettings_SIMPLE, 2725 CredentialName: "test", 2726 SubjectAltNames: []string{"reviews.default.svc"}, 2727 InsecureSkipVerify: &wrapperspb.BoolValue{ 2728 Value: false, 2729 }, 2730 }, 2731 }, 2732 }, valid: true}, 2733 2734 {name: "InsecureSkipVerify is set true with tls mode simple, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{ 2735 Host: "reviews", 2736 TrafficPolicy: &networking.TrafficPolicy{ 2737 Tls: &networking.ClientTLSSettings{ 2738 Mode: networking.ClientTLSSettings_SIMPLE, 2739 CredentialName: "test", 2740 InsecureSkipVerify: &wrapperspb.BoolValue{ 2741 Value: true, 2742 }, 2743 }, 2744 }, 2745 }, valid: false}, 2746 2747 {name: "InsecureSkipVerify is set true with tls mode simple, and the ca cert is specified by CredentialName", in: &networking.DestinationRule{ 2748 Host: "reviews", 2749 TrafficPolicy: &networking.TrafficPolicy{ 2750 Tls: &networking.ClientTLSSettings{ 2751 Mode: networking.ClientTLSSettings_SIMPLE, 2752 CaCertificates: "test", 2753 InsecureSkipVerify: &wrapperspb.BoolValue{ 2754 Value: true, 2755 }, 2756 }, 2757 }, 2758 }, valid: false}, 2759 2760 {name: "InsecureSkipVerify is set true with tls mode simple, and the san is specified", in: &networking.DestinationRule{ 2761 Host: "reviews", 2762 TrafficPolicy: &networking.TrafficPolicy{ 2763 Tls: &networking.ClientTLSSettings{ 2764 Mode: networking.ClientTLSSettings_SIMPLE, 2765 SubjectAltNames: []string{"reviews.default.svc"}, 2766 InsecureSkipVerify: &wrapperspb.BoolValue{ 2767 Value: true, 2768 }, 2769 }, 2770 }, 2771 }, valid: false}, 2772 2773 {name: "InsecureSkipVerify is not specified with tls mode mutual, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{ 2774 Host: "reviews", 2775 TrafficPolicy: &networking.TrafficPolicy{ 2776 Tls: &networking.ClientTLSSettings{ 2777 Mode: networking.ClientTLSSettings_MUTUAL, 2778 CaCertificates: "test", 2779 PrivateKey: "key", 2780 ClientCertificate: "cert", 2781 SubjectAltNames: []string{"reviews.default.svc"}, 2782 }, 2783 }, 2784 }, valid: true}, 2785 2786 {name: "InsecureSkipVerify is not specified with tls mode mutual, and the ca cert is specified by CredentialName", in: &networking.DestinationRule{ 2787 Host: "reviews", 2788 TrafficPolicy: &networking.TrafficPolicy{ 2789 Tls: &networking.ClientTLSSettings{ 2790 Mode: networking.ClientTLSSettings_MUTUAL, 2791 CredentialName: "test", 2792 SubjectAltNames: []string{"reviews.default.svc"}, 2793 }, 2794 }, 2795 }, valid: true}, 2796 2797 {name: "InsecureSkipVerify is set false with tls mode mutual, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{ 2798 Host: "reviews", 2799 TrafficPolicy: &networking.TrafficPolicy{ 2800 Tls: &networking.ClientTLSSettings{ 2801 Mode: networking.ClientTLSSettings_MUTUAL, 2802 CaCertificates: "test", 2803 PrivateKey: "key", 2804 ClientCertificate: "cert", 2805 SubjectAltNames: []string{"reviews.default.svc"}, 2806 InsecureSkipVerify: &wrapperspb.BoolValue{ 2807 Value: false, 2808 }, 2809 }, 2810 }, 2811 }, valid: true}, 2812 2813 {name: "InsecureSkipVerify is set false with tls mode mutual, and the ca cert is specified by CredentialName", in: &networking.DestinationRule{ 2814 Host: "reviews", 2815 TrafficPolicy: &networking.TrafficPolicy{ 2816 Tls: &networking.ClientTLSSettings{ 2817 Mode: networking.ClientTLSSettings_MUTUAL, 2818 CredentialName: "test", 2819 SubjectAltNames: []string{"reviews.default.svc"}, 2820 InsecureSkipVerify: &wrapperspb.BoolValue{ 2821 Value: false, 2822 }, 2823 }, 2824 }, 2825 }, valid: true}, 2826 2827 {name: "InsecureSkipVerify is set true with tls mode mutual, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{ 2828 Host: "reviews", 2829 TrafficPolicy: &networking.TrafficPolicy{ 2830 Tls: &networking.ClientTLSSettings{ 2831 Mode: networking.ClientTLSSettings_MUTUAL, 2832 CaCertificates: "test", 2833 PrivateKey: "key", 2834 ClientCertificate: "cert", 2835 InsecureSkipVerify: &wrapperspb.BoolValue{ 2836 Value: true, 2837 }, 2838 }, 2839 }, 2840 }, valid: false}, 2841 2842 {name: "InsecureSkipVerify is set true with tls mode mutual, and the ca cert is specified by CaCertificates", in: &networking.DestinationRule{ 2843 Host: "reviews", 2844 TrafficPolicy: &networking.TrafficPolicy{ 2845 Tls: &networking.ClientTLSSettings{ 2846 Mode: networking.ClientTLSSettings_MUTUAL, 2847 CredentialName: "test", 2848 InsecureSkipVerify: &wrapperspb.BoolValue{ 2849 Value: true, 2850 }, 2851 }, 2852 }, 2853 }, valid: true}, 2854 2855 {name: "InsecureSkipVerify is set true with tls mode mutual, and the ca cert is not specified", in: &networking.DestinationRule{ 2856 Host: "reviews", 2857 TrafficPolicy: &networking.TrafficPolicy{ 2858 Tls: &networking.ClientTLSSettings{ 2859 Mode: networking.ClientTLSSettings_MUTUAL, 2860 PrivateKey: "key", 2861 ClientCertificate: "cert", 2862 InsecureSkipVerify: &wrapperspb.BoolValue{ 2863 Value: true, 2864 }, 2865 }, 2866 }, 2867 }, valid: true}, 2868 2869 {name: "InsecureSkipVerify is set true with tls mode mutual, and the san is specified", in: &networking.DestinationRule{ 2870 Host: "reviews", 2871 TrafficPolicy: &networking.TrafficPolicy{ 2872 Tls: &networking.ClientTLSSettings{ 2873 Mode: networking.ClientTLSSettings_MUTUAL, 2874 PrivateKey: "key", 2875 ClientCertificate: "cert", 2876 SubjectAltNames: []string{"reviews.default.svc"}, 2877 InsecureSkipVerify: &wrapperspb.BoolValue{ 2878 Value: true, 2879 }, 2880 }, 2881 }, 2882 }, valid: false}, 2883 } 2884 for _, c := range cases { 2885 t.Run(c.name, func(t *testing.T) { 2886 warn, got := ValidateDestinationRule(config.Config{ 2887 Meta: config.Meta{ 2888 Name: someName, 2889 Namespace: someNamespace, 2890 }, 2891 Spec: c.in, 2892 }) 2893 if (got == nil) != c.valid { 2894 t.Errorf("ValidateDestinationRule failed on %v: got valid=%v but wanted valid=%v: %v", 2895 c.name, got == nil, c.valid, got) 2896 } 2897 if (warn == nil) == c.warning { 2898 t.Errorf("ValidateDestinationRule failed on %v: got warn=%v but wanted warn=%v: %v", 2899 c.name, warn == nil, c.warning, warn) 2900 } 2901 }) 2902 } 2903 } 2904 2905 func TestValidateTrafficPolicy(t *testing.T) { 2906 cases := []struct { 2907 name string 2908 in *networking.TrafficPolicy 2909 valid bool 2910 }{ 2911 { 2912 name: "valid traffic policy", in: &networking.TrafficPolicy{ 2913 LoadBalancer: &networking.LoadBalancerSettings{ 2914 LbPolicy: &networking.LoadBalancerSettings_Simple{ 2915 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 2916 }, 2917 }, 2918 ConnectionPool: &networking.ConnectionPoolSettings{ 2919 Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, 2920 Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, 2921 }, 2922 OutlierDetection: &networking.OutlierDetection{ 2923 MinHealthPercent: 20, 2924 }, 2925 }, 2926 valid: true, 2927 }, 2928 { 2929 name: "invalid traffic policy, nil entries", in: &networking.TrafficPolicy{}, 2930 valid: false, 2931 }, 2932 2933 { 2934 name: "invalid traffic policy, missing port in port level settings", in: &networking.TrafficPolicy{ 2935 PortLevelSettings: []*networking.TrafficPolicy_PortTrafficPolicy{ 2936 { 2937 LoadBalancer: &networking.LoadBalancerSettings{ 2938 LbPolicy: &networking.LoadBalancerSettings_Simple{ 2939 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 2940 }, 2941 }, 2942 ConnectionPool: &networking.ConnectionPoolSettings{ 2943 Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, 2944 Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, 2945 }, 2946 OutlierDetection: &networking.OutlierDetection{ 2947 MinHealthPercent: 20, 2948 }, 2949 }, 2950 }, 2951 }, 2952 valid: false, 2953 }, 2954 { 2955 name: "invalid traffic policy, bad connection pool", in: &networking.TrafficPolicy{ 2956 LoadBalancer: &networking.LoadBalancerSettings{ 2957 LbPolicy: &networking.LoadBalancerSettings_Simple{ 2958 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 2959 }, 2960 }, 2961 ConnectionPool: &networking.ConnectionPoolSettings{}, 2962 OutlierDetection: &networking.OutlierDetection{ 2963 MinHealthPercent: 20, 2964 }, 2965 }, 2966 valid: false, 2967 }, 2968 { 2969 name: "invalid traffic policy, bad max connection duration", in: &networking.TrafficPolicy{ 2970 LoadBalancer: &networking.LoadBalancerSettings{ 2971 LbPolicy: &networking.LoadBalancerSettings_Simple{ 2972 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 2973 }, 2974 }, 2975 ConnectionPool: &networking.ConnectionPoolSettings{ 2976 Tcp: &networking.ConnectionPoolSettings_TCPSettings{ 2977 MaxConnectionDuration: &durationpb.Duration{Nanos: 500}, 2978 }, 2979 }, 2980 }, 2981 valid: false, 2982 }, 2983 { 2984 name: "invalid traffic policy, panic threshold too low", in: &networking.TrafficPolicy{ 2985 LoadBalancer: &networking.LoadBalancerSettings{ 2986 LbPolicy: &networking.LoadBalancerSettings_Simple{ 2987 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 2988 }, 2989 }, 2990 ConnectionPool: &networking.ConnectionPoolSettings{ 2991 Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: 7}, 2992 Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: 11}, 2993 }, 2994 OutlierDetection: &networking.OutlierDetection{ 2995 MinHealthPercent: -1, 2996 }, 2997 }, 2998 valid: false, 2999 }, 3000 { 3001 name: "invalid traffic policy, both upgrade and use client protocol set", in: &networking.TrafficPolicy{ 3002 ConnectionPool: &networking.ConnectionPoolSettings{ 3003 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 3004 H2UpgradePolicy: networking.ConnectionPoolSettings_HTTPSettings_UPGRADE, 3005 UseClientProtocol: true, 3006 }, 3007 }, 3008 }, 3009 valid: false, 3010 }, 3011 } 3012 for _, c := range cases { 3013 if got := validateTrafficPolicy(c.in).Err; (got == nil) != c.valid { 3014 t.Errorf("ValidateTrafficPolicy failed on %v: got valid=%v but wanted valid=%v: %v", 3015 c.name, got == nil, c.valid, got) 3016 } 3017 } 3018 } 3019 3020 func TestValidateConnectionPool(t *testing.T) { 3021 cases := []struct { 3022 name string 3023 in *networking.ConnectionPoolSettings 3024 valid bool 3025 }{ 3026 { 3027 name: "valid connection pool, tcp and http", in: &networking.ConnectionPoolSettings{ 3028 Tcp: &networking.ConnectionPoolSettings_TCPSettings{ 3029 MaxConnections: 7, 3030 ConnectTimeout: &durationpb.Duration{Seconds: 2}, 3031 }, 3032 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 3033 Http1MaxPendingRequests: 2, 3034 Http2MaxRequests: 11, 3035 MaxRequestsPerConnection: 5, 3036 MaxRetries: 4, 3037 IdleTimeout: &durationpb.Duration{Seconds: 30}, 3038 MaxConcurrentStreams: 5, 3039 }, 3040 }, 3041 valid: true, 3042 }, 3043 3044 { 3045 name: "valid connection pool, tcp only", in: &networking.ConnectionPoolSettings{ 3046 Tcp: &networking.ConnectionPoolSettings_TCPSettings{ 3047 MaxConnections: 7, 3048 ConnectTimeout: &durationpb.Duration{Seconds: 2}, 3049 }, 3050 }, 3051 valid: true, 3052 }, 3053 3054 { 3055 name: "valid connection pool, http only", in: &networking.ConnectionPoolSettings{ 3056 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 3057 Http1MaxPendingRequests: 2, 3058 Http2MaxRequests: 11, 3059 MaxRequestsPerConnection: 5, 3060 MaxRetries: 4, 3061 IdleTimeout: &durationpb.Duration{Seconds: 30}, 3062 MaxConcurrentStreams: 5, 3063 }, 3064 }, 3065 valid: true, 3066 }, 3067 3068 { 3069 name: "valid connection pool, http only with empty idle timeout", in: &networking.ConnectionPoolSettings{ 3070 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 3071 Http1MaxPendingRequests: 2, 3072 Http2MaxRequests: 11, 3073 MaxRequestsPerConnection: 5, 3074 MaxRetries: 4, 3075 MaxConcurrentStreams: 5, 3076 }, 3077 }, 3078 valid: true, 3079 }, 3080 3081 {name: "invalid connection pool, empty", in: &networking.ConnectionPoolSettings{}, valid: false}, 3082 3083 { 3084 name: "invalid connection pool, bad max connections", in: &networking.ConnectionPoolSettings{ 3085 Tcp: &networking.ConnectionPoolSettings_TCPSettings{MaxConnections: -1}, 3086 }, 3087 valid: false, 3088 }, 3089 3090 { 3091 name: "invalid connection pool, bad connect timeout", in: &networking.ConnectionPoolSettings{ 3092 Tcp: &networking.ConnectionPoolSettings_TCPSettings{ 3093 ConnectTimeout: &durationpb.Duration{Seconds: 2, Nanos: 5}, 3094 }, 3095 }, 3096 valid: false, 3097 }, 3098 3099 { 3100 name: "invalid connection pool, bad max pending requests", in: &networking.ConnectionPoolSettings{ 3101 Http: &networking.ConnectionPoolSettings_HTTPSettings{Http1MaxPendingRequests: -1}, 3102 }, 3103 valid: false, 3104 }, 3105 3106 { 3107 name: "invalid connection pool, bad max requests", in: &networking.ConnectionPoolSettings{ 3108 Http: &networking.ConnectionPoolSettings_HTTPSettings{Http2MaxRequests: -1}, 3109 }, 3110 valid: false, 3111 }, 3112 3113 { 3114 name: "invalid connection pool, bad max requests per connection", in: &networking.ConnectionPoolSettings{ 3115 Http: &networking.ConnectionPoolSettings_HTTPSettings{MaxRequestsPerConnection: -1}, 3116 }, 3117 valid: false, 3118 }, 3119 3120 { 3121 name: "invalid connection pool, bad max retries", in: &networking.ConnectionPoolSettings{ 3122 Http: &networking.ConnectionPoolSettings_HTTPSettings{MaxRetries: -1}, 3123 }, 3124 valid: false, 3125 }, 3126 3127 { 3128 name: "invalid connection pool, bad idle timeout", in: &networking.ConnectionPoolSettings{ 3129 Http: &networking.ConnectionPoolSettings_HTTPSettings{IdleTimeout: &durationpb.Duration{Seconds: 30, Nanos: 5}}, 3130 }, 3131 valid: false, 3132 }, 3133 3134 { 3135 name: "invalid connection pool, bad max concurrent streams", in: &networking.ConnectionPoolSettings{ 3136 Http: &networking.ConnectionPoolSettings_HTTPSettings{MaxConcurrentStreams: -1}, 3137 }, 3138 valid: false, 3139 }, 3140 } 3141 3142 for _, c := range cases { 3143 if got := validateConnectionPool(c.in); (got == nil) != c.valid { 3144 t.Errorf("ValidateConnectionSettings failed on %v: got valid=%v but wanted valid=%v: %v", 3145 c.name, got == nil, c.valid, got) 3146 } 3147 } 3148 } 3149 3150 func TestValidateLoadBalancer(t *testing.T) { 3151 duration := durationpb.Duration{Seconds: int64(time.Hour / time.Second)} 3152 cases := []struct { 3153 name string 3154 in *networking.LoadBalancerSettings 3155 valid bool 3156 }{ 3157 { 3158 name: "valid load balancer with simple load balancing", in: &networking.LoadBalancerSettings{ 3159 LbPolicy: &networking.LoadBalancerSettings_Simple{ 3160 Simple: networking.LoadBalancerSettings_ROUND_ROBIN, 3161 }, 3162 }, 3163 valid: true, 3164 }, 3165 3166 { 3167 name: "valid load balancer with consistentHash load balancing", in: &networking.LoadBalancerSettings{ 3168 LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ 3169 ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ 3170 MinimumRingSize: 1024, 3171 HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ 3172 HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ 3173 Name: "test", 3174 Ttl: &duration, 3175 }, 3176 }, 3177 }, 3178 }, 3179 }, 3180 valid: true, 3181 }, 3182 3183 { 3184 name: "invalid load balancer with consistentHash load balancing, missing name", in: &networking.LoadBalancerSettings{ 3185 LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ 3186 ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ 3187 MinimumRingSize: 1024, 3188 HashKey: &networking.LoadBalancerSettings_ConsistentHashLB_HttpCookie{ 3189 HttpCookie: &networking.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{ 3190 Ttl: &duration, 3191 }, 3192 }, 3193 }, 3194 }, 3195 }, 3196 valid: false, 3197 }, 3198 3199 { 3200 name: "invalid load balancer with consistentHash load balancing, maglev not prime", in: &networking.LoadBalancerSettings{ 3201 LbPolicy: &networking.LoadBalancerSettings_ConsistentHash{ 3202 ConsistentHash: &networking.LoadBalancerSettings_ConsistentHashLB{ 3203 HashAlgorithm: &networking.LoadBalancerSettings_ConsistentHashLB_Maglev{ 3204 Maglev: &networking.LoadBalancerSettings_ConsistentHashLB_MagLev{TableSize: 1000}, 3205 }, 3206 }, 3207 }, 3208 }, 3209 valid: false, 3210 }, 3211 } 3212 3213 for _, c := range cases { 3214 if got := validateLoadBalancer(c.in, nil); (got.Err == nil) != c.valid { 3215 t.Errorf("validateLoadBalancer failed on %v: got valid=%v but wanted valid=%v: %v", 3216 c.name, got.Err == nil, c.valid, got) 3217 } 3218 } 3219 } 3220 3221 func TestValidateOutlierDetection(t *testing.T) { 3222 cases := []struct { 3223 name string 3224 in *networking.OutlierDetection 3225 valid bool 3226 warn bool 3227 }{ 3228 {name: "valid outlier detection", in: &networking.OutlierDetection{ 3229 Interval: &durationpb.Duration{Seconds: 2}, 3230 BaseEjectionTime: &durationpb.Duration{Seconds: 2}, 3231 MaxEjectionPercent: 50, 3232 }, valid: true}, 3233 3234 { 3235 name: "invalid outlier detection, bad interval", in: &networking.OutlierDetection{ 3236 Interval: &durationpb.Duration{Seconds: 2, Nanos: 5}, 3237 }, 3238 valid: false, 3239 }, 3240 3241 { 3242 name: "invalid outlier detection, bad base ejection time", in: &networking.OutlierDetection{ 3243 BaseEjectionTime: &durationpb.Duration{Seconds: 2, Nanos: 5}, 3244 }, 3245 valid: false, 3246 }, 3247 3248 { 3249 name: "invalid outlier detection, bad max ejection percent", in: &networking.OutlierDetection{ 3250 MaxEjectionPercent: 105, 3251 }, 3252 valid: false, 3253 }, 3254 { 3255 name: "invalid outlier detection, panic threshold too low", in: &networking.OutlierDetection{ 3256 MinHealthPercent: -1, 3257 }, 3258 valid: false, 3259 }, 3260 { 3261 name: "invalid outlier detection, panic threshold too high", in: &networking.OutlierDetection{ 3262 MinHealthPercent: 101, 3263 }, 3264 valid: false, 3265 }, 3266 { 3267 name: "deprecated outlier detection, ConsecutiveErrors", in: &networking.OutlierDetection{ 3268 ConsecutiveErrors: 101, 3269 }, 3270 valid: true, 3271 warn: true, 3272 }, 3273 { 3274 name: "consecutive local origin errors is set but split local origin errors is not set", in: &networking.OutlierDetection{ 3275 ConsecutiveLocalOriginFailures: &wrapperspb.UInt32Value{Value: 10}, 3276 }, 3277 valid: false, 3278 }, 3279 } 3280 3281 for _, c := range cases { 3282 got := validateOutlierDetection(c.in) 3283 if (got.Err == nil) != c.valid { 3284 t.Errorf("ValidateOutlierDetection failed on %v: got valid=%v but wanted valid=%v: %v", 3285 c.name, got.Err == nil, c.valid, got.Err) 3286 } 3287 if (got.Warning == nil) == c.warn { 3288 t.Errorf("ValidateOutlierDetection failed on %v: got warn=%v but wanted warn=%v: %v", 3289 c.name, got.Warning == nil, c.warn, got.Warning) 3290 } 3291 } 3292 } 3293 3294 func TestValidateServiceEntries(t *testing.T) { 3295 cases := []struct { 3296 name string 3297 in *networking.ServiceEntry 3298 valid bool 3299 warning bool 3300 }{ 3301 { 3302 name: "discovery type DNS", in: &networking.ServiceEntry{ 3303 Hosts: []string{"*.google.com"}, 3304 Ports: []*networking.ServicePort{ 3305 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3306 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3307 }, 3308 Endpoints: []*networking.WorkloadEntry{ 3309 {Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}}, 3310 {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}}, 3311 }, 3312 Resolution: networking.ServiceEntry_DNS, 3313 }, 3314 valid: true, 3315 }, 3316 { 3317 name: "discovery type DNS Round Robin", in: &networking.ServiceEntry{ 3318 Hosts: []string{"*.istio.io"}, 3319 Ports: []*networking.ServicePort{ 3320 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3321 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3322 }, 3323 Endpoints: []*networking.WorkloadEntry{ 3324 {Address: "api-v1.istio.io", Ports: map[string]uint32{"http-valid1": 8080}}, 3325 }, 3326 Resolution: networking.ServiceEntry_DNS_ROUND_ROBIN, 3327 }, 3328 valid: true, 3329 }, 3330 { 3331 name: "discovery type DNS, label tlsMode: istio", in: &networking.ServiceEntry{ 3332 Hosts: []string{"*.google.com"}, 3333 Ports: []*networking.ServicePort{ 3334 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3335 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3336 }, 3337 Endpoints: []*networking.WorkloadEntry{ 3338 {Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}, Labels: map[string]string{"security.istio.io/tlsMode": "istio"}}, 3339 {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}, Labels: map[string]string{"security.istio.io/tlsMode": "istio"}}, 3340 }, 3341 Resolution: networking.ServiceEntry_DNS, 3342 }, 3343 valid: true, 3344 }, 3345 { 3346 name: "discovery type DNS, one host set with IP address and https port", 3347 in: &networking.ServiceEntry{ 3348 Hosts: []string{"httpbin.org"}, 3349 Addresses: []string{"10.10.10.10"}, 3350 Ports: []*networking.ServicePort{ 3351 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3352 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3353 {Number: 443, Protocol: "https", Name: "https"}, 3354 }, 3355 Resolution: networking.ServiceEntry_DNS, 3356 }, 3357 valid: true, 3358 warning: false, 3359 }, 3360 3361 { 3362 name: "discovery type DNS, multi hosts set with IP address and https port", 3363 in: &networking.ServiceEntry{ 3364 Hosts: []string{"httpbin.org", "wikipedia.org"}, 3365 Addresses: []string{"10.10.10.10"}, 3366 Ports: []*networking.ServicePort{ 3367 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3368 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3369 {Number: 443, Protocol: "https", Name: "https"}, 3370 }, 3371 Resolution: networking.ServiceEntry_DNS, 3372 }, 3373 valid: true, 3374 warning: true, 3375 }, 3376 3377 { 3378 name: "discovery type DNS, IP address set", 3379 in: &networking.ServiceEntry{ 3380 Hosts: []string{"*.google.com"}, 3381 Addresses: []string{"10.10.10.10"}, 3382 Ports: []*networking.ServicePort{ 3383 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3384 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3385 }, 3386 Endpoints: []*networking.WorkloadEntry{ 3387 {Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}}, 3388 {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}}, 3389 }, 3390 Resolution: networking.ServiceEntry_DNS, 3391 }, 3392 valid: true, 3393 warning: false, 3394 }, 3395 3396 { 3397 name: "discovery type DNS, IP in endpoints", in: &networking.ServiceEntry{ 3398 Hosts: []string{"*.google.com"}, 3399 Ports: []*networking.ServicePort{ 3400 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3401 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3402 }, 3403 Endpoints: []*networking.WorkloadEntry{ 3404 {Address: "1.1.1.1", Ports: map[string]uint32{"http-valid1": 8080}}, 3405 {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}}, 3406 }, 3407 Resolution: networking.ServiceEntry_DNS, 3408 }, 3409 valid: true, 3410 }, 3411 3412 { 3413 name: "empty hosts", in: &networking.ServiceEntry{ 3414 Ports: []*networking.ServicePort{ 3415 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3416 }, 3417 Endpoints: []*networking.WorkloadEntry{ 3418 {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}}, 3419 }, 3420 Resolution: networking.ServiceEntry_DNS, 3421 }, 3422 valid: false, 3423 }, 3424 { 3425 name: "bad hosts", in: &networking.ServiceEntry{ 3426 Hosts: []string{"-"}, 3427 Ports: []*networking.ServicePort{ 3428 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3429 }, 3430 Endpoints: []*networking.WorkloadEntry{ 3431 {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}}, 3432 }, 3433 Resolution: networking.ServiceEntry_DNS, 3434 }, 3435 valid: false, 3436 }, 3437 { 3438 name: "full wildcard host", in: &networking.ServiceEntry{ 3439 Hosts: []string{"foo.com", "*"}, 3440 Ports: []*networking.ServicePort{ 3441 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3442 }, 3443 Endpoints: []*networking.WorkloadEntry{ 3444 {Address: "in.google.com", Ports: map[string]uint32{"http-valid2": 9080}}, 3445 }, 3446 Resolution: networking.ServiceEntry_DNS, 3447 }, 3448 valid: false, 3449 }, 3450 { 3451 name: "short name host", in: &networking.ServiceEntry{ 3452 Hosts: []string{"foo", "bar.com"}, 3453 Ports: []*networking.ServicePort{ 3454 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3455 }, 3456 Endpoints: []*networking.WorkloadEntry{ 3457 {Address: "in.google.com", Ports: map[string]uint32{"http-valid1": 9080}}, 3458 }, 3459 Resolution: networking.ServiceEntry_DNS, 3460 }, 3461 valid: true, 3462 }, 3463 { 3464 name: "undefined endpoint port", in: &networking.ServiceEntry{ 3465 Hosts: []string{"google.com"}, 3466 Ports: []*networking.ServicePort{ 3467 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3468 {Number: 80, Protocol: "http", Name: "http-valid2"}, 3469 }, 3470 Endpoints: []*networking.WorkloadEntry{ 3471 {Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}}, 3472 {Address: "in.google.com", Ports: map[string]uint32{"http-dne": 9080}}, 3473 }, 3474 Resolution: networking.ServiceEntry_DNS, 3475 }, 3476 valid: false, 3477 }, 3478 3479 { 3480 name: "discovery type DNS, non-FQDN endpoint", in: &networking.ServiceEntry{ 3481 Hosts: []string{"*.google.com"}, 3482 Ports: []*networking.ServicePort{ 3483 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3484 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3485 }, 3486 Endpoints: []*networking.WorkloadEntry{ 3487 {Address: "*.lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}}, 3488 {Address: "in.google.com", Ports: map[string]uint32{"http-dne": 9080}}, 3489 }, 3490 Resolution: networking.ServiceEntry_DNS, 3491 }, 3492 valid: false, 3493 }, 3494 3495 { 3496 name: "discovery type DNS, non-FQDN host", in: &networking.ServiceEntry{ 3497 Hosts: []string{"*.google.com"}, 3498 Ports: []*networking.ServicePort{ 3499 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3500 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3501 }, 3502 3503 Resolution: networking.ServiceEntry_DNS, 3504 }, 3505 valid: false, 3506 }, 3507 3508 { 3509 name: "discovery type DNS, no endpoints", in: &networking.ServiceEntry{ 3510 Hosts: []string{"google.com"}, 3511 Ports: []*networking.ServicePort{ 3512 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3513 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3514 }, 3515 3516 Resolution: networking.ServiceEntry_DNS, 3517 }, 3518 valid: true, 3519 }, 3520 3521 { 3522 name: "discovery type DNS, unix endpoint", in: &networking.ServiceEntry{ 3523 Hosts: []string{"*.google.com"}, 3524 Ports: []*networking.ServicePort{ 3525 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3526 }, 3527 Endpoints: []*networking.WorkloadEntry{ 3528 {Address: "unix:///lon/google/com"}, 3529 }, 3530 Resolution: networking.ServiceEntry_DNS, 3531 }, 3532 valid: false, 3533 }, 3534 3535 { 3536 name: "discovery type none", in: &networking.ServiceEntry{ 3537 Hosts: []string{"google.com"}, 3538 Ports: []*networking.ServicePort{ 3539 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3540 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3541 }, 3542 Resolution: networking.ServiceEntry_NONE, 3543 }, 3544 valid: true, 3545 }, 3546 3547 { 3548 name: "discovery type none, endpoints provided", in: &networking.ServiceEntry{ 3549 Hosts: []string{"google.com"}, 3550 Ports: []*networking.ServicePort{ 3551 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3552 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3553 }, 3554 Endpoints: []*networking.WorkloadEntry{ 3555 {Address: "lon.google.com", Ports: map[string]uint32{"http-valid1": 8080}}, 3556 }, 3557 Resolution: networking.ServiceEntry_NONE, 3558 }, 3559 valid: false, 3560 }, 3561 3562 { 3563 name: "discovery type none, cidr addresses", in: &networking.ServiceEntry{ 3564 Hosts: []string{"google.com"}, 3565 Addresses: []string{"172.1.2.16/16"}, 3566 Ports: []*networking.ServicePort{ 3567 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3568 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3569 }, 3570 Resolution: networking.ServiceEntry_NONE, 3571 }, 3572 valid: true, 3573 }, 3574 3575 { 3576 name: "discovery type static, cidr addresses with endpoints", in: &networking.ServiceEntry{ 3577 Hosts: []string{"google.com"}, 3578 Addresses: []string{"172.1.2.16/16"}, 3579 Ports: []*networking.ServicePort{ 3580 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3581 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3582 }, 3583 Endpoints: []*networking.WorkloadEntry{ 3584 {Address: "1.1.1.1", Ports: map[string]uint32{"http-valid1": 8080}}, 3585 {Address: "2.2.2.2", Ports: map[string]uint32{"http-valid2": 9080}}, 3586 }, 3587 Resolution: networking.ServiceEntry_STATIC, 3588 }, 3589 valid: true, 3590 }, 3591 3592 { 3593 name: "discovery type static", in: &networking.ServiceEntry{ 3594 Hosts: []string{"google.com"}, 3595 Addresses: []string{"172.1.2.16"}, 3596 Ports: []*networking.ServicePort{ 3597 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3598 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3599 }, 3600 Endpoints: []*networking.WorkloadEntry{ 3601 {Address: "1.1.1.1", Ports: map[string]uint32{"http-valid1": 8080}}, 3602 {Address: "2.2.2.2", Ports: map[string]uint32{"http-valid2": 9080}}, 3603 }, 3604 Resolution: networking.ServiceEntry_STATIC, 3605 }, 3606 valid: true, 3607 }, 3608 3609 { 3610 name: "discovery type static, FQDN in endpoints", in: &networking.ServiceEntry{ 3611 Hosts: []string{"google.com"}, 3612 Addresses: []string{"172.1.2.16"}, 3613 Ports: []*networking.ServicePort{ 3614 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3615 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3616 }, 3617 Endpoints: []*networking.WorkloadEntry{ 3618 {Address: "google.com", Ports: map[string]uint32{"http-valid1": 8080}}, 3619 {Address: "2.2.2.2", Ports: map[string]uint32{"http-valid2": 9080}}, 3620 }, 3621 Resolution: networking.ServiceEntry_STATIC, 3622 }, 3623 valid: false, 3624 }, 3625 3626 { 3627 name: "discovery type static, missing endpoints", in: &networking.ServiceEntry{ 3628 Hosts: []string{"google.com"}, 3629 Addresses: []string{"172.1.2.16"}, 3630 Ports: []*networking.ServicePort{ 3631 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3632 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3633 }, 3634 Resolution: networking.ServiceEntry_STATIC, 3635 }, 3636 valid: true, 3637 }, 3638 3639 { 3640 name: "discovery type static, bad endpoint port name", in: &networking.ServiceEntry{ 3641 Hosts: []string{"google.com"}, 3642 Addresses: []string{"172.1.2.16"}, 3643 Ports: []*networking.ServicePort{ 3644 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3645 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3646 }, 3647 Endpoints: []*networking.WorkloadEntry{ 3648 {Address: "1.1.1.1", Ports: map[string]uint32{"http-valid1": 8080}}, 3649 {Address: "2.2.2.2", Ports: map[string]uint32{"http-dne": 9080}}, 3650 }, 3651 Resolution: networking.ServiceEntry_STATIC, 3652 }, 3653 valid: false, 3654 }, 3655 3656 { 3657 name: "discovery type none, conflicting port names", in: &networking.ServiceEntry{ 3658 Hosts: []string{"google.com"}, 3659 Ports: []*networking.ServicePort{ 3660 {Number: 80, Protocol: "http", Name: "http-conflict"}, 3661 {Number: 8080, Protocol: "http", Name: "http-conflict"}, 3662 }, 3663 Resolution: networking.ServiceEntry_NONE, 3664 }, 3665 valid: false, 3666 }, 3667 3668 { 3669 name: "discovery type none, conflicting port numbers", in: &networking.ServiceEntry{ 3670 Hosts: []string{"google.com"}, 3671 Ports: []*networking.ServicePort{ 3672 {Number: 80, Protocol: "http", Name: "http-conflict1"}, 3673 {Number: 80, Protocol: "http", Name: "http-conflict2"}, 3674 }, 3675 Resolution: networking.ServiceEntry_NONE, 3676 }, 3677 valid: false, 3678 }, 3679 3680 { 3681 name: "unix socket", in: &networking.ServiceEntry{ 3682 Hosts: []string{"uds.cluster.local"}, 3683 Ports: []*networking.ServicePort{ 3684 {Number: 6553, Protocol: "grpc", Name: "grpc-service1"}, 3685 }, 3686 Resolution: networking.ServiceEntry_STATIC, 3687 Endpoints: []*networking.WorkloadEntry{ 3688 {Address: "unix:///path/to/socket"}, 3689 }, 3690 }, 3691 valid: true, 3692 }, 3693 3694 { 3695 name: "unix socket, relative path", in: &networking.ServiceEntry{ 3696 Hosts: []string{"uds.cluster.local"}, 3697 Ports: []*networking.ServicePort{ 3698 {Number: 6553, Protocol: "grpc", Name: "grpc-service1"}, 3699 }, 3700 Resolution: networking.ServiceEntry_STATIC, 3701 Endpoints: []*networking.WorkloadEntry{ 3702 {Address: "unix://./relative/path.sock"}, 3703 }, 3704 }, 3705 valid: false, 3706 }, 3707 3708 { 3709 name: "unix socket, endpoint ports", in: &networking.ServiceEntry{ 3710 Hosts: []string{"uds.cluster.local"}, 3711 Ports: []*networking.ServicePort{ 3712 {Number: 6553, Protocol: "grpc", Name: "grpc-service1"}, 3713 }, 3714 Resolution: networking.ServiceEntry_STATIC, 3715 Endpoints: []*networking.WorkloadEntry{ 3716 {Address: "unix:///path/to/socket", Ports: map[string]uint32{"grpc-service1": 6553}}, 3717 }, 3718 }, 3719 valid: false, 3720 }, 3721 { 3722 name: "unix socket, multiple service ports", in: &networking.ServiceEntry{ 3723 Hosts: []string{"uds.cluster.local"}, 3724 Ports: []*networking.ServicePort{ 3725 {Number: 6553, Protocol: "grpc", Name: "grpc-service1"}, 3726 {Number: 80, Protocol: "http", Name: "http-service2"}, 3727 }, 3728 Resolution: networking.ServiceEntry_STATIC, 3729 Endpoints: []*networking.WorkloadEntry{ 3730 {Address: "unix:///path/to/socket"}, 3731 }, 3732 }, 3733 valid: false, 3734 }, 3735 { 3736 name: "empty protocol", in: &networking.ServiceEntry{ 3737 Hosts: []string{"google.com"}, 3738 Addresses: []string{"172.1.2.16/16"}, 3739 Ports: []*networking.ServicePort{ 3740 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3741 {Number: 8080, Name: "http-valid2"}, 3742 }, 3743 Resolution: networking.ServiceEntry_NONE, 3744 }, 3745 valid: true, 3746 }, 3747 { 3748 name: "selector", in: &networking.ServiceEntry{ 3749 Hosts: []string{"google.com"}, 3750 WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"foo": "bar"}}, 3751 Ports: []*networking.ServicePort{ 3752 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3753 }, 3754 }, 3755 valid: true, 3756 warning: true, 3757 }, 3758 { 3759 name: "workload selector without labels", 3760 in: &networking.ServiceEntry{ 3761 Hosts: []string{"google.com"}, 3762 Ports: []*networking.ServicePort{ 3763 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3764 {Number: 8080, Protocol: "http", Name: "http-valid2"}, 3765 }, 3766 WorkloadSelector: &networking.WorkloadSelector{}, 3767 }, 3768 valid: true, 3769 warning: true, 3770 }, 3771 { 3772 name: "selector and endpoints", in: &networking.ServiceEntry{ 3773 Hosts: []string{"google.com"}, 3774 WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"foo": "bar"}}, 3775 Ports: []*networking.ServicePort{ 3776 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3777 }, 3778 Endpoints: []*networking.WorkloadEntry{ 3779 {Address: "1.1.1.1"}, 3780 }, 3781 }, 3782 valid: false, 3783 warning: true, 3784 }, 3785 { 3786 name: "selector and resolution NONE", in: &networking.ServiceEntry{ 3787 Hosts: []string{"google.com"}, 3788 WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"foo": "bar"}}, 3789 Ports: []*networking.ServicePort{ 3790 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3791 }, 3792 Resolution: networking.ServiceEntry_NONE, 3793 }, 3794 valid: true, 3795 warning: true, 3796 }, 3797 { 3798 name: "selector and resolution DNS", in: &networking.ServiceEntry{ 3799 Hosts: []string{"google.com"}, 3800 WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"foo": "bar"}}, 3801 Ports: []*networking.ServicePort{ 3802 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3803 }, 3804 Resolution: networking.ServiceEntry_DNS, 3805 }, 3806 valid: true, 3807 warning: true, 3808 }, 3809 { 3810 name: "bad selector key", in: &networking.ServiceEntry{ 3811 Hosts: []string{"google.com"}, 3812 WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"": "bar"}}, 3813 Ports: []*networking.ServicePort{ 3814 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3815 }, 3816 }, 3817 valid: false, 3818 }, 3819 { 3820 name: "repeat target port", in: &networking.ServiceEntry{ 3821 Hosts: []string{"google.com"}, 3822 Resolution: networking.ServiceEntry_DNS, 3823 Ports: []*networking.ServicePort{ 3824 {Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 80}, 3825 {Number: 81, Protocol: "http", Name: "http-valid2", TargetPort: 80}, 3826 }, 3827 }, 3828 valid: true, 3829 }, 3830 { 3831 name: "valid target port", in: &networking.ServiceEntry{ 3832 Hosts: []string{"google.com"}, 3833 Resolution: networking.ServiceEntry_DNS, 3834 Ports: []*networking.ServicePort{ 3835 {Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 81}, 3836 }, 3837 }, 3838 valid: true, 3839 }, 3840 { 3841 name: "invalid target port", in: &networking.ServiceEntry{ 3842 Hosts: []string{"google.com"}, 3843 Resolution: networking.ServiceEntry_DNS, 3844 Ports: []*networking.ServicePort{ 3845 {Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 65536}, 3846 }, 3847 }, 3848 valid: false, 3849 }, 3850 { 3851 name: "warn target port", in: &networking.ServiceEntry{ 3852 Hosts: []string{"google.com"}, 3853 WorkloadSelector: &networking.WorkloadSelector{Labels: map[string]string{"key": "bar"}}, 3854 Ports: []*networking.ServicePort{ 3855 {Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 1234}, 3856 }, 3857 }, 3858 valid: true, 3859 warning: true, 3860 }, 3861 { 3862 name: "valid endpoint port", in: &networking.ServiceEntry{ 3863 Hosts: []string{"google.com"}, 3864 Ports: []*networking.ServicePort{ 3865 {Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 81}, 3866 }, 3867 Resolution: networking.ServiceEntry_STATIC, 3868 Endpoints: []*networking.WorkloadEntry{ 3869 { 3870 Address: "1.1.1.1", 3871 Ports: map[string]uint32{ 3872 "http-valid1": 8081, 3873 }, 3874 }, 3875 }, 3876 }, 3877 valid: true, 3878 }, 3879 { 3880 name: "invalid endpoint port", in: &networking.ServiceEntry{ 3881 Hosts: []string{"google.com"}, 3882 Ports: []*networking.ServicePort{ 3883 {Number: 80, Protocol: "http", Name: "http-valid1", TargetPort: 81}, 3884 }, 3885 Resolution: networking.ServiceEntry_STATIC, 3886 Endpoints: []*networking.WorkloadEntry{ 3887 { 3888 Address: "1.1.1.1", 3889 Ports: map[string]uint32{ 3890 "http-valid1": 65536, 3891 }, 3892 }, 3893 }, 3894 }, 3895 valid: false, 3896 }, 3897 { 3898 name: "protocol unset for addresses empty", in: &networking.ServiceEntry{ 3899 Hosts: []string{"google.com"}, 3900 Addresses: []string{}, 3901 Ports: []*networking.ServicePort{ 3902 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3903 {Number: 8080, Name: "http-valid2"}, 3904 }, 3905 Resolution: networking.ServiceEntry_NONE, 3906 }, 3907 valid: true, 3908 warning: true, 3909 }, 3910 { 3911 name: "protocol is TCP for addresses empty", in: &networking.ServiceEntry{ 3912 Hosts: []string{"google.com"}, 3913 Addresses: []string{}, 3914 Ports: []*networking.ServicePort{ 3915 {Number: 80, Protocol: "http", Name: "http-valid1"}, 3916 {Number: 81, Protocol: "TCP", Name: "tcp-valid1"}, 3917 }, 3918 Resolution: networking.ServiceEntry_NONE, 3919 }, 3920 valid: true, 3921 warning: true, 3922 }, 3923 { 3924 name: "dns round robin with more than one endpoint", in: &networking.ServiceEntry{ 3925 Hosts: []string{"google.com"}, 3926 Addresses: []string{}, 3927 Ports: []*networking.ServicePort{ 3928 {Number: 8081, Protocol: "http", Name: "http-valid1"}, 3929 }, 3930 Endpoints: []*networking.WorkloadEntry{ 3931 { 3932 Address: "api-v1.istio.io", 3933 Ports: map[string]uint32{ 3934 "http-valid1": 8081, 3935 }, 3936 }, 3937 { 3938 Address: "1.1.1.2", 3939 Ports: map[string]uint32{ 3940 "http-valid1": 8081, 3941 }, 3942 }, 3943 }, 3944 Resolution: networking.ServiceEntry_DNS_ROUND_ROBIN, 3945 }, 3946 valid: false, 3947 warning: false, 3948 }, 3949 { 3950 name: "dns round robin with 0 endpoints", in: &networking.ServiceEntry{ 3951 Hosts: []string{"google.com"}, 3952 Addresses: []string{}, 3953 Ports: []*networking.ServicePort{ 3954 {Number: 8081, Protocol: "http", Name: "http-valid1"}, 3955 }, 3956 Endpoints: []*networking.WorkloadEntry{}, 3957 Resolution: networking.ServiceEntry_DNS_ROUND_ROBIN, 3958 }, 3959 valid: true, 3960 warning: false, 3961 }, 3962 { 3963 name: "partial wildcard hosts", in: &networking.ServiceEntry{ 3964 Hosts: []string{"*.nytimes.com", "*washingtonpost.com"}, 3965 Addresses: []string{}, 3966 Ports: []*networking.ServicePort{ 3967 {Number: 443, Protocol: "HTTPS", Name: "https-nytimes"}, 3968 }, 3969 Endpoints: []*networking.WorkloadEntry{}, 3970 Resolution: networking.ServiceEntry_NONE, 3971 }, 3972 valid: true, 3973 warning: true, 3974 }, 3975 { 3976 name: "network set with no address on endpoint", in: &networking.ServiceEntry{ 3977 Hosts: []string{"www.example.com"}, 3978 Addresses: []string{}, 3979 Ports: []*networking.ServicePort{ 3980 {Number: 443, Protocol: "HTTPS", Name: "https-example"}, 3981 }, 3982 Endpoints: []*networking.WorkloadEntry{ 3983 {Network: "cluster-1"}, 3984 }, 3985 Resolution: networking.ServiceEntry_STATIC, 3986 }, 3987 valid: true, 3988 warning: true, 3989 }, 3990 } 3991 3992 for _, c := range cases { 3993 t.Run(c.name, func(t *testing.T) { 3994 warning, err := ValidateServiceEntry(config.Config{ 3995 Meta: config.Meta{ 3996 Name: someName, 3997 Namespace: someNamespace, 3998 }, 3999 Spec: c.in, 4000 }) 4001 if (err == nil) != c.valid { 4002 t.Errorf("ValidateServiceEntry got valid=%v but wanted valid=%v: %v", 4003 err == nil, c.valid, err) 4004 } 4005 if (warning != nil) != c.warning { 4006 t.Errorf("ValidateServiceEntry got warning=%v but wanted warning=%v: %v", 4007 warning != nil, c.warning, warning) 4008 } 4009 }) 4010 } 4011 } 4012 4013 func TestValidateAuthorizationPolicy(t *testing.T) { 4014 cases := []struct { 4015 name string 4016 annotations map[string]string 4017 in proto.Message 4018 valid bool 4019 Warning bool 4020 }{ 4021 { 4022 name: "good", 4023 in: &security_beta.AuthorizationPolicy{ 4024 Selector: &api.WorkloadSelector{ 4025 MatchLabels: map[string]string{ 4026 "app": "httpbin", 4027 "version": "v1", 4028 }, 4029 }, 4030 Rules: []*security_beta.Rule{ 4031 { 4032 From: []*security_beta.Rule_From{ 4033 { 4034 Source: &security_beta.Source{ 4035 Principals: []string{"sa1"}, 4036 }, 4037 }, 4038 { 4039 Source: &security_beta.Source{ 4040 Principals: []string{"sa2"}, 4041 }, 4042 }, 4043 }, 4044 To: []*security_beta.Rule_To{ 4045 { 4046 Operation: &security_beta.Operation{ 4047 Methods: []string{"GET"}, 4048 }, 4049 }, 4050 { 4051 Operation: &security_beta.Operation{ 4052 Methods: []string{"POST"}, 4053 }, 4054 }, 4055 }, 4056 When: []*security_beta.Condition{ 4057 { 4058 Key: "source.ip", 4059 Values: []string{"1.2.3.4", "5.6.7.0/24"}, 4060 }, 4061 { 4062 Key: "request.headers[:authority]", 4063 Values: []string{"v1", "v2"}, 4064 }, 4065 }, 4066 }, 4067 }, 4068 }, 4069 valid: true, 4070 }, 4071 { 4072 name: "custom-good", 4073 in: &security_beta.AuthorizationPolicy{ 4074 Action: security_beta.AuthorizationPolicy_CUSTOM, 4075 ActionDetail: &security_beta.AuthorizationPolicy_Provider{ 4076 Provider: &security_beta.AuthorizationPolicy_ExtensionProvider{ 4077 Name: "my-custom-authz", 4078 }, 4079 }, 4080 Rules: []*security_beta.Rule{{}}, 4081 }, 4082 valid: true, 4083 }, 4084 { 4085 name: "custom-empty-provider", 4086 in: &security_beta.AuthorizationPolicy{ 4087 Action: security_beta.AuthorizationPolicy_CUSTOM, 4088 ActionDetail: &security_beta.AuthorizationPolicy_Provider{ 4089 Provider: &security_beta.AuthorizationPolicy_ExtensionProvider{ 4090 Name: "", 4091 }, 4092 }, 4093 Rules: []*security_beta.Rule{{}}, 4094 }, 4095 valid: false, 4096 }, 4097 { 4098 name: "custom-nil-provider", 4099 in: &security_beta.AuthorizationPolicy{ 4100 Action: security_beta.AuthorizationPolicy_CUSTOM, 4101 Rules: []*security_beta.Rule{{}}, 4102 }, 4103 valid: false, 4104 }, 4105 { 4106 name: "custom-invalid-rule", 4107 in: &security_beta.AuthorizationPolicy{ 4108 Action: security_beta.AuthorizationPolicy_CUSTOM, 4109 ActionDetail: &security_beta.AuthorizationPolicy_Provider{ 4110 Provider: &security_beta.AuthorizationPolicy_ExtensionProvider{ 4111 Name: "my-custom-authz", 4112 }, 4113 }, 4114 Rules: []*security_beta.Rule{ 4115 { 4116 From: []*security_beta.Rule_From{ 4117 { 4118 Source: &security_beta.Source{ 4119 Namespaces: []string{"ns"}, 4120 NotNamespaces: []string{"ns"}, 4121 Principals: []string{"id"}, 4122 NotPrincipals: []string{"id"}, 4123 RequestPrincipals: []string{"req"}, 4124 NotRequestPrincipals: []string{"req"}, 4125 }, 4126 }, 4127 }, 4128 When: []*security_beta.Condition{ 4129 { 4130 Key: "source.namespace", 4131 Values: []string{"source.namespace1"}, 4132 NotValues: []string{"source.namespace2"}, 4133 }, 4134 { 4135 Key: "source.principal", 4136 Values: []string{"source.principal1"}, 4137 NotValues: []string{"source.principal2"}, 4138 }, 4139 { 4140 Key: "request.auth.claims[a]", 4141 Values: []string{"claims1"}, 4142 NotValues: []string{"claims2"}, 4143 }, 4144 }, 4145 }, 4146 }, 4147 }, 4148 valid: false, 4149 }, 4150 { 4151 name: "provider-wrong-action", 4152 in: &security_beta.AuthorizationPolicy{ 4153 Action: security_beta.AuthorizationPolicy_ALLOW, 4154 ActionDetail: &security_beta.AuthorizationPolicy_Provider{ 4155 Provider: &security_beta.AuthorizationPolicy_ExtensionProvider{ 4156 Name: "", 4157 }, 4158 }, 4159 Rules: []*security_beta.Rule{{}}, 4160 }, 4161 valid: false, 4162 }, 4163 { 4164 name: "allow-rules-nil", 4165 in: &security_beta.AuthorizationPolicy{ 4166 Action: security_beta.AuthorizationPolicy_ALLOW, 4167 }, 4168 valid: true, 4169 }, 4170 { 4171 name: "dry-run-valid-allow", 4172 annotations: map[string]string{"istio.io/dry-run": "true"}, 4173 in: &security_beta.AuthorizationPolicy{ 4174 Action: security_beta.AuthorizationPolicy_ALLOW, 4175 }, 4176 valid: true, 4177 }, 4178 { 4179 name: "dry-run-valid-deny", 4180 annotations: map[string]string{"istio.io/dry-run": "false"}, 4181 in: &security_beta.AuthorizationPolicy{ 4182 Action: security_beta.AuthorizationPolicy_DENY, 4183 Rules: []*security_beta.Rule{{}}, 4184 }, 4185 valid: true, 4186 }, 4187 { 4188 name: "dry-run-invalid-value", 4189 annotations: map[string]string{"istio.io/dry-run": "foo"}, 4190 in: &security_beta.AuthorizationPolicy{ 4191 Action: security_beta.AuthorizationPolicy_ALLOW, 4192 }, 4193 valid: false, 4194 }, 4195 { 4196 name: "dry-run-invalid-action-custom", 4197 annotations: map[string]string{"istio.io/dry-run": "true"}, 4198 in: &security_beta.AuthorizationPolicy{ 4199 Action: security_beta.AuthorizationPolicy_CUSTOM, 4200 }, 4201 valid: false, 4202 }, 4203 { 4204 name: "dry-run-invalid-action-audit", 4205 annotations: map[string]string{"istio.io/dry-run": "true"}, 4206 in: &security_beta.AuthorizationPolicy{ 4207 Action: security_beta.AuthorizationPolicy_AUDIT, 4208 }, 4209 valid: false, 4210 }, 4211 { 4212 name: "deny-rules-nil", 4213 in: &security_beta.AuthorizationPolicy{ 4214 Action: security_beta.AuthorizationPolicy_DENY, 4215 }, 4216 valid: false, 4217 }, 4218 { 4219 name: "selector-empty-value", 4220 in: &security_beta.AuthorizationPolicy{ 4221 Selector: &api.WorkloadSelector{ 4222 MatchLabels: map[string]string{ 4223 "app": "", 4224 "version": "v1", 4225 }, 4226 }, 4227 }, 4228 valid: true, 4229 }, 4230 { 4231 name: "selector-empty-key", 4232 in: &security_beta.AuthorizationPolicy{ 4233 Selector: &api.WorkloadSelector{ 4234 MatchLabels: map[string]string{ 4235 "app": "httpbin", 4236 "": "v1", 4237 }, 4238 }, 4239 }, 4240 valid: false, 4241 }, 4242 { 4243 name: "selector-empty-labels", 4244 in: &security_beta.AuthorizationPolicy{ 4245 Selector: &api.WorkloadSelector{}, 4246 }, 4247 valid: true, 4248 Warning: true, 4249 }, 4250 { 4251 name: "selector-wildcard-value", 4252 in: &security_beta.AuthorizationPolicy{ 4253 Selector: &api.WorkloadSelector{ 4254 MatchLabels: map[string]string{ 4255 "app": "httpbin-*", 4256 }, 4257 }, 4258 }, 4259 valid: false, 4260 }, 4261 { 4262 name: "selector-wildcard-key", 4263 in: &security_beta.AuthorizationPolicy{ 4264 Selector: &api.WorkloadSelector{ 4265 MatchLabels: map[string]string{ 4266 "app-*": "httpbin", 4267 }, 4268 }, 4269 }, 4270 valid: false, 4271 }, 4272 { 4273 name: "target-ref-good", 4274 in: &security_beta.AuthorizationPolicy{ 4275 Action: security_beta.AuthorizationPolicy_DENY, 4276 TargetRef: &api.PolicyTargetReference{ 4277 Group: gvk.KubernetesGateway.Group, 4278 Kind: gvk.KubernetesGateway.Kind, 4279 Name: "foo", 4280 }, 4281 Rules: []*security_beta.Rule{ 4282 { 4283 From: []*security_beta.Rule_From{ 4284 { 4285 Source: &security_beta.Source{ 4286 Principals: []string{"temp"}, 4287 }, 4288 }, 4289 }, 4290 To: []*security_beta.Rule_To{ 4291 { 4292 Operation: &security_beta.Operation{ 4293 Ports: []string{"8080"}, 4294 Methods: []string{"GET", "DELETE"}, 4295 }, 4296 }, 4297 }, 4298 }, 4299 }, 4300 }, 4301 valid: true, 4302 Warning: false, 4303 }, 4304 { 4305 name: "target-refs-good", 4306 in: &security_beta.AuthorizationPolicy{ 4307 Action: security_beta.AuthorizationPolicy_DENY, 4308 TargetRefs: []*api.PolicyTargetReference{{ 4309 Group: gvk.KubernetesGateway.Group, 4310 Kind: gvk.KubernetesGateway.Kind, 4311 Name: "foo", 4312 }}, 4313 Rules: []*security_beta.Rule{ 4314 { 4315 From: []*security_beta.Rule_From{ 4316 { 4317 Source: &security_beta.Source{ 4318 Principals: []string{"temp"}, 4319 }, 4320 }, 4321 }, 4322 To: []*security_beta.Rule_To{ 4323 { 4324 Operation: &security_beta.Operation{ 4325 Ports: []string{"8080"}, 4326 Methods: []string{"GET", "DELETE"}, 4327 }, 4328 }, 4329 }, 4330 }, 4331 }, 4332 }, 4333 valid: true, 4334 Warning: false, 4335 }, 4336 { 4337 name: "target-refs-good-service", 4338 in: &security_beta.AuthorizationPolicy{ 4339 Action: security_beta.AuthorizationPolicy_DENY, 4340 TargetRefs: []*api.PolicyTargetReference{{ 4341 Group: gvk.Service.Group, 4342 Kind: gvk.Service.Kind, 4343 Name: "foo", 4344 }}, 4345 Rules: []*security_beta.Rule{ 4346 { 4347 From: []*security_beta.Rule_From{ 4348 { 4349 Source: &security_beta.Source{ 4350 Principals: []string{"temp"}, 4351 }, 4352 }, 4353 }, 4354 To: []*security_beta.Rule_To{ 4355 { 4356 Operation: &security_beta.Operation{ 4357 Ports: []string{"8080"}, 4358 Methods: []string{"GET", "DELETE"}, 4359 }, 4360 }, 4361 }, 4362 }, 4363 }, 4364 }, 4365 valid: true, 4366 Warning: false, 4367 }, 4368 { 4369 name: "target-ref-non-empty-namespace", 4370 in: &security_beta.AuthorizationPolicy{ 4371 Action: security_beta.AuthorizationPolicy_DENY, 4372 TargetRef: &api.PolicyTargetReference{ 4373 Group: gvk.KubernetesGateway.Group, 4374 Kind: gvk.KubernetesGateway.Kind, 4375 Name: "foo", 4376 Namespace: "bar", 4377 }, 4378 Rules: []*security_beta.Rule{ 4379 { 4380 From: []*security_beta.Rule_From{ 4381 { 4382 Source: &security_beta.Source{ 4383 Principals: []string{"temp"}, 4384 }, 4385 }, 4386 }, 4387 To: []*security_beta.Rule_To{ 4388 { 4389 Operation: &security_beta.Operation{ 4390 Ports: []string{"8080"}, 4391 Methods: []string{"GET", "DELETE"}, 4392 }, 4393 }, 4394 }, 4395 }, 4396 }, 4397 }, 4398 valid: false, 4399 Warning: false, 4400 }, 4401 { 4402 name: "target-ref-empty-name", 4403 in: &security_beta.AuthorizationPolicy{ 4404 Action: security_beta.AuthorizationPolicy_DENY, 4405 TargetRef: &api.PolicyTargetReference{ 4406 Group: gvk.KubernetesGateway.Group, 4407 Kind: gvk.KubernetesGateway.Kind, 4408 }, 4409 Rules: []*security_beta.Rule{ 4410 { 4411 From: []*security_beta.Rule_From{ 4412 { 4413 Source: &security_beta.Source{ 4414 Principals: []string{"temp"}, 4415 }, 4416 }, 4417 }, 4418 To: []*security_beta.Rule_To{ 4419 { 4420 Operation: &security_beta.Operation{ 4421 Ports: []string{"8080"}, 4422 Methods: []string{"GET", "DELETE"}, 4423 }, 4424 }, 4425 }, 4426 }, 4427 }, 4428 }, 4429 valid: false, 4430 Warning: false, 4431 }, 4432 { 4433 name: "target-ref-wrong-group", 4434 in: &security_beta.AuthorizationPolicy{ 4435 Action: security_beta.AuthorizationPolicy_DENY, 4436 TargetRef: &api.PolicyTargetReference{ 4437 Group: "wrong-group", 4438 Kind: gvk.KubernetesGateway.Kind, 4439 Name: "foo", 4440 }, 4441 Rules: []*security_beta.Rule{ 4442 { 4443 From: []*security_beta.Rule_From{ 4444 { 4445 Source: &security_beta.Source{ 4446 Principals: []string{"temp"}, 4447 }, 4448 }, 4449 }, 4450 To: []*security_beta.Rule_To{ 4451 { 4452 Operation: &security_beta.Operation{ 4453 Ports: []string{"8080"}, 4454 Methods: []string{"GET", "DELETE"}, 4455 }, 4456 }, 4457 }, 4458 }, 4459 }, 4460 }, 4461 valid: false, 4462 Warning: false, 4463 }, 4464 { 4465 name: "target-ref-wrong-kind", 4466 in: &security_beta.AuthorizationPolicy{ 4467 Action: security_beta.AuthorizationPolicy_DENY, 4468 TargetRef: &api.PolicyTargetReference{ 4469 Group: gvk.KubernetesGateway.Group, 4470 Kind: "wrong-kind", 4471 Name: "foo", 4472 }, 4473 Rules: []*security_beta.Rule{ 4474 { 4475 From: []*security_beta.Rule_From{ 4476 { 4477 Source: &security_beta.Source{ 4478 Principals: []string{"temp"}, 4479 }, 4480 }, 4481 }, 4482 To: []*security_beta.Rule_To{ 4483 { 4484 Operation: &security_beta.Operation{ 4485 Ports: []string{"8080"}, 4486 Methods: []string{"GET", "DELETE"}, 4487 }, 4488 }, 4489 }, 4490 }, 4491 }, 4492 }, 4493 valid: false, 4494 Warning: false, 4495 }, 4496 { 4497 name: "target-ref-and-selector-cannot-both-be-set", 4498 in: &security_beta.AuthorizationPolicy{ 4499 Action: security_beta.AuthorizationPolicy_DENY, 4500 TargetRef: &api.PolicyTargetReference{ 4501 Group: gvk.KubernetesGateway.Group, 4502 Kind: gvk.KubernetesGateway.Kind, 4503 Name: "foo", 4504 }, 4505 Selector: &api.WorkloadSelector{ 4506 MatchLabels: map[string]string{ 4507 "app": "httpbin", 4508 }, 4509 }, 4510 Rules: []*security_beta.Rule{ 4511 { 4512 From: []*security_beta.Rule_From{ 4513 { 4514 Source: &security_beta.Source{ 4515 Principals: []string{"temp"}, 4516 }, 4517 }, 4518 }, 4519 To: []*security_beta.Rule_To{ 4520 { 4521 Operation: &security_beta.Operation{ 4522 Ports: []string{"8080"}, 4523 Methods: []string{"GET", "DELETE"}, 4524 }, 4525 }, 4526 }, 4527 }, 4528 }, 4529 }, 4530 valid: false, 4531 Warning: false, 4532 }, 4533 { 4534 name: "target-refs-and-selector-cannot-both-be-set", 4535 in: &security_beta.AuthorizationPolicy{ 4536 Action: security_beta.AuthorizationPolicy_DENY, 4537 TargetRefs: []*api.PolicyTargetReference{{ 4538 Group: gvk.KubernetesGateway.Group, 4539 Kind: gvk.KubernetesGateway.Kind, 4540 Name: "foo", 4541 }}, 4542 Selector: &api.WorkloadSelector{ 4543 MatchLabels: map[string]string{ 4544 "app": "httpbin", 4545 }, 4546 }, 4547 Rules: []*security_beta.Rule{ 4548 { 4549 From: []*security_beta.Rule_From{ 4550 { 4551 Source: &security_beta.Source{ 4552 Principals: []string{"temp"}, 4553 }, 4554 }, 4555 }, 4556 To: []*security_beta.Rule_To{ 4557 { 4558 Operation: &security_beta.Operation{ 4559 Ports: []string{"8080"}, 4560 Methods: []string{"GET", "DELETE"}, 4561 }, 4562 }, 4563 }, 4564 }, 4565 }, 4566 }, 4567 valid: false, 4568 Warning: false, 4569 }, 4570 { 4571 name: "from-empty", 4572 in: &security_beta.AuthorizationPolicy{ 4573 Rules: []*security_beta.Rule{ 4574 { 4575 From: []*security_beta.Rule_From{}, 4576 }, 4577 }, 4578 }, 4579 valid: false, 4580 }, 4581 { 4582 name: "source-nil", 4583 in: &security_beta.AuthorizationPolicy{ 4584 Rules: []*security_beta.Rule{ 4585 { 4586 From: []*security_beta.Rule_From{ 4587 {}, 4588 }, 4589 }, 4590 }, 4591 }, 4592 valid: false, 4593 }, 4594 { 4595 name: "source-empty", 4596 in: &security_beta.AuthorizationPolicy{ 4597 Rules: []*security_beta.Rule{ 4598 { 4599 From: []*security_beta.Rule_From{ 4600 { 4601 Source: &security_beta.Source{}, 4602 }, 4603 }, 4604 }, 4605 }, 4606 }, 4607 valid: false, 4608 }, 4609 { 4610 name: "to-empty", 4611 in: &security_beta.AuthorizationPolicy{ 4612 Rules: []*security_beta.Rule{ 4613 { 4614 To: []*security_beta.Rule_To{}, 4615 }, 4616 }, 4617 }, 4618 valid: false, 4619 }, 4620 { 4621 name: "operation-nil", 4622 in: &security_beta.AuthorizationPolicy{ 4623 Rules: []*security_beta.Rule{ 4624 { 4625 To: []*security_beta.Rule_To{ 4626 {}, 4627 }, 4628 }, 4629 }, 4630 }, 4631 valid: false, 4632 }, 4633 { 4634 name: "operation-empty", 4635 in: &security_beta.AuthorizationPolicy{ 4636 Rules: []*security_beta.Rule{ 4637 { 4638 To: []*security_beta.Rule_To{ 4639 { 4640 Operation: &security_beta.Operation{}, 4641 }, 4642 }, 4643 }, 4644 }, 4645 }, 4646 valid: false, 4647 }, 4648 { 4649 name: "Principals-empty", 4650 in: &security_beta.AuthorizationPolicy{ 4651 Rules: []*security_beta.Rule{ 4652 { 4653 From: []*security_beta.Rule_From{ 4654 { 4655 Source: &security_beta.Source{ 4656 Principals: []string{"p1", ""}, 4657 }, 4658 }, 4659 }, 4660 }, 4661 }, 4662 }, 4663 valid: false, 4664 }, 4665 { 4666 name: "NotPrincipals-empty", 4667 in: &security_beta.AuthorizationPolicy{ 4668 Rules: []*security_beta.Rule{ 4669 { 4670 From: []*security_beta.Rule_From{ 4671 { 4672 Source: &security_beta.Source{ 4673 NotPrincipals: []string{"p1", ""}, 4674 }, 4675 }, 4676 }, 4677 }, 4678 }, 4679 }, 4680 valid: false, 4681 }, 4682 { 4683 name: "RequestPrincipals-empty", 4684 in: &security_beta.AuthorizationPolicy{ 4685 Rules: []*security_beta.Rule{ 4686 { 4687 From: []*security_beta.Rule_From{ 4688 { 4689 Source: &security_beta.Source{ 4690 RequestPrincipals: []string{"p1", ""}, 4691 }, 4692 }, 4693 }, 4694 }, 4695 }, 4696 }, 4697 valid: false, 4698 }, 4699 { 4700 name: "NotRequestPrincipals-empty", 4701 in: &security_beta.AuthorizationPolicy{ 4702 Rules: []*security_beta.Rule{ 4703 { 4704 From: []*security_beta.Rule_From{ 4705 { 4706 Source: &security_beta.Source{ 4707 NotRequestPrincipals: []string{"p1", ""}, 4708 }, 4709 }, 4710 }, 4711 }, 4712 }, 4713 }, 4714 valid: false, 4715 }, 4716 { 4717 name: "Namespaces-empty", 4718 in: &security_beta.AuthorizationPolicy{ 4719 Rules: []*security_beta.Rule{ 4720 { 4721 From: []*security_beta.Rule_From{ 4722 { 4723 Source: &security_beta.Source{ 4724 Namespaces: []string{"ns", ""}, 4725 }, 4726 }, 4727 }, 4728 }, 4729 }, 4730 }, 4731 valid: false, 4732 }, 4733 { 4734 name: "NotNamespaces-empty", 4735 in: &security_beta.AuthorizationPolicy{ 4736 Rules: []*security_beta.Rule{ 4737 { 4738 From: []*security_beta.Rule_From{ 4739 { 4740 Source: &security_beta.Source{ 4741 NotNamespaces: []string{"ns", ""}, 4742 }, 4743 }, 4744 }, 4745 }, 4746 }, 4747 }, 4748 valid: false, 4749 }, 4750 { 4751 name: "IpBlocks-empty", 4752 in: &security_beta.AuthorizationPolicy{ 4753 Rules: []*security_beta.Rule{ 4754 { 4755 From: []*security_beta.Rule_From{ 4756 { 4757 Source: &security_beta.Source{ 4758 IpBlocks: []string{"1.2.3.4", ""}, 4759 }, 4760 }, 4761 }, 4762 }, 4763 }, 4764 }, 4765 valid: false, 4766 }, 4767 { 4768 name: "NotIpBlocks-empty", 4769 in: &security_beta.AuthorizationPolicy{ 4770 Rules: []*security_beta.Rule{ 4771 { 4772 From: []*security_beta.Rule_From{ 4773 { 4774 Source: &security_beta.Source{ 4775 NotIpBlocks: []string{"1.2.3.4", ""}, 4776 }, 4777 }, 4778 }, 4779 }, 4780 }, 4781 }, 4782 valid: false, 4783 }, 4784 { 4785 name: "RemoteIpBlocks-empty", 4786 in: &security_beta.AuthorizationPolicy{ 4787 Rules: []*security_beta.Rule{ 4788 { 4789 From: []*security_beta.Rule_From{ 4790 { 4791 Source: &security_beta.Source{ 4792 RemoteIpBlocks: []string{"1.2.3.4", ""}, 4793 }, 4794 }, 4795 }, 4796 }, 4797 }, 4798 }, 4799 valid: false, 4800 }, 4801 { 4802 name: "NotRemoteIpBlocks-empty", 4803 in: &security_beta.AuthorizationPolicy{ 4804 Rules: []*security_beta.Rule{ 4805 { 4806 From: []*security_beta.Rule_From{ 4807 { 4808 Source: &security_beta.Source{ 4809 NotRemoteIpBlocks: []string{"1.2.3.4", ""}, 4810 }, 4811 }, 4812 }, 4813 }, 4814 }, 4815 }, 4816 valid: false, 4817 }, 4818 { 4819 name: "Hosts-empty", 4820 in: &security_beta.AuthorizationPolicy{ 4821 Rules: []*security_beta.Rule{ 4822 { 4823 To: []*security_beta.Rule_To{ 4824 { 4825 Operation: &security_beta.Operation{ 4826 Hosts: []string{"host", ""}, 4827 }, 4828 }, 4829 }, 4830 }, 4831 }, 4832 }, 4833 valid: false, 4834 }, 4835 { 4836 name: "NotHosts-empty", 4837 in: &security_beta.AuthorizationPolicy{ 4838 Rules: []*security_beta.Rule{ 4839 { 4840 To: []*security_beta.Rule_To{ 4841 { 4842 Operation: &security_beta.Operation{ 4843 NotHosts: []string{"host", ""}, 4844 }, 4845 }, 4846 }, 4847 }, 4848 }, 4849 }, 4850 valid: false, 4851 }, 4852 { 4853 name: "Ports-empty", 4854 in: &security_beta.AuthorizationPolicy{ 4855 Rules: []*security_beta.Rule{ 4856 { 4857 To: []*security_beta.Rule_To{ 4858 { 4859 Operation: &security_beta.Operation{ 4860 Ports: []string{"80", ""}, 4861 }, 4862 }, 4863 }, 4864 }, 4865 }, 4866 }, 4867 valid: false, 4868 }, 4869 { 4870 name: "NotPorts-empty", 4871 in: &security_beta.AuthorizationPolicy{ 4872 Rules: []*security_beta.Rule{ 4873 { 4874 To: []*security_beta.Rule_To{ 4875 { 4876 Operation: &security_beta.Operation{ 4877 NotPorts: []string{"80", ""}, 4878 }, 4879 }, 4880 }, 4881 }, 4882 }, 4883 }, 4884 valid: false, 4885 }, 4886 { 4887 name: "Methods-empty", 4888 in: &security_beta.AuthorizationPolicy{ 4889 Rules: []*security_beta.Rule{ 4890 { 4891 To: []*security_beta.Rule_To{ 4892 { 4893 Operation: &security_beta.Operation{ 4894 Methods: []string{"GET", ""}, 4895 }, 4896 }, 4897 }, 4898 }, 4899 }, 4900 }, 4901 valid: false, 4902 }, 4903 { 4904 name: "NotMethods-empty", 4905 in: &security_beta.AuthorizationPolicy{ 4906 Rules: []*security_beta.Rule{ 4907 { 4908 To: []*security_beta.Rule_To{ 4909 { 4910 Operation: &security_beta.Operation{ 4911 NotMethods: []string{"GET", ""}, 4912 }, 4913 }, 4914 }, 4915 }, 4916 }, 4917 }, 4918 valid: false, 4919 }, 4920 { 4921 name: "Paths-empty", 4922 in: &security_beta.AuthorizationPolicy{ 4923 Rules: []*security_beta.Rule{ 4924 { 4925 To: []*security_beta.Rule_To{ 4926 { 4927 Operation: &security_beta.Operation{ 4928 Paths: []string{"/path", ""}, 4929 }, 4930 }, 4931 }, 4932 }, 4933 }, 4934 }, 4935 valid: false, 4936 }, 4937 { 4938 name: "NotPaths-empty", 4939 in: &security_beta.AuthorizationPolicy{ 4940 Rules: []*security_beta.Rule{ 4941 { 4942 To: []*security_beta.Rule_To{ 4943 { 4944 Operation: &security_beta.Operation{ 4945 NotPaths: []string{"/path", ""}, 4946 }, 4947 }, 4948 }, 4949 }, 4950 }, 4951 }, 4952 valid: false, 4953 }, 4954 { 4955 name: "value-empty", 4956 in: &security_beta.AuthorizationPolicy{ 4957 Rules: []*security_beta.Rule{ 4958 { 4959 When: []*security_beta.Condition{ 4960 { 4961 Key: "request.headers[:authority]", 4962 Values: []string{"v1", ""}, 4963 }, 4964 }, 4965 }, 4966 }, 4967 }, 4968 valid: false, 4969 }, 4970 { 4971 name: "invalid ip and port in ipBlocks", 4972 in: &security_beta.AuthorizationPolicy{ 4973 Rules: []*security_beta.Rule{ 4974 { 4975 From: []*security_beta.Rule_From{ 4976 { 4977 Source: &security_beta.Source{ 4978 IpBlocks: []string{"1.2.3.4", "ip1"}, 4979 NotIpBlocks: []string{"5.6.7.8", "ip2"}, 4980 }, 4981 }, 4982 }, 4983 To: []*security_beta.Rule_To{ 4984 { 4985 Operation: &security_beta.Operation{ 4986 Ports: []string{"80", "port1"}, 4987 NotPorts: []string{"90", "port2"}, 4988 }, 4989 }, 4990 }, 4991 }, 4992 }, 4993 }, 4994 valid: false, 4995 }, 4996 { 4997 name: "invalid ip and port in remoteIpBlocks", 4998 in: &security_beta.AuthorizationPolicy{ 4999 Rules: []*security_beta.Rule{ 5000 { 5001 From: []*security_beta.Rule_From{ 5002 { 5003 Source: &security_beta.Source{ 5004 RemoteIpBlocks: []string{"1.2.3.4", "ip1"}, 5005 NotRemoteIpBlocks: []string{"5.6.7.8", "ip2"}, 5006 }, 5007 }, 5008 }, 5009 To: []*security_beta.Rule_To{ 5010 { 5011 Operation: &security_beta.Operation{ 5012 Ports: []string{"80", "port1"}, 5013 NotPorts: []string{"90", "port2"}, 5014 }, 5015 }, 5016 }, 5017 }, 5018 }, 5019 }, 5020 valid: false, 5021 }, 5022 { 5023 name: "condition-key-missing", 5024 in: &security_beta.AuthorizationPolicy{ 5025 Selector: &api.WorkloadSelector{ 5026 MatchLabels: map[string]string{ 5027 "app": "httpbin", 5028 }, 5029 }, 5030 Rules: []*security_beta.Rule{ 5031 { 5032 When: []*security_beta.Condition{ 5033 { 5034 Values: []string{"v1", "v2"}, 5035 }, 5036 }, 5037 }, 5038 }, 5039 }, 5040 valid: false, 5041 }, 5042 { 5043 name: "condition-key-empty", 5044 in: &security_beta.AuthorizationPolicy{ 5045 Selector: &api.WorkloadSelector{ 5046 MatchLabels: map[string]string{ 5047 "app": "httpbin", 5048 }, 5049 }, 5050 Rules: []*security_beta.Rule{ 5051 { 5052 When: []*security_beta.Condition{ 5053 { 5054 Key: "", 5055 Values: []string{"v1", "v2"}, 5056 }, 5057 }, 5058 }, 5059 }, 5060 }, 5061 valid: false, 5062 }, 5063 { 5064 name: "condition-value-missing", 5065 in: &security_beta.AuthorizationPolicy{ 5066 Selector: &api.WorkloadSelector{ 5067 MatchLabels: map[string]string{ 5068 "app": "httpbin", 5069 }, 5070 }, 5071 Rules: []*security_beta.Rule{ 5072 { 5073 When: []*security_beta.Condition{ 5074 { 5075 Key: "source.principal", 5076 }, 5077 }, 5078 }, 5079 }, 5080 }, 5081 valid: false, 5082 }, 5083 { 5084 name: "condition-value-invalid", 5085 in: &security_beta.AuthorizationPolicy{ 5086 Rules: []*security_beta.Rule{ 5087 { 5088 When: []*security_beta.Condition{ 5089 { 5090 Key: "source.ip", 5091 Values: []string{"a.b.c.d"}, 5092 }, 5093 }, 5094 }, 5095 }, 5096 }, 5097 valid: false, 5098 }, 5099 { 5100 name: "condition-notValue-invalid", 5101 in: &security_beta.AuthorizationPolicy{ 5102 Rules: []*security_beta.Rule{ 5103 { 5104 When: []*security_beta.Condition{ 5105 { 5106 Key: "source.ip", 5107 NotValues: []string{"a.b.c.d"}, 5108 }, 5109 }, 5110 }, 5111 }, 5112 }, 5113 valid: false, 5114 }, 5115 { 5116 name: "condition-unknown", 5117 in: &security_beta.AuthorizationPolicy{ 5118 Selector: &api.WorkloadSelector{ 5119 MatchLabels: map[string]string{ 5120 "app": "httpbin", 5121 }, 5122 }, 5123 Rules: []*security_beta.Rule{ 5124 { 5125 When: []*security_beta.Condition{ 5126 { 5127 Key: "key1", 5128 Values: []string{"v1"}, 5129 }, 5130 }, 5131 }, 5132 }, 5133 }, 5134 valid: false, 5135 }, 5136 { 5137 name: "L7DenyWithFrom", 5138 in: &security_beta.AuthorizationPolicy{ 5139 Action: security_beta.AuthorizationPolicy_DENY, 5140 Selector: &api.WorkloadSelector{ 5141 MatchLabels: map[string]string{ 5142 "app": "httpbin", 5143 }, 5144 }, 5145 Rules: []*security_beta.Rule{ 5146 { 5147 From: []*security_beta.Rule_From{ 5148 { 5149 Source: &security_beta.Source{ 5150 RequestPrincipals: []string{"example.com/sub-1"}, 5151 }, 5152 }, 5153 }, 5154 }, 5155 }, 5156 }, 5157 valid: true, 5158 Warning: true, 5159 }, 5160 { 5161 name: "L7DenyWithTo", 5162 in: &security_beta.AuthorizationPolicy{ 5163 Action: security_beta.AuthorizationPolicy_DENY, 5164 Selector: &api.WorkloadSelector{ 5165 MatchLabels: map[string]string{ 5166 "app": "httpbin", 5167 }, 5168 }, 5169 Rules: []*security_beta.Rule{ 5170 { 5171 To: []*security_beta.Rule_To{ 5172 { 5173 Operation: &security_beta.Operation{ 5174 Methods: []string{"GET", "DELETE"}, 5175 }, 5176 }, 5177 }, 5178 }, 5179 }, 5180 }, 5181 valid: true, 5182 Warning: true, 5183 }, 5184 { 5185 name: "L7DenyWithFromAndTo", 5186 in: &security_beta.AuthorizationPolicy{ 5187 Action: security_beta.AuthorizationPolicy_DENY, 5188 Selector: &api.WorkloadSelector{ 5189 MatchLabels: map[string]string{ 5190 "app": "httpbin", 5191 }, 5192 }, 5193 Rules: []*security_beta.Rule{ 5194 { 5195 From: []*security_beta.Rule_From{ 5196 { 5197 Source: &security_beta.Source{ 5198 Principals: []string{"temp"}, 5199 }, 5200 }, 5201 }, 5202 To: []*security_beta.Rule_To{ 5203 { 5204 Operation: &security_beta.Operation{ 5205 Methods: []string{"GET", "DELETE"}, 5206 }, 5207 }, 5208 }, 5209 }, 5210 }, 5211 }, 5212 valid: true, 5213 Warning: true, 5214 }, 5215 { 5216 name: "L7DenyWithFromAndToWithPort", 5217 in: &security_beta.AuthorizationPolicy{ 5218 Action: security_beta.AuthorizationPolicy_DENY, 5219 Selector: &api.WorkloadSelector{ 5220 MatchLabels: map[string]string{ 5221 "app": "httpbin", 5222 }, 5223 }, 5224 Rules: []*security_beta.Rule{ 5225 { 5226 From: []*security_beta.Rule_From{ 5227 { 5228 Source: &security_beta.Source{ 5229 Principals: []string{"temp"}, 5230 }, 5231 }, 5232 }, 5233 To: []*security_beta.Rule_To{ 5234 { 5235 Operation: &security_beta.Operation{ 5236 Ports: []string{"8080"}, 5237 Methods: []string{"GET", "DELETE"}, 5238 }, 5239 }, 5240 }, 5241 }, 5242 }, 5243 }, 5244 valid: true, 5245 Warning: false, 5246 }, 5247 } 5248 5249 for _, c := range cases { 5250 war, got := ValidateAuthorizationPolicy(config.Config{ 5251 Meta: config.Meta{ 5252 Name: "name", 5253 Namespace: "namespace", 5254 Annotations: c.annotations, 5255 }, 5256 Spec: c.in, 5257 }) 5258 if (got == nil) != c.valid { 5259 t.Errorf("test: %q error: got: %v\nwant: %v", c.name, got, c.valid) 5260 } 5261 if (war != nil) != c.Warning { 5262 t.Errorf("test: %q warning: got: %v\nwant: %v", c.name, war, c.valid) 5263 } 5264 } 5265 } 5266 5267 func TestValidateSidecar(t *testing.T) { 5268 tests := []struct { 5269 name string 5270 in *networking.Sidecar 5271 valid bool 5272 warn bool 5273 }{ 5274 {"empty ingress and egress", &networking.Sidecar{}, false, false}, 5275 {"default", &networking.Sidecar{ 5276 Egress: []*networking.IstioEgressListener{ 5277 { 5278 Hosts: []string{"*/*"}, 5279 }, 5280 }, 5281 }, true, false}, 5282 {"workload selector without labels", &networking.Sidecar{ 5283 Egress: []*networking.IstioEgressListener{ 5284 { 5285 Hosts: []string{"*/*"}, 5286 }, 5287 }, 5288 WorkloadSelector: &networking.WorkloadSelector{}, 5289 }, true, true}, 5290 {"import local namespace with wildcard", &networking.Sidecar{ 5291 Egress: []*networking.IstioEgressListener{ 5292 { 5293 Hosts: []string{"./*"}, 5294 }, 5295 }, 5296 }, true, false}, 5297 {"import local namespace with fqdn", &networking.Sidecar{ 5298 Egress: []*networking.IstioEgressListener{ 5299 { 5300 Hosts: []string{"./foo.com"}, 5301 }, 5302 }, 5303 }, true, false}, 5304 {"import nothing", &networking.Sidecar{ 5305 Egress: []*networking.IstioEgressListener{ 5306 { 5307 Hosts: []string{"~/*"}, 5308 }, 5309 }, 5310 }, true, false}, 5311 {"bad egress host 1", &networking.Sidecar{ 5312 Egress: []*networking.IstioEgressListener{ 5313 { 5314 Hosts: []string{"*"}, 5315 }, 5316 }, 5317 }, false, false}, 5318 {"bad egress host 2", &networking.Sidecar{ 5319 Egress: []*networking.IstioEgressListener{ 5320 { 5321 Hosts: []string{"/"}, 5322 }, 5323 }, 5324 }, false, false}, 5325 {"empty egress host", &networking.Sidecar{ 5326 Egress: []*networking.IstioEgressListener{ 5327 { 5328 Hosts: []string{}, 5329 }, 5330 }, 5331 }, false, false}, 5332 {"multiple wildcard egress", &networking.Sidecar{ 5333 Egress: []*networking.IstioEgressListener{ 5334 { 5335 Hosts: []string{ 5336 "*/foo.com", 5337 }, 5338 }, 5339 { 5340 Hosts: []string{ 5341 "ns1/bar.com", 5342 }, 5343 }, 5344 }, 5345 }, false, false}, 5346 {"wildcard egress not in end", &networking.Sidecar{ 5347 Egress: []*networking.IstioEgressListener{ 5348 { 5349 Hosts: []string{ 5350 "*/foo.com", 5351 }, 5352 }, 5353 { 5354 Port: &networking.SidecarPort{ 5355 Protocol: "http", 5356 Number: 8080, 5357 Name: "h8080", 5358 }, 5359 Hosts: []string{ 5360 "ns1/bar.com", 5361 }, 5362 }, 5363 }, 5364 }, false, false}, 5365 {"invalid Port", &networking.Sidecar{ 5366 Egress: []*networking.IstioEgressListener{ 5367 { 5368 Port: &networking.SidecarPort{ 5369 Protocol: "http1", 5370 Number: 1000000, 5371 Name: "", 5372 }, 5373 Hosts: []string{ 5374 "ns1/bar.com", 5375 }, 5376 }, 5377 }, 5378 }, false, false}, 5379 {"Port without name", &networking.Sidecar{ 5380 Egress: []*networking.IstioEgressListener{ 5381 { 5382 Port: &networking.SidecarPort{ 5383 Protocol: "http", 5384 Number: 8080, 5385 }, 5386 Hosts: []string{ 5387 "ns1/bar.com", 5388 }, 5389 }, 5390 }, 5391 }, true, false}, 5392 {"UDS bind in outbound", &networking.Sidecar{ 5393 Egress: []*networking.IstioEgressListener{ 5394 { 5395 Port: &networking.SidecarPort{ 5396 Protocol: "http", 5397 Number: 0, 5398 Name: "uds", 5399 }, 5400 Hosts: []string{ 5401 "ns1/bar.com", 5402 }, 5403 Bind: "unix:///@foo/bar/com", 5404 }, 5405 }, 5406 }, true, false}, 5407 {"UDS bind in inbound", &networking.Sidecar{ 5408 Ingress: []*networking.IstioIngressListener{ 5409 { 5410 Port: &networking.SidecarPort{ 5411 Protocol: "http", 5412 Number: 0, 5413 Name: "uds", 5414 }, 5415 Bind: "unix:///@foo/bar/com", 5416 DefaultEndpoint: "127.0.0.1:9999", 5417 }, 5418 }, 5419 }, false, false}, 5420 {"UDS bind in outbound 2", &networking.Sidecar{ 5421 Egress: []*networking.IstioEgressListener{ 5422 { 5423 Port: &networking.SidecarPort{ 5424 Protocol: "http", 5425 Number: 0, 5426 Name: "uds", 5427 }, 5428 Hosts: []string{ 5429 "ns1/bar.com", 5430 }, 5431 Bind: "unix:///foo/bar/com", 5432 }, 5433 }, 5434 }, true, false}, 5435 {"invalid bind", &networking.Sidecar{ 5436 Egress: []*networking.IstioEgressListener{ 5437 { 5438 Port: &networking.SidecarPort{ 5439 Protocol: "http", 5440 Number: 0, 5441 Name: "uds", 5442 }, 5443 Hosts: []string{ 5444 "ns1/bar.com", 5445 }, 5446 Bind: "foobar:///@foo/bar/com", 5447 }, 5448 }, 5449 }, false, false}, 5450 {"invalid capture mode with uds bind", &networking.Sidecar{ 5451 Egress: []*networking.IstioEgressListener{ 5452 { 5453 Port: &networking.SidecarPort{ 5454 Protocol: "http", 5455 Number: 0, 5456 Name: "uds", 5457 }, 5458 Hosts: []string{ 5459 "ns1/bar.com", 5460 }, 5461 Bind: "unix:///@foo/bar/com", 5462 CaptureMode: networking.CaptureMode_IPTABLES, 5463 }, 5464 }, 5465 }, false, false}, 5466 {"duplicate UDS bind", &networking.Sidecar{ 5467 Egress: []*networking.IstioEgressListener{ 5468 { 5469 Port: &networking.SidecarPort{ 5470 Protocol: "http", 5471 Number: 0, 5472 Name: "uds", 5473 }, 5474 Hosts: []string{ 5475 "ns1/bar.com", 5476 }, 5477 Bind: "unix:///@foo/bar/com", 5478 }, 5479 { 5480 Port: &networking.SidecarPort{ 5481 Protocol: "http", 5482 Number: 0, 5483 Name: "uds", 5484 }, 5485 Hosts: []string{ 5486 "ns1/bar.com", 5487 }, 5488 Bind: "unix:///@foo/bar/com", 5489 }, 5490 }, 5491 }, false, false}, 5492 {"duplicate ports", &networking.Sidecar{ 5493 Egress: []*networking.IstioEgressListener{ 5494 { 5495 Port: &networking.SidecarPort{ 5496 Protocol: "http", 5497 Number: 90, 5498 Name: "foo", 5499 }, 5500 Hosts: []string{ 5501 "ns1/bar.com", 5502 }, 5503 }, 5504 { 5505 Port: &networking.SidecarPort{ 5506 Protocol: "tcp", 5507 Number: 90, 5508 Name: "tcp", 5509 }, 5510 Hosts: []string{ 5511 "ns2/bar.com", 5512 }, 5513 }, 5514 }, 5515 }, false, false}, 5516 {"ingress without port", &networking.Sidecar{ 5517 Ingress: []*networking.IstioIngressListener{ 5518 { 5519 DefaultEndpoint: "127.0.0.1:110", 5520 }, 5521 }, 5522 Egress: []*networking.IstioEgressListener{ 5523 { 5524 Hosts: []string{"*/*"}, 5525 }, 5526 }, 5527 }, false, false}, 5528 {"ingress without port and with IPv6 endpoint", &networking.Sidecar{ 5529 Ingress: []*networking.IstioIngressListener{ 5530 { 5531 DefaultEndpoint: "[::1]:110", 5532 }, 5533 }, 5534 Egress: []*networking.IstioEgressListener{ 5535 { 5536 Hosts: []string{"*/*"}, 5537 }, 5538 }, 5539 }, false, false}, 5540 {"ingress with duplicate ports", &networking.Sidecar{ 5541 Ingress: []*networking.IstioIngressListener{ 5542 { 5543 Port: &networking.SidecarPort{ 5544 Protocol: "http", 5545 Number: 90, 5546 Name: "foo", 5547 }, 5548 DefaultEndpoint: "127.0.0.1:110", 5549 }, 5550 { 5551 Port: &networking.SidecarPort{ 5552 Protocol: "tcp", 5553 Number: 90, 5554 Name: "bar", 5555 }, 5556 DefaultEndpoint: "127.0.0.1:110", 5557 }, 5558 }, 5559 Egress: []*networking.IstioEgressListener{ 5560 { 5561 Hosts: []string{"*/*"}, 5562 }, 5563 }, 5564 }, false, false}, 5565 {"ingress with duplicate ports and IPv6 endpoint", &networking.Sidecar{ 5566 Ingress: []*networking.IstioIngressListener{ 5567 { 5568 Port: &networking.SidecarPort{ 5569 Protocol: "http", 5570 Number: 90, 5571 Name: "foo", 5572 }, 5573 DefaultEndpoint: "[::1]:110", 5574 }, 5575 { 5576 Port: &networking.SidecarPort{ 5577 Protocol: "tcp", 5578 Number: 90, 5579 Name: "bar", 5580 }, 5581 DefaultEndpoint: "[::1]:110", 5582 }, 5583 }, 5584 Egress: []*networking.IstioEgressListener{ 5585 { 5586 Hosts: []string{"*/*"}, 5587 }, 5588 }, 5589 }, false, false}, 5590 {"ingress without default endpoint", &networking.Sidecar{ 5591 Ingress: []*networking.IstioIngressListener{ 5592 { 5593 Port: &networking.SidecarPort{ 5594 Protocol: "http", 5595 Number: 90, 5596 Name: "foo", 5597 }, 5598 }, 5599 }, 5600 Egress: []*networking.IstioEgressListener{ 5601 { 5602 Hosts: []string{"*/*"}, 5603 }, 5604 }, 5605 }, true, false}, 5606 {"ingress with invalid default endpoint in IPv4", &networking.Sidecar{ 5607 Ingress: []*networking.IstioIngressListener{ 5608 { 5609 Port: &networking.SidecarPort{ 5610 Protocol: "http", 5611 Number: 90, 5612 Name: "foo", 5613 }, 5614 DefaultEndpoint: "1.1.1.1:90", 5615 }, 5616 }, 5617 }, false, false}, 5618 {"ingress with invalid default endpoint in IPv6", &networking.Sidecar{ 5619 Ingress: []*networking.IstioIngressListener{ 5620 { 5621 Port: &networking.SidecarPort{ 5622 Protocol: "http", 5623 Number: 90, 5624 Name: "foo", 5625 }, 5626 DefaultEndpoint: "[1:1:1:1:1:1:1:1]:90", 5627 }, 5628 }, 5629 }, false, false}, 5630 {"ingress with invalid default endpoint uds", &networking.Sidecar{ 5631 Ingress: []*networking.IstioIngressListener{ 5632 { 5633 Port: &networking.SidecarPort{ 5634 Protocol: "http", 5635 Number: 90, 5636 Name: "foo", 5637 }, 5638 DefaultEndpoint: "unix:///", 5639 }, 5640 }, 5641 Egress: []*networking.IstioEgressListener{ 5642 { 5643 Hosts: []string{"*/*"}, 5644 }, 5645 }, 5646 }, false, false}, 5647 {"ingress with invalid default endpoint port in IPv4", &networking.Sidecar{ 5648 Ingress: []*networking.IstioIngressListener{ 5649 { 5650 Port: &networking.SidecarPort{ 5651 Protocol: "http", 5652 Number: 90, 5653 Name: "foo", 5654 }, 5655 DefaultEndpoint: "127.0.0.1:hi", 5656 }, 5657 }, 5658 Egress: []*networking.IstioEgressListener{ 5659 { 5660 Hosts: []string{"*/*"}, 5661 }, 5662 }, 5663 }, false, false}, 5664 {"ingress with invalid default endpoint port in IPv6", &networking.Sidecar{ 5665 Ingress: []*networking.IstioIngressListener{ 5666 { 5667 Port: &networking.SidecarPort{ 5668 Protocol: "http", 5669 Number: 90, 5670 Name: "foo", 5671 }, 5672 DefaultEndpoint: "[::1]:hi", 5673 }, 5674 }, 5675 Egress: []*networking.IstioEgressListener{ 5676 { 5677 Hosts: []string{"*/*"}, 5678 }, 5679 }, 5680 }, false, false}, 5681 {"valid ingress and egress in IPv4", &networking.Sidecar{ 5682 Ingress: []*networking.IstioIngressListener{ 5683 { 5684 Port: &networking.SidecarPort{ 5685 Protocol: "http", 5686 Number: 90, 5687 Name: "foo", 5688 }, 5689 DefaultEndpoint: "127.0.0.1:9999", 5690 }, 5691 }, 5692 Egress: []*networking.IstioEgressListener{ 5693 { 5694 Hosts: []string{"*/*"}, 5695 }, 5696 }, 5697 }, true, false}, 5698 {"valid ingress and egress in IPv6", &networking.Sidecar{ 5699 Ingress: []*networking.IstioIngressListener{ 5700 { 5701 Port: &networking.SidecarPort{ 5702 Protocol: "http", 5703 Number: 90, 5704 Name: "foo", 5705 }, 5706 DefaultEndpoint: "[::1]:9999", 5707 }, 5708 }, 5709 Egress: []*networking.IstioEgressListener{ 5710 { 5711 Hosts: []string{"*/*"}, 5712 }, 5713 }, 5714 }, true, false}, 5715 {"valid ingress and empty egress in IPv4", &networking.Sidecar{ 5716 Ingress: []*networking.IstioIngressListener{ 5717 { 5718 Port: &networking.SidecarPort{ 5719 Protocol: "http", 5720 Number: 90, 5721 Name: "foo", 5722 }, 5723 DefaultEndpoint: "127.0.0.1:9999", 5724 }, 5725 }, 5726 }, true, false}, 5727 {"valid ingress and empty egress in IPv6", &networking.Sidecar{ 5728 Ingress: []*networking.IstioIngressListener{ 5729 { 5730 Port: &networking.SidecarPort{ 5731 Protocol: "http", 5732 Number: 90, 5733 Name: "foo", 5734 }, 5735 DefaultEndpoint: "[::1]:9999", 5736 }, 5737 }, 5738 }, true, false}, 5739 {"empty", &networking.Sidecar{}, false, false}, 5740 {"just outbound traffic policy", &networking.Sidecar{OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ 5741 Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, 5742 }}, true, false}, 5743 {"empty protocol in IPv4", &networking.Sidecar{ 5744 Ingress: []*networking.IstioIngressListener{ 5745 { 5746 Port: &networking.SidecarPort{ 5747 Number: 90, 5748 Name: "foo", 5749 }, 5750 DefaultEndpoint: "127.0.0.1:9999", 5751 }, 5752 }, 5753 Egress: []*networking.IstioEgressListener{ 5754 { 5755 Hosts: []string{"*/*"}, 5756 }, 5757 }, 5758 }, true, false}, 5759 {"empty protocol in IPv6", &networking.Sidecar{ 5760 Ingress: []*networking.IstioIngressListener{ 5761 { 5762 Port: &networking.SidecarPort{ 5763 Number: 90, 5764 Name: "foo", 5765 }, 5766 DefaultEndpoint: "[::1]:9999", 5767 }, 5768 }, 5769 Egress: []*networking.IstioEgressListener{ 5770 { 5771 Hosts: []string{"*/*"}, 5772 }, 5773 }, 5774 }, true, false}, 5775 {"ALLOW_ANY sidecar egress policy with no egress proxy ", &networking.Sidecar{ 5776 OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ 5777 Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, 5778 }, 5779 Egress: []*networking.IstioEgressListener{ 5780 { 5781 Hosts: []string{"*/*"}, 5782 }, 5783 }, 5784 }, true, false}, 5785 {"sidecar egress proxy with RESGISTRY_ONLY(default)", &networking.Sidecar{ 5786 OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ 5787 EgressProxy: &networking.Destination{ 5788 Host: "foo.bar", 5789 Subset: "shiny", 5790 Port: &networking.PortSelector{ 5791 Number: 5000, 5792 }, 5793 }, 5794 }, 5795 Egress: []*networking.IstioEgressListener{ 5796 { 5797 Hosts: []string{"*/*"}, 5798 }, 5799 }, 5800 }, false, false}, 5801 {"sidecar egress proxy with ALLOW_ANY", &networking.Sidecar{ 5802 OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ 5803 Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, 5804 EgressProxy: &networking.Destination{ 5805 Host: "foo.bar", 5806 Subset: "shiny", 5807 Port: &networking.PortSelector{ 5808 Number: 5000, 5809 }, 5810 }, 5811 }, 5812 Egress: []*networking.IstioEgressListener{ 5813 { 5814 Hosts: []string{"*/*"}, 5815 }, 5816 }, 5817 }, true, false}, 5818 {"sidecar egress proxy with ALLOW_ANY, service hostname invalid fqdn", &networking.Sidecar{ 5819 OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ 5820 Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, 5821 EgressProxy: &networking.Destination{ 5822 Host: "foobar*123", 5823 Subset: "shiny", 5824 Port: &networking.PortSelector{ 5825 Number: 5000, 5826 }, 5827 }, 5828 }, 5829 Egress: []*networking.IstioEgressListener{ 5830 { 5831 Hosts: []string{"*/*"}, 5832 }, 5833 }, 5834 }, false, false}, 5835 {"sidecar egress proxy(without Port) with ALLOW_ANY", &networking.Sidecar{ 5836 OutboundTrafficPolicy: &networking.OutboundTrafficPolicy{ 5837 Mode: networking.OutboundTrafficPolicy_ALLOW_ANY, 5838 EgressProxy: &networking.Destination{ 5839 Host: "foo.bar", 5840 Subset: "shiny", 5841 }, 5842 }, 5843 Egress: []*networking.IstioEgressListener{ 5844 { 5845 Hosts: []string{"*/*"}, 5846 }, 5847 }, 5848 }, false, false}, 5849 {"sidecar egress only one wildcarded", &networking.Sidecar{ 5850 Egress: []*networking.IstioEgressListener{ 5851 { 5852 Hosts: []string{ 5853 "*/*", 5854 "test/a.com", 5855 }, 5856 }, 5857 }, 5858 }, true, true}, 5859 {"sidecar egress wildcarded ns", &networking.Sidecar{ 5860 Egress: []*networking.IstioEgressListener{ 5861 { 5862 Hosts: []string{ 5863 "*/b.com", 5864 "test/a.com", 5865 }, 5866 }, 5867 }, 5868 }, true, false}, 5869 {"sidecar egress duplicated with wildcarded same namespace", &networking.Sidecar{ 5870 Egress: []*networking.IstioEgressListener{ 5871 { 5872 Hosts: []string{ 5873 "test/*", 5874 "test/a.com", 5875 }, 5876 }, 5877 }, 5878 }, true, true}, 5879 {"invalid partial wildcard", &networking.Sidecar{ 5880 Egress: []*networking.IstioEgressListener{ 5881 { 5882 Hosts: []string{ 5883 "test/*a.com", 5884 }, 5885 }, 5886 }, 5887 }, false, false}, 5888 {"sidecar egress duplicated with wildcarded same namespace .", &networking.Sidecar{ 5889 Egress: []*networking.IstioEgressListener{ 5890 { 5891 Hosts: []string{ 5892 "./*", 5893 "bar/a.com", 5894 }, 5895 }, 5896 }, 5897 }, true, true}, 5898 {"ingress tls mode set to ISTIO_MUTUAL in IPv4", &networking.Sidecar{ 5899 Ingress: []*networking.IstioIngressListener{ 5900 { 5901 Port: &networking.SidecarPort{ 5902 Protocol: "http", 5903 Number: 90, 5904 Name: "foo", 5905 }, 5906 DefaultEndpoint: "127.0.0.1:9999", 5907 Tls: &networking.ServerTLSSettings{ 5908 Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, 5909 }, 5910 }, 5911 }, 5912 }, false, false}, 5913 {"ingress tls mode set to ISTIO_MUTUAL in IPv6", &networking.Sidecar{ 5914 Ingress: []*networking.IstioIngressListener{ 5915 { 5916 Port: &networking.SidecarPort{ 5917 Protocol: "http", 5918 Number: 90, 5919 Name: "foo", 5920 }, 5921 DefaultEndpoint: "[::1]:9999", 5922 Tls: &networking.ServerTLSSettings{ 5923 Mode: networking.ServerTLSSettings_ISTIO_MUTUAL, 5924 }, 5925 }, 5926 }, 5927 }, false, false}, 5928 {"ingress tls mode set to ISTIO_AUTO_PASSTHROUGH in IPv4", &networking.Sidecar{ 5929 Ingress: []*networking.IstioIngressListener{ 5930 { 5931 Port: &networking.SidecarPort{ 5932 Protocol: "http", 5933 Number: 90, 5934 Name: "foo", 5935 }, 5936 DefaultEndpoint: "127.0.0.1:9999", 5937 Tls: &networking.ServerTLSSettings{ 5938 Mode: networking.ServerTLSSettings_AUTO_PASSTHROUGH, 5939 }, 5940 }, 5941 }, 5942 }, false, false}, 5943 {"ingress tls mode set to ISTIO_AUTO_PASSTHROUGH in IPv6", &networking.Sidecar{ 5944 Ingress: []*networking.IstioIngressListener{ 5945 { 5946 Port: &networking.SidecarPort{ 5947 Protocol: "http", 5948 Number: 90, 5949 Name: "foo", 5950 }, 5951 DefaultEndpoint: "[::1]:9999", 5952 Tls: &networking.ServerTLSSettings{ 5953 Mode: networking.ServerTLSSettings_AUTO_PASSTHROUGH, 5954 }, 5955 }, 5956 }, 5957 }, false, false}, 5958 {"ingress tls invalid protocol in IPv4", &networking.Sidecar{ 5959 Ingress: []*networking.IstioIngressListener{ 5960 { 5961 Port: &networking.SidecarPort{ 5962 Protocol: "tcp", 5963 Number: 90, 5964 Name: "foo", 5965 }, 5966 DefaultEndpoint: "127.0.0.1:9999", 5967 Tls: &networking.ServerTLSSettings{ 5968 Mode: networking.ServerTLSSettings_SIMPLE, 5969 }, 5970 }, 5971 }, 5972 }, false, false}, 5973 {"ingress tls invalid protocol in IPv6", &networking.Sidecar{ 5974 Ingress: []*networking.IstioIngressListener{ 5975 { 5976 Port: &networking.SidecarPort{ 5977 Protocol: "tcp", 5978 Number: 90, 5979 Name: "foo", 5980 }, 5981 DefaultEndpoint: "[::1]:9999", 5982 Tls: &networking.ServerTLSSettings{ 5983 Mode: networking.ServerTLSSettings_SIMPLE, 5984 }, 5985 }, 5986 }, 5987 }, false, false}, 5988 {"ingress tls httpRedirect is not supported in IPv4", &networking.Sidecar{ 5989 Ingress: []*networking.IstioIngressListener{ 5990 { 5991 Port: &networking.SidecarPort{ 5992 Protocol: "tcp", 5993 Number: 90, 5994 Name: "foo", 5995 }, 5996 DefaultEndpoint: "127.0.0.1:9999", 5997 Tls: &networking.ServerTLSSettings{ 5998 Mode: networking.ServerTLSSettings_SIMPLE, 5999 HttpsRedirect: true, 6000 }, 6001 }, 6002 }, 6003 }, false, false}, 6004 {"ingress tls httpRedirect is not supported in IPv6", &networking.Sidecar{ 6005 Ingress: []*networking.IstioIngressListener{ 6006 { 6007 Port: &networking.SidecarPort{ 6008 Protocol: "tcp", 6009 Number: 90, 6010 Name: "foo", 6011 }, 6012 DefaultEndpoint: "[::1]:9999", 6013 Tls: &networking.ServerTLSSettings{ 6014 Mode: networking.ServerTLSSettings_SIMPLE, 6015 HttpsRedirect: true, 6016 }, 6017 }, 6018 }, 6019 }, false, false}, 6020 {"ingress tls SAN entries are not supported in IPv4", &networking.Sidecar{ 6021 Ingress: []*networking.IstioIngressListener{ 6022 { 6023 Port: &networking.SidecarPort{ 6024 Protocol: "tcp", 6025 Number: 90, 6026 Name: "foo", 6027 }, 6028 DefaultEndpoint: "127.0.0.1:9999", 6029 Tls: &networking.ServerTLSSettings{ 6030 Mode: networking.ServerTLSSettings_SIMPLE, 6031 SubjectAltNames: []string{"httpbin.com"}, 6032 }, 6033 }, 6034 }, 6035 }, false, false}, 6036 {"ingress tls SAN entries are not supported in IPv6", &networking.Sidecar{ 6037 Ingress: []*networking.IstioIngressListener{ 6038 { 6039 Port: &networking.SidecarPort{ 6040 Protocol: "tcp", 6041 Number: 90, 6042 Name: "foo", 6043 }, 6044 DefaultEndpoint: "[::1]:9999", 6045 Tls: &networking.ServerTLSSettings{ 6046 Mode: networking.ServerTLSSettings_SIMPLE, 6047 SubjectAltNames: []string{"httpbin.com"}, 6048 }, 6049 }, 6050 }, 6051 }, false, false}, 6052 {"ingress tls credentialName is not supported in IPv4", &networking.Sidecar{ 6053 Ingress: []*networking.IstioIngressListener{ 6054 { 6055 Port: &networking.SidecarPort{ 6056 Protocol: "tcp", 6057 Number: 90, 6058 Name: "foo", 6059 }, 6060 DefaultEndpoint: "127.0.0.1:9999", 6061 Tls: &networking.ServerTLSSettings{ 6062 Mode: networking.ServerTLSSettings_SIMPLE, 6063 CredentialName: "secret-name", 6064 }, 6065 }, 6066 }, 6067 }, false, false}, 6068 {"ingress tls credentialName is not supported in IPv6", &networking.Sidecar{ 6069 Ingress: []*networking.IstioIngressListener{ 6070 { 6071 Port: &networking.SidecarPort{ 6072 Protocol: "tcp", 6073 Number: 90, 6074 Name: "foo", 6075 }, 6076 DefaultEndpoint: "[::1]:9999", 6077 Tls: &networking.ServerTLSSettings{ 6078 Mode: networking.ServerTLSSettings_SIMPLE, 6079 CredentialName: "secret-name", 6080 }, 6081 }, 6082 }, 6083 }, false, false}, 6084 // We're using the same validation code as DestinationRule, so we're really trusting the TrafficPolicy 6085 // validation code's testing. Here we just want to exercise the edge cases around Sidecar specifically. 6086 {"valid inbound connection pool", &networking.Sidecar{ 6087 InboundConnectionPool: &networking.ConnectionPoolSettings{ 6088 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 6089 Http1MaxPendingRequests: 1024, 6090 Http2MaxRequests: 1024, 6091 MaxRequestsPerConnection: 1024, 6092 MaxRetries: 1024, 6093 }, 6094 }, 6095 }, true, false}, 6096 {"valid port-level connection pool with top level default", &networking.Sidecar{ 6097 Ingress: []*networking.IstioIngressListener{ 6098 { 6099 Port: &networking.SidecarPort{ 6100 Protocol: "http", 6101 Number: 90, 6102 Name: "foo", 6103 }, 6104 ConnectionPool: &networking.ConnectionPoolSettings{ 6105 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 6106 Http1MaxPendingRequests: 1024, 6107 Http2MaxRequests: 1024, 6108 MaxRequestsPerConnection: 1024, 6109 MaxRetries: 1024, 6110 }, 6111 }, 6112 }, 6113 }, 6114 InboundConnectionPool: &networking.ConnectionPoolSettings{ 6115 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 6116 Http1MaxPendingRequests: 1024, 6117 Http2MaxRequests: 1024, 6118 MaxRequestsPerConnection: 1024, 6119 MaxRetries: 1024, 6120 }, 6121 }, 6122 }, true, false}, 6123 {"valid port-level connection pool without a top level default", &networking.Sidecar{ 6124 Ingress: []*networking.IstioIngressListener{ 6125 { 6126 Port: &networking.SidecarPort{ 6127 Protocol: "http", 6128 Number: 90, 6129 Name: "foo", 6130 }, 6131 ConnectionPool: &networking.ConnectionPoolSettings{ 6132 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 6133 Http1MaxPendingRequests: 1024, 6134 Http2MaxRequests: 1024, 6135 MaxRequestsPerConnection: 1024, 6136 MaxRetries: 1024, 6137 }, 6138 }, 6139 }, 6140 }, 6141 }, true, false}, 6142 {"warn http connection settings on tcp port", &networking.Sidecar{ 6143 Ingress: []*networking.IstioIngressListener{ 6144 { 6145 Port: &networking.SidecarPort{ 6146 Protocol: "tcp", 6147 Number: 90, 6148 Name: "foo", 6149 }, 6150 ConnectionPool: &networking.ConnectionPoolSettings{ 6151 Http: &networking.ConnectionPoolSettings_HTTPSettings{ 6152 Http1MaxPendingRequests: 1024, 6153 Http2MaxRequests: 1024, 6154 MaxRequestsPerConnection: 1024, 6155 MaxRetries: 1024, 6156 }, 6157 }, 6158 }, 6159 }, 6160 }, true, true}, 6161 {"invalid top level connection pool", &networking.Sidecar{ 6162 InboundConnectionPool: &networking.ConnectionPoolSettings{}, 6163 }, false, false}, 6164 {"invalid port-level connection pool", &networking.Sidecar{ 6165 Ingress: []*networking.IstioIngressListener{ 6166 { 6167 Port: &networking.SidecarPort{ 6168 Protocol: "tcp", 6169 Number: 90, 6170 Name: "foo", 6171 }, 6172 ConnectionPool: &networking.ConnectionPoolSettings{}, 6173 }, 6174 }, 6175 }, false, false}, 6176 } 6177 6178 for _, tt := range tests { 6179 t.Run(tt.name, func(t *testing.T) { 6180 warn, err := ValidateSidecar(config.Config{ 6181 Meta: config.Meta{ 6182 Name: "foo", 6183 Namespace: "bar", 6184 }, 6185 Spec: tt.in, 6186 }) 6187 checkValidation(t, warn, err, tt.valid, tt.warn) 6188 }) 6189 } 6190 } 6191 6192 func TestValidateRequestAuthentication(t *testing.T) { 6193 cases := []struct { 6194 name string 6195 configName string 6196 annotations map[string]string 6197 in proto.Message 6198 valid bool 6199 warning bool 6200 }{ 6201 { 6202 name: "empty spec", 6203 configName: constants.DefaultAuthenticationPolicyName, 6204 in: &security_beta.RequestAuthentication{}, 6205 valid: true, 6206 }, 6207 { 6208 name: "another empty spec", 6209 configName: constants.DefaultAuthenticationPolicyName, 6210 in: &security_beta.RequestAuthentication{ 6211 Selector: &api.WorkloadSelector{}, 6212 }, 6213 valid: true, 6214 warning: true, 6215 }, 6216 { 6217 name: "empty spec with non default name", 6218 configName: someName, 6219 in: &security_beta.RequestAuthentication{}, 6220 valid: true, 6221 }, 6222 { 6223 name: "dry run annotation not supported", 6224 configName: someName, 6225 annotations: map[string]string{"istio.io/dry-run": "true"}, 6226 in: &security_beta.RequestAuthentication{}, 6227 valid: false, 6228 }, 6229 { 6230 name: "default name with non empty selector", 6231 configName: constants.DefaultAuthenticationPolicyName, 6232 in: &security_beta.RequestAuthentication{ 6233 Selector: &api.WorkloadSelector{ 6234 MatchLabels: map[string]string{ 6235 "app": "httpbin", 6236 }, 6237 }, 6238 }, 6239 valid: true, 6240 }, 6241 { 6242 name: "empty jwt rule", 6243 configName: "foo", 6244 in: &security_beta.RequestAuthentication{ 6245 JwtRules: []*security_beta.JWTRule{ 6246 {}, 6247 }, 6248 }, 6249 valid: false, 6250 }, 6251 { 6252 name: "empty issuer", 6253 configName: "foo", 6254 in: &security_beta.RequestAuthentication{ 6255 JwtRules: []*security_beta.JWTRule{ 6256 { 6257 Issuer: "", 6258 }, 6259 }, 6260 }, 6261 valid: false, 6262 }, 6263 { 6264 name: "bad JwksUri - no protocol", 6265 configName: "foo", 6266 in: &security_beta.RequestAuthentication{ 6267 JwtRules: []*security_beta.JWTRule{ 6268 { 6269 Issuer: "foo.com", 6270 JwksUri: "foo.com", 6271 }, 6272 }, 6273 }, 6274 valid: false, 6275 }, 6276 { 6277 name: "bad JwksUri - invalid port", 6278 configName: "foo", 6279 in: &security_beta.RequestAuthentication{ 6280 JwtRules: []*security_beta.JWTRule{ 6281 { 6282 Issuer: "foo.com", 6283 JwksUri: "https://foo.com:not-a-number", 6284 }, 6285 }, 6286 }, 6287 valid: false, 6288 }, 6289 { 6290 name: "empty value", 6291 configName: "foo", 6292 in: &security_beta.RequestAuthentication{ 6293 Selector: &api.WorkloadSelector{ 6294 MatchLabels: map[string]string{ 6295 "app": "httpbin", 6296 "version": "", 6297 }, 6298 }, 6299 JwtRules: []*security_beta.JWTRule{ 6300 { 6301 Issuer: "foo.com", 6302 JwksUri: "https://foo.com/cert", 6303 }, 6304 }, 6305 }, 6306 valid: true, 6307 }, 6308 { 6309 name: "bad workload selector - empty key", 6310 configName: "foo", 6311 in: &security_beta.RequestAuthentication{ 6312 Selector: &api.WorkloadSelector{ 6313 MatchLabels: map[string]string{ 6314 "app": "httpbin", 6315 "": "v1", 6316 }, 6317 }, 6318 JwtRules: []*security_beta.JWTRule{ 6319 { 6320 Issuer: "foo.com", 6321 JwksUri: "https://foo.com/cert", 6322 }, 6323 }, 6324 }, 6325 valid: false, 6326 }, 6327 { 6328 name: "good targetRef", 6329 configName: "foo", 6330 in: &security_beta.RequestAuthentication{ 6331 TargetRef: &api.PolicyTargetReference{ 6332 Group: gvk.KubernetesGateway.Group, 6333 Kind: gvk.KubernetesGateway.Kind, 6334 Name: "foo", 6335 }, 6336 JwtRules: []*security_beta.JWTRule{ 6337 { 6338 Issuer: "foo.com", 6339 JwksUri: "https://foo.com/cert", 6340 }, 6341 }, 6342 }, 6343 valid: true, 6344 }, 6345 { 6346 name: "bad targetRef - empty name", 6347 configName: "foo", 6348 in: &security_beta.RequestAuthentication{ 6349 TargetRef: &api.PolicyTargetReference{ 6350 Group: gvk.KubernetesGateway.Group, 6351 Kind: gvk.KubernetesGateway.Kind, 6352 }, 6353 JwtRules: []*security_beta.JWTRule{ 6354 { 6355 Issuer: "foo.com", 6356 JwksUri: "https://foo.com/cert", 6357 }, 6358 }, 6359 }, 6360 valid: false, 6361 }, 6362 { 6363 name: "bad targetRef - non-empty namespace", 6364 configName: "foo", 6365 in: &security_beta.RequestAuthentication{ 6366 TargetRef: &api.PolicyTargetReference{ 6367 Group: gvk.KubernetesGateway.Group, 6368 Kind: gvk.KubernetesGateway.Kind, 6369 Name: "foo", 6370 Namespace: "bar", 6371 }, 6372 JwtRules: []*security_beta.JWTRule{ 6373 { 6374 Issuer: "foo.com", 6375 JwksUri: "https://foo.com/cert", 6376 }, 6377 }, 6378 }, 6379 valid: false, 6380 }, 6381 { 6382 name: "bad targetRef - wrong group", 6383 configName: "foo", 6384 in: &security_beta.RequestAuthentication{ 6385 TargetRef: &api.PolicyTargetReference{ 6386 Group: "wrong-group", 6387 Kind: gvk.KubernetesGateway.Kind, 6388 Name: "foo", 6389 }, 6390 JwtRules: []*security_beta.JWTRule{ 6391 { 6392 Issuer: "foo.com", 6393 JwksUri: "https://foo.com/cert", 6394 }, 6395 }, 6396 }, 6397 valid: false, 6398 }, 6399 { 6400 name: "bad targetRef - wrong kind", 6401 configName: "foo", 6402 in: &security_beta.RequestAuthentication{ 6403 TargetRef: &api.PolicyTargetReference{ 6404 Group: gvk.KubernetesGateway.Group, 6405 Kind: "wrong-kind", 6406 Name: "foo", 6407 }, 6408 JwtRules: []*security_beta.JWTRule{ 6409 { 6410 Issuer: "foo.com", 6411 JwksUri: "https://foo.com/cert", 6412 }, 6413 }, 6414 }, 6415 valid: false, 6416 }, 6417 { 6418 name: "targetRef and selector cannot both be set", 6419 configName: "foo", 6420 in: &security_beta.RequestAuthentication{ 6421 TargetRef: &api.PolicyTargetReference{ 6422 Group: gvk.KubernetesGateway.Group, 6423 Kind: gvk.KubernetesGateway.Kind, 6424 Name: "foo", 6425 }, 6426 JwtRules: []*security_beta.JWTRule{ 6427 { 6428 Issuer: "foo.com", 6429 JwksUri: "https://foo.com/cert", 6430 }, 6431 }, 6432 Selector: &api.WorkloadSelector{ 6433 MatchLabels: map[string]string{ 6434 "app": "httpbin", 6435 }, 6436 }, 6437 }, 6438 valid: false, 6439 }, 6440 { 6441 name: "bad header location", 6442 configName: constants.DefaultAuthenticationPolicyName, 6443 in: &security_beta.RequestAuthentication{ 6444 JwtRules: []*security_beta.JWTRule{ 6445 { 6446 Issuer: "foo.com", 6447 JwksUri: "https://foo.com", 6448 FromHeaders: []*security_beta.JWTHeader{ 6449 { 6450 Name: "", 6451 Prefix: "Bearer ", 6452 }, 6453 }, 6454 }, 6455 }, 6456 }, 6457 valid: false, 6458 }, 6459 { 6460 name: "bad param location", 6461 configName: constants.DefaultAuthenticationPolicyName, 6462 in: &security_beta.RequestAuthentication{ 6463 JwtRules: []*security_beta.JWTRule{ 6464 { 6465 Issuer: "foo.com", 6466 JwksUri: "https://foo.com", 6467 FromParams: []string{""}, 6468 }, 6469 }, 6470 }, 6471 valid: false, 6472 }, 6473 { 6474 name: "good", 6475 configName: constants.DefaultAuthenticationPolicyName, 6476 in: &security_beta.RequestAuthentication{ 6477 JwtRules: []*security_beta.JWTRule{ 6478 { 6479 Issuer: "foo.com", 6480 JwksUri: "https://foo.com", 6481 FromHeaders: []*security_beta.JWTHeader{ 6482 { 6483 Name: "x-foo", 6484 Prefix: "Bearer ", 6485 }, 6486 }, 6487 }, 6488 }, 6489 }, 6490 valid: true, 6491 }, 6492 { 6493 name: "bad cookie location", 6494 configName: constants.DefaultAuthenticationPolicyName, 6495 in: &security_beta.RequestAuthentication{ 6496 JwtRules: []*security_beta.JWTRule{ 6497 { 6498 Issuer: "foo.com", 6499 JwksUri: "https://foo.com", 6500 FromCookies: []string{"", "foo"}, 6501 }, 6502 }, 6503 }, 6504 valid: false, 6505 }, 6506 { 6507 name: "jwks ok", 6508 configName: constants.DefaultAuthenticationPolicyName, 6509 in: &security_beta.RequestAuthentication{ 6510 JwtRules: []*security_beta.JWTRule{ 6511 { 6512 Issuer: "foo.com", 6513 Jwks: "{ \"keys\":[ {\"e\":\"AQAB\",\"kid\":\"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ\",\"kty\":\"RSA\",\"n\":\"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ\"}]}", // nolint: lll 6514 FromHeaders: []*security_beta.JWTHeader{ 6515 { 6516 Name: "x-foo", 6517 Prefix: "Bearer ", 6518 }, 6519 }, 6520 }, 6521 }, 6522 }, 6523 valid: true, 6524 }, 6525 { 6526 name: "jwks error", 6527 configName: constants.DefaultAuthenticationPolicyName, 6528 in: &security_beta.RequestAuthentication{ 6529 JwtRules: []*security_beta.JWTRule{ 6530 { 6531 Issuer: "foo.com", 6532 Jwks: "foo", 6533 FromHeaders: []*security_beta.JWTHeader{ 6534 { 6535 Name: "x-foo", 6536 Prefix: "Bearer ", 6537 }, 6538 }, 6539 }, 6540 }, 6541 }, 6542 valid: false, 6543 }, 6544 { 6545 name: "null outputClaimToHeader", 6546 configName: constants.DefaultAuthenticationPolicyName, 6547 in: &security_beta.RequestAuthentication{ 6548 JwtRules: []*security_beta.JWTRule{ 6549 { 6550 Issuer: "foo.com", 6551 JwksUri: "https://foo.com", 6552 OutputClaimToHeaders: []*security_beta.ClaimToHeader{{}}, 6553 }, 6554 }, 6555 }, 6556 valid: false, 6557 }, 6558 { 6559 name: "null claim value in outputClaimToHeader ", 6560 configName: constants.DefaultAuthenticationPolicyName, 6561 in: &security_beta.RequestAuthentication{ 6562 JwtRules: []*security_beta.JWTRule{ 6563 { 6564 Issuer: "foo.com", 6565 JwksUri: "https://foo.com", 6566 OutputClaimToHeaders: []*security_beta.ClaimToHeader{ 6567 { 6568 Header: "x-jwt-claim", 6569 Claim: "", 6570 }, 6571 }, 6572 }, 6573 }, 6574 }, 6575 valid: false, 6576 }, 6577 { 6578 name: "null header value in outputClaimToHeader ", 6579 configName: constants.DefaultAuthenticationPolicyName, 6580 in: &security_beta.RequestAuthentication{ 6581 JwtRules: []*security_beta.JWTRule{ 6582 { 6583 Issuer: "foo.com", 6584 JwksUri: "https://foo.com", 6585 OutputClaimToHeaders: []*security_beta.ClaimToHeader{ 6586 { 6587 Header: "", 6588 Claim: "sub", 6589 }, 6590 }, 6591 }, 6592 }, 6593 }, 6594 valid: false, 6595 }, 6596 { 6597 name: "invalid header value in outputClaimToHeader ", 6598 configName: constants.DefaultAuthenticationPolicyName, 6599 in: &security_beta.RequestAuthentication{ 6600 JwtRules: []*security_beta.JWTRule{ 6601 { 6602 Issuer: "foo.com", 6603 JwksUri: "https://foo.com", 6604 OutputClaimToHeaders: []*security_beta.ClaimToHeader{ 6605 { 6606 Header: "abc%123", 6607 Claim: "sub", 6608 }, 6609 }, 6610 }, 6611 }, 6612 }, 6613 valid: false, 6614 }, 6615 } 6616 6617 for _, c := range cases { 6618 t.Run(c.name, func(t *testing.T) { 6619 warn, got := ValidateRequestAuthentication(config.Config{ 6620 Meta: config.Meta{ 6621 Name: c.configName, 6622 Namespace: someNamespace, 6623 Annotations: c.annotations, 6624 }, 6625 Spec: c.in, 6626 }) 6627 if (got == nil) != c.valid { 6628 t.Errorf("test: %q got(%v) != want(%v)\n", c.name, got, c.valid) 6629 } 6630 if (warn == nil) == c.warning { 6631 t.Errorf("test: %q warn(%v) != want(%v)\n", c.name, warn, c.warning) 6632 } 6633 }) 6634 } 6635 } 6636 6637 func TestValidatePeerAuthentication(t *testing.T) { 6638 cases := []struct { 6639 name string 6640 configName string 6641 in proto.Message 6642 valid bool 6643 warning bool 6644 }{ 6645 { 6646 name: "empty spec", 6647 configName: constants.DefaultAuthenticationPolicyName, 6648 in: &security_beta.PeerAuthentication{}, 6649 valid: true, 6650 }, 6651 { 6652 name: "empty mtls with selector of empty labels", 6653 configName: constants.DefaultAuthenticationPolicyName, 6654 in: &security_beta.PeerAuthentication{ 6655 Selector: &api.WorkloadSelector{}, 6656 }, 6657 valid: true, 6658 warning: true, 6659 }, 6660 { 6661 name: "empty spec with non default name", 6662 configName: someName, 6663 in: &security_beta.PeerAuthentication{}, 6664 valid: true, 6665 }, 6666 { 6667 name: "default name with non empty selector", 6668 configName: constants.DefaultAuthenticationPolicyName, 6669 in: &security_beta.PeerAuthentication{ 6670 Selector: &api.WorkloadSelector{ 6671 MatchLabels: map[string]string{ 6672 "app": "httpbin", 6673 }, 6674 }, 6675 }, 6676 valid: true, 6677 }, 6678 { 6679 name: "empty port level mtls", 6680 configName: "foo", 6681 in: &security_beta.PeerAuthentication{ 6682 Selector: &api.WorkloadSelector{ 6683 MatchLabels: map[string]string{ 6684 "app": "httpbin", 6685 }, 6686 }, 6687 PortLevelMtls: map[uint32]*security_beta.PeerAuthentication_MutualTLS{}, 6688 }, 6689 valid: false, 6690 }, 6691 { 6692 name: "empty selector with port level mtls", 6693 configName: constants.DefaultAuthenticationPolicyName, 6694 in: &security_beta.PeerAuthentication{ 6695 PortLevelMtls: map[uint32]*security_beta.PeerAuthentication_MutualTLS{ 6696 8080: { 6697 Mode: security_beta.PeerAuthentication_MutualTLS_UNSET, 6698 }, 6699 }, 6700 }, 6701 valid: false, 6702 }, 6703 { 6704 name: "port 0", 6705 configName: "foo", 6706 in: &security_beta.PeerAuthentication{ 6707 PortLevelMtls: map[uint32]*security_beta.PeerAuthentication_MutualTLS{ 6708 0: { 6709 Mode: security_beta.PeerAuthentication_MutualTLS_UNSET, 6710 }, 6711 }, 6712 }, 6713 valid: false, 6714 }, 6715 { 6716 name: "unset mode", 6717 configName: constants.DefaultAuthenticationPolicyName, 6718 in: &security_beta.PeerAuthentication{ 6719 Mtls: &security_beta.PeerAuthentication_MutualTLS{ 6720 Mode: security_beta.PeerAuthentication_MutualTLS_UNSET, 6721 }, 6722 }, 6723 valid: true, 6724 }, 6725 { 6726 name: "port level", 6727 configName: "port-level", 6728 in: &security_beta.PeerAuthentication{ 6729 Selector: &api.WorkloadSelector{ 6730 MatchLabels: map[string]string{ 6731 "app": "httpbin", 6732 }, 6733 }, 6734 PortLevelMtls: map[uint32]*security_beta.PeerAuthentication_MutualTLS{ 6735 8080: { 6736 Mode: security_beta.PeerAuthentication_MutualTLS_UNSET, 6737 }, 6738 }, 6739 }, 6740 valid: true, 6741 }, 6742 } 6743 6744 for _, c := range cases { 6745 t.Run(c.name, func(t *testing.T) { 6746 warn, got := ValidatePeerAuthentication(config.Config{ 6747 Meta: config.Meta{ 6748 Name: c.configName, 6749 Namespace: someNamespace, 6750 }, 6751 Spec: c.in, 6752 }) 6753 if (got == nil) != c.valid { 6754 t.Errorf("got(%v) != want(%v)\n", got, c.valid) 6755 } 6756 if (warn == nil) == c.warning { 6757 t.Errorf("warn(%v) != want(%v)\n", warn, c.warning) 6758 } 6759 }) 6760 } 6761 } 6762 6763 func Test_validateExportTo(t *testing.T) { 6764 tests := []struct { 6765 name string 6766 namespace string 6767 exportTo []string 6768 isDestinationRuleWithSelector bool 6769 isServiceEntry bool 6770 wantErr bool 6771 }{ 6772 { 6773 name: "empty exportTo is okay", 6774 namespace: "ns5", 6775 wantErr: false, 6776 }, 6777 { 6778 name: "* is allowed", 6779 namespace: "ns5", 6780 exportTo: []string{"*"}, 6781 wantErr: false, 6782 }, 6783 { 6784 name: ". and ns1 are allowed", 6785 namespace: "ns5", 6786 exportTo: []string{".", "ns1"}, 6787 wantErr: false, 6788 }, 6789 { 6790 name: "bunch of namespaces in exportTo is okay", 6791 namespace: "ns5", 6792 exportTo: []string{"ns1", "ns2", "ns5"}, 6793 wantErr: false, 6794 }, 6795 { 6796 name: "~ is allowed for service entry configs", 6797 namespace: "ns5", 6798 exportTo: []string{"~"}, 6799 isServiceEntry: true, 6800 wantErr: false, 6801 }, 6802 { 6803 name: "~ not allowed for non service entry configs", 6804 namespace: "ns5", 6805 exportTo: []string{"~", "ns1"}, 6806 wantErr: true, 6807 }, 6808 { 6809 name: ". and * together are not allowed", 6810 namespace: "ns5", 6811 exportTo: []string{".", "*"}, 6812 wantErr: true, 6813 }, 6814 { 6815 name: "* and ns1 together are not allowed", 6816 namespace: "ns5", 6817 exportTo: []string{"*", "ns1"}, 6818 wantErr: true, 6819 }, 6820 { 6821 name: ". and same namespace in exportTo is not okay", 6822 namespace: "ns5", 6823 exportTo: []string{".", "ns5"}, 6824 wantErr: true, 6825 }, 6826 { 6827 name: "duplicate namespaces in exportTo is not okay", 6828 namespace: "ns5", 6829 exportTo: []string{"ns1", "ns2", "ns1"}, 6830 wantErr: true, 6831 }, 6832 { 6833 name: "duplicate none in service entry exportTo is not okay", 6834 namespace: "ns5", 6835 exportTo: []string{"~", "~", "ns1"}, 6836 isServiceEntry: true, 6837 wantErr: true, 6838 }, 6839 { 6840 name: "invalid namespace names are not okay", 6841 namespace: "ns5", 6842 exportTo: []string{"ns1_"}, 6843 wantErr: true, 6844 }, 6845 { 6846 name: "none and other namespaces cannot be combined in service entry exportTo", 6847 namespace: "ns5", 6848 exportTo: []string{"~", "ns1"}, 6849 isServiceEntry: true, 6850 wantErr: true, 6851 }, 6852 { 6853 name: "destination rule with workloadselector cannot have exportTo (*)", 6854 namespace: "ns5", 6855 exportTo: []string{"*"}, 6856 isServiceEntry: false, 6857 isDestinationRuleWithSelector: true, 6858 wantErr: true, 6859 }, 6860 { 6861 name: "destination rule with workloadselector can have only exportTo (.)", 6862 namespace: "ns5", 6863 exportTo: []string{"."}, 6864 isServiceEntry: false, 6865 isDestinationRuleWithSelector: true, 6866 wantErr: false, 6867 }, 6868 { 6869 name: "destination rule with workloadselector cannot have another ns in exportTo (.)", 6870 namespace: "ns5", 6871 exportTo: []string{"somens"}, 6872 isServiceEntry: false, 6873 isDestinationRuleWithSelector: true, 6874 wantErr: true, 6875 }, 6876 { 6877 name: "destination rule with workloadselector cannot have another ns in addition to own ns in exportTo (.)", 6878 namespace: "ns5", 6879 exportTo: []string{".", "somens"}, 6880 isServiceEntry: false, 6881 isDestinationRuleWithSelector: true, 6882 wantErr: true, 6883 }, 6884 } 6885 for _, tt := range tests { 6886 t.Run(tt.name, func(t *testing.T) { 6887 if err := validateExportTo(tt.namespace, tt.exportTo, tt.isServiceEntry, tt.isDestinationRuleWithSelector); (err != nil) != tt.wantErr { 6888 t.Errorf("validateExportTo() error = %v, wantErr %v", err, tt.wantErr) 6889 } 6890 }) 6891 } 6892 } 6893 6894 func TestValidateTelemetry(t *testing.T) { 6895 tests := []struct { 6896 name string 6897 in proto.Message 6898 err string 6899 warning string 6900 }{ 6901 {"empty", &telemetry.Telemetry{}, "", ""}, 6902 {"invalid message", &networking.Server{}, "cannot cast", ""}, 6903 { 6904 "multiple providers", 6905 &telemetry.Telemetry{ 6906 Tracing: []*telemetry.Tracing{{ 6907 Providers: []*telemetry.ProviderRef{ 6908 {Name: "a"}, 6909 {Name: "b"}, 6910 }, 6911 }}, 6912 }, 6913 "", "multiple providers", 6914 }, 6915 { 6916 "multiple tracers", 6917 &telemetry.Telemetry{ 6918 Tracing: []*telemetry.Tracing{{}, {}}, 6919 }, 6920 "", "multiple tracing", 6921 }, 6922 { 6923 "bad randomSamplingPercentage", 6924 &telemetry.Telemetry{ 6925 Tracing: []*telemetry.Tracing{{ 6926 RandomSamplingPercentage: &wrapperspb.DoubleValue{Value: 101}, 6927 }}, 6928 }, 6929 "randomSamplingPercentage", "", 6930 }, 6931 { 6932 "tracing with a good custom tag", 6933 &telemetry.Telemetry{ 6934 Tracing: []*telemetry.Tracing{{ 6935 CustomTags: map[string]*telemetry.Tracing_CustomTag{ 6936 "clusterID": { 6937 Type: &telemetry.Tracing_CustomTag_Environment{ 6938 Environment: &telemetry.Tracing_Environment{ 6939 Name: "FOO", 6940 }, 6941 }, 6942 }, 6943 }, 6944 }}, 6945 }, 6946 "", "", 6947 }, 6948 { 6949 "tracing with a nil custom tag", 6950 &telemetry.Telemetry{ 6951 Tracing: []*telemetry.Tracing{{ 6952 CustomTags: map[string]*telemetry.Tracing_CustomTag{ 6953 "clusterID": nil, 6954 }, 6955 }}, 6956 }, 6957 "tag 'clusterID' may not have a nil value", "", 6958 }, 6959 { 6960 "bad metrics operation", 6961 &telemetry.Telemetry{ 6962 Metrics: []*telemetry.Metrics{{ 6963 Overrides: []*telemetry.MetricsOverrides{ 6964 { 6965 TagOverrides: map[string]*telemetry.MetricsOverrides_TagOverride{ 6966 "my-tag": { 6967 Operation: telemetry.MetricsOverrides_TagOverride_UPSERT, 6968 Value: "", 6969 }, 6970 }, 6971 }, 6972 }, 6973 }}, 6974 }, 6975 "must be set when operation is UPSERT", "", 6976 }, 6977 { 6978 "good metrics operation", 6979 &telemetry.Telemetry{ 6980 Metrics: []*telemetry.Metrics{{ 6981 Overrides: []*telemetry.MetricsOverrides{ 6982 { 6983 TagOverrides: map[string]*telemetry.MetricsOverrides_TagOverride{ 6984 "my-tag": { 6985 Operation: telemetry.MetricsOverrides_TagOverride_UPSERT, 6986 Value: "some-cel-expression", 6987 }, 6988 }, 6989 }, 6990 }, 6991 }}, 6992 }, 6993 "", "", 6994 }, 6995 { 6996 "multi-accessloggings", 6997 &telemetry.Telemetry{ 6998 AccessLogging: []*telemetry.AccessLogging{ 6999 { 7000 Providers: []*telemetry.ProviderRef{ 7001 { 7002 Name: "envoy", 7003 }, 7004 }, 7005 }, 7006 { 7007 Providers: []*telemetry.ProviderRef{ 7008 { 7009 Name: "otel", 7010 }, 7011 }, 7012 }, 7013 }, 7014 }, 7015 "", "", 7016 }, 7017 { 7018 "multi-accesslogging-providers", 7019 &telemetry.Telemetry{ 7020 AccessLogging: []*telemetry.AccessLogging{ 7021 { 7022 Providers: []*telemetry.ProviderRef{ 7023 { 7024 Name: "envoy", 7025 }, 7026 { 7027 Name: "otel", 7028 }, 7029 }, 7030 }, 7031 }, 7032 }, 7033 "", "", 7034 }, 7035 { 7036 "good targetRef", 7037 &telemetry.Telemetry{ 7038 Tracing: []*telemetry.Tracing{{ 7039 CustomTags: map[string]*telemetry.Tracing_CustomTag{ 7040 "clusterID": { 7041 Type: &telemetry.Tracing_CustomTag_Environment{ 7042 Environment: &telemetry.Tracing_Environment{ 7043 Name: "FOO", 7044 }, 7045 }, 7046 }, 7047 }, 7048 }}, 7049 TargetRef: &api.PolicyTargetReference{ 7050 Group: gvk.KubernetesGateway.Group, 7051 Kind: gvk.KubernetesGateway.Kind, 7052 Name: "foo", 7053 }, 7054 }, 7055 "", "", 7056 }, 7057 { 7058 "bad targetRef - empty name", 7059 &telemetry.Telemetry{ 7060 Tracing: []*telemetry.Tracing{{ 7061 CustomTags: map[string]*telemetry.Tracing_CustomTag{ 7062 "clusterID": { 7063 Type: &telemetry.Tracing_CustomTag_Environment{ 7064 Environment: &telemetry.Tracing_Environment{ 7065 Name: "FOO", 7066 }, 7067 }, 7068 }, 7069 }, 7070 }}, 7071 TargetRef: &api.PolicyTargetReference{ 7072 Group: gvk.KubernetesGateway.Group, 7073 Kind: gvk.KubernetesGateway.Kind, 7074 }, 7075 }, 7076 "targetRef name must be set", "", 7077 }, 7078 { 7079 "bad targetRef - non-empty namespace", 7080 &telemetry.Telemetry{ 7081 Tracing: []*telemetry.Tracing{{ 7082 CustomTags: map[string]*telemetry.Tracing_CustomTag{ 7083 "clusterID": { 7084 Type: &telemetry.Tracing_CustomTag_Environment{ 7085 Environment: &telemetry.Tracing_Environment{ 7086 Name: "FOO", 7087 }, 7088 }, 7089 }, 7090 }, 7091 }}, 7092 TargetRef: &api.PolicyTargetReference{ 7093 Group: gvk.KubernetesGateway.Group, 7094 Kind: gvk.KubernetesGateway.Kind, 7095 Name: "foo", 7096 Namespace: "bar", 7097 }, 7098 }, 7099 "targetRef namespace must not be set", "", 7100 }, 7101 { 7102 "bad targetRef - wrong group", 7103 &telemetry.Telemetry{ 7104 Tracing: []*telemetry.Tracing{{ 7105 CustomTags: map[string]*telemetry.Tracing_CustomTag{ 7106 "clusterID": { 7107 Type: &telemetry.Tracing_CustomTag_Environment{ 7108 Environment: &telemetry.Tracing_Environment{ 7109 Name: "FOO", 7110 }, 7111 }, 7112 }, 7113 }, 7114 }}, 7115 TargetRef: &api.PolicyTargetReference{ 7116 Group: "wrong-group", 7117 Kind: gvk.KubernetesGateway.Kind, 7118 Name: "foo", 7119 }, 7120 }, 7121 fmt.Sprintf("targetRef must be to one of %v but was %s/%s", 7122 allowedTargetRefs, "wrong-group", gvk.KubernetesGateway.Kind), "", 7123 }, 7124 { 7125 "bad targetRef - wrong kind", 7126 &telemetry.Telemetry{ 7127 Tracing: []*telemetry.Tracing{{ 7128 CustomTags: map[string]*telemetry.Tracing_CustomTag{ 7129 "clusterID": { 7130 Type: &telemetry.Tracing_CustomTag_Environment{ 7131 Environment: &telemetry.Tracing_Environment{ 7132 Name: "FOO", 7133 }, 7134 }, 7135 }, 7136 }, 7137 }}, 7138 TargetRef: &api.PolicyTargetReference{ 7139 Group: gvk.KubernetesGateway.Group, 7140 Kind: "wrong-kind", 7141 Name: "foo", 7142 }, 7143 }, 7144 fmt.Sprintf("targetRef must be to one of %v but was %s/%s", 7145 allowedTargetRefs, gvk.KubernetesGateway.Group, "wrong-kind"), "", 7146 }, 7147 { 7148 "targetRef and selector cannot both be set", 7149 &telemetry.Telemetry{ 7150 Tracing: []*telemetry.Tracing{{ 7151 CustomTags: map[string]*telemetry.Tracing_CustomTag{ 7152 "clusterID": { 7153 Type: &telemetry.Tracing_CustomTag_Environment{ 7154 Environment: &telemetry.Tracing_Environment{ 7155 Name: "FOO", 7156 }, 7157 }, 7158 }, 7159 }, 7160 }}, 7161 TargetRef: &api.PolicyTargetReference{ 7162 Group: gvk.KubernetesGateway.Group, 7163 Kind: "wrong-kind", 7164 Name: "foo", 7165 }, 7166 Selector: &api.WorkloadSelector{ 7167 MatchLabels: map[string]string{ 7168 "app": "httpbin", 7169 }, 7170 }, 7171 }, 7172 "only one of targetRefs or workloadSelector can be set", "", 7173 }, 7174 } 7175 for _, tt := range tests { 7176 t.Run(tt.name, func(t *testing.T) { 7177 warn, err := ValidateTelemetry(config.Config{ 7178 Meta: config.Meta{ 7179 Name: someName, 7180 Namespace: someNamespace, 7181 }, 7182 Spec: tt.in, 7183 }) 7184 checkValidationMessage(t, warn, err, tt.warning, tt.err) 7185 }) 7186 } 7187 } 7188 7189 func TestValidateProxyConfig(t *testing.T) { 7190 tests := []struct { 7191 name string 7192 in proto.Message 7193 out string 7194 warning string 7195 }{ 7196 {"empty", &networkingv1beta1.ProxyConfig{}, "", ""}, 7197 {name: "invalid concurrency", in: &networkingv1beta1.ProxyConfig{ 7198 Concurrency: &wrapperspb.Int32Value{Value: -1}, 7199 }, out: "concurrency must be greater than or equal to 0"}, 7200 } 7201 for _, tt := range tests { 7202 t.Run(tt.name, func(t *testing.T) { 7203 warn, err := ValidateProxyConfig(config.Config{ 7204 Meta: config.Meta{ 7205 Name: someName, 7206 Namespace: someNamespace, 7207 }, 7208 Spec: tt.in, 7209 }) 7210 checkValidationMessage(t, warn, err, tt.warning, tt.out) 7211 }) 7212 } 7213 } 7214 7215 func TestValidateTelemetryFilter(t *testing.T) { 7216 cases := []struct { 7217 filter *telemetry.AccessLogging_Filter 7218 valid bool 7219 }{ 7220 { 7221 filter: &telemetry.AccessLogging_Filter{ 7222 Expression: "response.code >= 400", 7223 }, 7224 valid: true, 7225 }, 7226 { 7227 filter: &telemetry.AccessLogging_Filter{ 7228 Expression: "connection.mtls && request.url_path.contains('v1beta3')", 7229 }, 7230 valid: true, 7231 }, 7232 { 7233 filter: &telemetry.AccessLogging_Filter{ 7234 // TODO: find a better way to verify this 7235 // this should be an invalid expression 7236 Expression: "response.code", 7237 }, 7238 valid: true, 7239 }, 7240 { 7241 filter: &telemetry.AccessLogging_Filter{ 7242 Expression: ")++++", 7243 }, 7244 valid: false, 7245 }, 7246 } 7247 7248 for _, tc := range cases { 7249 t.Run("", func(t *testing.T) { 7250 err := validateTelemetryFilter(tc.filter) 7251 errFound := err != nil 7252 if tc.valid && errFound { 7253 t.Errorf("validateTelemetryFilter(%v) produced unexpected error: %v", tc.filter, err) 7254 } 7255 if !tc.valid && !errFound { 7256 t.Errorf("validateTelemetryFilter(%v) did not produce expected error", tc.filter) 7257 } 7258 }) 7259 } 7260 } 7261 7262 func TestValidateWasmPlugin(t *testing.T) { 7263 tests := []struct { 7264 name string 7265 in proto.Message 7266 out string 7267 warning string 7268 }{ 7269 {"empty", &extensions.WasmPlugin{}, "url field needs to be set", ""}, 7270 {"invalid message", &networking.Server{}, "cannot cast", ""}, 7271 { 7272 "wrong scheme", 7273 &extensions.WasmPlugin{ 7274 Url: "ftp://test.com/test", 7275 }, 7276 "unsupported scheme", "", 7277 }, 7278 { 7279 "valid http", 7280 &extensions.WasmPlugin{ 7281 Url: "http://test.com/test", 7282 }, 7283 "", "", 7284 }, 7285 { 7286 "valid http w/ sha", 7287 &extensions.WasmPlugin{ 7288 Url: "http://test.com/test", 7289 Sha256: "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b", 7290 }, 7291 "", "", 7292 }, 7293 { 7294 "short sha", 7295 &extensions.WasmPlugin{ 7296 Url: "http://test.com/test", 7297 Sha256: "01ba47", 7298 }, 7299 "sha256 field must be 64 characters long", "", 7300 }, 7301 { 7302 "invalid sha", 7303 &extensions.WasmPlugin{ 7304 Url: "http://test.com/test", 7305 Sha256: "test", 7306 }, 7307 "sha256 field must be 64 characters long", "", 7308 }, 7309 { 7310 "invalid sha characters", 7311 &extensions.WasmPlugin{ 7312 Url: "http://test.com/test", 7313 Sha256: "01Ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b", 7314 }, 7315 "sha256 field must match [a-f0-9]{64} pattern", "", 7316 }, 7317 { 7318 "valid oci", 7319 &extensions.WasmPlugin{ 7320 Url: "oci://test.com/test", 7321 }, 7322 "", "", 7323 }, 7324 { 7325 "valid oci no scheme", 7326 &extensions.WasmPlugin{ 7327 Url: "test.com/test", 7328 }, 7329 "", "", 7330 }, 7331 { 7332 "invalid vm config - invalid env name", 7333 &extensions.WasmPlugin{ 7334 Url: "test.com/test", 7335 VmConfig: &extensions.VmConfig{ 7336 Env: []*extensions.EnvVar{ 7337 { 7338 Name: "", 7339 ValueFrom: extensions.EnvValueSource_HOST, 7340 }, 7341 }, 7342 }, 7343 }, 7344 "spec.vmConfig.env invalid", "", 7345 }, 7346 { 7347 "invalid vm config - duplicate env", 7348 &extensions.WasmPlugin{ 7349 Url: "test.com/test", 7350 VmConfig: &extensions.VmConfig{ 7351 Env: []*extensions.EnvVar{ 7352 { 7353 Name: "ENV1", 7354 Value: "VAL1", 7355 }, 7356 { 7357 Name: "ENV1", 7358 Value: "VAL1", 7359 }, 7360 }, 7361 }, 7362 }, 7363 "duplicate env", "", 7364 }, 7365 { 7366 "target-ref-good", 7367 &extensions.WasmPlugin{ 7368 Url: "http://test.com/test", 7369 TargetRef: &api.PolicyTargetReference{ 7370 Group: gvk.KubernetesGateway.Group, 7371 Kind: gvk.KubernetesGateway.Kind, 7372 Name: "foo", 7373 }, 7374 }, 7375 "", "", 7376 }, 7377 { 7378 "target-ref-good-service", 7379 &extensions.WasmPlugin{ 7380 Url: "http://test.com/test", 7381 TargetRef: &api.PolicyTargetReference{ 7382 Group: gvk.Service.Group, 7383 Kind: gvk.Service.Kind, 7384 Name: "foo", 7385 }, 7386 }, 7387 "", "", 7388 }, 7389 { 7390 "target-ref-non-empty-namespace", 7391 &extensions.WasmPlugin{ 7392 Url: "http://test.com/test", 7393 TargetRef: &api.PolicyTargetReference{ 7394 Group: gvk.KubernetesGateway.Group, 7395 Kind: gvk.KubernetesGateway.Kind, 7396 Name: "foo", 7397 Namespace: "bar", 7398 }, 7399 }, 7400 "targetRef namespace must not be set", "", 7401 }, 7402 { 7403 "target-ref-empty-name", 7404 &extensions.WasmPlugin{ 7405 Url: "http://test.com/test", 7406 TargetRef: &api.PolicyTargetReference{ 7407 Group: gvk.KubernetesGateway.Group, 7408 Kind: gvk.KubernetesGateway.Kind, 7409 }, 7410 }, 7411 "targetRef name must be set", "", 7412 }, 7413 { 7414 "target-ref-wrong-group", 7415 &extensions.WasmPlugin{ 7416 Url: "http://test.com/test", 7417 TargetRef: &api.PolicyTargetReference{ 7418 Group: "wrong-group", 7419 Kind: gvk.KubernetesGateway.Kind, 7420 Name: "foo", 7421 }, 7422 }, 7423 fmt.Sprintf("targetRef must be to one of %v but was %s/%s", 7424 allowedTargetRefs, "wrong-group", gvk.KubernetesGateway.Kind), "", 7425 }, 7426 { 7427 "target-ref-wrong-kind", 7428 &extensions.WasmPlugin{ 7429 Url: "http://test.com/test", 7430 TargetRef: &api.PolicyTargetReference{ 7431 Group: gvk.KubernetesGateway.Group, 7432 Kind: "wrong-kind", 7433 Name: "foo", 7434 }, 7435 }, 7436 fmt.Sprintf("targetRef must be to one of %v but was %s/%s", 7437 allowedTargetRefs, gvk.KubernetesGateway.Group, "wrong-kind"), "", 7438 }, 7439 { 7440 "target-ref-and-selector-cannot-both-be-set", 7441 &extensions.WasmPlugin{ 7442 Url: "http://test.com/test", 7443 TargetRef: &api.PolicyTargetReference{ 7444 Group: gvk.KubernetesGateway.Group, 7445 Kind: gvk.KubernetesGateway.Kind, 7446 Name: "foo", 7447 }, 7448 Selector: &api.WorkloadSelector{ 7449 MatchLabels: map[string]string{ 7450 "app": "httpbin", 7451 }, 7452 }, 7453 }, 7454 "only one of targetRefs or workloadSelector can be set", "", 7455 }, 7456 } 7457 for _, tt := range tests { 7458 t.Run(tt.name, func(t *testing.T) { 7459 warn, err := ValidateWasmPlugin(config.Config{ 7460 Meta: config.Meta{ 7461 Name: someName, 7462 Namespace: someNamespace, 7463 }, 7464 Spec: tt.in, 7465 }) 7466 checkValidationMessage(t, warn, err, tt.warning, tt.out) 7467 }) 7468 } 7469 } 7470 7471 func TestValidateHTTPHeaderValue(t *testing.T) { 7472 cases := []struct { 7473 input string 7474 expected error 7475 }{ 7476 { 7477 input: "foo", 7478 expected: nil, 7479 }, 7480 { 7481 input: "%HOSTNAME%", 7482 expected: nil, 7483 }, 7484 { 7485 input: "100%%", 7486 expected: nil, 7487 }, 7488 { 7489 input: "prefix %HOSTNAME% suffix", 7490 expected: nil, 7491 }, 7492 { 7493 input: "%DOWNSTREAM_PEER_CERT_V_END(%b %d %H:%M:%S %Y %Z)%", 7494 expected: nil, 7495 }, 7496 { 7497 input: "%DYNAMIC_METADATA(com.test.my_filter)%", 7498 }, 7499 { 7500 input: "%START_TIME%%", 7501 expected: errors.New("header value configuration %START_TIME%% is invalid"), 7502 }, 7503 { 7504 input: "abc%123", 7505 expected: errors.New("header value configuration abc%123 is invalid"), 7506 }, 7507 } 7508 7509 for _, tc := range cases { 7510 t.Run(tc.input, func(t *testing.T) { 7511 got := ValidateHTTPHeaderValue(tc.input) 7512 if tc.expected == nil { 7513 assert.NoError(t, got) 7514 } else { 7515 assert.Error(t, got) 7516 } 7517 }) 7518 } 7519 }