Перейти к содержимому

Магия SSH

С SSH многие знакомы давно, но, как и я, не все подозревают о том, какие возможности таятся за этими магическими тремя буквами. Хотел бы поделиться своим небольшим опытом использования SSH для решения различных административных задач.

1) Local TCP forwarding

Начнем с простого — local TCP forwarding:

Имеем удаленный сервер «host2» с неким приложением, допустим, PostgreSQL server, которое принимает TCP-соединения на порту 5432. При этом вполне логично, что на этом сервере стоит файрвол, который прямых соединений извне на порт 5432 не разрешает, но при этом есть доступ по SSH (по-умолчанию порт 22, рекомендую его изменить). Требуется подключиться с нашего рабочего места «host1» клиентским приложением к серверу PostgreSQL на «host2».

Для этого на «host1» в консоли набираем:

«host1# ssh -L 9999:localhost:5432 host2»

Теперь на «host1» мы можем соединяться с PostgreSQL сервером через локальный порт 9999:

«host1# psql -h localhost -p 9999 -U postgres»

Например, в PuTTy это делается так: Идем по дереву настроек: Connection → SSH → Tunnels. Далее в поле «Source port» вбиваем 9999, в «Destination» — localhost:5432, и нажимаем Add. Не забываем после этого сохранить настройки сессии, если требуется.

После успешного подключения к SSH-серверу на «host2», на «host1» SSH-клиент начинает слушать порт 9999. При подключении к порту 9999 на «host1», SSH-сервер на «host2» устанавливает соединение с localhost (коим и является для себя самого «host2») на порт 5432 и передает по этому соединению данные, принятые ssh-клиентом на «host1» на порт 9999. ВАЖНО! Все указанные на схемах стрелками соединения являются отдельными TCP-соединениями (сессиями).

Настройка SSH-сервера

Port forwarding, как правило, уже включен в настройках sshd по-умолчанию. /etc/ssh/sshd_config: «AllowTcpForwarding yes»

Мы также можем соединяться с приложением не на самом «host2», а на любой доступной ему машине:

Для этого при пробросе портов вместо «localhost» указываем имя хоста, например «host3»:

«host1# ssh -L 9999:host3:5432 host2»

Тут важно заметить, что «host3» должен быть известен (если это имя, а не IP-адрес) и доступен для машины «host2».

Также можно через «host1» предоставить доступ любому другому узлу (назовем его «host1A») к сервису на «host3»:

Для этого нужно вставить в команду соединения ssh IP-адрес интерфейса, на котором будет поднят локальный порт 9999:

«ssh -L 0.0.0.0:9999:host3:5432 host2»

В данном примере порт 9999 будет открыт на всех доступных на «host1» IPv4 интерфейсах.

2) Remote TCP forwarding

Но что делать, если, например, «host2» не имеет белого IP-адреса, находится за NAT или вообще все входящие соединения к нему закрыты? Или, например, на «host2» стоит Windows и нет возможности поставить SSH-сервер?

Для этого случая есть Remote TCP forwarding:

Теперь нужно устанавливать ssh-соединение в обратном направлении — от «host2» к «host1». Т.е. наша административная рабочая станция будет SSH-сервером и будет доступна по SSH с «host2», а на «host2» нужно будет выполнить подключение SSH-клиентом:

«ssh -R 9999:localhost:5432 host1»

Если на «host2» Windows

Например, в PuTTy это делается так: Идем по дереву настроек: Connection → SSH → Tunnels. Далее в поле «Source port» вбиваем 9999, в «Destination» — localhost:5432, а ниже выбираем «Remote», и нажимаем Add. Не забываем после этого сохранить настройки сессии, если требуется.

После успешного подключения, на «host1» SSH-сервер начинает слушать порт 9999. При подключении к порту 9999 на «host1», SSH-клиент на «host2» устанавливает соединение с localhost (коим и является для себя самого «host2») на порт 5432 и передает по этому соединению данные, принятые ssh-сервером на «host1» на порт 9999.

