Отключать IPv6, чтобы починить утечку VPN? Лучше определите политику
Адрес link-local — не утечка, и глобальный маршрут IPv6 тоже. Сначала выберите между блокировкой, переносом и бездействием.
Адрес из диапазона IPv6 появляется на тестовой странице, туннель явно его не переносит, а самый популярный совет в интернете — отключить IPv6 везде. Эта команда действительно заставляет панель замолчать. Но она также удаляет протокол, от которого может зависеть сеть доступа, и оставляет вас без представления, был ли адрес настоящей утечкой, адресом интерфейса link-local или резолвером, выполняющим свою работу. Правильное управление определяют три условия, и только одно из них ведёт к блокировке.
Определите, что инструмент на самом деле измерил
Самая частая ложная тревога — адрес link-local, который начинается с fe80: и существует на каждом интерфейсе с поддержкой IPv6, независимо от того, достигает ли машина интернета через IPv6. Он появляется в списках интерфейсов и в некоторых диагностиках приложений и ничего не говорит о пути вашего трафика.
Вторая ложная тревога — уникальный локальный адрес в fc00::/7, это частный диапазон, используемый внутри сети. Инструмент, который печатает каждый адрес хоста, с удовольствием перечислит его рядом с публичным. Кто читает быстро, называет это утечкой и потом тратит час на проблему, которой нет.
Третья — вовсе не ложная тревога. Глобальный адрес, который достигает интернета вне туннеля, и есть настоящая утечка, и его стоит измерить, прежде чем что-либо решать:
ip -6 addr show scope global
ip -6 route show default
curl -6 -sS --max-time 6 https://ifconfig.co
Если первая команда печатает глобальный адрес, а третья возвращает публичный адрес IPv6, не принадлежащий туннелю, у вас есть настоящий путь вне туннеля. Если первая не печатает ничего, кроме записей link-local, утечки, которую нужно исправить, никогда не было.
Управление определяют три условия
Решение не «IPv6 хороший» или «IPv6 плохой». Решение в том, в какой из трёх ситуаций вы находитесь, и у каждой свой правильный ответ с разной ценой ошибки.
| Situation | Correct control | Cost if you disable IPv6 instead |
|---|---|---|
| Exit has no IPv6, host does | Block IPv6 deliberately in the tunnel policy | Same visible result, no documentation of intent |
| Exit carries IPv6, host leaks | Route IPv6 into the tunnel and verify | You lose IPv6 reachability entirely |
| Access network is IPv6-only | Change nothing; use the tunnel's IPv6 | Machine loses connectivity completely |
Первая строка — то, что люди обычно имеют в виду, и это законно. Клиент, чей выход не поддерживает IPv6, не должен молча выпускать собственный трафик IPv6 хоста, потому что это документированная утечка двойного стека. Смысл в том, чтобы блокировать её как объявленную политику, а не отключать протокол в ядре. Видимый результат одинаков, и только один из них можно проверить позже.
Третья строка — место, где общий совет становится активно вредным. Мобильные операторы и некоторые домашние подключения используют сети IPv6-only с трансляцией для адресов IPv4. Уберите там IPv6, и вы уберёте единственный транспорт, который предлагает сеть, а это даёт гораздо худший сбой, чем тот, который вы устраняли.
Если у выхода нет IPv6, заблокируйте его намеренно
Предпочитайте собственный механизм клиента, потому что он действует ровно пока туннель поднят и отменяется, когда тот падает. OpenVPN имеет для этого опцию, а iptables имеет эквивалентное правило для других клиентов:
block-ipv6
ip6tables -A OUTPUT -o eth0 -j REJECT --reject-with ipv6-port-unreachable
Директива принадлежит профилю; правило — межсетевому экрану, который вы можете просмотреть и удалить. Оба отказывают закрыто, что и нужно, и оба видны в diff конфигурации, чего нельзя сказать о sysctl ядра, зарытом в стартовом скрипте.
Запишите, почему оно там. Правило блокировки без комментария удалит следующий человек, которому IPv6 понадобится для чего-то другого, и тогда утечка тихо вернётся.
Если выход переносит IPv6, маршрутизируйте его и проверьте оба семейства
Перенос IPv6 — лучший ответ, когда сервер его поддерживает, и это ненамного сложнее. Требование: у интерфейса туннеля есть адрес в этом семействе, а маршрут по умолчанию для него ведёт в туннель, а не наружу физического интерфейса:
ip -6 route get 2606:4700:4700::1111
ip -6 route show default
curl -6 -sS --max-time 6 https://ifconfig.co
curl -4 -sS --max-time 6 https://ifconfig.co
Поиск маршрута — решающая проверка: если он называет интерфейс туннеля, трафик IPv6 переносится. Два вызова curl затем подтверждают, что видит каждое семейство, и их сравнение — способ доказать, что оба пути теперь выходят через один и тот же выход.
Не останавливайтесь на успешном подключении. Переподключите туннель и проверьте снова, потому что протолкнутый маршрут IPv6 может потеряться при переустановлении, если сервер отправляет его только в начальной конфигурации. Затем проверьте один исключённый адрес назначения, потому что правила раздельного туннеля — это место, где два семейства расходятся чаще всего.
Если сеть доступа только IPv6, оставьте её в покое
Некоторые сети дают только IPv6 и ожидают, что устройство достигнет служб IPv4 через механизм трансляции на стороне оператора. Отключение IPv6 на такой машине не улучшение безопасности; оно убирает единственный путь хоть куда-нибудь. Это сбой, который вы сами себе устроили ради результата теста, который никому не был нужен.
Симптом этой ошибки узнаваем и неприятен: туннель вроде бы устанавливается, разрешение имён работает, а ничего не загружается. Если вы видите это после изменения IPv6, немедленно восстановите настройку и продолжите диагностику в другом месте.
Что бы вы ни выбрали, держите решение явным в профиле или межсетевом экране, привяжите его к тому, что выход действительно поддерживает, и отметьте поведение двойного стека, которое описывает обсуждение двойного стека в RFC 7359 . Объявленная блокировка — это политика. Отключённый протокол — это обход, который никто не может проверить, и с тестовой страницы они выглядят одинаково.