kmuto’s blog

はてな社でMackerel CREをやっています。料理と旅行といろんなIT技術

DebianのPostfixでのSPF / DKIM / DMARC設定 with Route 53

techbookfest.org

の感想文を書こうと読み進めていたのだが、読んでいるうちにいいかげんおうちサーバのkmuto.jpもなんとかしないとな、と重い腰を上げることにした。重要な連絡メールもたまに受けているのに、spam多すぎてSMTPへの呪詛が強まっていた今日この頃だったので、ちょうどいいきっかけである。

 

実際のところ、先人の

www.hs3.org

https://www.tyksnet.com/blog/archives/2593

だけでほぼ解決してしまうので、感謝してこれを読みましょう。で終わってしまうんだけど、ネットの情報は永遠に残るわけではないので、備忘録も兼ねてやり方はほぼコピー、内容は抜粋という雑なものではあるが記録しておく。

 

OSはDebian 11 bullseye, 現状のstableバージョン。DNSAWSのRoute 53に置いている。MTAはPostfixで、ここにSPFDKIM、DMARCを設定することにした。

 

SPF

送信設定は今の時代は必須なので、さすがにこれは以前から設定している。Route 53のドメインレコードとして、

  • レコード名: 空白.ドメイン
  • レコードタイプ: TXT
  • 値: v=spf1 +ip4:IPv4アドレス +ip6:IPv6アドレス ~all (kmuto.jpでは自身で出してるので普通に自身のアドレス。メールアプライアンス系使っている場合はincludeで別途取り込む指定方法がある。~allは「それ以外は詐称の疑いがある」を示す。-allとすれば「絶対に詐称」となるが、状況によってはまずいことに陥ることもあり、~のほうがよいらしい)

これで、SPFを見る受信サーバであれば、こちらのメールサーバから出したものではない、詐称kmuto.jpメールを蹴るようにしてくれる。

 

