Nginx transparent reverse proxy part 2

昔に公開したnginxのreverse proxyの設定から設定がだいぶ変わりました。

location ~ /api/v1/http/([^/]+)(/.*?)?([^/]*)?$ {
    set $rewrite_host $1;
    set $rewrite_path $2;
    set $rewrite_file $3;

    set $is "${rewrite_path}${rewrite_file}";
    if ($is = "") {
        set $rewrite_path "/";
    }

    resolver 8.8.8.8;
    proxy_http_version 1.1;
    proxy_pass http://$rewrite_host$rewrite_path$rewrite_file;
    proxy_redirect ~^(https?)://([^/]+)(/.*)?$ /api/v1/$1/$2$3;
    proxy_redirect ~^/(.+)?$                   /api/v1/http/$rewrite_host/$1;
    proxy_redirect ~^(.+)$                     /api/v1/http/$rewrite_host$rewrite_path$1;

    proxy_set_header Host               $1;
    proxy_set_header Referer            http://$1/$2;
    proxy_set_header Client-IP          $remote_addr;
    proxy_set_header X-Forwarded-For    "";
    proxy_set_header X-Forwarded-Server "";
    proxy_set_header X-Forwarded-Host   "";
    proxy_set_header X-Forwarded-Scheme "";
    proxy_set_header X-Forwarded-Port   "";
    proxy_set_header X-Forwarded-Proto  "";
    proxy_set_header X-Request-Id       "";
    proxy_set_header X-Request-Start    "";
    proxy_set_header Via                "";
    proxy_set_header Connect-Time       "";
    proxy_set_header Total-Route-Time   "";
    proxy_set_header Connection         "keep-alive";

    proxy_cookie_domain ~^.+?$ $host;
}

前回からの主な変更点は下記になります。

  • proxy_redirectでlocationに対応
  • proxy_cookie_domainでクッキーの書き換え

proxy_redirect

proxy_redirectでreverse proxy先から戻ってきたLocationヘッダを書き換えるようにしています。 ここで戻ってくるURLは3種類あるため、それぞれに対応する書き換えを定義します。

full URL

http://example.jp/path/to

上記のように完全なURLが戻ってきた場合

proxy_redirect ~^(https?)://([^/]+)(/.*)?$ /api/v1/$1/$2$3;

のように先頭がhttpから始まる正規表現で一致させ、後方参照で新しいURLを定義します。 いろいろなLocationヘッダを見てきましたが、この完全URLを戻すサーバーが大部分を占めます。

absolute URL

/path/to

上記のように絶対URLが戻ってきた場合

proxy_redirect ~^/(.+)?$ /api/v1/http/$rewrite_host/$1;

のように先頭が/から始まる正規表現で一致させ、後方参照で新しいURLを定義します。 絶対URLの場合はホストの情報が欠けてしまうため、事前にreverse proxy先のホスト名を変数に保持しておく必要があります。

location ~ /api/v1/http/([^/]+)(/.*?)?([^/]*)?$ {
    set $rewrite_host $1;

relative URL

path/to

完全URL、絶対URLにも一致しなかった場合は、上記のような相対URLとみなします。

proxy_redirect ~^(.+)$ /api/v1/http/$rewrite_host$rewrite_path$1;

のように全てを一致させ、後方参照で新しいURLを定義します。 この場合も絶対URLと同様にホスト情報が欠け、パスも欠けてしまうため、事前にreverse proxy先のホスト名、パスを変数に保持しておく必要があります。

location ~ /api/v1/http/([^/]+)(/.*?)?([^/]*)?$ {
    set $rewrite_host $1;
    set $rewrite_path $2;
    set $rewrite_file $3;
}

元のURLがファイル名を含むURLのため、ホスト名、パス、ファイル名に分割してあげる必要があります。

    set $is "${rewrite_path}${rewrite_file}";
    if ($is = "") {
        set $rewrite_path "/";
    }

また、今回locationで設定している正規表現では、パスとファイルが空の場合に/が欠けてしまうため補完してあげる必要があります。

相対URLがLocationヘッダで戻ってくることは滅多にないのですが、たまに相対URLのせいでエラーが出るので対応しておいた方が良いです。

proxy_cookie_domain

proxy_cookie_domainでreverse proxy先から戻ってきたSet-Cookieヘッダのドメインを自身のホスト名で書き換えるようにしています。

proxy_cookie_domain ~^.+?$ $host;

まだ、ここは煮詰めている段階なのでもう少し弄るかもしれません。


完全透過なreverse proxy serverとしてはまだ煮詰めている段階で、まだいくつか課題があります。

  • 各種Webサービスへのログイン対応
  • 元のクライアントIPをreverse proxy先にクライアントIPとして認識させる

そもそも、セキュリティ的にやって良いのかという話もありますが、技術的にやれるということは確認しておきたいですね。