Секреты: Ansible Vault
Пароли и ключи нельзя класть в git в открытом виде. ansible-vault шифрует файлы и отдельные значения, а в CI пароль приходит из защищённой переменной.
Проблема: секреты в репозитории
Playbook и роли живут в git. Вместе с ними туда тянутся переменные: пароль от базы, токен облака, приватный ключ. Положить их рядом в открытом виде — значит раздать всем, у кого есть доступ к репозиторию, включая историю коммитов, форки и зеркала.
Удалить секрет из последнего коммита мало: он остаётся в истории навсегда. Поэтому секрет не должен попадать в git незашифрованным вообще. Этим и занимается Ansible Vault.
Что делает ansible-vault
ansible-vault шифрует данные симметрично (AES256) по паролю. На входе -
обычный YAML, на выходе — блоб, который безопасно коммитить. При запуске
playbook Ansible расшифровывает его на лету, получив пароль.
Шифрование файла целиком
# зашифровать существующий файл
ansible-vault encrypt group_vars/prod/secrets.yml
# создать сразу зашифрованным
ansible-vault create group_vars/prod/secrets.yml
# редактировать (расшифровка во временный файл, потом обратно)
ansible-vault edit group_vars/prod/secrets.yml
# посмотреть, не расшифровывая на диск
ansible-vault view group_vars/prod/secrets.yml
# сменить пароль
ansible-vault rekey group_vars/prod/secrets.yml
После encrypt файл начинается со строки $ANSIBLE_VAULT;1.1;AES256 и
дальше идёт шифртекст. Внутри — обычный YAML, который Ansible видит как
переменные, когда расшифрует.
Шифрование одного значения
Шифровать целый файл не всегда удобно: тогда даже несекретные переменные
становятся нечитаемыми в diff. encrypt_string шифрует одно значение,
которое можно положить прямо в обычный vars-файл:
ansible-vault encrypt_string 'hunter2' --name 'db_pass'
Так пишут в каждом втором туториале, и так делать не стоит: пароль
уезжает в историю shell. Лежит он там открытым текстом до конца жизни
~/.bash_history или ~/.zsh_history, и его достаточно найти любому,
кто получит доступ к твоему домашнему каталогу. Заодно строку видно в
ps в момент выполнения.
Правильный вариант — читать значение со stdin:
ansible-vault encrypt_string --stdin-name 'db_pass'
Команда ждёт ввода, ты вставляешь пароль и жмёшь Ctrl-D. Аргументов с секретом нет, в историю попадает только безобидная строка.
Дальше всё одинаково — команда выводит готовый кусок YAML:
db_pass: !vault |
$ANSIBLE_VAULT;1.1;AES256
6162636465666768...
Остальные переменные в файле остаются в открытом виде, видны в diff, а секретное значение — зашифровано.
Как отдать пароль при запуске
# спросить пароль интерактивно
ansible-playbook site.yml --ask-vault-pass
# взять из файла
ansible-playbook site.yml --vault-password-file ~/.vault_pass
# или через переменную окружения
export ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass
ansible-playbook site.yml
--vault-password-file принимает и файл с паролем, и исполняемый скрипт,
который пароль печатает в stdout — так его тянут из внешнего хранилища.
Если сред несколько (dev, prod) и у каждой свой пароль, их различают через
vault-id:
ansible-playbook site.yml \
--vault-id dev@~/.vault_dev \
--vault-id prod@~/.vault_prod
Vault в CI
В пайплайне интерактивный ввод недоступен, а класть пароль в репозиторий нельзя. Схема такая:
- Пароль лежит в защищённой CI/CD-переменной (в GitLab — protected + masked): не видна в логах, доступна только на защищённых ветках, не уходит в форки.
- Джоба пишет её во временный файл и запускает с
--vault-password-file. - Разработчики коммитят зашифрованные файлы, но самого пароля не имеют - расшифровка происходит только в CI.
# .gitlab-ci.yml (фрагмент)
deploy:
script:
- install -m 600 /dev/null /tmp/vault_pass # создать файл сразу с правами 600
- echo "$ANSIBLE_VAULT_PASS" > /tmp/vault_pass
- ansible-playbook site.yml --vault-password-file /tmp/vault_pass
Когда Vault не лучший выбор
Vault хорош, пока секретов немного и они меняются редко. Когда секретов много, их надо ротировать и выдавать по-разному разным командам - зашифрованные файлы в git становятся неудобными.
Тогда секреты держат во внешнем хранилище (HashiCorp Vault, облачный
secrets manager) и тянут их в рантайме через lookup или модуль. В
репозитории секретов нет вообще, а доступ хранилищу даётся пайплайну по
короткоживущему токену или OIDC.
Что важно запомнить
Секрет не должен попадать в git незашифрованным — даже удалённый, он остаётся в истории.
ansible-vault encrypt/edit/view/rekey работают с файлом целиком;
encrypt_string шифрует одно значение внутри обычного vars-файла.
Секрет передавай через --stdin-name, а не аргументом — иначе он
осядет в истории shell.
Пароль при запуске даётся через --ask-vault-pass,
--vault-password-file или ANSIBLE_VAULT_PASSWORD_FILE; разные среды
разводятся через vault-id.
В CI пароль приходит из защищённой переменной во временный файл; ключ разработчикам не отдаётся.
Для большого числа ротируемых секретов вместо vault-файлов берут внешнее хранилище и тянут секреты в рантайме.
Дальше — короткий квиз по всему треку ansible-basics.
Дрилл · вопросы как на собесе
Проверь себя без подсказок
Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.
Vault-зашифрованный файл нужно дать прочитать GitLab CI и при этом не отдавать ключ разработчикам. Как организуешь?
ANS-019Как Ansible Vault защищает секреты: шифрование файлов, переменных, как работает в CI?
ANS-010Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс