Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新

Mar 5, 2026 - 日記

Eternal TerminalがmacOS+Tailscaleで接続できない(Receiving client idで止まる)ときの切り分け

Eternal TerminalがmacOS+Tailscaleで接続できない(Receiving client idで止まる)ときの切り分け cover image

macOS同士(Apple Silicon)のマシンをTailscale でつないでいます。 Eternal Terminal (GitHub: MisterTea/EternalTerminal )で接続しようとすると、Receiving client id から進まない沼にハマりました。

結論から言うと、この記事の時点では完全解決していません。 ただ、観測できた挙動と切り分けの手順を整理すると「どこが壊れていそうか」をかなり狭められます。ムダに時間を溶かさず、次のアクションへ進むための記事です。

本文中のホスト名とIPはダミー化しています(例: server, 100.64.0.10)。

最短チェック(ここだけで8割切り分け)

まずは以下だけ確認すると、問題の層をかなり絞れます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 1) SSHが通る
ssh server

# 2) サーバ側でetserverがLISTENしている
lsof -nP -iTCP:2022 -sTCP:LISTEN

# 3) クライアントから2022/tcpへ到達できる(通っても無罪証明にはならない)
nc -vz server 2022

# 4) サーバ上のlocalhost接続がどうなるか
et 127.0.0.1

症状(何が起きているか)

自分の環境では、同じホストに対して以下の差が出ました。

  • [SSH](/tags/SSH/) は通る: ssh server は成功する
  • Eternal Terminal は通らない: et serverReceiving client id のまま止まり、だいたい30秒で Connect timeout

「SSHでログインできるならETもいけるはず」なのに詰まるのが、精神的に一番きついポイントです。

環境

この記事の再現条件は、だいたい以下です。

  • クライアント/サーバともに macOS (Apple Silicon)
  • 経路はTailscale
  • サーバホスト名(ダミー): server
  • サーバのTailscale IP(ダミー): 100.64.0.10
  • Eternal Terminal: Homebrew版 6.2.11
  • ETのデフォルト待受ポート: 2022

なお、Tailscaleのセキュリティ観点の話は別記事「Tailsnitch: Tailscaleの通信を監査する(macOS) 」にまとめています(この記事では接続不具合の切り分けに集中します)。

環境差分になりやすい点(チェック項目)

自分は今回ここまで深掘りできていません。読者が自分の環境と差分を見つけるためのメモです。

  • Tailscale側のACLで 2022/tcp を絞っていないか
  • macOS側のファイアウォール(アプリ/ポート)で受信が落ちていないか
  • tailscale serve / tailscale funnel を有効化していないか
  • Tailscale SSH(tailscale ssh)の有効/無効で挙動が変わらないか

まず潰すべき前提(ETが依存しているもの)

Eternal Terminalは内部的にSSHを使います。なので最低限、次が成立している必要があります。

  1. クライアントからサーバへSSHできる(認証も含めて)
  2. クライアントからサーバの 2022/tcp(または設定したポート)へ到達できる
  3. サーバ側で etserver が起動していて、待受できている

このうち 1 は「ssh server が成功する」でOKです。残りを順に切り分けます。

切り分け1: 2022/tcp に到達できるか(疎通の最小確認)

まずは「ポート到達性」だけを見ます。成功しても解決ではありませんが、失敗するなら話が早いです。

1
nc -vz server 2022

自分の環境では、ここが成功するケースがありました。つまり「SYNすら届かない」タイプではなさそう、というところまで絞れます。

切り分け2: etserver が本当にLISTENしているか

launchd等で起動しているつもりでも、落ちていたり、別のアドレスにしかbindしていないことがあります。LISTEN の事実をまず確認します。

1
lsof -nP -iTCP:2022 -sTCP:LISTEN

出力が空なら、少なくともその時点では待受していません。etserver の起動方法(launchd / 手動起動)と設定ファイル(通常 /etc/et.cfg)を見直します。

切り分け3: localhost では通るのか(経路要因の切り分け)

Tailscale経由が怪しいのか、ETそのものが怪しいのかを分けるために、サーバ server 上で自己接続してみます。

1
et 127.0.0.1

自分の環境では、localhostでは acceptNewConnection accepted client-fd=... 的なログに進むケースがありました(後述)。

この差が出るなら、少なくとも「ETが常に壊れている」より「macOS + Tailscale 経路での相性」っぽさが増します。

upstream issue調査(決定打はなし)

Eternal Terminal側の既知の不具合・近い症状を探しましたが、今回の「Receiving client id から進まず Connect timeout」に刺さる決定打は見つけられませんでした。

(状況が近い人はいるものの、再現条件や原因がピン留めされていない、という印象です)

