github.com/cilium/cilium@v1.16.2/pkg/policy/api/rule_validation_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package api 5 6 import ( 7 "fmt" 8 "testing" 9 10 "github.com/cilium/proxy/pkg/policy/api/kafka" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 14 "k8s.io/apimachinery/pkg/util/intstr" 15 16 slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" 17 "github.com/cilium/cilium/pkg/labels" 18 "github.com/cilium/cilium/pkg/option" 19 ) 20 21 // This test ensures that only PortRules which have L7Rules associated with them 22 // are invalid if any protocol except TCP is used as a protocol for any port 23 // in the list of PortProtocol supplied to the rule. 24 func TestL7RulesWithNonTCPProtocols(t *testing.T) { 25 setUpSuite(t) 26 27 // Rule is valid because only ProtoTCP is allowed for L7 rules (except with ToFQDNs, below). 28 validPortRule := Rule{ 29 EndpointSelector: WildcardEndpointSelector, 30 Ingress: []IngressRule{ 31 { 32 IngressCommonRule: IngressCommonRule{ 33 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 34 }, 35 ToPorts: []PortRule{{ 36 Ports: []PortProtocol{ 37 {Port: "80", Protocol: ProtoTCP}, 38 {Port: "81", Protocol: ProtoTCP}, 39 }, 40 Rules: &L7Rules{ 41 HTTP: []PortRuleHTTP{ 42 {Method: "GET", Path: "/"}, 43 }, 44 }, 45 }}, 46 }, 47 }, 48 } 49 50 err := validPortRule.Sanitize() 51 require.Nil(t, err) 52 53 // Rule is invalid because no port is specified for DNS proxy rule. 54 validPortRule = Rule{ 55 EndpointSelector: WildcardEndpointSelector, 56 Egress: []EgressRule{ 57 { 58 EgressCommonRule: EgressCommonRule{ 59 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 60 }, 61 ToPorts: []PortRule{{ 62 Rules: &L7Rules{ 63 DNS: []PortRuleDNS{ 64 {MatchName: "domain.com"}, 65 }, 66 }, 67 }}, 68 }, 69 }, 70 } 71 72 err = validPortRule.Sanitize() 73 require.Error(t, err, "Port 53 must be specified for DNS rules") 74 75 // Rule is valid because all protocols are allowed for L7 rules with ToFQDNs. 76 validPortRule = Rule{ 77 EndpointSelector: WildcardEndpointSelector, 78 Egress: []EgressRule{ 79 { 80 EgressCommonRule: EgressCommonRule{ 81 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 82 }, 83 ToPorts: []PortRule{{ 84 Ports: []PortProtocol{ 85 {Port: "53", Protocol: ProtoTCP}, 86 {Port: "53", Protocol: ProtoUDP}, 87 }, 88 Rules: &L7Rules{ 89 DNS: []PortRuleDNS{ 90 {MatchName: "domain.com"}, 91 }, 92 }, 93 }}, 94 }, 95 }, 96 } 97 98 err = validPortRule.Sanitize() 99 require.NoError(t, err, "Saw an error for a L7 rule with DNS rules. This should be allowed.") 100 101 validSCTPRule := Rule{ 102 EndpointSelector: WildcardEndpointSelector, 103 Egress: []EgressRule{ 104 { 105 EgressCommonRule: EgressCommonRule{ 106 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 107 }, 108 ToPorts: []PortRule{{ 109 Ports: []PortProtocol{ 110 {Port: "4000", Protocol: ProtoSCTP}, 111 }, 112 }}, 113 }, 114 }, 115 } 116 117 err = validSCTPRule.Sanitize() 118 require.NoError(t, err, "Saw an error for an SCTP rule.") 119 120 // Rule is invalid because only ProtoTCP is allowed for L7 rules (except with DNS, below). 121 invalidPortRule := Rule{ 122 EndpointSelector: WildcardEndpointSelector, 123 Ingress: []IngressRule{ 124 { 125 IngressCommonRule: IngressCommonRule{ 126 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 127 }, 128 ToPorts: []PortRule{{ 129 Ports: []PortProtocol{ 130 {Port: "80", Protocol: ProtoUDP}, 131 }, 132 Rules: &L7Rules{ 133 HTTP: []PortRuleHTTP{ 134 {Method: "GET", Path: "/"}, 135 }, 136 }, 137 }}, 138 }, 139 }, 140 } 141 142 err = invalidPortRule.Sanitize() 143 require.ErrorContains(t, err, "L7 rules can only apply to TCP (not UDP) except for DNS rules") 144 145 // Rule is invalid because DNS proxy rules are not allowed on ingress rules. 146 invalidPortRule = Rule{ 147 EndpointSelector: WildcardEndpointSelector, 148 Ingress: []IngressRule{ 149 { 150 IngressCommonRule: IngressCommonRule{ 151 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 152 }, 153 ToPorts: []PortRule{{ 154 Ports: []PortProtocol{ 155 {Port: "53", Protocol: ProtoAny}, 156 }, 157 Rules: &L7Rules{ 158 DNS: []PortRuleDNS{ 159 {MatchName: "domain.com"}, 160 }, 161 }, 162 }}, 163 }, 164 }, 165 } 166 167 err = invalidPortRule.Sanitize() 168 require.Error(t, err, "DNS rule should not be allowed on ingress") 169 170 // Rule is invalid because only ProtoTCP is allowed for L7 rules (except with DNS, below). 171 invalidPortRule = Rule{ 172 EndpointSelector: WildcardEndpointSelector, 173 Ingress: []IngressRule{ 174 { 175 IngressCommonRule: IngressCommonRule{ 176 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 177 }, 178 ToPorts: []PortRule{{ 179 Ports: []PortProtocol{ 180 {Port: "80", Protocol: ProtoAny}, 181 }, 182 Rules: &L7Rules{ 183 HTTP: []PortRuleHTTP{ 184 {Method: "GET", Path: "/"}, 185 }, 186 }, 187 }}, 188 }, 189 }, 190 } 191 192 err = invalidPortRule.Sanitize() 193 require.NotNil(t, err) 194 require.Equal(t, "L7 rules can only apply to TCP (not ANY) except for DNS rules", err.Error()) 195 196 // Rule is invalid because only ProtoTCP is allowed for L7 rules (except with DNS, below). 197 invalidPortRule = Rule{ 198 EndpointSelector: WildcardEndpointSelector, 199 Ingress: []IngressRule{ 200 { 201 IngressCommonRule: IngressCommonRule{ 202 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 203 }, 204 ToPorts: []PortRule{{ 205 Ports: []PortProtocol{ 206 {Port: "80", Protocol: ProtoTCP}, 207 {Port: "12345", Protocol: ProtoUDP}, 208 }, 209 Rules: &L7Rules{ 210 HTTP: []PortRuleHTTP{ 211 {Method: "GET", Path: "/"}, 212 }, 213 }, 214 }}, 215 }, 216 }, 217 } 218 219 err = invalidPortRule.Sanitize() 220 require.NotNil(t, err) 221 require.Equal(t, "L7 rules can only apply to TCP (not UDP) except for DNS rules", err.Error()) 222 223 // Same as previous rule, but ensure ordering doesn't affect validation. 224 invalidPortRule = Rule{ 225 EndpointSelector: WildcardEndpointSelector, 226 Ingress: []IngressRule{ 227 { 228 IngressCommonRule: IngressCommonRule{ 229 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 230 }, 231 ToPorts: []PortRule{{ 232 Ports: []PortProtocol{ 233 {Port: "80", Protocol: ProtoUDP}, 234 {Port: "12345", Protocol: ProtoTCP}, 235 }, 236 Rules: &L7Rules{ 237 HTTP: []PortRuleHTTP{ 238 {Method: "GET", Path: "/"}, 239 }, 240 }, 241 }}, 242 }, 243 }, 244 } 245 246 err = invalidPortRule.Sanitize() 247 require.NotNil(t, err) 248 require.Equal(t, "L7 rules can only apply to TCP (not UDP) except for DNS rules", err.Error()) 249 250 // Rule is valid because ServerNames are allowed for SNI enforcement. 251 validPortRule = Rule{ 252 EndpointSelector: WildcardEndpointSelector, 253 Egress: []EgressRule{ 254 { 255 EgressCommonRule: EgressCommonRule{ 256 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 257 }, 258 ToPorts: []PortRule{{ 259 Ports: []PortProtocol{ 260 {Port: "443", Protocol: ProtoTCP}, 261 }, 262 ServerNames: []string{"foo.bar.com", "bar.foo.com"}, 263 }}, 264 }, 265 }, 266 } 267 err = validPortRule.Sanitize() 268 require.Nil(t, err) 269 270 // Rule is invalid because empty ServerNames are not allowed 271 invalidPortRule = Rule{ 272 EndpointSelector: WildcardEndpointSelector, 273 Egress: []EgressRule{ 274 { 275 EgressCommonRule: EgressCommonRule{ 276 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 277 }, 278 ToPorts: []PortRule{{ 279 Ports: []PortProtocol{ 280 {Port: "443", Protocol: ProtoTCP}, 281 }, 282 ServerNames: []string{""}, 283 }}, 284 }, 285 }, 286 } 287 err = invalidPortRule.Sanitize() 288 require.NotNil(t, err) 289 require.Equal(t, "Empty server name is not allowed", err.Error()) 290 291 // Rule is invalid because ServerNames with L7 rules are not allowed without TLS termination. 292 invalidPortRule = Rule{ 293 EndpointSelector: WildcardEndpointSelector, 294 Egress: []EgressRule{ 295 { 296 EgressCommonRule: EgressCommonRule{ 297 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 298 }, 299 ToPorts: []PortRule{{ 300 Ports: []PortProtocol{ 301 {Port: "443", Protocol: ProtoTCP}, 302 }, 303 ServerNames: []string{"foo.bar.com", "bar.foo.com"}, 304 Rules: &L7Rules{ 305 HTTP: []PortRuleHTTP{ 306 {Method: "GET", Path: "/"}, 307 }, 308 }, 309 }}, 310 }, 311 }, 312 } 313 err = invalidPortRule.Sanitize() 314 require.NotNil(t, err) 315 require.Equal(t, "ServerNames are not allowed with L7 rules without TLS termination", err.Error()) 316 317 // Rule is valid because ServerNames with L7 rules are allowed with TLS termination. 318 validPortRule = Rule{ 319 EndpointSelector: WildcardEndpointSelector, 320 Egress: []EgressRule{ 321 { 322 EgressCommonRule: EgressCommonRule{ 323 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 324 }, 325 ToPorts: []PortRule{{ 326 Ports: []PortProtocol{ 327 {Port: "443", Protocol: ProtoTCP}, 328 }, 329 TerminatingTLS: &TLSContext{ 330 Secret: &Secret{ 331 Name: "test-secret", 332 }, 333 }, 334 ServerNames: []string{"foo.bar.com", "bar.foo.com"}, 335 Rules: &L7Rules{ 336 HTTP: []PortRuleHTTP{ 337 {Method: "GET", Path: "/"}, 338 }, 339 }, 340 }}, 341 }, 342 }, 343 } 344 err = validPortRule.Sanitize() 345 require.Nil(t, err) 346 347 // Rule is valid because Listener is allowed on egress, default Kind 348 validPortRule = Rule{ 349 EndpointSelector: WildcardEndpointSelector, 350 Egress: []EgressRule{ 351 { 352 EgressCommonRule: EgressCommonRule{ 353 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 354 }, 355 ToPorts: []PortRule{{ 356 Ports: []PortProtocol{ 357 {Port: "443", Protocol: ProtoTCP}, 358 }, 359 Listener: &Listener{ 360 EnvoyConfig: &EnvoyConfig{ 361 Name: "test-config", 362 }, 363 Name: "myCustomListener", 364 }, 365 }}, 366 }, 367 }, 368 } 369 err = validPortRule.Sanitize() 370 require.Nil(t, err) 371 372 // Rule is valid because Listener is allowed on egress, Kind CiliumClusterwideEnvoyConfig 373 validPortRule = Rule{ 374 EndpointSelector: WildcardEndpointSelector, 375 Egress: []EgressRule{ 376 { 377 EgressCommonRule: EgressCommonRule{ 378 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 379 }, 380 ToPorts: []PortRule{{ 381 Ports: []PortProtocol{ 382 {Port: "443", Protocol: ProtoTCP}, 383 }, 384 Listener: &Listener{ 385 EnvoyConfig: &EnvoyConfig{ 386 Kind: "CiliumClusterwideEnvoyConfig", 387 Name: "shared-config", 388 }, 389 Name: "myCustomListener", 390 }, 391 }}, 392 }, 393 }, 394 } 395 err = validPortRule.Sanitize() 396 require.Nil(t, err) 397 398 // Rule is valid because Listener is allowed on egress, Kind CiliumEnvoyConfig 399 validPortRule = Rule{ 400 EndpointSelector: WildcardEndpointSelector, 401 Egress: []EgressRule{ 402 { 403 EgressCommonRule: EgressCommonRule{ 404 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 405 }, 406 ToPorts: []PortRule{{ 407 Ports: []PortProtocol{ 408 {Port: "443", Protocol: ProtoTCP}, 409 }, 410 Listener: &Listener{ 411 EnvoyConfig: &EnvoyConfig{ 412 Kind: "CiliumEnvoyConfig", 413 Name: "shared-config", 414 }, 415 Name: "myCustomListener", 416 }, 417 }}, 418 }, 419 }, 420 } 421 err = validPortRule.Sanitize() 422 require.Nil(t, err) 423 424 // Rule is invalid because Listener is not allowed on ingress (yet) 425 invalidPortRule = Rule{ 426 EndpointSelector: WildcardEndpointSelector, 427 Ingress: []IngressRule{ 428 { 429 IngressCommonRule: IngressCommonRule{ 430 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 431 }, 432 ToPorts: []PortRule{{ 433 Ports: []PortProtocol{ 434 {Port: "443", Protocol: ProtoTCP}, 435 }, 436 Listener: &Listener{ 437 EnvoyConfig: &EnvoyConfig{ 438 Name: "test-config", 439 }, 440 Name: "myCustomListener", 441 }, 442 }}, 443 }, 444 }, 445 } 446 err = invalidPortRule.Sanitize() 447 require.NotNil(t, err) 448 require.Equal(t, "Listener is not allowed on ingress (myCustomListener)", err.Error()) 449 450 // Rule is invalid because Listener is not allowed with L7 rules 451 invalidPortRule = Rule{ 452 EndpointSelector: WildcardEndpointSelector, 453 Egress: []EgressRule{ 454 { 455 EgressCommonRule: EgressCommonRule{ 456 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 457 }, 458 ToPorts: []PortRule{{ 459 Ports: []PortProtocol{ 460 {Port: "443", Protocol: ProtoTCP}, 461 }, 462 Listener: &Listener{ 463 EnvoyConfig: &EnvoyConfig{ 464 Name: "test-config", 465 }, 466 Name: "myCustomListener", 467 }, 468 Rules: &L7Rules{ 469 HTTP: []PortRuleHTTP{ 470 {Method: "GET", Path: "/"}, 471 }, 472 }, 473 }}, 474 }, 475 }, 476 } 477 err = invalidPortRule.Sanitize() 478 require.NotNil(t, err) 479 require.Equal(t, "Listener is not allowed with L7 rules (myCustomListener)", err.Error()) 480 } 481 482 // This test ensures that L7 rules reject unspecified ports. 483 func TestL7RuleRejectsEmptyPort(t *testing.T) { 484 setUpSuite(t) 485 486 invalidL7PortRule := Rule{ 487 EndpointSelector: WildcardEndpointSelector, 488 Ingress: []IngressRule{ 489 { 490 IngressCommonRule: IngressCommonRule{ 491 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 492 }, 493 ToPorts: []PortRule{{ 494 Ports: []PortProtocol{ 495 {Port: "0", Protocol: ProtoTCP}, 496 }, 497 Rules: &L7Rules{ 498 HTTP: []PortRuleHTTP{ 499 {}, 500 }, 501 }, 502 }}, 503 }, 504 }, 505 } 506 507 err := invalidL7PortRule.Sanitize() 508 require.NotNil(t, err) 509 } 510 511 // This test ensures that PortRules using the HTTP protocol have valid regular 512 // expressions for the method and path fields. 513 func TestHTTPRuleRegexes(t *testing.T) { 514 setUpSuite(t) 515 516 invalidHTTPRegexPathRule := Rule{ 517 EndpointSelector: WildcardEndpointSelector, 518 Ingress: []IngressRule{ 519 { 520 IngressCommonRule: IngressCommonRule{ 521 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 522 }, 523 ToPorts: []PortRule{{ 524 Ports: []PortProtocol{ 525 {Port: "80", Protocol: ProtoTCP}, 526 {Port: "81", Protocol: ProtoTCP}, 527 }, 528 Rules: &L7Rules{ 529 HTTP: []PortRuleHTTP{ 530 {Method: "GET", Path: "*"}, 531 }, 532 }, 533 }}, 534 }, 535 }, 536 } 537 538 err := invalidHTTPRegexPathRule.Sanitize() 539 require.NotNil(t, err) 540 541 invalidHTTPRegexMethodRule := Rule{ 542 EndpointSelector: WildcardEndpointSelector, 543 Ingress: []IngressRule{ 544 { 545 IngressCommonRule: IngressCommonRule{ 546 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 547 }, 548 ToPorts: []PortRule{{ 549 Ports: []PortProtocol{ 550 {Port: "80", Protocol: ProtoTCP}, 551 {Port: "81", Protocol: ProtoTCP}, 552 }, 553 Rules: &L7Rules{ 554 HTTP: []PortRuleHTTP{ 555 {Method: "*", Path: "/"}, 556 }, 557 }, 558 }}, 559 }, 560 }, 561 } 562 563 err = invalidHTTPRegexMethodRule.Sanitize() 564 require.NotNil(t, err) 565 } 566 567 // Test the validation of CIDR rule prefix definitions 568 func TestCIDRsanitize(t *testing.T) { 569 setUpSuite(t) 570 571 // IPv4 572 cidr := CIDRRule{Cidr: "0.0.0.0/0"} 573 err := cidr.sanitize() 574 require.Nil(t, err) 575 576 cidr = CIDRRule{Cidr: "10.0.0.0/24"} 577 err = cidr.sanitize() 578 require.Nil(t, err) 579 580 cidr = CIDRRule{Cidr: "192.0.2.3/32"} 581 err = cidr.sanitize() 582 require.Nil(t, err) 583 584 // IPv6 585 cidr = CIDRRule{Cidr: "::/0"} 586 err = cidr.sanitize() 587 require.Nil(t, err) 588 589 cidr = CIDRRule{Cidr: "ff02::/64"} 590 err = cidr.sanitize() 591 require.Nil(t, err) 592 593 cidr = CIDRRule{Cidr: "", CIDRGroupRef: "cidrgroup"} 594 err = cidr.sanitize() 595 require.Nil(t, err) 596 597 cidr = CIDRRule{Cidr: "2001:0db8:85a3:0000:0000:8a2e:0370:7334/128"} 598 err = cidr.sanitize() 599 require.Nil(t, err) 600 601 // Non-contiguous mask. 602 cidr = CIDRRule{Cidr: "10.0.0.0/254.0.0.255"} 603 err = cidr.sanitize() 604 require.Error(t, err) 605 } 606 607 func TestToServicesSanitize(t *testing.T) { 608 setUpSuite(t) 609 610 svcLabels := map[string]string{ 611 "app": "tested-service", 612 } 613 selector := ServiceSelector(NewESFromMatchRequirements(svcLabels, nil)) 614 toServicesL3L4 := Rule{ 615 EndpointSelector: WildcardEndpointSelector, 616 Egress: []EgressRule{ 617 { 618 EgressCommonRule: EgressCommonRule{ 619 ToServices: []Service{ 620 { 621 K8sServiceSelector: &K8sServiceSelectorNamespace{ 622 Selector: selector, 623 Namespace: "", 624 }, 625 }, 626 }, 627 }, 628 ToPorts: []PortRule{{ 629 Ports: []PortProtocol{ 630 {Port: "80", Protocol: ProtoTCP}, 631 {Port: "81", Protocol: ProtoTCP}, 632 }, 633 }}, 634 }, 635 }, 636 } 637 638 err := toServicesL3L4.Sanitize() 639 require.Error(t, err) 640 } 641 642 // This test ensures that PortRules using key-value pairs do not have empty keys 643 func TestL7Rules(t *testing.T) { 644 setUpSuite(t) 645 646 validL7Rule := Rule{ 647 EndpointSelector: WildcardEndpointSelector, 648 Ingress: []IngressRule{ 649 { 650 IngressCommonRule: IngressCommonRule{ 651 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 652 }, 653 ToPorts: []PortRule{{ 654 Ports: []PortProtocol{ 655 {Port: "80", Protocol: ProtoTCP}, 656 {Port: "81", Protocol: ProtoTCP}, 657 }, 658 Rules: &L7Rules{ 659 L7Proto: "test.lineparser", 660 L7: []PortRuleL7{ 661 {"method": "PUT", "path": "/"}, 662 {"method": "GET", "path": "/"}, 663 }, 664 }, 665 }}, 666 }, 667 }, 668 } 669 670 err := validL7Rule.Sanitize() 671 require.Nil(t, err) 672 673 validL7Rule2 := Rule{ 674 EndpointSelector: WildcardEndpointSelector, 675 Ingress: []IngressRule{ 676 { 677 IngressCommonRule: IngressCommonRule{ 678 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 679 }, 680 ToPorts: []PortRule{{ 681 Ports: []PortProtocol{ 682 {Port: "80", Protocol: ProtoTCP}, 683 {Port: "81", Protocol: ProtoTCP}, 684 }, 685 Rules: &L7Rules{ 686 L7Proto: "test.lineparser", 687 // No L7 rules 688 }, 689 }}, 690 }, 691 }, 692 } 693 694 err = validL7Rule2.Sanitize() 695 require.Nil(t, err) 696 697 invalidL7Rule := Rule{ 698 EndpointSelector: WildcardEndpointSelector, 699 Ingress: []IngressRule{ 700 { 701 IngressCommonRule: IngressCommonRule{ 702 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 703 }, 704 ToPorts: []PortRule{{ 705 Ports: []PortProtocol{ 706 {Port: "80", Protocol: ProtoTCP}, 707 {Port: "81", Protocol: ProtoTCP}, 708 }, 709 Rules: &L7Rules{ 710 L7Proto: "test.lineparser", 711 L7: []PortRuleL7{ 712 map[string]string{ 713 "method": "PUT", 714 "": "Foo"}, 715 }, 716 }, 717 }}, 718 }, 719 }, 720 } 721 722 err = invalidL7Rule.Sanitize() 723 require.NotNil(t, err) 724 } 725 726 // This test ensures that DNS rules do not accept port ranges 727 func TestPortRangesNotAllowedWithDNSRules(t *testing.T) { 728 // Rule is invalid because DNS rules do not support port ranges. 729 invalidPortRule := Rule{ 730 EndpointSelector: WildcardEndpointSelector, 731 Egress: []EgressRule{ 732 { 733 EgressCommonRule: EgressCommonRule{ 734 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 735 }, 736 ToPorts: []PortRule{{ 737 Ports: []PortProtocol{ 738 {Port: "443", EndPort: 445, Protocol: ProtoTCP}, 739 }, 740 Rules: &L7Rules{ 741 DNS: []PortRuleDNS{ 742 {MatchName: "www.google.com"}, 743 }, 744 }, 745 }}, 746 }, 747 }, 748 } 749 err := invalidPortRule.Sanitize() 750 require.NotNil(t, err) 751 require.Equal(t, "DNS rules do not support port ranges", err.Error()) 752 } 753 754 // This test ensures that host policies with L7 rules are rejected. 755 func TestL7RulesWithNodeSelector(t *testing.T) { 756 setUpSuite(t) 757 758 invalidL7RuleIngress := Rule{ 759 NodeSelector: WildcardEndpointSelector, 760 Ingress: []IngressRule{ 761 { 762 IngressCommonRule: IngressCommonRule{ 763 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 764 }, 765 ToPorts: []PortRule{{ 766 Ports: []PortProtocol{ 767 {Port: "80", Protocol: ProtoTCP}, 768 }, 769 Rules: &L7Rules{ 770 HTTP: []PortRuleHTTP{ 771 {Method: "PUT", Path: "/"}, 772 }, 773 }, 774 }}, 775 }, 776 }, 777 } 778 err := invalidL7RuleIngress.Sanitize() 779 require.Equal(t, "host policies do not support L7 rules yet", err.Error()) 780 781 invalidL7RuleEgress := Rule{ 782 NodeSelector: WildcardEndpointSelector, 783 Egress: []EgressRule{ 784 { 785 EgressCommonRule: EgressCommonRule{ 786 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 787 }, 788 ToPorts: []PortRule{{ 789 Ports: []PortProtocol{ 790 {Port: "53", Protocol: ProtoUDP}, 791 }, 792 Rules: &L7Rules{ 793 DNS: []PortRuleDNS{ 794 {MatchName: "domain.com"}, 795 }, 796 }, 797 }}, 798 }, 799 }, 800 } 801 err = invalidL7RuleEgress.Sanitize() 802 require.Equal(t, "host policies do not support L7 rules yet", err.Error()) 803 804 validL7RuleIngress := Rule{ 805 NodeSelector: WildcardEndpointSelector, 806 Ingress: []IngressRule{ 807 { 808 IngressCommonRule: IngressCommonRule{ 809 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 810 }, 811 }, 812 }, 813 } 814 err = validL7RuleIngress.Sanitize() 815 require.Nil(t, err) 816 } 817 818 func TestInvalidEndpointSelectors(t *testing.T) { 819 setUpSuite(t) 820 821 // Operator in MatchExpressions is invalid, so sanitization should fail. 822 labelSel := &slim_metav1.LabelSelector{ 823 MatchLabels: map[string]string{ 824 "any.foo": "bar", 825 "k8s.baz": "alice", 826 }, 827 MatchExpressions: []slim_metav1.LabelSelectorRequirement{ 828 { 829 Key: "any.foo", 830 Operator: "asdfasdfasdf", 831 Values: []string{"default"}, 832 }, 833 }, 834 } 835 836 invalidSel := NewESFromK8sLabelSelector(labels.LabelSourceK8sKeyPrefix, labelSel) 837 838 invalidEpSelectorRule := Rule{ 839 EndpointSelector: invalidSel, 840 } 841 842 err := invalidEpSelectorRule.Sanitize() 843 require.NotNil(t, err) 844 845 invalidEpSelectorIngress := Rule{ 846 EndpointSelector: WildcardEndpointSelector, 847 Ingress: []IngressRule{ 848 { 849 IngressCommonRule: IngressCommonRule{ 850 FromEndpoints: []EndpointSelector{invalidSel}, 851 }, 852 }, 853 }, 854 } 855 856 err = invalidEpSelectorIngress.Sanitize() 857 require.NotNil(t, err) 858 859 invalidEpSelectorIngressFromReq := Rule{ 860 EndpointSelector: WildcardEndpointSelector, 861 Ingress: []IngressRule{ 862 { 863 IngressCommonRule: IngressCommonRule{ 864 FromRequires: []EndpointSelector{invalidSel}, 865 }, 866 }, 867 }, 868 } 869 870 err = invalidEpSelectorIngressFromReq.Sanitize() 871 require.NotNil(t, err) 872 873 invalidEpSelectorEgress := Rule{ 874 EndpointSelector: WildcardEndpointSelector, 875 Egress: []EgressRule{ 876 { 877 EgressCommonRule: EgressCommonRule{ 878 ToEndpoints: []EndpointSelector{invalidSel}, 879 }, 880 }, 881 }, 882 } 883 884 err = invalidEpSelectorEgress.Sanitize() 885 require.NotNil(t, err) 886 887 invalidEpSelectorEgressToReq := Rule{ 888 EndpointSelector: WildcardEndpointSelector, 889 Egress: []EgressRule{ 890 { 891 EgressCommonRule: EgressCommonRule{ 892 ToRequires: []EndpointSelector{invalidSel}, 893 }, 894 }, 895 }, 896 } 897 898 err = invalidEpSelectorEgressToReq.Sanitize() 899 require.NotNil(t, err) 900 901 } 902 903 func TestNodeSelector(t *testing.T) { 904 setUpSuite(t) 905 906 // Operator in MatchExpressions is invalid, so sanitization should fail. 907 labelSel := &slim_metav1.LabelSelector{ 908 MatchLabels: map[string]string{ 909 "any.foo": "bar", 910 "k8s.baz": "alice", 911 }, 912 MatchExpressions: []slim_metav1.LabelSelectorRequirement{ 913 { 914 Key: "any.foo", 915 Operator: "asdfasdfasdf", 916 Values: []string{"default"}, 917 }, 918 }, 919 } 920 invalidSel := NewESFromK8sLabelSelector(labels.LabelSourceK8sKeyPrefix, labelSel) 921 invalidNodeSelectorRule := Rule{ 922 NodeSelector: invalidSel, 923 } 924 err := invalidNodeSelectorRule.Sanitize() 925 require.EqualError(t, err, "invalid label selector: matchExpressions[0].operator: Invalid value: \"asdfasdfasdf\": not a valid selector operator") 926 927 invalidRuleBothSelectors := Rule{ 928 EndpointSelector: WildcardEndpointSelector, 929 NodeSelector: WildcardEndpointSelector, 930 } 931 err = invalidRuleBothSelectors.Sanitize() 932 require.Equal(t, "rule cannot have both EndpointSelector and NodeSelector", err.Error()) 933 934 invalidRuleNoSelector := Rule{} 935 err = invalidRuleNoSelector.Sanitize() 936 require.Equal(t, "rule must have one of EndpointSelector or NodeSelector", err.Error()) 937 } 938 939 func TestTooManyPortsRule(t *testing.T) { 940 setUpSuite(t) 941 942 var portProtocols []PortProtocol 943 944 for i := 80; i <= 80+maxPorts; i++ { 945 portProtocols = append(portProtocols, PortProtocol{ 946 Port: fmt.Sprintf("%d", i), 947 Protocol: ProtoTCP, 948 }) 949 } 950 951 tooManyPortsRule := Rule{ 952 EndpointSelector: WildcardEndpointSelector, 953 Ingress: []IngressRule{ 954 { 955 IngressCommonRule: IngressCommonRule{ 956 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 957 }, 958 ToPorts: []PortRule{{ 959 Ports: portProtocols, 960 }}, 961 }, 962 }, 963 } 964 err := tooManyPortsRule.Sanitize() 965 require.Error(t, err) 966 } 967 968 func TestTooManyICMPFields(t *testing.T) { 969 setUpSuite(t) 970 971 var fields []ICMPField 972 973 for i := 1; i <= 1+maxICMPFields; i++ { 974 icmpType := intstr.FromInt(i) 975 fields = append(fields, ICMPField{ 976 Type: &icmpType, 977 }) 978 } 979 980 tooManyICMPRule := Rule{ 981 EndpointSelector: WildcardEndpointSelector, 982 Ingress: []IngressRule{ 983 { 984 IngressCommonRule: IngressCommonRule{ 985 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 986 }, 987 ICMPs: ICMPRules{{ 988 Fields: fields, 989 }}, 990 }, 991 }, 992 } 993 err := tooManyICMPRule.Sanitize() 994 require.Error(t, err) 995 } 996 997 func TestWrongICMPFieldFamily(t *testing.T) { 998 setUpSuite(t) 999 1000 icmpType := intstr.FromInt(0) 1001 wrongFamilyICMPRule := Rule{ 1002 EndpointSelector: WildcardEndpointSelector, 1003 Ingress: []IngressRule{ 1004 { 1005 IngressCommonRule: IngressCommonRule{ 1006 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 1007 }, 1008 ICMPs: ICMPRules{{ 1009 Fields: []ICMPField{{ 1010 Family: "hoge", 1011 Type: &icmpType, 1012 }}, 1013 }}, 1014 }, 1015 }, 1016 } 1017 err := wrongFamilyICMPRule.Sanitize() 1018 require.Error(t, err) 1019 } 1020 1021 func TestICMPRuleWithOtherRuleFailed(t *testing.T) { 1022 setUpSuite(t) 1023 1024 icmpType := intstr.FromInt(8) 1025 ingressICMPWithPort := Rule{ 1026 EndpointSelector: WildcardEndpointSelector, 1027 Ingress: []IngressRule{ 1028 { 1029 IngressCommonRule: IngressCommonRule{ 1030 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 1031 }, 1032 ToPorts: []PortRule{{ 1033 Ports: []PortProtocol{ 1034 {Port: "80", Protocol: ProtoTCP}, 1035 }, 1036 }}, 1037 ICMPs: ICMPRules{{ 1038 Fields: []ICMPField{{ 1039 Type: &icmpType, 1040 }}, 1041 }}, 1042 }, 1043 }, 1044 } 1045 1046 egressICMPWithPort := Rule{ 1047 EndpointSelector: WildcardEndpointSelector, 1048 Egress: []EgressRule{ 1049 { 1050 EgressCommonRule: EgressCommonRule{ 1051 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 1052 }, 1053 ToPorts: []PortRule{{ 1054 Ports: []PortProtocol{ 1055 {Port: "80", Protocol: ProtoTCP}, 1056 }, 1057 }}, 1058 ICMPs: ICMPRules{{ 1059 Fields: []ICMPField{{ 1060 Type: &icmpType, 1061 }}, 1062 }}, 1063 }, 1064 }, 1065 } 1066 1067 option.Config.EnableICMPRules = true 1068 errStr := "The ICMPs block may only be present without ToPorts. Define a separate rule to use ToPorts." 1069 err := ingressICMPWithPort.Sanitize() 1070 require.ErrorContains(t, err, errStr) 1071 err = egressICMPWithPort.Sanitize() 1072 require.ErrorContains(t, err, errStr) 1073 } 1074 1075 // This test ensures that PortRules aren't configured in the wrong direction, 1076 // which ends up being a no-op with only vague error messages rather than a 1077 // clear indication that something is wrong in the policy. 1078 func TestL7RuleDirectionalitySupport(t *testing.T) { 1079 setUpSuite(t) 1080 1081 // Kafka egress is now supported. 1082 egressKafkaRule := Rule{ 1083 EndpointSelector: WildcardEndpointSelector, 1084 Egress: []EgressRule{ 1085 { 1086 ToPorts: []PortRule{{ 1087 Ports: []PortProtocol{ 1088 {Port: "80", Protocol: ProtoTCP}, 1089 {Port: "81", Protocol: ProtoTCP}, 1090 }, 1091 Rules: &L7Rules{ 1092 Kafka: []kafka.PortRule{{ 1093 Role: "consume", 1094 Topic: "deathstar-plans", 1095 }}, 1096 }, 1097 }}, 1098 }, 1099 }, 1100 } 1101 1102 err := egressKafkaRule.Sanitize() 1103 require.Nil(t, err) 1104 1105 // DNS ingress is not supported. 1106 invalidDNSRule := Rule{ 1107 EndpointSelector: WildcardEndpointSelector, 1108 Ingress: []IngressRule{ 1109 { 1110 ToPorts: []PortRule{{ 1111 Ports: []PortProtocol{ 1112 {Port: "53", Protocol: ProtoTCP}, 1113 {Port: "53", Protocol: ProtoUDP}, 1114 }, 1115 Rules: &L7Rules{ 1116 DNS: []PortRuleDNS{{ 1117 MatchName: "empire.gov", 1118 }}, 1119 }, 1120 }}, 1121 }, 1122 }, 1123 } 1124 1125 err = invalidDNSRule.Sanitize() 1126 require.NotNil(t, err) 1127 1128 } 1129 1130 func BenchmarkCIDRSanitize(b *testing.B) { 1131 cidr4 := CIDRRule{Cidr: "192.168.100.200/24"} 1132 cidr6 := CIDRRule{Cidr: "2001:0db8:85a3:0000:0000:8a2e:0370:7334/128"} 1133 1134 b.ReportAllocs() 1135 b.ResetTimer() 1136 for i := 0; i < b.N; i++ { 1137 err := cidr4.sanitize() 1138 if err != nil { 1139 b.Fatal(err) 1140 } 1141 err = cidr6.sanitize() 1142 if err != nil { 1143 b.Fatal(err) 1144 } 1145 } 1146 } 1147 1148 func TestSanitizeDefaultDeny(t *testing.T) { 1149 for _, tc := range []struct { 1150 before Rule 1151 wantIngress bool 1152 wantEgress bool 1153 }{ 1154 { 1155 before: Rule{}, 1156 }, 1157 { 1158 before: Rule{ 1159 Ingress: []IngressRule{{}}, 1160 }, 1161 wantIngress: true, 1162 }, 1163 { 1164 before: Rule{ 1165 IngressDeny: []IngressDenyRule{{}}, 1166 }, 1167 wantIngress: true, 1168 }, 1169 { 1170 before: Rule{ 1171 Ingress: []IngressRule{{}}, 1172 IngressDeny: []IngressDenyRule{{}}, 1173 }, 1174 wantIngress: true, 1175 }, 1176 { 1177 before: Rule{ 1178 Egress: []EgressRule{{}}, 1179 EgressDeny: []EgressDenyRule{{}}, 1180 }, 1181 wantEgress: true, 1182 }, { 1183 before: Rule{ 1184 EgressDeny: []EgressDenyRule{{}}, 1185 }, 1186 wantEgress: true, 1187 }, 1188 { 1189 before: Rule{ 1190 Egress: []EgressRule{{}}, 1191 }, 1192 wantEgress: true, 1193 }, 1194 { 1195 before: Rule{ 1196 Egress: []EgressRule{{}}, 1197 Ingress: []IngressRule{{}}, 1198 }, 1199 wantEgress: true, 1200 wantIngress: true, 1201 }, 1202 } { 1203 b := tc.before 1204 b.EndpointSelector = EndpointSelector{LabelSelector: &slim_metav1.LabelSelector{}} 1205 1206 err := b.Sanitize() 1207 assert.Nil(t, err) 1208 assert.NotNil(t, b.EnableDefaultDeny.Egress) 1209 assert.NotNil(t, b.EnableDefaultDeny.Ingress) 1210 1211 assert.Equal(t, tc.wantEgress, *b.EnableDefaultDeny.Egress, "Rule.EnableDefaultDeny.Egress should match") 1212 assert.Equal(t, tc.wantIngress, *b.EnableDefaultDeny.Ingress, "Rule.EnableDefaultDeny.Ingress should match") 1213 } 1214 }