2023 年 10 月 4 日 1.1.1.1 查詢失敗

2023 年 10 月 4 日,Cloudflare 於世界標準時 7:00 開始至 11:00 結束期間遇到 DNS 解析問題。1.1.1.1 或 Warp 、 Zero Trust 等產品的一些使用者,或使用 1.1.1.1 的第三方 DNS 解析程式可能已經收到對有效查詢的 SERVFAIL DNS 回應。對於此次服務中斷,我們深感抱歉。此次服務中斷為內部軟體錯誤,而非攻擊造成的結果。在這篇部落格中,我們將討論失敗的內容、發生的原因,以及我們可以採取哪些措施來確保這種情況不再發生。

背景

Domain Name System (DNS) 中,每一個網域名稱存在於 DNS 區域內。區域是在一起接受控制的網域名稱和主機名稱的集合。例如,Cloudflare 負責網域 cloudflare.com,我們稱之為「cloudflare.com」區域。頂級網域 (TLD) .com 由第三方擁有,位於「com」區域。它提供如何連線 cloudflare.com 的指示。所有 TLD 之上為根區域,提供如何連線 TLD 的指示。這意味著根區域對於解析所有其他網域名稱很重要。與 DNS 的其他重要部分一樣,根區域使用 DNSSEC 進行簽署,這也意味著根區域本身包含加密簽章。

根區域發布於根伺服器上,但 DNS 營運商自動擷取並保留根區域副本的情況也很常見, 以便在無法連線根伺服器的情況下,根區域中的資訊仍然可供使用。Cloudflare 的遞迴 DNS 基礎架構會採用此方法,因為它還可加速解析程序。新版根區域通常一天發布兩次。1.1.1.1 具有稱為 static_zone 的 WebAssembly 應用程式,該應用程式執行於主 DNS 邏輯之上,當新版本可供使用時,即可提供這些新的版本。

狀況說明

9 月 21 日,作為根區域管理中的已知計畫內變更的一部分,新的資源記錄類型首次納入根區域。新資源記錄稱為 ZONEMD,實際上是根區域內容的總和檢查碼。

藉由執行於 Cloudflare 核心網路的軟體來擷取根區域。隨後,根區域被重新分散到 Cloudflare 在世界各地的資料中心。變更之後,可繼續正常擷取和分散包含 ZONEMD 記錄的根區域。然而,使用該資料的 1.1.1.1 解析程式系統在剖析 ZONEMD 記錄時遇到問題。由於區域必須完整載入和提供,因此系統無法剖析 ZONEMD,這意味著新版根區域未在 Cloudflare 的解析程式系統中使用。若託管 Cloudflare 解析程式基礎架構的某些伺服器未收到新的根區域,則會發生容錯移轉,直接逐個請求地查詢 DNS 根伺服器。不過,其他伺服器會繼續依賴其記憶體快取仍然可用的已知工作版根區域,這是在變更之前於 9 月 21 日提取的版本。

2023 年 10 月 4 日世界標準時 7:00,根區域版本中自 9 月 21 日開始的 DNSSEC 簽章到期。由於 Cloudflare 解析程式系統沒有能夠使用的更新版本,某些 Cloudflare 解析程式系統無法驗證 DNSSEC 簽章,並因此開始傳送錯誤回應 (SERVFAIL)。Cloudflare 解析程式產生 SERVFAIL 回應的速度提升了 12%。下圖說明了失敗的進度,以及如何顯示給使用者。

事件時間表和影響

9 月 21 日世界標準時 6:30:根區最後一次成功提取
10 月 4 日世界標準時 7:00:根區域中於 9 月 21 日取得的 DNSSEC 簽章到期,導致對用戶端查詢的 SERVFAIL 回應增加。
7:57︰第一個外部非預期 SERVFAIL 報告開始出現。
8:03︰正式宣佈發生內部 Cloudflare 事件。
8:50:初次嘗試阻止 1.1.1.1 使用具有覆寫規則的過時根區域檔案來提供回應。
10:30:完全阻止 1.1.1.1 預先載入根區域檔案。
10:32:回應恢復正常。
11:02︰事件結束。

