RFC-0145:Eager 套件更新

RFC-0145:Eager 套件更新
狀態已接受
區域
  • 軟體推送
說明

及時更新系統單體式以外的套件。

問題
變更
  • 583842
作者
審查人員
提交日期 (年/月)2021-10-15
審查日期 (年/月)2021-12-13

摘要

用於更新完整系統更新以外的套件,並在重新啟動後保留套件的機制,須考慮元件架構互動和更新版套件的驗證程序。

提振精神

我們需要提供一種機制,讓套件擁有者不必使用單一全域整合程序,就能發布裝置上軟體的更新。這樣一來,Fuchsia 平台和基礎系統就能與個別套件體驗 (例如網路瀏覽器或支援資料) 分開發布。

我們的目標是支援第一方 (1P) 產品和套件,包括在 Fuchsia 樹狀圖外建構的產品。我們目前沒有支援第三方 (3P) 產品或套件的要求,但對於這項設計,我們之後應該不會再為其增加支援。

相關人員

講師: hjfreyer@google.com

審查者:

  • ampearce@google.com - 安全性
  • computerdruid@google.com - 軟體推送
  • geb@google.com - 元件架構
  • hjfreyer@google.com - FEC、元件平台
  • jsankey@google.com - 結構化設定
  • marvinpaul@google.com - 伺服器基礎架構
  • camrdale@google.com - 鈷藍色

顧問:

列出應審查 RFC,但不需要經過核准的人員。

  • aaronwood@google.com
  • abarth@google.com
  • bryanhenry@google.com
  • ddorwin@google.com
  • gstai@google.com
  • 軟體推送團隊

社群媒體化:

此 RFC 多次以文件形式進行內部社交化,並與審查人員、軟體推送團隊和潛在客戶合作。

RFC 格式定義

本文件中的重要字詞「必須」、「不得」、「必要」、「應」、「不應」、「應該」、「不應該」、「建議」、「可能」和「選用」均以 IETF RFC 2119 說明描述。

相關規定

Fuchsia 平台的規定

套件更新

套件可從 Fuchsia 系統的其餘部分獨立更新。套件可在不重新啟動系統的情況下更新,這代表套件更新「無法」做為系統更新的一部分。

元件關係

套件擁有者必須能夠獨立於基礎系統之外,讓多個套件群組的發布作業符合條件,並只針對該群組中的套件和元件推送更新。

套件關係應可修改 (或空值),以僅支援單一套件更新。

注意:我們不會在 RFC 中完全滿足這項規定。對應於下一階段的工作,我們會在後續設計中解決這個問題。這項設計並無任何影響,我們日後應該都無法解決此規定。

A/B 更新:只有在套件中的所有 blob 已更新時,才使用套件的更新版本

提交更新前,套件應要能完整下載並通過驗證。

系統版本依附元件

頻繁更新的套件可能會宣告 Fuchsia 平台特定 ABI 修訂版本的依附元件 (由 RFC-0002 定義,這是本工作的依附元件),且只能透過支援其所需 ABI 修訂版本的 Fuchsia 平台版本下載。

指標

套件更新系統必須支援與系統更新流程類似的指標,並為套件擁有者提供指標,例如成功更新、下載時間、更新大小 (以位元組為單位) (系統更新目前不存在) 等。

我們必須允許套件擁有者依據執行中的版本版本,取得相關元件的指標,但這並不是最近承諾的磁碟版本。

不要在下載更新時禁止元件啟動

另外可解釋為「不要讓工作階段重新啟動等事件速度過慢」。假如我們在工作階段重新啟動或重新啟動時檢查套件的更新,但沒有即時更新檢查工具,那麼系統就很容易讓工作階段重新啟動變慢。這是因為裝置可能需要下載整個套件或一組套件才能完成工作階段重新啟動作業。

重新啟動系統不應還原套用更新的效果

如果裝置在重新啟動時沒有網路存取權,我們不得在重新啟動時改回使用舊版套件,且除非應用程式太過過時,否則我們不得在網路不穩定時禁止啟動應用程式啟動程序。

我們「可以」定義政策,說明套件狀態「夠快過時」,並拒絕推出,直到更新為止

如果應用程式已大幅過時,且已知有可用的更新,我們日後可能會進入「強制更新模式」,以防止啟動已知有安全漏洞或已知不佳的軟體版本。

在用戶端程式碼中只有單一網址的套件,必須能根據裝置內容包含不同的程式碼或資料

許多開發人員並不想在每次依附的套件有可用的更新時,也不想變更程式碼或資訊清單。舉例來說,模組設定會針對模組元件的特定元件網址編碼,因此您可以在該設定中找到相關範例。對依附元件中的每個變更而言,變更這些設定的開發人員體驗並不理想,且可能會遇到更多情況。

這表示單一套件網址必須根據使用的位置和時機,代表可能的不同套件版本 (不過特定套件的更新規則也可嵌入系統 ABI 等要求)。

由於單一元件網址不足以判斷執行的確切軟體版本,因此我們必須新增指標和意見回饋整合功能,才能取得用於偵錯流程的資訊。

無法長期接受需要在全球相容版本清單的解決方案

多位客戶可能不想與 Fuchsia 或產品擁有者進行協調或整合,就可能會想要發布套件。如果解決方案需要伺服器在預先定義元組中包含所有套件版本的單一資訊清單,就不符合這類要求。這也違反「軟體推送」目標,平台應能生產可安裝任意軟體 (而非在產品建構時間得知) 的產品。

分散一天的套件下載速度

限制下載套件在特定時間 (例如工作階段重新啟動) 後,代表裝置在嘗試更新期間可能會短暫離線。更新應在當天任何可用時段下載。

套件託管和發布程序需求

其中有些要求也會導致平台功能支援其伺服器端實作。

套件存放區

