Rspamd GPT Plugin をさくらのAI Engine で動かしてみた

Rspamd 3.9以降で標準的な外部AI連携機能(GPT Plugin)が利用可能になりました。
1ヶ月程度使用してみたので久しぶりに何か書いてみます。

環境

サーバ: さくらのVPS
OS: Rocky Linux 9.5
Rspamd: 3.13.0
LLM: さくらのAI Engine

メールサーバは個人ドメインのもので月2500通程度メールが届く
さくらのAIエンジンは月3000リクエストまでは無料のプランがある

設定

/etc/rspamd/local.d/gpt.conf
— ここから —
enabled = true;
type = “openai”;
url = “https://api.ai.sakura.ad.jp/v1/chat/completions”;
api_key = “さくらのAI Engine の アカウントトークン”;
model = “gpt-oss-120b” ;
autolearn = true;
timeout = 10s;
allow_ham = true;
reason_header = “X-GPT-Reason”;
— ここまで —

判定例

X-GPT-Reason に判定理由
X-Spamd-Result に GPT_PHISHING や GPT_SPAM、GPT_HAMなどが追加される。

結果

集計対象メール数: 2,485 通
GPTの判定平均時間: 1.66 秒 (最長で3秒程度)


さくらのAI Engine の利用グラフ

1. 【HAM(正常)】判定に多い理由

英文の理由 (Actual Reason) 日本語訳
Legitimate technical mailing list discussion with no suspicious links or requests, indicating ham. 不審なリンクや要求を含まない、正当な技術系メーリングリストの議論であるため、HAM(正常)と判断。
The email is a legitimate automated TLS report from example.com with no suspicious content. example.com からの正当な自動生成TLSレポートであり、不審なコンテンツは含まれていない。
Legitimate job recruitment notification from a known domain (example.com) with matching sender info. 既知のドメイン(example.com)からの正当な求人通知であり、送信者情報も一致している。
Likely ham: a legitimate technical mailing list reply with matching domain and benign URLs. 正常な可能性が高い:ドメインが一致しており、無害なURLを含む正当な技術メーリングリストへの返信である。

2. 【GPT_PHISHING(フィッシング)】判定に多い理由

英文の理由 (Actual Reason) 日本語訳
The email pretends to be from Amazon but originates from an unrelated domain (example.com) and uses suspicious links. Amazonを装っているが、無関係なドメイン(example.com)から送信されており、不審なリンクを使用している。
Suspicious request for account update or login using an external URL (example.com/path) that is not the official domain. 公式ドメインではない外部URL(example.com/path)を使用して、アカウント更新やログインを促す不審な要求。
Uses urgency and suspicious links (example.com) to solicit personal information, typical of phishing scams. フィッシング詐欺に典型的な、緊急性の強調と不審なリンク(example.com)を用いた個人情報の搾取。
Matches credential harvesting patterns by directing to a non-official login page for site administration. サイト管理用の非公式なログインページへ誘導しており、認証情報搾取(クレデンシャル・ハーベスティング)のパターンに一致する。

3. 【GPT_SPAM(スパム)】判定に多い理由

英文の理由 (Actual Reason) 日本語訳
High spam probability due to excessive promotional language, marketing keywords, and unsolicited recruitment. 過度な宣伝文句、マーケティングキーワード、および未承諾の求人勧誘が含まれるため、スパムの可能性が高い。
Primarily promotional content with multiple marketing links and emphasis on discounts or offers. 主にプロモーション内容であり、多数のマーケティング用リンクや、割引・特典の強調が見られる。
Frequent use of marketing phrases and a clear intent to drive traffic to a commercial service. マーケティング用語が頻繁に使用されており、商業サービスへ誘導しようとする明確な意図がある。

GPT_PHISHING がついたもので迷惑メールと判定されたくないものが数通あった。

注意

テストしていたrspamdバージョン(3.13.0)と最新のバージョンでプロンプトが大幅に改善されていることに気づいたので、バージョンアップ(3.14.2)をしてしばらくまた様子をみたい。

rspamd 3.13.0

  settings.prompt = "Analyze this email strictly as a spam detector given the email message, subject, " ..
      "FROM and url domains. Evaluate spam probability (0-1). " ..
      "Output ONLY 3 lines:\n" ..
      "1. Numeric score (0.00-1.00)\n" ..
      "2. One-sentence reason citing whether it is spam, the strongest red flag, or why it is ham\n" ..
      "3. Empty line or mention ONLY the primary concern category if found from the list: " ..
        table.concat(lua_util.keys(categories_map), ', ')

rspamd 3.14.2

  settings.prompt = "Analyze this email as a spam detector. Evaluate spam probability (0-1).\n\n" ..
      "LEGITIMATE patterns to recognize:\n" ..
      "- Verification emails with time-limited codes are NORMAL and legitimate\n" ..
      "- Transactional emails (receipts, confirmations, password resets) from services\n" ..
      "- 'Verify email' or 'confirmation code' is NOT automatically phishing\n" ..
      "- Emails from frequent/known senders (see context) are more trustworthy\n\n" ..
      "Flag as SPAM/PHISHING only with MULTIPLE red flags:\n" ..
      "- Urgent threats or fear tactics (account closure, legal action)\n" ..
      "- Domain impersonation or suspicious lookalikes\n" ..
      "- Requests for passwords, SSN, credit card numbers\n" ..
      "- Mismatched URLs pointing to different domains than sender\n" ..
      "- Poor grammar/spelling in supposedly professional emails\n\n" ..
      "IMPORTANT: If sender is 'frequent' or 'known', reduce phishing probability " ..
      "unless there are strong contradictory signals.\n\n" ..
      "Output ONLY 3 lines:\n" ..
      "1. Numeric score (0.00-1.00)\n" ..
      "2. One-sentence reason citing the strongest indicator\n" ..
      "3. Primary category if applicable: " ..
      table.concat(lua_util.keys(categories_map), ', ')

07. メールサーバ構築(3) – rspamd [さくらのVPS/CentOS7]

・rspamd の用途

  1. メール送信/転送時に DKIM/ARC署名
  2. メール受信時に SPF/DKIM/DMARC/ARC認証
  3. メール受信時に Virus/Spam Check (採点結果をヘッダーに追加)

・rspamd のインストール

#-- rspamd.com の repository を登録
curl https://rspamd.com/rpm-stable/centos-7/rspamd.repo > /etc/yum.repos.d/rspamd.repo
rpm --import https://rspamd.com/rpm-stable/gpg.key

#-- rspamd と redis のインストール
yum install -y rspamd redis

mkdir /etc/rspamd/local.d/keys/

#-- rspamd の設定
cat <<'_EOL_'> /etc/rspamd/local.d/options.inc
filters = "chartable,dkim,spf,surbl,regexp,fuzzy_check";
check_all_filters = true;
_EOL_

cat <<'_EOL_'> /etc/rspamd/local.d/milter_headers.conf
use = ["x-spamd-result","x-rspamd-server","x-rspamd-queue-id","authentication-results","x-spam-level","x-virus"];
#use = ["authentication-results"];
authenticated_headers = ["authentication-results"];
_EOL_

cat <<'_EOL_'> /etc/rspamd/local.d/redis.conf
servers = "127.0.0.1";
_EOL_

cat <<'_EOL_'> /etc/rspamd/local.d/actions.conf
reject = null;
add_header = 2.0 ;
greylist = null;
_EOL_

cat <<'_EOL_'> /etc/rspamd/local.d/greylist.conf
enabled = false
_EOL_

cat <<'_EOL_'> /etc/rspamd/local.d/phishing.conf
openphish_enabled = true;
phishtank_enabled = true;
_EOL_

cat <<_EOL_> /etc/rspamd/local.d/antivirus.conf
clamav {
  action  = "reject";
  type    = "clamav";
  servers = "/var/run/clamd.scan/clamd.sock";
  symbol = "CLAM_VIRUS";
  patterns {
    #symbol_name = "pattern";
    JUST_EICAR = "^Eicar-Test-Signature$";
  }
}
_EOL_

#-- clamd.sock にアクセスできるように group に追加
usermod -aG clamscan _rspamd
usermod -aG virusgroup _rspamd

cat <<'_EOL_'> /etc/rspamd/local.d/url_reputation.conf
enabled = true;

# Key prefix for redis - default "Ur."
key_prefix = "Ur.";

# Symbols to insert - defaults as shown
symbols {
  white = "URL_REPUTATION_WHITE";
  black = "URL_REPUTATION_BLACK";
  grey = "URL_REPUTATION_GREY";
  neutral = "URL_REPUTATION_NEUTRAL";
}

# DKIM/DMARC/SPF allow symbols - defaults as shown
foreign_symbols {
  dmarc = "DMARC_POLICY_ALLOW";
  dkim = "R_DKIM_ALLOW";
  spf = "R_SPF_ALLOW";
}

# SURBL metatags to ignore - default as shown
ignore_surbl = ["URIBL_BLOCKED", "DBL_PROHIBIT", "SURBL_BLOCKED"];

# Amount of samples required for scoring - default 5
threshold = 5;

#Maximum number of TLDs to update reputation on (default 1)
update_limit = 1;

# Maximum number of TLDs to query reputation on (default 100)
query_limit = 100;

# If true, try to find most 'relevant' URL (default true)
relevance = true;
_EOL_

#-- redisに書き込むデータ行数などの設定 10000以上が推奨
cat <<_EOL_> /etc/rspamd/local.d/history_redis.conf
servers         = 127.0.0.1:6379;
key_prefix      = "rs_history";
nrows           = 10000;
compress        = true;
subject_privacy = false;
_EOL_

#-- 拡張子の spam スコアを設定
cat <<_EOL_> /etc/rspamd/local.d/mime_types.conf
bad_extensions = {
    ace = 4,
    arj = 4,
    bat = 2,
    cab = 3,
    com = 2,
    exe = 1,
    jar = 2,
    lnk = 4,
    scr = 4,
};
bad_archive_extensions = {
    pptx = 0.1,
    docx = 0.1,
    xlsx = 0.1,
    pdf  = 0.1,
    jar  = 3,
    js   = 0.5,
    vbs  = 4,
};
archive_extensions = {
    zip = 1,
    arj = 1,
    rar = 1,
    ace = 1,
    7z  = 1,
    cab = 1,
};
_EOL_

