← Ansible
13 мин · средне · Урок 11 из 12

Секреты: Ansible Vault

Пароли и ключи нельзя класть в git в открытом виде. ansible-vault шифрует файлы и отдельные значения, а в CI пароль приходит из защищённой переменной.

Проблема: секреты в репозитории

Playbook и роли живут в git. Вместе с ними туда тянутся переменные: пароль от базы, токен облака, приватный ключ. Положить их рядом в открытом виде — значит раздать всем, у кого есть доступ к репозиторию, включая историю коммитов, форки и зеркала.

Удалить секрет из последнего коммита мало: он остаётся в истории навсегда. Поэтому секрет не должен попадать в git незашифрованным вообще. Этим и занимается Ansible Vault.

Что делает ansible-vault

ansible-vault шифрует данные симметрично (AES256) по паролю. На входе - обычный YAML, на выходе — блоб, который безопасно коммитить. При запуске playbook Ansible расшифровывает его на лету, получив пароль.

secrets.yml db_pass: hunter2 encrypt $ANSIBLE_VAULT 61626364... (AES256) безопасно в git + пароль расшифровка в памяти, на лету в файл не пишется
В репозитории лежит шифртекст, а открытое значение живёт только в памяти во время прогона.

Шифрование файла целиком

# зашифровать существующий файл
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

В пайплайне интерактивный ввод недоступен, а класть пароль в репозиторий нельзя. Схема такая:

  1. Пароль лежит в защищённой CI/CD-переменной (в GitLab — protected + masked): не видна в логах, доступна только на защищённых ветках, не уходит в форки.
  2. Джоба пишет её во временный файл и запускает с --vault-password-file.
  3. Разработчики коммитят зашифрованные файлы, но самого пароля не имеют - расшифровка происходит только в 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.

Дрилл · вопросы как на собесе

Проверь себя без подсказок

Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.

01

Vault-зашифрованный файл нужно дать прочитать GitLab CI и при этом не отдавать ключ разработчикам. Как организуешь?

ANS-019
02

Как Ansible Vault защищает секреты: шифрование файлов, переменных, как работает в CI?

ANS-010

Прошли материал до конца? Отметьте урок пройденным.