LIVESENSE ENGINEER BLOG

リブセンスエンジニアの活動や注目していることを発信しています

【いまさらやるPostfix】GmailにPostfix+Rspamd(SPF/DKIM)を使ってメールを送る

はじめに

技術部インフラグループの鈴木です。最近Postfixをいじっているのですが、Gmailにメールを送信するのに苦労しました。今回はその経験をもとに、PostfixからGmailにメールを送信するための設定をまとめました。
メール送信ではSMTPというプロトコルが使われますが、そのシンプルさ故にパッチが次々と当てられている経緯があります。そのため一から構築するとあれこれとミドルウェアが増えることがあります。
なので、今回は最低限のミドルウェア構成でPostfixからGmailにメールを送信するための設定をまとめました。今回は以下のようにPostfixとRspamdを使ってGmailにメールを届けます。

graph LR
    A[Python] --> B[Postfix];
    B[Postfix] --> C[Rspamd];
    C[Rspamd] -- SPF/DKIM認証 --> D[Gmail];

メール技術の関連としてDNSや暗号化、認証といったものも関わってくるのですが、それらについては2024年にでたメール技術の教科書がおすすめです。
この記事では、あくまでもミドルウェアの設定を中心に記載します。

www.shoeisha.co.jp

Gmailの送信制限

2024年にGoogleはメール送信者のガイドラインにて、セキュリティを向上させるために以下のような制限を設けています。 全ての送信者に適用されるポリシーと、1日5000通以上送る送信者に対してのポリシーがありますが、以下は前者の抜粋です。

送信元ドメインに SPF または DKIM メール認証を設定します。

送信元のドメインまたは IP に、有効なフォワードおよびリバース DNS レコード(PTR レコードとも呼ばれます)があることを確認します。

メールの送信に TLS 接続を使用します。

Postmaster Tools で報告される迷惑メール率を 0.3% 未満に維持します。

Internet Message Format 標準(RFC 5322)に準拠する形式でメールを作成します。

Gmail の From: ヘッダーのなりすましはしないでください。Gmail では、DMARC の quarantine(検疫)適用ポリシーの使用が開始されます。Gmail の From: ヘッダーのなりすましをした場合、メール配信に影響する可能性があります。

メーリング リストや受信ゲートウェイなどの転送サービスを管理する場合は、送信メールに ARC ヘッダーを追加します。ARC ヘッダーによって、メールが転送されたことが示され、送信者が転送者と見なされます。メーリング リストの送信者は、メーリン> グ リストを指定する List-id: ヘッダーも送信メールに追加する必要があります。

2025年2月の現在ではSPFかDKIMが設定されていれば、Gmailは受け取ってくれます(高確率で迷惑メールボックスに入りますが…)。
試しに何も設定しないでGmailに送信するともちろん届きません。バウンスされます。
バウンスになった場合、postfixのログの中でstatus=bouncedのうしろにバウンスの理由が出力されます。出力例を以下に記載します。

host gmail-smtp-in.l.google.com[74.125.204.27] said: 550-5.7.26 Your email has been blocked because the sender is unauthenticated. 550-5.7.26 Gmail requires all senders to authenticate with either SPF or DKIM. 550-5.7.26 550-5.7.26 Authentication results: 550-5.7.26 DKIM = did not pass 550-5.7.26 SPF [yabaibuki.dev] with ip: [XXX.XXX.XXX.XXX] = did not pass 550-5.7.26 550-5.7.26 For instructions on setting up authentication, go to 550 5.7.26 https://support.google.com/mail/answer/81126#authentication d2e1a72fcca58-734a00418a1si963101b3a.220 - gsmtp (in reply to end of DATA command)

では、ちゃんと受け取ってくれるように設定していきましょう。

Postfixの構築

前提条件

AWS上のパブリックサブネットに作成したUbuntu 24.04のEC2インスタンスを使用します。メール送信に必要な情報は以下のとおりとします。

  • グローバルIPアドレス
    • XXX.XXX.XXX.XXX
  • ドメイン
    • yabaibuki.dev

インストール

$ sudo apt update -y
$ sudo apt install postfix -y

基本設定

そうすると、以下のような設定画面が出ます。No Configurationを選択してください。

昔と比べて親切になっているのかな

設定ファイルを以下のように編集します。

$ ls /etc/postfix/
dynamicmaps.cf  dynamicmaps.cf.d  main.cf.proto  makedefs.out  master.cf  master.cf.proto  post-install  postfix-files  postfix-files.d  postfix-script  sasl
$ sudo nano /etc/postfix/main.cf
$ sudo cat /etc/postfix/main.cf
compatibility_level = 3.8
command_directory = /usr/sbin
daemon_directory = /usr/lib/postfix/sbin
data_directory = /var/lib/postfix
unknown_local_recipient_reject_code = 550
debugger_command =
     PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
     ddd $daemon_directory/$process_name $process_id & sleep 5

