Kubernetes: безопасное управление секретами с GitOps

GitOps  —  это модное словечко в последнее время на слуху. Обозначает оно, по сути, декларативное управление ресурсами для такого представления текущего состояния, чтобы в любой момент можно было понять, где что находится в git, а также оно обозначает разрешение этого декларативного состояния кластеру.

Больше всего ошибок при работе с GitOps связано со структурой. Структура репозиториев имеет первостепенное значение. От того, как организована GitOps в компании, часто зависит её успех или неудача.

Разобравшись со структурой, можно переходить к следующей по важности задаче  —  обеспечению сохранности секретных данных. Обычно выбирается компромиссный вариант решения этой задачи: получение доступа теми, кому эти данные не нужны, либо секретный ключ в общем доступе, который передаётся через Slack или 1Password.

Многим знаком такой способ хранения секретных данных на Kubernetes, как Sealed Secrets  —  неплохое решение, основанное на PKI, совместном использовании открытого ключа для шифрования и на установке закрытого ключа в кластер. Но всё-таки есть решение получше.

Лучший способ хранить секреты

Mozilla SOPS  —  этот инструмент поддерживает несколько источников материала для ключа шифрования, в том числе AWS KMS, GCE, Vault, PGP и другие. Он даёт возможность использовать материал внешнего ключа из AWS KMS для шифрования и дешифрования секретных данных. Кроме того, он поддерживает схему разделения секрета Шамира и группы ключей, фактически позволяя определять, какие ключи и в каком количестве требуются для расшифровки.

Благодаря применению в SOPS таких сервисов, как AWS KMS, можно контролировать, кто имеет доступ к шифрованию и дешифрованию, а также задействовать профили экземпляров AWS, позволяя машинам делать то, для чего они были созданы и что умеют лучше всего: через автоматизацию делать нашу жизнь проще.

И последняя особенность этого решения, на которую хотелось бы обратить ваше внимание: после того как файл будет зашифрован, все метаданные о том, как он был зашифрован, становятся частью самого файла. Нет необходимости в каком-либо стороннем сервисе, базе данных, скрытом каталоге или вспомогательном файле, для того чтобы узнать, как расшифровать этот файл.

Хорошая новость: Flux имеет первоклассную поддержку SOPS.

Структура

В начале статьи уже упоминалось о том, что очень важно правильно структурировать репозитории.

В структурировании выделяются две составляющие: расположение содержимого в репозитории и количество используемых репозиториев. Лично у меня три репозитория. Один для определения каждого базового ресурса, который необходим кластеру для функционирования; один для работы со всеми релизами продукта, за который отвечает команда разработчиков; и один для секретных данных. Я использую SOPS только с репозиторием, выделенным под секретные данные.

Flux и SOPS предоставляют нам эту возможность с беспрецедентной гибкостью.

Структура файла

|-- .sops.yaml
|-- secrets
    |-- dev (environment/cluster)
        |-- database.yaml
    |-- stg (environment/cluster)
        |-- database.yaml
    |-- prd (environment/cluster)
        |-- database.yaml

Эта структура файла даёт нам базу, необходимую для применения правил создания SOPS, чтобы контролировать то, как файлы шифруются. Можно использовать различные ключи, группы ключей и пороговые значения. Кроме того, такая структура позволяет указать Flux конкретный путь, которым он должен заниматься во время клонирования, а также определиться с содержимым кластера. Вот пример (нерабочий, но всё же):

creation_rules:
- path_regex: secrets/dev/.*
  encrypted_regex: "^(data|stringData)$"
  kms:
    arn: kms-arn
    role: ksm-role
- path_regex: secrets/stg/.*
  encrypted_regex: "^(data|stringData)$"
  kms:
    arn: kms-arn
    role: ksm-role
- path_regex: secrets/prd/.*
  encrypted_regex: "^(data|stringData)$"
  kms:
    arn: kms-arn
    role: ksm-role

Масштабирование

