2022/12/27(火)メールサーバの中規模改修と基礎知識(5)~ OpenDKIM
2022/12/27 5: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 の設定は、これで終わりです。