ネットワークオーバーレイ
アウトバウンドHTTPコール用のSOCKS5オーバーレイを構成し、継承、インバウンドリスナー、アプリケーション既定値、権限を確認します。
分類: 実行可能なSOCKS5チュートリアルと、部分的なTailscaleレシピです。 外部Torリスナーを利用できれば、direct/Torプローブは完全に実行できます。Tailscaleセクションは Wippy側の配線を説明しますが、アカウントのプロビジョニングはTailscale側に委ねます。 I2Pの設定は、後述のネットワークシステムリファレンスを参照してください。
概要
Wippyはオーバーレイネットワークをレジストリエントリとして表現します。コードは呼び出し用のオーバーレイを選択でき、 子孫が上書きするまでその選択がネストした呼び出しへ伝播します。
Wippyは3種類のオーバーレイエントリをサポートします:
network.socks5— 汎用SOCKS5プロキシ(TorのSOCKS5リスナーにも使用可)network.tailscale— tsnetオーバーレイノードnetwork.i2p— I2P SAM v3ブリッジ
前提条件
-
Wippyランタイム
v0.3.32a。 -
curlとapi.ipify.orgへのアウトバウンドHTTPSアクセス。 -
127.0.0.1:9050でSOCKS5を公開するTorデーモン。Tor Projectのダウンロードページから 対応パッケージをインストールして起動し、Wippyを実行する前にリスナーを確認します:curl --socks5-hostname 127.0.0.1:9050 https://api.ipify.org?format=json成功するとIPアドレスを含むJSONが返ります。Tor Browserは一般にポート9150を使用します。 意図してそのリスナーを使う場合は、レジストリエントリと検証コマンドを同時に変更してください。
-
空の作業ディレクトリ:
mkdir netdemo cd netdemo mkdir src
プロジェクト構造
netdemo/
├── wippy.lock
└── src/
├── _index.yaml
└── probe.lua
ステップ1: オーバーレイを定義する
src/_index.yamlを作成:
version: "1.0"
namespace: app
entries:
- name: net_policy
kind: security.policy
policy:
actions:
- http_client.request
- network.select
resources: "*"
effect: allow
- name: processes
kind: process.host
lifecycle:
auto_start: true
- name: terminal
kind: terminal.host
lifecycle:
auto_start: true
# SOCKS5 proxy entry (Tor exposes one at 127.0.0.1:9050 by default)
- name: tor
kind: network.socks5
host: 127.0.0.1
port: 9050
isolate_streams: true
- name: probe
kind: process.lua
meta:
command:
name: probe
short: Check outbound IP through overlays
security:
actor:
id: system.probe
policies:
- app:net_policy
source: file://probe.lua
method: main
modules:
- io
- http_client
- json
isolate_streams: trueを指定すると、SOCKS5ドライバーが接続ごとにランダムなクレデンシャルを生成し、Torが各ダイアルで新しいサーキットを開きます。
セキュリティはデフォルトでストリクトなため、コマンドは起動時に使用するアクターとポリシーを携えます。http_client.requestがアウトバウンドコールを、network.selectが明示的なオーバーレイ選択をカバーします。これらがないとすべてのチェックがフェイルクローズします。
ステップ2: アウトバウンドコールをルーティングする
src/probe.luaを作成:
local io = require("io")
local http_client = require("http_client")
local json = require("json")
local function fetch_ip(overlay)
local options = { timeout = "15s" }
if overlay then
options.overlay_network = overlay
end
local resp, err = http_client.get("https://api.ipify.org?format=json", options)
if err then
return nil, tostring(err)
end
if resp.status_code ~= 200 then
return nil, "HTTP " .. resp.status_code
end
local body = json.decode(resp.body or "")
return body and body.ip, nil
end
local function main()
local direct, d_err = fetch_ip(nil)
if d_err then
io.print("direct failed: " .. d_err)
else
io.print("direct IP: " .. direct)
end
local routed, r_err = fetch_ip("app:tor")
if r_err then
io.print("tor failed: " .. r_err)
else
io.print("tor IP: " .. routed)
end
return 0
end
return { main = main }
http_clientのoverlay_networkオプションは、その呼び出しのみにオーバーレイを適用します。指定しない場合、ダイアルはプロセスデフォルト(.wippy.yamlのnetwork_service.default_networkまたはダイレクト)を使用します。
ステップ3: 実行する
wippy init
wippy run probe
Torがローカルで動作している場合:
direct IP: <your public IP>
tor IP: <Tor exit IP>
Torが動作していない場合、tor IP行にダイアルエラーが報告されます — SOCKS5オーバーレイはダイレクト接続に静かにフォールバックしません。
継承
オーバーレイの選択はネストされた呼び出しを通じて伝播します。funcs.callまたはprocess.spawnの境界で一度オーバーレイを指定すれば、その下のすべての内部HTTPコール、ネストされたfuncs.call、およびprocess.spawnは明示的なオーバーライドがあるまでそれを使用します:
local funcs = require("funcs")
local result, err = funcs.new()
:with_options({ network = "app:tor" })
:call("app:scrape_site", url)
local pid, err = process.with_options({ network = "app:tor" })
:spawn_monitored("app.workers:probe", "app:processes")
ネストされた関数またはスポーンされたプロセスは、明示的に渡さなくても、すべてのアウトバウンドダイアルでオーバーレイを使用します。
リスナーのバインド
TailscaleはHTTPリスナーも受け付けられます。クライアントではなくhttp.serviceにオーバーレイを付与します:
- name: bind_policy
kind: security.policy
policy:
actions: "network.bind"
resources: "*"
effect: allow
- name: tailnet
kind: network.tailscale
hostname: wippy-node
auth_key: ${env:TS_AUTHKEY}
ephemeral: true
- name: gateway
kind: http.service
addr: ":8080"
network: app:tailnet
lifecycle:
auto_start: true
security:
actor:
id: system.gateway
policies:
- app:bind_policy
auth_keyはenv レジストリ経由で解決されるため、TS_AUTHKEYは登録済みの変数です。OSの値を使うにはenv.storage.osに紐づくenv.variableが必要です。
オーバーレイ経由のバインドはnetwork.bindで制御され、リスナーの起動時にチェックされます。そのためサービスはそれを許可するスコープを宣言します。サーバーはtailnetインターフェースにバインドし、クライアントはTailscaleアドレス経由でアクセスします。SOCKS5はアウトバウンド専用です — http.serviceに割り当てるとリスナーがinbound listeners are not exposed over SOCKS5で失敗します。
アプリ全体のデフォルト
.wippy.yamlにデフォルトオーバーレイを設定すると、オーバーライドされない限りすべての呼び出しで使用されます:
network_service:
state_dir: .wippy/net
default_network: app:tor
パーミッション
network.selectアクションが明示的なオーバーレイ選択を制御します。スコープで拒否するとコードがオーバーレイを選択できなくなります:
- name: deny_network
kind: security.policy
policy:
actions: "network.select"
resources: "*"
effect: deny
groups:
- untrusted
継承されたオーバーレイはこのチェックをバイパスします — 呼び出し元の境界で認可済みです。Lua境界での明示的な再選択のみが制御されます。
トラブルシューティングとクリーンアップ
127.0.0.1:9050でconnection refusedになる場合は、設定したポートでTorがリッスンしていません。 Wippyを調べる前に、前提条件のcurlコマンドでTorを確認してください。- 直接リクエストが失敗しルーティングしたリクエストが成功する場合、通常はローカルDNS、プロキシ、 ファイアウォールの規則が直接経路に影響しています。2つの呼び出しは独立しています。
- ルーティングした呼び出しの
access deniedは、コマンドのセキュリティコンテキストにapp:torへのnetwork.selectがないことを示します。meta.command.security配下にapp:probe_policyを付けたままにしてください。 - SOCKS5ドライバーは直接接続へフォールバックしません。デモを続行させるためだけにエラーを削除しないでください。
- Wippyコマンドが終了したら停止し、このチュートリアル専用にTorを起動した場合だけTorも停止してください。
SOCKS5の例は永続的なネットワーク状態を作りません。Tailscaleエントリは
.wippy/net/tailscale/にノード状態を 保存する場合があります。Wippyを停止し、ローカルのtailnet IDを破棄する意図がある場合だけ.wippy/netを削除してください。
次のステップ
- ネットワークシステム — エントリ種別リファレンス
- HTTPクライアント — 呼び出しごとのオーバーレイオプション
- セキュリティモデル — ポリシーとスコープ
- 認証 — トークンベースのセキュリティ