OpenSSL 很糟糕。BoringSSL 和 AWS-LC 分支被 Google 和 Amazon 徹底改造,他們只關心自己的使用案例,對其他人漠不關心。我不記得有過使用 GnuTLS 的良好經驗。LibreSSL 則不完整。
這篇文章是關於嘗試使用 WolfSSL 作為現有 Haproxy 伺服器的替換方案的經驗分享,Haproxy 傳統上使用 OpenSSL。WolfSSL 專案特別提供了一個 OpenSSL API 相容層,因此你可以在幾乎任何地方替換 OpenSSL。我在初步測試中遇到了一些無法解釋的錯誤,報告了一個 bug,然後就忘了它,繼續進行。後來我重新審視了我的基礎設施,換上了 Haproxy+WolfSSL,再次遇到那個 bug,認出了它,想起我確實已經報告過這個問題,並最終進行了追蹤。
Anthony 在 GitHub 問題追蹤上幾乎即時地回應,這很令人驚訝,因為我從未遇過這種情況。雖然比真正的聊天還笨拙,但如果他在 GitHub 上是這樣回應的,我敢打賭他們的支援合約會有驚人的 SLA 回應時間。
這篇文章的目的是記錄一個奇特的除錯歷程,這個問題我只能用一種語言(透過 Elixir 的 Erlang/OTP)重現,同時也讓未來在網路上搜尋的 Elixir/Erlang 開發者清楚如何識別這個錯誤的根本原因。在這個過程中,我學到了 TLS 1.3 有一個我之前不知道的功能,WolfSSL 5.8.4 沒有正確處理它,以及一個解決方法。
換上 WolfSSL 並遇到無法解釋的錯誤的經驗很糟糕。無法成功傳達修復此問題的急迫性和重要性也很糟糕。我沒有興趣在 GitHub 上與人爭論,尤其是我不是 TLS 專家(為什麼要聽我的?),所以我退出了。
另外,橙色網站的評論者需要找點事做。他們放大了這篇文章,導致 WolfSSL 本身被拖入泥潭。
聽起來 WolfSSL 將會很快解決這個問題,這真是個好消息。更換伺服器的 TLS 函式庫是一個相當冒險的變動,所以難怪我們在這個領域幾乎是單一文化。我夢想著有一個世界,除了 OpenSSL 或其分支之外,我們還有幾個功能齊全、可供生產環境使用的選項。也許我們快到了,而這只是最後的顛簸之一?
去年,Haproxy 發表了一篇關於 OpenSSL 變得多麼慢的文章。這篇文章流傳了好幾次,我也有個癢想抓,所以我幫助 FreeBSD 打包了一個基於 WolfSSL 的 Haproxy 變體。這似乎是讓更多人接觸 WolfSSL 的簡單方法,因為大多數 Linux 發行版不太可能這樣做,所以實際上,唯一體驗 WolfSSL 支援的 Haproxy 的人是那些知道自己在做什麼並自己客製化建置的人。我還沒實際檢查 Arch、Gentoo、Nix 等是否做了類似的事情,但它們應該是最容易產生類似 haproxy-wolfssl 套件的。
於是我做了,我在幾個地方運行了 WolfSSL,然後我遇到了一個 bug。我報告了 bug,忘記了它,然後繼續進行。然後我又遇到了它,並被激勵去弄清楚。於是我重新打開了 bug,並摸索著除錯這個問題,直到找出根本原因。
TLS 1.3 在 RFC 8446 中定義。它的工作方式與 TLS 1.2 有很大不同,這給他們帶來了無盡的問題,以至於他們記錄了「TLS 1.3 的設計受到廣泛部署的不相容 TLS 中介軟體的限制」。
啊,是的,就是那些臭名昭著的中介軟體。太棒了。那些看不見的垃圾,可以篡改你的流量,而你通常永遠不會知道它們的存在,直到它們給你帶來麻煩。它們一定會的。
地獄絕對是發明中介軟體的地方,任何美好的願望都無法將它們從存在中移除。雖然也許有些 Etsy 的女巫可以提供一些指導,因為她們在解決問題方面有驚人的運氣……
總之,我們有這麼多中介軟體,它們很糟糕,我們希望網路具有比 TLS 1.2 更好的安全性保證,但這些設備只懂 TLS 1.2,而 TLS 1.3 如果被這些設備破壞就無法存在,所以 TLS 1.3 必須能夠假裝成 TLS 1.2。這就是我們現在的處境。
所以作者們對此猶豫不決,並提出了一個解決方案:中介軟體相容模式。
本質上,客戶端可以選擇在 ClientHello 中設定一個非空的 session ID 來欺騙中介軟體,客戶端和伺服器交換虛假的 change_cipher_spec 記錄。這沒有用,只是增加了 TLS 會話建立的延遲,但它會起作用。公平。
RFC 清楚地說明了這一切應該如何進行。
這種「相容模式」是部分協商的:客戶端可以選擇提供 session ID 或不提供,伺服器必須回顯它。
如果客戶端發送一個非空的 session ID,伺服器必須按照此附錄所述發送 change_cipher_spec。
但 WolfSSL 說「謝謝,但不用了」。整個中介軟體相容功能都受制於使用 WOLFSSL_TLS13_MIDDLEBOX_COMPAT 編譯函式庫。[更新:最新的測試結果顯示,啟用此功能後,此功能確實有效。錯誤是我測試環境中的問題。]
因此,目前的狀況是,除非函式庫是使用此標誌啟用編譯的,否則 WolfSSL 不能信任其正確處理 TLS 1.3 客戶端。一般的 TLS 1.3 設定標誌不足夠。因此,預設情況下,互通性取決於客戶端的寬容程度,但當正確實施 RFC 時,你不應該寬容……結尾的 GitHub 問題評論讓我覺得他們對 RFC 合規性並不真正感興趣。這裡沒有中間地帶,也沒有「不同的方法」來實施中介軟體相容性。它要麼符合 RFC,要麼不符合。而他們不符合。要求我開啟一個新問題來討論這種行為,而不是讓他們將其作為高優先級在內部開啟一個新問題來修復,這很奇怪。我不是來為他們做功課的。
更正:先前的編輯提到 WolfSSL 由 ARM 擁有,但我已經在腦中將 PolarSSL 和 WolfSSL 混淆了。糟糕。
這尤其糟糕,因為 WolfSSL 也被用於許多嵌入式設備。從安全角度來看,正確的做法應該是在你的 TLS 1.3 客戶端上始終啟用中介軟體相容模式,以增加成功建立 TLS 1.3 連線的機會。但現在我們做不到。我們應該等幾年,等 WolfSSL 發布修復此問題的版本嗎?嗯,由於它也常用於嵌入式設備,如果你不控制客戶端和伺服器部署,你可能需要等十年才能盲目使用此功能。大錯特錯。
目前我只發現了一個這個決定的受害者,但肯定還有更多。Erlang/OTP 有自己的 ssl 函式庫實作,你可以理所當然地認為他們在添加 TLS 1.3 支援時採納了 Joe 的建議:
先讓它工作,然後讓它漂亮,然後如果你真的、真的必須,讓它快速。
所以為了保護自己,他們選擇預設啟用 middlebox_comp_mode。(如果你想要快速並且知道這樣做是安全的——關閉它)
現在,如果 TLS 1.3 可用,所有 Elixir/Erlang/etc 的 HTTP 客戶端都無法連線到 WolfSSL HTTPS 伺服器。
OpenBSD 可能是對的。我們只需要讓大家關注 LibreSSL,忘記這些其他函式庫。正如 Haproxy 所指出的,它不是 OpenSSL 3.0 搞砸的受害者,因為他們更早分叉,但它缺少一些優化。我認為這是一個公平的權衡,而且這些差距最終會被填補。
所以,不要像我一樣。這大概是我的傲慢。當然,我以為我可以聰明地為我的網站提供更快的 TLS 終止,但結果只是浪費了很多時間去學習我根本不想知道的東西,然後寫下這篇愚蠢的部落格文章。你已被警告。
對於 Elixir 1.17.3(使用 Erlang/OTP 26 編譯)的 PoC 如下所示:
錯誤看起來會像這樣:
這時你就知道你被狼群吞噬了。🤬 如果你將 http_options 改為將 middlebox_comp_mode 設定為 false,它將如預期般工作。