4 min read

メール鯖をDockerへ移行した

GitHub - docker-mailserver/docker-mailserver: Production-ready fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.) running inside a container.
Production-ready fullstack but simple mail server (SMTP, IMAP, LDAP, Antispam, Antivirus, etc.) running inside a container. - GitHub - docker-mailserver/docker-mailserver: Production-ready fullstac…

神か?

2024/06/09: v14版に更新(微修正、基本は変更なし)
2024/06/20: SPF, DKIM, DMARCの設定方法を追加

超参考

以前構築した記事

定義

  • Host: mail
  • Domain: sample.com
  • MailUser: user1
  • SSL/TLS証明書: /mnt/certificate/{cert,privkey}.pem
  • RelayHost: mail.so-net.ne.jp
  • RelayHostPort: 587
  • RelayHostUser: hogehoge@aa.so-net.ne.jp
  • RelayHostPass: hogegenoge

構築

DMS_GITHUB_URL='https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master'
wget "${DMS_GITHUB_URL}/docker-compose.yml"
wget "${DMS_GITHUB_URL}/mailserver.env"
wget "${DMS_GITHUB_URL}/setup.sh"
chmod a+x ./setup.sh

docker-compose.yml

version: '3.8'

services:
  mailserver:
    image: ghcr.io/docker-mailserver/docker-mailserver:latest
    container_name: mailserver
    hostname: mail.sample.com
    env_file: mailserver.env
    ports:
      - "25:25"    # SMTP  (explicit TLS => STARTTLS)
      #- "143:143"  # IMAP4 (explicit TLS => STARTTLS)
      - "465:465"  # ESMTP (implicit TLS)
      #- "587:587"  # ESMTP (explicit TLS => STARTTLS)
      - "993:993"  # IMAP4 (implicit TLS)
      #- "110:110"  # POP3
      - "995:995"  # POP3 (with TLS)
    volumes:
      - ./docker-data/dms/mail-data/:/var/mail/
      - ./docker-data/dms/mail-state/:/var/mail-state/
      - ./docker-data/dms/mail-logs/:/var/log/mail/
      - ./docker-data/dms/config/:/tmp/docker-mailserver/
      - /etc/localtime:/etc/localtime:ro
      - /mnt/certificate:/mnt/ssl:ro
    restart: always
    stop_grace_period: 1m
    cap_add:
      - NET_ADMIN
     healthcheck:
      test: "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1"
      timeout: 3s
      retries: 0
    logging:
      options:
        max-size: 1m
        max-file: '3'

diff mailserver.env

diff -u mailserver.env.orig mailserver.env | grep ^+
変更分のみ。

+POSTMASTER_ADDRESS=user1@mail.sample.com
+TZ=Asia/Tokyo
+ENABLE_POP3=1
+ENABLE_FAIL2BAN=1
+SSL_TYPE=manual
+SSL_CERT_PATH=/mnt/ssl/cert.pem
+SSL_KEY_PATH=/mnt/ssl/privkey.pem
+POSTFIX_INET_PROTOCOLS=ipv4
+DEFAULT_RELAY_HOST=[mail.so-net.ne.jp]:587
+RELAY_HOST=mail.so-net.ne.jp
+RELAY_PORT=587
+RELAY_USER=hogehoge@aa.so-net.ne.jp
+RELAY_PASSWORD=hogegenoge

ハマりポイント

Postfixの基本的な設定がわかっていればハマることはほとんどないと思う。
が、一点だけクソハマったので書き残しておく。

mail postfix/trivial-rewrite[1670]: warning: do not list domain mail.sample.com in BOTH mydestination and virtual_mailbox_domains
mail postfix/smtpd[1420]: NOQUEUE: reject: RCPT from unknown[{送信元Addr}]: 550 5.1.1 <user1@mail.sample.com>: Recipient address rejected: User unknown in local recipient table; from=<{送信元メアド}> to=<user1@mail.sample.com> proto=ESMTP helo=<{送信元メアド}>