頻繁更新的套件必須單獨託管於發布基礎架構,且與 Fuchsia 系統更新套件和 blob 無關。這可保證託管 Fuchsia 平台套件的存放區不會受到應用程式任何套件變更的影響。這表示目前平台套件不會立即更新,但日後可能會放寬這項限制。

如果產品想要提供自己的發布基礎架構,只要符合此 RFC 中指定的需求和設計點即可。

更新頻道

積極更新的套件必須支援以管道為基準的推出作業,管道名稱可能與用來發布產品其餘的管道組合不同。例如,Chrome 使用的版本可能會比用於執行的產品多或更少。

這需要用戶端支援來協商正確的管道分配。

階段推出

階段推出是管道管理的延伸,支援功能可確保使用者能夠為應用程式觸發百分比式推出作業 (例如 1%、10% 等),並且遵循最佳做法。

此外,您也應提供緊急的更新機制,以便迅速向 100% 的人口推出重要推送。請注意,平台提供緊急更新機制,允許在五小時內 100% 推出。注意:這不在 RFC 的適用範圍內,但這是長期要求。

這會需要用戶端支援,才能協商正確的階段推出群組。

堆疊的石頭

「步數」是指套件的版本,任何裝置都必須下載並執行,才能升級至最新的可用版本。

產品目前並未明確要求支援短期內準備包裹,但本設計考量到長期要求,這些要求都有可能會提出申請。我們不會在實作這個 RFC 時明確為其建構支援,但我們之後不得提供這類支援。

屆時,用戶端必須獲得支援,才能協商要下載的版本。

安全性要求

  • 確定基礎集中的套件無法用單獨可更新的存放區覆寫,請參閱可更新套件群組的必備屬性
  • 為符合 Fuchsia Verified Execution(FVX) 的規定,軟體推送堆疊從磁碟載入記憶體的任何項目都必須在載入時重新驗證,而不是在下載時重新驗證一次。請參閱跨重新啟動驗證的相關章節
  • 輕鬆瞭解及控管哪些套件可以包含執行檔
    • 請參閱執行能力控制項。不含可執行程式碼的套件仍涉及安全性,但不在 FVX 實作範圍內。
  • 可讓您輕鬆稽核套件版本的內容。針對技術控管機制 (而不只是程序控制項) 制定明確的核准程序,這不在這個 RFC 的適用範圍內,但會涵蓋在其他文件中。
  • 套件必須適用已驗證的啟動鏈結下的反復原機制,請參閱跨重新啟動驗證一節

設計

本節將嘗試針對含有機器程式碼的套件擷取套件更新的整體流程。我們會說明執行性檢查、程式碼驗證的檢查位置,以及開發人員體驗。我們會醒目顯示所需狀態和目前功能之間的落差,並依此決定實作策略。

我們將使用 Chrome 做為代表的用途,對這個工作的第一階段來說:

  • 這是要更新的單一套件 (例如 chrome)
    • 沒有任何必須在 Lockstep 中更新的依附元件
  • 套件包含可執行的機器程式碼
  • 需要管道和階段推出支援

可更新套件的必要條件

本節詳細說明提議的設計在短期內可支援的套件類型。我們預期長期下來會放寬這些限制,

  • 在程式碼和設定檔中透過非 fuchsia.com 網址參照,以免與基本套件發生衝突
  • 必須指定特定的系統 ABI (仰賴 RFC-0002 實作)
  • 無法依賴 config-data 套件 (也就是不得預期 config-data 隨結構定義或內容中的套件一起變更)
  • 可能會仰賴結構化設定
  • 必須在 Fuchsia Global 整合和 Fuchsia 版本評估以外,具備能執行 SDK 內容的資格評估
  • 必須保留剩下 2 組基本映像檔副本的一半空間,無須假設基本映像檔檔案重複 (為套件的 A/B 更新作業)
  • 無法依附非基礎套件 (例如其他可更新套件)
  • 完整系統 OTA 外部套件的更新不得以回溯不相容的方式變更套件中元件公開的 ABI,且在不具備這些互動的版本管理策略的情況下,不得變更從其他非系統套件使用的服務預期 ABI。
    • 可能是單一架構套件或多架構套件
  • 套件的相依關係必須處理該套件的解決錯誤 (例如,某些有備用版本的極端案例會嘗試降級套件,而 Fuchsia 會拒絕執行)。

總覽

建議使用 Omaha 用戶端來檢查正式版系統更新檢查套件更新。

我們將新增對 omaha-client 的支援,以檢查多個套件的更新以及系統映像檔。

我們會將 omaha-clientpkg-resolver 基礎架構整合,藉此省去開發人員在本機電腦上使用真正的 Omaha 伺服器或開發伺服器。

我們會支援針對可更新套件的垃圾收集和安全措施,確保套件更新不會封鎖系統 OTA。

我們會整合 Omaha 伺服器基礎架構,協商特定套件網址的適當套件版本。

我們將應用實例分為兩個主要事件:「更新檢查和套件下載」和「套件解析度」。前者會在計時器或開發人員手動要求時執行,並觸發套件下載及快取作業。後者會在元件架構「解析」時執行。

新版套件解析架構的完整總覽。我們會在下方各節中詳細說明。

整體架構

更新檢查和套件下載

檢查更新的時機

omaha-client 會執行每個「應用程式」可設定的狀態機器 (如果是 Omaha,意即用戶端會檢查更新的一組軟體)。

實際工作環境中系統更新目前的 omaha-client 檢查間隔為五小時,且會自動以加或減速為一小時。建議您針對系統映像檔與套件更新使用相同的更新檢查間隔,藉此降低 Omaha 伺服器的負載,並簡化有系統更新和套件更新項目時的實作。(請注意,我們預計會在一段時間後,以間隔和代管方式將系統更新檢查與套件更新的檢查分開。這個簡單來說,這是為了簡化初始實作而簡化的短期工作。)

檢查更新的位置 (存放區設定)