Также у вас возникнут дополнительные сложности с обеспечением безопасности на «host1», если вы не доверяете узлу «host2». Однако это выходит за рамки данной статьи.

И, конечно, вы каким-то образом (сами или с посторонней помощью) должны инициировать ssh-соединение со стороны «host2» вводом приведенной выше команды, а «host1» должен иметь белый IP-адрес и открытый порт SSH.

После установки ssh-соединения все работает аналогично предыдущей главе.

3) TCP forwarding chain через несколько узлов

В закрытых сетях часто бывает, что нужный нам узел напрямую недоступен. Т.е. мы можем зайти на нужный хост только по цепочке, например host1 → host2 → host3 → host4: «host1# ssh host2host2# ssh host3host3# ssh host4host4# echo hello host4»

Это может происходить например если эти узлы являются шлюзами, либо если на них доступны шлюзы только в соседние подсети.

В таком случае мы также можем делать TCP forwarding по цепочке:

Здесь порты 9991, 9992, 9993 выбраны для наглядности, на практике можно использовать один и тот же порт (например, 9999), если он свободен на всех узлах.

Итого нужно выполнить следующую цепочку команд:

«host1# ssh -L 9991:localhost:9992 host2host2# ssh -L 9992:localhost:9993 host3host3# ssh -L 9993:localhost:5432 host4»

Как это работает

После успешного выполнения перечисленных выше команд, на узлах выполняется следующее:

  • на «host1»: открывается порт 9991, при подключении к которому данные перенаправляются по ssh-соединению на порт 9992 на «host2»;
  • на «host2»: открывается порт 9992, при подключении к которому данные перенаправляются по ssh-соединению на порт 9993 на «host3»;
  • на «host3»: открывается порт 9993, при подключении к которому данные перенаправляются по ssh-соединению на порт 5432 на «host4»;

Таким образом, при соединении на порт 9991 на «host1», данные перенаправляются по цепочке на «host4» на порт 5432.

ВАЖНО! Все указанные на схемах стрелками соединения являются отдельными TCP-соединениями (сессиями).

4) TCP forwarding ssh-соединения

Иногда бывает нужно соединиться по ssh с сервером, который напрямую недоступен, а доступ возможен только по цепочке ssh-серверов (см. предыдущую главу). Теперь мы обладаем нужными знаниями чтобы сделать следующее:

«host1# ssh -L 2222:localhost:2222 host2host2# ssh -L 2222:host4:22 host3»

Таким образом, на порту 2222 на «host1» у нас теперь есть форвардинг на порт SSH (22) на «host4». Можем соединиться:

«host1# ssh -p 2222 localhosthost4# echo hello host4»

Казалось бы, зачем это нужно? Например, вот зачем:

«# копируем файл на host4host1# scp -P 2222 /local/path/to/some/file localhost:/path/on/host4# копируем файл с host4host1# scp -P 2222 localhost:/path/on/host4 /local/path/to/some/file# делаем еще один замечательный TCP forwarding на host4host1# ssh -p 2222 -L 9999:localhost:5432 localhosthost1# psql -h localhost -p 9999 -U postgres# обратите внимание, что порт для команды ssh задается ключем -p в нижнем регистре,# а для команды scp -P в верхнем регистре»

Ну и вообще, здорово что теперь «host4» так близко 🙂

Вывод: можно делать TCP forwarding большого уровня вложенности.

Замечания про RSA fingerprint

В некоторых случаях scp не отработает, пока не зайдете сначала через ssh -p 2222 localhost и не примете RSA fingerprint удаленного сервера.

Если пользуетесь одним и тем же портом (2222) для доступа к разным удаленным серверам, то будут ошибки RSA fingerprint, который остался от предыдущего сервера. Его нужно будет удалить из ~/.ssh/known_hosts.

5) SSH VPN Tunnel

TCP port forwarding — это отличная возможность. Но что если нам нужно больше? Доступ по UDP, доступ к множеству портов и хостов, доступ к динамическим портам? Ответ очевиден — VPN. И всемогущий SSH начиная с версии 4.3 и здесь придет нам на помощь.

