0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【IPv6】Qiita CLIにログインできないとき

0
Posted at

問題

  • Qiita CLIの npx qiita login が失敗してしまっていた
  • エラー文
TypeError: fetch failed
[cause]: Error: read ECONNRESET

原因

IPv6での通信がなぜか接続リセットされていた(IPv4なら通信成功した)

原因特定までの道のり

① Qiita CLI が失敗
   ↓
   fetch failed / ECONNRESET

② ほかのサイト(Google / GitHub)にHTTPS接続
   ↓
   成功
   ↓
   「PC全体のネットワークが死んでいるわけではなさそう」
   「Qiita固有の問題っぽい」と範囲を絞る

③ curl で Qiita に HTTPS 接続
   ↓
   curl -I https://qiita.com
   ↓
   Connection reset
   ↓
   Qiita CLI固有ではなく、QiitaへのHTTPS通信でも再現

④ TLS 1.2を指定して再試行
   ↓
   curl --tlsv1.2 -I https://qiita.com
   ↓
   やっぱり Connection reset
   ↓
   「TLSのバージョンが原因とは考えにくい」

⑤ ここで「どの段階で切られてる?」を考える
   ↓
   ECONNRESET = TCP接続がRSTでリセットされた
   ↓
   少なくともTCPレベルの接続が途中でリセットされている
   ※ただし、ECONNRESETだけでは
      TCP確立直後なのかTLS中なのかまでは断定できない

⑥ Qiitaの名前からIPアドレスを調べる
   ↓
   nslookup qiita.com
   ↓
   IPv4もIPv6も存在

⑦ IPv4とIPv6を明示的に分けて接続
   ↓
   curl -4 -I https://qiita.com → 成功
   curl -6 -I https://qiita.com → Connection reset
   ↓
   「IPv6経路では失敗し、IPv4経路では成功」
   と切り分けられた

⑧ Node.jsがどちらを選んでいるか確認
   ↓
   Node.js DNS lookup → IPv6を選択

⑨ IPv4を優先させる
   ↓
   NODE_OPTIONS=--dns-result-order=ipv4first
   ↓
   Qiita CLI login → 成功

エラー文解析

TypeError: fetch failed
[cause]: Error: read ECONNRESET
  • fetch は、Node.jsがHTTP/HTTPS通信を行うための機能
  • fetch failedってことはQiita CLIがQiita APIに対して、
Node.js(Qiita CLI)
       ↓
   HTTPS通信(ここで問題発生)
       ↓ ×
   Qiita API

をしようとして、通信処理が失敗したという意味

  • ECONNRESET→既存のTCP接続がリモート側などからRST(TCP Reset)によって強制的に切断された
  • 接続リセットとはブラウザとサーバー間の通信が途中で強制的に切断された状態
  • [cause]: Error: read ECONNRESET
    →つまり、ネットワーク通信で何か問題発生していた原因は、
    通信していたTCP接続が、相手側または通信経路上の何らかの機器によってRST(接続リセット)され、Node.jsがデータを読み取れなくなったから

「Qiita CLI固有の問題なのか?」を切り分ける

Qiita CLIからの通信ではなく、curlからQiitaサイトへ直接HTTPS通信してみる

curl https://qiita.com

→結果:

curl: (35) Recv failure: Connection was reset

→結論:Qiita CLIからの通信だけが失敗しているわけではない

HTTPSそのものの問題か確認

前提:HTTP(S)通信についての詳細

リンク

curl -I https://qiita.com

→失敗

  • -I はHTTP HEADリクエスト(レスポンスのヘッダーだけ要求)を使う指定
  • 今回の目的はQiitaのページを取得することではなく、QiitaへのHTTPS通信が成立するか確認するかどうかの確認だけなのでこれを指定
  • さらに、以下も試した:
curl: (35) Send failure: Connection was reset
curl --tlsv1.2 -I https://qiita.com