myhostname = yabaibuki.dev
mydomain = yabaibuki.dev
myorigin = $myhostname
mydestination = $myhostname localhost.$mydomain localhost $mydomain
mynetworks = XXX.XXX.XXX.XXX/32,127.0.0.0/8
smtpd_banner = $myhostname ESMTP
home_mailbox = Maildir/
inet_protocols = ipv4
inet_interfaces = all

smtpd_client_restrictions = permit_mynetworks

Rspamdの構築

このミドルウェアはスパムメールに対してのフィルタリングソフトですが、OpenDKIMやOpenDMARCと同一の設定ができます。
この記事では解説しませんが、伝統的なアンチスパムのミドルウェアとしてはSpamAssassinがあります。ただRspamdのほうが比較的新しく開発も活発です。 ちなみにメール関連のミドルウェアはGitHubで管理されている方が稀で、古いものだとtar.gzを解凍してREADMEを読まないと公式ドキュメントがないということは普通にあるので、ありがたいですね。

インストール

RspamdはRedisを使うので併せてインストールします。

$ sudo apt install redis rspamd -y
$ redis-server --version
Redis server v=7.0.15 sha=00000000:0 malloc=jemalloc-5.3.0 bits=64 build=d81b8ff71cfb150e
$ rspamd -v
Rspamd daemon version 3.8.1

CPU architecture ARM64; features:
Hyperscan enabled: FALSE
Jemalloc enabled: TRUE
LuaJIT enabled: TRUE (LuaJIT version: LuaJIT 2.1.1703358377)
ASAN enabled: FALSE
BLAS enabled: FALSE
Fasttext enabled: FALSE

初期設定

RspamdがRedisを使えるようにします。/etc/rspamd/local.d/redis.confを編集します。Rspamdは基本的にlocal.dにファイルを追加することで設定を変更できます。
ちなみにrspamadm configwizardを使って対話式に設定を行うこともできますが、今回は手動で設定します。

$ sudo nano /etc/rspamd/local.d/redis.conf
$ cat /etc/rspamd/local.d/redis.conf
write_servers = "127.0.0.1:6379";
read_servers = "127.0.0.1:6379";

DNSの設定

ここではyabaibuki.devとします。適宜自分の環境に合わせて置換してください。

SPF

SPFは送信元のドメインが許可されているIPアドレスからメールを送信することを許可するための仕組みです。以下のようなTXTレコードを設定します。

v=spf1 ip4:XXX.XXX.XXX.XXX/32 ~all

Route53の場合は以下のように追加します。

$ aws route53 change-resource-record-sets \
    --hosted-zone-id 自分のホストゾーンID \
    --change-batch '{
        "Changes": [{
            "Action": "UPSERT",
            "ResourceRecordSet": {
                "Name": "yabaibuki.dev",
                "Type": "TXT",
                "TTL": 300, # TTLは適宜調整
                "ResourceRecords": [{
                    "Value": "\"v=spf1 ip4:XXX.XXX.XXX.XXX/32 ~all\""
                }]
            }
        }]
    }'

上記の設定はソフトフェイルです。ユースケースに合わせて変更してください。

DKIM

OpenDKIMを使って鍵ペアを作る方法もありますが、今回はrspamdadmを使って鍵ペアを作ります。 秘密鍵は-kオプションで指定した場所にファイルとして出力され、DNSに設定するための公開鍵のTXTレコードが標準出力に出力されます。

# -sはセレクタで、20250228など日付を指定して鍵を一定周期でローテするのがセキュアです
# 今回は最低限の設定なので、ドキュメントに則り、セレクタをdkimとしています
# https://rspamd.com/doc/modules/dkim_signing.html#principles-of-operation
$ sudo rspamadm dkim_keygen -s 'dkim' -d yabaibuki.dev -k /var/lib/rspamd/dkim/yabaibuki.dev.dkim.key
dkim._domainkey IN TXT ( "v=DKIM1; k=rsa;"
    "p=${ここに公開鍵が入る}" ) ;

$ sudo chown _rspamd:_rspamd /var/lib/rspamd/dkim/yabaibuki.dev.dkim.key

Route53の場合は以下のように追加します。

$ aws route53 change-resource-record-sets \
    --hosted-zone-id 自分のホストゾーンID \
    --change-batch '{
        "Changes": [{
            "Action": "UPSERT",
            "ResourceRecordSet": {
                "Name": "dkim._domainkey.yabaibuki.dev",
                "Type": "TXT",
                "TTL": 300, # TTLは適宜調整
                "ResourceRecords": [{
                    "Value": "\"v=DKIM1; k=rsa; p=${ここに公開鍵が入る}""
                }]
            }
        }]
    }'

注意するところはRspamdのdkimモジュールrelaxed/relaxedかつrsa-sha256で動作します。

また、セキュリティ上、RSAではなくEd25519で認証を行いたい場合は、対応しているMTAが少ないのか両方設定したほうがいいとドキュメントにあるので、認証形式も注意です。

