Amazon CloudFront に Akamai の True-Client-IP ヘッダーを実装してみた
いわさです。
Amazon CloudFront のような CDN や ALB のようなリバースプロキシを挟んでいると、オリジンから見たときのクライアント IP アドレスは通常はそれらのリソースの IP アドレスになります。
そういった構成でオリジン側でクライアント IP を使って何か制御したい時は一般的にはX-Forwarded-Forというクライアント IP を渡すヘッダーを使います。CloudFront でもオリジンへのリクエスト時に X-Forwarded-For を自動で付与してくれます。
たいていはこれを使えば良いのですが、X-Forwarded-For にはクライアント IP だけでなく、経由した HTTP プロキシやロードバランサーの IP もカンマ区切りで並びます。
CloudFront 経由の場合もエッジサーバーの IP が後ろに付くので、オリジン側はカンマで分割して先頭を取り出す処理が必要になります。
先日 Akamai から Amazon CloudFront に CDN を移行する機会がありました。
その際、オリジンはX-Forwarded-ForではなくTrue-Client-IPというヘッダーでクライアント IP の判定を行っていました。
True-Client-IP は Akamai で使われてきたオプションヘッダーです。
Cloudflare Enterprise プランでも同じ名前のヘッダーを提供されているようです。
True-Client-IPprovides the original client IP address to the origin web server.True-Client-IPis only available on an Enterprise plan. (中略) There is no difference between theTrue-Client-IPandCF-Connecting-IPheaders besides the name of the header. Some Enterprise customers with legacy devices needTrue-Client-IPto avoid updating firewalls or load-balancers to read a custom header name.
こちらのヘッダーの場合、複数の IP アドレスが付与されたり、エッジからオリジンの間で途中で追加や変更がされずに単一のクライアント IP アドレスのみを取得できるようです。なるほどね。
オリジン直すのもあれだしな、でも CloudFront ではこのヘッダー無いしな、どうするかな、と思っていたのですが、CloudFront Functions で同ヘッダーを付与する AWS 公式のサンプルコードが公開されていることを知りました。
こ、これでいいじゃん。
試してみたので紹介します。
やってみた
CloudFront 自体には True-Client-IP を自動付与する設定はないので、CloudFront Functions のビューワーリクエストで付けます。
公式サンプルのコードはこれだけです。

event.viewer.ip には CloudFront へ接続してきた相手の IP が入るので、それを true-client-ip ヘッダーに入れてリクエストを返すだけです。それだけ!
クライアントと CloudFront の間にさらに別のプロキシやロードバランサーがある場合は、そのプロキシ・ロードバランサーの IP になりますので注意しましょう、まぁそれはTrue-Client-IPでも同じだが。
注意点として、CloudFront Functions でヘッダーを付けただけでは、オリジンまで転送されないです。オリジンリクエストポリシーの追加設定が必要です。
試しに、オリジンリクエストポリシーを付けていない状態で CloudFront 経由でアクセスしたところ、オリジン側で次のようになりました。
$ curl -s https://xxxxxxxxxxxxx.cloudfront.net/
{"true-client-ip":null,"x-forwarded-for":"203.0.113.10, 198.51.100.10","cloudfront-viewer-address":null}
true-client-ip が null になっていますね。これはヘッダー自体渡されたわけじゃなくてオリジン側で出力しているのですが、実体としてはヘッダー自体わたってきてないです。
CloudFront Functions でヘッダーを付けても、オリジンリクエストポリシーの許可リストに入っていないヘッダーは、CloudFront がオリジンへ渡す前に削除してしまうためです。
公式サンプルの README にも次のように注意書きがあります。[1]
Important: You must add this header to the allowed list of headers forwarded to the origin as part of a CloudFront origin request policy. Otherwise, CloudFront removes this header before making the request to the origin.
ということで、True-Client-IP を転送するオリジンリクエストポリシーを作成し、ビヘイビアに関連付けましょう。
ヘッダーの許可方式はホワイトリストで、True-Client-IP を1つ追加するだけです。

ディストリビューションのビヘイビアには、CloudFront Functions(ビューワーリクエスト)とこのオリジンリクエストポリシーの両方を関連付けておきます。

デプロイ後、同じ URL にもう一度アクセスした結果です。
$ curl -s https://xxxxxxxxxxxxx.cloudfront.net/
{"true-client-ip":"203.0.113.10","x-forwarded-for":"203.0.113.10, 198.51.100.11","cloudfront-viewer-address":null}
今度は true-client-ip に自分のグローバル IP(203.0.113.10)が単一の値で入りました。
さいごに
本日は CloudFront Functions とオリジンリクエストポリシーを使って、オリジンに True-Client-IP ヘッダーでクライアント IP を渡してみました。
X-Forwarded-Forでやることが多いと思いますが、現行やパース処理の実装とかオリジン側の都合に合わせたいときもあるので覚えておくと良さそうです。
また、True-Client-IP以外にも各 CDN サービスごとに専用ヘッダー結構あるみたいですね。CloudFront 以外の CDN から移行する際はそういった移行元の独自ヘッダーについても注意しておくと良いですね。
ちなみに、クライアント IP をオリジンで使いたいだけなら CloudFront も独自ヘッダー CloudFront-Viewer-Address というものもあります。
こちらは CloudFront Functions 実装が不要ですが、オリジンリクエストポリシーへの追加は必要です。値が IP アドレスとポート番号の組み合わせになります。









