在 Zoraxy 加入 TLS SNI 功能
最近我在開發的 Zoraxy v3 在我開發 Zoraxy 之前我也不知道 https 證書這東西對於 reverse proxy 伺服器來說到底有多複雜。一開始的時候因為我都是用一個域名(domain name),所以 reverse proxy 裡面也是只需要處理一個主機名稱(host name)。而初代的 Zoraxy 則是直接用 TLS hello info 裡面的 server name 作為 certificate 的 key 來找到合適的 certificate 並回傳給客戶端,所以時間複雜度(time complexity)而言是 O(1) 的速度。換成程式碼大概長這樣: http.ListenAndServeTLS(":443", "server.crt", "server.key", nil) 到後來發現 virtual directory 帶來的麻煩後,Zoraxy v2 加入了 sub-domain 的支援。為了解決找 certificate 的問題, v2 的做法是讓使用者把每個 sub-domain 都 map 到一張 certificate 上面,所以結果就是同一個域名下會有很多張證書(例如說 a.example.com 會有 a.example.com 的證書、b.example.com 會有 b.example.com 的證書)。這樣設計的好處是一來找 certificate 的演算法比較簡單,二來我們可以根據 hello info 的 server name 進行也是 O(1) 時間複雜度的 certificate lookup,不用任何存取 file system 的 loop 便可以直接找到證書。 if fileExists(helloInfo.ServerName+".pem") && fileExists( helloInfo.ServerName+".key") { //Direct hit pubKey = helloInfo.ServerName+".pem" priKey = helloInfo.ServerName+".key" } 然後在 Zoraxy v2 用了這一年多之後就又出現問題了。coauthor 的其中一張證書裡面包含了幾個不同的 domain 跟 subdomain,也有使用者的證書是含 wildcard 的,也有是舊版用 Common Name 來定義(而不是 DNS entry)來標記 host name 的,結果就是一大堆 issue 就出現在 repo 上面。 SNI 的基本概念 SNI 的原理大概就是由伺服器端跟據 TLS Hello Info 的 Server Name 來自動回傳合適的 certificate 給 Client (這裡因為 Zoraxy 是一個 http proxy,那我就以瀏覽器為 client 的例子)。對於很直接的域名例如說 v1 跟 v2 裡面出現的,基本上就是只要把 certificate map 到一個 string 轉 certificate 的資料結構裡面就可以了。但是對於 v3 之後的複雜案例,則是需要一些更複雜的邏輯來處理。 相信來得我部落格的人不是工程師應該都是技術大佬,所以我就直接把 code 拿出來講好了。這裡是 Zoraxy v3 TLS 解釋器裡面最重要的 function func(m * Manager) CertMatchExists(serverName string) bool { for _, certCacheEntry: = range m.LoadedCerts { if certCacheEntry.Cert.VerifyHostname(serverName) == nil || certCacheEntry.Cert.Issuer.CommonName ==…