2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

localhostのWebアプリをどうやって外部へ共有するのか: ngrok、Clourflare Tunnel、h5i

2
Posted at

AI coding agentにWebアプリを作らせていると、作業の途中で誰かに触ってもらいたくなることがあります。

  • デザイナーに画面を確認してもらいたい
  • チームメンバーに動作を再現してもらいたい
  • 顧客に一時的なデモを見せたい
  • 別の端末から開発中のアプリを試したい

しかし、開発サーバーは通常、次のようなURLで動いています。

http://localhost:3000

このURLは、そのままでは自分のPCからしか開けません。そこでよく使われるのが、ngrokCloudflare Tunnelh5iです。これらは、ローカルで動くWebサーバーにインターネット経由で到達するための経路を作り、外部からアクセスできるURLを発行します。

では、そもそもlocalhostはなぜ外部から見えないのでしょうか。Tunnelとは何を「トンネル」しているのでしょうか。通信は暗号化されているのでしょうか。また各サービス・ツールはどのように違うのでしょうか。

この記事では、IP address、port、NAT、TLSといった用語から順番に説明し、最後にh5i box shareのP2P共有とQuick Tunnel共有の仕組みを見ていきます。

TL;DR: ngrokやCloudflare Tunnelは、ローカルPCからクラウドへoutbound接続を作り、その中を通してpublic URLへのリクエストをlocalhostへ転送します。h5i box shareはそれに加えて、対象を特定のsandboxとportに固定し、期限付きticket、失効、監査記録を提供します。デフォルトではh5i同士のE2E暗号化P2P接続を使い、普通のブラウザへURLを渡したい場合はCloudflare Quick Tunnelをtransportとして利用できます。

1. まず「Webアプリへアクセスする」とは何か

Clientとserver

Web通信には、主に次の2つの役割があります。

  • Client:Webページを要求する側。通常はChromeやFirefoxなどのbrowserです。
  • Server:要求を受け取り、HTMLやJSONなどを返す側。開発中のNode.js、Python、RustのWebアプリなどです。

browserがWebページを開くと、clientからserverへrequestが送られ、serverからclientへresponseが返ります。

Client  -- request -->  Server
Client  <-- response -- Server

たとえば、browserがGET /というrequestを送ると、serverはHTMLをresponseとして返します。フォームを送信するときには、POST /submitのようなrequestを送ることもあります。

