SSH back-connect

Posted: 2012-12-26 in IT, Networks
Метки:

В данной заметке пойдёт речь про более хитрое использование SSH для оказания удалённой помощи серверу, находящемуся за NAT, причём без настройки роутера. …

Всем привет.

Есть такая мега-тулза в никсах — SSH. Обычно используется для удаленного консольного управления и выкачки бэкапов по ночам. Как правило, в большинстве случаев объекты управления доступны напрямую по сети, и для коннекта достаточно просто запустить ssh-клиент с указанием имени сервера и если надо — номера порта. Дело заметно усложняется, если нам надо помочь другу/удалённому клиенту или ещё кому-то, чья машина находится вне нашей сети за NAT-ом. В особо тяжёлых случаях NAT-ов может быть два (первый на выданном провайдером модеме (запароленном под гарантию), второй на роутере клиента), и соответственно нет возможности быстро настроить проброс порта во внутреннюю сеть.

Понятно, что ради коннекта по SSH раз в полгода нет никакого смысла ставить какие-то хитрые криптонеты, впн-ки, тим-вьюверы-на-соседней-машине и прочую неадекватную задаче тяжелую хрень. Потому что в нормальных ОС уже всё необходимое есть. Нормальные в данном случае — из семейства Linux и *BSD, где ssh работает со всеми своими фичами. Полагаю, что на MacOS предлооженная ниже схема также должна нормально работать, и не исключена возможность организовать такой доступ к оборудованию Juniper, Ubiquity и новым Cisco, где используются unix/linux-based прошивки.

