единый контроль секретов в нескольких облаках без разброса копий
Управление секретами в масштабе в мультиоблачных и гибридных средах
14 минут
Когда учётные данные лежат в AWS, GCP, Azure и локальном Vault одновременно, разброс копий и устаревшая ротация раздувают последствия утечки. Единый слой с External Secrets Operator и короткоживущими динамическими секретами держит доступ под контролем и аудитом.
Почему мультиоблачная среда умножает риски с секретами
Один кластер EKS может одновременно читать AWS Secrets Manager, корпоративный Vault в ЦОД и GCP Secret Manager. Без продуманной схемы команды возвращаются к переменным окружения, долгоживущим ключам в CI и разовым скриптам без ротации. Один пароль от базы оказывается в десятке микросервисов, трёх пайплайнах и двух системах мониторинга — у каждой копии свой доступ и свой след в логах. Сменили пароль в одном хранилище — остальные ссылки устарели. Аудитор спрашивает, кто читал продакшен-учётные данные за месяц; собрать ответ по разным бэкендам долго и больно. Утечка сервисного аккаунта в мультитенантном кластере может пройти через пространства имён, облачные аккаунты и интеграции с партнёрами. Цель — единая плоскость управления: короткоживущие учётные данные, централизованные политики, встроенная интеграция с Kubernetes и CI и доказательства для compliance.
Опорная архитектура: центральный Vault, мосты в облака и синхронизация в Kubernetes
Возьмите HashiCorp Vault — или сопоставимое корпоративное хранилище — как точку политик и аудита. Включите движки под задачи: KV v2 для конфигурации, database для временных пользователей PostgreSQL, AWS и GCP — для облачных IAM-учётных данных, PKI — для mTLS-сертификатов. Там, где нужны нативные облачные API, External Secrets Operator синхронизирует Vault в Secret Kubernetes или читает AWS Secrets Manager и GCP Secret Manager напрямую через отдельные ClusterSecretStore. Предпочитайте динамические секреты с TTL статическим паролям в манифестах. Vault держите в HA с integrated storage и auto-unseal через облачный KMS. Для runtime используйте External Secrets Operator, если нужен Secret в кластере, или Secrets Store CSI driver, если под читает файлы без объекта Secret. CI входит по JWT или AppRole, читает только свои пути и не выводит значения в логи.
helm repo add external-secrets https://charts.external-secrets.io
helm upgrade --install external-secrets external-secrets/external-secrets \
--namespace external-secrets --create-namespace \
--set installCRDs=trueРазвёртывание Vault HA и движки секретов
Запускайте Vault с хранилищем Raft и распечатыванием через KMS, чтобы перезапуск не зависел от ручного ввода ключей Shamir. Включите TLS на listener, зафиксируйте api_addr на DNS сервиса и подключите audit до продакшен-трафика. Монтируйте движки по назначению: KV для конфигов, database для динамических учётных записей, облачные — где приложению нужен федеративный доступ. Политики ограничивайте префиксом пути, чтобы workload читал только свой каталог.
storage "raft" {
path = "/vault/data"
node_id = "vault-node-1"
}
seal "awskms" {
region = "us-east-1"
kms_key_id = "alias/vault-unseal"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/vault/tls/tls.crt"
tls_key_file = "/vault/tls/tls.key"
}
api_addr = "https://vault.internal:8200"
cluster_addr = "https://vault-node-1.internal:8200"vault secrets enable -path=secret kv-v2
vault secrets enable database
vault secrets enable -path=aws/production aws
vault secrets enable -path=gcp/production gcp
vault secrets enable pki
vault audit enable file file_path=/var/log/vault/audit.logExternal Secrets Operator: Vault и ClusterSecretStore для нескольких облаков
ClusterSecretStore описывает, как оператор входит в Vault или облачный API. ExternalSecret по интервалу refresh подтягивает удалённые поля в Secret Kubernetes — чем короче интервал, тем быстрее подхватывается ротация; для динамических учётных записей БД ставьте TTL меньше часа и refresh короче TTL. Для Vault используйте Kubernetes auth от service account оператора. Если workload обязан оставаться в одном облаке, добавьте ClusterSecretStore на AWS Secrets Manager или GCP Secret Manager, а Vault пусть выдаёт общие динамические пользователи БД между регионами.
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: https://vault.internal:8200
path: secret
version: v2
auth:
kubernetes:
mountPath: kubernetes
role: external-secrets
serviceAccountRef:
name: external-secrets
namespace: external-secretsapiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-database-credentials
namespace: production
spec:
refreshInterval: 15m
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: db-credentials
creationPolicy: Owner
template:
engineVersion: v2
data:
DB_HOST: "{{ .host }}"
DB_USER: "{{ .username }}"
DB_PASS: "{{ .password }}"
DB_PORT: "{{ .port }}"
dataFrom:
- extract:
key: secret/data/production/databaseДинамические учётные данные, политики Terraform и JWT в CI/CD
Настройте роли database в Vault с SQL на создание и отзыв — каждый под или job получает уникального пользователя с автоматическим сроком жизни. Terraform описывает монтирования, подключения к БД, роли Kubernetes auth и политики least privilege для External Secrets и CI. В GitLab CI обменивайте JWT job на токен Vault; в GitHub Actions — hashicorp/vault-action с OIDC: секреты только в переменных окружения, без вывода в лог. Статические KV ротируйте по расписанию с версиями; после смены ESO по refresh разнесёт новые значения по кластерам.
resource "vault_database_secret_backend_connection" "postgresql" {
backend = vault_mount.database.path
name = "production-postgresql"
postgresql {
connection_url = "postgresql://{{username}}:{{password}}@db.internal:5432/app?sslmode=require"
}
}
resource "vault_database_secret_backend_role" "readonly" {
backend = vault_mount.database.path
name = "readonly"
db_name = vault_database_secret_backend_connection.postgresql.name
default_ttl = 3600
max_ttl = 86400
creation_statements = [
"CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';",
"GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";",
]
}
resource "vault_kubernetes_auth_backend_role" "external_secrets" {
role_name = "external-secrets"
bound_service_account_names = ["external-secrets"]
bound_service_account_namespaces = ["external-secrets"]
token_policies = [vault_policy.external_secrets.name]
token_ttl = 3600
}jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: hashicorp/vault-action@v3
with:
url: https://vault.internal:8200
method: jwt
role: github-actions
secrets: |
secret/data/production/api-key api_key | API_KEY
- name: Deploy
env:
API_KEY: ${{ env.API_KEY }}
run: ./deploy.shПрактика: сканирование репозитория, минимальные права, аудит и учения на отказ
Не допускайте секретов в Git: detect-secrets или git-secrets в pre-commit и CI. Каждому сервису и пайплайну — узкая политика Vault; перечисление родительского пути без чтения, если нужен только обзор имён. Отправляйте audit Vault в SIEM или Loki и фильтруйте по request.path для ответов аудиторам. Задайте максимальный TTL для каждого класса учётных данных и алерт при сбое ротации. В staging отрабатывайте отказ: остановите Vault и проверьте, стартуют ли поды с уже синхронизированными Secret; укоротите refresh ESO и убедитесь, что новые учётные записи БД доходят под нагрузкой. Не кладите секреты в образы и не печатайте их в выводе пайплайна — окупаемость архитектуры видна при первой утечке, когда пострадал часовой токен, а не вечный ключ без ротации.
path "secret/data/production/service-a/*" {
capabilities = ["read", "list"]
}
path "secret/data/production/*" {
capabilities = ["list"]
}jq 'select(.type=="response" and (.request.path|startswith("secret/data/production/db")))' \
/var/log/vault/audit.log \
| jq '{time, path: .request.path, identity: .auth.display_name}'- name: Scan for secrets
run: |
pip install detect-secrets
detect-secrets scan --all-files --exclude-files '\.lock$' > report.json
python -c "import json,sys; r=json.load(open('report.json'))['results']; sys.exit(1 if r else 0)"Паттерны Vault для конвейеров и монтирование через CSI разобраны в гайде по управлению секретами в DevOps и CI/CD.
Подстановка секретов при запуске дополняет hardening кластера из практического гайда по безопасности Kubernetes в продакшене.