下圖顯示了影響時間表,以及傳回 SERVFAIL 錯誤的 DNS 查詢百分比:

我們預計,在正常操作期間,常規流量的 SERVFAIL 錯誤數量會達到基準。該比例通常在 3% 左右。這些 SERVFAIL 可能是由於 DNSSEC 鏈中的合法問題、無法連線至權威伺服器、權威伺服器回應時間太長,以及其他因素引起。在事件發生期間,SERVFAIL 數量達到峰值,佔查詢總計的 15%,但影響在全球範圍內的分佈並不均勻,且主要集中在較大型的資料中心,例如維吉尼亞州阿什本、德國法蘭克福和新加坡。

為什麼會發生這一事件

剖析 ZONEMD 記錄失敗的原因

DNS 採用二進位格式來儲存資源記錄。在此二進位格式中,資源記錄 (TYPE) 的類型儲存為 16 位元整數。資源記錄的類型確定剖析資源資料 (RDATA) 的方式。當記錄類型為 1 時,這意味著它是 A 記錄,RDATA 可以解析為 IPv4 位址。記錄類型 28 為 AAAA 記錄,而其 RDATA 則剖析為 IPv6 位址。當剖析器遇到未知的資源類型時,它不知道如何剖析其 RDATA,但幸運的是它不必這樣做:RDLENGTH 欄位表示 RDATA 欄位的長度,允許剖析器將其視為 OPAQUE 資料元素。

                                   1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                                               /
    /                      NAME                     /
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      TYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     CLASS                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      TTL                      |
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                   RDLENGTH                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
    /                     RDATA                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
RFC 1035

之所以 static_zone 不支援新的 ZONEMD 記錄,是因為到目前為止,我們選擇以其呈現格式(而非二進位格式)在內部分散根區域。查看一些資源記錄的文字呈現時,我們可以看到不同記錄的呈現方式有很多變化版本。

.			86400	IN	SOA	a.root-servers.net. nstld.verisign-grs.com. 2023100400 1800 900 604800 86400
.			86400	IN	RRSIG	SOA 8 0 86400 20231017050000 20231004040000 46780 . J5lVTygIkJHDBt6HHm1QLx7S0EItynbBijgNlcKs/W8FIkPBfCQmw5BsUTZAPVxKj7r2iNLRddwRcM/1sL49jV9Jtctn8OLLc9wtouBmg3LH94M0utW86dKSGEKtzGzWbi5hjVBlkroB8XVQxBphAUqGxNDxdE6AIAvh/eSSb3uSQrarxLnKWvHIHm5PORIOftkIRZ2kcA7Qtou9NqPCSE8fOM5EdXxussKChGthmN5AR5S2EruXIGGRd1vvEYBrRPv55BAWKKRERkaXhgAp7VikYzXesiRLdqVlTQd+fwy2tm/MTw+v3Un48wXPg1lRPlQXmQsuBwqg74Ts5r8w8w==
.			518400	IN	NS	a.root-servers.net.
.			86400	IN	ZONEMD	2023100400 1 241 E375B158DAEE6141E1F784FDB66620CC4412EDE47C8892B975C90C6A102E97443678CCA4115E27195B468E33ABD9F78C
範例記錄源自 https://www.internic.net/domain/root.zone

當我們遇到未知的資源記錄時,並不總是能輕鬆知道如何處理。因此,我們用於剖析邊緣根區域的程式庫不會嘗試這樣做,而是傳回剖析器錯誤。

為什麼使用過時版根區域

static_zone 應用程式的任務是載入和剖析根區域,以便在本機提供根區域 (RFC 7706),並將最新版本儲存在記憶體中。當新版本發布時,它會對其進行剖析,並在成功完成後捨棄舊版本。然而,由於剖析失敗,static_zone 應用程式絕不會轉換至更新版本,而是無限期地繼續使用舊版本。當 1.1.1.1 服務首次啟動時,static_zone 應用程式的記憶體中沒有現有版本。當它嘗試剖析根區域時,剖析會失敗,但由於沒有可回復的舊版根區域,它會直接回復到查詢根伺服器的傳入請求。