我們會為每個容易更新的套件新增 Omaha 用戶端設定,並依據個別產品編譯至 SWD 堆疊的設定。這組網址應包含系統應參照該套件的設定。該設定會包含套件的名稱和網址,以及其 Omaha 應用程式 ID 和管道等其他預設設定選項。

這有良好的安全性副作用:立即更新的套件將無法覆寫 user\* 版本中其他存放區的套件,因為其主機名稱以硬式編碼的方式寫入。頻繁更新的套件一律無法覆寫基本套件 (n.b. 備用版本不位於基礎版本),因為在 /system/static_packages 清單中有名稱的套件,絕對不會傳送至網路進行解析。

花瓣和 fuchsia.git 的程式碼必須更新,使其不參照套件的 fuchsia.com 網址,而是與新存放區對應的主機名稱,例如 chrome-fuchsia-updates.googleusercontent.com

我們需要為套件更新設定新增其他中繼資料,以便由套件解析器剖析。我們尤其要新增一個布林值,指定套件是否預期包含可執行程式碼。這個布林值將用於執行階段執行限制,類似於 pkgfs 中已包含的限制。詳情請參閱「執行能力控制項」一節。

啟動時,系統會為源自 vbmeta 的可更新套件設定 omaha-client。目前沒有任何執行階段設定;也就是說,為了執行 omaha-client 的自動化測試,我們必須像 Omaha E2E 測試一樣,繼續建構 vbmeta 值。

套裝方案協商

