GCP HTTP(S) load balancing 配下のnginxでクライアントIPを取得する方法
GCP HTTP(S) load balancing の X-Forwarded-For ヘッダは少し変わっているのでメモ。
X-Forwarded-For とクライアントIP
ELBや他のproxyを使って、その配下のサーバにリクエスト元のIPアドレスを伝える際には、X-Forwarded-For
ヘッダが使われます。
X-Forwarded-For: $remote_addr
リクエストにすでにX-F-Fヘッダがあった場合は、後ろに追加します。
X-Forwarded-For: $http_x_forwarded_for, $remote_addr
となります。
X-F-Fを受け取ったサーバでは、アクセス元のIPが信用できるIPアドレスまたはIP帯域の場合に、X-F-Fの最後のIPアドレスを、remote_addrとして利用します。
nginxでは ngx_http_realip_module
を使います。
set_real_ip_from 192.168.1.0/24; real_ip_header X-Forwarded-For;
アクセス元が 192.168.1.0/24
の場合、X-F-Fの最後のIPを利用するという意味です。
GCP HTTP(S) load balancing の X-F-F
本題のGCP HTTP(S) load balancingの場合、X-F-Fは
X-Forwarded-For: 1.2.3.4, 35.186.x.x
となります。この場合、1.2.3.4 が実際のclientのIPアドレス、35.186.x.xがロードバランサーに割り当てたIPアドレスです。また、アクセス元のIPアドレスは、以下の帯域(2017/2/21現在)となります。
130.211.0.0/22 35.191.0.0/16
この状態で、
まず
set_real_ip_from 130.211.0.0/22; set_real_ip_from 35.191.0.0/16; real_ip_header X-Forwarded-For;
という設定をいれると、nginxはアクセス元を信用してX-F-Fの最後の一つをクライアントIPだとします。この場合は 35.186.x.x になってしまい、クライアントIPではありません。
さらに設定が必要になります。
set_real_ip_from 130.211.0.0/22; set_real_ip_from 35.191.0.0/16; set_real_ip_from 35.186.x.x; real_ip_header X-Forwarded-For; real_ip_recursive on;
set_real_ip_from 35.186.x.x
と real_ip_recursive
を追加しました。real_ip_recursiveを有効にすると信用できるIPである限り再帰的にX-F-Fをたどっていきます。set_real_ip_fromにロードバランサーのIPを追加することで、35.186.x.xを信用し、次のIPアドレスをクライアントのIPとして採用ができるようになります。
この設定でようやく$remote_addrが1.2.3.4となりました。
まとめ
IPアドレスの追加の機会はそれほど回数がないので、IPアドレスをバランサーに追加した際にset_real_ip_from の追加を忘れるということは必ず発生するでしょう(発生した)。
アクセスログで正しくIPアドレスが解決ができているかの監視を行ったり、blogに書くなどしてチームに共有する必要があります。
Googleの皆様におかれましては、X-F-Fだけではなく、Akamai/CloudFlareのように True-Client-IP
ヘッダの採用をお願いしたく候。True-Client-IPがあれば、ロードバランサーのIPアドレスをnginxのconfに書く必要がなくなり、
set_real_ip_from 130.211.0.0/22; set_real_ip_from 35.191.0.0/16; real_ip_header True-Client-IP;
だけとなります。
何卒 :bow: