Mail Server: PostfixとDovecotを使ったメールサーバの構築

PostfixとDovecotを使ったメールサーバはポピュラーなので、サイトを検索するといくつか設定方法がヒットしますが、ある程度セキュリティを取り入れた設定となるとあまり実例が見つかりません。そのなかで、分かりやすかったサイトがあったのでそれを参考に構築してみました。

SSL/TLSでIMAP、STARTTLSでSMTP認証を行います。メールアカウントとパスワードはLinuxのシステムを使わずPostfix側で行います。メーラー(MUA)はLMTPプロトコルでDovecotからPostfixに送りインターネットに送信するようにします。

パッケージのインストール

  • postfix(必須)
  • dovecot(必須)
  • postgrey(オプション)
  • opendkim(オプション)
  • fail2ban(オプション)
  • RoundCube – webmail(オプション)
apt install postfix dovecot-core dovecot-imapd dovecot-lmtpd postfix-policyd-spf-python sasl2-bin

SSL Certificate

SSL CertificateはフリーのStartSSLを使いました。まずStartSSLサイトでユーザー登録してサーバー鍵で作ったCSRを使いサイトで登録したホスト名に対してCSRをペーストします。するとzipファイルを作ってくれるのでそれをダウンロードします。zipファイルにはいくつかサーバごとにzipが纏められていますが、OtherServer.zipを展開して、サーバ証明と中間証明を結合します。StartSSLのルート証明書も一緒についてきますが、Ubuntu(16.04 LTS Xenial)等のディストリビューションにはすでにあるのでそれを使います。

Let’s Encryptを使う場合はウェブサーバーが必要になります。 (オススメ)

ファイヤーウォールで次のポートを開けます。

port 25 (SMTP), 587(submission), 143(imap) and 993 (imaps)
optional: 80(http), 443(https)

メールアカウント

パスワードは平文パスワードを暗号化したHUSHを使います。次のようにして作成します。

sudo doveadm pw -s SHA512-CRYPT -p <平文パスワード>

暗号化したHUSH情報は /etc/dovecot/usersに保存します。

<メールアカウント>:<HUSH>

パスワードのチェックは次のようにします。

sudo doveadm pw -t ’HUSH’

例)doveadm pw -t ’{SHA512-CRYPT}$6$VaEOV5m...'

postmaster_addressがないというエラーがある場合、/etc/dovecot/conf.d/15-lda.confに次のように追加します。

postmaster_address = postmaster@{server名}

ログの解析

エラーがでたらサイトで検索してみるにしても、ログをみて原因を発見し簡単に直ることも多いものです。ターミナルを別に開き、tail -f /var/log/mail.logで常時監視します。さらに、認証でのエラーがメールサーバの時は特に多いので、/etc/dovecot/conf.d/10-logging.confで次のようにしてデバッグします。設定が問題なく完成したらもとに戻します。

auth_debug_passwords = yes

Fail2banのインストール(Optional)

ユーザーの制限にFail2banがいいとのことので入れてみました。Iptablesのlimitとあまり違いがないみたいだけど、メールで知らせてくれたりと機能的には優れています。

設定はとても簡単で/etc/fail2ban/jail.dに設定ファイル追加だけです。Ubuntu Xenialではdefaults-debian.confがインストールされていて、sshがデフォルトで設定してあります。それを参考に例えばメールサーバーの場合、次の内容で設定ファイルを作ります。

[postfix-rbl]
 enabled = true
sudo systemctl restart fail2ban

で再起動すれば新しい設定が有効になります。iptablesで調べるには、

sudo iptables -S
 -P INPUT ACCEPT
 -P FORWARD ACCEPT
 -P OUTPUT ACCEPT
 -N f2b-postfix-rbl
 -N f2b-sshd
 -A INPUT -p tcp -m multiport --dports 25,465,587 -j f2b-postfix-rbl
 -A INPUT -p tcp -m multiport --dports 22 -j f2b-sshd
 -A f2b-postfix-rbl -j RETURN
 -A f2b-sshd -j RETURN

とこのように表示されFail2banが動いているのが分かります。

opendkimのインストール

sudo apt-get install opendkim opendkim-tools

/etc/opendkim.conf

Syslog			yes
UMask			007
Domain                  hottuna.tk
KeyFile                 /etc/dkimkeys/myselector.private
Selector                myselector
Socket                  inet:8891@localhost
UserID                  opendkim
PidFile                 /var/run/opendkim/opendkim.pid
OversignHeaders		From
TrustAnchorFile         /usr/share/dns/root.key

