EC2にメールサーバを構築(複数ドメイン)

# 要件
* 複数ドメインのメールが受信できる。
* メールアドレス毎にUNIX Userは作成しない。
* SMTP,POP3,IMAPで外部ネットワークのMUAから送受信可能。
* メールの容量制限をユーザ毎に設定可能にする。
* MUAでの送信、受信は認証必須。認証はメールアドレス+パスワード
* STARTTLSで送受信は暗号化する
* MUAでの送信と受信のパスワードは同じパスワードを使用できる。


# 概要
* MTAはsendmailからpostfixに変更
* メール配送はMaildir方式にする。
* postfixにバーチャルドメインを設定して、各ドメインからの受信を可能にする。
* ドメインとメールアドレスの管理用UIに[postfix Admin](http://postfixadmin.sourceforge.net/)を採用
* メールアドレス、パスワードの管理はMySQL
* POP3,IMAPでの受信はdovecotを使用する。
* OB25対策をして、通常のプロバイダ接続でも送信可能にする。

# 構築準備
## 環境
* メールサーバにする予定のインスタンスにElastic IPを割り当てておく。
* メールサーバのドメインを決めておく。
* メールサーバ上で、webサーバとphp5以上が動くようにしておく
* メールサーバからMySQLサーバにアクセス可能にする

メールサーバのドメイン：mail.example.jp
PostfixAdminのURL: http://mail.example.jp
データベースサーバ： localhost

として作成したとする。構築に使用したバージョンは、

* postfix 2.6.6
* dovecot 2.0.9
* postfixAdmin 2.3.7
* MySQL 5.6
* Apache 2.4
* PHP 5.5


## メール送信制限の解除とDNS逆引き申請
標準ではEC2インスタンスからの1日あたりのメール送信数は制限されているのと、
Elastic IPで割り当てたIPアドレスを逆引きした場合、アマゾンのドメインとなるためスパム判定される事がある。
制限解除兼、逆引き設定の依頼を行うフォームがあるのでそこから申請する。


Request to Remove Email Sending Limitations
https://portal.aws.amazon.com/gp/aws/html-forms-controller/contactus/ec2-email-limit-rdns-request

申請手順については、Amazon日本公式のドキュメント
http://www.slideshare.net/AmazonWebServicesJapan/aws-42885668
で確認できる

サーバの使用用途は、自社用メールサーバ等、素直に書けばOK。ただし英語で記載しないと無視される。

Elastic IP Address : 123.123.123.123 (使用する Elastic IP)
Reverse DNS Record for EIP 1  : mail.example.jp (メールサーバのドメイン)

を送信。数営業後に返答が決ます。



# sendmailからpostfixへの差し変え
## postifxインストール
`# yum install postfix`

## MTAの切り変え
`# alternatives --config mta`
現在インストールされているMTA一覧が出るので、postfixを選択

## sendmailの停止と、postfixの起動
`# service sendmail stop`
`# chkconfig sendmail off`
`# service postfix start`
`# chkconfig postfix on`

この時点では、外部からのメールはすべて拒否される状態。
動作が確認できるまで拒否設定のままにしておく。
sendmailは削除してもOK

# dovecotのインストール
postfixAdminをインストール前に必要。pop3,imapのポートは閉じたままにしておく。
メールアドレス、パスワード管理にMySQLを使用するので、mysql backendも導入。
`# yum install dovecot dovecot-mysql`

# postfix Admin のインストールと設定
postfixAdminは、ドメイン、メールアドレス、パスワード、メールボックス名をDBで管理するためのWebアプリ。
MTAにはpostfix、pop3,imapサーバはdovecotの利用が想定されている。postfixAdmin自体は、postfixとdovecotの設定は行わないので、自分で設定する。

まず、postfixAdminを使えるようにして、ドメインとメールアドレスをデータベースに登録できる状態にする。

## postfix Adminのダウンロード
パッケージはないので公式よりダウンロード。
[postfixadmin-2.3.7](http://sourceforge.net/projects/postfixadmin/files/postfixadmin/postfixadmin-2.3.7/)を使用した。

ダウンロードしたpostfixadminをwebサーバから見える所に展開する。設定が完了するまでWeb側にアクセス制限をつけておく。展開したファイルにあるINSTALL.txtを参照して設定

## データベースの作成
mysqlに接続し、postfixのデータベースとユーザを作成。パスワードはhogehoge
`CREATE DATABASE postfix CHARSET utf8`
`CREATE USER 'postfix'@'%' IDENTIFIED BY 'hogehoge'`
`GRANT ALL PRIVILEGES ON postfix.* TO postfix@%`
`FLUSH PRIVILEGES`

## postfixadminの設定ファイル変更
config.local.phpを新規作成する。
config.local.phpは、config.inc.phpの設定内容を上書きする仕組みになっているので、
デフォルトから変更する値のみをconfic.local.phpへ記載する。

config.inc.phpをコピーして使用する場合、下部にあるconfig.local.phpの読み込み部分を消さないと無限ループする。

```php:config.local.php
// 設定完了フラグ
$CONF['configured'] = true;
// postfixAdminのURL
$CONF['postfix_admin_url'] = 'http://mail.example.jp';
//日本語表示にする
$CONF['default_language'] = 'ja';
// データベース接続情報
$CONF['database_type'] = 'mysqli';       // mysqlからmysqliに変更
$CONF['database_host'] = 'localhost';    // mysqlのホスト名
$CONF['database_user'] = 'postfix';      // postfixAdminで使用するデータベースのユーザ
$CONF['database_password'] = 'hogehoge'; // パスワード
$CONF['database_name'] = 'postfix';      // データベース名

// 管理者メールアドレス
$CONF['admin_email'] = 'postmaster@example.jp';

// postfixが稼動しているホストとポート
// メールアドレス作成時にwelcomeメールを送信するのに使う。
// welcomeメールを送っておかないとmailboxが作成されないため、pop3でエラーになる。
$CONF['smtp_server'] = 'localhost';
$CONF['smtp_port'] = '25';

//標準の管理者メールアドレス
$CONF['default_aliases'] = array (
    'abuse' => 'abuse@example.jp',
    'hostmaster' => 'hostmaster@example.jp',
    'postmaster' => 'postmaster@example.jp',
    'webmaster' => 'webmaster@example.jp'
);

// username@domain.tldのメールの配送を、domain.tld/username にする。
$CONF['domain_path'] = 'YES';
$CONF['domain_in_mailbox'] = 'NO';

// 自動応答ドメイン。DNSに設定しなくてもよい
$CONF['vacation_domain'] = 'autoreply.example.jp';

//mainメニューへのリンク
$CONF['user_footer_link'] = "http://mail.example.jp/main.php";

// ページ下部にトップへ戻るリンク設定
$CONF['footer_text'] = 'Return to mail.example.jp';
$CONF['footer_link'] = 'http://mail.example.jp';
```

## システム管理者の登録
http://mail.example.jp/setup.php にブラウザでアクセスする。初回アクセス時に管理用テーブルが作成される。
動作に必要なモジュールが足りない場合画面に表示されるので、インストール後リロードしてチェック。
エラーが表示されなくなったら、画面に従い管理者を登録。セットアップパスワードが表示されるので、
config.local.phpに以下の行を追加する。

```php:config.local.php
$CONF['setup_password'] = '画面に表示されたセットアップパスワード'
```

http://mail.example.jp にアクセスし、ログインできる事を確認したらsetup.phpは削除するかパーミッションを落としてアクセス不可にしておく。


## ドメインとメールアドレスの登録
http://mail.example.jp に管理者でログインし、画面に従ってテスト用のドメインとメールアドレスを作成する。作成したドメインのMXレコードを設定し、メールサーバへメールが送信されるようにしておく。

# postfixの設定
postfixAdminの設定に合わせる。
バーチャルホスト宛のメールは /var/spool/virtual/ 以下に配送し、user@domain.ltdのメール配送のディレクトリは、/var/spool/virtual/domain.ltd/user/ にする

## MailDir形式への変更

```/etc/postfix/main.cf
home_mailbox = Maildir/ 
```
メールの配送方法をmbox形式からMailDir形式に変更する。
ついでに、/etc/skel へMaildirを作成し、新規ユーザでも自動作成されるようにする。

## バーチャルドメインの設定
[Postfix バーチャルドメインホスティング Howto](http://www.postfix-jp.info/trans-2.3/jhtml/VIRTUAL_README.html)を参考に作成。

### バーチャルドメイン用のユーザとメール用ディレクトリの作成
バーチャルドメインで受信したメールの所有者と保存先のディレクトリを作成。
ユーザはuid:10000で作成しておく。

`# adduser -u 10000 vmailuser`
`# mkdir /var/spool/virtual`
`# chmown 10000.10000 /var/spool/virtual`

### DBへのアクセス用クエリ設定
postfixadminのDOCUMENTS/POSTFIX_CONF.txtを参考に、postfixAdminで登録したメールアドレス関連の情報を、postfixからMySQLに接続して取得するクエリを作成する。 /etc/postfix/sql ディレクトリを作成し、以下のファイルを新規作成

```/etc/postfix/sql/mysql_virtual_domains_maps.cf
user = postfix
password = hogehoge
hosts = localhost
dbname = postfix
query          = SELECT domain FROM domain WHERE domain='%s' AND active = '1'
#query          = SELECT domain FROM domain WHERE domain='%s'
#optional query to use when relaying for backup MX
#query           = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '0' AND active = '1'
#expansion_limit = 100
```

```/etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf
# handles catch-all settings of target-domain
user = postfix
password = hogehoge
hosts = localhost
dbname = postfix
query  = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
```

```/etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf
user = postfix
password = hogehoge
hosts = localhost
dbname = postfix
query = SELECT maildir FROM mailbox,alias_domain WHERE alias_domain.alias_domain = '%d' and mailbox.username = CONCAT('%u', '@', alias_domain.target_domain) AND mailbox.active = 1 AND alias_domain.active='1'
```

```/etc/postfix/sql/mysql_virtual_alias_domain_maps.cf
user = postfix
password = hogehoge
hosts = localhost
dbname = postfix
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('%u', '@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
```

```/etc/postfix/sql/mysql_virtual_alias_maps.cf
user = postfix
password = hogehoge
hosts = localhost
dbname = postfix
query = SELECT goto FROM alias WHERE address='%s' AND active = '1'
#expansion_limit = 100
```


```/etc/postfix/sql/mysql_virtual_mailbox_maps.cf
user = postfix
password = hogehoge
hosts = localhost
dbname = postfix
query           = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'
#expansion_limit = 100
```

メール容量制限取得。容量制限を行わないなら不要

```/etc/postfix/sql/mysql_virtual_mailbox_limit_maps.cf
user = postfix
password = hogehoge
hosts = localhost
dbname = postfix
query = SELECT quota FROM mailbox WHERE username='%s' AND active = '1'
```


### バーチャルドメイン用メールボックスの設定
以下の設定を追記し、バーチャルドメインのメールを配送できるようにする


```/etc/postfix/main.cf
# Virtual mailbox 基本設定
virtual_mailbox_base = /var/spool/virtual
virtual_minimum_uid = 100
virtual_uid_maps = static:10000
virtual_gid_maps = static:10000

# メールアドレス関連情報の取得
virtual_mailbox_domains = proxy:mysql:/etc/postfix/sql/mysql_virtual_domains_maps.cf
virtual_alias_maps =
   proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf,
   proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_maps.cf,
   proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf
virtual_mailbox_maps =
   proxy:mysql:/etc/postfix/sql/mysql_virtual_mailbox_maps.cf,
   proxy:mysql:/etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf

# quota(容量制限)サポート設定。容量制限をしないなら不要
# 容量制限を越えた場合、送信元にエラーメールを返信する。
virtual_create_maildirsize = yes
virtual_mailbox_extended = yes
virtual_mailbox_limit_maps = mysql:/etc/postfix/sql/mysql_virtual_mailbox_limit_maps.cf
virtual_mailbox_limit_override = yes
virtual_maildir_limit_message = Sorry, the user's maildir has overdrawn his diskspace quota, please try again later.
virtual_overquota_bounce = yes 
```

この時点でpostfixを再起動し、mailコマンドでテスト用に作成したドメイン宛てにメールを送信してチェック。user@domain.ltdに送信したメールが、/var/spool/virtual/domain.ltd/user/new 以下に配信されていればOK


# dovecotの設定

## 
postfixAdminで登録した情報を取得するためのクエリを作成。以下の名前で新規作成する。
connect = の部分は改行するとダメ。

```/etc/dovecot/dovecot-sql.conf.ext
#
# for postfixadmin
#
connect = host=localhost dbname=postfix user=postfix password=hogehoge
driver = mysql

# Default password scheme.
# depends on your $CONF['encrypt'] setting:
# md5crypt  -&gt; MD5-CRYPT
# md5       -&gt; PLAIN-MD5
# cleartext -&gt; PLAIN
default_pass_scheme = MD5-CRYPT

password_query = SELECT username AS user,password FROM mailbox WHERE username = '%u' AND active='1'
user_query = SELECT CONCAT('/var/spool/virtual/', maildir) AS mail, 10000 AS uid, 10000 AS gid, CONCAT('*:bytes=', quota) AS quota_rule FROM mailbox WHERE username = '%u' AND active='1'
```

## 認証
メールアドレス+パスワードでPOP3,IMAP認証可能にし、PAMでのユーザ認証は無効にする。
/etc/dovecot/conf.d/10-auth.confを変更する。

```/etc/dovecot/conf.d/10-auth.conf
...
# STARTTLSで暗号化するので、plaintext認証を有効にする
disable_plaintext_auth = no
...

#コメントアウト
#!include auth-system.conf.ext

#コメントアウトを外す
!include auth-sql.conf.ext
...
```
/etc/dovecot/conf.d/auth-sql.conf.extを変更する

```/etc/dovecot/conf.d/auth-sql.conf.ext
# Authentication for SQL users. Included from auth.conf.
#
# <doc/wiki/AuthDatabase.SQL.txt>

passdb {
  driver = sql

  # Path for SQL configuration file, see example-config/dovecot-sql.conf.ext
  # 先程作成したファイルを指定
  args = /etc/dovecot/dovecot-sql.conf.ext
}

# "prefetch" user database means that the passdb already provided the
# needed information and there's no need to do a separate userdb lookup.
# <doc/wiki/UserDatabase.Prefetch.txt>
#userdb {
#  driver = prefetch
#}

userdb {
  driver = sql
  # 先程作成したファイルを指定
  args = /etc/dovecot/dovecot-sql.conf.ext
}

# If you don't have any user-specific settings, you can avoid the user_query
# by using userdb static instead of userdb sql, for example:
# <doc/wiki/UserDatabase.Static.txt>
#userdb {
  #driver = static
  #args = uid=vmail gid=vmail home=/var/vmail/%u
#}
```

SSL/TLSを使用可能にする

```/etc/dovecot/conf.d/10-ssl.conf
 # SSL/TLS support: yes, no, required. <doc/wiki/SSL.txt>
 ssl = yes
```

## ローカルメールの設定
postfixでのメール配送をMaidir形式にしたので、dovecotのデフォルトもMaidir形式にする。
mail_locationはローカルユーザ向けの設定なので、各ユーザのホームディレクトリを指定。

```/etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:~/Maildir
```

ここまで設定すれば、pop3,imapは使用できるようになるが、
MUAがThunderBirdの場合、送信サーバの認証ができないとメールアカウント登録でエラーとなる。



## Postfix用のSMTP認証用設定

PostfixでDovecotの認証を使用するための設定を記載。
設定後、dovecotを再起動し /var/spool/postfix/private/auth が作成されている事を確認する。

### dovecot側の設定
```/etc/dovecot/conf.d/10-master.conf
   # Postfix smtp-auth
   unix_listener /var/spool/postfix/private/auth {
      mode = 0666
      user = postfix
      group = postfix
   }
```

### postfix側の設定

```/etc/postfix/main.cf

myhostname = mail.example.jp
myorigin = $myhostname
// 外部からのメールを受信可能にする
inet_interfaces = all

#
# SASL(SMTP認証) 有効化
#
smtpd_sasl_auth_enable = yes
smtpd_recipient_restrictions = permit_mynetworks permit_sasl_authenticated reject_unauth_destination
# 認証にdovecotを使用
smtpd_sasl_type = dovecot
# 認証用ソケット。/var/spool/postfix からの相対パス
smtpd_sasl_path = private/auth
# 一部MUAもサポート
broken_sasl_auth_clients = yes

#
# TLS(STARTTLS) 有効化
#
smtpd_use_tls = yes
smtld_tls_loglevel = 1
# dovecotの証明書を流用.正式な証明書があるならそちらを指定する。
smtpd_tls_cert_file = /etc/pki/dovecot/certs/dovecot.pem
smtpd_tls_key_file = /etc/pki/dovecot/private/dovecot.pem
smtpd_tls_session_cache_database = btree:/etc/postfix/smtpd_scache
smtpd_tls_session_cache_timeout = 3600s
```

通常、メールを受信するドメインは、$mydestinationに指定するが、
バーチャルドメインで使用するドメインは、$mydestination に**追加してはいけない**。


# OB25対策
プロバイダによっては[OB25](http://www.atmarkit.co.jp/fsecurity/column/ueno/32.html)により、25番ポートでのメール送信ができないのでサブミッションポートを使用可能にする。

```/etc/postfix/master.cf
submission inet n       -       n       -       -       smtpd
#  -o smtpd_tls_security_level=encrypt
   -o smtpd_sasl_auth_enable=yes
   -o smtpd_client_restrictions=permit_sasl_authenticated,reject
```

SASL認証を有効にし、サブミッションポートを利用しての送信は認証成功以外は送信拒否とする。