SOCKS5 経由の OpenVPN:バージョン・トランスポート・DNS を揃える
SOCKS チェーンは、プロキシのバージョン、トランスポート、DNS の経路が食い違うと失敗します。SOCKS5 を揃え、TCP で運び、チェーンを一つの名前空間に閉じ込めてください。
このワークステーションから外へ出る経路の一つは SOCKS プロキシで、その背後の OpenVPN は有用な情報を何も付けずに接続タイムアウトを報告します。同じマシンのブラウザはそのプロキシ経由で動き、資格情報は正しく、ポートも応答します。よく食い違う三つの点を先に確認します:プロキシが話す SOCKS のバージョン、クライアントが選んだトランスポート、そして 名前解決が起きる場所です。ファイアウォールのせいだと決める前に、それぞれを確認してください。
ファイアウォールより先に SOCKS バージョンを揃える
コミュニティ版 OpenVPN は socks-proxy に SOCKS5 を使います。SOCKS4 しか提供しないプロキシに向けると、クライアントが相手の決して解析しない挨拶への返答を待つため、TCP 接続は開いたまま止まります。外から見るとこの停止はフィルタされたポートとまったく同じに見えるので、バージョンの確認はどのファイアウォール規則よりも先に来ます。SOCKS 対応の取得なら一つのコマンドで決着します。
curl --socks5-hostname 127.0.0.1:9050 gw.lab.example
完了したリクエストは同時に二つを証明します:プロキシが SOCKS リクエストを受け入れ、名前を解決したということです。URL スキームは不要です、curl は素のホストを HTTP として扱い、この試験にはそれで足りるからです。
その取得は、クライアントのログが隠す失敗も排除します。プロキシはブラウザを許可しながら VPN の宛先を拒否することがあり、その場合の拒否は CONNECT の段階で起き、VPN 認証より前です。OpenVPN はタイムアウトしか報告しないので、プロキシ自身の返答こそがあなたに必要な証拠です。
トランスポートは両側とも TCP でなければならない
ここでは SOCKS5 が TCP ストリームを運ぶので、トンネル自体に proto tcp-client が必要です。UDP のままのプロファイルはこの経路では動きません、クライアントがこのプロトコルの UDP アソシエーションモードを使わないからです。プロファイルを反転させるのは形式的な話ではなく実際の変更で、遠端は同じポートで TCP を待ち受ける必要があります。するとペイロードは TCP の中の TCP になり、失われたセグメントは二回再送され、ペースが落ちますが、対話的なセッションはこれを大きな転送よりも強く感じます。
proto tcp-client
remote gw.lab.example 8443
socks-proxy 127.0.0.1 9050
このディレクティブはプロキシのホスト、省略可能なポート、省略可能な資格情報ファイルを取ります。別の場所に置いた複製が同じように動くよう、これらはプロファイル自体に書いてください。
チェーン全体を 一つのコンテナに閉じ込める
チェーンをホスト上で行うと、その既定ルートを奪い、リゾルバを後から誰も説明できない状態に置くことになります。代わりに名前空間やコンテナで範囲を区切れば、ホストは自分の経路を保ったまま、SOCKS の跳躍とトンネルが同居します。中で何かが立ち上がるには、トンネルデバイス、それを設定する権限、到達可能なプロキシが要ります。Compose サービスなら cap_add: NET_ADMIN と bind マウントした /dev/net/tun でそれを得られます。
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
この組はコンテナを特権で動かすより狭く、デバイスアクセスとして必要なのはこれで全部です。Tor クライアントや別のプロキシコンテナがリスナーを用意できます。どちらの場合も loopback に束縛し、プロキシをネットワークに晒すのではなく、名前空間からホスト経由で到達してください。
隔離が破れるのは名前解決のところ
パケットを運びながらホストのリゾルバに問い合わせるトンネルは、何も隔離していません。隔離を本物にするのは二点です:名前空間が独自のリゾルバを持ち、その問い合わせがプロキシかトンネルを通り、物理インターフェースを決して通らないことです。試験には二つの名前を使います:トンネルの背後にしか存在しない名前と、同じ経路を通るべき公開名です。
docker compose exec vpn cat /etc/resolv.conf
docker compose exec vpn getent hosts internal.lab.example
遠端の名前が解決し、公開名が解決しないなら、チェーンは壊れているのではなく選択的で、その差が各問い合わせの経路を教えてくれます。両方ともホストで解決するのにコンテナ内ではどちらも解決しないなら、コンテナはまだホストのリゾルバを借りています。見つけたリゾルバのファイルを記録してください、変えるべき設定はそれだからです。
各ホップを順に証明する
リストは短い。SOCKS5 のリクエストに返答があり、トンネルはプロキシ経由でリスナーに届き、名前空間を出るパケットは遠端が期待するアドレスを運びます。設定を積み増すのではなく、一つずつ確認してください。クライアントが接続済みと言うのに何も動かないとき、断線はほぼ常に 二番目か三番目の事実にあります。
curl --socks5-hostname 127.0.0.1:9050 gw.lab.example
docker compose exec vpn ip route get 203.0.113.10
docker compose exec vpn getent hosts internal.lab.example
うまくいった組み合わせを書き留めてください:SOCKS のバージョン、トランスポート、リゾルバ、名前空間の設定です。このチェーンを記憶から解き直すのはメモより高くつきます。その四つの事実はチャットではなくチケットに残してください。
接続済みと報告しながら何も通さないチェーンは、たいていまだ証明していないホップで失敗しています。次の層を足す前にそのホップを試してください。