Rayfish: IrohベースのP2P VPN
(rayfish.xyz)- Rayfishは、サーバーやアカウントなしでユーザーのデバイス同士の間にP2PメッシュVPNを作る最初のリリースで、ネットワーク状態をメンバーが保持する署名済みレコードとして維持する
- 中央のコントロールプレーンをなくすことで、企業アカウント、運営者、常時稼働が必要なコーディネーションサーバーなしでも、参加後はネットワークが継続して動作する
- Iroh v1が、暗号化されたQUIC接続、NAT越え、ホールパンチング、直接経路がない場合のリレーフォールバックを担い、リリースの土台となった
- 現在は、複数の隔離ネットワークへの同時参加、招待コード・承認ベースの参加、Magic DNS、デバイスごとのファイアウォール、宣言的フリートプロビジョニング、1:1接続、ファイル転送を提供している
- Tailscaleより成熟度やスループットで不利な可能性があり、SDKでもないが、中央当事者のいないプライベートネットワークが必要な場合に焦点を当てている
Rayfishが解決しようとしている問題
- Rayfishは、デバイス間にプライベートネットワークを作るP2PメッシュVPNである
- 中継サーバーや、継続運用を信頼しなければならない企業なしで動作する
- ユーザーは鍵を受け取り、デバイスはその鍵で互いを見つけ、作られたネットワークはその中の人たちのものになる
中央コントロールプレーンをなくした設計
- 多くの現代的なVPNは、企業が運営するコントロールプレーンに依存している
- 企業がネットワークメンバーとポリシーを決める
- デバイスが通信している間も、企業がフローの中に残る
- 企業が停止したり、値上げしたり、顧客関係を打ち切ったりすると、プライベートネットワークを完全にユーザー所有のままにするのが難しい
- Rayfishはアカウントと中央運営者を取り除く
- ネットワーク状態は、どのメンバーでも他のメンバーへ渡せる署名済みレコードである
- Rayfishの開発元が消えても、既存ネットワークは動き続ける
1台のデバイスで複数ネットワークを扱う方法
- Rayfishは、ユーザーが1つのフラットなネットワークだけを使うわけではないという前提から出発している
- 1台のデバイスが、友人用、サイドプロジェクト用、業務用のネットワークに同時に所属できる
- 実装は、1つのプロセスと1つの仮想インターフェースの背後で動作する
- 各ネットワークは互いに分離され、ユーザーは参加したすべてのネットワークの正式メンバーになる
Iroh v1が可能にしたリリース
- Rayfishは、トランスポート層の難しい部分をIrohに任せている
- ピア間の暗号化されたQUIC接続
- NAT越え
- ホールパンチング
- 直接経路がない場合のリレーフォールバック
- Iroh v1は、ビルド中にトランスポート層が揺らがないだけの安定性を提供し、今回のリリースを可能にした
- 次の課題は、初期ツール群を本番環境で気軽に実行できるツールへ磨き上げることだ
Field Technologiesからのスピンオフ
- Rayfishは、高頻度取引会社Field Technologiesから分離したプロジェクトである
- 社内で分散システムを扱い、デバイス探索、共有状態の合意、高速でプライベートな通信を何度も構築してきた経験が土台になっている
- こうした社内インフラは主に、他社のコントロールプレーンに依存しないために作られてきた
- Rayfishは4年以上続いてきたアイデアだったが、中央運営者がいないという性質上、明確な課金ポイントがなく、製品化が遅れた
- 将来的にはフリート管理や監査のような企業向け課題へ広げられる可能性はあるが、そうした機能はまだ存在しない
現在提供している機能
-
ピアツーピア構造
- Rayfishはハブアンドスポークではなく、ピアツーピア構造である
- すべてのメンバーが他のすべてのメンバーと直接接続される
- メンバーシップはサーバーに問い合わせる値ではなく、各メンバーが持ち運ぶ署名済みレコードである
- ネットワークを作ったコーディネーターは、新しいメンバーを受け入れるときだけ必要で、参加後は中央の誰かなしにネットワークが動作する
-
クローズドネットワークと参加方式
- ネットワークは基本的にクローズドである
- コーディネーターは2つの方法で新しいメンバーを追加できる
- 使い捨て招待コード
- 参加リクエストに対するリアルタイム承認
- 公開ネットワークが必要なら、
ray create --openでゲートを開けられ、この場合はroom idだけで参加できる - 参加したメンバーには、安定したIPと分かりやすいMagic DNS名でアクセスできる
- デフォルトのホスト名は
ray up --hostname darioのように設定でき、ネットワークごとに--hostnameで上書きできる
ray up --hostname dario ray create --name prod ray invite prod --hostname web ray join <code> -
デバイスごとのファイアウォール
- 各デバイスは自分のファイアウォールを持ち、何を誰から受け取るかを自分で決める
- コーディネーターはネットワークごとの推奨ファイアウォールルールを公開できる
- 各ホストはルールを受け入れるか、自動インストールを選べる
- 全員が必ず従わなければならない中央ポリシーサーバーなしで、共有デフォルトを提供する方式である
-
フリートプロビジョニング
- 複数台のデバイスは宣言的ファイルでネットワークとファイアウォールルールを定義・適用できる
- 仕様は
networks:マップで、各ネットワーク配下にホストごとの許可ピアとポートを書く
networks: prod: web: allows: "*": "tcp:443" db: allows: web: "tcp:5432" game: "*": allows: "*": "tcp:6969"ray apply deploy.yaml --dry-run ray apply deploy.yaml --invite-missing- フリート向けには、ボックスごとにコードを発行せず再利用可能キーを作成できる
ray invite prod --reusable ray join <key> --hostname web01 --auto-accept-firewall- キーを失効させても新規参加だけが止まり、すでに参加済みのサーバーは影響を受けない
ray applyは冪等に動作し、仕様ファイルがフリートの基準状態として残る
-
1:1接続とファイル転送
- 別途ネットワークを作る必要がない場合は、
ray connectで連絡先IDベースの1:1接続を作れる
ray contact id ray connect <their-contact-id> ray connections approve <id>- 承認されると、プライベートな2ピアネットワークが自動作成される
- Magic DNS、ファイアウォール、メッシュ機能は同じように動作し、
ray statusには[direct]と表示される - 連絡先IDはローテーションでき、すでに作られた接続はそのまま動き続ける
- メンバー間には認証済みかつ暗号化された経路がすでにあるため、別サービスなしでファイルを送れる
ray send ./build.tar.gz web01 ray files accept 1 - 別途ネットワークを作る必要がない場合は、
Tailscaleとの違い
- TailscaleとRayfishはどちらも、安定したIP、Magic DNS、NAT越え、P2Pデータプレーンを提供する
- 最大の違いは制御の置き場所である
- TailscaleはTailscaleのコーディネーションサーバー、またはself-hostedなHeadscaleのようなコントロールプレーンを使う
- Rayfishは中央コントロールプレーンを持たず、ネットワーク状態をP2Pで伝播する署名済みレコードとして持つ
- データプレーンも異なる
- Tailscaleはカーネルで動くWireGuardを使う
- Rayfishはユーザー空間のIroh/QUICデータグラムを使う
- 速度では通常Tailscaleが有利である
- WireGuardはカーネルで動くため、高速リンクで大容量転送する際には差がはっきり出ることがある
- Rayfishは、中央当事者のいない構造とわずかな生スループットを引き換えにしている
- RayfishはアカウントやSSOではなく、ディスク上の鍵ペアをアイデンティティとして使う
- Rayfishは若く最小機能志向で、Tailscaleは成熟しており機能の幅が広い
Yggdrasilとの違い
- Yggdrasilは、中央権限のないエンドツーエンド暗号化IPv6ネットワークを提供する
- ユーザーが直接扱う最後の段階で違いがある
- Yggdrasilは1つのグローバルネットワークとIPv6アドレスを提供する
- Rayfishは、ユーザーが作成してメンバーを追加する個別のプライベートネットワークを提供する
- Rayfishは、招待コード、分かりやすいDNS名、ネットワークごとのファイアウォール、参加承認プロンプトのような利便機能を含む
- Yggdrasilはネットワーク知識のあるエンジニア向けで、Rayfishは少人数で使うプライベートネットワークを簡単に作りたいユーザーまで対象にしている
できることとできないこと
- Rayfishはライブラリではなく完成したツールである
- アプリケーション内にSDKのように埋め込むことはできない
- Rayfishがインストールされたデバイス同士でプライベートに通信する用途に向いている
- 友人同士が名前で接続するP2Pゲームサーバー
- 公開インターネットに触れない内部API
- バックアップ先
- ネットワークメンバー間のチャットや通話アプリ
- 互いに見えてはいけない2つのチームが共有するデータベース
- アプリケーションは
name.network.rayでピアにアクセスできる - Rayfishがインストールされていない外部者はネットワークにアクセスできず、外部者向けのゲートウェイもない
実際の構成例
-
2つの部門がデータベースを共有する場合
paymentsとanalyticsを別ネットワークにすれば、2つの部門は互いを見ることができない- データベース用ボックスだけを両方のネットワークに参加させる
- 各ネットワークで必要なポートだけを開けるよう、推奨ファイアウォールを設定する
networks: payments: db: allows: "*": "tcp:8123,tcp:9000" analytics: db: allows: "*": "tcp:8123,tcp:9000"- 各チームは
db.payments.rayまたはdb.analytics.rayでアクセスする - 2つのネットワークは互いの存在を知らず、中央ACL監査なしでネットワーク分離と共有ホストによるアクセス制御が構成される
-
友人向けMinecraftサーバー
- クローズドネットワークを作って友人を招待すれば、IPアドレス、ポートフォワーディング、動的DNSなしで名前で接続できる
ray create --name mc ray invite mc- クライアントは
mc-host.mc.ray:25565を宛先に接続する
-
全員がポートを開くクローズドグループ
- すべてのメンバーがTCP 6969を開く必要がある場合、管理者は1回だけルールを提案できる
ray firewall suggest mc --subject '*' --allow '*:tcp:6969'- 各メンバーは提案されたルールを確認し、受け入れるか拒否する
ray firewall pending mc ray firewall accept mc- ファイアウォール提案は強制ルールではなく、メンバーが望まなければ拒否できる
セキュリティとアドレス処理
- IPアドレスはサーバーが割り当てるのではなく、各ピアの暗号学的アイデンティティから導出される
- アドレス衝突はほとんどないが、衝突した場合はコーディネーターが2番目のピアを次の空きスロットに配置する
- すべてのピアは中央アロケーターなしで同じアドレス割り当てに決定的に収束する
- Rayfishは中央ACLの代わりに、2つの仕組みでセキュリティを構成する
- 同じネットワークを共有したピア同士だけが通信できる分離
- 各デバイスのネットワークごとのファイアウォール
prodとdevを分離したければ、2つのネットワークに分ければよい- 複数ネットワークに所属するホストは、各ネットワークごとに自分のファイアウォールを持つ
サーバーやポート開放は必要か
- 別途コントロールサーバーをホスティングする必要はない
- ネットワークを作ったコーディネーターは、すべてのメンバーが参加した後ならオフラインでもよい
- IrohがNAT越えとホールパンチングを処理し、直接経路が不可能なら暗号化されたリレーフォールバックを使う
- ルーターを制御でき、特定のボックスに保証された直接経路が欲しいなら、Rayfishの固定UDPポートをフォワードできるが、必須ではない
今後の計画と向かないケース
- 現在はデスクトップとサーバー向けにコマンドラインツールとして提供されている
- 次の作業は、ユーザーが実際に持ち歩くデバイスへ広げることである
- Androidクライアント
- iOSクライアント
- きちんとしたデスクトップアプリ
- 同時に、本番環境で気軽に使えるよう既存機能を堅牢化する作業も進めている
- すでにTailscale、企業VPN、自前構築のWireGuardメッシュがうまく動いているなら、Rayfishへ切り替える理由はそれほど大きくない
- Rayfishはより若く、機能も少なく、既存ツールのほうがより滑らかに提供している機能も多い
- Rayfishが取り除くものは1つである
- アカウント
- ネットワークを停止できる企業
- 常時稼働が必要なコントロールサーバー
- 他人が運営するポリシーデータベース
始め方
- インストールは1行コマンドで始められる
curl -fsSL https://rayfish.xyz/install.sh | sh
- 最初のネットワーク作成と招待手順はドキュメントで続けて案内されている
1件のコメント
Lobste.rsの意見
Rayfishが最初から複数のネットワークを同時に扱うことを前提にしている点が本当に良い。
Tailscaleではこれが優先事項ではないようなので、かなり大きな利点になり得る。
高頻度取引会社からスピンアウトしたというくだりは意外だったし、Tailscaleが速度で勝つ理由として「WireGuardのデータプレーンがカーネルで動く」と言うのは間違い。TailscaleはカーネルWireGuardを使っていないのに、人々はずっとそう誤解している。
「アイデア、製品、文章は自分のもの」と言っていたが、リポジトリに
CLAUDE.mdがあるのは残念。ただ、コントリビューションの概要を見ると、clodは1,017++ / 383--行で、作者は104,786++ / 47,064--行なので、コードの大半がAIで雑に作られたわけではなさそう。それでも作業量の1/100のために、リポジトリにそういう痕跡を残す理由があったのかは分からない。これまでIrohは、かわいいけれどあまり役に立たない形で使われるのを主に見てきたが、これこそそのアプリなのかもしれない。
これもTailscaleでは優先事項ではないようだ。
製品自体は本当に格好よく見えるし、自分のTailscaleメッシュを置き換えるのを妨げている唯一の点はAndroidクライアントがないこと。
ただ、文章は少なくとも一部がAIで書かれたように読めて、少し気になった。
それに、nightlyタグでデバッグAPKビルドも配布しているようだ: https://github.com/rayfish/rayfish/…
私もこの記事を5分前に見たところなので、実際に動くかどうかはそのリンク以上のことは分からない。
Yggdrasilとの比較が薄く、事実関係も間違っている。Yggdrasilのテストネットがグローバルネットワークとして運用されてはいるが、それはYggdrasilのトポロジー構造から便宜的に生じた結果にすぎない。
互いに分離した2つの鍵集合の間のどんな接続も動的に受け入れられるし、分離したYggdrasilネットワーク2つと大きなネットワーク1つの違いは、互いにメッセージをやり取りしているかどうかだけ。誰でも非公開のYggdrasilネットワークを作れる。
鍵は暗号学的に推測不可能だが、それが隠しサービスまで意味するとは思わない。ドメイン名は任意だが、多くの人がグローバルDNSでYggdrasilのアドレス空間を指すこともある。
「友人3人とゲームサーバー用のネットワークだけが欲しいなら、Yggdrasilは組み立てるものが多い」という見方なら、友人3人とゲームサーバーの標準に近いtincとの比較がないほうがむしろ驚きだ。
別の非公開ネットワークを作って人を受け入れるという概念がなく、招待コード、分かりやすいDNS名、ネットワークごとのファイアウォール、誰かが入ろうとしたときの承認プロンプトのような便利機能もない、という説明はその部分のことだ。ネットワークエンジニアなら問題ではないかもしれない。
Yggdrasilとの比較で抜けているもう1つの点は、RayfishがIrohのNATホールパンチングを活用していて、どちらのピアも公開アクセス可能でなくても2つのピアが通信できることだ。