Подключения API с IPv6

Продолжение обсуждения: Подключение к API через IPv6 против IPv4 — поддержка — Discourse Meta

Недавно я обновил Node.js с версии 18 до 20, и мое приложение внезапно перестало получать ответы от API Discourse. Мне потребовалось некоторое время, чтобы понять, что проблема связана с вышеупомянутой темой. Я подтвердил это, установив соединение только через IPv4, например:

import {Agent} from 'node:https'
import axios from 'axios'
import {env} from 'node:process'
export const axiosDiscourse = axios.create({
  baseURL: 'https://<discourse-url>',
  headers: {
    'api-key': env['DISCOURSE_KEY'],
    'api-username': env['DISCOURSE_USERNAME']
  },
  httpsAgent: new Agent({
    family: 4
  })
})

Ключевой момент здесь — указание HTTP-агента; ранее я использовал его без этого параметра. Теперь всё работает, но я пытался отказаться от Axios, чтобы использовать другие библиотеки на основе fetch, такие как Wretch (в первую очередь для уменьшения размера пакета, так как планирую использовать его также на стороне клиента): elbywan/wretch: Обёртка над fetch с интуитивно понятным синтаксисом. :candy: (github.com). Однако в fetch нет возможности явно указать использование IPv4 или IPv6. Поэтому я решил, что мне нужно либо:

  1. Принудительно использовать только IPv4, но, несмотря на все мои попытки, это пока не удалось.
  2. Связаться с поддержкой Discourse, чтобы узнать, как настроить работу через IPv6.

Мое приложение подключается к различным другим API, и ни у одного из них нет такой проблемы — отказывается работать только соединение с Discourse.

Вот ошибка, которую я получаю (надеюсь, это поможет):

Error: connect ETIMEDOUT 184.105.99.43:443
    at createConnectionError (node:net:1634:14)
    at Timeout.internalConnectMultipleTimeout (node:net:1685:38)
    at listOnTimeout (node:internal/timers:575:11)
    at processTimers (node:internal/timers:514:7) {
  errno: -110,
  code: 'ETIMEDOUT',
  syscall: 'connect',
  address: '184.105.99.43',
  port: 443
},
Error: connect ENETUNREACH 2602:fd3f:3:ff01::2b:443 - Local (:::0)
    at internalConnectMultiple (node:net:1176:40)
    at Timeout.internalConnectMultipleTimeout (node:net:1687:3)
    at listOnTimeout (node:internal/timers:575:11)
    at processTimers (node:internal/timers:514:7) {
  errno: -101,
  code: 'ENETUNREACH',
  syscall: 'connect',
  address: '2602:fd3f:3:ff01::2b',
  port: 443
}

Что интересно, в отличие от упомянутой выше темы, я не получаю ответ даже после долгого ожидания. Мой запрос сразу же завершается с этой ошибкой. Вызов API работает корректно в браузере, через curl и любые другие инструменты, которые я могу протестировать.

Способ отправки запроса к API не имеет значения, пока запрос выполняется из моего приложения. Он просто не работает и функционирует только при использовании опции family, как указано выше. В связанной теме я вижу следующее упоминание:

Вам нужно разобраться, почему соединения через IPv6 не работают в вашей сети.

У меня есть два возражения по этому поводу:

  1. Я не знаю, как именно это проверить.
  2. Не понимаю, почему это проблема возникает только с Discourse и только в моём приложении.

Ещё один момент: это происходит только внутри моего экземпляра WSL. Если я запускаю своё приложение напрямую на хост-устройстве Windows, оно успешно подключается.Я понимаю, что это, вероятно, что-то очень специфичное для моей конфигурации, но я просто не знаю, где искать проблему прямо сейчас. Любые подсказки будут полезны!

Похоже, что используемая вами библиотека пытается устанавливать соединения как через IPv4, так и через IPv6; это хорошая новость и означает, что у вас нет той же проблемы, что здесь!

