k8s.io/kubernetes@v1.29.3/test/e2e/network/netpol/network_policy.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package netpol 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 "k8s.io/apimachinery/pkg/util/intstr" 25 26 v1 "k8s.io/api/core/v1" 27 networkingv1 "k8s.io/api/networking/v1" 28 29 "github.com/onsi/ginkgo/v2" 30 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 "k8s.io/kubernetes/test/e2e/feature" 33 "k8s.io/kubernetes/test/e2e/framework" 34 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" 35 "k8s.io/kubernetes/test/e2e/network/common" 36 admissionapi "k8s.io/pod-security-admission/api" 37 utilnet "k8s.io/utils/net" 38 ) 39 40 const ( 41 isVerbose = true 42 43 // See https://github.com/kubernetes/kubernetes/issues/95879 44 // The semantics of the effect of network policies on loopback calls may be undefined: should 45 // they always be ALLOWED; how do Services affect this? 46 // Calico, Cillium, Antrea seem to do different things. 47 // Since different CNIs have different results, that causes tests including loopback to fail 48 // on some CNIs. So let's just ignore loopback calls for the purposes of deciding test pass/fail. 49 ignoreLoopback = true 50 namespaceLabelKey = "kubernetes.io/metadata.name" 51 ) 52 53 var ( 54 protocolTCP = v1.ProtocolTCP 55 protocolUDP = v1.ProtocolUDP 56 protocolSCTP = v1.ProtocolSCTP 57 ) 58 59 /* 60 You might be wondering, why are there multiple namespaces used for each test case? 61 62 These tests are based on "truth tables" that compare the expected and actual connectivity of each pair of pods. 63 Since network policies live in namespaces, and peers can be selected by namespace, 64 howing the connectivity of pods in other namespaces is key information to show whether a network policy is working as intended or not. 65 66 We use 3 namespaces each with 3 pods, and probe all combinations ( 9 pods x 9 pods = 81 data points ) -- including cross-namespace calls. 67 68 Here's an example of a test run, showing the expected and actual connectivity, along with the differences. Note how the 69 visual representation as a truth table greatly aids in understanding what a network policy is intended to do in theory 70 and what is happening in practice: 71 72 Oct 19 10:34:16.907: INFO: expected: 73 74 - x/a x/b x/c y/a y/b y/c z/a z/b z/c 75 x/a X . . . . . . . . 76 x/b X . . . . . . . . 77 x/c X . . . . . . . . 78 y/a . . . . . . . . . 79 y/b . . . . . . . . . 80 y/c . . . . . . . . . 81 z/a X . . . . . . . . 82 z/b X . . . . . . . . 83 z/c X . . . . . . . . 84 85 Oct 19 10:34:16.907: INFO: observed: 86 87 - x/a x/b x/c y/a y/b y/c z/a z/b z/c 88 x/a X . . . . . . . . 89 x/b X . . . . . . . . 90 x/c X . . . . . . . . 91 y/a . . . . . . . . . 92 y/b . . . . . . . . . 93 y/c . . . . . . . . . 94 z/a X . . . . . . . . 95 z/b X . . . . . . . . 96 z/c X . . . . . . . . 97 98 Oct 19 10:34:16.907: INFO: comparison: 99 100 - x/a x/b x/c y/a y/b y/c z/a z/b z/c 101 x/a . . . . . . . . . 102 x/b . . . . . . . . . 103 x/c . . . . . . . . . 104 y/a . . . . . . . . . 105 y/b . . . . . . . . . 106 y/c . . . . . . . . . 107 z/a . . . . . . . . . 108 z/b . . . . . . . . . 109 z/c . . . . . . . . . 110 */ 111 112 var _ = common.SIGDescribe("Netpol", func() { 113 f := framework.NewDefaultFramework("netpol") 114 f.SkipNamespaceCreation = true // we create our own 3 test namespaces, we don't need the default one 115 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 116 117 ginkgo.Context("NetworkPolicy between server and client", func() { 118 var k8s *kubeManager 119 120 f.It("should support a 'default-deny-ingress' policy", feature.NetworkPolicy, func(ctx context.Context) { 121 122 // Only poll TCP 123 protocols := []v1.Protocol{protocolTCP} 124 125 // Only testing port 80 126 ports := []int32{80} 127 128 // Create pods and namespaces for this test 129 k8s = initializeResources(ctx, f, protocols, ports) 130 131 // Only going to make a policy in namespace X 132 nsX, _, _ := getK8sNamespaces(k8s) 133 policy := GenNetworkPolicyWithNameAndPodSelector("deny-ingress", metav1.LabelSelector{}, SetSpecIngressRules()) 134 135 // Create the policy 136 CreatePolicy(ctx, k8s, policy, nsX) 137 138 // Make a truth table of connectivity for all pods in ns x y z 139 reachability := NewReachability(k8s.AllPodStrings(), true) 140 // Set the nsX as false, since it has a policy that blocks traffic 141 reachability.ExpectPeer(&Peer{}, &Peer{Namespace: nsX}, false) 142 143 // Confirm that the real world connectivity matches our matrix 144 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 145 }) 146 147 f.It("should support a 'default-deny-all' policy", feature.NetworkPolicy, func(ctx context.Context) { 148 policy := GenNetworkPolicyWithNameAndPodSelector("deny-all", metav1.LabelSelector{}, SetSpecIngressRules(), SetSpecEgressRules()) 149 protocols := []v1.Protocol{protocolTCP} 150 ports := []int32{80} 151 k8s = initializeResources(ctx, f, protocols, ports) 152 nsX, _, _ := getK8sNamespaces(k8s) 153 CreatePolicy(ctx, k8s, policy, nsX) 154 155 reachability := NewReachability(k8s.AllPodStrings(), true) 156 reachability.ExpectPeer(&Peer{}, &Peer{Namespace: nsX}, false) 157 reachability.ExpectPeer(&Peer{Namespace: nsX}, &Peer{}, false) 158 159 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 160 }) 161 162 f.It("should enforce policy to allow traffic from pods within server namespace based on PodSelector", feature.NetworkPolicy, func(ctx context.Context) { 163 allowedPods := metav1.LabelSelector{ 164 MatchLabels: map[string]string{ 165 "pod": "b", 166 }, 167 } 168 ingressRule := networkingv1.NetworkPolicyIngressRule{} 169 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{PodSelector: &allowedPods}) 170 policy := GenNetworkPolicyWithNameAndPodMatchLabel("x-a-allows-x-b", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 171 172 protocols := []v1.Protocol{protocolTCP} 173 ports := []int32{80} 174 k8s = initializeResources(ctx, f, protocols, ports) 175 nsX, _, _ := getK8sNamespaces(k8s) 176 CreatePolicy(ctx, k8s, policy, nsX) 177 178 reachability := NewReachability(k8s.AllPodStrings(), true) 179 reachability.ExpectAllIngress(NewPodString(nsX, "a"), false) 180 reachability.Expect(NewPodString(nsX, "b"), NewPodString(nsX, "a"), true) 181 182 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 183 }) 184 185 f.It("should enforce policy to allow ingress traffic for a target", feature.NetworkPolicy, func(ctx context.Context) { 186 protocols := []v1.Protocol{protocolTCP} 187 ports := []int32{80} 188 k8s = initializeResources(ctx, f, protocols, ports) 189 nsX, _, _ := getK8sNamespaces(k8s) 190 ginkgo.By("having a deny all ingress policy", func() { 191 // Deny all Ingress traffic policy to pods on namespace nsX 192 policy := GenNetworkPolicyWithNameAndPodSelector("deny-all", metav1.LabelSelector{}, SetSpecIngressRules()) 193 CreatePolicy(ctx, k8s, policy, nsX) 194 }) 195 196 // Allow Ingress traffic only to pod x/a from any pod 197 ingressRule := networkingv1.NetworkPolicyIngressRule{} 198 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{PodSelector: &metav1.LabelSelector{}, NamespaceSelector: &metav1.LabelSelector{}}) 199 allowPolicy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-all-to-a", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 200 CreatePolicy(ctx, k8s, allowPolicy, nsX) 201 202 reachability := NewReachability(k8s.AllPodStrings(), true) 203 reachability.ExpectAllIngress(NewPodString(nsX, "a"), true) 204 reachability.ExpectAllIngress(NewPodString(nsX, "b"), false) 205 reachability.ExpectAllIngress(NewPodString(nsX, "c"), false) 206 207 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 208 }) 209 210 f.It("should enforce policy to allow ingress traffic from pods in all namespaces", feature.NetworkPolicy, func(ctx context.Context) { 211 protocols := []v1.Protocol{protocolTCP} 212 ports := []int32{80} 213 k8s = initializeResources(ctx, f, protocols, ports) 214 nsX, _, _ := getK8sNamespaces(k8s) 215 ingressRule := networkingv1.NetworkPolicyIngressRule{} 216 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{}}}) 217 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-from-another-ns", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 218 CreatePolicy(ctx, k8s, policy, nsX) 219 220 reachability := NewReachability(k8s.AllPodStrings(), true) 221 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 222 }) 223 224 f.It("should enforce policy to allow traffic only from a different namespace, based on NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) { 225 protocols := []v1.Protocol{protocolTCP} 226 ports := []int32{80} 227 k8s = initializeResources(ctx, f, protocols, ports) 228 nsX, nsY, nsZ := getK8sNamespaces(k8s) 229 ingressRule := networkingv1.NetworkPolicyIngressRule{} 230 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{namespaceLabelKey: nsY}}}) 231 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-client-a-via-ns-selector", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 232 CreatePolicy(ctx, k8s, policy, nsX) 233 234 reachability := NewReachability(k8s.AllPodStrings(), true) 235 // disallow all traffic from the x or z namespaces 236 reachability.ExpectPeer(&Peer{Namespace: nsX}, &Peer{Namespace: nsX, Pod: "a"}, false) 237 reachability.ExpectPeer(&Peer{Namespace: nsZ}, &Peer{Namespace: nsX, Pod: "a"}, false) 238 239 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 240 }) 241 242 f.It("should enforce policy based on PodSelector with MatchExpressions", feature.NetworkPolicy, func(ctx context.Context) { 243 allowedPods := metav1.LabelSelector{ 244 MatchExpressions: []metav1.LabelSelectorRequirement{{ 245 Key: "pod", 246 Operator: metav1.LabelSelectorOpIn, 247 Values: []string{"b"}, 248 }}, 249 } 250 ingressRule := networkingv1.NetworkPolicyIngressRule{} 251 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{PodSelector: &allowedPods}) 252 policy := GenNetworkPolicyWithNameAndPodMatchLabel("x-a-allows-x-b", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 253 254 protocols := []v1.Protocol{protocolTCP} 255 ports := []int32{80} 256 k8s = initializeResources(ctx, f, protocols, ports) 257 nsX, _, _ := getK8sNamespaces(k8s) 258 CreatePolicy(ctx, k8s, policy, nsX) 259 260 reachability := NewReachability(k8s.AllPodStrings(), true) 261 reachability.ExpectAllIngress(NewPodString(nsX, "a"), false) 262 reachability.Expect(NewPodString(nsX, "b"), NewPodString(nsX, "a"), true) 263 264 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 265 }) 266 267 f.It("should enforce policy based on NamespaceSelector with MatchExpressions", feature.NetworkPolicy, func(ctx context.Context) { 268 protocols := []v1.Protocol{protocolTCP} 269 ports := []int32{80} 270 k8s = initializeResources(ctx, f, protocols, ports) 271 nsX, nsY, nsZ := getK8sNamespaces(k8s) 272 allowedNamespaces := &metav1.LabelSelector{ 273 MatchExpressions: []metav1.LabelSelectorRequirement{{ 274 Key: namespaceLabelKey, 275 Operator: metav1.LabelSelectorOpIn, 276 Values: []string{nsY}, 277 }}, 278 } 279 ingressRule := networkingv1.NetworkPolicyIngressRule{} 280 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedNamespaces}) 281 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ns-y-match-selector", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 282 CreatePolicy(ctx, k8s, policy, nsX) 283 284 reachability := NewReachability(k8s.AllPodStrings(), true) 285 // disallow all traffic from the x or z namespaces 286 reachability.ExpectPeer(&Peer{Namespace: nsX}, &Peer{Namespace: nsX, Pod: "a"}, false) 287 reachability.ExpectPeer(&Peer{Namespace: nsZ}, &Peer{Namespace: nsX, Pod: "a"}, false) 288 289 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 290 }) 291 292 f.It("should enforce policy based on PodSelector or NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) { 293 protocols := []v1.Protocol{protocolTCP} 294 ports := []int32{80} 295 k8s = initializeResources(ctx, f, protocols, ports) 296 nsX, _, _ := getK8sNamespaces(k8s) 297 allowedNamespaces := &metav1.LabelSelector{ 298 MatchExpressions: []metav1.LabelSelectorRequirement{{ 299 Key: namespaceLabelKey, 300 Operator: metav1.LabelSelectorOpNotIn, 301 Values: []string{nsX}, 302 }}, 303 } 304 podBAllowlisting := &metav1.LabelSelector{ 305 MatchLabels: map[string]string{ 306 "pod": "b", 307 }, 308 } 309 ingressRule := networkingv1.NetworkPolicyIngressRule{} 310 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedNamespaces}, networkingv1.NetworkPolicyPeer{PodSelector: podBAllowlisting}) 311 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ns-y-match-selector", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 312 CreatePolicy(ctx, k8s, policy, nsX) 313 314 reachability := NewReachability(k8s.AllPodStrings(), true) 315 reachability.Expect(NewPodString(nsX, "a"), NewPodString(nsX, "a"), false) 316 reachability.Expect(NewPodString(nsX, "c"), NewPodString(nsX, "a"), false) 317 318 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 319 }) 320 321 f.It("should enforce policy based on PodSelector and NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) { 322 protocols := []v1.Protocol{protocolTCP} 323 ports := []int32{80} 324 k8s = initializeResources(ctx, f, protocols, ports) 325 nsX, nsY, nsZ := getK8sNamespaces(k8s) 326 allowedNamespaces := &metav1.LabelSelector{ 327 MatchExpressions: []metav1.LabelSelectorRequirement{{ 328 Key: namespaceLabelKey, 329 Operator: metav1.LabelSelectorOpNotIn, 330 Values: []string{nsX}, 331 }}, 332 } 333 allowedPod := &metav1.LabelSelector{ 334 MatchLabels: map[string]string{ 335 "pod": "b", 336 }, 337 } 338 ingressRule := networkingv1.NetworkPolicyIngressRule{} 339 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedNamespaces, PodSelector: allowedPod}) 340 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ns-y-podselector-and-nsselector", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 341 CreatePolicy(ctx, k8s, policy, nsX) 342 343 reachability := NewReachability(k8s.AllPodStrings(), true) 344 reachability.ExpectAllIngress(NewPodString(nsX, "a"), false) 345 reachability.Expect(NewPodString(nsY, "b"), NewPodString(nsX, "a"), true) 346 reachability.Expect(NewPodString(nsZ, "b"), NewPodString(nsX, "a"), true) 347 348 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 349 }) 350 351 f.It("should enforce policy based on Multiple PodSelectors and NamespaceSelectors", feature.NetworkPolicy, func(ctx context.Context) { 352 protocols := []v1.Protocol{protocolTCP} 353 ports := []int32{80} 354 k8s = initializeResources(ctx, f, protocols, ports) 355 nsX, nsY, nsZ := getK8sNamespaces(k8s) 356 allowedNamespaces := &metav1.LabelSelector{ 357 MatchExpressions: []metav1.LabelSelectorRequirement{{ 358 Key: namespaceLabelKey, 359 Operator: metav1.LabelSelectorOpNotIn, 360 Values: []string{nsX}, 361 }}, 362 } 363 allowedPod := &metav1.LabelSelector{ 364 MatchExpressions: []metav1.LabelSelectorRequirement{{ 365 Key: "pod", 366 Operator: metav1.LabelSelectorOpIn, 367 Values: []string{"b", "c"}, 368 }}, 369 } 370 371 ingressRule := networkingv1.NetworkPolicyIngressRule{} 372 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedNamespaces, PodSelector: allowedPod}) 373 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ns-y-z-pod-b-c", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 374 CreatePolicy(ctx, k8s, policy, nsX) 375 376 reachability := NewReachability(k8s.AllPodStrings(), true) 377 reachability.ExpectPeer(&Peer{Namespace: nsX}, &Peer{Namespace: nsX, Pod: "a"}, false) 378 reachability.Expect(NewPodString(nsY, "a"), NewPodString(nsX, "a"), false) 379 reachability.Expect(NewPodString(nsZ, "a"), NewPodString(nsX, "a"), false) 380 381 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 382 }) 383 384 f.It("should enforce policy based on any PodSelectors", feature.NetworkPolicy, func(ctx context.Context) { 385 protocols := []v1.Protocol{protocolTCP} 386 ports := []int32{80} 387 k8s = initializeResources(ctx, f, protocols, ports) 388 nsX, _, _ := getK8sNamespaces(k8s) 389 ingressRule := networkingv1.NetworkPolicyIngressRule{} 390 for _, label := range []map[string]string{{"pod": "b"}, {"pod": "c"}} { 391 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{PodSelector: &metav1.LabelSelector{MatchLabels: label}}) 392 } 393 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ns-x-pod-b-c", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 394 CreatePolicy(ctx, k8s, policy, nsX) 395 396 reachability := NewReachability(k8s.AllPodStrings(), true) 397 reachability.ExpectAllIngress(NewPodString(nsX, "a"), false) 398 399 // Connect Pods b and c to pod a from namespace nsX 400 reachability.Expect(NewPodString(nsX, "b"), NewPodString(nsX, "a"), true) 401 reachability.Expect(NewPodString(nsX, "c"), NewPodString(nsX, "a"), true) 402 403 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 404 }) 405 406 f.It("should enforce policy to allow traffic only from a pod in a different namespace based on PodSelector and NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) { 407 protocols := []v1.Protocol{protocolTCP} 408 ports := []int32{80} 409 k8s = initializeResources(ctx, f, protocols, ports) 410 nsX, nsY, _ := getK8sNamespaces(k8s) 411 allowedNamespaces := &metav1.LabelSelector{ 412 MatchLabels: map[string]string{ 413 namespaceLabelKey: nsY, 414 }, 415 } 416 allowedPods := &metav1.LabelSelector{ 417 MatchLabels: map[string]string{ 418 "pod": "a", 419 }, 420 } 421 ingressRule := networkingv1.NetworkPolicyIngressRule{} 422 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedNamespaces, PodSelector: allowedPods}) 423 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ns-y-pod-a-via-namespace-pod-selector", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 424 CreatePolicy(ctx, k8s, policy, nsX) 425 426 reachability := NewReachability(k8s.AllPodStrings(), true) 427 reachability.ExpectAllIngress(NewPodString(nsX, "a"), false) 428 reachability.Expect(NewPodString(nsY, "a"), NewPodString(nsX, "a"), true) 429 430 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 431 }) 432 433 f.It("should enforce policy based on Ports", feature.NetworkPolicy, func(ctx context.Context) { 434 ginkgo.By("Creating a network allowPort81Policy which only allows allow listed namespaces (y) to connect on exactly one port (81)") 435 protocols := []v1.Protocol{protocolTCP} 436 ports := []int32{81} 437 k8s = initializeResources(ctx, f, protocols, ports) 438 nsX, nsY, nsZ := getK8sNamespaces(k8s) 439 allowedLabels := &metav1.LabelSelector{ 440 MatchLabels: map[string]string{ 441 namespaceLabelKey: nsY, 442 }, 443 } 444 ingressRule := networkingv1.NetworkPolicyIngressRule{} 445 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedLabels}) 446 ingressRule.Ports = append(ingressRule.Ports, networkingv1.NetworkPolicyPort{Port: &intstr.IntOrString{IntVal: 81}, Protocol: &protocolTCP}) 447 allowPort81Policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-client-a-via-ns-selector", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 448 CreatePolicy(ctx, k8s, allowPort81Policy, nsX) 449 450 reachability := NewReachability(k8s.AllPodStrings(), true) 451 reachability.ExpectPeer(&Peer{Namespace: nsX}, &Peer{Namespace: nsX, Pod: "a"}, false) 452 reachability.ExpectPeer(&Peer{Namespace: nsY}, &Peer{Namespace: nsX, Pod: "a"}, true) 453 reachability.ExpectPeer(&Peer{Namespace: nsZ}, &Peer{Namespace: nsX, Pod: "a"}, false) 454 455 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachability}) 456 }) 457 458 f.It("should enforce multiple, stacked policies with overlapping podSelectors", feature.NetworkPolicy, func(ctx context.Context) { 459 ginkgo.By("Creating a network allowPort81Policy which only allows allow listed namespaces (y) to connect on exactly one port (81)") 460 protocols := []v1.Protocol{protocolTCP} 461 ports := []int32{80, 81} 462 k8s = initializeResources(ctx, f, protocols, ports) 463 nsX, nsY, nsZ := getK8sNamespaces(k8s) 464 allowedLabels := &metav1.LabelSelector{ 465 MatchLabels: map[string]string{ 466 namespaceLabelKey: nsY, 467 }, 468 } 469 ingressRule := networkingv1.NetworkPolicyIngressRule{} 470 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedLabels}) 471 ingressRule.Ports = append(ingressRule.Ports, networkingv1.NetworkPolicyPort{Port: &intstr.IntOrString{IntVal: 81}, Protocol: &protocolTCP}) 472 allowPort81Policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-client-a-via-ns-selector", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 473 CreatePolicy(ctx, k8s, allowPort81Policy, nsX) 474 475 reachabilityALLOW := NewReachability(k8s.AllPodStrings(), true) 476 reachabilityALLOW.ExpectPeer(&Peer{Namespace: nsX}, &Peer{Namespace: nsX, Pod: "a"}, false) 477 reachabilityALLOW.ExpectPeer(&Peer{Namespace: nsY}, &Peer{Namespace: nsX, Pod: "a"}, true) 478 reachabilityALLOW.ExpectPeer(&Peer{Namespace: nsZ}, &Peer{Namespace: nsX, Pod: "a"}, false) 479 480 ginkgo.By("Verifying traffic on port 81.") 481 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachabilityALLOW}) 482 483 reachabilityDENY := NewReachability(k8s.AllPodStrings(), true) 484 reachabilityDENY.ExpectAllIngress(NewPodString(nsX, "a"), false) 485 486 ginkgo.By("Verifying traffic on port 80.") 487 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachabilityDENY}) 488 489 ingressRule = networkingv1.NetworkPolicyIngressRule{} 490 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedLabels}) 491 ingressRule.Ports = append(ingressRule.Ports, networkingv1.NetworkPolicyPort{Port: &intstr.IntOrString{IntVal: 80}, Protocol: &protocolTCP}) 492 493 allowPort80Policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-client-a-via-ns-selector-80", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 494 CreatePolicy(ctx, k8s, allowPort80Policy, nsX) 495 496 ginkgo.By("Verifying that we can add a policy to unblock port 80") 497 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachabilityALLOW}) 498 }) 499 500 f.It("should support allow-all policy", feature.NetworkPolicy, func(ctx context.Context) { 501 ginkgo.By("Creating a network policy which allows all traffic.") 502 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-all", map[string]string{}, SetSpecIngressRules(networkingv1.NetworkPolicyIngressRule{})) 503 protocols := []v1.Protocol{protocolTCP} 504 ports := []int32{80, 81} 505 k8s = initializeResources(ctx, f, protocols, ports) 506 nsX, _, _ := getK8sNamespaces(k8s) 507 CreatePolicy(ctx, k8s, policy, nsX) 508 509 ginkgo.By("Testing pods can connect to both ports when an 'allow-all' policy is present.") 510 reachability := NewReachability(k8s.AllPodStrings(), true) 511 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 512 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachability}) 513 }) 514 515 f.It("should allow ingress access on one named port", feature.NetworkPolicy, func(ctx context.Context) { 516 IngressRules := networkingv1.NetworkPolicyIngressRule{} 517 IngressRules.Ports = append(IngressRules.Ports, networkingv1.NetworkPolicyPort{Port: &intstr.IntOrString{Type: intstr.String, StrVal: "serve-81-tcp"}}) 518 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-all", map[string]string{}, SetSpecIngressRules(IngressRules)) 519 protocols := []v1.Protocol{protocolTCP} 520 ports := []int32{80, 81} 521 k8s = initializeResources(ctx, f, protocols, ports) 522 nsX, _, _ := getK8sNamespaces(k8s) 523 CreatePolicy(ctx, k8s, policy, nsX) 524 525 ginkgo.By("Blocking all ports other then 81 in the entire namespace") 526 527 reachabilityPort81 := NewReachability(k8s.AllPodStrings(), true) 528 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachabilityPort81}) 529 530 // disallow all traffic to the x namespace 531 reachabilityPort80 := NewReachability(k8s.AllPodStrings(), true) 532 reachabilityPort80.ExpectPeer(&Peer{}, &Peer{Namespace: nsX}, false) 533 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachabilityPort80}) 534 }) 535 536 f.It("should allow ingress access from namespace on one named port", feature.NetworkPolicy, func(ctx context.Context) { 537 protocols := []v1.Protocol{protocolTCP} 538 ports := []int32{80, 81} 539 k8s = initializeResources(ctx, f, protocols, ports) 540 nsX, nsY, nsZ := getK8sNamespaces(k8s) 541 allowedLabels := &metav1.LabelSelector{ 542 MatchLabels: map[string]string{ 543 namespaceLabelKey: nsY, 544 }, 545 } 546 ingressRule := networkingv1.NetworkPolicyIngressRule{} 547 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedLabels}) 548 ingressRule.Ports = append(ingressRule.Ports, networkingv1.NetworkPolicyPort{Port: &intstr.IntOrString{Type: intstr.String, StrVal: "serve-80-tcp"}, Protocol: &protocolTCP}) 549 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-client-a-via-ns-selector-80", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 550 CreatePolicy(ctx, k8s, policy, nsX) 551 552 reachability := NewReachability(k8s.AllPodStrings(), true) 553 // disallow all traffic from the x or z namespaces 554 reachability.ExpectPeer(&Peer{Namespace: nsX}, &Peer{Namespace: nsX, Pod: "a"}, false) 555 reachability.ExpectPeer(&Peer{Namespace: nsZ}, &Peer{Namespace: nsX, Pod: "a"}, false) 556 557 ginkgo.By("Verify that port 80 is allowed for namespace y") 558 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 559 560 ginkgo.By("Verify that port 81 is blocked for all namespaces including y") 561 reachabilityFAIL := NewReachability(k8s.AllPodStrings(), true) 562 reachabilityFAIL.ExpectAllIngress(NewPodString(nsX, "a"), false) 563 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachabilityFAIL}) 564 }) 565 566 f.It("should allow egress access on one named port", feature.NetworkPolicy, func(ctx context.Context) { 567 ginkgo.By("validating egress from port 81 to port 80") 568 egressRule := networkingv1.NetworkPolicyEgressRule{} 569 egressRule.Ports = append(egressRule.Ports, networkingv1.NetworkPolicyPort{Port: &intstr.IntOrString{Type: intstr.String, StrVal: "serve-80-tcp"}}) 570 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-egress", map[string]string{}, SetSpecEgressRules(egressRule)) 571 572 protocols := []v1.Protocol{protocolTCP} 573 ports := []int32{80, 81} 574 k8s = initializeResources(ctx, f, protocols, ports) 575 nsX, _, _ := getK8sNamespaces(k8s) 576 CreatePolicy(ctx, k8s, policy, nsX) 577 578 reachabilityPort80 := NewReachability(k8s.AllPodStrings(), true) 579 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachabilityPort80}) 580 581 // meanwhile no traffic over 81 should work, since our egress policy is on 80 582 reachabilityPort81 := NewReachability(k8s.AllPodStrings(), true) 583 reachabilityPort81.ExpectPeer(&Peer{Namespace: nsX}, &Peer{}, false) 584 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachabilityPort81}) 585 }) 586 587 f.It("should enforce updated policy", feature.NetworkPolicy, func(ctx context.Context) { 588 ginkgo.By("Using the simplest possible mutation: start with allow all, then switch to deny all") 589 // part 1) allow all 590 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-all-mutate-to-deny-all", map[string]string{}, SetSpecIngressRules(networkingv1.NetworkPolicyIngressRule{})) 591 protocols := []v1.Protocol{protocolTCP} 592 ports := []int32{81} 593 k8s = initializeResources(ctx, f, protocols, ports) 594 nsX, _, _ := getK8sNamespaces(k8s) 595 CreatePolicy(ctx, k8s, policy, nsX) 596 597 reachability := NewReachability(k8s.AllPodStrings(), true) 598 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachability}) 599 600 // part 2) update the policy to deny all 601 policy.Spec.Ingress = []networkingv1.NetworkPolicyIngressRule{} 602 UpdatePolicy(ctx, k8s, policy, nsX) 603 604 reachabilityDeny := NewReachability(k8s.AllPodStrings(), true) 605 reachabilityDeny.ExpectPeer(&Peer{}, &Peer{Namespace: nsX}, false) 606 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachabilityDeny}) 607 }) 608 609 f.It("should allow ingress access from updated namespace", feature.NetworkPolicy, func(ctx context.Context) { 610 protocols := []v1.Protocol{protocolTCP} 611 ports := []int32{80} 612 k8s = initializeResources(ctx, f, protocols, ports) 613 nsX, nsY, _ := getK8sNamespaces(k8s) 614 ginkgo.DeferCleanup(DeleteNamespaceLabel, k8s, nsY, "ns2") 615 616 allowedLabels := &metav1.LabelSelector{ 617 MatchLabels: map[string]string{ 618 "ns2": "updated", 619 }, 620 } 621 ingressRule := networkingv1.NetworkPolicyIngressRule{} 622 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedLabels}) 623 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-client-a-via-ns-selector", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 624 CreatePolicy(ctx, k8s, policy, nsX) 625 626 reachability := NewReachability(k8s.AllPodStrings(), true) 627 reachability.ExpectAllIngress(NewPodString(nsX, "a"), false) 628 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 629 630 // add a new label 631 AddNamespaceLabel(ctx, k8s, nsY, "ns2", "updated") 632 633 // anything from namespace 'y' should be able to get to x/a 634 reachabilityWithLabel := NewReachability(k8s.AllPodStrings(), true) 635 reachabilityWithLabel.ExpectAllIngress(NewPodString(nsX, "a"), false) 636 reachabilityWithLabel.ExpectPeer(&Peer{Namespace: nsY}, &Peer{}, true) 637 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachabilityWithLabel}) 638 }) 639 640 f.It("should allow ingress access from updated pod", feature.NetworkPolicy, func(ctx context.Context) { 641 protocols := []v1.Protocol{protocolTCP} 642 ports := []int32{80} 643 k8s = initializeResources(ctx, f, protocols, ports) 644 nsX, _, _ := getK8sNamespaces(k8s) 645 ginkgo.DeferCleanup(ResetPodLabels, k8s, nsX, "b") 646 647 // add a new label 648 matchLabels := map[string]string{"pod": "b", "pod2": "updated"} 649 allowedLabels := &metav1.LabelSelector{MatchLabels: matchLabels} 650 ingressRule := networkingv1.NetworkPolicyIngressRule{} 651 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{PodSelector: allowedLabels}) 652 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-client-a-via-pod-selector", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 653 CreatePolicy(ctx, k8s, policy, nsX) 654 655 reachability := NewReachability(k8s.AllPodStrings(), true) 656 reachability.ExpectAllIngress(NewPodString(nsX, "a"), false) 657 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 658 659 AddPodLabels(ctx, k8s, nsX, "b", matchLabels) 660 661 ginkgo.By("x/b is able to reach x/a when label is updated") 662 663 reachabilityWithLabel := NewReachability(k8s.AllPodStrings(), true) 664 reachabilityWithLabel.ExpectAllIngress(NewPodString(nsX, "a"), false) 665 reachabilityWithLabel.Expect(NewPodString(nsX, "b"), NewPodString(nsX, "a"), true) 666 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachabilityWithLabel}) 667 }) 668 669 f.It("should deny ingress from pods on other namespaces", feature.NetworkPolicy, func(ctx context.Context) { 670 protocols := []v1.Protocol{protocolTCP} 671 ports := []int32{80} 672 k8s = initializeResources(ctx, f, protocols, ports) 673 nsX, nsY, nsZ := getK8sNamespaces(k8s) 674 IngressRules := networkingv1.NetworkPolicyIngressRule{} 675 IngressRules.From = append(IngressRules.From, networkingv1.NetworkPolicyPeer{PodSelector: &metav1.LabelSelector{MatchLabels: map[string]string{}}}) 676 policy := GenNetworkPolicyWithNameAndPodSelector("deny-empty-policy", metav1.LabelSelector{}, SetSpecIngressRules(IngressRules)) 677 CreatePolicy(ctx, k8s, policy, nsX) 678 679 reachability := NewReachability(k8s.AllPodStrings(), true) 680 reachability.ExpectPeer(&Peer{Namespace: nsY}, &Peer{Namespace: nsX}, false) 681 reachability.ExpectPeer(&Peer{Namespace: nsZ}, &Peer{Namespace: nsX}, false) 682 683 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 684 }) 685 686 f.It("should deny ingress access to updated pod", feature.NetworkPolicy, func(ctx context.Context) { 687 protocols := []v1.Protocol{protocolTCP} 688 ports := []int32{80} 689 k8s = initializeResources(ctx, f, protocols, ports) 690 nsX, _, _ := getK8sNamespaces(k8s) 691 ginkgo.DeferCleanup(ResetPodLabels, k8s, nsX, "a") 692 693 policy := GenNetworkPolicyWithNameAndPodSelector("deny-ingress-via-label-selector", 694 metav1.LabelSelector{MatchLabels: map[string]string{"target": "isolated"}}, SetSpecIngressRules()) 695 CreatePolicy(ctx, k8s, policy, nsX) 696 697 ginkgo.By("Verify that everything can reach x/a") 698 reachability := NewReachability(k8s.AllPodStrings(), true) 699 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 700 701 AddPodLabels(ctx, k8s, nsX, "a", map[string]string{"target": "isolated"}) 702 703 reachabilityIsolated := NewReachability(k8s.AllPodStrings(), true) 704 reachabilityIsolated.ExpectAllIngress(NewPodString(nsX, "a"), false) 705 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachabilityIsolated}) 706 }) 707 708 f.It("should deny egress from pods based on PodSelector", feature.NetworkPolicy, func(ctx context.Context) { 709 protocols := []v1.Protocol{protocolTCP} 710 ports := []int32{80} 711 k8s = initializeResources(ctx, f, protocols, ports) 712 nsX, _, _ := getK8sNamespaces(k8s) 713 policy := GenNetworkPolicyWithNameAndPodSelector("deny-egress-pod-a", metav1.LabelSelector{MatchLabels: map[string]string{"pod": "a"}}, SetSpecEgressRules()) 714 CreatePolicy(ctx, k8s, policy, nsX) 715 716 reachability := NewReachability(k8s.AllPodStrings(), true) 717 reachability.ExpectAllEgress(NewPodString(nsX, "a"), false) 718 719 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 720 }) 721 722 f.It("should deny egress from all pods in a namespace", feature.NetworkPolicy, func(ctx context.Context) { 723 protocols := []v1.Protocol{protocolTCP} 724 ports := []int32{80} 725 k8s = initializeResources(ctx, f, protocols, ports) 726 nsX, _, _ := getK8sNamespaces(k8s) 727 policy := GenNetworkPolicyWithNameAndPodSelector("deny-egress-ns-x", metav1.LabelSelector{}, SetSpecEgressRules()) 728 CreatePolicy(ctx, k8s, policy, nsX) 729 730 reachability := NewReachability(k8s.AllPodStrings(), true) 731 reachability.ExpectPeer(&Peer{Namespace: nsX}, &Peer{}, false) 732 733 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 734 }) 735 736 f.It("should work with Ingress, Egress specified together", feature.NetworkPolicy, func(ctx context.Context) { 737 allowedPodLabels := &metav1.LabelSelector{MatchLabels: map[string]string{"pod": "b"}} 738 ingressRule := networkingv1.NetworkPolicyIngressRule{} 739 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{PodSelector: allowedPodLabels}) 740 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-client-a-via-pod-selector", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 741 742 // add an egress rule on to it... 743 policy.Spec.Egress = []networkingv1.NetworkPolicyEgressRule{ 744 { 745 Ports: []networkingv1.NetworkPolicyPort{ 746 { 747 // don't use named ports 748 Port: &intstr.IntOrString{Type: intstr.Int, IntVal: 80}, 749 }, 750 }, 751 }, 752 } 753 policy.Spec.PolicyTypes = []networkingv1.PolicyType{networkingv1.PolicyTypeEgress, networkingv1.PolicyTypeIngress} 754 protocols := []v1.Protocol{protocolTCP} 755 ports := []int32{80, 81} 756 k8s = initializeResources(ctx, f, protocols, ports) 757 nsX, _, _ := getK8sNamespaces(k8s) 758 CreatePolicy(ctx, k8s, policy, nsX) 759 760 reachabilityPort80 := NewReachability(k8s.AllPodStrings(), true) 761 reachabilityPort80.ExpectAllIngress(NewPodString(nsX, "a"), false) 762 reachabilityPort80.Expect(NewPodString(nsX, "b"), NewPodString(nsX, "a"), true) 763 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachabilityPort80}) 764 765 ginkgo.By("validating that port 81 doesn't work") 766 // meanwhile no egress traffic on 81 should work, since our egress policy is on 80 767 reachabilityPort81 := NewReachability(k8s.AllPodStrings(), true) 768 reachabilityPort81.ExpectAllIngress(NewPodString(nsX, "a"), false) 769 reachabilityPort81.ExpectAllEgress(NewPodString(nsX, "a"), false) 770 reachabilityPort81.Expect(NewPodString(nsX, "b"), NewPodString(nsX, "a"), true) 771 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachabilityPort81}) 772 }) 773 774 f.It("should support denying of egress traffic on the client side (even if the server explicitly allows this traffic)", feature.NetworkPolicy, func(ctx context.Context) { 775 // x/a --> y/a and y/b 776 // Egress allowed to y/a only. Egress to y/b should be blocked 777 // Ingress on y/a and y/b allow traffic from x/a 778 // Expectation: traffic from x/a to y/a allowed only, traffic from x/a to y/b denied by egress policy 779 780 protocols := []v1.Protocol{protocolTCP} 781 ports := []int32{80} 782 k8s = initializeResources(ctx, f, protocols, ports) 783 nsX, nsY, _ := getK8sNamespaces(k8s) 784 785 // Building egress policy for x/a to y/a only 786 allowedEgressNamespaces := &metav1.LabelSelector{ 787 MatchLabels: map[string]string{ 788 namespaceLabelKey: nsY, 789 }, 790 } 791 allowedEgressPods := &metav1.LabelSelector{ 792 MatchLabels: map[string]string{ 793 "pod": "a", 794 }, 795 } 796 egressRule1 := networkingv1.NetworkPolicyEgressRule{} 797 egressRule1.To = append(egressRule1.To, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedEgressNamespaces, PodSelector: allowedEgressPods}) 798 egressPolicy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-to-ns-y-pod-a", map[string]string{"pod": "a"}, SetSpecEgressRules(egressRule1)) 799 CreatePolicy(ctx, k8s, egressPolicy, nsX) 800 801 // Creating ingress policy to allow from x/a to y/a and y/b 802 allowedIngressNamespaces := &metav1.LabelSelector{ 803 MatchLabels: map[string]string{ 804 namespaceLabelKey: nsX, 805 }, 806 } 807 allowedIngressPods := &metav1.LabelSelector{ 808 MatchLabels: map[string]string{ 809 "pod": "a", 810 }, 811 } 812 ingressRule := networkingv1.NetworkPolicyIngressRule{} 813 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedIngressNamespaces, PodSelector: allowedIngressPods}) 814 allowIngressPolicyPodA := GenNetworkPolicyWithNameAndPodMatchLabel("allow-from-xa-on-ya-match-selector", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 815 allowIngressPolicyPodB := GenNetworkPolicyWithNameAndPodMatchLabel("allow-from-xa-on-yb-match-selector", map[string]string{"pod": "b"}, SetSpecIngressRules(ingressRule)) 816 817 CreatePolicy(ctx, k8s, allowIngressPolicyPodA, nsY) 818 CreatePolicy(ctx, k8s, allowIngressPolicyPodB, nsY) 819 820 // While applying the policies, traffic needs to be allowed by both egress and ingress rules. 821 // Egress rules only 822 // xa xb xc ya yb yc za zb zc 823 // xa X X X . *X* X X X X 824 // xb . . . . . . . . . 825 // xc . . . . . . . . . 826 // ya . . . . . . . . . 827 // yb . . . . . . . . . 828 // yc . . . . . . . . . 829 // za . . . . . . . . . 830 // zb . . . . . . . . . 831 // zc . . . . . . . . . 832 // Ingress rules only 833 // xa xb xc ya yb yc za zb zc 834 // xa . . . *.* . . . . . 835 // xb . . X X . . . . . 836 // xc . . X X . . . . . 837 // ya . . X X . . . . . 838 // yb . . X X . . . . . 839 // yc . . X X . . . . . 840 // za . . X X . . . . . 841 // zb . . X X . . . . . 842 // zc . . X X . . . . . 843 // In the resulting truth table, connections from x/a should only be allowed to y/a. x/a to y/b should be blocked by the egress on x/a. 844 // Expected results 845 // xa xb xc ya yb yc za zb zc 846 // xa X X X . *X* X X X X 847 // xb . . . X X . . . . 848 // xc . . . X X . . . . 849 // ya . . . X X . . . . 850 // yb . . . X X . . . . 851 // yc . . . X X . . . . 852 // za . . . X X . . . . 853 // zb . . . X X . . . . 854 // zc . . . X X . . . . 855 856 reachability := NewReachability(k8s.AllPodStrings(), true) 857 // Default all traffic flows. 858 // Exception: x/a can only egress to y/a, others are false 859 // Exception: y/a can only allow ingress from x/a, others are false 860 // Exception: y/b has no allowed traffic (due to limit on x/a egress) 861 862 reachability.ExpectPeer(&Peer{Namespace: nsX, Pod: "a"}, &Peer{}, false) 863 reachability.ExpectPeer(&Peer{}, &Peer{Namespace: nsY, Pod: "a"}, false) 864 reachability.ExpectPeer(&Peer{Namespace: nsX, Pod: "a"}, &Peer{Namespace: nsY, Pod: "a"}, true) 865 reachability.ExpectPeer(&Peer{}, &Peer{Namespace: nsY, Pod: "b"}, false) 866 867 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 868 }) 869 870 f.It("should enforce egress policy allowing traffic to a server in a different namespace based on PodSelector and NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) { 871 protocols := []v1.Protocol{protocolTCP} 872 ports := []int32{80} 873 k8s = initializeResources(ctx, f, protocols, ports) 874 nsX, nsY, _ := getK8sNamespaces(k8s) 875 allowedNamespaces := &metav1.LabelSelector{ 876 MatchLabels: map[string]string{ 877 namespaceLabelKey: nsY, 878 }, 879 } 880 allowedPods := &metav1.LabelSelector{ 881 MatchLabels: map[string]string{ 882 "pod": "a", 883 }, 884 } 885 egressRule1 := networkingv1.NetworkPolicyEgressRule{} 886 egressRule1.To = append(egressRule1.To, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedNamespaces, PodSelector: allowedPods}) 887 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-to-ns-y-pod-a", map[string]string{"pod": "a"}, SetSpecEgressRules(egressRule1)) 888 CreatePolicy(ctx, k8s, policy, nsX) 889 890 reachability := NewReachability(k8s.AllPodStrings(), true) 891 reachability.ExpectAllEgress(NewPodString(nsX, "a"), false) 892 reachability.Expect(NewPodString(nsX, "a"), NewPodString(nsY, "a"), true) 893 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 894 }) 895 896 f.It("should enforce ingress policy allowing any port traffic to a server on a specific protocol", feature.NetworkPolicy, feature.UDP, func(ctx context.Context) { 897 protocols := []v1.Protocol{protocolTCP, protocolUDP} 898 ports := []int32{80} 899 k8s = initializeResources(ctx, f, protocols, ports) 900 nsX, _, _ := getK8sNamespaces(k8s) 901 ingressRule := networkingv1.NetworkPolicyIngressRule{} 902 ingressRule.Ports = append(ingressRule.Ports, networkingv1.NetworkPolicyPort{Protocol: &protocolTCP}) 903 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ingress-by-proto", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 904 CreatePolicy(ctx, k8s, policy, nsX) 905 906 reachabilityTCP := NewReachability(k8s.AllPodStrings(), true) 907 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachabilityTCP}) 908 909 reachabilityUDP := NewReachability(k8s.AllPodStrings(), true) 910 reachabilityUDP.ExpectPeer(&Peer{}, &Peer{Namespace: nsX, Pod: "a"}, false) 911 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolUDP, Reachability: reachabilityUDP}) 912 }) 913 914 f.It("should enforce multiple ingress policies with ingress allow-all policy taking precedence", feature.NetworkPolicy, func(ctx context.Context) { 915 protocols := []v1.Protocol{protocolTCP} 916 ports := []int32{81} 917 k8s = initializeResources(ctx, f, protocols, ports) 918 nsX, _, _ := getK8sNamespaces(k8s) 919 IngressRules := networkingv1.NetworkPolicyIngressRule{} 920 IngressRules.Ports = append(IngressRules.Ports, networkingv1.NetworkPolicyPort{Port: &intstr.IntOrString{Type: intstr.Int, IntVal: 80}}) 921 policyAllowOnlyPort80 := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ingress-port-80", map[string]string{}, SetSpecIngressRules(IngressRules)) 922 CreatePolicy(ctx, k8s, policyAllowOnlyPort80, nsX) 923 924 ginkgo.By("The policy targets port 80 -- so let's make sure traffic on port 81 is blocked") 925 926 reachability := NewReachability(k8s.AllPodStrings(), true) 927 reachability.ExpectPeer(&Peer{}, &Peer{Namespace: nsX}, false) 928 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachability}) 929 930 ginkgo.By("Allowing all ports") 931 932 policyAllowAll := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ingress", map[string]string{}, SetSpecIngressRules(networkingv1.NetworkPolicyIngressRule{})) 933 CreatePolicy(ctx, k8s, policyAllowAll, nsX) 934 935 reachabilityAll := NewReachability(k8s.AllPodStrings(), true) 936 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachabilityAll}) 937 }) 938 939 f.It("should enforce multiple egress policies with egress allow-all policy taking precedence", feature.NetworkPolicy, func(ctx context.Context) { 940 egressRule := networkingv1.NetworkPolicyEgressRule{} 941 egressRule.Ports = append(egressRule.Ports, networkingv1.NetworkPolicyPort{Port: &intstr.IntOrString{Type: intstr.Int, IntVal: 80}}) 942 policyAllowPort80 := GenNetworkPolicyWithNameAndPodMatchLabel("allow-egress-port-80", map[string]string{}, SetSpecEgressRules(egressRule)) 943 protocols := []v1.Protocol{protocolTCP} 944 ports := []int32{81} 945 k8s = initializeResources(ctx, f, protocols, ports) 946 nsX, _, _ := getK8sNamespaces(k8s) 947 CreatePolicy(ctx, k8s, policyAllowPort80, nsX) 948 949 ginkgo.By("Making sure ingress doesn't work other than port 80") 950 951 reachability := NewReachability(k8s.AllPodStrings(), true) 952 reachability.ExpectPeer(&Peer{Namespace: nsX}, &Peer{}, false) 953 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachability}) 954 955 ginkgo.By("Allowing all ports") 956 957 policyAllowAll := GenNetworkPolicyWithNameAndPodMatchLabel("allow-egress", map[string]string{}, SetSpecEgressRules(networkingv1.NetworkPolicyEgressRule{})) 958 CreatePolicy(ctx, k8s, policyAllowAll, nsX) 959 960 reachabilityAll := NewReachability(k8s.AllPodStrings(), true) 961 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachabilityAll}) 962 }) 963 964 f.It("should stop enforcing policies after they are deleted", feature.NetworkPolicy, func(ctx context.Context) { 965 ginkgo.By("Creating a network policy for the server which denies all traffic.") 966 967 // Deny all traffic into and out of "x". 968 policy := GenNetworkPolicyWithNameAndPodSelector("deny-all", metav1.LabelSelector{}, SetSpecIngressRules(), SetSpecEgressRules()) 969 protocols := []v1.Protocol{protocolTCP} 970 ports := []int32{80} 971 k8s = initializeResources(ctx, f, protocols, ports) 972 nsX, _, _ := getK8sNamespaces(k8s) 973 CreatePolicy(ctx, k8s, policy, nsX) 974 reachability := NewReachability(k8s.AllPodStrings(), true) 975 976 // Expect all traffic into, and out of "x" to be False. 977 reachability.ExpectPeer(&Peer{Namespace: nsX}, &Peer{}, false) 978 reachability.ExpectPeer(&Peer{}, &Peer{Namespace: nsX}, false) 979 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 980 981 err := k8s.cleanNetworkPolicies(ctx) 982 time.Sleep(3 * time.Second) // TODO we can remove this eventually, its just a hack to keep CI stable. 983 framework.ExpectNoError(err, "unable to clean network policies") 984 985 // Now the policy is deleted, we expect all connectivity to work again. 986 reachabilityAll := NewReachability(k8s.AllPodStrings(), true) 987 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachabilityAll}) 988 }) 989 990 // TODO, figure out how the next 3 tests should work with dual stack : do we need a different abstraction then just "podIP"? 991 992 f.It("should allow egress access to server in CIDR block", feature.NetworkPolicy, func(ctx context.Context) { 993 // Getting podServer's status to get podServer's IP, to create the CIDR 994 protocols := []v1.Protocol{protocolTCP} 995 ports := []int32{80} 996 k8s = initializeResources(ctx, f, protocols, ports) 997 nsX, nsY, _ := getK8sNamespaces(k8s) 998 podList, err := f.ClientSet.CoreV1().Pods(nsY).List(ctx, metav1.ListOptions{LabelSelector: "pod=b"}) 999 framework.ExpectNoError(err, "Failing to list pods in namespace y") 1000 pod := podList.Items[0] 1001 1002 hostMask := 32 1003 if utilnet.IsIPv6String(pod.Status.PodIP) { 1004 hostMask = 128 1005 } 1006 podServerCIDR := fmt.Sprintf("%s/%d", pod.Status.PodIP, hostMask) 1007 egressRule1 := networkingv1.NetworkPolicyEgressRule{} 1008 egressRule1.To = append(egressRule1.To, networkingv1.NetworkPolicyPeer{IPBlock: &networkingv1.IPBlock{CIDR: podServerCIDR}}) 1009 policyAllowCIDR := GenNetworkPolicyWithNameAndPodMatchLabel("allow-client-a-via-cidr-egress-rule", 1010 map[string]string{"pod": "a"}, SetSpecEgressRules(egressRule1)) 1011 CreatePolicy(ctx, k8s, policyAllowCIDR, nsX) 1012 1013 reachability := NewReachability(k8s.AllPodStrings(), true) 1014 reachability.ExpectAllEgress(NewPodString(nsX, "a"), false) 1015 reachability.Expect(NewPodString(nsX, "a"), NewPodString(nsY, "b"), true) 1016 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 1017 }) 1018 1019 f.It("should enforce except clause while egress access to server in CIDR block", feature.NetworkPolicy, func(ctx context.Context) { 1020 // Getting podServer's status to get podServer's IP, to create the CIDR with except clause 1021 protocols := []v1.Protocol{protocolTCP} 1022 ports := []int32{80} 1023 k8s = initializeResources(ctx, f, protocols, ports) 1024 nsX, _, _ := getK8sNamespaces(k8s) 1025 podList, err := f.ClientSet.CoreV1().Pods(nsX).List(ctx, metav1.ListOptions{LabelSelector: "pod=a"}) 1026 framework.ExpectNoError(err, "Failing to find pod x/a") 1027 podA := podList.Items[0] 1028 1029 podServerAllowCIDR := fmt.Sprintf("%s/4", podA.Status.PodIP) 1030 1031 podList, err = f.ClientSet.CoreV1().Pods(nsX).List(ctx, metav1.ListOptions{LabelSelector: "pod=b"}) 1032 framework.ExpectNoError(err, "Failing to find pod x/b") 1033 podB := podList.Items[0] 1034 1035 hostMask := 32 1036 if utilnet.IsIPv6String(podB.Status.PodIP) { 1037 hostMask = 128 1038 } 1039 podServerExceptList := []string{fmt.Sprintf("%s/%d", podB.Status.PodIP, hostMask)} 1040 1041 egressRule1 := networkingv1.NetworkPolicyEgressRule{} 1042 egressRule1.To = append(egressRule1.To, networkingv1.NetworkPolicyPeer{IPBlock: &networkingv1.IPBlock{CIDR: podServerAllowCIDR, Except: podServerExceptList}}) 1043 policyAllowCIDR := GenNetworkPolicyWithNameAndPodMatchLabel("allow-client-a-via-cidr-egress-rule", map[string]string{"pod": "a"}, SetSpecEgressRules(egressRule1)) 1044 1045 CreatePolicy(ctx, k8s, policyAllowCIDR, nsX) 1046 1047 reachability := NewReachability(k8s.AllPodStrings(), true) 1048 reachability.Expect(NewPodString(nsX, "a"), NewPodString(nsX, "b"), false) 1049 1050 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 1051 }) 1052 1053 f.It("should ensure an IP overlapping both IPBlock.CIDR and IPBlock.Except is allowed", feature.NetworkPolicy, func(ctx context.Context) { 1054 // Getting podServer's status to get podServer's IP, to create the CIDR with except clause 1055 protocols := []v1.Protocol{protocolTCP} 1056 ports := []int32{80} 1057 k8s = initializeResources(ctx, f, protocols, ports) 1058 nsX, _, _ := getK8sNamespaces(k8s) 1059 podList, err := f.ClientSet.CoreV1().Pods(nsX).List(ctx, metav1.ListOptions{LabelSelector: "pod=a"}) 1060 framework.ExpectNoError(err, "Failing to find pod x/a") 1061 podA := podList.Items[0] 1062 1063 podList, err = f.ClientSet.CoreV1().Pods(nsX).List(ctx, metav1.ListOptions{LabelSelector: "pod=b"}) 1064 framework.ExpectNoError(err, "Failing to find pod x/b") 1065 podB := podList.Items[0] 1066 1067 // Exclude podServer's IP with an Except clause 1068 hostMask := 32 1069 if utilnet.IsIPv6String(podB.Status.PodIP) { 1070 hostMask = 128 1071 } 1072 1073 podServerAllowCIDR := fmt.Sprintf("%s/4", podA.Status.PodIP) 1074 podServerExceptList := []string{fmt.Sprintf("%s/%d", podB.Status.PodIP, hostMask)} 1075 egressRule1 := networkingv1.NetworkPolicyEgressRule{} 1076 egressRule1.To = append(egressRule1.To, networkingv1.NetworkPolicyPeer{IPBlock: &networkingv1.IPBlock{CIDR: podServerAllowCIDR, Except: podServerExceptList}}) 1077 policyAllowCIDR := GenNetworkPolicyWithNameAndPodMatchLabel("allow-client-a-via-cidr-egress-rule", 1078 map[string]string{"pod": "a"}, SetSpecEgressRules(egressRule1)) 1079 CreatePolicy(ctx, k8s, policyAllowCIDR, nsX) 1080 1081 reachability := NewReachability(k8s.AllPodStrings(), true) 1082 reachability.Expect(NewPodString(nsX, "a"), NewPodString(nsX, "b"), false) 1083 1084 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 1085 1086 podBIP := fmt.Sprintf("%s/%d", podB.Status.PodIP, hostMask) 1087 //// Create NetworkPolicy which allows access to the podServer using podServer's IP in allow CIDR. 1088 egressRule3 := networkingv1.NetworkPolicyEgressRule{} 1089 egressRule3.To = append(egressRule3.To, networkingv1.NetworkPolicyPeer{IPBlock: &networkingv1.IPBlock{CIDR: podBIP}}) 1090 allowPolicy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-client-a-via-cidr-egress-rule", 1091 map[string]string{"pod": "a"}, SetSpecEgressRules(egressRule3)) 1092 // SHOULD THIS BE UPDATE OR CREATE JAY TESTING 10/31 1093 UpdatePolicy(ctx, k8s, allowPolicy, nsX) 1094 1095 reachabilityAllow := NewReachability(k8s.AllPodStrings(), true) 1096 reachabilityAllow.ExpectAllEgress(NewPodString(nsX, "a"), false) 1097 reachabilityAllow.Expect(NewPodString(nsX, "a"), NewPodString(nsX, "b"), true) 1098 1099 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachabilityAllow}) 1100 }) 1101 1102 f.It("should enforce policies to check ingress and egress policies can be controlled independently based on PodSelector", feature.NetworkPolicy, func(ctx context.Context) { 1103 /* 1104 Test steps: 1105 1. Verify every pod in every namespace can talk to each other 1106 - including a -> b and b -> a 1107 2. Create a policy to allow egress a -> b (target = a) 1108 3. Create a policy to *deny* ingress b -> a (target = a) 1109 4. Verify a -> b allowed; b -> a blocked 1110 */ 1111 targetLabels := map[string]string{"pod": "a"} 1112 1113 ginkgo.By("Creating a network policy for pod-a which allows Egress traffic to pod-b.") 1114 1115 allowEgressPolicy := GenNetworkPolicyWithNameAndPodSelector("allow-egress-for-target", 1116 metav1.LabelSelector{MatchLabels: targetLabels}, SetSpecEgressRules(networkingv1.NetworkPolicyEgressRule{})) 1117 protocols := []v1.Protocol{protocolTCP} 1118 ports := []int32{80} 1119 k8s = initializeResources(ctx, f, protocols, ports) 1120 nsX, _, _ := getK8sNamespaces(k8s) 1121 CreatePolicy(ctx, k8s, allowEgressPolicy, nsX) 1122 1123 allowEgressReachability := NewReachability(k8s.AllPodStrings(), true) 1124 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: allowEgressReachability}) 1125 1126 ginkgo.By("Creating a network policy for pod-a that denies traffic from pod-b.") 1127 1128 denyAllIngressPolicy := GenNetworkPolicyWithNameAndPodSelector("deny-ingress-via-label-selector", metav1.LabelSelector{MatchLabels: targetLabels}, SetSpecIngressRules()) 1129 CreatePolicy(ctx, k8s, denyAllIngressPolicy, nsX) 1130 1131 denyIngressToXReachability := NewReachability(k8s.AllPodStrings(), true) 1132 denyIngressToXReachability.ExpectAllIngress(NewPodString(nsX, "a"), false) 1133 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: denyIngressToXReachability}) 1134 }) 1135 1136 // This test *does* apply to plugins that do not implement SCTP. It is a 1137 // security hole if you fail this test, because you are allowing TCP 1138 // traffic that is supposed to be blocked. 1139 f.It("should not mistakenly treat 'protocol: SCTP' as 'protocol: TCP', even if the plugin doesn't support SCTP", feature.NetworkPolicy, func(ctx context.Context) { 1140 protocols := []v1.Protocol{protocolTCP} 1141 ports := []int32{81} 1142 k8s = initializeResources(ctx, f, protocols, ports) 1143 nsX, _, _ := getK8sNamespaces(k8s) 1144 1145 ginkgo.By("Creating a default-deny ingress policy.") 1146 // Empty podSelector blocks the entire namespace 1147 policy := GenNetworkPolicyWithNameAndPodSelector("deny-ingress", metav1.LabelSelector{}, SetSpecIngressRules()) 1148 CreatePolicy(ctx, k8s, policy, nsX) 1149 1150 ginkgo.By("Creating a network policy for the server which allows traffic only via SCTP on port 81.") 1151 ingressRule := networkingv1.NetworkPolicyIngressRule{} 1152 ingressRule.Ports = append(ingressRule.Ports, networkingv1.NetworkPolicyPort{Port: &intstr.IntOrString{IntVal: 81}, Protocol: &protocolSCTP}) 1153 policy = GenNetworkPolicyWithNameAndPodMatchLabel("allow-only-sctp-ingress-on-port-81", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 1154 CreatePolicy(ctx, k8s, policy, nsX) 1155 1156 ginkgo.By("Trying to connect to TCP port 81, which should be blocked by the deny-ingress policy.") 1157 reachability := NewReachability(k8s.AllPodStrings(), true) 1158 reachability.ExpectPeer(&Peer{}, &Peer{Namespace: nsX}, false) 1159 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachability}) 1160 }) 1161 1162 // This test *does* apply to plugins that do not implement SCTP. It is a 1163 // security hole if you fail this test, because you are allowing TCP 1164 // traffic that is supposed to be blocked. 1165 f.It("should properly isolate pods that are selected by a policy allowing SCTP, even if the plugin doesn't support SCTP", feature.NetworkPolicy, func(ctx context.Context) { 1166 ginkgo.By("Creating a network policy for the server which allows traffic only via SCTP on port 80.") 1167 ingressRule := networkingv1.NetworkPolicyIngressRule{} 1168 ingressRule.Ports = append(ingressRule.Ports, networkingv1.NetworkPolicyPort{Port: &intstr.IntOrString{IntVal: 80}, Protocol: &protocolSCTP}) 1169 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-only-sctp-ingress-on-port-80", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 1170 protocols := []v1.Protocol{protocolTCP} 1171 ports := []int32{81} 1172 k8s = initializeResources(ctx, f, protocols, ports) 1173 nsX, _, _ := getK8sNamespaces(k8s) 1174 CreatePolicy(ctx, k8s, policy, nsX) 1175 1176 ginkgo.By("Trying to connect to TCP port 81, which should be blocked by implicit isolation.") 1177 reachability := NewReachability(k8s.AllPodStrings(), true) 1178 reachability.ExpectAllIngress(NewPodString(nsX, "a"), false) 1179 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachability}) 1180 }) 1181 1182 f.It("should not allow access by TCP when a policy specifies only UDP", feature.NetworkPolicy, func(ctx context.Context) { 1183 ingressRule := networkingv1.NetworkPolicyIngressRule{} 1184 ingressRule.Ports = append(ingressRule.Ports, networkingv1.NetworkPolicyPort{Port: &intstr.IntOrString{IntVal: 81}, Protocol: &protocolUDP}) 1185 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-only-udp-ingress-on-port-81", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 1186 protocols := []v1.Protocol{protocolTCP} 1187 ports := []int32{81} 1188 k8s = initializeResources(ctx, f, protocols, ports) 1189 nsX, _, _ := getK8sNamespaces(k8s) 1190 CreatePolicy(ctx, k8s, policy, nsX) 1191 1192 ginkgo.By("Creating a network policy for the server which allows traffic only via UDP on port 81.") 1193 1194 // Probing with TCP, so all traffic should be dropped. 1195 reachability := NewReachability(k8s.AllPodStrings(), true) 1196 reachability.ExpectAllIngress(NewPodString(nsX, "a"), false) 1197 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolTCP, Reachability: reachability}) 1198 }) 1199 1200 // Note that this default ns functionality is maintained by the APIMachinery group, but we test it here anyways because its an important feature. 1201 f.It("should enforce policy to allow traffic based on NamespaceSelector with MatchLabels using default ns label", feature.NetworkPolicy, func(ctx context.Context) { 1202 protocols := []v1.Protocol{protocolTCP} 1203 ports := []int32{80} 1204 k8s = initializeResources(ctx, f, protocols, ports) 1205 nsX, nsY, nsZ := getK8sNamespaces(k8s) 1206 allowedLabels := &metav1.LabelSelector{ 1207 MatchLabels: map[string]string{ 1208 v1.LabelMetadataName: nsY, 1209 }, 1210 } 1211 ingressRule := networkingv1.NetworkPolicyIngressRule{} 1212 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedLabels}) 1213 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-client-a-via-ns-selector-for-immutable-ns-label", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 1214 CreatePolicy(ctx, k8s, policy, nsX) 1215 1216 reachability := NewReachability(k8s.AllPodStrings(), true) 1217 reachability.ExpectPeer(&Peer{Namespace: nsX}, &Peer{Namespace: nsX, Pod: "a"}, false) 1218 reachability.ExpectPeer(&Peer{Namespace: nsZ}, &Peer{Namespace: nsX, Pod: "a"}, false) 1219 1220 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 1221 }) 1222 1223 // Note that this default ns functionality is maintained by the APIMachinery group, but we test it here anyways because its an important feature. 1224 f.It("should enforce policy based on NamespaceSelector with MatchExpressions using default ns label", feature.NetworkPolicy, func(ctx context.Context) { 1225 protocols := []v1.Protocol{protocolTCP} 1226 ports := []int32{80} 1227 k8s = initializeResources(ctx, f, protocols, ports) 1228 nsX, nsY, _ := getK8sNamespaces(k8s) 1229 allowedNamespaces := &metav1.LabelSelector{ 1230 MatchExpressions: []metav1.LabelSelectorRequirement{{ 1231 Key: v1.LabelMetadataName, 1232 Operator: metav1.LabelSelectorOpNotIn, 1233 Values: []string{nsY}, 1234 }}, 1235 } 1236 egressRule := networkingv1.NetworkPolicyEgressRule{} 1237 egressRule.To = append(egressRule.To, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedNamespaces}) 1238 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ns-y-match-selector-for-immutable-ns-label", map[string]string{"pod": "a"}, SetSpecEgressRules(egressRule)) 1239 CreatePolicy(ctx, k8s, policy, nsX) 1240 1241 reachability := NewReachability(k8s.AllPodStrings(), true) 1242 reachability.ExpectPeer(&Peer{Namespace: nsX, Pod: "a"}, &Peer{Namespace: nsY}, false) 1243 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability}) 1244 }) 1245 }) 1246 }) 1247 1248 var _ = common.SIGDescribe("Netpol [LinuxOnly]", func() { 1249 f := framework.NewDefaultFramework("udp-network-policy") 1250 f.SkipNamespaceCreation = true 1251 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 1252 var k8s *kubeManager 1253 ginkgo.BeforeEach(func() { 1254 // Windows does not support UDP testing via agnhost. 1255 e2eskipper.SkipIfNodeOSDistroIs("windows") 1256 }) 1257 1258 ginkgo.Context("NetworkPolicy between server and client using UDP", func() { 1259 1260 f.It("should support a 'default-deny-ingress' policy", feature.NetworkPolicy, func(ctx context.Context) { 1261 protocols := []v1.Protocol{protocolUDP} 1262 ports := []int32{80} 1263 k8s = initializeResources(ctx, f, protocols, ports) 1264 nsX, _, _ := getK8sNamespaces(k8s) 1265 policy := GenNetworkPolicyWithNameAndPodSelector("deny-all", metav1.LabelSelector{}, SetSpecIngressRules()) 1266 CreatePolicy(ctx, k8s, policy, nsX) 1267 1268 reachability := NewReachability(k8s.AllPodStrings(), true) 1269 reachability.ExpectPeer(&Peer{}, &Peer{Namespace: nsX}, false) 1270 1271 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolUDP, Reachability: reachability}) 1272 }) 1273 1274 f.It("should enforce policy based on Ports", feature.NetworkPolicy, func(ctx context.Context) { 1275 ginkgo.By("Creating a network policy allowPort81Policy which only allows allow listed namespaces (y) to connect on exactly one port (81)") 1276 protocols := []v1.Protocol{protocolUDP} 1277 ports := []int32{81} 1278 k8s = initializeResources(ctx, f, protocols, ports) 1279 nsX, nsY, nsZ := getK8sNamespaces(k8s) 1280 allowedLabels := &metav1.LabelSelector{ 1281 MatchLabels: map[string]string{ 1282 namespaceLabelKey: nsY, 1283 }, 1284 } 1285 1286 ingressRule := networkingv1.NetworkPolicyIngressRule{} 1287 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedLabels}) 1288 ingressRule.Ports = append(ingressRule.Ports, networkingv1.NetworkPolicyPort{Port: &intstr.IntOrString{IntVal: 81}, Protocol: &protocolUDP}) 1289 allowPort81Policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ingress-on-port-81-ns-x", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 1290 CreatePolicy(ctx, k8s, allowPort81Policy, nsX) 1291 1292 reachability := NewReachability(k8s.AllPodStrings(), true) 1293 reachability.ExpectPeer(&Peer{Namespace: nsX}, &Peer{Namespace: nsX, Pod: "a"}, false) 1294 reachability.ExpectPeer(&Peer{Namespace: nsZ}, &Peer{Namespace: nsX, Pod: "a"}, false) 1295 1296 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolUDP, Reachability: reachability}) 1297 }) 1298 1299 f.It("should enforce policy to allow traffic only from a pod in a different namespace based on PodSelector and NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) { 1300 protocols := []v1.Protocol{protocolUDP} 1301 ports := []int32{80} 1302 k8s = initializeResources(ctx, f, protocols, ports) 1303 nsX, nsY, _ := getK8sNamespaces(k8s) 1304 allowedNamespaces := &metav1.LabelSelector{ 1305 MatchLabels: map[string]string{ 1306 namespaceLabelKey: nsY, 1307 }, 1308 } 1309 allowedPods := &metav1.LabelSelector{ 1310 MatchLabels: map[string]string{ 1311 "pod": "a", 1312 }, 1313 } 1314 ingressRule := networkingv1.NetworkPolicyIngressRule{} 1315 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedNamespaces, PodSelector: allowedPods}) 1316 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ns-y-pod-a-via-namespace-pod-selector", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 1317 CreatePolicy(ctx, k8s, policy, nsX) 1318 1319 reachability := NewReachability(k8s.AllPodStrings(), true) 1320 reachability.ExpectAllIngress(NewPodString(nsX, "a"), false) 1321 reachability.Expect(NewPodString(nsY, "a"), NewPodString(nsX, "a"), true) 1322 1323 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolUDP, Reachability: reachability}) 1324 }) 1325 }) 1326 }) 1327 1328 var _ = common.SIGDescribe("Netpol", feature.SCTPConnectivity, "[LinuxOnly]", func() { 1329 f := framework.NewDefaultFramework("sctp-network-policy") 1330 f.SkipNamespaceCreation = true 1331 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 1332 var k8s *kubeManager 1333 ginkgo.BeforeEach(func() { 1334 // Windows does not support network policies. 1335 e2eskipper.SkipIfNodeOSDistroIs("windows") 1336 }) 1337 1338 ginkgo.Context("NetworkPolicy between server and client using SCTP", func() { 1339 1340 f.It("should support a 'default-deny-ingress' policy", feature.NetworkPolicy, func(ctx context.Context) { 1341 protocols := []v1.Protocol{protocolSCTP} 1342 ports := []int32{80} 1343 k8s = initializeResources(ctx, f, protocols, ports) 1344 nsX, _, _ := getK8sNamespaces(k8s) 1345 policy := GenNetworkPolicyWithNameAndPodSelector("deny-all", metav1.LabelSelector{}, SetSpecIngressRules()) 1346 CreatePolicy(ctx, k8s, policy, nsX) 1347 1348 reachability := NewReachability(k8s.AllPodStrings(), true) 1349 reachability.ExpectPeer(&Peer{}, &Peer{Namespace: nsX}, false) 1350 1351 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolSCTP, Reachability: reachability}) 1352 }) 1353 1354 f.It("should enforce policy based on Ports", feature.NetworkPolicy, func(ctx context.Context) { 1355 ginkgo.By("Creating a network allowPort81Policy which only allows allow listed namespaces (y) to connect on exactly one port (81)") 1356 protocols := []v1.Protocol{protocolSCTP} 1357 ports := []int32{81} 1358 k8s = initializeResources(ctx, f, protocols, ports) 1359 nsX, nsY, nsZ := getK8sNamespaces(k8s) 1360 allowedLabels := &metav1.LabelSelector{ 1361 MatchLabels: map[string]string{ 1362 namespaceLabelKey: nsY, 1363 }, 1364 } 1365 ingressRule := networkingv1.NetworkPolicyIngressRule{} 1366 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedLabels}) 1367 ingressRule.Ports = append(ingressRule.Ports, networkingv1.NetworkPolicyPort{Port: &intstr.IntOrString{IntVal: 81}, Protocol: &protocolSCTP}) 1368 allowPort81Policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ingress-on-port-81-ns-x", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 1369 CreatePolicy(ctx, k8s, allowPort81Policy, nsX) 1370 1371 reachability := NewReachability(k8s.AllPodStrings(), true) 1372 reachability.ExpectPeer(&Peer{Namespace: nsX}, &Peer{Namespace: nsX, Pod: "a"}, false) 1373 reachability.ExpectPeer(&Peer{Namespace: nsZ}, &Peer{Namespace: nsX, Pod: "a"}, false) 1374 1375 ValidateOrFail(k8s, &TestCase{ToPort: 81, Protocol: v1.ProtocolSCTP, Reachability: reachability}) 1376 }) 1377 1378 f.It("should enforce policy to allow traffic only from a pod in a different namespace based on PodSelector and NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) { 1379 protocols := []v1.Protocol{protocolSCTP} 1380 ports := []int32{80} 1381 k8s = initializeResources(ctx, f, protocols, ports) 1382 nsX, nsY, _ := getK8sNamespaces(k8s) 1383 allowedNamespaces := &metav1.LabelSelector{ 1384 MatchLabels: map[string]string{ 1385 namespaceLabelKey: nsY, 1386 }, 1387 } 1388 allowedPods := &metav1.LabelSelector{ 1389 MatchLabels: map[string]string{ 1390 "pod": "a", 1391 }, 1392 } 1393 ingressRule := networkingv1.NetworkPolicyIngressRule{} 1394 ingressRule.From = append(ingressRule.From, networkingv1.NetworkPolicyPeer{NamespaceSelector: allowedNamespaces, PodSelector: allowedPods}) 1395 policy := GenNetworkPolicyWithNameAndPodMatchLabel("allow-ns-y-pod-a-via-namespace-pod-selector", map[string]string{"pod": "a"}, SetSpecIngressRules(ingressRule)) 1396 CreatePolicy(ctx, k8s, policy, nsX) 1397 1398 reachability := NewReachability(k8s.AllPodStrings(), true) 1399 reachability.ExpectAllIngress(NewPodString(nsX, "a"), false) 1400 reachability.Expect(NewPodString(nsY, "a"), NewPodString(nsX, "a"), true) 1401 1402 ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolSCTP, Reachability: reachability}) 1403 }) 1404 }) 1405 }) 1406 1407 // getNamespaceBaseNames returns the set of base namespace names used by this test, taking a root ns as input. 1408 // The framework will also append a unique suffix when creating the namespaces. 1409 // This allows tests to run in parallel. 1410 func getNamespaceBaseNames(rootNs string) []string { 1411 if rootNs != "" { 1412 rootNs += "-" 1413 } 1414 nsX := fmt.Sprintf("%sx", rootNs) 1415 nsY := fmt.Sprintf("%sy", rootNs) 1416 nsZ := fmt.Sprintf("%sz", rootNs) 1417 return []string{nsX, nsY, nsZ} 1418 } 1419 1420 // defaultModel creates a new "model" pod system under namespaces (x,y,z) which has pods a, b, and c. Thus resulting in the 1421 // truth table matrix that is identical for all tests, comprising 81 total connections between 9 pods (x/a, x/b, x/c, ..., z/c). 1422 func defaultModel(namespaces []string, protocols []v1.Protocol, ports []int32) *Model { 1423 if framework.NodeOSDistroIs("windows") { 1424 return NewWindowsModel(namespaces, []string{"a", "b", "c"}, ports) 1425 } 1426 return NewModel(namespaces, []string{"a", "b", "c"}, ports, protocols) 1427 } 1428 1429 // getK8sNamespaces returns the 3 actual namespace names. 1430 func getK8sNamespaces(k8s *kubeManager) (string, string, string) { 1431 ns := k8s.NamespaceNames() 1432 return ns[0], ns[1], ns[2] 1433 } 1434 1435 func initializeCluster(ctx context.Context, f *framework.Framework, protocols []v1.Protocol, ports []int32) (*kubeManager, error) { 1436 dnsDomain := framework.TestContext.ClusterDNSDomain 1437 framework.Logf("dns domain: %s", dnsDomain) 1438 1439 k8s := newKubeManager(f, dnsDomain) 1440 rootNs := f.BaseName 1441 namespaceBaseNames := getNamespaceBaseNames(rootNs) 1442 1443 model := defaultModel(namespaceBaseNames, protocols, ports) 1444 1445 framework.Logf("initializing cluster: ensuring namespaces, pods and services exist and are ready") 1446 1447 if err := k8s.initializeClusterFromModel(ctx, model); err != nil { 1448 return nil, err 1449 } 1450 1451 framework.Logf("finished initializing cluster state") 1452 1453 if err := waitForHTTPServers(k8s, model); err != nil { 1454 return nil, err 1455 } 1456 1457 return k8s, nil 1458 } 1459 1460 // initializeResources uses the e2e framework to create all necessary namespace resources, based on the network policy 1461 // model derived from the framework. It then waits for the resources described by the model to be up and running 1462 // (i.e. all pods are ready and running in their namespaces). 1463 func initializeResources(ctx context.Context, f *framework.Framework, protocols []v1.Protocol, ports []int32) *kubeManager { 1464 k8s, err := initializeCluster(ctx, f, protocols, ports) 1465 framework.ExpectNoError(err, "unable to initialize resources") 1466 return k8s 1467 }