Есть в природе такие на всю голову отмороженные заказчики, которые хотят чтобы им всё сделали за***сь, в лучшем виде и внутри их контура. Но при этом, шосукахарактерно, нормальный доступ в этот самый свой контур дать не хотят. И вот начинается: установи у себя какой-нибудь checkpoint VPN, позвони Васе, спроси у него SMS-ку, введи её в VPN-клиента (хорошо если срок действия одноразового кода не протухнет пока ты Васе звонишь), дальше подключись к какому-нибудь PAM (
Privileged Access Management), получи там токен, вбей его в SSH-клиента, и может быть после этих всех плясок ты таки попадешь на нужную тебе машину. При этом пресловутый PAM играет роль MitM-а и контролирует всё что ты набираешь на клавиатуре. И стоит тебе случайно произнести вслух (точнее, пальцами) какое-нибудь запрещенное слово наподобие "sudoers" или "ssh-keygen", всё, 3.14zdets: тебя без каких-либо предупреждений дисконнектит. Списка запрещенных слов ты, конечно же, не знаешь.
А чтобы развернуть решение, тебе нужно провести тонну разных манипуляций на 10...20 виртуальных машинах. И не просто развернуть, а в достаточно сжатые сроки, руками команды из нескольких человек (на которых выдали один-два логина в PAM и в VPN), а потом это всё troubleshoot-ить и поддерживать. Мы с коллегами называем такое развлечение "клеить обои через замочную скважину". Вопрос только в размере этой самой скважины.
Заказчики же эти, как правило, люто дро озабочены "бизапаснастью", но при этом не имеют ни малейшего понятия что это вообще такое и как, да и Linux видят хорошо если во второй раз в жизни. Они совершенно искренне считают, что если подрядчик при настройке софта сказочно за***лся, то всё "безопасно". Поэтому при малейшем удобном случае я вкрадчиво так интересуюсь: "А не угодно ли будет благородным донам, чтобы мы поставили решение на свой мониторинг и оперативно реагировали на инциденты?". Удивительно, но обычно благородные доны совсем не против, поэтому неосмотрительно открывают для меня TCP:10050 вовнутрь своего контура через какой-нибудь IPSec. Рассказывать что происходит дальше? А вот возьму и расскажу. Но сперва небольшое лирическое отступление.
Конечно, есть способы "в лоб" - перевесить sshd на другой порт, запустить ещё один инстанс sshd или что-нибудь типа "iptables -t nat -A PREROUTING блаблабла -j REDIRECT --to-ports 22". Но это палево. В логах останутся IP-адреса с которых ты приходил. У них там обычно молотит auditd и генерирует по гигабайту логов системных вызвов в день (интересно, они действительно всерьёз собираются все эти логи читать?). Плюс, у заказчиков может быть и собственный Zabbix-сервер, поэтому вмешиваться в штатную работу Zabbix-клиента нехорошо. Ну и все "прямолинейные" методы типа вышеописанных достаточно легко обнаружить. Поэтому мы пойдём другим путём. Воспользуемся тем, что в состав практически любого решения входит NginX. А у него есть очень хороший модуль "
ngx_stream_core_module". Поэтому нарисуем конфиг типа такого.
stream {
proxy_timeout 8h;
map $ssl_server_name $upstream {
serv1.pidory.com 192.168.1.1:22;
serv2.pidory.com 192.168.1.2:22;
serv3.pidory.com 192.168.1.3:22;
serv4.pidory.com 192.168.1.4:22;
default 127.0.0.1:22;
}
map "$remote_addr:$ssl_preread_protocol" $is_ssl {
"~^10\.15\.2\.2\:TLS.*$" unix:/run/nginx.sock;
default 127.0.0.1:10050;
}
server {
listen 192.168.1.10:10050;
ssl_preread on;
access_log off;
error_log /dev/null;
proxy_pass $is_ssl;
}
server {
listen unix:/run/nginx.sock ssl;
access_log off;
error_log /dev/null;
ssl_certificate_key /etc/nginx/ssl/selfsigned.pem;
ssl_certificate /etc/nginx/ssl/cert.pem;
proxy_pass $upstream;
}
}
Несколько комментариев о том, зачем так сложно.
Сперва нам нужно понять, пришли к нам с TLS-ом или без оного. Если с TLS-ом, то терминировать; если без, то пропустить "как есть". В рамках одного server{} мы этого сделать не можем, поэтому их два. Один слушает на TCP-сокете и смотрит с какого IP пришли и с TLSом или без. Если с правильного IP с TLS-ом, то отправить на второй server{}. Если нет, то напрямую на Zabbix-agent. И да, последнего надо "перевесить" на "127.0.0.1" чтобы не дрался с NginX-ом за номер порта.
Второй server{} слушает на unix-сокете, терминирует TLS и смотрит какой заголовок приехал в TLS SNI (Server Name Indication). В зависимости от этого проксирует соединение на ту или другую машину. Либо на себя самого.
Идти на этот NginX тоже нужно "с припрыгом". В "~/.ssh/config" на клиенте прописать вот такое.
Host *.pidory.com
User goodboy
ProxyCommand openssl s_client -ign_eof -quiet -servername %h -connect 192.168.1.10:10050 2>/dev/null
TCPKeepAlive no
ServerAliveInterval 20
Сначала s_client устанавливает TLS-соединение с NginX-ом и отправляет ему SNI-заголовок, чтобы тот понял куда девать запрос. А дальше уже в игру вступает SSH. Особо долго я искал ключ "-ign_eof". Без него магии не случится, а эффект будет весьма неожиданным. Ради интереса можете попробовать сами, но это конкретное такое западло, про которое почему-то очень мало кто упоминает.
На базе HAProxy, конечно, всё то же самое можно было бы сделать
намного проще. Но мы же не ищем лёгких путей. В основном потому, что NginX обычно вне подозрения "бизапасников", и его конфиги пойдут смотреть скорее всего в самую последнюю очередь ежели вдруг заинтересуются. Но палиться, конечно, тоже не стоит. Для виду для проформы запустить все эти их мудацкие Chackpoint-ы и PAM-ы. А работать уже через нормальное удобное прямое соединение.
Всем хороших вменяемых адекватных заказчиков, и чтобы никогда не приходилось заниматься такой же вот х***той как мне.