Давайте разберём их по отдельности:

Это означает, что ваша сторона отправила TCP SYN для попытки подключения к размещённому экземпляру, но не получила ответа. Обычно это означает одно из следующего:

  • запрос не дошёл до сервера
  • сервер (или что-то на пути) намеренно молча отбросил его
  • ответ не вернулся на ваш компьютер

Поскольку

скорее всего, проблема связана с настройкой сети на вашем компьютере.

Это означает, что ваш сетевой стек сказал: «Я не могу туда добраться», и это нормально, поскольку используемое вами решение, очевидно, переключается на IPv4.

Оценивая всё в целом, я предполагаю:

  • IPv4 не работает с вашего компьютера (неясно, верно ли это, пожалуйста, проверьте — «может подключиться нормально» может означать, что используется либо IPv4, либо IPv6)
  • IPv4 не работает из контейнера WSL
  • IPv6 работает с вашего компьютера
  • IPv6 не работает из контейнера WSL

Пожалуйста, напишите команде @team ваш внешний IPv4-адрес, и мы проверим, не отбрасывается ли он намеренно — у нас есть заблокированные диапазоны IP-адресов, которые ведут себя некорректно, и это позволит нам исключить такую возможность.

Однако, если и IPv4, и IPv6 работают с вашего компьютера, но не работают из контейнера WSL, вам нужно будет решить эту проблему в настройках WSL.

Спасибо большое за ответ.

Что касается ваших предположений, вот что мне удалось протестировать на данный момент:

  • Я попробовал отключить IPv6 в настройках Windows:

По моему пониманию, это должно обеспечить использование только IPv4 для соединений. Если так, то это сработало.

  • Я попробовал выполнить команду:
 curl -4 https://<host>/latest.json?order=created

в WSL, и всё прошло успешно (я получил ответ).

  • Не уверен, как можно протестировать только IPv6, поскольку, как только я отключаю IPv4 (аналогично IPv6 на приведённом выше скриншоте, оставляя IPv6 включённым), мой компьютер практически теряет подключение к интернету — все соединения сбиваются, что, впрочем, ожидаемо.

  • Я попробовал выполнить:

 curl -6 https://<host>/latest.json?order=created -v
*   Trying 2602:fd3f:3:ff01::2b:443...
* Immediate connect fail for 2602:fd3f:3:ff01::2b: Network is unreachable
* Closing connection 0
curl: (7) Couldn't connect to server

в WSL, и это сразу же не удалось…

Сегодня у меня возникли проблемы с WSL, поэтому я решил настроить чистую установку. Это новый экземпляр, на котором установлены только asdf и Node.js (и все системные пакеты обновлены). Не уверен, связано ли это всё ещё с какой-то неправильной конфигурацией на стороне WSL — сейчас я должен использовать все настройки по умолчанию.

Я поделился своим публичным IPv4-адресом с указанной группой.

Что ж, я пока не вижу способа отправить личное сообщение, вероятно, потому что я новый пользователь.

Что-то более интересное… Когда я проверял свой публичный IPv4-адрес, сайт попытался также показать мой IPv6-адрес и написал: «IPv6 не обнаружен». Тогда я решил проверить, есть ли у меня вообще подключение IPv6, и нашёл эту страницу: Как настроить службу IPv6 на беспроводном маршрутизаторе TP-Link.

На ней указано, что в маршрутизаторах должен быть специальный раздел для использования IPv6. У моего маршрутизатора такого раздела нет, и я также не вижу опции PPPoE v6, показанной там. Возможно, мой маршрутизатор вообще не поддерживает IPv6 — это могло бы объяснить, почему подключения IPv6 не работают (а также почему у меня фактически пропал доступ в интернет, когда я отключил IPv4 в настройках Windows).

РЕДАКТИРОВАНИЕ: У меня этот маршрутизатор TL-WR841N | Беспроводной маршрутизатор N со скоростью 300 Мбит/с | TP-Link India, и там указано, что IPv6 поддерживается :thinking:

