Автоматизация получения доступа к базам данных для компонентов
в среде Kubernetes
Н.Б. Лазарева, Н.Н. Ловцова Тихоокеанский государственный университет, Хабаровск
Аннотация: В данной статье описывается процесс автоматизации процедуры получения критических с точки зрения безопасности данных, необходимых для работы компонентов с базами данных в среде Kubernetes; рассмотрены объекты Kubernetes, логика их работы, а также их взаимодействие с хранилищем Vault. Для управления объектами Kubernetes и автоматизации операций в процессе деплоя используется ПО Helm. В статье приведены необходимые конфигурации для Helm с пояснениями. В качестве примера в статье рассматривается случай взаимодействия компонентов с PostgreSQL и MongoDB как широко распространенными в информационных средах SQL и NO-SQL базами данных. Ключевые слова: Kubernetes, Vault, Helm, автоматизация, безопасность, база данных, Deployment, Job, PostgreSQL, MongoDB.
Часто в различных информационных средах компоненты должны иметь доступ к различным базам данных. Для этого коду этих компонентов нужно передать логин и пароль, имя базы данных и адрес базы данных. Адрес базы данных и имя базы это параметры, которые могут быть переданы компоненту динамически в момент его деплоя в открытом виде. Но если говорить о таких параметрах, как логин и пароль, то здесь имеет место проблема обеспечения безопасности хранения и передачи этих параметров. Очевидно, что поскольку нужно сообщить эти данные компоненту, необходимо их заранее создать и где-то сохранить. Также нужно иметь в виду, что после передачи этих параметров компоненту сами параметры не должны быть доступны извне, а быть доступны только компоненту.
Для хранения паролей и любых других, важных с точки зрения безопасности, данных, существует распространенное решение Hashicorp Vault [1]. В данной статье опустим процедуру конфигурации и деплоя Vault в Kubernetes, поскольку она довольно тривиальна и описана в официальной документации.
Необходимо продумать и реализовать процесс получения данных из Vault в процессе деплоя компонента в Kubernetes так, чтобы максимально снизить вероятность получения этих данных другими компонентами.
Предположим, компоненту необходим доступ в две базы данных: PostgreSQL и MongoDB. В Vault ответственное лицо поместило ряд ключевых переменных:
• mongodbAdminPassword - пароль администратора
• mongodbUserPassword - уникальный пароль компонента
• postgresqlAdminPassword - пароль администратора
• postgresqlUserPassword- уникальный пароль компонента Схема использования этих переменных следующая:
1. Во время деплоя компонента непосредственно перед его запуском временно запускает контейнер с клиентским приложением Vault, который забирает из сервера Vault указанные данные и сохраняет их в директории, которая будет доступна на шаге 2. После этого он завершает работу.
2. Запускается второй временный контейнер с кодом, который осуществляет подготовку базы данных для использования компонентом. Непосредственно перед запуском кода происходит публикация сохраненных данных на шаге 1 в виде переменных окружения, доступных коду.
3. В этом контейнере код, используя пароль администратора (или любой другой учетной записи, предварительно созданной в экземпляре базы данных и обладающей правами на создание базы данных, а также на создание пользователя с назначенным паролем и на смену этого пароля), подключается к экземпляру базы данных. Далее в случае PostgreSQL [2] создается пользователь и задается пароль, используя переменную postgresqlUserPassword. Этому пользователю
выдаются права на создание собственной базы данных. После этого происходит переключение с учетной записи администратора на вновь созданную и уже от ее имени создается база данных. В случае MongoDB [3] с использованием учетной записи администратора создается только пользователь и пароль с использованием переменной mongodbUserPassword, база данных будет создана автоматически при первой попытке что-либо записать в нее.
4. Временно запускается контейнер с клиентским приложением Vault, который забирает из сервера Vault указанные данные и сохраняет их в директории, которая будет доступна компоненту. После этого он завершает работу.
5. Запускается контейнер с компонентом, но непосредственно перед запуском кода компонента происходит публикация сохраненных данных на шаге 4 в виде переменных окружения, доступных компоненту. Используя созданные учетные записи и пароли, загруженные из переменных окружения, компонент может выполнить подключение к базам данных.
Реализовать подобную схему можно с помощью пакетного менеджера Helm [4]. Helm позволяет упростить и автоматизировать процесс деплоя компонентов в Kubernetes, являясь широко распространенным ПО. Кроме того, Helm несколько расширяет возможности деплоя компонентов, добавляя определенный функционал. В данном случае используются Helm-хуки, а также возможность генерации переменных на лету при помощи механизма helpers. Рассмотрим компонент Job [5], необходимый для запуска кода, ответственного за подготовку баз данных (рис. 1):
М Инженерный вестник Дона, №3 (2021) ivdon.ru/ru/magazine/arcliive/n3y2021/6844
apiversion: batch/vl kind: ЗзЬ metadata:
name: {{ .Values.name }}-createdb labels: ' app: {[ .Values.name }J chart: {{ .Values.name }} release: {{ .Values.name }} annotations: "helm.sh/hook": pre-installj pre-upgrade
"h e1m.s h/hook-d elete-policy": hook - s u с с e ed ed, before - hook- с re at ion "helm.sh/hook-weight": "IB"
template: metadata:
{{- if .Values.createdb.annotations }} annotations:
[{ toYaml .Values.createdb.annotations indent 3 }} «- end }}
name: {{ .Values.name }}-createdb spec:
restartPolicy: never containers: - name: createdb
image: "{{ .values.docker.registry }}/{{ .values.docker.db.image }}:{{ .values.docker.db.tag }}" command:
- /bin/bash
- -c
I
source /vault/secrets/config
export PGU5ER=f{ .Values.createdb.postgresql.adminuser quote }} export PGPAS5W0RD= tFGWHDIPASSWORD if echo -n > /dev/tcp/iPGHOST/iPGPORT; then if ! psql --dbname={[ .Values.createdb.postgresql.adminuser }} -qAtc \
"select usename from pg_user" grep -qw {{ .Values.createdb.postgresql.user }}; then psql --dbname=f{ .Values.createdb.postgresql.adminuser }} --commands "create lser {{ .values.createdb.postgresql.user }} password 1¡pguserpassword"
fi
psql --dbname={{ .Values.createdb.postgresql.adminuser }} --command=\
"ALTER LSER {{ .Values.createdb.postgresql.user J} WITH CREATEDB";
psql --dbname={{ .values.createdb.postgresql.adminuser }} --command='\
"ALTER LSER {{ .Values.createdb.postgresql.user }} WITH PASSWORD 1IPGUSERPASSWORD"";
if ! psql --dbname=postgres -lqt cut -d \| -f 1 grep -qw {{ .Values.createdb.postgresql.dbname }}; then export PGUSER=f{ .Values.createdb.postgresql.user J}; export PGPAS5W0RD=iPGUSERPASSW0RD;
createdb -0 [{ .Values.createdb.postgresql.user J} {[ .Values.createdb.postgresql.dbname }J;
fi else exit l;
fi
if echo -n > /dev/tcp/f{ .Values.createdb.mongodb.host }}/({ .Values.createdb.mongodb.port }}; then if ! mongo {{ .Values.createdb.mongodb.host }}:{[ .Values.createdb.mongodb.port J},'admin -u \ {{ .values.createdb.mongodb.adminuser }} -p imongodbAdminPassword --eval \ [{ include "mongodb_check_user" $ quote J} grep "V"user\" : \"{{ .Values.name }>V; then mongo {{ .Values.createdb.mongodb.host }}:{{ .Values.createdb.mongodb.port }}/admin -u \ {{ .Values.createdb.mongodb.adminuser }} -p imongodbAdminPassword --eval \ {{ include "mongodb_create_user" $ quote }};
fi else exit 1;
fi env:
- name: PGHOST
value: {{ .Values.createdb.postgresql.pghost | quote }}
- name: pgport
value: {[ .Values.createdb.postgresql.pgport | quote }J
- name: PGDATABASE
value: {{ .Values.createdb.postgresql.dbname | quote }}
Рис. 1. - Пример компонента Job Как видно из данного примера, необходимо сообщить Helm о том, что нужно выполнить этот код до запуска основного кода компонента. Это регулируется
блоком.те1аёа1а.аппо1айопв [6]. Далее блок.spec.template.metadata.annotations подгружает специальный блок аннотаций, который позволяет использовать переменные, полученные на шаге 1 после выгрузки из Vault. Данный блок хранится в файле values.yaml [7], который будет рассмотрен позднее.
Блок spec.template.metadata.annotations.spec.containers описывает шаги 2 и 3, в случае PostgreSQL, используя переменную окружения PGPASSWORD, получающую свое значение из данных, полученных на шаге 1, а также переменных, значения которых могут быть переданы в открытом виде, такие, как PGHOST (адрес базы данных), PGPORT (порт базы данных), PGDATABASE (имя базы данных), PGUSER (пользователь, от имени которого выполняются команды). В случае с MongoDB используются данные, полученные на шаге 1 напрямую, в виде переменных mongodbAdminPassword и mongodbUserPassword без создания переменных окружения, так как нельзя использовать переменные окружения для работы с MongoDB. Для работы с MongoDB используются специально подготовленные переменные mongodb_check_user, mongodb_create_user, по существу являющиеся консольными командами. Подготовка этих команд осуществляется в файле _helpers.tpl [8] (рис. 2):
db.createUser({user: '{{ .Values.name }}', pwd: "JmongQdbUserPassword", roles: [ { role: "readWrite", db: "{{ .Values.createdb.mongodb.dbname }}" } ]})
db = db. getSiblingDB("{{ .Values.createdb.mongodb.dbname }}"); db.getUsers()
Рис. 2 - Содержимое файла _helpers.tpl Для того, чтобы данный объект Job смог использовать данные, полученные на шаге 1, нужно добавить специальные аннотации в блок .spec.template.metadata.annotations. Для этого в файле Values.yaml нужно добавить блок аннотаций (рис. 3):
createdb: annotations:
vault. hashicorp. corn/agent -inject: "true" vault.hashicorp.corn/role: "all"
vault.hashicorp.com/agent-inject-secret-config: "secret/component-name/config" vault.hashicorp.corn/agent-pre-populate-only: "true" vault.hashicorp.corn/agent-inject-template-config: {{ with secret "secret/component-name/config" -}}
export mongodbAdminPassword="{{ .Data.data.mongodbAdminPassword }}" export mongodbUserPassword="{{ .Data.data.mongodbUserPassword }}" export P GADMIN PAS SW0RD="{{ .Data.data.postgresqlAdminPassword }}" export PG US E RPASSW0RD="{{ .Data.data.postgresqlUserPassword }}" {{-end}}
Рис. 3 - Блок аннотаций для объекта Job Кроме этого, нужно передать другие переменные, необходимые для работы Job (рис. 4):
image: "docker-image-name"|
pghost: &pghost "postgresql-name"
dbname: "component-name" host: &mhost "mongodb-name"
dbname: &mdbname "component-name" user: iimuser "component-name"
Рис. 4 - Дополнительные переменные для объекта Job С наличием данной конфигурации, при деплое будет запущен контейнер с кодом внутри, который подготовит базы к использованию. Останется лишь подготовить контейнер с кодом компонента. В случае, если контейнер компонента запускается путем создания объектов Pod [9] и
Deployment [10], нужно добавить блок аннотаций (рис. 5) по аналогии с блоком аннотаций для объекта Job:
annotations:
vault.hashicorp. corn/agent-init-first: "true" vault.hashicorp.conii/agent-inject: "true" vault.hashicorp.corn/role: "all"
vault.hashicorp.corn/agent-inject-secret-config: "secret/component-name/config" vault.hashicorp.сои/agent-pre-populate-only: "true" vault.hashicorp.corii/agent-inject-teriiplate-corifig: | {{ with secret "secret/component-name/config" -}}
export component_naniie_postgresql_pas5word="{{ .Data.data.mongodbUserPassword }}" export component_nanie_riiQngodb_pa55word="{{ .Data.data.postgresqlUserPassword }}" {{- end }}
Рис. 5 - Блок аннотаций для объекта Deployment Отличие данного блока аннотаций от похожего блока для Job состоит в том, что компоненту передаются только пароли от вновь созданных учетных записей. Таким образом, компонент не может использовать пароли администратора. Подключить данный блок аннотаций нужно в блоке Deployment .spec.template.metadata.annotations.
Также нужно сообщить контейнеру, что перед непосредственным запуском кода компонента нужно выполнить загрузку данных, полученных на шаге 4. Для этого необходимо модифицировать команду запуска контейнера в объекте Deployment:
service_cmd: "[\"/bin/bash\", \"-c\", Y'source /vault/secrets/config && npm startY']" вставив её в блок . spec.template.spec.containers.command.
Теперь после запуска кода компонент сможет выполнить подключение к базам данных, используя свои уникальные пары логин/пароль. Данные, выгруженные на шаге 1, после запуска компонента более недоступны. Данные, выгруженные на шаге 4, являются доступными только данному компоненту, другие компоненты к ним доступа не имеют. Таким образом, используя Helm и Vault, удалось автоматизировать процесс получения данных, необходимых для подключения к базам данных, обеспечив высокий уровень безопасности процесса.
M Инженерный вестник Дона, №3 (2021) ivdon.ru/ru/magazine/arcliive/n3y2021/6844
Литература
1. Официальная документация Vault // vaultproject.io. URL: vaultproject.io/ docs/platform/k8s (дата обращения: 14/02/2021).
2. PostgreSQL документация // postgresql.org. URL: postgresql.org/docs/ (дата обращения: 14/02/2021).
3. MongoDB документация // mongodb.com. URL: docs.mongodb.com/.
4. Официальная документация Helm // helm.sh URL: helm.sh/docs/intro/.
5. Job resource for Kubernetes // kubernetes.io. URL: kubernetes.io/docs/concepts/workloads/controllers/job/ (дата обращения: 14/02/2021).
6. Helm hooks // helm.sh. URL: helm.sh/docs/topics/charts_hooks/ (дата обращения: 14/02/2021).
7. Файлы значений Helm // helm.sh. URL: helm.sh/docs /chart_ template_ guide/values_files/ (дата обращения: 14/02/2021).
8. Именованные шаблоны Helm // helm.sh. URL: helm.sh/docs/ chart_ template_guide/named_templates/.
9. Описание ресурса Pod для Kubernetes // kubernetes.io. URL: kubernetes.io/ docs/concepts/workloads/pods/ (дата обращения: 14/02/2021).
10.Описание ресурса Deployment для Kubernetes // kubernetes.io. URL: kubernetes.io/docs/concepts/workloads/controllers/deployment/ (дата
обращения: 14/02/2021).
References
1. Oficialnaya dokumentaciya Vault [Vault documentation]. URL: vaultproject.io/docs/platform/k8s (data obrashheniya: 14/02/2021).
M Инженерный вестник Дона, №3 (2021) ivdon.ru/ru/magazine/arcliive/n3y2021/6844
2. PostgreSQL dokumentaciya. URL: postgresql.org/docs/ (data obrashheniya: 14/02/2021).
3. MongoDB dokumentaciya. URL: docs.mongodb.com/.
4. Oficial naya dokumentaciya Helm [Helm documentation]. URL: helm.sh/ docs/intro/.
5. Job resource for Kubernetes. URL: kubernetes.io/docs/concepts/ workloads/ controllers/job/ (data obrashheniya: 14/02/2021).
6. Helm hooks. URL: helm.sh/docs/topics/charts_hooks/ (data obrashheniya: 14/02/2021).
7. Fajly' znachenij Helm [Helm values files]. URL: helm.sh/docs/chart_ template_guide/values_files/ (data obrashheniya: 14/02/2021).
8. Imenovanny e shablony' Helm [Helm named templates]. URL: helm.sh/ docs/chart_template_guide/named_templates/.
9. Opisanie resursa Pod dlya Kubernetes [Pod resource for Kubernetes]. URL: kubernetes.io/docs/concepts/workloads/pods/ (data obrashheniya: 14/02/2021).
10. Opisanie resursa Deployment dlya Kubernetes [Deployment resource for Kubernetes]. URL: kubernetes.io/docs/concepts/workloads/ controllers/ deployment/ (data obrashheniya: 14/02/2021).