はじめに

Linux環境での「Postfix + Dovecot + Postfix Admin + MySQL(MariaDB)」はあちらこちらに記事がありますが、DBがPostgreSQLの場合には殆ど記事が見つかりません。MySQL(MariaDB)はWebサーバに必須なDBなので当然とも言えるのですが、個人的にはPostgreSQLが好みなので、今回はPostgreSQLを用いて環境を構築してみました。FreeBSD 14.1環境で確認しております。

大前提

WebサーバでPHPスクリプトが動く環境がすでにあることとします。PHPのバージョンはそれほどクリティカルではないと思いますが、今回は「PHP 8.3」で動作確認しています。WebサーバはPHPスクリプトを実行できるように設定されているとします。

前提

今回構築する環境は、以下を想定しています。

  • メールは、Dovecot独自のmdbox形式
  • メールの保管場所は、「/var/spool/vmail/メールドメイン/ユーザ名/」配下に格納
  • 「vmail」をOSのユーザとして作成し、メールのアクセスはこのユーザが行う(一般ユーザは「/var/spool/vmail/」以下のフォルダにはアクセスできない)
  • DovecotとPostfixのユーザ認証はDovecotが一括して行う(PostfixはDovecotにユーザ認証を任せる)
  • メールアカウントとOSのユーザは切り離す
  • OSのユーザ(例えば「root」など)がメールを受信する必要がある場合は、Postfix Adminで「user@サーバのドメイン」として「メールアカウントを作成」もしくは「実ユーザに転送」とする(ローカル配送は一切停止)
  • ユーザの認証方法(SASLの認証方法)は「PLAIN」のみ(解読可能な形でパスワードがネットワークを流れます。一見危険ですが普通はSTARTTLSやIMAPSしますよね?ということで…。逆に、その他の形式(CRAM-MD5等)ができるようにすると、データベースに平文のパスワードが格納されますので、こちらも危険だと思われます。詳しくはDovecotのマニュアルを参照して下さい。)。「LOGIN」は廃止(OBSOLETE)されていますので、指定しません。
  • データベースに格納されるパスワードのハッシュアルゴリズムはBLOWFISH(BLF-CRYPT)
  • Postfixからメールの保管場所(/var/spool/vmail/配下)への配送は、DovecotのLMTPを用いる(Postfixが直接アクセスしてもいいのですが、後に記事を作成する予定の「sieve」(サーバサイドでの振り分け機能)に対応するためDovecotのLMTP経由とします)

vmailユーザの作成

OSに「vmail」ユーザと「vmail」グループを追加し、vmailユーザの既定のグループをvmailにします。UID、GIDは何でもいいです。

注意すべき点は

  • 有効なshellは必要なし(/usr/sbin/nologinを指定)
  • 有効なホームディレクトリは必要なし(/nonexistentを指定)

この時点で、メールの配送先ディレクトリを作成してしまいます。

mkdir -p /var/spol/vmail
chown vmail:vmail /var/spool/vmail
chmow 700 /var/spool/vmail

サブディレクトリやファイルは、必要に応じてDovecotが自動的に作成します。

Postfix、Dovecot、PostgreSQLのインストール

FreeBSDですと、PostfixはPostgreSQLのサポートが組み込まれているパッケージがありますので、まずはそれをインストールします。一緒にPostgreSQLもインストールします。現在(2024年10月)、PostgreSQLのバージョンは16がパッケージのデフォルトの様です。

pkg install -y postfix-pgsql postgresql16-server

postfixをインストールすると、pkgからメッセージが出ますが、次の2つのファイルを作成する必要があります。

  1. /usr/local/etc/mail/mailer.conf
  2. /etc/periodic.conf (もしくは /etc/periodic.conf.local)

最初のフィアルは、sendmailコマンドが実行されたとき、実際に実行されるのはpostfixコマンドになるように設定するファイルです。これを作成するには

mkdir -p /usr/local/etc/mail
install -m 0644 /usr/local/sharec/postfix/mailer.conf.postfix /usr/local/etc/mail/mailer.conf

でOKです。

次に、periodic.conf(.local)ファイルは「毎日真夜中に、OSの状態をrootにメールする」という機能等を有効化するためのファイルです。必要があれば、設定します。

cd /etc
vi periodic.conf

ファイルがなければ新規作成で、存在していれば追記で以下の内容を書き込みます。

daily_clean_hoststat_enable="NO"
daily_status_mail_rejects_enable="NO"
daily_status_include_submit_mailq="NO"
daily_submit_queuerun="NO"

Dovecotには、PostgreSQLのサポートが有効化されているパッケージが無いようですのでPortsからコンパイルしますが、コンパイルする前にPostgreSQLのサポートを有効にするために「make config」を実行します。

cd /usr/ports/mail/dovecot
make config

実行すると、オプション選択画面が現れますので、「PostgreSQL database support」にチェックを入れて下さい。

設定を保存したら

BATCH=yes make install clean

でインストールします。Portsからインストールしたので、これが「ports upgrade」で消されないように、ロックします。

pkg lock dovecot

Postfix Adminのインストール

Postfix Adminもパッケージにはあるのですが、DBにPostgreSQLではなくMySQLを想定しているものになります。実際のところ、違いは「PHPのどのDB拡張を依存関係にするかどうか」だけの違いで、パッケージのものをそのまま入れても別のパッケージがPHPのPostgreSQL拡張機能をインストールしていればPostgreSQLで動くのですが、ここはパッケージの依存をキチンとするという意味で、portsからインストールします。