メール送信時、上記のエラーが出て「えぇ?ユーザーいるじゃん???」という頭になってしまう。

エラーにも書いてある通り、mydestinationvirtual_mailbox_domains の中身が被っていることが原因らしい。

docker-data/dms/config/postfix-main.cf に以下を追記すればOK

mydestination = localhost.$mydomain, localhost

このことに関する参考はこちら
Postfixの『Recipient address rejected: User unknown in local recipient table』で15分ほどハマった話

LANからはすべてのアクセスを許可する

docker-data/dms/config/postfix-main.cf にPostfixの設定を書けばDocker内のPostfixの main.cf の対象部分を上書いてくれる。

なにもしなければ

mynetworks = 127.0.0.0/8 [::1]/128 [fe80::]/64 192.168.64.2/32

になるが、LANからすべて許可したいので

mynetworks = 127.0.0.0/8 [::1]/128 [fe80::]/64 192.168.64.2/32 10.0.0.0/24

を追記すればOK。

初回起動時

とりあえず上記のように設定だけ済ませる。
docker-compose up [-d]せずにまずはメールアドレスを追加する

# メアド追加
./setup.sh email add user1@mail.sample.com
# postmasterのalias追加
./setup.sh alias add postmaster@mail.sample.com user1@mail.sample.com

# メアド確認
./setup.sh email list
# alias確認
./setup.sh alias list

これをすることで user1@mail.sample.com が追加され、postmaster@mail.sample.com にきたものを前者に転送することができる。
./setup.sh alias add postmaster user1@mail.sample.com
というように@以降を省略もできるのだがうまく動作しなかったためフルで書くべき。
ちなみに postmasteremail add しなくて良い。(すると転送されない)

たったこれだけで完璧なメール鯖を構築できる。
Docker様様や。

おわり

これで無事に送受信ができることを確認した。
4時間もかかってしまった…

2024/06/20 追記

SPF, DKIM, DMARCについて追記
基本的には公式ドキュメントを見ればOK
ただサブドメインで運用する場合にはかなり躓きやすいので書いておく。

DKIM, DMARC & SPF - Docker Mailserver
A fullstack but simple mail-server (SMTP, IMAP, LDAP, Anti-spam, Anti-virus, etc.) using Docker.

SPF

SPFはFQDN単位で有効なため、今回のようにサブドメインで運用する場合は注意

$ dig mail.sample.com txt +short
"v=spf1 mx ~all"

また、今回のように RelayHost に mail.so-net.ne.jp を設定している場合、そこからメールが送信される扱いになるため以下のようにする。
なお mail.so-net.ne.jp のSPFレコードは so-net.ne.jp にあった。

$ dig mail.sample.com txt +short
"v=spf1 include:so-net.ne.jp ~all"

DKIM

DKIMもFQDN単位で有効なため注意

# 鍵作成
$ ./setup.sh config dkim

# 公開鍵確認
$ sudo cat docker-data/dms/config/opendkim/keys/mail.sample.com/mail.txt
mail._domainkey	IN	TXT	( "v=DKIM1; h=sha256; k=rsa; "
	  "p=abcde"
	  "fghijk" )  ; ----- DKIM key mail for mail.sample.com

# コンテナ再起動
# これをしないと送信されるメールに署名が付かない
$ docker compose down && docker compose up -d

# DNS設定
# わかりづらいが <selector>._domainkey.<FQDN> という感じ
# 設定する際は " を除去してBase64は空白を空けず繋げる
$ dig mail._domainkey.mail.sample.com txt +short
"v=DKIM1; h=sha256; k=rsa; p=abcdefghijk"

DMARC

DMARCはルートから継承される

$ dig _dmarc.sample.com txt +short
"v=DMARC1; p=none; sp=none; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc.report@mail.sample.com; ruf=mailto:dmarc.report@mail.sample.com"

メールの疎通を確認でき、厳しく取り扱ってほしくなったら
p=quarantine sp=quarantine
にする。