阿裏雲 CDN HTTPS 最佳實踐系列——動態證書(一)
背景
了解阿裏雲 CDN 架構的朋友應該知道,阿裏雲 CDN 7層的接入組件是 Tengine,我們知道 Tengine 原生是支持 SSL 的,只需要在配置文件中配置證書和私鑰即可。在 CDN HTTPS 產品化以前,要開通 HTTPS 的域名需要把證書私鑰給我們,我們在 Tengine 靜態配置中配置,然後再同步到所有 CDN 邊緣節點,顯然這種方式在越來越多的域名開通 HTTPS 後,Tengine 靜態配置文件會越來越大,難以管理,同時也會導致 Tengine reload 變得很慢,這樣配置生效的時間就很糟糕,還有私鑰安全等等一系列問題。所以 CDN HTTPS 產品化時就必須采用動態證書的方式,目前阿裏雲 CDN HTTPS 證書配置之後1分鐘內生效,極大的提高了證書管理效率和用戶體驗。這兩種方式的簡單對比如下:
架構
動態配置的同步有兩種方式:
1. 主動同步
當用戶在用戶控制臺上配置證書和私鑰之後主動向下同步到 CDN 所有節點的 Tengine 中,這樣當配置同步成功後用戶域名的 https 訪問就正常了,但是這種方式有很多缺點,因為 Tengine 機器有幾千臺,甚至上萬臺,把證書私鑰同步到這麽多機器用時較大,生效時間較慢,另外其他一些域名並不是訪問到所有的 CDN 機器,如果把證書私鑰同步到這臺機器並沒有什麽用,白白浪費內存,以及影響證書的搜索性能。這種方式適合在少量機器群集中使用。
2. Lazy pull
當 https 請求到達 Tengine 後,Tengine 再去拉取配置,這樣就只拉取該機器有訪問的域名配置,其他域名配置不需要拉取,解決了 Tengine 靜態配置過多的問題,也避免了 Tengine reload,從而生效時間更快。
綜合以上兩種方式的優點,阿裏雲 CDN 在節點機房內部采用了 Lazy pull 方式拉取配置,在節點機房和中心機房之前采用主動同步的方式。以下是簡化的 HTTPS 動態配置架構圖。
實現
主動同步有很多種方式可以選擇,比如 redis、zoomkeeper等等,這裏只講 Lazy pull 的實現,其關鍵技術是基於 Tengine 的指令 ssl_certificate_by_lua_file,這個指令是 lua-nginx 模塊提供的,其用途就是在 openssl 中查找證書時的一個 lua 回調,然後在 lua 中動態設置證書和私鑰。總之,如果 Tengine 的 server 塊中配置了這個指令的話,當 openssl 在處理 SSL 握手消息 ClientHello 時會調用到這個指令配置的 lua 代碼,在 lua 中我們可以做我們想做的事情,比如發 http 請求去遠程拉取證書和私鑰,做配置緩存,私鑰加解密處理,動態設置證書和私鑰等等一系列業務邏輯。
以下是 Tengine 的配置:
server { listen 0.0.0.0:443 default_server ssl http2; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #ssl_ciphers [xx]; ssl_prefer_server_ciphers on; ssl_certificate default.crt; ssl_certificate_key default.key; ssl_certificate_by_lua_file dycert.lua; include dyconf.cfg; }
如上配置,dycert.lua 是阿裏雲 CDN 實現的動態證書模塊,在 SSL 完整握手時會調用到這個模塊,在 Session 復用的握手情況下不會調用到這個模塊,這是因為 Session 復用時不需要證書和私鑰,這是 openssl 回調接口的官方實現,但是阿裏雲 CDN 的實現中,還有很多 HTTPS 的動態配置需要在 dycert 模塊中來設置,所以我們修改了 openssl,讓其在 Session 復用時也調用到 dycert 模塊,這為我們實現很多 HTTPS 動態配置(比如: HTTP/2 開關,客戶端認證,TLS record size 配置)提供了方便。
如下圖所示,dycert 模塊是所有 HTTP(S) 業務模塊的第一個重要模塊,當 https 請求到達 CDN Tengine 時,dycert 模塊會去遠程拉取 HTTPS 動態配置(證書、私鑰、HTTP/2 開關、客戶端證書、TLS record size 配置等等),然後解密私鑰,將證書和私鑰設置到 openssl 中,讓其恢復 SSL 握手流程。握手成功後會將該請求交給 Tengine 的後續業務模塊處理。
下面是 dycert 模塊的實現原理圖:
眾所周知,Tengine 是多 worker 的,我們需要使用共享內存來緩存拉取到的動態配置,這樣可以防止多個 worker 進程同時去遠程拉取同一份配置,並且在 worker 進程內存中也同時做了緩存以提高效率。
當一個 https 請求調用到 Tengine dycert 模塊時,先在本 worker 緩存中查詢是否存在配置,如果沒有就去共享內存中查詢,如果還是沒有就得去遠程拉取了(遠程拉取采用 http 的 resty api 方式),拉取到動態配置之後在本 worker 中緩存,同時也在共享內存中緩存,為了防止共享內存暴增,加了一個定時器來刪除該域名的配置,下次從共享內存中查詢不到配置再重新從遠程拉取。
另外一個重點就是配置的更新後如何實時生效了:配置管理系統 agent 是可以實時發現域名的配置變化的,並且提供了配置變更註冊機制,dycert 會定時的去註冊 HTTPS 配置變更,並提供一個 purge resty api,當配置管理系統 agent 發現配置更新之後會回調到該接口,然後該接口會從共享內存中刪除該域名的配置,下次該域名的 https 請求到達 dycert 模塊後,發現共享內存中沒有配置會再去配置管理系統 agent 中拉取,從而解決了配置更新後實時生效的問題。
以上為本文內容。目前,阿裏雲CDN HTTPS已經全面降價,後付費HTTPS 0.05元/萬次請求。下一篇我們將介紹HTTPS最佳實踐——HTTP/2,敬請期待。
阿裏雲 CDN HTTPS 最佳實踐系列——動態證書(一)