diff --git a/checkov/kubernetes/graph_builder/graph_components/edge_builders/NetworkPolicyEdgeBuilder.py b/checkov/kubernetes/graph_builder/graph_components/edge_builders/NetworkPolicyEdgeBuilder.py index 8dc63e3c9e..6df5219120 100644 --- a/checkov/kubernetes/graph_builder/graph_components/edge_builders/NetworkPolicyEdgeBuilder.py +++ b/checkov/kubernetes/graph_builder/graph_components/edge_builders/NetworkPolicyEdgeBuilder.py @@ -29,12 +29,19 @@ def find_connections(vertex: KubernetesBlock, vertices: list[KubernetesBlock]) - """ connections: list[int] = [] + network_policy = vertex + network_policy_namespace = (network_policy.attributes.get("metadata") or {}).get("namespace") or "default" + for potential_pod_index, potential_vertex in enumerate(vertices): if potential_vertex.id == vertex.id or potential_vertex.attributes.get("kind") != "Pod": continue - network_policy = vertex pod = potential_vertex + pod_namespace = (pod.attributes.get("metadata") or {}).get("namespace") or "default" + + # NetworkPolicies are namespace-scoped and only apply to pods in the same namespace + if pod_namespace != network_policy_namespace: + continue pod_spec = network_policy.attributes.get("spec", {}) if pod_spec is None: @@ -54,7 +61,7 @@ def find_connections(vertex: KubernetesBlock, vertices: list[KubernetesBlock]) - shared_labels = [k for k in match_labels if k in pod_labels and match_labels[k] == pod_labels[k]] if len(shared_labels) == len(match_labels): connections.append(potential_pod_index) - # the network policy has a podSelector property with no labels and should apply for all pods + # the network policy has a podSelector property with no labels and should apply for all pods in the namespace else: connections.append(potential_pod_index) diff --git a/tests/kubernetes/graph/resources/Keyword/network-policy-namespace-scoping.yaml b/tests/kubernetes/graph/resources/Keyword/network-policy-namespace-scoping.yaml new file mode 100644 index 0000000000..13b215cf0b --- /dev/null +++ b/tests/kubernetes/graph/resources/Keyword/network-policy-namespace-scoping.yaml @@ -0,0 +1,45 @@ +apiVersion: v1 +kind: Pod +metadata: + name: pod-in-namespace-a + namespace: namespace-a + labels: + app: myapp +spec: + containers: + - name: app + image: nginx:1.7.9 +--- +apiVersion: v1 +kind: Pod +metadata: + name: pod-in-namespace-b + namespace: namespace-b + labels: + app: myapp +spec: + containers: + - name: app + image: nginx:1.7.9 +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-all-in-namespace-a + namespace: namespace-a +spec: + podSelector: {} + policyTypes: + - Ingress +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: label-policy-in-namespace-a + namespace: namespace-a +spec: + podSelector: + matchLabels: + app: myapp + policyTypes: + - Ingress diff --git a/tests/kubernetes/graph/test_local_graph.py b/tests/kubernetes/graph/test_local_graph.py index 97883a6619..a58ad7d245 100644 --- a/tests/kubernetes/graph/test_local_graph.py +++ b/tests/kubernetes/graph/test_local_graph.py @@ -147,6 +147,23 @@ def test_LabelSelectorEdgeBuilder_on_templates_with_network_policy(self) -> None self.assertEqual(5, len(local_graph.vertices)) self.assertEqual(4, len(local_graph.edges)) + def test_NetworkPolicyEdgeBuilder_respects_namespace_scoping(self) -> None: + relative_file_path = "resources/Keyword/network-policy-namespace-scoping.yaml" + definitions = {} + file = os.path.realpath(os.path.join(TEST_DIRNAME, relative_file_path)) + (definitions[relative_file_path], definitions_raw) = parse(file) + graph_flags = K8sGraphFlags(create_complex_vertices=True, create_edges=True) + + local_graph = KubernetesLocalGraph(definitions) + local_graph.edge_builders = [NetworkPolicyEdgeBuilder] + local_graph.build_graph(render_variables=False, graph_flags=graph_flags) + # 2 pods + 2 network policies = 4 vertices + self.assertEqual(4, len(local_graph.vertices)) + # Only pod-in-namespace-a should be connected; pod-in-namespace-b is in a different namespace + # allow-all-in-namespace-a -> pod-in-namespace-a (empty podSelector, same namespace) + # label-policy-in-namespace-a -> pod-in-namespace-a (matchLabels match, same namespace) + self.assertEqual(2, len(local_graph.edges)) + def test_extracting_pod_from_container_types(self) -> None: relative_file_path = "resources/statefulstate_nested_resource.yaml" definitions = {}