This commit is contained in:
2026-04-08 20:22:14 +07:00
commit 34fbdd1412
96 changed files with 5321 additions and 0 deletions

View File

@@ -0,0 +1,172 @@
# Урок 7.2 — Работа с внешними данными и API
## Файлы
| Файл | Описание |
|------|----------|
| `check-image-vulnerabilities.yaml` | Проверка уязвимостей через внешний API |
| `external-data-cache.yaml` | ConfigMap + CronJob + RBAC для кэша внешних данных |
| `validate-registry-from-cache.yaml` | Валидация реестра через кэшированный ConfigMap |
## Паттерн 1: Прямой вызов внешнего API
```yaml
# Требует включения в Kyverno:
# admissionController.extraArgs:
# - --enableExternalDataCall=true
context:
- name: result
apiCall:
urlPath: "https://your-api.company.com/check"
method: POST
data:
- key: image
value: "{{ request.object.spec.containers[0].image }}"
jmesPath: "status"
```
### Включение external data calls
```bash
helm upgrade kyverno kyverno/kyverno \
--namespace kyverno \
--reuse-values \
--set admissionController.extraArgs="{--enableExternalDataCall=true}"
# Проверить
kubectl get deployment kyverno-admission-controller -n kyverno \
-o jsonpath='{.spec.template.spec.containers[0].args}' | \
grep enableExternalDataCall
```
## Паттерн 2: Кэш через ConfigMap (рекомендуется)
Прямые вызовы внешних API медленные и создают зависимость.
Лучший паттерн: CronJob обновляет ConfigMap → политика читает из ConfigMap.
```
Внешний API ──(каждые N минут)──► CronJob ──► ConfigMap
Kyverno политика ◄───┘
(кэш, быстро)
```
### Применение кэша
```bash
# Применить всё: ConfigMap + CronJob + RBAC
kubectl apply -f external-data-cache.yaml
# Проверить ConfigMap
kubectl get configmap external-data-cache -n kyverno -o yaml
# Запустить CronJob вручную для немедленного обновления
kubectl create job --from=cronjob/update-policy-cache \
manual-update -n kyverno
# Следить за логами
kubectl logs -n kyverno \
-l job-name=manual-update \
--follow
# Применить политику использующую кэш
kubectl apply -f validate-registry-from-cache.yaml
```
## Паттерн 3: HashiCorp Vault интеграция
```bash
# Проверить доступность Vault перед деплоем с Vault секретами
kubectl apply -f - <<EOF
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: check-vault-availability
spec:
rules:
- name: verify-vault-health
match:
resources:
kinds: [Pod]
preconditions:
any:
- key: "{{ request.object.metadata.annotations.\"vault.hashicorp.com/agent-inject\" }}"
operator: Equals
value: "true"
context:
- name: vaultHealth
apiCall:
urlPath: "https://vault.company.com:8200/v1/sys/health"
jmesPath: "initialized"
validate:
message: "Vault недоступен. Деплой с Vault секретами временно заблокирован."
deny:
conditions:
- key: "{{ vaultHealth }}"
operator: NotEquals
value: true
EOF
```
## Обработка недоступности внешнего API
```bash
# Fail-open vs Fail-closed
# Выбор зависит от критичности проверки
# Fail-open (разрешить если API недоступен):
# jmesPath: "result || 'ALLOWED'"
# deny если result == "DENIED"
# При недоступности: result = "ALLOWED" → под создаётся
# Fail-closed (запретить если API недоступен):
# jmesPath: "result || 'DENIED'"
# deny если result != "ALLOWED"
# При недоступности: result = "DENIED" → под НЕ создаётся
```
## Настройка timeout для внешних вызовов
```bash
# Webhook timeout влияет на все вызовы включая внешние API
helm upgrade kyverno kyverno/kyverno \
--namespace kyverno \
--reuse-values \
--set config.webhooks.timeoutSeconds=15
# По умолчанию 10 секунд. Увеличьте если внешний API медленный.
# Максимум — 30 секунд (ограничение Kubernetes).
```
## Мониторинг внешних вызовов
```bash
# Проверить что CronJob работает регулярно
kubectl get cronjobs -n kyverno
kubectl get jobs -n kyverno | grep update-policy-cache
# Последнее обновление кэша
kubectl get configmap external-data-cache -n kyverno \
-o jsonpath='{.data.last-updated}'
# Алерт если кэш устарел (добавьте в PrometheusRule)
# Используйте kube_configmap_info метрику и проверяйте
# что last-updated не старше N минут
```
## Безопасность при работе с внешними данными
```bash
# НЕ хардкодьте токены в политиках — используйте Secret
kubectl create secret generic external-api-token \
--from-literal=token=your-secret-token \
-n kyverno
# В политике читаем из Secret
# context:
# - name: apiToken
# apiCall:
# urlPath: "/api/v1/namespaces/kyverno/secrets/external-api-token"
# jmesPath: "data.token | base64_decode(@)"
```

View File

@@ -0,0 +1,54 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: check-image-vulnerabilities
annotations:
policies.kyverno.io/title: "Проверка уязвимостей образов через внешний API"
policies.kyverno.io/category: Security
policies.kyverno.io/severity: critical
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Проверяет образ контейнера через внешний vulnerability scanning API
перед деплоем в production. Блокирует деплой если найдены
критические уязвимости.
НАСТРОЙТЕ URL вашего сканера (Trivy, Snyk, Grype и т.д.)
Требует: admissionController.extraArgs: [--enableExternalDataCall=true]
spec:
validationFailureAction: Enforce
background: false # только для живых запросов, не background scan
rules:
- name: check-critical-vulnerabilities
match:
resources:
kinds:
- Pod
namespaces:
- production
preconditions:
any:
- key: "{{ request.operation }}"
operator: In
value: [CREATE, UPDATE]
context:
- name: scanResult
apiCall:
# Замените на URL вашего vulnerability API
urlPath: "https://vuln-api.company.com/v1/scan"
method: POST
data:
- key: image
value: "{{ request.object.spec.containers[0].image }}"
- key: severity
value: CRITICAL
jmesPath: "critical_count || `0`"
validate:
message: >-
Образ '{{ request.object.spec.containers[0].image }}'
содержит {{ scanResult }} критических уязвимостей.
Деплой в production запрещён.
Обновите базовый образ и пересоберите: https://vuln-api.company.com/report
deny:
conditions:
- key: "{{ scanResult }}"
operator: GreaterThan
value: "0"

View File

@@ -0,0 +1,105 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: external-data-cache
namespace: kyverno
labels:
app: kyverno-config
data:
# Список разрешённых реестров (обновляется CronJob)
allowed-registries: |
registry.company.com
gcr.io/company-project
public.ecr.aws/company
# Список одобренных StorageClass
approved-storage-classes: |
standard-ssd
premium-ssd
backup-hdd
# Последнее обновление (проставляется CronJob)
last-updated: "2024-01-01T00:00:00Z"
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: update-policy-cache
namespace: kyverno
annotations:
description: >-
Обновляет ConfigMap external-data-cache данными из внешних API.
Позволяет политикам использовать актуальные данные без прямых apiCall
к внешним сервисам на каждый запрос.
spec:
schedule: "*/10 * * * *" # каждые 10 минут
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
serviceAccountName: policy-cache-updater
restartPolicy: OnFailure
containers:
- name: cache-updater
image: bitnami/kubectl:1.28
env:
- name: EXTERNAL_API_URL
value: "https://api.company.com/v1"
- name: CONFIGMAP_NAME
value: "external-data-cache"
- name: NAMESPACE
value: "kyverno"
command:
- /bin/bash
- -c
- |
set -e
echo "Fetching allowed registries from external API..."
# В реальности заменить на curl к вашему API
REGISTRIES=$(echo -e "registry.company.com\ngcr.io/company-project")
echo "Updating ConfigMap..."
kubectl patch configmap ${CONFIGMAP_NAME} \
-n ${NAMESPACE} \
--type merge \
-p "{\"data\":{
\"allowed-registries\": \"${REGISTRIES}\",
\"last-updated\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"
}}"
echo "Cache updated successfully"
resources:
limits:
cpu: 100m
memory: 64Mi
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: policy-cache-updater
namespace: kyverno
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: configmap-updater
namespace: kyverno
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["external-data-cache"]
verbs: ["get", "patch", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: policy-cache-updater
namespace: kyverno
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: configmap-updater
subjects:
- kind: ServiceAccount
name: policy-cache-updater
namespace: kyverno

View File

@@ -0,0 +1,54 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: validate-registry-from-cache
annotations:
policies.kyverno.io/title: "Проверка реестра через кэшированные данные"
policies.kyverno.io/category: Security
policies.kyverno.io/severity: high
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Проверяет образ контейнера против списка разрешённых реестров,
хранящегося в ConfigMap external-data-cache.
Список обновляется каждые 10 минут CronJob из внешнего API.
Паттерн "кэш в ConfigMap" — быстрее чем прямые apiCall к внешним API.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: check-registry-from-cache
match:
resources:
kinds:
- Pod
exclude:
resources:
namespaces:
- kube-system
- kyverno
context:
- name: allowedRegistries
configMap:
name: external-data-cache
namespace: kyverno
validate:
message: >-
Образ '{{ element.image }}' из недоверенного реестра.
Список разрешённых реестров (обновлён {{ allowedRegistries.data.\"last-updated\" }}):
{{ allowedRegistries.data.\"allowed-registries\" }}
foreach:
- list: >-
request.object.spec.containers[] |
merge(request.object.spec.initContainers[] || `[]`, @)
deny:
conditions:
all:
- key: "{{ element.image }}"
operator: NotStartsWith
value: "registry.company.com/"
- key: "{{ element.image }}"
operator: NotStartsWith
value: "gcr.io/company-project/"
- key: "{{ element.image }}"
operator: NotStartsWith
value: "public.ecr.aws/company/"