中継ホストを追加し、その Nebula アドレスを設定ファイルに書き込みましたが、二台のピアはまだ通信できません。これは、中継経路では三つの別々の記述が一致している必要があり、そのうち一つだけがおそらく編集したマシン上にあるからです。私はこれを二回追いかけました:一回は転送ホストに中継の役割がまったく与えられていなかったケース、もう一回は接続の反対側に設定が書かれていたケースです。以下の手順では、役割、広告、許可の順に確認し、各ステップには続行すべきかを示すコマンドがあります。

設定を編集する前に三つの役割を整理する

Nebula はすべてのホストに役割を与えますが、役割は継承されません。灯台は「このピアはどこか」という問いに答えて、ホスト同士が見つけられるようにします。中継は、使える直接経路がない二台のピア間でトラフィックを転送します。通常のホストはどちらも行いません。重要な三つの設定は三台の別々のマシンにあり、そのため完全に見えるクライアント設定でも両方のピアが待たされたままになります。

# on the discovery host
lighthouse:
  am_lighthouse: true
  hosts: []

# on every other host, point at the discovery host's Nebula address
lighthouse:
  hosts:
    - 10.42.0.1

一つの症状で二種類の障害を切り分けられます。両方のピアが互いを一覧に載せ、ログに決して完了しないハンドシェイクの試行が出ているなら、探索は機能しており、データ経路が NAT かファイアウォールで遮断されています。ピアが相手のアドレスをまったく学習しないなら、問題は灯台かその静的ホストマップにあり、中継の設定では修復できません。

ほかを編集する前に、各ホストが実際にどの設定を読み込んだかを確認してください。編集したファイルとは別のパスを読むユニットは、「変更しても何も起きない」のよくある恥ずかしい原因です:

systemctl cat nebula | grep ExecStart
nebula -version
journalctl -u nebula -n 20 --no-pager

混在した環境ではバージョンの行が重要です。中継の処理、パンチングの挙動、受け付ける設定キーはリリース間で同一ではないので、古いビルドが想定外のセクションを理解するとは考えないでください。ホストを同じマイナー系列に揃え、各ホストで nebula -test -config /etc/nebula/config.yml を実行し、コードが実装していないトランスポートをデバッグする前に出力を比較してください。

中継ホストで転送の役割を有効にする

中継の役割は転送ホスト上の一つのフラグで、灯台であることとは別です。探索が有効で中継転送が無効なホストは、依然として探索サービスにすぎません。中継側では:

relay:
  am_relay: true

このフラグは意図を宣言するだけで、到達性は作りません。中継は両方のピアから到達可能でなければならず、実際には公開アドレス、ホストのファイアウォールとクラウドのセキュリティグループで Nebula ポートの受信 UDP が開いていること、手前に二重 NAT がないことが必要です。待ち受けソケットを確認し、次にピアのパケットが届くのを観察します:

ss -lunp | grep nebula
tcpdump -ni any udp port 4242 and host 198.51.100.7

二つの制限が後で時間を節約します。中継は別の中継を経由して到達できないので、設定内の中継名の連鎖は転送ホストに使える経路を与える代わりにはなりません。また、中継はパケットを転送するだけで許可は与えないため、中継されたフローを許可するかどうかは宛先自身の受信ルールが決めます。役割と要件は中継の設定ドキュメントにまとめられています。

中継はクライアント側ではなく宛先側で広告する

これが最も時間を食う逆転です。中継リストは、そのホストへ到達するためにどのような中継を使ってよいかを他のピアに伝えるものなので、到達したいホスト側に置きます。接続を開始するクライアント側に置いても、クライアントが必要とする方向には何の効果もありません:

# on the DESTINATION host: relays others may use to reach me
relay:
  relays:
    - 10.42.0.5

ピアは灯台にチェックインするとき、自分のアドレスと一緒にそのリストを報告するので、広告は直接の交換ではなく探索を通じて広がります。宛先が灯台に到達できないと、その中継リストは決して広がらず、正しく設定された中継は遊休したままになります。その障害は壊れた中継とまったく同じに見えますが、壊れてはいません。

同じ手順で宛先にブートストラップ経路を与えます。静的ホストマップは Nebula アドレスを公開エンドポイントに結び付けて最初のハンドシェイクの行き先を作り、宛先が直接到達できないホストから中継トラフィックを受け入れるなら中継のためのエントリも必要です:

static_host_map:
  "10.42.0.5": ["203.0.113.10:4242"]

接続するピアに中継の使用を許可する

開始側にも独自の許可が要ります。relay.use_relays は既定で有効なので、探す価値があるのは明示的な false で、中継機能ができる前に書かれたテンプレートからコピーされたり、直接経路を強制しようとした以前の試みの残りだったりします:

relay:
  use_relays: true

この側の許可だけが関門ではありません。宛先の受信ルールは中継されたパケットにも適用され、照合される送信元は中継自身のアドレスではなくトンネル内の ID です。中継されたハンドシェイクが完了してもアプリケーションが失敗するときは、ルールを書き換える前に宛先のログで記録された送信元を読んでください。

NAT トラバーサルの設定はこれらすべてと相互作用します。punchy.punch は二台のピアが直接経路を開くのを助け、成功したときには観察できる中継トラフィックはなく、それは成功であって中継の欠落ではありません。断続的にしか動かないペアは、パンチングが時々成功しているだけのことが多いので、一回の試行で結論を出さず、そのペアを何度も試してください。

失敗したペアで検証してからリロードする

実際に失敗した二台のホストを、必要なアプリケーションで試してください。作業マシンから中継自身のサービスへの ping の成功は、他の二台のピア間の転送については何も証明せず、この手順全体で最もよくある誤検出です。

journalctl -u nebula -f | grep -iE "relay|handshake"
ping -c 3 10.42.0.9

再起動ではなくリロードし、実行中のプロセスが編集したファイルを取り込んだことを確認します。再起動はすべてのトンネルを切断し、次のテストを読みにくくしますが、SIGHUP はプロセスを維持したまま設定を読み直させます:

sudo systemctl kill -s HUP nebula
# same signal when nebula runs in the foreground
kill -HUP $(pidof nebula)

systemctl reload はローカルのユニットが ExecReload を定義しているときだけ機能し、プロジェクトに付属するユニットは定義していないので、標準インストールで成功するのは systemctl kill -s HUP のほうです。リロードの後、プロセスが開いた設定パスをジャーナルで読み、想定していたファイルではなく新しいファイルが読まれたことを確認してください。

中継の設定と NAT パンチングはどちらも経路に作用し、どちらも ID には作用しません。実行中の設定で am_relay、relays、use_relays が確認できた後もペアのハンドシェイクが失敗するなら、両方のホストが設定された名前に対して発行された証明書を提示していること、両側の CA が同じであることを確認してください。証明書の編集と中継の編集が同じテストに混ざるとどちらがハンドシェイクを動かしたのか分からなくなるので、証明書とファイアウォールの設定は固定したままトランスポートの設定を一つずつ変えてください。名前や CA の不一致は、決して見つからなかった中継と同じ場所で失敗するので、この逆確認も同じ手順に入ります。

最後に、各記述が属するファイルにあることを確認します:

  • 転送ホスト上の am_relay: true と、そのホストが両側から UDP で到達可能であること。
  • 宛先でそのホストの Nebula アドレスを指定する relays:。
  • 接続を開始するピア上の use_relays: true。
  • 経路が変わった後も、宛先の受信ルールが実際の送信元を許可していること。

関連記事