→いずれも失敗
つまり、

  • HTTPS通信は失敗
  • TLSバージョン指定しても失敗
  • エラーコード[cause]: Error: read ECONNRESETより、そもそもTLSハンドシェイクするまえの
    TCP接続の時点で失敗している可能性が高い

インターネットアクセスに問題はないか?

curl -I https://www.google.com
curl -I https://github.com

も実行

結果:

Google → 成功
GitHub → 成功
Qiita → 失敗

つまり、

PC
 ↓
インターネット
 ↓
Google       OK
GitHub       OK
Qiita        NG

なので、「PCのインターネット接続そのものが死んでいる」わけではない。

Qiitaの名前解決結果を確認

次に、

nslookup qiita.com

を実行
※nslookupはDNSサーバーに問い合わせてドメイン名からIPアドレスを調べたり、逆にIPアドレスからドメイン名を調べるためのコマンドラインツール

→Qiitaには、

  • IPv6アドレス(2600:9000:...)
  • IPv4アドレス(99.84.48....)

の2つの住所が存在することが分かった

つまり、

qiita.com(サーバープログラム)
   ↓ DNS
   ├── IPv6アドレス
   └── IPv4アドレス

という状態

IPv4 & IPv6とは?

どちらもIPアドレスのバージョン(規格)

  • IPv4
    • 例:99.84.48.46
    • 昔からの規格
  • IPb6
    • 例:2600:9000:208e:6000:8:e1de:4d80:93a1(めちゃ長い)
    • IPv4よりも、はるかにたくさんの住所を作れる
  • 最終的には同じサービス(プログラム)に到達するものの、IPv4とIPv6は別々のIPアドレスなので、ネットワーク上では別の宛先
                qiita.com
                    │
          ┌─────────┴─────────┐
          ↓                   ↓
     IPv4アドレス          IPv6アドレス
     99.84.48.xxx          2600:9000:...
          ↓                   ↓
       IPv4経路              IPv6経路
          ↓                   ↓
          └──── Qiitaのサービス ────┘

IPv4とIPv6を分けて試す

まず、

curl -4 -I https://qiita.com
  • 意図:IPv4を使ってQiitaにアクセス
  • 結果:成功

次に、

curl -6 -I https://qiita.com
  • 意図:IPv6を使ってQiitaにアクセス
  • 結果:失敗(Connection reset)

→今回の通信失敗は、Qiita CLIがQiitaへIPv6で接続しようとした場合に発生している可能性が高い

Qiita CLIが実際にIPv6を選んでいるか確認

  • Qiita CLIはNode.js上で動いている
  • そこでNode.jsに、
node -e "require('dns').lookup('qiita.com', (err, address, family) => console.log(err || {address, family}))"

を実行させる

結果:

{
  address: '2600:9000:...',
  family: 6
}

つまり、この環境ではNode.jsのDNS lookupが

qiita.com
   ↓
IPv6アドレス

を返していた。

つまり、

Qiita CLI
   ↓
Node.js
   ↓
DNS
   ↓
IPv6を選択(×)
   ↓
Qiita
   ↓
Connection reset

という今回の失敗経路がかなり明確になった。

Node.jsに「IPv4を先に選ばせる」

set NODE_OPTIONS=--dns-result-order=ipv4first

→そのコマンドプロンプトのセッションでは、Node.jsがIPv4を優先する状態Node.jsがDNSからIPv4とIPv6の両方を受け取ったとき、IPv4の結果を優先させるようにした

もう一度Qiita CLIでログイン

そして、

npx qiita login

を再実行。

→今度は、ログイン成功🎉

なぜIPv6ではだめだったのか?

今回はそこまでは調べてないが、候補としては例えば、

  • 自宅ルーターのIPv6設定
  • ISP(インターネット回線事業者)のIPv6接続
  • ISP側のIPv6経路
  • Qiita側/CDN側のIPv6対応
  • その間のネットワーク機器

などがある

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?