ということで、受信サーバの立場になってSPFを見るように設定してみる。

  1. postfix-policyd-spf-pythonパッケージをインストールする。
  2. README.Debianに従い、/etc/postfix/master.cfにpolicyd-spfの呼び出しを追加する。
    policyd-spf  unix  -       n       n       -       0       spawn    user=policyd-spf argv=/usr/bin/policyd-spf
    
  3. /etc/postfix/main.cfでpolicyd-spf_time_limitを追加、smtpd_recipient_restrictionsの既存行にcheck_policy_serviceを追加する。
    policyd-spf_time_limit = 3600
    smtpd_recipient_restrictions = (既存設定), check_policy_service unix:private/policyd-spf
  4. postfixをreload。
  5. うまくできていればSPFの通った受信メールに「Received-SPF: Pass (...」が付く。mail.logログを見ていると、詐称している場合はSPF failになってrejectされていた。SPF行がないときにはNoneでスルーされる。

 

DKIM

DKIMは実際どの程度効果があるのかわからないけど、これをレピュテーションにされているケースもあるようなので、設定しておくことにした。受信時に検証でひっかけられる数がどの程度あるのかは今後のウォッチ結果次第。

  1. opendkim、opendkim-toolsパッケージをインストールする。
  2. adduser postfix opendkim でグループに追加しておく。
  3. /etc/opendkim.confを編集する。Mode svはsign/verifyの両方の意味。SubDomainsはサブドメインを使うかどうかで、自分のところでは不要なのでnoとしている。ぽんこつ雑記で書かれていたCanonicalizationについてはDebianだとデフォルトでrelaxed/simple(ヘッダは緩く・ボディはきっちり)になっていた。
    Mode sv    
    SubDomains no
    Socket local:/var/spool/postfix/opendkim/opendkim.sock
    KeyTable refile:/etc/opendkim/key.table
    SigningTable refile:/etc/opendkim/signing.table
    ExternalIgnoreList  refile:/etc/opendkim/trusted.hosts
    InternalHosts       refile:/etc/opendkim/trusted.hosts
  4. 鍵用のフォルダを用意する。ついでにPostfix用の準備も。
    sudo mkdir -p /etc/opendkim/keys/ドメイン
    sudo chown -R opendkim:opendkim /etc/opendkim
    sudo chmod 700 /etc/opendkim/keys
    sudo mkdir /var/spool/postfix/opendkim
    sudo chown opendkim:postfix /var/spool/postfix/opendkim
    
  5. /etc/default/opendkimでPostfixを使うようにしておく。
    RUNDIR=/var/spool/postfix/run/opendkim
    
  6. /etc/opendkim/signing.tableを作成。
    *@ドメイン default._domainkey.ドメイン
    
  7. /etc/opendkim/key.tableを作成。
    default._domainkey.ドメイン ドメイン:default:/etc/opendkim/keys/ドメイン/default.private
    
  8. /etc/opendkim/trusted.hostsを作成。とりあえず自身のみにしておいた。
    127.0.0.1
    ::1
    localhost
    
  9. 鍵ペアを作成する。
    sudo opendkim-genkey -b 2048 -d ドメイン -D /etc/opendkim/keys/ドメイン -s default -v
    sudo chown opendkim:opendkim /etc/opendkim/keys/ドメイン/default.txt /etc/opendkim/keys/ドメイン/default.private
    

/etc/opendkim/keys/ドメイン/default.txtに公開鍵テキストが作成されるので、これをDNS、ここではRoute 53に登録するのだが、結果が1つの行にならなかったり長すぎと怒られたりで少々ハマった。結局、「"v=DKIM…"(空白)"p=鍵…"(空白)"鍵続き…"」と二重引用符で囲んだ文字列を空白で分けた1行としてテキストエリアに書き込めば、Route 53で結合してくれた。改行文字を入れるとダメ。

DNSをテスト。key not secureはDNSSEC関連なので無視してよい。key OKなのでよさそう。

sudo opendkim-testkey -d ドメイン -s default -vvv
opendkim-testkey: using default configfile /etc/opendkim.conf
opendkim-testkey: checking key 'default._domainkey.ドメイン'
opendkim-testkey: key not secure
opendkim-testkey: key OK

あとはPostfix側で通過するメールにDKIMフィルタを通すように設定する。/etc/postfix/main.cfを編集。

milter_default_action = accept
milter_protocol = 6
smtpd_milters = local:opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters

これで、あとはpostfixとopendkimをreload。メールを送出すると、ヘッダにDKIM-Signatureが付き、GmailなどだとDKIMチェックをPassしたことも示される。

 

DMARC

最後はDMARC。

  1. Route 53に_dmarcホストTXTレコードを作成。拒否ポリシー(p=)はひとまずnone。様子を見てquarantineかrejectにすることになる。rua(サマリレポート)、ruf(失敗レポート)のメールアドレスはDNS上で公開なので、これがspam先になりそうな気もする…。
    v=DMARC1; p=none; pct=100; fo=1; rua=mailto:メールアドレス; ruf=mailto:メールアドレス"
    
  2. opendmarcパッケージをインストール。
  3. 「opendmarc-check ドメイン」でDNSレコードが正しく設定されていることを確認。
  4. /etc/opendkim.confを編集。SPFチェックはすでに設定しているので、ここでは設定しないことにした(postfix-policyd-spf-pythonのほうが設定柔軟らしい)。RejectFailuresはDNSでp=rejectとしているときにこのエントリをtrueにすると本当にDMARCエラーメールをrejectするという設定。まだおっかないので慎重めにfalseにしておいた。
    AuthservID OpenDMARC
    RejectFailures false
    Socket local:/var/spool/postfix/opendmarc/opendmarc.sock
    TrustedAuthservIDs ドメイン
    UserID opendmarc
    IgnoreHosts /etc/opendmarc/ignore.hosts
    IgnoreAuthenticatedClients true
    RequiredHeaders true
    
  5. sudo mdir /etc/opendmarcでフォルダを作成し、/etc/opendmarc/ignore.hostsにホワイトリストを記述する。許可される記法は狭め。manによるとホスト名、IPアドレス、CIDRで、ワイルドカード的なドメイン表現みたいなのはできないようだ。
    localhost
    ::1
    127.0.0.0/8
    (その他パスさせたい範囲)
    
  6. Postfix用の諸々の準備をする。
    sudo mkdir /var/spool/postfix/opendmarc
    sudo chown -R opendmarc:opendmarc /var/spool/postfix/opendmarc
    sudo chmod 750 /var/spool/postfix/opendmarc
    sudo adduser postfix opendmarc
    
  7. Postfixの設定。/etc/postfix/main.cfのDKIMのsmtpd_miltersに追加するだけ。
    smtpd_milters = local:opendkim/opendkim.sock,local:opendmarc/opendmarc.sock
    
  8. postfixとopendkimをreload。

 

メールを送出すると、受信側でDMARCチェックをして、dmarc=passのようなヘッダが付く。逆にこちらが受信したときのDMARCチェック結果は、Authentication-Results: OpenDMARC; dmarc=passのようなヘッダで示される。

 

DMARCはprocmail等で外部転送しているとまずいことになる。これまで一部の重要そうなメールはGmailに転送していたんだけど、いろいろ探しても結論としては「don't forward to Gmail」らしい。まぁDMARCの仕組みからしてもよろしくないことは理解できる。Gmail転送はいったんあきらめて、ほかの保存や通知方法をそのうち考えることにした。

 

一晩経ってざっと見た感じで、SPF→まぁまぁ蹴ってるけど正規のspam(ってなんだ)もわりと通ってくる。DKIM→よくわからん。DMARC→効いている気がする(えきねっとAmazon楽天、VISAあたりの詐称reject。今はスルーだけど、結局spamassassinで殺されていた)。

debian.orgはSPFDKIMもDMARCも設定してないっぽい。開発者から離れたのでいろいろ抜けているとは言え、ユーザーとしていくつかはウォッチしているので、debian.orgのメールをヘッダ不備で蹴るのはちと困る。

 

DMARCのquarantine設定は様子を見てそのうち変更することにしよう。MLあたりからのメールの扱いでfailしている気がするので、いきなりrejectは危なそう。