私のmastodonは許可制にしているのに、承認待ちが機械的なSPAM登録で大量に埋まり承認/却下の手間が取られ、ここのところ問題になっていました。
今日は1日で100件以上のSPAM登録申請が押し寄せており、これはもう限界だと言うことで、自動却下の仕組みをもり混みました。
この記事では、ユーザー名が bp + 16桁hex の申請だけを、管理画面の「却下」と同じ経路で定期削除した手順をまとめます。
対象は Mastodon 4.7.x / Docker Compose です。承認済みアカウントは触りません。
来ていた申請の形
承認待ちに、次の型が短時間で大量に並びました。多い日は100件を超えます。
| 項目 | 中身 |
|---|---|
| ユーザー名 |
bp + 16桁の hex(例: bp6930842f68cfc730) |
| 参加理由 |
Automatedprotocoldeliverabilityprobeone の使い回し |
| 投稿 / フォロワー | どちらも 0 |
| メール |
docomo.ne.jp / yahoo.co.jp / gmail.com などがバラバラ |
| IP | 日本のコンシューマ回線が毎回違う |
ユーザー名は機械生成の指紋がはっきりしています。一方でメールとIPは正当な日本ユーザーと重なるので、ドメインブロックやIPルールは使えません。
同じ時期に、ユーザー名に規則がなく参加理由だけそれらしい申請も来ます。こちらは正規表現では切れないので、この記事の対象外です。
公式機能だけで足りない理由
Mastodon 4.5 以降の Username blocks は、単語の完全一致か部分一致です。^bp[0-9a-f]{16}$ のような正規表現は書けません。bp の部分一致にすると blueprint なども弾きます。
そのほか、次の手段もこの波には向きません。
- IPルール: 発信元が毎回違う
- メールドメインブロック: キャリアメールや大手フリーメールを巻き込む
-
tootctl accounts approve: 一括承認はあるが、一括却下はない - 許可制そのもの: ログインは正まるが、承認待ちと確認メールは残る
hCaptcha はメール確認時に出ます。未確認のままキューに溜まる分は止まりません。
方針
自動で切る条件を狭くします。
-
User.pending(未承認)だけを見る - ユーザー名が
\Abp[0-9a-f]{16}\zのときだけ却下する - 却下は管理画面と同じく、停止してから
AccountDeletionWorkerに渡す - 既存の申請は人手で消し、今後分だけ定期実行する
承認済みの正規ユーザーは、ユーザー名が偶然似ていても対象になりません。
却下する Ruby
コンテナ内で rails runner します。
# frozen_string_literal: true
PATTERN = /\Abp[0-9a-f]{16}\z/i
rejected = 0
Account.joins(:user).merge(User.pending).find_each do |account|
next unless account.username.match?(PATTERN)
account.suspend!(origin: :local)
AccountDeletionWorker.perform_async(account.id, 'reserve_username' => false, 'reserve_email' => false)
rejected += 1
puts "rejected #{account.username}"
end
puts "rejected_count=#{rejected}"
AccountDeletionWorker は、停止済み(unavailable?)でないと削除しません。先に suspend! してから enqueue します。ユーザー名とメールは予約せず、再利用できる状態に戻します。
先に件数だけ見るときは、suspend! の代わりに puts する dry-run にすれば足ります。
cron 用のラッパー
rails runner は起動に十数秒かかります。二重起動を避けるため flock を付けます。
#!/bin/bash
set -euo pipefail
LOCK=/tmp/reject-bp-hex-spam.lock
SCRIPT_DIR=/home/ubuntu/mastodon/scripts
COMPOSE_DIR=/home/ubuntu/mastodon/mastodon
exec 9>"$LOCK"
if ! flock -n 9; then
echo "$(date -Iseconds) skip: already running"
exit 0
fi
cd "$COMPOSE_DIR"
echo "$(date -Iseconds) start"
docker compose exec -T web bundle exec rails runner "$(cat "$SCRIPT_DIR/reject_bp_hex_spam.rb")"
echo "$(date -Iseconds) done"
ホスト側の Ruby ファイルを、引数としてコンテナの rails runner に渡しています。公式イメージの中身は書き換えません。
仕込み
初回は手動で一度走らせて、rejected_count とログを確認します。問題なければ5分ごとにします。
chmod 755 /home/ubuntu/mastodon/scripts/reject-bp-hex-spam.sh
crontab -l 2>/dev/null | grep -v reject-bp-hex-spam.sh; \
echo '*/5 * * * * /home/ubuntu/mastodon/scripts/reject-bp-hex-spam.sh >> /home/ubuntu/mastodon/logs/reject-bp-hex-spam.log 2>&1' \
| crontab -
ログは /home/ubuntu/mastodon/logs/reject-bp-hex-spam.log です。空振りのときは rejected_count=0 だけが残ります。
止めるとき:
crontab -l | grep -v reject-bp-hex-spam.sh | crontab -
運用上の境界
- ユーザー名のプレフィックスが変わったら、正規表現を直す
- 理由文の使い回しや、規則のない申請は見ない
- 承認待を「すべて却下」するボタンは使わない。正当な申請が混ざる
許可制のままキューだけ自動で空ける、という割り切りです。登録そのものを止めたい波が続くなら、一時的な登録クローズや招待制の方が確実です。
運用チェック
cron は問題なく動いています。 5分ごとに欠けなく起動し、失敗はありません。

