Docker-composeを使いMailuメールサーバをインストールする

2022年9月28日

Mailuはメール送受信サーバで、かつウェブGUIによる操作機能もついているオールインワンメールサーバといえる。もちろん、Thunderbird等のメールクライアントのPOP/IMAPプロトコルにも対応している。これをDockerを使ってインストールしてみることにする。

現在のバージョンは1.9のようだ

前提条件

例えば、example.comというドメイン用のメールサーバを立ち上げる。メールアドレス自体はme@example.comなどだが、メールサーバのドメインはmail.example.comとする。当然だが、example.comのウェブサイトをサービスするサーバと、メールサーバmail.example.comをサービスするサーバは別でも良いし、同じでもよい。

Thunderbird等のメールクライアントを使う場合には、mail.example.comに接続することになるし、mailu付属のウェブメーラを使う場合にもmail.example.comというサーバ上で稼働することになる。

ドメインの設定

このメールサーバのIPアドレスを123.123.123.123としてみると、例えばDNSには、少なくとも以下の設定が必要になる。

  • MXレコード:mail.example.com
  • TXTレコード:v=spf1 ip4:123.123.123.123 ~all
  • Aレコード:mail=123.123.123.123

この意味としては、example.comのメールサーバのドメインがmail.example.comで、そのIPアドレスが123.123.123.123ということ。仮にexample.comやwww.example.comのウェブサイトを作るとしたら、そのサーバは別のものでもよいし、IPアドレスは別になる。例えば、222.222.222.222など。

実際のメール関係の設定は以下である。

docker-compose.ymlの作成

mailuのセットアップもdocker-compose.ymlで行うが、これは手書きするものではなく、以下のウィザード形式のページで自動生成してくれる。質問に答えれば、適切なdocker-compose.ymlを勝手に作ってくれるということだ。

https://setup.mailu.io/1.9/

項目 説明
Version 1.9 1.9、masterと選択できるが、masterは安定版ではない
STEP 1
flavor Compose クラスターを作るわけではないのでCompose
STEP 2
Mailu storage path /opt/docker/mailu Mailuシステムをメールサーバ内のどこに格納するかを指定
Main mail domain and server display name example.com 例えばメアドadmin@example.comのexample.com部分
Postmaster local part admin ポストマスターのメアド
handle security letsencrypt ※1
他の設定 デフォルトのまま
Website name example おそらくウェブメール画面に表示される文言
Linked Website URL https://example.com メインとなるウェブサイト
Enabled the admin UI yes ウェブメールの管理者UI
path to the admin U /admin 管理者UIパス
STEP 3
Enable Web email client rainloop roundcubeかrainloopのどちらでもよい
path to the Web email client /webmail
antivirus service no お好み。どういうサービスを提供してくれるのか、使ってないので不明
webdav servbice no お好み。メールアカウントを使って連絡帳やカレンダーを使えるのだそうだが、NextCloudで運用しているので不要
fetchmail no おそらく他のメールサーバからメールをこのサーバにとってくる機能と思われる
STEP 4
IPV4 listen adddress 123.123.123.123 メールサーバのIPアドレス
Subnet of the docker network 172.30.0.0/24 ※2
Enable IPV6 no
Enable an internal DNS resolver yes 意味が良くわからないがnoにすると警告が出る
Public hostnames mail.example.com カンマ区切りで複数書けるが必要無し
Database sqlite どういう用途でデータベースを使うのかわからないが、軽量のもの

※1:TLS certificatesに解説がある。

※2:他とかち合わなければ良いので、デフォルトの192.168等のアドレスでも良いと思われる。

Setup MailUボタンをクリックすると、コンフィギュレーションが生成されたとのメッセージになる。

この二つのファイルを/opt/docker/mailuにダウンロードする。

docker-compose.ymlの実行と管理者メアドの作成

/opt/docker/mailuディレクトリにうつり、docker-compose up -dすればよいのだが、ただし、その前に後述する「ポートの競合解決」を読んでほしい。

その後で、管理者メアドを作成するのだが、以下の大文字の部分が実際の管理者メアド名とパスワードになる。

docker-compose exec admin flask mailu admin ADMIN example.com 'PASSWORD'

