shintaro.info infra

送信ドメイン認証 (SPF / DKIM / DMARC)

作成: Shintaro / Acia
対象OS: Debian 13 (Sakura VPS)
対象ドメイン: <DOMAIN>(例: example.com
メールホスト: <MAIL_HOST>(例: mail.<DOMAIN>mail.example.com


目的

送信ドメイン認証(SPF / DKIM / DMARC)を適切に設定し、

  • なりすましメールの抑止
  • スパム判定・迷惑メール行きリスクの低減
  • 自ドメインメールサービスの信頼性向上

を実現する。

このドキュメントは Debian 13 + Postfix 3.9 + Dovecot + OpenDKIM を前提とし、
将来の 完全再構築 においてもそのまま再現できることを目的とする。


前提

  • 既に以下が構成済みであること
  • 06_apache.md による HTTPS + certbot 導入
  • 07_postfix.md による Postfix 基本構成(Maildir / SMTP Submission / SMTPS)
  • 08_dovecot.md による Dovecot(IMAPS / POP3S / SASL連携)
  • TLS 証明書は Apache / Postfix / Dovecot で共用

/etc/letsencrypt/live/<DOMAIN>/fullchain.pem
/etc/letsencrypt/live/<DOMAIN>/privkey.pem

  • DNS ゾーンは さくらインターネットの「ドメインコントロールパネル」を利用
  • SPF レコードは次の形をベースとする:
  @  IN TXT "v=spf1 ip4:<SERVER_IPV4> -all"

SPF(Sender Policy Framework)

方針

  • 送信元を 自前VPS のみ に限定する。
  • 将来、外部サービス(例: Gmail, メール配信サービス)を使う場合は include: を追加する。

推奨レコード

@ 3600 IN TXT "v=spf1 ip4:<SERVER_IPV4> -all"
  • <SERVER_IPV4>: VPS のグローバル IPv4 アドレス

代替レコード例(MX ベース運用)

将来、MX で指しているサーバ郡をまとめて許可したい場合は以下の形式も利用可能。

@ 3600 IN TXT "v=spf1 mx -all"

外部サービス追加例

Gmail など外部MTAを併用する場合は include: を追加する。

@ 3600 IN TXT "v=spf1 ip4:<SERVER_IPV4> include:_spf.google.com -all"

SPF は 255 文字制限・DNS クエリ回数制限があるため、 include: を増やしすぎないように注意する。


DKIM(OpenDKIM)

役割と構成

  • 秘密鍵でメールに署名し、DNS上の公開鍵で受信側が検証できるようにする仕組み。
  • 本構成では、Postfix → OpenDKIM へ milter 経由で接続し、送信時に自動で DKIM 署名される。
flowchart LR
  Postfix -- Milter --> OpenDKIM
  OpenDKIM -- 検証用公開鍵 --> DNS

パッケージインストール

sudo apt update
sudo apt install -y opendkim opendkim-tools

ディレクトリ構成(例)

/etc/opendkim/
  ├── keys/
  │   └── <DOMAIN>/
  │        ├── mail.private   … 秘密鍵
  │        └── mail.txt       … 公開鍵(DNS登録用)
  ├── KeyTable
  ├── SigningTable
  └── TrustedHosts

鍵生成(selector=mail)

sudo mkdir -p /etc/opendkim/keys/<DOMAIN>
cd /etc/opendkim/keys/<DOMAIN>

sudo opendkim-genkey -b 2048 -r -s mail -d <DOMAIN>
sudo chown opendkim:opendkim mail.private
sudo chmod 600 mail.private

OpenDKIM 本体設定 /etc/opendkim.conf

以下の設定をベースに調整:

Syslog                  yes
Umask                   002

KeyTable                /etc/opendkim/KeyTable
SigningTable            refile:/etc/opendkim/SigningTable
InternalHosts           /etc/opendkim/TrustedHosts
ExternalIgnoreList      /etc/opendkim/TrustedHosts

Canonicalization        relaxed/simple
Mode                    sv
OversignHeaders         From

Socket                  inet:8891@localhost
UserID                  opendkim:opendkim
PidFile                 /run/opendkim/opendkim.pid

# 任意(デバッグ用)
LogWhy                  yes
AutoRestart             Yes
AutoRestartRate         10/1h

PidFile を明示しないと、systemd 起動がタイムアウトする事例あり。必ず指定する。 Socket は TCP:8891 で Postfix から接続される。 LogWhy は署名失敗時の原因分析用ログ出力(初期導入時に有効推奨)

テーブル定義

/etc/opendkim/KeyTable

mail._domainkey.<DOMAIN> <DOMAIN>:mail:/etc/opendkim/keys/<DOMAIN>/mail.private

/etc/opendkim/SigningTable

*@<DOMAIN> mail._domainkey.<DOMAIN>

/etc/opendkim/TrustedHosts

127.0.0.1
::1
localhost

ローカルホスト以外を信頼ホストに含めないことで、署名漏れを防止する。

Postfix との連携(main.cf)

/etc/postfix/main.cf に以下を追記:

milter_default_action = accept
milter_protocol = 2
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891

milter_macro_daemon_name = ORIGINATING は main.cf には 書かずmaster.cf 側の submission/smtps の -o で個別指定する。

サービス起動と連携確認

sudo systemctl restart opendkim
sudo systemctl reload postfix

sudo systemctl status opendkim
sudo journalctl -u opendkim -n 50

legacy: /etc/default/opendkim の扱い

Debian 13 以降の systemd 構成ではこのファイルは使用されない。
ファイルが存在していても、現在の systemctl に影響を与えることはない。

/etc/default/opendkim は legacy 互換用として残されており、 Socket 等の指定は /etc/opendkim.conf に集約されている。

DNS 登録(公開鍵)

/etc/opendkim/keys/<DOMAIN>/mail.txt に以下のような形式で出力される:

mail._domainkey IN TXT (
  "v=DKIM1; k=rsa; p=<公開鍵文字列>" )

さくらのドメインコントロールパネルでは:

  • エントリ名: mail._domainkey
  • タイプ: TXT
  • : "v=DKIM1; k=rsa; p=..."(改行・コメント除去し1行に整形)

TXT が255文字を超える場合、quoted-string を複数に分割して1レコードに収めること。 TXTレコードを2本に分けると opendkim-testkey が失敗する。

DMARC

役割

  • SPF / DKIM の認証結果に基づき、

受信側に対して「認証失敗メールをどう扱うか」のポリシーを通知する仕組み。

  • レポートメール(rua, ruf)を通じて、なりすましや設定不備を検知できる。

DMARC レコード基本形

DMARC レコードは _dmarc.<DOMAIN> の TXT として定義する。

現在の構成(p=quarantine)

実際の運用では、すでに SPF/DKIM/DMARC がすべて pass していることが確認できており、
ポリシーは以下のように quarantine モードに引き上げ済み:

_dmarc.<DOMAIN>. 3600 IN TXT "v=DMARC1; p=quarantine; rua=mailto:postmaster@<DOMAIN>; fo=1"
  • p=quarantine: 認証失敗メールを迷惑メールフォルダ等に隔離
  • rua: 集計レポート送信先
  • fo=1: SPFまたはDKIMのどちらかがfailした場合にもレポート収集

Gmailからのレポートで spf=pass dkim=pass dmarc=pass を確認済(2025-11-27)

1. 初期導入(p=none / 観測モード)

_dmarc.<DOMAIN>. 3600 IN TXT "v=DMARC1; p=none; rua=mailto:<POSTMASTER_ADDR>"
  • p=none: 認証失敗時も配送は許可し、レポートのみ収集
  • rua: 集計レポート送付先(一般に postmaster 相当のアドレス)

2. 段階的強化(quarantine)

運用に問題がないことを確認後、隔離ポリシーへ引き上げる。

_dmarc.<DOMAIN>. 3600 IN TXT "v=DMARC1; p=quarantine; rua=mailto:<POSTMASTER_ADDR>; ruf=mailto:<ADMIN_ADDR>; fo=1"
  • p=quarantine: 認証失敗メールを迷惑メールフォルダ等に隔離
  • ruf: フォレンジックレポート送付先
  • fo=1: SPF または DKIM が失敗した場合にレポートを要求

一部プロバイダ(例: Gmail)は ruf(フォレンジックレポート)をサポートしない。 必須ではないため、不要であれば ruf は省略してよい。

3. 最終段階(reject)

問題がなければ最終的に p=reject へ引き上げる。

_dmarc.<DOMAIN>. 3600 IN TXT "v=DMARC1; p=reject; rua=mailto:<POSTMASTER_ADDR>; fo=1"

検証手順

1. SPF の確認

  • dignslookup で TXT レコードを参照し、意図した値になっているか確認。

例:

dig TXT <DOMAIN> +short
dig TXT _dmarc.<DOMAIN> +short
dig TXT mail._domainkey.<DOMAIN> +short
  • SPF 評価ツール(外部サービス)でもチェックしておくと安心。

2. DKIM キー検証

OpenDKIM のテストコマンドで DNS と鍵の整合性を確認。

sudo opendkim-testkey -d <DOMAIN> -s mail -vvv
  • key OK と表示されれば DNS / 鍵ともに問題ない。
  • key not secure は DNSSEC 未使用の警告で、致命的ではない。

3. 実メール送信テスト(swaks)

swaks を利用して、実際に DKIM 署名・SPF 判定を確認する。

swaks の導入(TLS依存込み)

--tls を使うため Net::SSLeay / IO::Socket::SSL が必要。

sudo apt update
sudo apt install -y swaks libnet-ssleay-perl libio-socket-ssl-perl

テスト送信

**送信元(From / envelope-from)は <MAIN_USER>@<DOMAIN> に揃えること。**
ホスト名由来のサブドメイン(例: server.<DOMAIN>)で送ると Gmail により未認証扱いで拒否されたため、設計として必須条件にする。

read -s -p "SMTP password: " SMTPPASS; echo
swaks --to you@example.net \
      --from <MAIN_USER>@<DOMAIN> \
      --header "From: <MAIN_USER>@<DOMAIN>" \
      --server localhost \
      --auth-user <MAIN_USER> \
      --auth-pass "$SMTPPASS" \
      --auth PLAIN \
      --tls
unset SMTPPASS

受信側でメールヘッダを確認し、以下のような Authentication-Results を確認する。

  • spf=pass (sender IP is authorized)
  • dkim=pass (signature verified)
  • dmarc=pass (policy evaluation successful)

4. Gmail 受信ヘッダ検証(実機結果 / 2025-11-21)

実機検証結果:

  • 送信元を <MAIN_USER>@server.<DOMAIN> とした場合

Gmail 側で SPF/DKIM did not pass と判定され 550-5.7.26 unauthenticated sender でブロック。

  • 送信元を <MAIN_USER>@<DOMAIN> に揃えた場合

Gmail へ配送成功。Authentication-Results は以下の通り。

結果:

  • SPF: PASS
  • DKIM: PASS
  • DMARC: FAIL(DMARC レコード未設定のため) → 初回は迷惑メールフォルダに分類

ヘッダ抜粋(要旨):

spf=pass
dkim=pass
dmarc=fail (no DMARC policy found)

Postfix 配送ログ(journalctl)抜粋:

status=sent (250 2.0.0 OK - gsmtp)

DMARC を設定すると、以後の Gmail での迷惑判定率が大きく改善する。


運用ノート

  • DNS 伝播には数分〜数時間かかる場合があるため、

設定変更後すぐに結果が反映されないことがある。

  • DMARC は段階導入(none → quarantine → reject)とし、

レポートを見ながら少しずつポリシーを強める。

  • OpenDKIM / Postfix のログは以下で確認可能。
  sudo journalctl -u opendkim
  sudo journalctl -u postfix
  sudo tail -f /var/log/mail.log
  • certbot による証明書更新時(06_apache.md 参照)は、

Postfix / Dovecot が新証明書を読めているかを確認する。

  sudo systemctl reload postfix
  sudo systemctl reload dovecot
  • 外部メールサービスを追加した際は、必ず SPF / DKIM / DMARC への影響を確認する。

特に SPF の -all は厳格であるため、許可し忘れがあると正当なメールが拒否される。


変更履歴

日付編集者内容
2025-10-28Shintaro / Acia初版作成。SPF/DKIM/DMARC の最小構成と段階導入手順を記載。
2025-10-29Shintaro / Aciaユーザ名・メールアドレスを変数化し、教材用途での汎用性を強化。
2025-11-21Shintaro / AciaSPF(ip4 指定)を前提に再構成。KeyTable/SigningTable/TrustedHosts を明示し、Debian 13/Postfix 3.9 + OpenDKIM 連携手順を確定。Gmail での配送ブロック→PASS までの実機検証結果(Fromドメイン一致、TXT文字数制限の対処、PidFile明示)を追記。