Суть идеи — попросить друга на удаленной стороне запустить так называемым «обратным соединением» ssh-туннель до нашей машины с пробросом порта на удаленную машину. Подразумевается, что управляемый сервер может делать исходящие TCP-соединения во внешний мир на порт TCP:22, на самом сервере поднят ssh на стандартном 22-м порту [но доступный только из 192.168.0.0/24, ибо NAT], а у машины вызвавшегося удалённо помочь админа есть фиксированный внешний IP-адрес на рабочем компьютере (в нашем примере — 25.2.2.7), где ssh-сервер принимает соединения из внешнего мира на порту TCP:2222. В большинстве мануалов приводятся просто две команды, которые дают такой шелл (например, http://quiteusefulnotes.blogspot.ru/2009/03/back-connect.html), я же хочу рассказать более детально, что именно при этом происходит и какая часть настройки за это отвечает.

Сперва рассмотрим схему процессов и взаимодействий, которые происходят при организации туннелля обратным ssh-соединением:

Сперва поговорим о настройках. Самая простая настройка — со стороны сервера. Там у нас просто должна быть учетная запись (adm1), которой на этот сервер можно войти по SSH, причем не принципиально, по ключам или же по паролю. Со стороны сервера для этой учетки требуется только логин, никаких дополнительных привлегий (типа разрешения форвардинга портов в sshd_config) на _сервере_ настраивать не надо.
Настройка админской машины для принятия входящего коннета несколько сложнее. Важно понимать, что при такой схеме работы вы предоставляете удаленному клиенту доступ к своей рабочей машине, и потому прописывать чужой ключ к себе в ~/authorized_keys и уж тем более сообщать свой пароль — верх глупости. Поэтому для принятия коннектов со стороны клиентов создаём отдельную учетку именно для проброса портов — в нашем случае это пользователь pfwd. Если у вас по умолчанию для всех пользователей ssh запрещён форвардинг портов — то его можно включить выборочно для какого-либо пользователя или группы:

# ... sshd_config ...
AllowTcpForwarding no

Match User pfwd
        AllowTcpForwarding yes

В данном примере в конфиге sshd форвардинг портов (туннелирование) запрещено для всех пользователей, кроме пользователя pfwd. Не забываем, что sshd надо после этого перезапустить.
Теперь просим друга на удалённом сервере выполнить вот такую команду:

ssh -R 1522:localhost:22 pfwd@25.2.2.7 -p 2222

Что при этом произойдет ? Клиентская программа ssh на сервере (неважно, от чьего имени запущенная, лишь бы сетевой коннект был) соединиться с машиной админа 25.2.2.7 на порт 2222, и запросит от друга пароль или ключ доступа к учётной записи pfwd на машине админа. Если доступ есть — то на машине админа запустятся процессы такого вида:

[root@Admin-PC ~]# ps axfu | grep ssh
...
root      2887  ... /usr/sbin/sshd -D
root      5823  ...  _ sshd: pfwd [priv]
pfwd      5827  ...      _ sshd: pfwd@pts/1

А в сетевых соединениях появятся новые строки:

[root@Admin-PC ~]# netstat -natup | grep ssh
tcp   25.2.2.7:2222    0.0.0.0:*        LISTEN      2887/sshd
tcp   127.0.0.1:1522   0.0.0.0:*        LISTEN      5827/sshd: pfwd@pts
tcp   25.2.2.7:2222    15.1.1.8:49322   ESTABLISHED 5823/sshd: pfwd [priv]

Здесь первая стройка — от демона sshd, принимающего входящие соединения на порт 2222, последняя — установленное содинение (собственно туннель, инициированный коннектом с сервера внутренней сети), а строка посередине — занятый порт, все обращения к которому буду прозрачно передаваться на удаленный сервер через наш туннель. Обратите внимание, как система придерживается принципа минимума привилегий и от каких пользователей запущены процессы.
Теперь посмотрим, какие процессы и сетевые соединения появились на сервере при поднятии туннеля:

[root@Server]# ps axfu | grep ssh
... 14532    ssh -R 1522:localhost:22 pfwd@25.2.2.7 -p 2222

[root@Server]# netstat -natup | grep ssh
tcp ... 0.0.0.0:22         0.0.0.0:*       LISTEN      969/sshd
tcp ... 192.168.0.5:51018  25.2.2.7:2222   ESTABLISHED 14532/ssh
tcp ... 127.0.0.1:39630    127.0.0.1:22    ESTABLISHED 14532/ssh

Как видим, на сервере есть процесс ssh, который держит туннель, и соответствующие установленные сетевые соединения — в туннель и к локальному 22-му порту. Вот собственно туннель и поднят.

Всё, что теперь надо сделать админу — запустить на своей рабочей машине вот такую команду:

[Dobro@Admin-PC ~]$ ssh adm1@localhost -p 1522

Поскольку на машине админа порт 1522 прослушивается процессом sshd, обслуживающим туннель, то такой коннект пойдет как раз через этой самый туннель, и приземлится на удаленном сервере на порт TCP:22, тогда как выглдяеть это будет как самое обычное ssh-соединение. Посмотрим, что появилось нового в процессах и соединениях. На машине админа:

[root@Admin-PC]# ps axfu| grep ssh
... 6228   ssh adm1@localhost -p 1522
[root@Admin-PC]# netstat -natup | grep ssh
...
tcp   127.0.0.1:59769   127.0.0.1:1522   ESTABLISHED 6228/ssh

И на сервере соответственно:

[root@Server ~]# ps axfu | grep ssh
...
root  ... 14782  _ sshd: adm1 [priv]
adm1  ... 14797    _ sshd: adm1@pts/1

[root@Server ~]# netstat -natup | grep ssh
...
tcp  ...  127.0.0.1:22   127.0.0.1:39630   ESTABLISHED 14782/sshd: adm1

Для админа в консоли сессия выглядит вот так:

[Dobro@Admin-PC ~]$ ssh adm1@localhost -p 1522
adm1@localhost's password:
Last login: Wed Dec 26 02:24:38 2012 from localhost

-bash-4.2$

Как видите, ничего запредельно сложного нет. Две простых команды, достаточно тривиальная настройка, легкий и наглядный мониторинг. Важное и очень удобное свойство — что при такой организации удаленного доступа к серверу админу совершенно ничего не нужно знать ни о внешнем IP-адресе клиента, ни о его провайдере и шлюзе, ни о топологии внутренней клиентской сети. Но понимание, как работает такой механизм — иметь надо обязательно.
Для остановки туннеля достаточно на машине админа запретить логин по ssh пользователю pfwd и прибить соответствующие процессы — туннель тут же выключится.

Вот такая вот приятная и милая возможность в программе весом ~ 1.3 Мбайт 🙂 . В следующий раз расскажу об организации SOCKS-прокси через ssh (с таким же подробным разъяснением).

P.S. Эта статья получила хорошие отклики читателей. Я всегда рад помочь хорошим людям. Поэтому немного ссылок на тех, кому статья особо помогла:

http://spark-in.me/post/neuro-chicken-coop

Реклама

Обсуждение закрыто.