Helm позволяет модульно упаковывать приложения Kubernetes и применять различную логику развертывания на основе конфигурационных “values” пользователей.
Обычно при первом развертывании нам необходимо генерировать случайные секреты (например, JWT / секреты сессии или случайные пароли).
Мы можем позволить пользователям вручную создавать/управлять этими секретами вне жизненного цикла развертывания Helm чарта.
Но будет удобнее, если Helm чарт сможет обрабатывать автоматическую генерацию секретов.
Однако автогенерировать и управлять этими секретами в рамках развертывания чартов не так-то просто.
В этой статье мы поговорим о потенциальных проблемах некоторых простых решений и предложим более гибкое решение, основанное на функции поиска Helm.
Создание секретов с помощью функций случайных строк
Helm предоставляет список функций генерации случайных строк, которые позволяют нам генерировать криптографически безопасные случайные строки.
Простым решением может быть включение шаблона для создания секретного объекта k8s с помощью функции randAlphaNum.
apiVersion: v1 kind: Secret metadata: name: "jwt-secret" type: Opaque data: # сгенерировать случайную строку длиной 32 символа, закодировать ее в base64 и затем заключить полученную строку в двойные кавычки. jwt-secret: {{ randAlphaNum 32 | b64enc | quote }}
Это простое решение выглядит круто.
Но у вас возникнут проблемы, когда вы попытаетесь обновить существующее развертывание позже, поскольку шаблон не имеет представления о текущих секретных данных в кластере и всегда будет обновлять секрет новым случайным значением.
Условное создание секрета
Лучшим решением может быть получение существующих секретных данных и создание секрета с новым значением только в том случае, если секретных данных не существует.
apiVersion: v1 kind: Secret metadata: name: "jwt-secret" type: Opaque data: # получить секретные данные с помощью функции поиска и, если они не существуют, вернуть пустой словарь / мапу в качестве результата {{- $secret := (lookup "v1" "Secret" .Release.Namespace "jwt-secret") | default dict }} # назначить $jwtSecret в существующие секретные данные или сгенерировать случайные, если их нет {{- $jwtSecret := (get $secretData "jwt-secret") | default (randAlphaNum 32 | b64enc) }} # сгенерировать случайную строку длиной 32 символа, закодировать ее в base64 и затем заключить полученную строку в двойные кавычки. jwt-secret: {{ $jwtSecret | b64enc | quote }}
Здесь мы больше не будем всегда устанавливать секретные данные на случайно сгенерированное значение.
Вместо этого мы будем пытаться получить текущее секретное значение с помощью функции поиска и устанавливать секретным данным случайно сгенерированное значение только в том случае, если не удастся найти ни одного существующего секретного значения.
Настройка политики ресурсов Helm
Теперь у нас есть решение, которое может работать не только с первоначальным развертыванием, но и с последующими обновлениями.
Но поскольку секрет управляется Helm, он всегда будет удален при удалении развертывания Helm.
Это не всегда может быть желаемым поведением для автоматически генерируемого секрета.
Для ресурса, управляемого Helm, установка политики ресурса через аннотацию `helm.sh/resource-policy` на “keep” может предотвратить удаление ресурса, когда пользователь удаляет/деинсталирует развертывание Helm.
Вот пример сохранения нашего секретного ресурса после удаления с помощью аннотации resource policy:
apiVersion: v1 kind: Secret metadata: name: "jwt-secret" annotations: "helm.sh/resource-policy": "keep" type: Opaque data: {{- $secret := (lookup "v1" "Secret" .Release.Namespace "jwt-secret") | default dict }} {{- $jwtSecret := (get $secretData "jwt-secret") | default (randAlphaNum 32 | b64enc) }} jwt-secret: {{ $jwtSecret | b64enc | quote }}
Как разрешить ввод секрета пользователем
Несмотря на то, что в большинстве случаев мы хотим использовать автоматическую генерацию секретов, мы все же можем разрешить пользователю вводить созданный им вручную секрет, чтобы сделать решение более гибким.
Для этого мы можем попросить пользователя указать имя созданного вручную секрета через поле конфигурации, например .Values.manualSecretName, и отображать автогенерируемый секрет только в том случае, если .Values.manualSecretName пуст.
{{- if empty .Values.manualSecretName }} apiVersion: v1 kind: Secret metadata: name: "jwt-secret" annotations: "helm.sh/resource-policy": "keep" type: Opaque data: {{- $secret := (lookup "v1" "Secret" .Release.Namespace "jwt-secret") | default dict }} {{- $jwtSecret := (get $secretData "jwt-secret") | default (randAlphaNum 32 | b64enc) }} jwt-secret: {{ $jwtSecret | b64enc | quote }} {{- end }}
Подробнее о функции lookup
Функция lookup в Helm – это очень мощный инструмент, который позволяет нам применять различную логику развертывания в зависимости от состояния кластера.
Поскольку функция lookup требует данных о состоянии кластера, имейте в виду, что она всегда будет возвращать пустой ответ:
- Запускайте чарт с помощью helm template
- Развертывайте или обновляйте чарт с помощью переключателя –dry-run
Заключение
Это удобно заставить ваш чарт Helm автоматически генерировать секреты для ваших пользователей там, где это необходимо.
Используя функцию lookup Helm, мы можем проверить текущее состояние кластера, чтобы создать и управлять секретом в чарте Helm более надежным и гибким способом.
см. также:
- ☸️ Как копировать секреты Kubernetes между пространствами имен
- ☸️ Как скопировать секреты из одного кластера Kubernetes в другой
- ☸️ Kubernetes и RBAC: ограничение доступа пользователя в одном namespace
- ☸️ Как раскодировать / расшифровать секрет Kubernetes
- ☸️ Шифрование секретов Kubernetes | Используем secrets
- ☸️ Изучаем Kubernetes: 5 лучших практик безопасности
- 🦊 Безопасность CI/CD – Как защитить свой пайплайн CI/CD