キーを作成します。

cd /etc/dkimkeys 
sudo opendkim-genkey  -s myselector -d <server name>
sudo chown opendkim: *

DNSレコードのtxtを編集します。

myselector._domainkey IN TXT "v=DKIM1; k=rsa; s=email; p=xxxxxxxxxxxxxxxxxxxxxxxxxx"

postfixの設定をします

/etc/postfix/main.cf

milter_default_action = accept
milter_protocol       = 2
smtpd_milters         = inet:localhost:8891
non_smtpd_milters     = inet:localhost:8891

/etc/default/opendkim

SOCKET="inet:8891@localhost"

再起動します

systemctl restart opendkim
systemctl restart postfix

dkimのテストしてみます

host -t TXT myselector._domainkey.hottuna.tk

または

dig myselector._domainkey.hottuna.tk txt

結果

; <<>> DiG 9.11.3-1ubuntu1.8-Ubuntu <<>> myselector._domainkey.hottuna.tk txt
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62394
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;myselector._domainkey.hottuna.tk. IN	TXT

;; ANSWER SECTION:
myselector._domainkey.hottuna.tk. 3600 IN TXT	"v=DKIM1; h=sha256; k=rsa;  p=.....

メールヘッダーのチェックの方法は次のサイトにメールヘッダーをコピーして調べます

1	Authentication-Results	spf=pass (sender IP is xx.xx.xx.xx) smtp.mailfrom=hottuna.tk; hotmail.com; dkim=pass (signature was verified) header.d=hottuna.tk;hotmail.com; dmarc=temperror action=none header.from=hottuna.tk;

他DNSレコード編集

逆引きはVPS等の管理者に連絡。

MXA[IP ADDRESS]
MXmx.[DOMAIN NAME]
TXTv=spf1 +a:[DOMAIN NAME]
+a:mx.[DOMAIN NAME] +mx ~a
MYSELECTOR._DOMAINKEYTXTv=DKIM1; h=sha256; k=rsa; s=email; p=M……

設定例

postfix

/etc/postfix/main.cf

compatibility_level = 2 
command_directory = /usr/sbin 
daemon_directory = /usr/lib/postfix/sbin 
data_directory = /var/lib/postfix 
mail_owner = postfix 
myhostname = mx.<server name> 
mydomain = <server name> 
myorigin = $mydomain 
inet_interfaces = all 
mydestination = localhost, mx.$mydomain 
local_recipient_maps = unix:passwd.byname $alias_maps 
unknown_local_recipient_reject_code = 550 
mynetworks = 127.0.0.0/8, 192.168.10.0/24, 192.168.1.0/27, 192.168.250.1/32, 192.168.200.0/24 
virtual_transport = lmtp:unix:private/dovecot-lmtp 
virtual_mailbox_domains = $mydomain 
virtual_alias_maps = hash:/etc/postfix/virtual_aliases 
relay_domains =  
relayhost = 
alias_maps = hash:/etc/aliases 
alias_database = hash:/etc/aliases 
smtpd_banner = $myhostname ESMTP 
debugger_command = 
         PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin 
         ddd $daemon_directory/$process_name $process_id & sleep 5 
sendmail_path = /usr/sbin/postfix 
newaliases_path = /usr/bin/newaliases 
mailq_path = /usr/bin/mailq 
setgid_group = postdrop 
inet_protocols = ipv4 
message_size_limit = 10485760
#smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
#smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt 
smtpd_tls_cert_file = /etc/ssl/certs/<server name>.crt 
smtpd_tls_key_file = /etc/ssl/private/<server name>.key 
#smtpd_tls_security_level = encrypt
#smtp_tls_security_level = encrypt
smtpd_tls_security_level = may 
smtp_tls_security_level = may 
smtpd_tls_mandatory_ciphers = high
smtpd_tls_mandatory_exclude_ciphers = aNULL, MD5
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
smtpd_sasl_auth_enable = yes 
smtpd_sasl_type = dovecot 
smtpd_sasl_path = private/auth 
smtpd_tls_auth_only = yes 
smtpd_recipient_restrictions = permit_sasl_authenticated, 
        reject_invalid_hostname, 
        reject_unknown_recipient_domain, 
        reject_unauth_destination, 
        reject_rbl_client sbl.spamhaus.org, 
        permit 
smtpd_helo_restrictions =
        permit_mynetworks,
        permit_sasl_authenticated,
#        check_client_access cidr:/etc/postfix/local.cidr,
        reject_invalid_helo_hostname,
        reject_non_fqdn_helo_hostname,
        reject_unknown_helo_hostname
