コンテンツにスキップ

🛡 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 "--- &#x1F6E1; 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 "&#x274C; エラー:本物のメールファイルが見当たりません。"
    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 "&#x2705; 救出完遂:$REAL_TO 様へ配送しました。"
    else
        echo "&#x274C; エラー:宛先ディレクトリが存在しません ($REAL_TO)"
        exit 1
    fi
else
    echo "&#x274C; エラー:宛先を特定できませんでした。"
    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 最終集成。