Stunnel это программная обертка, позволяющая спрятать в шифрованный ssl-туннель произвольный трафик.

Собственно этой технологией вы пользуетесь каждый раз, когда заходите на сервера в Интернет, с которыми устанавливается соединение по протоколу HTTPS (в строке адреса вы видите: https:\\…..). Фактически web-сервер и клиентский браузер работают с HTTP запросами и ответами (не шифрованный трафик), однако в случае с HTTPS весь этот трафик передается внутри ssl-туннеля установленного между клиентом и сервером. Особенность такого туннеля в том, что весь трафик передается шифрованным. Для шифрования используется сессионные (временные) ключи, которые формируют в процессе обмена клиент и сервер на основе сертификата доступного на сервере. Таким образом - любой клиент может установить защищенное соединение с сервером, однако прочитать информацию передаваемую в канале может только тот клиент, который устанавливал это соединение и сервер, и никто другой (теоретически1)) эту информацию в процессе передачи прочитать не может.

HTTPS протокол имеет встроенный механизм ssl-туннелирования.

Важно! stunnel создает и поддерживает туннель как TCP соединение и передавать может только tcp-трафик.

Особенность stunnel в том, что позволяет «обернуть» в ssl-туннель трафик совершенно любого сервиса, который не имеет встроенных средств шифрования (однако и шифрованный трафик тоже можно передать через stunnel для того, что бы замаскировать его, допустим, под обмен по https протоколу). За счет чего же достигается возможность туннелировать сетевой трафик изначально не предполагавший ни шифрования ни туннелирования? А дело в том, что на любом компьютере, где реализован TCP/IP стек протоколов, есть такая (часто неприметная и порой забываемая вовсе) вещь как localhost. Механизм туннелирования заключается в том, что stunnel:

  • на клиенте: слушает порт на локальном хосте клиента и передает информацию (шифруя) на определенный порт удаленного сервера,
  • на сервере: слушает порт на внешнем адресе и передает информацию (расшифровывая) на определенный порт локального хоста сервера.

При этом клиент свой запрос отправляет не на удаленный сервер, а на тот самый порт localhost-а, на котором слушает stunnel. Сервер же слушает (ждет запроса клиента) не на внешнем порту, а на локальном порту на который транслирует передачу stunnel. Посмотрите на схему (чтобы было понятнее):

Вариант без stunnel (прямое соединение без шифрования)
+----------------------+                                                                                                         +----------------------+
| Клиентский компьютер |                                                                                                         | Сервер               |
|                      |(запрос к ex.t.ser.ver:service_port)                (слушает service_port на внешнем адресе ex.t.ser.ver)|                      |
| приложение клиент >--+-->----->------------------------------------------------------------------------------------------------+--> приложение сервер |
+----------------------+                                                                                                         +----------------------+
Вариант c stunnel (c шифрованием)
+----------------------+                                                                                                         +----------------------+
| Клиентский компьютер |                                                                                                         | Сервер               |
|                      |(запрос к localhost:s1_port)                                  (слушает service_port на localhost сервера)|                      |
| приложение клиент >--+-->-+                                                                                           +--->----+--> приложение сервер |
|                      |    |                                                                                           |        |                      |
| stunnel <------------+----+ (слушает s1_port на localhost)                                                            +---<----+--< stunnel           |
|    | < шифрование    | (отправляет на s2_port ex.t.ser.ver)                                   (слушает s2_port на ex.t.ser.ver)|       | < расшифровка|
|    +----->-----------+-->----->------------------------------------------------------------------------------------------------+--->---+              |
+----------------------+                                                                                                         +----------------------+

Порты s1_port и s2_port - служебные порты stunnel и могут быть выбраны произвольно (как правило s2_port выбирается таким, что бы соединение на него не блокировалось файерволами). С s1_port - никаких ограничений нет т.к. трафик фактически и не покидает клиентского компьютера (передача идет внутри стека TCP/IP протокола). Точно так же и service_port (порт приложения сервера) на сервере можно изменить со стандартного на произвольный (если нужно).

Если возникает вопрос «как же осуществляется обратный трафик?» то нужно понимать, что каждое клиентское приложение в каждом TCP-подключении открывает для передачи произвольный порт (из тех что имеют большие номера, например 50000+) и именно на этот порт stunnel возвращает расшифрованные данные клиента. На клиенте stunnel открывает для передачи на удаленный сервер еще один порт и между ним и портом s2_port собственно и устанавливается шифрованный канал. На сервере же, stunnel выступает в роли клиента и сам открывает произвольный порт (из старших номеров) для получения обратного трафика от приложения-сервера (который он шифрует перед отправкой на клиентский компьютер).
Все эти обратные порты открываются только для связи с другим портом (netstat такие порты отмечает статусом ESTABLISHED) и они не являются слушающими (LISTEN статус в выводе netstat) соединение (исходящие) от такого порта файерволом не блокируется.

И так, с механизмом встраивания stunnel в работу клиент-серверного приложения мы разобрались. Посмотрим теперь как он устанавливается и настраивается.

Установка Stunnel

Несмотря на то что настройки stunnel для сервера и клиента различаются, но устанавливается он и на сервер и на клиент одинаково:

apt-get install stunnel4

Настройка Stunnel

Настройка Stunnel на сервере

stunnel использует все файлы с расширением .conf, которые находит в каталоге /etc/stunnel как конфигурационные. В каждом файле могут быть описаны как и общие параметры настройки так и настройки для отдельных сервисов, причем в одном файле можно описать несколько сервисов.

