特定のサーブレットのみをIPでアクセス制限

今回は、Tomcatのみで実装されたWebAPサーバを使ったWebシステムにおいて、特定のサーブレットのみにアクセス制御をかけたい、という時の話。前提構成として、クライアントからサーバまでの経路上にL7レベルのフィルタを行う機器がなく、またApache-Tomcat連携構成への変更はせずTomcatのみの構成を変えないことが制約。さて、どうするか。

1つのアプリケーション全体へのアクセス制限であれば、オーソドックスにTomcatのRemoteAddrValveを使えば簡単に実装できる。が、この方式では、アクセス制限は特定のサーブレットだけではなくアプリケーション全体に制限がかかってしまいNG。参考までに、この場合の実装方法はこんな感じ。


${CATALINA_BASE}/conf/[Engine]/[Host]/context.xml




次に試した方法は、サーブレットの実行に認証をかける方法。が、この場合には当然ながらBASIC/FORM/DIGEST認証などの認証方式で対話的に認証情報の入力を求められる。実は今回作業対象としているサーブレットは、バッチによる自動実行もしたかったので、ちょっと厳しい。対話型認証のため、インターネットユーザが認証をブルートフォースアタック攻撃(総当り)で突破できる可能性がある点も考慮し、この方法も残念ながらNG。
で、最後に考えたのが、Javaでアクセス制限の機能を自作(既にこの時点でインフラだけで対処することを諦めてますが)する方法。自作機能のソース(IpAddressFilter.java)はこんな感じ。


import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class IpAddressFilter implements Filter {

private FilterConfig config;
private String allow_ip;

/* パスに対してアクセス制限を行う */

public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
final String ip_address = ((HttpServletRequest) request).getRemoteAddr();

if (!ip_address.startsWith(allow_ip)) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN);
}
else {
chain.doFilter(request, response);
}
}

public final void init(FilterConfig filterConfig) throws ServletException {
this.config = filterConfig;
this.allow_ip = config.getInitParameter("allowIP");
}

public final void destroy() {
this.config = null;
this.allow_ip = null;
}
}

上記javaファイルをコンパイルし、生成された.classファイルを${CATALINA_BASE}/lib/に配備。あとはアプリのweb.xmlで当該サーブレットのFilter定義に設定すれば解決。以下の例では、docBaseが/usr/local/tomcat/webapps/app01/の場合の設定例。/usr/local/tomcat/webapps/app01/WEB-IN/web.xml
に以下の設定を追記する。



Allow Local Network Access
IpAddressFilter

allowIP
192.168.100.


Allow Local Network Access
TestServlet


Allow Local Network Access
/servlet01 -->