SEのお仕事でお客様先のWebサーバの開発をするとき、当然お客様のネットワークには自分の会社から接続できないので困っちゃいますよね。そんなとき、お客様先にゲートウェイとなるサーバがあって、sshで接続できれば、sshのポートフォワーディングという方法で、お客様内の任意のホストで稼働しているWebサーバにアクセスすることができます。sshのポートフォワーディングはこのようなニーズを解決する便利な方法なんですが、使い方をよく忘れちゃうのでメモついでに書きます。
ネットワークに接続していないWebサーバにはアクセスできないよ
先ずは、自分のPCからWebサーバにアクセスできるネットワークの形態なんですが、イントラネットでもインターネットでも、WebサーバのIPアドレスに直接アクセスできるか、もしくは経路が確立されていればアクセスできます。あたりまえっすね。

WebサーバのIPアドレスに直接アクセスできず、経路も確立されていない場合はアクセスできません。これもあたりまえですね。
お客様先のネットワークは、インターネットとはFireWallできちんと遮断されているので、外部からはアクセスができません(自分の会社からもアクセスができません)。

FireWallはssh(port=22)が許可されていることが多い
FireWallはセキュリティの脅威から、企業内のネットワークを守るための機能です。企業内ネットワークからインターネットへのアクセスは比較的自由な通信が可能ですが、インターネットから企業内ネットワークへのアクセスは、接続元IPアドレスや接続portが制限されていることが一般的です。
ssh(Secure Shell, port=22)は、安全なアクセス形態のひとつと認識されており、接続元のIPアドレスをFireWallに登録した上で、許可されることがあります。
外部からのssh接続が許可されたら、企業内に最初に接続するためのゲートウェイマシンにアカウントが作成されて、sshログインが可能です。

sshポートフォワーディングとは何か?
sshの標準的な使い方は、sshの暗号化通信でsshサーバとなるリモートホストと接続して、リモートホストのシェルと会話処理をすることです。
一方、sshのポートフォワーディングでは、リモートホストと暗号化通信で接続(SSHトンネル)をするところは同じですが、そのリモートホスト(sshサーバ)を中継して、リモートホストの更に先のリモートホストの任意のportを、接続元ホスト(sshクライアント)の任意portに転送します。

このようにsshポートフォワーディングでは、3つのホストが登場します。上記の図で整理すると、以下のようになります。
| ホスト | 説明 |
| lhost | sshクライアント。sshコマンドを実行して、lhostからrhost1の間にSSHトンネルを作ります。rhost2の任意portから転送されてきた通信はlhostの任意port(この例ではport=10433)に転送されており、lhostはこのportでrhost2のサービスを提供することができます。 |
| rhost1 | sshサーバ。更に、rhost1からrhost2の任意portに接続をして、それをlhostに中継します。 |
| rhost2 | アクセスしたいサービスが稼働しているサーバ。この例ではWebサービス(https)をport=443で提供しています。 |
sshポートフォワーディングのオプション
次にsshポートフォワーディングのオプションについて説明します。上記の例では、
-L 10443:rhost2:443
と指定しています。これは、「rhost2のport=443をsshクライアントのport=10443」に転送するという意味です。rhost2は以下のようにIPアドレスでもOKです(むしろ、IPアドレスで指定するほうが多いかもしれません)。
-L 10443:192.168.2.210:443 (102.168.2.210はrhost2のIPアドレス)
さて、-Lオプションの中には中継処理を行うsshサーバのホストの情報がありません。sshサーバはsshコマンドの引数として指定します(上記の例ではrhost1, もちろんIPアドレスでもOKです)。これは通常のsshコマンドの使い方と同じです。
もし、rhost1にssh接続するアカウントがlhostと異なるのであれば、「ユーザ名@rhost1」のように接続先を指定して下さい。
また、上記の例では、-Lオプションの他に3つのオプションを指定しています。それらの意味は以下の通りです。
| オプション | 意味 |
| -f | sshのプロセスをbackgroundで実行する。 |
| -N | sshサーバ側でコマンドを実行しない。 |
| -o GatewayPorts=yes | 転送したportに対して、sshクライアント以外の外部のクライアントからも接続することを許可する。 |
さて、これで準備ができました。sshクライアントとなるホスト(lhost)で、以下の通りsshコマンドを実行します。
ssh -f -N -o GatewayPorts=yes -L 10443:rhost2:443 rhost1
すると、PCのブラウザからは、
https://lhost:10443/
または
https://lhostのIPアドレス:10443/
とURLを指定することで、rhost2のWebサービス(port=443)にアクセスすることができます。
今回の例では、sshコマンドの引数に「-f オプション」を指定しているので、sshのプロセスはbackgroundで実行されています。常時起動しておくのは、セキュリティ面でよろしくないので、作業が終わったら、killコマンドでsshプロセスを削除しておいて下さい。
ps -ef | grep ssh | grep -v grep | grep ‘10443:rhost2:443’
のようにプロセスを検索すると対象プロセスが特定できるかと思います。
2ホスト間でのsshポートフォワーディング
前述の説明では、3つのホストが登場しました。2ホストの場合はどうなるでしょうか? 例えば、自分のPCが、ゲートウェイマシンとなるホストに直接できるような場合です。
この場合も、基本的には3ホストの連携として考えます。3ホストのうち、どれかひとつをlocalhost(自ホスト)に置き換えて考えれば良いのです。置き換える方法としては、2つの方法があります。
1つ目は、sshの接続先をlocalhostとする方法です。

