權威指南:構建個人私有雲,拿回你的數據隱私的控制權!

(題圖來自 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]