お久しぶりです、AWSが好きなインフラ担当の大久保です。
12月にフルマラソンに参加することになり、最近ジョギングに力を入れてます。

今回は、以前遭遇した「Apacheのアクセスログと、Redmineのproduction.logの送信元アドレスが異なっている状態」について、調査・検証した内容と、解決方法について話したいと思います。

どんな環境で起こるか?


そもそも、どんな環境で送信元IPが異なる状態になるか?というと、以下の条件に当てはまる環境で起こる可能性があります。

  1. X-Forwarded-Forヘッダーの付与を行うプロキシ、リバースプロキシサーバーを複数経由している
  2. ApacheでRemoteIPHeader X-Forwarded-ForRemoteIPInternalProxyの設定が有効化されている

一例ですが、図にするとこんな環境です。

場所 X-Forwarded-For ヘッダーの中身
端末
プロキシサーバー xxx.xxx.xxx.xxx
ALB xxx.xxx.xxx.xxx, yyy.yyy.yyy.yyy

上図の環境では、X-Forwarded-Forヘッダー付与の設定がされた「プロキシサーバー」と「ALB(リバースプロキシサーバー)」の2つを経由することで、Redmineサーバーにリクエストが届く時にはX-Forwarded-Forヘッダー内に複数のIPアドレスがある状態になります。

そして、Apacheのアクセスログ上での送信元がyyy.yyy.yyy.yyy、Redmineのproduction.log上での送信元はxxx.xxx.xxx.xxxという、不可思議な状態となっていました。

なんでこんなことになるのか?


大久保が調査・検証した結果ベースでの話になりますが、下記の2つが起因していると考えています。

  • Apacheの設定によって、X-Forwarded-Forヘッダーの評価方法が違う
  • ApacheからX-Forwarded-Forヘッダーを渡された場合、Redmineはヘッダー内の一番右のIPアドレスを送信元IPアドレスとして利用する

根拠


たぶんX-Forwarded-Forヘッダーが悪さしてるんだろうなーと思い、Apacheの設定によってX-Forwarded-Forヘッダーがどう扱われるかを調査しました。

具体的には、以下の4パターンの設定で検証を行いました。

パターンRemoteIPHeader
X-Forwarded-For
RemoteIPInternalProxyRequestHeader unset
X-Forwarded-For
1
2
3
4
◯:設定有 -:設定無

結果は以下の表の通りとなり、送信元IPアドレスが異なっている状態となったのは「パターン2」でした。

パターン評価前の
X-Forwarded-Forヘッダーの中身
Apache上での送信元IPアドレス評価後の
X-Forwarded-Forヘッダーの中身
Redmine上での送信元IPアドレス
1xxx.xxx.xxx.xxx, yyy.yyy.yyy.yyyxxx.xxx.xxx.xxxxxx.xxx.xxx.xxx
2xxx.xxx.xxx.xxx, yyy.yyy.yyy.yyyyyy.yyy.yyy.yyyxxx.xxx.xxx.xxxxxx.xxx.xxx.xxx
3xxx.xxx.xxx.xxx, yyy.yyy.yyy.yyyxxx.xxx.xxx.xxxxxx.xxx.xxx.xxx
4xxx.xxx.xxx.xxx, yyy.yyy.yyy.yyyyyy.yyy.yyy.yyyyyy.yyy.yyy.yyy
-:無

この結果から以下のような動きをしていそうなことがわかり、Apacheの処理でX-Forwarded-Forヘッダーの中身が一部残された状態で、RedmineにX-Forwarded-Forヘッダー引き渡されることが原因と判断しました。

  1. Apacheでの処理
    • RemoteIPHeader X-Forwarded-Forを設定した場合
      • ヘッダー内のアドレスを一番右から評価していき、一番左のアドレスが信頼出来る送信元として判定され利用される (たぶんREMOTE_ADDR変数に代入される)
      • 評価されたアドレスはヘッダー内から削除される
    • RemoteIPInternalProxyでALBのプライベートIPアドレスも設定した場合
      • ヘッダー内のアドレスを一番右から評価していき、ALBがX-Forwarded-Forヘッダーに追加したIPアドレス(今回だとyyy.yyy.yyy.yyy)が、信頼出来る送信元として判定され利用される
      • ALBより前にX-Forwarded-Forヘッダーに追加されたIPアドレスは評価されず、ヘッダー内に残る
  2. Redmineでの処理
    • ApacheからX-Forwarded-Forヘッダーを渡された場合
      • ヘッダー内の一番右のアドレスを送信元IPアドレスとして利用する
    • ApacheからX-Forwarded-Forヘッダーを渡されない場合
      • Apacheと同じIPアドレスを送信元IPアドレスとして利用する (たぶんREMOTE_ADDR変数の値を使ってる)

解決方法


ということで、ApacheでRequestHeader unset X-Forwarded-Forを設定し、Redmineに渡す前に必ずX-Forwarded-Forヘッダーを削除することで、ApacheとRedmineで送信元IPアドレスが異なる状態になることを防げます。

おまけ


ちなみに、パターン3の設定で端末のIPアドレスを送信元IPアドレスに、パターン4の設定でプロキシサーバーのIPアドレスを送信元IPアドレスにすることが出来ます。

RemoteIPHeader X-Forwarded-For
RequestHeader unset X-Forwarded-For
RemoteIPHeader X-Forwarded-For
RequestHeader unset X-Forwarded-For
RemoteIPInternalProxy <ALBが配置されるサブネットのCIDR>

まとめ


今回の調査で、Apacheの設定によってX-Forwarded-Forヘッダー内のどのIPアドレスが利用されるかが分かり、とても勉強になりました。

ただ、今回調査した内容はググってもあまり詳細な情報が出てこず、本当にこの理解で良いのだろうか?という引っかかりもあるため、時間が空いた時にまた調査出来ればと思います。

この情報が誰か助けになれば幸いです。

参考にしたもの


By okubo

主にAWS上でのインフラ構築を担当してます。 ・AWS Certified Solutions Architect - Professional ・AWS Certified DevOps Engineer - Professional ・AWS Certified Database - Specialty ・AWS Certified Security - Specialty