はじめに
こんにちは、三菱総研DCS ビジネスイノベーション企画部の中川です。私は現在、新規SaaSビジネスの立ち上げプロジェクトに参画し、開発チームでインフラを担当しています。事業の立ち上げ段階では、顧客からの学びを機敏に取り込むために、素早くプロトタイプを作成・改良して、短いサイクルで仮説と検証を繰り返す必要があります。
プロトタイプを動かすインフラの構築においても、プロトタイプ開発のサイクルに合わせなければならず、スピードが重要視されます。そのため、プロトタイプが稼働できることのみを満たす構成にして、高いセキュリティや可用性については作りこまないようにしていました。
幸いにして、仮説検証が上手くいったためサービスインに向けて、非機能要件を整理することにしました。すると、環境内の踏み台サーバのセキュリティが問題になりました。それは、踏み台サーバとして稼働させていたEC2インスタンスのマルウェア対策です。唯一、サーバレスではないリソースでした。もし、サーバレスにすることができればOSのマルウェア対策が不要になるため、踏み台サーバにおいてもサーバレス方式を採用できないか検討しました。
サーバレス化された踏み台サーバの事例を調査した上で、今回のセキュリティ要件を満たしつつ、踏み台サーバの役割である「環境への入り口一元化」も実現する、新たな踏み台の実現方法を考案し、採用しました。
その結果、セキュリティ要件を満たすために必要な作業が不要となり、さらに使いやすさも向上させることができました。
本記事では、従来のEC2型の踏み台サーバをサーバレス化する方法を調査し、新しい踏み台を採用して得られた効果について紹介します。AWSにおける踏み台サーバを改善したい方、これから踏み台サーバを構築しようと考えている方はぜひご覧ください。
踏み台サーバとは
踏み台サーバとは、主にインターネットアクセスができないプライベートサブネット内に配置されたインスタンスに接続する目的で使用される中継サーバのことです。
このサーバでは、セキュリティの観点から、監査ログ(誰が何の作業をしたか)を収集できること、アクセスポイントとなるサーバを1台に集約することが求められます。(図1参照)
プロジェクトにおける踏み台サーバの構成
私たちのチームでは、踏み台サーバを素早く構築する必要があったため、ノウハウがあり手軽に構築ができるEC2インスタンスを踏み台サーバとして使用しました。(図2参照)
システム全体の構成図を図2に示します。アプリケーションは画面(S3)、API(ECS Fargate)、バッチ(ECS Fargate)、DB(RDS)で構成されています。画面以外のAPIとバッチ、DBはユーザが直接アクセスする必要がないのでインターネットアクセスを禁止するプライベートサブネットに配置してセキュリティを高めています。 しかし、アクセスを禁止してしまうと開発・運用者がバッチやDBにアクセスできないため、開発・運用者が目的サーバへアクセス可能な専用ルートを別途用意する必要があります。そこで、パブリックサブネットにEC2の踏み台サーバを置くことで、踏み台サーバを経由してバッチやDBへアクセスが可能な構成としました。
踏み台サーバの見直し
上記のようにスピード重視で構築した踏み台サーバは、サービスインに必要なセキュリティ要件を満たしていませんでした。具体的には、EC2インスタンスはOSレイヤにマルウェアの脅威があり、この脅威に対して自身でソフトウェアを導入するなどして対策しなければならないという要件があります。
EC2インスタンスのセキュリティ対策を徹底しようとすると、マルウェア対策ソフトの比較選定と導入、導入後のソフトウェア管理といった作業が発生してしまいます。これは、OSの更新やパッチ、セキュリティ対策などは全て利用者が行うことがAWS責任共有モデルで決められているためです。(図3左参照)
一方で、API/バッチサーバに使用しているECS Fargateはサーバレスアーキテクチャと呼ばれ、AWS側に責任範囲を広げることができ、OSレイヤのセキュリティ対策を始めとしたサーバ管理に関わる全タスクをAWSに任せることができます。 (図3右参照)
つまり、サーバレスアーキテクチャは、セキュリティに関連する作業だけでなくパッチの適用といった作業もなくすことができます。
API/バッチサーバと同様に踏み台サーバについても、サーバレスアーキテクチャを採用できないか検討しました。
踏み台×サーバレスの事例調査
踏み台のサーバレス化を実現している事例を調査した結果、いくつかの方式が見つかり、大きく二つの方式に整理できます。 なお、どちらの方式も、本来の踏み台サーバの要件を満たしています。
以下2つの方式は、Session Managerを使用することでEC2インスタンスを使わずにプライベートサブネットへの接続を可能にしています。
方式①:Session Managerのみ
Session Managerのみを使用して直接目的サーバへ接続する方式です。Session Managerは接続した利用者と操作ログの取得が可能であり、かつ踏み台を経由しないため、サーバの台数は気にする必要がありません。 ただし、Session Managerを使う条件として、目的サーバにSSMエージェントをインストールする必要があるため、SSMエージェントを導入できないRDSには接続できません。
方式②:Session Manager + Fargate
方式①のRDSに接続できない点を補う方式です。Session Manager の後ろにFargateの踏み台コンテナを配置し、そこから各目的サーバへ接続する方式です。こちらもSession Managerを経由しているので、接続した利用者と操作ログの取得が可能であり、かつ踏み台コンテナ1台に対して複数セッションを開始できるので、サーバ集約も可能です。 加えて、SSMエージェントをインストールしたFargateを経由するので、RDSへの接続も可能です。
新しい踏み台の実現方法
上記のサーバレス方式を参考にして、私たちのプロジェクト内での使い方に合わせた新たな踏み台の実現方法を考案しました。
プロジェクト内での要件
私たちが実現したいことは、ローカルの通信をRDSまで繋げて、pgAdmin(DB操作ツール)を使用してリモートのDBを操作することです。
方式②ではRDSに接続することは可能ですが、踏み台上でpsqlコマンドの実行によるDB操作となり、pgAdminを使用できないため、プロジェクトの要件を完全には満たせていません。(表1参照)
Session Manager + Fargate + ポートフォワーディング
方式②にポートフォワーディング機能を加え、新しい踏み台を実現します。ポートフォワーディングを利用することで、ローカル端末のpgAdminを用いてRDSへアクセスできます。これで全ての要件を満たすことができました。(表2参照)
新たな踏み台の構成図は次の通りです。(図6参照)
基本的な構成は方式②と同様ですが、ポートフォワーディング機能を利用することで踏み台を意識せずにRDSへの接続が可能となります。
新しい踏み台の作り方
新たな踏み台を構築する手順について記載します。
実現手順
Step1. FargateからRDSへポートフォワーディングする
Fargateでポートフォワーディングを実行する踏み台コンテナを稼働させることで、12345ポートに届いた通信をRDSの5432ポート(PostgreSQLのポート)まで到達させて、コンテナからDBまでのアクセスを実現します。
Fargateはサーバレスなコンテナをホスティングするサービスであり、プライベートサブネットに配置されたRDSへのアクセスを可能にします。コンテナのベースイメージにはalpine/socatを指定して、起動時のコマンドにsocatを実行するように設定します。コンテナを起動すると、container:12345に届いた通信は常にチェックされてrds:5432に転送することができます。(図7参照)
Step2. FargateにSession Managerエージェントを導入する
図6の状態では、踏み台コンテナはプライベートサブネットに配置されているため保守端末からはインターネット経由で接続ができません。Systems Managerはサーバ運用に関する統合サービスであり、その機能のひとつであるSession Managerを使うことでプライベートサブネットに配置された踏み台コンテナに接続することができます。この機能はECS Execと呼ばれます。
使用前の前提条件としてFargate側でECS Execの有効化が必要なので、細かい手順は公式ドキュメントをご参照ください。 ローカル端末にAWS CLIのセッションマネージャプラグインをインストールして以下のaws ecsコマンドでFargateに接続できます。
$ aws ecs execute-command -cluster \
--task [task-id] \
--container [container-name] \
--interactive \
--command “/bin/bash”
保守端末上のターミナルで上記コマンドを実行するとFargateに接続できるようになり、さらにそこからpsqlコマンドを実行すればRDSを操作することができます。(図8参照)
ここまでは、表1で説明したサーバレス方式②と同じ状態です。
Step3. ローカル端末からFargateへポートフォワーディングする
新たな踏み台を実現するために、Session Managerのポートフォワーディング機能を使用して、保守端末の指定ポートに届いた通信をRDSの指定ポートに転送します。
以下のAWS CLIのssmサブコマンドで保守端末の12345ポート(clinet:12345)の通信をリモートのFargateの12345ポート(container:12345)に転送することができます。 このコマンドを実行した状態でlocalhostの12345ポートにリクエストを送信すると最終的にRDSへポートフォワーディングすることができます。(図9参照)
$ aws ssm start-session --target <ターゲット名> \
--document-name AWS-StartPortForwardingSession \
--parameters '{"portNumber":["12345"],"localPortNumber":["12345"]}
考慮事項
この方式を具体的に実現するために、以下の考慮すべき点があります。
- Session ManagerからFargateにつなぐ際の接続IDが毎回変わること
- コンテナのセキュリティ対策
考慮事項1. Session ManagerからFargateにつなぐ際の接続IDが毎回変わること
踏み台コンテナは、常時稼働させているとその分攻撃されるリスクがあるので、使うときに起動して使い終わったら停止する運用が良いです。しかし、この運用にすると、Session Managerからコンテナに接続するときのタスクIDが毎回変わってしまうため、手順が多くなり利用負荷が高くなってしまいます。
RDSへ接続する際の利用ステップは次の通りです。
- 踏み台コンテナを起動する
- 起動したコンテナのタスクIDをメモする
- Session ManagerコマンドでメモしたタスクIDを指定してポートフォワーディングを開始する
- 作業が終了したら踏み台コンテナを停止する
私たちはこれらを一度に実行するシェルスクリプト(rds_connector_sample.sh)を作成して自動化しました。参考情報として、私たちが作成したシェルスクリプトを添付します。 rds_connector_sample.shをダウンロード
rds_connector.shを保守端末のWSLターミナル上で起動することで、保守端末からpgAdminを用いたDB操作が可能になります。 作業が終わったらCtrl+Cを入力することでコンテナを自動停止できます。
考慮事項2. コンテナのセキュリティ対策
踏み台コンテナに使用しているFargateではOSレイヤでのセキュリティ対策は考えなくて良いですが、コンテナに対してはユーザが責任を負わなければなりません。つまり、コンテナイメージに対して悪意のあるプログラムがないのか検出する仕組みが必要になります。
その対策として、コンテナイメージの保管庫であるECRにはコンテナイメージの脆弱性をスキャンする機能があります。ECRを利用して定期的にイメージスキャンしてセキュリティを強化しています。
運用後の気づき
現在は実運用のフェーズを迎えていないため、アプリのリリース時に新しい踏み台を数か月間使用して、気づいた点について記載します。
サーバレスのメリット
API/バッチサーバは既にサーバレスでしたが、踏み台をサーバレスにしたことで改めてその良さを実感しました。サービス全体をサーバレスアーキテクチャに統一したことでシンプルな構成となり、OSに関しては一切気にする必要がなくなり、サーバの管理や運用の負荷がなくなりました。
利便性の向上
EC2の踏み台サーバを使用していた頃は、インスタンスを起動して、TeraTermで接続して、TeraTerm のSSH転送機能でポートフォワーディングを設定して、というように接続作業のみで5分ほど時間が掛かっていました。ECS Fargateに変更してからは、接続を自動化したことで作業時間の短縮と手順書の排除ができ、利便性を向上させることができました。
AWSの最新アップデート情報
2022年5月にSession Managerがリモートホストへポートフォワーディングできるようになりました。 これを利用すればFargateコンテナからRDSへのポートフォワードを、socatコマンドを使わずにSession Managerが代わりに行ってくれます。つまり、実現手順のStep1を省略することができ、より簡単に扱えるようになりました。リモートポートフォワーディングの使い方は、Step3で実行するコマンドを下記のように変更するだけです。
$ aws ssm start-session --target <ターゲット名> \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host":["RDSのホスト名"],"portNumber":["5432"], "localPortNumber":["12345"]}'
ただし、この機能はSession Managerのバージョン 3.1.1374.0 以上が必要であり、ECS Execを有効化して自動インストールされるSSM エージェントはこれより古いため、手動でインストールする必要があります(2022/10/6時点)。 アップデートの対応が入ったら、socatコンテナをbusyboxのような必要最低限のコンテナに変更して、リモートホストのポートフォワーディングを使用する予定です。
さいごに
プロトタイプに限った話ではなく、インフラにおいて現状の構成に改善を加えていくことは重要です。昨今では運用の改善に力を入れたり、アプリケーション開発に関与したり、カバー範囲を広げてシステムの信頼性を向上させる動きが、インフラエンジニアには求められています。この役割はSRE(Site Reliability Engineering)とも呼ばれ、インフラエンジニアの活躍の幅も広がります。
今回紹介したサーバレス方式について、新たな導入効果の発見や更なる改良があった場合は続編としてお知らせしたいと思います。最後まで読んでいただきありがとうございます。