init
This commit is contained in:
75
02-validation/02-security/README.md
Normal file
75
02-validation/02-security/README.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Урок 2.2 — Политики безопасности и соответствия стандартам
|
||||
|
||||
## Файлы
|
||||
|
||||
| Файл | PSS профиль | Описание |
|
||||
|------|-------------|----------|
|
||||
| `disallow-privileged-containers.yaml` | Baseline | Запрет `privileged: true` |
|
||||
| `disallow-dangerous-capabilities.yaml` | Baseline | Запрет опасных capabilities |
|
||||
| `require-drop-all-capabilities.yaml` | Restricted | Обязательный `drop: [ALL]` |
|
||||
| `require-run-as-non-root.yaml` | Restricted | Запрет запуска от root |
|
||||
| `disallow-host-namespaces.yaml` | Baseline | Запрет hostNetwork/PID/IPC/Path |
|
||||
| `require-seccomp-profile.yaml` | Restricted | Обязательный seccomp |
|
||||
| `restrict-automount-sa-token.yaml` | CIS | Отключение автомонтирования токена |
|
||||
|
||||
## Стратегия внедрения (поэтапно)
|
||||
|
||||
### Этап 1 — Аудит (неделя 1–2)
|
||||
|
||||
```bash
|
||||
# Применить все политики в режиме Audit
|
||||
for f in *.yaml; do
|
||||
kubectl apply -f "$f"
|
||||
done
|
||||
|
||||
# Подождать 5 минут для background scan, затем:
|
||||
kubectl get policyreports -A -o json | \
|
||||
jq -r '[.items[].results[] | select(.result == "fail") | .policy] |
|
||||
group_by(.) | map({policy: .[0], count: length}) |
|
||||
sort_by(-.count)[] | "\(.count)\t\(.policy)"'
|
||||
```
|
||||
|
||||
### Этап 2 — Оценка нарушений
|
||||
|
||||
```bash
|
||||
# Детали нарушений по конкретной политике
|
||||
POLICY="disallow-privileged-containers"
|
||||
kubectl get policyreports -A -o json | \
|
||||
jq --arg p "$POLICY" \
|
||||
-r '.items[] | .metadata.namespace as $ns |
|
||||
.results[] | select(.policy == $p and .result == "fail") |
|
||||
"\($ns)/\(.resources[0].name): \(.message)"'
|
||||
```
|
||||
|
||||
### Этап 3 — Тестирование через Kyverno CLI
|
||||
|
||||
```bash
|
||||
# Проверить под с нарушениями
|
||||
kyverno apply . \
|
||||
--resource test-resources/pod-insecure.yaml \
|
||||
--table
|
||||
|
||||
# Проверить корректный под
|
||||
kyverno apply . \
|
||||
--resource test-resources/pod-secure.yaml \
|
||||
--table
|
||||
```
|
||||
|
||||
### Этап 4 — Перевод в Enforce
|
||||
|
||||
```bash
|
||||
# По одной политике
|
||||
kubectl patch clusterpolicy disallow-privileged-containers \
|
||||
--type merge \
|
||||
-p '{"spec":{"validationFailureAction":"Enforce"}}'
|
||||
```
|
||||
|
||||
## Тестовые ресурсы
|
||||
|
||||
```bash
|
||||
# Под с нарушениями (должен быть отклонён)
|
||||
kubectl apply -f test-resources/pod-insecure.yaml
|
||||
|
||||
# Безопасный под (должен пройти)
|
||||
kubectl apply -f test-resources/pod-secure.yaml
|
||||
```
|
||||
@@ -0,0 +1,59 @@
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-dangerous-capabilities
|
||||
annotations:
|
||||
policies.kyverno.io/title: "Запрет опасных Linux Capabilities"
|
||||
policies.kyverno.io/category: Pod Security Standards (Baseline)
|
||||
policies.kyverno.io/severity: high
|
||||
policies.kyverno.io/subject: Pod
|
||||
policies.kyverno.io/description: >-
|
||||
Запрещает добавление опасных Linux capabilities.
|
||||
SYS_ADMIN, NET_ADMIN, SYS_PTRACE и другие дают контейнеру
|
||||
привилегированный доступ к ядру и сети хоста.
|
||||
Допустима только NET_BIND_SERVICE (порты < 1024).
|
||||
spec:
|
||||
validationFailureAction: Enforce
|
||||
background: true
|
||||
rules:
|
||||
- name: disallow-dangerous-capabilities
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
exclude:
|
||||
resources:
|
||||
namespaces:
|
||||
- kube-system
|
||||
validate:
|
||||
message: >-
|
||||
Контейнер '{{ element.name }}' добавляет запрещённые capabilities:
|
||||
{{ element.securityContext.capabilities.add }}.
|
||||
Разрешена только NET_BIND_SERVICE.
|
||||
Пересмотрите необходимость этих привилегий.
|
||||
foreach:
|
||||
- list: >-
|
||||
request.object.spec.containers[] |
|
||||
merge(request.object.spec.initContainers[] || `[]`, @)
|
||||
deny:
|
||||
conditions:
|
||||
any:
|
||||
- key: "{{ element.securityContext.capabilities.add[] }}"
|
||||
operator: AnyIn
|
||||
value:
|
||||
- SYS_ADMIN
|
||||
- NET_ADMIN
|
||||
- SYS_PTRACE
|
||||
- SYS_MODULE
|
||||
- SYS_RAWIO
|
||||
- SYS_BOOT
|
||||
- SYS_NICE
|
||||
- SYS_RESOURCE
|
||||
- SYS_TIME
|
||||
- AUDIT_CONTROL
|
||||
- MAC_ADMIN
|
||||
- MAC_OVERRIDE
|
||||
- SETUID
|
||||
- SETGID
|
||||
- KILL
|
||||
- MKNOD
|
||||
57
02-validation/02-security/disallow-host-namespaces.yaml
Normal file
57
02-validation/02-security/disallow-host-namespaces.yaml
Normal file
@@ -0,0 +1,57 @@
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-host-namespaces
|
||||
annotations:
|
||||
policies.kyverno.io/title: "Запрет host namespaces и HostPath"
|
||||
policies.kyverno.io/category: Pod Security Standards (Baseline)
|
||||
policies.kyverno.io/severity: critical
|
||||
policies.kyverno.io/subject: Pod
|
||||
policies.kyverno.io/description: >-
|
||||
hostNetwork, hostPID, hostIPC и hostPath дают контейнеру прямой доступ
|
||||
к соответствующим ресурсам ноды. Это нарушает изоляцию контейнеров
|
||||
и является вектором для escape-атак.
|
||||
spec:
|
||||
validationFailureAction: Enforce
|
||||
background: true
|
||||
rules:
|
||||
- name: disallow-host-namespaces
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
exclude:
|
||||
resources:
|
||||
namespaces:
|
||||
- kube-system
|
||||
validate:
|
||||
message: >-
|
||||
Под '{{ request.object.metadata.name }}' использует host namespace.
|
||||
Запрещены: hostNetwork, hostIPC, hostPID.
|
||||
Эти настройки дают контейнеру доступ к сети/процессам/IPC ноды.
|
||||
pattern:
|
||||
spec:
|
||||
=(hostNetwork): false
|
||||
=(hostIPC): false
|
||||
=(hostPID): false
|
||||
|
||||
- name: disallow-hostpath-volumes
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
exclude:
|
||||
resources:
|
||||
namespaces:
|
||||
- kube-system
|
||||
validate:
|
||||
message: >-
|
||||
Под '{{ request.object.metadata.name }}' использует HostPath volume.
|
||||
HostPath даёт контейнеру прямой доступ к файловой системе ноды.
|
||||
Используйте emptyDir, configMap, secret или PersistentVolumeClaim.
|
||||
deny:
|
||||
conditions:
|
||||
any:
|
||||
- key: "{{ request.object.spec.volumes[].hostPath | length(@) }}"
|
||||
operator: GreaterThan
|
||||
value: "0"
|
||||
@@ -0,0 +1,43 @@
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-privileged-containers
|
||||
annotations:
|
||||
policies.kyverno.io/title: "Запрет привилегированных контейнеров"
|
||||
policies.kyverno.io/category: Pod Security Standards (Baseline)
|
||||
policies.kyverno.io/severity: critical
|
||||
policies.kyverno.io/subject: Pod
|
||||
policies.kyverno.io/description: >-
|
||||
Привилегированный контейнер имеет полный доступ к хост-системе —
|
||||
эквивалент root на самой ноде. Компрометация такого контейнера
|
||||
означает компрометацию всей ноды.
|
||||
Проверяются: containers, initContainers, ephemeralContainers.
|
||||
spec:
|
||||
validationFailureAction: Enforce
|
||||
background: true
|
||||
rules:
|
||||
- name: privileged-containers
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
exclude:
|
||||
resources:
|
||||
namespaces:
|
||||
- kube-system
|
||||
validate:
|
||||
message: >-
|
||||
Контейнер '{{ element.name }}' имеет securityContext.privileged: true.
|
||||
Привилегированные контейнеры запрещены — они получают полный доступ к хосту.
|
||||
Удалите поле securityContext.privileged или установите значение false.
|
||||
foreach:
|
||||
- list: >-
|
||||
request.object.spec.containers[] |
|
||||
merge(request.object.spec.initContainers[] || `[]`, @) |
|
||||
merge(request.object.spec.ephemeralContainers[] || `[]`, @)
|
||||
deny:
|
||||
conditions:
|
||||
any:
|
||||
- key: "{{ element.securityContext.privileged }}"
|
||||
operator: Equals
|
||||
value: true
|
||||
41
02-validation/02-security/require-drop-all-capabilities.yaml
Normal file
41
02-validation/02-security/require-drop-all-capabilities.yaml
Normal file
@@ -0,0 +1,41 @@
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-drop-all-capabilities
|
||||
annotations:
|
||||
policies.kyverno.io/title: "Обязательный drop ALL capabilities"
|
||||
policies.kyverno.io/category: Pod Security Standards (Restricted)
|
||||
policies.kyverno.io/severity: medium
|
||||
policies.kyverno.io/subject: Pod
|
||||
policies.kyverno.io/description: >-
|
||||
Каждый контейнер должен явно сбросить все capabilities через
|
||||
securityContext.capabilities.drop: [ALL].
|
||||
Это часть профиля Restricted согласно Pod Security Standards.
|
||||
spec:
|
||||
validationFailureAction: Enforce
|
||||
background: true
|
||||
rules:
|
||||
- name: require-drop-all
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
exclude:
|
||||
resources:
|
||||
namespaces:
|
||||
- kube-system
|
||||
validate:
|
||||
message: >-
|
||||
Контейнер '{{ element.name }}' не сбрасывает все capabilities.
|
||||
Добавьте в securityContext:
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
foreach:
|
||||
- list: "request.object.spec.containers"
|
||||
deny:
|
||||
conditions:
|
||||
all:
|
||||
- key: "ALL"
|
||||
operator: NotIn
|
||||
value: "{{ element.securityContext.capabilities.drop[] || `[]` }}"
|
||||
58
02-validation/02-security/require-run-as-non-root.yaml
Normal file
58
02-validation/02-security/require-run-as-non-root.yaml
Normal file
@@ -0,0 +1,58 @@
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-run-as-non-root
|
||||
annotations:
|
||||
policies.kyverno.io/title: "Запрет запуска от root"
|
||||
policies.kyverno.io/category: Pod Security Standards (Restricted)
|
||||
policies.kyverno.io/severity: high
|
||||
policies.kyverno.io/subject: Pod
|
||||
policies.kyverno.io/description: >-
|
||||
Контейнеры не должны запускаться от пользователя root (UID 0).
|
||||
Процесс от root может читать/писать файлы хоста через volume mounts
|
||||
даже без привилегированного режима.
|
||||
Установите runAsNonRoot: true или runAsUser >= 1000.
|
||||
spec:
|
||||
validationFailureAction: Enforce
|
||||
background: true
|
||||
rules:
|
||||
- name: check-runasnonroot-pod-level
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
exclude:
|
||||
resources:
|
||||
namespaces:
|
||||
- kube-system
|
||||
validate:
|
||||
message: >-
|
||||
Под '{{ request.object.metadata.name }}' должен иметь
|
||||
spec.securityContext.runAsNonRoot: true.
|
||||
Это гарантирует, что ни один контейнер не запустится от root.
|
||||
pattern:
|
||||
spec:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
|
||||
- name: check-runasuser-not-root
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
exclude:
|
||||
resources:
|
||||
namespaces:
|
||||
- kube-system
|
||||
validate:
|
||||
message: >-
|
||||
Контейнер '{{ element.name }}' использует runAsUser: 0 (root).
|
||||
Установите runAsUser >= 1000.
|
||||
foreach:
|
||||
- list: "request.object.spec.containers"
|
||||
deny:
|
||||
conditions:
|
||||
any:
|
||||
- key: "{{ element.securityContext.runAsUser }}"
|
||||
operator: Equals
|
||||
value: 0
|
||||
52
02-validation/02-security/require-seccomp-profile.yaml
Normal file
52
02-validation/02-security/require-seccomp-profile.yaml
Normal file
@@ -0,0 +1,52 @@
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-seccomp-profile
|
||||
annotations:
|
||||
policies.kyverno.io/title: "Обязательный Seccomp профиль"
|
||||
policies.kyverno.io/category: Pod Security Standards (Restricted)
|
||||
policies.kyverno.io/severity: medium
|
||||
policies.kyverno.io/subject: Pod
|
||||
policies.kyverno.io/description: >-
|
||||
Seccomp ограничивает системные вызовы контейнера, уменьшая
|
||||
поверхность атаки на ядро Linux.
|
||||
Используйте RuntimeDefault (профиль рантайма) или Localhost (кастомный).
|
||||
spec:
|
||||
validationFailureAction: Enforce
|
||||
background: true
|
||||
rules:
|
||||
- name: require-seccomp-profile
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
exclude:
|
||||
resources:
|
||||
namespaces:
|
||||
- kube-system
|
||||
validate:
|
||||
message: >-
|
||||
Под '{{ request.object.metadata.name }}' не имеет seccomp профиля.
|
||||
Добавьте в spec.securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
pattern:
|
||||
spec:
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: "RuntimeDefault | Localhost"
|
||||
|
||||
- name: disallow-unconfined-seccomp
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
validate:
|
||||
message: >-
|
||||
Тип Unconfined отключает seccomp защиту. Используйте RuntimeDefault.
|
||||
deny:
|
||||
conditions:
|
||||
any:
|
||||
- key: "{{ request.object.spec.securityContext.seccompProfile.type }}"
|
||||
operator: Equals
|
||||
value: Unconfined
|
||||
37
02-validation/02-security/restrict-automount-sa-token.yaml
Normal file
37
02-validation/02-security/restrict-automount-sa-token.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: restrict-automount-service-account-token
|
||||
annotations:
|
||||
policies.kyverno.io/title: "Ограничение автомонтирования ServiceAccount токена"
|
||||
policies.kyverno.io/category: Security
|
||||
policies.kyverno.io/severity: medium
|
||||
policies.kyverno.io/subject: Pod
|
||||
policies.kyverno.io/description: >-
|
||||
По умолчанию Kubernetes монтирует токен ServiceAccount в каждый под.
|
||||
Если приложение не использует Kubernetes API, этот токен — лишняя
|
||||
поверхность атаки. CIS Benchmark рекомендует отключать автомонтирование
|
||||
там, где токен не нужен.
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: true
|
||||
rules:
|
||||
- name: check-automount-service-account
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
exclude:
|
||||
resources:
|
||||
namespaces:
|
||||
- kube-system
|
||||
validate:
|
||||
message: >-
|
||||
Под '{{ request.object.metadata.name }}' автоматически монтирует
|
||||
ServiceAccount токен (automountServiceAccountToken: true по умолчанию).
|
||||
Если под не обращается к Kubernetes API, добавьте:
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
pattern:
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
21
02-validation/02-security/test-resources/pod-insecure.yaml
Normal file
21
02-validation/02-security/test-resources/pod-insecure.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-insecure
|
||||
namespace: default
|
||||
spec:
|
||||
hostNetwork: true # нарушение: host namespace
|
||||
hostPID: true # нарушение: host namespace
|
||||
containers:
|
||||
- name: app
|
||||
image: nginx:1.25.3
|
||||
securityContext:
|
||||
privileged: true # нарушение: привилегированный контейнер
|
||||
runAsUser: 0 # нарушение: запуск от root
|
||||
capabilities:
|
||||
add:
|
||||
- SYS_ADMIN # нарушение: опасная capability
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
28
02-validation/02-security/test-resources/pod-secure.yaml
Normal file
28
02-validation/02-security/test-resources/pod-secure.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-secure
|
||||
namespace: default
|
||||
spec:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
automountServiceAccountToken: false
|
||||
containers:
|
||||
- name: app
|
||||
image: nginx:1.25.3
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
Reference in New Issue
Block a user