Nginxで特定の国のIPだけを許可する方法


投稿日:2016年12月13日
  • 1
  • 1
  • 0



自動で最新の国別IPリストを取得し、Nginxに反映させます

nginx_allow_jp

とあるサイトで日本のIPのみアクセスを許可する必要がありました。

特定の国のIPだけを許可をする方法はApache.htaccessを使う方法はよく解説されていますが、Nginxの情報はあまり見なかったので、このページでまとめました。

応用すれば複数の国のIPを許可したり、特定の国のみ拒否、といったことも実現できます。


最新のIPリストを毎日取得して更新

各国に割り振られているIPリストは日々変動します。そのため定期的にリストを更新する必要があります。このページではcronを利用して毎日最新のIPリストを取得します。

IPリストを取得する方法は以前投稿したiptablesの解説で作成しました。

冗長なほどコメントを入れましたが、さらに解説が必要な方は「iptablesの設定ファイルをシェルスクリプトを利用して動的に作成」というページの「世界の国別 IPv4 割り当てリストを毎日ダウンロードして最新の状態に保つ」という部分を読んでください。

このページではiptables用の部分は削除しています。(通知するメールアドレスの部分は適宜変更してください)

/etc/cron.daily/」に「iplist_check.sh」というスクリプトを作成します。

# vi /etc/cron.daily/iplist_check.sh
#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
 
# ファイルの取得
wget -q http://nami.jp/ipv4bycc/cidr.txt.gz -P /tmp
 
# 解凍してリネーム
gunzip -q -f -c /tmp/cidr.txt.gz > /tmp/iplist.new
# /tmp下なので240時間で自動的に削除されますが、念のため削除
rm -f /tmp/cidr.txt.gz
 
# iplistが存在しているかチェック
if [ -s /tmp/iplist ]; then
 
     # diffでエラーが出るので実行権限の変更。「-f」を付けて初回にファイルがない場合に対応
     chmod -f 700 iplist.new
     chmod -f 700 iplist
 
     # 差分チェック(正常にファイルが取得できたかチェック)
     # grep -c パターンに一致した行の行数のみを出力する。diffで差分(違い)のある行は「< hoge」と表示されるため、行頭に「<」がある行をカウントしている。「=」の前後にスペースがあると正常に代入できないので注意。
     # $()の中に記述しているので、変数ipdiffに代入。
     ipdiff=$(diff /tmp/iplist /tmp/iplist.new  | grep -c "^<")
 
     # 上記が存在していて、差分の数が1000行以上の場合
     # (通常考えられない量の変更=ファイルが正常に取得できていないと判断)
     if [ $ipdiff -gt 1000 ]; then
          # メールでIPリストが正常に取得できなかった由を伝える。
          cat <<EOM | mail -s "iplist_check" info@example.com
               iplist false
EOM
          # 正常に取得できなかったリストを破棄
          rm -f /tmp/iplist.new
     else
          # 正常に取得できた場合ファイル名をiplistに変更
          mv /tmp/iplist.new /tmp/iplist
     fi
else
     # 初回実行時は差分をチェックすべきバックアップがないのでそのままファイル名を変更して利用
     mv /tmp/iplist.new /tmp/iplist
fi

これで「/tmp/iplist」というIPリストが作成されます。


Nginxの設定ファイルを書き出すスクリプトを作成

取得したIPリストを元に、日本のIPだけを許可するNginxの設定ファイルを書き出します。

/usr/script/」に「nginx_allow_jp.sh」というスクリプトを作成します。

# vi /usr/script/nginx_allow_jp.sh

IPリストの行頭がJPのIPを取得しNginxの形式に合わせてファイルを書き出しています。

#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin


# iplistが存在しているかチェック
if [ -s /tmp/iplist ]; then

	# 行頭がJPのIPを取得してnginxの形式に変換
	if [ -s /tmp/iplist ]; then
	     sed -n 's/^JP\t//p' /tmp/iplist | while read address;
	     do
	          echo allow    $address";";
	     done
	     echo deny     all";";
	fi
fi

具体的には以下のような形式で出力されます。

allow    191.168.l.l;
allow    191.168.l.2;
(同じようなIPがずらーっと続く)
deny     all;

あとでcronから実行するので実行権限をあたえます。

# chmod 755 /usr/script/nginx_allow_jp.sh

cronで最新の設定ファイルを適応するスクリプトを作成

上で作成したnginx_allow_jp.shを実行し、Nginxで反映させるスクリプトを作ります。

# vi /etc/cron.daily/nginx_allow_jp_daily.sh
#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin


# 日本のIPリストをnginx用の設定形式で書き出す
sh /usr/script/nginx_allow_jp.sh >/etc/nginx/global/nginx_allow_jp.conf

# 設定を反映
/usr/sbin/nginx -s reload

設定ファイルは「/etc/nginx/global/」に「nginx_allow_jp.conf」というファイル名で作成しました。

実行権限を当たえます。

# chmod 755 /usr/script/nginx_allow_jp_daily.sh

Nginxで外部の設定ファイルを読み込む

あとはNginx側で読み込むだけです。
設定ファイルにて、適応させたいディレクティブで、includeを利用しパスを指定するだけです。

# vi /etc/nginx/nginx.conf
location /hoge/ {
	# 日本のIPのみ許可する
	include /etc/nginx/global/nginx_allow_jp.conf;
}

これで最新のIPリストで日本のみアクセスを許可するNginxの設定は完了です。
毎日更新する必要がなければcron.weeklyやcron.monthlyをご利用ください。


上記の方法で数日運用してみましたが、特に問題はなさそうです。

というのも当初は「日本のIPのみアクセスを許可する」という要望に対し、上流で遮断するというセオリー通りiptablesで遮断しようと考えていました。

iptablesにはリクエストの文字列を条件にして、通信をコントロールするstringモジュールがあります。「stringモジュールを使えば日本のIPだけを許可するのも簡単だ。」と高を括っていたのですが、どうにも挙動が不安定でした。(ごく単純な指定でしか動作しませんでした)

またiptablesの解説にもstringモジュールで通信を遮断する方法は非推奨とあったので、すっぱり諦めました。
何かもっと良い方法をご存知の方がいたらコメント欄にでも一言いただけると助かります。



現在のページを共有する



現在のページに関連する記事


おすすめの記事


コメントを残す

プログラミングに関する質問は「日本語でプログラミングの悩みを解決するQ&Aサイト sukegra」をご利用ください。面倒な登録なしでご利用になりれます。