dovecotサーバーにroundcubeを連携する
前回、クラウド上にあるメールをローカルのdovecotサーバーに落とし込む仕組みを構築しました。今回はdovecotサーバーをより便利に使うため、IMAP接続環境を構築しようと思います。WebベースのIMAPクライアントroundcubeを用います。ローカル環境のサーバーOSはフリーOSのDebianを利用しています。
IMAPへの対応
/usr/share/dovecot/protocols.d/内にあるファイルを確認してください。存在するのがpop3d.protocolだけだった場合dovecotはIMAPには対応していませんので対応させます。
$ su
...
# apt update
...
# apt install dovecot-imapd
roundcube
IMAP用のブラウザインターフェースであるroundcubeをインストールします。MySQLかMariaDBがインストールされていない場合は合わせてインストールします。
$ su
...
# apt update
...
# apt install roundcube-core mariadb-server
...
途中dbconfig-commonによるMariaDB(MySQL)の設定をするか確認してくると思いますが、「いいえ」にしておきます。おそらく「はい」にしてもうまくいきません。デフォルトでインストールされるMariaDBのroot権限との連動がうまくいかないからです。
まずMariaDBから設定していきます。MySQLでも操作方法は変わりません。
MariaDBにアクセスしてroundcube用のデータベースを作成します。また、そこにアクセスできるユーザーを作成します。その後MariaDBのコンソールから一度抜けて、Debianのシェルより初期化用のSQLを実行します。
# mysql -u root
MariaDB[] > create database roundcube;
MariaDB[] > create user 'roundcube'@'localhost' identified by 'password';
MariaDB[] > grant all privileges on roundcube.* to 'roundcube'@'localhost';
MariaDB[] >exit;
# mysql -u roundcube -p roundcube </usr/share/dbconfig-common/data/roundcube/install/mysql
...(パスワード入力)
...
/etc/roundcube/debian-db.phpを次のように編集します。
- $dbuser="roundcube"
MariaDBに作成したユーザ名
- $dbpass="roundcube"
上記ユーザのMariaDBにおけるパスワード
- $basepath=""
ブランク
- $dbname ="roundcube"
作成したデータベース名
- $dbserver="localhost"
DBサーバーのIPアドレス
- $dbport="3306"
DBで使うポート番号
- $dbtype="mysql"
DBのタイプ
/etc/roundcube/config.inc.phpを次のように編集します。
- $config['default_host'] = 'IMAP接続サーバ';
ここでは自サーバーを示す、127.0.0.1を設定しました。SSL/TLS接続を用いる際には、ssl://かtls://を先頭に付与します。「localhost」と記述しても稼働しますが、この場合IPv4とIPv6との共存環境でroundcubeのレスポンス速度が極端に低下する現象がみられました。先にIPv6環境で接続しようしてタイムアウトになっているのではないかと思います。
- $config['smtp_server'] = 'SMTPサーバ';
SMTPSを利用する場合は、ssl://を先頭に付与します。STARTTLSを使うにはtls://を先頭に付与します。
- $config['smtp_port'] = 587;
SMTPサーバーのポート番号です。ssl時は465、tls時は587、暗号化なし時は25が普通ですが、接続先の仕様により変化します。
- $config['smtp_user'] = '%u';
SMTPサーバーのユーザー名です。roundcubeのログインユーザー%uに対して文字列の付与もできます。
- $config['smtp_pass'] = '%p';
SMTPサーバーのパスワードです。roundcubeのログインユーザー%pに対して文字列の付与もできます。
- $config['smtp_conn_options'] = ...
接続方式にsslやtlsを利用する場合は証明書の設定が必要です。今回はSMTPだけ外部サーバーと直接tls通信をしました。その設定に関しては環境によっても違うと思いますが、記事最後の「補足」を参考にしてください。
- $config['imap_conn_options']=...
今回はIMAPの暗号化通信の設定をしませんでしたが、設定の基本的なことはSMTPと同じです。自社でIMAPサーバーを運用する場合は「'allow_self_signed' => true,」(自己署名証明書の許可)あたりの設定が肝になるかと思います。
/etc/roundcube/default.inc.phpを次のように編集します。
- $config['mail_domain'] ='';
ログインしたユーザー名に対してメールドメインを設定する場合に用います。ユーザー作成時(初回ログイン時)のみ有効です。既存のユーザーはWebメールの画面より「設定」「識別情報」を選んで変更します。この情報は「メールサーバーのドメインを変更した場合に必要なroundcubeの設定変更箇所」を参考にさせていただきました。ありがとうございます。
/etc/apache2/conf-enabled/roundcube.confの先頭部分のAlias設定のコメントアウトを外します。
Alias /roundcube /var/lib/roundcube
これでapacheを再起動し、ブラウザにhttp://機器のIPアドレス/roundcubeとすると接続ができると思います。
クラウドから受信(IMAP受信時)
今回の構造は、クラウドにあるメールを定期的にすべてローカルのdovecotに落として、roundcubeから見に行くものです。
そのためメールの受信には少しタイムラグがあります。roundcubeから再受信を実行してもdovecotに見に行くだけなので、タイムラグは埋まりません。
それを解消するためにroundcubeのPHPファイルに少し手を加えます。
ブラウザがアクセスするroundcubeファイルは/usr/share/roundcube/index.phpにありますが、各種作業を受け持つPHPのコードは/usr/share/roundcube/program/lib/Rundcubeの中にあります。
その中のrcube_imap.phpがIMAPサーバーに読みこみに行くためのコードでこのconnectという関数で処理をします。
connect関数の一番最初に、クラウドサーバーからメール受信するための記述をします。編集前にはバックアップを取っておきましょう。
PHPの処理で次のスクリプトを実行させます。前回の記事で作成したgetmailによるクラウドからのメール受信スクリプト、getmail.shを改変したスクリプトを作成します。設定を共有化させる為、起動ユーザーはgetmail.shと同じにします。
#!/bin/sh
LIMITSIZEM=500
#パイプやサブシェルでエラーになったら、シェルを停止させるオプションを設定しています。
set -e
# 全体受信の稼働中はrunningというファイルが出力されているので存在したら処理をやめます
# ユーザー毎の重複稼働は考えません。
if [ -f $HOME/.getmail/running ]; then
echo "getmail is already running ... (if not, remove $HOME/.getmail/running)" >&2
pgrep -l "getmai[l]"
exit 1
else
echo "getmail has not been running ... "
fi
# 引数にはユーザー名が入りますので、存在しなかったら中止にします
if [ -z "$1" ]; then
echo "not direct user " >&2
exit
fi
# ユーザーの受信設定ファイルが存在しなかったら中止にします
echo $HOME/.getmail/config/$1
if [ ! -f $HOME/.getmail/config/$1 ]; then
echo "$1's config file not exist" >&2
exit
fi
# 容量オーバーリストをいったん削除します
if [ -f $HOME/.getmail/limitover/$1 ]; then
rm $HOME/.getmail/limitover/$1
fi
filepath=/var/mdir/$1
filesize=`du -ms $filepath | sed -r 's/^([0-9]*).*$/\1/'`
if [ $LIMITSIZEM -lt $filesize ]; then
touch $HOME/.getmail/limitover/$1
echo "over limit $1" >&2
exit
else
#getmailコマンドを実行します。
/usr/bin/getmail --rcfile $HOME/.getmail/config/$1
fi
# 所有権を変更します
sudo chown -R $1:mailuser /var/mdir/$1
スクリプトを作成したら実行権限をつけておきます。
通常、ブラウザがスクリプトを起動する際のユーザーはwww-dataとなると思いますので、起動ユーザーの変更をするためにsudoの設定をします。
# /sbin/visudo
www-data ALL=(ALL:ALL) NOPASSWD: /bin/sh /スクリプトへのフルパス.sh *
www-dataへshコマンドをすべて許可するのは少し危険なので、引数を与えて実行できるスクリプトを限定しています。
PHPのconnect関数の先頭に次の記述を加えます。
shell_exec("sudo -u [クラウドメール受信ユーザー] /bin/sh [スクリプトへのフルパス] $user");
POP環境でもクラウドから受信
先の設定では、roundcubeのPHPからフックしてクラウドメールサーバーへメールチェックする記述ができました。dovecotに通常のメーラーからPOP接続する時も同様のことがしたいと考える人もいるかもしれません。roundcubeから少し離れますが、その方法を記しておきます。こちらの設定はPOPだけでなくIMAP接続時でも動きますので、先のPHPの記述は不要です。
POP接続時にフックしてスクリプトを実行するには、pam-scriptというライブラリを使ってPAM認証時にスクリプトを実行させます。まずはライブラリをインストールします。
# apt install libpam-script
/usr/share/libpam-script/pam-script.d/にdovecotというディレクトリを作ります。その中にpam_script_authというファイルを作り、次のように記述します。ディレクトリ名は後で指定する際に読み替えれば変更が可能ですが、ファイル名は固定である必要があります。
#!/bin/sh
sudo -u [メール受信ユーザー] [前述のメール受信スクリプトへのパス] $PAM_USER
この設定で$PAM_USERには認証時のユーザー名が自動的に入ります。またPAMはroot権限で稼働するので、sudoでユーザーを変更する際のsudoersへの設定は不要です。
/etc/pam.d/dovecotの設定を次のように変更します。
auth requisite pam_unix.so
auth requisite pam_listfile.so item=user sense=allow file=/etc/dovecotusers onerr=fail
auth optional pam_script.so dir=/usr/share/libpam-script/pam-script.d/dovecot
account required pam_unix.so
session required pam_unix.so
通常のOS認証のpam_unixの後に、requisite pam_listfile...という記述がありますがこれはfileで指定してあるユーザーリストと照合してdovecotユーザーかどうかを判断しています。OSに登録してあるユーザーがすべてdovecotのメールアカウントを持っているならこの記述は不要です。
これでauthで認証が成功した時、dirで指定した場所にあるpam_script_authスクリプトを実行する設定になります。ひとつ前の認証の設定をrequisiteにしておくことで、認証に失敗した場合はスクリプトを実行させません。また、スクリプト実行の部分ではoptionalにしておくことで、スクリプトの実行結果を認証に影響させません。
ちなみにpam_script_authファイルを/usr/share/libpam-scriptディレクトリに置くと、多くのアプリの認証の際にスクリプトが実行されるようになります。これはインストール時libpam-scriptが、/pam.d/ディレクトリにあるcommon-から始まる多くの認証で利用される共通のPAMファイルに、pam_scriptの記述を追加するためです。
また、該当の記述はどこにも見つかりませんので断言はできませんが、筆者の環境ではここで設定するpam_script_authファイルの所有権はroot、権限は711か755にしないと稼働しませんでした。ver:1.1.9-4。
roundcubeで受信専用ユーザーを作りたい場合
roundcubeの管理上で、送信させたくないユーザーがあることも考えられます。その際は次のように修正することで実現できました。
rcube_smtp.phpのconnect 関数の$this->error = $this->response = nullの次に記述を追加します。
if ($this->is_send_ok()===FALSE) {
$this->response[]="Error Receive Only User";
$this->error= array('label' => 'receive_only_user','vars' => array('code' => 0));
$this->conn = null;
return false;
}
クラスの最終部分に関数を追加します。ファイルの最後の}はクラスを閉じる}なのでその手前に入れます。
private function is_send_ok() {
//拒否リストを確認して一致したら拒否します
if (isset($_SESSION['user_id']) === FALSE) {
//idが取得できないときはチェックできないのでTRUEにします
return TRUE;
}
//拒否リストへのパスを指定します
$strListPath = "/etc/roundcube/send_deny_list";
$strId = (string)$_SESSION['user_id'];
$blnAllow = TRUE;
$fp = fopen($strListPath,"r");
if ($fp === FALSE) {
//ファイルオープンが失敗した場合はTRUEにします。
return TRUE;
}
while($strLine = fgets($fp)) {
if (rtrim($strLine) === $strId) {
$blnAllow = FALSE;
break;
}
}
fclose($fp);
return $blnAllow;
}
リストファイルの中身は拒否したいIDを次のように指定します
17
28
30
...
指定するIDはMariaDBのroundcube.usersテーブルのuser_idとなります。
補足:証明書の検証に失敗する場合
今回、roundcubeのSMTPはクラウドのサーバーへ直接接続する設定にしました。セキュア通信では通常証明書の検証が行われますのでそのチェックが必要です。また、筆者の環境ではクラウド側のメールサーバーは「さくらインターネット」だったのですが、ここの証明書はDebianにはデフォルトではインストールされていないようでしたので、別途取得しました。加えて本来の契約しているメールドメインと運用しているメールドメインが違うのでそちらでもエラーがおきていました。同様の現象にみまわれている方に向け、それらの方法を補足しておきます。
接続できないのが証明書関連のエラーかどうかという切り分けは、/etc/roundcube/config.inc.phpの$config['smtp_conn_options']の項目を次のようにして、送信できるか試してみることで可能です。
$config['smtp_conn_options'] = array(
'ssl' => array(
'verify_peer' => false,
),
);
設定の反映にはapacheの再起動が必要です。これで送信ができる場合は証明書の証明書の検証でエラーとなっています。
たいていはルート証明書をコピーしてきて指定してやれば解決すると思います。
ルート証明書の取得方法を記述します。ちなみにWindows10とGoogleChrome、さくらインターネットでの環境での説明となります。
さくらのメールサーバー側が使用している証明書をチェックするには、ブラウザでhttps://メールサーバーのドメイン名とするとページが表示されます。
ブラウザのアドレスバーの右側の鍵マークから「証明書」と進みます。「証明書のパス」というタブに記述される一番上がルートCAです。筆者の環境では「Sectigo」となっていました。
「Sectigo」を選択した状態で「証明書の表示」を押すと今度はルートCAだけの証明書が表示されます。
ルート証明書「詳細」タブから「ファイルのコピー」を選択してください。証明書エクスポートウィザードが起動されます。
ウィザードを進めると、使用する形式を聞かれるので「Base 64 encoded X.509(.CER)」を選んでください。その後、わかりやすい場所名前を付けてに保存してください。名前は何でもいいですが英数字推奨です。ここではsectigo.cerとしました。
保存した証明書をダブルクリックすると中身が表示されます。証明書のパスを確認すると今までSectigoという名前だったものが、「USERTrust RSA Certification Authority」となっていると思います。
これはもともともフレンドリー名(≒表示名)が、コピーにより失われただけなので問題はありません。
証明書をDebianにコピーします。/usr/share/ca-certificates/ディレクトリの中(サブディレクトリを作ってもいいです)にコピーします。この時ファイル名の拡張子.cerを.crtに変更してください。
コピーしたファイルの所有権はroot:root、権限は644とします。
/etc/ca-certificates.confの最後の行に、証明書ファイルの/usr/share/ca-certificatesからの相対パスを記述します
update-ca-certificatesを実行します。実行できないときは/usr/sbin/update-ca-certificatesとするとうまくいくかもしれません。
/etc/roundcube/config.inc.phpの$config['smtp_conn_options']を次のようにします。エントリーが存在しない場合は新たに記述してください。
$config['smtp_conn_options'] = array(
'ssl' => array(
'verify_peer' => true,
'cafile' =>'証明書ファイルのパス', ),
);
ここでは証明書をDebianにもインストールしましたが、roundcubeのみで使うのなら好きな場所に証明書を置いてパスを指定するだけでも稼働します。
さらに、筆者の環境では、さくらインターネットで取得したオリジナルのメールドメインと運用上のドメインが違いエラーとなっていました。同等のエラーを解消する場合にはarrayの中に次の行を加えます。
'peer_name' =>'オリジナルのドメイン名',
または先の$config['smtp_server']の設定で、オリジナルのドメイン名にしておく方法もあります。
これはpeer_nameを省略すると、smtp_serverの名前からメールドメイン名をプログラム(PHP)が推定するためです。
メールサーバーの証明書にはオリジナルのドメイン名が表記されています。この値と、運用上のドメイン名は違うと検証が通りません。
なので強制的に検証用のドメイン名を指定してやるか、推定のもととなる接続先の情報にオリジナルのドメインを与えます。
検証にまつわる項目に関してはarchlinux:RoundcubeやPHP:SSL コンテキストオプションを参考にさせていただきました。ありがとうございます。