Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ spec:
action: ignore
- controlID: C-0015
action: ignore
- controlID: C-0007
action: ignore
- controlID: C-0037
action: ignore
- controlID: C-0031
Expand All @@ -35,14 +33,20 @@ spec:
action: ignore
- controlID: C-0035
action: ignore
# NOTE: RBAC-review controls are NOT suppressible here. Kubescape anchors
# NOTE: RBAC-object controls are NOT suppressible here. Kubescape anchors
# their findings on the RBAC Role/ClusterRole/binding object, not on a
# namespaced workload, so a namespaceSelector match never applies (proven:
# the namespaced velero-server binding kept failing despite `velero` being
# listed below). They are handled by explicit kind+name `match.resources` CRs:
# - C-0187 (wildcard RBAC, CIS-5.1.3) → wildcard-rbac.yaml
# - C-0002 (exec into container) → exec-into-container-rbac.yaml
# Headlamp is fixed at the root (SA scoped to read-only cluster-reader).
# the namespaced velero-server Role kept failing C-0187 despite `velero`
# being listed below; and 19 of C-0007's 26 findings terminate on
# cluster-scoped ClusterRoleBindings whose namespace is empty). They are
# handled by explicit kind+name `match.resources` CRs:
# - C-0187 (wildcard RBAC, CIS-5.1.3) -> wildcard-rbac.yaml
# - C-0007 (delete-capable roles) -> delete-rbac.yaml
# - C-0002 (exec into container) -> exec-into-container-rbac.yaml
# The other RBAC controls kept here (C-0015/C-0031/C-0035/C-0053/C-0063/
# C-0188 and C-0037) share this limitation; migrate them the same way if
# they surface failing objects. Headlamp is fixed at the root (SA scoped
# to read-only cluster-reader).
- controlID: C-0188
action: ignore
match:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
# Delete-capable-RBAC exception (Kubescape C-0007, "Roles with delete
# capabilities") for the infrastructure controllers and platform/app service
# accounts that require delete permissions BY DESIGN — GitOps reconcilers,
# operators, backup/restore, storage, virtualisation, cert/DB/secret managers,
# and tenant deployers all delete the resources they own.
#
# Why this is a separate CR matched by kind+name, and NOT a namespaceSelector
# entry in controller-rbac.yaml (mirrors wildcard-rbac.yaml / C-0187): Kubescape
# attaches an RBAC finding to the Role/ClusterRole/binding object, not to a
# namespaced workload, so a namespaceSelector never matches it. 19 of C-0007's
# 26 findings terminate on cluster-scoped ClusterRoleBindings whose namespace is
# empty — a namespace match can NEVER cover those. Matching each offending
# binding by kind+name is the mechanism that actually works.
#
# Deliberately explicit (one entry per binding) so C-0007 still flags any NEW or
# accidental delete-capable grant elsewhere — that residual signal is wanted.
# Names are regex-compared, so the one revision-hashed entry (the Crossplane
# provider CRB) matches by pattern and survives provider re-installs; the
# tofu-controller `tf-runner` RoleBinding is created on demand per Terraform
# apply (the exception no-ops while it is absent). Keep this list reconciled
# against a fresh scan (it can only be re-verified live once the scanner mount
# fix lands — see the kubescape HelmRelease postRenderer / helm-charts#862).
apiVersion: kubescape.io/v1beta1
kind: ClusterSecurityException
metadata:
name: delete-capable-rbac-by-design
spec:
reason: >-
Infrastructure controllers, operators and tenant deployers legitimately
require delete permissions by design (GitOps reconciliation, operator
lifecycle, backup/restore, storage, virtualisation, cert/DB/secret
management). Each offending Role/binding is matched by kind+name so C-0007
still catches any new or accidental delete-capable grant.
posture:
- controlID: C-0007
action: ignore
match:
resources:
# --- cluster-scoped: ClusterRoleBinding -> ClusterRole (19) ---
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: cdi-sa # CDI (KubeVirt storage import)
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: cert-manager-controller-issuers
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: cert-manager-controller-clusterissuers
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: cert-manager-controller-certificates
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: cert-manager-controller-challenges
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: cloudnative-pg # CNPG Postgres operator
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: cluster-reconciler-flux-system # Flux kustomize/helm controllers
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: flux-operator
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: coroot-operator
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: crossplane-admin # Crossplane core (Group crossplane-masters)
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
# The trailing hash is the provider-revision hash, rewritten on every
# provider re-install, so match it by pattern — exception name matching
# is regex-based (kubescape/opa-utils exceptions comparator regexCompare),
# the same mechanism that lets the literal names above match.
name: "^crossplane:provider:provider-upjet-github-[0-9a-f]+:system$"
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: flagger # progressive delivery
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: "kro:controller" # KRO resource-graph operator
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: ksail-operator
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: kube-prometheus-stack-operator
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: kubevirt-apiserver
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: kubevirt-controller
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: longhorn-bind # Longhorn distributed storage
- apiGroup: rbac.authorization.k8s.io
kind: ClusterRoleBinding
name: reloader-reloader-role-binding # config/secret change reloader
# --- namespaced: RoleBinding -> Role/ClusterRole (7) ---
- apiGroup: rbac.authorization.k8s.io
kind: RoleBinding
name: ascoachingogvaner # tenant deployer SA (edit)
- apiGroup: rbac.authorization.k8s.io
kind: RoleBinding
name: "kyverno:admission-controller"
- apiGroup: rbac.authorization.k8s.io
kind: RoleBinding
name: "kyverno:cleanup-controller"
- apiGroup: rbac.authorization.k8s.io
kind: RoleBinding
name: longhorn
- apiGroup: rbac.authorization.k8s.io
kind: RoleBinding
# FRAGILE: tofu-controller creates this on demand per Terraform apply;
# it is absent between runs (the exception simply no-ops while absent).
name: tf-runner
- apiGroup: rbac.authorization.k8s.io
kind: RoleBinding
name: velero-server # backup/restore
- apiGroup: rbac.authorization.k8s.io
kind: RoleBinding
name: wedding-app # tenant deployer SA (edit)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ resources:
- batch-workloads.yaml
- cilium-network-policies.yaml
- controller-rbac.yaml
- delete-rbac.yaml
- exec-into-container-rbac.yaml
- health-probes.yaml
- helm-chart-metadata.yaml
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,6 @@ data:
{
"controlID": "^C-0015$"
},
{
"controlID": "^C-0007$"
},
{
"controlID": "^C-0037$"
},
Expand Down Expand Up @@ -153,6 +150,42 @@ data:
}
]
},
{
"name": "delete-capable-rbac-by-design",
"policyType": "postureExceptionPolicy",
"actions": [
"alertOnly"
],
"reason": "Controllers, operators and tenant deployers require delete permissions by design (C-0007). Mirrors the operator CSE (delete-rbac.yaml) by kind+name: C-0007 findings terminate on the RBAC binding objects themselves (19 of 26 on cluster-scoped ClusterRoleBindings whose namespace is empty), so a namespace designator can never cover them.",
"resources": [
{
"designatorType": "Attributes",
"attributes": {
"kind": "^ClusterRoleBinding$",
"name": "^(cdi-sa|cert-manager-controller-(issuers|clusterissuers|certificates|challenges)|cloudnative-pg|cluster-reconciler-flux-system|flux-operator|coroot-operator|crossplane-admin|crossplane:provider:provider-upjet-github-[0-9a-f]+:system|flagger|kro:controller|ksail-operator|kube-prometheus-stack-operator|kubevirt-apiserver|kubevirt-controller|longhorn-bind|reloader-reloader-role-binding)$"
}
},
{
"designatorType": "Attributes",
"attributes": {
"kind": "^RoleBinding$",
"name": "^(ascoachingogvaner|kyverno:admission-controller|kyverno:cleanup-controller|longhorn|tf-runner|velero-server|wedding-app)$"
}
},
{
"designatorType": "Attributes",
"attributes": {
"kind": "^Group$",
"name": "^crossplane-masters$"
}
}
],
"posturePolicies": [
{
"controlID": "^C-0007$"
}
]
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
{
"name": "health-probes",
"policyType": "postureExceptionPolicy",
Expand Down