nginx でリバースプロキシするときの Tips

Linux
スポンサーリンク

自宅サーバを KVM で仮想化して複数の VM を稼働させているが、グローバル IP アドレスが一つしかないため、外部から見て1ポートにつき1つのプライベート IP アドレスにしか NAPT できない。

そこで HTTP (80), HTTPS (443) に関しては nginxリバースプロキシとして動作させ、プライベート IP を振った VM (バックエンド Web サーバ) に中継することにした。そのときの設定をまとめておく。OS は Ubuntu 12.04 を使用している。

スポンサーリンク

基本的な設定

単に HTTP を中継するだけなら、次の手順を実施すれば良い。

まずリバースプロキシとして動作させるホストに nginx をインストールする。

$ sudo apt-get install -y nginx

バーチャルホストごとに設定ファイルを作成する。

$ sudo vi /etc/nginx/sites-available/(任意の名前)
server {
    server_name (バーチャルホスト名);
    proxy_set_header Host $http_host;
    location / {
        proxy_pass http://(バックエンド Web サーバの IP アドレス);
    }
}

設定を有効化し、nginx をリロードする。

$ ln -s /etc/nginx/sites-available/(設定ファイル名) /etc/nginx/sites-enabled/
$ sudo service nginx reload

あとは DNS でバーチャルホスト名の A レコードをリバースプロキシに向ければ良い。

アクセス元の IP アドレスを通知

普通に設定しただけだと、バックエンドの Web サーバのログにリバースプロキシの IP アドレスが記録されてしまうので、アクセス元の IP アドレスを通知するようにする。

リバースプロキシ側の conf に、X-Forwarded-For ヘッダを追加する。

