PPTP в Docker проходит аутентификацию, но не передаёт трафик
PPTP нужен TCP 1723 для управления и GRE для полезной нагрузки. В контейнере часто нет помощника conntrack, поэтому вход работает, а больше ничего.
Клиент аутентифицируется, PPP согласует адрес, и дальше ничего не движется. Ping не проходит, браузер не работает, а контейнер всё время выглядит подключённым. Это не проблема таблицы маршрутизации, потому что проблемы маршрутизации обычно ломают и управляющее соединение. Это путь полезной нагрузки, а в PPTP полезная нагрузка — это другой протокол, не тот, который только что сработал.
Пять проверок в порядке, который экономит больше всего времени.
Проверка 1: отделите канал управления от полезной нагрузки
PPTP аутентифицируется через TCP 1723, а затем несёт полезную нагрузку PPP внутри GRE, протокола IP 47. GRE — не TCP и не UDP, поэтому каждое правило, пропускающее порт управления, ничего о нём не говорит. NAT или межсетевой экран, пропускающий 1723, может молча отбросить протокол 47, и сеанс остаётся наполовину построенным так, что это выглядит как успех.
Если вход работает и ни один пакет не возвращается, сначала предположите GRE, а затем докажите это.
Проверка 2: подтвердите помощник в ядре хоста
Отслеживание соединений не может связать управляющее соединение TCP с потоком GRE без помощника. Модуль — nf_conntrack_pptp, он живёт в ядре хоста, и загрузка его внутри образа контейнера ничего не даёт, потому что модули действуют на весь хост. lsmod | grep nf_conntrack_pptp на хосте Docker — это и есть проверка. Если его нет, modprobe nf_conntrack_pptp загружает его для работающего ядра и ничего не говорит о следующей загрузке, поэтому добавьте его и в конфигурацию хоста.
Модуль, исчезающий при перезагрузке, через месяц станет той же заявкой.
Проверка 3: прочитайте политику ядра, которая его отключает
Даже при наличии модуля ядра начиная с 4.7 по умолчанию ставят net.netfilter.nf_conntrack_helper в 0, что отключает автоматическое назначение помощника для каждого протокола. Именно из-за этого значения по умолчанию правильно установленный модуль всё равно ничего не делает: код есть, а политика говорит его не использовать. sysctl net.netfilter.nf_conntrack_helper показывает текущее значение, и установка его в 1 во время работы снова делает помощник доступным. Сохраните значение через конфигурацию sysctl хоста, если проект это позволяет.
Автоматическое назначение позволяет трафику неявно создавать ожидания, и именно поэтому значение по умолчанию изменили. Ограничьте его доверенным хостом или используйте явное правило, нацеленное на помощника.
Проверка 4: посмотрите на путь контейнера отдельно
Загруженный помощник не создаёт маршрут. Контейнеру нужен путь к серверу PPTP, и перенаправление на хосте должно разрешать GRE в обоих направлениях: iptables -L FORWARD -v -n показывает, принимается ли протокол 47 и движутся ли счётчики. Правила masquerade в Docker преобразуют TCP и UDP, а не протокол 47, поэтому контейнеру с мостом может понадобиться явное разрешение forward и, на некоторых хостах, собственное правило преобразования. Убедитесь также, что контейнер поддерживает устройства PPP, которых ожидает его образ.
Читайте счётчики, а не текст правила. Правило, которое существует и ни с чем не совпадает, — это украшение.
Проверка 5: докажите, что GRE отслеживается, затем решите насчёт PPTP
Пока клиент работает, прочитайте таблицу отслеживания. Установленная запись управления PPTP плюс связанная запись GRE означают, что помощник связывает потоки, а оставшаяся неисправность в другом месте. Запись управления без записи GRE означает, что полезная нагрузка отбрасывается или не отслеживается, что возвращает вас к проверкам 2 и 3.
Сам PPTP устарел, а MS-CHAPv2 слаб. Если сервер также предлагает L2TP/IPsec, IKEv2 или WireGuard, переход на один из них устраняет весь этот класс проблем.
Там, где PPTP должен остаться, запуск клиента на хосте, а не внутри контейнера убирает один слой NAT и фильтрации, что часто является более дешёвым решением.