Вопрос или проблема
Я хотел бы использовать ansible для управления группой существующих серверов. Я создал файл ansible_hosts
и успешно протестировал (с опцией -K
) команды, которые нацелены только на один хост.
ansible -i ansible_hosts host1 --sudo -K # + команды ...
Моя проблема сейчас в том, что пароли пользователей на каждом хосте разные, и я не могу найти способа обработать это в Ansible.
При использовании -K
мне предлагается ввести только один sudo пароль в начале, который затем, похоже, пробуется для всех последующих хостов без повторных уведомлений:
host1 | ...
host2 | FAILED => Неправильный sudo пароль
host3 | FAILED => Неправильный sudo пароль
host4 | FAILED => Неправильный sudo пароль
host5 | FAILED => Неправильный sudo пароль
Исследования на данный момент:
-
вопрос на StackOverflow с одним неверным ответом (“использовать
-K
“) и одним ответом автора, говорящим “Понял, что мне нужна sudo без пароля”. -
документация Ansible, где говорится “Использование sudo без пароля облегчает автоматизацию, но оно не требуется.” (выделено мной)
-
вопрос на Security StackExchange, где рассматривается, что
NOPASSWD
необходим -
статья “Масштабируемое и понятное оснащение…”, где говорится:
“выполнение sudo может требовать ввода пароля, что наверняка заблокирует Ansible навсегда. Простое решение — запустить visudo на целевом хосте и убедиться, что пользователь, которого Ansible будет использовать для входа, не должен вводить пароль”
-
статья “Базовые сценарии Ansible”, где говорится
“Ansible может войти на целевой сервер как root и избежать необходимости использования sudo, или позволить пользователю ansible использовать sudo без пароля, но мысль о том, чтобы сделать что-то из этого, заставляет мою селезенку угрожать подняться через мое горло и заблокировать дыхательную трубку, так что я этого не делаю”
Мои мысли точно такие же, но как же продвигаться дальше одного сервера?
-
an issue #1227 в ansible, “Ansible должен спрашивать sudo пароль для всех пользователей в playbook”, который был закрыт год назад mpdehaan с комментарием “Не видел большого спроса на это, я думаю, большинство людей использует sudo только для одной учетной записи пользователя или часто используют ключи.”
Итак… как люди используют Ansible в таких ситуациях? Установка NOPASSWD
в /etc/sudoers
, повторное использование паролей по хостам или разрешение входа SSH для root кажутся весьма радикальными снижениями безопасности.
Вы действительно провели исследование…
Из всего моего опыта работы с ansible, то, что вы пытаетесь сделать, не поддерживается. Как вы упомянули, ansible заявляет, что ему не требуется sudo без пароля, и вы правы, не требуется. Но я еще не видел ни одного способа использования нескольких sudo паролей в ansible, если, конечно, не использовать несколько конфигураций.
Поэтому я не могу предложить точное решение, которое вы ищете, но вы ведь спросили…
“Итак… как люди используют Ansible в таких ситуациях? Установка
NOPASSWD в /etc/sudoers, повторное использование паролей по хостам или разрешение
root SSH входа кажутся весьма радикальными снижениями безопасности.”
Я могу дать одну точку зрения на это. В моем случае это 1k узлов в нескольких дата-центрах, поддерживающие глобальную SaaS компанию, в которой мне необходимо проектировать/внедрять некоторые чрезвычайно жесткие меры безопасности из-за характера нашего бизнеса. Безопасность всегда является компромиссом, больше удобства — меньше безопасности, этот процесс не отличается, если вы управляете 10 серверами или 1,000 или 100,000.
Вы абсолютно правы, не используй непосредственные входы root ни через пароли, ни через ssh ключи. На самом деле, вход root должен быть полностью отключен, если у серверов подключен сетевой кабель.
Поговорим о повторном использовании паролей, в крупном предприятии, разумно ли требовать от системных администраторов различных паролей на каждом узле? Для пары узлов, возможно, но мои администраторы/инженеры устроили бы бунт, если бы им пришлось иметь разные пароли на 1000 узлов. Реализовать это было бы почти невозможно также, каждый пользователь должен был бы где-то хранить свои пароли, надеюсь, в keypass, а не в таблице Excel. И каждый раз, когда вы помещаете пароль в местоположение, где его можно извлечь в текстовом виде, вы сильно снижаете свою безопасность. Я бы предпочёл, чтобы они знали, по памяти, один или два действительно сильных пароля, чем каждый раз обращаться к файлу keypass, когда им нужно войти или использовать sudo на машине.
Поэтому повторное использование паролей и стандартизация совершенно приемлемы и стандартны даже в безопасной среде. В противном случае ldap, keystone и другие сервисы каталогов не существовали бы.
Когда мы переходим к автоматизированным пользователям, ssh ключи отлично помогают попасть внутрь, но вам все равно нужно пройти через sudo. Ваши варианты — стандартизированный пароль для автоматизированного пользователя (что приемлемо в многих случаях) или включение NOPASSWD, как вы указали. Большинство автоматизированных пользователей выполняют только несколько команд, так что вполне возможно и, конечно, желательно включить NOPASSWD, но только для заранее утвержденных команд. Я бы предложил использовать вашу систему управления конфигурациями (в данном случае ansible), чтобы управлять sudoers файлом, чтобы вы могли легко обновлять список команд без пароля.
Теперь, есть некоторые шаги, которые вы можете предпринять, когда вы начинаете масштабироваться, чтобы изолировать риск. Хотя у нас 1000 или около того узлов, не все из них являются ‘продуктивными’ серверами, некоторые являются тестовыми средами и т.д. Не все администраторы могут получить доступ к продуктивным серверам, те, кто может, используют свою же учетную запись/пароль|ключ SSO, как и в других местах. Но автоматизированные пользователи немного безопаснее, например, автоматизированный инструмент, к которому могут получить доступ не продуктивные администраторы, имеет пользователя и учетные данные, которые нельзя использовать в продуктивной среде. Если вы хотите запустить ansible на всех узлах, вам пришлось бы делать это в два этапа, один раз для непродуктивной среды и один раз для продуктивной.
Мы также используем puppet, так как это инструмент управления конфигурациями с принуждением, поэтому большинство изменений в среде распространяются через него.
Очевидно, если это предложение по функциональности, которое вы упомянули, будет переоткрыто/выполнено, то, что вы хотите сделать, будет полностью поддержано. Даже тогда, безопасность — это процесс оценки риска и компромисса. Если у вас всего несколько узлов, для которых вы можете запомнить пароли без использования заметок, отдельные пароли были бы немного более безопасными. Но для большинства из нас это неосуществимо.
С версии Ansible 1.5, появилась возможность использовать зашифрованный хранилище для host_vars и других переменных. Это, по крайней мере, позволяет безопасно хранить переменные ansible_sudo_pass
для каждого хоста (или группы). К сожалению, --ask-vault-pass
будет предлагать только один пароль от хранилища за вызов ansible, так что вы по-прежнему ограничены одним паролем от хранилища для всех хостов, которые вы используете вместе.
Тем не менее, для некоторых задач это может быть улучшением по сравнению с использованием единого пароля sudo для нескольких хостов, так как злоумышленнику без доступа к вашему зашифрованному host_vars по-прежнему потребуется отдельный пароль sudo для каждой машины (или группы машин), которые он или она атакует.
С версией Ansible 1.5 можно установить переменную ansible_sudo_pass с помощью lookup('password', …)
:
ansible_sudo_pass: "{{ lookup('password', 'passwords/' + inventory_hostname) }}"
Я нахожу это более удобным, чем использование файлов в host_vars/
по нескольким причинам:
-
Я фактически использую
with_password: "passwords/{{ inventory_hostname}} encrypt=sha256_crypt"
для создания паролей для удаленного пользователя deploy (который затем требуется для sudo),
поэтому они уже присутствуют в файлах
(хотя выполнение этих текстовых обращений теряет значение соли,
сохраненное в файле при генерации зашифрованного значения). -
Это позволяет хранить в файле только пароли (никак не
ansible_sudo_pass:
известный текст) для некоторого увеличения криптографической безопасности.
Гораздо значительнее, это означает, что вы не шифруете все другие переменные, специфичные для хоста,
поэтому их можно читать без пароля от хранилища. -
Помещение паролей в отдельное каталоги облегчает удержание файлов вне системы контроля версий, или использование такого инструмента, как git-crypt для хранения их в зашифрованной форме (вы можете использовать это с более ранними версиям Ansible, которые не имеют функции хранилища).
Я использую git-crypt, и поскольку я только извлекаю репозиторий в расшифрованной форме на зашифрованных файловых системах, я не забочусь о хранилище и, следовательно, мне не нужно вводить пароль от хранилища. (Использование обоих, конечно, было бы более безопасным.)
Вы также можете использовать функцию lookup с ansible_ssh_pass; это может даже быть возможным с более ранними версиями Ansible, которые не имеют ansible_sudo_pass.
Использование pass — это простой метод предоставления ansible sudo паролей. pass хранит по одному паролю на файл, что упрощает обмен паролями через git или другими методами. Это также безопасно (с использованием GnuPG), и если вы используете gpg-agent, это позволяет использовать ansible без ввода пароля при каждом использовании.
Чтобы предоставить пароль, хранящийся как servers/foo
для сервера foo
ansible, используйте это в инвентарном файле следующим образом:
[servers]
foo ansible_sudo=True \
ansible_sudo_pass="{{ lookup('pipe', 'pass servers/foo') }}"
Если вы разблокировали ключ для gpg-agent ранее, это позволит запустить ansible без необходимости вводить пароль.
Обновление: В Ansible эта функциональность теперь встроенная, через плагин passwordstore. Эквивалентом вышеуказанного будет:
[servers]
foo ansible_sudo=True \
ansible_sudo_pass="{{ lookup('passwordstore', 'servers/foo') }}"
Возможно даже создание пароля, если он еще не существует, что может быть чрезвычайно полезным.
Это довольно старая тема, тем не менее:
- У нас в компании используются две разные системы аутентификации, управление машинами осуществляется с локальных рабочих станций в моей команде.
- Я написал
vars_plugin
для Ansible (довольно полная реализация может быть найдена по ссылке https://gist.github.com/mfriedenhagen/e488235d732b7becda81), который различает несколько систем аутентификации: - Название системы аутентификации зависит от группы.
- Используемый для входа/судо пользователь зависит от группы и администратора.
- Таким образом, я извлекаю пользователя из окружения администратора и соответствующий пароль через библиотеку python-keyring из хранилища паролей (мы используем связку ключей Mac OS X, но, согласно документации, также поддерживаются kwallet Gnome и _win_crypto).
- Я устанавливаю разрешения на пароли в связке ключей Mac OS X так, чтобы меня уведомляли о том, что программная линия security запрашивает доступ к паролю для каждой использованной системы аутентификации
хостов, поэтому я запускаю “ansible -s all -m ping” и получаю два запроса (по одному для каждой системы аутентификации), где я нажимаю пробел и ansible выбирает пароли.
Кто окажется здесь поздно: см. https://stackoverflow.com/a/37002802
Для каждого хоста, в вашем файле инвентаря хостов (inventory/<inventoryname>/hosts)
[сервер]
10.0.0.0 ansible_sudo_pass=foobar
По группе, в вашем файле групп инвентаря (inventory/<inventoryname>/groups)
[сервер:vars]
ansible_sudo_pass=foobar
По группе, в group_vars (group_vars/<groupname>/ansible.yml) и зашифровано (ansible-vault create group_vars/<groupname>/ansible.yml)
ansible_sudo_pass: "foobar"
Один из возможных способов сделать это — использовать переменные окружения.
например:
pass1=foo pass2=bar ansible-playbook -i production servers.xml
Затем в сценариях вы можете найти sudo пароль, используя:
lookup('env', 'pass1')
lookup('env', 'pass2')
Это вполне возможно. Вы можете определить ваш инвентарь для использования переменных для таких вещей, как пароли:
INVENTORY:
[023_mqtt]
023-softbank-mqtt01 ansible_host=IP.IP.IP.IP ansible_ssh_private_key_file=/path/to/my_key ansible_ssh_user=my_user ansible_become=yes ansible_become_method=sudo ansible_become_pass="{{ my_user_pass }}"
Вам нужен файл group_vars/023_mqtt.yaml там, где находится ваш инвентарь, если вы используете промежуточный сервер(если он вам нужен):
ansible_ssh_common_args: '-o ProxyCommand="ssh -i /path/to/your/jump_key -W %h:%p -q [email protected]"'
Наконец, вы определяете хранилище для хранения ваших паролей. Предпочтительно создать его в той же папке, что и ваш файл инвентаря:
ansible-vault create passwds.yaml
[введите пароль от хранилища]
Добавьте пароли:
my_user : 'sudo пароль моего пользователя'
Чтобы запустить это, передайте хранилище как дополнительные переменные:
ansible -a "docker ps" -i /path/to/hosts hosts_group --ask-vault-pass --extra-vars '@passwd.yaml'
У меня это определено для нескольких хостов, каждый со своим SSH ключом, SSH паролем и SSH SUDO паролем, и это отлично работает. Для полной статьи, ознакомьтесь с моим git:
Это то, как я решаю эту задачу – я запускаю команду локально для чтения паролей для каждого хоста из моего хранилища паролей:
- name: Get sudo pass
ansible.builtin.shell: "pass {{inventory_hostname}}"
register: pass_result
delegate_to: 127.0.0.1
- set_fact:
ansible_become_password={{ pass_result.stdout }}
Таким образом, вы также можете получить пароли из системного хранилища, если у него есть интерфейс командной строки
Ответ или решение
Для успешного внедрения Ansible с паролями, зависящими от каждого хоста, необходимо учитывать сложности обеспечения безопасности и удобства использования. Ниже дано детальное объяснение на эту тему.
Теория
Ansible — это мощный инструмент автоматизации, который использует SSH для управления удалёнными серверами. Однако сетевая безопасность остаётся ключевой задачей, особенно когда речь идёт о доступе с использованием паролей. В случаях, когда у каждого сервера разные пароли для sudo, обеспечивать единообразное управление становится сложно. Использование единого пароля для всех серверов или полного отказа от применения паролей может негативно сказаться на безопасности. На этой основе важно построить систему, которая позволит безопасно использовать Ansible в указанном контексте без ущерба для эффективности.
Пример
Рассмотрим несколько стратегий, которые можно применить для решения проблемы:
-
Использование Ansible Vault: Ansible Vault позволяет шифровать ваши конфиденциальные данные, включая sudo пароли. Это улучшает безопасность, так как даже если файл с переменными будет скомпрометирован, содержимое останется зашифрованным. Например, в файле
group_vars
, вы можете хранить зашифрованные пароли для каждого хоста. -
Использование плагинов поиска (lookup plugins): Используя функцию
lookup('password', ...)
, вы можете извлекать пароли из безопасных хранилищ, таких как HashiCorp Vault или Pass (Linux password store). Это позволяет убрать необходимость хранить пароли в явном виде в файлах конфигурации Ansible. -
Установка NOPASSWD только для специфических команд: Используйте файл конфигурации sudoers, чтобы установить NOPASSWD для специфичных и безопасных команд, которые Ansible будет выполнять. Это позволяет минимизировать риск злоупотреблений, так как пользователи не получат полный контроль через sudo.
-
Использование ключей SSH и ограничения доступа: Вы можете использовать SSH ключи для авторизации и ограничить права доступа к sudo. Это позволит вам сохранить двухфакторную безопасность и ограничить использование паролей.
Применение
Как только определена стратегия, можно перейти к её реализации:
-
Конфигурирование Ansible Vault: Создайте файл конфигурации Vault и используйте его для шифрования ваших переменных. Это может включать
ansible_become_pass
переменные, которые хранятся в зашифрованном виде.ansible_become_pass: !vault | $ANSIBLE_VAULT;1.1;AES256 38656539393961623963326364636439633665323737666635313964643766336236353734366365
Используйте команду
ansible-vault encrypt
для создания зашифрованных данных, и--ask-vault-pass
для их расшифровки при применении playbook’ов. -
Настройка lookup plugins:
ansible_sudo_pass: "{{ lookup('passwordstore', 'servers/' + inventory_hostname) }}"
В этом примере используется плагин
passwordstore
, который позволяет извлекать пароли из хранилища Pass. -
Включение специфичных доступов для NOPASSWD в sudoers: Используйте команду
visudo
, чтобы добавить необходимые команды в список исключений на каждом хосте, где необходимо. Это позволяет снизить спрос на ввод паролей, сохраняя при этом безопасность.%dev_ops ALL=(ALL) NOPASSWD: /path/to/safe/command
-
Использование SSH ключей: Сгенерируйте SSH ключи для пользователей, которые будут использованы для входа. Обновите файл
authorized_keys
на каждом сервере соответствующим образом.
Заключение
Внедрение Ansible с поддержкой паролей для каждого хоста требует тщательного внимания к обеспечению безопасности. С точки зрения Ansible, использование Vault и lookup plugins представляют собой наилучшие практики, которые можно адаптировать к требованиям вашей инфраструктуры. Хотя могут понадобиться временные настройки для достижения идеального баланса между безопасностью и удобством, в долгосрочной перспективе подобные меры обеспечат эффективную и безопасную оркестрацию ваших ИТ-систем.