2026/06/27(土)nginxでGeoIP2とUserAgentを利用したアクセス制御を実装してみた
投稿日:
過去に何度かBOTなどを避けるためのアクセス制御を導入していたが、結果どれも微妙だった。しかしどうにかしたい、そうだ、nginxは高機能なのでできるんじゃないか?と思って実践してみたログ。
User-AgentはGEOIP2と何も関係ないが、ついでにやったので書いている。
確認環境
| Env | Ver(apt上) | 備考 |
|---|---|---|
| nginx | 1.24.0-2ubuntu7.13 | nginx -vではnginx/1.24.0 (Ubuntu) |
| nginx-common | 1.24.0-2ubuntu7.13 | |
| libnginx-mod-stream | 1.24.0-2ubuntu7.13 | |
| libnginx-mod-http-geoip2 | 1:3.4-5build2 |
やったこと
互換性確保のためnginxのダウングレード
インストールされていたnginxが1.26.1-2~jammyで、libnginx-mod-http-geoip2との互換性がなかったためダウングレードすることにした。
まず次のコマンドで整合性を確認した。コマンド自体はGPT-5.5が作ってくれたものであるが、こうすることでダウングレードを許可した上での互換性チェックができるようだ。
コマンドの実行結果としては既に追加されているコンポーネント、ダウングレードされるコンポーネント、新たに追加されるコンポーネントの一覧が出てくる。
# ngx_http_geoip2_module.soが存在しないことを確認。既にある場合は本項の工程は飛ばせると思う
ls -la /usr/lib/nginx/modules
sudo apt install -s --allow-downgrades \
nginx=1.24.0-2ubuntu7.13 \
nginx-common=1.24.0-2ubuntu7.13 \
libnginx-mod-stream=1.24.0-2ubuntu7.13 \
libnginx-mod-http-geoip2
上記の構成でインストールできることが分かったので-sを外してnginx本体をダウングレードし、他をインストールする。
sudo apt install --allow-downgrades \
nginx=1.24.0-2ubuntu7.13 \
nginx-common=1.24.0-2ubuntu7.13 \
libnginx-mod-stream=1.24.0-2ubuntu7.13 \
libnginx-mod-http-geoip2
# ngx_http_geoip2_module.soが増えていることを確認
ls -la /usr/lib/nginx/modules
ngx_http_geoip2_module.so
GEOIP2データベースの入手と配置
MMDBなら何でも行けると思うので、Matomoで使ってる無料の奴を使う。理想的にはCRONで定期取得するのがいいと思うが、面倒なので今回はやってない。
sudo mkdir -p /usr/share/GeoIP
sudo wget https://download.db-ip.com/free/dbip-country-lite-2026-06.mmdb.gz -O /usr/share/GeoIP/dbip-country-lite.gz
gunzip /usr/share/GeoIP/dbip-country-lite.gz
nginxの設定でGEOIPの有効化を行う
/etc/nginx/nginx.confに以下の設定を追記すると、$geoip2_で始まる変数にMMDBの中身が入るようになる。
load_module modules/ngx_http_geoip2_module.so;
http {
...
geoip2 /usr/share/GeoIP/dbip-country-lite {
auto_reload 24h;
$geoip2_metadata_country_build metadata build_epoch;
$geoip2_country_code country iso_code;
$geoip2_country_name country names en;
}
...
nginxのJSONログに国コードを含める
JSONログにキーを追加してやればよい。
http {
...
log_format main_json escape=json
'{'
...
'"country_code":"$geoip2_country_code"'
'}';
国コードとUserAgentの判定フィルタの元ネタを作る
nginxのmap構文を使って判定用の元ネタを作る。
左側は~で始めると正規表現扱いになり、~*とすると大文字小文字の違いを無視してくれるようになるらしい。
http {
# headlesschromeを2にしてるのは、これはちょっと毛色が違うと思ったため、便宜上分類分けしている。
# 但し現時点でこの値を特別扱いしておらず、1と等価なので無駄ではある。
map $http_user_agent $deny_ua {
default 0;
"~*meta-externalagent" 1;
"~*baidu" 1;
"~*headlesschrome" 2;
"" 1;
}
map $geoip2_country_code $deny_ca {
default 0;
"CN" 1;
"BR" 1;
"IN" 1;
}
# nginxのif文にはandやorに相当する演算子がないのでmapで合成しておく
map "$deny_ua:$deny_ca" $deny_client {
default 1;
"0:0" 0;
}
....
}
雰囲気で読んでいるが構文の意味合いはこうだと思う。KeyValueで並べていくと入力値($input_variable)のKey(input_value)に対するValue(output_value)が出力値($output_variable)に入るのだと思う。
map $input_variable $output_variable {
default <default_value>; # デフォルト値
input_value output_value;
...
}
フィルタ用のsnippetsを作る
vhostに撒くように/etc/nginx/snippets/deny_client.confのようなsnippetsを作る。
前述のmapで判定をまとめているのでif文一つで制御できるようになっている。
if ($deny_client = 1) {
return 403;
}
snippetsをvhostに撒く
反映したいvhostの設定にsnippetsを撒いていく。
server {
...
include snippets/deny_client.conf;
...
動作検証
こんな感じで検証して弾かれてれることを確認した。国コードはどうにもならないが原理上問題ないはずなので大丈夫だろう。
curl -H "User-Agent:meta-externalagent/1.1" https://lycolia.info
あとは念のために国コードが出ているかどうかをnginxのログを見て出ていたらOK。
関連記事
- 自宅サーバーの一部がダウンしたが監視を入れていてよかった話
- Facebook(Meta)のBOTのせいでサーバーが落ちた話
- Anubisを雑に設置したが、結果微妙だったので撤去した話
- Anubisで防げないか試してみたが、Anubisのストレージや、アクセス解析との親和性などの関係でなかったことにしたやつ
- このブログにやってくる海外IPのBOTの挙動を軽く調べた
- 取り敢えずどんな迷惑アクセスがあるか調べたときの奴
- CGIのラッパーCGIを作る方法
- この記事ではadiaryに対するアクセス制御のラッパーを書いていた
あとがき
今回のアクセス制御の導入にあたり、運用面で課題のあるAnubisを回避しつつ、大量に存在するCGIにラッパーCGIを嚙ますなどの対応を回避できたのはよかった。
nginxは何とも高性能だ。そして自宅サーバーに全部乗せしたおかげで、レンタルサーバーでは決して手が届かないことができるのが良いと思った。
しかしググってもGEOIP無印の情報ばかりで、GEOIP2の情報が全然なかったので地味にハマってしまった。こういう時LLMに情報を漁ってもらうと取っ掛かりが得られて突破口を見つけられたりするので便利だと感じる。
パッケージを見ていて気になったこと
パッケージを見ていて気になったことは元のコードは保守されてないにもかかわらず、ディストリごとにコードが保守されているように見えたことだ。
今回導入したのはlibnginx-mod-http-geoip2だが、これは元を辿ればDebianのlibnginx-mod-http-geoip2のようで、更に元を辿るとleev/ngx_http_geoip2_module: Nginx GeoIP2 moduleに行きつく。
何故ならDebianのリポジトリがGitHubより新しく、READMEの中身が同じだからだ。設定方法もまるっきり同じに見える。
GitHubのリポジトリは2年以上放置されており「Support nginx 1.23.0」で時が止まっているが、Ubuntuでは「Rebuild against new nginx 1.30.1.」とあるため、だいぶ新しいところまでサポートが進んでいる。
よく考えるとパッケージのバージョンに-ubuntuみたいなのがついているのも良くあることなので、もしかしたらディストリごとにこういったアプリケーションを保守していたりするのだろうか?
そういえばPHPなんかも本家がサポート切れててもUbuntuではサポート内とか聞いたことがある。動作サポートなのかセキュリティサポートなのか、何のサポート課なのかまでは知らないが、星の数ほどあるパッケージをディストリごとに保守しているとしたら、これは凄いことだなと思ったし、他人が書いた得体のしれないパッケージをどう保守しているのかも気になった。
Linuxの世界は興味深い…。
nginxのダウングレードからのバージョンの復旧について
ダウングレードしても基本的に影響はないと思うが、元のバージョンに上げようとするとUbuntuそのもののバージョンアップが必要なことを知った。
しかし24.04.04 LTSから26.04への安全なアップグレードはまだ提供されていないようなのでいったん断念した。26.04.01が出ると上げられるようになるらしく、現状はお試し版みたいな感じらしい。
よくあるx.00は安定板だけど不具合がある、x.01で真の安定板になるみたいな話はUbuntuにもあるんだなと思った。
あとがき
昔nginx公式のどこかにnginxでifを使うなみたいなのがあったと思う。たぶん今はもう消えていて、GitHubに残ってる残骸に残るだけになっているようだ。
ここを見る限りifを使うとifの判定後、別のifにかかることで想定外の挙動をするから、使うならreturnやrewrite ... lastと組み合わせて、確実にリクエストを殺すようにしろということのように読めた。
公式リファレンスを見てもifそのものを使っている個所はngx_http_rewrite_moduleに存在するため、「If is Evil...」だから使ってはいけないというより、適切に使えればよいのだと思った。
あと思ったのだが、恐らくこの「If is Evil...」はnginx公式ではなく、nginx plusの方にあったドキュメントだった気がする。サイトのカラーが緑だった記憶があるからというのと、このリポジトリがそれっぽいからだ。
2026/06/07(日)シェルの設定にRPS1を入れると微妙だったのでやめた話
投稿日:
私はzshをシェルとして使っており、かれこれ5年ほどプロンプトにPROMPTとRPS1を設定していたのだが、使っていてRPS1が非常に邪魔だったのでPROMPTに統一することにした。
邪魔だったもの
これまで私は画像のようにRPS1に日時と異常終了コードを出すようにしていたのだが、複数行選択したときに必然的に巻き込んでしまう問題があった。
これはコマンドの実行ログをブログに書く時や、複数コマンドをコピペするときにRPS1の部分を除去する必要があり、手間だった。
そこでRPS1を使うのをやめてPROMPTに統一することにした。行は増えたが、こっちのほうが視線移動もなくコピペ時の巻き込み問題も減るので楽になった気がする。
他にもRPS1はターミナルの幅が狭くなったり、一部のターミナルから開くと崩れやすかったので、今回の対応でそういったこともまとめて解消されてよかった。
これは余談だがPS1でなくPROMPTを使っている理由はPS1だとRPS1を使っているときに表示が崩れる環境があったが、PROMPTだとこれが起きなかったからだ。何故かはよくわかってない。
2026/06/05(金)Ubuntuのadiaryをlibfcgi-perlで動かす方法
サイト環境を移転したのでadiaryのパフォーマンス計測をやってみたの環境構築した時のログ。
確認環境
| Env | Ver |
|---|---|
| Ubuntu | 24.04.4 LTS |
| nginx | 1.26.1 |
| adiary | 3.52dev / Extends 0.25.0 |
| libfcgi-perl | 0.82+ds-3build2 |
前提
nginxとperlがインストール済
手順
- libfcgi-perlをインストールする
sudo apt install libfcgi-perl /etc/systemd/system/adiary.serviceを次のように作成し、adiaryのFastCGIデーモンを作る[Unit] Description=adiary daemon After=network.target [Service] Type=simple User=www-data Group=www-data WorkingDirectory=/var/www/path/to ExecStart=/usr/bin/perl /var/www/path/to/adiary.fcgi /var/www/path/to/adiary.sock 10 100 Restart=always [Install] WantedBy=multi-user.target- サービスを有効化する
sudo systemctl enable adiary.service sudo systemctl start adiary.service nginxの設定を書く
server { listen 443 ssl; listen [::]:443 ssl; server_name blog.example.com; client_max_body_size 100M; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; root /var/www/path/to/blog; location / { try_files $uri @adiary; } location /__cache { deny all; } location /data { deny all; } location ~ \.cgi$ { deny all; } location @adiary { include fastcgi_params; fastcgi_pass unix:/var/www/path/to/adiary.sock; fastcgi_param Basepath /; } }- nginxを再起動する
sudo systemctl restart nginx
あとがき
adiary.httpd.plがオススメらしいのは見たが、HTTPサーバーは脆弱になりやすいことや、ログの使い勝手がどうなのか確認するのが面倒なこと、IPv6対応させるのが面倒だったことがあり採用しなかった。
2026/04/10(金)便利なコマンド置き場
特定パス配下のサブフォルダのファイル数を計数して、ソートして列挙する
/path/to/hoge
/path/to/fuga
/path/to/piyo
とあるとして
/path/to/hoge 32
/path/to/piyo 22
/path/to/fuga 4
みたいなのが欲しい場合の方法。フォルダはカウントされないので注意。
サブフォルダの中を再帰的に計測する
for d in /path/to/*/; do printf "%s\t%d\n" "$d" "$(find "$d" -type f | wc -l)"; done | sort -t$'\t' -k2 -nr
サブフォルダの直下のみ計測する
for d in /path/to/*/; do printf "%s\t%d\n" "$d" "$(find "$d" -maxdepth 1 -type f | wc -l)"; done | sort -t$'\t' -k2 -nr
2026/03/31(火)ARM版ノートPCはUbuntuの夢を見ないが、Windowsには光明あり
投稿日:
以前、ARMとCore UltraのWindowsノートPCを軽く見て、バッテリー持ちと熱周りを比較してみたところ、かなりいい結果になったのでUbuntuを入れたいが、これができるのかどうかという話。結論から言うと無理らしい。
ラズパイで動くんだから動くんじゃね?と思っていたのだが、そんな都合のいい話はなかった。
x86やAMD64であれば、今時のLinuxは基本的に動作する。これは、これらのアーキテクチャがACPIをサポートしているからだ。ACPIと聞くと自作PC的には電源管理のイメージがあるが、Advanced Configuration and Power Interfaceの略であり、電源だけでなくハードウェアの攻勢も管理できる仕組みになっている。そしてARMにはこれが存在せず、標準化されたハードウェア管理方法がないというわけだ。
ではARMベースのPCではどうしているかというと、今のところOS側でハードウェア情報を持っておき、これを基に起動しているようだ。この情報のことはデバイスツリーと呼ばれているらしい。
例えば、gihyo.jpのそのSnapdragon X搭載PCでUbuntuを使えますか?によると、ARM版UbuntuではSMBIOSというものから端末情報を読み取って、あらかじめUbuntu側で決め打ちしているデバイスツリーを利用してOSを起動するといった手法がとられているようだった。ラズパイの場合も専用イメージがあるため、おそらくデバイスツリーが組み込まれているのだろう。
恐らくこれはARMの場合、基本的にSoCにすべて集約されているため、x86やAMD64のようにメモリやGPUなどが独立したCPUのように管理できないところがあり、ACPIで解決できず、デバイスツリーが必要という事態になっているのだと思われる。
ではWindowsはどうしているかというと、先ほどの記事にもあったがPlatform Extension Plug-ins(PEPs)を使っているとのことだ。これはLinuxでは今のところサポートされておらず、やっているプロジェクトもないようだ。
ならデバイスツリーを書けばよいとも思えてくるが、これは非常に忍耐が必要な作業で、ACPIコードやWindowsドライバのリバースエンジニアリングを行い、カーネルパッケージをビルドし、各ハードウェアが正しく動くかどうかを根気強くテスト、デバッグする作業になるようで、おいそれと気軽にできるものではないらしい。
つまり、ARM版ノートPCでUbuntuを動かすのは、公式にサポートされているもの以外は難しいだろうし、それさえきっちり動く保証はないだろうから今のところ難しそうだ。つまり、ARM版ノートPCを買う場合、重量やキーボードにこだわりのある私の場合、Windowsで運用する必要がありそうだ。何故なら日本のノートPCがサポートされる気がしないからである。
バッテリー持ちがいいのは魅力だが、Windowsにするとなるとメモリやストレージを多めにしないと厳しいので端末価格が上がってしまうのがネックだ。ただソフトウェアの互換性はよくなってきているらしく、出先で使うサブマシンとしてみた場合の実用性は十分にありそうにも思える。
マイクロソフトの案内でも、デバイスドライバやアンチウィルス、アンチチートソフトウェア以外の一般的なアプリケーションは動作するとあるため、この辺りは安心してもよいだろう。一部のソフトウェアの互換性についてはLinaroのWoA対応アプリ検索で確認することができ、例えばXnViewは動作することがわかる。XnVew MPは出てこないが、恐らく動くのではなかろうか?国産のソフトの動作状況はPC Watchの記事でも一部を確認できる。なおエミュレーションで単に動くだけなのか、パフォーマンスがいいかどうかは不明だ。

