Полный туннель OpenVPN обрывает вашу SSH-сессию: исправьте обратный путь
Маршрут по умолчанию отправляет SSH-ответы в туннель. Закрепите peer, отфильтруйте redirect-gateway или маршрутизируйте по источнику, пока не потеряли машину.
Включение клиента OpenVPN в режиме полного туннеля на машине, к которой вы подключаетесь по SSH, может оборвать сессию за секунду, и машина при этом ничего не делает неправильно. Туннель не меняет то, как пакеты достигают сервера; он меняет то, как уходят ответы. Как только маршрут по умолчанию указывает на tun0, ответ вашему SSH-клиенту уходит в сеть провайдера, у которой нет маршрута обратно к вашему адресу. Соединение зависает, следующая попытка завершается по таймауту, а демон всё это время слушает порт 22.
Прочитайте таблицу маршрутизации раньше, чем лог VPN. Лог скажет, что туннель поднялся, и будет прав. Вопрос в том, какому интерфейсу передаётся каждый ответ, а это поиск для каждого пакета в таблице, которую туннель только что переписал.
Почему ответ уходит не в ту дверь
Входящие пакеты по-прежнему приходят на физический интерфейс, потому что клиент VPN не может изменить то, как интернет достигает публичного адреса вашего сервера. Ответы — отдельное решение: для каждого пакета ядро ищет получателя, и после --redirect-gateway запись по умолчанию говорит «туннель». Выход провайдера видит адрес, который не обслуживает, и отбрасывает пакет.
Собственная защита OpenVPN покрывает ровно один адрес. Руководство 2.6 описывает --redirect-gateway как последовательность: он добавляет маршрут к хосту для peer VPN через шлюз, который использовался до туннеля, чтобы зашифрованный трафик не мог вернуться в себя, а затем заменяет маршрут по умолчанию. Этот маршрут к хосту предназначен только для peer. Ваш SSH-клиент — просто ещё один интернет-адрес и не получает исключения.
Одну ошибку с префиксом стоит исключить заранее. ip route add -net 0.0.0.0 gw 10.8.0.1 dev tun0 без длины совпадает с единственным адресом 0.0.0.0, а не с интернетом; должен быть 0.0.0.0/0. И если принятие маршрута по умолчанию ломает собственный транспорт туннеля, добавьте конечную точку как маршрут к хосту через физический интерфейс, используя реальный адрес провайдера.
Закрепите адрес клиента, чтобы исправить сразу
Самое быстрое исправление — маршрут к хосту для адреса вашего клиента через шлюз, который сервер использовал до появления туннеля. Со своей стороны найдите адрес, который увидит сервер, а затем прочитайте таблицу сервера, чтобы знать старый шлюз:
curl -s https://ifconfig.co
ip route
На сервере добавьте исключение до запуска клиента. Адреса здесь — заполнители; важна форма:
sudo ip route add 80.128.240.55 via 192.168.178.1
Это исправление использовано в описанном случае, и оно работает сразу. Оно также хрупко двумя предсказуемыми способами: маршрут исчезает при перезагрузке и становится неверным в тот момент, когда ваш ISP выдаёт другой адрес. Считайте его аварийной мерой и сделайте постоянным намеренно, через сетевой юнит или хук диспетчера, либо примите, что он временный, и запишите путь восстановления.
Не дайте клиенту взять маршрут по умолчанию
Более чистая опция — не давать туннелю заменять маршрут по умолчанию вовсе. route-nopull в сочетании с --client или --pull принимает проталкиваемые параметры, кроме маршрутов, block-outside-dns и параметров в стиле DHCP, таких как DNS-серверы, поэтому вы добавляете только нужные префиксы. Более узкий инструмент — фильтр вытягивания, который отбрасывает одну проблемную директиву и сохраняет остальное:
route-nopull
pull-filter ignore "redirect-gateway"
Используйте одно или другое, не оба, и ясно понимайте, что меняется. Без маршрута по умолчанию через туннель сервер больше не выходит в интернет через провайдера; он достигает только того, что вы явно маршрутизировали. Если полный туннель был всем смыслом настройки, это неправильный рычаг, и маршрут к хосту выше — правильный.
Разделите обратный путь по источнику или соединению
Когда входящий адрес стабилен, правило источника удерживает ответы локально, не трогая собственные маршруты туннеля. Дайте адресу физического интерфейса собственную таблицу с префиксом LAN и физическим маршрутом по умолчанию, а затем выбирайте эту таблицу для трафика, который от него исходит:
ip rule add from 192.168.1.11 table 128
ip route add table 128 to 192.168.1.0/24 dev eth0
ip route add table 128 default via 192.168.1.1
Это работает потому, что ответы на входящее SSH-соединение исходят от адреса, к которому подключился клиент. В описанном случае, где каждая входящая служба должна была продолжать работать, использовалась именно такая форма. Когда входящий адрес плавает или путь динамичен, вместо этого помечайте соединение: поставьте метку conntrack на первый пакет с физического интерфейса, скопируйте её на пакеты ответа в OUTPUT и маршрутизируйте помеченные пакеты через таблицу, указывающую на физический шлюз.
iptables -t mangle -A PREROUTING -i eth0 -m conntrack --ctstate NEW -j CONNMARK --set-mark 1234
iptables -t mangle -A OUTPUT -m connmark --mark 1234 -j MARK --set-mark 4321
ip route add default via 192.168.1.1 dev eth0 table 3412
ip rule add fwmark 4321 table 3412
Держите метку соединения, метку межсетевого экрана и номер таблицы раздельно во время тестов. Повторное использование одного номера для двух ролей даёт правило, которое выглядит правильным и попадает в неправильные пакеты. Записи таблицы по умолчанию тоже нужен via со шлюзом LAN на обычном канале Ethernet; голый dev eth0 работает только когда интерфейс типа точка-точка или следующий переход разрешается иначе. Описанный хост Jessie случайно оказался на канале, где голая форма работала, и это не правило дистрибутива для копирования.
Проверьте, не заперев себя снаружи
Не проверяйте, отключаясь. Оставьте первую сессию открытой, поднимите туннель и спросите ядро, какой путь выбрал бы ваш собственный адрес:
ip route get 80.128.240.55
Ответ должен называть физический интерфейс и старый шлюз, а не туннель. Затем откройте второе SSH-соединение; это настоящая проверка, потому что старая сессия может выжить на состоянии, которое новое соединение не воспроизведёт. Если второе висит, а первое ещё отвечает, правило так и не совпало с важным трафиком, и искать нужно в метках или в адресе источника.
Держите под рукой консоль вне полосы на всё время упражнения. Сервер, единственный путь доступа к которому — туннель, который вы только что сломали, нельзя починить через этот туннель, а откат проще, когда вы записали номера таблиц и префиксы, которые добавили.