昨天嘗試了用Let's Encrypt給自己的博客部署了HTTPS,感覺(jué)這個(gè)服務(wù)真的是非常方便邻悬。網(wǎng)上有很多關(guān)于Let's Encrypt的文章症昏,不過(guò)本站因?yàn)椴渴鹪赿ocker容器中,關(guān)于這種架構(gòu)的文章不多父丰,我把自己的思路寫下來(lái)吧肝谭。
先來(lái)說(shuō)點(diǎn)基礎(chǔ)的話題。
HTTPS可以提供全面的密碼學(xué)保護(hù)蛾扇,換句話說(shuō)攘烛,訪問(wèn)HTTPS網(wǎng)站時(shí),用戶和網(wǎng)站之間傳輸?shù)臄?shù)據(jù)不會(huì)被第三方竊取和監(jiān)聽(tīng)镀首。這里的第三方包括黑客坟漱、運(yùn)營(yíng)商、監(jiān)管部門更哄,有你想得到的也有你想不到的芋齿。
HTTPS是基于公鑰密碼的,簡(jiǎn)單說(shuō)就是服務(wù)器提供一個(gè)公鑰成翩,用戶用這個(gè)公鑰加密數(shù)據(jù)后發(fā)給服務(wù)器觅捆,然后服務(wù)器用自己的私鑰解密。這里面有一個(gè)問(wèn)題麻敌,即如何確定服務(wù)器的公鑰是真的惠拭,如果第三方攔截并偽造服務(wù)器的公鑰,用戶用第三方的假公鑰加密數(shù)據(jù),那么這些數(shù)據(jù)就會(huì)被第三方解密并竊取职辅。
為了避免這樣的問(wèn)題棒呛,就出現(xiàn)了證書。證書本身是個(gè)數(shù)字簽名域携,就像給服務(wù)器的公鑰蓋個(gè)公章簇秒,用戶看到公章就知道公鑰是真的了。但是秀鞭,這個(gè)問(wèn)題沒(méi)有根本解決趋观,怎么知道公章本身是不是真的呢?因?yàn)楣碌谋举|(zhì)是數(shù)字簽名锋边,于是瀏覽器里面內(nèi)置了一份“可信公章清單”皱坛,凡是在這份清單里的簽名都認(rèn)為是可信的。
因此豆巨,原則上說(shuō)所有人都可以自己頒發(fā)證書剩辟,但自己頒發(fā)的證書不被瀏覽器信任(不在可信公章清單里),瀏覽器就會(huì)報(bào)錯(cuò)(比如12306網(wǎng)站遇到的錯(cuò)誤)往扔。這時(shí)有三種解決方法贩猎,第一是讓用戶強(qiáng)制認(rèn)為自己的證書是可信的(12306的做法),第二是請(qǐng)?jiān)诳尚殴虑鍐卫锏臋C(jī)構(gòu)給自己的網(wǎng)站發(fā)證書萍膛,第三是讓瀏覽器把自己列到可信清單里去(即成為可信的CA)吭服。
第一種做法簡(jiǎn)單粗暴,風(fēng)險(xiǎn)也很大蝗罗,因?yàn)橛脩舭惭b的證書如果是偽造的艇棕,那在訪問(wèn)偽造的釣魚網(wǎng)站時(shí),就會(huì)誤認(rèn)為訪問(wèn)了可信的網(wǎng)站串塑。第三種做法很難欠肾,因?yàn)槌蔀榭尚诺腃A需要嚴(yán)格的條件。大部分網(wǎng)站用的第二種做法拟赊,即請(qǐng)可信CA來(lái)頒發(fā)證書刺桃。
但是CA作為盈利機(jī)構(gòu)不會(huì)免費(fèi)給你提供這種服務(wù),因此請(qǐng)CA頒發(fā)證書是要收費(fèi)的吸祟,這個(gè)費(fèi)用現(xiàn)在越來(lái)越便宜瑟慈,但還是不太便宜。此外屋匕,簽發(fā)和更新證書都要通過(guò)人工完成葛碧,這對(duì)于越來(lái)越要求自動(dòng)化運(yùn)維的互聯(lián)網(wǎng)行業(yè)成了一個(gè)絆腳石。
于是2015年誕生了一個(gè)叫做Let's Encrypt的項(xiàng)目过吻,這個(gè)項(xiàng)目的目標(biāo)是實(shí)現(xiàn)全互聯(lián)網(wǎng)加密进泼。先不管這個(gè)目標(biāo)是否現(xiàn)實(shí)蔗衡,從技術(shù)層面上,Let's Encrypt推出了兩個(gè)革新:第一是免費(fèi)簽發(fā)可信的證書乳绕,第二是實(shí)現(xiàn)證書簽發(fā)和更新的完全自動(dòng)化绞惦。
傳統(tǒng)的CA在簽發(fā)證書的時(shí)候必須驗(yàn)證申請(qǐng)人的身份,每個(gè)證書只能綁定特定的域名使用洋措,換句話說(shuō)济蝉,你得證明你擁有這個(gè)域名的控制權(quán)。Let's Encrypt可以采用多種方式自動(dòng)完成驗(yàn)證菠发,有多種客戶端程序支持Let's Encrypt的方案王滤,還提供了各種主流服務(wù)器的插件。不過(guò)本站的環(huán)境不是典型的環(huán)境滓鸠,關(guān)于各種典型環(huán)境下的部署方法請(qǐng)參見(jiàn)certbot(官方推薦的客戶端)的官方教程雁乡。
先介紹一下我的環(huán)境配置。服務(wù)器是Digital Ocean的一臺(tái)VPS糜俗,系統(tǒng)是CoreOS踱稍,所以這是一臺(tái)只能跑容器的服務(wù)器。服務(wù)器上運(yùn)行了3個(gè)網(wǎng)站吩跋,每個(gè)網(wǎng)站都是一個(gè)單獨(dú)的容器寞射,這些容器是基于PHP官方鏡像(with Apache)定制的渔工,另外有一個(gè)反向代理容器(基于nginx官方鏡像)負(fù)責(zé)根據(jù)域名將訪問(wèn)分配到每個(gè)網(wǎng)站锌钮。
由于CoreOS只能運(yùn)行容器,因此無(wú)法在CoreOS中直接安裝certbot引矩,還好certbot有個(gè)官方的docker鏡像梁丘,我們可以直接pull:
docker pull quay.io/letsencrypt/letsencrypt:latest
運(yùn)行這個(gè)鏡像就可以申請(qǐng)簽發(fā)證書,在運(yùn)行之前首先確保你要申請(qǐng)證書的域名能直接解析到你當(dāng)前這臺(tái)服務(wù)器上旺韭。
docker run -it --rm -p 80:80 -p 443:443 \
-v /etc/letsencrypt:/etc/letsencrypt \
quay.io/letsencrypt/letsencrypt auth
解釋一下這里都做了什么事氛谜。首先-it
選項(xiàng)表示開(kāi)啟交互輸入,因?yàn)樵谏暾?qǐng)證書的過(guò)程中需要用戶輸入一些信息区端;--rm
選項(xiàng)表示容器運(yùn)行結(jié)束后自動(dòng)刪除值漫,因?yàn)檫@是一個(gè)一次性容器;兩個(gè)-p
選項(xiàng)表示映射主機(jī)的兩個(gè)端口织盼,因?yàn)閏ertbot需要通過(guò)這兩個(gè)端口來(lái)做驗(yàn)證杨何,這里需要注意的是,我的nginx容器已經(jīng)占用了這兩個(gè)端口沥邻,因此在申請(qǐng)證書之前危虱,需要先停止nginx容器;-v
選項(xiàng)表示映射磁盤數(shù)據(jù)卷唐全,因?yàn)閏ertbot會(huì)將所有信息保存在/etc/letsencrypt
目錄中埃跷,我們需要讓這個(gè)目錄的內(nèi)容持久化并可以從主機(jī)以及其他容器(主要是nginx容器)訪問(wèn)它。
第一次申請(qǐng)證書需要注冊(cè)賬號(hào),過(guò)程很簡(jiǎn)單弥雹,先同意里面的協(xié)議垃帅,然后輸入一個(gè)郵件地址作為ID就可以了(不需要驗(yàn)證這個(gè)郵箱,只是一個(gè)ID)缅糟,以后運(yùn)行時(shí)賬號(hào)會(huì)保存在本地挺智,就不需要再輸入郵件地址了。接下來(lái)需要選擇驗(yàn)證方式窗宦,這里選擇Temporary web server方式赦颇,也就是說(shuō)讓certbot自己?jiǎn)?dòng)一個(gè)臨時(shí)Web服務(wù)器(因此需要開(kāi)放80和443端口)完成驗(yàn)證。最后輸入你要簽發(fā)證書的域名赴涵,程序自動(dòng)完成認(rèn)證之后媒怯,證書就簽發(fā)好了。
如果你不喜歡這樣一步一步的方式(比如我就不喜歡)髓窜,可以在命令行里提供所有的參數(shù)扇苞,這樣就一步搞定了:
docker run --rm -p 80:80 -p 443:443 \
-v /etc/letsencrypt:/etc/letsencrypt \
quay.io/letsencrypt/letsencrypt auth \
--standalone -m someone@email.com --agree-tos \
-d your.domain1.com -d your.domain2.com
證書簽發(fā)之后,會(huì)存放到/etc/letsencrypt
目錄中寄纵,剛才我們映射了數(shù)據(jù)卷鳖敷,因此可以直接從宿主機(jī)中看到這個(gè)目錄中的內(nèi)容,其中證書位于/etc/letsencrypt/live/your.domain1.com
中程拭。接下來(lái)需要讓nginx容器也能夠讀取這些證書定踱,方法放簡(jiǎn)單,把這個(gè)目錄映射給nginx容器就可以了:
docker run --name nginx -p 80:80 -p 443:443 \
-v /etc/nginx/conf.d:/etc/nginx/conf.d \
-v /etc/letsencrypt:/etc/letsencrypt \
nginx
第一個(gè)-v
是存放nginx配置文件的目錄恃鞋,第二個(gè)-v
就是存放證書的目錄崖媚,接下來(lái)我們?cè)诰W(wǎng)站的配置文件里把證書配上去:
server {
listen 443 ssl;
server_name your.domain1.com;
ssl_certificate /etc/letsencrypt/your.domain1.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/your.domain1.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
location / {
proxy_pass http://172.17.0.1:8001;
}
}
server {
listen 80;
server_name your.domain1.com;
return 301 https://$host$request_uri;
}
配置文件主要就是ssl_certificate
和ssl_certificate_key
兩行,第一行是提供給客戶端的公鑰(證書)恤浪,第二行是服務(wù)器用來(lái)解密客戶端消息的私鑰(私鑰不會(huì)畅哑,也不應(yīng)該在網(wǎng)絡(luò)上傳輸)。后面第二個(gè)server塊是將直接用HTTP的訪問(wèn)重定向到HTTPS連接上水由。
修改好配置文件之后重啟nginx容器荠呐,順利的話網(wǎng)站就可以通過(guò)HTTPS訪問(wèn)了,可以通過(guò)瀏覽器看一下證書信息砂客,頒發(fā)者是Let's Encrypt Authority X3泥张,它的根CA是DST,即IdenTrust鞭盟,這是一個(gè)為銀行和金融提供證書的可信CA圾结,通過(guò)和IdenTrust交叉驗(yàn)證,Let's Encrypt的證書可以在各種瀏覽器上確背菟撸可信筝野。不過(guò)Let's Encrypt簽發(fā)的證書是短效證書晌姚,有效期只有3個(gè)月,但沒(méi)關(guān)系歇竟,我們可以通過(guò)一個(gè)簡(jiǎn)單的命令對(duì)證書進(jìn)行更新挥唠,同樣是通過(guò)docker容器來(lái)運(yùn)行:
docker run --rm -p 80:80 -p 443:443 \
-v /etc/letsencrypt:/etc/letsencrypt \
quay.io/letsencrypt/letsencrypt renew \
--standalone
運(yùn)行這個(gè)命令時(shí),certbot會(huì)自動(dòng)檢查確認(rèn)證書有效期焕议,如果過(guò)期時(shí)間在一個(gè)月之內(nèi)宝磨,就會(huì)自動(dòng)更新。在CoreOS中盅安,由于沒(méi)有Cron唤锉,我們需要通過(guò)systemd的timer來(lái)做定時(shí)調(diào)度,比如每個(gè)月運(yùn)行一次這個(gè)renew任務(wù)就可以了别瞭,不過(guò)記得運(yùn)行之前先停止nginx容器窿祥,運(yùn)行之后再啟動(dòng)nginx容器。
除了standalone方式驗(yàn)證之外蝙寨,還可以使用wwwroot方式來(lái)做驗(yàn)證晒衩,但在我的環(huán)境中,nginx容器只是反向代理墙歪,本身沒(méi)有wwwroot听系,因此standalone方式比較簡(jiǎn)單,當(dāng)然缺點(diǎn)是每次簽發(fā)和更新證書都要先停止nginx容器虹菲,這會(huì)造成網(wǎng)站服務(wù)中斷靠胜。如果需要保證服務(wù)不中斷,可以為nginx容器單獨(dú)配一個(gè)驗(yàn)證用的wwwroot届惋。
好了髓帽,祝大家加密愉快菠赚,祝Let's Encrypt不要那么快被墻脑豹。