CLOVER🍀

That was when it all began.

nginxでForward Proxy(HTTPのみ)を構成してみる

これは、なにをしたくて書いたもの?

nginxでForward Proxyが立てられるのかな?と思って、ちょっと調べてみました。

結論としては可能といえば可能(?)ですが、CONNECTメソッドについては標準では対応していません。

環境

今回利用する、nginx。

$ nginx -v
nginx version: nginx/1.17.3

HTTPに対するForward Proxyを立てる

こんな感じの設定になります。

server {
    listen       8080;
    server_name  localhost;

    location / {
        resolver 8.8.8.8;
        proxy_pass   http://$http_host$request_uri;
    }
}

proxy_passで、Hostヘッダーの値とリクエスURIを、そのまま転送先に設定します。

Module ngx_http_proxy_module

変数「$http_host」と「$request_uri」の意味は前述のとおりですが、説明は以下に記載があります。

Alphabetical index of variables

ngx_http_core_module / Embedded Variables

$http_name
arbitrary request header field; the last part of a variable name is the field name converted to lower case with dashes replaced by underscores

$request_uri
full original request URI (with arguments)

また、resolverの指定が入っていますが、転送先のホスト名を名前解決する必要がある場合は、resolverの指定は必須のようです。
ご注意を。

ngx_http_proxy_module / proxy_pass

Parameter value can contain variables. In this case, if an address is specified as a domain name, the name is searched among the described server groups, and, if not found, is determined using a resolver.

ngx_http_core_module / resolver

とはいえ、Reverse Proxyで使うような機能をちょっと外れたような使い方をしているような感が否めないですが。

確認してみる

では、簡単なサーバーを立てて確認してみましょう。

Quarkusで作成。

$ mvn io.quarkus:quarkus-maven-plugin:0.21.2:create \
    -DprojectGroupId=org.littlewings \
    -DprojectArtifactId=simple-rest-api \
    -DclassName="org.littlewings.quarkus.HelloResource" \
    -Dpath="/hello" \
    -Dextensions="resteasy-jsonb"

生成されたJAX-RSリソースクラスを、ちょっと修正。
src/main/java/org/littlewings/quarkus/HelloResource.java

package org.littlewings.quarkus;

import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class HelloResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }

    @GET
    @Path("query")
    @Produces(MediaType.TEXT_PLAIN)
    public String query(@QueryParam("param") String param) {
        return "query param = " + param;
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.TEXT_PLAIN)
    public String post(Map<String, String> request) {
        return request.get("param");
    }
}

ビルドして起動。

$ mvn package && java -jar target/simple-rest-api-1.0-SNAPSHOT-runner.jar

nginxが172.17.0.2、アプリケーションが192.168.0.2で動作しているものとします。

確認。

## GET
$ http_proxy=http://172.17.0.2:8080 http://192.168.0.2:8080/hello
hello


## QueryString付き
$ http_proxy=http://172.17.0.2:8080 curl http://192.168.0.2:8080/hello/query?param=value
query param = value


## POST
$ http_proxy=http://172.17.0.2:8080 curl -XPOST -H 'Content-Type: application/json' http://192.168.0.2:8080/hello -d '{"param": "value"}'
value

OKそうですね。

CONNECTメソッドについて

HTTPのプロキシはできましたが、HTTPSのプロキシを行うためのCONNECTメソッドは、nginxはサポートしていないようです。

サードパーティ製のこちらのモジュールがあり、ビルドして組み込むことになるようです。

GitHub - chobits/ngx_http_proxy_connect_module: A forward proxy module for CONNECT request handling

今回は、ここまではやらなくていいかなぁと思います…。