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

Ansible Vault в Jenkins-пайплайне: зашифрованные секреты в git без паранойи

Ansible Vault 2.0 позволяет хранить секреты прямо в репозитории. Интегрируем vault-password с Jenkins через переменную среды - разработчики не видят prod-credentials, сборка работает.

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

Ansible Vault в составе Ansible 2.0 позволяет хранить зашифрованные секреты прямо в репозитории git

В одном из проектов на сопровождении у нас сложилась классическая ситуация: Ansible-роли в git, Jenkinsfile в git, а файл с паролями БД и API-ключами - на сервере в каком-то /etc/ansible/secrets.yml, который никто не версионировал и который однажды потерялся при переезде на новый Jenkins-агент. Мы потратили сорок минут на восстановление. После этого сели и нормально разобрались с Ansible Vault.

Что такое Ansible Vault

Vault - это механизм шифрования файлов прямо внутри Ansible-репозитория. Появился он не в 2.0 - базовая функциональность была ещё в 1.x - в 2.0 он подтянулся вместе с остальными улучшениями выпуска. Нас интересует именно сценарий «зашифрованный файл с секретами живёт в git».

Смысл простой: берёшь файл group_vars/production/vault.yml с паролями и ключами, прогоняешь его через ansible-vault encrypt, и в репозитории оказывается что-то вроде:

$ANSIBLE_VAULT;1.1;AES256
38663465373262303665656566323961343634376262626165336634613665666130356234633462
...

Этот зашифрованный blob можно коммитить без опасений. Расшифровать его без vault-password невозможно - AES-256. Разработчики видят в git историю изменений файла, но не содержимое.

Откуда Ansible берёт пароль

Вот тут начинается практика. Запустить ansible-playbook с vault-файлом можно тремя способами:

  • --ask-vault-pass - интерактивный промпт, не подходит для CI вообще.
  • --vault-password-file /path/to/file - читает пароль из файла. Вариант рабочий, но файл надо где-то хранить на агенте.
  • Переменная среды ANSIBLE_VAULT_PASSWORD_FILE - указывает на тот же файл, но через env.

Мы пошли таким путём: vault-password хранится в Jenkins как Secret Text (Credentials), Jenkins при запуске сборки записывает его во временный файл, ANSIBLE_VAULT_PASSWORD_FILE указывает на этот файл, после сборки файл удаляется. Никакого хранения на диске агента между сборками.

Как это выглядит в Jenkinsfile

node {
    withCredentials([string(credentialsId: 'ansible-vault-password', variable: 'VAULT_PASSWORD')]) {
        stage('Deploy') {
            sh '''
                echo "$VAULT_PASSWORD" > /tmp/vault_pass_$BUILD_NUMBER
                chmod 600 /tmp/vault_pass_$BUILD_NUMBER
                export ANSIBLE_VAULT_PASSWORD_FILE=/tmp/vault_pass_$BUILD_NUMBER
                ansible-playbook -i inventory/production deploy.yml
                rm -f /tmp/vault_pass_$BUILD_NUMBER
            '''
        }
    }
}

Jenkins маскирует содержимое VAULT_PASSWORD в логах сборки - в выводе появляется ****. Файл создаётся с правами 600. После деплоя удаляется.

Один момент, на который потратили время: если ansible-playbook упадёт с ошибкой до строчки с rm -f, файл останется на диске. Решили через trap:

sh '''
    VAULT_FILE=/tmp/vault_pass_$BUILD_NUMBER
    echo "$VAULT_PASSWORD" > "$VAULT_FILE"
    chmod 600 "$VAULT_FILE"
    trap "rm -f $VAULT_FILE" EXIT
    ANSIBLE_VAULT_PASSWORD_FILE="$VAULT_FILE" ansible-playbook -i inventory/production deploy.yml
'''

trap ... EXIT сработает при любом выходе из shell, включая ошибку.

Структура репозитория

Секреты живут отдельным файлом рядом с обычными переменными:

group_vars/
  production/
    vars.yml        # обычные переменные, открытый текст
    vault.yml       # зашифрованный vault-файл

В vars.yml можно даже сослаться на vault-переменные по имени для удобства:

# vars.yml
db_password: "{{ vault_db_password }}"
api_key: "{{ vault_api_key }}"

А в vault.yml уже реальные значения. Редактировать vault-файл можно командой ansible-vault edit vault.yml - откроет в $EDITOR, сохранит обратно зашифрованным.

Что получилось на практике

Разработчики работают с репозиторием как обычно. Vault-файл они видят - это зашифрованный blob, без пароля от него ноль пользы. Production-credentials знают только те, кому передан vault-password через личный канал. Jenkins знает его через Credentials Store.

Побочный эффект: git-история теперь показывает, что секреты менялись (и когда), но не что именно. Это лучше, чем было - хоть какой-то аудит.

Пара вещей, которые немного раздражают. Ротация vault-password - это ansible-vault rekey vault.yml, а потом нужно обновить секрет в Jenkins и у всех, кто имеет доступ. Не страшно, но вручную. И если в команде несколько человек редактируют vault-файл, нужно договариваться о единственном vault-password - несколько паролей на один файл vault не поддерживает.

В целом - работает, надёжно, и то, что мы потеряли сорок минут на восстановление старой схемы, больше не повторится.

Контакт

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

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