こんにちは!SRE兼クラウドアーキテクトの私です。
インフラの世界へ足を踏み入れたばかりの頃、「あれ? Webサーバーのアクセスログを見たら、アクセスしてきているはずのユーザーのIPアドレスが、なんだか変な数字ばかりになっている……!」と頭を抱えた経験はありませんか?
「さっきまで自分の手元のパソコンからアクセステストをしていたのに、ログに記録されているのは全然知らないIPアドレスばかり……」
実はこれ、AWSの「ALB(Application Load Balancer)」という仕組みを使い始めると、誰もが一度は通る「お約束の迷宮」なんです。
今回は、パケットの気持ちになり、現実世界の「郵便配達」の仕組みに例えながら、この謎をスッキリと解き明かしていきましょう。一歩ずつ丁寧に解説しますので、どうぞリラックスしてついてきてくださいね!
—
1. なぜ「真のクライアントIP」が見えなくなってしまうのか?
まずは、私たちが普段使っているWebシステムで何が起きているのか、身近な例えでイメージしてみましょう。
郵便配達に例える「ロードバランサー」の役割
想像してみてください。あなたは今、大きなお城(Webシステム)のなかにいる「お城の管理人(バックエンドのWebサーバー)」です。
城門の外には、毎日たくさんの手紙や荷物を届けてくれるお客さん(クライアント)がやってきます。
もし、お客さんが城門を叩いて直接あなたに話しかけてくるなら、「あ、〇番地のエプロンを着た〇〇さんだな」と、相手の住所(IPアドレス)をダイレクトに知ることができますよね。これが通常の直接通信です。
しかし、お城の入口があまりにも大人気で混雑してしまうため、「案内係(ALB)」を門のところに新しく配置することにしました。
案内係の仕事はこうです。
1. お客さんがやってきたら、まず案内係が荷物を受け取る。
2. 案内係は、お城の裏で控えている複数の管理人(Webサーバー)のうち、手が空いている人にその荷物をバトンタッチする。
この仕組み、お城のセキュリティや混雑緩和にはものすごく効果的なのですが、管理人(Webサーバー)の視点に立ってみると、ちょっとした困ったことが起きます。
それは、「管理人には、目の前にいる『案内係(ALB)』しか見えない」ということです。
荷物を渡してくれたのは目の前の案内係なので、バックエンドのWebサーバーから見ると、すべてのアクセスが「案内係のIPアドレス」から来ているように見えてしまうのです。
「これでは、どこの誰から来たアクセスなのか統計が取れない! 不正なアクセスをブロックしたくても、元の住所がわからないよ!」
そんな困った状況をスマートに解決してくれるのが、今回主役となる X-Forwarded-For ヘッダーという魔法のメモ書きなんです。
—
2. 魔法のメモ書き『X-Forwarded-For』の正体
案内係(ALB)は、とても気が利く優秀なスタッフです。
お客さんから荷物を受け取るとき、案内係は心の中でこう思います。
「おっと、この荷物は本当は〇〇番地に住むお客さんのものだな。裏の管理人さんに渡すとき、元の住所をメモに書いておいてあげよう」
この「本当の依頼主は誰だったのか」を書き留めておくメモ書きこそが、HTTPヘッダーの一つである X-Forwarded-For(通称:XFF)の正体です。
X-Forwarded-Forの実際の構造
ALBがバックエンドのWebサーバーへリクエストを転送する際、HTTPヘッダーの中に次のような情報をこっそり忍び込ませてくれます。
GET /index.html HTTP/1.1
Host: example.com
X-Forwarded-For: 203.0.113.195
X-Forwarded-Proto: https
X-Forwarded-Port: 443
この中の X-Forwarded-For: 203.0.113.195 という部分に注目してください。
ここには、インターネットの海を越えて最初にALBへリクエストを送ってきた「真のクライアントのIPアドレス(例:203.0.113.195)」がしっかりと記されています。
プロキシが複数ある場合の連鎖
もし、インターネットの世界が「お客さん ➔ CDN(CloudFrontなど) ➔ ALB ➔ Webサーバー」のように、複数の案内係を経由する長旅だったらどうなるでしょうか?
その場合、X-Forwarded-For の中身はカンマ(,)区切りで次のようにリレーされていきます。
X-Forwarded-For: 203.0.113.195, 198.51.100.42
- 左端:一番最初にリクエストを発信した「真のクライアント」のIPアドレス
- 右に向かって:経由してきた中継サーバー(プロキシやロードバランサー)のIPアドレスが順番に追加されていく
このルールを覚えておくだけで、複雑なマルチティア構成のネットワークトラブルに直面したときでも、パケットの足取りをスイスイと追跡できるようになりますよ!
—
3. アプリケーション側(バックエンド)でクライアントIPを正しく取得する方法
「理屈はわかったけれど、実際にプログラムやWebサーバーの設定ではどう書けばいいの?」
そんな疑問にお答えするために、実務でそのまま使える設定例を見ていきましょう。
パターンA:NginxをWebサーバーとして使っている場合
もしAWSのEC2などの上でNginxを動かし、その背後にALBがいる場合、Nginxのログフォーマットや設定を少し工夫してあげる必要があります。
何もしないと、NginxのアクセスログにはALBのプライベートIPアドレスばかりが記録されてしまいます。そこで、Nginxのコンフィグファイル(nginx.conf など)を次のように設定してみましょう。
http {
# ALBが挿入してくれた X-Forwarded-For ヘッダーから真のIPを取り出す設定
# ※本番環境では set_real_ip_from にALBが所属するVPCのCIDRなどを指定するとより安全です
set_real_ip_from 0.0.0.0/0;
real_ip_header X-Forwarded-For;
# ログフォーマットに $remote_addr を出力させると、自動的に真のクライアントIPに置き換わります
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';
}
この設定(real_ip_header)をしておくと、Nginxは自動的に X-Forwarded-For の先頭にある真のクライアントIPを「リモートアドレス」として扱ってくれるようになります。これで、見慣れたクライアントIPがログに戻ってきますね!
パターンB:Python (Flask) でアプリケーションを開発している場合
アプリケーションのコード内で直接クライアントIPを取得したい場合もあるでしょう。
Pythonの軽量WebフレームワークであるFlaskを例に見てみます。
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def index():
# X-Forwarded-For ヘッダーが存在する場合はその先頭を取得し、
# 存在しない場合(直接アクセスなど)は従来の request.remote_addr をフォールバックとして使う
if request.headers.get('X-Forwarded-For'):
# カンマ区切りの左端(一番最初のクライアント)を取得する
client_ip = request.headers.get('X-Forwarded-For').split(',')[0].strip()
else:
client_ip = request.remote_addr
return f"あなたの本当のIPアドレスは {client_ip} です!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
実務の現場では、セキュリティ上の理由や、プライベートネットワーク内からのアクセス(ALBのヘルスチェックなど)も考慮する必要があるため、ヘッダーの存在チェックやパース処理をこのように丁寧に書いてあげることが大切です。
—
4. セキュリティ上の注意点:ヘッダーの「偽装」に気をつけよう!
最後に、SREとしてのワンポイントアドバイス、そして現場で絶対に知っておかなければならない注意点をお伝えします。
ここまでお話した X-Forwarded-For は、あくまで「HTTPのヘッダー(テキストのメモ書き)」です。
つまり、悪意のある攻撃者が、自分のパソコンからわざと偽物のヘッダーを仕込んでリクエストを送ってくることが技術的には可能です。
「私は本当は1.2.3.4という安全なIPから来ましたよ」という嘘の X-Forwarded-For: 1.2.3.4 を自分で書いてALBに送りつける……なんてことも、手元のツールを使えば簡単にできてしまいます。
ALBを信頼するということ
ここで安心してください。AWSのALBは、クライアントから送られてきた不正な X-Forwarded-For ヘッダーをそのままスルーするようなお人好しではありません。
デフォルトの挙動として、ALBはクライアントから受け取った X-Forwarded-For の末尾に、「本当に今接続してきたクライアントのIPアドレス」をAWS側で責任を持って新しく追加(あるいは書き換え)してバックエンドへ転送します。
そのため、バックエンドのWebサーバー側では、
1. インターネットから直接サーバーにアクセスさせない(セキュリティグループで、ALBからの通信のみを許可する)
2. ALBを経由した正当なルートを通ってきたリクエストだけを信用する
この2点をしっかりとインフラの設計段階で担保してあげることで、偽装のリスクをきれいにシャットアウトすることができます。
—
まとめ:ネットワークの裏側を覗く楽しさを味わおう
今回は、ALBにおける X-Forwarded-For ヘッダーの仕様と、クライアントIPを特定するための考え方を解説しました。
- 課題:ロードバランサー(ALB)を挟むと、バックエンドサーバーからはALBのIPしか見えなくなる。
- 解決策:ALBが親切に添えてくれる
X-Forwarded-Forメモ(ヘッダー)を見ることで、真のクライアントIPがわかる。 - 実装:Webサーバーやアプリ側で、ヘッダーを正しく解釈する設定(Nginxのrealipモジュールやコード上のパース処理)を行う。
最初は「黒魔術のようで難しそう……」と感じるクラウドのネットワーク設定も、こうして郵便配達の仕組みに例えて一歩ずつ紐解いていけば、パケットたちがどんなルートを通ってどこに向かっているのかが手に取るように見えてきますよね。
インフラやネットワークの世界は、こうした「見えない流れ」を可視化していくパズルのような楽しさに満ち溢れています。
今日の知識が、あなたの実務やクラウド学習の小さな助けになればとても嬉しいです。それでは、また次回の技術解説でお会いしましょう!
コメント