github.phpd.cn/cilium/cilium@v1.6.12/pkg/k8s/network_policy_test.go (about) 1 // Copyright 2016-2019 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build !privileged_tests 16 17 package k8s 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "testing" 23 24 "github.com/cilium/cilium/api/v1/models" 25 "github.com/cilium/cilium/pkg/annotation" 26 "github.com/cilium/cilium/pkg/checker" 27 "github.com/cilium/cilium/pkg/identity" 28 k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" 29 "github.com/cilium/cilium/pkg/labels" 30 "github.com/cilium/cilium/pkg/policy" 31 "github.com/cilium/cilium/pkg/policy/api" 32 "github.com/cilium/cilium/pkg/testutils" 33 34 . "gopkg.in/check.v1" 35 v1 "k8s.io/api/core/v1" 36 networkingv1 "k8s.io/api/networking/v1" 37 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 38 "k8s.io/apimachinery/pkg/types" 39 "k8s.io/apimachinery/pkg/util/intstr" 40 ) 41 42 // Hook up gocheck into the "go test" runner. 43 func Test(t *testing.T) { 44 TestingT(t) 45 } 46 47 type K8sSuite struct{} 48 49 var _ = Suite(&K8sSuite{}) 50 51 var ( 52 labelsA = labels.LabelArray{ 53 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 54 labels.NewLabel("id", "a", labels.LabelSourceK8s), 55 } 56 57 labelSelectorA = metav1.LabelSelector{ 58 MatchLabels: map[string]string{ 59 "id": "a", 60 }, 61 } 62 63 labelsB = labels.LabelArray{ 64 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 65 labels.NewLabel("id1", "b", labels.LabelSourceK8s), 66 labels.NewLabel("id2", "c", labels.LabelSourceK8s), 67 } 68 69 labelsC = labels.LabelArray{ 70 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 71 labels.NewLabel("id", "c", labels.LabelSourceK8s), 72 } 73 74 labelSelectorC = metav1.LabelSelector{ 75 MatchLabels: map[string]string{ 76 "id": "c", 77 }, 78 } 79 80 ctxAToB = policy.SearchContext{ 81 From: labelsA, 82 To: labelsB, 83 Trace: policy.TRACE_VERBOSE, 84 } 85 86 ctxAToC = policy.SearchContext{ 87 From: labelsA, 88 To: labelsC, 89 Trace: policy.TRACE_VERBOSE, 90 } 91 92 port80 = networkingv1.NetworkPolicyPort{ 93 Port: &intstr.IntOrString{ 94 Type: intstr.Int, 95 IntVal: 80, 96 }, 97 } 98 99 dummySelectorCacheUser = &DummySelectorCacheUser{} 100 ) 101 102 type DummySelectorCacheUser struct{} 103 104 func testNewPolicyRepository() *policy.Repository { 105 repo := policy.NewPolicyRepository() 106 repo.GetSelectorCache().SetLocalIdentityNotifier(testutils.NewDummyIdentityNotifier()) 107 return repo 108 } 109 110 func (d *DummySelectorCacheUser) IdentitySelectionUpdated(selector policy.CachedSelector, selections, added, deleted []identity.NumericIdentity) { 111 } 112 113 func (s *K8sSuite) TestParseNetworkPolicyIngress(c *C) { 114 netPolicy := &networkingv1.NetworkPolicy{ 115 Spec: networkingv1.NetworkPolicySpec{ 116 PodSelector: metav1.LabelSelector{ 117 MatchLabels: map[string]string{ 118 "foo1": "bar1", 119 "foo2": "bar2", 120 }, 121 }, 122 Ingress: []networkingv1.NetworkPolicyIngressRule{ 123 { 124 From: []networkingv1.NetworkPolicyPeer{ 125 { 126 PodSelector: &metav1.LabelSelector{ 127 MatchLabels: map[string]string{ 128 "foo3": "bar3", 129 "foo4": "bar4", 130 }, 131 }, 132 }, 133 }, 134 Ports: []networkingv1.NetworkPolicyPort{ 135 { 136 Port: &intstr.IntOrString{ 137 Type: intstr.Int, 138 IntVal: 80, 139 }, 140 }, 141 }, 142 }, 143 }, 144 }, 145 } 146 147 _, err := ParseNetworkPolicy(netPolicy) 148 c.Assert(err, IsNil) 149 150 fromEndpoints := labels.LabelArray{ 151 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 152 labels.NewLabel("foo3", "bar3", labels.LabelSourceK8s), 153 labels.NewLabel("foo4", "bar4", labels.LabelSourceK8s), 154 } 155 156 ctx := policy.SearchContext{ 157 From: fromEndpoints, 158 To: labels.LabelArray{ 159 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 160 labels.NewLabel("foo1", "bar1", labels.LabelSourceK8s), 161 labels.NewLabel("foo2", "bar2", labels.LabelSourceK8s), 162 }, 163 Trace: policy.TRACE_VERBOSE, 164 } 165 166 rules, err := ParseNetworkPolicy(netPolicy) 167 c.Assert(err, IsNil) 168 c.Assert(len(rules), Equals, 1) 169 170 repo := testNewPolicyRepository() 171 172 repo.AddList(rules) 173 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied) 174 175 epSelector := api.NewESFromLabels(fromEndpoints...) 176 cachedEPSelector, _ := repo.GetSelectorCache().AddIdentitySelector(dummySelectorCacheUser, epSelector) 177 defer func() { repo.GetSelectorCache().RemoveSelector(cachedEPSelector, dummySelectorCacheUser) }() 178 179 ingressL4Policy, err := repo.ResolveL4IngressPolicy(&ctx) 180 c.Assert(ingressL4Policy, Not(IsNil)) 181 c.Assert(err, IsNil) 182 c.Assert(ingressL4Policy, checker.Equals, policy.L4PolicyMap{ 183 "80/TCP": { 184 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 185 CachedSelectors: policy.CachedSelectorSlice{cachedEPSelector}, 186 L7Parser: policy.ParserTypeNone, 187 L7RulesPerEp: policy.L7DataMap{}, 188 Ingress: true, 189 DerivedFromRules: []labels.LabelArray{ 190 labels.ParseLabelArray( 191 "k8s:"+k8sConst.PolicyLabelName, 192 "k8s:"+k8sConst.PolicyLabelUID, 193 "k8s:"+k8sConst.PolicyLabelNamespace+"=default", 194 "k8s:"+k8sConst.PolicyLabelDerivedFrom+"="+resourceTypeNetworkPolicy, 195 ), 196 }, 197 }, 198 }) 199 ingressL4Policy.Detach(repo.GetSelectorCache()) 200 201 ctx.To = labels.LabelArray{ 202 labels.NewLabel("foo2", "bar2", labels.LabelSourceK8s), 203 } 204 205 // ctx.To needs to have all labels from the policy in order to be accepted 206 c.Assert(repo.AllowsIngressRLocked(&ctx), Not(Equals), api.Allowed) 207 208 ctx = policy.SearchContext{ 209 From: labels.LabelArray{ 210 labels.NewLabel("foo3", "bar3", labels.LabelSourceK8s), 211 }, 212 To: labels.LabelArray{ 213 labels.NewLabel("foo1", "bar1", labels.LabelSourceK8s), 214 labels.NewLabel("foo2", "bar2", labels.LabelSourceK8s), 215 }, 216 Trace: policy.TRACE_VERBOSE, 217 } 218 // ctx.From also needs to have all labels from the policy in order to be accepted 219 c.Assert(repo.AllowsIngressRLocked(&ctx), Not(Equals), api.Allowed) 220 } 221 222 func (s *K8sSuite) TestParseNetworkPolicyMultipleSelectors(c *C) { 223 224 // Rule with multiple selectors in egress and ingress 225 ex1 := []byte(`{ 226 "kind":"NetworkPolicy", 227 "apiVersion":"extensions/networkingv1", 228 "metadata":{ 229 "name":"ingress-multiple-selectors" 230 }, 231 "spec":{ 232 "podSelector":{ 233 "matchLabels":{ 234 "role":"backend" 235 } 236 }, 237 "egress":[ 238 { 239 "ports":[ 240 { 241 "protocol":"TCP", 242 "port":5432 243 } 244 ], 245 "to":[ 246 { 247 "podSelector":{ 248 "matchLabels":{ 249 "app":"db1" 250 } 251 } 252 }, 253 { 254 "podSelector":{ 255 "matchLabels":{ 256 "app":"db2" 257 } 258 } 259 } 260 ] 261 } 262 ], 263 "ingress":[ 264 { 265 "from":[ 266 { 267 "podSelector":{ 268 "matchLabels":{ 269 "role":"frontend" 270 } 271 }, 272 "namespaceSelector":{ 273 "matchLabels":{ 274 "project":"myproject" 275 } 276 } 277 }, 278 { 279 "podSelector":{ 280 "matchLabels":{ 281 "app":"inventory" 282 } 283 } 284 } 285 ] 286 } 287 ] 288 } 289 }`) 290 291 np := networkingv1.NetworkPolicy{} 292 err := json.Unmarshal(ex1, &np) 293 c.Assert(err, IsNil) 294 295 rules, err := ParseNetworkPolicy(&np) 296 c.Assert(err, IsNil) 297 c.Assert(len(rules), Equals, 1) 298 299 repo := testNewPolicyRepository() 300 repo.AddList(rules) 301 302 endpointLabels := labels.LabelArray{ 303 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 304 labels.NewLabel("role", "backend", labels.LabelSourceK8s), 305 } 306 307 // Ingress context 308 ctx := policy.SearchContext{ 309 From: labels.LabelArray{ 310 labels.NewLabel("role", "frontend", labels.LabelSourceK8s), 311 }, 312 To: endpointLabels, 313 Trace: policy.TRACE_VERBOSE, 314 } 315 316 // should be DENIED because ctx.From is missing the namespace selector 317 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied) 318 319 ctx.From = labels.LabelArray{ 320 labels.NewLabel("role", "frontend", labels.LabelSourceK8s), 321 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "project"), "myproject", labels.LabelSourceK8s), 322 } 323 324 // should be ALLOWED with the namespace label properly set 325 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 326 327 ctx.From = labels.LabelArray{ 328 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 329 labels.NewLabel("app", "inventory", labels.LabelSourceK8s), 330 } 331 332 // should be ALLOWED since all rules in From must match 333 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 334 335 // Egress context 336 ctx = policy.SearchContext{ 337 From: endpointLabels, 338 To: labels.LabelArray{ 339 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 340 labels.NewLabel("app", "db1", labels.LabelSourceK8s), 341 }, 342 Trace: policy.TRACE_VERBOSE, 343 } 344 345 // should be DENIED because DPorts are missing in context 346 c.Assert(repo.AllowsEgressRLocked(&ctx), Equals, api.Denied) 347 348 ctx.DPorts = []*models.Port{{Port: 5432, Protocol: models.PortProtocolTCP}} 349 350 // should be ALLOWED with DPorts set correctly 351 c.Assert(repo.AllowsEgressRLocked(&ctx), Equals, api.Allowed) 352 353 ctx.To = labels.LabelArray{ 354 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 355 labels.NewLabel("app", "db2", labels.LabelSourceK8s), 356 } 357 358 // should be ALLOWED for db2 as well 359 c.Assert(repo.AllowsEgressRLocked(&ctx), Equals, api.Allowed) 360 } 361 362 func (s *K8sSuite) TestParseNetworkPolicyNoSelectors(c *C) { 363 364 // Ingress with neither pod nor namespace selector set. 365 ex1 := []byte(`{ 366 "kind": "NetworkPolicy", 367 "apiVersion": "extensions/networkingv1", 368 "metadata": { 369 "name": "ingress-cidr-test", 370 "namespace": "myns", 371 "uid": "11bba160-ddca-11e8-b697-0800273b04ff" 372 }, 373 "spec": { 374 "podSelector": { 375 "matchLabels": { 376 "role": "backend" 377 } 378 }, 379 "ingress": [ 380 { 381 "from": [ 382 { 383 "ipBlock": { 384 "cidr": "10.0.0.0/8", 385 "except": [ 386 "10.96.0.0/12" 387 ] 388 } 389 } 390 ] 391 } 392 ] 393 } 394 }`) 395 396 fromEndpoints := labels.LabelArray{ 397 labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s), 398 labels.NewLabel("role", "backend", labels.LabelSourceK8s), 399 } 400 401 epSelector := api.NewESFromLabels(fromEndpoints...) 402 np := networkingv1.NetworkPolicy{} 403 err := json.Unmarshal(ex1, &np) 404 c.Assert(err, IsNil) 405 406 expectedRule := api.NewRule(). 407 WithEndpointSelector(epSelector). 408 WithIngressRules([]api.IngressRule{ 409 { 410 FromCIDRSet: []api.CIDRRule{ 411 { 412 Cidr: api.CIDR("10.0.0.0/8"), 413 ExceptCIDRs: []api.CIDR{ 414 "10.96.0.0/12", 415 }, 416 }, 417 }, 418 }, 419 }). 420 WithEgressRules([]api.EgressRule{}). 421 WithLabels(labels.ParseLabelArray( 422 "k8s:"+k8sConst.PolicyLabelName+"=ingress-cidr-test", 423 "k8s:"+k8sConst.PolicyLabelUID+"=11bba160-ddca-11e8-b697-0800273b04ff", 424 "k8s:"+k8sConst.PolicyLabelNamespace+"=myns", 425 "k8s:"+k8sConst.PolicyLabelDerivedFrom+"="+resourceTypeNetworkPolicy, 426 )) 427 428 expectedRule.Sanitize() 429 430 expectedRules := api.Rules{ 431 expectedRule, 432 } 433 434 rules, err := ParseNetworkPolicy(&np) 435 c.Assert(err, IsNil) 436 c.Assert(rules, NotNil) 437 c.Assert(rules, checker.DeepEquals, expectedRules) 438 } 439 440 func (s *K8sSuite) TestParseNetworkPolicyEgress(c *C) { 441 442 netPolicy := &networkingv1.NetworkPolicy{ 443 Spec: networkingv1.NetworkPolicySpec{ 444 PodSelector: metav1.LabelSelector{ 445 MatchLabels: map[string]string{ 446 "foo1": "bar1", 447 "foo2": "bar2", 448 }, 449 }, 450 Egress: []networkingv1.NetworkPolicyEgressRule{ 451 { 452 To: []networkingv1.NetworkPolicyPeer{ 453 { 454 PodSelector: &metav1.LabelSelector{ 455 MatchLabels: map[string]string{ 456 "foo3": "bar3", 457 "foo4": "bar4", 458 }, 459 }, 460 }, 461 }, 462 Ports: []networkingv1.NetworkPolicyPort{ 463 { 464 Port: &intstr.IntOrString{ 465 Type: intstr.Int, 466 IntVal: 80, 467 }, 468 }, 469 }, 470 }, 471 }, 472 }, 473 } 474 475 rules, err := ParseNetworkPolicy(netPolicy) 476 c.Assert(err, IsNil) 477 c.Assert(len(rules), Equals, 1) 478 479 fromEndpoints := labels.LabelArray{ 480 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 481 labels.NewLabel("foo1", "bar1", labels.LabelSourceK8s), 482 labels.NewLabel("foo2", "bar2", labels.LabelSourceK8s), 483 } 484 485 toEndpoints := labels.LabelArray{ 486 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 487 labels.NewLabel("foo3", "bar3", labels.LabelSourceK8s), 488 labels.NewLabel("foo4", "bar4", labels.LabelSourceK8s), 489 } 490 491 ctx := policy.SearchContext{ 492 From: fromEndpoints, 493 To: toEndpoints, 494 Trace: policy.TRACE_VERBOSE, 495 } 496 497 repo := testNewPolicyRepository() 498 repo.AddList(rules) 499 // Because search context did not contain port-specific policy, deny is 500 // expected. 501 c.Assert(repo.AllowsEgressRLocked(&ctx), Equals, api.Denied) 502 503 epSelector := api.NewESFromLabels(toEndpoints...) 504 cachedEPSelector, _ := repo.GetSelectorCache().AddIdentitySelector(dummySelectorCacheUser, epSelector) 505 defer func() { repo.GetSelectorCache().RemoveSelector(cachedEPSelector, dummySelectorCacheUser) }() 506 507 egressL4Policy, err := repo.ResolveL4EgressPolicy(&ctx) 508 c.Assert(egressL4Policy, Not(IsNil)) 509 c.Assert(err, IsNil) 510 c.Assert(egressL4Policy, checker.DeepEquals, policy.L4PolicyMap{ 511 "80/TCP": { 512 Port: 80, Protocol: api.ProtoTCP, U8Proto: 6, 513 CachedSelectors: policy.CachedSelectorSlice{cachedEPSelector}, 514 L7Parser: policy.ParserTypeNone, 515 L7RulesPerEp: policy.L7DataMap{}, 516 Ingress: false, 517 DerivedFromRules: []labels.LabelArray{ 518 labels.ParseLabelArray( 519 "k8s:"+k8sConst.PolicyLabelName, 520 "k8s:"+k8sConst.PolicyLabelUID, 521 "k8s:"+k8sConst.PolicyLabelNamespace+"=default", 522 "k8s:"+k8sConst.PolicyLabelDerivedFrom+"="+resourceTypeNetworkPolicy, 523 ), 524 }, 525 }, 526 }) 527 egressL4Policy.Detach(repo.GetSelectorCache()) 528 529 ctx.From = labels.LabelArray{ 530 labels.NewLabel("foo2", "bar2", labels.LabelSourceK8s), 531 } 532 533 // ctx.From needs to have all labels from the policy in order to be accepted 534 c.Assert(repo.AllowsEgressRLocked(&ctx), Not(Equals), api.Allowed) 535 536 ctx = policy.SearchContext{ 537 To: labels.LabelArray{ 538 labels.NewLabel("foo3", "bar3", labels.LabelSourceK8s), 539 }, 540 From: labels.LabelArray{ 541 labels.NewLabel("foo1", "bar1", labels.LabelSourceK8s), 542 labels.NewLabel("foo2", "bar2", labels.LabelSourceK8s), 543 }, 544 Trace: policy.TRACE_VERBOSE, 545 } 546 547 // ctx.To also needs to have all labels from the policy in order to be accepted. 548 c.Assert(repo.AllowsEgressRLocked(&ctx), Not(Equals), api.Allowed) 549 } 550 551 func parseAndAddRules(c *C, p *networkingv1.NetworkPolicy) *policy.Repository { 552 repo := testNewPolicyRepository() 553 rules, err := ParseNetworkPolicy(p) 554 c.Assert(err, IsNil) 555 rev := repo.GetRevision() 556 _, id := repo.AddList(rules) 557 c.Assert(id, Equals, rev+1) 558 559 return repo 560 } 561 562 func (s *K8sSuite) TestParseNetworkPolicyEgressAllowAll(c *C) { 563 repo := parseAndAddRules(c, &networkingv1.NetworkPolicy{ 564 Spec: networkingv1.NetworkPolicySpec{ 565 PodSelector: labelSelectorA, 566 Egress: []networkingv1.NetworkPolicyEgressRule{ 567 { 568 To: []networkingv1.NetworkPolicyPeer{}, 569 }, 570 }, 571 }, 572 }) 573 574 c.Assert(repo.AllowsEgressRLocked(&ctxAToB), Equals, api.Allowed) 575 c.Assert(repo.AllowsEgressRLocked(&ctxAToC), Equals, api.Allowed) 576 577 ctxAToC80 := ctxAToC 578 ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 579 c.Assert(repo.AllowsEgressRLocked(&ctxAToC80), Equals, api.Allowed) 580 581 ctxAToC90 := ctxAToC 582 ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 583 c.Assert(repo.AllowsEgressRLocked(&ctxAToC90), Equals, api.Allowed) 584 } 585 586 func (s *K8sSuite) TestParseNetworkPolicyEgressL4AllowAll(c *C) { 587 repo := parseAndAddRules(c, &networkingv1.NetworkPolicy{ 588 Spec: networkingv1.NetworkPolicySpec{ 589 PodSelector: labelSelectorA, 590 Egress: []networkingv1.NetworkPolicyEgressRule{ 591 { 592 Ports: []networkingv1.NetworkPolicyPort{port80}, 593 To: []networkingv1.NetworkPolicyPeer{}, 594 }, 595 }, 596 }, 597 }) 598 599 ctxAToC80 := ctxAToC 600 ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 601 c.Assert(repo.AllowsEgressRLocked(&ctxAToC80), Equals, api.Allowed) 602 603 ctxAToC90 := ctxAToC 604 ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 605 c.Assert(repo.AllowsEgressRLocked(&ctxAToC90), Equals, api.Denied) 606 } 607 608 func (s *K8sSuite) TestParseNetworkPolicyIngressAllowAll(c *C) { 609 repo := parseAndAddRules(c, &networkingv1.NetworkPolicy{ 610 Spec: networkingv1.NetworkPolicySpec{ 611 PodSelector: labelSelectorC, 612 Ingress: []networkingv1.NetworkPolicyIngressRule{ 613 { 614 From: []networkingv1.NetworkPolicyPeer{}, 615 }, 616 }, 617 }, 618 }) 619 620 c.Assert(repo.AllowsIngressRLocked(&ctxAToB), Equals, api.Denied) 621 c.Assert(repo.AllowsIngressRLocked(&ctxAToC), Equals, api.Allowed) 622 623 ctxAToC80 := ctxAToC 624 ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 625 c.Assert(repo.AllowsIngressRLocked(&ctxAToC80), Equals, api.Allowed) 626 627 ctxAToC90 := ctxAToC 628 ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 629 c.Assert(repo.AllowsIngressRLocked(&ctxAToC90), Equals, api.Allowed) 630 } 631 632 func (s *K8sSuite) TestParseNetworkPolicyIngressL4AllowAll(c *C) { 633 repo := parseAndAddRules(c, &networkingv1.NetworkPolicy{ 634 Spec: networkingv1.NetworkPolicySpec{ 635 PodSelector: labelSelectorC, 636 Ingress: []networkingv1.NetworkPolicyIngressRule{ 637 { 638 Ports: []networkingv1.NetworkPolicyPort{port80}, 639 From: []networkingv1.NetworkPolicyPeer{}, 640 }, 641 }, 642 }, 643 }) 644 645 c.Assert(repo.AllowsIngressRLocked(&ctxAToB), Equals, api.Denied) 646 647 ctxAToC80 := ctxAToC 648 ctxAToC80.DPorts = []*models.Port{{Port: 80, Protocol: models.PortProtocolTCP}} 649 c.Assert(repo.AllowsIngressRLocked(&ctxAToC80), Equals, api.Allowed) 650 651 ctxAToC90 := ctxAToC 652 ctxAToC90.DPorts = []*models.Port{{Port: 90, Protocol: models.PortProtocolTCP}} 653 c.Assert(repo.AllowsIngressRLocked(&ctxAToC90), Equals, api.Denied) 654 } 655 656 func (s *K8sSuite) TestParseNetworkPolicyUnknownProto(c *C) { 657 netPolicy := &networkingv1.NetworkPolicy{ 658 Spec: networkingv1.NetworkPolicySpec{ 659 Ingress: []networkingv1.NetworkPolicyIngressRule{ 660 { 661 Ports: []networkingv1.NetworkPolicyPort{ 662 { 663 Port: &intstr.IntOrString{ 664 Type: intstr.String, 665 StrVal: "unknown", 666 }, 667 }, 668 }, 669 }, 670 }, 671 }, 672 } 673 674 rules, err := ParseNetworkPolicy(netPolicy) 675 c.Assert(err, Not(IsNil)) 676 c.Assert(len(rules), Equals, 0) 677 } 678 679 func (s *K8sSuite) TestParseNetworkPolicyEmptyFrom(c *C) { 680 // From missing, all sources should be allowed 681 netPolicy1 := &networkingv1.NetworkPolicy{ 682 Spec: networkingv1.NetworkPolicySpec{ 683 PodSelector: metav1.LabelSelector{ 684 MatchLabels: map[string]string{ 685 "foo1": "bar1", 686 }, 687 }, 688 Ingress: []networkingv1.NetworkPolicyIngressRule{ 689 {}, 690 }, 691 }, 692 } 693 694 rules, err := ParseNetworkPolicy(netPolicy1) 695 c.Assert(err, IsNil) 696 c.Assert(len(rules), Equals, 1) 697 698 ctx := policy.SearchContext{ 699 From: labels.LabelArray{ 700 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 701 labels.NewLabel("foo0", "bar0", labels.LabelSourceK8s), 702 }, 703 To: labels.LabelArray{ 704 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 705 labels.NewLabel("foo1", "bar1", labels.LabelSourceK8s), 706 }, 707 Trace: policy.TRACE_VERBOSE, 708 } 709 710 repo := testNewPolicyRepository() 711 repo.AddList(rules) 712 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 713 714 // Empty From rules, all sources should be allowed 715 netPolicy2 := &networkingv1.NetworkPolicy{ 716 Spec: networkingv1.NetworkPolicySpec{ 717 PodSelector: metav1.LabelSelector{ 718 MatchLabels: map[string]string{ 719 "foo1": "bar1", 720 }, 721 }, 722 Ingress: []networkingv1.NetworkPolicyIngressRule{ 723 { 724 From: []networkingv1.NetworkPolicyPeer{}, 725 Ports: []networkingv1.NetworkPolicyPort{}, 726 }, 727 }, 728 }, 729 } 730 731 rules, err = ParseNetworkPolicy(netPolicy2) 732 c.Assert(err, IsNil) 733 c.Assert(len(rules), Equals, 1) 734 repo = testNewPolicyRepository() 735 repo.AddList(rules) 736 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 737 } 738 739 func (s *K8sSuite) TestParseNetworkPolicyDenyAll(c *C) { 740 // From missing, all sources should be allowed 741 netPolicy1 := &networkingv1.NetworkPolicy{ 742 Spec: networkingv1.NetworkPolicySpec{ 743 PodSelector: metav1.LabelSelector{ 744 MatchLabels: map[string]string{}, 745 }, 746 }, 747 } 748 749 rules, err := ParseNetworkPolicy(netPolicy1) 750 c.Assert(err, IsNil) 751 c.Assert(len(rules), Equals, 1) 752 753 ctx := policy.SearchContext{ 754 From: labels.LabelArray{ 755 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 756 labels.NewLabel("foo0", "bar0", labels.LabelSourceK8s), 757 }, 758 To: labels.LabelArray{ 759 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 760 labels.NewLabel("foo1", "bar1", labels.LabelSourceK8s), 761 }, 762 Trace: policy.TRACE_VERBOSE, 763 } 764 765 repo := testNewPolicyRepository() 766 repo.AddList(rules) 767 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied) 768 } 769 770 func (s *K8sSuite) TestParseNetworkPolicyNoIngress(c *C) { 771 netPolicy := &networkingv1.NetworkPolicy{ 772 Spec: networkingv1.NetworkPolicySpec{ 773 PodSelector: metav1.LabelSelector{ 774 MatchLabels: map[string]string{ 775 "foo1": "bar1", 776 "foo2": "bar2", 777 }, 778 }, 779 }, 780 } 781 782 rules, err := ParseNetworkPolicy(netPolicy) 783 c.Assert(err, IsNil) 784 c.Assert(len(rules), Equals, 1) 785 } 786 787 func (s *K8sSuite) TestNetworkPolicyExamples(c *C) { 788 // Example 1a: Only allow traffic from frontend pods on TCP port 6379 to 789 // backend pods in the same namespace `myns` 790 ex1 := []byte(`{ 791 "kind": "NetworkPolicy", 792 "apiVersion": "extensions/v1beta1", 793 "metadata": { 794 "name": "allow-frontend", 795 "namespace": "myns" 796 }, 797 "spec": { 798 "podSelector": { 799 "matchLabels": { 800 "role": "backend" 801 } 802 }, 803 "ingress": [ 804 { 805 "from": [ 806 { 807 "podSelector": { 808 "matchLabels": { 809 "role": "frontend" 810 } 811 } 812 } 813 ], 814 "ports": [ 815 { 816 "protocol": "TCP", 817 "port": 6379 818 } 819 ] 820 } 821 ] 822 } 823 }`) 824 np := networkingv1.NetworkPolicy{} 825 err := json.Unmarshal(ex1, &np) 826 c.Assert(err, IsNil) 827 828 _, err = ParseNetworkPolicy(&np) 829 c.Assert(err, IsNil) 830 831 // Example 1b: Only allow traffic from frontend pods to backend pods 832 // in the same namespace `myns` 833 ex1 = []byte(`{ 834 "kind": "NetworkPolicy", 835 "apiVersion": "extensions/networkingv1", 836 "metadata": { 837 "name": "allow-frontend", 838 "namespace": "myns" 839 }, 840 "spec": { 841 "podSelector": { 842 "matchLabels": { 843 "role": "backend" 844 } 845 }, 846 "ingress": [ 847 { 848 "from": [ 849 { 850 "podSelector": { 851 "matchLabels": { 852 "role": "frontend" 853 } 854 } 855 } 856 ] 857 },{ 858 "ports": [ 859 { 860 "protocol": "TCP", 861 "port": 6379 862 } 863 ] 864 } 865 ] 866 } 867 }`) 868 np = networkingv1.NetworkPolicy{} 869 err = json.Unmarshal(ex1, &np) 870 c.Assert(err, IsNil) 871 872 rules, err := ParseNetworkPolicy(&np) 873 c.Assert(err, IsNil) 874 c.Assert(len(rules), Equals, 1) 875 876 repo := testNewPolicyRepository() 877 repo.AddList(rules) 878 ctx := policy.SearchContext{ 879 From: labels.LabelArray{ 880 labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s), 881 labels.NewLabel("role", "frontend", labels.LabelSourceK8s), 882 }, 883 To: labels.LabelArray{ 884 labels.NewLabel("role", "backend", labels.LabelSourceK8s), 885 }, 886 Trace: policy.TRACE_VERBOSE, 887 } 888 // Doesn't share the same namespace 889 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied) 890 891 ctx = policy.SearchContext{ 892 From: labels.LabelArray{ 893 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 894 labels.NewLabel("role", "frontend", labels.LabelSourceK8s), 895 }, 896 To: labels.LabelArray{ 897 labels.NewLabel("role", "backend", labels.LabelSourceK8s), 898 }, 899 Trace: policy.TRACE_VERBOSE, 900 } 901 // Doesn't share the same namespace 902 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied) 903 904 ctx = policy.SearchContext{ 905 From: labels.LabelArray{ 906 labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s), 907 labels.NewLabel("role", "frontend", labels.LabelSourceK8s), 908 }, 909 To: labels.LabelArray{ 910 labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s), 911 labels.NewLabel("role", "backend", labels.LabelSourceK8s), 912 }, 913 DPorts: []*models.Port{ 914 { 915 Port: 6379, 916 Protocol: models.PortProtocolTCP, 917 }, 918 }, 919 Trace: policy.TRACE_VERBOSE, 920 } 921 // Should be ACCEPT sense the traffic needs to come from `frontend` AND 922 // port 6379 and belong to the same namespace `myns`. 923 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 924 925 // Example 2a: Allow TCP 443 from any source in Bob's namespaces. 926 ex2 := []byte(`{ 927 "kind": "NetworkPolicy", 928 "apiVersion": "extensions/v1beta1", 929 "metadata": { 930 "name": "allow-tcp-443" 931 }, 932 "spec": { 933 "podSelector": { 934 "matchLabels": { 935 "role": "frontend" 936 } 937 }, 938 "ingress": [ 939 { 940 "ports": [ 941 { 942 "protocol": "TCP", 943 "port": 443 944 } 945 ], 946 "from": [ 947 { 948 "namespaceSelector": { 949 "matchLabels": { 950 "user": "bob" 951 } 952 } 953 } 954 ] 955 } 956 ] 957 } 958 }`) 959 960 np = networkingv1.NetworkPolicy{} 961 err = json.Unmarshal(ex2, &np) 962 c.Assert(err, IsNil) 963 964 _, err = ParseNetworkPolicy(&np) 965 c.Assert(err, IsNil) 966 967 // Example 2b: Allow from any source in Bob's namespaces. 968 ex2 = []byte(`{ 969 "kind": "NetworkPolicy", 970 "apiVersion": "extensions/networkingv1", 971 "metadata": { 972 "name": "allow-tcp-443" 973 }, 974 "spec": { 975 "podSelector": { 976 "matchLabels": { 977 "role": "frontend" 978 } 979 }, 980 "ingress": [ 981 { 982 "ports": [ 983 { 984 "protocol": "TCP", 985 "port": 443 986 } 987 ], 988 "from": [ 989 { 990 "namespaceSelector": { 991 "matchLabels": { 992 "user": "bob" 993 } 994 } 995 } 996 ] 997 } 998 ] 999 } 1000 }`) 1001 1002 np = networkingv1.NetworkPolicy{} 1003 err = json.Unmarshal(ex2, &np) 1004 c.Assert(err, IsNil) 1005 1006 rules, err = ParseNetworkPolicy(&np) 1007 c.Assert(err, IsNil) 1008 c.Assert(len(rules), Equals, 1) 1009 1010 repo = testNewPolicyRepository() 1011 repo.AddList(rules) 1012 ctx = policy.SearchContext{ 1013 From: labels.LabelArray{ 1014 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1015 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s), 1016 }, 1017 To: labels.LabelArray{ 1018 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1019 labels.NewLabel("role", "frontend", labels.LabelSourceK8s), 1020 }, 1021 Trace: policy.TRACE_VERBOSE, 1022 } 1023 1024 // Should be DENY sense the traffic needs to come from 1025 // namespace `user=bob` AND port 443. 1026 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied) 1027 1028 l4Policy, err := repo.ResolveL4IngressPolicy(&ctx) 1029 c.Assert(l4Policy, Not(IsNil)) 1030 c.Assert(err, IsNil) 1031 l4Policy.Detach(repo.GetSelectorCache()) 1032 1033 ctx = policy.SearchContext{ 1034 From: labels.LabelArray{ 1035 labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s), 1036 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s), 1037 }, 1038 DPorts: []*models.Port{ 1039 { 1040 Port: 443, 1041 Protocol: models.PortProtocolTCP, 1042 }, 1043 }, 1044 To: labels.LabelArray{ 1045 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1046 labels.NewLabel("role", "frontend", labels.LabelSourceK8s), 1047 }, 1048 Trace: policy.TRACE_VERBOSE, 1049 } 1050 // Should be ACCEPT sense the traffic comes from Bob's namespaces 1051 // (even if it's a different namespace than `default`) AND port 443. 1052 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 1053 1054 // Example 3: Allow all traffic to all pods in this namespace. 1055 ex3 := []byte(`{ 1056 "kind": "NetworkPolicy", 1057 "apiVersion": "extensions/v1beta1", 1058 "metadata": { 1059 "name": "allow-all" 1060 }, 1061 "spec": { 1062 "podSelector": null, 1063 "ingress": [ 1064 { 1065 } 1066 ] 1067 } 1068 }`) 1069 1070 np = networkingv1.NetworkPolicy{} 1071 err = json.Unmarshal(ex3, &np) 1072 c.Assert(err, IsNil) 1073 1074 rules, err = ParseNetworkPolicy(&np) 1075 c.Assert(err, IsNil) 1076 c.Assert(len(rules), Equals, 1) 1077 1078 repo = testNewPolicyRepository() 1079 repo.AddList(rules) 1080 ctx = policy.SearchContext{ 1081 From: labels.LabelArray{ 1082 labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s), 1083 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s), 1084 }, 1085 To: labels.LabelArray{ 1086 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1087 labels.NewLabel("role", "backend", labels.LabelSourceK8s), 1088 }, 1089 Trace: policy.TRACE_VERBOSE, 1090 } 1091 // Should be ACCEPT since it's going to `default` namespace 1092 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 1093 1094 ctx = policy.SearchContext{ 1095 From: labels.LabelArray{ 1096 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1097 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s), 1098 }, 1099 To: labels.LabelArray{ 1100 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1101 labels.NewLabel("role", "backend", labels.LabelSourceK8s), 1102 }, 1103 Trace: policy.TRACE_VERBOSE, 1104 } 1105 // Should be ACCEPT since it's coming from `default` and going to `default` ns 1106 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 1107 1108 ctx = policy.SearchContext{ 1109 From: labels.LabelArray{ 1110 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1111 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s), 1112 }, 1113 To: labels.LabelArray{ 1114 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1115 labels.NewLabel("role", "backend", labels.LabelSourceK8s), 1116 }, 1117 DPorts: []*models.Port{ 1118 { 1119 Port: 443, 1120 Protocol: models.PortProtocolTCP, 1121 }, 1122 }, 1123 Trace: policy.TRACE_VERBOSE, 1124 } 1125 // Should be ACCEPT since it's coming from `default` and going to `default` namespace. 1126 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 1127 1128 // Example 4a: Example 4 is similar to example 2 but we will add both network 1129 // policies to see if the rules are additive for the same podSelector. 1130 ex4 := []byte(`{ 1131 "kind": "NetworkPolicy", 1132 "apiVersion": "extensions/v1beta1", 1133 "metadata": { 1134 "name": "allow-tcp-8080" 1135 }, 1136 "spec": { 1137 "podSelector": { 1138 "matchLabels": { 1139 "role": "frontend" 1140 } 1141 }, 1142 "ingress": [ 1143 { 1144 "ports": [ 1145 { 1146 "protocol": "UDP", 1147 "port": 8080 1148 } 1149 ], 1150 "from": [ 1151 { 1152 "namespaceSelector": { 1153 "matchLabels": { 1154 "user": "bob" 1155 } 1156 } 1157 } 1158 ] 1159 } 1160 ] 1161 } 1162 }`) 1163 1164 np = networkingv1.NetworkPolicy{} 1165 err = json.Unmarshal(ex4, &np) 1166 c.Assert(err, IsNil) 1167 1168 rules, err = ParseNetworkPolicy(&np) 1169 c.Assert(err, IsNil) 1170 c.Assert(len(rules), Equals, 1) 1171 1172 // Example 4b: Example 4 is similar to example 2 but we will add both network 1173 // policies to see if the rules are additive for the same podSelector. 1174 ex4 = []byte(`{ 1175 "kind": "NetworkPolicy", 1176 "apiVersion": "extensions/networkingv1", 1177 "metadata": { 1178 "name": "allow-tcp-8080" 1179 }, 1180 "spec": { 1181 "podSelector": { 1182 "matchLabels": { 1183 "role": "frontend" 1184 } 1185 }, 1186 "ingress": [ 1187 { 1188 "ports": [ 1189 { 1190 "protocol": "UDP", 1191 "port": 8080 1192 } 1193 ] 1194 },{ 1195 "from": [ 1196 { 1197 "namespaceSelector": { 1198 "matchLabels": { 1199 "user": "bob" 1200 } 1201 } 1202 } 1203 ] 1204 } 1205 ] 1206 } 1207 }`) 1208 1209 np = networkingv1.NetworkPolicy{} 1210 err = json.Unmarshal(ex4, &np) 1211 c.Assert(err, IsNil) 1212 1213 rules, err = ParseNetworkPolicy(&np) 1214 c.Assert(err, IsNil) 1215 c.Assert(len(rules), Equals, 1) 1216 1217 repo = testNewPolicyRepository() 1218 // add example 4 1219 repo.AddList(rules) 1220 1221 np = networkingv1.NetworkPolicy{} 1222 err = json.Unmarshal(ex2, &np) 1223 c.Assert(err, IsNil) 1224 1225 rules, err = ParseNetworkPolicy(&np) 1226 c.Assert(err, IsNil) 1227 // add example 2 1228 repo.AddList(rules) 1229 1230 ctx = policy.SearchContext{ 1231 From: labels.LabelArray{ 1232 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1233 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s), 1234 }, 1235 DPorts: []*models.Port{ 1236 { 1237 Protocol: models.PortProtocolUDP, 1238 Port: 8080, 1239 }, 1240 }, 1241 To: labels.LabelArray{ 1242 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1243 labels.NewLabel("role", "frontend", labels.LabelSourceK8s), 1244 }, 1245 Trace: policy.TRACE_VERBOSE, 1246 } 1247 // Should be ACCEPT sense traffic comes from Bob's namespaces AND port 8080 as specified in `ex4`. 1248 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 1249 1250 ctx = policy.SearchContext{ 1251 From: labels.LabelArray{ 1252 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1253 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "bob", labels.LabelSourceK8s), 1254 }, 1255 DPorts: []*models.Port{ 1256 { 1257 Port: 443, 1258 Protocol: models.PortProtocolTCP, 1259 }, 1260 }, 1261 To: labels.LabelArray{ 1262 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1263 labels.NewLabel("role", "frontend", labels.LabelSourceK8s), 1264 }, 1265 Trace: policy.TRACE_VERBOSE, 1266 } 1267 // Should be ACCEPT sense traffic comes from Bob's namespaces AND port 443 as specified in `ex2`. 1268 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 1269 1270 ctx = policy.SearchContext{ 1271 From: labels.LabelArray{ 1272 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1273 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "user"), "alice", labels.LabelSourceK8s), 1274 }, 1275 DPorts: []*models.Port{ 1276 { 1277 Protocol: models.PortProtocolUDP, 1278 Port: 8080, 1279 }, 1280 }, 1281 To: labels.LabelArray{ 1282 labels.NewLabel(k8sConst.PodNamespaceLabel, v1.NamespaceDefault, labels.LabelSourceK8s), 1283 labels.NewLabel("role", "frontend", labels.LabelSourceK8s), 1284 }, 1285 Trace: policy.TRACE_VERBOSE, 1286 } 1287 // Should be ACCEPT despite coming from Alice's namespaces since it's port 8080 as specified in `ex4`. 1288 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 1289 1290 // Example 5: Some policies with match expressions. 1291 ex5 := []byte(`{ 1292 "kind": "NetworkPolicy", 1293 "apiVersion": "extensions/v1beta1", 1294 "metadata": { 1295 "name": "allow-tcp-8080", 1296 "namespace": "expressions" 1297 }, 1298 "spec": { 1299 "podSelector": { 1300 "matchLabels": { 1301 "component": "redis" 1302 }, 1303 "matchExpressions": [ 1304 { 1305 "key": "tier", 1306 "operator": "In", 1307 "values": [ 1308 "cache" 1309 ] 1310 }, 1311 { 1312 "key": "environment", 1313 "operator": "NotIn", 1314 "values": [ 1315 "dev" 1316 ] 1317 } 1318 ] 1319 }, 1320 "ingress": [ 1321 { 1322 "ports": [ 1323 { 1324 "protocol": "UDP", 1325 "port": 8080 1326 } 1327 ], 1328 "from": [ 1329 { 1330 "namespaceSelector": { 1331 "matchLabels": { 1332 "component": "redis" 1333 }, 1334 "matchExpressions": [ 1335 { 1336 "key": "tier", 1337 "operator": "In", 1338 "values": [ 1339 "cache" 1340 ] 1341 }, 1342 { 1343 "key": "environment", 1344 "operator": "NotIn", 1345 "values": [ 1346 "dev" 1347 ] 1348 } 1349 ] 1350 } 1351 } 1352 ] 1353 } 1354 ] 1355 } 1356 }`) 1357 1358 np = networkingv1.NetworkPolicy{} 1359 err = json.Unmarshal(ex5, &np) 1360 c.Assert(err, IsNil) 1361 1362 rules, err = ParseNetworkPolicy(&np) 1363 c.Assert(err, IsNil) 1364 c.Assert(len(rules), Equals, 1) 1365 repo.AddList(rules) 1366 1367 // A reminder: from the kubernetes network policy spec: 1368 // namespaceSelector: 1369 // Selects Namespaces using cluster scoped-labels. This 1370 // matches all pods in all namespaces selected by this label selector. 1371 // This field follows standard label selector semantics. 1372 // If omitted, this selector selects no namespaces. 1373 // If present but empty, this selector selects all namespaces. 1374 ctx = policy.SearchContext{ 1375 From: labels.LabelArray{ 1376 // doesn't matter the namespace. 1377 labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s), 1378 // component==redis is in the policy 1379 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "component"), "redis", labels.LabelSourceK8s), 1380 // tier==cache is in the policy 1381 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "tier"), "cache", labels.LabelSourceK8s), 1382 // environment is not in `dev` which is in the policy 1383 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "environment"), "production", labels.LabelSourceK8s), 1384 // doesn't matter, there isn't any matchExpression denying traffic from any zone. 1385 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "zone"), "eu-1", labels.LabelSourceK8s), 1386 }, 1387 DPorts: []*models.Port{ 1388 { 1389 Port: 8080, 1390 Protocol: models.PortProtocolUDP, 1391 }, 1392 }, 1393 To: labels.LabelArray{ 1394 // Namespace needs to be in `expressions` since the policy is being enforced for that namespace. 1395 labels.NewLabel(k8sConst.PodNamespaceLabel, "expressions", labels.LabelSourceK8s), 1396 // component==redis is in the policy. 1397 labels.NewLabel("component", "redis", labels.LabelSourceK8s), 1398 // tier==cache is in the policy 1399 labels.NewLabel("tier", "cache", labels.LabelSourceK8s), 1400 }, 1401 Trace: policy.TRACE_VERBOSE, 1402 } 1403 // Should be ACCEPT since the SearchContext is being covered by the rules. 1404 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 1405 1406 ctx.To = labels.LabelArray{ 1407 // Namespace needs to be in `expressions` since the policy is being enforced for that namespace. 1408 labels.NewLabel(k8sConst.PodNamespaceLabel, "myns", labels.LabelSourceK8s), 1409 // component==redis is in the policy. 1410 labels.NewLabel("component", "redis", labels.LabelSourceK8s), 1411 // tier==cache is in the policy 1412 labels.NewLabel("tier", "cache", labels.LabelSourceK8s), 1413 } 1414 // Should be DENY since the namespace doesn't belong to the policy. 1415 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied) 1416 1417 ctx = policy.SearchContext{ 1418 From: labels.LabelArray{ 1419 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "component"), "redis", labels.LabelSourceK8s), 1420 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "tier"), "cache", labels.LabelSourceK8s), 1421 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "environment"), "dev", labels.LabelSourceK8s), 1422 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "zone"), "eu-1", labels.LabelSourceK8s), 1423 }, 1424 DPorts: []*models.Port{ 1425 { 1426 Port: 8080, 1427 Protocol: models.PortProtocolUDP, 1428 }, 1429 }, 1430 To: labels.LabelArray{ 1431 labels.NewLabel(k8sConst.PodNamespaceLabel, "expressions", labels.LabelSourceK8s), 1432 labels.NewLabel("component", "redis", labels.LabelSourceK8s), 1433 labels.NewLabel("tier", "cache", labels.LabelSourceK8s), 1434 }, 1435 Trace: policy.TRACE_VERBOSE, 1436 } 1437 // Should be DENY since the environment is from dev. 1438 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Denied) 1439 1440 ctx = policy.SearchContext{ 1441 From: labels.LabelArray{ 1442 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "component"), "redis", labels.LabelSourceK8s), 1443 labels.NewLabel(policy.JoinPath(k8sConst.PodNamespaceMetaLabels, "tier"), "cache", labels.LabelSourceK8s), 1444 }, 1445 DPorts: []*models.Port{ 1446 { 1447 Port: 8080, 1448 Protocol: models.PortProtocolUDP, 1449 }, 1450 }, 1451 To: labels.LabelArray{ 1452 labels.NewLabel(k8sConst.PodNamespaceLabel, "expressions", labels.LabelSourceK8s), 1453 labels.NewLabel("component", "redis", labels.LabelSourceK8s), 1454 labels.NewLabel("tier", "cache", labels.LabelSourceK8s), 1455 }, 1456 Trace: policy.TRACE_VERBOSE, 1457 } 1458 // Should be ACCEPT since the environment is from dev. 1459 c.Assert(repo.AllowsIngressRLocked(&ctx), Equals, api.Allowed) 1460 } 1461 1462 func (s *K8sSuite) TestCIDRPolicyExamples(c *C) { 1463 ex1 := []byte(`{ 1464 "kind": "NetworkPolicy", 1465 "apiVersion": "extensions/networkingv1", 1466 "metadata": { 1467 "name": "ingress-cidr-test", 1468 "namespace": "myns" 1469 }, 1470 "spec": { 1471 "podSelector": { 1472 "matchLabels": { 1473 "role": "backend" 1474 } 1475 }, 1476 "ingress": [ 1477 { 1478 "from": [ 1479 { 1480 "namespaceSelector": { 1481 "matchLabels": { 1482 "user": "bob" 1483 } 1484 } 1485 } 1486 ] 1487 }, { 1488 "from": [ 1489 { 1490 "ipBlock": { 1491 "cidr": "10.0.0.0/8", 1492 "except": [ 1493 "10.96.0.0/12" 1494 ] 1495 } 1496 } 1497 ] 1498 } 1499 ] 1500 } 1501 }`) 1502 np := networkingv1.NetworkPolicy{} 1503 err := json.Unmarshal(ex1, &np) 1504 c.Assert(err, IsNil) 1505 1506 rules, err := ParseNetworkPolicy(&np) 1507 c.Assert(err, IsNil) 1508 c.Assert(rules, NotNil) 1509 c.Assert(len(rules), Equals, 1) 1510 c.Assert(len(rules[0].Ingress), Equals, 2) 1511 1512 ex2 := []byte(`{ 1513 "kind": "NetworkPolicy", 1514 "apiVersion": "extensions/networkingv1", 1515 "metadata": { 1516 "name": "ingress-cidr-test", 1517 "namespace": "myns" 1518 }, 1519 "spec": { 1520 "podSelector": { 1521 "matchLabels": { 1522 "role": "backend" 1523 } 1524 }, 1525 "egress": [ 1526 { 1527 "to": [ 1528 { 1529 "ipBlock": { 1530 "cidr": "10.0.0.0/8", 1531 "except": [ 1532 "10.96.0.0/12", "10.255.255.254/32" 1533 ] 1534 } 1535 }, 1536 { 1537 "ipBlock": { 1538 "cidr": "11.0.0.0/8", 1539 "except": [ 1540 "11.96.0.0/12", "11.255.255.254/32" 1541 ] 1542 } 1543 } 1544 ] 1545 } 1546 ] 1547 } 1548 }`) 1549 1550 np = networkingv1.NetworkPolicy{} 1551 err = json.Unmarshal(ex2, &np) 1552 c.Assert(err, IsNil) 1553 1554 rules, err = ParseNetworkPolicy(&np) 1555 c.Assert(err, IsNil) 1556 c.Assert(rules, NotNil) 1557 c.Assert(len(rules), Equals, 1) 1558 c.Assert(rules[0].Egress[0].ToCIDRSet[0].Cidr, Equals, api.CIDR("10.0.0.0/8")) 1559 1560 expectedCIDRs := []api.CIDR{"10.96.0.0/12", "10.255.255.254/32"} 1561 for k, v := range rules[0].Egress[0].ToCIDRSet[0].ExceptCIDRs { 1562 c.Assert(v, Equals, expectedCIDRs[k]) 1563 } 1564 1565 expectedCIDRs = []api.CIDR{"11.96.0.0/12", "11.255.255.254/32"} 1566 for k, v := range rules[0].Egress[1].ToCIDRSet[0].ExceptCIDRs { 1567 c.Assert(v, Equals, expectedCIDRs[k]) 1568 } 1569 1570 c.Assert(len(rules[0].Egress), Equals, 2) 1571 1572 } 1573 1574 func getSelectorPointer(sel api.EndpointSelector) *api.EndpointSelector { 1575 return &sel 1576 } 1577 1578 func Test_parseNetworkPolicyPeer(t *testing.T) { 1579 type args struct { 1580 namespace string 1581 peer *networkingv1.NetworkPolicyPeer 1582 } 1583 tests := []struct { 1584 name string 1585 args args 1586 want *api.EndpointSelector 1587 }{ 1588 { 1589 name: "peer-with-pod-selector", 1590 args: args{ 1591 namespace: "foo-namespace", 1592 peer: &networkingv1.NetworkPolicyPeer{ 1593 PodSelector: &metav1.LabelSelector{ 1594 MatchLabels: map[string]string{ 1595 "foo": "bar", 1596 }, 1597 MatchExpressions: []metav1.LabelSelectorRequirement{ 1598 { 1599 Key: "foo", 1600 Operator: metav1.LabelSelectorOpIn, 1601 Values: []string{"bar", "baz"}, 1602 }, 1603 }, 1604 }, 1605 }, 1606 }, 1607 want: getSelectorPointer( 1608 api.NewESFromMatchRequirements( 1609 map[string]string{ 1610 "k8s.foo": "bar", 1611 "k8s.io.kubernetes.pod.namespace": "foo-namespace", 1612 }, 1613 []metav1.LabelSelectorRequirement{ 1614 { 1615 Key: "k8s.foo", 1616 Operator: metav1.LabelSelectorOpIn, 1617 Values: []string{"bar", "baz"}, 1618 }, 1619 }, 1620 ), 1621 ), 1622 }, 1623 { 1624 name: "peer-nil", 1625 args: args{ 1626 namespace: "foo-namespace", 1627 }, 1628 want: nil, 1629 }, 1630 { 1631 name: "peer-with-pod-selector-and-ns-selector", 1632 args: args{ 1633 namespace: "foo-namespace", 1634 peer: &networkingv1.NetworkPolicyPeer{ 1635 PodSelector: &metav1.LabelSelector{ 1636 MatchLabels: map[string]string{ 1637 "foo": "bar", 1638 }, 1639 MatchExpressions: []metav1.LabelSelectorRequirement{ 1640 { 1641 Key: "foo", 1642 Operator: metav1.LabelSelectorOpIn, 1643 Values: []string{"bar", "baz"}, 1644 }, 1645 }, 1646 }, 1647 NamespaceSelector: &metav1.LabelSelector{ 1648 MatchLabels: map[string]string{ 1649 "ns-foo": "ns-bar", 1650 }, 1651 MatchExpressions: []metav1.LabelSelectorRequirement{ 1652 { 1653 Key: "ns-foo-expression", 1654 Operator: metav1.LabelSelectorOpExists, 1655 }, 1656 }, 1657 }, 1658 }, 1659 }, 1660 want: getSelectorPointer( 1661 api.NewESFromMatchRequirements( 1662 map[string]string{ 1663 "k8s.foo": "bar", 1664 "k8s.io.cilium.k8s.namespace.labels.ns-foo": "ns-bar", 1665 }, 1666 []metav1.LabelSelectorRequirement{ 1667 { 1668 Key: "k8s.io.cilium.k8s.namespace.labels.ns-foo-expression", 1669 Operator: metav1.LabelSelectorOpExists, 1670 }, 1671 { 1672 Key: "k8s.foo", 1673 Operator: metav1.LabelSelectorOpIn, 1674 Values: []string{"bar", "baz"}, 1675 }, 1676 }, 1677 ), 1678 ), 1679 }, 1680 { 1681 name: "peer-with-ns-selector", 1682 args: args{ 1683 namespace: "foo-namespace", 1684 peer: &networkingv1.NetworkPolicyPeer{ 1685 NamespaceSelector: &metav1.LabelSelector{ 1686 MatchLabels: map[string]string{ 1687 "ns-foo": "ns-bar", 1688 }, 1689 MatchExpressions: []metav1.LabelSelectorRequirement{ 1690 { 1691 Key: "ns-foo-expression", 1692 Operator: metav1.LabelSelectorOpExists, 1693 }, 1694 }, 1695 }, 1696 }, 1697 }, 1698 want: getSelectorPointer( 1699 api.NewESFromMatchRequirements( 1700 map[string]string{ 1701 "k8s.io.cilium.k8s.namespace.labels.ns-foo": "ns-bar", 1702 }, 1703 []metav1.LabelSelectorRequirement{ 1704 { 1705 Key: "k8s.io.cilium.k8s.namespace.labels.ns-foo-expression", 1706 Operator: metav1.LabelSelectorOpExists, 1707 }, 1708 }, 1709 ), 1710 ), 1711 }, 1712 { 1713 name: "peer-with-allow-all-ns-selector", 1714 args: args{ 1715 namespace: "foo-namespace", 1716 peer: &networkingv1.NetworkPolicyPeer{ 1717 NamespaceSelector: &metav1.LabelSelector{}, 1718 }, 1719 }, 1720 want: getSelectorPointer( 1721 api.NewESFromMatchRequirements( 1722 map[string]string{}, 1723 []metav1.LabelSelectorRequirement{ 1724 { 1725 Key: fmt.Sprintf("%s.%s", labels.LabelSourceK8s, k8sConst.PodNamespaceLabel), 1726 Operator: metav1.LabelSelectorOpExists, 1727 }, 1728 }, 1729 ), 1730 ), 1731 }, 1732 } 1733 for _, tt := range tests { 1734 t.Run(tt.name, func(t *testing.T) { 1735 got := parseNetworkPolicyPeer(tt.args.namespace, tt.args.peer) 1736 args := []interface{}{got, tt.want} 1737 names := []string{"obtained", "expected"} 1738 if equal, err := checker.DeepEquals.Check(args, names); !equal { 1739 t.Errorf("Failed to parseNetworkPolicyPeer():\n%s", err) 1740 } 1741 }) 1742 } 1743 } 1744 1745 func (s *K8sSuite) TestGetPolicyLabelsv1(c *C) { 1746 uuid := "1bba160-ddca-11e8-b697-0800273b04ff" 1747 tests := []struct { 1748 np *networkingv1.NetworkPolicy // input network policy 1749 name string // expected extracted name 1750 namespace string // expected extracted namespace 1751 uuid string // expected extracted uuid 1752 derivedFrom string // expected extracted derived 1753 }{ 1754 { 1755 np: &networkingv1.NetworkPolicy{}, 1756 name: "", 1757 namespace: v1.NamespaceDefault, 1758 uuid: "", 1759 derivedFrom: resourceTypeNetworkPolicy, 1760 }, 1761 { 1762 np: &networkingv1.NetworkPolicy{ 1763 ObjectMeta: metav1.ObjectMeta{ 1764 Annotations: map[string]string{ 1765 annotation.Name: "foo", 1766 }, 1767 }, 1768 }, 1769 name: "foo", 1770 uuid: "", 1771 namespace: v1.NamespaceDefault, 1772 derivedFrom: resourceTypeNetworkPolicy, 1773 }, 1774 { 1775 np: &networkingv1.NetworkPolicy{ 1776 ObjectMeta: metav1.ObjectMeta{ 1777 Name: "foo", 1778 Namespace: "bar", 1779 UID: types.UID(uuid), 1780 }, 1781 }, 1782 name: "foo", 1783 namespace: "bar", 1784 uuid: uuid, 1785 derivedFrom: resourceTypeNetworkPolicy, 1786 }, 1787 } 1788 1789 assertLabel := func(lbl labels.Label, key, value string) { 1790 c.Assert(lbl.Key, Equals, key) 1791 c.Assert(lbl.Value, Equals, value) 1792 c.Assert(lbl.Source, Equals, labels.LabelSourceK8s) 1793 } 1794 1795 for _, tt := range tests { 1796 lbls := GetPolicyLabelsv1(tt.np) 1797 1798 c.Assert(lbls, NotNil) 1799 c.Assert(len(lbls), Equals, 4, Commentf( 1800 "Incorrect number of labels: Expected DerivedFrom, Name, Namespace and UID labels.")) 1801 assertLabel(lbls[0], "io.cilium.k8s.policy.derived-from", tt.derivedFrom) 1802 assertLabel(lbls[1], "io.cilium.k8s.policy.name", tt.name) 1803 assertLabel(lbls[2], "io.cilium.k8s.policy.namespace", tt.namespace) 1804 assertLabel(lbls[3], "io.cilium.k8s.policy.uid", tt.uuid) 1805 } 1806 } 1807 1808 func (s *K8sSuite) TestIPBlockToCIDRRule(c *C) { 1809 blocks := []*networkingv1.IPBlock{ 1810 {}, 1811 {CIDR: "192.168.1.1/24"}, 1812 {CIDR: "192.168.1.1/24", Except: []string{}}, 1813 {CIDR: "192.168.1.1/24", Except: []string{"192.168.1.1/28"}}, 1814 { 1815 CIDR: "192.168.1.1/24", 1816 Except: []string{ 1817 "192.168.1.1/30", 1818 "192.168.1.1/26", 1819 "192.168.1.1/28", 1820 }, 1821 }, 1822 } 1823 1824 for _, block := range blocks { 1825 cidrRule := ipBlockToCIDRRule(block) 1826 1827 exceptCIDRs := make([]api.CIDR, len(block.Except)) 1828 for i, v := range block.Except { 1829 exceptCIDRs[i] = api.CIDR(v) 1830 } 1831 1832 c.Assert(cidrRule.Generated, Equals, false) 1833 c.Assert(cidrRule.Cidr, Equals, api.CIDR(block.CIDR)) 1834 1835 if block.Except == nil || len(block.Except) == 0 { 1836 c.Assert(cidrRule.ExceptCIDRs, IsNil) 1837 } else { 1838 c.Assert(cidrRule.ExceptCIDRs, checker.DeepEquals, exceptCIDRs) 1839 } 1840 } 1841 }