Секреты и конфиденциальные данные в Kubernetes: безопасное управление через Secrets, Vault и sealed-secrets
Введение: почему нативные Kubernetes Secrets небезопасны по умолчанию
Kubernetes Secrets — первое, что приходит в голову DevOps-инженеру, когда нужно передать пароль базы данных или API-ключ в Pod. Однако большинство команд недооценивают риски: по умолчанию секреты хранятся в etcd в формате Base64 — это не шифрование, а лишь кодирование. Любой, кто получит доступ к резервной копии etcd или к объекту Secret через kubectl, немедленно прочитает данные в открытом виде.
Реальные угрозы выглядят так:
- Компрометация etcd — злоумышленник с доступом к снапшоту etcd получает все секреты кластера.
- Избыточные RBAC-права — разработчики случайно получают права на
get secretsв production-namespace. - Секреты в Git — манифест с закодированными данными коммитится в репозиторий.
- Отсутствие ротации — один и тот же пароль живёт годами.
В 2026 году управление секретами в Kubernetes — это не опция, а обязательный элемент безопасной инфраструктуры. Разберём подходы и практику.
Обзор подходов к управлению секретами
1. Native Kubernetes Secrets
Встроенный механизм, простой в использовании, но требующий дополнительной защиты:
- Включить шифрование at rest через
EncryptionConfigurationв API-сервере. - Ограничить RBAC: минимальный набор прав на namespace-уровне.
- Включить Audit Logging для отслеживания доступа.
2. HashiCorp Vault
Полноценное хранилище секретов с динамическими credentials, lease-механизмом и детальным аудитом. Интегрируется с Kubernetes через Agent Injector (sidecar) или CSI Provider. Подходит для больших команд и высоких требований к безопасности.
3. Sealed Secrets (Bitnami)
Контроллер для Kubernetes, позволяющий безопасно хранить зашифрованные секреты прямо в Git. Секрет зашифрован публичным ключом кластера и может быть расшифрован только самим кластером. Идеально для GitOps-подхода.
4. External Secrets Operator
Оператор, синхронизирующий секреты из внешних хранилищ (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, Azure Key Vault) в нативные Kubernetes Secrets. Позволяет централизованно управлять секретами за пределами кластера.
Практика: настройка Sealed Secrets в кластере
Sealed Secrets — оптимальный выбор для команд, практикующих GitOps. Рассмотрим пошаговую установку и использование.
Шаг 1: Установка контроллера
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm repo update
helm install sealed-secrets sealed-secrets/sealed-secrets \
--namespace kube-system \
--set fullnameOverride=sealed-secrets-controllerШаг 2: Установка kubeseal CLI
# Linux
wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.26.0/kubeseal-0.26.0-linux-amd64.tar.gz
tar xvf kubeseal-0.26.0-linux-amd64.tar.gz
sudo mv kubeseal /usr/local/bin/Шаг 3: Создание и шифрование секрета
Сначала создаём обычный Secret-манифест, затем шифруем его kubeseal:
kubectl create secret generic db-credentials \
--from-literal=DB_PASSWORD=supersecret123 \
--from-literal=DB_USER=appuser \
--namespace=production \
--dry-run=client -o yaml | \
kubeseal --controller-name=sealed-secrets-controller \
--controller-namespace=kube-system \
--format yaml > sealed-db-credentials.yamlРезультирующий манифест sealed-db-credentials.yaml выглядит так:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: production
spec:
encryptedData:
DB_PASSWORD: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq...
DB_USER: AgAKIBDLs8yHFUVCG+p1k2M3N4...
template:
metadata:
name: db-credentials
namespace: production
type: OpaqueЭтот файл безопасно коммитить в Git — без доступа к приватному ключу кластера расшифровать его невозможно. Применяем манифест:
kubectl apply -f sealed-db-credentials.yamlКонтроллер автоматически создаст нативный Secret в namespace production.
Интеграция HashiCorp Vault с Kubernetes
Agent Injector
Vault Agent Injector работает как mutating admission webhook: при создании Pod с нужными аннотациями в него автоматически добавляется sidecar-контейнер, который аутентифицируется в Vault и монтирует секреты в файловую систему Pod.
Пример аннотаций для Pod:
apiVersion: v1
kind: Pod
metadata:
name: go-app
namespace: production
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "go-app-role"
vault.hashicorp.com/agent-inject-secret-config.env: "secret/data/production/go-app"
vault.hashicorp.com/agent-inject-template-config.env: |
{{- with secret "secret/data/production/go-app" -}}
export DB_PASSWORD={{ .Data.data.db_password }}
export API_KEY={{ .Data.data.api_key }}
{{- end }}
spec:
serviceAccountName: go-app-sa
containers:
- name: go-app
image: myregistry/go-app:latestCSI Provider
Vault CSI Provider монтирует секреты как обычные файлы через Kubernetes Secrets Store CSI Driver — без sidecar-контейнера, что снижает overhead. Подходит для случаев, когда приложение уже умеет читать конфигурацию из файлов.
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: vault-db-credentials
namespace: production
spec:
provider: vault
parameters:
vaultAddress: "https://vault.example.com"
roleName: "go-app-role"
objects: |
- objectName: "db_password"
secretPath: "secret/data/production/go-app"
secretKey: "db_password"Настройка Kubernetes Auth в Vault
# Включаем Kubernetes auth
vault auth enable kubernetes
# Настраиваем подключение к кластеру
vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc" \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
token_reviewer_jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token
# Создаём роль для приложения
vault write auth/kubernetes/role/go-app-role \
bound_service_account_names=go-app-sa \
bound_service_account_namespaces=production \
policies=go-app-policy \
ttl=1hУправление секретами в CI/CD пайплайне
GitHub Actions
В GitHub Actions секреты хранятся в настройках репозитория или организации и передаются в workflow через переменные окружения. Никогда не выводите секреты в логи.
name: Deploy to Kubernetes
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure kubectl
uses: azure/k8s-set-context@v3
with:
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Deploy application
env:
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
echo "$REGISTRY_TOKEN" | docker login registry.example.com -u ci --password-stdin
kubectl apply -f k8s/GitLab CI
В GitLab CI секреты задаются как Protected и Masked переменные в настройках проекта. Для интеграции с HashiCorp Vault GitLab поддерживает нативный JWT-механизм:
deploy:production:
stage: deploy
id_tokens:
VAULT_ID_TOKEN:
aud: https://vault.example.com
secrets:
DB_PASSWORD:
vault: production/go-app/db_password@secret
file: false
script:
- kubectl create secret generic db-credentials \
--from-literal=DB_PASSWORD="$DB_PASSWORD" \
--namespace=production \
--dry-run=client -o yaml | kubectl apply -f -Такой подход позволяет получать секреты напрямую из Vault без их хранения в GitLab, что соответствует принципу минимального доверия.
Ротация секретов без даунтайма
Ротация секретов — критически важный процесс, о котором часто забывают до первого инцидента. Рассмотрим стратегию для Go и PHP приложений.
Стратегия двойной записи
- В Vault создаётся новая версия секрета, старая остаётся активной.
- Приложение перезапускается с новым секретом (rolling update в Kubernetes).
- После успешного деплоя старая версия помечается как устаревшая.
Go-приложение: горячая перезагрузка конфигурации
Go-приложения могут использовать Vault SDK для динамического обновления credentials без рестарта:
// Упрощённый пример мониторинга lease
func renewSecret(client *vault.Client, secret *vault.Secret) {
renewer, _ := client.NewLifetimeWatcher(&vault.LifetimeWatcherInput{
Secret: secret,
Increment: 3600,
})
go renewer.Start()
defer renewer.Stop()
for {
select {
case renewal := <-renewer.RenewCh():
log.Printf("Secret renewed: %v", renewal.Secret.LeaseDuration)
case err := <-renewer.DoneCh():
log.Printf("Renewal failed, fetching new secret: %v", err)
// Получить новый секрет
return
}
}
}PHP-приложение: ротация через Kubernetes rolling update
PHP-приложения, как правило, не держат долгоживущих соединений, поэтому стандартный rolling update Kubernetes справляется с задачей без даунтайма:
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-app
namespace: production
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
template:
spec:
containers:
- name: php-app
image: myregistry/php-app:latest
envFrom:
- secretRef:
name: db-credentialsПри ротации секрета достаточно обновить Secret-объект и выполнить kubectl rollout restart deployment/php-app -n production.
Аудит и мониторинг доступа к секретам
Безопасность без мониторинга — это иллюзия. Настройте следующие механизмы:
Kubernetes Audit Logging
Включите аудит на уровне API-сервера, фиксируя обращения к ресурсу secrets:
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ""
resources: ["secrets"]
verbs: ["get", "list", "watch"]
- level: RequestResponse
resources:
- group: ""
resources: ["secrets"]
verbs: ["create", "update", "patch", "delete"]Vault Audit Devices
В HashiCorp Vault включите file или syslog audit device:
vault audit enable file file_path=/var/log/vault/audit.logВсе обращения к секретам фиксируются с указанием клиента, времени и результата операции. Интегрируйте логи с ELK Stack или Grafana Loki для централизованного анализа.
Alerting
Настройте алерты на аномальные события: массовый листинг секретов, доступ из неожиданных namespace, попытки доступа с отозванных токенов.
Чеклист безопасности для продакшен-кластера
- Шифрование at rest — включить
EncryptionConfigurationдля etcd с провайдером AES-CBC или KMS. - RBAC-минимализм — принцип минимальных привилегий: роли только на нужные namespace и ресурсы, без wildcard.
- Нет секретам в Git в открытом виде — использовать Sealed Secrets или External Secrets Operator.
- Аудит логи включены — минимум уровень Metadata для secrets-ресурсов.
- Ротация настроена — автоматическая ротация через Vault lease или внешний планировщик.
- Network Policy — ограничить сетевой доступ к Vault и другим хранилищам секретов.
- ServiceAccount-токены с ограниченным TTL — использовать Bound Service Account Tokens (не статические).
- Запрет монтирования ServiceAccount по умолчанию —
automountServiceAccountToken: falseв Deployment, если токен не нужен. - Image scanning — сканировать Docker-образы на наличие захардкоженных секретов (truffleHog, gitleaks в CI/CD).
- Мониторинг аномалий — алерты на нетипичное поведение при работе с секретами.
Заключение
Безопасное управление секретами в Kubernetes — многоуровневая задача, требующая правильного выбора инструментов под конкретный контекст. Для небольших команд с GitOps-подходом Sealed Secrets закрывает большинство рисков. Для enterprise-инфраструктуры с требованиями compliance HashiCorp Vault обеспечивает полный контроль: динамические credentials, детальный аудит и централизованное управление политиками. External Secrets Operator гибко объединяет Kubernetes с облачными хранилищами секретов.
Независимо от выбранного инструмента, фундамент безопасности строится на трёх китах: минимальные привилегии, аудит всех обращений и регулярная ротация. Внедрите эти практики сегодня — и ваш кластер будет готов к требованиям безопасности 2026 года.
Технологии
Теги
Руслан Исмаилов
Senior Web / Backend разработчик. Senior web/backend разработчик с 9-летним опытом. Стек: PHP, Laravel, PostgreSQL, Redis, Docker, Kubernetes, REST, микросервисы, CI/CD. Подробнее обо мне →