本文整理自 IvorySQL 2025 生態大會暨 PostgreSQL 高峯論壇的演講分享,演講嘉賓:Alvaro Hernandez。
個人簡介:
- OnGres 創始人&CEO
- 20年以上Postgres用户及數據庫管理員經驗
- 主要從事研發工作,基於Postgr0 es創建創新軟件
- 累計開展140餘場技術講座,多數以Postgres為主題
- 非營利組織PostgreSQL基金會創始人及主席
- AWS Data Hero
- 個人網站:aht.es
引言
你真的知道自己正在運行的 PostgreSQL 是什麼版本嗎?即便是官方發佈的安裝包,其編譯者、構建方式和環境都可能影響最終二進制文件的可復現性和安全性。本文將圍繞可復現構建、封閉式構建、自舉構建,以及 SBOM、數字簽名鏡像和自動漏洞掃描等實踐,探討如何打造可追溯、可驗證且安全的 PostgreSQL 發行版。
實驗演示
對不同來源的 PostgreSQL 17.0-1 安裝包提取二進制文件並計算 SHA256 摘要值,觀察二進制文件的一致性差異。
- 首先,從 官方 APT 倉庫的 Debian 安裝包提取二進制文件:
$ wget
https://apt.postgresql.org/pub/repos/apt/pool/main/p/p
ostgresql-17/postgresql-17_17.0-1.pgdg%2B1_amd64.deb
$ ar x postgresql-17_17.0-1.pgdg+1_amd64.deb
$ tar xf data.tar.xz
$ sha256sum usr/lib/postgresql/17/bin/postgres
108a683af350bf496cfc9488f7e9083076004d8f08c48eb07e4085
9bdf75b83c
結果:Debian APT 包生成的二進制文件摘要值以 108 開頭。
- 其次,從 官方 RPM/YUM 倉庫的安裝包提取二進制文件:
$ wget
https://download.postgresql.org/pub/repos/yum/17/redhat/r
hel-9-x86_64/postgresql17-server-17.0-1PGDG.rhel9.x86_64.
rpm
$ rpm2cpio
postgresql17-server-17.0-1PGDG.rhel9.x86_64.rpm | cpio
-idmv
$ sha256sum usr/pgsql-17/bin/postgres
8603bb3d4eb0e0304ddb5693b959fbedba4b6c217031fe116d5bc7385
32a4fe7
結果:RPM/YUM 包生成的二進制文件摘要值以 860 開頭,與 Debian 包不同。
- 最後,從 官方 Docker 鏡像(基於 Debian APT 包構建)提取二進制文件:
$ git clone https://github.com/docker-library/postgres.git
$ cd postgres/17/bookworm; docker build .
$ git log -1 Dockerfile |grep '^ '
Update 17 to 17.0, bookworm 17.0-1.pgdg120+1, bullseye
17.0-1.pgdg110+1
$ docker run --rm --name pg -it -e POSTGRES_PASSWORD=e
f20f9db75912 sha256sum /usr/lib/postgresql/17/bin/postgres
852a27cf85ec935174a0cafd259b3936d7345d780b9991cf45a3be08312b
1652
結果:Docker 鏡像生成的二進制文件摘要值以 852 開頭,再次不同。
實驗結果顯示,即便是同一版本的 PostgreSQL,由於來源和構建方式的差異,最終生成的二進制文件存在顯著差異。這説明構建環境、打包方式和來源對二進制文件的一致性具有直接影響,為可復現構建和安全性驗證提供了技術依據。
源代碼展示
二進制文件源自源代碼,那麼 PostgreSQL 的“真正源代碼”究竟是哪一個?
首先,從 PostgreSQL 官方網站下載源代碼壓縮包,該壓縮包中不包含二進制文件,因此無法直接計算二進制摘要,但可以計算源代碼壓縮包 tar 包的 SHA256 摘要值,計算結果以 bf81 開頭:
$ wget
https://ftp.postgresql.org/pub/source/v17.0/postgresql-17
.0.tar.gz
$ sha256sum postgresql-17.0.tar.gz
bf81c0c5161e456a886ede5f1f4133f43af000637e377156a02e7e835
69081ad
接着,從 PostgreSQL 官方 Git 倉庫克隆源代碼,並針對相同版本生成壓縮包,計算出的 SHA256 摘要值,以 4fdd 開頭,與官方網站下載的 tar 包不同:
$ git clone git://git.postgresql.org/git/postgresql.git
$ cd postgresql
$ git archive --format=tar.gz \
-o ../git-postgresql-17.0.tar.gz REL_17_0
$ sha256sum ../git-postgresql-17.0.tar.gz
4fdd9abf3185ba835e88e71a2dd1f08812567bb1747bf6c9c5b2dfaad
64b7b0b
這種差異是已知問題,主要由不同版本的 tar 命令導致。實際上,源代碼內容是一致的,但壓縮包生成方式的差異會導致哈希值不同。這説明,即便源代碼相同,其壓縮包形式在不同構建環境下也可能產生不同的摘要值,從而增加了驗證和復現的難度。
Postgres 發行版:面臨的挑戰
在分析 PostgreSQL 發行版的構建問題時,首先要面對的,是信任與可驗證性的問題。模擬一段對話:
“你用的是開源軟件吧?”
“對,我用的是開源的。”
“那你為什麼用開源?”
“因為如果我用某個系統,隨時能回到源代碼,知道它在幹什麼,還能檢查、分析。”
理論上,開源軟件的價值之一就在於可追溯和可驗證。然而在實際應用中,很多人並不清楚自己正在運行的二進制文件究竟來自何處,也無法確認這些文件是否在完全隔離、未被污染的環境中構建生成。換句話説,源代碼是開放的,但最終運行的結果未必可驗證。
開源軟件與供應鏈安全風險
近期曝光的 XZ Utils 事件是典型的供應鏈攻擊案例。
XZ 是 SSH 及多種系統組件所依賴的壓縮庫,雖然漏洞並非出現在 PostgreSQL 項目中,但部分 PostgreSQL 部署環境也可能直接或間接依賴該庫。攻擊者在受影響版本中植入惡意代碼,使系統具備遠程控制後門。
這一事件説明:即便開源代碼本身安全可靠,如果構建或分發環節被污染,最終運行環境仍可能受到攻擊。
可復現構建
供應鏈攻擊暴露了一個更底層的問題——如何證明你運行的二進制文件確實來源於可信的源代碼?要避免這種無法確認構建來源和安全性的風險,就需要一種系統化的方法——可復現構建(Reproducible Builds)。相關資料可參考https://reproducible-builds.org
可復現構建核心要求是:在不同環境、不同時間、不同架構上編譯相同源代碼,生成的二進制文件必須逐字節一致。驗證方法可通過計算摘要值完成,元數據差異(如編譯時間戳)可以忽略,但摘要值一致是最直接的判斷標準。
理論上,編譯器輸入相同輸出應一致,但實際情況卻不同:只要構建方式或環境存在差異,生成的二進制文件就可能不同,這正是可復現構建要解決的問題。
可重現構建的重要性
沒有可復現構建時,存在以下問題:
- 無法確保二進制文件的生成過程可控,也無法在不同環境中復現相同結果。
- 開發和測試環境無法使用與生產環境完全一致的二進制文件進行調試。
- 系統部署流程複雜,緩存利用效率下降,同時二進制文件的數量和管理成本增加。
可復現構建的重要意義在於:確保在開發、測試、生產環境中使用一致的二進制文件,便於性能問題排查和調試;同時提高大規模部署和緩存分發的效率。這也是 Facebook、Google 等大型互聯網公司推動可復現構建的核心原因。
封閉式構建
封閉式構建系統會將構建過程與宿主系統的任何變化隔離開來,因此只要源碼和配置相同,就能夠生成一致的結果。封閉式構建一般能夠實現以下效果:
- 可重現性。
- 防止環境污染。
- 能夠生成自包含或靜態的軟件包。
“密封性”(Hermiticity)是封閉式構建的核心概念。理論上,編譯相同源碼就能得到相同結果,但實際情況往往不同。大多數構建系統都會受到宿主環境的影響:在不同電腦上編譯相同軟件,結果很可能不同,無法復現。這是因為系統中現有的庫、工具甚至配置都會影響最終生成的二進制文件。
為保證構建結果一致,需要使用密封構建系統。密封系統會創建一個隔離環境,類似“沙箱”,在 Linux 上常為容器,在 macOS 上可能是虛擬化沙箱,並將構建所需的所有軟件和工具以明確版本控制的方式引入環境中。這樣,無論在哪台機器上構建,結果都一致,從而實現可復現。
封閉式構建的價值與優勢
封閉式構建能夠減少因環境差異導致的二進制文件差異,其主要原因包括:
- 二進制文件中與系統相關的嵌入內容:
- 時間戳,編譯時間不同會導致二進制文件差異。
- RPATH(運行時庫路徑),路徑硬編碼進二進制文件,與環境相關。
- GNU_BUILD_ID(構建標識)。
- 含有構建路徑、配置標誌等信息的字符串或調試信息。
- 反映構建系統的字符串,不同系統會導致文件差異。
- 代碼生成差異(例如 flex 的 #line 指令),不同版本的生成工具會導致結果不同。 - 依賴項和工具的版本不同,依賴庫或編譯工具版本差異會影響二進制文件生成。
封閉式構建雖然不能完全保證結果一致,但通常能夠提升二進制文件的可重現性,避免環境污染對構建結果的干擾,並能夠生成包含或靜態的軟件包。
密封構建是實現可復現構建的重要前提,也有助於防止環境污染,確保在任何地方都能生成一致、乾淨的二進制文件。如果能夠進一步實現自包含的二進制文件,就可以明確知道軟件不依賴外部庫,從而避免因環境不同導致的差異。
Debian 的可重現構建,名副其實嗎?
Debian 的 sid 分支中,大多數軟件包已實現可重現構建,前提是構建路徑和環境完全固定且事先定義清楚參考:https://wiki.debian.org/ReproducibleBuilds
對於 PostgreSQL 來説,情況並不完全相同。即便 Debian 的 PostgreSQL 安裝包能夠在特定條件下復現,普通用户在非標準環境中仍無法輕鬆生成與官方完全一致的二進制文件。換言之,構建路徑、環境和依賴必須嚴格控制,否則無法保證復現性。
Postgres 源代碼:在“一台黃金服務器”上打包生成
官方 PostgreSQL 源代碼包的生成依賴於項目組的特定服務器,只有極少數開發者可以訪問。這意味着,雖然源代碼公開,但能夠生成官方發佈的、逐字節一致的源代碼包的人非常有限。
這種集中式的打包方式限制了源代碼的可驗證性。如果構建環境能夠實現密封化和隔離化,任何人都可以生成與官方一致的源代碼包,從而提升 PostgreSQL 的安全性和可追溯性。
自舉式構建
構建系統中有一個被稱為“自舉式構建(Bootstrapable Builds)”的概念,它探討的是一個類似“先有雞還是先有蛋”的問題。以編譯器為例,構建一個編譯器通常需要另一個已經存在的編譯器來完成。那麼,這個“最初的編譯器”又是如何產生的?
自舉式構建正是為了解決這一問題——通過從一個極小、可人工驗證且可信的軟件片段開始,逐步構建出完整系統。這樣可以在整個構建過程中防止惡意代碼注入,確保生成的軟件完全可驗證、可復現。
Guix:實現純源碼自舉的方案
以 GNU Guix 為代表的部分自由軟件項目,正在嘗試實現“純源碼自舉”方案。其思路是從一個極簡的可信工具集出發,例如一個僅具備最基本功能的小型編譯器,用它構建出稍複雜的編譯工具,再逐步編譯出 make、configure 等基礎工具,接着構建早期版本的 GCC 2.95,最後再由該編譯器生成現代版本的 GCC 以及用於編譯 PostgreSQL 等軟件的完整工具鏈。
通過這種方式,整個軟件棧的構建路徑都可被驗證,且不依賴任何外部二進制環境,實現從源碼到最終系統的完全可追溯和可控構建過程。
但目前,純自舉構建仍是一個理想目標,但這一方向的研究與實踐正在持續推進。
Postgres 發行版:加強安全保障
將可復現與封閉式構建的理念應用於 PostgreSQL 發行版,可在構建過程中實現結果一致與環境可控,從而進一步提升系統的安全性與可信度。這一方向正成為發行版安全保障體系的重要發展路徑。
SBOM
軟件物料清單(SBOM,Software Bill of Materials)是一份記錄代碼庫中所有開源和第三方組件的清單,列明各組件的版本、許可協議、補丁狀態及依賴關係。其作用類似製造業中的物料清單:如同飛機制造中明確列出零部件的型號與數量,SBOM 列出了軟件構建所需的全部依賴庫及其來源信息。
在 PostgreSQL 發行版中,建立完整的 SBOM 能夠實現組件可追溯,便於識別依賴庫版本及其安全狀態,例如確認是否使用了存在漏洞的特定庫版本。
SBOM 的安全價值
SBOM 的應用能夠在多個層面強化軟件供應鏈安全:
- 提供合規與審計依據,確保組件使用符合法律與開源許可要求。
- 輔助防範與識別供應鏈攻擊,提升整體可追溯性。
- 滿足監管要求。2021 年 5 月,美國白宮發佈行政命令,要求所有政府機構及承包商在軟件交付中提供 SBOM,以確立安全透明的供應鏈標準。
隨着全球供應鏈安全治理的加強,SBOM 正逐步成為軟件交付與安全保障體系中的關鍵組成部分。
SBOM示例
這是一個 SBOM 示例,本質上是一個 JSON 文件,用於列出所有依賴項及其版本等信息。
可驗證的 SBOM
可驗證的 SBOM 是通過可復現構建生成的清單,能夠對軟件構建過程進行完整審查和驗證。單獨提供二進制文件和 SBOM 並不足以保證安全,關鍵在於能夠在可復現、密封的環境中重新生成 SBOM,從而確認其真實性。
這種方式不僅提升供應鏈透明度和信任度,還為合規審計提供依據,幫助企業驗證組件符合許可和安全要求,實現 PostgreSQL 發行版的安全可控。
數字簽名的(容器)鏡像
容器鏡像可以進行數字簽名,以驗證軟件包的作者身份。SBOM 同樣可以進行數字簽名。二者結合能夠形成完整的驗證閉環,從作者身份到組件來源,再到供應鏈的可靠性,實現端到端的安全保障。
在分發軟件時,使用數字簽名的容器鏡像能夠提升安全性。與單獨簽名安裝包相比,容器鏡像的生態更成熟,現有工具可直接進行簽名和驗證,使簽名操作高效且可靠。
自動漏洞(CVE)掃描
容器鏡像能夠方便地進行自動化安全掃描,以識別潛在漏洞。在容器鏡像的構建過程中,應當包含漏洞掃描環節,並可實現完全自動化。
大多數漏洞檢測工具通過安裝包指紋來識別漏洞,但如果提供可信的 SBOM,工具可以直接分析 SBOM 中列出的依賴版本,從而準確判斷軟件所使用組件的安全狀態,提升檢測效率和可靠性。
從技術到發行版的整合
前文介紹了可復現構建、密封構建、SBoM、數字簽名鏡像等多個技術點。結合這些原則,可以構建一個遵循安全和可控標準的 PostgreSQL 發行版,即“Monogres”項目。
Monogres
Monogres 是 PostgreSQL 的單體倉庫(Monorepo),將 PostgreSQL 本體、擴展和衍生版本統一集中管理。單體倉庫的核心價值在於創建連貫的軟件構建環境,確保所有代碼使用相同的庫和構建系統,並能夠一次性完成構建。
Monogres:目標
Monogres 的目標是建立一個集中式倉庫,用於索引、構建和打包 PostgreSQL 及其所有擴展。通過單體倉庫,能夠實現完全密封、可復現的構建,併為下游發行版提供上游源代碼和構建腳本。用户可以在任意環境中生成逐字節一致的二進制文件,並通過 SBoM 完整追溯構建過程和依賴。
Monogres:開源上游發行版
Monogres 旨在建立一個安全、可控的上游發行版,核心原則是從源代碼開始,以完全封閉且可復現的方式構建整個 PostgreSQL 生態及其擴展。主要特點包括:
- 採用完全封閉且可復現的構建流程,確保構建結果一致且可驗證。
- Monogres 以 Apache 2.0 許可證開源,便於下游使用與擴展。
- 作為上游發行版,允許下游衍生版本複用並重新打包。
- 提供二進制版本,同時可能提供源碼發行版以便審計和構建。
Monogres 與 Bazel:二選其一
為了實現可復現、密封構建,Monogres 使用 Bazel 作為構建工具。Bazel 是由 Google 開源的成熟構建系統,設計原則強調可復現性和構建正確性,適用於管理大型項目及多版本構建。通過 Bazel,Monogres 能夠統一編譯 PostgreSQL 本體和擴展,確保構建結果在不同環境中保持一致。
下一步計劃
- 將 Monogres 倉庫開源,提供完整的源代碼及構建腳本。
- 開發 Monobot 自動化工具,用於生成擴展和版本的 repo.json 文件,實現構建自動化。
- 添加更多擴展,目前已包含所有 contrib 擴展及部分 PGXS 擴展。
- 支持多版本 glibc 和多個 PostgreSQL 衍生分支,包括 Babelfish、IvorySQL、OrioleDB、OpenHalo、PgEdge 等。
- 確保衍生版本與擴展兼容性,通過自動化構建驗證每個擴展在各衍生版本上的適用性。
結語
開源 PostgreSQL 的價值不僅在於源代碼公開,更在於運行結果的可驗證性。然而,不同來源、不同構建環境生成的二進制文件可能存在顯著差異,使得最終軟件的安全性和可靠性難以確認。建立可靠的構建和分發體系,確保軟件從源代碼到運行環境的全程可控和可追溯,是提升供應鏈安全和軟件可信度的關鍵。這不僅讓企業和開發者能夠更安心地使用開源軟件,也為 PostgreSQL 的生態發展提供了堅實的基礎。