Nginxでの振り分け処理を押さえる

前回、AWS のロードバランサで IP ベースの振り分けをすることを試しました。
その際の振り分けルールとして「自分の使用しているグローバル IP だったら振り分ける」ということを行いました。
ですが、nginx のログに自分の使っているグローバル IP が載ってこなかったので、調べました。
(正確には記載場所が違ったということでしたが。)

アクセス元の IP アドレス他での振り分けをまとめたメモです。

目次

比較

nginx のログを比較します。

ログフォーマットは以下のようになってます。

ログフォーマット(/etc/nginx/nginx.confより)
1
2
3
log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

ログを比較します。

ログ比較
1
2
3
4
5
6
7
8
9
10
11
12
# サーバー自身で curl localhost
127.0.0.1 - - [04/Apr/2020:12:24:38 +0000] "GET / HTTP/1.1" 200 3521 "-" "curl/7.61.1" "-"

# chromeでサーバーのアドレスに直接アクセス http://xxx.xxx.xxx.xxx
xxx.xxx.xxx.xxx - - [04/Apr/2020:12:39:29 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36" "-"
# xxx.xxx.xxx.xxx部分は、アクセス元の使用しているグローバルIPアドレス

# chromeでサーバーに中継しているロードバランサーに割り当てたのドメインにアクセス http://[ドメイン]
172.31.39.178 - - [04/Apr/2020:12:43:24 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36" "xxx.xxx.xxx.xxx"
# 172.31.39.178部分は、ALBのIPアドレス?
# xxx.xxx.xxx.xxx部分は、アクセス元の使用しているグローバルIPアドレス

ALB の IP アドレス確認してみました。
AWS コンソールの、ネットワークインターフェースを見てみます。

ALB に割り当てられているアドレスで間違いなさそうです。

ログフォーマットと照らし合わせると、
直接リクエストを送ったクライアントの IP は、$remote_addrで取り扱う。
中継する前のクライアントの IP は、$http_x_forwarded_forとして取り扱うということがわかりました。

ロードバランサーを中継して通信しなかった場合をブロックしてみる

初めに/etc/nginx/nginx.confを以下のようにしました。

/etc/nginx/nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
worker_connections 1024;
}

http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

include /etc/nginx/mime.types;
default_type application/octet-stream;

include /etc/nginx/conf.d/*.conf;
}

デフォルトでは server の設定がありますが、こちらを削除しました。
詳細な内容はすべてinclude /etc/nginx/conf.d/以下に作成した.confファイルへ記述することにします。

/etc/nginx/conf.d/direct_block.confを以下のように作成します。

/etc/nginx/conf.d/direct_block.conf
1
2
3
4
5
6
7
8
server {
listen 80;
server_name localhost;
error_page 404 /404.html;
location / {
return 404;
}
}

/etc/nginx/conf.d/use_domain.confを以下のように作成します。

/etc/nginx/conf.d/use_domain.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 80;
server_name [ロードバランサーに割り当てているドメイン];
root /usr/share/nginx/html;

location / {
}

error_page 404 /404.html;
location = /40x.html {
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}

できたら、sudo nginx -tで記述に誤りがないかテストしてからsudo nginx -s reloadを実行して設定を再読み込みします。
http://[割り当てたドメイン]http://[サーバーのグローバルIPアドレス]にそれぞれアクセス。
後者の時は 404 が返ってくることが確認できます。

return 404;return 302 https://www.google.com;と書き換えることで、google へのリダイレクトもできました。

作った 2 ファイルは拡張子を.conf_とでもして読み込み対象から外します。

443 番ポートを受け付ける

今までサーバーは 80 番ポートを受け付けていました。
443 番ポートを受け付けるようにしてみます。

以下のディレクトリと、ファイルを作成します。ファイルの記述は見分けられればなんでもいいです。

  • /usr/share/nginx-443
  • /usr/share/nginx-443/html
  • /usr/share/nginx-443/html/index.html

/etc/nginx/conf.d/distinction_port.confを作成します。
以下の様にしました。

/etc/nginx/conf.d/distinction_port.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 80;
server_name _;
root /usr/share/nginx/html;

location / {
}
}
server {
listen 443;
server_name _;
root /usr/share/nginx-443/html;

location / {
}
}

http://[サーバーのグローバルIPアドレス]http://[サーバーのグローバルIPアドレス]:443でアクセスしたときの表示が変わりました。
ポートでの振り分けができました。

作ったファイルは拡張子を.conf_とでもして読み込み対象から外します。

転送前のアクセス元の IP で振り分ける

$http_x_forwarded_for に転送される前の IP が記述されること確認していました。
$http_x_forwarded_for を検証してアクセス元の IP アドレスで振り分けてみます。

以下のディレクトリと、ファイルを作成します。ファイルの記述は見分けられればなんでもいいです。

  • /usr/share/nginx-forwarded
  • /usr/share/nginx-forwarded/html
  • /usr/share/nginx-forwarded/html/index.html

/etc/nginx/conf.d/forwarded_ip.confを作成します。
以下の様にしました。

/etc/nginx/conf.d/forwarded_ip.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 80;
server_name _;
error_page 404 /404.html;

# アクセス元IPを検証
location / {
# 基本はこっちがルート
root /usr/share/nginx/html;

if ($http_x_forwarded_for = xxx.xxx.xxx.xxx){
# アクセス元IPが指定のものであればこっちがrootになる
root /usr/share/nginx-forwarded/html;
}
}
}

指定したグローバル IP を使っている手元の PC でアクセスしたときと、キャリア回線のスマートフォンで確認します。
http://[割り当てたドメイン]にアクセスすると、それぞれでの表示が変わりました。
アクセス元の IP を基準に振り分けできました。

しかし、1 つづつしか設定できないのは不便です。
以下のように/etc/nginx/conf.d/forwarded_ip.confを変更しました。

/etc/nginx/conf.d/forwarded_ip.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
geo $from {
default nginx;
yyy.yyy.yyy.yyy/32 nginx-forwarded;
zzz.zzz.zzz.zzz/32 nginx-forwarded;
}

server {
listen 80;
server_name _;
error_page 404 /404.html;

location / {
root /usr/share/$from/html;
}
}

geo ディレクティブを使用することで、アクセス元 IP アドレスの判定が可能でした。
上の場合、判定した結果が $from に返ってくることになります。

こちらで複数の IP アドレスを記述できました。
ただし、判定されるのは$remote_addrになるので、記述する IP アドレスはロードバランサーの IP アドレスになります。
少し意図と外れたので、別の方法を試します。

以下のように/etc/nginx/conf.d/forwarded_ip.confを変更しました。

/etc/nginx/conf.d/forwarded_ip.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
map $http_x_forwarded_for $local {
default 0;
xxx.xxx.xxx.xxx 1;
aaa.aaa.aaa.aaa 1;
}

server {
listen 80;
server_name _;
error_page 404 /404.html;

location / {
root /usr/share/nginx/html;
if ($local){
root /usr/share/nginx-forwarded/html;
}
}
}

map ディレクティブを使うことで、$http_x_forwarded_for の内容を場合分けして、変数に格納できます。
アクセス元の IP アドレスを一か所に複数記述して、振り分けることができました。

調べてみると$http_x_forwarded_for には、複数の IP アドレスが記述されていることがあるらしく、
正規表現での記述の必要がある様です。
参考:ELB + EC2 + nginx 環境でアクセス元 IP アドレスによる制限をかける

以下のように/etc/nginx/conf.d/forwarded_ip.confを直しました。

/etc/nginx/conf.d/forwarded_ip.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
map $http_x_forwarded_for $local {
default 0;
~\s*xxx.xxx.xxx.xxx$ 1;
~\s*aaa.aaa.aaa.aaa$ 1;
}

server {
listen 80;
server_name _;
error_page 404 /404.html;

location / {
root /usr/share/nginx/html;
if ($local){
root /usr/share/nginx-forwarded/html;
}
}
}

$http_x_forwarded_forに複数の IP が記述される再現できなかったですが、メモとして記載です。

作ったファイルは拡張子を.conf_とでもして読み込み対象から外します。

ファイルが見つからない時

アクセス先のファイルが見つからなかった時に、指定した先へリダイレクトするようにしてみます。
/etc/nginx/conf.d/not_found.confを作成します。
以下のようにしました。

/etc/nginx/conf.d/not_found.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
server{
listen 80;
server_name _;
error_page 404 /404.html;
root /usr/share/nginx/html;

location /{
try_files $uri $uri/ @not_found;
}
location @not_found{
return 302 https://www.google.com;
}
}

@[名称] を使うことで、ほかのlocation @[名称]へ飛ばすことができました。

http://[割り当てたドメイン]http://[割り当てたドメイン]/[適当な文字列]にアクセスすると、それぞれでの表示が変わりました。
http://[割り当てたドメイン]/[適当な文字列]時は、google にリダイレクトされました。

return 302 https://www.google.com;の部分は、rewrite ^(.*)$ https://www.google.com;でも可能でした。


今回は、nginx での振り分け処理を試してみました。
触っていない項目もあるので、もう少し探りたいところです。

ではでは。