cd /usr/ports/mail/postfixadmin
make config

オプション選択画面が現れます。

設定を保存したら

BATCH=yes make install clean

でインストールされます。Postfix Adminがインストールされるパスは、「/usr/local/www/postfixadmin/」配下ですので、PHPがここに書き込めるように

cd /usr/local/www
chown -R www:www postfixadmin

を実行します。

これも、「pkg upgrade」で上書きされないようにするために

pkg lock postfixadmin-php83 

としてロックします。パッケージの後ろに、OSにインストールされているPHPのバージョンがくっつくことに注意して下さい。もしPHP8.2をインストールしていれば、「postfixadmin-php82」がインストールしたPostfix Adminのパッケージ名になります。はっきりしない場合は

pkg prime-list | grep postfixadmin

で確認すると判ると思います。

PHPのリロード

Postfix Adminをインストールすると、依存関係でPHPの拡張機能がインストールされている可能性があります。PHP-FPMが実行中なら、念の為にリロードを行います。

service php_fpm reload

Webサーバの設定

Webサーバにブラウザでアクセスした時に、「/usr/local/www/postfixadmin/」配下にアクセスできるように設定します。ここで注意なのですが、ドキュメントルートは「/usr/local/www/postfixadmin/public/」になることに注意して下さい。例えば、シンボリックリンクを貼って「http://host.tld/postfixadmin/」でアクセスできるようにするためには

cd /usr/local/www/apache24/data
ln -s /usr/local/www/postfixadmin/public postfixadmin

とコマンドを実行します。

また、DNSを運用しているならば、ネームバーチャルドメインでも大丈夫です。この場合、Aapcheの設定ファイル(例えば、/usr/local/etc/apache24/Include/postfixadmin.conf)を作成し、そこに

<VirtualHost *:80>
    ServerName postfixadmin.example.com
    DocumentRoot "/usr/local/www/postfixadmin/public"
    <Directory "/usr/local/www/postfixadmin/public">
        Options None
        AllowOverride Limit
        Require ip 127.0.0.1 192.168.0.0/24
    </Directory>
    ErrorLog "/var/log/httpd-error-postfixadmin.log"
    CustomLog "/var/log/httpd-access-postfixadmin.log" combined
</VirtualHost>

<VirtualHost *:443>
    ServerName postfixadmin.example.com
    SSLEngine On
    SSLCertificateFile "/usr/local/etc/apache24/server.crt"
    SSLCertificateKeyFile "/usr/local/etc/apache24/server.key"
    DocumentRoot "/usr/local/www/postfixadmin/public"
    <Directory "/usr/local/www/postfixadmin">
        Options None
        AllowOverride Limit
        Require ip 127.0.0.1 192.168.0.0/24
    </Directory>
    ErrorLog "/var/log/httpd-error-postfixadmin.log"
    CustomLog "/var/log/httpd-access-postfixadmin.log" combined
</VirtualHost>

などと設定し(ServerNameやRequire ip、ErrorLogやCustomLogの値は適宜変更)

service apache24 reload

を実行するとこのネームバーチャルホストが有効化されます。

PostgreSQLの設定

PostgreSQLの初期化を行います。PostgreSQLが動作していないタイミングで

service postgresql enable
service postgresql initdb

を実行し、データベースクラスタ(PostgreSQLが管理する、データベースの集まりのフォルダ)の初期化を行います(既にPostgreSQLを使っている場合は、この手順はスキップして下さい)。

次に、PostgreSQLを起動します。

service postgresql start

次にPostfixとDovecot、それにPostfix Adminが使うデータベースのユーザを作成します。仮にPostgreSQLでのユーザ名を「postfix」(Postfix Adminでのデフォルト設定が「postfix」です)、パスワードを「password」とするなら、PostgreSQLにpostgresユーザ(PostgreSQLの管理ユーザ)で接続し

psql -U postgres

次のSQLを実行します。

CREATE ROLE postfix WITH LOGIN PASSWORD 'password';
CREATE DATABASE postfix;
ALTER DATABASE postfix OWNER TO postfix;
\q

これで、PostgreSQLのユーザ「postfix」が作成され、データベース「postfix」が作成され、データベース「postfix」の所有者がPostgreSQLのユーザ「postfix」に変更されました。

Postfix Adminの初期設定

次に、Postfix Adminの初期設定を行います。デフォルトを設定しているファイルが「/usr/local/www/postfixadmin/config.inc.php」になります。デフォルト設定を変更する際は、このフィアルを直接編集するのではなく、「/usr/local/www/postfixadmin/config.local.php」を新規に作成して、デフォルト設定を上書きします。このファイルには、データベースへの接続パスワードを平文で書く必要があるため、所有者とパーミッションに注意が必要です。

cd /usr/local/www/postfixadmin
touch config.local.php
chown www:www config.local.php
chmod 600 config.local.php
vi config.local.php

このファイルに書き込む設定は、次のとおりです。

<?php
$CONF['configured'] = true;
$CONF['setup_password'] = '';

$CONF['default_language'] = 'ja';

$CONF['database_type'] = 'pgsql';
$CONF['database_password'] = 'password';

$CONF['encrypt'] = 'php_crypt:BLOWFISH';

$CONF['default_aliases'] = array();