DMARC

DMARCはSPFとDKIMの結果を統合して、ドメインの認証を行う仕組みです。設定したドメインに対しての認証結果のレポートが一定周期で送られてくるので、それを受け取るためのメールアドレスを設定します。

$ aws route53 change-resource-record-sets \
    --hosted-zone-id 自分のホストゾーンID \
    --change-batch '{
        "Changes": [{
            "Action": "UPSERT",
            "ResourceRecordSet": {
                "Name": "_dmarc.yabaibuki.dev",
                "Type": "TXT",
                "TTL": 300,
                "ResourceRecords": [{
                    "Value": "\"v=DMARC1; p=none; rua=mailto:dmarc_report@yabaibuki.dev\""
                }]
            }
        }]
    }'

レポートのパース、可視化については手前味噌ですが、以前書いたブログ記事を参照してください。

made.livesense.co.jp

PostfixとRspamdの連携

以下の設定を/etc/postfix/main.cfに追記します。

smtpd_milters = inet:localhost:11332
milter_default_action = accept
milter_protocol = 6

設定を読み込ませます。statusがActiveであればOKです。

$ sudo systemctl restart postfix rspamd && sudo systemctl status postfix rspamd

うまくいかない場合は/var/log/mail.logないし/var/log/rspamd/rspamd.logを確認してください。私はこんな凡ミスをしていました。

# ファイルパスを間違えた
2025-03-03 03:27:46 #66412(normal) <415aa3>; task; dkim_module_load_key_format: cannot load dkim key /var/lib/rspamd/dkim/yabaibuki.dev.dkim.key: cannot stat key file: '/var/lib/rspamd/dkim/yabaibuki.dev.dkim.key' No such file or directory
# パーミッションが間違っていた
2025-03-03 03:29:13 #66412(normal) <c16122>; task; dkim_module_load_key_format: cannot load dkim key /var/lib/rspamd/dkim/yabaibuki.dev.dkim.key: cannot map key file: '/var/lib/rspamd/dkim/yabaibuki.dev.dkim.key' Permission denied

メール送信テスト

構成の挙動を確認するために、Pythonでテスト用のコードを書きました。

コード例

Pythonはsmtplibという標準ライブラリがあるので、外部ライブラリが不要です。 特に解説することがないですが、以下がコード例です。sendemail.pyとして保存してください。

import smtplib
from email.mime.text import MIMEText
import sys

def send_email(sender, recipient):
    # テキストメールを作成
    msg = MIMEText('Test.')
    # 件名を設定
    msg['Subject'] = 'Email sent from Python'
    # 送信元メールアドレスを設定
    msg['From'] = sender
    # 宛先メールアドレスを設定
    msg['To'] = recipient

    try:
        # ローカルホストのSMTPサーバーに接続
        with smtplib.SMTP('localhost') as s:
            # メールを送信
            s.sendmail(sender, recipient, msg.as_string())
        # 送信成功メッセージを表示
        print("Email sent successfully!")
    except Exception as e:
        # エラーメッセージを表示
        print(f"Error sending email: {e}")

# コマンドライン引数から送信元と宛先を取得
sender = sys.argv[1]
recipient = sys.argv[2]

# メール送信関数を呼び出す
send_email(sender, recipient)

実行

特筆することはありませんが、上記のコードを任意の場所に配置して、以下のように実行してください。

$ python3 sendmail.py sender@yabaibuki.dev recipient@yabaibuki.dev
Email sent successfully!

テスト結果

以下の通り問題なくSPF/DKIMをpassしたメールが送ることができました。受信側のGmailで「メッセージのソースを表示」して以下のようになっていれば成功です。

おわりに

最低限の構成ということで、今回は触れませんでしたが、IMAPやPOP3を使いたい時はDovecotが必要ですし、メールルーティングをGUIで行いたい場合はPostfixAdminが必要です。セキュリティ周りですと今回のRspamdの他にClamAVなどもあります。この辺りは公式にPostfix Add-on Softwareというページがあるので、ここから用途別に探すといいでしょう。

また、これらの設定はGit管理したいですよね。なので、次はこれらの設定をAnsibleでgit管理しつつ、GitHub Actionでデプロイできるようにする方法を書いていきます。

おまけ

ncやtelnetでSMTPのトランザクションを体験すると、IPAのネットワークスペシャリスト試験の勉強になります。
問題としてはH28の午後1-3あたりですかね。たまにはこういう素のプロトコルと向き合うのもいいですね。

$ nc 127.0.0.1 25
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
220 yabaibuki.dev ESMT
HELO yabaibuki.dev
250 yabaibuki.dev
MAIL FROM:test@yabaibuki.dev
250 2.1.0 Ok
RCPT TO:test@yabaibuki.dev
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
Subject: Test Mail

This is a test email.

.
250 2.0.0 Ok: queued as XXXXXXXXX
QUIT
221 2.0.0 Bye