recipient_delimiter = + 
non_smtpd_milters=inet:127.0.0.1:8891 
smtpd_milters=inet:127.0.0.1:8891 
milter_default_action = accept 
milter_protocol = 2

!SSLv2のエラーがある場合は次のように設定します。

smtpd_tls_mandatory_protocols = TLSv1

Optional:

[ここから]

gmail宛のメールをリレーサーバーにする場合:

/etc/postfix/main.cfに追加します。

transport_maps = hash:/etc/postfix/transport
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
mtp_sasl_mechanism_filter = plain, login
smtp_use_tls = yes

/etc/postfix/transport

gmail.com smtp:[smtp.gmail.com]:587

/etc/postfix/sasl_passwd

[smtp.gmail.com]:587 <user name>@gmail.com:<gmail password>
sudo chmod 400 /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/transport

[ここまで]

/etc/postfix/master.cf

smtp      inet  n       -       y       -       -       smtpd 
submission inet n       -       y       -       -       smtpd 
  -o smtpd_sasl_auth_enable=yes 
pickup    unix  n       -       y       60      1       pickup 
cleanup   unix  n       -       y       -       0       cleanup 
qmgr      unix  n       -       n       300     1       qmgr 
tlsmgr    unix  -       -       y       1000?   1       tlsmgr 
rewrite   unix  -       -       y       -       -       trivial-rewrite 
bounce    unix  -       -       y       -       0       bounce 
defer     unix  -       -       y       -       0       bounce 
trace     unix  -       -       y       -       0       bounce 
verify    unix  -       -       y       -       1       verify 
flush     unix  n       -       y       1000?   0       flush 
proxymap  unix  -       -       n       -       -       proxymap 
proxywrite unix -       -       n       -       1       proxymap 
smtp      unix  -       -       y       -       -       smtp 
relay     unix  -       -       y       -       -       smtp 
showq     unix  n       -       y       -       -       showq 
error     unix  -       -       y       -       -       error 
retry     unix  -       -       y       -       -       error 
discard   unix  -       -       y       -       -       discard 
local     unix  -       n       n       -       -       local 
virtual   unix  -       n       n       -       -       virtual 
lmtp      unix  -       -       y       -       -       lmtp 
anvil     unix  -       -       y       -       1       anvil 
scache    unix  -       -       y       -       1       scache 
maildrop  unix  -       n       n       -       -       pipe 
  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient} 
uucp      unix  -       n       n       -       -       pipe 
  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient) 
ifmail    unix  -       n       n       -       -       pipe 
  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient) 
bsmtp     unix  -       n       n       -       -       pipe 
  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient 
scalemail-backend unix  -       n       n       -       2       pipe 
  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension} 
mailman   unix  -       n       n       -       -       pipe 
  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py 
  ${nexthop} ${user}

/etc/postfix/reject_sender

touch reject_sender
postmap /etc/postfix/reject_sender

/etc/postfix/virtual_aliases

postmaster          root 
webmaster           root
 # Person who should get root's mail 
root                (user1)
info@<server name>  (user1)
sudo adduser --system --home /var/vmail --uid 550 --group --disabled-login vmail
sudo postmap /etc/postfix/virtual_aliases

dovecot

/etc/dovecot/dovecot.conf

