В продолжение поста Helm: пошаговое создание чарта и деплоймента из Jenkins — теперь нам надо добавить шифрование данных, что бы не хранить пароли и различные приватные ключи в открытом виде в Github-репозиториях — даже пусть и приватных.
Судя по гуглу, чуть ли не единственный вариант — это использование
Из плюшек — возможность работать с ключами из AWS KMS, и автоматом подставлять данные в шаблоны.
Суть простая: берёт GPG/PGP/KMS ключ, им шифруется файл. Всё 🙂
Под капотом использует
Что выполним — установим плагин, создадим AWS KMS ключ, создадим файл с паролем, обновим уже задеплоенный хельм-релиз.
В конце — добавим работу с плагином Jenkins-джобу из предыдущего поста.
Содержание
helm-secrets
Переходим в репозиторий, в каталог с файлами чарта:
Установка
helm-secrets & sops на Arch Linux
Вообще установка должна выполняться через helm plugin
— но на Arch Linux не сработало.
Сначала ругнулось на права:
Потом ещё раз на права — да и устанвавливать от sudo
в /root
— бред:
Потому — ставим сам sops
из AUR:
И читаем
$XDG_DATA_HOME/helm/plugins
— ага.
$XDG_DATA_HOME
$HOME/.local/share
— проверяем:
Смотрим содержимое:
В общем-то вся работа плагина построена на bash-скриптах. Прелестно.
Сами скрипты описаны в документации
Ладно.
Что плагин вообще выполняет, какие действия?
Читаем файл plugin.yaml
:
command: "$HELM_PLUGIN_DIR/secrets.sh"
— ага…
Посмотрим скрипт secrets.sh
:
21-й век — мы полагаемся на километровый баш-скрипт для работы с важными данными.
Окей… Прелестно.
Настройка шифрования
За настройку ключей используемых для encrypt/decrypt действий отвечает файл .sops.yaml
в каталоге чарта.
Примеры настроек —
Переходим к созданию ключа, которым будем шифровать наши данные.
AWS KMS
Документация —
Создаём KMS ключ, тип — дефолтный, симетричный (см.
Задаём его администратора:
Далее надо добавить пользователей, у которых будет доступ к ключу.
Так как мы будем использовать файл из Jenkins, который работает на EC2 и доступ в AWS получает через свой EC2 Instance Profile — то надо добавить роль, которая используется им для аутентификации и авторизации в нашем AWS-аккаунте.
Находим профиль:
Находим, и подключаем её:
Подтверждаем сгенерированную политику:
Кстати:
... "Principal": { "AWS": "arn:aws:iam::534***385:root" }, "Action": "kms:*", "Resource": "*" ...
ARN arn:aws:iam::534***385:root указывает на всех пользователей AWS-аккаунта, насколько я помню из поста AWS Elastic Kubernetes Service: RBAC-авторизация через AWS IAM и RBAC группы.
И таки да — у коллеги доступ к ключу есть, хотя явно user-прав ему не назначал. Окей — имеем это ввиду.
Ключ готов:
.sops.yaml
config
В корне каталога с файлами чарта создаём файл .sops.yaml
— это файл настроек для
В
Пока используем один дефолтный для всех — создаём правило:
--- creation_rules: - kms: 'arn:aws:kms:eu-west-1:534***385:key/620b89fe-***-25b435611e8b'
Файл секретов
В каталоге чарта bttrm-apps-backend создаём файл для паролей — secrets.yaml
.
Тут есть подводный камень, на который я напоролся — по-умолчанию helm-secrets
будет искать файл по маске secrets.*.yaml
— см.
By convention, files containing secrets are named secrets.yaml, or anything beginning with «secrets.» and ending with «.yaml». E.g. secrets.test.yaml and secrets.prod.yaml.
Т.е. назвать файл passwords.yaml
— не вариант, по крайней мере без подолнительных телодвижений.
Итак, сейчас в файле values.yaml
имеются пароли открытым текстом:
... image: registry: "docker.io" username: "bttrm" password: "pass" repository: "bttrm" name: "bttrm-apps" tag: "120" ...
Вырезаем из values.yaml
строку image.password
— переносим в файл secrets.yaml
:
image: password: "pass"
Или, что бы сделать тут пример более простым и наглядным — добавляем в secrets.yaml
новое значение, например:
test: secret: testecret
И создаём манифест Kubernetes Secret, в моём случае они создаётся из шаблона bttrm-apps-backend/templates/bttrm-apps-secrets.yaml
, где bttrm-apps-backend — каталог чарта:
--- apiVersion: v1 kind: Secret metadata: name: test-secret type: Opaque stringData: example-secret: {{ .Values.test.secret }}
В stringData
обращаемся к .Values
, как обычно.
helm secrets
— AccessDeniedException
Шифруем файл секретов:
А, окей.
Проблема, собственно, ясна сразу — SOPS пытается обратиться к AWS, используя default профиль из ~/.aws/credentials
, в котором у меня записан мой личный аккаунт AWS, а не рабочий.
Можно добавить aws_profile
в .sops.yaml
:
--- creation_rules: - kms: 'arn:aws:kms:eu-west-1:534***385:key/620b89fe-***-25b435611e8b' aws_profile: 'arseniy'
Но тогда этот же профиль будет использоваться при деплое чарта из Jenkins.
Поэтому — задаём профиль переменной окружения:
И ещё раз пробуем зашифровать данные в чарте bttrm-apps-backend:
Проверяем содержимое secrets.yaml
теперь:
image: password: ENC[AES256_GCM,data:1t7 .. PyY,iv:9mzTyB ... 9KG7+Hg=,tag:6KJ ... gvA==,type:str] test: secret: ENC[AES256_GCM,data:l4c ... vCy,iv:9riw ... 0fvQ=,tag:G3p ... oSw==,type:str] sops: kms: - arn: arn:aws:kms:eu-west-1:534***385:key/620b89fe- ... -25b435611e8b created_at: '2020-05-15T06:33:44Z' enc: AQICAHj9F0HBsgu ... kb6GOxJkiOMZSSxOtA== ...
В строках image.password
и test.secret
теперь содержатся зашифрованные значения, а затем — описание того, как SOPS его шифровал.
Просмотреть содержимое можно с помощью view
:
Отредактировать файл — с помощью edit
:
Test deploy
Что бы задеплоить — просто вызываем helm
c install
или upgrade
, или как в нашем случае в Jenkins — upgrade --install
, но добавляем secrets
— helm
вызовет скрипт secrets.sh
, который выполнит всю работу:
Тут может быть интересно посмотреть процессы, которые запускаются во время вызова helm secrets upgrade
:
Т.е:
- мы вызываем
helm secrets upgrade
- затем вызывается
bash /home/setevoy/.local/share/helm/plugins/helm-secrets/secrets.sh upgrade
- который вызывает
helm upgrade
Проверяем наш секрет в Kubernetes:
И значение dGVzdGVjcmV0:
Переходим к Jenkins.
Jenkins
Теперь надо всё это добавить в Jenkinsfile, который писали в предыдущем посте.
Если сейчас удалить релиз:
А потом попробовать задеплоить его заново из Jenkins — то получим ошибку, т.к. helm
не сможет получить данные, которые мы вынесли из values.yaml
в secrets.yaml
:
Что надо сделать — добавить в Докер-образ, который используется для билда, плагин helm-secrets
, а потом обновить стейдж stage(«Helm install») в скрипте пайплайна — добавить вызов secrets
и -f secrets.yaml
.
Jenkins Docker build-image
Сейчас Dockefile для сборки образа, который используется для билда и деплоя выглядит так:
FROM bitnami/minideb:stretch RUN apt update && install_packages ca-certificates wget RUN install_packages curl python-pip python-setuptools jq git RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl RUN chmod +x ./kubectl RUN mv ./kubectl /usr/local/bin/kubectl WORKDIR /tmp RUN curl --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp RUN mv /tmp/eksctl /usr/local/bin RUN pip install ansible boto3 awscli WORKDIR /tmp RUN curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 RUN /bin/bash get_helm.sh USER root
Добавляем установку плагина — там исходный образ bitnami/minideb:stretch
— SOPS должен установится нормально, а не как на Arch Linux:
... RUN curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 RUN /bin/bash get_helm.sh RUN helm plugin install https://github.com/futuresimple/helm-secrets USER root
Собираем:
Ага, счаз.
Окей — поставим SOPS из PIP:
... RUN mv /tmp/eksctl /usr/local/bin RUN pip install ansible boto3 awscli sops WORKDIR /tmp ...
Правда, во время сборки Helm сказал, что:
Посмотрим — как пойдёт во время билда в Jenkins.
Jenkinsfile
Обновляем скрипт пайплайна — добавляем secrets
и -f bttrm-apps-backend/secrets.yaml
:
... stage("Helm install") { ... sh "helm secrets upgrade --install --namespace ${AWS_EKS_NAMESPACE} --create-namespace --atomic ${APP_CHART_NAME} ${APP_CHART_NAME}-${RELEASE_VERSION}.tgz -f bttrm-apps-backend/secrets.yaml" } } } ...
Запускаем:
Готово.