Гайд по ssh-туннелированию
Оглавление
- ssh туннели: организовываем удалённый доступ одной командой
- туннель с локальной точкой входа – обеспечиваем доступ к серверу за nat
- туннель с удаленной точкой входа – публикуем локальный сервис
- двойное туннелирование
- динамический туннель
- туннелирование через sshuttle
- что делать пользователям microsoft windows?
- заключение
1. SSH ТУННЕЛИ: ОРГАНИЗОВЫВАЕМ УДАЛЁННЫЙ ДОСТУП ОДНОЙ КОМАНДОЙ
Для чего всё это нужно?
Каждый системный администратор или сетевой инженер хоть раз в своей практике сталкивался с ситуацией, когда нужно получить доступ из публичной сети Интернет к ресурсам своей рабочей сети, скрытой за NAT
и защищённой фаерволом. Конечно, для решения этой задачи можно настроить шифрованный site-to-site туннель или PPTP
. Или же воспользоваться сторонним приложением для организации удалённого доступа, например, TeamViewer
. Однако есть более простое решение, на реализацию которого уйдет буквально одна минута. К тому же, это решение не требует никакого стороннего программного обеспечения, кроме включённого по умолчанию в 90% Linux/Unix дистрибутивов пакета OpenSSH
.
Как это работает?
SSH
туннель или SSH Port Forwarding
, как его называет man(1) ssh
– это опциональный функционал протокола, который работает поверх всем знакомой обычной SSH
сессии. SSH
туннель позволяет послать TCP
пакет с одной стороны SSH
соединения на другую его сторону и произвести трансляцию IP заголовка по заранее определенному правилу в процессе передачи.
Понять, как работает SSH
туннель очень просто: если представить его в виде point-to-point соединения. Так же, как и в PPP
, любой пакет, попавший в один конец соединения, будет передан и получен на другом конце туннеля. Дальше, в зависимости от адреса получателя, заданного в IP
заголовке, пакет будет либо обработан принимающей стороной туннеля (если пакет предназначается непосредственно ей), либо смаршрутизирован дальше в сеть (если адресатом является другой узел сети).
Основное отличие SSH
туннеля от PPP
соединения в том, что в SSH
туннель можно завернуть только TCP
трафик. (Примечание: есть несколько хаков, как можно передать UDP
через TCP
-сокет внутри SSH
туннеля, но это решение выходит за рамки данной статьи).
Второе отличие состоит в том, что в point-to-point соединении, входящий трафик может быть инициирован с любой стороны, тогда как для SSH
туннеля необходимо явно задать "точку входа" для трафика. "Точка входа" – это параметр вида <адрес>:<порт>
, указывающий какой сокет открыть для входа в туннель (с локальной или удалённой стороны SSH
сессии).
Кроме точки входа дополнительно нужно указать правило вида <адрес>:<порт>
, по которому должен быть переписан заголовок (точнее, адрес и порт назначения) TCP
пакета в процессе передачи. Точка входа может задаваться с любого конца туннеля. За этот параметр отвечают ключи –L
(local) и –R
(remote).
Под local
и remote
подразумеваются стороны туннеля с точки зрения стороны-оригинатора, то есть того хоста, который устанавливает SSH
сессию.
Пока выглядит немного запутанно, поэтому давайте разберём на конкретном примере.
2. ТУННЕЛЬ С ЛОКАЛЬНОЙ ТОЧКОЙ ВХОДА – ОБЕСПЕЧИВАЕМ ДОСТУП К СЕРВЕРУ ЗА NAT
Боб работает системным администратором в маленькой компании Qwerty сakes, занимающейся производством пирогов с капустой. Вся сеть компании находится в одном броадкаст домене 192.168.0.0/24
. Для доступа в интернет используется программный маршрутизатор на базе Linux, адрес которого 192.168.0.1
со стороны сети компании и 1.1.1.1
со стороны сети Интернет. На маршрутизаторе поднят и работает демон OpenSSH
, который доступен по сокету 1.1.1.1:22
. Внутри сети на сервере с адресом 192.168.0.2
установлен внутренний корпоративный портал, на котором до завтрашнего утра Бобу нужно сделать изменения через Web интерфейс. Однако Боб не хочет задерживаться на работе допоздна, он хочет получить доступ к порталу из дома со своего домашнего компьютера с адресом 2.2.2.2
.
Боб приходит домой и после ужина устанавливает следующее соединение с маршрутизатором компании:
Что произошло? Боб установил SSH сессию между адресами 2.2.2.2
и 1.1.1.1
, при этом открыв локальную "точку входа" в туннель 127.0.0.1:8080
на своем домашнем компьютере:
bob@Bob-PC:~$ sudo lsof -nPi | grep 8080 ssh 3153 bob 4u IPv4 9862 0t0 TCP 27.0.0.1:8080 (LISTEN)
Любой TCP
пакет, который попадёт в сокет 127.0.0.1:8080
со стороны компьютера Боба, будет отправлен по point-to-point
соединению внутри сессии SSH
, при этом адрес назначения в TCP
заголовке будет перезаписан с 127.0.0.1
на 192.168.0.2
, а порт с 8080 на 80. Теперь Бобу, чтобы попасть на портал своей компании, нужно всего лишь набрать в браузере: http://127.0.0.1:8080
Давайте детально разберём, что произошло с TCP
пакетом в процессе его прохождения по SSH
туннелю:
TCP
пакет с адресом источника127.0.0.1
и адресом и портом назначения127.0.0.1:8080
попал в сокет127.0.0.1:8080
, открытый процессомssh
;- Процесс ssh получил пакет, в соответствии с правилом трансляции переписал адрес и порт назначения на
192.168.0.2:80
и отправил его внутриSSH
сессии удалённой стороне1.1.1.1
; - Процесс
sshd
на маршрутизаторе1.1.1.1
получил пакет и, просмотрев адрес назначения, отправил его хосту192.168.0.2
, переписав при этом адрес источника с127.0.0.1
на адрес собственного интерфейса192.168.0.1
, для того чтобы получатель, который ничего не знает про существованиеSSH
туннеля, вернул пакет роутеру, а не отправил в свой жеlocalhost
127.0.0.1
.
bob@Bob-PC:~$ ssh –L 127.0.0.1:8080:192.0.0.2:80 bob@1.1.1.1
В данном примере, если бы портал или любой другой ресурс, к которому Бобу нужно получить доступ, находился на самом роутере (например, по адресу 192.168.0.1:80
), то команда выглядела бы следующим образом:
bob@Bob-PC:~$ ssh -L 127.0.0.1:8080:192.0.0.1:80 bob@1.1.1.1
Если сервис доступен по адресу localhost (например, локальный SQL
сервер), то и к нему можно получить доступ:
bob@Bob-PC:~$ ssh -L 127.0.0.1:13306:127.0.0.1:3306 bob@1.1.1.1
Конструкции вида -L 127.0.0.1:80:127.0.0.1:80
могут выглядеть, на первый взгляд, довольно странными. Но в них нет ничего сложного, если помнить, что решение о маршрутизации пакета принимается на удалённой стороне туннеля. Нужно помнить основное правило: вторая пара <адрес>:<порт>
обрабатывается удалённой стороной туннеля. Поэтому пакет с адресом назначения 127.0.0.1
в правиле трансляции будет обработан второй стороной SSH
сессии, и никак иначе. Как вы уже, наверное, догадались, точку входа в туннель можно создавать не только на loopback
интерфейсе. Если туннель нужно сделать доступным не только для локального хоста, но и для других участников сети, то в качестве адреса сокета можно указать реальный адрес интерфейса.
bob@Bob-PC:~$ ssh -L 10.0.0.5:8080:192.0.0.2:80 bob@1.1.1.1
Компьютер Боба Bob-PC имеет два сетевых интерфейса с адресами 2.2.2.2
и 10.0.0.5
. В процессе установления сессии ssh откроет сокет 10.0.0.5:8080
на компьютере Bob-PC. Теперь Боб может получить доступ к порталу 192.168.0.2:80
со своего ноутбука с адресом 10.0.0.4
и со всей своей домашней сети 10.0.0.0/24
.
3. ТУННЕЛЬ С УДАЛЕННОЙ ТОЧКОЙ ВХОДА – ПУБЛИКУЕМ ЛОКАЛЬНЫЙ СЕРВИС
Как я уже говорил, точку входа в туннель можно открывать не только со стороны оригинатора ssh
сессии, но и с удалённой стороны, то есть с той, к которой мы устанавливаем ssh
сессию. Для этого вместо параметра -L
используется параметр R
. Для чего это нужно? Например, для того, чтобы можно было опубликовать локальный сервис для удалённого доступа. На ноутбуке Боба запущен Web сервер apache доступный по адресу 127.0.0.1
с тестовой копией портала компании. Бобу нужно дать доступ к Web серверу своим коллегам для проведения тестирования интерфейса. Вообще, для подобных целей Бобу неплохо было бы реализовать более надёжную тестовую песочницу. Но так как наш Боб не более чем виртуальный персонаж этой статьи, он для демонстрации работы SSH
туннеля устанавливает сессию между своим ноутбуком и маршрутизатором Linux. А с помощью параметра -R
открывает порт 8080 на внутреннем интерфейсе маршрутизатора с адресом 192.168.0.1
, который ссылается на сокета 127.0.0.1:80
его тестового Web сервера.
Как видите, на маршрутизаторе процесс sshd открыл локальный сокет 8080
bob@Router:~$ sudo lsof -nPi | grep 8080 sshd 17233 bob 9u IPv4 95930 0t0 TCP 192.168.0.1:8080 (LISTEN)
Давайте посмотрим, что произойдёт с TCP
пакетом, отправленным с компьютера 192.168.0.200
в сторону тестового портала, опубликованного на 192.168.0.1:8080
:
TCP
пакет с адресом источника192.168.0.200
и адресом и портом назначения192.168.0.1:8080
попадёт в сокет192.168.0.1:8080
, открытый процессомsshd
;- Процесс sshd, получив пакет, в соответствии с правилом трансляции перепишет адрес и порт назначения с
192.168.0.1:8080
на127.0.0.1:80
и отправит его внутриSSH
сессии стороне-оригинатора2.2.2.2
; - Процесс
ssh
на ноутбуке Боба, получив пакет и просмотрев адрес его назначения, перепишет адрес отправителя с192.168.0.200
на адрес своегоloopback
, и отправит его в локальный сокет127.0.0.1:80
, открытый процессомapache
.
Как видите, правила трансляции очень простые. Хост, который открывает сокет для туннеля, занимается трансляцией адреса и порта назначения согласно правилу трансляции. Хост с противоположной стороны туннеля производит подмену адреса и порта источника согласно своей таблице маршрутизации. Таблица маршрутизации необходима, во-первых, для того чтобы отправить пакет в нужную сторону, а, во-вторых, для того чтобы произвести подмену адреса источника на адрес интерфейса, с которого будет отправлен пакет.
Одно важное замечание, которое я оставил на конец статьи. Если при открытии точки входа в туннель используется localhost вместо адреса реального интерфейса, то его можно опустить, сократив, таким образом, команду с
bob@Bob-PC:~$ ssh -L 127.0.0.1:8080:192.0.0.1:80 bob@1.1.1.1
до
bob@Bob-PC:~$ ssh -L 8080:192.0.0.1:80 bob@1.1.1.1
Эта важная особенность синтаксиса пригодится нам в следующем примере.
4. ДВОЙНОЕ ТУННЕЛИРОВАНИЕ
Давайте посмотрим на чуть более сложный пример. Пользователю SQL-Tester
, находящемуся за NAT
, нужно получить доступ к базе данных на SQL
сервере, который тоже находится за NAT
. SQL-Tester не может установить соединение напрямую к серверу, так как в NAT
серверной сети нет соответствующих трансляций. Однако от обоих хостов можно установить SSH
сессию с промежуточным сервером 3.3.3.3
.
С SQL
сервера устанавливаем SSH
соединение с сервером 3.3.3.3
и открываем на loopback интерфейсе сервера 3.3.3.3
порт 13306
, ссылающийся на локальный сервис SQL
, запущенный на локальном сокете 127.0.0.1:3306
SQL
сервера: dbuser@SQL-server
:
~$ ssh -R 13306:127.0.0.1:3306 user1@3.3.3.3
Теперь с клиентского хоста SQL-Tester
устанавливаем соединение с 3.3.3.3
и открываем порт 3306 на loopback интерфейсе клиента, который, в свою очередь, ссылается на 127.0.0.1:13306
на сервере 3.3.3.3
, который ссылается на 27.0.0.1:3306
на SQL
сервере. Всё просто:
Jtester@SQL-Tester:~$ ssh -L 3306:127.0.0.1:13306 user2@3.3.3.3
5. ТУННЕЛИРОВАНИЕ ЧЕРЕЗ SSHUTTLE
Когда нам требуется пробросить порты сразу к нескольким серверам за NAT
, мы можем воспользоваться инструментом sshuttle. Для этого нам потребуется удалённый сервер, доступный по SSH
. Допустим, его IP
— 1.2.3.4
. Для того чтобы завернуть весь трафик через туннель до этого IP
, необходимо выполнить команду:
sshuttle -r root@1.2.3.4 0.0.0.0/0
Это означает, что мы собираемся добавить в нашу таблицу маршрутизации правило, по которому весь исходящий трафик будет отправляться на удалённый сервер, за исключением DNS-запросов. С параметром --dns
DNS-запросы также будут направляться на удалённый сервер.
Параметр --daemon
можно передать утилите, если мы хотим запустить туннель в фоновом режиме. Если мы хотим использовать тунель только для трафика с определённых IP
или для целой подсети, запустить sshuttle
можно так:
sshuttle -r root@1.2.3.4 2 2.2.2.0/24
В итоге мы сможем обращаться напрямую, например, к подсети 2.2.2.0/24
, не
пробрасывая нужный порт для каждого хоста. На текущий момент пакет sshuttle
удален из стандартного репозитория, но установить его можно самостоятельно. Более подробная информация здесь: https://github.com/sshuttle/sshuttle
6. ЧТО ДЕЛАТЬ ПОЛЬЗОВАТЕЛЯМ MICROSOFT WINDOWS?
Прочитав статью, вы возможно, решите, что все преимущества SSH
туннелей доступны только пользователям Unix-like
систем. Однако это не так. Практически все терминальные клиенты для Windows
работающие по протоколу SSH
имеют поддержку туннелирования, например putty
, или SecureCRT
.
7. ЗАКЛЮЧЕНИЕ
Конечно же, для создания постоянных туннелей на боевых серверах нужно использовать специальное программное обеспечение. Но для быстрого решения задачи по пробросу портов, траблшутинга, получения быстрого удалённого доступа, да и вообще решения конкретной задачи «здесь и сейчас» зачастую хорошим подспорьем будет использование SSH
туннелей.