Skip to content

Commit

Permalink
Merge pull request #56 from sig-bsi-grundschutz/bsi-app-4.4-a8to11
Browse files Browse the repository at this point in the history
Bsi app 4.4 a8to11
  • Loading branch information
sluetze authored May 31, 2024
2 parents 464d14a + 3dd2ce3 commit 78cc206
Show file tree
Hide file tree
Showing 28 changed files with 889 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
documentation_complete: true

title: 'Ensure no ClusterRoleBindings set for default Service Account'

description: |-
Using the <tt>default</tt> service account prevents accurate application
rights review and audit tracing. Instead of <tt>default</tt>, create
a new and unique service account and associate the required ClusterRoleBindings.
rationale: |-
Kubernetes provides a default service account which is used by
cluster workloads where no specific service account is assigned to the pod.
Where access to the Kubernetes API from a pod is required, a specific service account
should be created for that pod, and rights granted to that service account.
This increases auditability of service account rights and access making it
easier and more accurate to trace potential malicious behaviors to a specific
service account and project.
severity: medium

identifiers: {}

references:
bsi: APP.4.4.A9

{{% set jqfilter = '[.items[] | select ( .subjects[]?.name == "default" ) | select(.subjects[].namespace | startswith("kube-") or startswith("openshift-") | not) | .metadata.name ] | unique' %}}

ocil_clause: 'default service account is given permissions using ClusterRoleBindings'

ocil: |-
Run the following command to retrieve a list of ClusterRoleBindings that are
associated to the default service account:
<pre>$ oc get clusterrolebindings -o json | jq '{{{ jqfilter }}}'</pre>
There should be no ClusterRoleBindings associated with the the default service account
in any namespace.
warnings:
- general: |-
{{{ openshift_filtered_cluster_setting({'/apis/rbac.authorization.k8s.io/v1/clusterrolebindings?limit=10000': jqfilter}) | indent(4) }}}
template:
name: yamlfile_value
vars:
ocp_data: "true"
filepath: |-
{{{ openshift_filtered_path('/apis/rbac.authorization.k8s.io/v1/clusterrolebindings?limit=10000', jqfilter) }}}
yamlpath: "[:]"
check_existence: "none_exist"
entity_check: "all"
values:
- value: "(.*?)"
operation: "pattern match"
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#!/bin/bash
# remediation = none
# packages = jq

kube_apipath="/kubernetes-api-resources"
mkdir -p "$kube_apipath/apis/rbac.authorization.k8s.io/v1"
crb_apipath="/apis/rbac.authorization.k8s.io/v1/clusterrolebindings?limit=10000"

cat <<EOF > "$kube_apipath$crb_apipath"
{
"apiVersion": "v1",
"items": [
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": {
"creationTimestamp": "2023-11-07T10:07:21Z",
"labels": {
"app": "controller.csi.trident.netapp.io",
"k8s_version": "v1.25.14",
"trident_version": "v23.01.0"
},
"name": "trident-controller",
"ownerReferences": [
{
"apiVersion": "trident.netapp.io/v1",
"controller": true,
"kind": "TridentOrchestrator",
"name": "trident",
"uid": "eb7fe92c-3378-4d27-98a1-d4b543772e3c"
}
],
"resourceVersion": "989379693",
"uid": "8eab8cd7-8fc3-41a7-bd2a-1c9f6f7c2d8b"
},
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "trident-controller"
},
"subjects": [
{
"kind": "ServiceAccount",
"name": "trident-controller",
"namespace": "trident"
}
]
},
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": {
"annotations": {
"rbac.authorization.kubernetes.io/autoupdate": "false"
},
"creationTimestamp": "2021-01-04T14:30:26Z",
"labels": {
"app.kubernetes.io/instance": "roles"
},
"name": "self-provisioners",
"resourceVersion": "268316753",
"uid": "7c5ad812-cab1-40bb-88dc-a014015f7ede"
},
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "self-provisioner"
}
},
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": {
"creationTimestamp": "2021-01-04T14:49:26Z",
"name": "prometheus-k8s",
"resourceVersion": "39547",
"uid": "57de031f-a360-483d-903f-19f0fb597cc9"
},
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "prometheus-k8s"
},
"subjects": [
{
"kind": "ServiceAccount",
"name": "prometheus-k8s",
"namespace": "openshift-monitoring"
}
]
},
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": {
"creationTimestamp": "2021-01-04T14:27:59Z",
"name": "system:openshift:openshift-apiserver",
"resourceVersion": "5623",
"uid": "fe1c3593-7e89-4b2c-b47b-a6569f47d88f"
},
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "cluster-admin"
},
"subjects": [
{
"kind": "ServiceAccount",
"name": "openshift-apiserver-sa",
"namespace": "openshift-apiserver"
}
]
}
],
"kind": "List",
"metadata": {
"resourceVersion": ""
}
}
EOF

jq_filter='[.items[] | select ( .subjects[]?.name == "default" ) | select(.subjects[].namespace | startswith("kube-") or startswith("openshift-") | not) | .metadata.name ] | unique'

# Get file path. This will actually be read by the scan
filteredpath="$kube_apipath$crb_apipath#$(echo -n "$crb_apipath$jq_filter" | sha256sum | awk '{print $1}')"

# populate filtered path with jq-filtered result
jq "$jq_filter" "$kube_apipath$crb_apipath" > "$filteredpath"
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/bin/bash
# remediation = none
# packages = jq

kube_apipath="/kubernetes-api-resources"
mkdir -p "$kube_apipath/apis/rbac.authorization.k8s.io/v1"
crb_apipath="/apis/rbac.authorization.k8s.io/v1/clusterrolebindings?limit=10000"