Имея эту настройку, можно масштабировать её почти бесконечно. Ограничена она лишь объёмом данных, хранящихся в репозитории, и сервером git, который его обслуживает. Обычно секретные данные меняются нечасто, но когда это происходит, приходятся кстати дополнительные инструменты, такие как Reloader.

Настройка SOPS

Теперь, когда мы рассмотрели теоретические принципы, можем переходить к практическому их осуществлению. Есть множество способов применения SOPS, но реализую я лишь один из них. Это AWS KMS с несколькими ключами и профилем экземпляра AWS, который Flux может использовать.

Настройка AWS

В реальном сценарии я бы рекомендовал задействовать несколько учётных записей AWS, присвоив к ним права доступа, однако такой расклад мог бы стать неоправданно сложным для одной статьи, поэтому ограничимся единственной учётной записью AWS, но всё с теми же тремя ключами:

aws kms create-key

Не забудьте собрать названия ресурсов Amazon для последующего использования.

Теперь нам нужен файл .sops.yaml с правилами создания. В реальном сценарии порог должен быть не менее 2, а ключи  —  превышать это пороговое значение, по крайней мере, на N+1.

Начнём с создания базового каталога, а также файла .sops.yaml. Ниже показано его содержимое (замените названия ресурсов Amazon на валидные):

creation_rules:
- path_regex: secrets/dev/.*
  encrypted_regex: "^(data|stringData)$"
  shamir_threshold: 2
  key_groups:
    - kms:
        - arn: arn:aws:kms:us-west-2:000000000000:key/b5d44bf0-7ec5-49a9-b404-bc4d8b4036fb
    - kms:
        - arn: arn:aws:kms:us-west-2:000000000000:key/16d44186-2393-40d9-90e1-9a2f92fd5863
    - kms:
        - arn: arn:aws:kms:us-west-2:000000000000:key/2120d2c1-a89e-4aeb-844f-f17ae2abd210

Это правило создания гласит, что все файлы в каталоге secrets/dev будут зашифрованы с помощью трёх различных ключей с порогом дешифрования 2.

Теперь создадим каталог secrets/dev, а также файл example.yaml со следующим содержимым:

apiVersion: v1
kind: Secret
metadata:
  name: example
type: Opaque
data:
  username: bXktdXNlcm5hbWU=
  password: bXktcGFzc3dvcmQ=

Чтобы зашифровать файл, выполним такую команду:

sops -e -i secrets/dev/example.yaml

-e обозначает шифрование, -i  —  изменение файла на месте. Если хотите оставить файл без изменений и увидеть зашифрованный вывод, просто опустите аргумент -i.

Теперь можно сделать коммит этой настройки в репозитории.

Профиль экземпляра AWS

Потрясающей эту систему делает то, что Flux может просто воспользоваться профилем экземпляра AWS, присвоив права доступа через STS, и получить временные ключи для расшифровывания секретных данных во время работы в самом кластере.

Для этого нужно настроить права доступа с Encrypt/Decrypt (шифровать/дешифровать) или Generate permissions (генерировать разрешения) в зависимости от типа используемого ключа, а также настроить экземпляр AWS для использования профиля.

После настройки просто устанавливаем Flux в кластер с правильными опциями командной строки и ждём, когда он обновит кластер.

Настройка Flux

Чтобы убедиться в том, что Flux работает правильно, нужно, во-первых, активировать SOPS. В зависимости от метода развёртывания это может сделать параметр конфигурации или флаг CLI --sops. Во-вторых, указать экземпляру Flux конкретный путь в ветви по умолчанию, которым он должен заниматься. --git-path должен быть установлен в один из каталогов с секретными данными, например --git-path=secrets/dev.

Заключение

Применение этого метода позволяет лучше контролировать материал для ключа шифрования и тех, кто имеет к нему доступ. А кроме того, облегчает задачу предоставления ключей для доступа к ключам для дешифрования с помощью автоматизации и секретных данных временного хранения. Теперь можно хранить все секреты в одном месте, ведь менять секретные данные и управлять ими стало проще.

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Erik Kristensen: Managing Kubernetes Secrets Securely with GitOps