このとき使われる代表的な約束事がHTTP(Hypertext Transfer Protocolです。Protocolとは、通信する双方が守るmessage形式や手順の決まりです。HTTPは「どのpageを要求するか」「serverが成功・失敗のどちらを返したか」「bodyにどのdataを入れるか」などを定めています。

HTTPSは、HTTPをTLSで保護したものです。TLSについてはQuick Tunnelの節で詳しく説明しますが、この段階では「通信内容を暗号化し、途中で読まれたり書き換えられたりする危険を減らす仕組み」と考えてください。

Network上のdataは、多くの場合、packetと呼ばれる小さな単位に分けて送られます。大きなWeb pageや画像も、実際には複数のpacketとして運ばれ、受信側で組み立てられます。

IP address:通信相手の場所

インターネット上で通信相手を特定するために使われる番号がIP addressです。住所に近い役割を持ちます。

203.0.113.10

実際には、人間がIP addressを覚えなくて済むように、example.comのようなdomain nameを使います。domain nameからIP addressを調べる仕組みがDNSです。DNSは、よく「インターネットの電話帳」にたとえられます。

URL:接続方法と場所をまとめた表記

Browserへ入力する次の文字列全体をURL(Uniform Resource Locatorと呼びます。

https://example.com:8443/demo
^^^^^   ^^^^^^^^^^^ ^^^^ ^^^^^
scheme      host     port path
  • Scheme:どのprotocolで接続するか。ここではhttps
  • Host:接続先のdomain nameまたはIP address
  • Port:host上のどのserviceへ接続するか
  • Path:server上のどのresourceを要求するか

HTTPSでは通常port 443、HTTPでは通常port 80が省略されています。開発serverでは30008000などを明示することがよくあります。

Port:同じマシン上のどのserverか

1台のPCでは、複数のnetwork serviceが同時に動きます。そのため、IP addressだけでなくport numberを使って、どのserviceへ接続するかを指定します。

127.0.0.1:3000
          ^^^^
          port number

たとえば、同じPCで次のように複数のserverを動かせます。

127.0.0.1:3000  開発中のWebアプリ
127.0.0.1:5432  PostgreSQL
127.0.0.1:8000  API server

IP addressが建物の住所なら、portは部屋番号のようなものです。

localhostとloopback

localhostは「このPC自身」を表す特別な名前です。IPv4では通常、127.0.0.1へ変換されます。

localhost:3000 == 127.0.0.1:3000

127.0.0.0/8、つまり127で始まるIP addressの範囲はloopback addressと呼ばれます。loopbackへの通信はネットワークケーブルやWi-Fiへ出ていかず、自分のPC内で折り返されます。

そのため、localhost:3000でのみ待ち受けているserverへ、別のPCから接続することはできません。相手のPCでlocalhost:3000を開いても、それはあなたのPCではなく、相手自身のPCを意味します。

2. なぜ自分のPCへインターネットから直接接続できないのか

Private IP addressとpublic IP address

家庭や大学、企業のnetworkに接続したPCには、多くの場合、次のようなprivate IP addressが割り当てられます。

192.168.1.20
10.0.0.15

Private IP addressは、同じlocal network内では使えますが、インターネット全体では一意ではありません。世界中の家庭で同じ192.168.1.20が使われています。

一方、インターネットから見えるrouter側のaddressがpublic IP addressです。

NAT:private addressをpublic addressへ変換する仕組み

家庭内の複数端末が1つのpublic IP addressを共有できるのは、routerがNAT(Network Address Translationを行うからです。

PCやスマホから外部へ通信すると、routerは例えば次の対応を一時的に記録します。

192.168.1.20:51000  <->  203.0.113.10:42001  # PC
192.168.1.21:51000  <->  203.0.113.10:42002  # スマホ

それぞれ次を意味します。

192.168.1.20:51000
├─ 192.168.1.20:家庭内にあるPCのprivate IP address
└─ 51000:そのPCが通信に使っているport

203.0.113.10:42001
├─ 203.0.113.10:routerのpublic IP address
└─ 42001:routerがInternet側に割り当てたport

外部serverから応答が戻ると、routerはこの対応表を見て、元のPCへ転送します。

しかし、外部から突然203.0.113.10:3000へ接続されても、routerには家庭内のどのPCへ渡すべきか分かりません。そのため、通常は通信を破棄します。

Firewall:許可していない通信を止める仕組み

Firewallは、通信の送信元、送信先、port、protocolなどを見て、通してよい通信と拒否する通信を判断します。

  • Inbound traffic:外部からPCやnetworkへ入ってくる通信
  • Outbound traffic:PCやnetworkから外部へ出ていく通信

一般的な家庭や企業のnetworkでは、outbound trafficは比較的許可され、外部から突然届くinbound trafficは厳しく制限されます。

Web閲覧はPC側から開始するoutbound通信なので通ります。一方、外部の人があなたの開発serverへ接続する通信はinboundなので、そのままでは止められます。

3. Port forwardingとdeployment

ローカルのserverを外部公開する古典的な方法の一つが、routerのport forwardingです。

たとえばrouterへ、次のような設定を入れます。

public IPのport 3000へ来た通信
    -> 192.168.1.20のport 3000へ転送

しかし、この方法にはいくつか問題があります。

  • routerの設定権限が必要
  • public IP addressが変わる可能性がある
  • ISPのCGNATにより、そもそも自分専用のpublic IPがない場合がある
  • firewall設定が必要
  • HTTPS certificateやdomainを自分で準備する必要がある
  • 間違ったportや別のserviceを公開する危険がある

ISP(Internet Service Providerは、家庭や組織をインターネットへ接続する事業者です。CGNAT(Carrier-Grade NATは、ISPがさらに上流で多数の利用者に1つまたは少数のpublic IP addressを共有させる仕組みです。家庭のrouterでport forwardingを設定しても、ISP側のNATを自分では設定できないため、外部から直接到達できない場合があります。

別の選択肢は、Vercel、AWS、Fly.ioなどへアプリをdeployすることです。Deploymentでは、codeやbuild成果物をcloudへ送り、cloud上でアプリを実行します。

Deployment:
code -> cloudへ転送 -> cloud上でアプリを実行

これは本番運用には適していますが、数分だけ開発中の画面を見せたい場合には大がかりです。そこで登場するのがtunnelです。

4. Tunnelとは何か

Tunnelとは、本来は直接届かない2地点の通信を、別の接続の中に通して運ぶ仕組みです。

ngrokやCloudflare Tunnelでは、ローカルPC上のagentがcloud serviceへoutbound接続を作ります。外部からpublic URLへ届いたrequestは、その既存接続の中を通ってlocalhostへ運ばれます。

この形は、ローカル側からcloudへ接続を開始するため、多くのNATやfirewallを通過できます。routerへinbound portを開ける必要もありません。

この用途のtunnelは、PC全体を別networkへ参加させるVPNよりも、public reverse proxyとlocalhostを結ぶ持続的な通信経路に近いものです。

Reverse proxyとは

Proxyは、通信を中継するものです。Clientの代わりに外部serverへ接続するものをforward proxy、serverの手前でclientからのrequestを受けるものをreverse proxyと呼びます。

ngrokやCloudflareはpublic URLでrequestを受け、対応するlocal tunnel agentへ転送します。この意味で、cloud側はreverse proxyとして動きます。

5. ngrokの仕組み

ngrokは、localhostで動くserviceをpublic endpointへ接続するdeveloper向けserviceです。基本的な使い方は次のようになります。

ngrok http 3000

すると、次のようなpublic URLが発行されます。

https://example.ngrok.app

通信は次の経路を通ります。

Visitor browser
    -> ngrok edge
    -> PC上のngrok agent
    -> localhost:3000

ここでedgeとは、利用者に近い場所に配置されたprovider側のserverです。VisitorはあなたのPCへ直接接続するのではなく、まずngrokのedgeへ接続します。

PC上のngrok agentは、あらかじめngrok cloudへ暗号化されたoutbound接続を作っています。Edgeが受けたrequestはこの接続の中を通り、agentがlocalhostへ渡します。ngrokの公式ドキュメントでは、この仕組みをsecure tunnelとして説明しています。

ngrokが便利なのは、単にURLを発行するだけではない点です。

  • HTTP requestの内容を確認する
  • requestを再送する
  • Basic AuthやOAuthを追加する
  • 固定domainを利用する
  • HTTP以外のTCP/TLS serviceを公開する
  • APIやSDKからendpointを管理する

一方、ngrokは汎用的なtunnelです。ngrok http 3000と指定したとき、port 3000を保持しているprocessが「目的のsandbox内のdev serverか」までは判断しません。意図せずホスト側の別serviceが待ち受けていれば、それを公開する可能性があります。

また、一般的なHTTP endpointではngrokがpublic HTTPS endpointとして通信を中継します。利用するTLS構成によってtrust modelは変わりますが、単に「URLがHTTPSだから、ngrokにも内容が見えない」とは限りません。通信経路上の盗聴を防ぐことと、中継serviceにも内容を見せないE2E暗号化は別の性質です。

6. Cloudflare TunnelとQuick Tunnel

Cloudflare Tunnelも、基本構造はngrokとよく似ています。ローカルでcloudflaredというdaemonを動かし、Cloudflare networkへoutbound接続を作ります。

Daemonとは、画面を直接操作しなくてもbackgroundで継続して動くprocessです。

通常のCloudflare Tunnelでは、Cloudflare accountやdomainを設定し、次のように固定hostnameをlocal serviceへ対応付けられます。

https://demo.example.com -> http://localhost:3000

Cloudflare Accessと組み合わせれば、組織accountによるloginやaccess policyも追加できます。

Quick Tunnel

開発・テスト向けに、accountや独自domainなしで使えるのがQuick Tunnelです。

cloudflared tunnel --url http://localhost:3000

実行すると、ランダムなtrycloudflare.com URLが発行されます。

https://random-words.trycloudflare.com

このURLへのrequestをCloudflareが受け、cloudflaredを通してlocalhost:3000へ転送します。公式ドキュメントによると、Quick Tunnelは開発・テスト用であり、本番service向けではありません。現在は同時に処理中のrequestが最大200、Server-Sent Events(SSE)は非対応という制限もあります。

Quick Tunnelは暗号化されているのか

通信経路は暗号化されていますが、browserからlocalhostまで1本の鍵で暗号化されるE2E暗号化ではありません。

Browser
  == HTTPS/TLS ==> Cloudflare
  == encrypted tunnel ==> cloudflared
  -- HTTP on loopback --> localhost:3000

**TLS(Transport Layer Security)**は、通信内容の盗聴や改ざんを防ぐprotocolです。URLがhttps://から始まる場合、通常はbrowserと接続先serverの間でTLSが使われます。

Quick Tunnelでは、browserとCloudflareの間はHTTPS/TLSで暗号化されます。Cloudflareとcloudflaredの間も、QUICまたはHTTP/2を使う暗号化されたtunnelです。Cloudflareはtunnel接続の暗号化仕様を公開しています。

しかし、Cloudflareは自社edgeでbrowser側のTLSを終端します。TLS terminationとは、暗号化された通信を受け取り、そこで復号することです。その後、Cloudflareは別の暗号化された接続でcloudflaredへ転送します。

したがって、経路の途中にいる第三者による盗聴からは守られますが、Cloudflareは技術的にはHTTP requestやresponseの内容を処理できます。

Transport encryption: あり
Cloudflareからも内容を隠すE2E encryption: なし

最後のcloudflared -> http://localhost:3000はHTTPですが、同じPCのloopback内で完結します。必要ならoriginをhttps://localhost:3000にすることもできます。ただし、それだけでbrowserからoriginまでCloudflareにも読めないE2E暗号化になるわけではありません。

7. AI agentのsandboxを共有するときに追加で考えること

ngrokやQuick Tunnelは便利ですが、AI coding agentが作業するsandboxを共有する場合には、単に「portへ到達できる」だけでは不十分です。

本当にsandboxのportなのか

ホストとsandboxの両方にport 3000が存在するかもしれません。

Host port 3000     古い開発server
Sandbox port 3000  今回共有したいWebアプリ

共有toolが接続先を取り違えると、sandboxではなくホスト上のserviceを公開してしまいます。

誰が、いつまでアクセスできるのか

一時的なデモなら、URLを知る人へ無期限に公開する必要はありません。

  • 1時間後に自動失効する
  • 人ごとにaccessを取り消す
  • share全体を停止する
  • 共有中であることを明示する

といったcontrolが必要です。

何が入ってきたのか

Sandboxのnetwork policyは通常、外向きの通信、つまりegressを制御します。しかしshareは外からsandboxへ入るingressです。

  • 誰向けのgrantが利用されたか
  • 直接接続かrelay経由か
  • 何connection開かれたか
  • 何byte転送されたか
  • expired/revoked ticketが拒否されたか

といった情報を、agent自身の自己申告ではなくhost側で記録できることが重要です。

8. h5i box shareの基本

h5iは、coding agent、workspace、shell、dependency、dev server、browserを1つのdisposable sandbox内で動かすlocal-firstのtoolです。

h5i box shareは、そのbox内で動いているWebアプリを外部の相手へ一時的に共有する機能です。

h5i box share my-box --port 3000 --expire 60m --label alex

デフォルトはP2P共有です。相手側もh5iを使い、発行されたticketで接続します。

h5i join -

Ticketをcommand line argumentへ直接書くとshell historyやprocess listingへ残る可能性があるため、stdinから渡す形が安全です。

pbpaste | h5i join -
# または
h5i join - < ticket.txt

普通のbrowserだけで開けるURLが必要な場合は、--tunnelを使います。

h5i box share my-box --port 3000 --tunnel

このmodeでは、h5iがcloudflaredを起動し、Cloudflare Quick TunnelのURLを発行します。

9. デフォルトのP2P共有はどう動くのか

P2Pとは

**P2P(Peer to Peer)**とは、利用者同士の端末が直接通信する構成です。Providerのserverがすべてのapplication dataを受け取るcloud tunnelとは異なり、direct pathが作れれば2つのh5i processが直接通信します。

ここで重要なのは、P2Pだからといって2台が同じ家庭、大学、LANにいるわけではないことです。たとえば、h5i box shareを実行するAliceはニューヨークの自宅、h5i joinを実行するBobは東京の大学networkにいても構いません。

その場合、AliceとBobはそれぞれ別のNATの内側にいます。2人が1つのNATを共有したり、P2Pが共通のNATを用意したりするわけではありません。

Aliceから見て、Bobのprivate addressである192.168.1.8には意味がありません。同じaddressがAliceの自宅network内でも使われているかもしれないからです。BobもAliceの10.0.0.5へ直接は接続できません。

したがって「P2PでNATを使う」というより、正確には次のように捉えるべきです。

各peerがそれぞれ別のNATやfirewallの内側にいるため、それらを越えてdirect pathを作る。

このためにNAT traversalを使います。

NAT traversalとhole punching

NAT traversalは、NATやfirewallの内側にいる端末同士が通信経路を作る技術の総称です。

具体例で追ってみましょう。Aliceのh5iは、localでは次のaddressでUDP socketを開いているとします。

AliceのPC内: 10.0.0.5:5000

AliceがInternet上のserverへpacketを送ると、Alice側のNATは送信元を書き換えます。

NAT変換前: 10.0.0.5:5000
NAT変換後: 203.0.113.10:41000

同様に、Bob側でも別のNAT変換が起きます。

NAT変換前: 192.168.1.8:6000
NAT変換後: 198.51.100.20:52000

外部から到達できる可能性があるのは、private addressではなく、NAT変換後のpublic endpointです。Endpointとは、ここではIP addressとport numberの組み合わせを意味します。

Aliceのpublic endpoint: 203.0.113.10:41000
Bobのpublic endpoint:   198.51.100.20:52000

ただし、AliceとBobは最初から相手のpublic endpointを知っているわけではありません。また、相手のendpointを知るだけでも不十分です。Bob側のNATは、Bobがまだ通信を始めていないAliceから突然packetを受け取ると、それを予期しないinbound trafficとして破棄する可能性があります。

そこで、両者が到達できるcoordination serverを待ち合わせ場所として使います。Coordination serverは、peer同士の接続に必要なaddress情報を交換するserverです。P2P libraryによっては、relay serverがこの役割も担います。

Coordination serverには、NAT変換後の送信元addressが見えます。そこで両者は、次の手順でdirect connectionを試します。

  1. AliceとBobが、それぞれcoordination serverへoutbound packetを送る
  2. Serverが観測した両者のpublic endpointを交換する
  3. AliceとBobが、ほぼ同時に互いのpublic endpointへUDP packetを送る
  4. 両側のNATに相手との通信を許す一時的なmappingができれば、direct pathが成立する

両者がほぼ同時に互いへ送信する方法は、一般にhole punchingと呼ばれます。

「hole」といっても、firewallを破壊するわけではありません。両端末が自分から通信を開始し、その相手とのpacketを受け取れる一時的なmappingをNATに作らせる、という意味です。

AliceのPC -> Alice側NAT -> Internet -> Bob側NAT -> BobのPC
             ^                          ^
             Alice側のmapping           Bob側のmapping

Direct pathが成立した後、application dataは通常coordination serverを通らず、AliceとBobの間を直接流れます。Coordination serverは接続相手を発見するための待ち合わせ場所であり、常に通信内容を中継するserverとは限りません。

AliceとBobが偶然同じLANにいる場合は、private address同士で直接つながることもあります。P2P transportは通常、同一LANのaddress、IPv6の直接address、NAT traversalで発見したpublic endpoint、relayなど複数の候補を試し、利用可能な経路を選びます。しかし、同じLANにいることはP2Pの前提ではありません。

この仕組みはnetwork環境によって成功しないこともあります。企業firewall、mobile network、symmetric NAT、CGNATなどでは、外部から予測可能なmappingを作れない場合があります。

Symmetric NATは、同じlocal endpointからの通信でも、接続相手ごとに異なる外向きportを割り当てるNATの一種です。第三者serverが観測したmappingをそのまま別peerとのdirect接続へ使いにくいため、hole punchingが難しくなります。

QUICとは

Direct pathが見つかると、h5i同士はQUICで通信します。

QUICはUDPの上で動くtransport protocolです。UDPはpacketを相手へ送るための比較的単純なprotocolで、TCPのような接続管理や再送を単体では提供しません。QUICはUDPを土台にしながら、次の機能を提供します。

  • TLS 1.3による暗号化と相手の認証
  • packetが失われた場合の再送
  • 複数streamの同時利用
  • 少ない往復での接続確立
  • network経路が変化した場合のconnection migration

QUIC自体の標準はRFC 9000で定義されています。h5iのP2P transportでは、NAT traversal、relay fallback、暗号化されたQUIC接続が組み合わされています。

E2E暗号化とは

E2E(End-to-End)暗号化では、通信の両端だけが内容を復号できます。

h5i share == encrypted QUIC == h5i join

中継serverを通る場合でも、暗号化と復号を行うのはh5i同士です。これはCloudflare Quick Tunnelとの重要な違いです。Quick TunnelではCloudflareがTLSを終端しますが、P2P modeではrelayを含む中間者はapplication dataを復号できません。

10. Relayとは何か

Direct P2P接続を作れない場合、relay serverが暗号化されたpacketを中継します。

Relayは郵便物を運ぶ配送拠点に近い役割です。封筒の宛先を見て運ぶ必要はありますが、中身はE2E暗号化されているため読めません。

Relayが確認できるmetadataには、概ね次のものがあります。

  • 両endpointのIP address
  • 接続時刻と通信時間
  • packetのtiming
  • 転送量

一方、適切なE2E暗号化が維持されている限り、次の内容は確認できません。

  • 表示しているHTML
  • HTTP request/response body
  • フォーム入力
  • Cookie
  • WebSocket内のapplication data

Metadataとは、「通信内容そのものではないが、通信について分かる情報」です。内容が暗号化されていても、誰がいつどれくらい通信したかは別問題として残ります。

--direct-onlyを付けると、relayが必要な相手を拒否できます。

h5i box share my-box --port 3000 --direct-only

これは「できればdirect」という曖昧な指定ではありません。Share開始時にdirect pathを作れなければapplication dataを送らず、接続後に経路がrelayへ変わった場合も検出して停止します。

その代わり、厳しいNATや企業network内の相手は接続できない可能性があります。Privacyを強くするほどavailabilityが下がるtrade-offです。

11. P2P接続をbrowserでどう表示するのか

Browserはh5i独自のendpoint IDへQUIC接続できません。そのため、h5i joinは相手のPC上にlocal proxyを立て、browserにはloopback URLを渡します。

Browser
  -> http://127.x.x.x:random-port
  -> h5i join
  -> encrypted P2P connection
  -> h5i box share
  -> box内のlocalhost:3000

Linuxでは、利用可能であれば127.0.0.1ではなくshare専用のランダムな127.x.x.xを使います。Cookieはportではなくhostを基準に共有されることがあるため、別のloopback addressを使うことで、他のlocalhost serviceとのcookie jarを分離しやすくなります。

Joinする側の注意点

P2P modeで開くページは、browserから見るとpublic websiteではなくloopback上のpageです。一部のbrowser protectionは、public originからlocal networkへのrequestを重点的に制限しています。そのため、悪意ある共有pageが相手PC上の別のlocalhost serviceへrequestを送る点では、public originで開くQuick Tunnelより不利な場合があります。

Same-Origin PolicyやCORSによってresponseを読めなくても、requestそのものを送るだけで状態が変わるCSRF脆弱性がlocal serviceにあれば、攻撃が成立する可能性があります。BrowserのLocal Network Accessはこの種の問題を扱っていますが、loopbackから別のloopbackへの制限はpublicからloopbackへの制限と同一ではありません。

ここでいうoriginは、scheme、host、portの組み合わせです。たとえば、http://127.0.0.1:3000http://127.0.0.1:8000はportが異なるため別originです。

  • Same-Origin Policy:あるoriginのpageが、別originのresponseを自由に読み取れないようにするbrowserの基本policy
  • CORS(Cross-Origin Resource Sharing):別originからのresponse読み取りをserverが明示的に許可する仕組み
  • CSRF(Cross-Site Request Forgery):responseを読めなくても、被害者のbrowserから意図しないrequestを送らせ、状態変更を起こす攻撃

CORSは主にresponseの読み取りを制御します。そのため、認証やCSRF対策なしで「requestが届くだけで危険な操作を実行する」localhost APIは、CORSだけでは十分に守れません。

Private windowや専用browser profileは、Cookie、history、localStorageなどを日常用profileから分離する助けになります。ただし、同じhost上で動く限り、localhostへのnetwork reachabilityまでは分離しません。信頼できないpageを強く隔離したい場合は、VMやsandbox内のbrowserを使う方が安全です。

12. --tunnel modeは何を追加するのか

相手がh5iをinstallしていない場合、P2P ticketだけを渡してもbrowserでは開けません。そこで--tunnel modeではCloudflare Quick Tunnelをtransportとして利用します。

h5i box share my-box --port 3000 --tunnel

通信経路は概ね次のようになります。

Visitor browser
  -> Cloudflare Quick Tunnel
  -> cloudflared
  -> h5iのshare gate
  -> box内のlocalhost:3000

ここでCloudflare Quick Tunnelは、public URLを発行してrequestを運ぶtransportです。その下にあるh5iのauthorizationやreceiptは維持されます。

  • Linkには期限付きtokenが含まれる
  • Tokenはh5iのgrant tableに対して検証される
  • Grantをrevokeするとlive connectionも切断される
  • Tokenはbox内のWebアプリへ渡されない
  • Ingressの結果がh5i receiptへ記録される

一方、CloudflareがTLSを終端するため、この経路はE2E暗号化ではありません。h5iはその事実もreceiptへ記録します。またcloudflaredはh5iに同梱・version pinされていないため、別途installが必要です。

13. h5iがtunnelの下に追加するsecurity control

1. Boxのportだけを共有する

h5iは単純にhostのport 3000を外部公開しません。

Linuxでは、boxのnetwork namespaceへ入り、その中の127.0.0.1:3000へ接続します。Network namespaceとは、network interface、routing table、port空間などをprocess groupごとに分離するLinux kernel機能です。

そのため、box内のport 3000とhostのport 3000を別物として扱えます。Box独自のnetwork namespaceがなく、接続先を安全に区別できない場合、h5iは推測で公開せず拒否します。

macOSにはLinuxと同じnetwork namespaceがないため、h5iはportを保持しているprocessを調べ、そのprocessがbox sessionまたはその子processに属する場合だけ共有します。別のhost processがportを保持していれば拒否します。

2. Ticketをcapabilityとして扱う

P2P共有で発行されるticketは、次の情報を結び付けます。

  • どのboxか
  • どのportか
  • どのgrantか
  • いつ失効するか
  • 256-bit secret

このように、「持っていること自体が特定操作の権限になるtoken」をcapabilityと呼びます。

h5iはsecretそのものではなくSHA-256 digestを保存します。Hashは、入力から固定長の値を計算する一方向関数です。元のsecretをそのまま保存せず、提示されたsecretをhashして一致を確認できます。

Ticketは人のidentityへ自動的に固定されるものではありません。Ticketを転送すれば、受け取った別の人も同じgrantを使えます。そのため、ticketはpasswordと同様に扱う必要があります。

3. Expiryとrevocation

Shareには有効期限があります。デフォルトは1時間、最大24時間です。

h5i box share my-box --port 3000 --expire 30m --label alex

Grantを個別に失効できます。

h5i box share revoke my-box <grant-id>

Share全体を停止する場合は次を使います。

h5i box share stop my-box

Authorizationはconnectionごとに再確認され、revokeされた既存connectionもwatchdogによって短時間で閉じられます。Watchdogとは、別processやconnectionの状態を定期的に監視し、異常やpolicy変更を検出したときに処理を止める仕組みです。

4. Credentialを共有アプリへ渡さない

Tunnel modeでは、最初のURLにshare tokenが含まれます。h5iは最初のrequestを受けるとtokenをHttpOnly cookieへ移し、tokenを除いたURLへredirectします。

  • Address barにtokenを残さない
  • 外部linkへ移動するときのRefererへtokenを載せない
  • Box内のWebアプリのlogへtokenを渡さない
  • Share credential用cookieをboxへ転送しない

HttpOnly cookieは、browserのJavaScriptから直接読み取れないように指定されたcookieです。Refererは、どのpageから移動してきたかをrequest先へ伝えるHTTP headerです。

ただし、linkそのものを持つ人が認可されるcapability URLである点は変わりません。URLがchat logやscreenshotへ漏れれば、失効するまで他人に使われる可能性があります。

5. Connection数を制限する

Shareからboxへ同時に入れるconnection数には上限があります。これは、1人のvisitorや悪意あるpageが無制限にsocketを開き、boxのresourceを使い切ることを防ぐためです。

Resource exhaustionとは、CPU、memory、file descriptor、connection数など有限のresourceを大量消費させ、serviceを利用不能にする攻撃や障害です。

6. Ingress receiptを残す

Share終了後、h5iのreceiptには次のような情報が記録されます。

share session, 612s (p2p transport)
shared   port 3000 inside the box
peers    1
  ... via direct — grant ... (alex), 12 connections
refused  expired / revoked / unknown ticket attempts

Receiptは、「何が起きたか」を後から確認できる記録です。h5iでは、agentが生成した説明ではなく、shareのbridgeを所有するhost側が観測した情報として保存します。

これにより、最終的なpatchやexecution logと一緒に、「このboxが誰かへ共有されていたか」「どのtransportを使ったか」「何が拒否されたか」を確認できます。

14. 3つの方式を比較する

項目 ngrok Cloudflare Quick Tunnel h5i P2P h5i --tunnel
主な対象 任意のlocal service 一時的なlocal Web server h5i box内のWebアプリ h5i box内のWebアプリ
相手側のsoftware Browser Browser h5i Browser
Account 基本的に必要 不要 不要 不要
経路 ngrok cloud経由 Cloudflare経由 Direct、失敗時relay Cloudflare経由
Providerから内容を隠すE2E暗号化 構成による なし あり なし
NAT traversal 不要 不要 Direct path作成のため使用 不要
期限付きbox grant h5iの機能ではない Quick Tunnel単体にはない あり あり
個別失効 ngrok policyによる Quick Tunnel単体では限定的 あり あり
Box/host portの識別 しない しない h5iが確認 h5iが確認
h5i receipt なし なし あり あり
本番利用 対応可能 非推奨 非推奨 非推奨

ngrokやCloudflare Tunnelは汎用的なnetwork ingressです。Webhook受信、固定domain、高度なaccess policy、本番serviceなど、h5iより広い用途を扱えます。

h5i box shareの強みは汎用性ではなく、AI agentが作業している特定boxの特定portを、一時的・失効可能・監査可能な形で共有することです。

15. どのmodeを使うべきか

h5i利用者同士で、providerに内容を見せたくない

デフォルトのP2P modeが向いています。

h5i box share my-box --port 3000

Direct pathが作れない場合でもrelay経由で利用できます。Relayはmetadataを確認できますが、E2E暗号化された内容は復号できません。

Relayも使いたくない

h5i box share my-box --port 3000 --direct-only

ただし、企業、大学、mobile networkなどでは接続できない相手が増えます。

顧客やデザイナーへ普通のURLを送りたい

h5i box share my-box --port 3000 --tunnel

相手はh5iをinstallする必要がありません。ただしCloudflareを通信経路上のtrusted partyとして受け入れる必要があります。

Webhook、固定domain、本番serviceを公開したい

ngrokや通常のCloudflare Tunnelの方が適しています。h5i box shareは、動作中のsandbox内Webアプリを短時間共有するための機能であり、本番deployment platformではありません。

まとめ

localhostのWebアプリが外部から見えないのは、loopbackが自分のPC内だけを指し、さらにNATとfirewallが予期しないinbound通信を止めるからです。

ngrokとCloudflare Tunnelは、local agentからcloudへoutbound接続を作り、public URLへ届いたrequestをその中に通すことで、この問題を解決します。Quick Tunnelなら、accountやdomainなしで一時的なHTTPS URLを発行できます。ただしCloudflareがTLSを終端するため、通信経路は暗号化されていてもE2E暗号化ではありません。

h5i box shareは、共有経路だけでなく、その手前と奥にあるsecurity boundaryも扱います。

  • 本当に対象boxが保持するportか確認する
  • Box、port、有効期限をticketへ固定する
  • Grantを途中で失効する
  • P2P modeではQUICによるE2E暗号化を使う
  • Direct接続できなければ暗号化されたままrelayする
  • Browser URLが必要ならQuick Tunnelをtransportとして使う
  • 共有中に何が起きたかをreceiptへ残す

つまり、ngrokやCloudflare Tunnelが「localhostへ通信を届ける」ための汎用toolなのに対し、h5i box shareは「AI agentが動くsandbox内のdev serverを、boundaryを保ったまま一時共有する」ための機能です。

参考資料

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?