追記: 2014/08/20 23:10 JST: 読者の方にご指摘をいただき Nginx の設定ファイルに誤りがあったことが判明したため修正しました。

はじめに

こんばんは技術開発部技術局の yosida95 こと吉田 昂平です。業務中に twitter を眺めていたところ、 Gehirn RS2 を使って多段リバースプロキシの設定を試みているものの意図したとおりに設定できていない方を見かけたので、 Nginx をリバースプロキシとして利用する設定の一例を紹介してみます。

Gehirn RS2 で多段リバースプロキシをするモチベーション

Gehirn RS2 ではリバースプロキシ用のポートがアカウントごとに 3 つ割り当てられています。割り当てられたポートでウェブアプリケーションをリッスンし Gehirn RS2 のリバースプロキシを設定をすると、 Well-known な HTTP/HTTPS である 80/443 ポートでウェブアプリケーションにアクセスできます。

しかし、 3 つよりも多くのアプリケーションを 1 つのアカウントで運用したい場合、ポート数が足らなくなってしまいます。そこで、割り当てられた内の 1 つのポートで Nginx などをリッスンし、この Nginx がさらにリバースプロキシを行うことで 3 つよりも多いアプリケーションを 1 つのアカウントで運用することが可能になります。

Nginx の設定例

server {
    listen (割り当てポート);

    set $requested_host $http_X_FORWARDED_HOST;
    set $app_server 'http://unix:/home/yosida95/default.sock';

    if ($requested_host = app2.example.com) {
        set $app_server 'http://unix:/home/yosida95/app2.sock';
    }

    if ($requested_host = app3.example.com) {
        set $app_server 'http://unix:/home/yosida95/app3.sock';
    }

    location / {
        proxy_set_header Host $http_X_FORWARDED_HOST;
        proxy_set_header X-Forwarded-Host $http_X_FORWARDED_HOST;
        proxy_set_header X-Forwarded-For $http_X_FORWARDED_FOR;
        proxy_set_header X-Real-IP $http_X_FORWARDED_FOR;
        proxy_set_header X-Forwarded-Server $http_X_FORWARDED_SERVER;
        proxy_pass $app_server;
    }
}

この設定のポイントとしては以下の点が挙げられます。

  • 一度リバースプロキシされた HTTP リクエストなので、 Host ヘッダーはリバースプロキシ先 (Nginx) のものに置き換えられている
    • ユーザーエージェントがリクエストしたホストネームは X-Forwarded-Host ヘッダーに格納されているため、そこを確認する
  • X-Forwarded-Host ヘッダーの値によってリバースプロキシ先を切り替える
  • 想定しないホスト名をユーザーエージェントにリクエストされた場合の挙動を定義する
    • この場合は http://unix:/home/yosida95/default.sock にリバースプロキシする
    • 他に、 301 や 302 レスポンスを Nginx で返却する方法などが考えられる
  • アプリケーションサーバーにユーザーエージェントの IP アドレスや、ユーザーエージェントがリクエストしてきたホスト名を通知するために、 Gehirn RS2 がやっているように X-Forwarded-HostX-Forwarded-For などのヘッダーを付加する

おわりに

注意点としては、リバースプロキシ先のサーバーが TCP ソケットをリッスンすると他のユーザーに割り当てられたリバースプロキシ先ポートを横取りしてしまい迷惑になること、また他のユーザーからアクセスされてしまうセキュリティ上の懸念があるため、 UNIX ドメインソケットを利用するべきです。

以上です。ありがとうございました。