$CONF['domain_quota_default'] = '0';

$CONF['show_footer_text'] = 'NO';

データベースへの接続パスワードは、「$CONF['database_password']」で設定します。

「$CONF['encrypt'] = 'php_crypt:BLOWFISH';」が、データベースにパスワードを格納する際のハッシュアルゴリズムの指定です。前提の通り「BLOWFISH」を指定します。

「$CONF['database_type'] = 'pgsql';」はデータベースがPostgreSQLであることを指定しています。「$CONF['database_password'] = 'password';」には、実際のデータベースのパスワードを書き込んでください。

「$CONF['default_aliases']」には空配列を指定していますが、これは「Postfix Adminが、既定のメール転送アドレスを勝手に作らないようにする」設定です。「$CONF['domain_quota_default'] = '0';」は後ほど設定する「クォータ(容量制限)」で、新たに作成したドメインのドメインレベルの容量設定を無制限にする設定です。「$CONF['show_footer_text'] = 'NO';」ではフッターの表示を消しています。

先頭の「$CONF['configured'] = true;」は、「初期設定ファイルの設置が完了した」という意味になります。「$CONF['setup_password'] = ''」は「セットアップ画面のパスワード(のハッシュ)を、Web経由で作成する」という設定で、空文字にしない場合は、自分でパスワードをハッシュ化しなければならなく、それは面倒なので空文字に設定しています。

では、Web経由で初期設定画面にアクセスします。Postfix AdminのURLが「http://example.com/postfixadmin/」であれば、「http://example.com/postfixadmin/setup.php」にアクセスします。エラーにならず、次のような画面が出れば成功です。

次に、セットアップ用のパスワードを生成します。「Generate setup_password」にパスワードを入力し、「Generate setup_password hash」ボタンを押します。ボタンを押すと、次の画面の様に「config.local.php」に書き込むパスワードのハッシュ値が生成されます。