Забегая вперед скажу: этот функционал SSH хорошо работает если вам нужно временное решение для каких-то административных задач. Для построения постоянных VPN этот вариант далеко не самый подходящий, т. к. он предполагает TCP-over-TCP, что плохо скажется на скорости соединения.

Еще про TCP forwarding

А вот TCP port forwarding с помощью SSH, если его достаточно, во многих случаях выиграет по производительности у VPN, т. к. при TCP port forwarding передаются только данные приложения, а не исходные пакеты целиком вместе с заголовками

Настройка SSH-сервера: PermitTunnel в настройках sshd по-умолчанию выключен, его нужно включить в /etc/ssh/sshd_config: «PermitTunnel yes» или «PermitTunnel point-to-point»

ВАЖНО: для поднятия нового сетевого интерфейса туннеля и на ssh-клиенте, и на ssh-сервере необходимы права суперпользователя. Можно долго спорить о том, насколько это небезопасно, но в большинстве случаев на ssh-сервере достаточно настройки:

«PermitRootLogin without-password»

Таким образом вы запрещаете вход root по паролю, а разрешаете только другими средствами, например, по ключу RSA, что гораздо безопаснее.

Перезапускаем sshd: «sudo service sshd restart # centos» или «/etc/init.d/ssh restart # (debian/ubuntu)»

Туннель поднимается при использовании магического ключа -w:

«host1# sudo ssh -w 5:5 root@host2»

Где 5:5 — номер интерфейса на локальной машине и на удаленной соответственно. Здесь вас может смутить, что ifconfig не выдаст в списке интерфейса «tun5». Это потому что он в состоянии «down», а вот если вызвать «ifconfig -a» или «ifconfig tun5», то интерфейс будет виден:

«host1# ifconfig tun5tun5 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00POINTOPOINT NOARP MULTICAST MTU:1500 Metric:1RX packets:0 errors:0 dropped:0 overruns:0 frame:0TX packets:0 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:500RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)»

Назначаем интерфейсам IP-адреса и поднимаем их:

«host1# sudo ifconfig tun5 192.168.150.101/24 pointopoint 192.168.150.102host2# sudo ifconfig tun5 192.168.150.102/24 pointopoint 192.168.150.101»

Если есть файрвол, не забываем разрешить соединения с интерфейса tun5:

«host1# # сохраняем исходные правила файрволаhost1# sudo iptables-save > /tmp/iptables.rules.orighost1# sudo iptables -I INPUT 1 -i tun5 -j ACCEPThost2# # сохраняем исходные правила файрволаhost2# sudo iptables-save > /tmp/iptables.rules.orighost2# sudo iptables -I INPUT 1 -i tun5 -j ACCEPT»

На host1 это делать необязательно, здесь это сделано лишь для того чтобы ping работал в обе стороны.

Наслаждаемся пингом:

«host1# ping 192.168.150.102host2# ping 192.168.150.101»

Если рассмотреть более ранний пример с PostgreSQL, то теперь схема будет такая:

А команда для подключения к серверу PostgreSQL будет выглядеть так:

«host1# psql -h 192.168.150.102 -U postgres»

Ну а далее можно делать какой-либо из этих узлов шлюзом, если нужно обеспечить доступ не к одному узлу, а к сети. Например:

«host2# # разрешаем IP forwardinghost2# sudo sysctl -w net.ipv4.ip_forward=1host2# # разрешаем IP forwarding с host1host2# sudo iptables -I FORWARD 1 -s 192.168.150.101 -j ACCEPThost2# # разрешаем IP forwarding на host1host2# sudo iptables -I FORWARD 1 -d 192.168.150.101 -j ACCEPThost2# # маскируем IP адрес host1host2# sudo iptables -t nat -A POSTROUTING -s 192.168.150.101 -j MASQUERADE»