ソースコードを当たって分かったこと(観測ベース)

ここから先は「推測」ではなく「この環境で観測できた挙動」を中心に書きます。

調査で追いかけた主なファイルは以下です。

  • SshSetupHandler.cpp
  • TerminalClient.cpp
  • ServerConnection.cpp
  • UnixSocketHandler.cpp
  • TerminalServer.cpp

追加ログを入れて観測すると、サーバプロセス(etserver)自体は生きていて main loop も回り続けます。一方で、クライアント接続に関連する部分で詰まるケースが多いです。

自分の環境での典型パターンは、ざっくりこうでした。

  • FIFO(名前付きパイプ。プロセス間通信の仕組み)側の処理は addClientKey まで完了する(つまり「途中までは進む」)
  • ただし、Tailscale経由だと accept()EAGAINerrno=35)を返し続け、clientHandler() に進まないことが多い

accept() は「待受ソケットに来た接続を取り出す」システムコールです。EAGAIN は「今はacceptできる接続がない」なので、単発なら普通です。ただ「接続を試みているのに、ずっと EAGAIN を踏み続ける」状態になるのがポイントで、イベント通知/ソケット状態/経路のどこかで期待が外れている気配があります。

tcpdumpで見えたこと(見えなかったこと)

パケットがどうなっているかを見るため、tcpdump も試しました。

macOSの場合、Tailscaleのインターフェースは utunX になっていることが多いです(utun0 とは限りません)。まずは自分の環境の utunX を特定してから実行してください。

1
sudo tcpdump -n -i utun0 'host 100.64.0.10 and tcp port 2022'

localhost比較をするときは、ループバックで見ます。

1
sudo tcpdump -n -i lo0 'tcp port 2022'
  • nc での疎通確認は、SYNSYN-ACKACKFIN が素直に見える
  • しかしET本番のハンドシェイクは、期待するほど明確な流れが取れないケースがあった

自分の理解では、これは「ETが内部で複数の段階を踏む」「アプリ層で詰まっていると、TCPとしては何も起きていないように見える」など、いくつか解釈があり得ます。少なくとも「nc はOKだからネットワークは完全に無罪」とは言い切れません。

Tailscale側で試したこと(効果なし)

Tailscale側の設定が絡んでいる可能性を疑い、以下を試しましたが改善しませんでした。

1
2
tailscale serve reset
tailscale funnel reset

また、ETの設定で bind_ip = 100.64.0.10 を試す(Tailscale IPに明示bindする)も効果なしでした。

このあたりまでやって改善しないなら、少なくとも「serve/funnelの残骸」や「bindアドレスの取り違え」みたいな分かりやすい地雷ではなさそうです。

結論(現時点)

現時点の結論は、以下です。

  • Eternal Terminalと「macOS + Tailscale 経路」の相性問題の可能性が高い
  • 恒久修正(原因の特定とパッチ適用)までは到達していない

同じ沼にハマった場合は、この記事の切り分けで「SSHはOK」「2022/tcp到達性は(場合によって)OK」「localhostだと動くことがある」「Tailscale経由でacceptループが不自然」という形まで持っていけると、次の打ち手が選びやすくなります。

代替案(今すぐ作業を進める)

自分は当面、ETを諦めて ssh + tmux に寄せました。作業を止めないのが最優先です。

tmuxセッションを再利用する形なら、体験としてはETにかなり近づけられます。

1
ssh server -t 'tmux new -A -s main'

もし許容できるなら、moshも候補になります(ただし環境によっては別のハマりポイントがあります)。mosh絡みの別の罠は「moshでOSC52クリップボードが壊れるときの対処 」に書きました。

次にやるなら(再現条件を固めてupstreamへ渡す)

完全解決に向けて次にやるなら、優先度はこの順が良さそうです。

  1. Tailscaleを使わない経路(LAN直、WireGuard直、IPv6直など)と比較し、差分を固定する
  2. ホスト名解決をいじらずに、/etc/hosts やローカルトンネル等で「Tailscale IPを使わない形」を試し、回避できるかを見る
  3. upstreamへ再現条件とログを共有する(accept()EAGAIN で回り続ける点、localhostとの差、macOS/Apple Silicon、ET 6.2.11、Tailscale IP帯など)

この記事の観測が、どなたかの再現・修正の足がかりになればうれしいです。

まとめ

  • ssh が通っても、etReceiving client id で止まるケースはありました
  • nc が成功しても、ETの接続確立を保証するものではありません(今回の症状では特に)
  • 観測上は、Tailscale経由で accept() が進まず、サーバが応答フェーズに入れない可能性が高そうでした
  • 作業を止めない目的なら、まずは ssh + tmux へ退避するのが現実的です

参考リンク