次に、表示された「$CONF['setup_password' = '...';」をコピーし、「/usr/local/www/postfixadmin/config.local.php」ファイルを編集し、もともとあった空文字の設定と置き換えてから保存します。保存してから再度「http://example.com/postfixadmin/setup.php」にブラウザからアクセスすると(再読み込みでOKです)

このように表示され、データベースの初期設定がなされます。この画面では、管理者を追加しなければなりません。ページ下部にある「Add Superadmin Account」に必要な設定を書き込みます。

Setup passwordは先程設定した初期化用のパスワードです。管理者は「管理者のメールアドレス」になります。Postfix Adminに管理者としてログインするためには、メールアドレスとパスワードでログインすることになります。必要事項を入力し、「管理者を追加」ボタンを押すと、データベースに管理者のメールアドレスとパスワードが書き込まれます。

注意

「http://example.com/postfixadmin/setup.php」に再度アクセスされ、設定パスワードを入力すると、管理者の更なる追加ができてしまいます。これはPostfix Adminを運用する際にはセキュリティ上の懸念があります。これを防止するためには、「config.local.php」の設定を書き換え、「$CONF['setup_password'] = '...'」の行そのものを削除してしまえば設定パスワードが無効になり、setup.phpへのアクセスを無効にできます。

ここまでの設定を終え、「http:/example.com/postfixadmin/」にアクセスすると

このように表示され、ドメインの追加やユーザの追加ができます。今の段階ではまだPostfix・Dovecotの設定が終わっていないので、その操作は後回しにします(ユーザを追加すると、そのユーザに対して「Welcome」メッセージが送信されるのですが、Postfixの設定が終わっておらず、その送信に失敗します)。

DovecotとPostfix Adminの連携

次に、Postfix AdminとDovecotの連携を行います。FreeBSDのPortsからインストールした直後には設定ファイルが無いので、まずは雛形を用意します。サンプル設定ファイルは「/usr/local/etc/dovecot/example-config/」配下にあるので、これを所定の場所にコピーします。

cd /usr/local/etc/dovecot/example-config
cp -pr * ../

/usr/local/etc/dovecot/dovecot-sql.conf.ext

まずは、認証関係です。認証情報をPostgreSQLから検索するクエリをdovecot-sql.conf.extに書き込みます。これはパッチファイルを用意したので、password=passwordを実際のものに書き換えてからパッチを当てます。まずはこのコードをコピーしファイルに保存します。

*** example-config/dovecot-sql.conf.ext	Sat Oct 12 17:44:22 2024
--- dovecot-sql.conf.ext	Mon Nov  4 20:04:17 2024
***************
*** 29,35 ****
  # );
  
  # Database driver: mysql, pgsql, sqlite
! #driver = 
  
  # Database connection string. This is driver-specific setting.
  #
--- 29,35 ----
  # );
  
  # Database driver: mysql, pgsql, sqlite
! driver = pgsql
  
  # Database connection string. This is driver-specific setting.
  #
***************
*** 71,84 ****
  #   connect = host=sql.example.com dbname=virtual user=virtual password=blarg
  #   connect = /usr/local/etc/dovecot/authdb.sqlite
  #
! #connect =
  
  # Default password scheme.
  #
  # List of supported schemes is in
  # http://wiki2.dovecot.org/Authentication/PasswordSchemes
  #
! #default_pass_scheme = MD5
  
  # passdb query to retrieve the password. It can return fields:
  #   password - The user's password. This field must be returned.
--- 71,84 ----
  #   connect = host=sql.example.com dbname=virtual user=virtual password=blarg
  #   connect = /usr/local/etc/dovecot/authdb.sqlite
  #
! connect = host=localhost dbname=postfix user=postfix password=password
  
  # Default password scheme.
  #
  # List of supported schemes is in
  # http://wiki2.dovecot.org/Authentication/PasswordSchemes
  #
! default_pass_scheme = BLF-CRYPT
  
  # passdb query to retrieve the password. It can return fields:
  #   password - The user's password. This field must be returned.
***************
*** 107,115 ****
  #   password_query = SELECT userid AS user, pw AS password \
  #     FROM users WHERE userid = '%u' AND active = 'Y'
  #
! #password_query = \
! #  SELECT username, domain, password \
! #  FROM users WHERE username = '%n' AND domain = '%d'
  
  # userdb query to retrieve the user information. It can return fields:
  #   uid - System UID (overrides mail_uid setting)
--- 107,115 ----
  #   password_query = SELECT userid AS user, pw AS password \
  #     FROM users WHERE userid = '%u' AND active = 'Y'
  #
! password_query = \
!   SELECT username AS user, password \
!   FROM mailbox WHERE username = '%u' AND active = true
  
  # userdb query to retrieve the user information. It can return fields:
  #   uid - System UID (overrides mail_uid setting)
***************
*** 127,135 ****
  #   user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u'
  #   user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u'
  #
! #user_query = \
! #  SELECT home, uid, gid \
! #  FROM users WHERE username = '%n' AND domain = '%d'
  
  # If you wish to avoid two SQL lookups (passdb + userdb), you can use
  # userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll
--- 127,136 ----
  #   user_query = SELECT dir AS home, user AS uid, group AS gid FROM users where userid = '%u'
  #   user_query = SELECT home, 501 AS uid, 501 AS gid FROM users WHERE userid = '%u'
  #
! user_query = \
!   SELECT '/var/spool/vmail/' || maildir AS home, \
!   'vmail' AS uid, 'vmail' AS gid \
!   FROM mailbox WHERE username = '%u' AND active = true
  
  # If you wish to avoid two SQL lookups (passdb + userdb), you can use
  # userdb prefetch instead of userdb sql in dovecot.conf. In that case you'll
***************
*** 141,144 ****
  #  FROM users WHERE userid = '%u'
  
  # Query to get a list of all usernames.
! #iterate_query = SELECT username AS user FROM users
--- 142,145 ----
  #  FROM users WHERE userid = '%u'
  
  # Query to get a list of all usernames.
! iterate_query = SELECT username AS user FROM mailbox

保存したファイルが、例えば「dovecot-sql.conf.ext.diff」であれば、次のコマンドでパッチを当てます。

cd /usr/local/etc/dovecot
patch dovecot-sql.conf.ext < /path/to/file/dovecot-sql.conf.ext.diff

このファイルにはデータベースのパスワード等が平文で書かれているので、所有者とパーミッションを設定します。

cd /usr/local/etc/dovecot
chown dovecot:dovecot dovecot-sql.conf.ext
chmod 600 dovecot-sql.conf.ext

/usr/local/etc/dovecot/dovecot.conf

次に、「dovecot.conf」を設定します。Dovecotが有効にするプロトコルを指定するためと、「私はDovecotです」と名乗っているのを(セキュリティ上の理由で)隠すため、次のパッチを当てます。

*** example-config/dovecot.conf Sat Oct 12 17:44:22 2024
--- dovecot.conf        Wed Oct 23 05:38:43 2024
***************
*** 21,27 ****
  # --sysconfdir=/usr/local/etc --localstatedir=/var
  
  # Protocols we want to be serving.
! #protocols = imap pop3 lmtp submission
  
  # A comma separated list of IPs or hosts where to listen in for connections. 
  # "*" listens in all IPv4 interfaces, "::" listens in all IPv6 interfaces.
--- 21,27 ----
  # --sysconfdir=/usr/local/etc --localstatedir=/var
  
  # Protocols we want to be serving.
! protocols = imap pop3 lmtp
  
  # A comma separated list of IPs or hosts where to listen in for connections. 
  # "*" listens in all IPv4 interfaces, "::" listens in all IPv6 interfaces.
***************
*** 39,45 ****
  #instance_name = dovecot
  
  # Greeting message for clients.
! #login_greeting = Dovecot ready.
  
  # Space separated list of trusted network ranges. Connections from these
  # IPs are allowed to override their IP addresses and ports (for logging and
--- 39,45 ----
  #instance_name = dovecot
  
  # Greeting message for clients.
! login_greeting = IMAP server ready.
  
  # Space separated list of trusted network ranges. Connections from these
  # IPs are allowed to override their IP addresses and ports (for logging and

見ての通り、「IMAP」「POP3」」「LMTP」を有効化し、「Dovecot」の部分を「IMAP Server」に書き換えています。これもファイル(例えば「dovecot.conf.diff」)に保存し、patchコマンドを実行します。

cd /usr/local/etc/dovecot
patch dovecot.conf < /path/to/file/dovecot.conf.diff

/usr/local/etc/dovecot/conf.d/10-auth.conf

次からは、「/usr/local/etc/dovecot/conf.d」配下のファイルを設定します。

まずは「10-auth.conf」のパッチです。デフォルトではTLSを使わない場合のPLAIN認証が無効化されているので、これを有効化します。また、デフォルトではOSのユーザ認証を用いる設定になっているので、これを無効化します。

*** ../example-config/conf.d/10-auth.conf	Sat Oct 12 17:44:22 2024
--- 10-auth.conf	Thu Oct 24 16:47:48 2024
***************
*** 7,13 ****
  # matches the local IP (ie. you're connecting from the same computer), the
  # connection is considered secure and plaintext authentication is allowed.
  # See also ssl=required setting.
! #disable_plaintext_auth = yes
  
  # Authentication cache size (e.g. 10M). 0 means it's disabled. Note that
  # bsdauth and PAM require cache_key to be set for caching to be used.
--- 7,13 ----
  # matches the local IP (ie. you're connecting from the same computer), the
  # connection is considered secure and plaintext authentication is allowed.
  # See also ssl=required setting.
! disable_plaintext_auth = no
  
  # Authentication cache size (e.g. 10M). 0 means it's disabled. Note that
  # bsdauth and PAM require cache_key to be set for caching to be used.
***************
*** 119,126 ****
  #!include auth-deny.conf.ext
  #!include auth-master.conf.ext
  
! !include auth-system.conf.ext
! #!include auth-sql.conf.ext
  #!include auth-ldap.conf.ext
  #!include auth-passwdfile.conf.ext
  #!include auth-checkpassword.conf.ext
--- 119,126 ----
  #!include auth-deny.conf.ext
  #!include auth-master.conf.ext
  
! #!include auth-system.conf.ext
! !include auth-sql.conf.ext
  #!include auth-ldap.conf.ext
  #!include auth-passwdfile.conf.ext
  #!include auth-checkpassword.conf.ext

/usr/local/etc/dovecot/conf.d/10-mail.conf

次に、「10-mail.conf」のパッチです。変更点は

  • メールが保管される場所を、Maildir形式の「/var/spool/vmail/メールアドレスのドメイン部分/メールアドレスのユーザ部分/」に設定
  • メールを操作するユーザをvmial、同じくグループをvmailに設定

です。

*** ../example-config/conf.d/10-mail.conf	Sat Oct 12 17:44:22 2024
--- 10-mail.conf	Mon Nov  4 19:41:57 2024
***************
*** 27,33 ****
  #
  # <doc/wiki/MailLocation.txt>
  #
! #mail_location = 
  
  # If you need to set multiple mailbox locations or want to change default
  # namespace settings, you can do it by defining namespace sections.
--- 27,33 ----
  #
  # <doc/wiki/MailLocation.txt>
  #
! mail_location = mdbox:/var/spool/vmail/%d/%n
  
  # If you need to set multiple mailbox locations or want to change default
  # namespace settings, you can do it by defining namespace sections.
***************
*** 105,112 ****
  # System user and group used to access mails. If you use multiple, userdb
  # can override these by returning uid or gid fields. You can use either numbers
  # or names. <doc/wiki/UserIds.txt>
! #mail_uid =
! #mail_gid =
  
  # Group to enable temporarily for privileged operations. Currently this is
  # used only with INBOX when either its initial creation or dotlocking fails.
--- 105,112 ----
  # System user and group used to access mails. If you use multiple, userdb
  # can override these by returning uid or gid fields. You can use either numbers
  # or names. <doc/wiki/UserIds.txt>
! mail_uid = vmail
! mail_gid = vmail
  
  # Group to enable temporarily for privileged operations. Currently this is
  # used only with INBOX when either its initial creation or dotlocking fails.

/usr/local/etc/dovecot/conf.d/10-master.conf

次は、「10-master.conf」のパッチです。ここでは

  • LMTPの設定(デフォルトでは「root」ユーザとしてLMTPプロセスが実行されるが、「vmail」ユーザに権限を落とす&LMTPへのアクセス権を、Postfixユーザに限定)
  • Postfixに対して、Dovecotが代わりに認証を提供する設定(有効化とともに、認証にアクセスできるユーザをPostfixに限定)

を設定します。

*** ../example-config/conf.d/10-master.conf	Sun Nov  3 13:17:05 2024
--- 10-master.conf	Sun Nov  3 13:18:34 2024
***************
*** 55,62 ****
  }
  
  service lmtp {
!   unix_listener lmtp {
!     #mode = 0666
    }
  
    # Create inet listener only if you can't use the above UNIX socket
--- 55,65 ----
  }
  
  service lmtp {
!   user = vmail
! 
!   unix_listener /var/spool/postfix/private/dovecot-lmtp {
!     mode = 0600
!     user = postfix
    }
  
    # Create inet listener only if you can't use the above UNIX socket
***************
*** 107,115 ****
    }
  
    # Postfix smtp-auth
