前章: プライベートサブネットにEC2を置いたWebアプリケーションを作成する②
続いては、セキュリティを高める設定をしていきます。
手順
以下の手順で一つずつセキュリティを高めていこうと思います。
- Route53でドメインを登録する
- ACMで証明書を発行してHTTPS通信を有効化する
- WAFを有効化する
- Secrets Managerを利用して、認証情報のハードコーディングをやめる
- [おまけ1] Cloud Frontのキャッシュを有効化してレスポンスを早くする
- [おまけ2] Webサーバーの冗長化
Route53でドメインを登録する
マネジメントコンソールのRoute53を開いたら、「ドメインを登録」を左のメニューバーから選択します。
取得したいドメイン名を入力しチェックアウトに進み、必要な連絡先情報を入力したら作成してください。
リクエストしたドメイン名でパブリックホストゾーンが自動作成されたら、レコードの作成に進みます。
レコードの作成
左のメニューバーから「ホストゾーン」に進み、作成したホストゾーンを選んだら、レコードの作成をクリックして作成画面に遷移します。レコード名を入力し、エイリアスをオンにします。
トラフィックのルーティング先にエンドポイントとしてALBを登録し、レコードを追加します。
ACMで証明書を発行する
Certificate Managerにマネジメントコンソールから移動します。
リクエストボタンから証明書をリクエストします。
- ドメイン名: 作成したドメイン名を入力
- 検証方法: DNS検証
- キーアルゴリズム: RSA2048
これらを設定し、作成したらCNAMEが作成されるのでこれをRoute53に登録します。
- リクエストした証明書の詳細画面のドメイン欄にあるRoute53で「レコードを作成」をクリック
- ステータスが発行済みになるまでしばらく待つので次の作業へ
ALBにHTTPSリスナーを登録する
今回は、ALBからEC2インスタンスまではVPC内のセキュアな通信と考えて、ALBをHTTPS通信のターミネーションとして設定していきます。
設定済みのALBの詳細画面を開き、「リスナーとルール」では「リスナーを追加」をクリックして、
- プロトコル: HTTPS
- アクションのルーティング: ターゲットグループへ転送
- サーバー証明書: 作成したものを選択
セキュリティグループの確認
インターネットからALBへの接続はHTTPS通信のみを許可して、ALBからEC2インスタンスへはHTTP通信のみを許可します。したがって、EC2インスタンスのセキュリティグループは変更する必要が無く、ALBのセキュリティグループを修正します。
これで、HTTPS通信が可能になったはずなので、作成したドメイン名を用いて
https://{your-domain}
と検索してみてください。全セクションと同じ画面が出たら成功です。
WAFを有効化する
ありがたいことに、WAFを有効化するのは簡単です。
作成したALBの詳細画面から統合のタブをクリックし、WAFのプルダウンを開きます。
「WAFウェブACLを関連付ける」とあるので、それをクリックして待てば完了です。
Secrets Managerを利用して、認証情報のハードコーディングをやめる
前章ではRDSの認証情報をapplication.yamlにハードコーディングしてしまっていて、セキュリティ上よろしくない状態なので改善していきます。
AWSでは、パスワードのような認証情報を安全に管理する方法として
- System ManagerのParameter Store
- Secrets Manager
という二つの手段が用意されています。
データベースの認証情報は定期的にローテーションすることが推奨されているため、今回はSecretManagerを利用してRDSの認証情報を管理します。
RDSの認証情報の登録
マネジメントコンソールのSecrets Managerで新しいシークレット情報を登録します。
- シークレットタイプ: Amazon RDSデータベースの認証情報
- 認証情報: スーパーユーザーのユーザー名とパスワード
- 暗号化キー: デフォルト
- データベース: 管理するDBインスタンスを選択
- シークレット名: flash_card/rds
データベースの認証情報は定期的にローテーションすることが推奨されているため、今回はSecretManagerを利用してRDSの認証情報を管理します。
と書いたはものの、今回は説明を省略するので、デフォルトのままで問題ありません。
シークレットの利用
シークレット情報を作成したら、EC2インスタンスに移動し、これらの値を環境変数に設定するように修正します。
Secrets Managerから値を取得する手段として、SDKを利用するのが一般的ですが、ここでは少し特殊にシェルスクリプトから、CLIを利用して環境変数に値を代入します。
Session Managerでいつも通りログインしたら、適当な場所にシェルスクリプトファイルを作成します。
作成したシェルスクリプトには次のように書き込みます。
secret_values=$(aws secretsmanager get-secret-value --secret-id flash_card/rds --region us-east-2 | jq .SecretString | jq fromjson)
export DATABASE_HOST=$(echo $secret_values | jq -r .host)
export DATABASE_USERNAME=$(echo $secret_values | jq -r .username)
export DATABASE_PASSWORD=$(echo $secret_values | jq -r .password)
export DATABASE_NAME=$(echo $secret_values | jq -r .dbname)
export DATABASE_URL=jdbc:mysql://$DATABASE_HOST:3306/$DATABASE_NAME
region情報にはアプリケーションを作成しているリージョンの情報に適宜修正してください。
このシェルスクリプト(sample.sh)を実行してみます。
$ chmod +x sample.sh
$ ./sample.sh
$ echo $DATABASE_NAME
echoコマンドで、データベース名が出力されていたらOKです。
application.yamlの修正
この環境変数を利用するために、application.yamlを修正します。
spring:
datasource:
url: ${DATABASE_URL}
username: ${DATABASE_USERNAME}
password: ${DATABASE_PASSWORD}
実際に、アプリケーションが動かせることを確認するために、ビルドをします。
$ ./gradlew build
テストが通れば、完了です。
[おまけ1] Cloud Frontのキャッシュを有効化してレスポンスを早くする
本編は今回のアプリケーションとは関係ありませんが、研修で実際に作成したときにはサイズの大きい静的ファイルがあったので、Cloud Frontのキャッシュ機能でレスポンスを早くすることにしました。せっかくなのでこちらも紹介しておきます。
もし、背景画像など変更頻度が低いサイズの大きな静的なファイルがアプリケーションに組み込まれている場合は、利用を検討してみてください。
今回のユースケースとしては、クライアントがページを表示しようとしたときに背景画像を取得するためにリクエストを送信します。そのリクエストをCloud Frontにリダイレクトさせてキャッシュで返すのが今回行いたいことです。
S3バケットの作成
静的ファイルを保管しておくためのS3バケットを作成します。
S3には機密情報は保存しないと仮定してS3のブロックパブリックアクセスを無効化して作成します。
そのほかはデフォルトのままで大丈夫です。作成したバケットには適当なファイルを保存します。
背景画像を保存する場合には、Spring内のクラスパスと同じになるようにプレフィックスを設定すると後続の設定が楽になります。
ディストリビューションの作成
Cloud Frontで、ディストリビューションの作成をクリックし作成していきます。今回は全てデフォルトのまま作成して問題ありません。
ALBの設定
このままでは、S3にアクセスされることが無いので、ALBからCloud Frontにリダイレクトするような設定を行っていきます。
ALBのリスナーを追加を選択し、「デフォルトアクション」のアクションのルーティングにはURLにリダイレクトに変更します。
完全なURLでは、#{host}の部分だけをCloudFrontのディストリビューションドメイン名を設定することで、パスはそのままにCloudFrontにリダイレクトすることができます。
リスナーの適用
作成したら、リスナーの優先順位を変更します。今のままではもともと作成していたリスナーが最小に適用されてしまい、ターゲットグループへ転送されてしまうので、ALBのリスナーとルールタブから、ルールを管理を選択し、今作成したものの順位を先に適用するように設定します。
これで、設定は完了です。
[おまけ2] Webサーバーの冗長化
EC2インスタンスから、「イメージを作成」をクリックしてイメージ名を設定して作成します。ここで作成したAMIを利用して、EC2インスタンスを新しくもう一つのAZに立ち上げることでアプリケーションの冗長性を高めることができます。
セキュリティグループはもう一つのEC2インスタンスと同様のものを作成し設定することで、RDSへもALBからのアクセスも許可されます。
ALBのターゲットグループにもこのEC2インスタンスを登録してリクエストの分散先として登録します。
ターゲットを登録から、新しく作成したEC2インスタンスを保留中として含めるで追加します。
これで冗長化は完了です。
まとめ
長くなりましたが、今回作成したかったアプリケーションの説明は以上となります。
極力セキュリティ面を上げて作成したので、運用面では課題の残る構成となっていますが、サイバー攻撃での被害を考慮するとある程度仕方のないコストだと思っています。
特に、CI/CDについては外部からアクセスできないのでGitHub Actionが利用できず、AWSのCI/CDをサポートするCodeシリーズを利用することが要求されます。踏み台サーバーはセキュリティ上立てたくないというのが今回の方針だったので、セキュリティと運用コストを天秤にかけながら構築していくことをお勧めします。
記事一覧
プライベートサブネットにEC2を置いたWebアプリケーションを作成する ーイントロー
プライベートサブネットにEC2を置いたWebアプリケーションを作成する①
プライベートサブネットにEC2を置いたWebアプリケーションを作成する②
プライベートサブネットにEC2を置いたWebアプリケーションを作成する③