diff --git a/checkov/kubernetes/checks/graph_checks/ImpersonatePermissions.yaml b/checkov/kubernetes/checks/graph_checks/ImpersonatePermissions.yaml new file mode 100644 index 00000000000..29df1f02662 --- /dev/null +++ b/checkov/kubernetes/checks/graph_checks/ImpersonatePermissions.yaml @@ -0,0 +1,61 @@ +metadata: + id: "CKV2_K8S_3" + name: "No ServiceAccount/Node should have `impersonate` permissions for groups/users/service-accounts" + category: "KUBERNETES" +definition: + and: + - cond_type: filter + value: + - ClusterRoleBinding + - RoleBinding + operator: within + attribute: kind + - or: + - cond_type: connection + operator: not_exists + resource_types: + - ClusterRoleBinding + - RoleBinding + connected_resource_types: + - ClusterRole + - Role + - not: + cond_type: attribute + attribute: 'subjects.*.kind' + operator: within + value: + - 'Node' + - 'ServiceAccount' + resource_types: + - ClusterRoleBinding + - RoleBinding + - and: + - cond_type: connection + operator: exists + resource_types: + - ClusterRoleBinding + - RoleBinding + connected_resource_types: + - ClusterRole + - Role + - or: + - cond_type: attribute + attribute: rules.resources + operator: not_intersects + value: + - 'groups' + - 'users' + - 'serviceaccounts' + - '*' + resource_types: + - ClusterRole + - Role + - cond_type: attribute + attribute: rules.verbs + operator: not_intersects + value: + - 'impersonate' + - '*' + resource_types: + - ClusterRole + - Role diff --git a/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/1/clusterrole1.yaml b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/1/clusterrole1.yaml new file mode 100644 index 00000000000..82421dc58c0 --- /dev/null +++ b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/1/clusterrole1.yaml @@ -0,0 +1,11 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: failing-cr1 +rules: +- apiGroups: + - "" + resources: + - "serviceaccounts" + verbs: + - "*" \ No newline at end of file diff --git a/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/1/clusterrolebinding1.yaml b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/1/clusterrolebinding1.yaml new file mode 100644 index 00000000000..92d4a2bb460 --- /dev/null +++ b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/1/clusterrolebinding1.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: failing-crb1 +subjects: +- kind: ServiceAccount + name: over-privileged-sa1 + namespace: default +roleRef: + kind: ClusterRole + name: failing-cr1 + apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/2/clusterrole2.yaml b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/2/clusterrole2.yaml new file mode 100644 index 00000000000..54ad5bf3531 --- /dev/null +++ b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/2/clusterrole2.yaml @@ -0,0 +1,11 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: failing-cr2 +rules: +- apiGroups: + - "" + resources: + - "users" + verbs: + - "impersonate" \ No newline at end of file diff --git a/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/2/clusterrolebinding2.yaml b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/2/clusterrolebinding2.yaml new file mode 100644 index 00000000000..0d7ded36575 --- /dev/null +++ b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/2/clusterrolebinding2.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: failing-crb2 +subjects: +- kind: ServiceAccount + name: over-privileged-sa2 + namespace: prod +roleRef: + kind: ClusterRole + name: failing-cr2 + apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/3/Role.yaml b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/3/Role.yaml new file mode 100644 index 00000000000..aeeae8e5dff --- /dev/null +++ b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/3/Role.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + namespace: default + name: risky-impersonate-role +rules: + - apiGroups: + - "*" + resources: + - "groups" + - "users" + - "serviceccounts" + - "*" + verbs: + - "impersonate" \ No newline at end of file diff --git a/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/3/RoleBinding.yaml b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/3/RoleBinding.yaml new file mode 100644 index 00000000000..f56ac5cf94f --- /dev/null +++ b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Failing/3/RoleBinding.yaml @@ -0,0 +1,13 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: sa-escalate + namespace: default +subjects: +- kind: ServiceAccount + name: escalating-sa + namespace: dev +roleRef: + kind: Role + name: risky-impersonate-role + apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Passing/1/clusterrole1.yaml b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Passing/1/clusterrole1.yaml new file mode 100644 index 00000000000..2a02ce3cef0 --- /dev/null +++ b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Passing/1/clusterrole1.yaml @@ -0,0 +1,11 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: passing-cr1 +rules: +- apiGroups: + - "" + resources: + - "serviceaccounts" + verbs: + - "create" diff --git a/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Passing/1/clusterrolebinding1.yaml b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Passing/1/clusterrolebinding1.yaml new file mode 100644 index 00000000000..3a497e862fa --- /dev/null +++ b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/Passing/1/clusterrolebinding1.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: passing-crb1 +subjects: +- kind: ServiceAccount + name: over-privileged-sa1 + namespace: default +roleRef: + kind: ClusterRole + name: passing-cr1 + apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/expected.yaml b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/expected.yaml new file mode 100644 index 00000000000..73600b3df83 --- /dev/null +++ b/tests/kubernetes/graph/checks/resources/ImpersonatePermissions/expected.yaml @@ -0,0 +1,6 @@ +fail: + - "ClusterRoleBinding.default.failing-crb1" + - "ClusterRoleBinding.default.failing-crb2" + - "RoleBinding.default.sa-escalate" +pass: + - "ClusterRoleBinding.default.passing-crb1" \ 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 2a39b32fd90..eef2a38d0a4 100644 --- a/tests/kubernetes/graph/checks/test_yaml_policies.py +++ b/tests/kubernetes/graph/checks/test_yaml_policies.py @@ -49,6 +49,10 @@ def test_RoleBindingPE(self) -> None: @with_k8s_graph_flags() def test_NoCreateNodesProxyOrPodsExec(self) -> None: self.go('NoCreateNodesProxyOrPodsExec') + + @with_k8s_graph_flags() + def test_ImpersonatePermissions(self) -> None: + self.go("ImpersonatePermissions") def create_report_from_graph_checks_results(self, checks_results, check): report = Report("kubernetes")