【Linux】Nginxを用いたリバースプロキシの構築体験

2024年12月30日

こんにちは、キクです。

先日、初めてリバースプロキシの構築を行いました。

本記事では、その復習も兼ねて「Nginxを用いたリバースプロキシの構築方法」について整理していこうと思います。

それでは、よろしくお願いします。

リバースプロキシとは

リバースプロキシは、クライアント(ユーザー)からのリクエストを受け取り、適切なバックエンドサーバーに転送する役割を持つサーバーです。

主な特徴としては以下のようなものが挙げられます。

1. リクエストの仲介

クライアントは直接バックエンドサーバーにアクセスせず、リバースプロキシを介してアクセスする

2. セキュリティ向上

バックエンドサーバーを外部から隠し、不正アクセスや攻撃を防ぐ

3. 負荷分散

複数のバックエンドサーバーへのリクエストを均等に分配して、負荷を分散する

4. キャッシュ機能

静的コンテンツ(画像やCSSファイルなど)をキャッシュすることで、バックエンドの負担を軽減し、レスポンス速度を向上させる

5. SSLオフロード

SSL/TLS通信をリバースプロキシで終了させることで、バックエンドの負荷を減らす

今回の構成

今回は「リバースプロキシ」ということで、接続元クライアントとWebサーバとの間にリバースプロキシを挟んだ以下のような構成になります。

本記事では「test.example.com」というFQDNを用いて接続を行いますが、DNS設定については触れませんのでご了承ください。

また、通常バックエンドサーバは複数台で構成されることから、今回の作業でも一応は2台構成にしています。

ただし、本記事としては複数台での設定方法を明記できればOKなので、動作確認等では2台目は停止しているものとして扱います。

作業1. Nginx導入

本項では、本記事のテーマである「リバースプロキシ」を動作させるためのNginxを導入していきます。

1. Nginxをインストール
root@rp-host:~# apt install nginx
2. Nginxの起動/自動起動設定
root@rp-host:~# systemctl start nginx
root@rp-host:~# systemctl enabled nginx

作業2. リバースプロキシとしての設定追加

本項では、先ほどインストールしたNginxサーバをリバースプロキシとして稼働させるための設定を行っていきます。

1. デフォルトのバーチャルホスト設定ファイルへのシンボリックリンクを削除
root@rp-host:~# ls /etc/nginx/sites-enabled/
total 8
drwxr-xr-x 2 root root 4096 Dec 20 11:11 .
drwxr-xr-x 8 root root 4096 Dec 20 11:11 ..
lrwxrwxrwx 1 root root   34 Dec 20 11:11 default -> /etc/nginx/sites-available/default
root@rp-host:~# rm /etc/nginx/sites-enabled/default
補足

Nginxのメイン設定ファイル「nginx.conf」には次のようにバーチャルホストの設定として読み込まれるファイル情報が記されています。

http {

    ~中略~

        ##
        # Virtual Host Configs
        ##

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}

本手順で削除したシンボリックリンクの親ディレクトリ「sites-enabled」配下のファイルもその一部であることが分かります。

先ほどの作業では、これを削除して後続の手順で新たにバーチャルホストの設定ファイルをシンボリックリンクとして追加していきます。

2. 新規バーチャルホスト設定ファイルの作成
root@rp-host:~# vi /etc/nginx/sites-available/test.example.com
# Reverse proxy configuration
upstream backend_servers {
        server 192.168.1.21;
        server 192.168.1.22;
}