!   #unix_listener /var/spool/postfix/private/auth {
!   #  mode = 0666
!   #}
  
    # Auth process is run as this user.
    #user = $default_internal_user
--- 110,119 ----
    }
  
    # Postfix smtp-auth
!   unix_listener /var/spool/postfix/private/auth {
!     mode = 0600
!     user = postfix
!   }
  
    # Auth process is run as this user.
    #user = $default_internal_user

/usr/local/etc/dovecot/conf.d/20-imap.conf

ここでは、IMAP COMPRESS(IMAPの転送時に、データを圧縮してネットワークの負担を軽くするIMAP拡張)の設定を行います。次のパッチを当ててください。

*** ../example-config/conf.d/20-imap.conf	Sat Oct 12 17:44:22 2024
--- 20-imap.conf	Fri Nov 29 23:13:46 2024
***************
*** 91,97 ****
  
  protocol imap {
    # Space separated list of plugins to load (default is global mail_plugins).
!   #mail_plugins = $mail_plugins
  
    # Maximum number of IMAP connections allowed for a user from each IP address.
    # NOTE: The username is compared case-sensitively.
--- 91,97 ----
  
  protocol imap {
    # Space separated list of plugins to load (default is global mail_plugins).
!   mail_plugins = $mail_plugins imap_zlib
  
    # Maximum number of IMAP connections allowed for a user from each IP address.
    # NOTE: The username is compared case-sensitively.

/usr/local/etc/dovecot/conf.d/90-plugin.conf

先ほど設定したIMAP COMPRESSの効き具合を設定します。次のパッチを当ててください。

*** ../example-config/conf.d/90-plugin.conf	Sat Oct 12 17:44:22 2024
--- 90-plugin.conf	Fri Nov 29 23:15:07 2024
***************
*** 8,11 ****
--- 8,12 ----
  
  plugin {
    #setting_name = value
+   imap_compress_deflate_level = 6
  }

TLSを設定する前なので…(/usr/local/etc/dovecot/conf.d/10-ssl.conf)

現段階では、まだSSL/TLSの設定を行いません(すぐに有効化の記事を書きます…)。

DovecotはデフォルトでTLSが有効化されていますので、一度これを無効化します。また、存在しないTLS証明書を読み込もうとしてエラーを起こすので、これも無効化します。設定ファイルは「10-ssl.conf」になります。パッチは次の内容になります。

*** ../example-config/conf.d/10-ssl.conf	Sat Oct 12 17:44:22 2024
--- 10-ssl.conf	Wed Oct 23 13:47:43 2024
***************
*** 3,16 ****
  ##
  
  # SSL/TLS support: yes, no, required. <doc/wiki/SSL.txt>
! #ssl = yes
  
  # PEM encoded X.509 SSL/TLS certificate and private key. They're opened before
  # dropping root privileges, so keep the key file unreadable by anyone but
  # root. Included doc/mkcert.sh can be used to easily generate self-signed
  # certificate, just make sure to update the domains in dovecot-openssl.cnf
! ssl_cert = </etc/ssl/certs/dovecot.pem
! ssl_key = </etc/ssl/private/dovecot.pem
  
  # If key file is password protected, give the password here. Alternatively
  # give it when starting dovecot with -p parameter. Since this file is often
--- 3,16 ----
  ##
  
  # SSL/TLS support: yes, no, required. <doc/wiki/SSL.txt>
! ssl = no
  
  # PEM encoded X.509 SSL/TLS certificate and private key. They're opened before
  # dropping root privileges, so keep the key file unreadable by anyone but
  # root. Included doc/mkcert.sh can be used to easily generate self-signed
  # certificate, just make sure to update the domains in dovecot-openssl.cnf
! #ssl_cert = </etc/ssl/certs/dovecot.pem
! #ssl_key = </etc/ssl/private/dovecot.pem
  
  # If key file is password protected, give the password here. Alternatively
  # give it when starting dovecot with -p parameter. Since this file is often

PostfixとDovecot、Postfix Adminの連携

次に、PostfixとDovecot、Postfix Adminを連携させます。

  • 認証はDovecotに任せる
  • メールを受け取るべきドメイン・ユーザを、Postfix Adminが設定したデータベースから参照して決定する
  • 受け取ったメールは、DovecotのLMTPに投げる
  • その他、セキュリティ的な設定

を施します。設定を変更するファイルは「/usr/local/etc/postfix/」配下にある「main.cf」、それにどのようなサービスを動かすかを決定する「master.cf」を変更します。

/usr/local/etc/postfix/main.cf

まずは、「main.cf」のパッチです。

*** /usr/local/etc/postfix/main.cf.sample	Wed Oct  9 05:55:04 2024
--- main.cf	Wed Nov  6 06:54:22 2024
***************
*** 119,125 ****
  # to recipient addresses that have no @domain part.
  #
  #myorigin = $myhostname
! #myorigin = $mydomain
  
  # RECEIVING MAIL
  
--- 119,125 ----
  # to recipient addresses that have no @domain part.
  #
  #myorigin = $myhostname
! myorigin = $mydomain
  
  # RECEIVING MAIL
  
***************
*** 269,276 ****
  # only the local machine.
  # 
  #mynetworks_style = class
! #mynetworks_style = subnet
! mynetworks_style = host
  
  # Alternatively, you can specify the mynetworks list by hand, in
  # which case Postfix ignores the mynetworks_style setting.
--- 269,276 ----
  # only the local machine.
  # 
  #mynetworks_style = class
! mynetworks_style = subnet
! #mynetworks_style = host
  
  # Alternatively, you can specify the mynetworks list by hand, in
  # which case Postfix ignores the mynetworks_style setting.
***************
*** 575,580 ****
--- 575,581 ----
  #
  #smtpd_banner = $myhostname ESMTP $mail_name
  #smtpd_banner = $myhostname ESMTP $mail_name ($mail_version)
+ smtpd_banner = $myhostname ESMTP MTA
  
  # PARALLEL DELIVERY TO THE SAME DESTINATION
  #
***************
*** 687,689 ****
--- 688,737 ----
  smtp_tls_CApath = /etc/ssl/certs
  meta_directory = /usr/local/libexec/postfix
  shlib_directory = /usr/local/lib/postfix
+ 
+ # SASL
+ smtpd_sasl_auth_enable = yes
+ smtpd_sasl_type = dovecot
+ smtpd_sasl_path = private/auth
+ smtpd_sasl_security_options = noanonymous
+ 
+ # Security
+ smtpd_helo_required = yes
+ disable_vrfy_command = yes
+ strict_rfc821_envelopes = yes
+ smtpd_relay_restrictions =
+ 	permit_mynetworks,
+ 	permit_sasl_authenticated,
+ 	reject_unauth_destination
+ smtpd_client_restrictions =
+ 	permit_mynetworks,
+ 	permit_sasl_authenticated,
+ 	reject_invalid_hostname,
+ 	reject_non_fqdn_sender,
+ 	reject_non_fqdn_recipient,
+ 	reject_unknown_sender_domain,
+ 	reject_unauth_destination,
+ 	reject_unknown_client,
+ 	check_client_access hash:/usr/local/etc/postfix/reject_clients,
+ 	permit
+ smtpd_helo_restrictions =
+ 	permit_mynetworks,
+ 	reject_invalid_hostname,
+ 	permit
+ smtpd_sender_restrictions =
+ 	check_sender_access hash:/usr/local/etc/postfix/reject_sender,
+ 	reject_unknown_sender_domain,
+ 	reject_non_fqdn_sender
+ smtpd_etrn_restrictions =
+ 	permit_mynetworks,
+ 	reject_invalid_hostname
+ 
+ # Virtual Domain
+ virtual_transport = lmtp:unix:private/dovecot-lmtp
+ virtual_mailbox_domains =
+ 	pgsql:/usr/local/etc/postfix/pgsql_virtual_domains_maps.cf
+ virtual_mailbox_maps =
+ 	pgsql:/usr/local/etc/postfix/pgsql_virtual_mailbox_maps.cf
+ virtual_alias_maps =
+ 	pgsql:/usr/local/etc/postfix/pgsql_virtual_alias_maps.cf,
+ 	pgsql:/usr/local/etc/postfix/pgsql_virtual_alias_domains_maps.cf

ここでの設定ですが、

  • 宛先が、例えば「root@exmaple.com」ではなく、単純にrootの場合(「/usr/bin/mail root」コマンドでメールを送信すると、宛先が「root」になります)に、Postfixは自動的にドメイン名を付け加えますが、そのドメイン名をPostfixが動いているマシンのhostnameのドメイン名にするために、「myorigin = $mydomain」を設定しています。
  • 自分が属しているIPアドレスのサブネットの範囲は、認証無しでメールを受け付けるように設定するため、「mynetworks_style」を「host」から「subnet」に変更しています。
  • SMTP接続した際に、「わたしはPostfix」ですと名乗っているのですが、これを隠します。
  • 「# SASL」以下は、Dovecotに認証を任せる設定です。
  • 「# Security」以下は、セキュリティ関連の設定をしています。コピペで使えると思いますが、詳しく知りたい場合はPostfixのマニュアルを参照して下さい。
  • 「# Virtual Domain」以下の設定が、バーチャルドメイン関連の設定です。ここでは、「メールボックスへの配送はDovecotのLMTPに任せる」設定と、「PostgreSQLに問い合わせをして、受け入れていいユーザであるかどうかを決定する」ための設定をしています。「pgsql:...」と書かれている部分は、PostgreSQLに対して発行するクエリが書いてあるファイルを指定しています。指定の通り、次のファイルを用意します。
    • pgsql_virtual_domains_maps.cf
    • pgsql_virtual_mailbox_maps.cf
    • pgsql_virtual_alias_maps.cf
    • pgsql_virtual_alias_domains_map.cf

では、PostgreSQLに対するクエリ発行用ファイルを新規に作成します。次の内容のファイルを「/usr/local/etc/postfix/」ディレクトリに配置して下さい。

/usr/local/etc/postfix/pgsql_virtual_domains_maps.cf

user = postfix
password = password
hosts = localhost
dbname = postfix
table = domain
select_field = domain
where_field = domain
additional_conditions = AND active = true

/usr/local/etc/postfix/pgsql_virtual_mailbox_maps.cf

user = postfix
password = password
hosts = localhost
dbname = postfix
table = mailbox
select_field = maildir
where_field = username
additional_conditions = AND active = true

/usr/local/etc/postfix/pgsql_virtual_alias_maps.cf

user = postfix
password = password
hosts = localhost
dbname = postfix
table = alias
select_field = goto
where_field = address
additional_conditions = AND active = true

/usr/local/etc/postfix/pgsql_virtual_alias_domains_maps.cf

user = postfix
password = password
hosts = localhost
dbname = postfix
query = SELECT goto FROM alias, alias_domain WHERE alias_domain.alias_domain = '%d' AND alias.address = '%u' || '@' || alias_domain.target_domain AND alias.active = true AND alias_domain.active = true

これでPostgreSQLに問い合わせるファイルが揃いました。

共通の注意事項ですが、

  • 「password = 」のパスワードを、実際のものに変更して下さい。
  • 見ての通り、データベースのパスワードが平文で書かれています。そのため、所有者とパーミッションを設定します。

所有者とパーミッションの変更は、次の様に行います。

cd /usr/local/etc/postfix
chown root:postfix pgsql_*
chmod 640 pgsql_*

また、次のファイルも用意します。

  • /usr/local/etc/dovecot/reject_clients (拒否したい送信サーバを指定。サーバなのに「clients」はおかしい、とお思いかもしれませんが、送信サーバ=受取側のサーバで動いているPostfixから見たらクライアント、という意味です。)
  • /usr/local/etc/dovecot/reject_sender (拒否したい送信者をメールアドレスで指定)

例えば、次のように設定します。

/usr/local/etc/postfix/reject_clients

10.0.3.123 REJECT

/usr/local/etc/postfix/reject_sender

spam@spam.com REJECT
spam.org REJECT

内容が無くても、ファイルは作成して下さい。(touchコマンド等)そのうえで、これらのファイルをPostfixが読み込めるhash形式に変換します。

cd /usr/local/etc/postfix
postmap reject_clients
postmap reject_sender

/usr/local/etc/postfix/master.cf

次に、「master.cf」ファイルを編集します。このファイルは、どのようなサービス(例えば、Submissionポート(TCP/587)でクライアントのメーラーから受信する)を提供するかを決定するファイルです。デフォルトから「Submissionポート」を有効にする変更を施します。

/usr/local/etc/postfix/master.cfに次のパッチを当てます。

*** master.cf.sample	Wed Oct  9 05:55:02 2024
--- master.cf	Wed Oct 23 15:34:08 2024
***************
*** 16,25 ****
  #tlsproxy  unix  -       -       n       -       0       tlsproxy
  # Choose one: enable submission for loopback clients only, or for any client.
  #127.0.0.1:submission inet n -   n       -       -       smtpd
! #submission inet n       -       n       -       -       smtpd
! #  -o syslog_name=postfix/submission
  #  -o smtpd_tls_security_level=encrypt
! #  -o smtpd_sasl_auth_enable=yes
  #  -o smtpd_tls_auth_only=yes
  #  -o local_header_rewrite_clients=static:all
  #  -o smtpd_reject_unlisted_recipient=no
--- 16,25 ----
  #tlsproxy  unix  -       -       n       -       0       tlsproxy
  # Choose one: enable submission for loopback clients only, or for any client.
  #127.0.0.1:submission inet n -   n       -       -       smtpd
! submission inet n       -       n       -       -       smtpd
!   -o syslog_name=postfix/submission
  #  -o smtpd_tls_security_level=encrypt
!   -o smtpd_sasl_auth_enable=yes
  #  -o smtpd_tls_auth_only=yes
  #  -o local_header_rewrite_clients=static:all
  #  -o smtpd_reject_unlisted_recipient=no
***************
*** 31,37 ****
  #  -o smtpd_helo_restrictions=
  #  -o smtpd_sender_restrictions=
  #  -o smtpd_relay_restrictions=
! #  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
  #  -o milter_macro_daemon_name=ORIGINATING
  # Choose one: enable submissions for loopback clients only, or for any client.
  #127.0.0.1:submissions inet n  -       n       -       -       smtpd
--- 31,37 ----
  #  -o smtpd_helo_restrictions=
  #  -o smtpd_sender_restrictions=
  #  -o smtpd_relay_restrictions=
!   -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
  #  -o milter_macro_daemon_name=ORIGINATING
  # Choose one: enable submissions for loopback clients only, or for any client.
  #127.0.0.1:submissions inet n  -       n       -       -       smtpd

変更部分の説明ですが、

  • Submissionポートでの待受を有効にしています
  • ログに記録される際に、デフォルトだと「postfix/smtpd」と記録されるのですが、これはサーバ間のsmtp(TCP/25)と区別がつかないので、「postfix/submission」に変更しています(実際のログを見ると、「postfix/submission/smtpd」のようです)
  • 認証を有効化しています
  • 認証を必須化しています

今まで通りの設定を行えば、システムが持っている転送情報(/etc/aliases)は機能しないのですが、Postfixは起動時にこのファイルの存在を確認します。正確にはハッシュ化したファイルの存在を確認するのですが、デフォルトではハッシュ化されていません。ハッシュ化されたエイリアス情報がないとログにいちいち報告するので、ハッシュ化してしまいましょう。コマンドは

newaliases

です。このコマンドをを実行すると、設定完了です。DovecotとPostfixを起動しましょう。Postfixの認証とローカル配信はDovecotに依存していますので、先にDovecotを起動します。

service dovecot enable
service dovecot start
service postfix enable
service postfix start

Postfix Adminでドメイン・ユーザの追加

ここまで正常に動けば、あとはPostfix Adminでドメイン・ユーザを追加して「TLSなし」であればThunderbirdなどからサーバに接続してメールのやりとりができます。

MySQLとPostgreSQLの違い

ここに書かれている情報は、実はMySQLの設定を参考にしている、というか、ほぼパクっています(設定したのが相当前なので、URL等はわからないのですが…ググればあちこちで見つかると思います)。Postfix Adminのテーブル構造にデータベース間での差異はほぼ無く、クエリ発行のSQLもほぼそのまま使っています。

一点、変更したのはWHERE句の「active = 1」を「active = true」にしたところです。カラムの型が「boolean」の時、MySQLでは「1」であれば「true」という扱いをするのですが(その方が効率がいい)、PostgreSQLではそのまま「true」ないしは「false」でチェックします。そのため、その部分だけは書き換えています。