Ansible и подключение через промежуточный хост.

Потребовалось мне управлять при помощи Ansible серверами, у которых ssh доступен только во внутренней сети заказчика через подконтрольный мне сервер, который доступен через Интернет. Вариант на трёх или четырёх, как я их называю, центральных серврах поднимать Ansible и синхронизировать playbooks мне как-то не улыбалась. Решил поинтересоваться, как народ эту задачу решает при помощи Ansible, установленного на одной машине. В интернете много вопросов на эту тему. Ответов нормальных на этот вопрос я не нашёл. А вот мысль одна у меня в голове крутилась давно: ProxyCommand. В итоге занялся расширением своих познаний о ssh.
Оказалось, что ProxyCommand – это то что нужно!
Для получения желаемого результата достаточно было “подкрутить” ssh_args в ansible.conf и указать, что подключаться к машинам и списка хостов надо используя промежуточный хост:
-o ProxyCommand="ssh -q -A -x root@xxx.xxx.xxx.xxx -p 2022 -W %h:22,
где xxx.xxx.xxx.xxx – IP того самого подконтрольного сервера. Остальные ключи и параметры подробно описаны в man 1 ssh.
Как видно, тут есть одно ограничение – у опрашиваемых серверов sshd должен слушть на 22 порту (или каком-то другом), лишь бы у всех одинаковый был.
Для работоспособности этой схемы надо соблюсти некоторые условия:
– на центральном сервере и дирижируемых хостах надо в authorized_keys добавить открытый ключ учётной записи от имени которой запускается Ansible.
– в /etc/ssh/sshd на центральном сервере значение MaxSessions должно быть больше, чем значение forks в ansible.cfg, чтобы sshd не отбрасывал соединения.
А теперь небольшая ложка дёгтя. ssh_args нельзя указать в файле инвентаризации, так что для каждого случая с центральным сервером понадобится отдельный конфигурационный файл ansible.cfg. В нём можно указать путь к инвентаризационному файлу со списком внутренних IP дирижируемых хостов. Запускать ansible придётся с указанием переменной окружения ANSIBLE_CONFIG=/etc/ansible/ansible_intnet1.cfg.
Как показал дальнейший поиск, добавлять возможность указания ssh_args в инвентаризационном файле разработчики отказывабтся, мол и в конфиге достаточно.

Share

Списки IP адресов для iptables

Так как не все наши заказчики выполняют требование по защите наших серверов файрволлом (доступ по ssh только с доверенных IP), быстрым решением было ограничить подключения в /etc/ssh/sshd_config через AllowUsers. В целом задачу свою подобное решение выполняло, за исключением одного момента – подключение с неизвестного IP отвергается, а в логах записи, тем не менее, остаются. При активных bruteforce-атаках логи пухли как на дроджжах и в условиях ограниченного дискового пространства это вызывало определённые проблемы. В итоге, улучив минутку, сел переделывать по-человечески – настраивать iptables на подконтрольном сервере, раз заказчики не чешутся и не выполняют наши техтребования.
Как оказалось, сами iptables этого не умеют. Решение нашлось быстро – ipset. Конечно, в сравнении с pf, меня несколько удивляет, что для подобной элементарной задачи нужна отдельная утилита… Буду считать это ярчайшим проявлением unixway: одна задача – один инструмент.
Список для ipset готовится элементарно, потом экспотируется командой ipset save в понятный ipset формат и подгружается при помощи ipset restore.
В набор адресов для ipset я добавил внешние адреса офиса и внутренние подсети, определённые RFC. Вот что получилось:
create trusted hash:net family inet hashsize 1024 maxelem 65536
add trusted 192.168.0.0/16
add trusted 10.0.0.0/8
add trusted 172.16.0.0/12
add trusted xxx.xxx.xxx.0/24
add trusted yyy.yyy.yyy.yyy

Где trusted – это имя списка IP, точнее списка подсетей.
Правило для iptables трансформировалось из
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
в
-A INPUT -m state --state NEW -m set --set trusted src -m tcp -p tcp --dport 22 -j ACCEPT
т.е. добавил -m set --set trusted src, где -m set указание на использование списка ipset, --set trusted имя списка и параметр указывающий что это список источников – src.
А вот дальше началось самое интересное – как при загрузке ОС скормить эти списки iptables, т.к. если список не загружен, то правила iptables не применяются, ссылаясь на ошибку.
Т.е. комманда ipset restore должна выполняться ДО iptables rstore. Оказалось, что в Scientific Linux с этим проблема. В пакете ipset-6.11-1.el6.i686 службы ipset нет, а из /etc/rc.local комманды выполняются ПОСЛЕ iptables restore, что, как я писал выше, приводит к ошибке и правила не загружаются. Получается, что по умолчанию два варианта – писать свои скрипты инициализации (как вариант использовать найденные в интернете) или колхозить ещё более жутким образом, типа прописать ipset restore < /etc/ipset.rules в /etc/init.d/iptables. Оба этих варианта меня как-то не сильно радовали.
Выход таки нашёлся – в репозиториях CentOS есть пакет ipset-init, который содержит в себе необходимые для решения данной задачи скрипты init.d.
После установки пакета добавил вышеприведённый конфиг в /etc/sysconfig/ipset и все проблемы с подгрузкой списков разрешились.
К слову сказать, в Arch Linux пакет ipset идёт со скриптами для systemd и использование сторонних скриптов и пакетов не требуется.
UPDATE: В более позднем пакете ipset-6.11-3.el6.i686 из SL6.6 служба ipset всё-таки появилась и необходимость в стороннем пакете отпала.

