HostPath volumes mounting sensitive OS directories should be read-only.
apiVersion: rules.spotter.dev/v1alpha1
kind: SpotterRule
metadata:
name: spotter-workload-009
labels:
severity: "critical"
category: "workload"
annotations:
rules.spotter.dev/title: "Volume Mount With OS Directory Write Permissions"
rules.spotter.dev/version: "1.0.0"
rules.spotter.dev/cwe: "CWE-732"
rules.spotter.dev/description: "HostPath volumes mounting sensitive OS directories should be read-only."
spec:
match:
resources:
kubernetes:
apiGroups:
- ""
- "apps"
versions:
- "v1"
kinds:
- Pod
- Deployment
- StatefulSet
- DaemonSet
namespaces:
include: ["*"]
exclude: ["kube-system", "kube-public"]
labels:
exclude:
rules.spotter.dev/ignore: ["true"]
cel: |
(object.kind == 'Pod' && (
(has(object.spec.containers) && object.spec.containers.exists(c,
has(c.volumeMounts) && c.volumeMounts.exists(vm,
(vm.mountPath.startsWith('/etc') || vm.mountPath.startsWith('/var') || vm.mountPath.startsWith('/usr') || vm.mountPath.startsWith('/bin')) &&
((!has(vm.readOnly) || vm.readOnly == false) || (has(vm.recursiveReadOnly) && vm.recursiveReadOnly == 'Disabled')) &&
has(object.spec.volumes) && object.spec.volumes.exists(v, v.name == vm.name &&
(has(v.hostPath) || has(v.emptyDir) || has(v.scaleIO) || (!has(v.configMap) && !has(v.secret))))
)
)) ||
(has(object.spec.initContainers) && object.spec.initContainers.exists(c,
has(c.volumeMounts) && c.volumeMounts.exists(vm,
(vm.mountPath.startsWith('/etc') || vm.mountPath.startsWith('/var') || vm.mountPath.startsWith('/usr') || vm.mountPath.startsWith('/bin')) &&
((!has(vm.readOnly) || vm.readOnly == false) || (has(vm.recursiveReadOnly) && vm.recursiveReadOnly == 'Disabled')) &&
has(object.spec.volumes) && object.spec.volumes.exists(v, v.name == vm.name &&
(has(v.hostPath) || has(v.emptyDir) || has(v.scaleIO) || (!has(v.configMap) && !has(v.secret))))
)
))
)) ||
(object.kind != 'Pod' && (
(has(object.spec.template.spec.containers) && object.spec.template.spec.containers.exists(c,
has(c.volumeMounts) && c.volumeMounts.exists(vm,
(vm.mountPath.startsWith('/etc') || vm.mountPath.startsWith('/var') || vm.mountPath.startsWith('/usr') || vm.mountPath.startsWith('/bin')) &&
((!has(vm.readOnly) || vm.readOnly == false) || (has(vm.recursiveReadOnly) && vm.recursiveReadOnly == 'Disabled')) &&
has(object.spec.template.spec.volumes) && object.spec.template.spec.volumes.exists(v, v.name == vm.name &&
(has(v.hostPath) || has(v.emptyDir) || has(v.scaleIO) || (!has(v.configMap) && !has(v.secret))))
)
)) ||
(has(object.spec.template.spec.initContainers) && object.spec.template.spec.initContainers.exists(c,
has(c.volumeMounts) && c.volumeMounts.exists(vm,
(vm.mountPath.startsWith('/etc') || vm.mountPath.startsWith('/var') || vm.mountPath.startsWith('/usr') || vm.mountPath.startsWith('/bin')) &&
((!has(vm.readOnly) || vm.readOnly == false) || (has(vm.recursiveReadOnly) && vm.recursiveReadOnly == 'Disabled')) &&
has(object.spec.template.spec.volumes) && object.spec.template.spec.volumes.exists(v, v.name == vm.name &&
(has(v.hostPath) || has(v.emptyDir) || has(v.scaleIO) || (!has(v.configMap) && !has(v.secret))))
)
))
))
remediation:
manual: "Set `readOnly: true` for hostPath volume mounts that access sensitive OS directories."
references:
- title: "Volumes - hostPath"
url: "https://kubernetes.io/docs/concepts/storage/volumes/#hostpath"