ISP 邮件服务器 2.x HowTo
![]() Alpine Linux 2.x 支持已于 2015 年 11 月结束。请升级 |
全功能邮件服务器
本文档描述了最新 Alpine Linux 2.x 平台的安装过程。本文档的目标是描述如何设置 postfix、dovecot、clamav、dspam、roundecube 和 postfixadmin 以构建一个全功能的“ISP”级别邮件服务器。我们建议在不早于 2.2 版本的 Alpine Linux 上运行服务器,因为 2.2 版本修复了许多重要的错误和安全问题。(另请参阅先前在 1.10 平台上的安装。还有关于从 v1 升级到 v2 的说明。)
服务器必须提供
- 多个虚拟域名
- 每个域的管理员(用于添加/删除虚拟账户)
- 每个域/账户的配额支持
- 通过 IMAP / IMAPS / POP3 / POP3S 下载电子邮件
- 为通过 TLS 或 SSL 验证的用户中继电子邮件(Submission / SMTPS 协议)
- 标准过滤器(病毒/垃圾邮件/rbl/等)
- Web 邮件客户端
- 增值服务
设置 Lighttpd + PHP
PostfixAdmin 需要 php pgpsql 和 imap 模块,所以我们在此步骤中进行安装。
apk add lighttpd php php-pgsql php-imap
停止并移除 mini_httpd,并将 ACF 移动到 lighttpd;我们将其设置为多域虚拟 Web 服务器(将 host.example.com 替换为实际域名)
rc-service mini_httpd stop apk del mini_httpd mkdir -p /var/www/domains/host.example.com/www ln -s /usr/share/acf/www /var/www/domains/host.example.com/www/acf
编辑 /var/www/domains/host.example.com/www/index.html 以放置一个简单的重定向页面
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>host.example.com Redirector</title> </head> <body> <ul> <li><a href="/acf">ACF</a></li> <li><a href="/postfixadmin">PostfixAdmin</a></li> <li><a href="/roundcube">Roundcube</a></li> </ul> </body>
编辑 /etc/lighttpd/mod_cgi.conf 以通过添加 "" => "" cgi 处理程序来服务 haserl 文件,并将 /acf/cgi-bin 视为 CGI 目录(移除 '^')
$HTTP["url"] =~ "/cgi-bin/" { # disable directory listings dir-listing.activate = "disable" # only allow cgi's in this directory cgi.assign = ( ".pl" => "/usr/bin/perl", ".cgi" => "/usr/bin/perl", "" => "" ) }
获取 Web 证书并安装它。您有两个选择:1. 如果您想使用自签名证书,可以使用使用 ACF 生成 SSL 证书或使用 ACF 1.9 生成 SSL 证书中的说明来生成它。 2. 使用使用 setup-acf 命令创建的证书。 3. 从受信任的根证书颁发机构获取证书。
选项 1: 如果您创建自己的自签名证书,可以使用以下命令创建 "server-bundle.pem" 和 "ca-crt.pem" 文件
openssl pkcs12 -nokeys -cacerts -in certificate.pfx -out /etc/lighttpd/ca-crt.pem openssl pkcs12 -nodes -in certificate.pfx -out /etc/lighttpd/server-bundle.pem chown root:root /etc/lighttpd/server-bundle.pem chmod 400 /etc/lighttpd/server-bundle.pem
注意: 服务器证书和密钥都在 server-bundle.pem 文件中,因此至关重要的是该文件只能由用户 "root" 读取。
选项 2: 如果您更愿意只使用使用 setup-acf 命令创建的默认证书,那么您需要执行以下操作
setup-acf
在上述过程中,mini_httpd 将被启动(如果尚未启动),并且将创建一个证书。完成 setup-acf 步骤后,执行以下操作将证书文件移动到 lighttpd 使用的正确位置。
mv /etc/ssl/mini_httpd/server.pem /etc/lighttpd/server-bundle.pem chown root:root /etc/lighttpd/server-bundle.pem chmod 400 /etc/lighttpd/server-bundle.pem
选项 3: 您可以决定从受信任的根证书颁发机构接收证书。一个不错的选择是从 StartSSL 页面请求免费证书:https://www.startssl.com/?app=33。
将以下行添加到 /etc/lighttpd/lighttpd.conf 以指向新的文档根目录,并将其设置为监听端口 443(将host.example.com替换为实际域名,将ip_address_of_server替换为实际 IP 地址)
simple-vhost.server-root = "/var/www/domains/" simple-vhost.default-host = "/host.example.com/" simple-vhost.document-root = "www/" $SERVER["socket"] == "ip_address_of_server:443" { ssl.engine = "enable" ssl.pemfile = "/etc/lighttpd/server-bundle.pem" }
如果您选择了上面的选项 1,则在 ssl.pem 文件行下方添加一行,以便该部分显示如下
$SERVER["socket"] == "ip_address_of_server:443" { ssl.engine = "enable" ssl.pemfile = "/etc/lighttpd/server-bundle.pem" ssl.ca-file = "/etc/lighttpd/ca-crt.pem" }
确保已加载 simple_vhosts 模块以及 cgi 配置文件,方法是取消注释 /etc/lighttpd/lighttpd.conf 中的以下行
server.modules = ( # other modules may be listed "mod_simple_vhost", # other modules may be listed . . . include "mod_cgi.conf" include "mod_fastcgi.conf"
停止并移除 mini_httpd;启动 lighttpd,测试
rc-service mini_httpd stop rc-update del mini_httpd apk del mini_httpd rc-update add lighttpd rc-service lighttpd start
此时,您应该能够看到使用 lighttpd 提供的 ACF(注意:这适用于 alpine 1.10 和 2.x。在早期版本中会出现问题。) https://host.example.com/acf/
安装 Postgresql
添加和配置 postgresql
apk add acf-postgresql postgresql-client rc-service postgresql setup rc-service postgresql start rc-update add postgresql
此时,任何用户都可以使用 "trust" 机制连接到 sql 服务器。如果您想强制执行密码身份验证(您可能需要这样做),请编辑 /var/lib/postgresql/9.0/data/pg_hba.conf。由于默认情况下 "trust" 机制仅适用于本地连接,因此我们假设使用 trust 无密码访问是安全的。
创建 postfix 数据库
psql -U postgres create user postfix with password '******'; create database postfix owner postfix; \q
(当然,在上面显示的 ******* 位置使用您选择的密码。)
安装 PostfixAdmin
我们将在安装邮件服务器之前安装 postfix admin Web 前端。这只是创建一个界面来填充 postfix 和 dovecot 将使用的 SQL 表。
从 Sourceforge 下载 PostfixAdmin。在编写这些说明时,2.3 是当前版本,因此(将 host.example.com 替换为实际域名)
wget https://downloads.sourceforge.net/project/postfixadmin/postfixadmin/postfixadmin-2.3.3/postfixadmin-2.3.3.tar.gz tar zxvf postfixadmin-2.3.3.tar.gz mkdir -p /var/www/domains/host.example.com/www/postfixadmin mv postfixadmin-2.3.3/* /var/www/domains/host.example.com/www/postfixadmin rm -rf postfixadmin*
编辑 /var/www/domains/host.example.com/www/postfixadmin/config.inc.php 并至少修改以下行(将 host.example.com 替换为实际域名)
$CONF['configured'] = true; $CONF['setup_password'] = ""; << Don't change this yet $CONF['database_type'] = 'pgsql'; $CONF['database_host'] = 'localhost'; $CONF['database_user'] = 'postfix'; $CONF['database_password'] = '*****'; << The password you chose above $CONF['database_name'] = 'postfix'; $CONF['database_prefix'] = ""; $CONF['admin_email'] = 'you@some.email.com'; << Your email address $CONF['encrypt'] = 'md5crypt'; $CONF['authlib_default_flavor'] = 'md5raw'; $CONF['dovecotpw'] = "/usr/sbin/dovecotpw"; $CONF['domain_path'] = 'YES'; $CONF['domain_in_mailbox'] = 'NO'; $CONF['aliases'] = '10'; $CONF['mailboxes'] = '10'; $CONF['maxquota'] = '10'; $CONF['quota'] = 'YES'; $CONF['quota_multiplier'] = '1024000'; $CONF['vacation'] = 'NO'; $CONF['vacation_control'] ='NO'; $CONF['vacation_control_admin'] = 'NO'; $CONF['alias_control'] = 'YES'; $CONF['alias_control_admin'] = 'YES'; $CONF['special_alias_control'] = 'YES'; $CONF['fetchmail'] = 'NO'; $CONF['user_footer_link'] = "https://host.example.com/postfixadmin"; $CONF['footer_link'] = 'https://host.example.com/postfixadmin/main.php'; $CONF['create_mailbox_subdirs_prefix']=""; $CONF['used_quotas'] = 'YES'; $CONF['new_quota_table'] = 'YES';
您应该进一步编辑 /var/www/domains/host.example.com/www/postfixadmin/config.inc.php 并将所有 "change-this-to-your.domain.tld" 实例替换为您的实际邮件域名。这可以使用 busybox sed 完成(将 example.com 替换为您的域名)
sed -i -e 's/change-this-to-your.domain.tld/example.com/g' /var/www/domains/host.example.com/www/postfixadmin/config.inc.php
转到 https://host.example.com/postfixadmin/setup.php
创建密码哈希,将其添加到 config.inc.php 文件
返回 https://host.example.com/postfixadmin/setup.php
创建超级管理员帐户。
注意: 如果您在列出域页面时遇到错误,请查看 https://sourceforge.net/tracker/index.php?func=detail&aid=2859165&group_id=191583&atid=937964。
安装 Postfix
为虚拟邮件传递创建一个用户,并获取其 uid/gid(您将需要 postfix 的数字 uid/gid)
adduser vmail -H -D -s /bin/false grep vmail /etc/passwd
(在下面的示例中,我们使用 1006/1006 作为 uid/gid)
创建邮件目录,并将 vmail 分配为所有者
mkdir -p /var/mail/domains chown -R vmail:vmail /var/mail/domains
安装 postfix
apk add acf-postfix postfix-pgsql postfix-pcre
编辑 /etc/postfix/main.cf 文件。这是一个示例(不要忘记替换 uid/gid)
myhostname=host.example.com mydomain=example.com mydestination = localhost.$mydomain, localhost mynetworks_style = subnet mynetworks = 127.0.0.0/8 virtual_mailbox_domains = proxy:pgsql:/etc/postfix/sql/pgsql_virtual_domains_maps.cf virtual_alias_maps = proxy:pgsql:/etc/postfix/sql/pgsql_virtual_alias_maps.cf, proxy:pgsql:/etc/postfix/sql/pgsql_virtual_alias_domain_maps.cf, proxy:pgsql:/etc/postfix/sql/pgsql_virtual_alias_domain_catchall_maps.cf virtual_mailbox_maps = proxy:pgsql:/etc/postfix/sql/pgsql_virtual_mailbox_maps.cf, proxy:pgsql:/etc/postfix/sql/pgsql_virtual_alias_domain_mailbox_maps.cf virtual_mailbox_base = /var/mail/domains/ virtual_gid_maps = static:1006 virtual_uid_maps = static:1006 virtual_minimum_uid = 100 virtual_transport = virtual # This next command means you must create a virtual # domain for the host itself - ALL mail goes through # The virtual transport mailbox_transport = virtual local_transport = virtual local_transport_maps = $virtual_mailbox_maps smtpd_helo_required = yes disable_vrfy_command = yes message_size_limit = 10240000 queue_minfree = 51200000 smtpd_sender_restrictions = permit_mynetworks, reject_non_fqdn_sender, reject_unknown_sender_domain smtpd_recipient_restrictions = reject_non_fqdn_recipient, reject_unknown_recipient_domain, permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_rbl_client dnsbl.sorbs.net, reject_rbl_client zen.spamhaus.org, reject_rbl_client bl.spamcop.net smtpd_data_restrictions = reject_unauth_pipelining # we will use this later - This prevents cleartext authentication # for relaying smtpd_tls_auth_only = yes
现在我们需要创建大量文件,以便 postfix 可以从 sql 中获取传递信息。这是一个 shell 脚本来创建脚本。将 PGPW 更改为 postfix SQL 数据库的 postfix 用户的密码
cd /etc/postfix mkdir sql PGPW="ChangeMe" cat - <<EOF >sql/pgsql_virtual_alias_domain_catchall_maps.cf user=postfix password = $PGPW hosts = localhost dbname = postfix query = Select goto From alias,alias_domain where alias_domain.alias_domain = '%d' and alias.address = '@' || alias_domain.target_domain and alias.active = true and alias_domain.active= true EOF cat - <<EOF >sql/pgsql_virtual_alias_domain_mailbox_maps.cf user=postfix password = $PGPW hosts = localhost dbname = postfix query = Select maildir from mailbox,alias_domain where alias_domain.alias_domain = '%d' and mailbox.username = '%u' || '@' || alias_domain.target_domain and mailbox.active = true and alias_domain.active EOF cat - <<EOF >sql/pgsql_virtual_alias_domain_maps.cf user=postfix password = $PGPW 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 EOF cat - <<EOF >sql/pgsql_virtual_alias_maps.cf user=postfix password = $PGPW hosts = localhost dbname = postfix query = Select goto From alias Where address='%s' and active ='1' EOF cat - <<EOF >sql/pgsql_virtual_domains_maps.cf user=postfix password = $PGPW hosts = localhost dbname = postfix query = Select domain from domain where domain='%s' and active='1' EOF cat - <<EOF >sql/pgsql_virtual_mailbox_maps.cf user=postfix password = $PGPW hosts = localhost dbname = postfix query = Select maildir from mailbox where username='%s' and active=true EOF chown -R postfix:postfix sql chmod 640 sql/*
此时,您应该能够启动 postfix
newaliases # so postfix is happy... rc-service postfix start rc-update add postfix
在 PostfixAdmin 中创建域并测试
转到 https://host.example.com/postfixadmin/
使用超级管理员帐户登录,为本地框创建一个域(例如 example.com),并创建一个用户邮箱(例如 root)。
从机器发送测试消息
sendmail -t root@example.com subject: test . ^d
在 /var/log/mail.log (或 /var/log/messages,如果您仍然运行 busybox syslogd)中,您应该看到消息已排队。该消息应该在 /var/mail/domains/example.com/root/new 中
安装 Dovecot
Dovecot 是用于检索邮件的 POP3/IMAP 服务器。
与之前一样,我们安装 dovecot
apk add acf-dovecot dovecot-pgsql
编辑 /etc/dovecot/dovecot.conf
auth_mechanisms = plain login auth_username_format = %Lu #auth_verbose = yes #auth_debug = yes #auth_debug_passwords = no disable_plaintext_auth = no mail_location = maildir:/var/mail/domains/%d/%n first_valid_gid = 1000 first_valid_uid = 1000 last_valid_gid = 65535 last_valid_uid = 65535 log_timestamp = "%Y-%m-%d %H:%M:%S " login_greeting = IMAP server ready protocols = imap service anvil { client_limit = 2100 } ssl_cert = </etc/lighttpd/server-bundle.pem ssl_key = </etc/lighttpd/server-bundle.pem userdb { args = uid=1006 gid=1006 home=/var/mail/domains/%d/%n driver = static } passdb { args = /etc/dovecot/dovecot-sql.conf driver = sql } protocol imap { mail_plugins = autocreate } plugin { autocreate = Trash autocreate2 = Spam autocreate3 = Sent autosubscribe = Trash autosubscribe2 = Spam autosubscribe3 = Sent }
请务必将 uid 和 gid 替换为 vmail 用户的相应值。
我们需要用于 SSL/TLS 身份验证的证书,因此在上面的示例中,我们使用了 lighttpd 证书。这样,当证书续订/替换时,Dovecot 也将可以访问新证书。
创建 /etc/dovecot/dovecot-sql.conf 文件
driver = pgsql connect = host=localhost dbname=postfix user=postfix password=******** password_query = select username,password from mailbox where local_part = '%n' and domain = '%d' default_pass_scheme = MD5-CRYPT
再次,将上面的密码更改为您的 postfix 用户密码,并保护该文件免受窥探
chown root:root /etc/dovecot/dovecot-sql.conf chmod 600 /etc/dovecot/dovecot-sql.conf
启动 dovecot
rc-service dovecot start rc-update add dovecot
测试
确保您的防火墙允许端口 25(SMTP)、110 (POP3)、995 (POP3S)、143(IMAP)、993(IMAPS) 或您支持的任何子集。
此时,您应该能够
* Create a new domain and add users with PostfixAdmin * Send mail to those users via SMTP to port 25 * Retrieve mail using the user's full email and password (e.g. username: user@example.com password: ChangeMe)
增值功能
如果您按照上面的指南操作,您现在拥有一个功能齐全的邮件服务器,其中包含许多互连的部件。以下功能假定服务器已如上所述运行。您应该能够添加以下任何或所有功能,以进一步增强邮件服务。
病毒扫描
此过程使用 clamav 和 postfix content_filter 机制扫描入站和出站电子邮件中的病毒。受感染的电子邮件将被丢弃。干净的电子邮件将被标记为 "scanned by clamav" 标头。
- 安装 clamav 和 clamsmtp
apk add acf-clamav clamsmtp
- 如果需要,编辑 /etc/clamav/clamd.conf 文件(在大多数情况下不是必需的)
- 编辑 /etc/clamsmtpd.conf 并验证以下行
OutAddress: 10026 Listen: 127.0.0.1:10025 Header: X-Virus-Scanned: ClamAV using ClamSMTP Action: drop User: clamav
- 启动守护程序
rc-update add clamd rc-update add clamsmtpd rc-service clamd start rc-service clamsmtpd start
- 验证 clamsmtp 是否正在监听端口 10025
netstat -anp | grep clamsmtp
- 按照 clamsmtp 说明
- 编辑 /etc/postfix/main.cf 并添加
content_filter = scan:[127.0.0.1]:10025
- 编辑 /etc/postfix/master.cf 并添加
# AV scan filter (used by content_filter) scan unix - - n - 16 smtp -o smtp_send_xforward_command=yes -o smtp_enforce_tls=no # For injecting mail back into postfix from the filter 127.0.0.1:10026 inet n - n - 16 smtpd -o content_filter= -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks -o smtpd_helo_restrictions= -o smtpd_client_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o mynetworks_style=host -o smtpd_authorized_xforward_hosts=127.0.0.0/8
- postfix reload
- 发送电子邮件到本地虚拟域 - 它应该具有 X-Virus-Scanned: ClamAV using ClamSMTP 标头。
为已验证用户中继
如上配置,邮件服务器接受来自 Internet 的电子邮件,但它不中继电子邮件。如果它是受保护网络的边界交换器,则可以将受保护的网络添加到 /etc/postfix/main.cf 中的 mynetworks 配置行
此配置更改允许远程用户对邮件服务器进行身份验证并通过它进行中继。中继规则是
- 只有经过身份验证的用户才能中继
- 身份验证凭据必须使用 TLS 或 SSL 加密
- 允许 Submission 和 SMTPS 端口进行中继(许多消费者网络默认阻止端口 25 - SMTP)
该过程使用 dovecot 身份验证机制(与 IMAPS 一起使用)在允许用户通过 postfix 中继之前对其进行身份验证。
- 编辑 /etc/dovecot/dovecot.conf 并添加以下内容
- 这是用于 postfix SASL(已验证用户可以通过我们中继)
service auth { unix_listener /var/spool/postfix/private/auth { group = postfix mode = 0660 user = postfix } unix_listener /var/spool/postfix/auth-master { group = postfix mode = 0660 user = vmail } user = root }
- 重启 dovecot
rc-service dovecot restart
- 编辑 /etc/postfix/main.cf 并添加
# TLS Stuff -- since we allow SASL with tls *only*, we have to set up TLS first smtpd_tls_cert_file = /etc/lighttpd/server-bundle.pem smtpd_tls_key_file = /etc/lighttpd/server-bundle.pem smtpd_tls_CAfile = /etc/lighttpd/ca-crt.pem # If tls_security_level is set to "encrypt", then SMTP rejects # unencrypted email (e.g. normal mail) which is bad. # By setting it to "may" you get TLS encrypted mail from google, slashdot, and other # interesting places. Check your logs to see who smtpd_tls_security_level = may # Log info about the negotiated encryption levels smtpd_tls_received_header = yes smtpd_tls_loglevel = 1 # SASL - this allows senders to authenticiate themselves # This along with "permit_sasl_authenticated" in smtpd_recipient_restrictions allows relaying smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth smtpd_sasl_auth_enable = yes smtpd_sasl_authenticated_header = yes broken_sasl_auth_clients = yes smtpd_tls_auth_only = yes
- 编辑 /etc/postfix/master.cf 并启用 submission 和 smtps 传输。它们可能已经在您的 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 -o milter_macro_daemon_name=ORIGINATING smtps inet n - n - - smtpd -o smtpd_tls_security_level=encrypt -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING
- 验证 submission 和 smtps 是否在 /etc/services 中定义
grep "submission\|ssmtp" /etc/services submission 587/tcp # mail message submission submission 587/udp smtps 465/tcp ssmtp # smtp protocol over TLS/SSL smtps 465/udp ssmtp
- 重启 postfix
postfix reload
此时,您应该能够设置邮件客户端以通过服务器使用 TLS(端口 587)或 SSL(端口 465)进行中继。请注意,由于底层链接已加密,因此使用 "plain" 身份验证。例如,在 Thunderbird 中,取消选中“安全身份验证”,并为连接安全性选择 STARTTLS(或 TLS)。
邮箱配额
在默认配置中,PostfixAdmin 知道配额,但它们未被强制执行。 Web 上的文档提到了 vda patch to postfix 来强制执行配额。唯一不好的是... 这是一个补丁。 Postfix 和 Dovecot 都是保守的系统,因此如果补丁不在上游源代码中,我们将假设有充分的理由。有一种无需补丁即可使用配额的方法 - 它涉及使用 dovecot 的 lda 进行本地传递。
- 将 /etc/dovecot/dovecot.conf 替换为以下内容
auth_mechanisms = plain login auth_username_format = %Lu #auth_verbose = yes #auth_debug = yes #auth_debug_passwords = no disable_plaintext_auth = no info_log_path = /var/log/dovecot-info.log log_path = /var/log/dovecot.log mail_location = maildir:/var/mail/domains/%d/%n first_valid_gid = 1000 first_valid_uid = 1000 last_valid_gid = 65535 last_valid_uid = 65535 log_timestamp = "%Y-%m-%d %H:%M:%S " login_greeting = IMAP server ready protocols = imap service anvil { client_limit = 2100 } service auth { unix_listener /var/spool/postfix/auth-master { group = postfix mode = 0660 user = vmail } unix_listener /var/spool/postfix/private/auth { group = postfix mode = 0660 user = postfix } user = root } service imap-login { inet_listener imap { address = 127.0.0.1 port = 143 } inet_listener imaps { address = * port = 993 } process_limit = 1024 } service pop3-login { process_limit = 1024 } service dict { unix_listener dict { group = mode = 0600 user = vmail } } ssl_ca = </etc/ssl/certs/<CA Certificate file> ssl_cert = </etc/ssl/private/<Public part of certificate file> ssl_key = </etc/ssl/private/<Private part of certificate file> passdb { args = /etc/dovecot/dovecot-pgsql.conf driver = sql } userdb { driver = prefetch } userdb { args = /etc/dovecot/dovecot-pgsql.conf driver = sql } plugin { quota = dict:user::proxy::quotadict autocreate = Trash autocreate2 = Spam autocreate3 = Sent autosubscribe = Trash autosubscribe2 = Spam autosubscribe3 = Sent } protocol imap { mail_plugins = autocreate quota imap_quota } protocol pop3 { mail_plugins = quota } dict { quotadict = pgsql:/etc/dovecot/dovecot-dict-quota.conf } protocol lda { auth_socket_path = /var/spool/postfix/auth-master mail_plugins = quota postmaster_address = postmaster@host.example.com sendmail_path = /usr/sbin/sendmail }
- 编辑/etc/dovecot/dovecot-sql.conf并将用户和密码查询替换为以下内容(您可能还没有 user_query - 添加它)
password_query = select username as user, password, 1006 as userdb_uid, 1006 as userdb_gid, '*:bytes=' || quota as userdb_quota_rule from mailbox where local_part = '%n' and domain = '%d' user_query = select '/var/mail/domains/' || maildir as home, 1006 as uid, 1006 as gid, '*:bytes=' || quota as quota_rule from mailbox where local_part = '%n' and domain ='%d'
- 创建/etc/dovecot/dovecot-dict-quota.conf
connect = host=localhost dbname=postfix user=postfix password=******** map { pattern = priv/quota/storage table = quota2 username_field =username value_field = bytes } map { pattern= priv/quota/messages table = quota2 username_field = username value_field = messages }
再次,将上面的密码更改为您的 postfix 用户密码,并保护该文件免受窥探
chown dovecot:root /etc/dovecot/dovecot-sql.conf chmod 600 /etc/dovecot/dovecot-sql.conf chown dovecot:root /etc/dovecot/dovecot-dict-quota.conf chmod 600 /etc/dovecot/dovecot-dict-quota.conf
旁注:Dovecot 配额文档提到了 pgsql 需要触发器。这在 PostfixAdmin 安装中创建,这就是为什么您在创建数据库时实例化了 pgsql 语言的原因。 如果没有,您将需要创建触发器,以引用 quota2 表,而不是 dovecot 文档中提到的 quota 表。
- 为 dovecot lda 创建新的传输。将以下内容添加到 /etc/postfix/master.cf
# The dovecot deliver lda dovecot unix - n n - - pipe flags=DRhu user=vmail:vmail argv=/usr/libexec/dovecot/deliver -f ${sender} -d ${user}@${nexthop}
- 编辑 /etc/postfix/main.cf。替换
virtual_transport = virtual
为
virtual_transport = dovecot dovecot_destination_recipient_limit = 1
更改 /var/log/dovecot* 日志文件的权限,以便 vmail 用户可以写入它们
chown vmail:vmail /var/log/dovecot*
重启 Postfix 和 Dovecot
rc-service postfix restart rc-service dovecot restart
TODO 这将导致超出配额的电子邮件被退回。这可能是反向散射的来源。我们需要一种在 RBL 检查之后但在消息被队列接受之前检查配额限制的方法。
WebMail (RoundCube)
RoundCube 是一个 "ajax /Web2.0" Web 邮件客户端。这些说明适用于 Alpine Linux 2.2 存储库
- 验证您是否在 /etc/postfix/main.cf 中至少有以下内容。除非您已按照上面的为已验证用户中继部分进行操作,否则请设置 smtpd_tls_auth_only = no,否则将其设置为 yes
# SASL - this allows senders to authenticiate themselves # This along with "permit_sasl_authenticated" in smtpd_recipient_restrictions allows relaying smtpd_sasl_type = dovecot smtpd_sasl_path = private/dovecot-auth.sock smtpd_sasl_auth_enable = yes smtpd_sasl_authenticated_header = yes # Set the next line to no if TLS auth is not configured smtpd_tls_auth_only = no
- 确保您已按照为已验证用户中继部分进行操作。
- 重启相关服务
rc-service postfix restart rc-service dovecot restart
- 添加软件包和相关的 php 模块
apk add roundcubemail php-xml php-openssl php-mcrypt php-gd php-iconv php-dom php-intl
- 将 roundcube 应用程序链接回 docroot
ln -s /usr/share/webapps/roundcube /var/www/domains/host.example.com/www/roundcube
- 安装 roundcubemail-install 软件包
apk add roundcubemail-installer
- 按照 /usr/share/webapps/roundcube/INSTALL 中的说明进行操作
cd /usr/share/webapps/roundcube chown -R lighttpd:lighttpd /var/log/roundcube su postgres createuser roundcube Shall the new role be a superuser? (y/n) n Shall the new role be allowed to create databases? (y/n) n Shall the new role be allowed to create more new roles? (y/n) y createdb -O roundcube -E UNICODE -T template0 roundcubemail psql roundcubemail roundcubemail=# ALTER USER roundcube WITH PASSWORD 'the_new_password'; roundcubemail=# \c - roundcube roundcubemail=> \i /usr/share/webapps/roundcube/SQL/postgres.initial.sql roundcubemail=> \q exit
- 编辑 /etc/php/php.ini 并将 date.timezone 设置为您的本地时区或 UTC
- 重启 lighttpd 以验证是否使用了新的 php 库
rc-service lighttpd restart
- 在 /etc/roundcube/main.inc.php 文件中启用安装程序模式
$rcmail_config['enable_installer'] = true;
- 将浏览器指向 https://host.example.com/roundcube/installer
- 开始安装
对于安装步骤中的特定配置参数
属性 | 设置 |
---|---|
enable_spellcheck | disabled |
identities_level | 一个身份,可以编辑所有参数,但不能编辑电子邮件地址 |
log driver | syslog |
sylog_id | roundcube |
syslog_facility | mailsubsystem |
db_dnsw | pgsql 属性,如上所述 |
imap_host | 127.0.0.1 |
auto_create_user | enabled |
smtp_server | 127.0.0.1 |
smtp_port | 25 |
smtp_user/smtp_pass | 启用使用当前 IMAP 用户名和密码进行 SMTP 身份验证 |
smtp_log | 启用(可选,但提供额外的日志记录) |
其他项目可以保留为默认设置,或根据需要进行调整。
- 按照安装步骤 2 中的说明将文件复制到服务器
- 您现在应该可以访问 https://host.example.com/roundcube 上的 roundcube
- 在其工作后,INSTALL 文件建议删除 install 目录。
apk del roundcubemail-installer
- 在 /etc/roundcube/main.inc.php 文件中禁用安装程序模式
$rcmail_config['enable_installer'] = false;
- 更改所有权和权限
cd /usr/share/webapps/roundcube chown -R root:root LICENSE UPGRADING INSTALL README CHANGELOG chmod -R 600 LICENSE UPGRADING INSTALL README CHANGELOG
- 如果需要,自定义徽标,例如 watermark.gif、roundcube_logo.gif、favicon.ico
- 如果您想禁用标准徽标的显示,请相应地更新模板文件
- 注释掉文件中所有类似 <div ... img src="/images/roundcube_logo.png"... 的条目
includes/header.html templates/error.html templates/messageprint.html templates/login.html templates/printmessage.html
- 注释掉文件中所有类似 <img src="/images/watermark.gif"... 的条目
templates/identities.html templates/messageerror.html watermark.html
启用插件
RoundCube 具有各种有用的插件,可以在 /usr/share/webapps/roundcube/plugins 目录中找到。例如,您可能想要启用 password 插件,以允许用户直接从 RoundCube 使用添加到用户设置的额外“密码”选项卡来更改其密码。
- 为 roundcube 数据库角色授予有限权限
psql -U postgres postfix postfix=# GRANT UPDATE (password,modified) ON mailbox TO roundcube; postfix=# GRANT SELECT (username) ON mailbox TO roundcube; postfix=# GRANT INSERT ON log TO roundcube; postfix=# \q
- 在 /usr/share/webapps/roundcube/plugins/password/config.inc.php 中设置 password 插件参数
mv /usr/share/webapps/roundcube/plugins/password/config.inc.php.dist /usr/share/webapps/roundcube/plugins/password/config.inc.php vi /usr/share/webapps/roundcube/plugins/password/config.inc.php
$rcmail_config['password_minimum_length'] = 7; $rcmail_config['password_require_nonalpha'] = true; ... $rcmail_config['password_db_dsn'] = 'pgsql://roundcube:<roundcube_password>@localhost/postfix'; ... $rcmail_config['password_query'] = "UPDATE mailbox set password = %c, modified = NOW() where username = %u; INSERT INTO log (timestamp,username,domain,action,data) VALUES (NOW(),%u || ' (' || %h || ')',%d,'edit_password',%u)";
- 启用 password 插件
vi /usr/share/webapps/roundcube/config/main.inc.php
... $rcmail_config['plugins'] = array('password');
- 为 RoundCube 启用 create_default_folders
vi /usr/share/webapps/roundcube/config/main.inc.php
... $rcmail_config['create_default_folders'] = TRUE; ...
基于 OpenLDAP 的地址簿
此 OpenLDAP 配置使用 SQL 后端,该后端将 PostgreSQL 中存储的信息表示为 LDAP 子树,用于电子邮件查找、用户身份验证甚至站点之间的复制帐户信息。此过程使用一些元信息将 LDAP 查询转换为 SQL 查询,使关系模式保持不变,这允许 SQL 和 LDAP 应用程序在没有复制的情况下互操作,并根据需要交换数据。 SQL 后端使用 UnixODBC 连接到 PostgresSQL。
- 安装 OpenLDAP 和 ODBC
apk add openldap libldap openldap-back-sql php-ldap unixodbc psqlodbc ca-certificates
- 更新 "postfix" 数据库(它将向 mailbox 和 domain 表添加 'id' 列,还将创建表和视图以表示 LDAP 元信息)
注意:这些说明适用于示例域名 example.com。因此,请确保您已根据域名部分替换了 'example' 和 'com' 的所有条目。
将以下内容放入名为 script 的新文件中
ALTER TABLE domain ADD COLUMN id SERIAL; ALTER TABLE mailbox ADD COLUMN id SERIAL; CREATE TABLE ldap_entry_objclasses ( entry_id integer NOT NULL, oc_name character varying(64) ); CREATE TABLE ldap_oc_mappings ( name character varying(64) NOT NULL, keytbl character varying(64) NOT NULL, keycol character varying(64) NOT NULL, create_proc character varying(255), delete_proc character varying(255), expect_return integer NOT NULL ); ALTER TABLE ldap_oc_mappings ADD COLUMN id SERIAL; ALTER TABLE ldap_oc_mappings ADD PRIMARY KEY (id); CREATE TABLE ldap_attr_mappings ( oc_map_id integer NOT NULL REFERENCES ldap_oc_mappings(id), name character varying(255) NOT NULL, sel_expr character varying(255) NOT NULL, sel_expr_u character varying(255), from_tbls character varying(255) NOT NULL, join_where character varying(255), add_proc character varying(255), delete_proc character varying(255), param_order integer NOT NULL, expect_return integer NOT NULL ); ALTER TABLE ldap_attr_mappings ADD COLUMN id SERIAL; ALTER TABLE ldap_attr_mappings ADD PRIMARY KEY (id); CREATE VIEW ldap_dcs AS ((SELECT (domain.id + 100000) AS id, ('dc='::text || replace((domain.domain)::text, '.'::text, ',dc='::text)) AS dn, 1 AS oc_map_id, 100000 AS parent, 0 AS keyval, domain.domain FROM domain WHERE domain.domain <> 'ALL') UNION (SELECT 100000 AS id, ('dc=' || regexp_replace((domain.domain)::text, '.*\\.', ''::text)) AS dn, 1 AS oc_map_id, 0 AS parent, 0 AS keyval, (regexp_replace((domain.domain)::text, '.*\\.', ''::text)) AS domain FROM domain WHERE domain.domain <> 'ALL' LIMIT 1)); CREATE VIEW ldap_entries AS SELECT mailbox.id, ((('cn='::text || initcap(replace(split_part((mailbox.username)::text, '@'::text, 1), '.'::text, ' '::text))) || ',dc='::text) || replace(regexp_replace((mailbox.username)::text, '.*@', ''::text), '.'::text, ',dc='::text)) AS dn, 1 AS oc_map_id, (SELECT ldap_dcs.id FROM ldap_dcs WHERE ((ldap_dcs.domain)::text = (mailbox.domain)::text)) AS parent, mailbox.id AS keyval FROM mailbox UNION SELECT ldap_dcs.id, ldap_dcs.dn, ldap_dcs.oc_map_id, ldap_dcs.parent, ldap_dcs.keyval FROM ldap_dcs;
专家提问:在此脚本中出现 "WARNING: nonstandard use of \\ in a string literal" 是否正常?
最后,使用以下命令执行文件中的命令
cat script | psql -U postfix postfix rm script
- 根据以下示例填写 LDAP 表(确保用制表符分隔值)
将以下内容放入名为 script 的新文件中
COPY ldap_oc_mappings (id, name, keytbl, keycol, create_proc, delete_proc, expect_return) FROM stdin; 1 exampleBox mailbox id \N \N 1 \. COPY ldap_attr_mappings (id, oc_map_id, name, sel_expr, sel_expr_u, from_tbls, join_where, add_proc, delete_proc, param_order, expect_return) FROM stdin; 1 1 displayName mailbox.name \N mailbox \N \N \N 3 0 2 1 mail mailbox.username \N mailbox \N \N \N 3 0 3 1 cn mailbox.name \N mailbox \N \N \N 3 0 4 1 userPassword '{CRYPT}'||mailbox.password \N mailbox \N \N \N 3 0 \.
最后,使用以下命令执行文件中的命令
cat script | psql -U postfix postfix rm script
- 检查 "ldap_dcs" 视图看起来像这样
echo 'select * from ldap_dcs' | psql -U postgres postfix
id | dn | oc_map_id | parent | keyval | domain --------+-----------------------------+-----------+--------+--------+-------------------- 100000 | dc=com | 1 | 0 | 0 | com 100001 | dc=example,dc=com | 1 | 100000 | 0 | example.com
- 检查 "ldap_entries" 视图看起来像这样
echo 'select * from ldap_entries' | psql -U postgres postfix
id | dn | oc_map_id | parent | keyval --------+-------------------------------------------------------+-----------+--------+-------- 1 | cn=address1,dc=example,dc=com | 1 | 100001 | 1 ... 123 | cn=address123,dc=example,dc=com | 1 | 100001 | 1 100000 | dc=com | 1 | 0 | 0 100001 | dc=example,dc=com | 1 | 100000 | 0
- 配置 ODBC 参数
编辑 /etc/odbc.ini
[PostgreSQL] Description = Connection to Postgres Driver = PostgreSQL Trace = Yes TraceFile = sql.log Database = postfix Servername = 127.0.0.1 UserName = Password = Port = 5432 Protocol = 6.4 ReadOnly = No RowVersining = No ShowSystemTables = No ShowOidColumn = No FakeOidIndex = No ConnSettings =
编辑 /etc/odbcinst.ini
[PostgreSQL] Description = PostgreSQL driver for Linux Driver = /usr/lib/psqlodbcw.so Setup = /usr/lib/libodbcpsqlS.so FileUsage = 1
- 测试 ODBC 连接
echo "select * from domain;" | isql PostgreSQL postgres
- 为 LDAP 服务器提供证书权限
chown ldap /etc/lighttpd/server-bundle.pem
- 编辑 LDAP 模式
编辑 /etc/openldap/schema/example.com.schema
attributetype ( 0.9.2342.19200300.100.1.3 NAME ( 'mail' 'rfc822Mailbox' ) DESC 'RFC1274: RFC822 Mailbox' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) attributetype ( 2.16.840.1.113730.3.1.241 NAME 'displayName' DESC 'RFC2798: preferred name to be used when displaying entries' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) objectclass ( 2.16.840.1.113730.3.2.2 NAME 'exampleBox' DESC 'example.com mailbox' MUST ( displayName $ mail $ userPassword ) ) # RFC 1274 + RFC 2247 attributetype ( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domainComponent' ) DESC 'RFC1274/2247: domain component' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) attributetype ( 2.5.4.46 NAME 'dnQualifier' DESC 'RFC2256: DN qualifier' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
- 配置 LDAP 服务器
编辑 /etc/openldap/slapd.conf
include /etc/openldap/schema/example.com.schema pidfile /var/run/openldap/slapd.pid argsfile /var/run/openldap/slapd.args # Uncomment next five TLS... lines if you want to use LDAPs (secured). Probably you don't want it... #TLSCipherSuite HIGH #TLSCACertificateFile /etc/lighttpd/ca-crt.pem #TLSCertificateFile /etc/lighttpd/server-bundle.pem #TLSCertificateKeyFile /etc/lighttpd/server-bundle.pem #TLSVerifyClient never # This is needed for proper representation of MD5-CRYPT format stored in database # see more details in https://strugglers.net/~andy/blog/2010/01/23/openldap-and-md5crypt/ password-hash {CRYPT} password-crypt-salt-format "$1$%.8s" loglevel stats moduleload /usr/lib/openldap/back_sql.so sizelimit 3000 database sql dbname PostgreSQL dbuser postfix dbpasswd ***** suffix "dc=example,dc=com" upper_func "upper" strcast_func "text" concat_pattern "?||?" has_ldapinfo_dn_ru no lastmod off access to attrs=userPassword by * auth access to * by peername.ip=127.0.0.1 read # by peername.ip=<IP>%<netmask> read # by peername.ip=<IP> read by users read
- 设置 slapd.conf 的权限
chown ldap:ldap /etc/openldap/slapd.conf
- 配置启动参数以确保 LDAP 服务器在 PostgreSQL 之后启动,并监听本地主机的明文和公共 IP 的 SSL。如果您取消注释了 slapd.conf 中的 TLS 行,请使用以下字符串:OPTS="-h 'ldaps:// ldap://'"
编辑 /etc/conf.d/slapd
rc_need="postgresql" OPTS="-h 'ldap://'"
- 启动 LDAP 服务器
rc-update add slapd default rc-service slapd start
- 配置 LDAP 客户端实用程序。如果您取消注释了 slapd.conf 中的 TLS 行,请将 ldap 替换为 ldaps
编辑 /etc/openldap/ldap.conf
BASE dc=example,dc=com URI ldap://host.example.com # Uncomment next three TLS... lines if you want to use LDAPs (secured). Probably you don't want it... #TLS_CACERT /etc/lighttpd/ca-crt.pem #TLS_CERT /etc/lighttpd/server-bundle.pem #TLS_KEY /etc/lighttpd/server-bundle.pem
- 测试 LDAP 服务器
ldapsearch -z 3 ldapsearch -z 3 -x -W -D cn=admin,dc=example,dc=com ldapsearch -z 3 -x -W -D cn=address1,dc=example,dc=com
- 配置 RoundCube Webmail 以进行电子邮件查找
为了启用 php-ldap 支持,您需要重启 lighttpd 服务器
rc-service lighttpd restart
编辑 /etc/roundcube/main.inc.php
$rcmail_config['ldap_debug'] = false; ... $rcmail_config['address_book_type'] = 'sql'; $rcmail_config['ldap_public']['example.com'] = array( 'name' => 'example.com', 'hosts' => array('127.0.0.1'), 'port' => 389, 'use_tls' => false, 'user_specific' => false, 'base_dn' => 'dc=example,dc=com', 'bind_dn' => '', 'bind_pass' => '', 'writable' => false, 'LDAP_Object_Classes' => array("top", "exampleBox"), 'required_fields' => array("cn", "sn", "mail"), 'LDAP_rdn' => 'mail', 'ldap_version' => 3, 'search_fields' => array('mail', 'cn', 'sn', 'givenName'), 'name_field' => 'cn', 'email_field' => 'mail', 'surname_field' => 'sn', 'firstname_field' => 'gn', 'sort' => 'cn', 'scope' => 'sub', 'filter' => '(objectClass=*)', // Construct here any filter you need 'fuzzy_search' => true); $rcmail_config['autocomplete_addressbooks'] = array('sql','example.com');
- 修复 PostfixAdmin 以使用新的表定义
编辑 /var/www/domains/example.com/www/postfixadmin/list-domain.php。替换行
SELECT domain.* , COUNT( DISTINCT mailbox.username ) AS mailbox_count
为以下行
SELECT domain.domain, domain.description, domain.aliases, domain.mailboxes, domain.maxquota, domain.quota, domain.transport, domain.backupmx, domain.created, domain.modified, domain.active, COUNT( DISTINCT mailbox.username ) AS mailbox_count
日志轮换
确保 busybox cron 服务已启动并配置为自动启动
rc-service cron start rc-update add cron default
添加日志轮换
apk add logrotate
根据需要编辑 /etc/logrotate.conf,但默认值对于大多数人来说应该足够了。
可选:配置 Web 服务器虚拟域名
注意: 这些步骤可以在上面的默认 lighttpd 配置之外完成,这允许您将 ACF、PostfixAdmin 和 Roundcube 接口作为单个 Web 服务的子文件夹访问。
注意: 如果您为多域站点提供 SSL 访问,您可能需要遵循 https://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:SSL#SSL-on-multiple-domains 以提供多域证书。如果您想将主机重定向到其安全等效项,请使用以下说明 https://redmine.lighttpd.net/projects/lighttpd/wiki/HowToRedirectHttpToHttps。
此服务器托管三个独立的 Web 应用程序,这些应用程序可以在同一 Web 服务器上作为三个不同的虚拟域名处理。它们将通过其 DNS 名称来区分,因此您可以为三个单独的服务选择域名(或至少是您想要发布的域名)
- ACF - 用于管理服务器的 Alpine 配置框架
- PostfixAdmin - 用于管理 postfix 安装
- RoundCube - 用于访问个人邮箱
选择三个不同的域名(以下称为 ACF_DOMAIN、POSTFIXADMIN_DOMAIN 和 ROUNDCUBE_DOMAIN)并配置所有三个的 DNS 以指向您的主机的 IP 地址。这些应该是 DNS A 记录。
然后,配置 lighttpd 以处理三个单独的域名,方法是编辑 /etc/lighttpd/lighttpd.conf
$HTTP["host"] == "ACF_DOMAIN" { simple-vhost.server-root = "/var/www/domains/" simple-vhost.default-host = "/ACF_DOMAIN/" simple-vhost.document-root = "www/" } $HTTP["host"] == "POSTFIXADMIN_DOMAIN" { simple-vhost.server-root = "/var/www/domains/" simple-vhost.default-host = "/POSTFIXADMIN_DOMAIN/" simple-vhost.document-root = "www/" } $HTTP["host"] == "ROUNDCUBE_DOMAIN" { simple-vhost.server-root = "/var/www/domains/" simple-vhost.default-host = "/ROUNDCUBE_DOMAIN/" simple-vhost.document-root = "www/" }
然后,链接相应的 www 目录。
mkdir -p /var/www/domains/ACF_DOMAIN ln -s /usr/share/acf/www /var/www/domains/ACF_DOMAIN/www mkdir -p /var/www/domains/POSTFIXADMIN_DOMAIN ln -s /var/www/domains/host.example.com/www/postfixadmin /var/www/domains/POSTFIXADMIN_DOMAIN/www mkdir -p /var/www/domains/ROUNDCUBE_DOMAIN ln -s /usr/share/webapps/roundcube /var/www/domains/ROUNDCUBE_DOMAIN/www
可选:在 Lighttpd 中启用压缩
- 取消注释 mod_compress 和 mod_setenv 并按如下方式修改网站部分
mkdir -p /var/lib/lighttpd/cache chown lighttpd:lighttpd /var/lib/lighttpd/cache
vi /etc/lighttpd/lighttpd.conf
... "mod_setenv", "mod_compress", ... $HTTP["host"] == "ROUNDCUBE_DOMAIN" { ... static-file.etags = "enable" etag.use-mtime = "enable" $HTTP["url"] =~ "^/(plugins|skins|program)" { setenv.add-response-header = ( "Cache-Control" => "public, max-age=2592000") } compress.cache-dir = var.statedir + "/cache/compress" compress.filetype = ("text/plain", "text/html", "text/javascript", "text/css", "text/xml", "image/gif", "image/png") }