SVX日記
2024-04-15(Mon) 遅ればせながら「DKIM」してみる
「つぶやき」がメジャー化するにつれブログが衰退してきているように、各種チャットツールがメジャー化するにつれメールが衰退してきているように感じる。なんつうか「軽いノリ」が好まれるという傾向か。とはいえ、個人的には「つぶやき」では済まない記事を書くのが好きなので、このようにブログを書き続けているわけなのだが。
個人的にメールの歴史はGmailの「前」か「後」に分かれると思う。無料にもかかわらずあまりに高機能なので個人的にメールサーバを運用する理由が消失してしまった。メーラの仕様には多少なりとも不満があるので個人的に皮を被せているが、Gmailの素晴らしさに疑う余地はない。
そんな今日このごろだが「ドメインを勝手に使ってメールを送られないためのDNS設定」みたいな記事をふと目にして、ボチボチ「DKIM」とか何とかいうやつを勉強しておくべきかな、という気になった。つうのも、ちょっと前に神奈川県の高校入試関連システムが受験生にメールを出したが届かないトラブルが起きた、というニュースが心に引っかかっていたからだ。
その文脈では、半ば「DKIM」が常識のように語られていたのだが、自分はほぼ聞いたことがなかった。OSサポートという仕事からは、ジャストではないものの、守備範囲としてはだいぶ近い。とはいえ、先のOAuth2.0の対応とか、Googleは攻めすぎている印象もある。
まずは概念。なんか「3つくらいある」とボヤっと認識していたメール関連のセキュリティ機構だが、それは「SPF」「DKIM」「DMARC」の3つであった。「SPF」は簡単なので既に知っていたし「DMARC」は送る立場であれば必要性は薄く、重要なのは「DKIM」だけなのであった。
「SPF」は、メール受信サーバがSMTP接続される都度、メールの「From」の送信元アドレスのドメインにDNS経由で照会し、SMTP接続してきた相手のIPが妥当か確認するものだ。DNSの汎用(TXT)レコードを流用する形の実装で、実に頭のいい実装である。
「DKIM」は、それを先へ進めたもので、メール1通毎に「電子署名(ドメインの秘密鍵で署名)」を付けて送信し、メール受信サーバがメールを受信する都度、メールの「From」の送信元アドレスのドメインにDNS経由で「公開鍵」を要求し、メールの「電子署名」が妥当か確認するものらしい。こちらも、DNSの汎用(TXT)レコードを流用する形の実装になっている。
「メール」「電子署名」というと、既に「S/MIME」があるじゃねーか、と思わなくもないが「S/MIME」が「個人の署名」「メーラへの実装」であるのに対して「DKIM」は「ドメインの署名」「メールサーバへの実装」という違いがある。誰もが「個人の公開鍵」を持ち、それを広く周知できる仕組みがあれば、それを使ってメールも認証すればいいわけなので「S/MIME」でもいいのだが、一般に「個人の公開鍵」を持つには金がかかる、という「セキュリティビジネス的な側面」の影響でそれができてないことに起因して、「DKIM」が生まれたという見方もできるだろう。
別の見方をすると「組織に取り組ませる」は「誰もが取り組む必要がある」よりも格段に容易という見解も得られる。前者の例が法人税。後者の例がマイナンバ。前者と後者の中間の例がインボイス。「誰もが取り組む必要がある」ものは、仮にそれが正当でも実現は容易ではないということだ。
やや脱線したが「DKIM」に戻る。送信時に「電子署名」の付与が必要になるが、それは「メールサーバへの実装」により行われる。具体的にメールサーバとはPostfixなのだが、そんな機能を持っているはずもない。調べていくと案の定だ。Milter機構を使って送信直前に署名処理を挟むらしい。Milter機構はsendmailの頃からあるが、個人的には気持ちのよくない実装である。
それはそれとして、Milterの先に何を置くかというと、それは「opendkim」らしい。コマンドではなくデーモンだ。ちょっと大げさなような気がするが、処理するメールの数が多い場合は、都度プロセスを上げるより効率がいいのかしらん。
不思議なのは「opendkim」がRHELには含まれないところ。場外の売場であるEPELでの扱いらしい。なんでも、何かRHEL要件に合わないということで、永久追放みたいな説明がある。永久追放て……ケンカでもしたのかしらん。デファクトスタンダードなGmailには必須技術なのに、デファクトスタンダードなRHELが対応しないのは矛盾だなぁ。サポートする立場としては責任放棄できる反面、扱わない説明に苦慮させられる予感がする。
# telnet localhost 25
mail from: xxxxxxxxxx@itline.jp
rcpt to: xxxxxxxxxx@gmail.com
xxx xx xx:xx:xx xxxxxx postfix/smtp[xxxxxxx]: xxxxxxxxxx:
to=<xxxxxxxxxx@gmail.com>, relay=gmail-smtp-in.l.google.com[74.125.23.27]:25, delay=21, delays=20/0.03/0.81/0.81, dsn=5.7.26,
status=bounced (host gmail-smtp-in.l.google.com[74.125.23.27] said:
550-5.7.26 This mail 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 [itline.jp] 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 xxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxx - gsmtp (in reply to end of DATA command))
# cat /var/named/external.itline.jp
@ IN TXT "v=spf1 +ip4:xxx.xxx.xxx.xxx -all"
# dnf install opendkim opendkim-tools
# opendkim-genkey -D /etc/opendkim/keys -d itline.jp
# ls /etc/opendkim/keys
default.private default.txt
# chown opendkim:opendkim /etc/opendkim/keys/*
# vi opendkim.conf
# diff opendkim.conf.org opendkim.conf
< Mode v
> Mode s
< Umask 002
> Umask 000
< # KeyTable /etc/opendkim/KeyTable
> KeyTable /etc/opendkim/KeyTable
< # SigningTable refile:/etc/opendkim/SigningTable
> SigningTable refile:/etc/opendkim/SigningTable
# vi KeyTable
# diff KeyTable.org KeyTable
> default._domainkey.itline.jp itline.jp:default:/etc/opendkim/keys/default.private
# vi SigningTable
# diff SigningTable.org SigningTable
> *@itline.jp default._domainkey.itline.jp
# systemctl start opendkim
# systemctl status opendkim
# netstat -anp | grep dkim
unix 2 [ ACC ] STREAM LISTENING 181622904 3273917/opendkim /run/opendkim/opendkim.sock
# ls -l /run/opendkim/opendkim.sock
srwxrwxrwx 1 opendkim opendkim 0 4月 13 20:54 /run/opendkim/opendkim.sock=
# chown opendkim:mail /run/opendkim
# ls -l /run | grep dkim
drwxr-x--- 2 opendkim mail 60 4月 13 20:54 opendkim/
# diff main.cf.org main.cf
> smtpd_milters = unix:/run/opendkim/opendkim.sock
> non_smtpd_milters = $smtpd_milters
> milter_default_action = accept
# cat /etc/opendkim/keys/default.txt >> external.itline.jp
# systemctl reload named-chroot
ほぼ書いてあるどおりの内容だが、権限関係で少し引っかかった。「Umask 000」と「chown opendkim:mail /run/opendkim」だ。これをしないとソケットの権限の都合で、Postfixがopendkimへの接続に失敗し、署名が付かない。opendkimのパッケージングバグという印象も感じるが……EPEL扱いと関連があるのかしらん。なんにせよ設定を済ませたら、改めて自分のGmailアカウントに対してメールを送らせる。
# telnet localhost 25
mail from: xxxxxxxxxx@itline.jp
rcpt to: xxxxxxxxxx@gmail.com
# tail /var/log/maillog
xxx xx xx:xx:xx xxxxxx opendkim[xxxxxxx]: xxxxxxxxxx: DKIM-Signature field added (s=default, d=itline.jp)
ちゃんとログに「署名を足したゼ」という報告が出ている。よっしゃよっしゃ……と、Gmailを開くが、メールがちっとも来ない……と、思ったら「迷惑メール」じゃなくて「受信トレイ」に届いていた。そらそうか。
SPF: PASS(IP: xxx.xxx.xxx.xxx)。詳細
DKIM: 'PASS'(ドメイン: itline.jp)詳細
ちなみに上で必要性は薄いと書いた「DMARC」だが、「DKIM」と同列に語られるものの、別にセキュリティレベルが向上するわけでもなんでもなく、DKIM関連の情報をやりとりするというだけの機構だ。送る立場であればDNSの設定に以下を加えるくらい。
_dmarc IN TXT "v=DMARC1; p=quarantine; rua=mailto:xxxxxxxxxx@gmail.com"
SPF: PASS(IP: xxx.xxx.xxx.xxx)。詳細
DKIM: 'PASS'(ドメイン: itline.jp)詳細
DMARC: 'PASS' 詳細