nginxでIPアドレス直打ちのアクセスを拒否する

# はじめに

80番ポートを開放してWebサーバーを構築すると、よくわからない国からの変なアクセスがログに残っていることが多々あります。  
そのログを確認してみると「admin」なり「eval」なりの不正アクセスを目的としてそうな単語が…  
何か良からぬことをされる前に、URLからのアクセスのみを許可してIPアドレス直打ちでのアクセスをすべてブロックする設定をしてしまいましょう。

ここでは「ブラックリスト方式」と「ホワイトリスト方式」の二種類を紹介しています。  
最後まで読んだ上で自分に合った方式を利用することをおすすめします。

:::note warn
警告
この記事では何かしらの方法で証明書を取得した前提で説明を行っています。  
証明書を取得しておらずhttp通信のみでnginxを運用している場合、https関連の設定（443番ポートやsslなど）を上手いことスルーして設定を行ってください。
:::

# 環境

* Ubuntu 20.04.4 LTS
* nginx 1.18.0
* Let's Encryptにて証明書を取得
* http通信には80番ポートを使用
* https通信には443番ポートを使用

# ブラックリスト方式

この方式はとてもシンプルで、`/etc/nginx/conf.d/`にIPアドレスへのアクセスをブロックする旨の設定ファイルを追加するだけです。  
実際の内容は以下の通りです。

```nanoを使ってファイル「blockip.conf」を追加
$ sudo nano /etc/nginx/conf.d/blockip.conf
```

```nginx:blockip.conf
# http通信に対する記述
server {
    # nginxが利用するポートの指定
    listen 80;
    # IPv6を有効にするときの特殊な指定
    listen [::]:80;

    server_name 0.0.0.0;

    return 444;
}

# https通信に対する記述
server {
    # nginxが利用するポートの指定
    listen 443;
    # IPv6を有効にするときの特殊な指定
    listen [::]:443;

    server_name 0.0.0.0;

    return 444;
}
```

0.0.0.0 の部分はサーバーのグローバルIPアドレスを入力してください。

簡単に説明を挟むと、server_name に一致するURL（今回の場合はIPアドレス）でのリクエストが有った場合に  
ステータスコード「444」を返すというのが今回行った設定です。  
444については最下部で詳しく説明しています。

この方法はサーバーのIPアドレスを指定し、その指定したアドレスへのアクセスをブロックしています。  
しかし、プロバイダーとの契約がモデムを再起動するたびにIPアドレスが変わる「動的IPアドレス契約」だった場合、  
毎回設定ファイルを編集する必要があり非常に面倒です。

その場合、次のホワイトリスト方式をおすすめします。

# ホワイトリスト方式

この方式は許可するURLを前もって定義し、それ以外のアクセス（例外）を全てブロックするという設定です。  
特徴として、ブラックリスト方式と比べると設定が少しめんどくさいという一面がありますが、想定外のアクセスを全てブロックできるという面では前者よりも優れており、設定する価値は大いにあると言えるでしょう。

## 準備

今までに何度かnginxを弄った経験があり`conf.d`と`sites-available`の仕様を理解しているのなら問題はありませんが、  
そうでない場合は、既存設定の削除を行う必要があります。  
なので準備として、バックアップを取った上で該当ファイルを削除しましょう。

既存設定のバックアップ
```
$ mkdir ~/nginx_backup
$ cp -RT /etc/nginx/sites-available/ ~/nginx_backup/sites-available/
$ cp -RT /etc/nginx/sites-enabled/ ~/nginx_backup/sites-enabled/
$ cp -RT /etc/nginx/conf.d/ ~/nginx_backup/conf.d/
```

初期設定の削除
```
$ sudo rm /etc/nginx/sites-available/*
$ sudo rm /etc/nginx/sites-enabled/*
$ sudo rm /etc/nginx/conf.d/*
```

空の設定ファイルを作成
```
$ sudo touch /etc/nginx/sites-available/default
```

## 手順1 許可するURLの設定

nanoを使って`/etc/nginx/conf.d/`にファイルを追加していきます。

```nanoを使ってファイル「main.conf」を追加
$ sudo nano /etc/nginx/conf.d/main.conf
```

```nginx:main.conf
# http通信に対する記述
server {
    # nginxが利用するポートの指定
    listen 80;
    # IPv6を有効にするときの特殊な指定
    listen [::]:80;

    server_name hogehoge.com;

    # Webサーバーのルートディレクトリを設定
    root /var/www/html;

    # indexファイルの指定
    index index.html index.htm;

    # 存在しないファイルにアクセスされた場合の処理
    location / {
            try_files $uri $uri/ =404;
    }
}

# https通信に対する記述
server {
    # nginxが利用するポートの指定
    listen 443;
    # IPv6を有効にするときの特殊な指定
    listen [::]:443;

    server_name hogehoge.com;

    # Webサーバーのルートディレクトリを設定
    root /var/www/html;

    # indexファイルの指定
    index index.html index.htm;

    # 存在しないファイルにアクセスされた場合の処理
    location / {
            try_files $uri $uri/ =404;
    }

    # サーバー証明書のフルパス
    ssl_certificate /etc/letsencrypt/live/hogehoge.com/fullchain.pem;
    # 暗号鍵のフルパス
    ssl_certificate_key /etc/letsencrypt/live/hogehoge.com/privkey.pem;
    # Let's Encryptのnginx用設定を読み込む
    include /etc/letsencrypt/options-ssl-nginx.conf;
    # Let's Encryptのアルゴリズムを指定
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
```

hogehoge.com の部分はサーバーのURLを入力してください。  
複数のURLを使用する場合は`server_name hogehoge.com fugafuga.net`のように半角スペースで区切って入力してください。  

Let's Encryptを使用していない場合は include と ssl_dhparam は無視しても構いません。~~（多分）~~

## 手順2 例外処理の追加

手順1で指定したURL以外でのリクエスト（IPアドレス直打ちなど）に対する処理を設定します。  
先程と同じく、nanoを使って`/etc/nginx/conf.d/`にファイルを追加していきます。

```nanoを使ってファイル「block.conf」を追加
$ sudo nano /etc/nginx/conf.d/block.conf
```

```nginx:block.conf
# http通信に対する記述
server {
        listen 80 default_server;
        listen [::]:80 default_server;

        server_name _;

        return 444;
}

# https通信に対する記述
server {
        listen 443 ssl default_server;
        listen [::]:443 ssl default_server;

        server_name _;

        # サーバー証明書のフルパス
        ssl_certificate /etc/letsencrypt/live/hogehoge.com/fullchain.pem;
        # 暗号鍵のフルパス
        ssl_certificate_key /etc/letsencrypt/live/hogehoge.com/privkey.pem;

        return 444;
}

```

ここは少し説明が必要かもしれません。

listen の default_server についてですが、これはとても単純で「設定ファイルで定義されていない場合はこの処理をする」という記述です。  
今回の場合は前もって指定したURL以外でのリクエストがあった場合（IPアドレス直打ちなど）の処理として動作します。

server_name の _（アンダースコアと言います）については、所謂ワイルドカードというやつで、定義されていない全てのURLに当てはまります。  
要は、特別書かないといけないURLはないけど、でも書かないと動いてくれないから仕方なく書くみたいな認識で大丈夫です。

`return 444`については下で詳しく説明していますが、ステータスコード「444」を指定するという意味です。

## 手順3 LANからのアクセスを許可する

この設定では指定したURL以外からのアクセスを全てブロックすることになります。  
したがって、LANから`http://192.168.1.1`のようにアクセスした場合でもブロックされてしまいます。  
LANからアクセスすることがないのならこの手順は無視しても構いませんが、そうでないのならこの設定を推奨します。

今までと同じく、nanoを使って`/etc/nginx/conf.d/`にファイルを追加していきます。

```nanoを使ってファイル「local.conf」を追加
$ sudo nano /etc/nginx/conf.d/local.conf
```

```nginx:local.conf
server {
    listen 80;
    server_name 192.168.1.1;

    root /var/www/html;
    index index.html index.htm;
}
```

192.168.1.1 の部分はサーバーのローカルIPアドレスを指定してください。

LANからのアクセスにhttpsやIPv6は使わないだろうという読みで、http通信のIPv4のみ設定しましたが、  
必要なら場合に応じて変更してください。

これにて設定は完了です。  
お疲れさまでした。

# 余談

以下、ちょっとした余談です。

## ステータスコード「444」について

nginxには独自のステータスコードがいくつか存在しており、444はその内の一つです。  
444が返されたときnginxは接続を切断します。  
つまりクライアント（アクセスした人）の元にはデータが何も返ってこないため、その接続をブロックしたということになります。