В этом руководстве показано, как настроить Kyverno для применения политик безопасности томов, которые ограничивают использование опасных типов томов и конфигураций, способных скомпрометировать безопасность контейнеров.
Безопасность томов включает контроль над тем, какие типы томов могут монтировать контейнеры и каким образом они могут к ним обращаться. Правильная безопасность томов предотвращает:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-volume-types
annotations:
policies.kyverno.io/title: Restrict Volume Types
policies.kyverno.io/category: Pod Security Standards (Restricted)
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod,Volume
policies.kyverno.io/description: >-
Only allow safe volume types. This policy restricts volumes to configMap, csi,
downwardAPI, emptyDir, ephemeral, persistentVolumeClaim, projected, and secret.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: restrict-volume-types
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
Only the following types of volumes may be used: configMap, csi, downwardAPI,
emptyDir, ephemeral, persistentVolumeClaim, projected, and secret.
foreach:
- list: "request.object.spec.volumes || []"
deny:
conditions:
all:
- key: "{{ element.keys(@) }}"
operator: AnyNotIn
value:
- name
- configMap
- csi
- downwardAPI
- emptyDir
- ephemeral
- persistentVolumeClaim
- projected
- secret
# Применить политику
kubectl apply -f restrict-volume-types.yaml
# Попытка создать pod с томом hostPath (должно завершиться ошибкой)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: test-hostpath
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: host-vol
mountPath: /host
volumes:
- name: host-vol
hostPath:
path: /
EOF
# Сначала создать тестовый ConfigMap
kubectl create configmap test-config --from-literal=key=value
# Попытка создать pod с разрешённым томом (должно сработать)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: test-configmap
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: config-vol
mountPath: /config
volumes:
- name: config-vol
configMap:
name: test-config
EOF
# Очистка
kubectl delete pod test-hostpath test-configmap --ignore-not-found
kubectl delete configmap test-config --ignore-not-found
Запретить контейнерам монтировать пути файловой системы хоста:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-host-path
annotations:
policies.kyverno.io/title: Disallow Host Path
policies.kyverno.io/category: Pod Security Standards (Baseline)
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod,Volume
policies.kyverno.io/description: >-
HostPath volumes let Pods use host directories and volumes in containers.
Using host resources can be used to access shared data or escalate privileges
and should not be allowed.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: host-path
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
HostPath volumes are forbidden. The field spec.volumes[*].hostPath must be unset.
pattern:
spec:
=(volumes):
- X(hostPath): "null"
Разрешить определённые тома hostPath с доступом только для чтения:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-host-path-readonly
annotations:
policies.kyverno.io/title: Restrict Host Path (Read-Only)
policies.kyverno.io/category: Pod Security Standards (Baseline)
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod,Volume
policies.kyverno.io/description: >-
HostPath volumes which are allowed must be read-only and restricted to specific paths.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: host-path-readonly
match:
any:
- resources:
kinds:
- Pod
preconditions:
all:
- key: "{{ request.object.spec.volumes[?hostPath] | length(@) }}"
operator: GreaterThan
value: 0
validate:
message: >-
HostPath volumes must be read-only and limited to allowed paths.
foreach:
- list: "request.object.spec.volumes[?hostPath]"
deny:
conditions:
any:
# Запрет, если путь не входит в разрешённый список
- key: "{{ element.hostPath.path }}"
operator: AnyNotIn
value:
- "/var/log"
- "/var/lib/docker/containers"
- "/proc"
- "/sys"
foreach:
- list: "request.object.spec.containers[].volumeMounts[?name]"
deny:
conditions:
any:
# Запрет, если монтирование тома не является только для чтения
- key: "{{ element.readOnly || false }}"
operator: Equals
value: false
Блокировать типы томов, которые могут обходить механизмы безопасности:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-privileged-volumes
annotations:
policies.kyverno.io/title: Disallow Privileged Volume Types
policies.kyverno.io/category: Pod Security Standards (Baseline)
policies.kyverno.io/severity: high
policies.kyverno.io/subject: Pod,Volume
policies.kyverno.io/description: >-
Certain volume types are considered privileged and should not be allowed.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: disallow-privileged-volumes
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
Privileged volume types are not allowed: hostPath, gcePersistentDisk,
awsElasticBlockStore, gitRepo, nfs, iscsi, glusterfs, rbd, flexVolume,
cinder, cephFS, flocker, fc, azureFile, azureDisk, vsphereVolume, quobyte,
portworxVolume, scaleIO, storageos.
foreach:
- list: "request.object.spec.volumes || []"
deny:
conditions:
any:
- key: "{{ element.keys(@) }}"
operator: AnyIn
value:
- hostPath
- gcePersistentDisk
- awsElasticBlockStore
- gitRepo
- nfs
- iscsi
- glusterfs
- rbd
- flexVolume
- cinder
- cephFS
- flocker
- fc
- azureFile
- azureDisk
- vsphereVolume
- quobyte
- portworxVolume
- scaleIO
- storageos
Обеспечить использование контейнерами корневой файловой системы только для чтения:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-readonly-rootfs
annotations:
policies.kyverno.io/title: Require Read-Only Root Filesystem
policies.kyverno.io/category: Pod Security Standards (Restricted)
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
A read-only root file system helps to enforce an immutable infrastructure strategy;
the container only needs to write on the mounted volume that persists the state.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: readonly-rootfs
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
Root filesystem must be read-only. Set readOnlyRootFilesystem to true.
foreach:
- list: request.object.spec.[ephemeralContainers, initContainers, containers][]
deny:
conditions:
any:
- key: "{{ element.securityContext.readOnlyRootFilesystem || false }}"
operator: Equals
value: false
Ограничить разрешения и пути монтирования томов:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: control-volume-mounts
annotations:
policies.kyverno.io/title: Control Volume Mount Permissions
policies.kyverno.io/category: Pod Security Standards (Restricted)
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod,Volume
policies.kyverno.io/description: >-
Control where volumes can be mounted and with what permissions.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: restrict-mount-paths
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
Volume mounts to sensitive paths are not allowed.
foreach:
- list: request.object.spec.[ephemeralContainers, initContainers, containers][].volumeMounts[]
deny:
conditions:
any:
# Блокировать монтирование в чувствительные системные пути
- key: "{{ element.mountPath }}"
operator: AnyIn
value:
- "/etc"
- "/root"
- "/var/run/docker.sock"
- "/var/lib/kubelet"
- "/var/lib/docker"
- "/usr/bin"
- "/usr/sbin"
- "/sbin"
- "/bin"
- name: require-readonly-sensitive-mounts
match:
any:
- resources:
kinds:
- Pod
validate:
message: >-
Mounts to /proc and /sys must be read-only.
foreach:
- list: request.object.spec.[ephemeralContainers, initContainers, containers][].volumeMounts[]
preconditions:
any:
- key: "{{ element.mountPath }}"
operator: AnyIn
value:
- "/proc"
- "/sys"
deny:
conditions:
any:
- key: "{{ element.readOnly || false }}"
operator: Equals
value: false
Различные ограничения томов для разных сред:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: environment-volume-security
spec:
validationFailureAction: Enforce
background: true
rules:
# Production: Строгий контроль томов
- name: production-volume-restrictions
match:
any:
- resources:
kinds:
- Pod
namespaces:
- production
- prod-*
validate:
message: "Production environments allow only secure volume types"
foreach:
- list: "request.object.spec.volumes || []"
deny:
conditions:
all:
- key: "{{ element.keys(@) }}"
operator: AnyNotIn
value:
- name
- configMap
- secret
- persistentVolumeClaim
- emptyDir
# Development: Более либеральные, но безопасные
- name: development-volume-restrictions
match:
any:
- resources:
kinds:
- Pod
namespaces:
- development
- dev-*
- staging
validate:
message: "Development environments allow additional volume types"
foreach:
- list: "request.object.spec.volumes || []"
deny:
conditions:
any:
- key: "{{ element.keys(@) }}"
operator: AnyIn
value:
- hostPath # Всё ещё блокировать hostPath в dev
- nfs # Блокировать сетевые файловые системы
Различные политики томов для разных типов приложений:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: application-volume-policies
spec:
validationFailureAction: Enforce
background: true
rules:
# Приложения баз данных: Разрешить постоянное хранилище
- name: database-volume-policy
match:
any:
- resources:
kinds:
- Pod
selector:
matchLabels:
app.type: database
validate:
message: "Database applications must use persistent volumes"
pattern:
spec:
volumes:
- persistentVolumeClaim: {}
# Веб-приложения: Ограничить безопасными томами
- name: web-app-volume-policy
match:
any:
- resources:
kinds:
- Pod
selector:
matchLabels:
app.type: web
validate:
message: "Web applications can only use safe volume types"
foreach:
- list: "request.object.spec.volumes || []"
deny:
conditions:
all:
- key: "{{ element.keys(@) }}"
operator: AnyNotIn
value:
- name
- configMap
- secret
- emptyDir
- projected
Контроль размеров томов и использования ресурсов:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: volume-resource-limits
spec:
validationFailureAction: Enforce
background: true
rules:
- name: limit-emptydir-size
match:
any:
- resources:
kinds:
- Pod
validate:
message: "EmptyDir volumes must have size limits"
foreach:
- list: "request.object.spec.volumes[?emptyDir]"
deny:
conditions:
any:
- key: "{{ element.emptyDir.sizeLimit || '' }}"
operator: Equals
value: ""
- name: limit-emptydir-memory
match:
any:
- resources:
kinds:
- Pod
validate:
message: "EmptyDir memory volumes are not allowed"
foreach:
- list: "request.object.spec.volumes[?emptyDir]"
deny:
conditions:
any:
- key: "{{ element.emptyDir.medium || '' }}"
operator: Equals
value: "Memory"
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: test-hostpath
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: host-vol
mountPath: /host
volumes:
- name: host-vol
hostPath:
path: /
EOF