!include_try /usr/share/dovecot/protocols.d/*.protocol 
listen = * 
dict { 
} 
!include conf.d/*.conf 
!include_try local.conf

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

disable_plaintext_auth = yes 
auth_mechanisms = plain login
!include auth-system.conf.ext

/etc/dovecot/conf.d/10-logging.conf: (デバッグ用です)

log_path = syslog
auth_debug_passwords = yes  # for debug
plugin { 
}

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

mail_location = maildir:/var/vmail/%d/%n 
passdb { 
  driver = passwd-file 
  args = scheme=CRYPT username_format=%u /etc/dovecot/users 
} 
userdb { 
  driver = static 
  args = uid=vmail gid=vmail home=/var/vmail/%d/%n 
} 
namespace inbox { 
  inbox = yes
}

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

service imap-login { 
  inet_listener imap { 
    port = 0 
  } 
  inet_listener imaps { 
    port = 993 
    ssl = yes
  } 
} 
service pop3-login { 
  inet_listener pop3 { 
  } 
  inet_listener pop3s { 
  } 
} 
service lmtp { 
  unix_listener /var/spool/postfix/private/dovecot-lmtp { 
    mode = 0666 
    user = postfix 
    group = postfix 
  } 
} 
service imap { 
} 
service pop3 { 
} 
service auth { 
  unix_listener /var/spool/postfix/private/auth { 
    mode = 0666 
    user = postfix 
    group = postfix 
  } 
  user = $default_internal_user 
} 
service auth-worker { 
  user = $default_internal_user 
} 
service dict { 
  unix_listener dict { 
  } 
}

/etc/dovecot/conf.d/10-ssl.conf

ssl = required 
ssl_cert = </etc/letsencrypt/live/<mydomain>/fullchain.pem
ssl_key = </etc/letsencrypt/live/<mydomain>/privkey.pem
ssl_protocols = !SSLv3
ssl_cipher_list = kEECDH:+kEECDH+SHA:kEDH:+kEDH+SHA:+kEDH+CAMELLIA:kECDH:+kECDH+SHA:kRSA:+kRSA+SHA:+kRSA+CAMELLIA:!aNULL:!eNULL:!SSLv2:!RC4:!MD5:!DES:!EXP:!SEED:!IDEA:!3DES

/etc/dovecot/conf.d/15-lda.conf

postmaster_address = postmaster@<server name>
protocol lda { 
}

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

otocol imap { 
}

/etc/dovecot/conf.d/20-lmtp.conf

protocol lmtp { 
}

/etc/dovecot/conf.d/20-pop3.conf

protocol pop3 { 
}

Optional: ユーザーパスワードの作成

pass-creator.sh

#!/bin/sh

pass=`date +%N | sha256sum | base64 | head -c 16 ; echo`
domain=`postconf -f | grep ^mydomain | sed 's/^mydomain\s*=\s*\(.*\)$/\1/'`
passfile="users"
readable_pass="users-pass.txt"
DOVECOT_CONF="/etc/dovecot"

echo
echo -n "Name: "
read name
echo
mailpass=`doveadm pw -s SHA512-CRYPT -p $pass`
doveadm pw -t $mailpass -p $pass | grep -q \(verified\)

if [ "$?" -eq "0" ]; then
        echo "Password check --> OK"
else
        echo "Password check --> Failed"
        exit 1
fi

echo "${name}@${domain}:${mailpass}" >> $DOVECOT_CONF/$passfile
echo "${name}@${domain} : ${pass}" >> $readable_pass
echo
echo "Account  : ${name}@${domain}"
echo "Password : ${pass}"
echo
exit 0

Postgrey

/etc/postfix/main.cf

smtpd_recipient_restrictions =
  check_policy_service inet:127.0.0.1:10023

/etc/default/postgrey

OSTGREY_OPTS="--inet=10023"

Roundcubeの設定

install

apt install roundcube roundcube-mysql roundcube-plugins php-net-idna2

データベース
MySQL:

CREATE DATABASE roundcubemail /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
GRANT ALL PRIVILEGES ON roundcubemail.* TO 'roundcube'@'<roundcube IP>' IDENTIFIED BY '<password>';
FLUSH PRIVILEGES;

RoundCube:

roundcube HOST:
cd /usr/share/dbconfig-common/data/roundcube/{install | upgrade}
mysql -h <mysql IP> -u roundcube -D roundcubemail -p < mysql

/etc/roundcube/debian-db.php

<?php
$dbuser='roundcube';
$dbpass='<password>';
$basepath='';
$dbname='roundcubemail';
$dbserver='<IP or localhost>';
$dbport='3306';
$dbtype='mysql';

/etc/roundcube/config.inc.php

<?php
$config = array();
include_once("/etc/roundcube/debian-db-roundcube.php");
$config['default_host'] = 'ssl://<host name>';
$config['smtp_server'] = 'tls://<host name>';
$config['smtp_port'] = 587;
$config['smtp_user'] = '%u';
$config['smtp_pass'] = '%p';
$config['support_url'] = '';
$config['product_name'] = 'Roundcube Webmail';
$config['des_key'] = 'x*********************';
$config['plugins'] = array(
'archive',
'zipdownload',
);
$config['skin'] = 'larry';

/etc/roundcube/defaults.inc.php

...

$config['smtp_server'] = '<host name>';
$config['smtp_port'] = 587;
$config['smtp_user'] = '';
$config['smtp_pass'] = '';
$config['smtp_auth_type'] = '';
$config['smtp_auth_cid'] = null;
$config['smtp_auth_pw'] = null;
$config['smtp_helo_host'] = '';
$config['smtp_timeout'] = 0;
$config['smtp_conn_options'] = null;
$config['mime_types'] = '/etc/roundcube/mime.types';
$config['log_dir'] = '/var/log/roundcube/';
$config['temp_dir'] = '/var/cache/roundcube';


...
...

$config['cipher_method'] = 'AES-256-CBC';

...

/etc/hosts(ポートフォワーディングしている場合)

...

<HOST IP> <host name>  # e.g: 192.168.1.1  mymail.com

...

/etc/nginx/sites-enabled/mail

server {
    listen         80;
    server_name    <host name>;

    #location = /50x.html {
    #    root /usr/share/nginx/html;
    #}
    #error_page 404 /404.html;
    #error_page 500 502 503 504 /50x.html;

     location /webmail {
        alias /var/lib/roundcube;
        index index.php
        error_log /var/log/nginx/roundcube.error;
        access_log /var/log/nginx/roundcube.access;
        # Favicon
        location ~ ^/webmail/favicon.ico$ {
            root /var/lib/roundcube/skins/classic/images;
            log_not_found off;
            access_log off;
            expires max;
        }
        # Robots file
        location ~ ^/webmail/robots.txt {
            allow all;
            log_not_found off;
            access_log off;
        }
        # Deny Protected directories 
            location ~ ^/webmail/(config|temp|logs)/ {
            deny all;
        }
        location ~ ^/webmail/(README|INSTALL|LICENSE|CHANGELOG|UPGRADING)$ {
            deny all;
        }
        location ~ ^/webmail/(bin|SQL)/ {
            deny all;
        }
        # Hide .md files
        location ~ ^/webmail/(.+\.md)$ {
                deny all;
        }
        location ~ ^/webmail/\. {
                deny all;
                access_log off;
                log_not_found off;
        }
        #Roundcube fastcgi config
        location ~ /webmail(/.*\.php)$ {
            fastcgi_pass unix:/run/php/php7.2-fpm.sock;
            fastcgi_split_path_info ^/webmail/(.+\.php)(/.*)$;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $request_filename;
            fastcgi_param PATH_INFO $fastcgi_path_info;
            fastcgi_param PHP_VALUE open_basedir="/tmp/:/usr/share/roundcube:/var/cache/roundcube:/var/lib/roundcube:/etc/roundcube:/var/lib/roundcube/temp:/var/log/roundcube:/usr/share/php";
            include fastcgi_params;
        }
    }
}

/etc/php/7.2/fpm/php.ini

date.timezone = Asia/Tokyo
systemctl reload php7.2-fpm

インストーラーの設定:(設定後’false’に変えます)

/etc/roundcube/defaults.inc.php

$config['enable_installer'] = true;
cd /usr/share/roundcube
ln -s /etc/roundcube config
cd /var/lib/roundcube
ln -s /usr/share/roundcube/installer/ .
cd /etc/roundcube
chown root:www-data config.inc.php debian-db.php defaults.inc.php 
chmod 640 config.inc.php debian-db.php defaults.inc.php
mkdir -p /var/cache/roundcube
chown www-data: /var/log/roundcube /var/cache/roundcube
chmod 640 /var/log/roundcube /var/cache/roundcube
wget http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
mv mime.types /etc/roundcube

debian系は設定が独自なのでDBチェックエラーは無視します。

メールの送受信のテストが済んだらインストーラーを外します。

$config['enable_installer'] = false;
rm /var/lib/roundcube/installer

Roundcubeのスキン

有料のスキンは機能豊富で良いのですが無料でシンプルなスキンをインストールしてみます。

https://github.com/EstudioNexos/mabola

追記:multiple domainの設定

非常に困ったことに古いドメインを使わざるを得ない状況なったので調べてみました。

DDNSの設定:

IPアドレス、mxドメインはそのままです。

Let’s encrypt:

サブドメインだけでなく、old_domainと組み合わせてマルチドメイン化ができます。これによって認証が可能になります。

Postfix:

ログや検索で調べた結果、つぎのところを変更します。

mydestination = localhost, $myhostname, mx.$old_domain
virtual_mailbox_domains = $mydomain, $old_domain

これで着信はできるようになりました。

References:

  • https://appbead.com/blog/how-to-setup-mail-server-on-debian-8-jessie-with-postfix-dovecot-1.html
  • https://appbead.com/blog/how-to-setup-mail-server-on-debian-8-jessie-with-postfix-dovecot-2.html
  • Roundcube
  • RainLoop Webmail
  • https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-dkim-with-postfix-on-debian-wheezy
  • Arch:OpenDKIM
  • Debian:OpenDKIM