Модуль 4.3: Управління секретами
Складність:
[MEDIUM]— критична навичка CKSЧас на виконання: 45-50 хвилин
Передумови: Модуль 4.2 (Pod Security Admission), основи RBAC
Що ви зможете робити
Розділ «Що ви зможете робити»Після завершення цього модуля ви зможете:
- Налаштувати шифрування etcd у стані спокою для Kubernetes Secrets
- Реалізувати зовнішнє управління секретами за допомогою Vault або сховищ секретів хмарних провайдерів
- Аудитувати дозволи RBAC для виявлення надмірно широкого доступу до ресурсів Secret
- Спроєктувати стратегію управління секретами, що усуває ризики зберігання лише в base64
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Kubernetes Secrets зберігають конфіденційні дані, такі як паролі, ключі API та сертифікати. За замовчуванням вони лише кодуються в base64 (не шифруються!) і доступні будь-кому з відповідними дозволами RBAC. Належне управління секретами запобігає витоку облікових даних та підвищенню привілеїв.
CKS значною мірою тестує практики безпеки секретів.
Проблеми безпеки секретів
Розділ «Проблеми безпеки секретів»┌─────────────────────────────────────────────────────────────┐│ БЕЗПЕКА СЕКРЕТІВ ЗА ЗАМОВЧУВАННЯМ │├─────────────────────────────────────────────────────────────┤│ ││ Base64 — це НЕ шифрування! ││ ───────────────────────────────────────────────────────── ││ $ echo "mysecretpassword" | base64 ││ bXlzZWNyZXRwYXNzd29yZAo= ││ ││ $ echo "bXlzZWNyZXRwYXNzd29yZAo=" | base64 -d ││ mysecretpassword ││ ││ Проблеми секретів за замовчуванням: ││ ├── Зберігаються нешифрованими в etcd ││ ├── Видимі для всіх з дозволом get secrets ││ ├── Відображаються в специфікаціях Pod (kubectl describe) ││ ├── Можуть потрапити в журнали аудиту ││ └── Монтуються як текстові файли в контейнерах ││ │└─────────────────────────────────────────────────────────────┘Створення секретів
Розділ «Створення секретів»Generic Secret
Розділ «Generic Secret»# From literal valueskubectl create secret generic db-creds \ --from-literal=username=admin \ --from-literal=password=secretpass123
# From fileskubectl create secret generic ssh-key \ --from-file=id_rsa=/path/to/id_rsa \ --from-file=id_rsa.pub=/path/to/id_rsa.pub
# From env filekubectl create secret generic app-config \ --from-env-file=secrets.envTLS Secret
Розділ «TLS Secret»# Create TLS secretkubectl create secret tls web-tls \ --cert=server.crt \ --key=server.keyDocker Registry Secret
Розділ «Docker Registry Secret»# Create registry credentialkubectl create secret docker-registry regcred \ --docker-server=registry.example.com \ --docker-username=user \ --docker-password=password \ --docker-email=user@example.comВикористання секретів у Pod
Розділ «Використання секретів у Pod»Змінні середовища
Розділ «Змінні середовища»apiVersion: v1kind: Podmetadata: name: secret-env-podspec: containers: - name: app image: nginx env: - name: DB_USERNAME valueFrom: secretKeyRef: name: db-creds key: username - name: DB_PASSWORD valueFrom: secretKeyRef: name: db-creds key: passwordМонтування томів (рекомендовано)
Розділ «Монтування томів (рекомендовано)»apiVersion: v1kind: Podmetadata: name: secret-volume-podspec: containers: - name: app image: nginx volumeMounts: - name: secrets mountPath: /etc/secrets readOnly: true volumes: - name: secrets secret: secretName: db-creds # Optional: set specific permissions defaultMode: 0400Чому монтування томів краще
Розділ «Чому монтування томів краще»┌─────────────────────────────────────────────────────────────┐│ ЗМІННІ СЕРЕДОВИЩА проти МОНТУВАННЯ ТОМІВ │├─────────────────────────────────────────────────────────────┤│ ││ Змінні середовища: ││ ├── Видимі в /proc/<pid>/environ ││ ├── Можуть потрапити до дочірніх процесів ││ ├── Часто логуються додатками ││ └── Видимі в 'docker inspect' ││ ││ Монтування томів: ││ ├── Файли з обмеженими правами доступу ││ ├── tmpfs (у пам'яті, не записуються на диск) ││ ├── Автоматично оновлюються при зміні секрету ││ └── Контрольований доступ через права файлів ││ ││ Найкраща практика: завжди використовуйте монтування томів ││ │└─────────────────────────────────────────────────────────────┘Шифрування у стані спокою
Розділ «Шифрування у стані спокою»Перевірка поточного стану шифрування
Розділ «Перевірка поточного стану шифрування»# Check API server configurationps aux | grep kube-apiserver | grep encryption-provider-config
# Or check the manifestcat /etc/kubernetes/manifests/kube-apiserver.yaml | grep encryptionУвімкнення шифрування etcd
Розділ «Увімкнення шифрування etcd»apiVersion: apiserver.config.k8s.io/v1kind: EncryptionConfigurationresources: - resources: - secrets providers: # aescbc - recommended for production - aescbc: keys: - name: key1 secret: <base64-encoded-32-byte-key> # identity is the fallback (unencrypted) - identity: {}Генерація ключа шифрування
Розділ «Генерація ключа шифрування»# Generate random 32-byte keyhead -c 32 /dev/urandom | base64
# Example output (use your own!):# K8sSecretEncryptionKey1234567890ABCDEF==Налаштування API Server
Розділ «Налаштування API Server»apiVersion: v1kind: Podmetadata: name: kube-apiserverspec: containers: - command: - kube-apiserver # Add this flag - --encryption-provider-config=/etc/kubernetes/enc/encryption-config.yaml volumeMounts: # Mount the encryption config - mountPath: /etc/kubernetes/enc name: enc readOnly: true volumes: - hostPath: path: /etc/kubernetes/enc type: DirectoryOrCreate name: encПеревірка роботи шифрування
Розділ «Перевірка роботи шифрування»# Create a test secretkubectl create secret generic test-encryption --from-literal=mykey=myvalue
# Read directly from etcd (on control plane)ETCDCTL_API=3 etcdctl get /registry/secrets/default/test-encryption \ --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key | hexdump -C
# If encrypted: You'll see random bytes, not readable text# If NOT encrypted: You'll see "mykey" and "myvalue" in plain textПовторне шифрування наявних секретів
Розділ «Повторне шифрування наявних секретів»# After enabling encryption, re-encrypt all existing secretskubectl get secrets -A -o json | kubectl replace -f -Провайдери шифрування
Розділ «Провайдери шифрування»┌─────────────────────────────────────────────────────────────┐│ ПРОВАЙДЕРИ ШИФРУВАННЯ │├─────────────────────────────────────────────────────────────┤│ ││ identity (за замовчуванням) ││ └── Без шифрування, відкрите зберігання ││ ││ aescbc (рекомендований) ││ └── AES-CBC з доповненням PKCS#7 ││ Надійний, широко підтримуваний ││ ││ aesgcm ││ └── Автентифіковане шифрування AES-GCM ││ Швидший, потрібна ротація ключів кожні 200K записів ││ ││ kms ││ └── Зовнішній KMS провайдер (AWS KMS, Azure Key Vault) ││ Найкращий для продакшену, ключі не потрапляють в etcd ││ ││ secretbox ││ └── XSalsa20 + Poly1305 ││ Надійний, фіксований розмір nonce ││ ││ Порядок має значення: перший провайдер шифрує нові секрети││ Всі перелічені провайдери можуть дешифрувати ││ │└─────────────────────────────────────────────────────────────┘RBAC для секретів
Розділ «RBAC для секретів»Обмеження доступу до секретів
Розділ «Обмеження доступу до секретів»# Only allow access to specific secretsapiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: secret-reader namespace: productionrules:- apiGroups: [""] resources: ["secrets"] resourceNames: ["app-config", "db-creds"] # Specific secrets only verbs: ["get"]Небезпечні шаблони RBAC
Розділ «Небезпечні шаблони RBAC»# НЕ РОБІТЬ ЦЕ - надає доступ до ВСІХ секретівapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: dangerous-rolerules:- apiGroups: [""] resources: ["secrets"] verbs: ["get", "list", "watch"] # Can read ALL secrets cluster-wide!Аудит доступу до секретів
Розділ «Аудит доступу до секретів»# Find who can access secretskubectl auth can-i get secrets --as=system:serviceaccount:default:defaultkubectl auth can-i list secrets --as=system:serviceaccount:kube-system:default
# List all roles that can access secretskubectl get clusterroles -o json | jq '.items[] | select(.rules[]?.resources[]? == "secrets") | .metadata.name'Запобігання витоку секретів
Розділ «Запобігання витоку секретів»Вимкнення автоматичного монтування секретів
Розділ «Вимкнення автоматичного монтування секретів»apiVersion: v1kind: Podmetadata: name: no-automount-podspec: automountServiceAccountToken: false # Don't mount SA token containers: - name: app image: nginxВикористання монтування тільки для читання
Розділ «Використання монтування тільки для читання»apiVersion: v1kind: Podmetadata: name: readonly-secretsspec: containers: - name: app image: nginx volumeMounts: - name: secrets mountPath: /etc/secrets readOnly: true # Prevent modification volumes: - name: secrets secret: secretName: app-secrets defaultMode: 0400 # Read-only for ownerРеальні сценарії іспиту
Розділ «Реальні сценарії іспиту»Сценарій 1: Увімкнення шифрування etcd
Розділ «Сценарій 1: Увімкнення шифрування etcd»# Step 1: Create encryption config directorysudo mkdir -p /etc/kubernetes/enc
# Step 2: Generate encryption keyENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
# Step 3: Create encryption configsudo tee /etc/kubernetes/enc/encryption-config.yaml << EOFapiVersion: apiserver.config.k8s.io/v1kind: EncryptionConfigurationresources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: ${ENCRYPTION_KEY} - identity: {}EOF
# Step 4: Edit API server manifestsudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
# Add to command:# - --encryption-provider-config=/etc/kubernetes/enc/encryption-config.yaml
# Add volume mount:# volumeMounts:# - mountPath: /etc/kubernetes/enc# name: enc# readOnly: true
# Add volume:# volumes:# - hostPath:# path: /etc/kubernetes/enc# type: DirectoryOrCreate# name: enc
# Step 5: Wait for API server to restartkubectl get nodes # Wait until this works
# Step 6: Re-encrypt existing secretskubectl get secrets -A -o json | kubectl replace -f -Сценарій 2: Виправлення RBAC для секретів
Розділ «Сценарій 2: Виправлення RBAC для секретів»# Find ServiceAccount with too much secret accesskubectl get rolebindings,clusterrolebindings -A -o json | \ jq -r '.items[] | select(.roleRef.name | contains("secret")) | "\(.metadata.namespace // "cluster")/\(.metadata.name) -> \(.roleRef.name)"'
# Create restrictive rolecat <<EOF | kubectl apply -f -apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: app-secret-reader namespace: defaultrules:- apiGroups: [""] resources: ["secrets"] resourceNames: ["app-config"] # Only this secret verbs: ["get"]EOFСценарій 3: Створення секрету з файлу
Розділ «Сценарій 3: Створення секрету з файлу»# Create secret containing certificatekubectl create secret generic tls-cert \ --from-file=tls.crt=./server.crt \ --from-file=tls.key=./server.key \ -n production
# Use in pod with volume mountcat <<EOF | kubectl apply -f -apiVersion: v1kind: Podmetadata: name: secure-app namespace: productionspec: containers: - name: app image: nginx volumeMounts: - name: tls mountPath: /etc/tls readOnly: true volumes: - name: tls secret: secretName: tls-cert defaultMode: 0400EOFЗовнішнє управління секретами
Розділ «Зовнішнє управління секретами»┌─────────────────────────────────────────────────────────────┐│ ЗОВНІШНІ РІШЕННЯ ДЛЯ СЕКРЕТІВ │├─────────────────────────────────────────────────────────────┤│ ││ HashiCorp Vault ││ └── Галузевий стандарт, багаті можливості ││ Vault Agent Injector для Kubernetes ││ ││ AWS Secrets Manager + External Secrets Operator ││ └── Нативна інтеграція з AWS ││ Синхронізує секрети AWS до Kubernetes ││ ││ Azure Key Vault ││ └── Нативне рішення Azure ││ Доступний CSI driver ││ ││ Sealed Secrets (Bitnami) ││ └── Шифрування секретів для зберігання в Git ││ Тільки кластер може дешифрувати ││ ││ Примітка: Зовнішні рішення НЕ входять до іспиту CKS, ││ але їх розуміння демонструє зрілість у безпеці ││ │└─────────────────────────────────────────────────────────────┘Чи знали ви?
Розділ «Чи знали ви?»-
Base64 — це лише кодування, а не шифрування. Будь-хто може його декодувати. Іспит CKS перевіряє, чи розумієте ви цю критичну відмінність.
-
etcd зберігає секрети відкритим текстом за замовчуванням. Без шифрування у стані спокою будь-хто з доступом до etcd може прочитати всі секрети кластера.
-
Секрети, змонтовані як томи, зберігаються в tmpfs (пам’яті), а не на диску. Вони безпечніші за змінні середовища.
-
Порядок конфігурації шифрування має значення. Нові секрети шифруються першим провайдером. Всі перелічені провайдери можуть дешифрувати, що дозволяє ротацію ключів.
Поширені помилки
Розділ «Поширені помилки»| Помилка | Чому це шкідливо | Рішення |
|---|---|---|
| Вважати base64 безпечним | Дані відкриті | Увімкнути шифрування у стані спокою |
| Використовувати змінні середовища | Витік у логи | Використовувати монтування томів |
| Широкий RBAC для секретів | Будь-який Pod може прочитати | Використовувати resourceNames |
| Не перешифровувати після увімкнення | Старі секрети нешифровані | Виконати kubectl replace |
| Секрети в Git | Постійний витік | Використовувати Sealed Secrets |
Тест
Розділ «Тест»-
Чи є кодування base64 шифруванням?
Відповідь
Ні! Base64 — це зворотне кодування, а не шифрування. Будь-хто може декодувати його за допомогою `base64 -d`. Секрети потребують шифрування у стані спокою для справжньої безпеки. -
Де слід зберігати конфігурацію шифрування?
Відповідь
На вузлі площини управління, зазвичай `/etc/kubernetes/enc/encryption-config.yaml`, із посиланням через прапорець API server `--encryption-provider-config`. -
Чому монтування томів переважає над змінними середовища для секретів?
Відповідь
Монтування томів: зберігаються в tmpfs (пам'яті), мають права доступу до файлів, автоматично оновлюються при зміні секрету. Змінні середовища: видимі в /proc, можуть потрапити до дочірніх процесів, часто логуються. -
Як перевірити, що секрети зашифровані в etcd?
Відповідь
Прочитати безпосередньо з etcd за допомогою etcdctl. Якщо зашифровано, ви побачите випадкові байти. Якщо не зашифровано, ви побачите значення секретів відкритим текстом.
Практична вправа
Розділ «Практична вправа»Завдання: Увімкнути шифрування у стані спокою та перевірити його роботу.
# Step 1: Check current encryption statusps aux | grep kube-apiserver | grep encryption-provider-config || echo "Not configured"
# Step 2: Create test secret BEFORE encryptionkubectl create secret generic pre-encryption --from-literal=test=beforeencryption
# Step 3: Create encryption config (on control plane node)sudo mkdir -p /etc/kubernetes/enc
ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)sudo tee /etc/kubernetes/enc/encryption-config.yaml << EOFapiVersion: apiserver.config.k8s.io/v1kind: EncryptionConfigurationresources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: ${ENCRYPTION_KEY} - identity: {}EOF
# Step 4: Backup API server manifestsudo cp /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/kube-apiserver.yaml.bak
# Step 5: Edit API server manifest (add encryption config)# Add: --encryption-provider-config=/etc/kubernetes/enc/encryption-config.yaml# Add volume and volumeMount for /etc/kubernetes/enc
# Step 6: Wait for API server restartsleep 30kubectl get nodes
# Step 7: Create test secret AFTER encryptionkubectl create secret generic post-encryption --from-literal=test=afterencryption
# Step 8: Re-encrypt pre-existing secretkubectl get secret pre-encryption -o json | kubectl replace -f -
# Step 9: Verify in etcd (if you have access)# Encrypted secrets show random bytes, not plain text
# Cleanupkubectl delete secret pre-encryption post-encryptionКритерії успіху: Розуміти конфігурацію та перевірку шифрування.
Підсумок
Розділ «Підсумок»Проблеми безпеки секретів:
- Base64 — це НЕ шифрування
- etcd зберігає відкритий текст за замовчуванням
- Змінні середовища мають витоки
Найкращі практики:
- Увімкнути шифрування у стані спокою (aescbc)
- Використовувати монтування томів, а не змінні середовища
- Обмежити RBAC за допомогою resourceNames
- Перешифрувати після увімкнення шифрування
Налаштування шифрування:
- Створити EncryptionConfiguration
- Додати прапорець API server
- Перезапустити API server
- Перешифрувати наявні секрети
Поради для іспиту:
- Знайте формат конфігурації шифрування
- Розумійте порядок провайдерів
- Вмійте перевірити роботу шифрування
Наступний модуль
Розділ «Наступний модуль»Модуль 4.4: Ізоляція середовища виконання — gVisor та Kata Containers для ізоляції контейнерів.