JavaでX-Forwarded-Forを取る
JavaでX-Forwarded-Forを取るため、以下のコードを書いた。
String xff = httpServletRequest.getHeader("X-Forwarded-For");
ローカル環境で実行しているときは問題なく取得できたが、Kubernetes上では取得できなかった。
getHeader
の代わりにgetHeaders
を試してもうまくいかない。
Enumeration<String> xffs = httpServletRequest.getHeaders("X-Forwarded-For");
HTTPヘッダーの一覧を取得してみても、Kubernetes環境ではX-Forwarded-For
が含まれていない。
Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
System.out.println(headerName);
}
アプリケーションはSpring Boot 2.7 で実装していたため、SpringとX-Forwarded-For
を検索して調査することにした。
server.use-forwarded-headers
HerokuのHelpにWhy can't I access the X-Forwarded-For header in my Java Spring app?というものがあり、Spring FrameworkがX-Forwarded-For
を取り除いてTomcatのRemoteIpValve
機能の設定に使用するということが書いてあった。
Helpに従い、server.use-forward-headers=false
の設定をapplication.propertiesに書いてみたが、IntelliJ IDEAにDeprecatedであることを指摘された。
server.forward-headers-strategy
server.use-forwarded-headers
の代わりにserver.forward-headers-strategy
が導入されていた。
server.forward-headers-strategy
について調べると、Spring Boot / How-to Guides / Embedded Web Serversに以下の記述があった。
If your application runs in a supported Cloud Platform, the
server.forward-headers-strategy
property defaults toNATIVE
. In all other instances, it defaults toNONE
.
以下のクラウドプラットフォームではserver.forward-headers-strategy
のデフォルトがNONE
からNATIVE
に変わっているらしい。
- Azure App Service platform.
- Cloud Foundry platform.
- Heroku platform.
- Kubernetes platform.
- Nomad platform.
- SAP Cloud platform.
ローカル環境でもserver.forward-headers-strategy=NATIVE
に設定してテストしたところ、X-Forwarded-For
が取れないことがわかった。
Kubernetes上のSpring BootでX-Forwarded-Forを取得する
httpServletRequest.getRemoteAddr()
server.forward-headers-strategy=NATIVE
のとき、httpServletRequest.getRemoteAddr()
をするとカンマで区切られたX-Forwarded-For
のうち、プライベートIPアドレスの左隣の値が取得できた。全てプライベートIPアドレスだった場合は一番最初の値が取得できた。
server.tomcat.remoteip.internal-proxies
のデフォルトが以下になっている。
10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|192\\.168\\.\\d{1,3}\\.\\d{1,3}|169\\.254\\.\\d{1,3}\\.\\d{1,3}|127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|100\\.6[4-9]{1}\\.\\d{1,3}\\.\\d{1,3}|100\\.[7-9]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|100\\.1[0-1]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|100\\.12[0-7]{1}\\.\\d{1,3}\\.\\d{1,3}|172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|0:0:0:0:0:0:0:1|::1
X-Forwarded-For
に設定されているIPアドレスのリストのうち、信頼できるIPアドレス(デフォルトではプライベートIPアドレス)の一個前をクライアントIPアドレスとして採用するというスタンダードな方法なため、特に違和感なく使用できる。
server.forward-headers-strategy=NONE
Kubernetes上で実行するときとローカルで実行するときで動作が変わってしまうのが一番分かりづらいポイントだった。明示的にserver.forward-headers-strategy=NONE
を指定すれば、環境に関わらずX-Forwarded-For
が取得できる。
ただし、信頼できるIPアドレスの一個前をクライアントIPアドレスとして採用する処理を自前で書かないといけないという問題がある。
単にX-Forwarded-For
に設定されている全IPアドレスを取得するなどが目的の場合はこちらが適していると思う。