🛡 RBL・ホワイトリスト現代化の歩み(全履歴集成)
dazoo.ne.jp 陣営の防衛規律は、2026年4月のサーバー移設を機に、MariaDB (dbispconfig) を核とした動的・リアルタイム執行システムへと進化した。本頁には、その苦難と進化の全歴史を記録する。
I. 防衛規律の変遷(2026年4月?5月)
当初の IP 遮断規律から、ドメイン救済(RHSWL)の確立に至るまでの記録。
1. Sieve 正典:知性の最前線 📜
# /etc/dovecot/sieve/global_before.sieve
require ["fileinto", "regex", "variables", "envelope", "copy"];
if envelope :is "to" ["spam_test@dazoo.ne.jp", "spam@dazoo.ne.jp"] {
keep;
stop;
}
redirect :copy "spam_test@dazoo.ne.jp";
if header :regex "X-Spamd-Result" "\[[[:space:]]*(-?[0-9.]+)" {
set "score" "${1}";
if string :regex "${score}" "^-" {
keep;
# stop; (D-Spec: Allow forwarding)
}
# 18.0点以上を隔離箱(spam@)へ
if string :regex "${score}" "^(1[8-9]|[2-9][0-9]|[0-9]{3,})" {
redirect "spam@dazoo.ne.jp";
stop;
}
}
keep;
2. 標本分類規律:spam_test@ の変遷
当初の不純物を含んだ V27.2.3 から、純粋な数値規律 V27.3.x への進化。
# /var/vmail/dazoo.ne.jp/spam_test/.ispconfig.sieve
# D-Spec Intelligence Sieve V27.3.2 - Mailbox Extension Added
require ["fileinto", "regex", "variables", "envelope", "comparator-i;ascii-numeric", "relational", "mailbox"];
# 規律:Rspamd スコア抽出(絶対精度版)
if header :regex "X-Spamd-Result" "\\\[[[:space:]]*(-?[0-9.]+)" {
set "score" "${1}";
# 1. マイナススコア(救済)
if string :regex "${score}" "^-" {
fileinto :create "minus4";
stop;
}
# 2. プラススコア(階層分類)
if string :value "ge" :comparator "i;ascii-numeric" "${score}" "35" { fileinto :create "35plus"; stop; }
elsif string :value "ge" :comparator "i;ascii-numeric" "${score}" "30" { fileinto :create "30"; stop; }
elsif string :value "ge" :comparator "i;ascii-numeric" "${score}" "25" { fileinto :create "25"; stop; }
elsif string :value "ge" :comparator "i;ascii-numeric" "${score}" "20" { fileinto :create "20"; stop; }
elsif string :value "ge" :comparator "i;ascii-numeric" "${score}" "18" { fileinto :create "18"; stop; }
elsif string :value "ge" :comparator "i;ascii-numeric" "${score}" "15" { fileinto :create "15"; stop; }
elsif string :value "ge" :comparator "i;ascii-numeric" "${score}" "10" { fileinto :create "10"; stop; }
elsif string :value "ge" :comparator "i;ascii-numeric" "${score}" "8" { fileinto :create "8"; stop; }
elsif string :value "ge" :comparator "i;ascii-numeric" "${score}" "6" { fileinto :create "6"; stop; }
}
# デフォルト:救済
fileinto :create "minus4";
stop;
II. 2026年8月:覚醒と物理修復
PHP 8.3 互換性バグの克服、および即時執行規律の完全開通。
1. 処刑執行の掟:spam@ Reject 聖域 ⚡
# /var/vmail/dazoo.ne.jp/spam/.ispconfig.sieve
# D-Spec Isolation Logic V31.1 - Source Compliance Edition
require ["fileinto", "comparator-i;ascii-numeric", "relational", "variables", "regex", "mailbox", "vnd.dovecot.pipe", "copy"];
if header :regex "X-Spamd-Result" "\[[[:space:]]*(-?[0-9.]+)" {
set "score" "${1}";
if string :value "ge" :comparator "i;ascii-numeric" "${score}" "30" {
pipe :copy "register_black_ip.sh";
fileinto :create "Reject";
stop;
}
}
keep;
III. 付録:執行プログラム ソースコード全集 📑
システムをゼロから再構築するための、s2 サーバー上の「生きたコード」を以下に集成する。
① RBL/White 統合マスター更新:lc_rbl_update_db.sh
#!/bin/bash
# D-Spec: Black RBL Physical Rebuilder (No Mining)
PATH=/bin:/sbin:/usr/bin:/usr/sbin
RBL_DB="/var/lib/rbldns/ip"
TMP="/tmp/ispc_rbl_update.tmp"
PASS=$(cat /usr/local/ispconfig/server/lib/mysql_clientdb.conf | grep password | cut -d"'" -f2)
MYSQL_CMD="mysql -u root -p$PASS dbispconfig -N -s -e"
# 物理反映
$MYSQL_CMD "SELECT concat('!', ip, ' # ', IFNULL(comment,'white')) FROM rbl_exceptions" > "$TMP"
$MYSQL_CMD "SELECT concat(ip, ' # last:', last_seen, ' cnt:', occurrence_count) FROM rbl_tracker WHERE last_seen >= DATE_SUB(NOW(), INTERVAL 30 DAY)" >> "$TMP"
mv "$TMP" "$RBL_DB"
chown rbldns:rbldns "$RBL_DB"
systemctl reload rbldnsd 2>/dev/null
echo "Black RBL Rebuilt: $(wc -l < $RBL_DB) entries."
② 厳選抽出 V1.2 (Deep Search):lc_process_selected_whitelist.sh
#!/bin/bash
# ==============================================================================
# D-Spec: Manual Specimen Whitelist Processor (V1.2 - Deep Search)
# 規律:ヘッダーの折り返し(Folding)を物理的に突破し、アドレスを確実に抽出する
# ==============================================================================
PASS=$(cat /usr/local/ispconfig/server/lib/mysql_clientdb.conf | grep password | cut -d"'" -f2)
SELECT_DIR="/var/vmail/dazoo.ne.jp/spam_test/Maildir/.minus4.select/cur"
PROCESSED_DIR="/var/vmail/dazoo.ne.jp/spam_test/Maildir/.minus4.select.processed/cur"
SYNC_SCRIPT="/root/lc_rbl_update_db.sh"
mkdir -p "$PROCESSED_DIR"
chown 5000:5000 "$PROCESSED_DIR"
shopt -s nullglob
FILES=("$SELECT_DIR"/*)
if [ ${#FILES[@]} -eq 0 ]; then
echo "通知:検体は存在しません。"
exit 0
fi
COUNT=0
for file in "${FILES[@]}"; do
# 規律:From または Return-Path の行と、その直後の行(折り返し)をセットでスキャン
# その中から最初に見つかったメールアドレスを抽出する
EMAIL=$(grep -iE "^(From|Return-Path):" -A 1 "$file" | grep -oE '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' | head -n 1)
DOMAIN=$(echo "$EMAIL" | cut -d'@' -f2 | tr -d '\r\n[:space:]' | tr '[:upper:]' '[:lower:]')
if [[ -n "$DOMAIN" && "$DOMAIN" =~ \. ]]; then
echo "登録執行: $DOMAIN"
mysql -u root -p$PASS dbispconfig -e "
INSERT INTO white_domain_tracker (domain, hostname, first_seen, last_seen, occurrence_count, comment)
VALUES ('$DOMAIN', 'Manual_Select', NOW(), NOW(), 1, 'Deep Extracted by V1.2')
ON DUPLICATE KEY UPDATE last_seen = NOW(), occurrence_count = occurrence_count + 1;"
mv "$file" "$PROCESSED_DIR/"
((COUNT++))
else
echo "警告:ドメイン抽出失敗(スキップ): $(basename "$file")"
fi
done
if [ $COUNT -gt 0 ]; then
echo "通知:$COUNT 件を登録完了。DNS同期を執行します..."
bash "$SYNC_SCRIPT"
chown -R 5000:5000 "/var/vmail/dazoo.ne.jp/spam_test/Maildir/.minus4.select.processed/"
echo "[Success] 執行完了。"
fi
③ 即時執行規律:register_black_ip.sh (V18.4)
中継(Google等)を透過し最深部を射抜く知性。
#!/bin/bash
# ==============================================================================
# L-Core: Auto-Execution Guard (V18.4 - Logic Hardening)
# 規律:空の共有リストによる誤判定の防止 & 権限完全同期。
# ==============================================================================
PATH=/bin:/sbin:/usr/bin:/usr/sbin
LOG_FILE="/var/log/honeypot_trapped.log"
CONF="/usr/local/ispconfig/server/lib/config.inc.php"
SHARED_LIST="/etc/postfix/shared_infra_domains.txt"
# DB接続情報の抽出
DB_USER=$(grep "['\"]db_user['\"]" "$CONF" | sed -n "s/.*=[[:space:]]*['\"]\(.*\)['\"];.*/\1/p" | head -n 1)
DB_PASS=$(grep "['\"]db_password['\"]" "$CONF" | sed -n "s/.*=[[:space:]]*['\"]\(.*\)['\"];.*/\1/p" | head -n 1)
T_MAIL=$(mktemp)
cat > "$T_MAIL"
# 【精密抽出】最深部の外部 IP を特定
DAZOO_INFRA="(^127\.0\.0\.1$|^10\.|^100\.|^172\.(1[6-9]|2[0-9]|3[01])\.|^192\.168\.|^210\.237\.157\.|^210\.237\.158\.|^213\.136\.91\.179|^163\.44\.191\.189|^210\.175\.7\.(6[4-9]|7[01]))"
ip=$(awk '/^$/ {exit} {if (/^[ \t]/) {line=line " " $0} else {if (line) print line; line=$0}} END {print line}' "$T_MAIL" | \
grep -i "^Received: from" | grep -oE "(\[|\()([0-9]{1,3}\.){3}[0-9]{1,3}(\]|\))" | \
tr -d '[]() ' | grep -vE "$DAZOO_INFRA" | tail -n 1)
if [ -n "$ip" ]; then
# ホスト名特定
host=$(awk '/^$/ {if (NR > 100) exit} {if (/^[ \t]/) {line=line " " $0} else {if (line) print line; line=$0}} END {print line}' "$T_MAIL" | \
grep -i "^Received:" | grep -F "$ip" | grep -oE "from [^ ]+" | awk '{print $2}' | tr -d '()[] ' | tail -n 1)
# 【三重の盾】SHARED_PATTERNが空でない場合のみ判定を執行する規律
if [ -s "$SHARED_LIST" ]; then
SHARED_PATTERN=$(awk '{printf (NR==1?"":"|") "(^|\\.)"$0"$"}' "$SHARED_LIST")
if [[ "$host" =~ $SHARED_PATTERN ]]; then
rm -f "$T_MAIL"
exit 0
fi
fi
# DB 刻印 & 同期執行
mysql -u "$DB_USER" -p"$DB_PASS" dbispconfig -e "DELETE FROM white_tracker WHERE ip = '$ip'; INSERT INTO rbl_tracker (ip, hostname, first_seen, last_seen, occurrence_count) VALUES ('$ip', '${host:-external}', NOW(), NOW(), 1) ON DUPLICATE KEY UPDATE last_seen = NOW(), occurrence_count = occurrence_count + 1;"
sudo /root/lc_rbl_update_db.sh > /dev/null 2>&1
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Trapped & Synced: $ip ($host)" >> "$LOG_FILE"
fi
rm -f "$T_MAIL"
④ 善玉ドメイン「厳選・抽出」:lc_white_domain_select.sh
# 以前の名称または別名で保存されています
⑤ 万能救出エンジン:lc_mail_rescue_universal.sh
#!/bin/bash
# ==============================================================================
# D-Spec: Universal Precision Rescue Engine (V2.3)
# 規律:dovecot* 等のメタファイルを除外し、本物のメールのみを救出します
# ==============================================================================
Q_ID=$1
SEARCH_ROOT="/var/vmail/dazoo.ne.jp/spam/Maildir"
if [ -z "$Q_ID" ]; then
echo "使用法: $0 <Queue-ID>"
exit 1
fi
echo "--- 🛡 D-Spec: Universal Rescue Start (V2.3) ---"
echo "捜索キー: $Q_ID"
# 1. dovecot* で始まるファイルを除外して、中身から ID を特定
# ! -name "dovecot*" を加えることで、メタファイルを完全に排除します
FILE_PATH=$(find "$SEARCH_ROOT" -type f ! -name "dovecot*" -exec grep -l "$Q_ID" {} + | head -n 1)
if [ -z "$FILE_PATH" ]; then
echo "❌ エラー:本物のメールファイルが見当たりません。"
exit 1
fi
echo "発見:$FILE_PATH"
# 2. 本来の受取人を抽出
REAL_TO=$(grep -i "X-Sieve-Redirected-From:" "$FILE_PATH" | head -n 1 | cut -d: -f2 | xargs)
if [ -z "$REAL_TO" ]; then
# Delivered-To から救済(spam@dazoo.ne.jp 以外を抽出)
REAL_TO=$(grep -i "Delivered-To:" "$FILE_PATH" | grep -vE "spam@dazoo.ne.jp|spam_test@dazoo.ne.jp" | head -n 1 | cut -d: -f2 | xargs)
fi
if [ -n "$REAL_TO" ]; then
DOMAIN=$(echo "$REAL_TO" | cut -d'@' -f2)
USER=$(echo "$REAL_TO" | cut -d'@' -f1)
TARGET_DIR="/var/vmail/${DOMAIN}/${USER}/Maildir/new"
if [ -d "/var/vmail/${DOMAIN}/${USER}" ]; then
echo "配送先:$REAL_TO"
mkdir -p "$TARGET_DIR"
# 3. 物理移動の執行
mv "$FILE_PATH" "$TARGET_DIR/"
# 4. 権限規律と再同期
chown -R vmail:vmail "/var/vmail/${DOMAIN}/${USER}/Maildir"
doveadm force-resync -u "$REAL_TO" INBOX
echo "✅ 救出完遂:$REAL_TO 様へ配送しました。"
else
echo "❌ エラー:宛先ディレクトリが存在しません ($REAL_TO)"
exit 1
fi
else
echo "❌ エラー:宛先を特定できませんでした。"
exit 1
fi
⑥ Rspamd 学習規律:rspamd_learn_v15.sh
#!/bin/bash
# =================================================================
# Rspamd Learning & Verification Engine [Version 16.8]
# Policy: Learn -> Verify -> Secure Stratification (Filename Focus)
# 規律:探査の利便性を優先し、ID列にファイル名の先頭18文字を表示する。
# =================================================================
DOMAIN="dazoo.ne.jp"
USER="spam"
MAILDIR_BASE="/var/vmail/${DOMAIN}/${USER}/Maildir"
HAM_ALERT_LIMIT=15.0
SPAM_CAPTURE_LIMIT=18.0
DAZOO_INFRA="127.0.0.1|192.168.|10.|210.237.157.|213.136.91.179|163.44.191.189|210.175.7.(6[4-9]|7[01])"
declare -A TARGETS
TARGETS["ham_pass"]="${MAILDIR_BASE}/.ham_samples.processed/cur"
TARGETS["ham_fail"]="${MAILDIR_BASE}/.ham_samples.alert/cur"
TARGETS["spam_pass"]="${MAILDIR_BASE}/.spam_samples.captured/cur"
TARGETS["spam_fail"]="${MAILDIR_BASE}/.spam_samples.stubborn/cur"
for dir in "${TARGETS[@]}"; do mkdir -p "$dir"; done
echo "============================================================================"
echo " Rspamd 精度検証型・階層整理プロセッサ (V16.8)"
echo "============================================================================"
get_edge_ip() {
grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" "$1" | grep -vE "$DAZOO_INFRA" | grep -vE "\.0$|\.255$" | tail -n 1
}
get_live_score() {
rspamc --header="Flags: no_cache" ${2:+-i "$2"} -j < "$1" 2>/dev/null | grep -oP '"score":\s*-?[0-9.]+' | head -n 1 | awk -F: '{print $2}' | tr -d ' '
}
get_subject() {
local raw=$(grep -m 1 -i "^Subject: " "$1" | sed 's/[Ss]ubject: //;s/^[[:space:]]*//')
local dec=$(echo "$raw" | rspamadm decode 2>/dev/null | sed 's/Subject: //g')
echo "${dec:-$raw}" | tr -d '\r\n' | cut -c 1-38
}
process_training() {
local src_dir="$1"; local mode="$2"
[ -d "$src_dir/cur" ] || return 0
find "$src_dir" -type f -name "*:2,*" | grep "T" | xargs rm -f 2>/dev/null
local files=$(find "$src_dir/cur" "$src_dir/new" -type f 2>/dev/null)
[ -z "$files" ] && { echo "通知: ${mode^^} ソースは空です。"; return 0; }
echo "[執行]: ${mode^^} 学習開始"
printf "%-18s | %-38s | %5s | %5s | %s\n" "FILE-PREFIX" "SUBJECT" "ARR" "LIVE" "STAT"
echo "--------------------------------------------------------------------------------"
echo "$files" | while read -r file; do
[ -f "$file" ] || continue
local ip=$(get_edge_ip "$file")
local subj=$(get_subject "$file")
local file_prefix=$(basename "$file" | cut -c 1-18)
local score_arrival=$(grep -m 1 "X-Spamd-Result:" "$file" | grep -oP "\[\s*-?[0-9.]+" | tr -d '[ ' || echo "0.0")
# 学習
rspamc ${ip:+-i "$ip"} learn_${mode} "$file" > /dev/null 2>&1
[ "$mode" == "spam" ] && rspamc ${ip:+-i "$ip"} fuzzy_add "$file" > /dev/null 2>&1
# 検証
local score_live=$(get_live_score "$file" "$ip")
[ -z "$score_live" ] && score_live="0.0"
local target_key=""
local status=""
if [ "$mode" == "ham" ]; then
if (( $(echo "$score_live >= $HAM_ALERT_LIMIT" | bc -l) )); then
target_key="ham_fail"; status="ALRT"
else
target_key="ham_pass"; status="PASS"
fi
else
if (( $(echo "$score_live >= $SPAM_CAPTURE_LIMIT" | bc -l) )); then
target_key="spam_pass"; status="CAP"
else
target_key="spam_fail"; status="STUB"
fi
fi
printf "%-18s | %-38s | %5.1f | %5.1f | %s\n" "$file_prefix" "$subj" "$score_arrival" "$score_live" "$status"
local dest_path="${TARGETS[$target_key]}"
[ -d "$dest_path" ] && mv -f "$file" "$dest_path/"
done
echo ""
}
process_training "${MAILDIR_BASE}/.ham_samples" "ham"
process_training "${MAILDIR_BASE}/.spam_samples" "spam"
chown -R vmail:vmail "$MAILDIR_BASE"
echo "--------------------------------------------------------------------------------"
echo "[RESULT] 現在の累計学習統計:"
rspamc stat | awk '/Statfile: BAYES/ {for(i=1;i<=NF;i++) if($i=="learned:") {gsub(/;/,"",$(i+1)); printf "%-12s %s\n", $2, $(i+1)}}'
echo "================================================================================"
⑦ 運用維持:ログローテーション規律
/var/log/honeypot_trapped.log {
weekly
missingok
rotate 12
compress
delaycompress
notifempty
copytruncate
create 0664 vmail vmail
}
歴史備考: 全ての記録は物理的な事実に基づき、Dazoo方式の正典としてここに完成を宣言する。2026-08-21 最終集成。