Mozilla SOPS vs Ansible Vault: шифрование отдельных полей YAML в GitOps-пайплайне
Протестировали SOPS от Mozilla как альтернативу ansible-vault: шифрование отдельных полей YAML, нормальный diff в git, интеграция с AWS KMS и HashiCorp Vault.
Mozilla SOPS (Secrets OPerationS) - инструмент для шифрования отдельных значений в YAML/JSON/ENV с поддержкой AWS KMS, GCP KMS и PGP
С ansible-vault мы жили несколько лет и в целом понимали его ограничения. Шифрование целого файла - это грубо, но терпимо, пока файл маленький и один человек его меняет. Когда в проекте появляются три-четыре DevOps-а и конфиги начинают меняться часто, ansible-vault превращается в источник боли.
Конкретный триггер: третий merge conflict за неделю в зашифрованном group_vars/all/vault.yml. Файл бинарный - мерджить невозможно, git не видит что там внутри, надо перешифровывать вручную. Плюс git diff на зашифрованный файл показывает бессмысленный поток байт. Ревью pull request в таком случае - формальность, а не реальная проверка.
Стали смотреть что есть.
Что такое SOPS
Mozilla SOPS (Secrets OPerationS) работает по другой модели: шифруются не файлы целиком, а значения отдельных ключей. Структура YAML остаётся в открытом виде, зашифрованы только сами значения. Это меняет картину принципиально.
До SOPS:
# group_vars/all/vault.yml - содержимое файла бинарное после ansible-vault encrypt
$ANSIBLE_VAULT;1.1;AES256
30363239383430653936643339383566...
(и ещё 40 строк такого же)
После SOPS:
db_password: ENC[AES256_GCM,data:k3Gh...,iv:abc...,tag:xyz...,type:str]
api_key: ENC[AES256_GCM,data:p9Rq...,iv:def...,tag:uvw...,type:str]
db_host: postgres.internal # не секрет - в открытом виде
db_port: 5432 # не секрет - в открытом виде
git diff теперь показывает что изменилось по имени ключа. Merge conflict если и возникает - то только если два человека меняли одно и то же значение, и git умеет его разрешить нормально. Ревью становится осмысленным.
Ключи: KMS и PGP
SOPS поддерживает несколько способов управления ключами. Попробовали два.
AWS KMS - для проектов где инфраструктура в AWS. SOPS использует data key, зашифрованный через KMS. Расшифровка происходит автоматически если у исполнителя есть нужные IAM-права. Для GitLab CI это instance profile EC2-машины с runner-ом - никаких ключей в переменных не нужно вообще. Ротация: меняешь data key в KMS, перешифровываешь файлы через sops -r. Доступ к секрету отзывается через IAM-политику.
PGP - для окружений вне AWS и для локальной разработки. Добавляешь fingerprint-ы всех кому нужен доступ в .sops.yaml, файл шифруется под все ключи одновременно. Каждый расшифровывает своим PGP-ключом. Новый человек в команде: добавил его fingerprint, перешифровал файлы - и всё.
Конфиг .sops.yaml в корне репозитория:
creation_rules:
- path_regex: environments/prod/.*
kms: arn:aws:kms:eu-west-1:123456789:key/aaaaa-bbbbb-ccccc
pgp: >-
FINGERPRINT1,
FINGERPRINT2
- path_regex: environments/staging/.*
pgp: >-
FINGERPRINT1,
FINGERPRINT2,
FINGERPRINT3
Прод - KMS плюс несколько PGP для аварийного доступа. Стейджинг - только PGP, у всей команды есть доступ.
Интеграция с GitLab CI
В пайплайне всё сводится к одному вызову перед ansible-playbook:
before_script:
- curl -LO https://github.com/mozilla/sops/releases/download/0.0.9/sops_linux_amd64 && mv sops_linux_amd64 /usr/local/bin/sops && chmod +x /usr/local/bin/sops
- sops --decrypt environments/prod/secrets.yml > /tmp/decrypted-secrets.yml
deploy:
script:
- ansible-playbook -e @/tmp/decrypted-secrets.yml site.yml
Runner крутится на EC2 с нужным instance profile - IAM-права на KMS есть автоматически. Никаких VAULT_SECRET_ID или ключей в CI Variables. Расшифровка происходит прямо в job-е, файл кладётся в /tmp и не покидает runner.
Можно и по-другому: расшифровать в переменную окружения и передать напрямую, не записывая на диск:
export DB_PASSWORD=$(sops -d --extract '["db_password"]' environments/prod/secrets.yml)
ansible-playbook site.yml
Второй способ аккуратнее: расшифрованные данные не пишутся на диск вообще, живут только в памяти процесса.
Связка с HashiCorp Vault
Для проектов где Vault уже есть, SOPS - не конкурент. Там секреты живут в Vault, и вопрос git-шифрования не стоит. Но есть класс файлов которые всё равно нужно хранить в git: Ansible-переменные с конфигурацией не-секретного типа вперемешку с секретами, Terraform-переменные, файлы с environment-специфичными параметрами. Туда HashiCorp Vault не тянешь - не тот уровень.
SOPS закрывает именно этот случай: «нам нужно хранить yaml в git, там есть пять секретных полей из двадцати, мы не хотим ради этого поднимать отдельный сервис».
Про сопровождение инфраструктуры с Vault мы писали недавно - там полноценный secret management для динамических credentials и PKI. SOPS это другой инструмент для другой задачи: статические конфиги в Git с минимальными операционными расходами.
Что не понравилось
Две вещи которые надо учитывать.
Нет ключа - нет файла. Если потерял PGP-ключ или IAM-права отозваны - файл нечем расшифровать. Ansible-vault в этом смысле не отличается, но с PGP ошибка выглядит менее очевидно: SOPS молча говорит «нет ни одного подходящего ключа» и уходит с ненулевым кодом. Нужно следить за backup-ами ключей.
Частичное шифрование приучает расслабляться. Когда часть файла в открытом виде, появляется соблазн «ой это не секрет» - и в открытой части оказываются данные которые лучше бы шифровать. Надо договариваться в команде что считать секретом явно.
Где сейчас
Перевели два проекта. На одном - AWS KMS для прода, PGP для стейджинга. На другом - только PGP, AWS там нет. Merge-конфликты в зашифрованных файлах с переездом на SOPS прекратились.
Ansible-vault остался на трёх старых проектах - переводить их не спешим, работает и ладно. Но на новых проектах где несколько человек работают с Ansible-переменными, будем начинать с SOPS.