SSHポートフォワーディングでローカルから外部サーバーへアクセスする

投稿日:2019-08-15

環境

  • amazonlinux:2017.03-with-sources
    • Dockerコンテナのベースイメージです。
    • sshクライアント用のソフトはyumでインストールしてあるとします。

やりたい事

ローカル環境(Docker)で動かしているアプリケーションから
アクセス制限のかかっているAPIサーバーやDBサーバーへアクセスしたかったので
SSHポートフォワーディングを使って実現しました。

SSHポートフォワーディングを使えば、自分が特定のポートで受けた通信を特定のサーバー(SSHで入れる事が条件)を経由して別のサーバーへ受け流す事ができるようになります。
コマンドは以下のように実行します。

ssh -f -N -L {自分が待ち受けるポート}:{アクセスしたいサーバーのIPやホスト名}:{アクセスしたいサーバーのポート} -i {SSHするための秘密鍵のパス} {SSH先のユーザー}@{SSH先のサーバー} -p {SSHポート} -4

(sshのオプション説明は調べればすぐ出てくるので割愛します)

例えば以下のようにコマンドを打つと
自分が10080ポートで受けた通信はserver2経由でserver1の80ポートに流して、そのレスポンスをそのままリクエスト元に戻す
という意味になります。
なお、server2はポート22でec2-userユーザーにSSH接続できるものとします。(秘密鍵認証)

ssh -f -N -L 10080:server1:80 -i ~/.ssh/id_rsa ec2-user@server2 -p 22 -4

手順

①純粋なポートフォワーディングを使う

下図のような通信イメージです。

  • WEBアプリのコンテナにてSSHポートフォワーディングを実行。
  • WEBアプリから自身の3306ポートへ通信が発生したらポートフォワーディング発動し、
    ssh.server.com経由でdb.server.com:3306へアクセスする。
  • WEBアプリから自身の10443ポートへ通信が発生したらポートフォワーディング発動し、
    ssh.server.com経由でapi.server.com:443へアクセスする。

WEBアプリが動いているDockerコンテナ内にてSSHポートフォワーディングのコマンドを実行します。

ssh -f -N -L 3306:db.server.com:3306  -L 10443:api.server.com:443  -i /path/to/secret_key user@ssh.server.com -p 22 -4

あとはアプリケーション内で以下の箇所を変更すればポートフォワーディングでアクセスできるようになります。
・「db.server.com:3306」の通信箇所を「localhost:3306」へ通信するよう変更
・「api.server.com:443」の通信箇所を「localhost:10443」へ通信するよう変更

②踏み台コンテナを利用してポートフォワーディングする

もし上記①のアプリケーション変更箇所がソースコードにハードコーディングされている場合など、
変更が困難な場合はこちらの手順で対応します。

下図のような通信イメージです。

  • 踏み台コンテナにてSSHポートフォワーディングを実行。
  • WEBアプリから踏み台コンテナの3306ポートへ通信が発生したらポートフォワーディング発動し、
    ssh.server.com経由でdb.server.com:3306へアクセスする。
  • WEBアプリから踏み台コンテナの443ポートへ通信が発生したらポートフォワーディング発動し、
    ssh.server.com経由でapi.server.com:443へアクセスする。

踏み台専用のDockerコンテナを作成し、コンテナの実行コマンドとしてSSHポートフォワーディングのコマンドを実行するようにします。
※プロセスがバックグラウンドに行かないように、fオプションを外しています。

ssh -N -L 0.0.0.0:3306:db.server.com:3306 -L 0.0.0.0:443:api.server.com:443 -i /path/to/secret_key user@ssh.server.com -p 22 -4

あとはWEBアプリが動いているコンテナのhostsに
『db.server.com』と『api.server.com』が踏み台コンテナのIPアドレスとなるように記載します。
踏み台コンテナのIPアドレスは以下のコマンドで調べられます。

nslookup {踏み台コンテナ名} | grep Address | tail -n +2 | cut -f2 -d ' '

シェルスクリプトにするとこんな感じになります。

#!/bin/bash -eu

HUMIDAI_IP=`nslookup humidai | grep Address | tail -n +2 | cut -f2 -d ' '`
echo "${HUMIDAI_IP} db.server.com" >> /etc/hosts
echo "${HUMIDAI_IP} api.server.com" >> /etc/hosts

これでSSHポートフォワーディングを使った通信の設定は完了です。

補足

ポートフォワーディングを使ってアクセスしたいサーバーがDBサーバーだけであれば
①の手順のコマンドを実行後、②のようにhostsを記載してdb.server.comを127.0.0.1(自身)にしてしまえば良いです。

しかし、APIサーバーのようにWEBアプリと同じポート(443)への通信をポートフォワーディングしたい場合、
既にWEBアプリで使っているポートをSSHポートフォワーディングでも待ち受ける事はできないので、
②の手順のような踏み台コンテナが必要となります。