2022/12/27(火)メールサーバの中規模改修と基礎知識(5)~ OpenDKIM
2022/12/27 05:06
公式公開バージョンは、記事公開日時点では、2.10.3 です。一部のLinux ディストリビューションに 2.11.x が提供されているようですが、これは、RC版(Release Candidate:リリース候補の意)の位置づけの状態です。
OpenDKIM の概要
OpenDKIM は、電子メールメッセージを基に検証鍵を電子メールヘッダに埋め込み、DNSに登録した公開鍵を使って検証することで、電子メール発信元の正当性を確認し、電子メール改ざんがされていないことも確認する一助になります。具体的には、予め送信元DNS情報に下記2つの TXTレコードを公開する形で登録しておき、


電子メール送信の際は、下記のように「DKIM-Signature:」というヘッダ行にて、一定のルールで計算された暗号キーを追加する処理を行います。

受信の際は、このDNS情報を基にメールヘッダに添付されてきたヘッダ暗号キーの検証を行い、この暗号キーに問題が無ければ「メール改ざんが無かった」と見做す、という処理を行うのが OpenDKIM の役割です。
送信の際の「DKIM-Signature:」ヘッダ行追加・受信の際の「DKIM-Signature:」ヘッダ行検証処理は、postfix にて、milter インタフェースを介して行うことになります。
OpenDKIM のインストール準備(1)
この先は、root アカウントにて、一連の作業を行います。OpenDKIM は、公開鍵の管理にPython と Perlを使用しているため、この2つは必須です。
OpenDKIM をインストールする場合、FreeBSD13 では、下記のモジュールが事前に必要です。(但し、2022/12/24 現在)
これらの多くは、Ports や Package でインストールしても、管理上特段問題にはなりません。
・perl5-5.36.0 (Ports から カテゴリ:lang) ・python39-3.9.16 (Ports から カテゴリ:lang) ・lua54-5.4.4 (Ports から カテゴリ:lang) ・autoconf-2.71 (Ports から カテゴリ:devel) ・automake-1.16 (Ports から カテゴリ:devel) ・libltdl-2.4.7 (Ports から カテゴリ:devel) ・libtool-2.4.7 (Ports から カテゴリ:devel) ・gnutls-3.7.8_1 (Ports から カテゴリ:security) ・db5-5.3.28_9 (Ports から カテゴリ:databases) ※ これは BerkeleyDB 5.3.28 ですOpenDKIM 2.10.3 は、OpenSSL 1.0 系までは対応しているものの、OpenSSL 1.1 系には対応していません。
昨今の FreeBSD にて採用されている OpenSSL は1.1 系のため、代替選択として、gnutls のインストールが必要になります。OpenSSL 1.1系対応なら、素直に OpenSSL を使うことで事が足りるのですが。。
更に、インストールに先立ち、OpenDKIM のセキュリティポリシーに従うため、ユーザ opendkim を、vipw や useradd コマンドで追加します:
vipw コマンドの場合は、下記の1行を編集画面で追加します。
opendkim::2006:3000::0:0:OpenDKIM execute user:/var/empty:/usr/sbin/nologinvipw でユーザ追加した場合、/etc/group に下記の1行を例示のように変更しておきます:
mailuser:*:3000:opendkim更に vipw でユーザ追加した場合は、つまらないセキュリティホールを作らないために、下記コマンドも一応実行しておきます:
# passwd opendkim
OpenDKIM のインストール準備(2)
先ず、ソースコードの展開を行います。# cp opendkim-2.10.3.tar.gz /usr/local/src # cd /usr/local/src # tar xvzf opendkim-2.10.3.tar.gz # cd opendkim-2.10.3実は、実際にソースコードのコンパイルを実施したところ、FreeBSDにおいては、素のソースコードでは、GnuTLS 使用環境での構築も上手く出来ません。
具体的には、configure スクリプト、libopendkim/tests/Makefile.in、libopendkim/dkim-canon.c、libopendkim/dkim.c、miltertest/miltertest.c、opendkim/tests/Makefile.in、opendkim/opendkim-crypto.c、 opendkim/opendkim-lua.c の8本の変更が必要なようです。
これらは、patch コマンドを使用するか、手入力修正して、作業実施前にソースコードの修正を行う必要があります。
FreeBSD patch コマンド向けの差分ファイルは、https://ctrl.basekernel.ne.jp/rebase/download.html からダウンロードできるようにしてありますので、『opendkim-2.3.10用 FreeBSD 向け Patch ファイル群』をダウンロードしてみて、使えるようであれば、使ってみてください。
おそらく、以下のように実行すると良いと思います(当方では、この手法で出来ることの確認をしていません)
# cp opendkim-2.10.3.patch.gz /usr/local/src/opendkim-2.10.3 # cd /usr/local/src/opendkim-2.10.3 # tar xvzf opendkim-2.10.3.patch.gz # patch < patch-configure.ac # patch < patch-libopendkim__tests__Makefile.in ・ ・ # patch < patch-opendkim_opendkim-lua.c
OpenDKIM のインストール
以下の手順で実行していきます:# cd /usr/local/src/opendkim-2.10.3 # setenv CPPFLAGS '-I/usr/local/include -I/usr/include' (configure が上手くライブラリを探せないため) # setenv LDFLAGS '-L/usr/local/lib -L/usr/lib -L/usr/local/lib/db5'(configure が上手くライブラリを探せないため) # unsetenv LD_LIBRARY_PATH ※以下、説明のために改行していますが、 ./configure の部分は、改行せずに、半角スペース区切りで、一気に入力。 # ./configure --localstatedir=/var (unix ソケットなどを格納するディレクトリ) --with-gnutls (openssl 1.1.x は未サポートのため、gnutls を使用する) --with-milter (milter インタフェースを構築する) --with-db (BerkeleyDBをサポートする) --with-domain (domain 設定をサポートする) # make # make installここまで、エラー無く出来たら、インストール不具合が1つあるので、下記の要領でファイル修正を行います。
# vi /usr/local/sbin/opendkim-genkey
1行目を下記の要領で変更 #!/usr/bin/perl ↓ #!/usr/local/bin/perl次に設定作業を行います。
# mkdir /usr/local/etc/opendkim # cd /usr/local/etc/opendkim # mkdir example.com.keys (メールサーバ収容ドメイン毎にディレクトリを作成) # cd example.com.keys # /usr/local/sbin/opendkim-genkey --bits=2048 --domain=example.com --selector=sel (--domain:収容ドメイン --selector:任意) ※ --selector の文字列は公開鍵識別子になるので、適当にせずに何等かの規則を作るとよいでしょう。 pop1 とか mx1 とか、メールサーバの付番に対応すると良いと思います。 # cd ../ # chown -R opendkim:mailuser example.com.key # vi KeyTable ※ 以下のように編集をする(複数ドメインになる場合は、行追加) sel._example.com example.com:sel:/usr/local/etc/opendkim/example.com.keys/sel.private # vi SigningTable ※ 以下のように編集をする(複数ドメインになる場合は、行追加) *@example.com sel._domainkey.example.com # chown opendkim:mailuser KeyTable # chown opendkim:mailuser SigningTableここで、KeyTable ファイルへの記述方法ですが、
①._②:①:/usr/local/etc/opendkim/②.keys/①.private
(①= --selector への設定文字列 ②= --domain への設定文字列)
SigningTable ファイルへの記述方法は、
*@② ①._domainkey.②
(①= --selector への設定文字列 ②= --domain への設定文字列) のようにします。
次に、上記で /usr/local/sbin/opendkim-genkey を実行した時に生成された、sel.txt の内容と、取り扱いポリシーをDNSへ登録します。(--selector の指定により、拡張子 .txt は変わりませんが、ファイル名は変わります。)
生成された、sel.txt の内容を見ると、テキスト形式にて、
sel._domainkey IN TXT ( "v=DKIM1; k=rsa; " "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAulH2mMsm8eP7bpAmlXI9EE5TQ8tjWBaMDaSP34zB+0+LLjJhbi9dhGV9T5reppaxaU7lzokGaHJX+l9X4sA53G9PlpzQ0JwsGO5Z1E9LaN1CDijqtGNQWr7bdLpy4AwzfhDThY25rwzMg4Wt4LK74Ba7Q5a2M9m0d/gEqs6ZWrK/6DiQ1qnzU7JTm13SZRBvnNhmLBMnSu1sz3"
"Hc8nE+02oAJC04xCjI8XUowu+sxyJQm7OY97MOvOt9HAXt/jbBVyG1scJTggjYsH1JcfC1xbE1VnNiWSkpid2P+4bXy/GIG90nZ7We4KNMshXEby/FbUZPPERHW1ntCyga58rPJQIDAQAB" ) ;
----- DKIM key sel0 for example.jp
のような内容になっています。この例に沿うと、example.jp のDNSゾーンに、・name : sel._domainkey.example.jp.
・type : TXT
・content:"v=DKIM1; .... 以下 .... yga58rPJQIDAQAB" まで
を登録します。もう一つ、example.jp のDNSゾーンに
・name : _adsp._domainkey.example.jp.
・type : TXT
・content:"dkim=unknown"
を登録します。このパラメータの意味は下記のとおりです:
| unknown | 該当ドメインからの送信メールには、メール作成者署名(DKIM)がある場合と無い場合がある |
|---|---|
| all | 該当ドメインからの送信メールには、必ずメール作成者署名(DKIM)がある もしメール作成者署名(DKIM)が得られない場合の処置は受信側の任意。(通常は配送する) |
| discardable | 該当ドメインからの送信メールには、必ずメール作成者署名(DKIM)がある。 もしメール作成者署名(DKIM)が得られない場合は、受信者はそのメールを破棄することが望まれる |
起動・停止スクリプトの設置(FreeBSD に特化している項目)
このスクリプトは一度作っておくと、バージョンアップの際に再作成の必要はありません。○ OpenDKIM の起動スクリプト
/usr/local/etc/rc.d/ ディレクトリ配下に、milter-opendkim のファイル名で下記内容を作成します。
実行権限を与えることを忘れないようにしてください:
#!/bin/sh
# PROVIDE: milter-opendkim
# REQUIRE: DAEMON
# BEFORE: mail
# KEYWORD: shutdown
# Define these milteropendkim_* variables in one of these files:
# /etc/rc.conf
# /etc/rc.conf.local
# /etc/rc.conf.d/milteropendkim
#
# milteropendkim_enable (bool): Set to "NO" by default.
# Set it to "YES" to enable dkim-milter
# milteropendkim_uid (str): Set username to run milter.
# milteropendkim_gid (str): Set group to run milter.
# milteropendkim_profiles (list): Set to "" by default.
# Define your profiles here.
# milteropendkim_cfgfile (str): Configuration file. See opendkim.conf(5)
#
# milteropendkim_${profile}_* : Variables per profile.
# Sockets must be different from each other.
#
# milteropendkim_socket_perms (str):
# Permissions for local|unix socket.
#
# all parameters below now can be set in opendkim.conf(5).
# milteropendkim_socket (str): Path to the milter socket.
# milteropendkim_domain (str): Domainpart of From: in mails to sign.
# milteropendkim_key (str): Path to the private key file to sign with.
# milteropendkim_selector (str): Selector to use when signing
# milteropendkim_alg (str): Algorithm to use when signing
# milteropendkim_flags (str): Flags passed to start command.
. /etc/rc.subr
name="milteropendkim"
rcvar=milteropendkim_enable
extra_commands="reload"
start_precmd="dkim_prepcmd"
start_postcmd="dkim_start_postcmd"
stop_postcmd="dkim_postcmd"
command="/usr/local/sbin/opendkim"
_piddir="/var/run/milteropendkim"
pidfile="${_piddir}/pid"
sig_reload="USR1"
load_rc_config $name
#
# DO NOT CHANGE THESE DEFAULT VALUES HERE
#
: ${milteropendkim_enable:="NO"}
: ${milteropendkim_uid:="opendkim"}
: ${milteropendkim_gid:="mailuser"}
: ${milteropendkim_cfgfile:="/usr/local/etc/opendkim/opendkim.conf"}
: ${milteropendkim_socket_perms:="0775"}
# Options other than above can be set with $milteropendkim_flags.
# see dkim-milter documentation for detail.
extra_commands="reload"
start_precmd="dkim_prepcmd"
start_postcmd="dkim_start_postcmd"
stop_postcmd="dkim_cleansockets"
command="/usr/local/sbin/opendkim"
sig_reload="USR1"
dkim_cleansockets()
{
case ${milteropendkim_socket%:*} in
local|unix)
rm -f "${milteropendkim_socket#*:}"
;;
esac
}
dkim_get_pidfile()
{
if get_pidfile_from_conf PidFile ${milteropendkim_cfgfile#-x }; then
pidfile="$_pidfile_from_conf"
else
pidfile="/var/run/milteropendkim/${profile:-pid}"
fi
}
dkim_prepcmd()
{
dkim_cleansockets
dkim_get_pidfile
if [ ! -d "$(dirname "$pidfile")" ]; then
mkdir "$(dirname "$pidfile")"
fi
case ${milteropendkim_socket%:*} in
local|unix)
socketfile=${milteropendkim_socket#*:}
install -d -o ${milteropendkim_uid%:*} -g $milteropendkim_gid \
-m ${milteropendkim_socket_perms} \
${pidfile%/*} ${socketfile%/*}
;;
esac
}
dkim_start_postcmd()
{
case ${milteropendkim_socket%:*} in
local|unix)
# postcmd is executed too fast and socket is not created before checking...
sleep 1
chmod -f ${milteropendkim_socket_perms} ${milteropendkim_socket#*:}
;;
esac
}
if [ -n "$2" ]; then
profile="$2"
if [ -n "${milteropendkim_profiles}" ]; then
pidfile="${_piddir}/${profile}.pid"
eval milteropendkim_enable="\${milteropendkim_${profile}_enable:-${milteropendkim_enable}}"
eval milteropendkim_socket="\${milteropendkim_${profile}_socket:-}"
eval milteropendkim_socket_perms="\${milteropendkim_${profile}_socket_perms:-}"
if [ -z "${milteropendkim_socket}" ];then
echo "You must define a socket (milteropendkim_${profile}_socket)"
exit 1
fi
eval milteropendkim_cfgfile="\${milteropendkim_${profile}_cfgfile:-${milteropendkim_cfgfile}}"
eval milteropendkim_domain="\${milteropendkim_${profile}_domain:-${milteropendkim_domain}}"
eval milteropendkim_key="\${milteropendkim_${profile}_key:-${milteropendkim_key}}"
eval milteropendkim_selector="\${milteropendkim_${profile}_selector:-${milteropendkim_selector}}"
eval milteropendkim_alg="\${milteropendkim_${profile}_alg:-${milteropendkim_alg}}"
eval milteropendkim_flags="\${milteropendkim_${profile}_flags:-${milteropendkim_flags}}"
if [ -f "${milteropendkim_cfgfile}" ];then
milteropendkim_cfgfile="-x ${milteropendkim_cfgfile}"
else
milteropendkim_cfgfile=""
fi
if [ -n "${milteropendkim_socket}" ];then
_socket_prefix="-p"
fi
if [ -n "${milteropendkim_uid}" ];then
_uid_prefix="-u"
if [ -n "${milteropendkim_gid}" ];then
milteropendkim_uid=${milteropendkim_uid}:${milteropendkim_gid}
fi
fi
if [ -n "${milteropendkim_domain}" ];then
milteropendkim_domain="-d ${milteropendkim_domain}"
fi
if [ -n "${milteropendkim_key}" ];then
milteropendkim_key="-k ${milteropendkim_key}"
fi
if [ -n "${milteropendkim_selector}" ];then
milteropendkim_selector="-s ${milteropendkim_selector}"
fi
if [ -n "${milteropendkim_alg}" ];then
milteropendkim_alg="-S ${milteropendkim_alg}"
fi
dkim_get_pidfile
command_args="-l ${_socket_prefix} ${milteropendkim_socket} ${_uid_prefix} ${milteropendkim_uid} -P ${pidfile} ${milteropendkim_cfgfile}
${milteropendkim_domain} ${milteropendkim_key} ${milteropendkim_selector} ${milteropendkim_alg}"
else
echo "$0: extra argument ignored"
fi
else
if [ -n "${milteropendkim_profiles}" ] && [ -n "$1" ]; then
if [ "$1" != "restart" ]; then
for profile in ${milteropendkim_profiles}; do
echo "===> milteropendkim profile: ${profile}"
/usr/local/etc/rc.d/milter-opendkim $1 ${profile}
retcode="$?"
if [ "${retcode}" -ne 0 ]; then
failed="${profile} (${retcode}) ${failed:-}"
else
success="${profile} ${success:-}"
fi
done
exit 0
else
restart_precmd=""
fi
else
if [ -f "${milteropendkim_cfgfile}" ];then
milteropendkim_cfgfile="-x ${milteropendkim_cfgfile}"
else
milteropendkim_cfgfile=""
fi
if [ -n "${milteropendkim_socket}" ];then
_socket_prefix="-p"
fi
if [ -n "${milteropendkim_uid}" ];then
_uid_prefix="-u"
if [ -n "${milteropendkim_gid}" ];then
milteropendkim_uid=${milteropendkim_uid}:${milteropendkim_gid}
fi
fi
if [ -n "${milteropendkim_domain}" ];then
milteropendkim_domain="-d ${milteropendkim_domain}"
fi
if [ -n "${milteropendkim_key}" ];then
milteropendkim_key="-k ${milteropendkim_key}"
fi
if [ -n "${milteropendkim_selector}" ];then
milteropendkim_selector="-s ${milteropendkim_selector}"
fi
if [ -n "${milteropendkim_alg}" ];then
milteropendkim_alg="-S ${milteropendkim_alg}"
fi
dkim_get_pidfile
command_args="-l ${_socket_prefix} ${milteropendkim_socket} ${_uid_prefix} ${milteropendkim_uid} -P ${pidfile} ${milteropendkim_cfgfile}
${milteropendkim_domain} ${milteropendkim_key} ${milteropendkim_selector} ${milteropendkim_alg}"
fi
fi
run_rc_command "$1"
OpenDKIM の設定は、これで終わりです。