デバイスはキャリア NAT の背後にあり、外向きの接続は開けますが、誰も到達できない SSH サーバーを動かしています。届く依頼はたいてい「VPN を付けてほしい」という形ですが、それはいくつもある答えのひとつです。引き受ける前に、必要なのが管理用 TCP ポートひとつなのか、それとも サブネット全体への経路なのかを決めてください。どちらも別のプロジェクトであり、露出も異なります。

リバーストンネルは狭いほうの答えとしてよく機能します。デバイスはハブへ接続し、ハブにループバックポートで待ち受けるよう頼み、そのポートを自分の SSH サービスへ転送します。その後、管理者は公開インターネットではなくハブ経由で待受に到達します。

選択を決める三つの条件

どの設定よりも先に、これを書き出してください。これは限られた管理用の入口とネットワーク拡張の境界であり、混同すると五行的なトンネルが誰も所有しないフラットなネットワークになります。

ConditionReverse tunnel fitsSomething else is needed
How many services must be reachable?One, or a small fixed set on loopbackAnything that needs many ports or dynamic discovery
Who connects, and how are they authenticated?Named administrators with their own hub accountsThird parties, or anything needing per-flow audit you do not control
Does the device's own LAN need to be reachable?NoYes, so a routed VPN or a managed platform is the honest answer

三行目は議論になりやすい項目です。デバイスの SSH ポートへのトンネルは、その隣人をあなたのネットワークに載せるものではなく、さらに転送を重ねてそう見せかけるべきでもありません。要件が本当に LAN なら、そう言って LAN を設計してください。

ハブ側:範囲を絞ったリレーアカウント

各デバイスに、公開鍵認証で対話セッションなしの専用リレーアカウントを与えます。 Match ブロックをそのアカウントに限定すれば許可は小さく保たれ、秘密鍵はあるべき場所であるデバイス上に残ります。

Match User relay-dev-7
    AuthenticationMethods publickey
    PubkeyAuthentication yes
    AllowTcpForwarding remote
    PermitListen 127.0.0.1:21022
    GatewayPorts no
    MaxSessions 0
    PermitTTY no
    X11Forwarding no
    AllowAgentForwarding no

MaxSessions 0 が興味深い行です。シェルとサブシステムのセッションを拒否しつつ、デバイスが必要とする転送は許可するので、盗まれた鍵を対話ログインに変えることはできません。 PermitListen はハブ側の待受アドレスとポートを固定し、 GatewayPorts no はハブが置かれたネットワークへ開くのではなくループバックに留めます。

再読み込みの前に実効設定を試し、その間は二つ目のセッションを開いたままにします。 Match ブロックが誤って全員に適用されたり、オプション名の打ち間違いがあったりするのは、固めているマシンから自分を締め出す定番の方法です。実行中の設定を出力し、いま書いたファイルを信じるのではなくオプションを読み直してください。オプションの対応はビルドによって異なるため、新しいサーバーで受け付けられた行が古いサーバーでは単に無視されることがあります。

デバイス側:大きな音で失敗する転送

デバイスは接続を開き、待受を要求します。引数の中の二つのアドレスは -R 別の意味を持ち、左から右へ読むのがどちらがどちらかを覚える最短の方法です。

ssh -NT \
    -o ExitOnForwardFailure=yes \
    -o ServerAliveInterval=30 \
    -o ServerAliveCountMax=3 \
    -R 127.0.0.1:21022:127.0.0.1:22 \
    [email protected]

左の組はハブが待ち受ける場所です。右の組は要求が届いたときにデバイスが接続する先で、デバイス自身の側から解決されます。これを入れ替えると、きれいに上がって誤った場所へ転送するトンネルになり、失敗より悪い結果になります。

ExitOnForwardFailure=yes は、要求した待受を確立できないときにコマンドを終了させるので、再起動ループが中途半端なセッションを残す代わりに真実を教えてくれます。ローカルの SSH サービスが後で健全かどうかには触れないため、プロセス管理者と時折の実接続テストと組み合わせてください。

余分なホップを避けるために、ループバックの待受アドレスをワイルドカードに置き換えないでください。その一つの変更が管理経路と公開到達可能なポートの違いであり、たいていデバッグ中に起きてそのまま残ります。同僚がハブ経由でポートに届かないなら、バインドを広げるのではなくジャンプの設定を直してください。この設計のセキュリティ論はループバックそのものです。

ハブ経由の管理者アクセス

ハブに通常のアカウントを持つ管理者は、そこを経由して転送されたポートへジャンプできます。リレーアカウントはこの用途には使わず、それを分けておくことこそが要点です。

ssh -J [email protected] -o HostKeyAlias=device-7 \
    -p 21022 [email protected]

選んだ別名の下でデバイスのホスト鍵を検証してください。その鍵が経路の終端のデバイスを識別し、トンネルは輸送にすぎないからです。端から端までのログインは依然として両端を認証します。共有ハブで終わるトンネルが、それだけで信頼できる経路になるわけではありません。

ライフサイクルを所有する

デバイスのコマンドは、ちょうど一つの有効なインスタンスと意図的に選んだ再起動ポリシーを持つプロセス管理者の下で動かしてください。keepalive のオプションは相手の死を検出しますが、終了したプロセスを蘇らせるものではありません。

次に廃止手順を書きます。各デバイスは専用の待受と専用の鍵を持つので、廃止とはそのアカウントを取り消し、その鍵を削除することであり、艦隊全体で共有する秘密を回すことではありません。

最後に、ループバックが何をもたらすかを忘れないでください。ネットワークの露出を限りますが、共有ハブ上の他のローカル利用者については何もしないので、ハブ自身のアクセス方針は依然として正しくなければなりません。 sshd_config のリファレンス は上記で使った各オプションを説明しており、どれかを広げる前に転送の節を全文読む価値があります。

関連記事