Создадим новый конфигурационный файл:

sudo nano /etc/stunnel/stunnel.conf

В нем нам нужно указать настройки самого stunell:

# Укажем файл сертификата который будет использоваться для шифрования (его мы создадим чуть позже)
cert = /etc/stunnel/stunnel.pem 
# Дадим директиву не использовать скомпрометированный протокол openSSL V2
options = NO_SSLv2

В принципе, этого вполне достаточно.
Дополнительно можно указать сжатие: бывает полезно кроме шифрования еще и сжать трафик, но это не обязательно если оригинальный трафик и так уже сжат.

compression = zlib

Можно еще указать путь куда будут записываться логи сервиса stunnel

output = /var/log/stunnel.log

Теперь опишем наш сервис по следующему:

[<service1>]
# Укажем, что это конфигурация серверной стороны (в принципе это не обязательно - stunnel по умолчанию считает, что работает как сервер)
client = no
accept = <ex.t.ser.ver>:<s2_port>
connect = localhost:<service_port>

В этом шаблоне нужно будет указать реальные порты приложения сервера (<service_port>) и порт на котором будет принимать соединения stunnel для этого сервиса (<s2_port>). Имя хоста или внешний IP адрес сервера (<ex.t.ser.ver>) можно и не указывать (пропустить), тогда stunnel будет слушать этот порт на всех адресах сервера. Пример для сервиса telnet будет выглядеть примерно так (stunnel будет ждать установки соединения с клиентской стороны на порту 8023)

[telnet]
accept = 8023
connect = localhost:23

stunnel имеет достаточно большое количество настроек - все их мы не будем перечислять. О них можно узнать из манула на stunnel

man stunnel

Созданный файл конфигурации закрываем и сохраняем.
Кроме настройки конфигурационных файлов, нужно еще разрешить запуск stunnel. Для этого в файле /etc/default/stunnel нужно заменить

ENABLED=0

на

ENABLED=1

Теперь создадим сертификат:

cd /etc/stunnel/
sudo -s
openssl genrsa -out key.pem 2048
openssl req -new -x509 -key key.pem -out cert.pem 
cat key.pem cert.pem >> stunnel.pem
rm key.pem cert.pem
exit

Созданный сертификат нужно также передать (по защищенным каналам) на клиентскую машину.

Настроенный stunnel запускаем:

service stunnel start

Настройка Stunnel на клиенте

Настройка на клиентской машине не сильно отличается от настройки на серверной машине.
Общие настройки (минимальный вариант):

# Укажем файл сертификата который будет использоваться для шифрования (не забудьте его перенести с сервера на клиента)
cert = /etc/stunnel/stunnel.pem 
# Дадим директиву не использовать скомпрометированный протокол openSSL V2
options = NO_SSLv2

И настройки сервиса по шаблону:

[<service1>]
# Укажем, что это конфигурация клиентской стороны
client = yes
accept = localhost:<s1_port> 
connect = <ex.t.ser.ver>:<s2_port>

Для telnet такая конфигурация будет выглядеть так:

[<service1>]
client = yes
accept = localhost:23
connect = aa.bb.cc.dd:8023

где aa.bb.cc.dd - IP адрес или DNS-имя внешнего сервера.

Настроенный stunnel разрешаем (так же как и на сервере, редактируя /etc/default/stunnel) и запускаем:

service stunnel start

Команда telnet соединения с внешним сервером по ssl-туннелированному протоколу будет выглядеть так:

telnet localhost

Соединиться по нешифрованному каналу с telnet сервером можно было бы командой

telnet aa.bb.cc.dd

Но этого делать не стоит, и особенно не стоит telnet сервис выставлять в интернет (крайне вероятно что ваш сервер очень скоро взломают хакеры).

На самом деле, пример с telnet - исключительно умозрительный и надуманный (хотя и может применяться для отладки, но с соблюдением мер предосторожности), для удаленного логина в консоль сервера гораздо проще использовать ssh (там уже есть встроенное шифрование).

Особенности настроек приложений при работе через stunnel

Нужно понимать, что указание в строке подключения клиента localhost вместо IP адреса или имени сервера может оказать влияние на работу сервиса.

К примеру, вы решили обернуть трафик OpenVPN в stunnel (довольно популярное решение при маскировке openVPN соединения под https трафик, для обмана умных файерволов). Для этого, в конфигурационном файле OpenVPN, строку с указанием адреса и порта сервера

remote аа.bb.cc.dd 1194 

заменяем на соединение с localhost:

remote localhost <s1_port>

Но это приведет к тому, что в маршрутизацию клиента (ip route), при установлении соединения с OpenVPN сервером, будет записан маршрут не на внешний сервер, а на localhost (ведь соединение было с localhost, а не с удаленным сервером), что приведет к практически полной неработоспособности сети на клиенте (из за возникшего цикла в правилах маршрутизации).

Что бы исправить эту ситуацию нужно «вручную» прописать правильный маршрут, а неверный маршрут - удалить. Для этого в конфигурационный файл OpenVPN добавляем строки:

route аа.bb.cc.dd 255.255.255.255 net_gateway
route-up "/bin/ip route del 127.0.0.1"

здесь аа.bb.cc.dd - IP адрес внешнего сервера.

Ссылки

1)
На практике крипто-стойкость канала целиком и полностью зависит от алгоритмов шифрования, которые используются (и к примеру протокол OpenSSL v.2 уже объявлен не безопасным т.к. в нем были выявлены серьезные уязвимости).