«host1# # Предположим, у host2 есть доступ к сети 192.168.2.x, куда нам нужно попасть с host1host1# # Прописываем host2 как шлюз в сеть 192.168.2.xhost1# sudo ip route add 192.168.2.0/24 via 192.168.150.2host1# # Наслаждаемся доступом в сеть с host1host1# ping 192.168.2.1»

После окончания работы не забываем вернуть net.ipv4.ip_forward и файрвол в исходное состояние.

«host1# sudo iptables-restore host2# sudo iptables-restore

Более интересный случай с временным расшариванием Интернета

Допустим, нужно настроить сервер в закрытой сети, где доступ в Интернет запрещен, но тем не менее у вас туда есть лазейка — доступ через один ssh-сервер или цепочку ssh-серверов. Предположим, для настройки сервера вам нужен на нем доступ в Интернет. Тогда проще самостоятельно настроить временный доступ в Интернет на требующем настройки сервере, чем просить это сделать обслуживающий персонал.

Допустим, есть доступ по ssh с вашей рабочей машины host1 на сервер host2, с него — на host3, а уже оттуда — на нужный вам host4. Тогда делаем TCP forwarding для ssh (если с host1 вы сразу можете соединиться с host4, пропустите этот шаг):

host1# ssh -L 2222:localhost:2222 host2<br>host2# ssh -L 2222:host4:22 host3

Далее, соединяемся с host4 и поднимаем интерфейс tun5:

«host1# sudo ssh -p 2222 -w 5:5 root@localhosthost1# # или если host4 доступен сразу: sudo ssh -w 5:5 root@host4host1# sudo ifconfig tun5 192.168.150.101/24 pointopoint 192.168.150.102host4# sudo ifconfig tun5 192.168.150.102/24 pointopoint 192.168.150.101»

Смотрим таблицу маршрутизации на host4, допустим видим следующее:

«host4# route -nKernel IP routing tableDestination Gateway Genmask Flags Metric Ref Use Iface192.168.150.0 0.0.0.0 255.255.255.0 U 0 0 0 tun5192.168.56.0 0.0.0.0 255.255.255.0 U 1 0 0 eth00.0.0.0 192.168.56.254 0.0.0.0 UG 0 0 0 eth0»

ВАЖНО! Далее нам скорее всего захочется сделать маршрутом по-умолчанию интерфейс tun5 со шлюзом 192.168.150.101, через который будет доступен Интернет. Поэтому на данном этапе важно точно знать, какие маршруты нужно дописать, чтобы заменить маршрут по-умолчанию. Это важно, поскольку довольно часто маршруты на отдельные сети не прописывают отдельно, а просто задают маршрут по-умолчанию (0.0.0.0/0) со шлюзом, через который и идет весь межсетевой трафик. Более того, вполне вероятно что ваше ssh-соединение с сервером также использует исходный шлюз по-умолчанию.

Для простоты в данном примере предположим, что никаких маршрутов кроме 192.168.56.0/24 серверу для нормального функционирования не нужно и что предыдущий ssh-хост host3 имеет IP-адрес из этой же сети.

Запоминаем и записываем куда-нибудь исходную маршрутную таблицу со шлюзом по-умолчанию: «host4# route -n > routes.orig»

Настраиваем наш host1 для работы в качестве шлюза в Интернет для host4:

«host1# # разрешаем IP forwardinghost1# sudo sysctl -w net.ipv4.ip_forward=1host1# # сохраняем исходные правила файрволаhost1# sudo iptables-save > /tmp/iptables.rules.orighost1# # разрешаем IP forwarding с host4host1# sudo iptables -I FORWARD 1 -s 192.168.150.102 -j ACCEPThost1# # разрешаем IP forwarding на host4host1# sudo iptables -I FORWARD 1 -d 192.168.150.102 -j ACCEPThost1# # маскируем IP адрес host4host1# sudo iptables -t nat -A POSTROUTING -s 192.168.150.102 -j MASQUERADE»

На всякий случай можно прописать серые сети на шлюз из текущего маршрута по-умолчаниюЕсли не прописаны: «sudo ip route add 192.168.0.0/16 via 192.168.56.254sudo ip route add 10.0.0.0/8 via 192.168.56.254sudo ip route add 172.16.0.0/12 via 192.168.56.254»

