Как правильно управлять секретами в контейнерах?
Управление секретами в контейнерах – это набор практик безопасного хранения, доставки и ротации конфиденциальных данных (паролей, ключей API, сертификатов), исключающий их попадание в образ, исходный код или переменные окружения в открытом виде.
Как НЕ надо хранить секреты
Пример
# НЕПРАВИЛЬНО: секрет в Dockerfile -- сохраняется во ВСЕХ слоях образа!
ENV DB_PASSWORD=SuperSecret123
RUN echo "password=SuperSecret123" > /app/config.properties
# НЕПРАВИЛЬНО: использование ARG -- видно через docker history
ARG DB_PASSWORD
RUN echo $DB_PASSWORD > /app/config.properties
Пример
# НЕПРАВИЛЬНО: секрет в docker run через переменную окружения
docker run -e DB_PASSWORD=SuperSecret123 my-app
# Секрет виден через:
docker inspect <container_id> # в разделе Env
cat /proc/1/environ # внутри контейнера
Пример
# НЕПРАВИЛЬНО: секрет в plain-text в Kubernetes манифесте (попадёт в Git!)
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
env:
- name: DB_PASSWORD
value: "SuperSecret123"
Kubernetes Secrets (базовый уровень)
Kubernetes Secrets кодирует данные в base64 – это не шифрование, а лишь кодирование. Для реальной защиты необходимо включить encryption at rest.
Пример создания и использования Kubernetes Secret
# Создание секрета
apiVersion: v1
kind: Secret
metadata:
name: banking-db-credentials
namespace: banking
type: Opaque
data:
username: YWRtaW4= # base64 (НЕ шифрование!)
password: UGFzc3dvcmQxMjM= # base64
---
# Использование секрета как volume (рекомендуемый способ)
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
image: registry.bank.local/banking-service:1.0.0
volumeMounts:
- name: db-creds
mountPath: /etc/secrets/db
readOnly: true
volumes:
- name: db-creds
secret:
secretName: banking-db-credentials
defaultMode: 0400 # Только чтение для владельца
Чтение секрета в Spring Boot:
Пример
@Value("file:/etc/secrets/db/password")
private String dbPassword;
Настройка encryption at rest для секретов в etcd
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}
HashiCorp Vault (enterprise-уровень)
Vault – рекомендуемое решение для банковских систем. Vault Agent Injector автоматически подставляет секреты в файлы внутри пода.
Пример интеграции Vault с Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: banking-service
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "banking-service"
vault.hashicorp.com/agent-inject-secret-db-password: "secret/data/banking/db"
vault.hashicorp.com/agent-inject-template-db-password: |
{{- with secret "secret/data/banking/db" -}}
{{ .Data.data.password }}
{{- end -}}
spec:
serviceAccountName: banking-service
containers:
- name: app
image: registry.bank.local/banking-service:1.0.0
Vault Agent автоматически подставит секрет в файл /vault/secrets/db-password внутри пода.
Docker Secrets (для Docker Swarm)
Пример
# Создание секрета
echo "SuperSecret123" | docker secret create db_password -
Пример
version: '3.8'
services:
banking-service:
image: banking-service:1.0.0
secrets:
- db_password
secrets:
db_password:
external: true
Секрет доступен внутри контейнера по пути /run/secrets/db_password в tmpfs (не записывается на диск).
Sealed Secrets (Bitnami) – для хранения секретов в Git
Пример
# Зашифровать секрет публичным ключом кластера
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
Пример
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: banking-db-credentials
spec:
encryptedData:
password: AgBy3i...encrypted...data...==
Sealed Secret можно безопасно хранить в Git – расшифровать его может только контроллер в кластере.
External Secrets Operator – интеграция с внешними хранилищами
Пример ExternalSecret для интеграции с Vault
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: banking-db-creds
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: banking-db-credentials
data:
- secretKey: password
remoteRef:
key: secret/data/banking/db
property: password
Сравнение подходов
| Подход | Шифрование | Ротация | GitOps-совместимость | Рекомендация |
|---|---|---|---|---|
| Kubernetes Secrets | base64 (нет) | Ручная | Нет (plain-text) | Минимум + encryption at rest |
| Sealed Secrets | RSA | Ручная | Да | Для небольших команд |
| HashiCorp Vault | AES-256 | Автоматическая | Через ESO | Для банков и enterprise |
| External Secrets Operator | Зависит от бэкенда | Автоматическая | Да | Универсальный выбор |
Вывод
Секреты никогда не должны попадать в образ, исходный код или переменные окружения в открытом виде. Для банковских систем рекомендуется HashiCorp Vault с Vault Agent Injector или External Secrets Operator. Минимальное требование – Kubernetes Secrets с encryption at rest и монтированием через volume (не через env).
На собеседовании: начните с антипаттернов (секрет в ENV, в Dockerfile, в plain-text манифесте), затем покажите правильные подходы. Обязательно подчеркните, что base64 – это не шифрование. Упомяните Vault и Sealed Secrets для полноты.