株式会社クラシコム テクノロジーグループの冨田です。
北欧、暮らしの道具店とクラシコムのコーポレートサイトにCloudFrontを導入しました。以前から画像についてはCloudFrontやimgixを利用し配信していました。今回はサイト自体を配信するようにしました。
背景
2022年8月に東証グロース市場へ上場しました。
- メディアへの露出が増えたため、突発的なアクセスがきても耐えられるようにしたい。
- 既に導入していたCloudFront Security Savings Bundleでコストを抑えたい。
という動機からCloudFrontを導入しました。
やったこと
Cognitoによる認証
クラシコムの検証環境(ステージング環境)はALBでCognitoによる認証をかけており、社員のみアクセス可能にしていました。今回ALBの前段にCloudFrontを導入しましたが、そのままの構成でCloudFrontを前段に置いてしまうと社員以外もキャッシュされた内容がCloudFrontで閲覧可能になってしまいます。
検証環境(ステージング環境)と本番環境でなるべくインフラの構成を合わせておきたいのでLambda@Edgeでcognito-at-edgeをデプロイし、Cognitoでの認証をかけました。
Lambda@Edgeの制約で下記の点ではまってしまいました。
- Lambda環境変数が利用できないのでCognito
userPoolAppSecret
をAWS System Manager パラメータで管理するようにしました。 - LambdaのNode.jsはSDK for JavaScript 2.1055.0が標準 バージョン3を組み込もうとしたが1MBの関数および組み込みライブラリの最大圧縮サイズにひっかかってしまいました。
ALBへのリクエストの制限
CloudFrontを用意したため、ALBに直接アクセスされてしまうと用意した意味がありません。
CloudFrontからALBへのアクセスはリクエストにカスタムHTTPヘッダを付与し、カスタムヘッダが付与されているものだけに応答するようにしてあります。2022年2月からCloudFront マネージドプレフィックスリストが用意されたため、ALBのセキュリティグループで、CloudFrontからのインバウンドHTTPSアクセスのみを許可するようにしました。また、万が一に備えてステージング環境においてはALBでもCognitoによる認証は引き続きかけています。
はまったこと
クライアントのIPアドレス、スキーマの取り扱い
ECS Fargate上でNGINXとphp-fpm、Laravelを利用しています。
NGINXのアクセスログでクライアントIPアドレスやリクエストスキーマを記録しています。また、Laravel上でもクライアントIPアドレスやリクエストスキーマを利用しています。
クライアントのIPアドレスはNGINXで real_ip_header X-Forwarded-For;
リクエストヘッダ、 set_real_ip_from 192.168.1.0/24;
で信頼できるアドレス、 real_ip_recursive on;
で信頼されない最後のアドレスを取得するようにしていました。
CloudFrontのIPアドレスはJSON形式で公開されていますが不定期に変更されるため、set_real_ip_from
を自動的に更新するのは少し面倒です。
クラシコムではインフラのアドバイザーとしてカヤックの藤原さんにアドバイス、レビューをしていただいています。CloudFront-Viewer-Address
やCloudFront-Forwarded-Proto
リクエストヘッダを利用すれば良いとアドバイスをいただきました。
CloudFront-Viewer-Address
はIPアドレスとポート番号が含まれているため、IPアドレスのみを利用したい場合には分割する必要があります。
NGINXのLua moduleを利用し分割しました。
NGINXのオフィシャルのDockerイメージそのままではLua moduleは利用できないため、オフィシャルのドキュメントをもとにビルドしました。
運用は無事回っているもののデプロイ時にLua moduleのビルド時間がのびてしまいました。今後の課題としてはビルド方法の見直しを行うか、OpenRestyのイメージを利用するか、CloudFront Functions側に任せようかなと思います。
カナリアリリースやメンテナンス
カナリアリリースやメンテナンス時にALBのリスナールール 送信元IPアドレス に 作業者のIPアドレスを設定して動作確認をしていました。CloudFrontを経由することになり送信元IPアドレスがCloudFrontになってしまいました。
ALBのリスナールール の HTTPヘッダで ヘッダ名は CloudFront-Viewer-Address
を指定し、比較文字列には 作業者のIPアドレスを指定しました、ポート番号はワイルドカード文字列を利用しました。
設定の例
203.0.113.1:*
並行運用
日中に旧ALBからCloudFrontへDNSの切り替えを行い、DNSのキャッシュが切れるまで並行運用を行いました。
DNSを変更して3分後には大半のトラフィックはCloudFront経由に切り替わったのですが、CATV系のプロバイダやBotは1週間経過しても旧ALBへのアクセスが続きました。
ALBも運用中にIPアドレスが変わることもあるということで旧ALBは退役させました。
まとめ
CloudFrontの導入で7/12のテレビ東京系列のWBS(ワールドビジネスサテライト)でとりあげられたタイミングや、8/5の上場当日も無事乗り切ることができほっとしました。
一点悔やまれるのはALB側のWAFにレートリミットを設定してしまい、一部のリクエストをブロックしてしまいました。今後も改善を重ね「サイトに快適・安全にアクセスできて当たり前」な状態にしていきたいと思っています。