$ sudo vi /etc/nginx/sites-enabled/(設定ファイル名)
server {
    ...
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    ...

バックエンド側の Web サーバで、X-Forwarded-For の値をクライアントの IP アドレスとして使用するように設定する。nginx なら HttpRealipModule、Apache なら mod-rpaf で実現できる。

nginx の場合、次のように設定する。

$ sudo vi /etc/nginx/conf.d/realip.conf
set_real_ip_from (リバースプロキシの IP アドレス);
real_ip_header X-Forwarded-For;
$ sudo service nginx reload

これでアクセス元の IP アドレスが記録されるようになる。

SSL を有効化

クライアントとリバースプロキシ間の通信を SSL 化するには、次のようにする (リバースプロキシと振り先の Web サーバ間は HTTP のまま)。今回はオレオレ証明書を使う。

まずリバースプロキシで秘密鍵、CSR、オレオレ証明書を生成する。すべてのバーチャルホストで同じドメインのサブドメインを使うなら、Common Name はワイルドカードを使えば良い。別々のドメインを使うなら、それぞれ別のファイル名で証明書を生成する。

$ sudo mkdir /etc/nginx/cert
$ cd /etc/nginx/cert
$ sudo sh -c "openssl genrsa 2048 > ssl.key"
$ sudo sh -c "openssl req -new -key ssl.key > ssl.csr"
$ sudo sh -c "openssl x509 -days 3650 -req -signkey ssl.key < ssl.csr > ssl.crt"

リバースプロキシ側の conf に、SSL を使う設定を追加する。

$ sudo vi /etc/nginx/sites-enabled/(設定ファイル名)
...
server {
    listen 443;
    server_name (バーチャルホスト名);
    ssl on;
    ssl_certificate /etc/nginx/cert/ssl.crt;
    ssl_certificate_key /etc/nginx/cert/ssl.key;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    location / {
        proxy_pass http://(バックエンド Web サーバの IP アドレス);
    }
}

このままだと、振り先の Web アプリケーションによっては、HTTP の URL にリダイレクトされてしまう場合がある。これを防ぐには、conf で次のように設定する。

$ sudo vi /etc/nginx/sites-enabled/(設定ファイル名)
...
server {
    listen 443;
    ...
    proxy_set_header X-Forwarded-Proto $scheme;
    ...
    location / {
        proxy_pass http://(バックエンド Web サーバの IP アドレス);
        proxy_redirect http:// https://;
    }
}

クライアントが HTTP にアクセスしてきたときに HTTPS にリダイレクトするには、次のように設定する。

$ sudo vi /etc/nginx/sites-enabled/(設定ファイル名)
server {
    listen 80;
    server_name (バーチャルホスト名);
    rewrite ^(.*)$ https://$host$1 permanent;
}

以上をまとめると次のような conf になる。

server {
    listen 80;
    server_name (バーチャルホスト名);
    rewrite ^(.*)$ https://$host$1 permanent;
}

server {
    listen 443;
    server_name (バーチャルホスト名);
    ssl on;
    ssl_certificate /etc/nginx/cert/ssl.crt;
    ssl_certificate_key /etc/nginx/cert/ssl.key;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto $scheme;
    location / {
        proxy_pass http://(バックエンド Web サーバの IP アドレス);
        proxy_redirect http:// https://;
    }
}

以上で SSL 通信できるようになる。なお、ネームバーチャルで SSL を利用するには、Web ブラウザが SNI (Server Name Indication) に対応している必要がある。

BASIC 認証をかける

クライアントとリバースプロキシ間で BASIC 認証をかけるには、次のようにする。

まず必要なパッケージを入れる。

$ sudo apt-get install apache2-utils

.htpasswd ファイルを生成する。

$ sudo htpasswd -c /etc/nginx/.htpasswd (ユーザ名)
New password: (パスワード)
Re-type new password: (パスワード)

リバースプロキシ側の conf に、BASIC 認証を使う設定を追加する。

$ sudo vi /etc/nginx/sites-enabled/(設定ファイル名)
server {
    ...
    auth_basic "Please enter your ID and password.";
    auth_basic_user_file "/etc/nginx/.htpasswd";
    ...

これで BASIC 認証が有効になる。

BASIC 認証のかかった振り先にアクセスする

振り先のアプリケーションに BASIC 認証がかかっているときに、リバースプロキシで認証を済ませるようにするには、次のようにする。例えば、ブロードバンドルータの設定画面にアクセスするときなどに使えるだろう。この場合、セキュリティレベルが下がらないように、クライアントとリバースプロキシ間で別のアクセス制限を施すこと。

BASIC 認証は HTTP ヘッダの “Authorization” で行なう。文字列は BASE64 エンコードされているので、その文字列を生成する。

$ echo -n '(ユーザ名):(パスワード)' | base64
dXNlcjpwYXNz

リバースプロキシ側の conf に、Authorization ヘッダを送出する設定を追加する。

$ sudo vi /etc/nginx/sites-enabled/(設定ファイル名)
server {
    ...
    proxy_set_header Authorization "Basic (生成された文字列)";
    ...

これで BASIC 認証を通過できるようになる。

WebSocket を中継する

WebSocket を用いたアプリケーション (noVNC など) を中継するには、nginx 1.3.13 以上である必要があるらしい。

現在のバージョンを調べてみる。

$ nginx -v
nginx version: nginx/1.2.1

ということで、まず nginx のバージョンをあげる。手順は下記のページが参考になる。

バージョンが上がったか確認する。

$ nginx -v
nginx version: nginx/1.4.5

nginx のオフィシャルパッケージ版だと、今まで sites-enabled 配下に置いていた設定ファイルを読んでくれなくなるので、その対処として次のように設定する。

$ cat /etc/nginx/conf.d/sites-enabled.conf
include /etc/nginx/sites-enabled/*;
server_names_hash_bucket_size 64;

サイトの設定ファイルに、WebSocket を中継するための記述を追加する。

$ sudo vi /etc/nginx/sites-enabled/(設定ファイル名)
...
location / {
    ...
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    ...

これで WebSocket を中継できる。

参考ページ

コメント

タイトルとURLをコピーしました