ADG Оставить заявку
Блог Автоматизация 4 мин чтения

Ansible Vault: шифруем секреты прямо в репозитории

Ansible 1.7 добавил Vault для шифрования секретов в playbook-файлах. Разбираем процесс миграции с файлов вне git, ротацию ключа и интеграцию с CI.

Контекст момента

Ansible 1.7 представил Vault - встроенное шифрование секретов внутри playbook-файлов и переменных

До появления Vault у нас было стандартное решение: секреты - пароли к базам, токены API, приватные ключи - лежали в отдельных файлах вне репозитория. Playbook-и в git, секреты на общем сетевом ресурсе или просто в ~/secrets/ у каждого из команды. Синхронизировали вручную. Всё работало ровно до момента, когда кто-то деплоил со старым файлом переменных после ротации пароля. Ansible не ругался - просто использовал устаревший пароль, и сервис падал уже после деплоя. Искать потом было весело.

В Ansible 1.7 появился Vault - AES-256 прямо внутри YAML. Зашифрованный файл переменных живёт рядом с playbook-ом в том же репозитории.

Как это выглядит на практике

Переменные с секретами раньше хранились в group_vars/production/secrets.yml - файл был в .gitignore, передавался отдельно. Теперь он шифруется на месте:

ansible-vault encrypt group_vars/production/secrets.yml

Команда запрашивает vault-пароль, перезаписывает файл. После этого содержимое выглядит так:

$ANSIBLE_VAULT;1.1;AES256
36663430383938373066643234323733303936623865623162626263323039323236306461343734
...

Файл можно коммитить - без vault-пароля это просто набор hex-байт. Ansible при запуске просит пароль сам:

ansible-playbook site.yml --ask-vault-pass

или берёт из файла:

ansible-playbook site.yml --vault-password-file ~/.vault_pass

Структура, которая у нас сложилась

После миграции на роли в июле разделили переменные на два уровня: vars.yml с незасекреченными параметрами (порты, пути, флаги) и secrets.yml с паролями и токенами - только второй под Vault. Это удобно: можно читать vars.yml без пароля и понимать что вообще настраивается.

group_vars/
  production/
    vars.yml        # открытый, в git
    secrets.yml     # зашифрован Vault, тоже в git

В playbook-е оба файла подхватываются автоматически по директории.

Ротация ключа vault

Слабое место схемы - если vault-пароль утёк, нужно перезашифровать всё. Команда rekey для этого:

ansible-vault rekey group_vars/production/secrets.yml

Команда просит старый пароль, затем новый. Один файл - одна операция. Если зашифрованных файлов несколько, приходится делать по одному - скриптуем через find:

find . -name 'secrets.yml' | xargs -I{} ansible-vault rekey {}

Важно: rekey меняет ключ шифрования, но не меняет сами секреты. Если нужна полная ротация (и пароль vault, и пароли внутри), это два отдельных шага.

Интеграция с CI

На управляемых серверах у нас деплои идут через Jenkins. Передавать vault-пароль интерактивно очевидно нельзя, файл с паролем на агент тоже класть не хочется - ещё один объект для защиты.

Пока остановились на таком компромиссе: vault-пароль хранится в Jenkins в разделе Credentials как секретный текст, в pipeline-е пишется во временный файл перед запуском playbook-а и удаляется после:

withCredentials([string(credentialsId: 'vault-pass', variable: 'VAULT_PASS')]) {
    sh '''
        echo "$VAULT_PASS" > /tmp/vp_$BUILD_NUMBER
        ansible-playbook site.yml --vault-password-file /tmp/vp_$BUILD_NUMBER
        rm -f /tmp/vp_$BUILD_NUMBER
    '''
}

Не идеально - в случае аварийного завершения файл может остаться. Правим через trap в sh-блоке. Решение рабочее, но требует аккуратности.

Другой вариант, который смотрели - --vault-password-file с исполняемым скриптом, который достаёт пароль из защищённого хранилища. Но это уже требует отдельного хранилища секретов, а у нас его пока нет.

Что изменилось в работе

Секреты в одном репозитории с кодом. Нет рассинхронизации между кодом и переменными - они версионируются вместе. Если в коммите меняется переменная, видно сразу что именно.

Дифф зашифрованных файлов. В git-е зашифрованные файлы меняются полностью при каждом изменении - осмысленного diff-а нет. Это раздражает: не видно что конкретно поменялось в secrets.yml. Vault не умеет шифровать отдельные значения в YAML - только файл целиком. Обходим комментариями в незашифрованном vars.yml типа «пароль обновлён, причина - плановая ротация».

Grep по секретам невозможен. Очевидно - зашифровано. Если нужно найти где используется переменная db_password, ищем по коду, а не по значению. Это нормально.

Один пароль на всё. Сейчас у нас один vault-пароль на весь репозиторий. В Ansible 1.7 это единственный вариант - разные пароли для разных файлов не поддерживаются. Если понадобится разделить доступ - придётся держать отдельные репозитории.

Итог

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

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.