github.com/cilium/cilium@v1.16.2/pkg/policy/repository_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 "testing" 11 12 "github.com/cilium/proxy/pkg/policy/api/kafka" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 "k8s.io/apimachinery/pkg/util/intstr" 16 17 "github.com/cilium/cilium/api/v1/models" 18 "github.com/cilium/cilium/pkg/identity" 19 ipcachetypes "github.com/cilium/cilium/pkg/ipcache/types" 20 k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" 21 slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" 22 "github.com/cilium/cilium/pkg/labels" 23 "github.com/cilium/cilium/pkg/option" 24 "github.com/cilium/cilium/pkg/policy/api" 25 ) 26 27 // mustAdd inserts a rule into the policy repository 28 // This is just a helper function for unit testing. 29 // Only returns error for signature reasons 30 func (p *Repository) mustAdd(r api.Rule) (uint64, map[uint16]struct{}, error) { 31 p.Mutex.Lock() 32 defer p.Mutex.Unlock() 33 34 if err := r.Sanitize(); err != nil { 35 panic(err) 36 } 37 38 newList := make([]*api.Rule, 1) 39 newList[0] = &r 40 _, rev := p.AddListLocked(newList) 41 return rev, map[uint16]struct{}{}, nil 42 } 43 44 func TestComputePolicyEnforcementAndRules(t *testing.T) { 45 46 // Cache policy enforcement value from when test was ran to avoid pollution 47 // across tests. 48 oldPolicyEnable := GetPolicyEnabled() 49 defer SetPolicyEnabled(oldPolicyEnable) 50 51 SetPolicyEnabled(option.DefaultEnforcement) 52 53 td := newTestData() 54 repo := td.repo 55 56 fooSelectLabel := labels.ParseSelectLabel("foo") 57 fooNumericIdentity := 9001 58 fooIdentity := identity.NewIdentity(identity.NumericIdentity(fooNumericIdentity), lbls) 59 td.addIdentity(fooIdentity) 60 fooIngressRule1Label := labels.NewLabel(k8sConst.PolicyLabelName, "fooIngressRule1", labels.LabelSourceAny) 61 fooIngressRule2Label := labels.NewLabel(k8sConst.PolicyLabelName, "fooIngressRule2", labels.LabelSourceAny) 62 fooEgressRule1Label := labels.NewLabel(k8sConst.PolicyLabelName, "fooEgressRule1", labels.LabelSourceAny) 63 fooEgressRule2Label := labels.NewLabel(k8sConst.PolicyLabelName, "fooEgressRule2", labels.LabelSourceAny) 64 combinedLabel := labels.NewLabel(k8sConst.PolicyLabelName, "combined", labels.LabelSourceAny) 65 initIdentity := identity.LookupReservedIdentity(identity.ReservedIdentityInit) 66 67 fooIngressRule1 := api.Rule{ 68 EndpointSelector: api.NewESFromLabels(fooSelectLabel), 69 Ingress: []api.IngressRule{ 70 { 71 IngressCommonRule: api.IngressCommonRule{ 72 FromEndpoints: []api.EndpointSelector{ 73 api.NewESFromLabels(fooSelectLabel), 74 }, 75 }, 76 }, 77 }, 78 Labels: labels.LabelArray{ 79 fooIngressRule1Label, 80 }, 81 } 82 fooIngressRule1.Sanitize() 83 84 fooIngressRule2 := api.Rule{ 85 EndpointSelector: api.NewESFromLabels(fooSelectLabel), 86 Ingress: []api.IngressRule{ 87 { 88 IngressCommonRule: api.IngressCommonRule{ 89 FromEndpoints: []api.EndpointSelector{ 90 api.NewESFromLabels(fooSelectLabel), 91 }, 92 }, 93 }, 94 }, 95 Labels: labels.LabelArray{ 96 fooIngressRule2Label, 97 }, 98 } 99 fooIngressRule2.Sanitize() 100 101 fooEgressRule1 := api.Rule{ 102 EndpointSelector: api.NewESFromLabels(fooSelectLabel), 103 Egress: []api.EgressRule{ 104 { 105 EgressCommonRule: api.EgressCommonRule{ 106 ToEndpoints: []api.EndpointSelector{ 107 api.NewESFromLabels(fooSelectLabel), 108 }, 109 }, 110 }, 111 }, 112 Labels: labels.LabelArray{ 113 fooEgressRule1Label, 114 }, 115 } 116 fooEgressRule1.Sanitize() 117 118 fooEgressRule2 := api.Rule{ 119 EndpointSelector: api.NewESFromLabels(fooSelectLabel), 120 Egress: []api.EgressRule{ 121 { 122 EgressCommonRule: api.EgressCommonRule{ 123 ToEndpoints: []api.EndpointSelector{ 124 api.NewESFromLabels(fooSelectLabel), 125 }, 126 }, 127 }, 128 }, 129 Labels: labels.LabelArray{ 130 fooEgressRule2Label, 131 }, 132 } 133 fooEgressRule2.Sanitize() 134 135 combinedRule := api.Rule{ 136 EndpointSelector: api.NewESFromLabels(fooSelectLabel), 137 Ingress: []api.IngressRule{ 138 { 139 IngressCommonRule: api.IngressCommonRule{ 140 FromEndpoints: []api.EndpointSelector{ 141 api.NewESFromLabels(fooSelectLabel), 142 }, 143 }, 144 }, 145 }, 146 Egress: []api.EgressRule{ 147 { 148 EgressCommonRule: api.EgressCommonRule{ 149 ToEndpoints: []api.EndpointSelector{ 150 api.NewESFromLabels(fooSelectLabel), 151 }, 152 }, 153 }, 154 }, 155 Labels: labels.LabelArray{ 156 combinedLabel, 157 }, 158 } 159 combinedRule.Sanitize() 160 161 ing, egr, matchingRules := repo.computePolicyEnforcementAndRules(fooIdentity) 162 require.Equal(t, false, ing, "ingress policy enforcement should not apply since no rules are in repository") 163 require.Equal(t, false, egr, "egress policy enforcement should not apply since no rules are in repository") 164 require.EqualValues(t, ruleSlice{}, matchingRules, "returned matching rules did not match") 165 166 _, _, err := repo.mustAdd(fooIngressRule1) 167 require.NoError(t, err, "unable to add rule to policy repository") 168 ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity) 169 require.Equal(t, true, ing, "ingress policy enforcement should apply since ingress rule selects") 170 require.Equal(t, false, egr, "egress policy enforcement should not apply since no egress rules select") 171 require.EqualValues(t, fooIngressRule1, matchingRules[0].Rule, "returned matching rules did not match") 172 173 _, _, err = repo.mustAdd(fooIngressRule2) 174 require.NoError(t, err, "unable to add rule to policy repository") 175 ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity) 176 require.Equal(t, true, ing, "ingress policy enforcement should apply since ingress rule selects") 177 require.Equal(t, false, egr, "egress policy enforcement should not apply since no egress rules select") 178 require.ElementsMatch(t, matchingRules.AsPolicyRules(), api.Rules{&fooIngressRule1, &fooIngressRule2}) 179 180 _, _, numDeleted := repo.DeleteByLabelsLocked(labels.LabelArray{fooIngressRule1Label}) 181 require.Equal(t, 1, numDeleted) 182 require.NoError(t, err, "unable to add rule to policy repository") 183 ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity) 184 require.Equal(t, true, ing, "ingress policy enforcement should apply since ingress rule selects") 185 require.Equal(t, false, egr, "egress policy enforcement should not apply since no egress rules select") 186 require.EqualValues(t, fooIngressRule2, matchingRules[0].Rule, "returned matching rules did not match") 187 188 _, _, numDeleted = repo.DeleteByLabelsLocked(labels.LabelArray{fooIngressRule2Label}) 189 require.Equal(t, 1, numDeleted) 190 191 ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity) 192 require.Equal(t, false, ing, "ingress policy enforcement should not apply since no rules are in repository") 193 require.Equal(t, false, egr, "egress policy enforcement should not apply since no rules are in repository") 194 require.EqualValues(t, ruleSlice{}, matchingRules, "returned matching rules did not match") 195 196 _, _, err = repo.mustAdd(fooEgressRule1) 197 require.NoError(t, err, "unable to add rule to policy repository") 198 ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity) 199 require.Equal(t, false, ing, "ingress policy enforcement should not apply since no ingress rules select") 200 require.Equal(t, true, egr, "egress policy enforcement should apply since egress rules select") 201 require.EqualValues(t, fooEgressRule1, matchingRules[0].Rule, "returned matching rules did not match") 202 _, _, numDeleted = repo.DeleteByLabelsLocked(labels.LabelArray{fooEgressRule1Label}) 203 require.Equal(t, 1, numDeleted) 204 205 _, _, err = repo.mustAdd(fooEgressRule2) 206 require.NoError(t, err, "unable to add rule to policy repository") 207 ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity) 208 require.Equal(t, false, ing, "ingress policy enforcement should not apply since no ingress rules select") 209 require.Equal(t, true, egr, "egress policy enforcement should apply since egress rules select") 210 require.EqualValues(t, fooEgressRule2, matchingRules[0].Rule, "returned matching rules did not match") 211 212 _, _, numDeleted = repo.DeleteByLabelsLocked(labels.LabelArray{fooEgressRule2Label}) 213 require.Equal(t, 1, numDeleted) 214 215 _, _, err = repo.mustAdd(combinedRule) 216 require.NoError(t, err, "unable to add rule to policy repository") 217 ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity) 218 require.Equal(t, true, ing, "ingress policy enforcement should apply since ingress rule selects") 219 require.Equal(t, true, egr, "egress policy enforcement should apply since egress rules selects") 220 require.EqualValues(t, combinedRule, matchingRules[0].Rule, "returned matching rules did not match") 221 _, _, numDeleted = repo.DeleteByLabelsLocked(labels.LabelArray{combinedLabel}) 222 require.Equal(t, 1, numDeleted) 223 224 SetPolicyEnabled(option.AlwaysEnforce) 225 require.NoError(t, err, "unable to add rule to policy repository") 226 ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity) 227 require.Equal(t, true, ing, "ingress policy enforcement should apply since ingress rule selects") 228 require.Equal(t, true, egr, "egress policy enforcement should apply since egress rules selects") 229 require.EqualValues(t, ruleSlice{}, matchingRules, "returned matching rules did not match") 230 231 SetPolicyEnabled(option.NeverEnforce) 232 _, _, err = repo.mustAdd(combinedRule) 233 require.NoError(t, err, "unable to add rule to policy repository") 234 ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity) 235 require.Equal(t, false, ing, "ingress policy enforcement should not apply since policy enforcement is disabled ") 236 require.Equal(t, false, egr, "egress policy enforcement should not apply since policy enforcement is disabled") 237 require.Nil(t, matchingRules, "no rules should be returned since policy enforcement is disabled") 238 239 // Test init identity. 240 241 SetPolicyEnabled(option.DefaultEnforcement) 242 // If the mode is "default", check that the policy is always enforced for 243 // endpoints with the reserved:init label. If no policy rules match 244 // reserved:init, this drops all ingress and egress traffic. 245 ingress, egress, matchingRules := repo.computePolicyEnforcementAndRules(initIdentity) 246 require.Equal(t, true, ingress) 247 require.Equal(t, true, egress) 248 require.EqualValues(t, ruleSlice{}, matchingRules, "no rules should be returned since policy enforcement is disabled") 249 250 // Check that the "always" and "never" modes are not affected. 251 SetPolicyEnabled(option.AlwaysEnforce) 252 ingress, egress, _ = repo.computePolicyEnforcementAndRules(initIdentity) 253 require.Equal(t, true, ingress) 254 require.Equal(t, true, egress) 255 256 SetPolicyEnabled(option.NeverEnforce) 257 ingress, egress, _ = repo.computePolicyEnforcementAndRules(initIdentity) 258 require.Equal(t, false, ingress) 259 require.Equal(t, false, egress) 260 261 } 262 263 func TestAddSearchDelete(t *testing.T) { 264 td := newTestData() 265 repo := td.repo 266 267 lbls1 := labels.LabelArray{ 268 labels.ParseLabel("tag1"), 269 labels.ParseLabel("tag2"), 270 } 271 rule1 := api.Rule{ 272 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("foo")), 273 Labels: lbls1, 274 } 275 rule1.Sanitize() 276 rule2 := api.Rule{ 277 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 278 Labels: lbls1, 279 } 280 rule2.Sanitize() 281 lbls2 := labels.LabelArray{labels.ParseSelectLabel("tag3")} 282 rule3 := api.Rule{ 283 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 284 Labels: lbls2, 285 } 286 rule3.Sanitize() 287 288 nextRevision := uint64(1) 289 290 require.Equal(t, nextRevision, repo.GetRevision()) 291 nextRevision++ 292 293 // add rule1,rule2 294 rev, _, err := repo.mustAdd(rule1) 295 require.Nil(t, err) 296 require.Equal(t, nextRevision, rev) 297 nextRevision++ 298 rev, _, err = repo.mustAdd(rule2) 299 require.Nil(t, err) 300 require.Equal(t, nextRevision, rev) 301 nextRevision++ 302 303 // rule3 should not be in there yet 304 repo.Mutex.RLock() 305 require.EqualValues(t, api.Rules{}, repo.SearchRLocked(lbls2)) 306 repo.Mutex.RUnlock() 307 308 // add rule3 309 rev, _, err = repo.mustAdd(rule3) 310 require.Nil(t, err) 311 require.Equal(t, nextRevision, rev) 312 nextRevision++ 313 314 // search rule1,rule2 315 repo.Mutex.RLock() 316 require.ElementsMatch(t, api.Rules{&rule1, &rule2}, repo.SearchRLocked(lbls1)) 317 require.ElementsMatch(t, api.Rules{&rule3}, repo.SearchRLocked(lbls2)) 318 repo.Mutex.RUnlock() 319 320 // delete rule1, rule2 321 rev, n := repo.DeleteByLabels(lbls1) 322 require.Equal(t, 2, n) 323 require.Equal(t, nextRevision, rev) 324 nextRevision++ 325 326 // delete rule1, rule2 again has no effect 327 rev, n = repo.DeleteByLabels(lbls1) 328 require.Equal(t, 0, n) 329 require.Equal(t, nextRevision-1, rev) 330 331 // rule3 can still be found 332 repo.Mutex.RLock() 333 require.EqualValues(t, api.Rules{&rule3}, repo.SearchRLocked(lbls2)) 334 repo.Mutex.RUnlock() 335 336 // delete rule3 337 rev, n = repo.DeleteByLabels(lbls2) 338 require.Equal(t, 1, n) 339 require.Equal(t, nextRevision, rev) 340 341 // rule1 is gone 342 repo.Mutex.RLock() 343 require.EqualValues(t, api.Rules{}, repo.SearchRLocked(lbls2)) 344 repo.Mutex.RUnlock() 345 } 346 347 func BenchmarkParseLabel(b *testing.B) { 348 td := newTestData() 349 repo := td.repo 350 351 b.ResetTimer() 352 var err error 353 var cntAdd, cntFound int 354 355 lbls := make([]labels.LabelArray, 100) 356 for i := 0; i < 100; i++ { 357 I := fmt.Sprintf("%d", i) 358 lbls[i] = labels.LabelArray{labels.NewLabel("tag3", I, labels.LabelSourceK8s), labels.NewLabel("namespace", "default", labels.LabelSourceK8s)} 359 } 360 for i := 0; i < b.N; i++ { 361 for j := 0; j < 100; j++ { 362 J := fmt.Sprintf("%d", j) 363 _, _, err = repo.mustAdd(api.Rule{ 364 EndpointSelector: api.NewESFromLabels(labels.NewLabel("foo", J, labels.LabelSourceK8s), labels.NewLabel("namespace", "default", labels.LabelSourceK8s)), 365 Labels: labels.LabelArray{ 366 labels.ParseLabel("k8s:tag1"), 367 labels.NewLabel("namespace", "default", labels.LabelSourceK8s), 368 labels.NewLabel("tag3", J, labels.LabelSourceK8s), 369 }, 370 }) 371 if err == nil { 372 cntAdd++ 373 } 374 } 375 376 repo.Mutex.RLock() 377 for j := 0; j < 100; j++ { 378 cntFound += len(repo.SearchRLocked(lbls[j])) 379 } 380 repo.Mutex.RUnlock() 381 } 382 b.Log("Added: ", cntAdd) 383 b.Log("found: ", cntFound) 384 } 385 386 func TestAllowsIngress(t *testing.T) { 387 td := newTestData() 388 repo := td.repo 389 390 fooToBar := &SearchContext{ 391 From: labels.ParseSelectLabelArray("foo"), 392 To: labels.ParseSelectLabelArray("bar"), 393 } 394 395 repo.Mutex.RLock() 396 // no rules loaded: Allows() => denied 397 require.Equal(t, api.Denied, repo.AllowsIngressRLocked(fooToBar)) 398 repo.Mutex.RUnlock() 399 400 tag1 := labels.LabelArray{labels.ParseLabel("tag1")} 401 rule1 := api.Rule{ 402 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 403 Ingress: []api.IngressRule{ 404 { 405 IngressCommonRule: api.IngressCommonRule{ 406 FromEndpoints: []api.EndpointSelector{ 407 api.NewESFromLabels(labels.ParseSelectLabel("foo")), 408 }, 409 }, 410 }, 411 }, 412 Labels: tag1, 413 } 414 415 // selector: groupA 416 // require: groupA 417 rule2 := api.Rule{ 418 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("groupA")), 419 Ingress: []api.IngressRule{ 420 { 421 IngressCommonRule: api.IngressCommonRule{ 422 FromRequires: []api.EndpointSelector{ 423 api.NewESFromLabels(labels.ParseSelectLabel("groupA")), 424 }, 425 }, 426 }, 427 }, 428 Labels: tag1, 429 } 430 rule3 := api.Rule{ 431 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar2")), 432 Ingress: []api.IngressRule{ 433 { 434 IngressCommonRule: api.IngressCommonRule{ 435 FromEndpoints: []api.EndpointSelector{ 436 api.NewESFromLabels(labels.ParseSelectLabel("foo")), 437 }, 438 }, 439 }, 440 }, 441 Labels: tag1, 442 } 443 444 _, _, err := repo.mustAdd(rule1) 445 require.Nil(t, err) 446 _, _, err = repo.mustAdd(rule2) 447 require.Nil(t, err) 448 _, _, err = repo.mustAdd(rule3) 449 require.Nil(t, err) 450 451 // foo=>bar is OK 452 require.Equal(t, api.Allowed, repo.AllowsIngressRLocked(fooToBar)) 453 454 // foo=>bar2 is OK 455 require.Equal(t, api.Allowed, repo.AllowsIngressRLocked(&SearchContext{ 456 From: labels.ParseSelectLabelArray("foo"), 457 To: labels.ParseSelectLabelArray("bar2"), 458 })) 459 460 // foo=>bar inside groupA is OK 461 require.Equal(t, api.Allowed, repo.AllowsIngressRLocked(&SearchContext{ 462 From: labels.ParseSelectLabelArray("foo", "groupA"), 463 To: labels.ParseSelectLabelArray("bar", "groupA"), 464 })) 465 466 // groupB can't talk to groupA => Denied 467 require.Equal(t, api.Denied, repo.AllowsIngressRLocked(&SearchContext{ 468 From: labels.ParseSelectLabelArray("foo", "groupB"), 469 To: labels.ParseSelectLabelArray("bar", "groupA"), 470 })) 471 472 // no restriction on groupB, unused label => OK 473 require.Equal(t, api.Allowed, repo.AllowsIngressRLocked(&SearchContext{ 474 From: labels.ParseSelectLabelArray("foo", "groupB"), 475 To: labels.ParseSelectLabelArray("bar", "groupB"), 476 })) 477 478 // foo=>bar3, no rule => Denied 479 require.Equal(t, api.Denied, repo.AllowsIngressRLocked(&SearchContext{ 480 From: labels.ParseSelectLabelArray("foo"), 481 To: labels.ParseSelectLabelArray("bar3"), 482 })) 483 } 484 485 func TestAllowsEgress(t *testing.T) { 486 td := newTestData() 487 repo := td.repo 488 489 fooToBar := &SearchContext{ 490 From: labels.ParseSelectLabelArray("foo"), 491 To: labels.ParseSelectLabelArray("bar"), 492 } 493 494 repo.Mutex.RLock() 495 // no rules loaded: Allows() => denied 496 require.Equal(t, api.Denied, repo.AllowsEgressRLocked(fooToBar)) 497 repo.Mutex.RUnlock() 498 499 tag1 := labels.LabelArray{labels.ParseLabel("tag1")} 500 rule1 := api.Rule{ 501 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("foo")), 502 Egress: []api.EgressRule{ 503 { 504 EgressCommonRule: api.EgressCommonRule{ 505 ToEndpoints: []api.EndpointSelector{ 506 api.NewESFromLabels(labels.ParseSelectLabel("bar")), 507 }, 508 }, 509 }, 510 }, 511 Labels: tag1, 512 } 513 514 // selector: groupA 515 // require: groupA 516 rule2 := api.Rule{ 517 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("groupA")), 518 Egress: []api.EgressRule{ 519 { 520 EgressCommonRule: api.EgressCommonRule{ 521 ToRequires: []api.EndpointSelector{ 522 api.NewESFromLabels(labels.ParseSelectLabel("groupA")), 523 }, 524 }, 525 }, 526 }, 527 Labels: tag1, 528 } 529 rule3 := api.Rule{ 530 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("foo")), 531 Egress: []api.EgressRule{ 532 { 533 EgressCommonRule: api.EgressCommonRule{ 534 ToEndpoints: []api.EndpointSelector{ 535 api.NewESFromLabels(labels.ParseSelectLabel("bar2")), 536 }, 537 }, 538 }, 539 }, 540 Labels: tag1, 541 } 542 _, _, err := repo.mustAdd(rule1) 543 require.Nil(t, err) 544 _, _, err = repo.mustAdd(rule2) 545 require.Nil(t, err) 546 _, _, err = repo.mustAdd(rule3) 547 require.Nil(t, err) 548 549 // foo=>bar is OK 550 logBuffer := new(bytes.Buffer) 551 result := repo.AllowsEgressRLocked(fooToBar.WithLogger(logBuffer)) 552 if !assert.EqualValues(t, api.Allowed, result) { 553 t.Logf("%s", logBuffer.String()) 554 t.Errorf("Resolved policy did not match expected: \n%s", err) 555 } 556 557 // foo=>bar2 is OK 558 require.Equal(t, api.Allowed, repo.AllowsEgressRLocked(&SearchContext{ 559 From: labels.ParseSelectLabelArray("foo"), 560 To: labels.ParseSelectLabelArray("bar2"), 561 })) 562 563 // foo=>bar inside groupA is OK 564 require.Equal(t, api.Allowed, repo.AllowsEgressRLocked(&SearchContext{ 565 From: labels.ParseSelectLabelArray("foo", "groupA"), 566 To: labels.ParseSelectLabelArray("bar", "groupA"), 567 })) 568 569 buffer := new(bytes.Buffer) 570 // groupB can't talk to groupA => Denied 571 ctx := &SearchContext{ 572 To: labels.ParseSelectLabelArray("foo", "groupB"), 573 From: labels.ParseSelectLabelArray("bar", "groupA"), 574 Logging: stdlog.New(buffer, "", 0), 575 Trace: TRACE_VERBOSE, 576 } 577 verdict := repo.AllowsEgressRLocked(ctx) 578 require.Equal(t, api.Denied, verdict) 579 580 // no restriction on groupB, unused label => OK 581 require.Equal(t, api.Allowed, repo.AllowsEgressRLocked(&SearchContext{ 582 From: labels.ParseSelectLabelArray("foo", "groupB"), 583 To: labels.ParseSelectLabelArray("bar", "groupB"), 584 })) 585 586 // foo=>bar3, no rule => Denied 587 require.Equal(t, api.Denied, repo.AllowsEgressRLocked(&SearchContext{ 588 From: labels.ParseSelectLabelArray("foo"), 589 To: labels.ParseSelectLabelArray("bar3"), 590 })) 591 } 592 593 func TestWildcardL3RulesIngress(t *testing.T) { 594 td := newTestData() 595 repo := td.repo 596 597 labelsL3 := labels.LabelArray{labels.ParseLabel("L3")} 598 labelsKafka := labels.LabelArray{labels.ParseLabel("kafka")} 599 labelsICMP := labels.LabelArray{labels.ParseLabel("icmp")} 600 labelsICMPv6 := labels.LabelArray{labels.ParseLabel("icmpv6")} 601 labelsHTTP := labels.LabelArray{labels.ParseLabel("http")} 602 labelsL7 := labels.LabelArray{labels.ParseLabel("l7")} 603 604 l3Rule := api.Rule{ 605 EndpointSelector: selFoo, 606 Ingress: []api.IngressRule{ 607 { 608 IngressCommonRule: api.IngressCommonRule{ 609 FromEndpoints: []api.EndpointSelector{selBar1}, 610 }, 611 }, 612 }, 613 Labels: labelsL3, 614 } 615 l3Rule.Sanitize() 616 _, _, err := repo.mustAdd(l3Rule) 617 require.Nil(t, err) 618 619 kafkaRule := api.Rule{ 620 EndpointSelector: selFoo, 621 Ingress: []api.IngressRule{ 622 { 623 IngressCommonRule: api.IngressCommonRule{ 624 FromEndpoints: []api.EndpointSelector{selBar2}, 625 }, 626 ToPorts: []api.PortRule{{ 627 Ports: []api.PortProtocol{ 628 {Port: "9092", Protocol: api.ProtoTCP}, 629 }, 630 Rules: &api.L7Rules{ 631 Kafka: []kafka.PortRule{ 632 {APIKey: "produce"}, 633 }, 634 }, 635 }}, 636 }, 637 }, 638 Labels: labelsKafka, 639 } 640 kafkaRule.Sanitize() 641 _, _, err = repo.mustAdd(kafkaRule) 642 require.Nil(t, err) 643 644 httpRule := api.Rule{ 645 EndpointSelector: selFoo, 646 Ingress: []api.IngressRule{ 647 { 648 IngressCommonRule: api.IngressCommonRule{ 649 FromEndpoints: []api.EndpointSelector{selBar2}, 650 }, 651 ToPorts: []api.PortRule{{ 652 Ports: []api.PortProtocol{ 653 {Port: "80", Protocol: api.ProtoTCP}, 654 }, 655 Rules: &api.L7Rules{ 656 HTTP: []api.PortRuleHTTP{ 657 {Method: "GET", Path: "/"}, 658 }, 659 }, 660 }}, 661 }, 662 }, 663 Labels: labelsHTTP, 664 } 665 _, _, err = repo.mustAdd(httpRule) 666 require.Nil(t, err) 667 668 l7Rule := api.Rule{ 669 EndpointSelector: selFoo, 670 Ingress: []api.IngressRule{ 671 { 672 IngressCommonRule: api.IngressCommonRule{ 673 FromEndpoints: []api.EndpointSelector{selBar2}, 674 }, 675 ToPorts: []api.PortRule{{ 676 Ports: []api.PortProtocol{ 677 {Port: "9090", Protocol: api.ProtoTCP}, 678 }, 679 Rules: &api.L7Rules{ 680 L7Proto: "tester", 681 L7: []api.PortRuleL7{map[string]string{"method": "GET", "path": "/"}}, 682 }, 683 }}, 684 }, 685 }, 686 Labels: labelsL7, 687 } 688 _, _, err = repo.mustAdd(l7Rule) 689 require.Nil(t, err) 690 691 icmpV4Type := intstr.FromInt(8) 692 icmpRule := api.Rule{ 693 EndpointSelector: selFoo, 694 Ingress: []api.IngressRule{ 695 { 696 IngressCommonRule: api.IngressCommonRule{ 697 FromEndpoints: []api.EndpointSelector{selBar2}, 698 }, 699 ICMPs: api.ICMPRules{{ 700 Fields: []api.ICMPField{{ 701 Type: &icmpV4Type, 702 }}, 703 }}, 704 }, 705 }, 706 Labels: labelsICMP, 707 } 708 _, _, err = repo.mustAdd(icmpRule) 709 require.Nil(t, err) 710 711 icmpV6Type := intstr.FromInt(128) 712 icmpV6Rule := api.Rule{ 713 EndpointSelector: selFoo, 714 Ingress: []api.IngressRule{ 715 { 716 IngressCommonRule: api.IngressCommonRule{ 717 FromEndpoints: []api.EndpointSelector{selBar2}, 718 }, 719 ICMPs: api.ICMPRules{{ 720 Fields: []api.ICMPField{{ 721 Type: &icmpV6Type, 722 Family: api.IPv6Family, 723 }}, 724 }}, 725 }, 726 }, 727 Labels: labelsICMPv6, 728 } 729 _, _, err = repo.mustAdd(icmpV6Rule) 730 require.Nil(t, err) 731 732 ctx := &SearchContext{ 733 To: labels.ParseSelectLabelArray("id=foo"), 734 } 735 736 repo.Mutex.RLock() 737 defer repo.Mutex.RUnlock() 738 739 policy, err := repo.ResolveL4IngressPolicy(ctx) 740 require.Nil(t, err) 741 742 expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{ 743 "0/ANY": { 744 Port: 0, 745 Protocol: api.ProtoAny, 746 U8Proto: 0x0, 747 PerSelectorPolicies: L7DataMap{ 748 td.cachedSelectorBar1: nil, 749 }, 750 Ingress: true, 751 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar1: {labelsL3}}, 752 }, 753 "8/ICMP": { 754 Port: 8, 755 Protocol: api.ProtoICMP, 756 U8Proto: 0x1, 757 PerSelectorPolicies: L7DataMap{ 758 td.cachedSelectorBar2: nil, 759 }, 760 Ingress: true, 761 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsICMP}}, 762 }, 763 "128/ICMPV6": { 764 Port: 128, 765 Protocol: api.ProtoICMPv6, 766 U8Proto: 0x3A, 767 PerSelectorPolicies: L7DataMap{ 768 td.cachedSelectorBar2: nil, 769 }, 770 Ingress: true, 771 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsICMPv6}}, 772 }, 773 "9092/TCP": { 774 Port: 9092, 775 Protocol: api.ProtoTCP, 776 U8Proto: 0x6, 777 L7Parser: ParserTypeKafka, 778 Ingress: true, 779 PerSelectorPolicies: L7DataMap{ 780 td.cachedSelectorBar2: &PerSelectorPolicy{ 781 L7Rules: api.L7Rules{ 782 Kafka: []kafka.PortRule{kafkaRule.Ingress[0].ToPorts[0].Rules.Kafka[0]}, 783 }, 784 isRedirect: true, 785 }, 786 }, 787 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsKafka}}, 788 }, 789 "80/TCP": { 790 Port: 80, 791 Protocol: api.ProtoTCP, 792 U8Proto: 0x6, 793 L7Parser: ParserTypeHTTP, 794 Ingress: true, 795 PerSelectorPolicies: L7DataMap{ 796 td.cachedSelectorBar2: &PerSelectorPolicy{ 797 L7Rules: api.L7Rules{ 798 HTTP: []api.PortRuleHTTP{httpRule.Ingress[0].ToPorts[0].Rules.HTTP[0]}, 799 }, 800 isRedirect: true, 801 }, 802 }, 803 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsHTTP}}, 804 }, 805 "9090/TCP": { 806 Port: 9090, 807 Protocol: api.ProtoTCP, 808 U8Proto: 0x6, 809 L7Parser: L7ParserType("tester"), 810 Ingress: true, 811 PerSelectorPolicies: L7DataMap{ 812 td.cachedSelectorBar2: &PerSelectorPolicy{ 813 L7Rules: api.L7Rules{ 814 L7Proto: "tester", 815 L7: []api.PortRuleL7{l7Rule.Ingress[0].ToPorts[0].Rules.L7[0]}, 816 }, 817 isRedirect: true, 818 }, 819 }, 820 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsL7}}, 821 }, 822 }) 823 require.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy)) 824 policy.Detach(repo.GetSelectorCache()) 825 } 826 827 func TestWildcardL4RulesIngress(t *testing.T) { 828 td := newTestData() 829 repo := td.repo 830 831 selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo")) 832 selBar1 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar1")) 833 selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2")) 834 835 labelsL4Kafka := labels.LabelArray{labels.ParseLabel("L4-kafka")} 836 labelsL7Kafka := labels.LabelArray{labels.ParseLabel("kafka")} 837 labelsL4HTTP := labels.LabelArray{labels.ParseLabel("L4-http")} 838 labelsL7HTTP := labels.LabelArray{labels.ParseLabel("http")} 839 840 l49092Rule := api.Rule{ 841 EndpointSelector: selFoo, 842 Ingress: []api.IngressRule{ 843 { 844 IngressCommonRule: api.IngressCommonRule{ 845 FromEndpoints: []api.EndpointSelector{selBar1}, 846 }, 847 ToPorts: []api.PortRule{{ 848 Ports: []api.PortProtocol{ 849 {Port: "9092", Protocol: api.ProtoTCP}, 850 }, 851 }}, 852 }, 853 }, 854 Labels: labelsL4Kafka, 855 } 856 l49092Rule.Sanitize() 857 _, _, err := repo.mustAdd(l49092Rule) 858 require.Nil(t, err) 859 860 kafkaRule := api.Rule{ 861 EndpointSelector: selFoo, 862 Ingress: []api.IngressRule{ 863 { 864 IngressCommonRule: api.IngressCommonRule{ 865 FromEndpoints: []api.EndpointSelector{selBar2}, 866 }, 867 ToPorts: []api.PortRule{{ 868 Ports: []api.PortProtocol{ 869 {Port: "9092", Protocol: api.ProtoTCP}, 870 }, 871 Rules: &api.L7Rules{ 872 Kafka: []kafka.PortRule{ 873 {APIKey: "produce"}, 874 }, 875 }, 876 }}, 877 }, 878 }, 879 Labels: labelsL7Kafka, 880 } 881 kafkaRule.Sanitize() 882 _, _, err = repo.mustAdd(kafkaRule) 883 require.Nil(t, err) 884 885 l480Rule := api.Rule{ 886 EndpointSelector: selFoo, 887 Ingress: []api.IngressRule{ 888 { 889 IngressCommonRule: api.IngressCommonRule{ 890 FromEndpoints: []api.EndpointSelector{selBar1}, 891 }, 892 ToPorts: []api.PortRule{{ 893 Ports: []api.PortProtocol{ 894 {Port: "80", Protocol: api.ProtoTCP}, 895 }, 896 }}, 897 }, 898 }, 899 Labels: labelsL4HTTP, 900 } 901 l480Rule.Sanitize() 902 _, _, err = repo.mustAdd(l480Rule) 903 require.Nil(t, err) 904 905 httpRule := api.Rule{ 906 EndpointSelector: selFoo, 907 Ingress: []api.IngressRule{ 908 { 909 IngressCommonRule: api.IngressCommonRule{ 910 FromEndpoints: []api.EndpointSelector{selBar2}, 911 }, 912 ToPorts: []api.PortRule{{ 913 Ports: []api.PortProtocol{ 914 {Port: "80", Protocol: api.ProtoTCP}, 915 }, 916 Rules: &api.L7Rules{ 917 HTTP: []api.PortRuleHTTP{ 918 {Method: "GET", Path: "/"}, 919 }, 920 }, 921 }}, 922 }, 923 }, 924 Labels: labelsL7HTTP, 925 } 926 _, _, err = repo.mustAdd(httpRule) 927 require.Nil(t, err) 928 929 ctx := &SearchContext{ 930 To: labels.ParseSelectLabelArray("id=foo"), 931 } 932 933 repo.Mutex.RLock() 934 defer repo.Mutex.RUnlock() 935 936 policy, err := repo.ResolveL4IngressPolicy(ctx) 937 require.Nil(t, err) 938 939 expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{ 940 "80/TCP": { 941 Port: 80, 942 Protocol: api.ProtoTCP, 943 U8Proto: 0x6, 944 L7Parser: ParserTypeHTTP, 945 Ingress: true, 946 PerSelectorPolicies: L7DataMap{ 947 td.cachedSelectorBar1: nil, 948 td.cachedSelectorBar2: &PerSelectorPolicy{ 949 L7Rules: api.L7Rules{ 950 HTTP: []api.PortRuleHTTP{httpRule.Ingress[0].ToPorts[0].Rules.HTTP[0]}, 951 }, 952 isRedirect: true, 953 }, 954 }, 955 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 956 td.cachedSelectorBar1: {labelsL4HTTP}, 957 td.cachedSelectorBar2: {labelsL7HTTP}, 958 }, 959 }, 960 "9092/TCP": { 961 Port: 9092, 962 Protocol: api.ProtoTCP, 963 U8Proto: 0x6, 964 L7Parser: ParserTypeKafka, 965 Ingress: true, 966 PerSelectorPolicies: L7DataMap{ 967 td.cachedSelectorBar1: nil, 968 td.cachedSelectorBar2: &PerSelectorPolicy{ 969 L7Rules: api.L7Rules{ 970 Kafka: []kafka.PortRule{kafkaRule.Ingress[0].ToPorts[0].Rules.Kafka[0]}, 971 }, 972 isRedirect: true, 973 }, 974 }, 975 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 976 td.cachedSelectorBar1: {labelsL4Kafka}, 977 td.cachedSelectorBar2: {labelsL7Kafka}, 978 }, 979 }, 980 }) 981 require.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy)) 982 policy.Detach(repo.GetSelectorCache()) 983 } 984 985 func TestL3DependentL4IngressFromRequires(t *testing.T) { 986 td := newTestData() 987 repo := td.repo 988 989 selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo")) 990 selBar1 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar1")) 991 selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2")) 992 993 l480Rule := api.Rule{ 994 EndpointSelector: selFoo, 995 Ingress: []api.IngressRule{ 996 { 997 IngressCommonRule: api.IngressCommonRule{ 998 FromEndpoints: []api.EndpointSelector{ 999 selBar1, 1000 }, 1001 }, 1002 ToPorts: []api.PortRule{{ 1003 Ports: []api.PortProtocol{ 1004 {Port: "80", Protocol: api.ProtoTCP}, 1005 }, 1006 }}, 1007 }, 1008 { 1009 IngressCommonRule: api.IngressCommonRule{ 1010 FromRequires: []api.EndpointSelector{selBar2}, 1011 }, 1012 }, 1013 }, 1014 } 1015 l480Rule.Sanitize() 1016 _, _, err := repo.mustAdd(l480Rule) 1017 require.Nil(t, err) 1018 1019 ctx := &SearchContext{ 1020 To: labels.ParseSelectLabelArray("id=foo"), 1021 } 1022 1023 repo.Mutex.RLock() 1024 defer repo.Mutex.RUnlock() 1025 1026 policy, err := repo.ResolveL4IngressPolicy(ctx) 1027 require.Nil(t, err) 1028 1029 expectedSelector := api.NewESFromMatchRequirements(map[string]string{"any.id": "bar1"}, []slim_metav1.LabelSelectorRequirement{ 1030 { 1031 Key: "any.id", 1032 Operator: slim_metav1.LabelSelectorOpIn, 1033 Values: []string{"bar2"}, 1034 }, 1035 }) 1036 expectedCachedSelector, _ := td.sc.AddIdentitySelector(dummySelectorCacheUser, nil, expectedSelector) 1037 1038 expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{ 1039 "80/TCP": { 1040 Port: 80, 1041 Protocol: api.ProtoTCP, 1042 U8Proto: 0x6, 1043 PerSelectorPolicies: L7DataMap{ 1044 expectedCachedSelector: nil, 1045 }, 1046 Ingress: true, 1047 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 1048 expectedCachedSelector: {nil}, 1049 }, 1050 }, 1051 }) 1052 require.Equal(t, expectedPolicy, policy) 1053 policy.Detach(repo.GetSelectorCache()) 1054 } 1055 1056 func TestL3DependentL4EgressFromRequires(t *testing.T) { 1057 td := newTestData() 1058 repo := td.repo 1059 1060 selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo")) 1061 selBar1 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar1")) 1062 selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2")) 1063 1064 l480Rule := api.Rule{ 1065 EndpointSelector: selFoo, 1066 Egress: []api.EgressRule{ 1067 { 1068 EgressCommonRule: api.EgressCommonRule{ 1069 ToEndpoints: []api.EndpointSelector{ 1070 selBar1, 1071 }, 1072 }, 1073 ToPorts: []api.PortRule{{ 1074 Ports: []api.PortProtocol{ 1075 {Port: "80", Protocol: api.ProtoTCP}, 1076 }, 1077 }}, 1078 }, 1079 { 1080 EgressCommonRule: api.EgressCommonRule{ 1081 ToEndpoints: []api.EndpointSelector{ 1082 api.WildcardEndpointSelector, 1083 }, 1084 ToRequires: []api.EndpointSelector{selBar2}, 1085 }, 1086 }, 1087 }, 1088 } 1089 l480Rule.Sanitize() 1090 _, _, err := repo.mustAdd(l480Rule) 1091 require.Nil(t, err) 1092 1093 ctx := &SearchContext{ 1094 From: labels.ParseSelectLabelArray("id=foo"), 1095 } 1096 1097 repo.Mutex.RLock() 1098 defer repo.Mutex.RUnlock() 1099 1100 logBuffer := new(bytes.Buffer) 1101 policy, err := repo.ResolveL4EgressPolicy(ctx.WithLogger(logBuffer)) 1102 require.Nil(t, err) 1103 1104 expectedSelector := api.NewESFromMatchRequirements(map[string]string{"any.id": "bar1"}, []slim_metav1.LabelSelectorRequirement{ 1105 { 1106 Key: "any.id", 1107 Operator: slim_metav1.LabelSelectorOpIn, 1108 Values: []string{"bar2"}, 1109 }, 1110 }) 1111 expectedSelector2 := api.NewESFromMatchRequirements(map[string]string{}, []slim_metav1.LabelSelectorRequirement{ 1112 { 1113 Key: "any.id", 1114 Operator: slim_metav1.LabelSelectorOpIn, 1115 Values: []string{"bar2"}, 1116 }, 1117 }) 1118 expectedCachedSelector, _ := td.sc.AddIdentitySelector(dummySelectorCacheUser, nil, expectedSelector) 1119 expectedCachedSelector2, _ := td.sc.AddIdentitySelector(dummySelectorCacheUser, nil, expectedSelector2) 1120 1121 expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{ 1122 "0/ANY": { 1123 Port: 0, 1124 Protocol: "ANY", 1125 U8Proto: 0x0, 1126 PerSelectorPolicies: L7DataMap{ 1127 expectedCachedSelector2: nil, 1128 }, 1129 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 1130 expectedCachedSelector2: {nil}, 1131 }, 1132 }, 1133 "80/TCP": { 1134 Port: 80, 1135 Protocol: api.ProtoTCP, 1136 U8Proto: 0x6, 1137 PerSelectorPolicies: L7DataMap{ 1138 expectedCachedSelector: nil, 1139 }, 1140 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 1141 expectedCachedSelector: {nil}, 1142 }, 1143 }, 1144 }) 1145 if !assert.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy)) { 1146 t.Errorf("Policy doesn't match expected:\n%s", logBuffer.String()) 1147 } 1148 policy.Detach(repo.GetSelectorCache()) 1149 } 1150 1151 func TestWildcardL3RulesEgress(t *testing.T) { 1152 td := newTestData() 1153 repo := td.repo 1154 1155 selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo")) 1156 selBar1 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar1")) 1157 selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2")) 1158 1159 labelsL4 := labels.LabelArray{labels.ParseLabel("L4")} 1160 labelsDNS := labels.LabelArray{labels.ParseLabel("dns")} 1161 labelsHTTP := labels.LabelArray{labels.ParseLabel("http")} 1162 labelsICMP := labels.LabelArray{labels.ParseLabel("icmp")} 1163 labelsICMPv6 := labels.LabelArray{labels.ParseLabel("icmpv6")} 1164 1165 l3Rule := api.Rule{ 1166 EndpointSelector: selFoo, 1167 Egress: []api.EgressRule{ 1168 { 1169 EgressCommonRule: api.EgressCommonRule{ 1170 ToEndpoints: []api.EndpointSelector{selBar1}, 1171 }, 1172 }, 1173 }, 1174 Labels: labelsL4, 1175 } 1176 l3Rule.Sanitize() 1177 _, _, err := repo.mustAdd(l3Rule) 1178 require.Nil(t, err) 1179 1180 dnsRule := api.Rule{ 1181 EndpointSelector: selFoo, 1182 Egress: []api.EgressRule{ 1183 { 1184 EgressCommonRule: api.EgressCommonRule{ 1185 ToEndpoints: []api.EndpointSelector{selBar2}, 1186 }, 1187 ToPorts: []api.PortRule{{ 1188 Ports: []api.PortProtocol{ 1189 {Port: "53", Protocol: api.ProtoUDP}, 1190 }, 1191 Rules: &api.L7Rules{ 1192 DNS: []api.PortRuleDNS{ 1193 {MatchName: "empire.gov"}, 1194 }, 1195 }, 1196 }}, 1197 }, 1198 }, 1199 Labels: labelsDNS, 1200 } 1201 dnsRule.Sanitize() 1202 _, _, err = repo.mustAdd(dnsRule) 1203 require.Nil(t, err) 1204 1205 httpRule := api.Rule{ 1206 EndpointSelector: selFoo, 1207 Egress: []api.EgressRule{ 1208 { 1209 EgressCommonRule: api.EgressCommonRule{ 1210 ToEndpoints: []api.EndpointSelector{selBar2}, 1211 }, 1212 ToPorts: []api.PortRule{{ 1213 Ports: []api.PortProtocol{ 1214 {Port: "80", Protocol: api.ProtoTCP}, 1215 }, 1216 Rules: &api.L7Rules{ 1217 HTTP: []api.PortRuleHTTP{ 1218 {Method: "GET", Path: "/"}, 1219 }, 1220 }, 1221 }}, 1222 }, 1223 }, 1224 Labels: labelsHTTP, 1225 } 1226 _, _, err = repo.mustAdd(httpRule) 1227 require.Nil(t, err) 1228 1229 icmpV4Type := intstr.FromInt(8) 1230 icmpRule := api.Rule{ 1231 EndpointSelector: selFoo, 1232 Egress: []api.EgressRule{ 1233 { 1234 EgressCommonRule: api.EgressCommonRule{ 1235 ToEndpoints: []api.EndpointSelector{selBar2}, 1236 }, 1237 ICMPs: api.ICMPRules{{ 1238 Fields: []api.ICMPField{{ 1239 Type: &icmpV4Type, 1240 }}, 1241 }}, 1242 }, 1243 }, 1244 Labels: labelsICMP, 1245 } 1246 _, _, err = repo.mustAdd(icmpRule) 1247 require.Nil(t, err) 1248 1249 icmpV6Type := intstr.FromInt(128) 1250 icmpV6Rule := api.Rule{ 1251 EndpointSelector: selFoo, 1252 Egress: []api.EgressRule{ 1253 { 1254 EgressCommonRule: api.EgressCommonRule{ 1255 ToEndpoints: []api.EndpointSelector{selBar2}, 1256 }, 1257 ICMPs: api.ICMPRules{{ 1258 Fields: []api.ICMPField{{ 1259 Type: &icmpV6Type, 1260 Family: "IPv6", 1261 }}, 1262 }}, 1263 }, 1264 }, 1265 Labels: labelsICMPv6, 1266 } 1267 _, _, err = repo.mustAdd(icmpV6Rule) 1268 require.Nil(t, err) 1269 1270 ctx := &SearchContext{ 1271 From: labels.ParseSelectLabelArray("id=foo"), 1272 } 1273 1274 repo.Mutex.RLock() 1275 defer repo.Mutex.RUnlock() 1276 1277 logBuffer := new(bytes.Buffer) 1278 policy, err := repo.ResolveL4EgressPolicy(ctx.WithLogger(logBuffer)) 1279 require.Nil(t, err) 1280 1281 // Traffic to bar1 should not be forwarded to the DNS or HTTP 1282 // proxy at all, but if it is (e.g., for visibility, the 1283 // "0/ANY" rule should allow such traffic through. 1284 expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{ 1285 "53/UDP": { 1286 Port: 53, 1287 Protocol: api.ProtoUDP, 1288 U8Proto: 0x11, 1289 L7Parser: ParserTypeDNS, 1290 Ingress: false, 1291 PerSelectorPolicies: L7DataMap{ 1292 td.cachedSelectorBar2: &PerSelectorPolicy{ 1293 L7Rules: api.L7Rules{ 1294 DNS: []api.PortRuleDNS{dnsRule.Egress[0].ToPorts[0].Rules.DNS[0]}, 1295 }, 1296 isRedirect: true, 1297 }, 1298 }, 1299 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsDNS}}, 1300 }, 1301 "80/TCP": { 1302 Port: 80, 1303 Protocol: api.ProtoTCP, 1304 U8Proto: 0x6, 1305 L7Parser: ParserTypeHTTP, 1306 Ingress: false, 1307 PerSelectorPolicies: L7DataMap{ 1308 td.cachedSelectorBar2: &PerSelectorPolicy{ 1309 L7Rules: api.L7Rules{ 1310 HTTP: []api.PortRuleHTTP{httpRule.Egress[0].ToPorts[0].Rules.HTTP[0]}, 1311 }, 1312 isRedirect: true, 1313 }, 1314 }, 1315 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsHTTP}}, 1316 }, 1317 "8/ICMP": { 1318 Port: 8, 1319 Protocol: api.ProtoICMP, 1320 U8Proto: 0x1, 1321 PerSelectorPolicies: L7DataMap{ 1322 td.cachedSelectorBar2: nil, 1323 }, 1324 Ingress: false, 1325 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsICMP}}, 1326 }, 1327 "128/ICMPV6": { 1328 Port: 128, 1329 Protocol: api.ProtoICMPv6, 1330 U8Proto: 0x3A, 1331 PerSelectorPolicies: L7DataMap{ 1332 td.cachedSelectorBar2: nil, 1333 }, 1334 Ingress: false, 1335 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsICMPv6}}, 1336 }, 1337 "0/ANY": { 1338 Port: 0, 1339 Protocol: "ANY", 1340 U8Proto: 0x0, 1341 L7Parser: "", 1342 PerSelectorPolicies: L7DataMap{ 1343 td.cachedSelectorBar1: nil, 1344 }, 1345 Ingress: false, 1346 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar1: {labelsL4}}, 1347 }, 1348 }) 1349 if !assert.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy)) { 1350 t.Logf("%s", logBuffer.String()) 1351 t.Errorf("Resolved policy did not match expected: \n%s", err) 1352 } 1353 policy.Detach(repo.GetSelectorCache()) 1354 } 1355 1356 func TestWildcardL4RulesEgress(t *testing.T) { 1357 td := newTestData() 1358 repo := td.repo 1359 1360 selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo")) 1361 selBar1 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar1")) 1362 selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2")) 1363 1364 labelsL3DNS := labels.LabelArray{labels.ParseLabel("L3-dns")} 1365 labelsL7DNS := labels.LabelArray{labels.ParseLabel("dns")} 1366 labelsL3HTTP := labels.LabelArray{labels.ParseLabel("L3-http")} 1367 labelsL7HTTP := labels.LabelArray{labels.ParseLabel("http")} 1368 1369 l453Rule := api.Rule{ 1370 EndpointSelector: selFoo, 1371 Egress: []api.EgressRule{ 1372 { 1373 EgressCommonRule: api.EgressCommonRule{ 1374 ToEndpoints: []api.EndpointSelector{selBar1}, 1375 }, 1376 ToPorts: []api.PortRule{{ 1377 Ports: []api.PortProtocol{ 1378 {Port: "53", Protocol: api.ProtoUDP}, 1379 }, 1380 }}, 1381 }, 1382 }, 1383 Labels: labelsL3DNS, 1384 } 1385 l453Rule.Sanitize() 1386 _, _, err := repo.mustAdd(l453Rule) 1387 require.Nil(t, err) 1388 1389 dnsRule := api.Rule{ 1390 EndpointSelector: selFoo, 1391 Egress: []api.EgressRule{ 1392 { 1393 EgressCommonRule: api.EgressCommonRule{ 1394 ToEndpoints: []api.EndpointSelector{selBar2}, 1395 }, 1396 ToPorts: []api.PortRule{{ 1397 Ports: []api.PortProtocol{ 1398 {Port: "53", Protocol: api.ProtoUDP}, 1399 }, 1400 Rules: &api.L7Rules{ 1401 DNS: []api.PortRuleDNS{ 1402 {MatchName: "empire.gov"}, 1403 }, 1404 }, 1405 }}, 1406 }, 1407 }, 1408 Labels: labelsL7DNS, 1409 } 1410 dnsRule.Sanitize() 1411 _, _, err = repo.mustAdd(dnsRule) 1412 require.Nil(t, err) 1413 1414 l480Rule := api.Rule{ 1415 EndpointSelector: selFoo, 1416 Egress: []api.EgressRule{ 1417 { 1418 EgressCommonRule: api.EgressCommonRule{ 1419 ToEndpoints: []api.EndpointSelector{selBar1}, 1420 }, 1421 ToPorts: []api.PortRule{{ 1422 Ports: []api.PortProtocol{ 1423 {Port: "80", Protocol: api.ProtoTCP}, 1424 }, 1425 }}, 1426 }, 1427 }, 1428 Labels: labelsL3HTTP, 1429 } 1430 l480Rule.Sanitize() 1431 _, _, err = repo.mustAdd(l480Rule) 1432 require.Nil(t, err) 1433 1434 httpRule := api.Rule{ 1435 EndpointSelector: selFoo, 1436 Egress: []api.EgressRule{ 1437 { 1438 EgressCommonRule: api.EgressCommonRule{ 1439 ToEndpoints: []api.EndpointSelector{selBar2}, 1440 }, 1441 ToPorts: []api.PortRule{{ 1442 Ports: []api.PortProtocol{ 1443 {Port: "80", Protocol: api.ProtoTCP}, 1444 }, 1445 Rules: &api.L7Rules{ 1446 HTTP: []api.PortRuleHTTP{ 1447 {Method: "GET", Path: "/"}, 1448 }, 1449 }, 1450 }}, 1451 }, 1452 }, 1453 Labels: labelsL7HTTP, 1454 } 1455 _, _, err = repo.mustAdd(httpRule) 1456 require.Nil(t, err) 1457 1458 ctx := &SearchContext{ 1459 From: labels.ParseSelectLabelArray("id=foo"), 1460 } 1461 1462 repo.Mutex.RLock() 1463 defer repo.Mutex.RUnlock() 1464 1465 logBuffer := new(bytes.Buffer) 1466 policy, err := repo.ResolveL4EgressPolicy(ctx.WithLogger(logBuffer)) 1467 require.Nil(t, err) 1468 1469 // Bar1 should not be forwarded to the proxy, but if it is (e.g., for visibility), 1470 // the L3/L4 allow should pass it without an explicit L7 wildcard. 1471 expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{ 1472 "80/TCP": { 1473 Port: 80, 1474 Protocol: api.ProtoTCP, 1475 U8Proto: 0x6, 1476 L7Parser: ParserTypeHTTP, 1477 Ingress: false, 1478 PerSelectorPolicies: L7DataMap{ 1479 td.cachedSelectorBar1: nil, 1480 td.cachedSelectorBar2: &PerSelectorPolicy{ 1481 L7Rules: api.L7Rules{ 1482 HTTP: []api.PortRuleHTTP{httpRule.Egress[0].ToPorts[0].Rules.HTTP[0]}, 1483 }, 1484 isRedirect: true, 1485 }, 1486 }, 1487 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 1488 td.cachedSelectorBar1: {labelsL3HTTP}, 1489 td.cachedSelectorBar2: {labelsL7HTTP}, 1490 }, 1491 }, 1492 "53/UDP": { 1493 Port: 53, 1494 Protocol: api.ProtoUDP, 1495 U8Proto: 0x11, 1496 L7Parser: ParserTypeDNS, 1497 Ingress: false, 1498 PerSelectorPolicies: L7DataMap{ 1499 td.cachedSelectorBar1: nil, 1500 td.cachedSelectorBar2: &PerSelectorPolicy{ 1501 L7Rules: api.L7Rules{ 1502 DNS: []api.PortRuleDNS{dnsRule.Egress[0].ToPorts[0].Rules.DNS[0]}, 1503 }, 1504 isRedirect: true, 1505 }, 1506 }, 1507 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 1508 td.cachedSelectorBar1: {labelsL3DNS}, 1509 td.cachedSelectorBar2: {labelsL7DNS}, 1510 }, 1511 }, 1512 }) 1513 if !assert.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy)) { 1514 t.Logf("%s", logBuffer.String()) 1515 t.Error("Resolved policy did not match expected") 1516 } 1517 policy.Detach(repo.GetSelectorCache()) 1518 } 1519 1520 func TestWildcardCIDRRulesEgress(t *testing.T) { 1521 td := newTestData() 1522 repo := td.repo 1523 1524 labelsL3 := labels.LabelArray{labels.ParseLabel("L3")} 1525 labelsHTTP := labels.LabelArray{labels.ParseLabel("http")} 1526 1527 cidrSlice := api.CIDRSlice{"192.0.0.0/3"} 1528 cidrSelectors := cidrSlice.GetAsEndpointSelectors() 1529 var cachedSelectors CachedSelectorSlice 1530 for i := range cidrSelectors { 1531 c, _ := td.sc.AddIdentitySelector(dummySelectorCacheUser, nil, cidrSelectors[i]) 1532 cachedSelectors = append(cachedSelectors, c) 1533 defer td.sc.RemoveSelector(c, dummySelectorCacheUser) 1534 } 1535 selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo")) 1536 1537 l480Get := api.Rule{ 1538 EndpointSelector: selFoo, 1539 Egress: []api.EgressRule{ 1540 { 1541 EgressCommonRule: api.EgressCommonRule{ 1542 ToCIDR: api.CIDRSlice{"192.0.0.0/3"}, 1543 }, 1544 ToPorts: []api.PortRule{{ 1545 Ports: []api.PortProtocol{ 1546 { 1547 Port: "80", 1548 Protocol: api.ProtoTCP, 1549 }, 1550 }, 1551 Rules: &api.L7Rules{ 1552 HTTP: []api.PortRuleHTTP{ 1553 { 1554 Headers: []string{"X-My-Header: true"}, 1555 Method: "GET", 1556 Path: "/", 1557 }, 1558 }, 1559 }, 1560 }}, 1561 }, 1562 }, 1563 Labels: labelsHTTP, 1564 } 1565 l480Get.Sanitize() 1566 _, _, err := repo.mustAdd(l480Get) 1567 require.Nil(t, err) 1568 1569 l3Rule := api.Rule{ 1570 EndpointSelector: selFoo, 1571 Egress: []api.EgressRule{ 1572 { 1573 EgressCommonRule: api.EgressCommonRule{ 1574 ToCIDR: api.CIDRSlice{"192.0.0.0/3"}, 1575 }, 1576 }, 1577 }, 1578 Labels: labelsL3, 1579 } 1580 l3Rule.Sanitize() 1581 _, _, err = repo.mustAdd(l3Rule) 1582 require.Nil(t, err) 1583 1584 ctx := &SearchContext{ 1585 From: labels.ParseSelectLabelArray("id=foo"), 1586 } 1587 1588 repo.Mutex.RLock() 1589 defer repo.Mutex.RUnlock() 1590 1591 logBuffer := new(bytes.Buffer) 1592 policy, err := repo.ResolveL4EgressPolicy(ctx.WithLogger(logBuffer)) 1593 require.Nil(t, err) 1594 1595 // Port 80 policy does not need the wildcard, as the "0" port policy will allow the traffic. 1596 // HTTP rules can have side-effects, so they need to be retained even if shadowed by a wildcard. 1597 expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{ 1598 "80/TCP": { 1599 Port: 80, 1600 Protocol: api.ProtoTCP, 1601 U8Proto: 0x6, 1602 L7Parser: ParserTypeHTTP, 1603 Ingress: false, 1604 PerSelectorPolicies: L7DataMap{ 1605 cachedSelectors[0]: &PerSelectorPolicy{ 1606 L7Rules: api.L7Rules{ 1607 HTTP: []api.PortRuleHTTP{{ 1608 Headers: []string{"X-My-Header: true"}, 1609 Method: "GET", 1610 Path: "/", 1611 }}, 1612 }, 1613 isRedirect: true, 1614 }, 1615 }, 1616 RuleOrigin: map[CachedSelector]labels.LabelArrayList{cachedSelectors[0]: {labelsHTTP}}, 1617 }, 1618 "0/ANY": { 1619 Port: 0, 1620 Protocol: api.ProtoAny, 1621 U8Proto: 0x0, 1622 L7Parser: ParserTypeNone, 1623 Ingress: false, 1624 PerSelectorPolicies: L7DataMap{ 1625 cachedSelectors[0]: nil, 1626 }, 1627 RuleOrigin: map[CachedSelector]labels.LabelArrayList{cachedSelectors[0]: {labelsL3}}, 1628 }, 1629 }) 1630 if !assert.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy)) { 1631 t.Logf("%s", logBuffer.String()) 1632 t.Error("Resolved policy did not match expected") 1633 } 1634 policy.Detach(repo.GetSelectorCache()) 1635 } 1636 1637 func TestWildcardL3RulesIngressFromEntities(t *testing.T) { 1638 td := newTestData() 1639 repo := td.repo 1640 1641 selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo")) 1642 selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2")) 1643 1644 labelsL3 := labels.LabelArray{labels.ParseLabel("L3")} 1645 labelsKafka := labels.LabelArray{labels.ParseLabel("kafka")} 1646 labelsHTTP := labels.LabelArray{labels.ParseLabel("http")} 1647 1648 l3Rule := api.Rule{ 1649 EndpointSelector: selFoo, 1650 Ingress: []api.IngressRule{ 1651 { 1652 IngressCommonRule: api.IngressCommonRule{ 1653 FromEntities: api.EntitySlice{api.EntityWorld}, 1654 }, 1655 }, 1656 }, 1657 Labels: labelsL3, 1658 } 1659 l3Rule.Sanitize() 1660 _, _, err := repo.mustAdd(l3Rule) 1661 require.Nil(t, err) 1662 1663 kafkaRule := api.Rule{ 1664 EndpointSelector: selFoo, 1665 Ingress: []api.IngressRule{ 1666 { 1667 IngressCommonRule: api.IngressCommonRule{ 1668 FromEndpoints: []api.EndpointSelector{selBar2}, 1669 }, 1670 ToPorts: []api.PortRule{{ 1671 Ports: []api.PortProtocol{ 1672 {Port: "9092", Protocol: api.ProtoTCP}, 1673 }, 1674 Rules: &api.L7Rules{ 1675 Kafka: []kafka.PortRule{ 1676 {APIKey: "produce"}, 1677 }, 1678 }, 1679 }}, 1680 }, 1681 }, 1682 Labels: labelsKafka, 1683 } 1684 kafkaRule.Sanitize() 1685 _, _, err = repo.mustAdd(kafkaRule) 1686 require.Nil(t, err) 1687 1688 httpRule := api.Rule{ 1689 EndpointSelector: selFoo, 1690 Ingress: []api.IngressRule{ 1691 { 1692 IngressCommonRule: api.IngressCommonRule{ 1693 FromEndpoints: []api.EndpointSelector{selBar2}, 1694 }, 1695 ToPorts: []api.PortRule{{ 1696 Ports: []api.PortProtocol{ 1697 {Port: "80", Protocol: api.ProtoTCP}, 1698 }, 1699 Rules: &api.L7Rules{ 1700 HTTP: []api.PortRuleHTTP{ 1701 {Method: "GET", Path: "/"}, 1702 }, 1703 }, 1704 }}, 1705 }, 1706 }, 1707 Labels: labelsHTTP, 1708 } 1709 _, _, err = repo.mustAdd(httpRule) 1710 require.Nil(t, err) 1711 1712 ctx := &SearchContext{ 1713 To: labels.ParseSelectLabelArray("id=foo"), 1714 } 1715 1716 repo.Mutex.RLock() 1717 defer repo.Mutex.RUnlock() 1718 1719 policy, err := repo.ResolveL4IngressPolicy(ctx) 1720 require.Nil(t, err) 1721 require.Equal(t, 3, policy.Len()) 1722 selWorld := api.EntitySelectorMapping[api.EntityWorld][0] 1723 require.Equal(t, 1, len(policy.ExactLookup("80", 0, "TCP").PerSelectorPolicies)) 1724 cachedSelectorWorld := td.sc.FindCachedIdentitySelector(selWorld) 1725 require.NotNil(t, cachedSelectorWorld) 1726 1727 cachedSelectorWorldV4 := td.sc.FindCachedIdentitySelector(api.ReservedEndpointSelectors[labels.IDNameWorldIPv4]) 1728 require.NotNil(t, cachedSelectorWorldV4) 1729 1730 cachedSelectorWorldV6 := td.sc.FindCachedIdentitySelector(api.ReservedEndpointSelectors[labels.IDNameWorldIPv6]) 1731 require.NotNil(t, cachedSelectorWorldV6) 1732 1733 expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{ 1734 "0/ANY": { 1735 Port: 0, 1736 Protocol: "ANY", 1737 U8Proto: 0x0, 1738 L7Parser: "", 1739 PerSelectorPolicies: L7DataMap{ 1740 cachedSelectorWorld: nil, 1741 cachedSelectorWorldV4: nil, 1742 cachedSelectorWorldV6: nil, 1743 }, 1744 Ingress: true, 1745 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 1746 cachedSelectorWorld: {labelsL3}, 1747 cachedSelectorWorldV4: {labelsL3}, 1748 cachedSelectorWorldV6: {labelsL3}, 1749 }, 1750 }, 1751 "9092/TCP": { 1752 Port: 9092, 1753 Protocol: api.ProtoTCP, 1754 U8Proto: 0x6, 1755 L7Parser: ParserTypeKafka, 1756 Ingress: true, 1757 PerSelectorPolicies: L7DataMap{ 1758 td.cachedSelectorBar2: &PerSelectorPolicy{ 1759 L7Rules: api.L7Rules{ 1760 Kafka: []kafka.PortRule{kafkaRule.Ingress[0].ToPorts[0].Rules.Kafka[0]}, 1761 }, 1762 isRedirect: true, 1763 }, 1764 }, 1765 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsKafka}}, 1766 }, 1767 "80/TCP": { 1768 Port: 80, 1769 Protocol: api.ProtoTCP, 1770 U8Proto: 0x6, 1771 L7Parser: ParserTypeHTTP, 1772 Ingress: true, 1773 PerSelectorPolicies: L7DataMap{ 1774 td.cachedSelectorBar2: &PerSelectorPolicy{ 1775 L7Rules: api.L7Rules{ 1776 HTTP: []api.PortRuleHTTP{httpRule.Ingress[0].ToPorts[0].Rules.HTTP[0]}, 1777 }, 1778 isRedirect: true, 1779 }, 1780 }, 1781 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsHTTP}}, 1782 }, 1783 }) 1784 1785 require.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy)) 1786 policy.Detach(repo.GetSelectorCache()) 1787 } 1788 1789 func TestWildcardL3RulesEgressToEntities(t *testing.T) { 1790 td := newTestData() 1791 repo := td.repo 1792 1793 selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo")) 1794 selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2")) 1795 1796 labelsL3 := labels.LabelArray{labels.ParseLabel("L3")} 1797 labelsDNS := labels.LabelArray{labels.ParseLabel("dns")} 1798 labelsHTTP := labels.LabelArray{labels.ParseLabel("http")} 1799 1800 l3Rule := api.Rule{ 1801 EndpointSelector: selFoo, 1802 Egress: []api.EgressRule{ 1803 { 1804 EgressCommonRule: api.EgressCommonRule{ 1805 ToEntities: api.EntitySlice{api.EntityWorld}, 1806 }, 1807 }, 1808 }, 1809 Labels: labelsL3, 1810 } 1811 l3Rule.Sanitize() 1812 _, _, err := repo.mustAdd(l3Rule) 1813 require.Nil(t, err) 1814 1815 dnsRule := api.Rule{ 1816 EndpointSelector: selFoo, 1817 Egress: []api.EgressRule{ 1818 { 1819 EgressCommonRule: api.EgressCommonRule{ 1820 ToEndpoints: []api.EndpointSelector{selBar2}, 1821 }, 1822 ToPorts: []api.PortRule{{ 1823 Ports: []api.PortProtocol{ 1824 {Port: "53", Protocol: api.ProtoUDP}, 1825 }, 1826 Rules: &api.L7Rules{ 1827 DNS: []api.PortRuleDNS{ 1828 {MatchName: "empire.gov"}, 1829 }, 1830 }, 1831 }}, 1832 }, 1833 }, 1834 Labels: labelsDNS, 1835 } 1836 dnsRule.Sanitize() 1837 _, _, err = repo.mustAdd(dnsRule) 1838 require.Nil(t, err) 1839 1840 httpRule := api.Rule{ 1841 EndpointSelector: selFoo, 1842 Egress: []api.EgressRule{ 1843 { 1844 EgressCommonRule: api.EgressCommonRule{ 1845 ToEndpoints: []api.EndpointSelector{selBar2}, 1846 }, 1847 ToPorts: []api.PortRule{{ 1848 Ports: []api.PortProtocol{ 1849 {Port: "80", Protocol: api.ProtoTCP}, 1850 }, 1851 Rules: &api.L7Rules{ 1852 HTTP: []api.PortRuleHTTP{ 1853 {Method: "GET", Path: "/"}, 1854 }, 1855 }, 1856 }}, 1857 }, 1858 }, 1859 Labels: labelsHTTP, 1860 } 1861 _, _, err = repo.mustAdd(httpRule) 1862 require.Nil(t, err) 1863 1864 ctx := &SearchContext{ 1865 From: labels.ParseSelectLabelArray("id=foo"), 1866 } 1867 1868 repo.Mutex.RLock() 1869 defer repo.Mutex.RUnlock() 1870 1871 policy, err := repo.ResolveL4EgressPolicy(ctx) 1872 require.Nil(t, err) 1873 require.Equal(t, 3, policy.Len()) 1874 selWorld := api.EntitySelectorMapping[api.EntityWorld][0] 1875 require.Equal(t, 1, len(policy.ExactLookup("80", 0, "TCP").PerSelectorPolicies)) 1876 cachedSelectorWorld := td.sc.FindCachedIdentitySelector(selWorld) 1877 require.NotNil(t, cachedSelectorWorld) 1878 1879 cachedSelectorWorldV4 := td.sc.FindCachedIdentitySelector(api.ReservedEndpointSelectors[labels.IDNameWorldIPv4]) 1880 require.NotNil(t, cachedSelectorWorldV4) 1881 1882 cachedSelectorWorldV6 := td.sc.FindCachedIdentitySelector(api.ReservedEndpointSelectors[labels.IDNameWorldIPv6]) 1883 require.NotNil(t, cachedSelectorWorldV6) 1884 1885 expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{ 1886 "0/ANY": { 1887 Port: 0, 1888 Protocol: "ANY", 1889 U8Proto: 0x0, 1890 L7Parser: "", 1891 PerSelectorPolicies: L7DataMap{ 1892 cachedSelectorWorld: nil, 1893 cachedSelectorWorldV4: nil, 1894 cachedSelectorWorldV6: nil, 1895 }, 1896 Ingress: false, 1897 RuleOrigin: map[CachedSelector]labels.LabelArrayList{ 1898 cachedSelectorWorld: {labelsL3}, 1899 cachedSelectorWorldV4: {labelsL3}, 1900 cachedSelectorWorldV6: {labelsL3}, 1901 }, 1902 }, 1903 "53/UDP": { 1904 Port: 53, 1905 Protocol: api.ProtoUDP, 1906 U8Proto: 0x11, 1907 L7Parser: ParserTypeDNS, 1908 Ingress: false, 1909 PerSelectorPolicies: L7DataMap{ 1910 td.cachedSelectorBar2: &PerSelectorPolicy{ 1911 L7Rules: api.L7Rules{ 1912 DNS: []api.PortRuleDNS{dnsRule.Egress[0].ToPorts[0].Rules.DNS[0]}, 1913 }, 1914 isRedirect: true, 1915 }, 1916 }, 1917 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsDNS}}, 1918 }, 1919 "80/TCP": { 1920 Port: 80, 1921 Protocol: api.ProtoTCP, 1922 U8Proto: 0x6, 1923 L7Parser: ParserTypeHTTP, 1924 Ingress: false, 1925 PerSelectorPolicies: L7DataMap{ 1926 td.cachedSelectorBar2: &PerSelectorPolicy{ 1927 L7Rules: api.L7Rules{ 1928 HTTP: []api.PortRuleHTTP{httpRule.Egress[0].ToPorts[0].Rules.HTTP[0]}, 1929 }, 1930 isRedirect: true, 1931 }, 1932 }, 1933 RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsHTTP}}, 1934 }, 1935 }) 1936 1937 require.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy)) 1938 policy.Detach(repo.GetSelectorCache()) 1939 } 1940 1941 func TestMinikubeGettingStarted(t *testing.T) { 1942 td := newTestData() 1943 repo := td.repo 1944 1945 app2Selector := labels.ParseSelectLabelArray("id=app2") 1946 1947 fromApp2 := &SearchContext{ 1948 From: app2Selector, 1949 To: labels.ParseSelectLabelArray("id=app1"), 1950 Trace: TRACE_VERBOSE, 1951 } 1952 1953 fromApp3 := &SearchContext{ 1954 From: labels.ParseSelectLabelArray("id=app3"), 1955 To: labels.ParseSelectLabelArray("id=app1"), 1956 } 1957 1958 repo.Mutex.RLock() 1959 // no rules loaded: Allows() => denied 1960 require.Equal(t, api.Denied, repo.AllowsIngressRLocked(fromApp2)) 1961 require.Equal(t, api.Denied, repo.AllowsIngressRLocked(fromApp3)) 1962 repo.Mutex.RUnlock() 1963 1964 selFromApp2 := api.NewESFromLabels( 1965 labels.ParseSelectLabel("id=app2"), 1966 ) 1967 1968 selectorFromApp2 := []api.EndpointSelector{ 1969 selFromApp2, 1970 } 1971 1972 _, _, err := repo.mustAdd(api.Rule{ 1973 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("id=app1")), 1974 Ingress: []api.IngressRule{ 1975 { 1976 IngressCommonRule: api.IngressCommonRule{ 1977 FromEndpoints: selectorFromApp2, 1978 }, 1979 ToPorts: []api.PortRule{{ 1980 Ports: []api.PortProtocol{ 1981 {Port: "80", Protocol: api.ProtoTCP}, 1982 }, 1983 }}, 1984 }, 1985 }, 1986 }) 1987 require.Nil(t, err) 1988 1989 _, _, err = repo.mustAdd(api.Rule{ 1990 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("id=app1")), 1991 Ingress: []api.IngressRule{ 1992 { 1993 IngressCommonRule: api.IngressCommonRule{ 1994 FromEndpoints: selectorFromApp2, 1995 }, 1996 ToPorts: []api.PortRule{{ 1997 Ports: []api.PortProtocol{ 1998 {Port: "80", Protocol: api.ProtoTCP}, 1999 }, 2000 Rules: &api.L7Rules{ 2001 HTTP: []api.PortRuleHTTP{ 2002 {Method: "GET", Path: "/"}, 2003 }, 2004 }, 2005 }}, 2006 }, 2007 }, 2008 }) 2009 require.Nil(t, err) 2010 2011 _, _, err = repo.mustAdd(api.Rule{ 2012 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("id=app1")), 2013 Ingress: []api.IngressRule{ 2014 { 2015 IngressCommonRule: api.IngressCommonRule{ 2016 FromEndpoints: selectorFromApp2, 2017 }, 2018 ToPorts: []api.PortRule{{ 2019 Ports: []api.PortProtocol{ 2020 {Port: "80", Protocol: api.ProtoTCP}, 2021 }, 2022 Rules: &api.L7Rules{ 2023 HTTP: []api.PortRuleHTTP{ 2024 {Method: "GET", Path: "/"}, 2025 }, 2026 }, 2027 }}, 2028 }, 2029 }, 2030 }) 2031 require.Nil(t, err) 2032 2033 repo.Mutex.RLock() 2034 defer repo.Mutex.RUnlock() 2035 2036 // L4 from app2 is restricted 2037 logBuffer := new(bytes.Buffer) 2038 l4IngressPolicy, err := repo.ResolveL4IngressPolicy(fromApp2.WithLogger(logBuffer)) 2039 require.Nil(t, err) 2040 2041 cachedSelectorApp2 := td.sc.FindCachedIdentitySelector(selFromApp2) 2042 require.NotNil(t, cachedSelectorApp2) 2043 2044 expected := NewL4Policy(repo.GetRevision()) 2045 expected.Ingress.PortRules.Upsert("80", 0, "TCP", &L4Filter{ 2046 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 2047 L7Parser: ParserTypeHTTP, 2048 PerSelectorPolicies: L7DataMap{ 2049 cachedSelectorApp2: &PerSelectorPolicy{ 2050 L7Rules: api.L7Rules{ 2051 HTTP: []api.PortRuleHTTP{{Method: "GET", Path: "/"}, {}}, 2052 }, 2053 isRedirect: true, 2054 }, 2055 }, 2056 Ingress: true, 2057 RuleOrigin: map[CachedSelector]labels.LabelArrayList{cachedSelectorApp2: {nil}}, 2058 }) 2059 2060 if !assert.EqualValues(t, expected.Ingress.PortRules, l4IngressPolicy) { 2061 t.Logf("%s", logBuffer.String()) 2062 t.Errorf("Resolved policy did not match expected") 2063 } 2064 l4IngressPolicy.Detach(td.sc) 2065 expected.Detach(td.sc) 2066 2067 // L4 from app3 has no rules 2068 expected = NewL4Policy(repo.GetRevision()) 2069 l4IngressPolicy, err = repo.ResolveL4IngressPolicy(fromApp3) 2070 require.Nil(t, err) 2071 require.Equal(t, 0, l4IngressPolicy.Len()) 2072 require.Equal(t, expected.Ingress.PortRules, l4IngressPolicy) 2073 l4IngressPolicy.Detach(td.sc) 2074 expected.Detach(td.sc) 2075 } 2076 2077 func buildSearchCtx(from, to string, port uint16) *SearchContext { 2078 ports := []*models.Port{{Port: port, Protocol: string(api.ProtoAny)}} 2079 return &SearchContext{ 2080 From: labels.ParseSelectLabelArray(from), 2081 To: labels.ParseSelectLabelArray(to), 2082 DPorts: ports, 2083 Trace: TRACE_ENABLED, 2084 } 2085 } 2086 2087 func buildRule(from, to, port string) api.Rule { 2088 reservedES := api.NewESFromLabels(labels.ParseSelectLabel("reserved:host")) 2089 fromES := api.NewESFromLabels(labels.ParseSelectLabel(from)) 2090 toES := api.NewESFromLabels(labels.ParseSelectLabel(to)) 2091 2092 ports := []api.PortRule{} 2093 if port != "" { 2094 ports = []api.PortRule{ 2095 {Ports: []api.PortProtocol{{Port: port}}}, 2096 } 2097 } 2098 return api.Rule{ 2099 EndpointSelector: toES, 2100 Ingress: []api.IngressRule{ 2101 { 2102 IngressCommonRule: api.IngressCommonRule{ 2103 FromEndpoints: []api.EndpointSelector{ 2104 reservedES, 2105 fromES, 2106 }, 2107 }, 2108 ToPorts: ports, 2109 }, 2110 }, 2111 } 2112 } 2113 2114 func (repo *Repository) checkTrace(t *testing.T, ctx *SearchContext, trace string, 2115 expectedVerdict api.Decision) { 2116 2117 buffer := new(bytes.Buffer) 2118 ctx.Logging = stdlog.New(buffer, "", 0) 2119 2120 repo.Mutex.RLock() 2121 verdict := repo.AllowsIngressRLocked(ctx) 2122 repo.Mutex.RUnlock() 2123 2124 expectedOut := "Tracing " + ctx.String() + "\n" + trace 2125 require.EqualValues(t, expectedOut, buffer.String()) 2126 require.Equal(t, expectedVerdict, verdict) 2127 } 2128 2129 func TestPolicyTrace(t *testing.T) { 2130 td := newTestData() 2131 repo := td.repo 2132 2133 // Add rules to allow foo=>bar 2134 l3rule := buildRule("foo", "bar", "") 2135 rules := api.Rules{&l3rule} 2136 _, _ = repo.MustAddList(rules) 2137 2138 // foo=>bar is OK 2139 expectedOut := ` 2140 Resolving ingress policy for [any:bar] 2141 * Rule {"matchLabels":{"any:bar":""}}: selected 2142 Allows from labels {"matchLabels":{"reserved:host":""}} 2143 Allows from labels {"matchLabels":{"any:foo":""}} 2144 Found all required labels 2145 1/1 rules selected 2146 Found allow rule 2147 Found no deny rule 2148 Ingress verdict: allowed 2149 ` 2150 ctx := buildSearchCtx("foo", "bar", 0) 2151 repo.checkTrace(t, ctx, expectedOut, api.Allowed) 2152 2153 // foo=>bar:80 is OK 2154 ctx = buildSearchCtx("foo", "bar", 80) 2155 repo.checkTrace(t, ctx, expectedOut, api.Allowed) 2156 2157 // bar=>foo is Denied 2158 ctx = buildSearchCtx("bar", "foo", 0) 2159 expectedOut = ` 2160 Resolving ingress policy for [any:foo] 2161 0/1 rules selected 2162 Found no allow rule 2163 Found no deny rule 2164 Ingress verdict: denied 2165 ` 2166 repo.checkTrace(t, ctx, expectedOut, api.Denied) 2167 2168 // bar=>foo:80 is also Denied by the same logic 2169 ctx = buildSearchCtx("bar", "foo", 80) 2170 repo.checkTrace(t, ctx, expectedOut, api.Denied) 2171 2172 // Now, add extra rules to allow specifically baz=>bar on port 80 2173 l4rule := buildRule("baz", "bar", "80") 2174 _, _, err := repo.mustAdd(l4rule) 2175 require.Nil(t, err) 2176 2177 // baz=>bar:80 is OK 2178 ctx = buildSearchCtx("baz", "bar", 80) 2179 expectedOut = ` 2180 Resolving ingress policy for [any:bar] 2181 * Rule {"matchLabels":{"any:bar":""}}: selected 2182 Allows from labels {"matchLabels":{"reserved:host":""}} 2183 Allows from labels {"matchLabels":{"any:foo":""}} 2184 No label match for [any:baz] 2185 * Rule {"matchLabels":{"any:bar":""}}: selected 2186 Allows from labels {"matchLabels":{"reserved:host":""}} 2187 Allows from labels {"matchLabels":{"any:baz":""}} 2188 Found all required labels 2189 Allows port [{80 0 ANY}] 2190 2/2 rules selected 2191 Found allow rule 2192 Found no deny rule 2193 Ingress verdict: allowed 2194 ` 2195 repo.checkTrace(t, ctx, expectedOut, api.Allowed) 2196 2197 // bar=>bar:80 is Denied 2198 ctx = buildSearchCtx("bar", "bar", 80) 2199 expectedOut = ` 2200 Resolving ingress policy for [any:bar] 2201 * Rule {"matchLabels":{"any:bar":""}}: selected 2202 Allows from labels {"matchLabels":{"reserved:host":""}} 2203 Allows from labels {"matchLabels":{"any:foo":""}} 2204 No label match for [any:bar] 2205 * Rule {"matchLabels":{"any:bar":""}}: selected 2206 Allows from labels {"matchLabels":{"reserved:host":""}} 2207 Allows from labels {"matchLabels":{"any:baz":""}} 2208 No label match for [any:bar] 2209 2/2 rules selected 2210 Found no allow rule 2211 Found no deny rule 2212 Ingress verdict: denied 2213 ` 2214 repo.checkTrace(t, ctx, expectedOut, api.Denied) 2215 2216 // Test that FromRequires "baz" drops "foo" traffic 2217 l3rule = api.Rule{ 2218 EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")), 2219 Ingress: []api.IngressRule{{ 2220 IngressCommonRule: api.IngressCommonRule{ 2221 FromRequires: []api.EndpointSelector{ 2222 api.NewESFromLabels(labels.ParseSelectLabel("baz")), 2223 }, 2224 }, 2225 }}, 2226 } 2227 _, _, err = repo.mustAdd(l3rule) 2228 require.Nil(t, err) 2229 2230 // foo=>bar is now denied due to the FromRequires 2231 ctx = buildSearchCtx("foo", "bar", 0) 2232 expectedOut = ` 2233 Resolving ingress policy for [any:bar] 2234 * Rule {"matchLabels":{"any:bar":""}}: selected 2235 Enforcing requirements [{Key:any.baz Operator:In Values:[]}] 2236 Allows from labels {"matchLabels":{"reserved:host":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]} 2237 Allows from labels {"matchLabels":{"any:foo":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]} 2238 No label match for [any:foo] 2239 * Rule {"matchLabels":{"any:bar":""}}: selected 2240 Enforcing requirements [{Key:any.baz Operator:In Values:[]}] 2241 Allows from labels {"matchLabels":{"reserved:host":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]} 2242 Allows from labels {"matchLabels":{"any:baz":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]} 2243 No label match for [any:foo] 2244 * Rule {"matchLabels":{"any:bar":""}}: selected 2245 3/3 rules selected 2246 Found no allow rule 2247 Found no deny rule 2248 Ingress verdict: denied 2249 ` 2250 repo.checkTrace(t, ctx, expectedOut, api.Denied) 2251 2252 // baz=>bar is only denied because of the L4 policy 2253 ctx = buildSearchCtx("baz", "bar", 0) 2254 expectedOut = ` 2255 Resolving ingress policy for [any:bar] 2256 * Rule {"matchLabels":{"any:bar":""}}: selected 2257 Enforcing requirements [{Key:any.baz Operator:In Values:[]}] 2258 Allows from labels {"matchLabels":{"reserved:host":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]} 2259 Allows from labels {"matchLabels":{"any:foo":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]} 2260 No label match for [any:baz] 2261 * Rule {"matchLabels":{"any:bar":""}}: selected 2262 Enforcing requirements [{Key:any.baz Operator:In Values:[]}] 2263 Allows from labels {"matchLabels":{"reserved:host":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]} 2264 Allows from labels {"matchLabels":{"any:baz":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]} 2265 Found all required labels 2266 Allows port [{80 0 ANY}] 2267 No port match found 2268 * Rule {"matchLabels":{"any:bar":""}}: selected 2269 3/3 rules selected 2270 Found no allow rule 2271 Found no deny rule 2272 Ingress verdict: denied 2273 ` 2274 repo.checkTrace(t, ctx, expectedOut, api.Denied) 2275 2276 // Should still be allowed with the new FromRequires constraint 2277 ctx = buildSearchCtx("baz", "bar", 80) 2278 repo.Mutex.RLock() 2279 verdict := repo.AllowsIngressRLocked(ctx) 2280 repo.Mutex.RUnlock() 2281 require.Equal(t, api.Allowed, verdict) 2282 } 2283 2284 func TestIterate(t *testing.T) { 2285 td := newTestData() 2286 repo := td.repo 2287 2288 numWithEgress := 0 2289 countEgressRules := func(r *api.Rule) { 2290 if len(r.Egress) > 0 { 2291 numWithEgress++ 2292 } 2293 } 2294 repo.Iterate(countEgressRules) 2295 2296 require.Equal(t, 0, numWithEgress) 2297 2298 numRules := 10 2299 lbls := make([]labels.Label, 10) 2300 for i := 0; i < numRules; i++ { 2301 it := fmt.Sprintf("baz%d", i) 2302 epSelector := api.NewESFromLabels( 2303 labels.NewLabel( 2304 "foo", 2305 it, 2306 labels.LabelSourceK8s, 2307 ), 2308 ) 2309 lbls[i] = labels.NewLabel("tag3", it, labels.LabelSourceK8s) 2310 _, _, err := repo.mustAdd(api.Rule{ 2311 EndpointSelector: epSelector, 2312 Labels: labels.LabelArray{lbls[i]}, 2313 Egress: []api.EgressRule{ 2314 { 2315 EgressCommonRule: api.EgressCommonRule{ 2316 ToEndpoints: []api.EndpointSelector{ 2317 epSelector, 2318 }, 2319 }, 2320 }, 2321 }, 2322 }) 2323 require.Nil(t, err) 2324 } 2325 2326 numWithEgress = 0 2327 repo.Iterate(countEgressRules) 2328 2329 require.Equal(t, numRules, numWithEgress) 2330 2331 numModified := 0 2332 modifyRules := func(r *api.Rule) { 2333 if r.Labels.Contains(labels.LabelArray{lbls[1]}) || r.Labels.Contains(labels.LabelArray{lbls[3]}) { 2334 r.Egress = nil 2335 numModified++ 2336 } 2337 } 2338 2339 repo.Iterate(modifyRules) 2340 2341 require.Equal(t, 2, numModified) 2342 2343 numWithEgress = 0 2344 repo.Iterate(countEgressRules) 2345 2346 require.Equal(t, numRules-numModified, numWithEgress) 2347 2348 repo.Mutex.Lock() 2349 _, _, numDeleted := repo.DeleteByLabelsLocked(labels.LabelArray{lbls[0]}) 2350 repo.Mutex.Unlock() 2351 require.Equal(t, 1, numDeleted) 2352 2353 numWithEgress = 0 2354 repo.Iterate(countEgressRules) 2355 2356 require.Equal(t, numRules-numModified-numDeleted, numWithEgress) 2357 } 2358 2359 // TestDefaultAllow covers the defaulting logic in determining an identity's default rule 2360 // in the presence or absence of rules that do not enable default-deny mode. 2361 func TestDefaultAllow(t *testing.T) { 2362 2363 // Cache policy enforcement value from when test was ran to avoid pollution 2364 // across tests. 2365 oldPolicyEnable := GetPolicyEnabled() 2366 defer SetPolicyEnabled(oldPolicyEnable) 2367 2368 SetPolicyEnabled(option.DefaultEnforcement) 2369 2370 fooSelectLabel := labels.ParseSelectLabel("foo") 2371 2372 genRule := func(ingress, defaultDeny bool) api.Rule { 2373 name := fmt.Sprintf("%v_%v", ingress, defaultDeny) 2374 r := api.Rule{ 2375 EndpointSelector: api.NewESFromLabels(fooSelectLabel), 2376 Labels: labels.LabelArray{labels.NewLabel(k8sConst.PolicyLabelName, name, labels.LabelSourceAny)}, 2377 } 2378 2379 if ingress { 2380 r.Ingress = []api.IngressRule{{ 2381 IngressCommonRule: api.IngressCommonRule{ 2382 FromEndpoints: []api.EndpointSelector{api.NewESFromLabels(fooSelectLabel)}}}} 2383 } else { 2384 r.Egress = []api.EgressRule{{ 2385 EgressCommonRule: api.EgressCommonRule{ 2386 ToEndpoints: []api.EndpointSelector{api.NewESFromLabels(fooSelectLabel)}}}} 2387 } 2388 if ingress { 2389 r.EnableDefaultDeny.Ingress = &defaultDeny 2390 } else { 2391 r.EnableDefaultDeny.Egress = &defaultDeny 2392 } 2393 require.Nil(t, r.Sanitize()) 2394 return r 2395 } 2396 2397 iDeny := genRule(true, true) // ingress default deny 2398 iAllow := genRule(true, false) // ingress default allow 2399 2400 eDeny := genRule(false, true) // egress default deny 2401 eAllow := genRule(false, false) // egress default allow 2402 2403 type testCase struct { 2404 rules []api.Rule 2405 ingress, egress bool 2406 ruleC int // count of rules; indicates wildcard 2407 } 2408 2409 ingressCases := []testCase{ 2410 { 2411 rules: nil, // default case, everything disabled 2412 }, 2413 { 2414 rules: []api.Rule{iDeny}, 2415 ingress: true, 2416 ruleC: 1, 2417 }, 2418 { 2419 rules: []api.Rule{iAllow}, // Just a default-allow rule 2420 ingress: true, 2421 ruleC: 2, // wildcard must be added 2422 }, 2423 { 2424 rules: []api.Rule{iDeny, iAllow}, // default-deny takes precedence, no wildcard 2425 ingress: true, 2426 ruleC: 2, 2427 }, 2428 } 2429 2430 egressCases := []testCase{ 2431 { 2432 rules: nil, // default case, everything disabled 2433 }, 2434 { 2435 rules: []api.Rule{eDeny}, 2436 egress: true, 2437 ruleC: 1, 2438 }, 2439 { 2440 rules: []api.Rule{eAllow}, // Just a default-allow rule 2441 egress: true, 2442 ruleC: 2, // wildcard must be added 2443 }, 2444 { 2445 rules: []api.Rule{eDeny, eAllow}, // default-deny takes precedence, no wildcard 2446 egress: true, 2447 ruleC: 2, 2448 }, 2449 } 2450 2451 // three test runs: ingress, egress, and ingress + egress cartesian 2452 for i, tc := range ingressCases { 2453 td := newTestData() 2454 td.addIdentity(fooIdentity) 2455 repo := td.repo 2456 2457 for _, rule := range tc.rules { 2458 _, _, err := repo.mustAdd(rule) 2459 require.NoError(t, err, "unable to add rule to policy repository") 2460 } 2461 2462 ing, egr, matchingRules := repo.computePolicyEnforcementAndRules(fooIdentity) 2463 require.Equal(t, tc.ingress, ing, "case %d: ingress should match", i) 2464 require.Equal(t, tc.egress, egr, "case %d: egress should match", i) 2465 require.Equal(t, tc.ruleC, len(matchingRules), "case %d: rule count should match", i) 2466 } 2467 2468 for i, tc := range egressCases { 2469 td := newTestData() 2470 td.addIdentity(fooIdentity) 2471 repo := td.repo 2472 2473 for _, rule := range tc.rules { 2474 _, _, err := repo.mustAdd(rule) 2475 require.NoError(t, err, "unable to add rule to policy repository") 2476 } 2477 2478 ing, egr, matchingRules := repo.computePolicyEnforcementAndRules(fooIdentity) 2479 require.Equal(t, tc.ingress, ing, "case %d: ingress should match", i) 2480 require.Equal(t, tc.egress, egr, "case %d: egress should match", i) 2481 require.Equal(t, tc.ruleC, len(matchingRules), "case %d: rule count should match", i) 2482 } 2483 2484 // test all combinations of ingress + egress cases 2485 for e, etc := range egressCases { 2486 for i, itc := range ingressCases { 2487 td := newTestData() 2488 td.addIdentity(fooIdentity) 2489 repo := td.repo 2490 2491 for _, rule := range etc.rules { 2492 _, _, err := repo.mustAdd(rule) 2493 require.NoError(t, err, "unable to add rule to policy repository") 2494 } 2495 2496 for _, rule := range itc.rules { 2497 _, _, err := repo.mustAdd(rule) 2498 require.NoError(t, err, "unable to add rule to policy repository") 2499 } 2500 2501 ing, egr, matchingRules := repo.computePolicyEnforcementAndRules(fooIdentity) 2502 require.Equal(t, itc.ingress, ing, "case ingress %d + egress %d: ingress should match", i, e) 2503 require.Equal(t, etc.egress, egr, "case ingress %d + egress %d: egress should match", i, e) 2504 require.Equal(t, itc.ruleC+etc.ruleC, len(matchingRules), "case ingress %d + egress %d: rule count should match", i, e) 2505 } 2506 } 2507 } 2508 2509 func TestReplaceByResource(t *testing.T) { 2510 // don't use the full testdata() here, since we want to watch 2511 // selectorcache changes carefully 2512 repo := NewPolicyRepository(nil, nil, nil) 2513 sc := testNewSelectorCache(nil) 2514 repo.selectorCache = sc 2515 assert.Len(t, sc.selectors, 0) 2516 2517 numRules := 10 2518 rules := make(api.Rules, 0, numRules) 2519 // share the dest selector 2520 destSelector := api.NewESFromLabels(labels.NewLabel("peer", "pod", "k8s")) 2521 for i := 0; i < numRules; i++ { 2522 it := fmt.Sprintf("num-%d", i) 2523 epSelector := api.NewESFromLabels( 2524 labels.NewLabel( 2525 "subject-pod", 2526 it, 2527 labels.LabelSourceK8s, 2528 ), 2529 ) 2530 lbl := labels.NewLabel("policy-label", it, labels.LabelSourceK8s) 2531 rule := &api.Rule{ 2532 EndpointSelector: epSelector, 2533 Labels: labels.LabelArray{lbl}, 2534 Egress: []api.EgressRule{ 2535 { 2536 EgressCommonRule: api.EgressCommonRule{ 2537 ToEndpoints: []api.EndpointSelector{ 2538 destSelector, 2539 }, 2540 }, 2541 }, 2542 }, 2543 } 2544 require.Nil(t, rule.Sanitize()) 2545 rules = append(rules, rule) 2546 } 2547 2548 rulesMatch := func(s ruleSlice, rs api.Rules) { 2549 t.Helper() 2550 ss := make(api.Rules, 0, len(s)) 2551 for _, rule := range s { 2552 ss = append(ss, &rule.Rule) 2553 } 2554 assert.ElementsMatch(t, ss, rs) 2555 } 2556 toSlice := func(m map[ruleKey]*rule) ruleSlice { 2557 out := ruleSlice{} 2558 for _, v := range m { 2559 out = append(out, v) 2560 } 2561 return out 2562 } 2563 2564 rID1 := ipcachetypes.ResourceID("res1") 2565 rID2 := ipcachetypes.ResourceID("res2") 2566 2567 new, old, rev := repo.ReplaceByResourceLocked(rules[0:1], rID1) 2568 assert.Len(t, new, 1) 2569 assert.Len(t, old, 0) 2570 assert.EqualValues(t, rev, 2) 2571 2572 // check basic bookkeeping 2573 assert.Len(t, repo.rules, 1) 2574 assert.Len(t, repo.rulesByResource, 1) 2575 assert.Len(t, repo.rulesByResource[rID1], 1) 2576 rulesMatch(toSlice(repo.rulesByResource[rID1]), rules[0:1]) 2577 2578 // Check that the selectorcache is sane 2579 // It should have one selector: the subject pod for rule 0 2580 assert.Len(t, sc.selectors, 1) 2581 2582 // add second resource 2583 new, old, rev = repo.ReplaceByResourceLocked(rules[1:3], rID2) 2584 2585 assert.Len(t, new, 2) 2586 assert.Len(t, old, 0) 2587 assert.EqualValues(t, rev, 3) 2588 2589 // check basic bookkeeping 2590 assert.Len(t, repo.rules, 3) 2591 assert.Len(t, repo.rulesByResource, 2) 2592 assert.Len(t, repo.rulesByResource[rID1], 1) 2593 assert.Len(t, repo.rulesByResource[rID2], 2) 2594 assert.Len(t, sc.selectors, 3) 2595 2596 // replace rid1 with new rules 2597 new, old, _ = repo.ReplaceByResourceLocked(rules[3:5], rID1) 2598 assert.Len(t, new, 2) 2599 assert.Len(t, old, 1) 2600 repo.Release(old) 2601 2602 // check basic bookkeeping 2603 assert.Len(t, repo.rules, 4) 2604 assert.Len(t, repo.rulesByResource, 2) 2605 assert.Len(t, repo.rulesByResource[rID1], 2) 2606 assert.Len(t, repo.rulesByResource[rID2], 2) 2607 assert.Len(t, sc.selectors, 4) 2608 2609 rulesMatch(old, rules[0:1]) 2610 rulesMatch(new, rules[3:5]) 2611 rulesMatch(toSlice(repo.rulesByResource[rID1]), rules[3:5]) 2612 assert.Equal(t, repo.rules[ruleKey{ 2613 resource: rID1, 2614 idx: 0, 2615 }].Rule, *rules[3]) 2616 2617 // delete rid1 2618 old, _ = repo.DeleteByResourceLocked(rID1) 2619 assert.Len(t, old, 2) 2620 repo.Release(old) 2621 2622 assert.Len(t, repo.rules, 2) 2623 assert.Len(t, repo.rulesByResource, 1) 2624 assert.Len(t, repo.rulesByResource[rID2], 2) 2625 assert.Len(t, sc.selectors, 2) 2626 2627 // delete rid1 again (noop) 2628 old, _ = repo.DeleteByResourceLocked(rID1) 2629 assert.Len(t, old, 0) 2630 2631 assert.Len(t, repo.rules, 2) 2632 assert.Len(t, repo.rulesByResource, 1) 2633 assert.Len(t, repo.rulesByResource[rID2], 2) 2634 assert.Len(t, sc.selectors, 2) 2635 2636 // delete rid2 2637 old, _ = repo.DeleteByResourceLocked(rID2) 2638 assert.Len(t, old, 2) 2639 repo.Release(old) 2640 2641 assert.Len(t, repo.rules, 0) 2642 assert.Len(t, repo.rulesByResource, 0) 2643 assert.Len(t, sc.selectors, 0) 2644 }