リダイレクトするときにクライアントとwebサーバーが対話する際に使ったプロトコルが無視される問題についてのお話。
いきさつ
- jenkinsをnginxの下に立てようとした
- httpsで受けてhttpでjenkins.warを動かしたjettyに流すようにした
- ログイン/アウト時にhttpにリダイレクトされる
- httpsしかファイアウォールで許可してなかったので怒られる
そんな感じ。
色々調べてみたところ、jenkinsのリダイレクトはreq.sendRedirect("/hoge")
とかでやっていることが分かった。
だとすると、それを受けたservletの方でhttpにしてるのでは?という疑惑が芽生えた。
tomcatの場合
初動検証
apache-tomcat-9.0.0.M10.tar.gzをダウンロードし、以下のようなファイルをビルドしたものを組み込んで検証すると、無事httpsからhttpにリダイレクトされた。
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class Risa extends HttpServlet{
public void doGet(HttpServletRequest req,
HttpServletResponse res)
throws ServletException, IOException {
System.out.println(req.getContextPath());
res.sendRedirect(req.getContextPath());
}
}
直す
conf/server.xmlに以下の一行を足すだけ。
<Valve className="org.apache.catalina.valves.RemoteIpValve" protocolHeader="x-forwarded-proto"/>
これでX-Forward-Protoで得たプロトコルを利用できるようになったので、nginxにproxy_set_header X-Forwarded-Proto $scheme;
と入れてproxy_passすればhttpsでリダイレクトされるようになる。
jettyの場合
初動検証
tomcatの時と同じものをwebappsフォルダの下に置いてjava -jar start.jar
で実行すると、やはりhttpにリダイレクトされた。
直す
jettyの9.3系だとアーカイブの中にjetty-http-forwarded.xmlファイルなるものが存在しており、それを喰わせればいい。これもX-Forwarded-Protoを見るようになるので、nginxの設定を足せばいい。
#!/bin/sh
cd `dirname $0`
/usr/bin/java -jar start.jar ./etc/jetty-http-forwarded.xml
愚痴
さらっと書いたけど、普通の人は「httpをhttpsにリダイレクトする設定を入れればいいじゃん」って感じになるのか、軽く動かすにしてもなかなか同じトラブルシューティングが見つからなくてめっちゃ困った。
ここ最近アプリの動作確認取る上で同じ問題にしばしばハマってて、rundeckだとprofileファイルに-Drundeck.jetty.connector.forwarded=true
と書き足して、gitbucket(jetty)の場合だとGUI中のbaseUrlにhttpsのURLを書いたりしてた。
アプリごとにそれぞれ個別設定入れて対策取らないといけないのかなりカロリー使うしどうにかならないんだろうか……。