Изменяем маршрут по-умолчанию на host4 (ОСТОРОЖНО, см. предупреждение выше!):

«host4# sudo ip route replace default via 192.168.150.101host4# route -nKernel IP routing tableDestination Gateway Genmask Flags Metric Ref Use Iface192.168.150.0 0.0.0.0 255.255.255.0 U 0 0 0 tun5192.168.56.0 0.0.0.0 255.255.255.0 U 1 0 0 eth00.0.0.0 192.168.150.101 0.0.0.0 UG 0 0 0 tun5»

Если весь Интернет нам не нужен, а только конкретные IP-адреса/маски, то можно маршрут по-умолчанию не менять, а дописать только нужные нам адреса через шлюз на tun5.

Проверяем, что есть Интернет:

«host4# ping 8.8.8.8»

Отлично. Осталось настроить DNS. Есть множество способов это сделать, проще всего отредактировать файл /etc/resolv.conf и добавить туда строчки:

«nameserver 8.8.8.8nameserver 8.8.4.4»

После этого Интернет должен быть полностью доступен:

«host4# ping ya.ru»

После окончания работы не забываем вернуть все в исходное состояние:

«host1# # восстанавливаем правила файрвола на host1host1# sudo iptables-restore host1# # не забудьте восстановить также значение net.ipv4.ip_forward»

«host2# # восстановите маршрут по-умолчанию на host4:host2# sudo ip route replace default via 192.168.56.254host2# # и уберите добавленные ранее DNS-сервера из /etc/resolv.conf»

6) Коротко о беспарольном доступе

Думаю, все уже знают что авторизация по паролю это не про нас. Но на всякий случай впихну сюда краткую инструкцию по настройке аутентификации по ключу RSA:

1. На клиентских машинах генерируем пользователю свой ключ RSA:

«client1# ssh-keygen -t rsa»

По-умолчанию приватный ключ сохраняется в ~/.ssh/id_rsa, а открытый — в ~/.ssh/id_rsa.pub. Приватный ключ храните как зеницу ока и никому не давайте, никуда не копируйте. При создании ключа можно задать пароль (passphrase), которым ключ будет зашифрован.

2. Клиентские открытые ключи нужно сохранить на ssh-сервере в файле ~/.ssh/authorized_keys (~ это домашняя директория того пользователя, которым будете логиниться), каждый на отдельной строке. Для того чтобы это не делать вручную, на каждом клиенте можно воспользоваться командой:

«ssh-copy-id user@sshserver»

Где user — имя пользователя на сервере, sshserver — имя или IP-адрес ssh-сервера. Права на файл ~/.ssh/authorized_keysUPD от sabio: В случае ручного создания файла ~/.ssh/authorized_keys на ssh-сервере необходимо задать следующие права: «chmod 0700 ~/.sshchmod 0600 ~/.ssh/authorized_keys»

3. Проверьте, что можете зайти на сервер по ключу, без ввода пароля (не путать с passphrase): «ssh user@sshserver» Рекомендую не закрывать хотя бы одну активную ssh-сессию с сервером до тех пор, пока окончательно не закончите настройку и не убедитесь что все работает.

4. Отключите на SSH-сервере возможность входа по паролю в файле /etc/ssh/sshd_config:

«PasswordAuthentication no»

Возможность входа по открытому ключу обычно уже включена по-умолчанию:

«PubkeyAuthentication yes»

Я обычно также отключаю две следующие опции:

«GSSAPIAuthentication noUseDNS no»

В некоторых случаях это позволяет ускорить процесс соединения (например, когда на сервере нет доступа в Интернет).

5. Перезапустите sshd: «service sshd restart» или «/etc/init.d/ssh restart»

В случае ошибок полезно бывает смотреть лог /var/log/secure либо использовать опции -v, -vv или -vvv для вывода детального лога соединения: «ssh -vvv user@sshserver»

Опубликовано вLinux

Ваш комментарий будет первым

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *