← Back to notes

ConfigMap и
Secret


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/tlsTLS-сертификат и ключ (поля tls.crt, tls.key)
kubernetes.io/dockerconfigjsonCredentials для Docker registry
kubernetes.io/basic-authBasic auth (поля username, password)
kubernetes.io/ssh-authSSH-ключ (поле 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
aescbcAES-CBC + PKCS#7Не рекомендован (padding oracle)
aesgcmAES-GCMБыстрый; требует аккуратной ротации ключей
secretboxNaCl secretbox (XSalsa20+Poly1305)Локальный 32-byte ключ
kms v2Envelope 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

КритерийConfigMapSecret
ДанныеНеконфиденциальныеКонфиденциальные
КодированиеPlain textbase64
РазмерДо 1 MiBДо 1 MiB
МонтированиеVolume или envVolume или 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 InjectorSidecar рендерит секрет в файл подаVaultНет
Sealed Secrets (Bitnami)Шифрует Secret в SealedSecret для gitGit (шифр.)Да (после расшифровки контроллером)
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


07: Ingress

09: Volumes

ConfigMap и Secret | Aleksandr Suprun