我們將套件協商定義為將套件網址 (例如 fuchsia-pkg://chromium.org/chrome) 轉換為 (主機名稱、墨爾克) 元組的過程。這項轉換的精確輸出內容會受到多個變數的影響,包括:

  • 將套件設為
  • 裝置上執行的系統版本
  • 是否有階段推出作業,以及更新檢查工具所屬的階段推出群組。

我們建議大部分的用戶端在伺服器端進行套件協商,盡可能簡化用戶端程式碼。

omaha-client 會將要求傳送至套件更新設定中列出的 Omaha 伺服器,至少包含以下資訊:

  • 應用程式 ID (這是 Omaha 服務套件的 ID)
  • 套件更新管道
  • 套件更新變種版本 (通常在應用程式 ID 中編碼)
    • 「Flavor」用於要求套件的不同變化版本,例如含有偵錯符號或工具的套件。由於系統會使用應用程式 ID 來編碼,因此正式版裝置無法在沒有系統 OTA 的情況下變更其在欄位中執行的變種版本
  • 支援的系統 ABI (通常會在平台版本欄位中編碼)
  • 階段推出會員
  • 目前的套件版本 (採用 A.B.C.D 格式,其中 A-D 是 32 位元整數的字串,按照版本編號的規範),因此 Omaha 可執行降級防範措施
    • OMCL 會從套件最新修訂版本的 CUP 中繼資料或備用版本的 vbmeta 版本中繼資料中擷取這項資訊;如果磁碟上目前沒有套件的版本,則會將這個欄位設為 0.0.0.0
  • 支援的系統架構 (例如 x64、Arm64 等,會比對 Fuchsia 支援架構清單中的名稱)

Omaha 伺服器會計算裝置的正確套件版本,然後傳回要下載的 blob 固定網址,其中含有要下載的 blob 套件 (套件主機、Package name、Mrkle root) 三重組。我們在使用資料之前,必須先對照 Omaha 提供的 Merkle 檢查回應內容。回應的格式可能會與系統 OTA 使用目前的 update 套件格式相符。

回應將包括該應用程式的裝置同類群組,其中包含管道資訊。由於系統會簽署完整回應,因此可以利用這在啟動時有效判斷套件的管道。回應同類群組也可用來變更後續要求的管道 (請參閱下文有關管道支援的說明)。

Omaha 回應必須實作用戶端更新通訊協定 (CUP),該通訊協定可透過 Omaha 回應提供簽名。並於 Omaha 用戶端新增這項通訊協定的支援。系統必須使用 CUP,才能保留磁碟回應,之後還須根據儲存在基本套件中且取得 vbmeta 權限的公開金鑰重新驗證。

管道支援

Omaha 通訊協定會透過 cohort 概念支援管道。

由於可更新的套件可能會使用與基礎系統不同的管道組合,因此我們需要與 fuchsia.update.channelcontrol 類似的開發人員適用的新 API,才能管理開發中套件的管道設定。我們會將管道資訊儲存在 CUP 回應中,類似將管道資訊儲存在 vbmeta 中,以便更新系統。我們打算透過擴充功能公開這個 API,pkgctl 作為 CLI 工具,而且可能想元件的開發人員能在執行階段變更元件的管道。

如果裝置同時含有修訂版更新和套件的備用版本,即使備用版本較新 (即使認可版本比回溯版本更舊),我們也應使用已承諾更新中的版本資訊,以免透過更新回溯版本版本的方式覆寫管道指派。我們仍需驗證已承諾的回應的簽名,才能使用其管道資訊。

如果 Omaha 要求包含不存在的管道,Omaha 伺服器應重新導向至存在的通道,或者應使用備用版本。

當在 Fuchsia 裝置上使用管道控制 API 要求變更頻道時,除非使用包含管道的永久已簽署 CUP 回應形式,否則該設定不會在重新啟動後保留。我們不希望將未簽署的資料視為政策的考量。下一次更新檢查將使用記憶體內管道,一旦在磁碟提交正確的套件版本,就會儲存該更新檢查的回應。

系統映像檔中的 Omaha 設定應包含新套件的預設管道。如果特定套件沒有保留的 CUP 回應,Omaha 應使用預設管道。

如果管道變更導致傳回的套件版本相同 (因為該套件位於兩個發布管道),則下載流程應會照常執行。pkg-resolver 不會下載任何新的 blob,而是傳回磁碟上已有的 blob。

支援階段推出

伺服器根據伺服器上的隨機擲骰子 (這是現有的 Omaha 功能),將裝置指派至同類群組,並用於追蹤裝置是否應收到階段推出。而擲骰子結果則與伺服器端的每個應用程式 ID 無關。

保留所需的大小

Omaha 要求和 CUP 回應相當小,以數百位元組的順序表示。每個套件可能需要小於 1 KB 才能保留中繼資料。

套件下載

omaha-client 擁有由 Omaha 伺服器計算的最終 Merkle 固定套件網址後,就會使用新的通訊協定 (也稱為 fuchsia.pkg.cup) 將套件下載至 pkg-resolver 元件。如果裝置上已有要求的套件版本,pkg-resolver 不會重新下載。

如果裝置上沒有新版套件,pkg-resolver 會下載新版本並按住其傳回的目錄,藉此防止垃圾收集。將控制代碼傳回套件目錄之前,pkg-resolver 必須先將 CUP 要求/回應組合提交至儲存空間,以便日後重新驗證。

pkg-resolver 必須保持這個目錄控點開啟,以免套件收集垃圾 (此設計假設我們根據採用開放式套件追蹤進行垃圾收集,而這項設計仍在開發階段)。必須捨棄舊版套件的控制代碼。

我們也會在 pkg-resolver 的設定中,為由 pkg-resolver 下載的每個可更新套件新增大小限制。在下載之後,如果套件大於所設的限制,pkg-resolver 將刪除與套件相關聯的所有 blob,並以適當的錯誤條件讓更新失敗。

此流程的示意圖如下:

套件下載流程

如果下載失敗,會發生什麼情況?

下載失敗必須防止系統提交更新檢查的 CUP 結果。新的解析項目將繼續使用先前修訂的版本。

空間管理和垃圾收集

我們下載套件的更新時,需要維持一些與空間相關的不變性:

  • 始終有足夠的空間來快取執行核心產品體驗所需的套件 (例如 Chrome)。
  • 必須有足夠的空間安裝系統更新,或者也可以建立足夠的空間來安裝系統更新。

如果快速套件更新能夠使用足夠的無法回收空間來防止系統 OTA,則需要 FDR 修正問題。若要避免發生這個錯誤,您需要變更目前的垃圾收集程序。

我們的垃圾收集機制目前使用以下條件保留套件:

  • 如果套件位於目前執行的基本套件組合,請保留該套件
  • 如果套件是最近解析的非基本套件版本,且在目前開機期間已解析,請保留套件
  • 否則請允許刪除套件。

這項策略目前已生效,但有一些需要明顯的缺點:

  • 如果執行系統日後需要的套件尚未在目前啟動期間解決,這個方法可能會刪除該套件
  • 可能不會刪除在目前啟動期間已解決的套件,但不再需要必要的測試套件 (例如隨時間累積的測試套件)

在託管套件快取之前,我們依然可以不斷變更目前的垃圾收集配置。現在就差不多完成這項工作了,我們可以對垃圾收集配置進行實作變更,以保持這些不變性,也就是防止目前使用中套件進行垃圾收集的配置。

針對電子器更新的短期實作 (在根據包裹追蹤進行垃圾收集之前),我們會進行嚴格的大小檢查,取得系統更新和即時套件更新。這表示可更新套件必須遵循產品擁有者所分配到的空間,而我們會在上傳套件時 (目前集中管理) 檢查其空間需求。長期而言,更理想的垃圾收集實作方式將能夠享有更多彈性。

如果儲存空間用盡,會有什麼影響?

儘管我們已盡力在發布時檢查套件大小,並確保所有項目符合預算條件,裝置仍可能無法下載更新,

為解決這個問題,我們將新增空間管理的「核子選項」,當 OTA 執行期間空間用盡時,請根據新的重新啟動原因 (OUT_OF_SPACE_PANIC) 重新啟動裝置,然後立即在網路上執行 OTA,不必等待檢查間隔。這會觸發垃圾收集作業。系統更新檢查工具應封鎖 Eager 套件更新,直到 OTA 完成為止。

我們會在 system-updater 中新增設定選項,決定是否應在 NO_SPACE 錯誤時重新啟動系統。我們將允許產品擁有者在 user* 版本時將這個選項設為 true,但它在 _eng 版本中應預設為 false,以協助偵錯。如果選項為 True,且 system-updater 在嘗試安裝更新的空間不足時,system-updater 會觸發系統重新啟動,原因為 OUT_OF_SPACE_PANIC

重新啟動時,系統更新檢查工具元件 (omaha 用戶端或 system-update-checker) 會在開機時啟動。如果更新檢查工具元件發現重新啟動原因為 OUT_OF_SPACE_PANIC,就會立即嘗試執行 OTA,同時禁止套件更新。

我們也會在 OUT_OF_SPACE_PANIC 重新啟動時新增輪詢,確保當裝置以 OUT_OF_SPACE_PANIC 在同一列中多次重新啟動,做為我們無法持續以迴圈重新啟動的原因。

從可更新的套件啟動元件

下次透過元件架構解析該元件時,系統會使用可更新套件中的元件。在某些產品中,夜間工作階段重新啟動會自動處理元件重新啟動作業,因此可能不需要更新通知。然而,其他產品元件可能會想接收更新通知,以便向使用者顯示「請重新啟動」通知。詳情請參閱未來工作一節。

新元件的解析度流程

所有套件網址仍會採用 fuchsia-pkg://{hostname}/{package_name} 格式。然而,由於部分套件網址的套件協商會由 Omaha Client 管理,且部分套件會由其他方法 (例如 cache_packagesbase_packages) 管理,因此我們需要套件解決流程,以便區分應透過 Omaha 管理的套件以及應透過其他中繼資料管理的套件。

因此,我們建議採取下列解決方案流程。如要呼叫使用 fuchsia.pkg.PackageResolver 通訊協定解析指定套件網址,pkg-resolver

  1. 套用基本固定及重寫規則
  2. 判斷要解析的重寫網址是否由 Omaha + CUP 管理,如果是...
  3. 要求一個內部程式庫,由管理該網址的相關目錄控制代碼處理 CUP 套件。
  4. CUP 程式庫會從該網址最近一次修訂的 CUP 回應中,擷取 Merkle 快取指定的套件,如果發生錯誤且符合備用版本的使用需求,則會改回使用備用版本。針對所選的套件版本,系統會直接前往 pkg-cache 並傳回目錄控制代碼 (避免在寫入新套件時,因為各種競爭狀況而讀取其中)。

此流程的示意圖如下:

可更新的套件解析流程

執行能力控管

pkg-resolver 是正式版裝置上有效解析路徑的一部分,代表必須參與 FVX 強制執行。需要信任的金鑰清單也包含 CUP 金鑰。

我們會在 pkg-resolver 中實作執行控制項,確保使用者* 會建構唯一可包含可執行程式碼的套件:

  • 基數
  • 由 Omaha 管理,並在 pkg 解析器設定中設為可執行檔
  • 已透過現有的 pkgfs_static_packages 許可清單機制加入許可清單,以便進行執行控制

這些控制項的具體實作方式將透過後續文件的主旨。

在重新啟動後驗證已下載的中繼資料

簽章驗證和可稽核性

為了在重新啟動後保留可更新套件,我們需要記住其 Merkle 根層級。我們會保留 Omaha 要求和 CUP 回應。使用保留的資料尋找套件時,我們會驗證:

  • 要求/回應組合的簽章與信任的金鑰相符
  • 要求等同裝置有網際網路連線 (在 Omaha 應用程式 ID、管道和變種版本上比對) 時,我們「要求」的情況
  • 回應與簽名參數相符,且為有效的回應,
  • 回應的預期版本大於或等於系統映像檔中的 backstop 版本
  • 回應中包含的套件 可支援執行中的系統 ABI 版本

如果符合上述所有條件,pkg 解析器中的 CUP 模組就會從 CUP 回應傳回殘存的陰道。

這個方法的缺點:我們需要剖析保留的要求和回應,才能取得保留的 Merkle 根值。不過,我們認為在實作過程中,可以隔離實際執行剖析的元件,藉此降低這項風險。

復原預防

這項解決方案需要使用 vbmeta 提供的復原保護機制,並使用以版本為基礎的反向停止功能。如果 CUP 回應中的預期版本號碼比系統映像檔設定中的最低需求版本,我們會改回使用 OTA 映像檔中包含的套件版本 (如有),且不會提交回應中的版本。

例如,我們以這個配置實作復原預防措施的範例,我們假設可更新套件的 1.0 版有在 1.1 中修補完成的安全漏洞。我們必須指示 Omaha 停止提供 1.0 服務並開始提供 1.1 版,然後再使用新的返回停止版本 1.1 版本向機群發出系統 OTA。這可提供保證,在沒有重播保護記憶體區塊或同等功能的情況下,裝置上目前的單體式行為也同樣重要。

如果套件未設定回溯版本,我們會新增以時間戳記為基礎的 Backstop,以確認時間比該套件的每個 CUP 回應更早。必須使用這個模式才能支援系統事先不知道的更新套件。回溯時間會設為系統回溯時間,也就是目前產生系統映像檔的 Fuchsia 版本時間戳記。對於更新先前無系統映像檔的套件,我們目前沒有要求,但應該在設計時考量這些需求。

早期啟動所需的套件備用版本

某些套件可能會在啟動程序期間或裝置連上網路之前就可能需要用到 (例如,它們可能會為設定網路的立即可用體驗 (OOBE) 提供使用者介面。這表示如果特定套件版本過舊,我們無法仰賴網路下載這些套件,且我們必須在系統更新映像檔中包含這些套件的版本。我們將這些封裝套件稱為「備用版本」,因為通常只有在套件的其他更新版本無法使用時,才會使用這些套件。

使用備用版本時,設計會承受數個設計壓力:

  • 必須在沒有網路存取權的情況下執行
  • 這些備份必須在恢復原廠設定 (FDR) 後持續保留,因為 OOBE 可能需要這些資料。
  • 發布時必須一併更新系統, 並通過系統更新的資格
  • 我們必須盡量減少備用版本使用的空間,特別是與套件的其他版本交錯時
    • 當我們必須儲存備用版本、目前執行中的版本,以及尚未執行的新套件版本,表示我們必須一次儲存特定套件的三個複本 (假設我們同時禁止進行系統更新和套件更新)
  • 我們必須設法避免發生系統執行套件 N 版本,且系統更新強制降級至套件 N-1 版本的情況,因為避免降級是提供給開發人員的有用屬性
  • 同時避免套件降級,同時也必須避免因降級而拒絕系統更新

指定的套件擁有者應決定是否需要與產品擁有者合作時,其套件的備用版本。所有可更新套件都不需要備用版本。

為了在 OTA 映像檔中加入備用版本,我們會將備用版本套件放入系統更新套件,並在系統的 system_image 套件中納入該套件的版本管理中繼資料。版本管理中繼資料 (主要為 Merkle 根) 將允許 pkg-resolver 回應套件的要求,即使沒有網路連線也不成問題。事實上,版本管理中繼資料包含在 vbmeta 中,因此不會遭到 FDR 刪除。

在系統更新期間,我們會垃圾收集及刪除更新套件的非承諾版本,但仍可能需要儲存三個包含備用版本的套件:目前系統映像檔中的備用版本、已下載且可能正在執行的修訂版本,以及新的系統映像檔中的新的備用版本。

使用備用版本的時機

CUP 模組只有在依據 CUP 中繼資料而定,備用版本為「較新」 (版本號碼較大) 時,才會傳回備用版本的目錄。

如果套件的最近更新版本毀損,手動恢復原廠設定將清除儲存的 CUP 中繼資料,並滿足條件 1 讓我們重設為備用版本。我們日後也可以使用 blob 損毀處理常式判斷更新後的套件損毀,並使用備用版本 (如 Future Work 所述)。

技術上來說,新系統映像檔中的備用版本可能是從最近修訂的套件降級。然而,新的系統映像檔必須不支援最近修訂套件的必要 ABI,才能使用該降級回退。我們可以發現,系統是從「較新的」系統 ABI 和「舊版」備用版本發布系統,而該版本支援較新的系統 ABI,但我們會調查新增伺服器端檢查,避免發生這類情況。

如果系統更新升級的備用版本高於最新修訂版本,建議您根據 CUP 中繼資料中的版本字串,使用最新更新的套件版本。

指標

可更新套件中的指標

為了享有第一方套件開發人員的好處,我們將新增 Cobalt 指標,該指標是由產生指標的元件套件版本自動匯總。Cobalt 在記錄任意字串方面有一些限制 (例如,可更新套件的雜湊值)。我們不打算直接在套件格式中嵌入版本號碼或其他任意版本字串,而是將這些版本號碼嵌入用戶端的 CUP 中繼資料,然後再透過該中繼資料將版本回報給工具。由於 pkg-resolver 可以存取套件的 CUP 中繼資料,因此能夠針對已修訂的套件,回報使用者可理解的版本字串。

有數種方法可前往 Cobalt,而且全都必須支援版本資訊:

  1. 直接從元件記錄
  2. 代表另一個元件進行記錄,例如可讀取元件資料並傳播為 Cobalt 指標的 Sampler
  3. 代表多個元件中的記錄,例如記錄統計資料。這可能與一般案例 2.,但代表在我們的選擇的任何解決方案中需要擴充性。

Fuchsia 使用 Cobalt 時需要考量以下幾項整合可更新套件的規定:

  1. 元件擁有者不想使用依商品根層級細分的版本,而使用使用者可理解的字串
  2. 為了透過使用者可理解的版本字串準確匯總資料,我們需要在裝置上執行這項操作
  3. 我們無法針對套件的每個新版本更新 Cobalt 註冊資料庫,因為這會打敗獨立套件更新發布流程的時間點
  4. 提供給 Cobalt 的元件版本相關資料可能會參照已停止執行的元件版本,就像記錄統計資料的情況一樣
  5. 短期解決方案無法動態更新,所以我們不需要支援 v1 元件,但我們需要針對每個「可以」更新的元件回報版本字串。Cobalt 想要每個套件的版本字串,但是我們無法要求所有套件都必須使用新格式,才能啟用任何套件的版本回報功能。

為了將套件的版本字串記錄到 Cobalt,我們建議提供一套系統,讓套件的 Merkle 根目錄連同指標一起傳播,以便追蹤發送特定指標的元件版本。

我們將修改 Cobalt 記錄通訊協定,納入選用的套件 ID 欄位。在 Cobalt 元件本身擁有與一組特定指標相關聯的 Merkle 根,就可以從該 Merkle 根層級對應至以字串為基礎的版本字串,然後用於本機匯總指標。還可以將這個 Merkle 根憑證與其他來源的元件名稱結合 (例如記錄統計資料或取樣資料)。

為符合 Cobalt 的隱私權規定,我們必須按裝置上的版本字串匯總,而不是將裝置傳送至裝置。由於我們可能對裝置有人類可讀的版本資訊,因此在裝置 (而非後端系統) 上進行對應是非常謹慎。

客戶也希望使用使用者可理解的任意字串,以便對指標進行分區。目前,個別套件支援這項功能,這些套件會記錄其版本,不過並未針對整個系統或標準套件提供支援。Android 支援此版本名稱

為此,我們建議您為 CUP 中繼資料新增另一個欄位,並以擴充功能的形式 (也稱為 version-name) 做為 Omaha 回應的擴充功能。除了由人類處理版本字串以外,這個欄位不會用於任何用途。SWD 堆疊無法保證其獨特性,也不會將其用於降級防範;套件擁有者需要維護自己的版本字串。

驗證版本字串:新的 SWD API

基於隱私權考量,Cobalt 會限制可記錄的字串類型。特別是,Cobalt 無法信任不包含個人識別資訊 (PII) 的任意字串 (例如版本字串)。為了允許我們記錄元件中的版本字串 (由於元件可能會更新為任意版本,所以 Cobalt 需要透過可信系統元件驗證特定版本字串已知特定版本字串的方法),以便記錄該元件的版本字串。

Software Delivery 會與 Cobalt 團隊合作,驗證套件 ID (Merkle roots 或套件網址),並將這些 ID 與使用者可理解的版本字串建立關聯,以用於遙測用途。當 Cobalt 嘗試根據套件 ID 尋找特定套件的版本時,SWD 堆疊就會傳回版本名稱和版本號碼 (如果有的話)。

我們目前不支援與系統映像檔相關任意元件的版本資訊,但我們稍後可能會針對使用者可理解的套件資訊,將編碼標準化,並擴大這裡的保證範圍。

如果未從記錄元件提供套件 ID (可能是記錄統計資料這類項目的情況),Cobalt 應根據套件 ID 回報目前執行中的元件版本。

Software Delivery 堆疊預期 Cobalt 會快取針對使用者可理解版本字串的要求回應。Cobalt 元件必須避免為每個指標呼叫呼叫版本字串 API,否則會產生不必要的資源費用。我們可能會選擇建立 Cobalt 的大量 API 來定期呼叫,藉此減少版本字串的 FIDL 流量。

我們日後可能會決定將版本字串和版本名稱整合到套件格式,但目前不提議這麼做。

更新系統本身的指標

我們也需要回報套件下載程序本身的指標,例如成功/失敗指標。如前所述,基於隱私考量,Cobalt 對記錄任意字串有一些限制。

然而,由於 Omaha 的後端指標有專屬的隱私保護策略,Omaha 本身可針對搭載特定版本的裝置提供精確的指標。這是此解決方案相較於 TUF 替代方案的優勢。

對於更新程序本身的指標,我們打算利用 Omaha 的指標,並與 Cobalt 團隊合作,在極少數情況下根據任意字串回報維度 (經過可信任的系統元件驗證)。

最後,我們會將更新基礎架構中的可更新套件版本記錄為裝置快照,以協助排解問題。

實作

我們可能會將這項工作分為不同階段,並對應功能增加的情形。

第一階段可能會涵蓋以 Omaha 為基礎的套件擷取通訊協定,需要:

  • SWD
    • omaha-client 的多應用程式支援
    • 實作 CUP,整合至套件解決流程
    • 用於測試的最低開放原始碼 Omaha 伺服器
    • 套件設定,將套件網址對應至 Omaha 應用程式 ID。
  • 伺服器端包裝團隊
    • 伺服器端封裝基礎架構中的版本管理,以及基本套件協商,例如管道和降級防護
    • 適用於套件與 blob 儲存庫整合的儲存空間。
    • 伺服器端套件協商 (管道支援、階段推出等)

第二階段將介紹實際工作環境使用時需要的額外安全機制,以及空間管理所需的安全措施:

  • 瑞士法郎

    • 實作 CUP 反向停止
    • 空間管理:實作開放式套件追蹤及覆寫垃圾收集,以提供更靈活的套件管理。
    • pkg-Cache 以外的可執行限制
    • 應用程式專屬評估程序
    • 套件的備用套件,內建於基本映像檔中
    • 新增 Cobalt API 以驗證版本字串
  • 伺服器端封裝基礎架構

    • 啟用 CUP
    • 簽署前的發布時間來源和程式碼簽署檢查
    • 加強套件協商支援,例如階段推出、ABI 修訂版本協商等功能。
    • 版本時間大小檢查
    • 新增套件上傳介面,以指定版本字串

注意:本節不涵蓋 RFC-0002 實作項目,這是依附元件,但有自己的實作計畫。

效能

這項變更預計會對執行階段效能造成顯著影響。 在我們推出這項變更時,我們必須密切監控 omaha-client 的記憶體和 CPU 用量和 SWD 堆疊的其他部分。我們也會監控迴歸的啟動和重新啟動時間。

這項變更確實可能會增加磁碟用量,也就是儲存套件所需的額外空間,以及儲存套件中繼資料所需的少量額外空間。這些注意事項請參閱「空間管理」一節。

人體工學

工程工作流程

我們會建構新的 _eng 工作流程,以使用 omaha-client 測試及驗證套件更新。我們希望大多數使用套件的開發人員在開發時都會使用以 TUF 為基礎的工作流程,但我們需要先建構開放原始碼 Omaha 伺服器,進行自動化測試,然後才將平台更新發布至實際工作環境。

我們預期採用 SDK 工具上層的產品工具最終會在 eng 建構中支援 Omaha 設定。在他們這麼做之前,產品開發人員可能必須與 Omaha 設定互動 (這是設定套件測試的工作流程的一部分)。

我們也必須擴充指令列工具,以支援在執行階段新增動態設定的可更新套件。

回溯相容性

這項工作應能回溯相容於「軟體推送」堆疊的現有用途,主要原因是我們開發 Omaha 流程期間,不會變更套件網址命名空間結構。

安全性考量

這項解決方案有許多安全性考量。首先,這項設計首次為使用者裝置導入間接驗證功能。這雖然是安全性風險較高的安全防護機制,但我們認為權衡利弊,而且可透過所有現有防禦機制和預定的因應措施來加以管理。

以下是與 TUF 替代值不同的安全性機密工作:

  • 我們需要新增受信任的通訊協定和相關的剖析器:CUP
  • 我們必須將 CUP 公開金鑰新增至 vbmeta。
  • 我們需要將 omaha-client 設為 fuchsia.pkg.PackageResolver 通訊協定的實作者
  • 我們必須在 Omaha Client 中實施一定程度的執行限制,這可能類似於執行功能控管中詳述的個別存放區設定的執行能力位元。

所有的安全性工作都可以解決,但需要與安全性團隊進行詳細的設計及協調。

隱私權注意事項

無法選擇不採用伺服器端上由 Omaha 伺服器收集的指標,用戶端必須將其執行背景資訊 (ABI 修訂版本、應用程式 ID 等) 提供給伺服器,而且伺服器可能會決定保留該次互動的指標。

Google 的 Omaha 伺服器

如要進一步瞭解 Omaha 通訊協定如何保護隱私權,請參閱 Omaha 通訊協定規格

測試

有關套件版本的評估說明請參閱相關章節。

實作部分中詳述的變更需要進行全面測試。具體來說,我們會新增對樹狀結構內 Omaha 伺服器的支援,以便對 Omaha 用戶端執行整合測試。我們也會投入心力進行整合,以測試 Omaha 用戶端和套件解析器之間的新互動。

我們將使用新的 Omaha 伺服器,投入端對端測試,以測試新的更新流程。

此外,我們也必須嚴格測試新的垃圾收集機制,包括填滿磁碟的整合測試,並確保能夠繼續安裝系統更新。

最後,我們會對全新或變更的安全防護機制進行嚴格驗證,也就是 CUP 回應驗證和持續性,以及執行限制。

說明文件

我們在推出這些異動時,需要更新 fuchsia.dev 的軟體推送說明文件,以便至少提供詳細資料:

  • 可更新套件的模型和狀態轉換圖表
  • 如何設定更新
  • Omaha 伺服器實作程序的必要條件

未來的工作

更新通知 API

過去這個初始提案的實作方式,我們也會提供通知 API,方便用戶端訂閱套件的更新項目,以便用戶端在需要時觸發元件重新啟動作業。更新通知的用途如下:

當父項元件重新解析元件時 (例如自行重新啟動或工作階段重新啟動時),套件解析度就會使用新版套件。

建議您採用下列新的元件解析器 API:

  • 解析元件
  • 提供管道,用於通知該元件可用的更新
  • 在新版本下載完成且有新版本可用時,通知管道的另一端持有者,並提供自行重新啟動的機制

我們會將通知和觸發邏輯透過元件解析器提供給套件解析器,藉此提供類似的 API。我們也新增一個向 framework 要求的元件能力,以通知元件有本身有更新,讓元件可以自行重新啟動。

更新套件的 Blob 損毀通知

如果下載套件後發生毀損,我們會嘗試重新下載損毀的 blob,或者在存在時使用該套件的備用版本 (前提是其符合使用備用版本的規定)。

這包括整合現有的 blob 損毀通知 API,並針對毀損的可更新套件,觸發重新下載或抹除快取 CUP 中繼資料。

無法啟動可更新套件的前景更新

如果可更新套件中的元件無法啟動,我們應在套件系統重新下載套件時,讓產品擁有者選擇顯示前景更新畫面。這樣一來,使用者就必須為裝置進行 FDR 才能取得執行套件的備用版本。

針對可更新元件更新自我檢查

部分元件可能會希望能夠自行檢查更新並觸發下載作業。我們應調查適用這項功能的客戶,以及是否應將其整合至元件架構和軟體推送 API。

缺點、替代方案和未知

替代做法:使用 TUF 進行套件協商

除了使用以 Omaha 為基礎的通訊協定,我們也可以使用更新架構來發布套件版本中繼資料,並將特定 fuchsia-pkg:// 網址轉換為要下載的 blob 主機名稱和 Merkle。

這項功能的基本設計如下:

  • 將「eager update」函式新增至 pkg-resolver,以複製目前 omaha-client 狀態機器的部分,並定期檢查套件群組更新。
  • 覆寫我們的 TUF 中繼資料格式,並在 Fuuchsia 更新伺服器上實作該新格式,藉此實作管道、階段推出、逐步推出等功能。也都是相當可觀的成果。
  • 當 pkg-resolver 收到特定套件網址的要求時,請讓它下載該存放區中所有套件的新中繼資料 (如果沒有網路或轉送到更新伺服器,則使用保留的中繼資料),然後根據要求的情境 (包括管道、階段推出會員資格等) 計算要下載的正確的 Merkle 根層級。上述所有項目均必須存放在裝置本機中。這意味著伺服器無法根據情境變更用戶端的回應,所有運算作業都會在本機執行。
  • 為每個快速更新的套件儲存已下載的中繼資料,這可能取決於 TUF 存放區中存在的通道和版本數量 (根據我們的預估值,採用簡易方法的每個套件約 3 MiB;如果我們投入更多工作來啟用 TUF 中繼資料資料分割,則每個套件最多約 82KiB)
  • 找出客戶可接受的 Cobalt 指標方法 (確保搭載特定版本的裝置數量等條件夠精確),且未違反 Cobalt 的設計原則
  • 透過 ffx 新增工程工作流程,以支援即時更新存放區,並更新開發人員工具以支援新的 TUF 中繼資料格式。
  • 實作降級防範機制 (Omaha 在伺服器端提供這項功能)。

這個替代方案有一些缺點:

  • 用戶端可以「檢查工作」,因為 TUF 中繼資料持續性意味著所有必要資料,用來證明套件的正確版本已確實在裝置上。
  • 我們已使用 TUF 進行工程流程,因此有很多工具可用。
  • 與 TUF 存放區和套件名稱相對應的套件網址概念很實用,且不需對應至其他服務專屬 ID (例如 Omaha 應用程式 ID)。

此外,這項替代做法也有缺點:

  • 我們已在實際工作環境中使用 Omaha 進行系統更新,因此這會導入第二個更新伺服器,這是執行 Fuchsia 裝置所需的第二個更新伺服器。
  • 我們必須重新實作 Omaha 提供的許多功能,包括階段推出、管道支援和逐步堆疊。
  • 如果系統映像檔中的套件協商代碼有誤,則必須透過 OTA 裝置修正問題,而不是修正伺服器端的交涉邏輯

我們認為採用此方法的缺點超過缺點,且這兩種方法之間的差異大致上可以去除,因此我們已使用如上文所編寫的 Omaha 方法。

先前的圖片和參考資料

可更新軟體套件有許多實作方式。其中一些重要指標包括:

更新中繼資料通訊協定的參考資料包括:

附錄:發布時的大小計算

對於具有嚴格大小預算的裝置,我們不希望使用過多空間來幹擾 OTA,因此我們需要針對套件發布時間檢查的套件提供選用的尺寸預算。

Fuchsia 目前會在將資產寫入磁碟時進行串流壓縮,而壓縮等級是在系統組合時間決定。如要預先計算特定套件將佔用的空間,我們必須瞭解指定系統版本的壓縮等級,再將 blobfs 壓縮等級和壓縮配置部分設為系統 ABI 的一部分以達到此目的。這可讓我們在伺服器上使用相同的壓縮參數,檢查套件是否能在預算範圍內,並將套件傳遞至套件存放區或無法發布該套件。

在發布程序中,套件伺服器基礎架構會檢查應用程式的壓縮大小 (以目標 ABI 的壓縮參數和 SDK 提供的工具壓縮的 blob 組合大小) 是否超出目標裝置上套件設定的預算。

在發布流程期間,無論尺寸檢查多麼好,我們最終都會破壞尺寸檢查程序,且裝置在嘗試下載更新時空間不足。如要進一步瞭解如何管理這個部分,請參閱「空間管理和垃圾收集」一節。

替代方法是模擬在裝置上完成的壓縮作業,藉此檢查大小限制,因此我們可以實作離線壓縮,以便確實瞭解套件中的特定組檔案會佔用多少空間。不過,這項工作尚未優先執行,且可能需要大幅變更 SWD 和 Storage 程式碼集。