CentOS 5.9のqmailをDKIMやTLSv1.3に対応させてみた

今更ながらではあるのだけど、CentOS 5.９などという古のの環境で動くqmail-1.3を今話題のGoogleが突きつけてきた難題[「メール送信者のガイドライン」](https://support.google.com/a/answer/81126?hl=ja)に対応させる必要が出てしまったのでざっくりとその手順を。

結論から言えばqmail-1.3に色々とパッチを当てまくってみたけど、オリジナルのqmailでの対応には失敗して、TLSv1.3対応パッチが施されたnetqmail-1.06のコンパイルとインストールを行うこととなった。
以下の手順はインストール直後のすっぴんなCentOS 5.9で実施した際の手順なので、利用環境によってはそのままでは適用できないかもなのでご容赦を。

# Cent OSのリポジトリを修正
※もう随分と昔にCentOS 5はEOLを迎えているのでまずはyumのリポジトリを現在でも生きている環境へと向けてあげる必要があります。

参考データとして自分が使ったyumのリポジトリ設定をあげておきます。
基本は`ftp.jaist.ac.jp`から取得し、epelは`ftp.iij.ad.jp`から取得するようにしました
※yumが古くなり過ぎてssl通信(`https//`)で取得しようとするとエラーになるので、暗号化なし(`https://`)で通信可能なミラーサイトから取得するのがミソです
```config:/etc/yum.repos.d/CentOS-Base.repo
# CentOS-Base.repo
#
# The mirror system uses the connecting IP address of the client and the
# update status of each mirror to pick mirrors that are updated to and
# geographically close to the client.  You should use this for CentOS updates
# unless you are manually picking other mirrors.
#
# If the mirrorlist= does not work for you, as a fall back you can try the
# remarked out baseurl= line instead.
#
#

[base]
name=CentOS-$releasever - Base
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os
#baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
baseurl=http://ftp.jaist.ac.jp/pub/Linux/CentOS-vault/5.9/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#released updates
[updates]
name=CentOS-$releasever - Updates
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates
#baseurl=http://mirror.centos.org/centos/$releasever/updates/$basearch/
baseurl=http://ftp.jaist.ac.jp/pub/Linux/CentOS-vault/5.9/updates/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras
#baseurl=http://mirror.centos.org/centos/$releasever/extras/$basearch/
baseurl=http://ftp.jaist.ac.jp/pub/Linux/CentOS-vault/5.9/extras/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=centosplus
#baseurl=http://mirror.centos.org/centos/$releasever/centosplus/$basearch/
baseurl=http://ftp.jaist.ac.jp/pub/Linux/CentOS-vault/5.9/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5

#contrib - packages by Centos Users
[contrib]
name=CentOS-$releasever - Contrib
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=contrib
#baseurl=http://mirror.centos.org/centos/$releasever/contrib/$basearch/
baseurl=http://ftp.jaist.ac.jp/pub/Linux/CentOS-vault/5.9/contrib/$basearch/
gpgcheck=1
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-5
```

```config:/etc/yum.repos.d/epel.repo
[epel]
name=Extra Packages for Enterprise Linux 5 - $basearch
baseurl=http://ftp.iij.ad.jp/pub/linux/Fedora/archive/epel/5/$basearch
#mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=epel-5&arch=$basearch
failovermethod=priority
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL

[epel-debuginfo]
name=Extra Packages for Enterprise Linux 5 - $basearch - Debug
baseurl=http://ftp.iij.ad.jp/pub/linux/Fedora/archive/epel/5/$basearch/debug
#mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=epel-debug-5&arch=$basearch
failovermethod=priority
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL
gpgcheck=1

[epel-source]
name=Extra Packages for Enterprise Linux 5 - $basearch - Source
baseurl=http://ftp.iij.ad.jp/pub/linux/Fedora/archive/epel/5/SRPMS
#mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=epel-source-5&arch=$basearch
failovermethod=priority
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL
gpgcheck=1
```

# yumでCentOS 5.9環境標準のperlをインストール
自分の環境では諸々の作業にとりあえずperlとc++が足りないと言われたのでyumでの追加インストールが必要でしたが、環境によっては他にも色々と足りないと言われるでしょうから、適時インストールします

```shell:
yum install perl
```

# yumでgcc-c++をインストール
```shell:
sudo yum install gcc-c++
```

# patchの新しいバージョンをインストール
※標準のpachが古くてパッチが充てられない
patchのソースコードはGNU Projectの[公式FTPサイト](https://ftp.gnu.org/gnu/patch/)からダウンロードしています。
https://ftp.gnu.org/gnu/patch/

```shell:
tar -zvxf patch-2.7.6.tar.gz
cd patch-2.7.6
./configure
make
sudo make install
```

# perl-5.10.1をインストール
※なぜならnetqmailのTLSv1.3対応版のコンパイルに必要なopenssl-1.1.1wのビルドに以下の通り新しいperlを要求されるので…
Perlのソースコードも公式サイトのアーカイブ(https://www.cpan.org/src/5.0/)からダウンロードしました
https://www.cpan.org/src/5.0/

openssl-1.1.1wのビルドをするのにConfigureを実行するとperlが古いと言われる
```console
cd openssl-1.1.1w
./Configure
Perl v5.10.0 required--this is only v5.8.8, stopped at ./Configure line 12.
BEGIN failed--compilation aborted at ./Configure line 12.
```

今のところopensslのConfigureでしか使わないし、面倒なので設定は全てデフォルトで
```shell
tar -zvxf perl-5.10.1.tar.gz
cd perl-5.10.1
sh Configure -de
make
make test
sudo make install
```

最後にmake testでテストをするとたぶんこんな感じにエラーが出るけど無視してインストールする
```console
t/pod/pod2usage2..............................................ok
t/pod/podchkenc...............................................ok
t/pod/poderrs.................................................ok
t/pod/podselect...............................................ok
t/pod/special_seqs............................................ok
t/pod/twice...................................................ok
t/x2p/s2p.....................................................ok
Failed 1 test out of 1609, 99.94% okay.
	../lib/Time/Local.t
### Since not all tests were successful, you may want to run some of
### them individually and examine any diagnostic messages they produce.
### See the INSTALL document's section on "make test".
### You have a good chance to get more information by running
###   ./perl harness
### in the 't' directory since most (>=80%) of the tests succeeded.
### You may have to set your dynamic library search path,
### LD_LIBRARY_PATH, to point to the build directory:
###   setenv LD_LIBRARY_PATH `pwd`; cd t; ./perl harness
###   LD_LIBRARY_PATH=`pwd`; export LD_LIBRARY_PATH; cd t; ./perl harness
###   export LD_LIBRARY_PATH=`pwd`; cd t; ./perl harness
### for csh-style shells, like tcsh; or for traditional/modern
### Bourne-style shells, like bash, ksh, and zsh, respectively.
u=3.33  s=3.62  cu=215.65  cs=41.03  scripts=1609  tests=226458
make[2]: *** [_test_tty] エラー 1
make[2]: ディレクトリ `/usr/local/src/perl-5.10.1' から出ます
make[1]: *** [_test] エラー 2
make[1]: ディレクトリ `/usr/local/src/perl-5.10.1' から出ます
make: *** [test] エラー 2
```

エラーは無視してインストール
```shell
sudo make install
```

# 本題のopenssl-1.1.1wのインストール
OpenSSLも[公式サイト](https://www.openssl.org/source/)から1.1.1系の最新をダウンロードしましたダウンロードしました
https://www.openssl.org/source/

※Configure(perlスクリプト)の実行には先程ビルドした/usr/local/bin/perlを使う
```shell
cd openssl-1.1.1w
/usr/local/bin/perl ./Configure shared linux-x86_64
make
sudo make install
```

#### インストールしたopenssl-1.1.1wの共有ライブラリが使われるようにld.so.confを調整
```conf:/etc/ld.so.conf.d/openssl.conf
/usr/local/lib64
```

#### ld.so.confの設定を反映
```shell
sudo /sbin/ldconfig
```

# TLS対応パッチ適用ずみのnetqmail-1.06(smtp-auth + qmail-tls + forcetls patch for qmail)をインストール
[smtp-auth + qmail-tls + forcetls patch for qmail](https://notes.sagredo.eu/qmail-notes-185/smtp-auth-qmail-tls-forcetls-patch-for-qmail-84.html)をインストール
ソースコードはGithubのリポジトリより取得
https://notes.sagredo.eu/qmail-notes-185/smtp-auth-qmail-tls-forcetls-patch-for-qmail-84.html

```shell:
cd /usr/local/src
unzip qmail-auth-tls-forcetls-main.zip
cd qmail-auth-tls-forcetls-main
```

#### qmailのユーザーとグループを作成
すでにqmailを導入済みの環境ではユーザーとグループは存在するはずなので不要な作業だが、もし新規にインストールする場合にはqmailをビルドするのに先立ちユーザーとグループを作っておかないとビルドに失敗する

```shell
sudo mkdir /var/qmail 
sudo /usr/sbin/groupadd nofiles
sudo /usr/sbin/useradd -g nofiles -d /var/qmail/alias alias
sudo /usr/sbin/useradd -g nofiles -d /var/qmail qmaild
sudo /usr/sbin/useradd -g nofiles -d /var/qmail qmaill
sudo /usr/sbin/useradd -g nofiles -d /var/qmail qmailp
sudo /usr/sbin/groupadd qmail
sudo /usr/sbin/useradd -g qmail -d /var/qmail qmailq
sudo /usr/sbin/useradd -g qmail -d /var/qmail qmailr
sudo /usr/sbin/useradd -g qmail -d /var/qmail qmails
```

先にビルドしておいたopenssl-1.1.1wが使われるようにnetqmailのビルド設定を変更
```conf:conf-cc
cc -O2 -g -DEXTERNAL_TODO -DTLS=20231230 -I/usr/local/include

This will be used to compile .c files.
```

```conf:conf-ld
cc -s -L/usr/local/lib64

This will be used to link .o files into an executable.
```

ビルドしたqmailをインストール
```console
make
sudo make setup check
sudo ./install
sudo ./instcheck
```

qmailの初期設定ファイルを/var/qmail/controlに設定
```console
sudo ./config-fast <送信元ドメイン名>
Your fully qualified host name is hoge.hoge.net.
Putting hoge.hoge.net into control/me...
Putting hoge.net into control/defaultdomain...
Putting hoge.net into control/plusdomain...
Putting hoge.hoge.net into control/locals...
Putting hoge.hoge.net into control/rcpthosts...
Now qmail will refuse to accept SMTP messages except to hoge.hoge.net.
Make sure to change rcpthosts if you add hosts to locals or virtualdomains!
[root@msl-backup qmail-auth-tls-forcetls-2024.01.15]#
```

# qmailをサービス化するためのucspi-tcpを(tcpserver)インストール
ucspi-tcpは[こちらから](http://cr.yp.to/ucspi-tcp/ucspi-tcp-0.88.tar.gz)ダウンロード
http://cr.yp.to/ucspi-tcp/ucspi-tcp-0.88.tar.gz

tcpserverをSSLに対応させるパッチを追加
tcpserverをSSLに対応させるパッチは[こちらのサイト(NRG4U)](http://www.nrg4u.com/)からダウンロードしてきます

パッチファイルは[こちらから](http://www.nrg4u.com/qmail/ucspi-tcp-ssl-20050405.patch.gz)ダウンロードしました
http://www.nrg4u.com/qmail/ucspi-tcp-ssl-20050405.patch.gz

以下の３本のパッチファイルはnetqmail-1.06のソースツリーのother-patchesディレクトリ以下に入っている
- ucspi-tcp-0.88.a_record.patch
- ucspi-tcp-0.88.errno.patch
- ucspi-tcp-0.88.nodefaultrbl.patch

ソースコードを展開しSSL対応パッチ他を適用
```shell:
cd /usr/local/src
tar -zvxf ucspi-tcp-0.88.tar.gz
cd ucspi-tcp-0.88
patch -p1 < ../ucspi-tcp-0.88.a_record.patch
patch -p1 < ../ucspi-tcp-0.88.errno.patch
patch -p1 < ../ucspi-tcp-0.88.nodefaultrbl.patch
zcat ../ucspi-tcp-ssl-20050405.patch.gz | patch -p0
```

Makefileで先にインストールしたopenssl-1.1.1wが使われるように修正
```diff:Makefile
--- Makefile.old	2024-01-26 18:36:51.000000000 +0900
+++ Makefile	2024-02-02 18:46:15.000000000 +0900
@@ -4,8 +4,8 @@
 #add -DWITH_SSL to enable ssl support

 # LIBS for additional libraries and INCS for additional includes
-LIBS=-lcrypto -lssl
-#INCS=-I/usr/local/include
+LIBS=-L/usr/local/lib64 -lcrypto -lssl
+INCS=-I/usr/local/include
 OPENSSLBIN=openssl

 SHELL=/bin/sh
```

ビルドしてインストール
```shell:
make
sudo make setup check 
```

tcpserver用の設定ファイルを作成
※設定内容は自身の環境に合わせて！
```file:/etc/tcp.smtp
192.168.:allow,RELAYCLIENT=""
127.:allow,RELAYCLIENT=""
```

tcpserver用の設定ファイルを反映
```shell:
sudo /usr/local/bin/tcprules /etc/tcp.smtp.cdb /etc/tcp.smtp.tmp < /etc/tcp.smtp
```

#### tcpserver用のSSL証明書を準備

tcpserver（ucspi-tcp-ssl）では秘密鍵と証明書(CAの証明書があればそれも)を一つにまとめたファイルを読み込んで利用するのでこれを用意します
自分の環境ではLet's Encryptで発行したSSL証明書を使っているので以下のように秘密鍵とCAの証明書、自局の証明書をcatで一つに結合して作成しました

秘密鍵と証明書を結合
```shell:
sudo cat /etc/letsencrypt/live/mydomain.domain.tld/privkey.pem /etc/letsencrypt/live/mydomain.domain.tld/fullchain.pem > servercert.pem
```

servercert.pemを所定の場所(デフォルトではqmailの設定ディレクトリ)に移動
※作成したservercert.pemには機密情報である秘密鍵が含まれているのでコピーではなくmvで移動しています
またchownとchmodを使い、rootとqmail以外からは読み取れないようにしました
```shell:
sudo mv servercert.pem /var/qmail/control/servercert.pem
sudo chown root:qmail /var/qmail/control/servercert.pem
sudo chmod 640 /var/qmail/control/servercert.pem
```

qmailの起動スクリプトを(存在しないなら)作成
※たぶん普通はすでにどこかにある

この起動スクリプトの場合には/var/qmail/boot/homeファイルへのシンボリックリンクを/var/qmail/rcとして作成済みを想定しているので、以下のようにシンボリックリンクを/var/qmail/rcに作成した

```shell
sudo ln -sf /var/qmail/boot/home /var/qmail/rc
```

サービス起動スクリプト
```shell:/etc/rc.d/init.d/qmail
#!/bin/sh
#
# qmail: /var/qmail
PATH=/var/qmail/bin:/usr/local/bin:/bin:/usr/bin
#FORCETLS=0
[ -f /var/qmail/rc ] || exit 0
case "$1" in
  start)
        # Start daemons.
        echo "Starting qmail."
        csh -cf '/var/qmail/rc &'
        tcpserver -v -u <qmaildのUID> -g <nofilesのGID> -x /etc/tcp.smtp.cdb \
       0 smtp /var/qmail/bin/qmail-smtpd 2>&1 | /var/qmail/bin/splogger smtpd 3 &
        echo
        touch /var/lock/qmail
        ;;
  stop)
        # Stop daemons.
        echo "Shutting down qmail."
        PID=`/bin/ps -aefw | grep qmail | awk '{print $2}'`
        if [ ! -z "$PID" ] ;  then
            /bin/kill ${PID} 1> /dev/null 2>&1
        fi
        echo
        rm -f /var/lock/qmail
        ;;
   *)
        echo "Usage: S99qmail {start|stop}"
        exit 1
esac
exit 0
```

qmailとtcpserverをサービスとして起動する
```shell:
sudo /sbin/service qmail start
```

#### opensslを使いtcpserverで設定したSSL証明書が使われているか確認する
(192.168.xxx.xxxはqmailサービスを起動したサーバーのIPアドレス）
```shell:
openssl s_client -connect 192.168.xxx.xxx:25 -starttls smtp -ign_eof
```

コンソールにSSL証明書の内容(有効期限やコモンネームなど)が表示されれば正しくSSL証明書が読み込まれている
```console:
CONNECTED(00000004)
Can't use SSL_get_servername
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = mydomain.domain.tld
verify return:1
---
...などなど...
```
※コンソールに`read R BLOCK`が出たら`^C`などで停止

#### testssl.shを使ったTLSの動作確認
念を入れるなら、[この記事](https://notes.sagredo.eu/qmail-notes-185/smtp-auth-qmail-tls-forcetls-patch-for-qmail-84.html#:~:text=Reply%20%7C%20Permalink-,Cipher%20suite%20preference%20order,-Gabriel%20Torres%20%20June)で紹介されている`testssl.sh`などを使い、サポートされているプロトコルのバージョンなども要求通りになっているか確認しておくと良いでしょう
自分の場合は`testssl.sh`は[Githubのリポジトリ](https://github.com/drwetter/testssl.sh.git)から最新版をmacOS上で取得して利用しました

(192.168.xxx.xxxはqmailサービスを起動したサーバーのIPアドレス）
```shell:
git clone --depth 1 https://github.com/drwetter/testssl.sh.git
cd testssl.sh
./testssl.sh -t smtp 192.168.xxx.xxx:25
```

```console:
kazu@VM-Host testssl.sh % ./testssl.sh -t smtp 192.168.xxx.xxx:25

###########################################################
    testssl.sh       3.2rc3 from https://testssl.sh/dev/
    (c30e541 2024-02-02 20:25:46)

      This program is free software. Distribution and
             modification under GPLv2 permitted.
      USAGE w/o ANY WARRANTY. USE IT AT YOUR OWN RISK!

       Please file bugs @ https://testssl.sh/bugs/

###########################################################

 Using "OpenSSL 1.0.2-bad (1.0.2k-dev)" [~183 ciphers]
 on VM-Host:./bin/openssl.Darwin.x86_64
 (built: "Sep  3 14:46:36 2022", platform: "darwin64-x86_64-cc")


 Start 2024-02-04 09:13:37                -->> 192.168.xxx.xxx:25 (192.168.xxx.xxx) <<--

 rDNS (192.168.xxx.xxx):  --
 Service set:            STARTTLS via SMTP

 Testing protocols via sockets

 SSLv2      not offered (OK)
 SSLv3      likely not offered (OK), received 4xx/5xx after STARTTLS handshake, rerun with DEBUG>=2 or --ssl-native
 TLS 1      offered (deprecated)
 TLS 1.1    offered (deprecated)
 TLS 1.2    offered (OK)
 TLS 1.3    offered (OK): final

```

# wget-1.21をインストール
これはメール送信のローカルテストに便利なのでインストールしたけど、運用には必要はないです
wgetもGNU Projctの[公式FTPサイト](https://ftp.gnu.org/gnu/wget/)からダウンロードしました
(CentOS 5.9の標準でインストールできるwgetではsslの暗号化プロトコルが合わないのでhttp://でのダウンリロードが必要でした)

```shell:
cd /usr/local/src
tar -zvxf wget-1.21.tar.gz
cd wget-1.21
./configure --with-ssl=openssl --with-libssl-prefix=/usr/local
make CFLAGS=-std=c99
sudo make install
```

Let,s encryptなどのCA証明書(中間認証局の証明書)が必要なサイトの中間証明書が記録されたファイルの最新版を以下のサイトからダウンロード
https://curl.se/docs/caextract.html

先程インストールされたwgetを使うが、今まさにダウンロードするファイルがないと相手側を信頼できないのでオプションに--no-check-certificateを付けて検証をスキップ
```
/usr/local/bin/wget --no-check-certificate https://curl.se/ca/cacert-2023-12-12.pem
```

#### ダウンロードしたCA証明書を所定の場所に移動
```shell
sudo mkdir -p /usr/local/ssl/certs/
sudo mv cacert-2023-12-12.pem /usr/local/ssl/certs/ca-certificates.crt
```

#### インストールしたwgetがCA証明書を使うように設定
```conf:~/.wgetrc
ca_certificate=/usr/local/ssl/certs/ca-certificates.crt
```

# curl-8.5.0をインストール
これもローカルテストに便利なのでインストールしたけど、運用には必要はないです
ソースコードはcurlの公式サイト(https://curl.se/download.html)からダウンロードしました

```shell
cd /usr/local/src
tar -zvxf curl-8.5.0
cd curl-8.5.0
./configure --with-openssl=/usr/local --with-ca-bundle=/usr/local/ssl/certs/ca-certificates.crt
make
sudo make install
```

#### インストールしたcurlを使ってメール送信のテストをしてみる
```shell
cd ~/qmail_dkim_test
 /usr/local/bin/curl -v --url 'smtp://<qmailサーバー>' --mail-from <送信元メールアドレス> --mail-rcpt <送信元メールアドレス> --upload-file mail_txt
```

mail_txtにはこんな感じでテストメールの中身を書き込む
※メール本文は改行コードがCRLFであることに注意！　（nkf -Lwとかで変換すると良い)
```apiblueprint
From:  <送信元メールアドレス>
To: <送信元メールアドレス>
Subject: an msl-build example email
Date: Thu Jan 25 22:30:47 PST 2024

Dear Joe,
Welcome to this example email. What a lovely day.

```

もし送信相手がDKIMやSFP、DMARCなどの情報を設定済みでDNSのレスポンスが５１２バイトを超えるようだとqmailがエラー'CNAME_lookup_failed_temporarily..,'を吐くのでログを確認してみる
```plaintext:/var/log/maillog
Jan 25 22:38:35 msl-build qmail: 1706251115.405085 info msg 585532: bytes 415 from <john_doe@my.domain.dom> qp 11617 uid 491
Jan 25 22:38:35 msl-build qmail: 1706251115.410840 starting delivery 1: msg 585532 to remote someone@remote.domain.dom
Jan 25 22:38:35 msl-build qmail: 1706251115.410975 status: local 0/10 remote 1/20
Jan 25 22:38:35 msl-build qmail: 1706251115.905974 delivery 1: deferral: CNAME_lookup_failed_temporarily._(#4.4.3)/
Jan 25 22:38:35 msl-build qmail: 1706251115.906140 status: local 0/10 remote 0/20
Jan 25 22:41:05 msl-build qmail: 1706251265.400220 status: exiting
Jan 25 22:41:09 msl-build qmail: 1706251269.030761 status: local 0/10 remote 0/20
```

この問題(DNSのレスポンスが512バイトを超えるとエラーになる)を解消するための[パッチqmail-103.patch](http://www.ckdhr.com/ckd/qmail-103.patch)が存在するので、これをqmail-auth-tlsに適用する
http://www.ckdhr.com/ckd/qmail-103.patch

#### qmail-103.patch (DNSのレスポンスが512バイトを超えてもエラーにならないようにする)を充ててqmailを再ビルド、インストール
```
cd qmail-auth-tls-forcetls-main
/usr/local/bin/patch -p1 < ../qmail-103.patch
make
sudo service qmail stop
sudo make setup check
sudo service qmail start
```

もう一度テストメールを送ってちゃんと送れればOK
```shell
cd qmail_dkim_test
/usr/local/bin/curl -v --url 'smtp://<qmailサーバー>' --mail-from <送信元メールアドレス> --mail-rcpt <送信元メールアドレス> --upload-file mail_txt
```

メール送信テストをqmailをインストールしたサーバーで行うなら、以下のようなスクリプトを使い(手動でもOKですか)qmail-inject経由でメール送信をテストすることもできます

#### qmail-inject経由でのメール送信をテスト
```shell:send_test_mail.sh
#!/bin/sh

smtp_host=`hostname`
from_user=`whoami`
from_domain=`cat /var/qmail/control/defaultdomain | head -1`
to_mail_address=[テストメール送信先]
send_date=`LANG=C date`
date_string=`date '+%Y%m%d'`
meessage_id=${date_string}${from_user}\@${from_domain}
mail_txt=`mktemp -p /tmp -t mail.XXXXXXXXXXXXXXX`

cat << EOT > ${mail_txt}
From: ${from_user}@${from_domain}
Reply-To: noreply-appledev@${from_domain}
To: ${to_mail_address}
Subject: an msl-build example email
Date: ${send_date}
Message-ID: <${meessage_id}>

Dear Joe,
Welcome to this example email. What a lovely day.

EOT

cat ${mail_txt} | /var/qmail/bin/qmail-inject
rm -f ${mail_txt}
```

ここまでがTLSv1.3をサポートするqmail(netqmail)のインストールなので、ここまでの動作が確認できればあとはDNSへのTXTレコードの設定や証明書の取得、DKIMを有効化するスクリプトの設定を行えばDKIMも有効になります

# DKIMへの対応
qmail(netqmail)のDKIM対応についてはGithubの[こちらの記事](https://github.com/corokada/return-qmail/blob/master/qmail-dkim.md)を参考にさせて頂きました。
Githubの参考記事
https://github.com/corokada/return-qmail/blob/master/qmail-dkim.md

具体的な手順としては「２.perlのDKIMモジュールをインストール」は記事のとおりyumを使い`perl-Mail-DKIM`のインストールを行います。

```shell
sudo yum install perl-Mail-DKIM --nogpgcheck
```
※自分の環境では証明書の期限切れでインストールには`yum`のオプション`--nogpgcheck`が必要でした

#### openssl-1.0.2uのインストール
ここまでの手順ですでに新しいopenssl-1.1.1wがインストール済みなのだけどlibdomainkeysとの互換性がないので手を抜いて[古いopenssl-1.0.2u](https://ftp.openssl.org/source/old/1.0.2/)もインストールします
古いOpenSSLのアーカイブ
https://ftp.openssl.org/source/old/1.0.2/

(ここでは個人的好みで/usr/local/opensslにインストールした)
```shell
cd /usr/local/src
tar -zvxf openssl-1.0.2u.tar.gz
cd openssl-1.0.2u
./Configure shared linux-x86_64 --prefix=/usr/local/openssl
make
```

出来上がったライブラリをインストール
```shell
sudo make install
ls /usr/local/openssl/
bin  certs  include  lib  man  misc  openssl.cnf  private
```

＃＃##　「3.libdomainkeysをインストール」

```shell:
cd /usr/local/src
tar -zvxf libdomainkeys-0.69.tar.gz
cd libdomainkeys-0.69
```

先ほどインストールしたちょっとバージョンの古いopenssl-1.0.2uが使われるようにMakefileを一部修正します

```patch:libdomainkeys-0.69-Makefile.patch
--- Makefile.orig	2006-01-17 16:28:58.000000000 -0800
+++ Makefile	2024-01-21 22:43:26.000000000 -0800
@@ -2,8 +2,8 @@
 CFLAGS=-DBIND_8_COMPAT -O2
 #CFLAGS += -DDK_DEBUG -DDK_HASH_BUFF -Wall
 #CFLAGS += -DUNIXWARE
-INCS=-I.
-LIBS=-L. -ldomainkeys -lcrypto
+INCS=-I. -I/usr/local/openssl/include
+LIBS=-L. -L/usr/local/openssl/lib -ldomainkeys -lcrypto
 MAKE=make

 dktest: dktest.o libdomainkeys.a dns.lib socket.lib
```

```shell:
patch -p0 < libdomainkeys-0.69-Makefile.patch
echo -lresolv > dns.lib
make
sudo install -m 644 libdomainkeys.a /usr/local/lib
sudo install -m 644 domainkeys.h dktrace.h /usr/local/include
sudo install -m 755 dknewkey /usr/local/bin
```

#### 「4.libdkimをインストール」
ここではいくつか注意が必要で、Makefileと設定ファイルの一部を直さないとソースコードと互換性のあるライブラリが使われないので修正します

```
cd /usr/local/src
unzip libdkim-1.0.19.zip
cd libdkim/src/
/usr/local/bin/patch -p2 --binary < ../../libdkim-1.0.19-linux.patch
/usr/local/bin/patch -p2 --binary < ../../libdkim-1.0.19-extra-options.patch
```

```patch:libdkim-1.0.19-Makefile.patch
--- Makefile.prev	2024-01-28 23:41:41.000000000 -0800
+++ Makefile	2024-01-29 00:00:58.000000000 -0800
@@ -1,9 +1,9 @@
 # libdkim makefile for UNIX
 #

-DESTDIR ?= /
+DESTDIR ?= /usr/local

-CFLAGS  += -fPIC
+CFLAGS  += -fPIC -I/usr/local/openssl/include
 LIBS    = -lcrypto -lresolv

 INCL	=  -I /usr/include/openssl/
@@ -30,7 +30,7 @@

 libdkimtest : libdkim.so.1 libdkimtest.o
 	$(CXX) -o$@ $(LDFLAGS) \
-	       -L. libdkimtest.o $(LIBS) -ldkim
+	       -L. libdkimtest.o  -L/usr/local/openssl/lib $(LIBS) -ldkim

 libdkimtest.o: libdkimtest.cpp $(HDRS)

@@ -39,10 +39,10 @@
 	rm -f *.o libdkim.{a,so,so.1} libdkimtest

 install: all
-	install -D -m 0755 libdkim.so.1 $(DESTDIR)/usr/lib/libdkim.so.1
-	ln -s libdkim.so.1 $(DESTDIR)/usr/lib/libdkim.so
-	ln -s libdkim.so.1 $(DESTDIR)/usr/lib/libdkim.so.1.1
-	install -D -m 0644 dkim.h $(DESTDIR)/usr/include/dkim.h
-	install -D -m 0755 libdkimtest $(DESTDIR)/usr/bin/libdkimtest
+	install -D -m 0755 libdkim.so.1 $(DESTDIR)/lib/libdkim.so.1
+	ln -s libdkim.so.1 $(DESTDIR)/lib/libdkim.so
+	ln -s libdkim.so.1 $(DESTDIR)/lib/libdkim.so.1.1
+	install -D -m 0644 dkim.h $(DESTDIR)/include/dkim.h
+	install -D -m 0755 libdkimtest $(DESTDIR)/bin/libdkimtest

```

```shell:
patch -p0 < libdkim-1.0.19-Makefile.patch
make
sudo make install
```

#### 「5.DKIM署名・検証スクリプトをダウンロード」
DKIM署名・検証スクリプトのダウンロードも参考記事のはそのままで問題ありませんが、自分の場合は以下のようにwgetで一旦作業ディレクトリへと必要なファイルをダウンロードしてから所定の場所へとそれをコピーする手順に変更しました
また、オリジナルではwgetにオプション`--no-check-certificate`を付加して証明書の検証をスキップしていますが、せっかく新しいwgetとCAの証明書リストをインストールしたのでそれ（`/usr/local/bin/wget`）を使い`--no-check-certificate`は削除しています

```shell:
cd /usr/local/src
mkdir qmail_dkim_scripts
cd qmail_dkim_scripts
/usr/local/bin/wget https://github.com/corokada/return-qmail/raw/master/dkimsign.pl
/usr/local/bin/wget https://github.com/corokada/return-qmail/raw/master/dkimverify.pl
sudo cp ./dkimsign.pl /var/qmail/bin/dkimsign.pl
sudo chmod +x /var/qmail/bin/dkimsign.pl
sudo chgrp qmail /var/qmail/bin/dkimsign.pl
sudo cp ./dkimverify.pl /var/qmail/bin/dkimverify.pl
sudo chmod +x /var/qmail/bin/dkimverify.pl
sudo chgrp qmail /var/qmail/bin/dkimverify.pl
```

```shell:
sudo sed -i -e "s/example.com/hogehoge.jp/" /var/qmail/bin/dkimverify.pl
```

#### 「6.DKIM署名をする際の作業ディレクトリを作成」
参考記事のままで問題ありませんでした

```shell:
sudo mkdir -p /var/domainkeys
sudo chown qmailr:qmail /var/domainkeys
sudo chmod 0700 /var/domainkeys
```

#### 「7.DKIM検証をする際の作業ディレクトリを作成」
ではディレクトリのオーナーを`root`ではなく`qmaild`に変更しないとこの後インストールする`qmail-dkimverify`での受信したメールのDKIM署名の確認に失敗します

```shell:
sudo mkdir -p /var/domainkeys-verify
sudo chown qmaild.root /var/domainkeys-verify
sudo chmod 0700 /var/domainkeys-verify
```

#### 「8.メール送信時にDKIM署名を行うwrapperをダウンロード」
ここも参考記事のままで問題ありませんでしたが、自分の場合は以下のようにwgetで一旦作業ディレクトリへと必要なファイルをダウンロードしてから所定の場所へとそれをコピーする手順に変更しました
※また、１行目の`mv /var/qmail/bin/qmail-remote /var/qmail/bin/qmail-remote.orig
`の処理は単なるオリジナルの`qmail-remote`のバックアップではなく、新しくコピーされるWarapperスクリプトからこのオリジナルファイルが呼び出されるので、移動の先は必ず`/var/qmail/bin/qmail-remote.orig`である必要があります

```shell:
sudo mv /var/qmail/bin/qmail-remote /var/qmail/bin/qmail-remote.orig
/usr/local/bin/wget https://github.com/corokada/return-qmail/raw/master/qmail-remote
sudo cp ./qmail-remote /var/qmail/bin/qmail-remote 
sudo chown root:qmail /var/qmail/bin/qmail-remote
sudo chmod 0755 /var/qmail/bin/qmail-remote
```

余談ですが、`mv`で`/var/qmail/bin/qmail-remote`を`/var/qmail/bin/qmail-remote.orig`へと移動するのに先立って`file`コマンドなどで`/var/qmail/bin/qmail-remote`がすでにWarapperスクリプトに置き換えられていないか確認した方が良いでしょう

```shell:
sudo file /var/qmail/bin/qmail-remote
/var/qmail/bin/qmail-remote: ELF 64-bit LSB executable, AMD x86-64, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), stripped
```
となれば`/var/qmail/bin/qmail-remote`はオリジナルのファイルですが、

```shell:
sudo file /var/qmail/bin/qmail-remote
/var/qmail/bin/qmail-remote: Bourne shell script text executable
```
となってしまったらすでに`/var/qmail/bin/qmail-remoteはWarapper`スクリプトに置き換えられてしまっているので、これを`/var/qmail/bin/qmail-remote.orig`へと移動してしまうと元の実行ファイルが無くなってしまいます

##### 追記：
DKIM署名をするためのperlスクリプト(qmail-remote)に不具合あり送信元メールアドレスがサブドメインだと署名されないようです

こんな感じにWarapperスクリプト`qmail-remote`にデバッグを仕込んで試してみました
```shell:qmail-remote
#!/bin/sh

#
# DKIM signing for qmail
#
# permissions must be 0755
#
# Author: Joerg Backschues
#

[ "$DKSIGN" ]   || DKSIGN="/etc/domainkeys/%/default"
[ "$DKREMOTE" ] || DKREMOTE="/var/qmail/bin/qmail-remote.orig"

# parent domains (see RFC 4871 3.8)

FQDN=${2##*@}
TLD=`echo $FQDN | awk 'BEGIN {FS = "."} {print $NF}'`
DOM=`echo $FQDN | awk 'BEGIN {FS = "."} {print $(NF-1)}'`
echo FQDN=$FQDN
echo TLD=$TLD
echo DOM=$DOM

# get domainkey file

if [[ $DKSIGN == *%* ]] ; then
    DOMAIN=$DOM.$TLD
    DKSIGN="${DKSIGN%%%*}${DOMAIN}${DKSIGN#*%}"
fi
echo DOMAIN=$DOMAIN
echo DKSIGN=$DKSIGN

if [ -f "$DKSIGN" ] ; then
  echo GO
```

サブドメインがなければOK
```shell:
./qmail-remote remote.mail.dom me@domain.dom you@your.domain.dom
FQDN=domain.dom
TLD=dom
DOM=domain
DOMAIN=domain.dom
DKSIGN=/etc/domainkeys/domain.dom/default
GO
```

サブドメインがあると動かない
```shell:
./qmail-remote remote.mail.dom me@sub.domain.dom you@your.domain.dom
FQDN=sub.domain.dom
TLD=dom
DOM=domain
DOMAIN=domain.dom
DKSIGN=/etc/domainkeys/domain.dom/default
```

qmail-remoteの修正箇所
```file:qmail-remote.patch
--- qmail-remote	2024-01-22 23:29:22.000000000 -0800
+++ /var/qmail/bin/qmail-remote	2024-01-24 17:23:07.000000000 -0800
@@ -12,17 +12,23 @@
 [ "$DKREMOTE" ] || DKREMOTE="/var/qmail/bin/qmail-remote.orig"

 # parent domains (see RFC 4871 3.8)
-
-FQDN=${2##*@}
-TLD=`echo $FQDN | awk 'BEGIN {FS = "."} {print $NF}'`
-DOM=`echo $FQDN | awk 'BEGIN {FS = "."} {print $(NF-1)}'`
+# 多分RFC 4871の解釈が間違っていて送信元メールアドレスにサブドメインがあると
+# 送信元メールアドレスからドメインがうまく取れない
+#FQDN=${2##*@}
+#TLD=`echo $FQDN | awk 'BEGIN {FS = "."} {print $NF}'`
+#DOM=`echo $FQDN | awk 'BEGIN {FS = "."} {print $(NF-1)}'`

 # get domainkey file

 if [[ $DKSIGN == *%* ]] ; then
-    DOMAIN=$DOM.$TLD
+    # 先のコメントと同様
+    #DOMAIN=$DOM.$TLD
+    DOMAIN=${2##*@}
     DKSIGN="${DKSIGN%%%*}${DOMAIN}${DKSIGN#*%}"
 fi
+# デバック用なので消して！
+echo DOMAIN=$DOMAIN >> /tmp/qmail_args.log
+echo DKSIGN=$DKSIGN >> /tmp/qmail_args.log

 if [ -f "$DKSIGN" ] ; then
```

ただし、RFC 4871の節3.8に書かれているようにサブドメインの場合には「親ドメインによる署名」を行うのであれば私の修正でも問題はあるのだが、その場合でもこのコードのTLDとDOMの取得方法は間違っていて、これだとdom.co.jpのようなドメイン(ccTLD)には対応できない

>3.8. Signing by Parent Domains
3.8. 親ドメインによる署名
In some circumstances, it is desirable for a domain to apply a signature on behalf of any of its subdomains without the need to maintain separate selectors (key records) in each subdomain. By default, private keys corresponding to key records can be used to sign messages for any subdomain of the domain in which they reside; e.g., a key record for the domain example.com can be used to verify messages where the signing identity ("i=" tag of the signature) is sub.example.com, or even sub1.sub2.example.com. In order to limit the capability of such keys when this is not intended, the "s" flag may be set in the "t=" tag of the key record to constrain the validity of the record to exactly the domain of the signing identity. If the referenced key record contains the "s" flag as part of the "t=" tag, the domain of the signing identity ("i=" flag) MUST be the same as that of the d= domain. If this flag is absent, the domain of the signing identity MUST be the same as, or a subdomain of, the d= domain. Key records that are not intended for use with subdomains SHOULD specify the "s" flag in the "t=" tag.

>状況によっては、ドメインがサブドメインごとに個別のセレクター（キーレコード）を維持する必要なしに、サブドメインの代わりに署名を適用することが望ましい場合があります。デフォルトでは、鍵レコードに対応する秘密鍵を使用して、それらが存在するドメインの任意のサブドメインのメッセージに署名できます。たとえば、ドメインexample.comのキーレコードを使用して、署名ID（署名の「i =」タグ）がsub.example.com、さらにはsub1.sub2.example.comであるメッセージを検証できます。これが意図されていない場合にこのようなキーの機能を制限するには、キーレコードの「t =」タグに「s」フラグを設定して、レコードの有効性を署名IDのドメインに限定します。参照されたキーレコードに "t ="タグの一部として "s"フラグが含まれている場合、署名IDのドメイン（ "i ="フラグ）はd =ドメインのドメインと同じである必要があります。このフラグが存在しない場合、署名IDのドメインは、d =ドメインと同じか、そのサブドメインでなければなりません（MUST）。サブドメインでの使用を目的としていないキーレコードは、「t =」タグに「s」フラグを指定する必要があります（SHOULD）。

#### 「9.メール受信時にDKIM検証を行うwrapperをダウンロード」
参考記事のままで問題ありませんでしたが、自分の場合は以下のようにwgetで一旦作業ディレクトリへと必要なファイルをダウンロードしてから所定の場所へとそれをコピーする手順に変更しました

```shell:
/usr/local/bin/wget https://raw.githubusercontent.com/corokada/return-qmail/master/qmail-dkimverify
sudo cp qmail-dkimverify /var/qmail/bin/qmail-dkimverify
sudo chown root:qmail /var/qmail/bin/qmail-dkimverify
sudo chmod 0755 /var/qmail/bin/qmail-dkimverify
```

#### 「10.QMAILQUEUEを利用できるようにする」
QMAILQUEUEの利用についてはパッチずみのnetqmail-1.06(smtp-auth + qmail-tls + forcetls patch for qmail)ではすでに適用済みなのでスキップしました

#### 「11.QMAILQUEUEを設定する」
QMAILQUEUEを利用する設定は先にインストール済みのucspi-tcp-0.88(tcpserver)の設定にメール受信時に受信メールのDKIM署名検証スクリプト`/var/qmail/bin/qmail-dkimverify`を呼び出す設定を追加します

##### tcpserver用の設定ファイルを作成
最後の１行が受信したメールのDKIM署名を検証してその結果をキューに入れるのに必要
※そのほかの設定内容は自身の環境に合わせて！
```conf:/etc/tcp.smtp
192.168.:allow,RELAYCLIENT=""
127.:allow,RELAYCLIENT=""
:allow,QMAILQUEUE="/var/qmail/bin/qmail-dkimverify"
```

##### tcpserver用の設定ファイルを反映
```shell:
sudo /usr/local/bin/tcprules /etc/tcp.smtp.cdb /etc/tcp.smtp.tmp < /etc/tcp.smtp
```

#### 「12.DKIM署名用の秘密鍵を保存するディレクトリを作成」
参考記事のままで問題ありませんでした

```shell:
sudo mkdir -p /usr/local/etc/domainkeys
sudo ln -s /usr/local/etc/domainkeys /etc/domainkeys
```

#### 「13.DKIM署名用の秘密鍵を作成するShellをダウンロード」
これも参照記事のとおりですが、自分の場合は以下のようにwgetで一旦作業ディレクトリへと必要なファイルをダウンロードしてから所定の場所へとそれをコピーする手順に変更しました

```shell:
cd /usr/local/src
cd qmail_dkim_scripts
/usr/local/bin/wget https://github.com/corokada/return-qmail/raw/master/dkim_keygen.sh
chmod +x ./dkim_keygen.sh
```

#### 「14.秘密鍵を作成」
これも参照記事のままで問題ないのでスクリプトを実行しDKIM署名用の秘密鍵を生成します

```shell:
sudo ./dkim_keygen.sh [ドメイン名]
```

以下のような感じでDNSサーバーにDKIM署名のレコードを追加するように指示されるのでそれを実行し、
```console:
example.domain.tld DNS records adding sample
default._domainkey	IN	TXT	"k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8…"
_adsp._domainkey	IN	TXT	"dkim=unknown"
```

あとは参考記事のとおりメールの送受信テストをすれば完了です