- 個人サーバーのGiteaとブログがスクレイピングトラフィックに圧迫され、ディスク・CPU・メモリの警告が相次いだため、運営者はログ分析・Nginx・Fail2Banを組み合わせて防御に乗り出した
- Zabbix基準でトラフィックは平常時より大きく跳ね上がり、Nginxの1か月平均は毎秒8リクエスト、最悪の区間では毎秒20超のリクエストまで上昇した
- 原因はRedditやHacker Newsのような単一流入の急増ではなく、複数のIPブロックが
www.lambdacreate.comとkrei.lambdacreate.comのURLをクロールする分散スクレイピングに近かった
- Nginxは既知の悪意あるuser agentに403を返し、IPごとのリクエスト制限を適用。403を過度に発生させるクライアントはFail2Banで24時間遮断した
- 執筆時点で遮断リストは合計735 bansまで増加しており、特にGiteaの公開リポジトリのすべてのコミットに対してtarballを生成しようとするリクエストがサーバー負荷を高めていた
個人サーバーを圧迫したスクレイピングトラフィック
- 個人のインターネット空間に望まないボットトラフィックが突然殺到し、実際の読者がアクセスすべきサービスにまで影響が及んだ
- Archive.orgのようにサイト保存を目的としてインデックスするサービスは許容対象だが、Amazon、Facebook、OpenAIやさまざまな無作為なボットは、コンテンツを自分たちの目的のために消費する存在として区別される
- 大企業による広範なインターネットデータ収集と、AIモデル学習用データ需要がスクレイピング圧力をさらに強めたと見ている
- このトラフィックはLambdacreateの実際の利用者ではなく、人間の読者のアクセス性を下げる妨害要因として扱われている
Zabbixが先に捉えた異常兆候
- 最初に問題を知らせたのはZabbixで、コンテナ用ディスクがいっぱいになったという警告が発生した
- LXDベース環境でZFS sparse fileを拡張し、サイトを一時的に停止して再起動したが、根本原因は残っていた
- その後、Giteaインスタンスが毎日ディスクを使い切り、1日20〜30GBのデータを生成するようになった
- 当初はGiteaがリポジトリアーカイブの整理をデフォルトで有効化していない問題だと見て、積極的なクリーンアップ作業を設定した
- ほどなくCPUとメモリの警告にまで発展し、Giteaで
git pull・git pushが難しくなり、weechatクライアントも接続を維持できなくなった
平常時比10倍に跳ね上がったリクエスト量
- 過去指標と現在の状態を比較するためにout-of-bandモニタリングを置いていたおかげで、異常パターンを確認できた
- Zabbixダッシュボードでの主要指標はNginxリクエスト数とネットワーク処理量のグラフだった
- 1か月基準の平均Nginxリクエスト量は毎秒8リクエスト程度である
- 最悪の時期には毎秒20超のリクエストが入り、大規模サービス基準では小さくても、個人サーバーには平常時の10倍に当たり影響が大きかった
- 単純なリクエスト量だけでなく、Gitea関連のディスク・CPU使用量の増加も同時に現れ、サーバー運用負担が大きくなった
lnavとgoaccessでログを追跡
- サーバーを十分長く稼働させてログを確認するため、コンテナとNginxを一時停止して分析を始めた
- 使用したツールはlnavとgoaccessだった
lnavはログファイルを色分けされたTUIで表示し、一般的なログフォーマットの上に抽象化レイヤーを提供して、SQLクエリでログを問い合わせられる
access.logでは次の質問を中心に確認した
- 全体の訪問者IPはいくつあるか
- IPアドレスにパターンがあるか
- 特定のreferrerから来るトラフィックか
- どのuser agentが使われているか
- どのIPがどのuser agentと結び付くか
- 分析の結果、単一referrerから押し寄せた“hug of death”ではなく、複数のIPブロックがサイト全体のURLをクロールしていた
user agentベースの403とリクエスト制限
- 最初の対応は、Nginxで問題を起こすuser agentをリスト化して403を返す方式だった
- 例示された設定では
map $http_user_agent $badagentを使い、AdsBot-Google、Amazonbot、Amazonbot/0.1のようなagentをマークしている
- 基本のNginx設定にはuser agentルールをincludeし、IPごとのrate limitも併せて設定した
- 仮想ホスト設定には次の動作が含まれる
limit_req zone=krei burst=20 nodelay;でリクエスト制限を適用
$badagentが真ならreturn 403;でコンテンツアクセスを遮断
- この方式はバックエンド処理を減らす助けになるが、サーバーはHTTPリクエストを受けて403を処理しなければならないため、大量同時リクエストでは依然として負担が残る
Fail2Banでファイアウォール遮断を自動化
- 403ログが蓄積した後は、
lnavで403を受けたユニークIPを確認できる
goaccessは過去・現在のログをまとめて分析し、サーバーに入ったリクエスト数と最も多く狙われたエンドポイントを確認するために使われた
- 実際のサーバー保護のためにFail2Banを追加した
- Fail2Banルールは、Nginx access logで403レスポンスを過度に発生させるIPを捕まえる単純な形である
- 遮断時間は86400秒、つまり24時間に設定された
- 執筆時点の
fail2ban-client status nginx-forbiddenの結果は次のとおり
- 現在の失敗数: 13
- 総失敗数: 57135
- 現在の遮断数: 38
- 総遮断数: 735
実際の標的はブログではなくGiteaのtarball生成
- 最終的に読者はブログとLambdacreateのほかのサービスに再びアクセスできるようになった
- ロボットトラフィックを止めなければならない状況では、ブログ記事を書くこと自体も難しい
- 問題のトラフィックはブログのスクレイピングではなく、Giteaインスタンスの公開リポジトリごとにすべてのコミットのtarball生成を狙っていた
- 長期的には遮断リストを拡張したり、Archive.orgのような正当なサービスに例外を設ける方法を検討する予定である
- 検索エンジンからコンテンツが消えてほしいわけではないが、インターネットのAI劣化のための燃料として放置したくもないという立場だ
まだコメントはありません。