ISP 邮件服务器指南

来自 Alpine Linux
此材料已过时...

要使用 Alpine Linux 3.x 设置邮件服务器,请参阅 ISP Mail Server 3.x HowTo讨论

全功能邮件服务器

本文档的目标是描述如何设置 postfix、dovecot、clamav、dspam、roundecube 和 postfixadmin 以构建一个全功能“ISP”级别的邮件服务器。

服务器必须提供

  • 多个虚拟域
  • 每个域的管理员(用于添加/删除虚拟账户)
  • 每个域/账户的配额支持
  • 通过 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. 如果您想使用自签名证书,可以使用 生成 SSL 证书与 ACF生成 SSL 证书与 ACF 1.9 中找到的说明来生成它。 2. 使用使用 setup-acf 命令创建的证书。

选项 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

将以下行添加到 /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.pemfile 行下方添加一行,以便该部分显示如下:

 $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 上效果良好。在早期版本中会存在问题。)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/8.4/data/pg_hba.conf。


 Editme: What should we recommend?


创建 postfix 数据库

 psql -U postgres
  create user postfix with password '******';
  create database postfix owner postfix;
  \c postfix
  create language plpgsql;
  \q

(当然,在上面显示 ******* 的位置使用您选择的密码。)

安装 PostfixAdmin

我们将在安装邮件服务器之前安装 postfix 管理 Web 前端。这只是创建一个界面来填充 postfix 和 dovecot 将使用的 SQL 表。

从 Sourceforge 下载 PostfixAdmin。在编写这些说明时,2.3 是当前版本,因此(将 host.example.com 替换为实际域名)

