nginxで国単位のIPアドレス制限
セキュリティのために日本国外からのアクセスをブロックしようとしたときの覚書。
環境: CentOS Stream 8, nginx 1.20.2, PostgreSQL 14.2
IPアドレスから国を判定するためにMaxMind社が提供しているデータを使う。
MaxMind社はIPアドレスの位置情報を提供しているアメリカの会社。
参考: MaxMind - Wikipedia
nginxにGeoIP2モジュールを追加するとパフォーマンスに影響が出そうな気がするので、まずは国ごとのIPアドレスリストを生成することから始めてみた。
MaxMindはAPIを幅広く提供していて使いやすい。
GeoLite2はクリエイティブコモンズライセンスで提供されている。
配布する場合はクレジット表示が必要。
参考: GeoLite2 Free Geolocation Data | MaxMind Developer Portal
国別IPアドレスリストをPostgreSQLにインポート
公式サイトを参考に。MySQLへのインポート方法もある。
PostgreSQLにはcidrというIPv4とIPv6を格納するデータ型があるので便利。
- Importing GeoIP2 and GeoLite2 databases to PostgreSQL | MaxMind Developer Portal
- PostgreSQL: Documentation: 14: 8.9. Network Address Types
簡単な手順
- メールアドレスでサインアップする。
- マイページ左メニューの「Download Files」から「GeoLite2-Country-CSV」zipファイルをダウンロード。
ASNはAS番号を割り当てられた組織
参考: 自律システム (インターネット) - Wikipedia - PostgreSQLにインポート
CSVファイルを開発サーバーに置いて、DBeaverでデータベースを作成して、公式サイトのcreate table文を実行。
参考: Importing GeoIP2 and GeoLite2 databases to PostgreSQL | MaxMind Developer Portal
あとはコンソールで作業する。作成したgeoデータベースにアクセス。
# su - postgres
$ psql geo
確認のためテーブル一覧表示。
geo=# \d
IPv4リストのCSVをgeoip2_networkテーブルへインポート実行。
geo=# \copy geoip2_network(network, geoname_id, registered_country_geoname_id, represented_country_geoname_id,is_anonymous_proxy, is_satellite_provider) from '/home/httpd/GeoLite2-Country-Blocks-IPv4.csv' with (format csv, header);
IPv6リストのCSVをgeoip2_networkテーブルへインポート実行。
geo=# \copy geoip2_network(network, geoname_id, registered_country_geoname_id, represented_country_geoname_id,is_anonymous_proxy, is_satellite_provider) from '/home/httpd/GeoLite2-Country-Blocks-IPv6.csv' with (format csv, header);
IPアドレスと国コードを紐づけるテーブルをインポート実行。
geo=# \copy geoip2_location(geoname_id, locale_code, continent_code, continent_name, country_iso_code, country_name, is_in_european_union) from '/home/httpd/GeoLite2-Country-Locations-ja.csv' with (format csv, header);
試しに自分のIPがどこの国かselectしてみる。
geo=# SELECT * FROM geoip2_network gn LEFT JOIN geoip2_location gl on gn.geoname_id = gl.geoname_id WHERE gn.network >> '210.191.11.22';
nginxのアクセスログで変な動きのIPアドレスの送信元を調べてみる。
geo=# SELECT * FROM geoip2_network gn LEFT JOIN geoip2_location gl on gn.geoname_id = gl.geoname_id WHERE gn.network >> '185.153.199.66';
ビット演算子を初めて使った。
参考: PostgreSQL: Documentation: 14: 9.6. Bit String Functions and Operators
geo=# quit
nginxの拒否/許可リストを生成
nginxの設定ファイル用にdeny/allow文字を付けてSQL文を実行する。
(SQLの実行結果をファイルに書き出す)
ロシアからの拒否リスト生成。
$ psql geo -c "SELECT concat('deny ', network, ';') FROM geoip2_network gn LEFT JOIN geoip2_location gl on gn.geoname_id = gl.geoname_id WHERE gl.country_iso_code = 'RU';" >> deny_RU.conf
中国からの拒否リスト生成。
$ psql geo -c "SELECT concat('deny ', network, ';') FROM geoip2_network gn LEFT JOIN geoip2_location gl on gn.geoname_id = gl.geoname_id WHERE gl.country_iso_code = 'CN';" >> deny_CN.conf
日本からの許可リスト生成。
$ psql geo -c "SELECT concat('allow ', network, ';') FROM geoip2_network gn LEFT JOIN geoip2_location gl on gn.geoname_id = gl.geoname_id WHERE gl.country_iso_code = 'JP';" >> allow_JP.conf
ファイルの先頭と末尾のいらない文を削除。
viコマンドは「1G」で先頭に、「G」でファイル末尾に移動できる。
「dd」で行削除。
# less deny_RU.conf
rootに戻ってnginx設定。
conf構成はGistに置いてあるので参考に。
参考: Nginx Configuration Files for WordPress. Nginx is installed by YUM. @see:https://codex.wordpress.org/Nginx
# cd /var/lib/pgsql/
# mv deny_CN.conf deny_RU.conf allow_JP.conf /etc/nginx/conf.d/global/
# cd /etc/nginx/conf.d/global/
# less wordpress_restrictions.conf
## Black List#include conf.d/global/deny_RU.conf;include conf.d/global/deny_CN.conf;
nginxは上から順に評価していく。
なのでdenyのあとに「allow all;」は必要ない。
allow_JP.confを使う場合はincludeのあとに「deny all;」は必要。
例えば中国の一部のIPアドレスを許可する場合はdeny_CN.confの前にallowしてあげればOK。
nginx再読み込み。
# nginx -t
# systemctl reload nginx
ロシアのdenyリストは22,316行、中国は13,831行ある。
これだけ毎回チェックするのはパフォーマンスに影響しそう。
nginxモジュールの方がいいかもしれないと思い始めた。
結局Cloudflareと連携する方が幸せになるのかもしれない。
(CloudflareにはIPアドレスから国コードを判定する機能がある)
定期更新
公式サイトでも定期的に更新する方法の説明がある。
GeoLite2 Countryは毎週火曜日に更新される。
- Updating GeoIP and GeoLite Databases | MaxMind Developer Portal
- Download and Update Databases – MaxMind
そのうち自動化しようと思う。
【関連記事】
- CentOS8にPostgreSQLをインストール。Windows10からDBeaverで接続
- Nginxで特定のIPアドレスからのアクセスを拒否
- WordPressのXML-RPCとREST API(WP-JSON)を無効にする(Nginxの設定)