diff --git a/checkov/kubernetes/graph_builder/local_graph.py b/checkov/kubernetes/graph_builder/local_graph.py index 19e755e1139..e665988d76a 100644 --- a/checkov/kubernetes/graph_builder/local_graph.py +++ b/checkov/kubernetes/graph_builder/local_graph.py @@ -171,6 +171,12 @@ def _extract_nested_resources_recursive(conf: Dict[str, Any], all_resources: Lis template['apiVersion'] = conf.get('apiVersion') template_metadata = template.get('metadata') + + template_namespace = template_metadata.get('namespace') + metadata_namespace = metadata.get('namespace') + if template_namespace is None and metadata_namespace is not None: + template_metadata['namespace'] = metadata_namespace + annotations = metadata.get('annotations') if annotations is not None and template_metadata is not None and 'annotations' not in template_metadata: # Updates annotations to template as well to handle metadata added to the parent resource diff --git a/tests/kubernetes/checks/example_NoDefaultNamespace/Dev-PASSED.yaml b/tests/kubernetes/checks/example_NoDefaultNamespace/Dev-PASSED.yaml new file mode 100644 index 00000000000..16a60d3bb51 --- /dev/null +++ b/tests/kubernetes/checks/example_NoDefaultNamespace/Dev-PASSED.yaml @@ -0,0 +1,40 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: dev + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + namespace: dev +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:latest + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx-service + namespace: dev +spec: + selector: + app: nginx + ports: + - protocol: TCP + port: 80 + targetPort: 80 + type: LoadBalancer \ No newline at end of file diff --git a/tests/kubernetes/graph/checks/resources/AllowPrivilegeEscalation/expected.yaml b/tests/kubernetes/graph/checks/resources/AllowPrivilegeEscalation/expected.yaml index 601b5394c65..4f9d0de6218 100644 --- a/tests/kubernetes/graph/checks/resources/AllowPrivilegeEscalation/expected.yaml +++ b/tests/kubernetes/graph/checks/resources/AllowPrivilegeEscalation/expected.yaml @@ -1,4 +1,4 @@ pass: - - 'Pod.default.nginx-ingress-controller-2.app.kubernetes.io/name-ingress-nginx.app.kubernetes.io/part-of-ingress-nginx' + - 'Pod.example-ns.nginx-ingress-controller-2.app.kubernetes.io/name-ingress-nginx.app.kubernetes.io/part-of-ingress-nginx' fail: - - 'Pod.default.nginx-ingress-controller.app.kubernetes.io/name-ingress-nginx.app.kubernetes.io/part-of-ingress-nginx' + - 'Pod.example-ns.nginx-ingress-controller.app.kubernetes.io/name-ingress-nginx.app.kubernetes.io/part-of-ingress-nginx' diff --git a/tests/kubernetes/graph/checks/resources/NoDefaultNamespace/expected.yaml b/tests/kubernetes/graph/checks/resources/NoDefaultNamespace/expected.yaml new file mode 100644 index 00000000000..eb3578dc778 --- /dev/null +++ b/tests/kubernetes/graph/checks/resources/NoDefaultNamespace/expected.yaml @@ -0,0 +1,4 @@ +pass: + - 'Pod.dev.nginx-deployment.app-nginx' + - 'Deployment.dev.nginx-deployment' + - 'Service.dev.nginx-service' \ No newline at end of file diff --git a/tests/kubernetes/graph/checks/resources/NoDefaultNamespace/template.yaml b/tests/kubernetes/graph/checks/resources/NoDefaultNamespace/template.yaml new file mode 100644 index 00000000000..16a60d3bb51 --- /dev/null +++ b/tests/kubernetes/graph/checks/resources/NoDefaultNamespace/template.yaml @@ -0,0 +1,40 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: dev + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx-deployment + namespace: dev +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:latest + ports: + - containerPort: 80 +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx-service + namespace: dev +spec: + selector: + app: nginx + ports: + - protocol: TCP + port: 80 + targetPort: 80 + type: LoadBalancer \ No newline at end of file diff --git a/tests/kubernetes/graph/checks/test_checks/NoDefaultNamespace.yaml b/tests/kubernetes/graph/checks/test_checks/NoDefaultNamespace.yaml new file mode 100644 index 00000000000..f5bdb2607e0 --- /dev/null +++ b/tests/kubernetes/graph/checks/test_checks/NoDefaultNamespace.yaml @@ -0,0 +1,61 @@ +metadata: + id: "CKV_K8S_160" + name: "Ensure resources in k8s not in default namespace" + category: "KUBERNETES" +definition: + and: + - cond_type: "attribute" + resource_types: + - "Deployment" + - "StatefulSet" + - "DaemonSet" + - "Job" + - "CronJob" + - "Pod" + - "Service" + - "ConfigMap" + - "Secret" + attribute: "metadata.namespace" + operator: "exists" + - cond_type: "attribute" + resource_types: + - "Deployment" + - "StatefulSet" + - "DaemonSet" + - "Job" + - "CronJob" + - "Pod" + - "Service" + - "ConfigMap" + - "Secret" + attribute: "metadata.namespace" + operator: "not_equals" + value: "default" + - cond_type: "attribute" + resource_types: + - "Deployment" + - "StatefulSet" + - "DaemonSet" + - "Job" + - "CronJob" + - "Pod" + - "Service" + - "ConfigMap" + - "Secret" + attribute: "metadata.namespace" + operator: "not_equals" + value: "kube-system" + - cond_type: "attribute" + resource_types: + - "Deployment" + - "StatefulSet" + - "DaemonSet" + - "Job" + - "CronJob" + - "Pod" + - "Service" + - "ConfigMap" + - "Secret" + attribute: "metadata.namespace" + operator: "not_regex_match" + value: "^kube-.*" \ No newline at end of file diff --git a/tests/kubernetes/graph/checks/test_yaml_policies.py b/tests/kubernetes/graph/checks/test_yaml_policies.py index f0f31ea1c8a..71c4e075153 100644 --- a/tests/kubernetes/graph/checks/test_yaml_policies.py +++ b/tests/kubernetes/graph/checks/test_yaml_policies.py @@ -54,6 +54,9 @@ def test_PodIsPubliclyAccessibleExample(self) -> None: def test_RequireAllPodsToHaveNetworkPolicy(self) -> None: self.go('RequireAllPodsToHaveNetworkPolicy') + def test_NoDefaultNamespace(self): + self.go('NoDefaultNamespace') + def create_report_from_graph_checks_results(self, checks_results, check): report = Report("kubernetes") first_results_key = list(checks_results.keys())[0]