Модуль 4.2: Secrets
Складність:
[MEDIUM]— Схоже на ConfigMaps, але з міркуваннями безпекиЧас на виконання: 40–50 хвилин
Передумови: Модуль 4.1 (ConfigMaps), розуміння кодування base64
Що ви зможете робити
Розділ «Що ви зможете робити»Після завершення цього модуля ви зможете:
- Створити Secrets за допомогою
kubectl create secretдля типів generic, TLS та docker-registry - Налаштувати Піди для безпечного використання Secrets як змінних оточення та монтування томів
- Пояснити як Kubernetes зберігає Secrets (base64, не зашифровано) та наслідки для безпеки
- Діагностувати помилки автентифікації, спричинені неправильно закодованими або відсутніми даними Secret
Чому цей модуль важливий
Розділ «Чому цей модуль важливий»Secrets зберігають конфіденційні дані, такі як паролі, API-ключі та TLS-сертифікати. Хоча за використанням вони схожі на ConfigMaps, Secrets мають додаткові функції безпеки та спеціально призначені для конфіденційної інформації.
На іспиті CKAD перевіряють вашу здатність:
- Створювати Secrets з літералів, файлів та YAML
- Використовувати Secrets як змінні оточення та томи
- Розуміти різні типи Secrets
- Знати наслідки для безпеки
Аналогія з банківською коміркою
Якщо ConfigMaps — це публічна дошка оголошень, то Secrets — це банківські комірки. Дані все ще доступні авторизованим сторонам (подам), але зберігаються ретельніше, обробляються інакше, і ви б не стали розміщувати там те, що готові показати публічно.
Створення Secrets
Розділ «Створення Secrets»Загальні Secrets (з літералів)
Розділ «Загальні Secrets (з літералів)»# Одна пара ключ-значенняk create secret generic db-secret --from-literal=password=mysecretpassword
# Кілька пар ключ-значенняk create secret generic db-secret \ --from-literal=username=admin \ --from-literal=password=mysecretpassword \ --from-literal=host=db.example.comЗ файлів
Розділ «З файлів»# Створити файли з конфіденційними данимиecho -n 'admin' > username.txtecho -n 'mysecretpassword' > password.txt
# Створити Secret з файлівk create secret generic db-secret \ --from-file=username=username.txt \ --from-file=password=password.txt
# Очистити файлиrm username.txt password.txtЗ YAML (кодування Base64)
Розділ «З YAML (кодування Base64)»apiVersion: v1kind: Secretmetadata: name: db-secrettype: Opaquedata: username: YWRtaW4= # base64 від 'admin' password: bXlzZWNyZXQ= # base64 від 'mysecret'З YAML (відкритий текст через stringData)
Розділ «З YAML (відкритий текст через stringData)»apiVersion: v1kind: Secretmetadata: name: db-secrettype: OpaquestringData: username: admin password: mysecretПримітка: stringData доступний лише для запису та перетворюється на data (base64) при збереженні.
Типи Secrets
Розділ «Типи Secrets»| Тип | Призначення |
|---|---|
Opaque | За замовчуванням, довільні дані користувача |
kubernetes.io/service-account-token | Токени Сервісного акаунта |
kubernetes.io/dockerconfigjson | Облікові дані реєстру Docker |
kubernetes.io/tls | TLS-сертифікат і ключ |
kubernetes.io/basic-auth | Базова автентифікація |
kubernetes.io/ssh-auth | Облікові дані SSH |
Secret реєстру Docker
Розділ «Secret реєстру Docker»k create secret docker-registry my-registry \ --docker-server=registry.example.com \ --docker-username=user \ --docker-password=pass \ --docker-email=user@example.comTLS Secret
Розділ «TLS Secret»k create secret tls my-tls \ --cert=path/to/cert.pem \ --key=path/to/key.pemВикористання Secrets
Розділ «Використання Secrets»Як змінні оточення
Розділ «Як змінні оточення»Одна змінна:
apiVersion: v1kind: Podmetadata: name: appspec: containers: - name: app image: nginx env: - name: DB_PASSWORD valueFrom: secretKeyRef: name: db-secret key: passwordУсі ключі як змінні:
apiVersion: v1kind: Podmetadata: name: appspec: containers: - name: app image: nginx envFrom: - secretRef: name: db-secretЯк файли тому
Розділ «Як файли тому»apiVersion: v1kind: Podmetadata: name: appspec: containers: - name: app image: nginx volumeMounts: - name: secret-volume mountPath: /etc/secrets readOnly: true volumes: - name: secret-volume secret: secretName: db-secretЗ конкретними правами доступу
Розділ «З конкретними правами доступу»volumes:- name: secret-volume secret: secretName: db-secret defaultMode: 0400 # Лише читання для власникаМонтування конкретних ключів
Розділ «Монтування конкретних ключів»volumes:- name: secret-volume secret: secretName: db-secret items: - key: password path: db-passwordКодування/декодування Base64
Розділ «Кодування/декодування Base64»# Закодуватиecho -n 'mysecret' | base64# bXlzZWNyZXQ=
# Декодуватиecho 'bXlzZWNyZXQ=' | base64 -d# mysecret
# Переглянути декодований secretk get secret db-secret -o jsonpath='{.data.password}' | base64 -dВажливо: Використовуйте -n з echo, щоб уникнути кодування символу нового рядка!
Міркування безпеки
Розділ «Міркування безпеки»Що забезпечують Secrets
Розділ «Що забезпечують Secrets»- Кодування Base64 (не шифрування!)
- Зберігання в etcd (можна зашифрувати у стані спокою з відповідною конфігурацією)
- Захист RBAC — контроль того, хто може читати secrets
- Обмежена видимість — не показуються у виводі
kubectl get
Чого Secrets не забезпечують
Розділ «Чого Secrets не забезпечують»- Шифрування за замовчуванням — base64 — це кодування, а не шифрування
- Захист пам’яті — secrets у подах зберігаються у відкритому тексті в пам’яті
- Захист від логування — застосунки можуть записувати значення secrets у логи
Найкращі практики
Розділ «Найкращі практики»# Монтувати як лише для читанняvolumeMounts:- name: secrets mountPath: /etc/secrets readOnly: true
# Використовувати конкретні права доступуvolumes:- name: secrets secret: secretName: my-secret defaultMode: 0400Візуалізація
Розділ «Візуалізація»┌─────────────────────────────────────────────────────────────┐│ Потік Secrets │├─────────────────────────────────────────────────────────────┤│ ││ Створення Secret ││ ┌─────────────────────────────────────┐ ││ │ k create secret generic db-secret │ ││ │ --from-literal=pass=mysecret │ ││ └─────────────────────────────────────┘ ││ │ ││ ▼ ││ Зберігається в etcd (base64) ││ ┌─────────────────────────────────────┐ ││ │ data: │ ││ │ pass: bXlzZWNyZXQ= │ ││ └─────────────────────────────────────┘ ││ │ ││ ┌─────────┴─────────┐ ││ ▼ ▼ ││ ┌──────────────┐ ┌──────────────┐ ││ │ Змінна │ │ Монтування │ ││ │ оточення │ │ тому │ ││ │ │ │ │ ││ │ $PASS= │ │ /secrets/ │ ││ │ "mysecret" │ │ файл pass │ ││ │ (декодовано) │ │ (декодовано) │ ││ └──────────────┘ └──────────────┘ ││ │└─────────────────────────────────────────────────────────────┘Secrets проти ConfigMaps
Розділ «Secrets проти ConfigMaps»| Характеристика | ConfigMap | Secret |
|---|---|---|
| Кодування даних | Відкритий текст | Base64 |
| Призначення | Неконфіденційна конфігурація | Конфіденційні дані |
| Обмеження розміру | 1 МБ | 1 МБ |
| Шифрування у стані спокою | Ні | За бажанням |
| Спеціальні типи | Ні | Так (TLS, docker-registry) |
| Права монтування | За замовчуванням | Можна обмежити (0400) |
Швидка довідка
Розділ «Швидка довідка»# Створитиk create secret generic NAME --from-literal=KEY=VALUEk create secret generic NAME --from-file=FILEk create secret tls NAME --cert=CERT --key=KEYk create secret docker-registry NAME --docker-server=... --docker-username=...
# Переглянути (у кодуванні base64)k get secret NAME -o yaml
# Декодувати конкретний ключk get secret NAME -o jsonpath='{.data.KEY}' | base64 -d
# Редагуватиk edit secret NAME
# Видалитиk delete secret NAMEЧи знали ви?
Розділ «Чи знали ви?»-
Base64 — це не шифрування. Будь-хто з доступом до кластера може декодувати secrets. Це лише кодування для безпечної роботи з бінарними даними у YAML.
-
Kubernetes може шифрувати secrets у стані спокою в etcd за допомогою EncryptionConfiguration. Це налаштування адміністратора кластера, не тема іспиту CKAD.
-
Secrets мають простір імен. Під може отримати доступ лише до secrets у своєму власному просторі імен (якщо RBAC не дозволяє міжпросторовий доступ).
-
Змінні оточення зі secrets можуть витікати у логах, дампах збоїв або при виведенні застосунками. Монтування томів загалом безпечніше.
Типові помилки
Розділ «Типові помилки»| Помилка | Чому це шкодить | Рішення |
|---|---|---|
Забули -n при кодуванні | Символ нового рядка кодується разом з даними | Завжди використовуйте echo -n |
| Вважати, що base64 — це безпечно | Будь-хто може декодувати | Використовуйте належний RBAC + шифрування у стані спокою |
| Логування змінних оточення зі secrets | Secrets відкриті у логах | Монтуйте як файли, не логуйте |
| Не встановлювати readOnly | Контейнер може змінити монтування | Завжди використовуйте readOnly: true |
| Збереження secrets у git | Secrets відкриті у репозиторії | Використовуйте зовнішнє керування secrets |
Тест
Розділ «Тест»-
Як створити secret з іменем користувача та паролем?
Відповідь
`kubectl create secret generic my-secret --from-literal=username=admin --from-literal=password=secret` -
Як декодувати значення secret?
Відповідь
`kubectl get secret my-secret -o jsonpath='{.data.password}' | base64 -d` -
Яка різниця між
dataтаstringDataу YAML Secret?Відповідь
`data` вимагає значення у кодуванні base64. `stringData` приймає відкритий текст, і Kubernetes кодує його автоматично. `stringData` доступний лише для запису та перетворюється на `data` при збереженні. -
Чи кодування base64 — це те саме, що шифрування?
Відповідь
Ні. Base64 — це зворотне кодування, а не шифрування. Будь-хто може його декодувати. Для справжнього шифрування потрібне шифрування etcd у стані спокою або зовнішнє керування secrets.
Практична вправа
Розділ «Практична вправа»Завдання: Створити та використати secrets кількома способами.
Підготовка:
# Створити secretk create secret generic app-secret \ --from-literal=api-key=supersecretkey123 \ --from-literal=db-password=dbpass456Частина 1: Змінні оточення
cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: secret-envspec: containers: - name: app image: busybox command: ['sh', '-c', 'echo "API Key: $API_KEY" && echo "DB Pass: $DB_PASSWORD" && sleep 3600'] env: - name: API_KEY valueFrom: secretKeyRef: name: app-secret key: api-key - name: DB_PASSWORD valueFrom: secretKeyRef: name: app-secret key: db-passwordEOF
k logs secret-envЧастина 2: Монтування тому
cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: secret-volspec: containers: - name: app image: busybox command: ['sh', '-c', 'ls -la /secrets && cat /secrets/api-key && sleep 3600'] volumeMounts: - name: secrets mountPath: /secrets readOnly: true volumes: - name: secrets secret: secretName: app-secret defaultMode: 0400EOF
k logs secret-volЧастина 3: Декодування Secret
# Переглянути у кодуванніk get secret app-secret -o yaml
# Декодуватиk get secret app-secret -o jsonpath='{.data.api-key}' | base64 -decho # новий рядокОчищення:
k delete pod secret-env secret-volk delete secret app-secretПрактичні вправи
Розділ «Практичні вправи»Вправа 1: Створення з літералів (Ціль: 1 хвилина)
Розділ «Вправа 1: Створення з літералів (Ціль: 1 хвилина)»k create secret generic drill1 --from-literal=pass=secret123k get secret drill1 -o yamlk delete secret drill1Вправа 2: Декодування Secret (Ціль: 2 хвилини)
Розділ «Вправа 2: Декодування Secret (Ціль: 2 хвилини)»k create secret generic drill2 --from-literal=token=mytoken123k get secret drill2 -o jsonpath='{.data.token}' | base64 -dechok delete secret drill2Вправа 3: Змінна оточення (Ціль: 3 хвилини)
Розділ «Вправа 3: Змінна оточення (Ціль: 3 хвилини)»k create secret generic drill3 --from-literal=DB_PASS=dbsecret
cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: drill3spec: containers: - name: app image: busybox command: ['sh', '-c', 'echo $DB_PASS && sleep 3600'] env: - name: DB_PASS valueFrom: secretKeyRef: name: drill3 key: DB_PASSEOF
k logs drill3k delete pod drill3 secret drill3Вправа 4: Монтування тому (Ціль: 3 хвилини)
Розділ «Вправа 4: Монтування тому (Ціль: 3 хвилини)»k create secret generic drill4 --from-literal=cert=CERTIFICATE_DATA
cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: drill4spec: containers: - name: app image: busybox command: ['sh', '-c', 'cat /certs/cert && sleep 3600'] volumeMounts: - name: certs mountPath: /certs readOnly: true volumes: - name: certs secret: secretName: drill4EOF
k logs drill4k delete pod drill4 secret drill4Вправа 5: YAML з stringData (Ціль: 3 хвилини)
Розділ «Вправа 5: YAML з stringData (Ціль: 3 хвилини)»cat << 'EOF' | k apply -f -apiVersion: v1kind: Secretmetadata: name: drill5type: OpaquestringData: username: admin password: supersecretEOF
# Перевірити, що дані закодованіk get secret drill5 -o yaml | grep -A2 data
# Декодуватиk get secret drill5 -o jsonpath='{.data.password}' | base64 -decho
k delete secret drill5Вправа 6: Повний сценарій (Ціль: 5 хвилин)
Розділ «Вправа 6: Повний сценарій (Ціль: 5 хвилин)»Сценарій: Розгорнути застосунок з обліковими даними бази даних.
# Створити secret бази данихk create secret generic drill6-db \ --from-literal=MYSQL_USER=appuser \ --from-literal=MYSQL_PASSWORD=apppass123 \ --from-literal=MYSQL_DATABASE=myapp
# Розгорнути застосунок з усіма secrets як змінними оточенняcat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: drill6spec: containers: - name: app image: busybox command: ['sh', '-c', 'env | grep MYSQL && sleep 3600'] envFrom: - secretRef: name: drill6-dbEOF
k logs drill6k delete pod drill6 secret drill6-dbНаступний модуль
Розділ «Наступний модуль»Модуль 4.3: Вимоги до ресурсів — Налаштування запитів та лімітів CPU і пам’яті.