РЕДАКТИРОВАНИЕ 2: Но при использовании такого ресурса, как v6.testmyipv6.com, я получаю ошибку ERR_NAME_NOT_RESOLVED, значит, IPv6 действительно не работает у меня.

Если проблема в этом, я всё ещё не понимаю, почему это вдруг началось. Раньше всё работало нормально уже довольно долгое время.

Я немного подбодрил вас. :slight_smile: Теперь, если вы нажмёте на @team, вы должны увидеть кнопку сообщения. :+1:

Спасибо, отправлено!

Дополнительный факт: я переключился на точку доступа от моего мобильного оператора, который использует IPv6. Теперь я могу проверить это с помощью:

curl -6 https://<host>/latest.json?order=created -v
*   Trying 2602:fd3f:3:ff01::2b:443...
* Immediate connect fail for 2602:fd3f:3:ff01::2b: Network is unreachable
* Closing connection 0
curl: (7) Couldn't connect to server

ошибка по-прежнему возникает аналогичным образом. Однако, если в настройках Windows отключить IPv4, я могу получить доступ к интернету, чего раньше не было. Таким образом, новые данные:

  • IPv4 в Windows: работает
  • IPv6 в Windows: работает
  • IPv4 в WSL: работает
  • IPv6 в WSL: не работает

Если я не указываю -4 или -6 для curl, он возвращается к IPv4 (IPv6 сразу не работает):

curl https://<host>/latest.json?order=created -v
*   Trying 184.105.99.43:443...
*   Trying 2602:fd3f:3:ff01::2b:443...
* Immediate connect fail for 2602:fd3f:3:ff01::2b: Network is unreachable
* Connected to <host> (184.105.99.43) port 443 (#0)

Я также выполнил:

ping -6 google.com

как в Windows, так и в WSL. В Windows это работает, в WSL — нет. Похоже, у меня достаточно информации и подтверждений того, что проблема связана с подключением IPv6 в моём экземпляре WSL. Похоже на известную проблему: cannot reach ipv6 only address · Issue #4518 · microsoft/WSL (github.com)

Наконец-то!

Основываясь на обсуждении по ссылке выше, я смог добавить

[experimental]
networkingMode=mirrored

в файл .wslconfig, и это обеспечило мне подключение IPv6 внутри экземпляра WSL. Теперь я могу успешно выполнить:

curl -6 https://<host>/latest.json?order=created

Однако это всё ещё не работает через мой Wi-Fi, что, скорее всего, связано с тем, что мой старый роутер не поддерживает IPv6. При использовании точки доступа с телефона всё работает отлично.

Огромное спасибо @JammyDodger за подсказки — вы направили меня в правильном направлении!

Хорошо, очевидно, что между оригинальным сообщением и этим что-то изменилось, поскольку ранее вы получали:

Рада видеть, что теперь всё работает.

Да, это меня всё ещё сбивает с толку. Как я уже упоминал ранее, я мог отправлять запросы через curl в WSL ещё до этого, и они работали. Неизвестно почему, но в моём приложении с использованием Axios запросы не проходили (возможно, это проблема самого Axios или Node.js?).

Тем не менее, curl -4 всё ещё работает, и Axios тоже работает, если указать httpFamily, как показано в моём первоначальном сообщении. Но это не та ошибка, из-за которой я буду терять сон.

Просто для проверки я откатился к Node.js 18, и мне удалось подключиться без указания httpFamily, так что это точно изменение, появившееся в Node.js 19 или 20. Но благодаря этому я хотя бы узнал об этой проблеме с IPv6, иначе я бы всё ещё не искал информацию и не изучал её.

Также я могу запустить этот код с помощью Bun вместо Node.js, не указывая настройку для IPv4. Я обратился в сообщество Node.js: https://github.com/orgs/nodejs/discussions/50826