本記事の趣旨
とあるアプリケーションの前段にCloudFrontを挟む作業をしていたのですが、CloudFrontが挟まるとnginx側でシンプルに X-Forwarder-for
を使用してuser側のIPを取得することができませんでした。
一部にALB越しでのアクセスが存在しており、かつ無停止でalbからcloudfrontへ移行する必要がありました。
なので、nginx側でcloudfront越しのIPとalb越しのIPどちらのIPも適切なものを所得できる必要がありました。
一部のブログではset_real_ip_from
ディレクティブに 0.0.0.0/0
を挿入し、すべてのIPを信頼するといったやり方が紹介されていますが、 X-Fowarded-For
は偽装可能なので今回は使用を避けました。
(もちろんアプリケーションの仕様によってはこちらを採用しても良いと思います。)
そこで、cloudfrontのカスタムヘッダである CloudFront-Viewer-Address
を使用しクライアントのIP:Portを取得し、そのままだとport番号まで文字列に含まれているので一部加工してからアプリケーションへ渡すことにしました。
また、alb cloudfrontともに対応するために該当ヘッダが存在するかどうかで最終的に渡すipを決めています。
前提
X-Forwarded-for
とかそのあたりの使用は既に理解しており、その上でタイトルのことを実現したい人に向けて書いてます。
X-Fowarded-For
周りに関しては下記ブログがわかりやすいのでおすすめです。
こんばんは、X-Forwarded-For警察です - エムスリーテックブログ
構成
解決方法
CloudFrontのカスタムヘッダを使用する
Amazon CloudFront はクライアント IP アドレスと接続ポートヘッダーのサポートを追加
2021年のアプデでCloudFrontのカスタムヘッダとして、リクエスト元のIPとPortをヘッダ情報に記載してCloudFrontの後ろのリソースへアクセスを送信することができるようになりました。
# 以下の形で登録される
CloudFront-Viewer-Address: 127.0.0.1:4430
が、このままだとportも記述されており、IPだけがほしいのでnginx上でluaを使用し取り出します。
# server ディレクティブの中に記述
set $real_ip "";
set_by_lua_block $real_ip {
local cfip = ngx.req.get_headers()["CloudFront-Viewer-Address"]
n = string.find(cfip, ":")
ip = string.sub(cfip, 1, n-1)
return ip
}
本来であればこの $real_ip
を real_ip_header
に文字列として挿入したいのですが、syntaxとしてサポートしていないので別の方法を考えます。
今回はphp-fpmを使用しているので fastcgi_param
として REMOTE_ADDR
を $real_ip
で上書きしてあげます。
fastcgi_param REMOTE_ADDR $real_ip;
# や
fastcgi_param HTTP_X_FORWARDED_FOR $real_ip;
# や
fastcgi_param HTTP_X_REAL_IP $real_ip;
# など
参考
CloudFront=>ELB=>EC2(nginx) で、接続元 IP アドレスを取得する - Qiita
CloudFront, ALBどちらでも正しいクライアントIPを取得する
httpディレクティブ内でnginxの変数 $remote_addr
にIPが入るように設定を行います。
set_real_ip_from <VPC CIDRレンジとか>;
real_ip_header X-Forwarded-For;
次に、例のluaスクリプト部分でカスタムヘッダが存在するかどうかで $real_ip
の中身を決めます。
# server ディレクティブの中に記述
set_by_lua_block $real_ip {
if (ngx.req.get_headers()["CloudFront-Viewer-Address"]) then
local cfip = ngx.req.get_headers()["CloudFront-Viewer-Address"]
n = string.find(cfip, ":")
ip = string.sub(cfip, 1, n-1)
return ip
else
return ngx.var.remote_addr
end
その後は同じ様にfastcgi等に $real_ip
を渡します。
余談
今までnginxのdocker imageを使用していましたが、luaが使えなかったので今回はopenrestyのdocker imageを採用しました。