Траблшутинг IPv6-прокси: MTU, DNS-резолв и утечки
Содержание
- Почему IPv6-прокси ведёт себя не так, как IPv4
- MTU 1280 — это не рекомендация, а пол
- DNS: AAAA ломает прокси тихо и незаметно
- Утечки через IPv6 в обход прокси
- Как ловить эти проблемы в проде
- Сравнение подходов к обходу проблем
- Что делать с ICMPv6 в файрволе
- Реальный случай: прокси на nginx 1.24 и 1420 MTU
- Ещё один случай: медленный резолв через прокси
- Клиентские утечки в браузерах
- Итог: чек-лист перед деплоем
Почему IPv6-прокси ведёт себя не так, как IPv4
Прокси на IPv6 — это не «то же самое, только адрес длиннее». Тут другой стек, другой MTU по умолчанию, другая логика фрагментации. И если перенести конфиг с IPv4 один в один, получишь грабли, которых на IPv4 не было никогда.
Главная причина — PMTUD (Path MTU Discovery). В IPv4 маршрутизаторы на пути фрагментируют пакеты, если те не влезают. В IPv6 фрагментация только на отправителе. Роутер, который не может пропихнуть пакет, просто дропает его и шлёт ICMPv6 «Packet Too Big». Если ICMPv6 заблокирован файрволом — соединение виснет намертво. TCP-хендшейк проходит, а данные не идут. Классика.
Плюс DNS. Резолверы на IPv6 отдают AAAA-записи, и клиент по умолчанию пробует их первыми (RFC 6724). Если прокси не умеет ходить по IPv6 наружу — получаем задержку на таймауте, потом фолбэк на A. Иногда 5 секунд на каждый запрос. Пользователь думает, что прокси «тормозит», а на деле это Happy Eyeballs не сработал.
MTU 1280 — это не рекомендация, а пол
Минимальный MTU для IPv6 — 1280 байт. Меньше нельзя, роутеры обязаны пропускать пакеты такого размера. Но реальный MTU линка часто больше: 1500 в локалке, 1492 в PPPoE, 1420 в некоторых туннелях.
Проблема в туннелях. Если ты гонишь IPv6 через GRE или 6in4, заголовки съедают байты. 6in4 поверх IPv4 — минус 20 байт (IPv4 header). GRE — минус 4. IPsec добавляет ещё больше. Итоговый MTU падает, а приложение об этом не знает.
Проверить реальный PMTU можно так:
```bash
ping6 -c 3 -M do -s 1400 2001:db8::1
```
`-M do` запрещает фрагментацию, `-s 1400` — размер данных. Если пакет проходит — PMTU не меньше 1400+48 (заголовки). Если нет — уменьшай, пока не пойдёт. Найденное значение и есть реальный путь.
Для TCP MSS можно прибить вручную через nftables, чтобы не зависеть от PMTUD:
```bash
nft add rule ip6 mangle forward tcp flags syn tcp option maxseg size set rt mtu
```
Это подменяет MSS в SYN-пакетах на актуальный MTU маршрута. Костыль, но работает там, где ICMPv6 режут.
DNS: AAAA ломает прокси тихо и незаметно
Клиент резолвит `example.com`, получает AAAA `2606:2800:...` и A `93.184.216.34`. По RFC 6724 IPv6 в приоритете. Клиент идёт на AAAA через прокси. Прокси не может достучаться — у него нет IPv6-маршрута наружу. Таймаут. Потом фолбэк на A. Суммарно 3-5 секунд задержки.
Диагностика простая — посмотреть, что реально отдаёт резолвер и что делает прокси:
```bash
dig AAAA example.com +short
curl -6 -v --max-time 5 https://example.com 2>&1 | grep -E "Trying|Connected|timed out"
```
Если `curl -6` висит, а `curl -4` летает — вот и причина. Лечится либо маршрутом наружу (правильно), либо отключением AAAA в резолвере прокси (быстро, но грязно).
В squid отключение IPv6 делается так:
```
dns_v4_first on
```
Это заставляет squid резолвить только A-записи. Минус — теряешь доступ к IPv6-only ресурсам. Если они не нужны — вариант рабочий.
Утечки через IPv6 в обход прокси
Самая неприятная категория. Прокси настроен на IPv4, трафик идёт через него. Но у клиента есть link-local `fe80::` и глобальный `2001:db8::`. Приложение резолвит AAAA и лезет напрямую, минуя прокси. Привет, деанонимизация.
Это происходит потому, что правила маршрутизации часто пишут только для IPv4. `ip route add default via ...` — есть. `ip -6 route` — пусто, но default через RA прилетел автоматически. И весь IPv6-трафик уходит в обход.
Проверка утечки — три команды:
```bash
curl -6 https://ifconfig.co
curl -4 https://ifconfig.co
ip -6 route show default
```
Если первый и второй показывают разные адреса, а третий — дефолт через RA — утечка есть. Решение: либо явно роутить IPv6 через прокси-интерфейс, либо дропать весь IPv6 на клиенте, если он не нужен.
```bash
ip6tables -A OUTPUT -o eth0 -p tcp --dport 443 -j REJECT
```
Жёстко, но эффективно. Клиент получит ICMPv6 unreachable и мгновенно уйдёт на IPv4-путь. Без пятисекундных таймаутов.
Как ловить эти проблемы в проде
Мониторинг IPv6-прокси отличается от IPv4. Метрики те же, но точки отказа другие. Смотри на три вещи: долю ICMPv6-дропов, время резолва AAAA и количество ретраев TCP.
ICMPv6-дропы видны через `netstat -s -6` или счётчики nftables. Растущий счётчик `packet too big` — сигнал, что PMTUD где-то режут. Время резолва AAAA — через `dig` в цикле или метрики резолвера. Ретраи TCP — через conntrack.
Минимальный healthcheck для прокси на Python:
```python
import socket, time
def check_v6(host, port=443, timeout=3):
start = time.monotonic()
try:
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
s.settimeout(timeout)
s.connect((host, port))
s.close()
return round((time.monotonic() - start) * 1000, 1)
except Exception as e:
return f"fail: {e}"
print(check_v6("2606:4700:4700::1111"))
```
Запускай раз в минуту, пиши в Prometheus. Если p95 уходит выше 500 мс — копай MTU. Если растут фейлы — копай маршрутизацию.
Сравнение подходов к обходу проблем
| Подход | Задержка | Сложность | Риск утечки |
|---|---|---|---|
| Полный IPv6-маршрут через прокси | 20-40 мс | средняя | нет |
| Отключение AAAA в резолвере | +0 мс | низкая | нет |
| Дроп IPv6 на клиенте | +0 мс | низкая | нет |
| MSS-clamp без PMTUD | +5-15 мс | средняя | нет |
| Игнор проблемы | +3000-5000 мс | нулевая | высокий |
Цифры задержек — из реальных замеров на стенде с задержкой до апстрима 15 мс. Таймаут Happy Eyeballs по умолчанию 250 мс на попытку, но с ретраями и TCP-син набегает больше.
Что делать с ICMPv6 в файрволе
Многие файрволы режут ICMPv6 целиком «для безопасности». Это ломает PMTUD, NDP и SLAAC. Минимум, что нужно пропускать:
- Type 1 (Destination Unreachable)
- Type 2 (Packet Too Big) — критично для PMTUD
- Type 3 (Time Exceeded)
- Type 4 (Parameter Problem)
- Type 133-136 (NDP: Router/Neighbor Solicitation/Advertisement)
Правило для nftables:
```bash
nft add rule ip6 filter input icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept
```
Без этого PMTUD не работает, и любая попытка пройти через туннель с MTU < 1500 превращается в лотерею.
Реальный случай: прокси на nginx 1.24 и 1420 MTU
Стенд: nginx 1.24 в роли forward-прокси, апстрим за 6in4-туннелем с MTU 1420. Клиенты шлют POST на 8 КБ. Часть запросов виснет на 30 секунд, потом 502.
Причина: nginx отдаёт пакеты по 1500 байт (MTU своего интерфейса), туннель их дропает, ICMPv6 «Packet Too Big» режется файрволом на границе. PMTUD не срабатывает, TCP ретраит, таймаут.
Решение: снизить MTU на интерфейсе nginx до 1420 и включить MSS-clamp.
```bash
ip link set dev eth0 mtu 1420
nft add rule ip6 mangle postrouting tcp flags syn tcp option maxseg size set 1380
```
После этого 502-е ушли, p95 latency упала с 2800 мс до 45 мс. Классический кейс, когда «прокси глючит», а виноват MTU.
Ещё один случай: медленный резолв через прокси
Прокси на 200 RPS, средняя задержка 800 мс. Апстримы живы, CPU свободен. Смотрим в логи — каждый десятый запрос висит ровно 5 секунд.
Причина: резолвер прокси отдаёт AAAA, прокси пытается идти по IPv6, у него нет маршрута наружу, ждёт таймаут 5 секунд (дефолт `resolv.conf`), потом фолбэк. Каждый десятый апстрим имеет AAAA-запись.
Решение: либо маршрут наружу, либо `dns_v4_first` в конфиге. Второе быстрее, но временно. В итоге настроили NAT66 на прокси-хосте — и задержка вернулась к 120 мс.
Клиентские утечки в браузерах
Chrome и Firefox резолвят AAAA параллельно с A и пробуют оба. Если IPv6-путь есть, но медленный — браузер может залипнуть на нём. Это не баг, это Happy Eyeballs с таймаутом 250 мс. Но если прокси не умеет IPv6, а клиент умеет — трафик частично идёт напрямую.
Проверка через `chrome://net-internals/#sockets` показывает, какие сокеты открыты и куда. Если видишь соединения на IPv6-адреса в обход прокси — утечка. Лечится политикой на уровне ОС или настройкой прокси с явным запретом IPv6 для клиента.
Итог: чек-лист перед деплоем
Перед тем как поднимать IPv6-прокси, проверь: PMTUD работает (ICMPv6 type 2 проходит), резолвер знает про AAAA и умеет их отдавать, маршрут наружу есть или явно закрыт, MSS-clamp настроен на границе туннеля, healthcheck ловит IPv6-специфичные фейлы.
lexic.ml держит IPv6-прокси с 2015 года, и за это время грабли с MTU и DNS всплывали чаще, чем любые другие. Не потому что протокол плохой, а потому что его настраивают по аналогии с IPv4. Аналогия не работает — стек другой, поведение другое, точки отказа другие.