Проброс порта против доступа через VPN: куда на самом деле идёт пакет
Проброс порта переписывает назначение и на этом останавливается; VPN сначала упаковывает пакет. Посмотрите, какой уровень меняет каждый и что это значит для HTTPS.
Две схемы, одно HTTPS-приложение, и пакет в каждой идёт своим путём. Проброс порта меняет, куда доставляется пакет, и остальное не трогает. VPN сначала несёт этот пакет внутри другого пакета, поэтому слушатель приложения видит другой источник и меньшую максимальную полезную нагрузку. Шифрование, о котором спорят, присутствует в обоих случаях, потому что приложение по-прежнему HTTPS; различается то, кто может открыть соединение и что может наблюдать сеть между ними. Это различие решает большинство практических вопросов.
Правило DNAT переписывает назначение и на этом останавливается
На шлюзе Linux опубликовать сервис означает правило в цепочке PREROUTING таблицы nat или эквивалент в nftables. Правило совпадает с публичным адресом и портом, затем переписывает назначение на частный адрес приложения. Адрес источника, порт источника и полезная нагрузка остаются нетронутыми, а conntrack записывает сопоставление, чтобы ответы переписывались при выходе. Поведение — это традиционная модель трансляции адресов, описанная в RFC 3022.
sudo iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 443 \
-j DNAT --to-destination 10.20.30.40:443
sudo iptables -t nat -L PREROUTING -n -v
sudo conntrack -L -p tcp --dport 443
Слушатель приложения никогда не узнаёт, что соединение пришло через трансляцию. Он видит настоящий адрес источника клиента, что полезно для журналов доступа и ограничений скорости, и отвечает на частный адрес, потому что conntrack обращает сопоставление. Ничто на этом пути не расшифровывает и не перешифровывает: TLS по-прежнему работает между клиентом и собственным сертификатом приложения.
Правило также определяет режимы отказа, с которыми вы столкнётесь позже. Если шлюз пересылает, но у хоста приложения нет обратного маршрута к клиенту, ответы уходят с неправильным источником и рукопожатие застревает. Если второе правило совпадает первым, порядок решает результат, и iptables -t nat -L -n -v показывает счётчики, доказывающие, какое правило взяло пакет.
Туннель упаковывает пакет до того, как слушатель его увидит
VPN вообще не публикует приложение. Клиент строит внешний пакет, адресованный публичному адресу шлюза, шифрует внутренний пакет внутри него, а шлюз расшифровывает и направляет внутренний пакет к частному назначению. В WireGuard внешний транспорт — UDP, обычно на высоком порту; в OpenVPN поверх TCP 443 внешний пакет выглядит как TLS для всего, что наблюдает за путём.
Внутри туннеля внутренний пакет сохраняет частные адреса: адрес туннеля клиента, скажем 10.8.0.6, и приложение на 10.20.30.40. Поэтому слушатель видит источник из пула туннеля, и каждый клиент делит этот шлюз как следующий шаг. Журналы доступа за VPN часто показывают шлюз, а не пользователя, именно по этой причине, если шлюз не сохраняет исходный источник.
Упаковка стоит места в заголовке, и это единственное различие на уровне механизма с измеримым следствием. Внешний IP-заголовок плюс UDP плюс собственный заголовок WireGuard съедают MTU пути, поэтому большие пакеты нужно фрагментировать или внутреннему стеку TCP нужно ограничить свой MSS. Передача, которая работает по проброшенному пути, может застрять на туннеле, когда промежуточное устройство отбрасывает ICMP, который сообщил бы о меньшем MTU.
ip route get 10.20.30.40
ping -M do -s 1400 10.20.30.40
sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
-j TCPMSS --clamp-mss-to-pmtu
Экспозиция решается на входе, а не шифром
Обе схемы заканчиваются на одном и том же TLS-слушателе, поэтому обе наследуют один сертификат, одни версии протокола и один вход в приложение. Меняется то, кому разрешено начать рукопожатие. При публичном пробросе любой хост, достигающий 203.0.113.10:443, получает TCP-рукопожатие и TLS ClientHello ещё до того, как авторизация вашего приложения вообще будет проверена. При доступе только через туннель слушатель доступен лишь с адресов, которые шлюз направляет в туннель, а SYN чужого умирает на политике шлюза.
Это различие касается доступности, а не конфиденциальности. Проброшенный сервис HTTPS не является открытым текстом, и VPN не отменяет необходимость аутентификации приложения: допуск в туннель отвечает, может ли устройство отправлять пакеты в эту сеть, а файловое приложение отвечает, может ли пользователь читать документ. Смешивать эти два понятия — путь к VPN, который отдаёт всю локальную сеть, и приложению с одним общим паролем.
Проверяйте слушатель и политику отдельно, потому что любое из них может быть шире, чем вы предполагаете:
sudo ss -ltnp 'sport = :443'
sudo nft list ruleset
openssl s_client -connect 203.0.113.10:443 -servername files.example.com </dev/null
Если ss показывает, что слушатель привязан к 0.0.0.0, приложение доступно откуда угодно, куда пускают маршрутизация и политика брандмауэра, а проброс — лишь один из путей внутрь. Привязка к частному адресу или ограничение диапазона источников в брандмауэре — отдельное решение от выбора VPN, и именно оно действительно сужает экспозицию.
Измеряйте путь, а не ярлык
Когда кто-то утверждает, что проброс быстрее, честное сравнение держит всё остальное постоянным: тот же клиент, та же сеть доступа, тот же файл, та же конфигурация TLS, тот же хост приложения. Запустите передачу дважды на каждом пути и запишите общее время, число сбоев и отзывчивость, а не вывод постороннего теста скорости.
curl -sS -o /dev/null -w '%{time_connect} %{time_appconnect} %{time_total}\n' \
https://files.example.com/report.pdf
mtr -rwc 20 203.0.113.10
mtr -rwc 20 10.20.30.40
Эти числа отделяют установление соединения от времени передачи, что важно, потому что два пути обычно различаются задержкой туда-обратно и MTU, а не стоимостью шифра. Работа TLS происходит на концах в обеих схемах, а туннель добавляет инкапсуляцию, другой маршрут и, возможно, более загруженный шлюз. Если путь туннеля длиннее или его шлюз загружен, различие принадлежит пути, а не слову шифрование.
Выберите политику входа, которая действительно нужна сервису, затем решите, оправдывает ли измеренное различие её изменение. Публичный проброс подходит сервису, предназначенному для открытого интернета, с ограничениями источников, обновлённым TLS и мониторингом перед ним. Доступ только через туннель подходит внутреннему инструменту, пользователи которого уже зарегистрированы, при условии, что маршрутизация шлюза и права приложения оба уже, чем вся локальная сеть.
Прежде чем объявить схему законченной, подтвердите четыре вещи: к какому адресу привязан слушатель, какое правило или маршрут пускает пакет внутрь, работает ли обратный путь с настоящего адреса клиента и переживают ли большие нагрузки MTU. Для каждого из них выше есть команда, и ни на один не отвечает слово безопасно или график пропускной способности.