ポートの競合解決

webmailを選択した場合には、このメールサーバのhttpsポート443番でウェブメール画面にアクセスできるようになるはずだ。そのために、mailuはホストポートの80,443番を専有しようとする。

しかし、私の場合はhttps-portal/Traefik等リバースプロキシを使用して、既に80,443を使用しているため、https-portalとmailuのwebmailの同時使用はできない。どうも、mailuはこういったケースを想定していないようで、必ず80,443番を掴んでしまうようなdocker-compose.ymlが作成されてしまうようだ(すべてのケースについて試したわけではない)。

この解決策は、いくつか考えられる。

webmail側のポートを8443等に変更する。80番は削除する。

※この解決策は中途半端だった。問題点を最後に書く。

docker-compose.ymlを以下のように変更する。特にIPアドレスを明示する必要はないと思う。

    ports:
      - "123.123.123.123:80:80"
      - "123.123.123.123:8443:443"
      - "123.123.123.123:25:25"
    ports:
      - "80:80"
      - "8443:443"
      - "25:25"

いったん、リバースプロキシを停止して、mailuを起動する。これが必要な理由は、LetsEncrypt側からのドメインの存在の問い合わせが、80に来るからである。ここが確実に開いていないと、LetsEncryptのCERT取得が必ず失敗してしまう。

おそらくいったん取得すれば不要になるので、以下のように80番を削除し、https-portalを再起動すればよいと思う。

    ports:
      - "80:80" " #削除
      - "8443:443"
      - "25:25"

そして、webmail画面は「https://mail.example.com:8443」でアクセスすることになる。

LetsEncryptのCERT取得に失敗した場合は、以下のようなログが現れる。

front_1     | Requesting a certificate for mail.example.com
front_1     | 
front_1     | Certbot failed to authenticate some domains (authenticator: standalone). The Certificate Authority reported these problems:
front_1     |   Domain: mail.example.com
front_1     |   Type:   connection
front_1     |   Detail: 123.123.123.123: Fetching http://mail.example.com/.well-known/acme-challenge/8ytx29WfDyC_9lfNrskI_PU0Zln***************: Error getting validation data
front_1     | 
front_1     | Hint: The Certificate Authority couldn't exterally verify that the standalone plugin completed the required http-01 challenges. Ensure the plugin is configured correctly and that the changes it makes are accessible from the internet.
front_1     | 

※Let’s EncryptのCERTが期限切れになった場合、mailuは自動的に更新を行うが、その際にも80番が空いていなくてはならない。だから、更新の際にもリバースプロキシを停止しないといけない。

https-portalにhttps機能を任せる

以下はうまく行かなかった。画面が真っ白になってしまい、何のアクセスもできなかったのだが、一応手順を書いておく。

mailu.envを以下のように変更する。つまり、mailu側ではwebmail画面のSSL保護は行わない。

# Choose how secure connections will behave (value: letsencrypt, cert, notls, mail, mail-letsencrypt)
TLS_FLAVOR=mail-letsencrypt

※良く考えてみると、この場合でも、mailu自身で80番ポートが空ける必要があると思われる。何にしてもCERTが必要だからである。

docker-compose.ymlから80, 443を削除する。

    ports:
      - "123.123.123.123:80:80" # これは削除
      - "123.123.123.123:443:443" # これも削除
      - "123.123.123.123:25:25"

あとは、https-portalの設定に以下のように記述する。

version: '3'

services:
  https-portal:
    image: steveltn/https-portal:1
    container_name: https-portal
    ports:
      - '80:80'
      - '443:443'
    restart: always
    volumes:
      - ./ssl_certs:/var/lib/https-portal
    networks:
      - default
      - mailu_default
    environment:
      DOMAINS: >-
        example.com -> http://wp:80
        ,nodebb.example.com -> http://nodebb:4567
        ,mail.example.com -> http://webmail:80
      STAGE: 'production' # Don't use production until staging works

networks:
  mailu_default:
    external: true
  default:
    external:
      name: common-network

common-networkは、複数のdocker-compose.ymlで起動したコンテナ間で共有しているネットワークで、これとは別にmailuのネットワークを指定してある。