权威指南:构建个人私有云,拿回你的数据隐私的控制权!

(题图来自 ttgtmedia.com)
但是事实并不是一定必须这样的。你可以生活在21世纪,拿着智能手机,每天都用电子邮件和GPS,却仍然可以保留自己的隐私。你所需要的就是拿回自己个人数据的控制权:邮件、日程、联系人、文件,等等。Prism-Break.org网站上列出了一些能帮你掌握个人数据命运的软件。除此以外,控制自己个人数据的最安全和最有效的方式是架设自己的服务器并搭建自己的云。不过你也许只是没有时间或精力去研究具体该怎么做以及如何让它能流畅工作。
这也是这篇文章的意义所在。仅仅5个小时内,我们将配置出一台服务器来支撑你的邮件、联系人、日程表和各种文件,为你、你的朋友和你的家人。这个服务器将设计成一个个人数据中心或云,所以你能时刻保留它的完整控制。数据将自动在你的台式机/笔记本、手机和平板之间同步。从根本上来说,我们将建立一个系统来代替Gmail、Google文件/Dropbox、Google联系人、Google日历和Picasa。
为自己做这件事情已经是迈出很大一步了。但是,你个人信息的很大一部分将仍然泄漏出去并保存到硅谷的一些主机上,只是因为和你日常来往的太多人在用Gmail和使用智能手机,所以最好是带上你一些比较亲近的人加入这次探险。
我们将构建的系统能够:
- 支持任意数目的域名和用户。这样就能轻易地和你的家人朋友共享这台服务器,所以他们也能掌控自己的个人数据,并且还能和你一起分摊服务费用。和你一起共享服务器的人可以使用他们自己的域名或者共享你的。
- 允许你从任意网络发送和接收电子邮件,需要成功登录服务器之后。这样,你可以通过任意的邮件地址、任意设备(台式机、手机、平板)、任意网络(家里、公司、公共网络、...)来发送电子邮件。
- 在发送和接收邮件的时候加密网络数据,这样,你不信任的人不能钓出你的密码,也不能看到你的私人邮件。
- 提供最先进的反垃圾邮件技术,结合了已知垃圾邮件黑名单、自动灰名单、和自适应垃圾邮件过滤。如果邮件被误判了只需要简单地把它拖入或拖出垃圾目录就可以重新调校垃圾邮件过滤器。而且,服务器还会为基于社区的反垃圾邮件努力做出贡献。
- 一段时间里只需要几分钟的维护,基本上只是安装安全更新和简单地检查一下服务器日志。添加一个新的邮件地址只需要在数据库中插入一条记录。除此之外,你可以忘记它的存在过自己的生活。我在14个月之前搭建了本文描述的这个系统,从那以后就一直顺利运行。所以我完全把它给忘了,直到我最近觉得随便按下手机上的‘检查邮件’会导致电子信号一路跑到冰岛(我放置服务器的地方)再回来的想法有点好笑才想起来。
要完成这篇文章里的工作,你需要一点基本的技术能力。如果你知道SMTP和IMAP的区别,什么是DNS,以及对TCP/IP有基本了解的话,就够了。你还将需要一点基本的Unix知识(在命令行下和文件一起工作,基本的系统管理)。然后你需要花总共5小时时间来搭建。
下面是我们将要做的事情的概述。
- 申请一个虚拟私人服务器,一个域名,并把它们配置好
- 设置postfix和dovecot来收发电子邮件
- 阻止垃圾邮件进入你的收件箱
- 确保你发出的邮件能通过垃圾邮件过滤器
- 使用Owncloud提供日历,联系人,文件服务并配置webmail
- 在云上同步你的设备
这篇文章是受之前工作的启发并以之为基础
本文很大程度参考了两篇文章,由Xavier Claude和Drew Crawford写的关于架设私有邮件服务器的介绍。
本文覆盖了Xavier和Drew的文章里所描述的所有功能,除了3个地方Drew有而我没有:邮件推送支持(我喜欢由我主动检查邮件,而其他时候都不会被打扰),邮件全文检索(我一直都没用过),以及使用加密方式存储邮件(我的邮件和数据还没那么重要到要把它们加密后再存到本地服务器上)。如果你需要这些功能,只需要按照Drew的文章里相应部分的说明做就好了,和本文的内容兼容。
和Xavier和Drew的成果比起来,本文有下面几个主要改进:
- 根据我自己按Drew文章操作的经验以及原文的大量回复,修改了一些问题和文字错误。我也把本文所介绍的内容仔细检查了几遍,从头开始设定了几次服务器做重复验证以确保能正常工作。
- 低维护:和Xavier的方式比起来,本文增加了在服务器上支持多个邮件域名。这样做是为了尽可能地减少服务器维护工作:基本上,要添加一个域名或用户,只需要往mysql数据库表里增加一行就好了(不需要增加过滤脚本,等等)。
- 我增加了webmail。
- 我增加了设定云服务器的部分,不仅能收发邮件还能管理文件,地址本/联系人(邮件地址,电话号码,生日,等等等),日程表和图片,供所有设备访问使用。
申请一个虚拟私人服务器,一个域名,并把它们配置好
让我们从设置基础设施开始:我们的虚拟私人主机和我们的域名。
我用过1984.is和Linode提供的虚拟私人主机(VPS),体验非常好。在本文中,我们将使用Debian Wheezy,这个在1984和Linode都提供了已经做好的映像文件可以直接布置到你的VPS上。我喜欢1984是因为它的服务器在冰岛,也是唯一使用可再生能源(地热和水力发电)的地方,目前还没有影响过气候变化,不像大多数美国数据中心目前大多数依赖于烧煤的火力发电站。而且,他们注重民权,透明,自由以及免费软件。
最好是在服务器上创建一个文件用来保存后面要用到的各种密码(用户账号、邮件账号、云帐号、数据库帐号)。当然最好是加密一下(可以用GnuPG),这样就算用来设定服务器的电脑被偷了或被入侵了,你的服务器就不会那么容易被攻击。
关于注册域名,我已经使用grandi的服务超过10年了,也很满意。在本文中,我们将开辟一个叫jhausse.net的域名。然后在上面增加一个叫cloud.jhausse.net的二级域名,并绑定MX纪录。在完成之后,设置比较短的纪录生存时间(TTL)比如300秒,这样你在设置服务器的时候,可以修改你的域并很快测试到结果。
最后,设置PTR纪录(反向DNS),这样IP地址可以反向映射回它的域名。如果你不理解前面这句话,看下这篇文章来获得相关背景知识。如果你使用Linode的服务,你可以在远程访问这一栏的控制面板里设置PTR纪录。如果是1984,联系一下技术支持来帮你搞定。
在服务器上,我们从添加一个普通用户开始,这样我们不用从头到尾一直用root账号。另外,用root登陆也需要额外多一层安全措施。
adduser roudy
然后,在文件/etc/ssh/sshd_config中设置:
PermitRootLogin no
然后重启ssh服务:
service ssh reload
然后,我们要修改服务器的主机名。编辑文件/etc/hostname,只有一行就是自己的主机名,我们这个例子中是:
cloud
然后,编辑ssh服务的公钥文件/etc/ssh/ssh_host_rsa_key.pub, /etc/ssh/ssh_host_dsa_key.pub, /etc/ssh/ssh_host_ecdsa_key.pub,这样文件末尾可以反映你的主机名,比如root@cloud。然后重启系统保证主机名在系统的每个需要它的角落都生效了。
reboot
我们将更新系统并移除不必要的服务以降低远程攻击的风险。
apt-get update
apt-get dist-upgrade
service exim4 stop
apt-get remove exim4 rpcbind
apt-get autoremove
apt-get install vim
我喜欢使用vim远程编辑配置文件。打开vim 的自动语法高亮会很有帮助。添加下面这一行到~/.vimrc文件中。
syn on
设置postfix和dovecot来收发电子邮件
postfix
apt-get install postfix postfix-mysql dovecot-core dovecot-imapd dovecot-mysql mysql-server dovecot-lmtpd postgrey
在Postfix的配置菜单里,选择Internet Site
,设置这个系统的邮件名称为jhausse.net。
现在开始添加一个数据库用于保存主机上管理的域名列表,和每个域名下的用户列表(同时也包括他们各自的密码),以及邮件别名列表(用于从一个地址往另一个地址转发邮件)。
mysqladmin -p create mailserver
mysql -p mailserver
mysql> GRANT SELECT ON mailserver.* TO 'mailuser'@'localhost' IDENTIFIED BY 'mailuserpass';
mysql> FLUSH PRIVILEGES;
mysql> CREATE TABLE `virtual_domains` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
mysql> CREATE TABLE `virtual_users` (
`id` int(11) NOT NULL auto_increment,
`domain_id` int(11) NOT NULL,
`password` varchar(106) NOT NULL,
`email` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
mysql> CREATE TABLE `virtual_aliases` (
`id` int(11) NOT NULL auto_increment,
`domain_id` int(11) NOT NULL,
`source` varchar(100) NOT NULL,
`destination` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
这里我们为jhausse.net域名提供邮件服务。如果还需要加入其他域名,也没问题。我们也会为每个域名设置一个邮件管理地址(postmaster),转寄给[email protected]。
mysql> INSERT INTO virtual_domains (`name`) VALUES ('jhausse.net');
mysql> INSERT INTO virtual_domains (`name`) VALUES ('otherdomain.net');
mysql> INSERT INTO virtual_aliases (`domain_id`, `source`, `destination`) VALUES ('1', 'postmaster', '[email protected]');
mysql> INSERT INTO virtual_aliases (`domain_id`, `source`, `destination`) VALUES ('2', 'postmaster', '[email protected]');
现在已经添加了一个本地邮件账号[email protected]。首先,为它生成一个密码的哈希串:
doveadm pw -s SHA512-CRYPT
然后把哈希值加入到数据库中:
mysql> INSERT INTO `mailserver`.`virtual_users` (`domain_id`, `password`, `email`) VALUES ('1', '$6$YOURPASSWORDHASH', '[email protected]');
现在我们的域名、别名和用户列表都设置好了,然后开始设置postfix(这是一个SMTP服务器,用来发送邮件)。把文件/etc/postfix/main.cf替换为下面的内容:
myhostname = cloud.jhausse.net
myorigin = /etc/mailname
mydestination = localhost.localdomain, localhost
mynetworks_style = host
# We disable relaying in the general case
smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination
# Requirements on servers that contact us: we verify the client is not a
# known spammer (reject_rbl_client) and use a graylist mechanism
# (postgrey) to help reducing spam (check_policy_service)
smtpd_client_restrictions = permit_mynetworks, reject_rbl_client zen.spamhaus.org, check_policy_service inet:127.0.0.1:10023
disable_vrfy_command = yes
inet_interfaces = all
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no
append_dot_mydomain = no
readme_directory = no
# TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/cloud.crt
smtpd_tls_key_file=/etc/ssl/private/cloud.key
smtpd_use_tls=yes
smtpd_tls_auth_only = yes
smtp_tls_security_level=may
smtp_tls_loglevel = 1
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
# Delivery
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
message_size_limit = 50000000
recipient_delimiter = +
# The next lines are useful to set up a backup MX for myfriendsdomain.org
# relay_domains = myfriendsdomain.org
# relay_recipient_maps =
# Virtual domains
virtual_transport = lmtp:unix:private/dovecot-lmtp
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf
local_recipient_maps = $virtual_mailbox_maps
现在我们要让postfix知道如何从我们设定的数据库里找出需要接收邮件的域名。建立一个新文件/etc/postfix/mysql-virtual-mailbox-domains.cf并添加以下内容:
user = mailuser
password = mailuserpass
hosts = 127.0.0.1
dbname = mailserver
query = SELECT 1 FROM virtual_domains WHERE name='%s'
我们可以让postfix判断给定的电子邮件账号是否存在,创建文件/etc/postfix/mysql-virtual-mailbox-maps.cf并写入以下内容:
user = mailuser
password = mailuserpass
hosts = 127.0.0.1
dbname = mailserver
query = SELECT 1 FROM virtual_users WHERE email='%s'
最后,postfix会根据文件/etc/postfix/mysql-virtual-alias-maps.cf的内容来查找邮件别名
user = mailuser
password = mailuserpass
hosts = 127.0.0.1
dbname = mailserver
query = SELECT virtual_aliases.destination as destination FROM virtual_aliases, virtual_domains WHERE virtual_aliases.source='%u' AND virtual_aliases.domain_id = virtual_domains.id AND virtual_domains.name='%d'
在配置好这些后,现在要测试一下postfix是否能正常查询数据库。我们可以用postmap命令测试:
postmap -q jhausse.net mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
postmap -q [email protected] mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
postmap -q [email protected] mysql:/etc/postfix/mysql-virtual-alias-maps.cf
postmap -q [email protected] mysql:/etc/postfix/mysql-virtual-alias-maps.cf
如果一切都正常配置了的话,头两个查询应该输出1,第3个查询应该输出[email protected],而最后一个应该什么都不输出。
dovecot
现在,让我们设置一下dovecot(一个IMAP服务程序,用来在我们的设备上从服务器获取收到的邮件)。编辑文件/etc/dovecot/dovecot.conf设置以下参数:
# Enable installed protocol
# !include_try /usr/share/dovecot/protocols.d/*.protocol
protocols = imap lmtp
这样将只打开imap(让我们可以获取邮件)和lmtp(postfix用来将收件箱里的邮件转给dovecot)。编辑/etc/dovecot/conf.d/10-mail.conf并设置以下参数:
mail_location = maildir:/var/mail/%d/%n
[...]
mail_privileged_group = mail
[...]
first_valid_uid = 0
这样邮件将被保存到目录 /var/mail/domainname/username 下。注意下这几个选项散布在配置文件的不同位置,有时已经在那里写好了:我们只需要取消注释即可。文件里的其他设定选项,可以维持原样。在本文后面还有很多文件需要用同样的方式更新设置。在文件/etc/dovecot/conf.d/10-auth.conf里,设置以下参数:
disable_plaintext_auth = yes
auth_mechanisms = plain
#!include auth-system.conf.ext
!include auth-sql.conf.ext
在文件/etc/dovecot/conf.d/auth-sql.conf.ext里,设置以下参数:
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = static
args = uid=mail gid=mail home=/var/mail/%d/%n
}
这是告诉dovecot用户的邮件保存在目录/var/mail/domainname/username下,以及如何从我们刚建立的数据库里查找密码。现在我们还需要告诉dovecot具体如何使用数据库。这样需要把下面的内容加入/etc/dovecot/dovecot-sql.conf.ext文件:
driver = mysql
connect = host=localhost dbname=mailserver user=mailuser password=mailuserpass
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';
我们现在修改一下配置文件的权限
chown -R mail:dovecot /etc/dovecot
chmod -R o-rwx /etc/dovecot
基本差不多了!只是还需要再多编辑几个文件。在文件/etc/dovecot/conf.d/10-master.conf里,设置以下参数:
service imap-login {
inet_listener imap {
#port = 143
port = 0
}
inet_listener imaps {
port = 993
ssl = yes
}
}
service pop3-login {
inet_listener pop3 {
#port = 110
port = 0
}
inet_listener pop3s {
#port = 995
#ssl = yes
port = 0
}
}
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0666
group = postfix
user = postfix
}
user = mail
}
service auth {
unix_listener auth-userdb {
mode = 0600
user = mail
#group =
}
# Postfix smtp-auth
unix_listener /var/spool/postfix/private/auth {
mode = 0666
user = postfix
group = postfix
}
# Auth process is run as this user.
#user = $default_internal_user
user = dovecot
}
service auth-worker {
user = mail
}
注意下我们把除了imaps之外所有服务的端口都设置成了0,这样可以有效地禁止这些服务。然后,在文件/etc/dovecot/conf.d/15-lda.conf里,指定一个邮箱管理地址:
postmaster_address = [email protected]
最后但很重要的一点,我们为服务器需要生成一对公钥和私钥,可以同时用于dovecot和postfix:
openssl req -new -newkey rsa:4096 -x509 -days 365 -nodes -out "/etc/ssl/certs/cloud.crt" -keyout "/etc/ssl/private/cloud.key"
请确保你指定了服务器的完全限定域名(FQDN),在本文的例子里:
Common Name (e.g. server FQDN or YOUR name) []:cloud.jhausse.net
如果没有的话,我们的客户端会抱怨在SSL证书里的服务器名字和所连接的服务器名字不一致。我们将通过修改配置文件/etc/dovecot/conf.d/10-ssl.conf里的如下选项来告诉dovecot使用刚生成的密钥:
ssl = required
ssl_cert = </etc/ssl/certs/cloud.crt
ssl_key = </etc/ssl/private/cloud.key
测试
就这些了!现在开始测试postfix和dovecot服务!
service dovecot restart
service postfix restart
在服务器上,尝试发送邮件给本地用户:
telnet localhost 25
EHLO cloud.jhausse.net
MAIL FROM:[email protected]
RCPT TO:[email protected]
data
Subject: Hallo!
This is a test, to check if cloud.jhausse.net is ready to be an MX!
Cheers, Roudy
.
QUIT
服务器应该接受我们的邮件并返回类似消息:
250 2.0.0 Ok: queued as 58D54101DB
如果一切正常的话,检查一下/var/log/mail.log里的日志。应该有类似下面的一行:
Nov 14 07:57:06 cloud dovecot: lmtp(4375, [email protected]): ... saved mail to INBOX
到这里一切都正常吗?不错。现在,让我尝试从不同的机器发邮件,比如说我们用来设定服务器的电脑。这次我们使用加密方式(TLS)和服务器对话:
openssl s_client -connect cloud.jhausse.net:25 -starttls smtp
EHLO cloud.jhausse.net
MAIL FROM:[email protected]
rcpt to:[email protected]
服务器应该有这样的响应:
554 5.7.1 <[email protected]>: Relay access denied
这个没问题:如果服务器能接受这封邮件而不是返回如上的拒绝消息,那意味着我们架设的postfix是一个对全世界所有垃圾邮件都开放的中继,这将完全没法使用。除了'Relay access denied'消息,你也可能会收到这样的响应:
554 5.7.1 Service unavailable; Client host [87.68.61.119] blocked using zen.spamhaus.org; http://www.spamhaus.org/query/bl?ip=87.68.61.119
意思是你正尝试从一个被标记成垃圾邮件发送者的IP地址连接服务器。我在通过普通的因特网服务提供商(ISP)连接服务器时曾收到过这样的消息。要解决这个问题,可以试着从另一个主机发起连接,比如另外一个你可以SSH登录的主机。另外一种方式是,你可以修改postfix的main.cf配置文件,不要使用Spamhous的RBL,重启postfix服务,然后再检查上面的测试是否正常。不管用哪种方式,最重要的是你要确定一个能工作的,因为我们后面马上要测试其他功能。如果你选择了重新配置postfix不使用RBL,别忘了在完成本文后重新开启RBL并重启postfix,以避免收到一些不必要的垃圾邮件。(LCTT 译者注:在国内可以使用 CASA 的 RBL:cblplus.anti-spam.org.cn,参见:http://www.anti-spam.org.cn/ 。)
现在,我们试一下往SMTP端口25发送一封有效的邮件,这是一般正常的邮件服务器用来彼此对话的方式:
openssl s_client -connect cloud.jhausse.net:25 -starttls smtp
EHLO cloud.jhausse.net
MAIL FROM:[email protected]
RCPT TO:[email protected]
服务器应该有这样的响应:
Client host rejected: Greylisted, see http://postgrey.schweikert.ch/help/jhausse.net.html
这意味着postgrey工作正常。postgrey做的是用临时错误拒绝未知发送者的邮件。邮件的技术规则是要求邮件服务器尝试重新发送邮件。在5分钟后,postgrey就会接收这封邮件。一般世界范围内遵守规则的邮件服务器都会尝试为我们重复投递邮件,但大多数垃圾邮件发送者不会这样做。所以,等上5分钟,再次通过上面的命令发送一次,然后检查postfix应该正常接收了邮件。
之后,我们检查一下我们可以通过IMAP和dovecot对话获取刚才发送的两封邮件。
openssl s_client -crlf -connect cloud.jhausse.net:993
1 login [email protected] "mypassword"
2 LIST "" "*"
3 SELECT INBOX
4 UID fetch 1:1 (UID RFC822.SIZE FLAGS BODY.PEEK[])
5 LOGOUT
这里,你应该把mypassword替换为你自己为这个邮件账号设定的密码。如果能正常工作,基本上我们已经拥有一个能接收邮件的邮件服务器了,通过它我们可以在各种设备(PC/笔记本、平板、手机...)上收取邮件了。
外发(中继)邮件
但是我们不能把邮件给它发送出去,除非我们自己从服务器发送。现在我们将让postfix为我们转发邮件,但是这个只有成功登录才可以,这是为了保证邮件是由服务器上的某个有效帐号发出来的。要做到这个,我们要打开一个特殊的,全程SSL连接的,SASL鉴权的邮件提交服务。在文件/etc/postfix/master.cf里设置下面的参数:
submission inet n - - - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
-o smtpd_sasl_security_options=noanonymous
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject_non_fqdn_recipient,reject_unauth_destination
然后重启postfix服务:
service postfix reload
现在,让我们试试从一台不同的机器连接这个服务,确定一下postfix现在能够正常中继我们自己的而不是其他任何人的邮件:
openssl s_client -connect cloud.jhausse.net:587 -starttls smtp
EHLO cloud.jhausse.net
注意一下服务器建议的'250-AUTH PLAIN'功能,在从端口25连接的时候不会出现。
MAIL FROM:[email protected]
rcpt to:[email protected]
554 5.7.1 <[email protected]>: Relay access denied
QUIT
这个没问题,postfix在不认识我们的时候是不会中继邮件的。所以,首先让我们先鉴定一下自己的身份。要这样做,我们首先需要生成一个鉴权字符串:
echo -ne ' [email protected]