# This file assumes that we dont have any clusterrolebindings.
cat <<EOF > "$kube_apipath$crb_apipath"
{
"apiVersion": "v1",
"items": [
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": {
"creationTimestamp": "2023-11-07T10:07:21Z",
"labels": {
"app": "controller.csi.trident.netapp.io",
"k8s_version": "v1.25.14",
"trident_version": "v23.01.0"
},
"name": "trident-controller",
"ownerReferences": [
{
"apiVersion": "trident.netapp.io/v1",
"controller": true,
"kind": "TridentOrchestrator",
"name": "trident",
"uid": "eb7fe92c-3378-4d27-98a1-d4b543772e3c"
}
],
"resourceVersion": "989379693",
"uid": "8eab8cd7-8fc3-41a7-bd2a-1c9f6f7c2d8b"
},
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "trident-controller"
},
"subjects": [
{
"kind": "ServiceAccount",
"name": "trident-controller",
"namespace": "trident"
}
]
},
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": {
"creationTimestamp": "2021-01-18T15:31:25Z",
"name": "system:openshift:scc:anyuid",
"resourceVersion": "5666124",
"uid": "a85b6fd7-2d53-4e98-9c12-afc8d2e0896f"
},
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "system:openshift:scc:anyuid"
},
"subjects": [
{
"kind": "ServiceAccount",
"name": "default",
"namespace": "app1"
}
]
},
{
"apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding",
"metadata": {
"creationTimestamp": "2021-08-10T10:35:31Z",
"name": "ds-operator-manager-rolebinding",
"resourceVersion": "1116129289",
"uid": "0deb4996-a03c-4428-8135-5468cb218c8f"
},
"roleRef": {
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "ds-operator-manager-role"
},
"subjects": [
{
"kind": "ServiceAccount",
"name": "default",
"namespace": "ds-system"
},
{
"kind": "ServiceAccount",
"name": "default",
"namespace": "magic-controller"
}
]
}
],
"kind": "List",
"metadata": {
"resourceVersion": ""
}
}
EOF


jq_filter='[.items[] | select ( .subjects[]?.name == "default" ) | select(.subjects[].namespace | startswith("kube-") or startswith("openshift-") | not) | .metadata.name ] | unique'

# Get file path. This will actually be read by the scan
filteredpath="$kube_apipath$crb_apipath#$(echo -n "$crb_apipath$jq_filter" | sha256sum | awk '{print $1}')"

# populate filtered path with jq-filtered result
jq "$jq_filter" "$kube_apipath$crb_apipath" > "$filteredpath"
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash
# remediation = none
# packages = jq

kube_apipath="/kubernetes-api-resources"
mkdir -p "$kube_apipath/apis/rbac.authorization.k8s.io/v1"
crb_apipath="/apis/rbac.authorization.k8s.io/v1/clusterrolebindings?limit=10000"

# This file assumes that we dont have any clusterrolebindings.
cat <<EOF > "$kube_apipath$crb_apipath"
{
"apiVersion": "v1",
"items": [],
"kind": "List",
"metadata": {
"resourceVersion": "",
"selfLink": ""
}
}
EOF


jq_filter='[.items[] | select ( .subjects[]?.name == "default" ) | select(.subjects[].namespace | startswith("kube-") or startswith("openshift-") | not) | .metadata.name ] | unique'

# Get file path. This will actually be read by the scan
filteredpath="$kube_apipath$crb_apipath#$(echo -n "$crb_apipath$jq_filter" | sha256sum | awk '{print $1}')"

# populate filtered path with jq-filtered result
jq "$jq_filter" "$kube_apipath$crb_apipath" > "$filteredpath"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
default_result: PASS
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
documentation_complete: true

title: 'Ensure no RoleBindings set for default Service Account'

description: |-
Using the <tt>default</tt> service account prevents accurate application
rights review and audit tracing. Instead of <tt>default</tt>, create
a new and unique service account and associate the required RoleBindings.
rationale: |-
Kubernetes provides a default service account which is used by
cluster workloads where no specific service account is assigned to the pod.
Where access to the Kubernetes API from a pod is required, a specific service account
should be created for that pod, and rights granted to that service account.
This increases auditability of service account rights and access making it
easier and more accurate to trace potential malicious behaviors to a specific
service account and project.
severity: medium

identifiers: {}

references:
bsi: APP.4.4.A9

{{% set jqfilter = '[.items[] | select(.metadata.namespace | startswith("kube-") or startswith("openshift-") | not) | select ( .subjects[]?.name == "default" ) | .metadata.namespace + "/" + .metadata.name ] | unique' %}}

ocil_clause: 'default service account is given permissions using RoleBindings'

ocil: |-
Run the following command to retrieve a list of RoleBindings that are
associated to the default service account:
<pre>$ oc get rolebindings --all-namespaces -o json | jq '{{{ jqfilter }}}'</pre>
There should be no RoleBindings associated with the the default service account
in any namespace.
warnings:
- general: |-
{{{ openshift_filtered_cluster_setting({'/apis/rbac.authorization.k8s.io/v1/rolebindings?limit=10000': jqfilter}) | indent(4) }}}
template:
name: yamlfile_value
vars:
ocp_data: "true"
filepath: |-
{{{ openshift_filtered_path('/apis/rbac.authorization.k8s.io/v1/rolebindings?limit=10000', jqfilter) }}}
yamlpath: "[:]"
check_existence: "none_exist"
entity_check: "all"
values:
- value: "(.*?)"
operation: "pattern match"
Loading

0 comments on commit 78cc206

Please sign in to comment.