wget https://downloads.sourceforge.net/project/postfixadmin/postfixadmin/postfixadmin-2.3.2/postfixadmin-2.3.2.tar.gz
tar zxvf postfixadmin-2.3.2.tar.gz
mkdir -p /var/www/domains/host.example.com/www/postfixadmin
mv  postfixadmin-2.3.2/* /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'] = "http://host.example.com/postfixadmin";
$CONF['footer_link'] = 'http://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

创建超级管理员账户。

安装 Postfix

为虚拟邮件传递创建一个用户,并获取其 uid/gid(您将需要 postfix 的数字 uid/gid)

addgroup -S vmail
adduser vmail -S -H -D -s /bin/false -G vmail
getent passwd vmail

(在以下示例中,我们使用 1006/1006 作为 uid/gid)

创建邮件目录,并将 vmail 分配为所有者

mkdir -p /var/mail/domains
chown -R vmail:vmail /var/mail/domains

安装 postfix

apk add acf-postfix postfix-pgsql

编辑 /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 中创建域并测试

转到 http://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

备份默认配置

mv /etc/dovecot/dovecot.conf

使用以下内容创建一个新的 /etc/dovecot/dovecot.conf

auth_mechanisms = plain login
auth_username_format = %Lu
auth_verbose = yes
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
passdb {
  args = /etc/dovecot/dovecot-sql.conf
  driver = sql
}
plugin {
  autocreate = Trash
  autocreate2 = Spam
  autocreate3 = Sent
  autosubscribe = Trash
  autosubscribe2 = Spam
  autosubscribe3 = Sent
}
protocols = pop3 imap
# uncomment if you want disable imap on port 143 to enforce imaps
#service imap-login {
#  inet_listener imap {
#    port = 0
#  }
#}
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 first_valid_uid=100
  driver = static
}
protocol imap {
  mail_plugins = autocreate
}

请务必将 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
cmd|netstat -anp | grep clamsmtp
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 并添加以下内容
service auth {
  # this is for postfix SASL (authenticated users can relay through us)
  unix_listener /var/spool/postfix/private/dovecot-auth.sock {
    group = postfix
    mode = 0660
    user = postfix
  }
}
  • 重启 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/dovecot-auth.sock
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 补丁到 postfix 以强制执行配额。唯一不好的是...它是一个补丁。Postfix 和 Dovecot 都是保守系统,因此如果补丁不在上游源中,我们将假定有充分的理由。有一种无需补丁即可使用配额的方法 - 它涉及使用 dovecot 的 deliver lda 进行本地传递。

注意:截至 2010 年 1 月,文档令人困惑,其中引用了多个版本的 dovecot、PostfixAdmin 和 Mysql。这些说明适用于:

  • Postgresql 8.4.2
  • PostfixAdmin 2.3
  • Dovecot 1.2.13
  • Postfix 2.6.5

据推测,更高版本的工作方式相同,但如果不是,请更新上面的文档和版本。

  • 更新 /etc/dovecot/dovecot.conf(旧行显示为注释掉)
# old postfix 
#       userdb static {
#               args =  uid=1006 gid=1006 home=/var/mail/domains/%d/%n
#               }

# new quota support:
        userdb prefetch {
                }

        userdb sql {
                args = /etc/dovecot/dovecot-sql.conf
                }

        socket listen {
                client {
                        path    = /var/spool/postfix/private/dovecot-auth.sock
                        mode    = 0660
                        user    = postfix
                        group   = postfix
                        }
                # These lines below are for the deliver lda
                master {
                        path =  /var/run/dovecot/auth-master
                        mode    = 0660
                        user    = vmail
                        group   = vmail
                        }
                }
}
#user = root
#}

protocol imap {                                                               
         mail_plugins = quota imap_quota                                       
         }                                                                     
                                                                              
protocol pop3 {                                                               
         mail_plugins = quota                                                  
         }                                                                     
                                                                              
dict {                                                                        
        quotadict = pgsql:/etc/dovecot/dovecot-dict-quota.conf                
        }                                                                     
                                                                              
plugin {                                                                      
         quota = dict:user::proxy::quotadict                                   
         }                                                     
                                                              
protocol lda {                                                
   postmaster_address = postmaster@host.example.com
   mail_plugins = quota                                        
   auth_socket_path =  /var/run/dovecot/auth-master
   sendmail_path = /usr/sbin/sendmail
}                                                                            

您应该已经有一个:socket-> listen-> client部分,但上面列出了它在与以下关系中的位置:socket -> listen -> master部分


  • 编辑/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 root: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 1.10 存储库。

  • 验证您在 /etc/postfix/main.cf 中至少有以下内容。除非您遵循了上面的 "为已验证用户中继" 部分,否则将 smtpd_tls_auth_only = no 设置为 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
  • 确保您在 /etc/dovecot/dovecot.conf 的 auth default 节中包含此部分。
# this is for postfix SASL (authenticated users can relay through us)
socket listen {
               client {
                       path    = /var/spool/postfix/private/dovecot-auth.sock
                       mode    = 0660
                       user    = postfix
                       group   = postfix
                       }
               }
       }
  • 重启相关服务
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-json php-intl
  • 将 roundcube 应用程序链接回 docroot
ln -s /usr/share/webapps/roundcube /var/www/domains/host.example.com/www/roundcube
  • 按照 /usr/share/webapps/roundcube/INSTALL 中的说明进行操作
cd /usr/share/webapps/roundcube
chown -R lighttpd:lighttpd temp logs

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
  [Question to experts: Is this error message normal at this point? "could not save history to file "/var/lib/postgresql/.psql_history": Permission denied"]
  roundcubemail=> \q
exit
  • 编辑 /etc/php/php.ini 并将 date.timezone 设置为您的本地时区或 UTC
  • 重启 lighttpd 以验证是否使用了新的 php 库
rc-service lighttpd restart
  • 将您的浏览器指向 https://host.example.com/roundcube/installer
  • 开始安装

对于安装步骤中的特定配置参数:

属性 设置
enable_spellcheck 禁用
identities_level 一个身份,可以编辑所有参数,但不能编辑电子邮件地址
log driver syslog
sylog_id roundcube
syslog_facility 邮件子系统
db_dnsw pgsql 属性,如上所述
imap_host 127.0.0.1
auto_create_user 已启用
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 文件建议删除安装目录。如果您想稍后保留安装程序,您可以简单地更改所有权和权限。因此,请执行以下操作之一

cd /usr/share/webapps/roundcube
rm -rf LICENSE UPGRADING INSTALL README CHANGELOG  SQL installer

cd /usr/share/webapps/roundcube
chown -R root:root LICENSE UPGRADING INSTALL README CHANGELOG  SQL installer
chmod -R 600 LICENSE UPGRADING INSTALL README CHANGELOG SQL 
chmod 700 SQL installer

启用插件

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

注意:psqlodbc 软件包当前不可用

  • 更新 "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 表(确保使用 TAB 分隔值)

将以下内容放入名为 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 之后启动,并使用纯文本在 localhost 上监听,并使用 SSL 在公共 IP 上监听。如果您在 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

编辑 /usr/share/webapps/roundcube/config/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 记录。

然后,通过编辑 /etc/lighttpd/lighttpd.conf 配置 lighttpd 以处理三个单独的域。

 $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