自宅サーバを 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 を中継できる。
参考ページ
- nginx 入門 – slideshare
コメント