ちょっと話題の記事

Amazon VPC環境にメンテナンス用の踏み台サーバを構築する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

bastion

よく訓練されたアップル信者、都元です。前回は「Amazon VPCを使ったミニマム構成のサーバ環境を構築する」と題して、Amazon VPCに小さなサーバ環境を構築しました。この環境は、アプリケーションサーバ(Webサーバ)がユーザからのHTTPを受け付けつつ、管理者によるメンテナンスのためのSSHの受け付けも兼ねている状態です。セキュリティの観点からは、あまり好ましい状態とは言えませんね。

そこで今回は、メンテナンスのための踏み台(bastion)サーバを構築し、よりセキュアな構成にしてみましょう。環境の構成図は右の通りです。まず、アプリケーションサーバはHTTPのみを受け付けるようにSecurity Groupを調整します。また、public subnetの中にもう一つサーバを起動し、踏み台として使います。こちらはSSHのみを受け付けるように調整します。踏み台サーバは常時起動しておく必要はないため、通常はStopの状態にしておき、メンテナンス時にのみStartするように運用すれば、セキュリティ的にもお財布的にも幸せになれます。

ちなみに、本エントリーは一応前回の続きとして書いてありますが、非VPC環境にも適用できる知識です。

Security Groupを整備する

前回はSecurity Groupについてはあまり触れませんでした。単純に「80番と22番を開けておく」とだけ書いたので、もしかしたら1つのSecurity Groupを作って、そこにHTTPとSSHの2機能の設定をしているかもしれません。しかし、AWSのSecurity Groupは「役割ごと」に1つずつ作成するのが良いとされています。従ってここでは「80番のみを通す "web"」と「22番のみを通す "ssh"」という2つのSecurity Groupを作成しましょう。 create-secg

続いて、作成したSecurity Groupを選択し、HTTPのポートを 0.0.0.0/0 に対して許可します。(要件によっては、同時にHTTPSポートも許可します。) setup-secg

同じ手順でssh用のSecurity Groupも設定します。これらを各サーバに適用します。VPCのインスタンスであれば、そのインスタンスを選択して、Actionメニューから「Change Security Group」を選択することで変更できます。Webサーバであれば、webとdefaultを両方適用しましょう。 change-secg

ちなみに、非VPCのEC2インスタンスは、起動時にしかSecurity Groupを設定できず、起動後に変更はできません。

これで、Webサーバには外部から直接SSH接続することはできなくなりました。

$ ssh -i ********.pem ec2-user@54.249.xx.xx
ssh: connect to host 54.249.xx.xx port 22: Operation timed out

踏み台(bastion)サーバを構築する

続いて、踏み台サーバを起動しましょう。通常のAmazon LinuxをVPCのpublic subnetに起動するだけで構いません。単なる踏み台なので、インスタンスクラスもmicroで充分です。Security Groupは、default(VPCの場合)と上記で作成したsshを設定しておきましょう。さらに、SSH接続を受け付けるためのElastic IP(54.249.yy.yy)も振っておきましょう。(非VPC環境では、Elastic IPは不要です。自動で割り当てられたグローバルIPアドレスを利用できます。)

ところで、通常、EC2インスタンスにSSH接続する場合は以下のように、少々長いコマンドを入力する必要があります。

$ ssh -i /path/to/********.pem ec2-user@54.249.yy.yy

これは、以下のような記述を ~/.ssh/config に記述しておくことによって「ssh bastion」と入力するだけで接続できるようになります。便利ですね。

Host bastion
  Hostname 54.249.yy.yy
  User ec2-user
  IdentityFile /path/to/********.pem

これで、踏み台サーバにSSHで接続(A)しつつ、さらにWebサーバにSSH接続(B)をする、という多段SSHが可能になります。ただし、接続(A)も接続(B)も鍵認証であるため、単純に考えれば踏み台サーバに接続(B)のための秘密鍵を置かなければなりません。しかし、それもセキュリティ上好ましい状態ではありません。そこで、 ~/.ssh/config に対してさらに以下のような記述を追記します。

Host appserver
  Hostname 10.0.0.115
  User ec2-user
  IdentityFile /path/to/********.pem
  ProxyCommand ssh bastion -W %h:%p

これにより「ssh appserver」というコマンドで、自動的に(A)と(B)による2段SSHが実行されるようになります。一度設定してしまえば、踏み台を介していることさえも忘れてしまいそうなくらい簡単ですね。ちなみに、HostnameにVPC内にあるアプリケーションサーバのプライベートIPアドレスを記述しているという点に、なんとなく違和感を感じますが、まぁそういうものですよね。

現実のプロジェクトでは、 ~/.ssh/config に定義するホストの名前はプロジェクト名(例えばfoo)をつけて「foo-bastion」や「foo-appserver」等になると思います。ProxyCommandの行にある「bastion」という記述は、踏み台サーバにつけた名前と一致させましょう。

private subnetのMySQLサーバに、MySQLWorkbenchから接続する

今回取り扱っている環境では、踏み台サーバにMySQLのCLIクライアントを入れる(sudo yum install mysql)等の対応で、裏側(private subnet)にあるMySQLにアクセスできます。最低限の用はこれで足りると思いますが、やはりMySQLWorkbench等のGUIクライアントを利用したいこともあるでしょう。しかし、このMySQLサーバはグローバルIPアドレスを持っていません。このようなクライアントはSSHと異なり、踏み台サーバではなくメンテナンスを行う端末で実行されるプロセスなので、接続には一工夫が必要になります。

幸い、MySQLWorkbench等、大抵のMySQLクライアントにはSSHトンネリング(TCP/IP over SSH)による接続をサポートしていますので、各クライアントの機能に従って接続すれば良いでしょう。以下に、MySQLWorkbenchの接続設定例を示します。 mysqlworkbench

仮にクライアントアプリケーションにSSHトンネリングのサポートがなかった場合でも、ローカルで「ssh -N -L (localhostのport):(private-subnetのIPまたはホスト名):(privateなサーバのport) bastion」のようにsshコマンドを実行しておくと、localhostの指定したポートが、あたかもprivate subnetのサーバのように振る舞ってくれるようになります。つまり、以下のように実行しておくと、localhost:13306に対してMySQLWorkbenchで接続できるようになります。クライアントが機能を持っているので、あまり意味はありませんが。いざという時に覚えておくと便利です。

$ ssh -N -L 13306:********.*******.ap-northeast-1.rds.amazonaws.com:3306 bastion

以上で、VPC環境内に、メンテナンスや開発の体制が整ったことになります。