Где хранятся Secrets и почему по умолчанию они не зашифрованы? Как это починить?
Secrets хранятся в etcd в base64 (не зашифровано). Для шифрования нужно включить Encryption at Rest через EncryptionConfiguration с провайдером aescbc/aesgcm/kms. После включения надо перезаписать все существующие Secrets командой kubectl get/replace.
Где хранятся Secrets и почему они не зашифрованы
Все объекты Kubernetes, включая Secret, хранятся в etcd. По умолчанию данные пишутся в base64-кодировке, что является кодированием, а не шифрованием. Любой, кто имеет доступ к файлам etcd или бэкапам, может тривиально декодировать содержимое:
echo "c3VwZXJzZWNyZXQ=" | base64 -d
# supersecret
Причина исторически проста: изначально Secrets задумывались как механизм доступа (RBAC), а не хранения. Шифрование добавили позже как опциональную фичу.
Encryption at Rest: включение
Нужно создать файл EncryptionConfiguration и передать его API-серверу через флаг --encryption-provider-config.
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aesgcm:
keys:
- name: key1
secret: c2VjcmV0a2V5MTIzNDU2Nzg5MDEyMzQ1Ng==
- identity: {}
Провайдеры (первый в списке — для записи, остальные — для чтения при ротации):
identity— без шифрования (дефолт).aescbc— AES-CBC, устарел, уязвим к padding oracle в старых версиях.aesgcm— AES-GCM, рекомендован для локального шифрования.kms v2— делегирует шифрование внешнему KMS (AWS KMS, GCP KMS, HashiCorp Vault).secretbox— XSalsa20+Poly1305, быстрее AES на CPU без AES-NI.
KMS v2 (production-рекомендация)
providers:
- kms:
apiVersion: v2
name: aws-kms
endpoint: unix:///var/run/kmsplugin/socket.sock
timeout: 3s
- identity: {}
KMS v2 использует envelope encryption: данные шифруются локальным DEK, сам DEK шифруется мастер-ключом в KMS. Это изолирует ротацию ключей от объёма данных.
Применение конфигурации
# На узле control-plane (kubeadm)
sudo mkdir -p /etc/kubernetes/enc
sudo cp encryption-config.yaml /etc/kubernetes/enc/
# Добавить в /etc/kubernetes/manifests/kube-apiserver.yaml:
# - --encryption-provider-config=/etc/kubernetes/enc/encryption-config.yaml
# + volumeMount и hostPath volume
Важно: после включения шифрования уже существующие Secrets в etcd остаются в открытом виде. Их нужно перезаписать принудительно:
kubectl get secrets --all-namespaces -o json | \
kubectl replace -f -
Проверка
# Прочитать raw-данные из etcd (на control-plane узле)
ETCDCTL_API=3 etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
get /registry/secrets/default/my-secret | hexdump -C | head -5
# Если видите k8s:enc:aesgcm:v1 в начале — шифрование работает
Подводные камни
- Ключи шифрования хранятся на том же узле. Если файл конфигурации лежит в
/etc/kubernetes/enc/, а etcd — там же, атакующий с доступом к ФС получает и данные, и ключ. KMS решает эту проблему. - Ротация ключей требует перезаписи всех Secrets. Добавить новый ключ первым в список, подождать перезапуска API-сервера, потом сделать bulk-replace.
- Потеря ключа = потеря данных. Без резервной копии ключей расшифровать etcd невозможно. Храните ключи в защищённом хранилище (Vault, AWS Secrets Manager).
- Encryption at Rest не защищает Secrets в памяти. Данные расшифровываются при чтении API-сервером и передаются в открытом виде в pod (через tmpfs).
- RBAC важнее шифрования. Если у serviceaccount есть право
get secrets, шифрование в etcd ему не помеха — он получит данные через API. - Managed-кластеры шифруют по-разному. EKS, GKE, AKS включают envelope encryption автоматически, но требуют явного включения customer-managed keys (CMK/CMEK).
- ConfigMap тоже может содержать чувствительные данные.
EncryptionConfigurationпозволяет зашифровать иconfigmaps— добавьте их вresources.
Common mistakes
- Путать base64 и encryption
- Не знать про etcd
- Забывать KMS и rotation
What the interviewer is testing
- Понимает storage path
- Знает encryption providers
- Говорит про RBAC и external secrets