linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • Симулятор
  • База знаний
  • Собеседование
Index
Categories
All entries
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
home/linux/kb/Безопасность/secrets-management

kb/security ── Безопасность ── intermediate

Управление секретами - Vault, k8s Secrets, sealed-secrets

Секреты не в git, не в env-vars в коде. Опции: HashiCorp Vault (универсал, dynamic creds), k8s Secrets (base64, нужен encryption- at-rest), sealed-secrets (commit-friendly), external-secrets (sync из cloud-vault).

view as markdownaka: vault, k8s-secrets, sealed-secrets, external-secrets, secret-management

Зачем отдельная инфраструктура для секретов

«Секрет» = значение, которое нельзя видеть никому, кроме тех, кто должен. Пароли БД, API-keys, TLS-private-keys, OAuth-client-secret.

Способы как НЕ надо:

  • В коде (API_KEY = "sk-..."), git история, code review, утечки навсегда (даже после revert виден в history)
  • В .env коммичен в git, то же самое, плюс случайно попадает в Docker image
  • В Dockerfile через ENV, виден в docker history, попадает в image
  • В CI как plain переменная, лог CI, артефакты build'а
  • В k8s ConfigMap, base64 не шифрование, kubectl get cm покажет

Что действительно нужно:

  1. Секрет хранится зашифрованным
  2. Доступ аутентифицирован (известно кто запросил)
  3. Доступ авторизован (этому identity положено)
  4. Каждый запрос залогирован (audit-trail)
  5. Возможна ротация без перезапуска приложения
  6. Секреты не лежат в git (даже зашифрованные если возможно)

Уровень 0: env-vars из orchestrator'а

Простейший подход: задать env-var через docker-compose / systemd / k8s, без хранилища.

yaml
# docker-compose.yml
services:
  app:
    image: myapp
    environment:
      DB_PASSWORD: ${DB_PASSWORD}     # из .env (НЕ в git!)

Минусы: ротация = restart, no audit, secret виден в docker inspect. Подход для local-dev и небольших pet-проектов. Для прода, мало.

HashiCorp Vault, универсал

Самый зрелый менеджер секретов. Работает как REST-сервис + encrypted backend (etcd, raft, S3).

Ключевые концепции:

  • Secrets engine, что хранить (KV, database, AWS, PKI, transit)
  • Auth method, как клиент аутентифицируется (token, AppRole, kerberos, k8s SA, AWS IAM, OIDC)
  • Policy, кому что разрешено (HCL-policy на пути)
  • Audit device, куда писать audit-log
bash
# Запись секрета
vault kv put secret/app/db password="s3cret" username="myapp"
# Чтение
vault kv get -field=password secret/app/db

Dynamic secrets, киллер-фича

Не хранить пароль БД, а выпускать на лету короткоживущий:

bash
vault write database/config/postgres-prod \
    plugin_name=postgresql-database-plugin \
    connection_url="postgresql://{{username}}:{{password}}@db:5432/" \
    allowed_roles=readonly \
    username=vault-admin password=...
vault write database/roles/readonly \
    db_name=postgres-prod \
    creation_statements="CREATE USER \"{{name}}\" WITH PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
                        GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
    default_ttl=1h max_ttl=24h
# клиент:
vault read database/creds/readonly

▸username=v-app-..., password=..., lease 1h

Нет долгоживущих credentials → нечего ротировать. У каждого pod'а, свои уникальные. При утечке, ttl истекает за час.

Transit engine, encryption as a service

Vault шифрует/дешифрует, ключ никогда не покидает Vault:

bash
vault write transit/encrypt/app plaintext=$(echo "secret" | base64)

▸ciphertext: vault:v1:...

vault write transit/decrypt/app ciphertext="vault:v1:..."

▸plaintext: c2VjcmV0

Полезно для encryption-at-rest без управления KMS-ключами в коде.

k8s Secrets, нативно, но осторожно

yaml
apiVersion: v1
kind: Secret
metadata: { name: app-db }
type: Opaque
stringData:
  password: s3cret                    # k8s сам base64 закодирует

Pod использует:

yaml
spec:
  containers:
  - name: app
    env:
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef: { name: app-db, key: password }

Подводные:

  • base64 ≠ encryption, kubectl get secret app-db -o yaml | base64 -d видно всем с get secrets permission
  • По умолчанию в etcd plain! Включить encryption-at-rest через EncryptionConfiguration (KMS-ключ или в-файле AES)
  • RBAC на secrets, отдельно. Не давать cluster-reader автоматически (часто включает secrets)
  • Mount как файл vs env-var, файл предпочтительнее (меньше leak через /proc/<pid>/environ)

sealed-secrets, секреты в git

Если у вас GitOps (ArgoCD/Flux), хочется хранить всё в git, включая Secrets. Но Secret base64-наружу, лажа.

Решение sealed-secrets (Bitnami):

  • В кластере controller с парой ключей
  • CLI kubeseal шифрует Secret публичным ключом → SealedSecret CRD
  • SealedSecret коммитится в git
  • Controller дешифрует и создаёт реальный Secret в кластере
bash
kubectl create secret generic db -n prod --from-literal=pwd=s3cret \
  --dry-run=client -o yaml | \
  kubeseal -o yaml > db-sealedsecret.yaml
git add db-sealedsecret.yaml && git commit

Только этот конкретный кластер может расшифровать (private key только там). Утечка git → secret защищён.

Минус: при потере private key cluster'а, все sealed-secrets невосстановимы. Бэкап ключа критичен.

external-secrets, мост к cloud-vault'у

Если используете AWS Secrets Manager / GCP Secret Manager / Azure Key Vault / Vault, стандартный CRD-операторы:

yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata: { name: app-db }
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets
    kind: ClusterSecretStore
  target: { name: app-db }
  data:
  - secretKey: password
    remoteRef:
      key: prod/app/db
      property: password

Оператор каждый час идёт в AWS Secrets Manager, тянет значение, обновляет k8s Secret. Источник истины, облачный vault, k8s Secret, кэш.

Бонус: ротация секрета в AWS → автоматически обновляется в k8s.

CSI driver для секретов (Secrets Store CSI Driver)

Альтернатива external-secrets: монтировать секрет прямо в pod как файл, без k8s Secret промежуточно:

yaml
volumes:
- name: secrets
  csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
      secretProviderClass: app-secrets

Плюсы: никогда не лежит в etcd, ротация без restart (с enableSecretRotation).

Ротация, главная боль

Хранение секрета, простая часть. Сложная, обновить везде, где используется, без даунтайма.

Подходы:

  • Restart pod при изменении Secret, крайне нежелательно для stateful
  • App watch'ит файл секрета, паттерн с CSI driver
  • Sidecar перезагружает (Vault Agent injector + sigtemplate)
  • Dynamic secrets, обновление автоматическое через TTL

Никогда не предполагайте, что секрет вечен. Минимум раз в год ротация (compliance: SOC 2, PCI требуют).

Audit и compliance

Любой production-vault должен логировать каждый read/write с:

  • identity клиента
  • какой secret path
  • timestamp
  • source IP

Vault: vault audit enable file file_path=/var/log/vault/audit.log. AWS Secrets Manager: CloudTrail. k8s Secrets: [[auditd|audit-policy]] на apiserver.

При инциденте audit-log = единственное что покажет, кто и когда читал секрет.

Что НЕ хранить в secrets

  • Hash паролей пользователей, это БД, не secret
  • Сессии / JWT-токены, отдельный store (Redis)
  • Public-данные (URLs, env-flags), ConfigMap, не Secret

Слишком всё в Secret = больше attack surface, тяжелее audit.

Когда что-то пошло не так

  • Error: secret not found в pod'е, Secret в другом namespace, или ServiceAccount без RBAC на чтение этого Secret.
  • Восстановление из бэкапа etcd показывает старый secret encryption-at-rest с KMS-ключом, но KMS ключ потерян. Бэкап бесполезен. Бэкапить и KMS-ключ.
  • pod cannot read secret file после rotation, приложение запомнило старый файл-handle (cached open). Решение: SIGHUP-handler или sidecar-watch.
  • sealed-secret invalid, controller перерегенерировал ключ (новый кластер). Расшифровать старый невозможно, regenerate с нуля.
  • Vault sealed после рестарта, Vault стартует sealed (HA feature). Нужен vault operator unseal с N из M shamir-keys (или auto-unseal через cloud KMS).
  • Secret видится в логах приложения, приложение печатает env при старте (debug). Никогда не делать. Линтить через CI: grep -i "password\|secret" app-logs.
  • .env закоммичен в git, git filter-repo --invert-paths --path .env, потом ROTATE all secrets, git history помнит навсегда.

§ команды

bash
vault kv put secret/app/db password=s3cret

Положить секрет в KV-engine - простейший use-case Vault

bash
vault read database/creds/readonly

Получить dynamic-credentials с TTL - короткоживущий пароль БД

bash
kubectl create secret generic app-db --from-literal=pwd=s3cret -n prod

Создать k8s Secret из CLI - быстро, но без encryption если etcd-at-rest off

bash
kubeseal -o yaml < secret.yaml > sealed.yaml

Зашифровать Secret в SealedSecret для commit в git

bash
kubectl get secrets -A -o json | jq '.items[] | select(.type=="Opaque") | .metadata.name'

Аудит - все Opaque-секреты в кластере, для review кто что хранит

bash
git secrets --scan

Сканер git-репо на случайно закоммиченные API-keys, AWS credentials

bash
vault audit enable file file_path=/var/log/vault/audit.log

Включить audit-log Vault - обязательно для прода и compliance

§ см. также

  • pamPAM - Pluggable Authentication ModulesPAM - фреймворк аутентификации в Linux. Программы (sudo, login, sshd) не проверяют пароли сами, а вызывают PAM, который через стек модулей в `/etc/pam.d/<service>` решает: пускать или нет.
  • ssh-hardeningSSH hardening - закрытие сервераSSH hardening: ключи only (PasswordAuthentication no), отключить root-login, AllowUsers/AllowGroups, MaxAuthTries, fail2ban-jail на sshd. Опционально - нестандартный порт + Match-блоки для гостей.
  • kubernetes-pod-lifecycleKubernetes pod lifecycle - от Pending до TerminatedPod проходит фазы Pending → Running → Succeeded/Failed/Unknown. Init-containers выполняются последовательно до основных. Probes: startup → readiness/liveness. SIGTERM + grace period при удалении.
  • auditdauditd - syscall и file auditauditd пишет события ядра в /var/log/audit/audit.log: file watches (-w), syscall rules (-a), exec'и. ausearch для поиска, aureport для отчётов. Основа compliance (PCI-DSS, HIPAA, ФЗ-152).
  • bash-scriptingbash-скрипты - основы и идиомыBash-скрипт - текстовый файл с shebang `#!/usr/bin/env bash` и `chmod +x`. Обязательный starting point - `set -euo pipefail` и `shellcheck` для проверки.
  • gpg-pgpGPG/PGP - подпись, шифрование, web of trustGPG = open-source реализация OpenPGP. Пара ключей (RSA/ECDSA), публичный для verify/encrypt, приватный для sign/decrypt. keyring локально, web-of-trust между людьми. Используется в git-commit signing, package-signing (apt/rpm), email.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки