DPIを回避する:WSTunnelを使用してWireGuardをWebSockets経由でトンネリングする方法

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

制限されたネットワークにおけるUDPの問題

WireGuardは非常に高速ですが、企業環境においては致命的な弱点があります。それはUDPのみを使用するという点です。多くの企業のファイアウォールやDeep Packet Inspection (DPI) システムは、UDPトラフィックに対して「すべて拒否(deny-all)」ポリシーを設定しています。不正なトンネリングを防ぐため、ポート53のDNSクエリ以外のすべてをブロックすることがよくあります。

オフィスのWi-FiでVPNステータスが「Handshake did not complete(ハンドシェイクが完了しませんでした)」のまま止まったことがあるなら、この壁に突き当たっています。厳しいPalo Alto Networksのファイアウォールがあるクライアント先でのテストでは、標準のWireGuardは100%失敗しました。これを回避するには、トラフィックをファイアウォールがすでに信頼しているもの、つまり標準的なHTTPSウェブトラフィックに偽装する必要があります。

WireGuardのUDPパケットをWebSocketストリームの中にラップすることで、ファイアウォールを欺くことができます。WebSocketは標準的なHTTP/1.1またはHTTP/2のハンドシェイクを介して開始されるため、トラフィックはSlack、Discord、またはトレーディングプラットフォームへの通常のブラウザ接続のように見えます。

WSTunnelのアーキテクチャを理解する

重い処理を担当させるために、Rustで書かれた高性能リレーであるWSTunnelを使用します。サーバーのUDPポートに直接接続する代わりに、クライアントはトラフィックをWebSocketに変換するローカルの「ブリッジ」と通信します。フローは以下の通りです:

  • クライアント側: WireGuardクライアント → ローカルWSTunnel (UDP 51820) → WebSocket (TCP 443) → インターネット
  • サーバー側: インターネット → WSTunnelサーバー (TCP 443) → WireGuardサーバー (ローカルUDP 51820)

DPIエンジンにとって、これは暗号化されたTLS接続に見えます。標準的なWebSocketアップグレードヘッダーを確認すると、そのまま通過させます。これにより、通常は許可せざるを得ないトンネルの中にVPNトラフィックを効果的に隠すことができます。

ステップ1:WSTunnelのインストール

WSTunnelはポータブルなバイナリです。リモートサーバーとローカルマシンの両方に必要です。GitHubから最新のリリースを取得できます。

# バイナリのダウンロード (Linux x64の例) 
wget https://github.com/erebe/wstunnel/releases/download/v9.7.1/wstunnel-x64-linux.tar.gz
tar -xvf wstunnel-x64-linux.tar.gz
sudo mv wstunnel /usr/local/bin/

wstunnel --versionを実行してインストールを確認します。エラーなくバージョン番号が返されるはずです。

ステップ2:WireGuardサーバーの設定

サーバーの設定は標準的ですが、ローカルでリッスンするようにします。サーバーの内部IPを10.0.0.1と仮定します。

/etc/wireguard/wg0.confを編集します:

[Interface]
PrivateKey = <SERVER_PRIVATE_KEY>
Address = 10.0.0.1/24
ListenPort = 51820

[Peer]
PublicKey = <CLIENT_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32

インターフェースを起動します:

sudo wg-quick up wg0

ステップ3:WSTunnelサーバーの起動

次に、WSTunnelサーバーを起動します。TCPポート443でリッスンし、受信したWebSocketトラフィックをWireGuardポートに転送します。

wstunnel server --listen http://0.0.0.0:443 --restrict-to 127.0.0.1:51820

注意: すでにNginxなどのウェブサーバーをポート443で実行している場合は、WSTunnelを別のポート(8443など)で実行するか、リバースプロキシを使用して特定のURLパスに基づいてトラフィックをルーティングする必要があります。

ステップ4:クライアントブリッジのセットアップ

ラップトップまたはローカルマシンで、トンネルの反対側を作成する必要があります。このコマンドは、WSTunnelにローカルのUDPポートでリッスンし、セキュアなWebSocketを介してすべてをサーバーに送信するように指示します。

wstunnel client --listen udp://127.0.0.1:51820 --to wss://your-server-ip:443

これで、ローカルマシンの127.0.0.1:51820に送信されたデータは、自動的にTLSでラップされ、サーバーに送信されます。

ステップ5:WireGuardクライアントの設定

最後のステップは、WireGuardクライアントをローカルブリッジに向けることです。ここが魔法の起こる場所です。

ローカルのclient.confを編集します:

[Interface]
PrivateKey = <CLIENT_PRIVATE_KEY>
Address = 10.0.0.2/24
# 重要:断片化を避けるためMTUを1300に下げる
MTU = 1300

[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
# ローカルのWSTunnelブリッジを指定
Endpoint = 127.0.0.1:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

なぜMTUを変更するのでしょうか?標準のイーサネットは1500バイトのMTUを使用します。WireGuardは通常1420を使用します。しかし、WebSocket、TCP、およびTLSヘッダーを追加しているため、パケットサイズが大きくなります。1420のままにしておくと、パケットが大きすぎて破棄されたり、断片化されたりします。1300は、接続の安定性を確保するための安全な「ちょうど良い」値です。

Systemdによる自動化

サーバーでトンネルが自動的に開始されるように、/etc/systemd/system/wstunnel.serviceにサービスファイルを作成します:

[Unit]
Description=WSTunnel サーバー
After=network.target

[Service]
ExecStart=/usr/local/bin/wstunnel server --listen http://0.0.0.0:443 --restrict-to 127.0.0.1:51820
Restart=always
User=root

[Install]
WantedBy=multi-user.target

sudo systemctl enable --now wstunnelで有効にします。

パフォーマンスと潜在的な落とし穴

この方法はブロックを回避するのに優れていますが、「TCP-over-TCP」問題が発生します。WireGuardはパケット損失を適切に処理するUDP向けに設計されています。これを(WebSocketを介して)TCPでラップすると、両方のレイヤーが失われたパケットを同時に再送しようとします。遅延の大きい接続では、これにより速度が大幅に低下する「TCPメルトダウン」が発生する可能性があります。

スムーズに動作させるために、ws://ではなく常にwss://(暗号化あり)を使用してください。プレーンなWebSocketは、最新のファイアウォールによって容易に識別され、帯域制限(スロットリング)される可能性があります。TLS暗号化により、トラフィックを標準のHTTPSセッションと区別がつかないようにします。

最後に

WireGuardとWSTunnelを組み合わせることで、最新のVPNのセキュリティと、標準的なウェブトラフィックの到達性を手に入れることができます。これは、制限された環境で作業するすべての人にとって不可欠なセットアップです。MTU設定を監視し、ファイアウォールによる接続のタイムアウトを防ぐためにPersistentKeepaliveを有効にしておくことを忘れないでください。

Share: