ネットワーク
NGINX で IP アドレスによるアクセス制限を行う方法
IP アドレス によるアクセス制限をしたいとのご要望があり、実現方法について詳しく調べてみました。ここではその3つの方法と特徴、使い分け例をご紹介します。
こんにちは narai です。
特定の IP アドレスからのリクエストを拒否したいというご要望があり、そのやり方について詳しく調べてみました。
IP アドレスでの制限について
結論から言えば、NGINX で IP アドレスによるアクセス制限を行う方法は、主に以下の 3 種類になります。
- allow/deny ディレクティブで制限する
- geo ディレクティブで変数を管理して if 文で制限する
- keyval ディレクティブで変数を管理して if 文で制限する (NGINX Plus)
どれも特徴があるので、シチュエーションに合わせて使い分けると良いですね。
方法 | 特徴 |
allow/deny ディレクティブ |
設定が単純で容易にできる・ location でも利用可能 403 しか返せない・ 設定が長くなる ・設定変更毎に reload が必要 |
geo ディレクティブ |
任意のステータスコードをレスポンスできる・ server 毎に細かい設定が可能 設定変更枚に reload が必要 ・ location 単位での制御に考慮が必要 ※1 |
keyval ディレクティブ |
任意のステータスコードをレスポンスできる・server 毎に細かい設定が可能 ・設定変更に reload が不要 値は API でしか設定できない ・ location 単位での制御に考慮が必要 ※1 |
※1 : location で if 文を使用する場合、return , reweight … last 以外のディレクティブを使うと予期せぬ動作をする場合があります。
では、各設定方法を詳しく見てみましょう。
allow/deny ディレクティブで制限する
allow/deny ディレクティブは、IP アドレスを許可/拒否するためのディレクティブです 。
サイト全体に対しての制限や、特定のコンテンツのみ制限を掛けたい場合に有用です。
設定方法
allow/deny は http/server/location context で使用でき、deny に該当したリクエストに対しては、403 Forbidden を返します。
また、上から順番に処理されるので、一番上に deny all; があると、後で許可していてもすべて拒否されてしまうので注意が必要です。
書式
allow <IP address> ;
deny <IP address> ;
設定例
以下は、http context ですべてのアドレスを拒否したうえで、server context で必要なアドレスのみを許可する設定です。
また、多数のアドレスを指定する必要があるため、アドレスリストを別ファイルで用意して、inculde ディレクティブで読み込みます。
こうすることで、nginx.conf が見ずらくなることを防ぎます。
geo ディレクティブで変数を割り当てて、if 文で制限
任意のステータスコードを返したい場合や、別ページにリダイレクトさせたい場合は geo ディレクティブを使用します。
設定方法
geo ディレクティブは http context で使用でき、 IP アドレス(サブネットマスク含む)に基づき、変数を格納するディレクティブです。
default パラメータを指定することで、マッチしなかった場合の値を決めることができますが、指定しない場合、ヌルになります。
書式
get <$variable> {
<IP address> : <variable> ;
}
設定例
以下は、geo ディレクティブで拒否するアドレスであれば 1 (True) を$deny_ip に代入します。
そして、if 文にて deny_ip が 1 (True) だった場合に 503 のステータスコードを返すとともに、制限された旨のページ(noauth.html)を返すような設定となります。
keyval ディレクティブを使用してファイルで制限(NGINX Plus)
allow/deny と geo ディレクティブはいずれもアドレスの追加/削除の際、nginx の reload が必要となります。
ですが、NGINX Plus では keyval ディレクティブを使用することで、アドレス追加/削除を動的に実施することができます。
設定方法
keyval ディレクティブは、API および njs を使用して外部から設定することができる変数です。
keyval_zone ディレクティブでデータベースを保存する共有メモリゾーンを作成します。また、state オプションを使用することで、ファイルに保存し、永続化することができます。
次に、keyval ディレクティブでデータベースの形式を指定します。
書式
keyval_zone zone=<name>:<size> state=<file> ;
keyval <key> <$variable> zone=<name> ;
また、データベースへの登録は、API を使用して外部から行います。
API path
http://<hostname>/api/6/http/keyvals/<file>
設定例
以下は、one という zone に geo ディレクティブと同様、拒否する IP アドレスであれば 1(True) を$deny_ip に代入します。
そして、if 文にて deny_ip が 1 (True) だった場合に、403 を返します。
また、制御する IP アドレスの追加や削除は、API 経由で行いますので、/api location で 受け付けられるようにします。
制御する IP アドレスを curl を使用して設定します。
keyval_zone が空のときは以下のように複数の値を指定できますが、keyval_zone に値が入っている場合は、1つずつ追加する必要があります。
また、keyval_zone を保存しているファイル(/var/lib/nginx/state/one.keyval)を直接編集することはサポートされていません。
正しく設定されると、内容がファイルにも保存されます。
API によるアドレスの管理方法については、このサイトを参照してください。
まとめ
ここまで 3 つの方法を見てきました。
それぞれの方法に特徴がありますが、以下のような基準で判断するのが良いかと思います。
– 単純なアクセス制限であれば allow/deny
– 任意のエラーコードやコンテンツを返したい場合は geo
– 対象アドレスを柔軟に制御したい場合は keyval
なお、NGINX Plus の機能である Keyval は IP アドレス以外の値も設定できますので、任意の値によるアクセス制御も可能です。
「こういったことをやりたい」といったことがありましたら、ご気軽にお問い合わせください。