LoginSignup
5
1

More than 1 year has passed since last update.

【nginx】CloudFront, ALBどちらからのリクエストでも適切なremote_addrを取得する【php-fpm】

Last updated at Posted at 2022-04-27

本記事の趣旨

とあるアプリケーションの前段に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警察です - エムスリーテックブログ

構成

Screenshot_2022-04-27 17.23.48_iQP5qr.png

解決方法

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_ipreal_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を採用しました。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1