#-- ホワイトリストの設定
cat <<'_EOL_'>/etc/rspamd/local.d/multimap.conf
WHITELIST_SENDER_DOMAIN {
  type = "from";
  map = "/etc/rspamd/local.d/whitelist_sender_domain.map";
  filter = "email:domain";
  score = -10.0
}
WHITELIST_IP {
  type = "ip";
  map = "/etc/rspamd/local.d/whitelist_ip.map";
  score = -10.0
}
_EOL_

#-- 変数に必要な値を代入
DOMAIN=masdon.life
IPV4=$(ip addr show eth0 | awk '/inet /{print $2}' | sed 's#/.*##')
IPV6=$(ip addr show eth0 | awk '/inet6 /&&/global/{print $2}' | sed 's#/.*##')

cat <<_EOL_>/etc/rspamd/local.d/whitelist_sender_domain.map
$DOMAIN
_EOL_

#-- これを設定しないとUser Unknown 時の MAILER DAEMONがSPAM扱いになってしまう。
cat <<_EOL_>/etc/rspamd/local.d/whitelist_ip.map
${IPV4}
${IPV6}
_EOL_

・DKIMで使用する秘密鍵と共通鍵の作成

#-- 変数に必要な値を代入
DOMAIN=masdon.life

#-- 鍵の生成
rspamadm dkim_keygen -d ${DOMAIN} -s default -b 2048
-----BEGIN PRIVATE KEY-----
....
....
....
....
-----END PRIVATE KEY-----
default._domainkey IN TXT ( "v=DKIM1; k=rsa; "
  "p=...."
  "...."
) ;

#-- 秘密鍵の登録 ----BEGIN〜 行から ----END〜 行までをファイルに記述する
vi /etc/rspamd/local.d/keys/default.${DOMAIN}.key
chmod 600 /etc/rspamd/local.d/keys/default.${DOMAIN}.key
chown _rspamd. /etc/rspamd/local.d/keys/default.${DOMAIN}.key

#-- 共通鍵の登録 default._domainkey〜 から ) ; 行までをゾーンファイルに記述し、シリアルを更新し nsd を再起動する
vi /etc/nsd/zone/${DOMAIN}.zone
systemctl restart nsd

#-- dkimの署名の設定
cat <<'_EOL_'> /etc/rspamd/local.d/dkim_signing.conf
# メーリングリストや転送の対応
allow_hdrfrom_mismatch = true;
sign_local = true;

# subdomain の sign 対応
use_esld = false;
try_fallback = false;

# sign 対象のヘッダー
sign_headers = '(o)from:(o)sender:(o)reply-to:(o)subject:(o)date:(o)message-id:(o)to:(o)cc:(o)mime-version:(o)content-type:(o)content-transfer-encoding:resent-to:resent-cc:resent-from:resent-sender:resent-message-id:(o)in-reply-to:(o)references:list-id:list-owner:list-unsubscribe:list-subscribe:list-post';

domain {
  masdon.life {
    # Private key path
    path = "/etc/rspamd/local.d/keys/$selector.$domain.key";
    # Selector
    selector = "default";
  }
}
_EOL_

#-- arc署名の設定
cat <<'_EOL_'> /etc/rspamd/local.d/arc.conf
# メーリングリストや転送の対応
allow_hdrfrom_mismatch = true;
sign_local = true;
use_domain = "envelope";

# subdomain の sign 対応
use_esld = false;
try_fallback = false;

sign_headers = "(o)from:(o)sender:(o)reply-to:(o)subject:(o)date:(o)message-id:(o)to:(o)cc:(o)mime-version:(o)content-type:(o)content-transfer-encoding:resent-to:resent-cc:resent-from:resent-sender:resent-message-id:(o)in-reply-to:(o)references:list-id:list-owner:list-unsubscribe:list-subscribe:list-post:dkim-signature";

domain {
  masdon.life {
    # Private key path
    path = "/etc/rspamd/local.d/keys/$selector.$domain.key";
    # Selector
    selector = "default";
  }
}
_EOL_

・Web interface のパスワード設定

#-- Web interface のパスワードを生成
PASSWORD=$(rspamadm pw -p ********)

cat <<_EOL_> /etc/rspamd/local.d/worker-controller.inc
password        = "${PASSWORD}";
enable_password = "${PASSWORD}";
_EOL_

・rspamd, redis 起動

systemctl enable rspamd redis
systemctl start rspamd redis

・Web interface用の nginx の設定ファイルを用意

mkdir -p /etc/nginx/conf.d/https.d
cat <<'_EOL_' > /etc/nginx/conf.d/https.d/rspamd.conf
  location ^~ /rspamd {
    location /rspamd/ {
      proxy_pass       http://localhost:11334/;
      proxy_set_header Host      $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
  }
_EOL_