github.com/cilium/cilium@v1.16.2/pkg/policy/rule_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package policy 5 6 import ( 7 "bytes" 8 "fmt" 9 stdlog "log" 10 "strings" 11 "testing" 12 13 "github.com/cilium/proxy/pkg/policy/api/kafka" 14 "github.com/stretchr/testify/require" 15 16 "k8s.io/apimachinery/pkg/util/intstr" 17 18 "github.com/cilium/cilium/api/v1/models" 19 "github.com/cilium/cilium/pkg/identity" 20 "github.com/cilium/cilium/pkg/labels" 21 "github.com/cilium/cilium/pkg/policy/api" 22 "github.com/cilium/cilium/pkg/u8proto" 23 ) 24 25 func TestL4Policy(t *testing.T) { 26 td := newTestData() 27 28 toBar := &SearchContext{To: labels.ParseSelectLabelArray("bar")} 29 fromBar := &SearchContext{From: labels.ParseSelectLabelArray("bar")} 30 toFoo := &SearchContext{To: labels.ParseSelectLabelArray("foo")} 31 fromFoo := &SearchContext{From: labels.ParseSelectLabelArray("foo")} 32 33 rule1 := &rule{ 34 Rule: api.Rule{ 35 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 36 Ingress: []api.IngressRule{ 37 { 38 ToPorts: []api.PortRule{{ 39 Ports: []api.PortProtocol{ 40 {Port: "80", Protocol: api.ProtoTCP}, 41 {Port: "8080", Protocol: api.ProtoTCP}, 42 }, 43 Rules: &api.L7Rules{ 44 HTTP: []api.PortRuleHTTP{ 45 {Method: "GET", Path: "/"}, 46 }, 47 }, 48 }}, 49 }, 50 }, 51 Egress: []api.EgressRule{ 52 { 53 ToPorts: []api.PortRule{{ 54 Ports: []api.PortProtocol{ 55 {Port: "3000", Protocol: api.ProtoAny}, 56 }, 57 }}, 58 }, 59 }, 60 }, 61 } 62 63 l7rules := api.L7Rules{ 64 HTTP: []api.PortRuleHTTP{{Path: "/", Method: "GET"}}, 65 } 66 l7map := L7DataMap{ 67 td.wildcardCachedSelector: &PerSelectorPolicy{ 68 L7Rules: l7rules, 69 isRedirect: true, 70 }, 71 } 72 73 expected := NewL4Policy(0) 74 expected.Ingress.PortRules.Upsert("80", 0, "TCP", &L4Filter{ 75 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 76 wildcard: td.wildcardCachedSelector, 77 L7Parser: "http", PerSelectorPolicies: l7map, Ingress: true, 78 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 79 }) 80 expected.Ingress.PortRules.Upsert("8080", 0, "TCP", &L4Filter{ 81 Port: 8080, Protocol: api.ProtoTCP, U8Proto: 6, 82 wildcard: td.wildcardCachedSelector, 83 L7Parser: "http", PerSelectorPolicies: l7map, Ingress: true, 84 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 85 }) 86 87 expected.Egress.PortRules.Upsert("3000", 0, "TCP", &L4Filter{ 88 Port: 3000, Protocol: api.ProtoTCP, U8Proto: 6, Ingress: false, 89 wildcard: td.wildcardCachedSelector, 90 PerSelectorPolicies: L7DataMap{ 91 td.wildcardCachedSelector: nil, 92 }, 93 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 94 }) 95 expected.Egress.PortRules.Upsert("3000", 0, "UDP", &L4Filter{ 96 Port: 3000, Protocol: api.ProtoUDP, U8Proto: 17, Ingress: false, 97 wildcard: td.wildcardCachedSelector, 98 PerSelectorPolicies: L7DataMap{ 99 td.wildcardCachedSelector: nil, 100 }, 101 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 102 }) 103 expected.Egress.PortRules.Upsert("3000", 0, "SCTP", &L4Filter{ 104 Port: 3000, Protocol: api.ProtoSCTP, U8Proto: 132, Ingress: false, 105 wildcard: td.wildcardCachedSelector, 106 PerSelectorPolicies: L7DataMap{ 107 td.wildcardCachedSelector: nil, 108 }, 109 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 110 }) 111 112 ingressState := traceState{} 113 egressState := traceState{} 114 res := NewL4Policy(0) 115 var err error 116 res.Ingress.PortRules, err = 117 rule1.resolveIngressPolicy(td.testPolicyContext, toBar, &ingressState, NewL4PolicyMap(), nil, nil) 118 require.NoError(t, err) 119 require.NotNil(t, res.Ingress) 120 121 res.Egress.PortRules, err = 122 rule1.resolveEgressPolicy(td.testPolicyContext, fromBar, &egressState, NewL4PolicyMap(), nil, nil) 123 require.NoError(t, err) 124 require.NotNil(t, res.Egress) 125 126 require.Equal(t, &expected, &res) 127 require.Equal(t, 1, ingressState.selectedRules) 128 require.Equal(t, 1, ingressState.matchedRules) 129 130 require.Equal(t, 1, egressState.selectedRules) 131 require.Equal(t, 1, egressState.matchedRules) 132 res.Detach(td.sc) 133 expected.Detach(td.sc) 134 135 // Foo isn't selected in the rule1's policy. 136 ingressState = traceState{} 137 egressState = traceState{} 138 139 res1, err := rule1.resolveIngressPolicy(td.testPolicyContext, toFoo, &ingressState, NewL4PolicyMap(), nil, nil) 140 require.NoError(t, err) 141 res2, err := rule1.resolveEgressPolicy(td.testPolicyContext, fromFoo, &ingressState, NewL4PolicyMap(), nil, nil) 142 require.NoError(t, err) 143 144 require.Nil(t, res1) 145 require.Nil(t, res2) 146 require.Equal(t, 0, ingressState.selectedRules) 147 require.Equal(t, 0, ingressState.matchedRules) 148 require.Equal(t, 0, egressState.selectedRules) 149 require.Equal(t, 0, egressState.matchedRules) 150 151 // This rule actually overlaps with the existing ingress "http" rule, 152 // so we'd expect it to merge. 153 rule2 := &rule{ 154 Rule: api.Rule{ 155 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 156 Ingress: []api.IngressRule{ 157 { 158 // Note that this allows all on 80, so the result should wildcard HTTP 159 ToPorts: []api.PortRule{{ 160 Ports: []api.PortProtocol{ 161 {Port: "80", Protocol: api.ProtoTCP}, 162 }, 163 }}, 164 }, 165 { 166 ToPorts: []api.PortRule{{ 167 Ports: []api.PortProtocol{ 168 {Port: "80", Protocol: api.ProtoTCP}, 169 }, 170 Rules: &api.L7Rules{ 171 HTTP: []api.PortRuleHTTP{ 172 {Method: "GET", Path: "/"}, 173 }, 174 }, 175 }}, 176 }, 177 }, 178 Egress: []api.EgressRule{ 179 { 180 ToPorts: []api.PortRule{{ 181 Ports: []api.PortProtocol{ 182 {Port: "3000", Protocol: api.ProtoAny}, 183 }, 184 }}, 185 }, 186 }, 187 }, 188 } 189 190 expected = NewL4Policy(0) 191 expected.Ingress.PortRules.Upsert("80", 0, "TCP", &L4Filter{ 192 Port: 80, 193 Protocol: api.ProtoTCP, 194 U8Proto: 6, 195 wildcard: td.wildcardCachedSelector, 196 L7Parser: ParserTypeHTTP, 197 PerSelectorPolicies: L7DataMap{ 198 td.wildcardCachedSelector: &PerSelectorPolicy{ 199 L7Rules: api.L7Rules{ 200 HTTP: []api.PortRuleHTTP{{Path: "/", Method: "GET"}, {}}, 201 }, 202 isRedirect: true, 203 }, 204 }, 205 Ingress: true, 206 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 207 }) 208 expected.Egress.PortRules.Upsert("3000", 0, "TCP", &L4Filter{ 209 Port: 3000, Protocol: api.ProtoTCP, U8Proto: 6, Ingress: false, 210 wildcard: td.wildcardCachedSelector, 211 PerSelectorPolicies: L7DataMap{ 212 td.wildcardCachedSelector: nil, 213 }, 214 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 215 }) 216 expected.Egress.PortRules.Upsert("3000", 0, "UDP", &L4Filter{ 217 Port: 3000, Protocol: api.ProtoUDP, U8Proto: 17, Ingress: false, 218 wildcard: td.wildcardCachedSelector, 219 PerSelectorPolicies: L7DataMap{ 220 td.wildcardCachedSelector: nil, 221 }, 222 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 223 }) 224 expected.Egress.PortRules.Upsert("3000", 0, "SCTP", &L4Filter{ 225 Port: 3000, Protocol: api.ProtoSCTP, U8Proto: 132, Ingress: false, 226 wildcard: td.wildcardCachedSelector, 227 PerSelectorPolicies: L7DataMap{ 228 td.wildcardCachedSelector: nil, 229 }, 230 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 231 }) 232 233 ingressState = traceState{} 234 egressState = traceState{} 235 res = NewL4Policy(0) 236 237 buffer := new(bytes.Buffer) 238 ctx := SearchContext{To: labels.ParseSelectLabelArray("bar"), Trace: TRACE_VERBOSE} 239 ctx.Logging = stdlog.New(buffer, "", 0) 240 241 res.Ingress.PortRules, err = rule2.resolveIngressPolicy(td.testPolicyContext, &ctx, &ingressState, NewL4PolicyMap(), nil, nil) 242 require.NoError(t, err) 243 require.NotNil(t, res.Ingress) 244 245 t.Log(buffer) 246 247 res.Egress.PortRules, err = rule2.resolveEgressPolicy(td.testPolicyContext, fromBar, &egressState, NewL4PolicyMap(), nil, nil) 248 require.NoError(t, err) 249 require.NotNil(t, res.Egress) 250 251 require.Equal(t, 1, res.Ingress.PortRules.Len()) 252 require.Equal(t, &expected, &res) 253 require.Equal(t, 1, ingressState.selectedRules) 254 require.Equal(t, 1, ingressState.matchedRules) 255 256 require.Equal(t, 1, egressState.selectedRules) 257 require.Equal(t, 1, egressState.matchedRules) 258 res.Detach(td.sc) 259 expected.Detach(td.sc) 260 261 ingressState = traceState{} 262 egressState = traceState{} 263 264 res1, err = rule2.resolveIngressPolicy(td.testPolicyContext, toFoo, &ingressState, NewL4PolicyMap(), nil, nil) 265 require.NoError(t, err) 266 require.Nil(t, res1) 267 268 res2, err = rule2.resolveEgressPolicy(td.testPolicyContext, fromFoo, &egressState, NewL4PolicyMap(), nil, nil) 269 require.NoError(t, err) 270 require.Nil(t, res2) 271 272 require.Equal(t, 0, ingressState.selectedRules) 273 require.Equal(t, 0, ingressState.matchedRules) 274 275 require.Equal(t, 0, egressState.selectedRules) 276 require.Equal(t, 0, egressState.matchedRules) 277 } 278 279 func TestMergeL4PolicyIngress(t *testing.T) { 280 td := newTestData() 281 toBar := &SearchContext{To: labels.ParseSelectLabelArray("bar")} 282 //toFoo := &SearchContext{To: labels.ParseSelectLabelArray("foo")} 283 284 rule1 := &rule{ 285 Rule: api.Rule{ 286 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 287 Ingress: []api.IngressRule{ 288 { 289 IngressCommonRule: api.IngressCommonRule{ 290 FromEndpoints: []api.EndpointSelector{fooSelector}, 291 }, 292 ToPorts: []api.PortRule{{ 293 Ports: []api.PortProtocol{ 294 {Port: "80", Protocol: api.ProtoTCP}, 295 }, 296 }}, 297 }, 298 { 299 IngressCommonRule: api.IngressCommonRule{ 300 FromEndpoints: []api.EndpointSelector{bazSelector}, 301 }, 302 ToPorts: []api.PortRule{{ 303 Ports: []api.PortProtocol{ 304 {Port: "80", Protocol: api.ProtoTCP}, 305 }, 306 }}, 307 }, 308 }, 309 }, 310 } 311 312 mergedES := L7DataMap{ 313 td.cachedFooSelector: nil, 314 td.cachedBazSelector: nil, 315 } 316 expected := NewL4PolicyMapWithValues(map[string]*L4Filter{"80/TCP": { 317 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 318 L7Parser: ParserTypeNone, PerSelectorPolicies: mergedES, Ingress: true, 319 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 320 td.cachedFooSelector: {nil}, 321 td.cachedBazSelector: {nil}, 322 }, 323 }}) 324 325 state := traceState{} 326 res, err := rule1.resolveIngressPolicy(td.testPolicyContext, toBar, &state, NewL4PolicyMap(), nil, nil) 327 require.NoError(t, err) 328 require.NotNil(t, res) 329 require.Equal(t, expected, res) 330 require.Equal(t, 1, state.selectedRules) 331 require.Equal(t, 1, state.matchedRules) 332 res.Detach(td.sc) 333 expected.Detach(td.sc) 334 } 335 336 func TestMergeL4PolicyEgress(t *testing.T) { 337 td := newTestData() 338 339 buffer := new(bytes.Buffer) 340 fromBar := &SearchContext{ 341 From: labels.ParseSelectLabelArray("bar"), 342 Logging: stdlog.New(buffer, "", 0), 343 Trace: TRACE_VERBOSE, 344 } 345 346 // bar can access foo with TCP on port 80, and baz with TCP on port 80. 347 rule1 := &rule{ 348 Rule: api.Rule{ 349 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 350 Egress: []api.EgressRule{ 351 { 352 EgressCommonRule: api.EgressCommonRule{ 353 ToEndpoints: []api.EndpointSelector{fooSelector}, 354 }, 355 ToPorts: []api.PortRule{{ 356 Ports: []api.PortProtocol{ 357 {Port: "80", Protocol: api.ProtoTCP}, 358 }, 359 }}, 360 }, 361 { 362 EgressCommonRule: api.EgressCommonRule{ 363 ToEndpoints: []api.EndpointSelector{bazSelector}, 364 }, 365 ToPorts: []api.PortRule{{ 366 Ports: []api.PortProtocol{ 367 {Port: "80", Protocol: api.ProtoTCP}, 368 }, 369 }}, 370 }, 371 }, 372 }, 373 } 374 375 mergedES := L7DataMap{ 376 td.cachedFooSelector: nil, 377 td.cachedBazSelector: nil, 378 } 379 expected := NewL4PolicyMapWithValues(map[string]*L4Filter{"80/TCP": { 380 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 381 L7Parser: ParserTypeNone, PerSelectorPolicies: mergedES, Ingress: false, 382 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 383 td.cachedFooSelector: {nil}, 384 td.cachedBazSelector: {nil}, 385 }, 386 }}) 387 388 state := traceState{} 389 res, err := rule1.resolveEgressPolicy(td.testPolicyContext, fromBar, &state, NewL4PolicyMap(), nil, nil) 390 391 t.Log(buffer) 392 393 require.NoError(t, err) 394 require.NotNil(t, res) 395 require.Equal(t, expected, res) 396 require.Equal(t, 1, state.selectedRules) 397 require.Equal(t, 1, state.matchedRules) 398 res.Detach(td.sc) 399 expected.Detach(td.sc) 400 } 401 402 func TestMergeL7PolicyIngress(t *testing.T) { 403 td := newTestData() 404 toBar := &SearchContext{To: labels.ParseSelectLabelArray("bar")} 405 toFoo := &SearchContext{To: labels.ParseSelectLabelArray("foo")} 406 407 fooSelectorSlice := []api.EndpointSelector{ 408 fooSelector, 409 } 410 rule1 := &rule{ 411 Rule: api.Rule{ 412 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 413 Ingress: []api.IngressRule{ 414 { 415 // Note that this allows all on 80, so the result should wildcard HTTP 416 ToPorts: []api.PortRule{{ 417 Ports: []api.PortProtocol{ 418 {Port: "80", Protocol: api.ProtoTCP}, 419 }, 420 }}, 421 }, 422 { 423 ToPorts: []api.PortRule{{ 424 Ports: []api.PortProtocol{ 425 {Port: "80", Protocol: api.ProtoTCP}, 426 }, 427 Rules: &api.L7Rules{ 428 HTTP: []api.PortRuleHTTP{ 429 {Method: "GET", Path: "/"}, 430 }, 431 }, 432 }}, 433 }, 434 { 435 IngressCommonRule: api.IngressCommonRule{ 436 FromEndpoints: fooSelectorSlice, 437 }, 438 ToPorts: []api.PortRule{{ 439 Ports: []api.PortProtocol{ 440 {Port: "80", Protocol: api.ProtoTCP}, 441 }, 442 Rules: &api.L7Rules{ 443 HTTP: []api.PortRuleHTTP{ 444 {Method: "GET", Path: "/"}, 445 }, 446 }, 447 }}, 448 }, 449 }, 450 }, 451 } 452 453 expected := NewL4PolicyMapWithValues(map[string]*L4Filter{"80/TCP": { 454 Port: 80, 455 Protocol: api.ProtoTCP, 456 U8Proto: 6, 457 wildcard: td.wildcardCachedSelector, 458 L7Parser: ParserTypeHTTP, 459 PerSelectorPolicies: L7DataMap{ 460 td.wildcardCachedSelector: &PerSelectorPolicy{ 461 L7Rules: api.L7Rules{ 462 HTTP: []api.PortRuleHTTP{{Path: "/", Method: "GET"}, {}}, 463 }, 464 isRedirect: true, 465 }, 466 td.cachedFooSelector: &PerSelectorPolicy{ 467 L7Rules: api.L7Rules{ 468 HTTP: []api.PortRuleHTTP{{Path: "/", Method: "GET"}}, 469 }, 470 isRedirect: true, 471 }, 472 }, 473 Ingress: true, 474 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 475 td.cachedFooSelector: {nil}, 476 td.wildcardCachedSelector: {nil}, 477 }, 478 }}) 479 480 state := traceState{} 481 res, err := rule1.resolveIngressPolicy(td.testPolicyContext, toBar, &state, NewL4PolicyMap(), nil, nil) 482 require.NoError(t, err) 483 require.NotNil(t, res) 484 require.EqualValues(t, expected, res) 485 require.Equal(t, 1, state.selectedRules) 486 require.Equal(t, 1, state.matchedRules) 487 res.Detach(td.sc) 488 expected.Detach(td.sc) 489 490 state = traceState{} 491 res, err = rule1.resolveIngressPolicy(td.testPolicyContext, toFoo, &state, NewL4PolicyMap(), nil, nil) 492 require.NoError(t, err) 493 require.Nil(t, res) 494 require.Equal(t, 0, state.selectedRules) 495 require.Equal(t, 0, state.matchedRules) 496 497 rule2 := &rule{ 498 Rule: api.Rule{ 499 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 500 Ingress: []api.IngressRule{ 501 { 502 ToPorts: []api.PortRule{{ 503 Ports: []api.PortProtocol{ 504 {Port: "80", Protocol: api.ProtoTCP}, 505 }, 506 Rules: &api.L7Rules{ 507 Kafka: []kafka.PortRule{ 508 {Topic: "foo"}, 509 }, 510 }, 511 }}, 512 }, 513 { 514 IngressCommonRule: api.IngressCommonRule{ 515 FromEndpoints: fooSelectorSlice, 516 }, 517 ToPorts: []api.PortRule{{ 518 Ports: []api.PortProtocol{ 519 {Port: "80", Protocol: api.ProtoTCP}, 520 }, 521 Rules: &api.L7Rules{ 522 Kafka: []kafka.PortRule{ 523 {Topic: "foo"}, 524 }, 525 }, 526 }}, 527 }, 528 }, 529 }, 530 } 531 532 l7rules := api.L7Rules{ 533 Kafka: []kafka.PortRule{{Topic: "foo"}}, 534 } 535 l7map := L7DataMap{ 536 td.wildcardCachedSelector: &PerSelectorPolicy{ 537 L7Rules: l7rules, 538 isRedirect: true, 539 }, 540 td.cachedFooSelector: &PerSelectorPolicy{ 541 L7Rules: l7rules, 542 isRedirect: true, 543 }, 544 } 545 546 expected = NewL4PolicyMapWithValues(map[string]*L4Filter{"80/TCP": { 547 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 548 wildcard: td.wildcardCachedSelector, 549 L7Parser: "kafka", PerSelectorPolicies: l7map, Ingress: true, 550 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 551 td.cachedFooSelector: {nil}, 552 td.wildcardCachedSelector: {nil}, 553 }, 554 }}) 555 556 state = traceState{} 557 res, err = rule2.resolveIngressPolicy(td.testPolicyContext, toBar, &state, NewL4PolicyMap(), nil, nil) 558 require.NoError(t, err) 559 require.NotNil(t, res) 560 require.EqualValues(t, expected, res) 561 require.Equal(t, 1, state.selectedRules) 562 require.Equal(t, 1, state.matchedRules) 563 res.Detach(td.sc) 564 expected.Detach(td.sc) 565 566 state = traceState{} 567 res, err = rule2.resolveIngressPolicy(td.testPolicyContext, toFoo, &state, NewL4PolicyMap(), nil, nil) 568 require.NoError(t, err) 569 require.Nil(t, res) 570 require.Equal(t, 0, state.selectedRules) 571 require.Equal(t, 0, state.matchedRules) 572 573 // Resolve rule1's policy, then try to add rule2. 574 res, err = rule1.resolveIngressPolicy(td.testPolicyContext, toBar, &state, NewL4PolicyMap(), nil, nil) 575 require.NoError(t, err) 576 require.NotNil(t, res) 577 578 state = traceState{} 579 _, err = rule2.resolveIngressPolicy(td.testPolicyContext, toBar, &state, res, nil, nil) 580 581 require.NotNil(t, err) 582 res.Detach(td.sc) 583 584 // Similar to 'rule2', but with different topics for the l3-dependent 585 // rule and the l4-only rule. 586 rule3 := &rule{ 587 Rule: api.Rule{ 588 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 589 Ingress: []api.IngressRule{ 590 { 591 IngressCommonRule: api.IngressCommonRule{ 592 FromEndpoints: fooSelectorSlice, 593 }, 594 ToPorts: []api.PortRule{{ 595 Ports: []api.PortProtocol{ 596 {Port: "80", Protocol: api.ProtoTCP}, 597 }, 598 Rules: &api.L7Rules{ 599 Kafka: []kafka.PortRule{ 600 {Topic: "foo"}, 601 }, 602 }, 603 }}, 604 }, 605 { 606 IngressCommonRule: api.IngressCommonRule{ 607 FromEndpoints: []api.EndpointSelector{api.WildcardEndpointSelector}, 608 }, 609 ToPorts: []api.PortRule{{ 610 Ports: []api.PortProtocol{ 611 {Port: "80", Protocol: api.ProtoTCP}, 612 }, 613 Rules: &api.L7Rules{ 614 Kafka: []kafka.PortRule{ 615 {Topic: "bar"}, 616 }, 617 }, 618 }}, 619 }, 620 }, 621 }, 622 } 623 624 fooRules := api.L7Rules{ 625 Kafka: []kafka.PortRule{{Topic: "foo"}}, 626 } 627 628 barRules := api.L7Rules{ 629 Kafka: []kafka.PortRule{{Topic: "bar"}}, 630 } 631 632 // The L3-dependent L7 rules are not merged together. 633 l7map = L7DataMap{ 634 td.cachedFooSelector: &PerSelectorPolicy{ 635 L7Rules: fooRules, 636 isRedirect: true, 637 }, 638 td.wildcardCachedSelector: &PerSelectorPolicy{ 639 L7Rules: barRules, 640 isRedirect: true, 641 }, 642 } 643 expected = NewL4PolicyMapWithValues(map[string]*L4Filter{"80/TCP": { 644 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 645 wildcard: td.wildcardCachedSelector, 646 L7Parser: "kafka", PerSelectorPolicies: l7map, Ingress: true, 647 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 648 td.cachedFooSelector: {nil}, 649 td.wildcardCachedSelector: {nil}, 650 }, 651 }}) 652 653 state = traceState{} 654 res, err = rule3.resolveIngressPolicy(td.testPolicyContext, toBar, &state, NewL4PolicyMap(), nil, nil) 655 require.NoError(t, err) 656 require.NotNil(t, res) 657 require.EqualValues(t, expected, res) 658 require.Equal(t, 1, state.selectedRules) 659 require.Equal(t, 1, state.matchedRules) 660 res.Detach(td.sc) 661 expected.Detach(td.sc) 662 } 663 664 func TestMergeL7PolicyEgress(t *testing.T) { 665 td := newTestData() 666 fromBar := &SearchContext{From: labels.ParseSelectLabelArray("bar")} 667 fromFoo := &SearchContext{From: labels.ParseSelectLabelArray("foo")} 668 669 fooSelector := []api.EndpointSelector{ 670 api.NewESFromLabels(labels.ParseSelectLabel("foo")), 671 } 672 673 rule1 := &rule{ 674 Rule: api.Rule{ 675 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 676 Egress: []api.EgressRule{ 677 { 678 // Note that this allows all on 80, so the result should wildcard HTTP 679 ToPorts: []api.PortRule{{ 680 Ports: []api.PortProtocol{ 681 {Port: "80", Protocol: api.ProtoTCP}, 682 }, 683 }}, 684 }, 685 { 686 ToPorts: []api.PortRule{{ 687 Ports: []api.PortProtocol{ 688 {Port: "80", Protocol: api.ProtoTCP}, 689 }, 690 Rules: &api.L7Rules{ 691 HTTP: []api.PortRuleHTTP{ 692 {Method: "GET", Path: "/public"}, 693 }, 694 }, 695 }}, 696 }, 697 { 698 EgressCommonRule: api.EgressCommonRule{ 699 ToEndpoints: fooSelector, 700 }, 701 ToPorts: []api.PortRule{{ 702 Ports: []api.PortProtocol{ 703 {Port: "80", Protocol: api.ProtoTCP}, 704 }, 705 Rules: &api.L7Rules{ 706 HTTP: []api.PortRuleHTTP{ 707 {Method: "GET", Path: "/private"}, 708 }, 709 }, 710 }}, 711 }, 712 }, 713 }, 714 } 715 716 expected := NewL4PolicyMapWithValues(map[string]*L4Filter{"80/TCP": { 717 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 718 wildcard: td.wildcardCachedSelector, 719 L7Parser: ParserTypeHTTP, 720 PerSelectorPolicies: L7DataMap{ 721 td.wildcardCachedSelector: &PerSelectorPolicy{ 722 L7Rules: api.L7Rules{ 723 HTTP: []api.PortRuleHTTP{{Path: "/public", Method: "GET"}, {}}, 724 }, 725 isRedirect: true, 726 }, 727 td.cachedFooSelector: &PerSelectorPolicy{ 728 L7Rules: api.L7Rules{ 729 HTTP: []api.PortRuleHTTP{{Path: "/private", Method: "GET"}}, 730 }, 731 isRedirect: true, 732 }, 733 }, 734 Ingress: false, 735 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 736 td.wildcardCachedSelector: {nil}, 737 td.cachedFooSelector: {nil}, 738 }, 739 }}) 740 741 state := traceState{} 742 res, err := rule1.resolveEgressPolicy(td.testPolicyContext, fromBar, &state, NewL4PolicyMap(), nil, nil) 743 require.NoError(t, err) 744 require.NotNil(t, res) 745 require.EqualValues(t, expected, res) 746 require.Equal(t, 1, state.selectedRules) 747 require.Equal(t, 1, state.matchedRules) 748 res.Detach(td.sc) 749 expected.Detach(td.sc) 750 751 state = traceState{} 752 res, err = rule1.resolveEgressPolicy(td.testPolicyContext, fromFoo, &state, NewL4PolicyMap(), nil, nil) 753 require.NoError(t, err) 754 require.Nil(t, res) 755 require.Equal(t, 0, state.selectedRules) 756 require.Equal(t, 0, state.matchedRules) 757 758 rule2 := &rule{ 759 Rule: api.Rule{ 760 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 761 Egress: []api.EgressRule{ 762 { 763 // Note that this allows all on 9092, so the result should wildcard Kafka 764 ToPorts: []api.PortRule{{ 765 Ports: []api.PortProtocol{ 766 {Port: "9092", Protocol: api.ProtoTCP}, 767 }, 768 }}, 769 }, 770 { 771 ToPorts: []api.PortRule{{ 772 Ports: []api.PortProtocol{ 773 {Port: "9092", Protocol: api.ProtoTCP}, 774 }, 775 Rules: &api.L7Rules{ 776 Kafka: []kafka.PortRule{ 777 {Topic: "foo"}, 778 }, 779 }, 780 }}, 781 }, 782 { 783 EgressCommonRule: api.EgressCommonRule{ 784 ToEndpoints: fooSelector, 785 }, 786 ToPorts: []api.PortRule{{ 787 Ports: []api.PortProtocol{ 788 {Port: "9092", Protocol: api.ProtoTCP}, 789 }, 790 Rules: &api.L7Rules{ 791 Kafka: []kafka.PortRule{ 792 {Topic: "foo"}, 793 }, 794 }, 795 }}, 796 }, 797 }, 798 }, 799 } 800 801 expected = NewL4PolicyMapWithValues(map[string]*L4Filter{"9092/TCP": { 802 Port: 9092, Protocol: api.ProtoTCP, U8Proto: 6, 803 wildcard: td.wildcardCachedSelector, 804 L7Parser: ParserTypeKafka, 805 PerSelectorPolicies: L7DataMap{ 806 td.wildcardCachedSelector: &PerSelectorPolicy{ 807 L7Rules: api.L7Rules{ 808 Kafka: []kafka.PortRule{{Topic: "foo"}, {}}, 809 }, 810 isRedirect: true, 811 }, 812 td.cachedFooSelector: &PerSelectorPolicy{ 813 L7Rules: api.L7Rules{ 814 Kafka: []kafka.PortRule{{Topic: "foo"}}, 815 }, 816 isRedirect: true, 817 }, 818 }, 819 Ingress: false, 820 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 821 td.cachedFooSelector: {nil}, 822 td.wildcardCachedSelector: {nil}, 823 }, 824 }}) 825 826 state = traceState{} 827 res, err = rule2.resolveEgressPolicy(td.testPolicyContext, fromBar, &state, NewL4PolicyMap(), nil, nil) 828 require.NoError(t, err) 829 require.NotNil(t, res) 830 require.EqualValues(t, expected, res) 831 require.Equal(t, 1, state.selectedRules) 832 require.Equal(t, 1, state.matchedRules) 833 res.Detach(td.sc) 834 expected.Detach(td.sc) 835 836 state = traceState{} 837 res, err = rule2.resolveEgressPolicy(td.testPolicyContext, fromFoo, &state, NewL4PolicyMap(), nil, nil) 838 require.NoError(t, err) 839 require.Nil(t, res) 840 require.Equal(t, 0, state.selectedRules) 841 require.Equal(t, 0, state.matchedRules) 842 843 // Resolve rule1's policy, then try to add rule2. 844 res, err = rule1.resolveEgressPolicy(td.testPolicyContext, fromBar, &state, NewL4PolicyMap(), nil, nil) 845 require.NoError(t, err) 846 require.NotNil(t, res) 847 res.Detach(td.sc) 848 849 // Similar to 'rule2', but with different topics for the l3-dependent 850 // rule and the l4-only rule. 851 rule3 := &rule{ 852 Rule: api.Rule{ 853 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 854 Egress: []api.EgressRule{ 855 { 856 EgressCommonRule: api.EgressCommonRule{ 857 ToEndpoints: fooSelector, 858 }, 859 ToPorts: []api.PortRule{{ 860 Ports: []api.PortProtocol{ 861 {Port: "80", Protocol: api.ProtoTCP}, 862 }, 863 Rules: &api.L7Rules{ 864 Kafka: []kafka.PortRule{ 865 {Topic: "foo"}, 866 }, 867 }, 868 }}, 869 }, 870 { 871 ToPorts: []api.PortRule{{ 872 Ports: []api.PortProtocol{ 873 {Port: "80", Protocol: api.ProtoTCP}, 874 }, 875 Rules: &api.L7Rules{ 876 Kafka: []kafka.PortRule{ 877 {Topic: "bar"}, 878 }, 879 }, 880 }}, 881 }, 882 }, 883 }, 884 } 885 886 fooRules := api.L7Rules{ 887 Kafka: []kafka.PortRule{{Topic: "foo"}}, 888 } 889 barRules := api.L7Rules{ 890 Kafka: []kafka.PortRule{{Topic: "bar"}}, 891 } 892 893 // The l3-dependent l7 rules are not merged together. 894 l7map := L7DataMap{ 895 td.cachedFooSelector: &PerSelectorPolicy{ 896 L7Rules: fooRules, 897 isRedirect: true, 898 }, 899 td.wildcardCachedSelector: &PerSelectorPolicy{ 900 L7Rules: barRules, 901 isRedirect: true, 902 }, 903 } 904 expected = NewL4PolicyMapWithValues(map[string]*L4Filter{"80/TCP": { 905 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 906 wildcard: td.wildcardCachedSelector, 907 L7Parser: "kafka", PerSelectorPolicies: l7map, Ingress: false, 908 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 909 td.cachedFooSelector: {nil}, 910 td.wildcardCachedSelector: {nil}, 911 }, 912 }}) 913 914 state = traceState{} 915 res, err = rule3.resolveEgressPolicy(td.testPolicyContext, fromBar, &state, NewL4PolicyMap(), nil, nil) 916 require.NoError(t, err) 917 require.NotNil(t, res) 918 require.EqualValues(t, expected, res) 919 require.Equal(t, 1, state.selectedRules) 920 require.Equal(t, 1, state.matchedRules) 921 res.Detach(td.sc) 922 expected.Detach(td.sc) 923 } 924 925 func TestRuleWithNoEndpointSelector(t *testing.T) { 926 apiRule1 := api.Rule{ 927 Ingress: []api.IngressRule{ 928 { 929 IngressCommonRule: api.IngressCommonRule{ 930 FromCIDR: []api.CIDR{ 931 "10.0.1.0/24", 932 "192.168.2.0", 933 "10.0.3.1", 934 "2001:db8::1/48", 935 "2001:db9::", 936 }, 937 }, 938 }, 939 }, 940 Egress: []api.EgressRule{ 941 { 942 EgressCommonRule: api.EgressCommonRule{ 943 ToCIDR: []api.CIDR{ 944 "10.1.0.0/16", 945 "2001:dbf::/64", 946 }, 947 }, 948 }, { 949 EgressCommonRule: api.EgressCommonRule{ 950 ToCIDRSet: []api.CIDRRule{{Cidr: api.CIDR("10.0.0.0/8"), ExceptCIDRs: []api.CIDR{"10.96.0.0/12"}}}, 951 }, 952 }, 953 }, 954 } 955 956 err := apiRule1.Sanitize() 957 require.NotNil(t, err) 958 } 959 960 func TestL3Policy(t *testing.T) { 961 apiRule1 := api.Rule{ 962 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 963 964 Ingress: []api.IngressRule{ 965 { 966 IngressCommonRule: api.IngressCommonRule{ 967 FromCIDR: []api.CIDR{ 968 "10.0.1.0/24", 969 "192.168.2.0", 970 "10.0.3.1", 971 "2001:db8::1/48", 972 "2001:db9::", 973 }, 974 }, 975 }, 976 }, 977 Egress: []api.EgressRule{ 978 { 979 EgressCommonRule: api.EgressCommonRule{ 980 ToCIDR: []api.CIDR{ 981 "10.1.0.0/16", 982 "2001:dbf::/64", 983 }, 984 }, 985 }, { 986 EgressCommonRule: api.EgressCommonRule{ 987 ToCIDRSet: []api.CIDRRule{{Cidr: api.CIDR("10.0.0.0/8"), ExceptCIDRs: []api.CIDR{"10.96.0.0/12"}}}, 988 }, 989 }, 990 }, 991 } 992 993 err := apiRule1.Sanitize() 994 require.NoError(t, err) 995 996 rule1 := &rule{Rule: apiRule1} 997 err = rule1.Sanitize() 998 require.NoError(t, err) 999 1000 // Must be parsable, make sure Validate fails when not. 1001 err = (&api.Rule{ 1002 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1003 Ingress: []api.IngressRule{{ 1004 IngressCommonRule: api.IngressCommonRule{ 1005 FromCIDR: []api.CIDR{"10.0.1..0/24"}, 1006 }, 1007 }}, 1008 }).Sanitize() 1009 require.NotNil(t, err) 1010 1011 // Test CIDRRule with no provided CIDR or ExceptionCIDR. 1012 // Should fail as CIDR is required. 1013 err = (&api.Rule{ 1014 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1015 Ingress: []api.IngressRule{{ 1016 IngressCommonRule: api.IngressCommonRule{ 1017 FromCIDRSet: []api.CIDRRule{{Cidr: "", ExceptCIDRs: nil}}, 1018 }, 1019 }}, 1020 }).Sanitize() 1021 require.NotNil(t, err) 1022 1023 // Test CIDRRule with only CIDR provided; should not fail, as ExceptionCIDR 1024 // is optional. 1025 err = (&api.Rule{ 1026 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1027 Ingress: []api.IngressRule{{ 1028 IngressCommonRule: api.IngressCommonRule{ 1029 FromCIDRSet: []api.CIDRRule{{Cidr: "10.0.1.0/24", ExceptCIDRs: nil}}, 1030 }, 1031 }}, 1032 }).Sanitize() 1033 require.NoError(t, err) 1034 1035 // Cannot provide just an IP to a CIDRRule; Cidr must be of format 1036 // <IP>/<prefix>. 1037 err = (&api.Rule{ 1038 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1039 Ingress: []api.IngressRule{{ 1040 IngressCommonRule: api.IngressCommonRule{ 1041 FromCIDRSet: []api.CIDRRule{{Cidr: "10.0.1.32", ExceptCIDRs: nil}}, 1042 }, 1043 }}, 1044 }).Sanitize() 1045 require.NotNil(t, err) 1046 1047 // Cannot exclude a range that is not part of the CIDR. 1048 err = (&api.Rule{ 1049 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1050 Ingress: []api.IngressRule{{ 1051 IngressCommonRule: api.IngressCommonRule{ 1052 FromCIDRSet: []api.CIDRRule{{Cidr: "10.0.0.0/10", ExceptCIDRs: []api.CIDR{"10.64.0.0/11"}}}, 1053 }, 1054 }}, 1055 }).Sanitize() 1056 require.NotNil(t, err) 1057 1058 // Must have a contiguous mask, make sure Validate fails when not. 1059 err = (&api.Rule{ 1060 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1061 Ingress: []api.IngressRule{{ 1062 IngressCommonRule: api.IngressCommonRule{ 1063 FromCIDR: []api.CIDR{"10.0.1.0/128.0.0.128"}, 1064 }, 1065 }}, 1066 }).Sanitize() 1067 require.NotNil(t, err) 1068 1069 // Prefix length must be in range for the address, make sure 1070 // Validate fails if given prefix length is out of range. 1071 err = (&api.Rule{ 1072 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1073 Ingress: []api.IngressRule{{ 1074 IngressCommonRule: api.IngressCommonRule{ 1075 FromCIDR: []api.CIDR{"10.0.1.0/34"}, 1076 }, 1077 }}, 1078 }).Sanitize() 1079 require.NotNil(t, err) 1080 } 1081 1082 func TestICMPPolicy(t *testing.T) { 1083 td := newTestData() 1084 var err error 1085 toBar := &SearchContext{To: labels.ParseSelectLabelArray("bar")} 1086 fromBar := &SearchContext{From: labels.ParseSelectLabelArray("bar")} 1087 1088 // A rule for ICMP 1089 icmpV4Type := intstr.FromInt(8) 1090 rule1 := &rule{ 1091 Rule: api.Rule{ 1092 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1093 Ingress: []api.IngressRule{ 1094 { 1095 ICMPs: api.ICMPRules{{ 1096 Fields: []api.ICMPField{{ 1097 Type: &icmpV4Type, 1098 }}, 1099 }}, 1100 }, 1101 }, 1102 Egress: []api.EgressRule{ 1103 { 1104 ICMPs: api.ICMPRules{{ 1105 Fields: []api.ICMPField{{ 1106 Type: &icmpV4Type, 1107 }}, 1108 }}, 1109 }, 1110 }, 1111 }, 1112 } 1113 1114 expected := NewL4Policy(0) 1115 expected.Ingress.PortRules.Upsert("8", 0, "ICMP", &L4Filter{ 1116 Port: 8, 1117 Protocol: api.ProtoICMP, 1118 U8Proto: u8proto.ProtoIDs["icmp"], 1119 Ingress: true, 1120 wildcard: td.wildcardCachedSelector, 1121 PerSelectorPolicies: L7DataMap{ 1122 td.wildcardCachedSelector: nil, 1123 }, 1124 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 1125 }) 1126 expected.Egress.PortRules.Upsert("8", 0, "ICMP", &L4Filter{ 1127 Port: 8, 1128 Protocol: api.ProtoICMP, 1129 U8Proto: u8proto.ProtoIDs["icmp"], 1130 Ingress: false, 1131 wildcard: td.wildcardCachedSelector, 1132 PerSelectorPolicies: L7DataMap{ 1133 td.wildcardCachedSelector: nil, 1134 }, 1135 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 1136 }) 1137 1138 ingressState := traceState{} 1139 egressState := traceState{} 1140 res := NewL4Policy(0) 1141 res.Ingress.PortRules, err = 1142 rule1.resolveIngressPolicy(td.testPolicyContext, toBar, &ingressState, NewL4PolicyMap(), nil, nil) 1143 require.NoError(t, err) 1144 require.NotNil(t, res.Ingress) 1145 1146 res.Egress.PortRules, err = 1147 rule1.resolveEgressPolicy(td.testPolicyContext, fromBar, &egressState, NewL4PolicyMap(), nil, nil) 1148 require.NoError(t, err) 1149 require.NotNil(t, res.Egress) 1150 1151 require.Equal(t, &expected, &res) 1152 require.Equal(t, 1, ingressState.selectedRules) 1153 require.Equal(t, 1, ingressState.matchedRules) 1154 require.Equal(t, 1, egressState.selectedRules) 1155 require.Equal(t, 1, egressState.matchedRules) 1156 1157 res.Detach(td.sc) 1158 expected.Detach(td.sc) 1159 1160 // A rule for Ports and ICMP 1161 rule2 := &rule{ 1162 Rule: api.Rule{ 1163 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1164 Ingress: []api.IngressRule{ 1165 { 1166 ToPorts: []api.PortRule{{ 1167 Ports: []api.PortProtocol{ 1168 {Port: "80", Protocol: api.ProtoTCP}, 1169 }, 1170 }}, 1171 ICMPs: api.ICMPRules{{ 1172 Fields: []api.ICMPField{{ 1173 Type: &icmpV4Type, 1174 }}, 1175 }}, 1176 }, 1177 }, 1178 }, 1179 } 1180 1181 expected = NewL4Policy(0) 1182 expected.Ingress.PortRules.Upsert("80", 0, "TCP", &L4Filter{ 1183 Port: 80, 1184 Protocol: api.ProtoTCP, 1185 U8Proto: u8proto.ProtoIDs["tcp"], 1186 Ingress: true, 1187 wildcard: td.wildcardCachedSelector, 1188 PerSelectorPolicies: L7DataMap{ 1189 td.wildcardCachedSelector: nil, 1190 }, 1191 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 1192 }) 1193 expected.Ingress.PortRules.Upsert("8", 0, "ICMP", &L4Filter{ 1194 Port: 8, 1195 Protocol: api.ProtoICMP, 1196 U8Proto: u8proto.ProtoIDs["icmp"], 1197 Ingress: true, 1198 wildcard: td.wildcardCachedSelector, 1199 PerSelectorPolicies: L7DataMap{ 1200 td.wildcardCachedSelector: nil, 1201 }, 1202 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 1203 }) 1204 1205 ingressState = traceState{} 1206 res = NewL4Policy(0) 1207 res.Ingress.PortRules, err = 1208 rule2.resolveIngressPolicy(td.testPolicyContext, toBar, &ingressState, NewL4PolicyMap(), nil, nil) 1209 require.NoError(t, err) 1210 require.NotNil(t, res.Ingress) 1211 1212 require.Equal(t, &expected, &res) 1213 require.Equal(t, 1, ingressState.selectedRules) 1214 require.Equal(t, 1, ingressState.matchedRules) 1215 1216 res.Detach(td.sc) 1217 expected.Detach(td.sc) 1218 1219 // A rule for ICMPv6 1220 icmpV6Type := intstr.FromInt(128) 1221 rule3 := &rule{ 1222 Rule: api.Rule{ 1223 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1224 Ingress: []api.IngressRule{ 1225 { 1226 ICMPs: api.ICMPRules{{ 1227 Fields: []api.ICMPField{{ 1228 Family: "IPv6", 1229 Type: &icmpV6Type, 1230 }}, 1231 }}, 1232 }, 1233 }, 1234 }, 1235 } 1236 1237 expected = NewL4Policy(0) 1238 expected.Ingress.PortRules.Upsert("128", 0, "ICMPV6", &L4Filter{ 1239 Port: 128, 1240 Protocol: api.ProtoICMPv6, 1241 U8Proto: u8proto.ProtoIDs["icmpv6"], 1242 Ingress: true, 1243 wildcard: td.wildcardCachedSelector, 1244 PerSelectorPolicies: L7DataMap{ 1245 td.wildcardCachedSelector: nil, 1246 }, 1247 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.wildcardCachedSelector: {nil}}, 1248 }) 1249 1250 ingressState = traceState{} 1251 res = NewL4Policy(0) 1252 res.Ingress.PortRules, err = 1253 rule3.resolveIngressPolicy(td.testPolicyContext, toBar, &ingressState, NewL4PolicyMap(), nil, nil) 1254 require.NoError(t, err) 1255 require.NotNil(t, res.Ingress) 1256 1257 require.Equal(t, &expected, &res) 1258 require.Equal(t, 1, ingressState.selectedRules) 1259 require.Equal(t, 1, ingressState.matchedRules) 1260 } 1261 1262 // Tests the restrictions of combining certain label-based L3 and L4 policies. 1263 // This ensures that the user is informed of policy combinations that are not 1264 // implemented in the datapath. 1265 func TestEgressRuleRestrictions(t *testing.T) { 1266 fooSelector := []api.EndpointSelector{ 1267 api.NewESFromLabels(labels.ParseSelectLabel("foo")), 1268 } 1269 1270 // Cannot combine ToEndpoints and ToCIDR 1271 apiRule1 := api.Rule{ 1272 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1273 Egress: []api.EgressRule{ 1274 { 1275 EgressCommonRule: api.EgressCommonRule{ 1276 ToCIDR: []api.CIDR{ 1277 "10.1.0.0/16", 1278 "2001:dbf::/64", 1279 }, 1280 ToEndpoints: fooSelector, 1281 }, 1282 }, 1283 }, 1284 } 1285 1286 err := apiRule1.Sanitize() 1287 require.NotNil(t, err) 1288 } 1289 1290 func TestPolicyEntityValidationEgress(t *testing.T) { 1291 r := api.Rule{ 1292 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1293 Egress: []api.EgressRule{ 1294 { 1295 EgressCommonRule: api.EgressCommonRule{ 1296 ToEntities: []api.Entity{api.EntityWorld}, 1297 }, 1298 }, 1299 }, 1300 } 1301 require.Nil(t, r.Sanitize()) 1302 require.Equal(t, 1, len(r.Egress[0].ToEntities)) 1303 1304 r.Egress[0].ToEntities = []api.Entity{api.EntityHost} 1305 require.Nil(t, r.Sanitize()) 1306 require.Equal(t, 1, len(r.Egress[0].ToEntities)) 1307 1308 r.Egress[0].ToEntities = []api.Entity{"trololo"} 1309 require.NotNil(t, r.Sanitize()) 1310 } 1311 1312 func TestPolicyEntityValidationIngress(t *testing.T) { 1313 r := api.Rule{ 1314 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1315 Ingress: []api.IngressRule{ 1316 { 1317 IngressCommonRule: api.IngressCommonRule{ 1318 FromEntities: []api.Entity{api.EntityWorld}, 1319 }, 1320 }, 1321 }, 1322 } 1323 require.Nil(t, r.Sanitize()) 1324 require.Equal(t, 1, len(r.Ingress[0].FromEntities)) 1325 1326 r.Ingress[0].FromEntities = []api.Entity{api.EntityHost} 1327 require.Nil(t, r.Sanitize()) 1328 require.Equal(t, 1, len(r.Ingress[0].FromEntities)) 1329 1330 r.Ingress[0].FromEntities = []api.Entity{"trololo"} 1331 require.NotNil(t, r.Sanitize()) 1332 } 1333 1334 func TestPolicyEntityValidationEntitySelectorsFill(t *testing.T) { 1335 r := api.Rule{ 1336 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1337 Ingress: []api.IngressRule{ 1338 { 1339 IngressCommonRule: api.IngressCommonRule{ 1340 FromEntities: []api.Entity{api.EntityWorld, api.EntityHost}, 1341 }, 1342 }, 1343 }, 1344 Egress: []api.EgressRule{ 1345 { 1346 EgressCommonRule: api.EgressCommonRule{ 1347 ToEntities: []api.Entity{api.EntityWorld, api.EntityHost}, 1348 }, 1349 }, 1350 }, 1351 } 1352 require.Nil(t, r.Sanitize()) 1353 require.Equal(t, 2, len(r.Ingress[0].FromEntities)) 1354 require.Equal(t, 2, len(r.Egress[0].ToEntities)) 1355 } 1356 1357 func TestL3RuleLabels(t *testing.T) { 1358 td := newTestData() 1359 ruleLabels := map[string]labels.LabelArray{ 1360 "rule0": labels.ParseLabelArray("name=apiRule0"), 1361 "rule1": labels.ParseLabelArray("name=apiRule1"), 1362 "rule2": labels.ParseLabelArray("name=apiRule2"), 1363 } 1364 1365 rules := map[string]api.Rule{ 1366 "rule0": { 1367 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1368 Labels: ruleLabels["rule0"], 1369 Ingress: []api.IngressRule{}, 1370 Egress: []api.EgressRule{}, 1371 }, 1372 "rule1": { 1373 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1374 Labels: ruleLabels["rule1"], 1375 Ingress: []api.IngressRule{ 1376 { 1377 IngressCommonRule: api.IngressCommonRule{ 1378 FromCIDR: []api.CIDR{"10.0.1.0/32"}, 1379 }, 1380 }, 1381 }, 1382 Egress: []api.EgressRule{ 1383 { 1384 EgressCommonRule: api.EgressCommonRule{ 1385 ToCIDR: []api.CIDR{"10.1.0.0/32"}, 1386 }, 1387 }, 1388 }, 1389 }, 1390 "rule2": { 1391 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1392 Labels: ruleLabels["rule2"], 1393 Ingress: []api.IngressRule{ 1394 { 1395 IngressCommonRule: api.IngressCommonRule{ 1396 FromCIDR: []api.CIDR{"10.0.2.0/32"}, 1397 }, 1398 }, 1399 }, 1400 Egress: []api.EgressRule{ 1401 { 1402 EgressCommonRule: api.EgressCommonRule{ 1403 ToCIDR: []api.CIDR{"10.2.0.0/32"}, 1404 }, 1405 }, 1406 }, 1407 }, 1408 } 1409 1410 testCases := []struct { 1411 description string // the description to print in asserts 1412 rulesToApply []string // the rules from the rules map to resolve, in order 1413 expectedIngressLabels map[string]labels.LabelArrayList // the slice of LabelArray we should see, per CIDR prefix 1414 expectedEgressLabels map[string]labels.LabelArrayList // the slice of LabelArray we should see, per CIDR prefix 1415 1416 }{ 1417 { 1418 description: "Empty rule that matches. Should not apply labels", 1419 rulesToApply: []string{"rule0"}, 1420 expectedIngressLabels: nil, 1421 expectedEgressLabels: nil, 1422 }, { 1423 description: "A rule that matches. Should apply labels", 1424 rulesToApply: []string{"rule1"}, 1425 expectedIngressLabels: map[string]labels.LabelArrayList{"10.0.1.0/32": {ruleLabels["rule1"]}}, 1426 expectedEgressLabels: map[string]labels.LabelArrayList{"10.1.0.0/32": {ruleLabels["rule1"]}}, 1427 }, { 1428 description: "Multiple matching rules. Should apply labels from all that have rule entries", 1429 rulesToApply: []string{"rule0", "rule1", "rule2"}, 1430 expectedIngressLabels: map[string]labels.LabelArrayList{ 1431 "10.0.1.0/32": {ruleLabels["rule1"]}, 1432 "10.0.2.0/32": {ruleLabels["rule2"]}}, 1433 expectedEgressLabels: map[string]labels.LabelArrayList{ 1434 "10.1.0.0/32": {ruleLabels["rule1"]}, 1435 "10.2.0.0/32": {ruleLabels["rule2"]}}, 1436 }} 1437 1438 // endpoint selector for all tests 1439 toBar := &SearchContext{To: labels.ParseSelectLabelArray("bar"), Trace: TRACE_VERBOSE} 1440 fromBar := &SearchContext{From: labels.ParseSelectLabelArray("bar"), Trace: TRACE_VERBOSE} 1441 1442 for _, test := range testCases { 1443 finalPolicy := NewL4Policy(0) 1444 for _, r := range test.rulesToApply { 1445 apiRule := rules[r] 1446 err := apiRule.Sanitize() 1447 require.NoError(t, err, "Cannot sanitize Rule: %+v", apiRule) 1448 1449 rule := &rule{Rule: apiRule} 1450 1451 _, err = rule.resolveIngressPolicy(td.testPolicyContext, toBar, &traceState{}, finalPolicy.Ingress.PortRules, nil, nil) 1452 require.NoError(t, err) 1453 _, err = rule.resolveEgressPolicy(td.testPolicyContext, fromBar, &traceState{}, finalPolicy.Egress.PortRules, nil, nil) 1454 require.NoError(t, err) 1455 } 1456 // For debugging the test: 1457 //require.EqualValues(t, NewL4PolicyMap(), finalPolicy.Ingress) 1458 1459 type expectedResult map[string]labels.LabelArrayList 1460 mapDirectionalResultsToExpectedOutput := map[*L4Filter]expectedResult{ 1461 finalPolicy.Ingress.PortRules.ExactLookup("0", 0, "ANY"): test.expectedIngressLabels, 1462 finalPolicy.Egress.PortRules.ExactLookup("0", 0, "ANY"): test.expectedEgressLabels, 1463 } 1464 for filter, exp := range mapDirectionalResultsToExpectedOutput { 1465 if len(exp) > 0 { 1466 for cidr, rule := range exp { 1467 matches := false 1468 for _, origin := range filter.RuleOrigin { 1469 if origin.Equals(rule) { 1470 matches = true 1471 break 1472 } 1473 } 1474 require.True(t, matches, fmt.Sprintf("%s: expected filter %+v to be derived from rule %s", test.description, filter, rule)) 1475 1476 matches = false 1477 for sel := range filter.PerSelectorPolicies { 1478 cidrLabels := labels.ParseLabelArray("cidr:" + cidr) 1479 t.Logf("Testing %+v", cidrLabels) 1480 if matches = sel.(*identitySelector).source.(*labelIdentitySelector).xxxMatches(cidrLabels); matches { 1481 break 1482 } 1483 } 1484 require.True(t, matches, fmt.Sprintf("%s: expected cidr %s to match filter %+v", test.description, cidr, filter)) 1485 } 1486 } 1487 } 1488 } 1489 } 1490 1491 func TestL4RuleLabels(t *testing.T) { 1492 td := newTestData() 1493 ruleLabels := map[string]labels.LabelArray{ 1494 "rule0": labels.ParseLabelArray("name=apiRule0"), 1495 "rule1": labels.ParseLabelArray("name=apiRule1"), 1496 "rule2": labels.ParseLabelArray("name=apiRule2"), 1497 } 1498 1499 rules := map[string]api.Rule{ 1500 "rule0": { 1501 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1502 Labels: ruleLabels["rule0"], 1503 Ingress: []api.IngressRule{}, 1504 Egress: []api.EgressRule{}, 1505 }, 1506 1507 "rule1": { 1508 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1509 Labels: ruleLabels["rule1"], 1510 Ingress: []api.IngressRule{ 1511 { 1512 ToPorts: []api.PortRule{{ 1513 Ports: []api.PortProtocol{{Port: "1010", Protocol: api.ProtoTCP}}, 1514 }}, 1515 }, 1516 }, 1517 Egress: []api.EgressRule{ 1518 { 1519 ToPorts: []api.PortRule{{ 1520 Ports: []api.PortProtocol{{Port: "1100", Protocol: api.ProtoTCP}}, 1521 }}, 1522 }, 1523 }, 1524 }, 1525 "rule2": { 1526 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 1527 Labels: ruleLabels["rule2"], 1528 Ingress: []api.IngressRule{ 1529 { 1530 ToPorts: []api.PortRule{{ 1531 Ports: []api.PortProtocol{{Port: "1020", Protocol: api.ProtoTCP}}, 1532 }}, 1533 }, 1534 }, 1535 Egress: []api.EgressRule{ 1536 { 1537 ToPorts: []api.PortRule{{ 1538 Ports: []api.PortProtocol{{Port: "1200", Protocol: api.ProtoTCP}}, 1539 }}, 1540 }, 1541 }, 1542 }, 1543 } 1544 1545 testCases := []struct { 1546 description string // the description to print in asserts 1547 rulesToApply []string // the rules from the rules map to resolve, in order 1548 expectedIngressLabels map[string]labels.LabelArrayList // the slice of LabelArray we should see, in order 1549 expectedEgressLabels map[string]labels.LabelArrayList // the slice of LabelArray we should see, in order 1550 1551 }{ 1552 { 1553 description: "Empty rule that matches. Should not apply labels", 1554 rulesToApply: []string{"rule0"}, 1555 expectedIngressLabels: map[string]labels.LabelArrayList{}, 1556 expectedEgressLabels: map[string]labels.LabelArrayList{}, 1557 }, 1558 { 1559 description: "A rule that matches. Should apply labels", 1560 rulesToApply: []string{"rule1"}, 1561 expectedIngressLabels: map[string]labels.LabelArrayList{"1010/TCP": {ruleLabels["rule1"]}}, 1562 expectedEgressLabels: map[string]labels.LabelArrayList{"1100/TCP": {ruleLabels["rule1"]}}, 1563 }, { 1564 description: "Multiple matching rules. Should apply labels from all that have rule entries", 1565 rulesToApply: []string{"rule0", "rule1", "rule2"}, 1566 expectedIngressLabels: map[string]labels.LabelArrayList{ 1567 "1010/TCP": {ruleLabels["rule1"]}, 1568 "1020/TCP": {ruleLabels["rule2"]}}, 1569 expectedEgressLabels: map[string]labels.LabelArrayList{ 1570 "1100/TCP": {ruleLabels["rule1"]}, 1571 "1200/TCP": {ruleLabels["rule2"]}}, 1572 }} 1573 1574 // endpoint selector for all tests 1575 toBar := &SearchContext{To: labels.ParseSelectLabelArray("bar")} 1576 fromBar := &SearchContext{From: labels.ParseSelectLabelArray("bar")} 1577 1578 for _, test := range testCases { 1579 finalPolicy := NewL4Policy(0) 1580 for _, r := range test.rulesToApply { 1581 apiRule := rules[r] 1582 err := apiRule.Sanitize() 1583 require.NoError(t, err, "Cannot sanitize api.Rule: %+v", apiRule) 1584 1585 rule := &rule{Rule: apiRule} 1586 1587 rule.resolveIngressPolicy(td.testPolicyContext, toBar, &traceState{}, finalPolicy.Ingress.PortRules, nil, nil) 1588 rule.resolveEgressPolicy(td.testPolicyContext, fromBar, &traceState{}, finalPolicy.Egress.PortRules, nil, nil) 1589 } 1590 1591 require.Equal(t, len(test.expectedIngressLabels), finalPolicy.Ingress.PortRules.Len(), fmt.Sprintf(test.description)) 1592 for portProto := range test.expectedIngressLabels { 1593 portProtoSlice := strings.Split(portProto, "/") 1594 out := finalPolicy.Ingress.PortRules.ExactLookup(portProtoSlice[0], 0, portProtoSlice[1]) 1595 require.NotNil(t, out, test.description) 1596 require.Equal(t, 1, len(out.RuleOrigin), fmt.Sprintf(test.description)) 1597 require.EqualValues(t, test.expectedIngressLabels[portProto], out.RuleOrigin[out.wildcard], fmt.Sprintf(test.description)) 1598 } 1599 1600 require.Equal(t, len(test.expectedEgressLabels), finalPolicy.Egress.PortRules.Len(), fmt.Sprintf(test.description)) 1601 for portProto := range test.expectedEgressLabels { 1602 portProtoSlice := strings.Split(portProto, "/") 1603 out := finalPolicy.Egress.PortRules.ExactLookup(portProtoSlice[0], 0, portProtoSlice[1]) 1604 require.NotNil(t, out, test.description) 1605 1606 require.Equal(t, 1, len(out.RuleOrigin), fmt.Sprintf(test.description)) 1607 require.EqualValues(t, test.expectedEgressLabels[portProto], out.RuleOrigin[out.wildcard], fmt.Sprintf(test.description)) 1608 } 1609 finalPolicy.Detach(td.sc) 1610 } 1611 } 1612 1613 var ( 1614 labelsA = labels.LabelArray{ 1615 labels.NewLabel("id", "a", labels.LabelSourceK8s), 1616 } 1617 1618 endpointSelectorA = api.NewESFromLabels(labels.ParseSelectLabel("id=a")) 1619 1620 labelsB = labels.LabelArray{ 1621 labels.NewLabel("id1", "b", labels.LabelSourceK8s), 1622 labels.NewLabel("id2", "t", labels.LabelSourceK8s), 1623 } 1624 1625 labelsC = labels.LabelArray{ 1626 labels.NewLabel("id", "t", labels.LabelSourceK8s), 1627 } 1628 1629 endpointSelectorC = api.NewESFromLabels(labels.ParseSelectLabel("id=t")) 1630 1631 ctxAToB = SearchContext{From: labelsA, To: labelsB, Trace: TRACE_VERBOSE} 1632 ctxAToC = SearchContext{From: labelsA, To: labelsC, Trace: TRACE_VERBOSE} 1633 ) 1634 1635 func expectResult(t *testing.T, expected, obtained api.Decision, buffer *bytes.Buffer) { 1636 if obtained != expected { 1637 t.Errorf("Unexpected result: obtained=%v, expected=%v", obtained, expected) 1638 t.Log(buffer) 1639 } 1640 } 1641 1642 func checkIngress(t *testing.T, repo *Repository, ctx *SearchContext, verdict api.Decision) { 1643 repo.Mutex.RLock() 1644 defer repo.Mutex.RUnlock() 1645 1646 buffer := new(bytes.Buffer) 1647 ctx.Logging = stdlog.New(buffer, "", 0) 1648 expectResult(t, verdict, repo.AllowsIngressRLocked(ctx), buffer) 1649 } 1650 1651 func checkEgress(t *testing.T, repo *Repository, ctx *SearchContext, verdict api.Decision) { 1652 repo.Mutex.RLock() 1653 defer repo.Mutex.RUnlock() 1654 1655 buffer := new(bytes.Buffer) 1656 ctx.Logging = stdlog.New(buffer, "", 0) 1657 expectResult(t, verdict, repo.AllowsEgressRLocked(ctx), buffer) 1658 } 1659 func TestIngressAllowAll(t *testing.T) { 1660 td := newTestData() 1661 repo := td.repo 1662 repo.MustAddList(api.Rules{ 1663 &api.Rule{ 1664 EndpointSelector: endpointSelectorC, 1665 Ingress: []api.IngressRule{ 1666 { 1667 // Allow all L3&L4 ingress rule 1668 IngressCommonRule: api.IngressCommonRule{ 1669 FromEndpoints: []api.EndpointSelector{ 1670 api.WildcardEndpointSelector, 1671 }, 1672 }, 1673 }, 1674 }, 1675 }, 1676 }) 1677 1678 checkIngress(t, repo, &ctxAToB, api.Denied) 1679 checkIngress(t, repo, &ctxAToC, api.Allowed) 1680 1681 ctxAToC80 := ctxAToC 1682 ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 1683 checkIngress(t, repo, &ctxAToC80, api.Allowed) 1684 1685 ctxAToC90 := ctxAToC 1686 ctxAToC90.DPorts = []*models.Port{{Name: "port-90", Protocol: models.PortProtocolTCP}} 1687 checkIngress(t, repo, &ctxAToC90, api.Allowed) 1688 } 1689 1690 func TestIngressAllowAllL4Overlap(t *testing.T) { 1691 td := newTestData() 1692 repo := td.repo 1693 repo.MustAddList(api.Rules{ 1694 &api.Rule{ 1695 EndpointSelector: endpointSelectorC, 1696 Ingress: []api.IngressRule{ 1697 { 1698 // Allow all L3&L4 ingress rule 1699 IngressCommonRule: api.IngressCommonRule{ 1700 FromEndpoints: []api.EndpointSelector{ 1701 api.WildcardEndpointSelector, 1702 }, 1703 }, 1704 }, 1705 { 1706 // This rule is a subset of the above 1707 // rule and should *NOT* restrict to 1708 // port 80 only 1709 ToPorts: []api.PortRule{{ 1710 Ports: []api.PortProtocol{ 1711 {Port: "80", Protocol: api.ProtoTCP}, 1712 }, 1713 }}, 1714 }, 1715 }, 1716 }, 1717 }) 1718 1719 ctxAToC80 := ctxAToC 1720 ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 1721 checkIngress(t, repo, &ctxAToC80, api.Allowed) 1722 1723 ctxAToC90 := ctxAToC 1724 ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 1725 checkIngress(t, repo, &ctxAToC90, api.Allowed) 1726 } 1727 1728 func TestIngressAllowAllL4OverlapNamedPort(t *testing.T) { 1729 td := newTestData() 1730 repo := td.repo 1731 repo.MustAddList(api.Rules{ 1732 &api.Rule{ 1733 EndpointSelector: endpointSelectorC, 1734 Ingress: []api.IngressRule{ 1735 { 1736 // Allow all L3&L4 ingress rule 1737 IngressCommonRule: api.IngressCommonRule{ 1738 FromEndpoints: []api.EndpointSelector{ 1739 api.WildcardEndpointSelector, 1740 }, 1741 }, 1742 }, 1743 { 1744 // This rule is a subset of the above 1745 // rule and should *NOT* restrict to 1746 // port 80 only 1747 ToPorts: []api.PortRule{{ 1748 Ports: []api.PortProtocol{ 1749 {Port: "port-80", Protocol: api.ProtoTCP}, 1750 }, 1751 }}, 1752 }, 1753 }, 1754 }, 1755 }) 1756 1757 ctxAToC80 := ctxAToC 1758 ctxAToC80.DPorts = []*models.Port{{Name: "port-80", Protocol: models.PortProtocolTCP}} 1759 checkIngress(t, repo, &ctxAToC80, api.Allowed) 1760 1761 ctxAToC90 := ctxAToC 1762 ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 1763 checkIngress(t, repo, &ctxAToC90, api.Allowed) 1764 } 1765 1766 func TestIngressL4AllowAll(t *testing.T) { 1767 td := newTestData() 1768 repo := td.repo 1769 repo.MustAddList(api.Rules{ 1770 &api.Rule{ 1771 EndpointSelector: endpointSelectorC, 1772 Ingress: []api.IngressRule{ 1773 { 1774 ToPorts: []api.PortRule{{ 1775 Ports: []api.PortProtocol{ 1776 {Port: "80", Protocol: api.ProtoTCP}, 1777 }, 1778 }}, 1779 }, 1780 }, 1781 }, 1782 }) 1783 1784 ctxAToC80 := ctxAToC 1785 ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 1786 checkIngress(t, repo, &ctxAToC80, api.Allowed) 1787 1788 ctxAToC90 := ctxAToC 1789 ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 1790 checkIngress(t, repo, &ctxAToC90, api.Denied) 1791 1792 ctxAToCNamed90 := ctxAToC 1793 ctxAToCNamed90.DPorts = []*models.Port{{Name: "port-90", Protocol: models.PortProtocolTCP}} 1794 checkIngress(t, repo, &ctxAToCNamed90, api.Denied) 1795 1796 l4IngressPolicy, err := repo.ResolveL4IngressPolicy(&ctxAToC80) 1797 require.NoError(t, err) 1798 1799 filter := l4IngressPolicy.ExactLookup("80", 0, "TCP") 1800 require.NotNil(t, filter) 1801 require.Equal(t, uint16(80), filter.Port) 1802 require.True(t, filter.Ingress) 1803 1804 require.Equal(t, 1, len(filter.PerSelectorPolicies)) 1805 require.Nil(t, filter.PerSelectorPolicies[td.wildcardCachedSelector]) 1806 l4IngressPolicy.Detach(repo.GetSelectorCache()) 1807 } 1808 1809 func TestIngressL4AllowAllNamedPort(t *testing.T) { 1810 td := newTestData() 1811 repo := td.repo 1812 repo.MustAddList(api.Rules{ 1813 &api.Rule{ 1814 EndpointSelector: endpointSelectorC, 1815 Ingress: []api.IngressRule{ 1816 { 1817 ToPorts: []api.PortRule{{ 1818 Ports: []api.PortProtocol{ 1819 {Port: "port-80", Protocol: api.ProtoTCP}, 1820 }, 1821 }}, 1822 }, 1823 }, 1824 }, 1825 }) 1826 1827 ctxAToCNamed80 := ctxAToC 1828 ctxAToCNamed80.DPorts = []*models.Port{{Name: "port-80", Protocol: models.PortProtocolTCP}} 1829 checkIngress(t, repo, &ctxAToCNamed80, api.Allowed) 1830 1831 ctxAToC80 := ctxAToC 1832 ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 1833 checkIngress(t, repo, &ctxAToC80, api.Denied) 1834 1835 ctxAToC90 := ctxAToC 1836 ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 1837 checkIngress(t, repo, &ctxAToC90, api.Denied) 1838 1839 ctxAToCNamed90 := ctxAToC 1840 ctxAToCNamed90.DPorts = []*models.Port{{Name: "port-90", Protocol: models.PortProtocolTCP}} 1841 checkIngress(t, repo, &ctxAToCNamed90, api.Denied) 1842 1843 l4IngressPolicy, err := repo.ResolveL4IngressPolicy(&ctxAToCNamed80) 1844 require.NoError(t, err) 1845 1846 filter := l4IngressPolicy.ExactLookup("port-80", 0, "TCP") 1847 require.NotNil(t, filter) 1848 require.Equal(t, uint16(0), filter.Port) 1849 require.Equal(t, "port-80", filter.PortName) 1850 require.True(t, filter.Ingress) 1851 1852 require.Equal(t, 1, len(filter.PerSelectorPolicies)) 1853 require.Nil(t, filter.PerSelectorPolicies[td.wildcardCachedSelector]) 1854 l4IngressPolicy.Detach(repo.GetSelectorCache()) 1855 } 1856 1857 func TestEgressAllowAll(t *testing.T) { 1858 td := newTestData() 1859 repo := td.repo 1860 repo.MustAddList(api.Rules{ 1861 &api.Rule{ 1862 EndpointSelector: endpointSelectorA, 1863 Egress: []api.EgressRule{ 1864 { 1865 EgressCommonRule: api.EgressCommonRule{ 1866 ToEndpoints: []api.EndpointSelector{ 1867 api.WildcardEndpointSelector, 1868 }, 1869 }, 1870 }, 1871 }, 1872 }, 1873 }) 1874 1875 checkEgress(t, repo, &ctxAToB, api.Allowed) 1876 checkEgress(t, repo, &ctxAToC, api.Allowed) 1877 1878 ctxAToC80 := ctxAToC 1879 ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 1880 checkEgress(t, repo, &ctxAToC80, api.Allowed) 1881 1882 ctxAToC90 := ctxAToC 1883 ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 1884 checkEgress(t, repo, &ctxAToC90, api.Allowed) 1885 } 1886 1887 func TestEgressL4AllowAll(t *testing.T) { 1888 td := newTestData() 1889 repo := td.repo 1890 repo.MustAddList(api.Rules{ 1891 &api.Rule{ 1892 EndpointSelector: endpointSelectorA, 1893 Egress: []api.EgressRule{ 1894 { 1895 ToPorts: []api.PortRule{{ 1896 Ports: []api.PortProtocol{ 1897 {Port: "80", Protocol: api.ProtoTCP}, 1898 }, 1899 }}, 1900 }, 1901 }, 1902 }, 1903 }) 1904 1905 ctxAToC80 := ctxAToC 1906 ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 1907 checkEgress(t, repo, &ctxAToC80, api.Allowed) 1908 1909 ctxAToC90 := ctxAToC 1910 ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 1911 checkEgress(t, repo, &ctxAToC90, api.Denied) 1912 1913 buffer := new(bytes.Buffer) 1914 ctx := SearchContext{From: labelsA, Trace: TRACE_VERBOSE} 1915 ctx.Logging = stdlog.New(buffer, "", 0) 1916 1917 l4EgressPolicy, err := repo.ResolveL4EgressPolicy(&ctx) 1918 require.NoError(t, err) 1919 1920 t.Log(buffer) 1921 1922 filter := l4EgressPolicy.ExactLookup("80", 0, "TCP") 1923 require.NotNil(t, filter) 1924 require.Equal(t, uint16(80), filter.Port) 1925 require.Equal(t, false, filter.Ingress) 1926 1927 require.Equal(t, 1, len(filter.PerSelectorPolicies)) 1928 require.Nil(t, filter.PerSelectorPolicies[td.wildcardCachedSelector]) 1929 l4EgressPolicy.Detach(repo.GetSelectorCache()) 1930 } 1931 1932 func TestEgressL4AllowWorld(t *testing.T) { 1933 td := newTestData() 1934 repo := td.repo 1935 repo.MustAddList(api.Rules{ 1936 &api.Rule{ 1937 EndpointSelector: endpointSelectorA, 1938 Egress: []api.EgressRule{ 1939 { 1940 EgressCommonRule: api.EgressCommonRule{ 1941 ToEntities: []api.Entity{api.EntityWorld}, 1942 }, 1943 ToPorts: []api.PortRule{{ 1944 Ports: []api.PortProtocol{ 1945 {Port: "80", Protocol: api.ProtoTCP}, 1946 }, 1947 }}, 1948 }, 1949 }, 1950 }, 1951 }) 1952 1953 worldLabel := labels.ParseSelectLabelArray("reserved:world") 1954 ctxAToWorld80 := SearchContext{From: labelsA, To: worldLabel, Trace: TRACE_VERBOSE} 1955 ctxAToWorld80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 1956 checkEgress(t, repo, &ctxAToWorld80, api.Allowed) 1957 1958 ctxAToWorld90 := ctxAToWorld80 1959 ctxAToWorld90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 1960 checkEgress(t, repo, &ctxAToWorld90, api.Denied) 1961 1962 // Pod to pod must be denied on port 80 and 90, only world was whitelisted 1963 fooLabel := labels.ParseSelectLabelArray("k8s:app=foo") 1964 ctxAToFoo := SearchContext{From: labelsA, To: fooLabel, Trace: TRACE_VERBOSE, 1965 DPorts: []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}}} 1966 checkEgress(t, repo, &ctxAToFoo, api.Denied) 1967 ctxAToFoo90 := ctxAToFoo 1968 ctxAToFoo90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 1969 checkEgress(t, repo, &ctxAToFoo90, api.Denied) 1970 1971 buffer := new(bytes.Buffer) 1972 ctx := SearchContext{From: labelsA, Trace: TRACE_VERBOSE} 1973 ctx.Logging = stdlog.New(buffer, "", 0) 1974 1975 l4EgressPolicy, err := repo.ResolveL4EgressPolicy(&ctx) 1976 require.NoError(t, err) 1977 1978 t.Log(buffer) 1979 1980 filter := l4EgressPolicy.ExactLookup("80", 0, "TCP") 1981 require.NotNil(t, filter) 1982 require.Equal(t, uint16(80), filter.Port) 1983 require.Equal(t, false, filter.Ingress) 1984 1985 require.Equal(t, 3, len(filter.PerSelectorPolicies)) 1986 l4EgressPolicy.Detach(repo.GetSelectorCache()) 1987 } 1988 1989 func TestEgressL4AllowAllEntity(t *testing.T) { 1990 td := newTestData() 1991 repo := td.repo 1992 repo.MustAddList(api.Rules{ 1993 &api.Rule{ 1994 EndpointSelector: endpointSelectorA, 1995 Egress: []api.EgressRule{ 1996 { 1997 EgressCommonRule: api.EgressCommonRule{ 1998 ToEntities: []api.Entity{api.EntityAll}, 1999 }, 2000 ToPorts: []api.PortRule{{ 2001 Ports: []api.PortProtocol{ 2002 {Port: "80", Protocol: api.ProtoTCP}, 2003 }, 2004 }}, 2005 }, 2006 }, 2007 }, 2008 }) 2009 2010 worldLabel := labels.ParseSelectLabelArray("reserved:world") 2011 ctxAToWorld80 := SearchContext{From: labelsA, To: worldLabel, Trace: TRACE_VERBOSE} 2012 ctxAToWorld80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 2013 checkEgress(t, repo, &ctxAToWorld80, api.Allowed) 2014 2015 ctxAToWorld90 := ctxAToWorld80 2016 ctxAToWorld90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 2017 checkEgress(t, repo, &ctxAToWorld90, api.Denied) 2018 2019 // Pod to pod must be allowed on port 80, denied on port 90 (all identity) 2020 fooLabel := labels.ParseSelectLabelArray("k8s:app=foo") 2021 ctxAToFoo := SearchContext{From: labelsA, To: fooLabel, Trace: TRACE_VERBOSE, 2022 DPorts: []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}}} 2023 checkEgress(t, repo, &ctxAToFoo, api.Allowed) 2024 ctxAToFoo90 := ctxAToFoo 2025 ctxAToFoo90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 2026 checkEgress(t, repo, &ctxAToFoo90, api.Denied) 2027 2028 buffer := new(bytes.Buffer) 2029 ctx := SearchContext{From: labelsA, Trace: TRACE_VERBOSE} 2030 ctx.Logging = stdlog.New(buffer, "", 0) 2031 2032 l4EgressPolicy, err := repo.ResolveL4EgressPolicy(&ctx) 2033 require.NoError(t, err) 2034 2035 t.Log(buffer) 2036 2037 filter := l4EgressPolicy.ExactLookup("80", 0, "TCP") 2038 require.NotNil(t, filter) 2039 require.Equal(t, uint16(80), filter.Port) 2040 require.Equal(t, false, filter.Ingress) 2041 2042 require.Equal(t, 1, len(filter.PerSelectorPolicies)) 2043 l4EgressPolicy.Detach(repo.GetSelectorCache()) 2044 } 2045 2046 func TestEgressL3AllowWorld(t *testing.T) { 2047 td := newTestData() 2048 repo := td.repo 2049 repo.MustAddList(api.Rules{ 2050 &api.Rule{ 2051 EndpointSelector: endpointSelectorA, 2052 Egress: []api.EgressRule{ 2053 { 2054 EgressCommonRule: api.EgressCommonRule{ 2055 ToEntities: []api.Entity{api.EntityWorld}, 2056 }, 2057 }, 2058 }, 2059 }, 2060 }) 2061 2062 worldLabel := labels.ParseSelectLabelArray("reserved:world") 2063 ctxAToWorld80 := SearchContext{From: labelsA, To: worldLabel, Trace: TRACE_VERBOSE} 2064 ctxAToWorld80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 2065 checkEgress(t, repo, &ctxAToWorld80, api.Allowed) 2066 2067 ctxAToWorld90 := ctxAToWorld80 2068 ctxAToWorld90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 2069 checkEgress(t, repo, &ctxAToWorld90, api.Allowed) 2070 2071 // Pod to pod must be denied on port 80 and 90, only world was whitelisted 2072 fooLabel := labels.ParseSelectLabelArray("k8s:app=foo") 2073 ctxAToFoo := SearchContext{From: labelsA, To: fooLabel, Trace: TRACE_VERBOSE, 2074 DPorts: []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}}} 2075 checkEgress(t, repo, &ctxAToFoo, api.Denied) 2076 ctxAToFoo90 := ctxAToFoo 2077 ctxAToFoo90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 2078 checkEgress(t, repo, &ctxAToFoo90, api.Denied) 2079 2080 buffer := new(bytes.Buffer) 2081 ctx := SearchContext{From: labelsA, Trace: TRACE_VERBOSE} 2082 ctx.Logging = stdlog.New(buffer, "", 0) 2083 } 2084 2085 func TestEgressL3AllowAllEntity(t *testing.T) { 2086 td := newTestData() 2087 repo := td.repo 2088 repo.MustAddList(api.Rules{ 2089 &api.Rule{ 2090 EndpointSelector: endpointSelectorA, 2091 Egress: []api.EgressRule{ 2092 { 2093 EgressCommonRule: api.EgressCommonRule{ 2094 ToEntities: []api.Entity{api.EntityAll}, 2095 }, 2096 }, 2097 }, 2098 }, 2099 }) 2100 2101 worldLabel := labels.ParseSelectLabelArray("reserved:world") 2102 ctxAToWorld80 := SearchContext{From: labelsA, To: worldLabel, Trace: TRACE_VERBOSE} 2103 ctxAToWorld80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 2104 checkEgress(t, repo, &ctxAToWorld80, api.Allowed) 2105 2106 ctxAToWorld90 := ctxAToWorld80 2107 ctxAToWorld90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 2108 checkEgress(t, repo, &ctxAToWorld90, api.Allowed) 2109 2110 // Pod to pod must be allowed on both port 80 and 90 (L3 only rule) 2111 fooLabel := labels.ParseSelectLabelArray("k8s:app=foo") 2112 ctxAToFoo := SearchContext{From: labelsA, To: fooLabel, Trace: TRACE_VERBOSE, 2113 DPorts: []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}}} 2114 checkEgress(t, repo, &ctxAToFoo, api.Allowed) 2115 ctxAToFoo90 := ctxAToFoo 2116 ctxAToFoo90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 2117 checkEgress(t, repo, &ctxAToFoo90, api.Allowed) 2118 2119 buffer := new(bytes.Buffer) 2120 ctx := SearchContext{From: labelsA, Trace: TRACE_VERBOSE} 2121 ctx.Logging = stdlog.New(buffer, "", 0) 2122 } 2123 2124 func TestL4WildcardMerge(t *testing.T) { 2125 2126 // First, test implicit case. 2127 // 2128 // Test the case where if we have rules that select the same endpoint on the 2129 // same port-protocol tuple with one that is L4-only, and the other applying 2130 // at L4 and L7, that the L4-only rule shadows the L4-L7 rule. This is because 2131 // L4-only rule implicitly allows all traffic at L7, so the L7-related 2132 // parts of the L4-L7 rule are useless. 2133 td := newTestData() 2134 repo := td.repo 2135 repo.MustAddList(api.Rules{&api.Rule{ 2136 EndpointSelector: endpointSelectorA, 2137 Ingress: []api.IngressRule{ 2138 { 2139 IngressCommonRule: api.IngressCommonRule{ 2140 FromEndpoints: []api.EndpointSelector{endpointSelectorC}, 2141 }, 2142 ToPorts: []api.PortRule{{ 2143 Ports: []api.PortProtocol{ 2144 {Port: "80", Protocol: api.ProtoTCP}, 2145 }, 2146 Rules: &api.L7Rules{ 2147 HTTP: []api.PortRuleHTTP{ 2148 {Method: "GET", Path: "/"}, 2149 }, 2150 }, 2151 }}, 2152 }, 2153 { 2154 ToPorts: []api.PortRule{{ 2155 Ports: []api.PortProtocol{ 2156 {Port: "80", Protocol: api.ProtoTCP}, 2157 }, 2158 }}, 2159 }, 2160 { 2161 IngressCommonRule: api.IngressCommonRule{ 2162 FromEndpoints: []api.EndpointSelector{endpointSelectorC}, 2163 }, 2164 ToPorts: []api.PortRule{{ 2165 Ports: []api.PortProtocol{ 2166 {Port: "7000", Protocol: api.ProtoTCP}, 2167 }, 2168 Rules: &api.L7Rules{ 2169 L7Proto: "testparser", 2170 L7: []api.PortRuleL7{ 2171 {"Key": "Value"}, 2172 }, 2173 }, 2174 }}, 2175 }, 2176 { 2177 IngressCommonRule: api.IngressCommonRule{ 2178 FromEndpoints: []api.EndpointSelector{endpointSelectorC}, 2179 }, 2180 ToPorts: []api.PortRule{{ 2181 Ports: []api.PortProtocol{ 2182 {Port: "7000", Protocol: api.ProtoTCP}, 2183 }, 2184 }}, 2185 }, 2186 }, 2187 }}) 2188 2189 expected := &L4Filter{ 2190 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 2191 wildcard: td.wildcardCachedSelector, 2192 L7Parser: "http", 2193 PerSelectorPolicies: L7DataMap{ 2194 td.wildcardCachedSelector: nil, 2195 td.cachedSelectorC: &PerSelectorPolicy{ 2196 L7Rules: api.L7Rules{ 2197 HTTP: []api.PortRuleHTTP{{Path: "/", Method: "GET"}}, 2198 }, 2199 isRedirect: true, 2200 }, 2201 }, 2202 Ingress: true, 2203 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 2204 td.cachedSelectorC: {nil}, 2205 td.wildcardCachedSelector: {nil}, 2206 }, 2207 } 2208 2209 buffer := new(bytes.Buffer) 2210 ctx := SearchContext{To: labelsA, Trace: TRACE_VERBOSE} 2211 ctx.Logging = stdlog.New(buffer, "", 0) 2212 2213 l4IngressPolicy, err := repo.ResolveL4IngressPolicy(&ctx) 2214 require.NoError(t, err) 2215 2216 t.Log(buffer) 2217 2218 filter := l4IngressPolicy.ExactLookup("80", 0, "TCP") 2219 require.NotNil(t, filter) 2220 require.Equal(t, uint16(80), filter.Port) 2221 require.True(t, filter.Ingress) 2222 2223 require.Equal(t, 2, len(filter.PerSelectorPolicies)) 2224 require.NotNil(t, filter.PerSelectorPolicies[td.cachedSelectorC]) 2225 require.Nil(t, filter.PerSelectorPolicies[td.wildcardCachedSelector]) 2226 require.EqualValues(t, expected, filter) 2227 require.Equal(t, ParserTypeHTTP, filter.L7Parser) 2228 2229 expectedL7 := &L4Filter{ 2230 Port: 7000, Protocol: api.ProtoTCP, U8Proto: 6, 2231 L7Parser: "testparser", 2232 PerSelectorPolicies: L7DataMap{ 2233 td.cachedSelectorC: &PerSelectorPolicy{ 2234 L7Rules: api.L7Rules{ 2235 L7Proto: "testparser", 2236 L7: []api.PortRuleL7{{"Key": "Value"}, {}}, 2237 }, 2238 isRedirect: true, 2239 }, 2240 }, 2241 Ingress: true, 2242 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorC: {nil}}, 2243 } 2244 2245 filterL7 := l4IngressPolicy.ExactLookup("7000", 0, "TCP") 2246 require.NotNil(t, filterL7) 2247 require.Equal(t, uint16(7000), filterL7.Port) 2248 require.True(t, filterL7.Ingress) 2249 2250 require.Equal(t, 1, len(filterL7.PerSelectorPolicies)) 2251 require.NotNil(t, filterL7.PerSelectorPolicies[td.cachedSelectorC]) 2252 require.Nil(t, filterL7.PerSelectorPolicies[td.wildcardCachedSelector]) 2253 require.EqualValues(t, expectedL7, filterL7) 2254 require.Equal(t, L7ParserType("testparser"), filterL7.L7Parser) 2255 2256 l4IngressPolicy.Detach(repo.GetSelectorCache()) 2257 2258 // Test the reverse order as well; ensure that we check both conditions 2259 // for if L4-only policy is in the L4Filter for the same port-protocol tuple, 2260 // and L7 metadata exists in the L4Filter we are adding; expect to resolve 2261 // to L4-only policy without any L7-metadata. 2262 repo = td.resetRepo() 2263 repo.MustAddList(api.Rules{&api.Rule{ 2264 EndpointSelector: endpointSelectorA, 2265 Ingress: []api.IngressRule{ 2266 { 2267 IngressCommonRule: api.IngressCommonRule{ 2268 FromEndpoints: []api.EndpointSelector{endpointSelectorC}, 2269 }, 2270 ToPorts: []api.PortRule{{ 2271 Ports: []api.PortProtocol{ 2272 {Port: "7000", Protocol: api.ProtoTCP}, 2273 }, 2274 }}, 2275 }, 2276 { 2277 IngressCommonRule: api.IngressCommonRule{ 2278 FromEndpoints: []api.EndpointSelector{endpointSelectorC}, 2279 }, 2280 ToPorts: []api.PortRule{{ 2281 Ports: []api.PortProtocol{ 2282 {Port: "7000", Protocol: api.ProtoTCP}, 2283 }, 2284 Rules: &api.L7Rules{ 2285 L7Proto: "testparser", 2286 L7: []api.PortRuleL7{ 2287 {"Key": "Value"}, 2288 }, 2289 }, 2290 }}, 2291 }, 2292 { 2293 ToPorts: []api.PortRule{{ 2294 Ports: []api.PortProtocol{ 2295 {Port: "80", Protocol: api.ProtoTCP}, 2296 }, 2297 }}, 2298 }, 2299 { 2300 IngressCommonRule: api.IngressCommonRule{ 2301 FromEndpoints: []api.EndpointSelector{endpointSelectorC}, 2302 }, 2303 ToPorts: []api.PortRule{{ 2304 Ports: []api.PortProtocol{ 2305 {Port: "80", Protocol: api.ProtoTCP}, 2306 }, 2307 Rules: &api.L7Rules{ 2308 HTTP: []api.PortRuleHTTP{ 2309 {Method: "GET", Path: "/"}, 2310 }, 2311 }, 2312 }}, 2313 }, 2314 }, 2315 }}) 2316 2317 buffer = new(bytes.Buffer) 2318 ctx = SearchContext{To: labelsA, Trace: TRACE_VERBOSE} 2319 ctx.Logging = stdlog.New(buffer, "", 0) 2320 2321 l4IngressPolicy, err = repo.ResolveL4IngressPolicy(&ctx) 2322 require.NoError(t, err) 2323 2324 t.Log(buffer) 2325 2326 filter = l4IngressPolicy.ExactLookup("80", 0, "TCP") 2327 require.NotNil(t, filter) 2328 require.Equal(t, uint16(80), filter.Port) 2329 require.True(t, filter.Ingress) 2330 2331 require.Equal(t, 2, len(filter.PerSelectorPolicies)) 2332 require.Nil(t, filter.PerSelectorPolicies[td.wildcardCachedSelector]) 2333 require.NotNil(t, filter.PerSelectorPolicies[td.cachedSelectorC]) 2334 require.EqualValues(t, expected, filter) 2335 require.Equal(t, ParserTypeHTTP, filter.L7Parser) 2336 2337 filterL7 = l4IngressPolicy.ExactLookup("7000", 0, "TCP") 2338 require.NotNil(t, filterL7) 2339 require.Equal(t, uint16(7000), filterL7.Port) 2340 require.True(t, filterL7.Ingress) 2341 2342 require.Equal(t, 1, len(filterL7.PerSelectorPolicies)) 2343 require.NotNil(t, filterL7.PerSelectorPolicies[td.cachedSelectorC]) 2344 require.Nil(t, filterL7.PerSelectorPolicies[td.wildcardCachedSelector]) 2345 require.EqualValues(t, expectedL7, filterL7) 2346 require.Equal(t, L7ParserType("testparser"), filterL7.L7Parser) 2347 2348 // Second, test the expeicit allow at L3. 2349 repo = td.resetRepo() 2350 repo.MustAddList(api.Rules{&api.Rule{ 2351 EndpointSelector: endpointSelectorA, 2352 Ingress: []api.IngressRule{ 2353 { 2354 IngressCommonRule: api.IngressCommonRule{ 2355 FromEndpoints: []api.EndpointSelector{endpointSelectorC}, 2356 }, 2357 ToPorts: []api.PortRule{{ 2358 Ports: []api.PortProtocol{ 2359 {Port: "80", Protocol: api.ProtoTCP}, 2360 }, 2361 Rules: &api.L7Rules{ 2362 HTTP: []api.PortRuleHTTP{ 2363 {Method: "GET", Path: "/"}, 2364 }, 2365 }, 2366 }}, 2367 }, 2368 { 2369 IngressCommonRule: api.IngressCommonRule{ 2370 FromEndpoints: []api.EndpointSelector{api.WildcardEndpointSelector}, 2371 }, 2372 ToPorts: []api.PortRule{{ 2373 Ports: []api.PortProtocol{ 2374 {Port: "80", Protocol: api.ProtoTCP}, 2375 }, 2376 }}, 2377 }, 2378 }, 2379 }}) 2380 2381 buffer = new(bytes.Buffer) 2382 ctx = SearchContext{To: labelsA, Trace: TRACE_VERBOSE} 2383 ctx.Logging = stdlog.New(buffer, "", 0) 2384 2385 l4IngressPolicy, err = repo.ResolveL4IngressPolicy(&ctx) 2386 require.NoError(t, err) 2387 2388 t.Log(buffer) 2389 2390 filter = l4IngressPolicy.ExactLookup("80", 0, "TCP") 2391 require.NotNil(t, filter) 2392 require.Equal(t, uint16(80), filter.Port) 2393 require.True(t, filter.Ingress) 2394 2395 require.Equal(t, ParserTypeHTTP, filter.L7Parser) 2396 require.Equal(t, 2, len(filter.PerSelectorPolicies)) 2397 require.EqualValues(t, expected, filter) 2398 2399 // Test the reverse order as well; ensure that we check both conditions 2400 // for if L4-only policy is in the L4Filter for the same port-protocol tuple, 2401 // and L7 metadata exists in the L4Filter we are adding; expect to resolve 2402 // to L4-only policy without any L7-metadata. 2403 repo = td.resetRepo() 2404 repo.MustAddList(api.Rules{&api.Rule{ 2405 EndpointSelector: endpointSelectorA, 2406 Ingress: []api.IngressRule{ 2407 { 2408 IngressCommonRule: api.IngressCommonRule{ 2409 FromEndpoints: []api.EndpointSelector{api.WildcardEndpointSelector}, 2410 }, 2411 ToPorts: []api.PortRule{{ 2412 Ports: []api.PortProtocol{ 2413 {Port: "80", Protocol: api.ProtoTCP}, 2414 }, 2415 }}, 2416 }, 2417 { 2418 IngressCommonRule: api.IngressCommonRule{ 2419 FromEndpoints: []api.EndpointSelector{endpointSelectorC}, 2420 }, 2421 ToPorts: []api.PortRule{{ 2422 Ports: []api.PortProtocol{ 2423 {Port: "80", Protocol: api.ProtoTCP}, 2424 }, 2425 Rules: &api.L7Rules{ 2426 HTTP: []api.PortRuleHTTP{ 2427 {Method: "GET", Path: "/"}, 2428 }, 2429 }, 2430 }}, 2431 }, 2432 }, 2433 }}) 2434 2435 buffer = new(bytes.Buffer) 2436 ctx = SearchContext{To: labelsA, Trace: TRACE_VERBOSE} 2437 ctx.Logging = stdlog.New(buffer, "", 0) 2438 2439 l4IngressPolicy, err = repo.ResolveL4IngressPolicy(&ctx) 2440 require.NoError(t, err) 2441 2442 t.Log(buffer) 2443 2444 filter = l4IngressPolicy.ExactLookup("80", 0, "TCP") 2445 require.NotNil(t, filter) 2446 require.Equal(t, uint16(80), filter.Port) 2447 require.True(t, filter.Ingress) 2448 2449 require.Equal(t, ParserTypeHTTP, filter.L7Parser) 2450 require.Equal(t, 2, len(filter.PerSelectorPolicies)) 2451 require.EqualValues(t, expected, filter) 2452 } 2453 2454 func TestL3L4L7Merge(t *testing.T) { 2455 2456 // First rule allows ingress from all endpoints to port 80 only on 2457 // GET to "/". However, second rule allows all traffic on port 80 only to a 2458 // specific endpoint. When these rules are merged, it equates to allowing 2459 // all traffic from port 80 from any endpoint. 2460 // 2461 // TODO: This comment can't be correct, the resulting policy 2462 // should allow all on port 80 only from endpoint C, traffic 2463 // from all other endpoints should still only allow only GET 2464 // on "/". 2465 td := newTestData() 2466 repo := td.repo 2467 repo.MustAddList(api.Rules{&api.Rule{ 2468 EndpointSelector: endpointSelectorA, 2469 Ingress: []api.IngressRule{ 2470 { 2471 ToPorts: []api.PortRule{{ 2472 Ports: []api.PortProtocol{ 2473 {Port: "80", Protocol: api.ProtoTCP}, 2474 }, 2475 Rules: &api.L7Rules{ 2476 HTTP: []api.PortRuleHTTP{ 2477 {Method: "GET", Path: "/"}, 2478 }, 2479 }, 2480 }}, 2481 }, 2482 { 2483 IngressCommonRule: api.IngressCommonRule{ 2484 FromEndpoints: []api.EndpointSelector{endpointSelectorC}, 2485 }, 2486 ToPorts: []api.PortRule{{ 2487 Ports: []api.PortProtocol{ 2488 {Port: "80", Protocol: api.ProtoTCP}, 2489 }, 2490 }}, 2491 }, 2492 }, 2493 }}) 2494 2495 buffer := new(bytes.Buffer) 2496 ctx := SearchContext{To: labelsA, Trace: TRACE_VERBOSE} 2497 ctx.Logging = stdlog.New(buffer, "", 0) 2498 2499 l4IngressPolicy, err := repo.ResolveL4IngressPolicy(&ctx) 2500 require.NoError(t, err) 2501 2502 t.Log(buffer) 2503 2504 filter := l4IngressPolicy.ExactLookup("80", 0, "TCP") 2505 require.NotNil(t, filter) 2506 require.Equal(t, uint16(80), filter.Port) 2507 require.True(t, filter.Ingress) 2508 2509 require.Equal(t, 2, len(filter.PerSelectorPolicies)) 2510 require.NotNil(t, filter.PerSelectorPolicies[td.wildcardCachedSelector]) 2511 require.Nil(t, filter.PerSelectorPolicies[td.cachedSelectorC]) 2512 2513 require.Equal(t, ParserTypeHTTP, filter.L7Parser) 2514 require.Equal(t, 2, len(filter.PerSelectorPolicies)) 2515 require.Equal(t, &L4Filter{ 2516 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 2517 wildcard: td.wildcardCachedSelector, 2518 L7Parser: "http", 2519 PerSelectorPolicies: L7DataMap{ 2520 td.cachedSelectorC: nil, 2521 td.wildcardCachedSelector: &PerSelectorPolicy{ 2522 L7Rules: api.L7Rules{ 2523 HTTP: []api.PortRuleHTTP{{Path: "/", Method: "GET"}}, 2524 }, 2525 isRedirect: true, 2526 }, 2527 }, 2528 Ingress: true, 2529 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 2530 td.cachedSelectorC: {nil}, 2531 td.wildcardCachedSelector: {nil}, 2532 }, 2533 }, filter) 2534 2535 repo = td.resetRepo() 2536 repo.MustAddList(api.Rules{&api.Rule{ 2537 EndpointSelector: endpointSelectorA, 2538 Ingress: []api.IngressRule{ 2539 { 2540 IngressCommonRule: api.IngressCommonRule{ 2541 FromEndpoints: []api.EndpointSelector{endpointSelectorC}, 2542 }, 2543 ToPorts: []api.PortRule{{ 2544 Ports: []api.PortProtocol{ 2545 {Port: "80", Protocol: api.ProtoTCP}, 2546 }, 2547 }}, 2548 }, 2549 { 2550 ToPorts: []api.PortRule{{ 2551 Ports: []api.PortProtocol{ 2552 {Port: "80", Protocol: api.ProtoTCP}, 2553 }, 2554 Rules: &api.L7Rules{ 2555 HTTP: []api.PortRuleHTTP{ 2556 {Method: "GET", Path: "/"}, 2557 }, 2558 }, 2559 }}, 2560 }, 2561 }, 2562 }}) 2563 2564 buffer = new(bytes.Buffer) 2565 ctx = SearchContext{To: labelsA, Trace: TRACE_VERBOSE} 2566 ctx.Logging = stdlog.New(buffer, "", 0) 2567 2568 l4IngressPolicy, err = repo.ResolveL4IngressPolicy(&ctx) 2569 require.NoError(t, err) 2570 2571 t.Log(buffer) 2572 2573 filter = l4IngressPolicy.ExactLookup("80", 0, "TCP") 2574 require.NotNil(t, filter) 2575 require.Equal(t, uint16(80), filter.Port) 2576 require.True(t, filter.Ingress) 2577 2578 require.Equal(t, ParserTypeHTTP, filter.L7Parser) 2579 require.Equal(t, 2, len(filter.PerSelectorPolicies)) 2580 require.NotNil(t, filter.PerSelectorPolicies[td.wildcardCachedSelector]) 2581 require.Nil(t, filter.PerSelectorPolicies[td.cachedSelectorC]) 2582 require.Equal(t, &L4Filter{ 2583 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 2584 wildcard: td.wildcardCachedSelector, 2585 L7Parser: "http", 2586 PerSelectorPolicies: L7DataMap{ 2587 td.cachedSelectorC: nil, 2588 td.wildcardCachedSelector: &PerSelectorPolicy{ 2589 L7Rules: api.L7Rules{ 2590 HTTP: []api.PortRuleHTTP{{Path: "/", Method: "GET"}}, 2591 }, 2592 isRedirect: true, 2593 }, 2594 }, 2595 Ingress: true, 2596 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 2597 td.cachedSelectorC: {nil}, 2598 td.wildcardCachedSelector: {nil}, 2599 }, 2600 }, filter) 2601 2602 } 2603 2604 func TestMatches(t *testing.T) { 2605 td := newTestData() 2606 repo := td.repo 2607 repo.MustAddList(api.Rules{ 2608 &api.Rule{ 2609 EndpointSelector: endpointSelectorA, 2610 Ingress: []api.IngressRule{ 2611 { 2612 IngressCommonRule: api.IngressCommonRule{ 2613 FromEndpoints: []api.EndpointSelector{endpointSelectorC}, 2614 }, 2615 }, 2616 }, 2617 }, 2618 &api.Rule{ 2619 NodeSelector: endpointSelectorA, 2620 Ingress: []api.IngressRule{ 2621 { 2622 IngressCommonRule: api.IngressCommonRule{ 2623 FromEndpoints: []api.EndpointSelector{endpointSelectorC}, 2624 }, 2625 }, 2626 }, 2627 }, 2628 }) 2629 2630 epRule := repo.rules[ruleKey{idx: 0}] 2631 hostRule := repo.rules[ruleKey{idx: 1}] 2632 2633 selectedEpLabels := labels.ParseSelectLabel("id=a") 2634 selectedIdentity := identity.NewIdentity(54321, labels.Labels{selectedEpLabels.Key: selectedEpLabels}) 2635 td.addIdentity(selectedIdentity) 2636 2637 notSelectedEpLabels := labels.ParseSelectLabel("id=b") 2638 notSelectedIdentity := identity.NewIdentity(9876, labels.Labels{notSelectedEpLabels.Key: notSelectedEpLabels}) 2639 td.addIdentity(notSelectedIdentity) 2640 2641 hostLabels := labels.Labels{selectedEpLabels.Key: selectedEpLabels} 2642 hostLabels.MergeLabels(labels.LabelHost) 2643 hostIdentity := identity.NewIdentity(identity.ReservedIdentityHost, hostLabels) 2644 td.addIdentity(hostIdentity) 2645 2646 // notSelectedEndpoint is not selected by rule, so we it shouldn't be added 2647 // to EndpointsSelected. 2648 require.Equal(t, false, epRule.matchesSubject(notSelectedIdentity)) 2649 2650 // selectedEndpoint is selected by rule, so we it should be added to 2651 // EndpointsSelected. 2652 require.True(t, epRule.matchesSubject(selectedIdentity)) 2653 2654 // Test again to check for caching working correctly. 2655 require.True(t, epRule.matchesSubject(selectedIdentity)) 2656 2657 // Possible scenario where an endpoint is deleted, and soon after another 2658 // endpoint is added with the same ID, but with a different identity. Matching 2659 // needs to handle this case correctly. 2660 require.Equal(t, false, epRule.matchesSubject(notSelectedIdentity)) 2661 2662 // host endpoint is not selected by rule, so we it shouldn't be added to EndpointsSelected. 2663 require.Equal(t, false, epRule.matchesSubject(hostIdentity)) 2664 2665 // selectedEndpoint is not selected by rule, so we it shouldn't be added to EndpointsSelected. 2666 require.Equal(t, false, hostRule.matchesSubject(selectedIdentity)) 2667 2668 // host endpoint is selected by rule, but host labels are mutable, so don't cache them 2669 require.True(t, hostRule.matchesSubject(hostIdentity)) 2670 2671 // Assert that mutable host identities are handled 2672 // First, add an additional label, ensure that match succeeds 2673 hostLabels.MergeLabels(labels.NewLabelsFromModel([]string{"foo=bar"})) 2674 hostIdentity = identity.NewIdentity(identity.ReservedIdentityHost, hostLabels) 2675 td.addIdentity(hostIdentity) 2676 require.True(t, hostRule.matchesSubject(hostIdentity)) 2677 2678 // Then, change host to id=c, which is not selected, and ensure match is correct 2679 hostIdentity = identity.NewIdentity(identity.ReservedIdentityHost, labels.NewLabelsFromModel([]string{"id=c"})) 2680 td.addIdentity(hostIdentity) 2681 require.False(t, hostRule.matchesSubject(hostIdentity)) 2682 } 2683 2684 func BenchmarkRuleString(b *testing.B) { 2685 r := &rule{ 2686 Rule: api.Rule{ 2687 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 2688 Ingress: []api.IngressRule{ 2689 { 2690 ToPorts: []api.PortRule{{ 2691 Ports: []api.PortProtocol{ 2692 {Port: "80", Protocol: api.ProtoTCP}, 2693 {Port: "8080", Protocol: api.ProtoTCP}, 2694 }, 2695 Rules: &api.L7Rules{ 2696 HTTP: []api.PortRuleHTTP{ 2697 {Method: "GET", Path: "/"}, 2698 }, 2699 }, 2700 }}, 2701 }, 2702 }, 2703 Egress: []api.EgressRule{ 2704 { 2705 ToPorts: []api.PortRule{{ 2706 Ports: []api.PortProtocol{ 2707 {Port: "3000", Protocol: api.ProtoAny}, 2708 }, 2709 }}, 2710 }, 2711 }, 2712 }, 2713 } 2714 b.ReportAllocs() 2715 b.ResetTimer() 2716 for i := 0; i < b.N; i++ { 2717 _ = r.String() 2718 } 2719 } 2720 2721 // Test merging of L7 rules when the same rules apply to multiple selectors. 2722 // This was added to prevent regression of a bug where the merging of l7 rules for "foo" 2723 // also affected the rules for "baz". 2724 func TestMergeL7PolicyEgressWithMultipleSelectors(t *testing.T) { 2725 td := newTestData() 2726 fromBar := &SearchContext{From: labels.ParseSelectLabelArray("bar")} 2727 fromFoo := &SearchContext{From: labels.ParseSelectLabelArray("foo")} 2728 2729 fooSelector := []api.EndpointSelector{ 2730 api.NewESFromLabels(labels.ParseSelectLabel("foo")), 2731 } 2732 foobazSelector := []api.EndpointSelector{ 2733 api.NewESFromLabels(labels.ParseSelectLabel("foo")), 2734 api.NewESFromLabels(labels.ParseSelectLabel("baz")), 2735 } 2736 2737 rule1 := &rule{ 2738 Rule: api.Rule{ 2739 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 2740 Egress: []api.EgressRule{ 2741 { 2742 EgressCommonRule: api.EgressCommonRule{ 2743 ToEndpoints: fooSelector, 2744 }, 2745 // Note that this allows all on 80, so the result should wildcard HTTP to "foo" 2746 ToPorts: []api.PortRule{{ 2747 Ports: []api.PortProtocol{ 2748 {Port: "80", Protocol: api.ProtoTCP}, 2749 }, 2750 }}, 2751 }, 2752 { 2753 EgressCommonRule: api.EgressCommonRule{ 2754 ToEndpoints: foobazSelector, 2755 }, 2756 ToPorts: []api.PortRule{{ 2757 Ports: []api.PortProtocol{ 2758 {Port: "80", Protocol: api.ProtoTCP}, 2759 }, 2760 Rules: &api.L7Rules{ 2761 HTTP: []api.PortRuleHTTP{ 2762 {Method: "GET"}, 2763 }, 2764 }, 2765 }}, 2766 }, 2767 }, 2768 }, 2769 } 2770 2771 expected := NewL4PolicyMapWithValues(map[string]*L4Filter{"80/TCP": { 2772 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 2773 L7Parser: ParserTypeHTTP, 2774 PerSelectorPolicies: L7DataMap{ 2775 td.cachedFooSelector: &PerSelectorPolicy{ 2776 L7Rules: api.L7Rules{ 2777 HTTP: []api.PortRuleHTTP{{Method: "GET"}, {}}, 2778 }, 2779 isRedirect: true, 2780 }, 2781 td.cachedBazSelector: &PerSelectorPolicy{ 2782 L7Rules: api.L7Rules{ 2783 HTTP: []api.PortRuleHTTP{{Method: "GET"}}, 2784 }, 2785 isRedirect: true, 2786 }, 2787 }, 2788 Ingress: false, 2789 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 2790 td.cachedBazSelector: {nil}, 2791 td.cachedFooSelector: {nil}, 2792 }, 2793 }}) 2794 2795 state := traceState{} 2796 res, err := rule1.resolveEgressPolicy(td.testPolicyContext, fromBar, &state, NewL4PolicyMap(), nil, nil) 2797 require.NoError(t, err) 2798 require.NotNil(t, res) 2799 require.EqualValues(t, expected, res) 2800 require.Equal(t, 1, state.selectedRules) 2801 require.Equal(t, 1, state.matchedRules) 2802 res.Detach(td.sc) 2803 expected.Detach(td.sc) 2804 2805 state = traceState{} 2806 res, err = rule1.resolveEgressPolicy(td.testPolicyContext, fromFoo, &state, NewL4PolicyMap(), nil, nil) 2807 require.NoError(t, err) 2808 require.Nil(t, res) 2809 require.Equal(t, 0, state.selectedRules) 2810 require.Equal(t, 0, state.matchedRules) 2811 } 2812 2813 func TestMergeListenerReference(t *testing.T) { 2814 // No listener remains a no listener 2815 ps := &PerSelectorPolicy{} 2816 err := ps.mergeListenerReference(ps) 2817 require.NoError(t, err) 2818 require.Equal(t, "", ps.Listener) 2819 require.Equal(t, uint16(0), ps.Priority) 2820 2821 // Listener reference remains when the other has none 2822 ps0 := &PerSelectorPolicy{Listener: "listener0"} 2823 err = ps0.mergeListenerReference(ps) 2824 require.NoError(t, err) 2825 require.Equal(t, "listener0", ps0.Listener) 2826 require.Equal(t, uint16(0), ps0.Priority) 2827 2828 // Listener reference is propagated when there is none to begin with 2829 err = ps.mergeListenerReference(ps0) 2830 require.NoError(t, err) 2831 require.Equal(t, "listener0", ps.Listener) 2832 require.Equal(t, uint16(0), ps.Priority) 2833 2834 // A listener is not changed when there is no change 2835 err = ps0.mergeListenerReference(ps0) 2836 require.NoError(t, err) 2837 require.Equal(t, "listener0", ps0.Listener) 2838 require.Equal(t, uint16(0), ps0.Priority) 2839 2840 // Cannot merge two different listeners with the default (zero) priority 2841 ps0a := &PerSelectorPolicy{Listener: "listener0a"} 2842 err = ps0.mergeListenerReference(ps0a) 2843 require.NotNil(t, err) 2844 2845 err = ps0a.mergeListenerReference(ps0) 2846 require.NotNil(t, err) 2847 2848 // Listener with a defined (non-zero) priority takes precedence over 2849 // a listener with an undefined (zero) priority 2850 ps1 := &PerSelectorPolicy{Listener: "listener1", Priority: 1} 2851 err = ps1.mergeListenerReference(ps0) 2852 require.NoError(t, err) 2853 require.Equal(t, "listener1", ps1.Listener) 2854 require.Equal(t, uint16(1), ps1.Priority) 2855 2856 err = ps0.mergeListenerReference(ps1) 2857 require.NoError(t, err) 2858 require.Equal(t, "listener1", ps0.Listener) 2859 require.Equal(t, uint16(1), ps0.Priority) 2860 2861 // Listener with the lower priority value takes precedence 2862 ps2 := &PerSelectorPolicy{Listener: "listener2", Priority: 2} 2863 err = ps1.mergeListenerReference(ps2) 2864 require.NoError(t, err) 2865 require.Equal(t, "listener1", ps1.Listener) 2866 require.Equal(t, uint16(1), ps1.Priority) 2867 2868 err = ps2.mergeListenerReference(ps1) 2869 require.NoError(t, err) 2870 require.Equal(t, "listener1", ps2.Listener) 2871 require.Equal(t, uint16(1), ps2.Priority) 2872 2873 // Cannot merge two different listeners with the same priority 2874 ps12 := &PerSelectorPolicy{Listener: "listener1", Priority: 2} 2875 ps2 = &PerSelectorPolicy{Listener: "listener2", Priority: 2} 2876 err = ps12.mergeListenerReference(ps2) 2877 require.NotNil(t, err) 2878 err = ps2.mergeListenerReference(ps12) 2879 require.NotNil(t, err) 2880 2881 // Lower priority is propagated also when the listeners are the same 2882 ps23 := &PerSelectorPolicy{Listener: "listener2", Priority: 3} 2883 err = ps2.mergeListenerReference(ps23) 2884 require.NoError(t, err) 2885 require.Equal(t, "listener2", ps2.Listener) 2886 require.Equal(t, uint16(2), ps2.Priority) 2887 2888 err = ps23.mergeListenerReference(ps2) 2889 require.NoError(t, err) 2890 require.Equal(t, "listener2", ps23.Listener) 2891 require.Equal(t, uint16(2), ps23.Priority) 2892 }