SSH tunnel on Mac

В целях организации шифрования последней мили я перепробовал массу сервисов, предоставляющих и VPN-туннели и SSH-туннели. Везде разная стоимость и разная политика использования. В итоге решил попробовать организовать свой собственный сервис.

В качестве VPS я выбрал Selectel, потому что минимальная стоимость сервера составляет 60 рублей в месяц и плюс к тому, у меня на аккаунте уже была небольшая сумма денег от предыдущих опытов.

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

Основные проблемы начинаются на клиентской машине. В статьях по организации и использованию SSH-туннелей постоянно приводятся примеры ручного запуска команды. И за состоянием соединения приходиться следить самостоятельно, что довольно муторно.

Наткнулся на описание программы autossh, которая обеспечивает поддержание туннеля в рабочем состоянии и при потере соединения, автоматически создает новое. Для установки в среде MacOS лучше всего использовать homebrew:

$ brew install autossh

И теперь можно использовать туннель следующим образом (первая команда с использованием ssh, вторая ее аналог с autossh):

$ ssh -q -D 8080 -N shell
$ /usr/local/bin/autossh -M0 -q -D 8080 -N shell

Где shell – это имя сервера, которое я использовал в файле ~/.ssh/config при описании его конфигурации. Итак, стабильное соединение уже организовано, осталось разобраться, каким образом автоматически его запускать.

Как оказалось, в MacOS была полностью переделана система инициализации и обычные скрипты, как в Linux, уже не работали. Вместо этого стал использоваться демон launchd.

Данный демон при своей работе использует три директории:

/Library/LaunchDaemons – используется для хранения задач, которые запускаются в момент, когда пользователи еще не зашли в систему.

/Library/LaunchAgents – используется для хранения задач, которые запускаются в случае входа в систему пользователей.

$HOME/Library/LaunchAgents – используется для хранения задач, которые запускаются только при входе определенного пользователя, при этом, в отличие от предыдущих директорий, задачи запускаются от имени данного пользователя.

При организации ssh-туннеля наиболее разумным видится использование именно третьего варианта. Переходим в эту директорию и создаем plist-файл с описанием конфигурации создаваемой задачи:

$ cd ~/Library/LaunchAgents
$ vim org.evsyukov.shell.plist

За основу я взял файл, который используется для запуска демона Pow.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>org.evsyukov.shell</string>
	<key>ProgramArguments</key>
	<array>
		<string>sh</string>
		<string>-i</string>
		<string>-c</string>
		<string>$SHELL --login -c "'/Users/juev/Library/Scripts/ssh-tunnel.sh'"</string>
	</array>
	<key>KeepAlive</key>
	<true/>
	<key>RunAtLoad</key>
	<true/>
</dict>
</plist>

В данном примере я использую скрипт ssh-tunnel.sh, который разместил в директории ~/Library/Scripts/, сам скрипт содержит только строки для запуска туннеля:

#!/bin/bash
/usr/local/bin/autossh -M0 -q -D 8080 -N shell

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

Самые интересные опции в приведенном выше plist-файле, это KeepAlive и RunAtLoad.

RunAtLoad – параметр, указывающий на запуск задачи во время входа пользователя в систему. Именно он позволяет нам автоматически запускать задачу.

KeepAlive – параметр, который следит за работоспособностью задачи. Это довольно удобная опция, которая позволяет запускать задачу повторно в случае, если процесс неожиданно завершает свою работу.

Теперь, после создания файла описания конфигурации задачи, ее необходимо загрузить:

$ launchctl load /Users/juev/Library/LaunchAgents/org.evsyukov.shell.plist

Для того, чтобы убедиться, что задача была добавлена, используем следующие команды:

$ launchctl list | grep org.evsyukov
1451	-	org.evsyukov.shell

$ ps -e | grep autossh
1453 ??         0:00.00 /usr/local/bin/autossh -M0 -q -D 8080 -N shell
1730 ttys000    0:00.00 grep autossh

Здесь же показан вывод этих команд, в случае удачного запуска.

Таким образом, с использованием launchd был организован автоматический запуск ssh-туннеля во время старта системы. Данный демон постоянно поддерживает в запущенном состоянии autossh, который постоянно проверяет работоспособность туннеля и при необходимости его пересоздает.

Теперь же можно просто в настройках системы указать прокси-сервер socks5 с адресом 127.0.0.1:8080 и забыть о проблеме запуска и зависания соединения.