Почему домашний NAT не защищает ваш VPN-интерфейс
Домашний роутер пересылает туннель, но хост оценивает расшифрованный пакет. Читайте слушателя, внутренний путь и входную политику по отдельности.
Ноутбуки за домашним роутером без проброса портов по-прежнему принимают SSH-подключения, приходящие через VPN. Это не противоречие, потому что два разных устройства оценивают два разных пакета. Роутер пересылает зашифрованный транспорт, а хост решает, что делать с расшифрованным пакетом, доходящим до его туннельного интерфейса. Разделять эти два решения — и есть всё упражнение.
Роутер пересылает туннель, хост оценивает пакет
Нарисуйте путь от удалённого источника до сервиса, прежде чем смотреть на единственное правило. Внешний участок — это транспорт VPN, которым занимаются роутер и шлюз. Внутренний участок начинается, когда конечная точка снимает туннельный слой и хост получает пакет, адресованный его VPN-адресу.
Объедините эти два участка, и роутер получит заслугу за решение, которое принимает хост. Страница проброса портов или журнал роутера описывают только внешние разговоры, и ни один не говорит, какой расшифрованный трафик хост примет. Хост применяет собственную входную политику к внутреннему пакету так же, как к любому пакету, приходящему на интерфейс. Именно в этой политике вопрос экспозиции и решается.
Слушатель с подстановочным знаком — не публичный сервис
В Linux привязка к INADDR_ANY означает каждый локальный адрес, поэтому сервис может отвечать на туннельном интерфейсе, который он никогда не называл. Справочник по IP-сокетам описывает эту привязку, и его стоит прочитать, прежде чем делать выводы из списка слушателей. Запись с подстановочным знаком — это факт о сокете, а не утверждение о доступности.
Две строки ниже — один и тот же сервис в двух разных привязках, и разница — весь ответ на вопрос, имеет ли значение интерфейс. Сначала прочитайте, на каком адресе сидит сокет.
ss -lntp "sport = :22"
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=812,fd=3))
LISTEN 0 4096 10.8.0.6:22 0.0.0.0:* users:(("sshd",pid=812,fd=4))
Назовите расклад, который мог бы достичь порта
Три расклада ставят пакет на этот интерфейс, и у них разные владельцы. Назвать правильный — значит решить, кто должен менять политику.
| Arrangement | What reaches the port | Where the decision lives |
|---|---|---|
| Another permitted VPN peer | A packet from the same tunnel subnet | Gateway forwarding policy and host input rules |
| A deliberate public forward | Traffic the gateway publishes on purpose | Gateway service definition |
| A subnet routed behind the gateway | Client traffic from the far side | Gateway route and host input rules |
Результат браузера с публичным адресом выхода ничего не говорит ни об одном из трёх, потому что описывает, как выходит исходящий трафик, а не что разрешено входящему. Работайте в обратную сторону от слушателя и адреса источника в журнале сервиса. Если источник — другой узел туннеля, политика шлюза — интересный документ. Если это транслированный адрес, найдите трансляцию, прежде чем менять правило.
Ограничьте сервис тем путём, который вы имели в виду
Если SSH нужен только с пути управления, запишите этот путь как диапазон источника плюс интерфейс. Затем ограничьте входные правила хоста по интерфейсу, назначению и источнику, а не по имени сети, поскольку имя, выглядящее приватным, может означать любой адрес. Оставьте маршрут для человека, который решит следующую проблему.
nft add rule inet filter input iifname "tun0" ip saddr 10.8.0.0/24 tcp dport 22 accept
nft list chain inet filter input
Правило, называющее туннельный интерфейс и диапазон управления, выражает намерение в одном месте. Сравните его с правилом, написанным для сети Wi-Fi, которое ничего не говорит о расшифрованном туннельном трафике. Когда два слоя не согласны, политика хоста — та, которую встречает пакет.
Оставьте входы и заплатки ровно там, где они есть; достичь порта никогда не было тем же самым, что получить разрешение им пользоваться. Запишите семейство адресов и время для любого незнакомого источника и разберитесь, прежде чем обвинять функцию или уязвимость. Правило, пропускающее каждый узел туннеля, — иной дизайн, чем то, что пропускает подсеть управления, и ни одна строка журнала не скажет, какое имелось в виду.
Примите исправление с одним успехом и одним отказом
Докажите обе половины: одно разрешённое соединение успешно, а один исключённый путь отклоняется. Отказ — это та половина, которую люди пропускают, и именно она показывает, что правило работает, а не стоит рядом с разрешающим значением по умолчанию. Тестируйте с исключённой стороны, вместо того чтобы рассуждать об этом.
Повторите пару после переподключения, потому что туннель, возвращающийся с другим именем интерфейса, может тихо обойти правило, ограниченное старым. Граница, которая имеет значение, — та, которую хост применяет к пакетам, которые он действительно получает.