Потребовалось мне управлять при помощи 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
в инвентаризационном файле разработчики отказывабтся, мол и в конфиге достаточно.
Tag Archives: network
Списки 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
всё-таки появилась и необходимость в стороннем пакете отпала.
Munin SSH transport
Решил настроить 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 на внутреннем (ноде) для беспарольного доступа.