為什麼停用 static_zone 的初始嘗試不起作用

最初,我們嘗試透過覆寫規則來停用 static_zone 應用程式,這是一種允許我們以程式設計方式變更 1.1.1.1 某些行為的機制。我們的部署規則是:

phase = pre-cache set-tag rec_disable_static

針對任何傳入的請求,此規則都會將標籤 rec_disable_static 新增至請求。在 static_zone 應用程式內,我們會檢查此標籤,如果已設定,我們不會透過快取的靜態根區域傳回回應。但是,為了改善快取效能,如果目前節點在自己的快取中找不到回應,有時會將查詢轉傳至另一個節點。遺憾的是,轉傳至其他節點的查詢中不包括 rec_disable_static 標籤,這會導致 static_zone 應用程式繼續使用過時的資訊進行回覆,直至我們最終完全停用該應用程式。

為什麼影響是局部的

Cloudflare 會定期對託管我們服務的伺服器執行滾動重啟,以執行核心更新等任務,這些任務只能在系統完全重新啟動後才會生效。在此服務中斷期間,在發生 ZONEMD 變更與 DNSSEC 無效期間重新啟動的解析程式伺服器執行個體不會造成影響。如果它們在這兩週期間重新啟動,則無法在啟動時載入根區域,而是藉由向根伺服器傳送 DNS 查詢來回復到解析。此外,解析程式使用一種稱為「提供過時」(RFC 8767) 的技術,目的是能夠繼續從可能過時的快取中提供常用的記錄,以限制影響。自透過上游擷取記錄以來,一旦達到 TTL 秒數,該記錄就會被視為過時。這避免了完全的服務中斷;影響主要發生在我們最大型的資料中心,其中有許多伺服器在該時間範圍內沒有重新啟動 1.1.1.1 服務。

補救措施和後續步驟

該事件產生了廣泛的影響,我們極其重視服務的可用性。我們已經確定了幾個需要改進的領域,並將繼續努力發現可能導致事件再次發生的任何其他漏洞。

下面是我們立即採取的措施:

可見度:我們新增了警示,以便在 static_zone 提供過時的根區域檔案時發出通知。只要提供過時的根區域檔案,都不應被忽視。如果我們利用現有的快取更好地監控此問題,就不會有影響。我們的目標是保護我們的客戶及其使用者免受上游變更的影響。

復原能力:我們將重新評估在內部擷取和分散根區域的方式。我們的擷取和分散管道應無縫處理新的 RRTYPE,並且管道的任何短暫服務中斷都應不為最終使用者所見。

測試:儘管圍繞此問題設置了測試,包括與剖析新 ZONEMD 記錄時未發布變更相關的測試,但我們沒有對根區域剖析失敗時發生的情況進行充分測試。我們將改善測試覆蓋範圍和相關程序。

架構:我們不應使用超過某種程度的根區域過時副本。雖然在有限的時間內,繼續使用過時的根區域資料當然是可能的,但超過某種程度,就會出現不可接受的操作風險。我們將採取各種措施,確保快取的根區域資料生命週期得到更好的管理,如 RFC 8806:對解析程式本機執行根伺服器中所述。

結論

對於這次發生的事件,我們深感抱歉。這一事件傳遞了一個明確的訊息:永遠不要認為事情不會改變!許多現代系統都是使用一長串程式庫構建,這些程式庫被提取至最終的可執行檔中,其中的每個程式庫都可能存在錯誤,或者可能沒有足夠早地更新,使得程式在輸入發生變更時無法正確運作。我們瞭解,設置良好的測試非常重要,以便在輸入變更時進行迴歸偵測,以及偵測正常失敗的系統和元件。我們還瞭解,我們需要始終認為網際網路中最關鍵的系統(DNS 和 BGP)發生「格式」變更將會產生影響。

在內部,我們還有很多事情要跟進,我們正在夜以繼日的處理,以確保此類事件不會再次發生。

Via Cloudflare.com