Share

Munin SSH transport

This entry is part [part not set] of 1 in the series munin

Решил настроить munin сервер для опроса порядка 500 нод.
В штатном режиме сервер опрашивает ноду по дефолтному порту 4949. Это прекрасно и замечательно, когда этот порт доступен “напрямую”. В моём случае большинство серверов сидят за чужим NAT-ом и наружу проброшен только ssh, причём ещё на какой-нибудь высокий порт. Или есть второй распространённый вариант – проброшен порт от одного сервера, а остальные доступны только по внутренней сети.
Доступ по ssh открыт, так как это является нашим основным требованием к сети заказчика.
В официальной документации как-то не сильно рекламируют возможность работы через ssh, но, как оказалось, в munin 2.x она есть. О её существовании помог узнать большой брат, который вывел на ссылку http://munin-monitoring.org/wiki/Native_ssh.
В принципе всё понятно, за исключением конфигурации с нестандартным ssh портом, но об этом чуть позже.
Тестовый сервер у меня был поднят на OpenBSD и серверная часть munin запускалась с правами пользователя _munin, который по умолчанию вместо shell имеет /sbin/nologin. Продакшн настраивал на Scientific Linux, в котором munin запускается от пользователя munin. Соответственно для возможности подключения по ssh к нодам надо было в обоих случаях этому пользователю прописать рабочий шелл, например /bin/sh, создать домашнюю директорию, сгенерировать ключи при помощи ssh-keygen (ключ должен быть без passphrase) и переслать публичный ключ на ноду для той учётной записи, под которой планируется на неё заходить. Пусть учётка на ноде зовётся user. Соответственно в домашней директории пользователя user в .ssh/autorized_keys должен быть прописан публичный ключ пользователя munin с сервера.
На машине с munin-node должен быть установлен netcat (nc), который позволит обращаться к munin-node через:

nc localhost 4949
# munin node at ms13090624.domain
list
df mssversion uptime

То есть для подобного режима работы ноде достаточно слушать только на 127.0.0.1 и отвечать на запросы тоже только с локалхоста. Одним сервисом “глядящим” наружу будет меньше.
Значимые строки в munin-node.conf для подключения к ней при помощи nc с localhost:

host_name ms13090624.domain
allow ^127\.0\.0\.1$
host 127.0.0.1

Вручную ноду можно проверить запустив с сервера через ssh nc на ноде:
sudo -u munin ssh user@xxx.xxx.xxx.xxx nc localhost 4949
В ответ должны получить приглашение munin-node, как и при локальном подключении.
Кусок конфига munin.conf для опроса ноды по 22 порту (описан в документации к munin):

[ms13090624.domain]
use_node_name no
address ssh://user@xxx.xxx.xxx.xxx/usr/bin/nc localhost 4949

Про использование альтернативного порта ssh я в документации не увидел ни слова, но просмотр кода и дальейшие тесты на стенде показали, что в конфиге сервера порт указывается вот в таком формате:

[ms13090622.domain]
use_node_name no
address ssh://user@xxx.xxx.xxx.xxx:2022/usr/bin/nc localhost 4949

то есть через двоеточие сразу после IP.

Про усложнённый вариант подключения через ssh туннель в оригинальной документации впринципе ничего не говорят. Подозреваю, что это не самый распространённый вариант доступа к ноде. Но особенности топологии сетей заказчиков подтолкнули к внимательному изучению конфига и кода, этот конфиг парсящему. В результате оказалось, что всё довольно просто:

[ms13090625.domain]
use_node_name no
address ssh://user@zzz.zzz.zzz.zzz:2022/usr/bin/ssh user1@192.168.xxx.yyy /usr/bin/nc localhost 4949

ms13090625.domain – имя внутренней машины, прописанное в конфиге ноды
192.168.xxx.yyy – IP ноды, которая находится во внутренней сети
zzz.zzz.zzz.zzz:2022 ИП и порт машины к которой мы имеем доступ по ssh извне и с которй по ssh можно попасть на машину во внутренней сети, т.е. на ноду.

Итого для работы через туннель к “обычному” конфигу доступа по ssh вида
address ssh://user@zzz.zzz.zzz.zzz:2022/usr/bin/nc localhost 4949
добавилась команда для подключения с внешнего сервера на внутреннюю ноду: /usr/bin/ssh user1@192.168.xxx.yyy

Надеюсь это понятно сразу, что user на внешнем сервере должен быть обменян ssh ключом с user1 на внутреннем (ноде) для беспарольного доступа.

Share

PTR

Ура! Провайдер добавил меня в обратную зону.
6.250.123.188.in-addr.arpa name = linklevel.net.
Жаль только, что для подключения они коаксиал используют…

Share