ターミナルは接続し、プロンプトが現れ、全員が SSH アクセスは動いていると同意します。ところがノート PC のデータベースクライアントは依然としてプライベートホストに届かず、セッションのせいにされます。作られたのはシェルであり、それは SSH が提供できる四つのまったく異なるインターフェースの一つです。まずインターフェースを特定し、次にアプリが本当にそれを使っているかを試します。各インターフェースはそれぞれの仕方で失敗するため、順序が重要です。

何かを比較する前にインターフェースを特定する

OpenSSH クライアントのマニュアルは、混同しやすい四つの仕組みを区別しています。シェルやリモートコマンドは SSH ホスト上で実行されます。ローカル転送はクライアント側で待ち受けるソケットを開き、先の接続はリモート側から行います。リモート転送はその逆で、サーバ側で待ち受けます。動的転送は、使い方を知っているプログラムに SOCKS エンドポイントを公開します。

  • シェル: プログラムはリモートマシン上で動き、ローカルでは何も待ち受けません。
  • ローカル転送: ノート PC にポートがあり、リモートホストが外向きの接続を行います。
  • リモート転送: リモート側にポートがあり、通信はこちらへ戻ってきます。
  • 動的: クライアント上の SOCKS プロキシで、プロキシを理解するアプリだけが使います。

これらのどれもルートを設定しません。プロキシ設定を無視するプログラム、UDP を話すプログラム、名前を自分で解決するプログラムは、有効な SSH セッションの影響を受けません。これは SSH の不具合ではなく、選んだインターフェースの範囲です。

ローカル・リモート・動的転送は、誰が待ち受けるかで異なる

ほとんどのケースを決める違いは、待ち受けるソケットを誰が持つかです。ローカル転送ではクライアントが待ち受け、遠端が宛先に発信するため、宛先アドレスはサーバの視点で解決されます。リモート転送ではサーバが待ち受け、接続はこちらの側へ運ばれ、こうしてローカルサービスを直接公開せずに晒せます。

ssh -N -L 5432:db.internal.example:5432 [email protected]
ssh -N -D 1080 [email protected]
ss -ltnp | grep -E '5432|1080'

最初のコマンドはノート PC のポート 5432 を束縛し、踏み台から見たデータベースへ転送します。二つ目はポート 1080 に SOCKS プロキシを作ります。三つ目のコマンドは実際に待ち受けているプロセスを確認し、ポート衝突で黙って失敗したのではなく転送が存在する唯一の証拠となります。

動的転送には独自の警告が必要です:SOCKS を尊重するアプリしか対象になりません。ブラウザは向けられますし、コマンドラインツールには使うよう指示できますが、独自の接続スタックを持つデータベースドライバは通常できません。マシン上のすべてのプログラムが今やトンネルを使っていると仮定するのが、この分野で最も多い誤りです。

トンネルデバイスモードだけが唯一の本当のネットワーク例外

OpenSSH は -w トンネルデバイス転送にも対応しており、TCP リスナーの代わりにポイントツーポイントのインターフェースを通します。これは本物のネットワークトンネルで、シェルを開いた副作用ではなく、意図的に設定されます。

sudo ssh -w 0:0 [email protected]
ip addr show tun0

両端で利用可能な対応するインターフェース番号、サーバ側の正しい設定、デバイスを作るためのクライアント側の十分な権限が必要です。また、どの宛先がそれを使うべきかという方針を素のインターフェースは持たないため、アドレスとルーティングの計画も必要です。

それでもトンネルは遠端のホストで終わります。SSH トランスポートが提供した保護はそこで終わり、先の接続は別のホップです。VPN ゲートウェイも同じ形の境界を持つので、比較はどのツールが強いかではなく、範囲と終端について行うべきです。その境界を声に出して名づけることは、明らかにそうではないため、トンネルが「すべてを暗号化する」かという議論も決着させます。

アプリが作ったインターフェースを使っているか確認する

検証はセッション単位ではなくアプリ単位です。動く転送はポートが束縛され遠端が発信できることを証明しますが、あなたが気にするプログラムがそれを通ったことは証明しません。

curl -sS --socks5-hostname 127.0.0.1:1080 -o /dev/null -w '%{http_code}\n' https://app.internal.example/health
psql "host=127.0.0.1 port=5432 dbname=app sslmode=require" -c 'select 1'

最初のコマンドはホスト名を遠隔で解決しつつ SOCKS エンドポイント経由で要求を送り、二つ目は TLS を依然必須としたまま転送されたデータベースポートを試します。どちらでも通るテストはトンネルを使っていなかったから、転送を止めた状態で同じテストを実行して失敗を確かめてください。

次に、重要な次元だけでルーテッド VPN と比べます:どの宛先が対象か、どのプロトコルが動くか、保護はどこで終わるか、誰がアクセスを承認するか。単一サービスの転送はより狭く良い答えになり得ますし、多くのアプリが同じネットワーク経路を必要とするなら VPN が正しい形です。どちらも、その遠端から到達できるすべてを許可するわけではありません。

関連記事