自宅サーバー構築譚：LXDコンテナにDovecotをインストール / LMTP

# 能書き

[自宅サーバー構築譚：基本構想](https://qiita.com/katz_engineer/items/cb592be44daa28a51d9b)に基づく自宅サーバー構築、[Ubuntu22.04LTSインストールその2](https://qiita.com/katz_engineer/items/033471b69143bf76650e)の続きです。

自宅サーバーの華と言えばメールサーバーでしょう！というのは私の個人的な考えですが、皆様はいかがでしょうか。

[LXD](https://qiita.com/katz_engineer/items/74d7b357750e51e9c61c)と[Docker](https://qiita.com/katz_engineer/items/f3d6ab54e5db711b03ab)を準備している今日この頃の私ですが、メールサーバーをそのコンテナに構築します。

メールサーバーと言えばMRAであるDovecotとMTAであるPostfixです。私の構想では、Dovecot
をLXDコンテナに構築してLDAを兼ねさせて、PostfixはDockerコンテナに構築して連携させようと考えています。

DovecotをDockerではなくてLXDコンテナで構築する意味ですが。

- 受信メールをコンテナ内に蓄積できます。Dockerコンテナではコンテナ内へのデータ蓄積は不向きです。ホスト側のディレクトリに保存させる手段もありますけども、LXDコンテナならそのままコンテナ内に蓄積できます。
- 蓄積したメールのバックアップコピーを作ろうとした場合、LXDコンテナ内にメールがあれば、コンテナをエクスポートする事でDovecotの設定ごとバックアップできます。
- 将来はDRBDも入れて、リアルタイムでミラーリングして「絶対に喪失しないメールシステム」を実現したい。こういった複数ソフトのインストールにはDockerは不向きでしょう。

以上のような理由で、DovecotはLXDコンテナに入れます。

# 目標

構築の手順は動作確認の都合を考えて、下記の順序で構築する予定です。普通の構築手順とはちょっと異なりますな。

1. DovecotのLMTP
1. DovecotのIMAP
1. Postfix

今回は、[LXDコンテナ](https://qiita.com/katz_engineer/items/74d7b357750e51e9c61c)にDovecotをインストールして、LMTPサーバを構築します。

前提として[cloud-initによるいつもの初期設定の手順を確立済](https://qiita.com/katz_engineer/items/7ac5cadce3bdde9c76dc)とします。内容は`/etc`のSubversion管理ですので、無くても良いんですが。私の拘りです。

# 参考文献

- [Postfix and Dovecot LMTP - DOVECOT Navigation](https://doc.dovecot.org/configuration_manual/howto/postfix_dovecot_lmtp/)
- [11.6. LMTP ソケットと LMTPS リスナーの設定 - 第11章 Dovecot IMAP および POP3 サーバーの設定と管理 - Red Hat Enterprise Linux 8 にさまざまな種類のサーバーをデプロイするためのガイド](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/deploying_different_types_of_servers/configuring-an-lmtp-socket-and-lmtps-listener_configuring-and-maintaining-a-dovecot-imap-and-pop3-server)
- [自宅サーバー構築譚：LXD - Qiita](https://qiita.com/katz_engineer/items/74d7b357750e51e9c61c)
- [自宅サーバー構築譚：LXDでcloud-initによる初期設定の自動化 - Qiita](https://qiita.com/katz_engineer/items/7ac5cadce3bdde9c76dc)

# コンテナ構築

コンテナ名とそのIPアドレスは何度か参照するので、環境変数に設定しておきます。

```console:ホスト側
CONTAINER=dovecot1
IPADDRESS=192.168.0.4
```

コンテナを構築します。

```console:ホスト側
lxc init images:ubuntu/jammy/cloud $CONTAINER --device eth0,ipv4.address=$IPADDRESS
lxc config set $CONTAINER cloud-init.user-data="$(cat <<___
#cloud-config
timezone: Asia/Tokyo
locale: ja_JP.utf8
package_upgrade: true
packages:
  - subversion
runcmd:
  - [mkdir, /etc/.svn_repo]
  - [svnadmin, create, /etc/.svn_repo/]
  - [mkdir, /tmp/etc]
  - [svn, import, /tmp/etc, file:///etc/.svn_repo/, -m, create project]
  - [rmdir, /tmp/etc]
  - [svn, co, file:///etc/.svn_repo/, /etc]
  - rm -v /etc/ssl/certs/NetLock*
  - [/bin/bash, -c, ls /etc/ssl/certs/*.0 | while read f; do echo -n \$f\$'\\t'; readlink \$f; done | grep NetLock | cut -f 1 | xargs -rt rm]
  - [sed, -i, -e, /NetLock/d, /etc/ca-certificates.conf.dpkg-old, /etc/ca-certificates.conf]
  - svn add /etc/*
  - [/bin/bash, -c, ls /etc/*- | xargs -rt svn revert]
  - [svn, ci, /etc, -m, first commit]
  - [svn, up, /etc]
  - /bin/bash -c "cd /etc; svn propset svn:ignore -F <(svn st | cut -b9-) ."
  - [svn, ci, /etc, -m, set svn:ignore]
___
)"
lxc start $CONTAINER
lxc exec $CONTAINER -- cloud-init status --wait
```

# Dovecot LMTP をインストール

コンテナに入ります。

```console:ホスト側
lxc exec $CONTAINER /bin/bash
```

コンテナの中で、今回は`dovecot-lmtpd`だけをインストールします。

```console:コンテナ内
apt install -y dovecot-lmtpd
```

```console:コンテナ内
svn st /etc | grep "^?" | cut -b9- | xargs -I{} sudo find {} -type f -or -type d -or -type l | xargs -t svn add
svn ci /etc -m "install dovecot"
```

Dovecotの設定状態を見るには`doveconf`コマンドを使います。

```console:コンテナ内
doveconf | grep protocols
```

`protocols`をチェックすると`lmtp`だけが設定されています。

```console:コンテナ内
# doveconf | grep protocols
protocols = " lmtp"
```

ここまで出来たら、コンテナから抜けます。

```console:コンテナ内
exit
```

# Dovecot LMTP の設定

設定は、ホスト側で`patch`コマンドを使用して一気にやります。

まずは必要な設定ファイルを取り出します。

```console:ホスト側
cd
mkdir etc
lxc file pull $CONTAINER/etc/dovecot/conf.d/{10-auth.conf,10-logging.conf,10-mail.conf,10-master.conf} etc
```

パッチを用意します。もしかするとエラーが発生するかも知れないので、ファイルの形にしておきます。

LMTPのポートは流儀によって様々のようですね。私は24番にしてみました。

```console:ホスト側
LMTP_PORT=24
```

```console:ホスト側
cat >lmtp.patch <<___
Index: /etc/dovecot/conf.d/10-auth.conf
===================================================================
--- /etc/dovecot/conf.d/10-auth.conf    (revision 3)
+++ /etc/dovecot/conf.d/10-auth.conf    (working copy)
@@ -48,7 +48,7 @@
 # the standard variables here, eg. %Lu would lowercase the username, %n would
 # drop away the domain if it was given, or "%n-AT-%d" would change the '@' into
 # "-AT-". This translation is done after auth_username_translation changes.
-#auth_username_format = %Lu
+auth_username_format = %Ln

 # If you want to allow master users to log in by specifying the master
 # username within the normal username string (ie. not using SASL mechanism's
Index: /etc/dovecot/conf.d/10-logging.conf
===================================================================
--- /etc/dovecot/conf.d/10-logging.conf (revision 3)
+++ /etc/dovecot/conf.d/10-logging.conf (working copy)
@@ -4,7 +4,7 @@

 # Log file to use for error messages. "syslog" logs to syslog,
 # /dev/stderr logs to stderr.
-#log_path = syslog
+log_path = syslog

 # Log file to use for informational messages. Defaults to log_path.
 #info_log_path =
@@ -14,7 +14,7 @@
 # Syslog facility to use if you're logging to syslog. Usually if you don't
 # want to use "mail", you'll use local0..local7. Also other standard
 # facilities are supported.
-#syslog_facility = mail
+syslog_facility = mail

 ##
 ## Logging verbosity and debugging.
Index: /etc/dovecot/conf.d/10-mail.conf
===================================================================
--- /etc/dovecot/conf.d/10-mail.conf    (revision 3)
+++ /etc/dovecot/conf.d/10-mail.conf    (working copy)
@@ -27,7 +27,7 @@
 #
 # <doc/wiki/MailLocation.txt>
 #
-mail_location = mbox:~/mail:INBOX=/var/mail/%u
+mail_location = maildir:~/Maildir

 # If you need to set multiple mailbox locations or want to change default
 # namespace settings, you can do it by defining namespace sections.
Index: /etc/dovecot/conf.d/10-master.conf
===================================================================
--- /etc/dovecot/conf.d/10-master.conf  (revision 3)
+++ /etc/dovecot/conf.d/10-master.conf  (working copy)
@@ -52,16 +52,16 @@
 }

 service lmtp {
-  unix_listener lmtp {
+  #unix_listener lmtp {
     #mode = 0666
-  }
+  #}

   # Create inet listener only if you can't use the above UNIX socket
-  #inet_listener lmtp {
+  inet_listener lmtp {
     # Avoid making LMTP visible for the entire internet
     #address =
-    #port =
-  #}
+    port = $LMTP_PORT
+  }
 }

 service imap {
___
```

これを適用します。

```console:ホスト側
patch -d etc <lmtp.patch
```

もしかしたら、警告とエラーが表示されるかも知れません。パッチをブラウザからのコピペで作成すると調子が悪いようです。

```console:ホスト側
$ patch -d etc <lmtp.patch
patching file 10-auth.conf
patching file 10-logging.conf
Hunk #1 succeeded at 4 with fuzz 1.
patching file 10-mail.conf
patching file 10-master.conf
Hunk #1 FAILED at 52.
1 out of 1 hunk FAILED -- saving rejects to file 10-master.conf.rej
```

この場合、今回は`--ignore-whitespace`オプションを付ければ解決しました。

```console:ホスト側
patch --ignore-whitespace etc/10-master.conf <etc/10-master.conf.rej
```

エラーが出なければ成功です。

```console:ホスト側
$ patch --ignore-whitespace etc/10-master.conf <etc/10-master.conf.rej
patching file etc/10-master.conf
```

エラー関連で作成された余計なファイルは削除しましょう。

```console:ホスト側
rm etc/*.orig etc/*.rej
```

パッチを当てた設定ファイルをコンテナ内に戻します。

```console:ホスト側
lxc file push etc/* $CONTAINER/etc/dovecot/conf.d/
```

Dovecotに設定を読み込ませます。

```console:ホスト側
lxc exec $CONTAINER systemctl reload dovecot
```

# コンテナのネットワークの設定

現在のネットワークの状態を確認してみましょう。

```console:ホスト側
lxc network list-leases lxdbr0
```

今の状態ならこんな風に表示されるでしょう。コンテナを既に色々弄っていたら、違う表示になるかも知れません。

```console:ホスト側
$ lxc network list-leases lxdbr0
+-----------+-------------------+-------------+---------+
| HOSTNAME  |    MAC ADDRESS    | IP ADDRESS  |  TYPE   |
+-----------+-------------------+-------------+---------+
| dovecot1  | 00:16:3e:24:50:52 | 192.168.0.4 | STATIC  |
+-----------+-------------------+-------------+---------+
| lxdbr0.gw |                   | 192.168.0.1 | GATEWAY |
+-----------+-------------------+-------------+---------+
```

コンテナのNATを設定します。`172.16.1.3`は、私のサーバー（LXDコンテナのホストになっているマシン）のIPアドレスです。

```console:ホスト側
lxc network forward port add lxdbr0 172.16.1.3 tcp $LMTP_PORT $IPADDRESS $LMTP_PORT
```

# 確認

動作確認ですが。コンテナにはデフォルトでubuntuユーザーが居るらしいので、test@sender.homeからubuntu宛にメールを出してみます。

`nc`を使用してLMTPコマンドを直接流し込みます。`telnet`も使えるようですので、ハッカー気分を味わいたいならそちらでも良いでしょう。

```console:ホスト側
nc -C 172.16.1.3 $LMTP_PORT <<___
lhlo sender.home
mail from:<test@sender.home>
rcpt to:<ubuntu@secondary.home>
data
From: test@sender.home
To: ubuntu@secondary.home
Subject: test mail

hello, world
.
quit
___
```

こんな表示になります。

```console:ホスト側
$ nc -C 172.16.1.3 $LMTP_PORT <<___
> lhlo sender.home
> mail from:<test@sender.home>
> rcpt to:<ubuntu@secondary.home>
> data
> From: test@sender.home
> To: ubuntu@secondary.home
> Subject: test mail
>
> hello, world
> .
> quit
> ___
220 dovecot1 Dovecot (Ubuntu) ready.
250-dovecot1
250-8BITMIME
250-CHUNKING
250-ENHANCEDSTATUSCODES
250-PIPELINING
250 STARTTLS
250 2.1.0 OK
250 2.1.5 OK
354 OK
250 2.0.0 <ubuntu@secondary.home> YCN9CcXQcWQbEQAA9sUnBg Saved
221 2.0.0 Bye
```

設定が正しいならば`/home/ubuntu`にメールが届いている筈です。

```console:ホスト側
lxc exec $CONTAINER ls /home/ubuntu/Maildir/new | xargs -rI{} lxc file pull $CONTAINER/home/ubuntu/Maildir/new/{} -
```

こんな風に表示されれば成功です。

```console:ホスト側
$ lxc exec $CONTAINER ls /home/ubuntu/Maildir/new | xargs -rI{} lxc file pull $CONTAINER/home/ubuntu/Maildir/new/{} -
Return-Path: <test@sender.home>
Delivered-To: ubuntu
Received: from sender.home ([172.16.1.3])
        by dovecot2 with LMTP
        id nqQyA4P7TWSiEAAAdVeHhA
        (envelope-from <test@sender.home>)
        for <ubuntu>; Sun, 30 Apr 2023 14:24:19 +0900
From: test@sender.home
To: ubuntu@secondary.home
Subject: test mail

hello, world
```

# 仕舞い

コンテナのDovecot設定を、Subversionに登録しましょう。

```console:ホスト側
lxc exec $CONTAINER /bin/bash
```

既存ファイルの修正だけで、新規作成ファイルはありません。

```console:コンテナ内
svn st /etc
```

確認結果。

```console:コンテナ内
# svn st /etc
M       /etc/dovecot/conf.d/10-auth.conf
M       /etc/dovecot/conf.d/10-logging.conf
M       /etc/dovecot/conf.d/10-mail.conf
M       /etc/dovecot/conf.d/10-master.conf
```

なのでcommitするだけです。

```console:コンテナ内
svn ci -m"Dovecot LMTP setting" /etc
exit
```

これで、LMTPでローカル配送できるようになりました。やったね:thumbsup_tone1: