DevOps

Секреты и конфиденциальные данные в Kubernetes: безопасное управление через Secrets, Vault и sealed-secrets

Ruslan Ismailov Опубликовано 12 мин чтения
С

Введение: почему нативные 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:latest

CSI 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 приложений.

Стратегия двойной записи

  1. В Vault создаётся новая версия секрета, старая остаётся активной.
  2. Приложение перезапускается с новым секретом (rolling update в Kubernetes).
  3. После успешного деплоя старая версия помечается как устаревшая.

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. Подробнее обо мне →