server {
        listen 80;
        server_name test.example.com;

        location / {
                proxy_pass http://backend_servers;

                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

ポイント

upstream backend_serversとして、バックエンドサーバとなるWebサーバの情報を記載します。

加えて、後続の「proxy_pass」に「http://backend_servers」と記載しておくことで、upstreamで指定した複数のWebサーバに対して通信を振り分けるようになります。

なお、振り分ける通信の対象としては「server_name」で指定している「test.example.com」での通信になります。

すなわち、「http://test.example.com」という通信を受信した場合には、バックエンドとなっている「192.168.1.21」もしくは「192.168.1.22」に振り分ける形になります。

3. 上記ファイルへの有効なシンボリックリンクを作成
root@rp-host:~# ln -s /etc/nginx/sites-available/test.example.com /etc/nginx/sites-enabled/
補足

先ほど、nginx.confにて「/etc/nginx/sites-enabled/*」という記載があることは確認しましたが、「/etc/nginx/sites-available」は含まれていませんでした。

従って、本手順にもある通り「/etc/nginx/sites-enabled」へのシンボリックリンクを作成することではじめて設定として有効化されます。

4. 構文チェック
root@rp-host:~# nginx -t
[出力例]
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
5. サービス再起動
root@rp-host:~# systemctl restart nginx.service
6. 動作確認

6-1. クライアント#1(192.168.1.51)からcurlコマンドで以下のURLに接続できることを確認

root@client01:~# curl http://test.example.com
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Test Page</title>
</head>
<body>
<h1>Test Page</h1>
<pre style="font-family: monospace;">
 ########   #######    #######  ########
    ##      ##        ##           ##
    ##      ##        ##           ##
    ##      ######     ######      ##
    ##      ##              ##     ##
    ##      ##              ##     ##
    ##      #######   #######      ##
</pre>
</body>
</html>

想定通り、Webサーバで公開しているindex.htmlの情報を取得できました。

6-2. アクセスログ確認

リバプロ側(192.168.1.10)
192.168.1.51 - - [27/Dec/2024:10:58:16 +0900] "GET / HTTP/1.1" 200 537 "-" "curl/7.81.0"

接続元がクライアント#1(192.168.1.51)であり、ステータスコード200(正常)が返されていることが分かります。

備忘録
項目説明
192.168.1.51接続元クライアントのIPアドレス
-(1個目)認証ユーザ名(認証が必要な場合に記載される)
-(2個目)識別子(通常は記録されない)
[27/Dec/2024:10:58:16 +0900]リクエストの受信日時
"GET / HTTP/1.1"GET:使用されたHTTPメソッド
/ :リクエストされたリソース(ルートパス、ホームページなど)
HTTP/1.1:使用されたHTTPプロトコルのバージョン
200HTTPステータスコード
537サーバがクライアントに返したレスポンスのサイズ(バイト単位)
“-”参照元URL(リファラー)
“curl/7.81.0”クライアントのソフトウェア情報
今回はcurl(バージョン7.81.0)が使用されてリクエストが送信されたことを示す
バックエンド側(192.168.1.21)
192.168.1.10 - - [27/Dec/2024:10:58:16 +0900] "GET / HTTP/1.0" 200 537 "-" "curl/7.81.0"

バックエンド側は接続元がリバースプロキシ(192.168.1.10)になっており、ステータスコードは同様に200(正常)が返されていることが分かります。

「リバースプロキシで受けてバックエンドに流す」という想定通りの挙動を確認できました。

作業3. アクセス制御(ACL)の設定追加

本項では、アクセス制御を行うためにACLの設定を実施していきます。

具体的には、クライアント#2(192.168.1.52)からの接続は拒否し、その他の接続は許可するようにしていきます。

1. バーチャルホスト設定ファイルにACL情報を追記
root@rp-host:~# vi /etc/nginx/sites-available/test.example.com
# Reverse proxy configuration
upstream backend_servers {
        server 192.168.1.21;
        server 192.168.1.22;
}

server {
        listen 80;
        server_name test.example.com;

        location / {
		deny 192.168.1.52;
		allow all;
        
                proxy_pass http://backend_servers;

                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

ポイント

locationディレクティブに「deny」と「allow」を追加することでアクセス制御を行っています。

2. 構文チェック
root@rp-host:~# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
3. サービス再起動
root@rp-host:~# systemctl restart nginx.service
4. 動作確認

4-1. クライアント#1(192.168.1.51)からの接続

root@client01:~# curl http://test.example.com
リバプロ側(192.168.1.10)
192.168.1.51 - - [27/Dec/2024:11:08:22 +0900] "GET / HTTP/1.1" 200 537 "-" "curl/7.81.0"
バックエンド側(192.168.1.21)
192.168.1.10 - - [27/Dec/2024:11:08:22 +0900] "GET / HTTP/1.0" 200 537 "-" "curl/7.81.0"

4-2. クライアント#2(192.168.1.52)からの接続

root@client02:~# curl https://test.example.com
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.24.0 (Ubuntu)</center>
</body>
</html>

-> アクセス拒否に成功(403)

リバプロ側(192.168.1.10)
192.168.1.52 - - [27/Dec/2024:11:09:34 +0900] "GET / HTTP/1.1" 403 162 "-" "curl/7.81.0"
バックエンド側(192.168.1.21)
未到達のためアクセスログなし

作業4. SSLオフロード設定

1. SSL証明書(.crt)および鍵ファイル(.key)の設置
root@rp-host:~# mkdir /etc/nginx/ssl

以下のファイルを上記ディレクトリにアップロード

アップロードファイル

  • server.crt(SSL証明書ファイル)
  • server.key(秘密鍵ファイル)

※ファイル名は適当

補足

中間証明書(例:intermediate.crt)が存在する場合は、以下のようなコマンドで結合する

root@rp-host:/etc/nginx/ssl# cat server.crt intermediate.crt > /etc/nginx/ssl/fullchain.crt
備忘録

上記コマンドで結合されたファイルにおいて、結合部分が以下のように1行になっていたので別途改行した

改行前

-----BEGIN CERTIFICATE-----
    ~SSL証明書~
-----END CERTIFICATE----------BEGIN CERTIFICATE-----
    ~中間証明書~
-----END CERTIFICATE-----

改行後

-----BEGIN CERTIFICATE-----
    ~SSL証明書~
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
    ~中間証明書~
-----END CERTIFICATE-----
2. バーチャルホスト設定ファイルの修正
root@rp-host:~# vi /etc/nginx/sites-available/test.example.com
# Reverse proxy configuration
upstream backend_servers {
        server 192.168.1.21;
        server 192.168.1.22;
}

server {
        listen 443 ssl;
        server_name test.example.com;
				
		# SSL証明書ファイルを指定
        ssl_certificate /etc/nginx/ssl/server.crt;
        # 秘密鍵を指定
        ssl_certificate_key /etc/nginx/ssl/server.key;
				
		# SSL詳細設定(適宜)
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;

        location / {
                deny 192.168.1.52;
                allow all;

                proxy_pass http://backend_servers;

                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto https;
        }
}

# HTTPリクエストを受けた場合にHTTPSにリダイレクトする設定
server {
        listen 80;
        server_name test.example.com;

        return 301 https://$host$request_uri;
}

ポイント

  • 事前に設置したSSL証明書と秘密鍵のパスを指定
  • SSLに関する設定を適宜指定
  • HTTPリクエストだった場合にHTTPSにリダイレクトする設定も入れた
3. 構文チェック
root@rp-host:~# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
4. サービス再起動
root@rp-host:~# systemctl restart nginx.service
5. 動作確認

5-1. HTTPS接続

root@client01:~# curl https://test.example.com
リバプロ側(192.168.1.10)
192.168.1.51 - - [27/Dec/2024:11:16:52 +0900] "GET / HTTP/1.1" 200 537 "-" "curl/7.81.0"
バックエンド側(192.168.1.21)
192.168.1.10 - - [27/Dec/2024:11:16:52 +0900] "GET / HTTP/1.0" 200 537 "-" "curl/7.81.0"

5-2. HTTP接続

root@client01:~# curl http://test.example.com
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.24.0 (Ubuntu)</center>
</body>
</html>
備忘録

301 Moved Permenently

リクエストされたリソースの URL が永遠に変更されたことを示す。
レスポンスで新しい URL が与えられる。

ちなみに、「新しいURL」はオプションとして「-v」を入れることで「Location: 」として見れる模様。

root@client01:~# curl -v http://test.example.com
*   Trying 192.168.1.10:80...
* Connected to test.example.com (192.168.1.10) port 80 (#0)
> GET / HTTP/1.1
~中略~
< Content-Length: 178
< Connection: keep-alive
< Location: https://test.example.com/
~省略~
リバプロ側(192.168.1.10)
192.168.1.51 - - [27/Dec/2024:11:24:43 +0900] "GET / HTTP/1.1" 301 178 "-" "curl/7.81.0"
バックエンド側(192.168.1.21)
ログなし
メモ

curlでリダイレクトとなった場合、バックエンド側までは通信しない模様?
オプション「-L」を付けるとリダイレクト先まで接続しに行く模様

参考:[小ネタ]curl で3xxの時にリダイレクトする

HTTP接続(オプション-L あり)

root@client01:~# curl -L http://test.example.com
リバプロ側(192.168.1.10)
192.168.1.51 - - [27/Dec/2024:11:27:13 +0900] "GET / HTTP/1.1" 301 178 "-" "curl/7.81.0"
バックエンド側(192.168.1.21)
192.168.1.10 - - [27/Dec/2024:11:27:13 +0900] "GET / HTTP/1.0" 200 537 "-" "curl/7.81.0"
6. 追加確認

6-1. バックエンドへの直接接続(HTTP)

root@client01:~# curl http://192.168.1.11
バックエンド(192.168.1.21)
192.168.1.51 - - [27/Dec/2024:20:05:45 +0900] "GET / HTTP/1.1" 200 537 "-" "curl/7.81.0"

6-2. バックエンドへの直接接続(HTTPS)

root@client01:~# curl <https://192.168.1.11>
curl: (7) Failed to connect to 192.168.1.11 port 443 after 12 ms: Connection refused

ポイント

バックエンド側ではHTTPS通信は有効化していないため「Connection refused」となりました。

リバースプロキシ経由ではHTTPS通信ができていたことから、リバースプロキシでのSSLオフロードができていることが確認できました。

バックエンド(192.168.1.21)
未到達のためログなし

まとめ

今回はNginxを用いたリバースプロキシの構築方法について整理しました。

改めて、本記事では以下の内容に取り組みました。

今回のおさらい

1. Nginxの導入

2. リバースプロキシの設定

└ 既存バーチャルホスト設定ファイルの削除
└ 新規バーチャルホスト設定ファイルの作成

3. アクセス制御設定の追加

└ allow / denyの追記

4. SSLオフロード設定

└ SSL証明書の設置
└ SSL設定項目の追記

5. 各種動作確認

本記事を最後まで読んでいただき、ありがとうございました。
ではでは!

-Linux
-