ConfigMap и Secret отделяют конфигурацию от образа контейнера и позволяют менять её без пересборки. Разные окружения используют разные значения с одним и тем же образом.
source: kubernetes.io/docs/concepts/configuration/configmap/
ConfigMap
ConfigMap хранит неконфиденциальные конфигурационные данные в виде пар ключ-значение. Размер — до 1 MiB.
Создание ConfigMap
Из литералов:
kubectl create configmap app-config \
--from-literal=DB_HOST=postgres \
--from-literal=DB_PORT=5432 \
--from-literal=LOG_LEVEL=info
Из файла:
# Файл целиком как одно значение (ключ = имя файла)
kubectl create configmap nginx-config --from-file=nginx.conf
# С явным ключом
kubectl create configmap nginx-config --from-file=my-nginx=nginx.conf
# Из .env файла (каждая строка KEY=VALUE становится отдельным ключом)
kubectl create configmap app-config --from-env-file=.env
Через YAML-манифест (рекомендуется для GitOps):
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
DB_HOST: postgres
DB_PORT: "5432"
LOG_LEVEL: info
app.properties: |
server.port=8080
server.context-path=/api
feature.cache.enabled=true
Использование ConfigMap
Все ключи как переменные окружения (envFrom):
spec:
containers:
- name: app
image: myapp:1.0
envFrom:
- configMapRef:
name: app-config
Все ключи ConfigMap становятся переменными окружения контейнера.
Отдельные ключи (valueFrom):
spec:
containers:
- name: app
image: myapp:1.0
env:
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DB_HOST
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
Как файлы через Volume (рекомендуется для конфигов с автообновлением):
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: config-volume
mountPath: /etc/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
Каждый ключ ConfigMap становится файлом в /etc/config/. При обновлении ConfigMap файлы в Volume обновляются автоматически (с задержкой до kubelet sync period — обычно ~1 минута). Переменные окружения (env/envFrom) не обновляются — требуется перезапуск Pod.
Secret
Secret хранит конфиденциальные данные: пароли, токены, ключи. По структуре аналогичен ConfigMap, но значения хранятся в base64. Размер — до 1 MiB.
source: kubernetes.io/docs/concepts/configuration/secret/
Создание Secret
Императивно:
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=s3cret123
Через YAML с data (значения в base64):
# Кодирование
echo -n 'admin' | base64 # YWRtaW4=
echo -n 's3cret123' | base64 # czNjcmV0MTIz
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
username: YWRtaW4=
password: czNjcmV0MTIz
Через YAML с stringData (без ручного кодирования):
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
username: admin
password: s3cret123
stringData автоматически кодируется в base64 при сохранении. Удобнее для манифестов, но не коммить такие файлы в git без шифрования (Sealed Secrets, SOPS).
Типы Secret
| Тип | Назначение |
|---|---|
Opaque | Произвольные данные (по умолчанию) |
kubernetes.io/tls | TLS-сертификат и ключ (поля tls.crt, tls.key) |
kubernetes.io/dockerconfigjson | Credentials для Docker registry |
kubernetes.io/basic-auth | Basic auth (поля username, password) |
kubernetes.io/ssh-auth | SSH-ключ (поле ssh-privatekey) |
# TLS Secret
kubectl create secret tls my-tls \
--cert=server.crt \
--key=server.key
# Docker registry Secret
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=user \
--docker-password=pass
Использование Secret
Как переменные окружения:
spec:
containers:
- name: app
image: myapp:1.0
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
Как файлы через Volume (предпочтительно):
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: db-credentials
Volume mount предпочтительнее env для Secret: файлы обновляются автоматически при ротации, не попадают в kubectl describe pod вывод так легко, не логируются средами выполнения.
Docker registry credentials (imagePullSecrets):
spec:
imagePullSecrets:
- name: regcred
containers:
- name: app
image: registry.example.com/myapp:1.0
Безопасность Secret
По умолчанию Secret хранятся в etcd без шифрования — только в base64, а это кодирование, не шифрование. Декодируется тривиально:
echo 'czNjcmV0MTIz' | base64 -d # s3cret123
Из документации: «Anyone with API access can retrieve or modify a Secret, and so can anyone with access to etcd». Поэтому минимальный набор мер — encryption at rest, RBAC least-privilege и ограничение доступа контейнерам.
Encryption at rest
Для шифрования Secret в etcd настраивается EncryptionConfiguration на API Server (флаг --encryption-provider-config у kube-apiserver):
source: kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- secretbox:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}
Порядок провайдеров важен: первый в списке используется для шифрования новых записей, все остальные — для дешифрования при чтении. identity: {} означает отсутствие шифрования, поэтому если он стоит первым — encryption at rest фактически выключен.
| Провайдер | Что это | Заметка |
|---|---|---|
identity | Без шифрования | Default; plain text в etcd |
aescbc | AES-CBC + PKCS#7 | Не рекомендован (padding oracle) |
aesgcm | AES-GCM | Быстрый; требует аккуратной ротации ключей |
secretbox | NaCl secretbox (XSalsa20+Poly1305) | Локальный 32-byte ключ |
kms v2 | Envelope encryption через внешний KMS | Рекомендуется для prod |
kms v2 — envelope encryption: kube-apiserver по gRPC (UNIX-сокет) обращается к KMS-плагину, тот деривирует DEK и оборачивает его KEK во внешнем KMS/HSM (AWS KMS, GCP KMS, Azure Key Vault). Корень доверия — вне кластера, что сильнее локальных ключей в файле. KMS v2 стабилен (GA) с Kubernetes v1.29; KMS v1 объявлен deprecated в v1.28 и по умолчанию отключён с v1.29.
source: kubernetes.io/docs/tasks/administer-cluster/kms-provider/
После включения шифрования существующие секреты остаются в старом формате, пока их не перезапишут. Форсировать перешифровку:
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
RBAC и модель доступа
Encryption at rest не спасает от чрезмерных прав в API. Ключевой факт из документации: любой, кто может создать Pod в namespace, может прочитать любой Secret этого namespace — в том числе косвенно, через право создавать Deployment. Поэтому:
- Права
get/list/watchнаsecretsвыдавать по least-privilege, точечно. - Ограничивать, кто может создавать поды/деплойменты в чувствительных namespace.
- Секреты для разных приложений — в разных namespace.
source: kubernetes.io/docs/concepts/configuration/secret/
Immutable Secrets
Поле immutable: true делает Secret неизменяемым:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
immutable: true
data:
password: czNjcmV0MTIz
- Защита от случайного изменения критичного секрета.
- Снижение нагрузки на kube-apiserver: control plane закрывает watch'и по immutable-объектам.
- Необратимо: снять флаг или изменить
dataуже нельзя — только удалить и пересоздать Secret.
Рекомендации
- Не коммить Secret-манифесты в git в открытом виде
- Использовать Sealed Secrets (Bitnami) или SOPS для шифрования манифестов в репозитории
- Использовать внешние менеджеры секретов: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault — с CSI driver или оператором
- Включить encryption at rest в etcd
- Ограничить доступ к Secret через RBAC
- Монтировать как Volume вместо env
Сравнение ConfigMap и Secret
| Критерий | ConfigMap | Secret |
|---|---|---|
| Данные | Неконфиденциальные | Конфиденциальные |
| Кодирование | Plain text | base64 |
| Размер | До 1 MiB | До 1 MiB |
| Монтирование | Volume или env | Volume или env |
| Автообновление в Volume | Да (~1 мин) | Да (~1 мин) |
| Автообновление в env | Нет (перезапуск) | Нет (перезапуск) |
| Рекомендации по git | Можно коммитить | Только зашифрованными |
Типичные ошибки
Секрет в ConfigMap. Пароль или токен в ConfigMap — ошибка. ConfigMap не имеет дополнительных защитных механизмов.
Коммит Secret манифеста в git. stringData в YAML читается как plain text. Стандартное решение — Sealed Secrets или внешний secret manager.
Обновление ConfigMap не приводит к обновлению env в Pod. После обновления ConfigMap Pod с envFrom продолжает видеть старые значения до перезапуска. Для автоматического перезапуска при изменении ConfigMap используй Reloader (stakater/Reloader) или пересоздавай Pod через kubectl rollout restart.
Забыть readOnly: true при монтировании Secret. Хорошая практика — монтировать Secret как read-only.
Secret слишком большой. Лимит 1 MiB. Большие артефакты (TLS chain, binary keys) — в отдельные Secret или внешнее хранилище.
Внешнее управление секретами
Нативный Secret — это по сути plaintext в etcd. Продвинутые подходы выносят источник истины наружу либо шифруют манифесты. Два разных класса задач:
- Sync/mount из внешнего стора (ESO, CSI Secrets Store, Vault Agent Injector) — источник истины во внешнем менеджере (Vault, AWS/GCP/Azure), в кластер попадает только рабочая копия.
- Шифрование манифеста для git (Sealed Secrets, SOPS) — источник истины в репозитории, но в зашифрованном виде.
| Инструмент | Модель | Источник истины | Секрет в etcd |
|---|---|---|---|
| External Secrets Operator | Синхронизирует внешний секрет в нативный K8s Secret | Внешний менеджер | Да (копия) |
| Secrets Store CSI Driver | Монтирует секрет как tmpfs-том, sync в Secret опционален | Внешний менеджер | Нет (или опц. копия) |
| Vault Agent Injector | Sidecar рендерит секрет в файл пода | Vault | Нет |
| Sealed Secrets (Bitnami) | Шифрует Secret в SealedSecret для git | Git (шифр.) | Да (после расшифровки контроллером) |
| SOPS | Шифрует YAML/JSON (KMS/PGP/age) | Git (шифр.) | Да (после расшифровки) |
HashiCorp Vault через Vault Agent Injector, CSI provider или External Secrets Operator — динамические секреты, аудит доступа, автоматическая ротация. Стандарт для security-требовательных окружений.
AWS Secrets Manager / Azure Key Vault / GCP Secret Manager — облачные менеджеры секретов с интеграцией через CSI Secrets Store Driver, ESO или нативные операторы.
Sealed Secrets (Bitnami) — шифрует Secret в SealedSecret ресурс, который безопасно хранить в git. Расшифровывается только контроллером в кластере.
SOPS — шифрует YAML/JSON файлы (включая Secret манифесты) с использованием KMS, PGP или age. Хорошо интегрируется с GitOps.
source: secrets-store-csi-driver.sigs.k8s.io/concepts source: external-secrets.io/latest/provider/hashicorp-vault