サーバにSPFを設定しよう

メール受信時に送信者側が「SPF」を設定しているかどうかである程度の迷惑メールが判定できます。
他の「DKIM」や「DMARC」と組み合わせればさらに効果は上がりますが、ここでは SPF だけの設定を記載します。

そもそのSPFとは

「SPF」とは「Sender Policy Framework」の略称で、目的は「送信元の保証」と解釈しています。

メールにはいくつかの「From」があります。送信元を示す「エンベロープFrom」。メール送信者を示す「ヘッダFrom」です。SPFは送信元を示す「エンベロープFrom」をチェックします。
「あなたのドメインからはどのサーバ名(IPアドレス)でメールを送信しますか?」と問い合わせるのに発信元(エンベロープFrom)情報を使います。

もし、指定以外のサーバからメールが発信されていればそのメールは正しいサーバから発信されていないという事になります。
なので、SPFはネームサーバとメールサーバともに設定していて初めてその効果があります。(ネームサーバの設定は「33:Gmailサーバにメールを送信する」を参照して設定してください)

メールを受信したときは以下の流れでチェックしていきます。

  1. PostFixで接続・受信を行う。
  2. 情報を抽出します。
  3. DNSを使いSPFレコードの問合せ

この判定をもって通過・破棄の対応になります。

では、設定をしていきましょう。
参考URL: Almalinux9 postfixでspf認証 https://www.unix-power.net/networking/postfix-spf
OS: Alma Linux 9.6
ソフト: Postfix 3.5.25
        pypolicyd-SPF 2.9.3

# dnf install pypolicyd-spf
メタデータの期限切れの最終確認: 00:00:00 前の 2025年06月04日 12時34分56秒 に実施しました。
依存関係が解決しました。
=========================================================================================================================
 パッケージ                        アーキテクチャー         バージョン                      リポジトリー           サイズ
=========================================================================================================================
インストール:
 pypolicyd-spf                     noarch                   2.9.3-4.el9                     epel                    62 k
依存関係のインストール:
 python3-authres                   noarch                   1.2.0-6.el9                     epel                    42 k
 python3-py3dns                    noarch                   3.2.1-7.el9                     epel                    48 k
 python3-pyspf                     noarch                   2.0.14-13.el9                   epel                    51 k

トランザクションの概要
=========================================================================================================================
インストール  4 パッケージ

ダウンロードサイズの合計: 203 k
インストール後のサイズ: 591 k
これでよろしいですか? [y/N]: y
パッケージのダウンロード:
(1/4): python3-authres-1.2.0-6.el9.noarch.rpm                                            782 kB/s |  42 kB     00:00
(2/4): pypolicyd-spf-2.9.3-4.el9.noarch.rpm                                              1.0 MB/s |  62 kB     00:00
(3/4): python3-py3dns-3.2.1-7.el9.noarch.rpm                                             783 kB/s |  48 kB     00:00
(4/4): python3-pyspf-2.0.14-13.el9.noarch.rpm                                            1.8 MB/s |  51 kB     00:00
-------------------------------------------------------------------------------------------------------------------------
合計                                                                                     310 kB/s | 203 kB     00:00
トランザクションを確認しています
トランザクションの確認に成功しました。
トランザクションをテストしています
トランザクションのテストに成功しました。
トランザクションを実行しています
  準備中           :                                                                                                 1/1
  インストール中   : python3-py3dns-3.2.1-7.el9.noarch                                                               1/4
  インストール中   : python3-pyspf-2.0.14-13.el9.noarch                                                              2/4
  インストール中   : python3-authres-1.2.0-6.el9.noarch                                                              3/4
  scriptletの実行中: pypolicyd-spf-2.9.3-4.el9.noarch                                                                4/4
  インストール中   : pypolicyd-spf-2.9.3-4.el9.noarch                                                                4/4
  scriptletの実行中: pypolicyd-spf-2.9.3-4.el9.noarch                                                                4/4
  検証中           : pypolicyd-spf-2.9.3-4.el9.noarch                                                                1/4
  検証中           : python3-authres-1.2.0-6.el9.noarch                                                              2/4
  検証中           : python3-py3dns-3.2.1-7.el9.noarch                                                               3/4
  検証中           : python3-pyspf-2.0.14-13.el9.noarch                                                              4/4

インストール済み:
  pypolicyd-spf-2.9.3-4.el9.noarch        python3-authres-1.2.0-6.el9.noarch      python3-py3dns-3.2.1-7.el9.noarch
  python3-pyspf-2.0.14-13.el9.noarch

完了しました!

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

// conf ファイルを注釈(英語)月に変更する
# mv /etc/python-policyd-spf/policyd-spf.conf /etc/python-policyd-spf/policyd-spf.conf.org
# cp /usr/share/doc/pypolicyd-spf/policyd-spf.conf.commented /etc/python-policyd-spf/policyd-spf.conf

