🙃

CloudFrontが不正なリクエストヘッダーをいい感じにしてくれていた件

に公開

これは何?

不具合調査の中で、CloudFrontがリクエストヘッダーの前後の半角スペースをトリムする、という仕様?挙動?があることを知ったので、その備忘録です。

不具合の原因調査

ある施策の対応中に発生した不具合について、原因調査の流れと内容をざっくりまとめていきます。

まず、前提としてシステムの構成について簡単に説明します。

基本的には以下の構成(Development環境、以降Dev環境)になっています。
ただ、不具合が起きた環境はインフラ改修が入っていたこともありCloudFrontをかませず名前解決でELBに直接アクセスするような構成(Temporary環境、以降Temp環境)にしていました。

Dev環境では問題なかったテストケースが、Temp環境だと起きるという状態だったので、実際にどこでエラーが起きているのかをアクセスログを掘った内容をまとめると以下です。

  • リクエストの処理フロー
    • Dev環境
      • アプリケーションサーバーまでリクエストが到達
      • ELBのアクセスログにRFC違反のタグ無し
    • Temp環境
      • リバースプロキシで400エラー発生
        • リクエストヘッダーがRFC違反していることによるパース処理エラー
      • ELBのアクセスログにRFC違反のタグ(NonCompliantHeader)がついていた

クライアント側のソースコードを確認すると、カスタムリクエストヘッダーのキーの末尾に半角スペースが入ってる箇所(ex. hoge : fugaのようにhogeの末尾にスペースが入っている)を発見しました。擬似的にヘッダーに半角スペースを含んだリクエストをテストしたところ挙動を再現できたため、リクエストヘッダーにASCII以外の文字が含まれていることが不具合の原因であることが分かりました。

ここで気になることはなぜDev環境では同じリクエストなはずなのに、問題なく処理できているのかという点です。
そこでCloudFrontの有無がDev環境とTemp環境の分かりやすい差分なので、そこを深掘ることにしました。
具体的にはCloudFrontに来たリクエスト内容をLambda Edgeにて出力し、CloudFrontを通した際にリクエストヘッダーがどうなっているかを確認しました。

結果は冒頭でも書いている通り、リクエストヘッダーの空白スペースが除去されていました
より詳細には以下のような挙動になっていると確認できました。

  • 半角スペースに関しては処理されることを確認(全角スペース _などは除去されない)
    • ヘッダーのキー
      • 後ろの半角スペースのみ除去される
      • 前に半角スペース入れると、前のヘッダーの値の続きだと判定される
    • ヘッダーのバリュー
      • 前後ともに半角スペースは除去される

CloudFrontのこの挙動により助けられていたようなので、今後該当箇所は修正予定です。
また、今回の検証で初めてLambda Edgeを使ってみましたが、ログの出力以外のつまづく点は少なく、ささっとリクエストやレスポンスを加工するには使えそうだと思いました。

まとめ

  • CloudFrontを通すとリクエストヘッダーの半角スペースが除去された
  • インフラはできるだけ同じ環境を用意した方が事故が少ない
  • CloudFront + Lambda Edgeはこういうリクエストの検証にも使える
    • CloudFrontのアクセスログはカスタムヘッダーが出せないので不便

おまけ

CloudFrontでLambda Edgeをトリガーできるイベント

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-cloudfront-trigger-events.html

今回の検証では、CloudFrontに入ってきたリクエスト、後続コンポーネントに渡される前のリクエストが確認したかったため、ビューワーリクエストとオリジンリクエストをLmabdaトリガーとして設定した。
レスポンスの加工もできるみたいなので、便利そう。

Lambda Edgeのログ出力先が分かりづらかった

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/edge-functions-logs.html#cloudfront-function-logs

Lambda@Edge は、関数ログを CloudWatch Logs に自動的に送信し、関数が呼び出された AWS リージョンにログストリームを作成します。AWS Lambda で関数を作成または変更するときは、デフォルトの CloudWatch ロググループ名を使用するか、カスタマイズできます。

ドキュメントに記載してある通り、CloudWatch Logsのロググループが作成されるリージョンが、関数が呼び出されたリージョンに作成されます。そのためus-east-1で関数を作ってても、東京リージョンからアクセスしたらap-northeast-1にログストリームが作成されます。

AWSコンソールでLambda関数からCloudWatchに飛ぶと、「ロググループが存在しません」と表示されるので初見でつまづきました。

参考

https://dev.classmethod.jp/articles/lambda-edge-logs-role/

Lambda EdgeのIAM設定

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-permissions.html

信頼ポリシーに"edgelambda.amazonaws.com"を追記

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "edgelambda.amazonaws.com",
                    "lambda.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

IAMポリシーはよしなに。

参考

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-permissions.html
https://techracho.bpsinc.jp/baba/2024_07_30/143561

Omiai Tech Blog

Discussion