sshコマンドの引数にlocalhostを指定しているので、sshクライアントは自ホスト(localhost)にsshトンネルを作成して、更にその先のrhostのport=443のサービスに接続しています。
2つ目は、sshの接続先をrhostにしつつ、接続するportをlocalhostにする方法です。

sshコマンドの引数にrhostを指定しているので、sshクライアントはrhostに対してsshトンネルを作成して、更にlocalhostのport=443のサービスに接続しています。
3ホスト間以上のsshポートフォワーディング
(1)ポートフォワーディングを多段で接続する方法
環境によっては、目的とするサーバにたどり着くまでに、ゲートウェイマシンが複数台存在する場合もあります。この場合は、3ホスト以上でポートフォワーディングを行うこととなりますが、今までの基本を踏まえて、sshポートフォワーディングを多段で接続すれば簡単です。

上記の図は、もう1台のゲートウェイマシン(rhost2)が挟まった場合の例です。
sshトンネルは、lhostとrhost1の間、rhost1とrhost2の間で作成します。
2番目のsshトンネルでは、rhost3のport=443をrhost1のport=10443に転送しています。
1番目のsshトンネルでは、rhost1(接続先のsshサーバと同一なのでlocalhostで指定)のport=10443をlhostのport=10443に転送しています。2番目のsshトンネルを作成するときに「-o GatewayPorts=yes」を指定していないのは、接続元がlocalhost(この場合はrhost1)に限定されるためです。尚、sshトンネルを作成する順番は、どちらが先でもOKです。
(2)ProxyJumpを使用する方法
sshのProxyJumpの機能を利用すると、1回のsshコマンドの実行でsshの多段接続を実現することができます。詳細は「犬でも分かるsshのProxyJump」をご覧ください。
・rhost2がrhost3のport=443のサービスに接続
・lhostがport=10443でサービスを提供
という考え方は(1)と同じです。違うのは、ProxyJumpを利用してlhostとrhost2の間を1回のssh接続で済ませるという点です。

これを実現するために、lhostの~/.ssh/config には、以下の例のように設定しましょう。
(ホスト名、IPアドレス、ユーザ名は、それぞれの環境に合わせた値を記述して下さい)
大事な点は、rhost2への接続はrhost1でProxyJumpして、rhost3への接続はrhost2でProxyJumpするという設定を定義することです。
Host rhost1
HostName 192.168.1.210
User user01
Host rhost2
Hostname 192.168.2.230
User user01
ProxyJump rhost1
Host rhost3
Hostname 192.168.122.231
User user01
ProxyJump rhost2
このように設定した上で、以下のようにsshコマンドを実行します。
ssh -f -N -o GatewayPorts=yes -L 10443:rhost3:443 rhost2
ポートフォワーディングでハマること..
テストをしていて、うまくいかなくてハマったことをメモします。
サービスを提供するlhostにおいて、firewallの制限によっては、指定したportでサービスを提供することができない場合があるので、そのportを許可するようにしましょう(firewalldの運用が不要でしれば、停止することもひとつの手段です)。
lhostにおいて、サービスを提供するportに10080を使用していると、PCのブラウザが勝手にプロキシだと判断して、ブラウザに表示ができませんでした。wgetではアクセスできるのに、ブラウザではアクセスできないので不思議に思っていました。portを10081に変更するとブラウザからでもアクセスできました。これが分からなくて半日悩みました。。。
その他の便利なオプション
sshポートフォワーディングでは、少なくとも前述に紹介した「-L」「-f」「-N」「-o GatewayPorts=yes (必要に応じて)」のオプションを使うことになりますが、私の場合は更に
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
を指定して、ssh実行時に ~/.ssh/known_hosts のチェック及び登録をしないようにしています(理由は、いろいろと面倒だから..)。
また、sshポートフォワーディングの度に、パスワードの入力を求められるのも手間なので、鍵ペアを登録して、パスワード無しでsshできるようにしておくと便利です。
パスワード無しでsshするための設定は、パスワード無しでsshするための設定 で紹介しているので参考にして下さい。
広告主へのリンク
おうちでLinuxやるなら、ミニPCがちょうど良いです(私はこの3台を使っています)。
このブログにおける関連リンク
・パスワード無しでsshするための設定
・犬でも分かる sshポートフォーワーディング2: リバーストンネル
・犬でも分かるsshのProxyJump



コメント