// policyd-spf.conf を編集する
# vi /etc/python-policyd-spf/policyd-spf.conf

#  HELO check rejection policy. Options are:
#  HELO_reject = SPF_Not_Pass (default) - Reject if result not Pass/None/Tempfail.
#  HELO_reject = Softfail - Reject if result Softfail and Fail
#  HELO_reject = Fail - Reject on HELO Fail
#  HELO_reject = Null - Only reject HELO Fail for Null sender (SPF Classic)
#  HELO_reject = False - Never reject/defer on HELO, append header only.
#  HELO_reject = No_Check - Never check HELO.
##HELO_reject = Fail
# とりあえず不問と知る
HELO_reject = False

#  Mail From rejection policy.  Options are:
#  Mail_From_reject = SPF_Not_Pass - Reject if result not Pass/None/Tempfail.
#  Mail_From_reject = Softfail - Reject if result Softfail and Fail
#  Mail_From_reject = Fail - Reject on Mail From Fail (default)
#  Mail_From_reject = False - Never reject/defer on Mail From, append header only
#  Mail_From_reject = No_Check - Never check Mail From/Return Path.
# デフォルトのまま。Fail判定はブロックする。
Mail_From_reject = Fail

#  Policy for rejecting due to SPF PermError.  Options are:
#  PermError_reject = True
#  PermError_reject = False
# 恒久的なエラー(構文ミスなど)があっても受信する
PermError_reject = False

#  Policy for deferring messages due to SPF TempError.  Options are:
# 一時的なエラーの場合、保留(再送を促す)する
  TempError_Defer = True
#  TempError_Defer = False
###TempError_Defer = False  Default


#  Do not check SPF for localhost addresses - add to skip addresses to
#  skip SPF for internal networks if desired. Defaults are standard IPv4 and
#  IPv6 localhost addresses.
##skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1
# 自サーバのIPアドレスとローカルIPアドレスをセットする
skip_addresses = 555.666.777.888/32,3000:4000:5000:6000:7000:8000:9000:0001,127.0.0.0/8,::ffff:127.0.0.0/104,::1

【説明】
HELO_reject
・メールセッションの開始時に、送信側のSMTPクライアントが自身のホスト名を名乗るコマンド(HELO または EHLO)を送信します。
・この名乗られたホスト名(helo=... に記載されるホスト名)のDNSレコード(通常はAレコードまたはAAAAレコード)をSPFレコードとして参照し、実際に接続してきたIPアドレスがそのホスト名で許可されているかをチェックします。
・これは、メールエンベロープの情報よりも先に検証されるため、より初期段階のチェックとなります。

Mail_From_reject
・MAIL FROM: コマンドで指定されるメールアドレス(いわゆる「エンベロープFrom」または「Return-Path」)のドメインに対してSPFチェックを行います。
・実際にメールを送信してきたIPアドレスが、このドメインのSPFレコードで許可されているかを検証します。
・迷惑メールやフィッシングメールの多くは、この Mail From ドメインを詐称して送信されるため、SPFチェックにおいて最も重要な検証ポイントの一つとされています。

skip_addresses 
・SPFを確認しないIPアドレス。
・ローカルネットやサーバ自身を設定する。


-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

// /etc/potfix/master.cf を編集する

master.cfを編集して、postfix から policyed-SPFを呼び出してもらう。

# vi /etc/postfix/master.cf 

一番最後の行に以下を追加

# SPF検証設定

policyd-spf   unix  -       n       n       -       0       spawn
 user=nobody argv=/usr/libexec/postfix/policyd-spf

// /etc/postfix/main.cf を編集する
# vi /etc/postfix/main.cf


smtpd_recipient_restrictions =
#    permit_mynetworks
    permit_sasl_authenticated
    reject_unauth_destination
    check_policy_service unix:private/policyd-spf  <-- 追加

policyd-spf_time_limit = 3600   <-- 追加

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
文法チェック
# postfix check
# systemctl restart postfix
# systemctl status postfix

Active: active (running) の表示があればOK

-*-*-*-*- おまけ -*-*-*-*-
SPFの判定がされれば、以下の様にログが残ります。

