私的サーバー構築日誌：Postfix

# 能書き

[私的サーバー構築日誌：仕切り直し](https://qiita.com/katz_engineer/items/1f21de2bd9072c260ae6)からの自宅サーバー構築の続きです。

MTAをまともに（GoogleMailなどのガッチリした世界的大手メールサービスともやり取り出来るように）立てるのは非常に大変です。なのでここでは、非公開を前提としたLAN内限定MTAとします。

今回の設定で公開すると、息つく暇も無く迷惑メールの踏み台にされる＆メール爆弾の如く迷惑メールが届く筈ですので、絶対に公開しないで下さい。

そんな不自由なメールサーバーを立てる理由は、GitLabです。[前回も触れました](https://qiita.com/katz_engineer/items/0e6b647e9cc3c7c457e5#%E8%83%BD%E6%9B%B8%E3%81%8D)が。この後でGitLabサーバーを立てたいと考えていますが、そこでメールサーバーとの連携が必要になります。これが今のモチベーションになっています。

# 目標

- PostfixをDockerコンテナに立てる
    - 非公開を前提に、SMTP認証しない
    - 受信ユーザーが1人しかいない事を前提に、全ドメインの全宛先を受信する

# 参考文献

内容は、以前の記事の焼き直しです。これを元にして現在の設定に従った微調整をします。また、最後に纏めますが、受信できないケースがあります。これを許容範囲にするべく微調整しています。

- [自宅サーバー構築譚：DockerコンテナにPostfixをインストール / 全メール受信サーバー - Qiita](https://qiita.com/katz_engineer/items/8a4e09f6a2de374a5208)

Dovecotは下記の通りに設定済とします。

- [私的サーバー構築日誌：SSSDとDovecot - Qiita](https://qiita.com/katz_engineer/items/0e6b647e9cc3c7c457e5)

Dockerコンテナの置き場所についての、私の決め事です。

- [Dockerコンテナの置き場所 - 私的サーバー構築日誌：Dockerコンテナを自動起動する - Qiita](https://qiita.com/katz_engineer/items/24b7732cd13606c78ce2#docker%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E7%BD%AE%E3%81%8D%E5%A0%B4%E6%89%80)

インストール時に実行するシェルスクリプトは、こちらから拝借しました。

- [entrypoint.sh - UbuntuのDockerコンテナにPostfixをインストールする - Zenn](https://zenn.dev/flyingbarbarian/articles/5bb1d38b1ada40#entrypoint.sh)

# 準備

## Dockerコンテナ構築用ディレクトリ

[Dockerコンテナの置き場所として、ホストマシン管理者アカウントのhomeディレクトリにdocker-postfixサブディレクトリを作成します](https://qiita.com/katz_engineer/items/24b7732cd13606c78ce2#docker%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E7%BD%AE%E3%81%8D%E5%A0%B4%E6%89%80)。

```console:ホスト側
cd
mkdir docker-postfix
cd docker-postfix
```

## Postfix設定ファイル

上記のディレクトリの中にPostfix関連ファイルを準備します。

Postfixの設定```main.cf```はこんな感じにしました。

- ドメイン```example.com```は説明用の仮設定です。お金を出して買った自分だけのドメインに置き換えて下さい。
- LMTPのIPアドレス```172.16.1.2```とポート番号```24```は、[Dovecotで設定したLMTPサーバー](https://qiita.com/katz_engineer/items/0e6b647e9cc3c7c457e5#lxd%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E7%94%A8%E6%84%8F)に合わせて下さい。LXDコンテナではなくて、そのホストマシンを設定します。
- ユーザー```taro```は私の環境で唯一のメール受信ユーザーです。色んな宛先に来たメールを、すべてこのユーザーに転送します。

```console:ホスト側
SMTP_PORT=25
LMTP_PORT=24
HOST_IPADDR=172.16.1.2
MYUSER=taro
MYDOMAIN=example.com
```

```console:ホスト側
cat <<___ >main.cf
compatibility_level = 3.6

mydomain = $MYDOMAIN
myhostname = mail.\$mydomain
myorigin = \$mydomain

inet_protocols = ipv4
inet_interfaces = all

mydestination = home
local_recipient_maps =

mynetworks = 127.0.0.0/8

alias_maps = hash:/etc/aliases
mailbox_transport = lmtp:inet:$HOST_IPADDR:$LMTP_PORT

smtpd_banner = \$myhostname ESMTP
biff = no

readme_directory = no

virtual_alias_domains = regexp:/etc/virtual_domains
virtual_alias_maps = regexp:/etc/virtual_users

mailbox_size_limit = 0
recipient_delimiter = +
___
```

全ドメインの全ユーザーを受信する為に、Postfixの仮想ドメインを利用します。その設定は下記の通りです。```home```以外のドメイン宛に来たメールは、全て```$MYUSER```に転送します。

後で確認してみますが、設定の都合で```home```ドメインだけは不在ユーザーに届きません。```home```ドメインなら構いませんよね。

```console:ホスト側
cat <<___ >virtual_domains
!/^home\$/ OK
___

cat <<___ >virtual_users
!/^.*@home\$/ $MYUSER@home
___
```

## Dockerコンテナ構築

```docker-compose.yml```ファイルはこうなります。

```console:ホスト側
cat <<___ >docker-compose.yml
version: '3.8'
services:
  postfix1:
    build: .
    volumes:
      - ./main.cf:/etc/postfix/main.cf
      - ./virtual_domains:/etc/virtual_domains
      - ./virtual_users:/etc/virtual_users
    ports:
      - "$SMTP_PORT:$SMTP_PORT"
    restart: unless-stopped
___
```

そして```Dockerfile```です。

```console:ホスト側
cat <<___ >Dockerfile
FROM ubuntu:22.04
RUN apt update && apt upgrade -y && \
 DEBIAN_FRONTEND=noninteractive apt install postfix -y
COPY entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]
___
```

最後に、起動時に実行される```entrypoint.sh```です。下記のようになります。内容の詳細は[参考文献](https://zenn.dev/flyingbarbarian/articles/5bb1d38b1ada40#entrypoint.sh)を参照ください。

```console:ホスト側
cat <<___ >entrypoint.sh
#!/bin/bash
postfix start
postmap /etc/virtual_domains
postmap /etc/virtual_users
cp /etc/resolv.conf /var/spool/postfix/etc/resolv.conf
postfix reload
tail -f /dev/null
___
chmod +x entrypoint.sh
```

Dockerコンテナを構築します。特別な事は何もありません。

```console:ホスト側
docker compose up -d
```

# 動作確認

## メール受信ユーザー宛

私の環境で唯一のメール受信ユーザー```taro```にメールしてみます。

```console:ホスト側
nc -C $HOST_IPADDR $SMTP_PORT <<___
ehlo sender.home
mail from: ubuntu@test.corp
rcpt to: $MYUSER@home
data
From: ubuntu@test.corp
To: $MYUSER@home
Subject: test

hello, $MYUSER
$(date +"%Y/%m/%d %H:%M:%S")
.
quit
___
```

確認はIMAPを使わず、[LXDコンテナ```dovecot1```](https://qiita.com/katz_engineer/items/0e6b647e9cc3c7c457e5#lxd%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E7%94%A8%E6%84%8F)の```/home/taro/Maildir```を直接覗いてみます。「最新メール1通の確認」を簡単に自動化できるので。

新着メールは```Maildir/new```ディレクトリに置かれる筈です。

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

```console:ホスト側
lxc shell $CONTAINER
```

```console:LXDコンテナ内
cd /home/taro/Maildir/new
ls -t | head -n1 | xargs cat
exit
```

実行結果は下記のような感じになります。きちんと届いてますね。

```console:LXDコンテナ内
# cd /home/taro/Maildir/new
# ls -t | head -n1 | xargs cat
Return-Path: <ubuntu@test.corp>
Delivered-To: taro@home
Received: from mail.example.com ([192.168.0.1])
        by dovecot1 with LMTP
        id P6GfBFZyVGWMIAAA9sUnBg
        (envelope-from <ubuntu@test.corp>)
        for <taro@home>; Wed, 15 Nov 2023 16:25:10 +0900
Received: from sender.home (primary [172.16.1.2])
        by mail.example.com (Postfix) with ESMTP id 0BF5558C
        for <taro@home>; Wed, 15 Nov 2023 07:25:10 +0000 (UTC)
From: ubuntu@test.corp
To: taro@home
Subject: test

hello, taro
2023/11/15 16:25:09
# exit
logout
```

## 私のドメインのメール受信ユーザー宛

私のドメイン```$MYDOMAIN```の、メール受信ユーザー```taro```に宛ててメールしてみます。

```console:ホスト側
nc -C $HOST_IPADDR $SMTP_PORT <<___
ehlo sender.home
mail from: ubuntu@test.corp
rcpt to: $MYUSER@$MYDOMAIN
data
From: ubuntu@test.corp
To: $MYUSER@$MYDOMAIN
Subject: test

hello, $MYUSER
$(date +"%Y/%m/%d %H:%M:%S")
.
quit
___
```

メール内容確認結果。設定どおり```$MYUSER@home```に転送されて届いてます。

```console:LXDコンテナ内
# cd /home/taro/Maildir/new
# ls -t | head -n1 | xargs cat
Return-Path: <ubuntu@test.corp>
Delivered-To: taro@home
Received: from mail.example.com ([192.168.0.1])
        by dovecot1 with LMTP
        id hdjnAw53VGXGIAAA9sUnBg
        (envelope-from <ubuntu@test.corp>)
        for <taro@home>; Wed, 15 Nov 2023 16:45:18 +0900
Received: from sender.home (primary [172.16.1.2])
        by mail.example.com (Postfix) with ESMTP id EFDFB6A4
        for <taro@example.com>; Wed, 15 Nov 2023 07:45:17 +0000 (UTC)
From: ubuntu@test.corp
To: taro@example.com
Subject: test

hello, taro
2023/11/15 16:45:17
# exit
logout
```

## 知らないドメインの知らないユーザー宛

知らないドメイン```unknown.corp```の知らないユーザー```hanako```に宛ててメールしてみます。

```console:ホスト側
nc -C $HOST_IPADDR $SMTP_PORT <<___
ehlo sender.home
mail from: ubuntu@test.corp
rcpt to: hanako@unknown.corp
data
From: ubuntu@test.corp
To: hanako@unknown.corp
Subject: test

hello, hanako
$(date +"%Y/%m/%d %H:%M:%S")
.
quit
___
```

設定どおり、きちんと```$MYUSER@home```に転送されてます。

```console:LXDコンテナ内
# cd /home/taro/Maildir/new
# ls -t | head -n1 | xargs cat
Return-Path: <ubuntu@test.corp>
Delivered-To: taro@home
Received: from mail.example.com ([192.168.0.1])
        by dovecot1 with LMTP
        id anzxOil4VGXiIAAA9sUnBg
        (envelope-from <ubuntu@test.corp>)
        for <taro@home>; Wed, 15 Nov 2023 16:50:01 +0900
Received: from sender.home (primary [172.16.1.2])
        by mail.example.com (Postfix) with ESMTP id DCAB075A
        for <hanako@unknown.corp>; Wed, 15 Nov 2023 07:50:01 +0000 (UTC)
From: ubuntu@test.corp
To: hanako@unknown.corp
Subject: test

hello, hanako
2023/11/15 16:50:01
# exit
logout
```

## 私のドメインの存在しないユーザー宛

私のドメイン```$MYDOMAIN```の存在しないユーザー```hanako```に宛ててメールしてみます。

```console:ホスト側
nc -C $HOST_IPADDR $SMTP_PORT <<___
ehlo sender.home
mail from: ubuntu@test.corp
rcpt to: hanako@$MYDOMAIN
data
From: ubuntu@test.corp
To: hanako@$MYDOMAIN
Subject: test

hello, hanako
$(date +"%Y/%m/%d %H:%M:%S")
.
quit
___
```

これも設定どおり、きちんと```$MYUSER@home```に転送されてます。

```console:LXDコンテナ内
# cd /home/taro/Maildir/new
# ls -t | head -n1 | xargs cat
Return-Path: <ubuntu@test.corp>
Delivered-To: taro@home
Received: from mail.example.com ([192.168.0.1])
        by dovecot1 with LMTP
        id S3AjJzp5VGX+IAAA9sUnBg
        (envelope-from <ubuntu@test.corp>)
        for <taro@home>; Wed, 15 Nov 2023 16:54:34 +0900
Received: from sender.home (primary [172.16.1.2])
        by mail.example.com (Postfix) with ESMTP id 8A93345E
        for <hanako@example.com>; Wed, 15 Nov 2023 07:54:34 +0000 (UTC)
From: ubuntu@test.corp
To: hanako@example.com
Subject: test

hello, hanako
2023/11/15 16:54:34
# exit
logout
```

## homeドメインの存在しないユーザー宛

存在しないユーザー```hanako```に、```home```ドメインへメールしてみます。

```console:ホスト側
nc -C $HOST_IPADDR $SMTP_PORT <<___
ehlo sender.home
mail from: ubuntu@test.corp
rcpt to: hanako@home
data
From: ubuntu@test.corp
To: hanako@home
Subject: test

hello, hanako
$(date +"%Y/%m/%d %H:%M:%S")
.
quit
___
```

唯一この場合だけメールのパターンが変わります。

転送せずに直接受信しようとしてユーザーが居ない
→エラーメールを返信
→エラーメールが知らないユーザー宛なので```$MYUSER@home```に転送
→```$MYUSER```がエラーメールを受信

```console:LXDコンテナ内
# cd /home/taro/Maildir/new
# ls -t | head -n1 | xargs cat
Return-Path: <>
Delivered-To: taro@home
Received: from mail.example.com ([192.168.0.1])
        by dovecot1 with LMTP
        id MJbZD1t6VGUaIQAA9sUnBg
        (envelope-from <>)
        for <taro@home>; Wed, 15 Nov 2023 16:59:23 +0900
Received: by mail.example.com (Postfix)
        id 3EB07760; Wed, 15 Nov 2023 07:59:23 +0000 (UTC)
Date: Wed, 15 Nov 2023 07:59:23 +0000 (UTC)
From: MAILER-DAEMON@example.com (Mail Delivery System)
Subject: Undelivered Mail Returned to Sender
To: ubuntu@test.corp
Auto-Submitted: auto-replied
MIME-Version: 1.0
Content-Type: multipart/report; report-type=delivery-status;
        boundary="22EBB75C.1700035163/mail.example.com"
Content-Transfer-Encoding: 8bit
Message-Id: <20231115075923.3EB07760@mail.example.com>

This is a MIME-encapsulated message.

--22EBB75C.1700035163/mail.example.com
Content-Description: Notification
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 8bit

This is the mail system at host mail.example.com.

I'm sorry to have to inform you that your message could not
be delivered to one or more recipients. It's attached below.

For further assistance, please send mail to postmaster.

If you do so, please include this problem report. You can
delete your own text from the attached returned message.

                   The mail system

<hanako@home>: host 172.16.1.2[172.16.1.2] said: 550 5.1.1 <hanako@home> User
    doesn't exist: hanako@home (in reply to RCPT TO command)

--22EBB75C.1700035163/mail.example.com
Content-Description: Delivery report
Content-Type: message/delivery-status

Reporting-MTA: dns; mail.example.com
X-Postfix-Queue-ID: 22EBB75C
X-Postfix-Sender: rfc822; ubuntu@test.corp
Arrival-Date: Wed, 15 Nov 2023 07:59:23 +0000 (UTC)

Final-Recipient: rfc822; hanako@home
Original-Recipient: rfc822;hanako@home
Action: failed
Status: 5.1.1
Remote-MTA: dns; 172.16.1.2
Diagnostic-Code: smtp; 550 5.1.1 <hanako@home> User doesn't exist: hanako@home

--22EBB75C.1700035163/mail.example.com
Content-Description: Undelivered Message
Content-Type: message/rfc822
Content-Transfer-Encoding: 8bit

Return-Path: <ubuntu@test.corp>
Received: from sender.home (primary [172.16.1.2])
        by mail.example.com (Postfix) with ESMTP id 22EBB75C
        for <hanako@home>; Wed, 15 Nov 2023 07:59:23 +0000 (UTC)
From: ubuntu@test.corp
To: hanako@home
Subject: test

hello, hanako
2023/11/15 16:59:23

--22EBB75C.1700035163/mail.example.com--
# exit
logout
```

# 仕舞い

「```home```ドメイン以外なら全ドメインの全ユーザー宛メールを受信」するメールサーバーMTAを設定できました！
```home```ドメインは諦めましょう。たとえ家庭内限定だとしても```home```ドメインでメールアドレスを作ってはいけない、という事で一つ御容赦ください。