こんにちは。クリアコードの結城です。
SSHを使うと、手元のコンピュータから別のコンピュータへネットワーク越しにログインして、bashやzshなどのコマンドラインシェルを使ってそのコンピュータをリモート操作できます。scpを使えば、ネットワーク越しにファイルをコピーすることもできます。
しかし、以下のコマンド列を見ると分かる通り、SSH経由で接続できるコンピュータは基本的には、手元で操作しているコンピュータから直接ホスト名またはIPアドレスで参照できるコンピュータに限られます。
% ssh www.example.com # 接続先をホスト名で指定
% scp 192.168.1.10:/var/log/apache2/access.log /tmp/ # 接続先をIPアドレスで指定
さて、以下のような事をしたくなった・する必要に迫られたとしましょう。
- ホテル、喫茶店、ネカフェ、街角のWi-Fiアクセスポイント、スマホのテザリング、などなど、外出先のネットワークから、社内のPCにログインしたい。しかしVPN等は特に用意していない。
- 社内のファイル共有サーバにしか置いていないファイルを、外出先のネットワークに接続したPCにコピー(ダウンロード)したい。VPNは用意していない。
- 社内のネットワークから、データセンターに設置されているデータベースサーバにログインしたい。データベースサーバはファイアウォールの向う側にあり、データセンターの外からはログインできず、同じデータセンター内に設置されている別のサーバからしかログインできない。
これらの場合に出てくるネットワークとコンピュータの位置関係を大まかに図で示すと、こんな感じです:
図の上の方にあるのが「ログインしたいコンピュータが所属しているLAN(接続先LAN)」、下の方にあるのが「自分が今ログインしているコンピュータと、その所属ネットワーク(接続元LAN)」と思って下さい。2つのネットワークを見分けやすいように、接続先LANは192.168.1.0/24のネットワーク、接続元LANは192.168.10.0/24だとします。(※この表記の仕方の意味については、過去の記事も併せて参照して下さい。) 192.168.1.0/24には、192.168.1.10(work1)と192.168.1.11(work2)の2台が存在しています。また、192.168.10.0/24には192.168.10.10(mobile1)が存在しています。
図中で説明している通り、2つのLANはどちらもルーターを経由してインターネットに接続されていますが、LAN同士の間には直接の繋がりはなく、互いにパケットが転送されるということもありません。仮にmobile1からwork1に向けてping 192.168.1.10
とPINGを送っても、到達不能でタイムアウトしてしまいます。
このような場合にも、SSHのポートフォワード機能を使えば、やりたいことを実現できます。
ポートフォワードとは、あるコンピュータの特定のポート番号に対して送られる通信内容を、別のコンピュータの特定のポート番号への接続として転送する事を言います。 インターネットとLANの間でルーターは日常的にこれをこなしていますが、SSHのポートフォワード機能を使うと、様々な設定でポートフォワードすることができます。あるLANの中にあるPCから別のLANの中にあるPCにSSHで接続するということも、この技術を使って実現できます。
ここでは、例として「インターネット上の中継サーバを使う方法」と「LAN内の中継サーバを使う方法」の2通りのやり方を紹介します。
インターネット上の中継サーバを使う方法
先の構成に、インターネット上に存在するサーバを1台加えてみました:
このサーバには固定のIPアドレスと、www.example.comというホスト名が割り当てられているとします。また、sshdも動作していて、以下のようにSSHでログインできるとします。
% ssh www.example.com
(ここからwww.example.comにログイン済み)
% hostname
www.example.com
このサーバを中継サーバとして利用して、mobile1からwork1やwork2にログインしたり、scpでファイルを転送したりすることができます。
そのためにはまず、接続先LAN内にあるログインしたいコンピュータと、中継サーバの間で、ポートフォワードのためのSSH接続を確立しておく必要があります。
ポートフォワードのためのSSH接続
mobile1からログインしたいコンピュータであるwork1で、以下のようなコマンド列を実行します:
(work1にログイン済み)
% ssh www.example.com -R 10022:192.168.1.10:22
sshコマンドに-R 転送元ポート:転送先ホスト:転送先ポート
というオプションを指定すると、SSH接続が維持されている間、接続先サーバから接続元PCへのポートフォワードが行われるようになります。
これを、俗に「トンネルを掘る」と言います。
上記の例だと、*www.example.comの10022番ポートに対する通信が、work1(192.168.1.10)の22番ポートに転送されます*。
実際、www.example.comにログインした状態から、そのサーバ自身の10022番ポートを指定してssh localhost -p 10022
とSSH接続を行うと、www.example.comのサーバ自身ではなく、work1の方にログインできるようになっています:
(www.example.comにログイン済み)
% ssh localhost -p 10022
(ここからwork1にログイン済み)
% hostname
work1
ここまでの流れを図にすると、こんな感じです:
192.168.1.0/24の外にあるサーバから、192.168.1.0/24の中にあるwork1へと、本来できないはずのSSH接続ができているという所がポイントです。
インターネットからwork1(192.168.1.10)に接続する
「ちょっと待った!!! インターネット上にあるサーバからLAN内のPCにSSH接続できるようになってるって、セキュリティ的に超危ないんじゃないの!?!?!」
はい、そのように危惧するのも無理はないですが、心配する必要はありません。この状態はローカル転送といって、*www.example.comのサーバの上から自分自身の10022番ポートに対して行う通信だけが192.168.1.10に転送されます*。仮に第三者が他のコンピュータから`ssh www.example.com -p 10022`と接続を試みても、その通信は転送されないため、接続は失敗します。安心ですね。
ではどうやってインターネットからwork1に接続すればいいのかというと、話は単純で、中継サーバであるwww.example.com自体に一旦ログインすればいいのです。www.example.comにログインできてしまえば、そこからssh localhost -p 10022
でwork1にログインできるというわけです。
(mobile1にログイン済み)
% ssh www.example.com
(ここからwww.example.comにログイン済み)
% ssh localhost -p 10022
(ここからwork1にログイン済み)
% hostname
work1
こうして、一度192.168.1.0/24のネットワークの内側に入れてしまえば、あとは何でもし放題です。そこからさらに他のPCであるwork2にログインすることもできますね。
(work1にログイン済み)
% ssh 192.168.1.11
(ここからwork2にログイン済み)
% hostname
work2
出かける前にすること
さて、準備ができましたので会社を出発しましょう……と言いたい所ですが、もう少しだけ待って下さい。
この状態だと、work1とwww.example.comの間で接続が不意に切れてしまったら、それでもうおしまいです。外出先からwww.example.comには接続できても、そこからwork1に接続することはできなくなってしまいます。
ですので、work1とwww.example.comとのSSH接続は、自動的に再接続するようにしておきましょう。これは、sshコマンドの代わりにautosshというコマンドを使えばOKです。autosshをインストールして、sshコマンドと同様の引数・オプションを指定し実行するだけで、準備は完了です:
% autossh www.example.com -R 10022:192.168.1.10:22
これで、もしwork1とwww.example.comの間で接続が不意に切れてしまっても、自動的に再接続してくれるようになります。
また、work1がUbuntuのデスクトップ環境を備えているなら、スクリーンをロックして出掛ければよいのですが、そうでないなら、シェルにログインしっぱなしのまま出掛けるというのは不用心すぎます。work1の上で仮想端末を起動して、autosshでの接続は仮想端末上で行うようにしましょう。例えばtmuxを使うのであれば、こんな感じです:
(work1にログイン済み)
% tmux
(ここから仮想端末の中)
% autossh www.example.com -R 10022:192.168.1.10:22
(ここからwww.example.comにログイン済み)
(Ctrl-B, Dで仮想端末から切断)
(ここからwork1にログイン済み)
% exit
以上ですべての準備は終わりです。会社を出発して、外出先から社内のPCにSSHでログインし、快適リモート操作を存分に楽しみましょう!
ここまでの流れを図にすると、以下の通りです:
LAN内のサーバを中継サーバにする方法
接続元となるLAN内にサーバが置かれていて、Dynamic DNSを用いるなどしてインターネットからそのサーバにログインできるようになっている場合には、そのサーバを中継サーバにすることもできます:
先の例とは違うネットワークであることが分かりやすいように、今度は接続元LANを192.168.20.0/24としています。この自宅ネットワーク内に、自分が操作するhomepcという名前のPCと、homeserverという名前のホームサーバ(192.168.20.100)があり、インターネットからdynamic.example.comというホストの22番ポートにSSH接続するとhomeserverにSSHでログインできる、という状態になっていると仮定します。
% ssh dynamic.example.com
(ここからhomeserverにログイン済み)
% hostname
homeserver
ここで、帰宅後に自宅のhomepcから、社内にあるwork1やwork2に接続したいという場合を考えます。 この場合も、ポートフォワードを使った接続のやり方は、基本的に先程の例と同じ要領です。
まず、homepcからログインしたいコンピュータであるwork1で、以下のようなコマンド列を実行して、homeserverとの間にトンネルを掘ります:
(work1にログイン済み)
% ssh dynamic.example.com -R 10022:192.168.1.10:22
これで、homeserverの10022番ポートに対する通信が、work1(192.168.1.10)の22番ポートに転送されるようになりました。試しにwork1にログインしてみましょう:
(work1にログイン済み)
% ssh dynamic.example.com -R 10022:192.168.1.10:22
(ここからhomeserverにログイン済み)
% ssh localhost -p 10022
(ここからwork1にログイン済み)
% hostname
work1
ここまでの流れを図にすると、こんな感じです:
ポートフォワードが期待通りに働いていることを確認できたら、sshコマンドの代わりにautosshコマンドを使うようにしましょう。また、直接ログインした端末ではなく仮想端末の中から、work1とhomeserverにトンネルを掘っておきましょう:
(work1にログイン済み)
% tmux
(ここから仮想端末の中)
% autossh dynamic.example.com -R 10022:192.168.1.10:22
(ここからhomeserverにログイン済み)
(Ctrl-B, Dで仮想端末から切断)
(ここからwork1にログイン済み)
% exit
これで準備は完了です。颯爽と帰宅し、自宅から社内のPCにSSHでログインして、快適リモート勤務を満喫しましょう!
(homepcにログイン済み)
% ssh 192.168.20.100
(ここからhomeserverにログイン済み)
% ssh localhost -p 10022
(ここからwork1にログイン済み)
% hostname
work1
% ssh 192.168.1.11
(ここからwork2にログイン済み)
% hostname
work2
ここまでの流れを図にすると、以下の通りです:
まとめ
以上、SSHのポートフォワード機能を使って、インターネット上のサーバやLAN内のサーバを中継サーバに利用し、外部からインターネット経由でLAN内のコンピュータにSSH接続する手順を紹介しました。
SSHのポートフォワードの指定は、どのオプションのどの部分に何を指定すればよいか、ぱっと見で分かりにくいのが難点です。この記事では具体的な例を挙げて、それぞれのコンピュータの関係を分かりやすく示した状態で、オプションに指定する内容を示してみました。
自分の場合のネットワーク環境でも、この記事を参考に、ぜひ一度試してみて下さい。
なお、筆者が執筆した「まんがでわかるLinux シス管系女子3」という書籍では、SSHポートフォワードの様々な例についてまんが形式でさらに詳しい解説を行っています。SSHポートフォワードをより自在に使いこなせるようになりたいという方は、そちらも併せてご覧下さい。