【例】
Aパターン
Jun  3 12:51:04 mz policyd-spf[453411]: prepend Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=***.***.***.***; helo=hogehoga; envelope-from=nanja@hogehoge; receiver=
Bパターン
Jun  3 23:35:58 mon policyd-spf[583082]: prepend Received-SPF: None (mailfrom) identity=mailfrom; client-ip=***.***.***.***; helo=hogehoga; envelope-from=kanja@hogehogu; receiver=
Cパターン
Jun 4 10:30:01 mz policyd-spf[666644]: 550 5.7.23 Message rejected due to: SPF fail - not authorized. Please see http://www.openspf.net/Why?s=mfrom;id=support@example.jp;ip=203.0.113.20;r=
Jun 4 10:30:01 mz postfix/smtpd[666639]: NOQUEUE: reject: RCPT from white.mail.com[203.0.113.10]: 550 5.7.23 : Recipient address rejected: Message rejected due to: SPF fail - not authorized. Please see http://www.openspf.net/Why?s=mfrom;id=support@example.jp;ip=203.0.113.20;r=; from= to= proto=ESMTP helo=
Jun 4 10:30:01 mz postfix/smtpd[666639]: disconnect from white.mail.com[203.0.113.10] ehlo=2 starttls=1 mail=1 rcpt=0/1 rset=1 quit=1 commands=6/7

Dパターン
Jun 4 10:35:01 mz policyd-spf[12348]: prepend Received-SPF: Softfail (mailfrom) identity=mailfrom; client-ip=203.0.113.20; helo=suspicious.host; envelope-from=marketing@example.org; receiver=
Eパターン
Jun 4 10:40:01 mz policyd-spf[12352]: prepend Received-SPF: Neutral (mailfrom) identity=mailfrom; client-ip=203.0.113.30; helo=neutral-sender.com; envelope-from=info@example.net; receiver=
Fパターン
Jun 4 10:45:01 mz policyd-spf[12356]: prepend Received-SPF: PermError (mailfrom) identity=mailfrom; client-ip=203.0.113.40; helo=error-prone.net; envelope-from=test@bad-spf.com; receiver=
Jun 4 10:45:01 mz postfix/smtpd[12355]: NOQUEUE: reject: RCPT from error-prone.net[203.0.113.40]: 550 5.7.1 : Sender address rejected: SPF PermError; from= to= proto=ESMTP helo=
Gパターン
Jun 4 10:50:01 mz policyd-spf[12358]: prepend Received-SPF: TempError (mailfrom) identity=mailfrom; client-ip=203.0.113.50; helo=transient-issue.com; envelope-from=support@example.com; receiver=
Jun 4 10:50:01 mz postfix/smtpd[12357]: BADC0FFEE00: client=transient-issue.com[203.0.113.50]
Jun 4 10:50:02 mz postfix/smtpd[12357]: NOQUEUE: defer: RCPT from transient-issue.com[203.0.113.50]: 450 4.3.2 : Sender address rejected: Temporary SPF error; from= to= proto=ESMTP helo=

ここの「Received-SPF:」に注目してください。
Aパターンは「Pass」。メールを受信します。
Bパターンは「None」。メールを受信します。
Cパターンは「Fail」。メールを受信しません。
Dパターンは「Softfail」。メールを受信します。ただし、許可しないが、厳密な拒否はしない(様子見)
Eパターンは「Neutral」。メールを受信します。許可も拒否もしません。(SPFレコードに ?all となっている事)
Fパターンは「PremError」。メールを受信しません。SPFレコードの文法エラーです。policy-spf.confの「PermError_reject=True」になっていれば受信拒否です。
Gパターンは「TempError」。メールを受信しません。一時的障害と判断します。policy-spf.confの「TempError_Defer=True」になっていれば再送を促します。

SPFの判定結果は以下のようになります。

    Pass: SPFレコードが存在し、送信元IPアドレスが許可されている。
    None: SPFレコードが存在しない。
    Fail: SPFレコードが存在し、送信元IPアドレスが許可されていない。
    Softfail: SPFレコードが存在し、送信元IPアドレスが許可されていないが、厳密な拒否は推奨されない。
    Neutral: SPFレコードが存在するが、明確な許可も拒否も示されていない。
    PermError: SPFレコードの構文に永続的なエラーがある。
    TempError: SPFレコードの検索中に一時的なDNSエラーが発生した。

判定結果から、
Aパターンは「問題なし」
Bパターンは「SPFレコードがDNSに存在しない」という事になります。
Cパターンは「メールを受信しない」
Dパターンは「疑わしいが受信はする」
Eパターンは「何もせず受信します」
Fパターンは「送信元のSPFレコードの書き間違いなので、判定できず受信しません」
Gパターンは「一時的通信エラーなので、今はメールを受信せず、一旦時間をおいて改めて再送してください」

SPFでは「送信元IPアドレスを認証」するだけなので、「怪しい」や「疑わしい」は設定によりスルーする事ができます。
ですので、SPFだけで迷惑メールを完全にブロックする事は不可能となります。
DKIMやDMARCと組み合わせて迷惑メール排除の精度を上げていく事が大切と考えます。

ちなみに
DKIM ・・・ メールの改ざんがされていないことを保証するため、電子署名を付与します。
DMARC・・・ SPFやDKIM認証結果を基に不正メールの判定後、拒否や隔離の処理指定ができます。

2025-06-04 初版
Let's PC の TOPに戻る
ホームページのTOPに戻る