В этой статье я расскажу о том, как мы управляем секретами в Kubernetes-инфраструктуре BaseCRM.
Наша цель — использование Helm Charts в Kubernetes-кластерах BaseCRM с минимальными усилиями, подразумевающими управление лишь значениями параметров и секретами. В Helm или Kubernetes нет официального инструмента управления секретами, и мы решили это исправить.
Введение
Kubernetes — это разработанная в Google при существенной поддержке сообщества платформа для автоматизации развертывания и масштабирования контейнеризованных приложений в кластере. Это переносимая, надежная и способная к самовосстановлению система, предназначенная для построения PaaS-инфраструктур следующего поколения.
Поверх Kubernetes мы используем Helm — официальный менеджер пакетов Kubernetes. Располагая системой Charts с поддержкой шаблонов, он помогает управлять различными типами выполняющихся в Kubernetes приложений.
Helm Charts нужен для определения, установки и обновления многократно используемых шаблонов приложений в кластере Kubernetes, включая самые сложные, c множественными зависимыми субчартами (sub-charts). С Helm Charts можно легко управлять множественными версиями чартов, а также останавливать их с помощью прямых манифестов (direct manifests), которые в стоковом Kubernetes управляются только копипастом, что совершенно неприемлемо для сложных окружений.
С помощью Helm мы можем использовать части шаблонов чартов с чартами приложений любой сложности, которые пригодны для многократного использования в развертываниях Kubernetes PaaS в нескольких кластерах. С CI/CD мы можем с легкостью развернуть стек приложений в кластере Kubernetes или просто запустить Helm вручную и задеплоить целый мир с нуля.
Какую проблему мы пытаемся решить?
Для обеспечения безопасности инфраструктуры нужны пароли и маркеры доступа. При этом инфраструктура все же должна быть легко доступна и распространяться согласно четким правилам. Также не помешают логирование изменений, версионирование секретов, ведение журналов и, может быть, даже предупреждения об изменениях значений секретов. В то же время мы хотим иметь возможность пересоздавать кластеры Kubernetes, при этом там будут учетные данные, которые не нужно менять.
Одни хранят секреты в Git за VPN, при этом получая проблемы с множественными копиями, незашифрованными дисками и ротацией секретов. Другие прописывают их прямо в кластере и теряют эти данные вместе с кластером, когда с ним что-то случается.
С другой стороны, существует возможность интегрировать Kubernetes с сервисом Hashicorp Vault, который является хранилищем секретов. В этом случае кластер при получении секретов Kubernetes будет ссылаться на этот сервис.
Мы хотим разворачивать приложения в кластерах Kubernetes без использования сервиса Vault, который по соображениям безопасности не должен быть частью кластера Kubernetes. Нам нужна возможность работать локально и выполнять холостые прогоны без необходимости подключения к инфраструктуре Vault.
Как мы это делаем?
С самого основания компании Base мы используем Ansible. Также мы решили, что для развертывания кластеров Kubernetes будем использовать Helm (эта тема, скорее всего, заслуживает отдельной статьи). Столкнувшись с проблемой управления секретами, мы придумали плагин helm-secrets, который во многом напоминал Ansible vault. В то время мы использовали PGP и шифровали файл с секретами целиком.
Это было простое решение, хотя с самого начала было понятно, что оно не окончательное. Вскоре мы решили перейти к управлению каждым YAML-секретом по отдельности.
Сначала мы составили список требований и провели поиск инструментов, которые бы им удовлетворяли. Нам удалось найти Mozilla SOPS, который по своим возможностям подходил в качестве бэкенда системы управления секретами для нашего плагина.
Все, что оставалось сделать, — это написать простую прослойку между SOPS-бэкендом и Helm-фронтендом с использованием первой версии helm-secrets.
Именно это мы и сделали, попутно решив поделиться результатами работы с общественностью. Встречайте релиз плагина helm-secrets – https://github.com/futuresimple/helm-secrets.
Вот список его возможностей (их даже больше, чем перечислено здесь):
- Простой уровень интеграции и установки совместно с плагином helm.
- Поддержка шифрования YAML-структур Helm.
- Шифрование отдельных значений – это позволяет использовать diff даже с зашифрованными файлами.
- Для git diff обеспечивается расшифровка «на лету».
- Для команд helm install/upgrade/rollback с помощью команды helm-wrapper обеспечивается расшифровка «на лету» и очистка.
- Поддерживается одновременное использование в одном файле секретов решений управления множественными ключами, таких как PGP и AWS KMS.
- Для зашифрованных файлов поддерживается простое добавление/удаление ключей.
- Поддерживается работа с системой управления разрешениями для ключей AWS KMS без необходимости перешифрования.
- Разделение дерева каталогов для хранения файлов секретов с использованием рекурсивного поиска по файлам .sops.yaml.
- Извлечение отдельных элементов из зашифрованной структуры файлов.
- Шифрование части файла.
Система способна работать в рамках CI/CD, обслуживая несколько команд разработчиков, имеющих изолированный доступ к определенным поддиректориям репозитория Git, который содержит секреты в разрезе проектов/окружений/регионов для определенных кластеров Kubernetes. Мы шифруем секреты с помощью разных ключей KMS и мастер-ключа PGP в качестве резерва.
Для автоматизации и ускорения выполнения задач в нашем внутреннем чарт-репозитории используется Makefile.
Вот как это работает
Установка Helm
Следуйте инструкциям на странице проекта helm.
Установка плагина helm-secrets
helm plugin install https://github.com/futuresimple/helm-secrets
Пример использования
В репозитории helm-secrets приведен пример работы с этим плагином, а также соответствующая структура каталогов, предназначенная для хранения значений чартов helm. Для чартов kubernetes применяется аналогичная структура.
Для этого примера используется файл test.sh
из репозитория helm-secrets.
example/helm_vars/
├── .sops.yaml
├── projectX
│ ├── .sops.yaml
│ ├── production
│ │ └── us-east-1
│ │ └── java-app
│ │ ├── secrets.yaml
│ │ └── value.yaml
│ └── sandbox
│ └── us-east-1
│ └── java-app
│ ├── secrets.yaml
│ └── value.yaml
├── projectY
│ ├── .sops.yaml
│ ├── production
│ │ └── us-east-1
│ │ └── java-app
│ │ ├── secrets.yaml
│ │ └── value.yaml
│ └── sandbox
│ └── us-east-1
│ └── java-app
│ ├── secrets.yaml
│ └── value.yaml
├── secrets.yaml
└── values.yaml
Давайте разберем этот пример.
- У нас есть два PGP-ключа (также можно использовать KMS-ключи):
- один для projectx;
- другой для projecty.
- Все остальные секреты управляются в рамках проекта, и для каждого проекта используется один ключ.
- Ключи изолированы друг от друга, и только глобальный файл secrets.yaml, находящийся в корне helm_vars, может быть расшифрован любым из этих ключей.
- Все правила глубины рекурсии для шифрования/расшифровки находятся в файлах .sops.yaml.
Шифрование, расшифровка и даже больше
Перед шифрованием example/helm_vars/secrets.yaml
global_secret: global_bar
Теперь шифруем:
helm-wrapper secrets enc example/helm_vars/secrets.yaml
В результате мы получим файл с незашифрованными ключами и зашифрованными значениями.
После раздела с ключами идут sops-данные, необходимые для шифрования или расшифровки структур секретов.
Также есть метаданные типа sops version, lastmodified или unencrypted_suffix. Почитать о формате файла sops можно на сайте проекта Mozilla SOPS.
global_secret: ENC[AES256_GCM,data:pTyPdC6YA+z84Q==,iv:aF5hb9CS8Au0B3RWADPtP8fXYzYakU7JJ8ZxzJgHRF0=,tag:c3pCyOf0NpQU7VPL/72XPg==,type:str]
sops:
.…
….
….
unencrypted_suffix: _unencrypted
version: 2.0.9
Теперь расшифровка:
helm-wrapper secrets dec example/helm_vars/secrets.yaml
И результат:
global_secret: global_bar
Теперь мы можем работать с такими файлами без расшифровки, например, при поиске ключей в нескольких файлах секретов. Мы даже можем расшифровывать их «на лету» с git diff config, что делает работу еще более комфортной, особенно по сравнению с приложениями, которые не поддерживают подобную функциональность.
Мы также можем более гибко управлять разрешениями секретов, не пересоздавая их благодаря более продвинутому AWS KMS.
Более того, просмотр и редактирование секретов с помощью простых команд плагина облегчают ежедневную работу.
helm-wrapper secrets view example/helm_vars/secrets.yaml
После расшифровки файл секретов будет выведен в stdout:
helm-wrapper secrets edit example/helm_vars/secrets.yaml
По команде edit
расшифрованный файл откроется в редакторе, а изменения будут автоматически сохранены в зашифрованном виде.
Эти функции значительно облегчают работу с зашифрованными данными практически при любом сценарии.
Пример использования Helm
Давайте развернем что-нибудь в кластере Kubernetes.
Чтобы упростить процесс и сделать его более прозрачным, мы создали helm-wrapper. Эта написанная на bash обвязка для helm автоматически расшифровывает все заданные секреты и использует расшифрованные данные для выполнения процедуры развертывания с помощью helm. При возникновении ошибки или в случае успешного завершения процедуры все временные данные с секретами удаляются.
Реальный пример для приложения типа helloworld:
AWS_PROFILE=production helm-secrets upgrade --install --timeout 600 --wait helloworld stable/java-app --kube-context=production --namespace=projectx --set global.app_version=bff8fc4 -f helm_vars/projectx/sandbox/us-east-1/java-app/helloworld/secrets.yaml -f helm_vars/projectx/sandbox/us-east-1/java-app/helloworld/values.yaml -f helm_vars/secrets.yaml -f helm_vars/values.yaml
>>>>>> Decrypt
Decrypting helm_vars/projectx/sandbox/us-east-1/java-app/helloworld/secrets.yaml
>>>>>> Decrypt
Decrypting helm_vars/secrets.yaml
Release "helloworld" has been upgraded. Happy Helming!
LAST DEPLOYED: Fri May 5 13:27:01 2017
NAMESPACE: projectx
STATUS: DEPLOYED
RESOURCES:
==> extensions/v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
helloworld 3 3 3 2 1h
==> v1/Secret
NAME TYPE DATA AGE
helloworld Opaque 10 1h
==> v1/ConfigMap
NAME DATA AGE
helloworld 2 1h
==> v1/Service
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
helloworld 100.65.221.245 8080/TCP 1h
NOTES:
Deploy success helloworld-bff8fc4 in namespace projectx
>>>>>> Cleanup
helm_vars/projectx/sandbox/us-east-1/java-app/helloworld/secrets.yaml.dec
helm_vars/secrets.yaml.dec
Как можно увидеть из листинга, там есть специальная команда, которая выполняется на стороне CI для развертывания приложения. В этом примере используется наш внутренний универсальный чарт для приложений на java, который содержит шаблон для config map, секретов, сервисов и развертывания.
Все это генерируется из значений в файлах, указанных с помощью опции -f команды helm. Если в таком файле встретится зашифрованный секрет, скрипт расшифрует его «на лету», и helm ничего не заметит.
При использовании AWS KMS необходимо указать имя AWS_PROFILE.
При возникновении ошибки будет выполнена очистка.
AWS_PROFILE=production helm-wrapper upgrade --install --timeout 600 --wait helloworld stable/java-app --kube-context=wrongcontext --namespace=projectx --set global.app_version=bff8fc4 -f helm_vars/projectx/sandbox/us-east-1/java-app/helloworld/secrets.yaml -f helm_vars/projectx/sandbox/us-east-1/java-app/helloworld/values.yaml -f helm_vars/secrets.yaml -f helm_vars/values.yaml
>>>>>> Decrypt
Decrypting helm_vars/projectx/sandbox/us-east-1/java-app/helloworld/secrets.yaml
>>>>>> Decrypt
Decrypting helm_vars/secrets.yaml
Error: could not get kubernetes config for context 'wrongcontext': context "wrongcontext" does not exist
>>>>>> Cleanup
helm_vars/projectx/sandbox/us-east-1/java-app/helloworld/secrets.yaml.dec
helm_vars/secrets.yaml.dec
Такой подход при использовании секретов в helm позволяет минимизировать усилия, сохраняя максимальный уровень безопасности.
Безопасное использование с git
При наличии расшифрованных секретов (расшифрованных вручную) у нас остается еще два уровня обеспечения безопасности: исключение расшифрованных файлов с помощью .gitignore и добавление хуков, которые проверяют, не зашифрованы ли файлы коммита с помощью SOPS.
Все это также есть в документации helm-secret и проверено нашим процессом CI/CD.
Заключение
Helm-secrets — это лишь тонкая обертка для sops-бэкенда, и любая команда этого плагина может быть заменена на другие инструменты.
Мы также используем этот плагин для распространения секретов через публичный git-репозиторий и планируем добавить в процесс работы с секретами интегрированную в Kubernetes службу Vaulting, что придаст нашему решению законченность.
Для инфраструктуры небольшого размера должно быть достаточно плагина helm-secrets. Надеемся, его использование вам понравится.
При желании добавить в проект какие-либо функции или просто его обсудить заходите на https://github.com/futuresimple/helm-secrets.
Ссылки:
- Оригинал: HELM SECRETS – A MISSING PIECE IN KUBERNETES.
Автор: olemskoi