今天終於來到了第三條主線:可以切換、也可以復原的服務。這條主線會逐步建立 PostgreSQL、共識系統、自動故障切換、固定服務入口、備份與監控,最後再用故障演練驗證整條服務路徑。
網路規則可以限制哪些來源連向服務,卻無法單獨證明連線另一端的身分。若用戶端(Client)只確認 TCP 連得上或 TLS 已加密,可能把帳號、查詢與資料送給錯誤的主機。建立資料庫節點前,必須先準備一個可共同信任的身分驗證基礎。
Day 17 以 OpenVPN 內建 CA 簡單說明憑證的用途。本篇會從金鑰、數位簽章與 X.509 憑證開始,完整建立公開金鑰基礎建設與內部 CA。PostgreSQL 的安裝、TLS 設定與存取控制留到下一篇處理。
本文閱讀方式
- 完整理解技術與底層原理:依序閱讀全文。
- 掌握完成部署所需的基本概念:優先閱讀標有「⭐」的章節。
- 快速了解當天的部署內容:閱讀文末的 Lab/實作章節。
- 跟著系列建立完整系統:依照 GitHub 詳細部署文件 的天數順序操作。
TLS 需要一張憑證將服務名稱與公鑰綁在一起,也需要一個用戶端已經信任的來源驗證這張憑證。內部服務常使用私有 DNS 名稱與私有 IP,無法直接照搬公開網站的憑證取得方式,因此要先選擇合適的信任模型。
| 項目 | 主要作用 |
|---|---|
| TLS | 建立受保護的連線,協商暫時金鑰並加密後續傳輸資料 |
| X.509 憑證 | 記錄服務名稱、公鑰、簽發者、用途與有效期間,供 TLS 驗證連線對象的身分 |
簡單來說,TLS 負責保護連線,X.509 憑證負責提供可驗證的身分。憑證本身不會建立連線,也不會單獨加密應用資料。
| 作法 | 用戶端如何建立信任 | 適合情境與限制 |
|---|---|---|
| 每台服務各自建立自簽伺服器憑證 | 將每一張伺服器憑證逐一匯入用戶端 | 少量測試可以使用。節點增加或換證時,每個用戶端都要更新信任資料 |
| 公開 CA | 作業系統或瀏覽器通常已信任公開根 CA | 適合可驗證控制權的公開 DNS 名稱。公開 CA 不會替內部名稱或保留 IP 簽發公開信任憑證 |
| 內部 CA | 先將組織的根憑證安全匯入用戶端,再由同一套 CA 簽發服務憑證 | 適合私有服務與內部名稱。組織必須自行保護 CA、散布信任根並管理憑證生命週期 |
每台服務各自建立自簽憑證時,信任關係會隨服務數量增加。使用內部 CA 後,用戶端主要信任共同的根憑證。新增節點或更新伺服器憑證時,只要信任鏈與名稱符合政策,便不必重新匯入每一張伺服器憑證。
之後部署的公開網站會使用可公開驗證的網域名稱與 Cloudflare TLS。本篇處理的是 PostgreSQL、etcd 等內部服務的身分。兩者都使用 TLS 與 X.509 憑證,信任來源、簽發條件與流量位置則不同。
建立憑證機構前,先分清楚三種容易混在一起的功能:
| 功能 | 使用方式 | 解決的問題 | 無法單獨證明的事情 |
|---|---|---|---|
| 加密(Encryption) | 使用金鑰將明文轉成密文 | 讓通訊內容只有預期的通訊雙方能讀取 | 對方是否真的是預期主機 |
| 雜湊(Hash) | 將任意長度資料計算成固定長度摘要 | 比較資料是否改變 | 摘要由誰建立 |
| 數位簽章(Digital Signature) | 私鑰產生簽章,公鑰驗證簽章 | 證明簽署者持有對應私鑰,並檢查被簽資料是否遭修改 | 公鑰最初屬於誰 |
加密不代表封包只會經過通訊雙方。交換器、路由器與其他中途設備會轉送密文,但不能因此讀到原始內容。加密要保護的是通訊內容只由預期的雙方理解。憑證與數位簽章則進一步確認目前使用的公鑰確實屬於預期對象,避免把資料加密後送給冒充的主機。
| 類型 | 使用的金鑰 | 主要用途 |
|---|---|---|
| 對稱式加密(Symmetric Encryption) | 雙方使用同一份祕密金鑰 | 運算速度快,適合保護大量連線資料 |
| 非對稱式密碼學(Asymmetric Cryptography) | 使用互相對應的私鑰與公鑰 | 適合數位簽章、驗證身分與建立共享祕密 |
CA 使用非對稱式密碼學,以 CA 私鑰簽署憑證,再由驗證端使用 CA 公鑰驗證簽章。CA 金鑰不會拿來加密 TLS 傳輸內容。
TLS 不會使用伺服器憑證內的公鑰直接加密整段應用資料。它會以非對稱式密碼學驗證身分與建立共享祕密,再使用速度較快的對稱式加密保護後續資料。完整順序會在後面的 TLS 1.3 流程說明。
私鑰必須留在擁有該身分的主機。憑證與公鑰可以公開傳送。私鑰一旦外洩,攻擊者就可能冒用原本的身分。
在網路上提供一把公鑰,不足以證明連線者就是它所宣稱的主機。公開金鑰基礎建設(Public Key Infrastructure,PKI)透過憑證、簽發政策與信任鏈,將公鑰綁定到可驗證的服務身分,讓連線雙方能確認目前正在和誰通訊。
| 元件或角色 | 功能 |
|---|---|
| 私鑰(Private Key) | 由身分持有者保管,用來產生簽章。不得隨憑證一起散布 |
| 公鑰(Public Key) | 由其他端驗證簽章,也是憑證綁定的核心資料 |
| 憑證簽署請求(Certificate Signing Request,CSR) | 帶著公鑰與申請名稱向 CA 提出簽發請求,並以私鑰證明申請者持有對應金鑰 |
| 憑證(Certificate) | 由 CA 簽署,將公鑰、名稱、用途與有效期間綁定在一起 |
| 憑證機構(Certificate Authority,CA) | 驗證申請是否符合政策,並簽發、續期或撤銷憑證 |
| 信賴憑證者(Relying Party) | 使用受信任的根憑證驗證對方憑證,例如 PostgreSQL 用戶端 |
一張 X.509 憑證包含多個欄位,用戶端不會只檢查檔名或共同名稱(Common Name):
| 欄位 | 用途 |
|---|---|
| 主體(Subject) | 描述憑證代表的對象 |
| 主體替代名稱(Subject Alternative Name,SAN) | 列出這張憑證可代表的 DNS 名稱或 IP |
| 簽發者(Issuer) | 指出簽發這張憑證的 CA |
| 公鑰(Public Key) | 提供對應公鑰與演算法資訊 |
| Not Before/Not After | 定義憑證開始生效與失效時間 |
| 金鑰用途(Key Usage)/擴充金鑰用途(Extended Key Usage,EKU) | 限制金鑰可用於簽章、CA 簽發或 TLS 伺服器等用途 |
| 簽章(Signature) | 由簽發者建立,讓驗證端確認欄位與公鑰未遭竄改 |
SAN 是連線名稱驗證的主要依據。若用戶端使用固定的資料庫名稱連線,伺服器憑證必須包含該名稱。只有節點本身的主機名稱(Hostname),無法保證日後使用另一個服務名稱時能通過完整驗證。
一張憑證從本機金鑰變成受信任身分,大致會經過以下流程:

圖(一)服務主機在本機建立私鑰與 CSR,CA 驗證申請後簽發並回傳憑證。私鑰不會離開服務主機。
CA 的簽章證明這份公鑰與身分資料通過該 CA 的簽發程序。真正使用憑證時,用戶端還要檢查信任鏈、名稱、用途與有效期間,不能把簽章存在直接等同於連線安全。
CSR 的簽章證明申請端持有對應私鑰。JWK Provisioner 透過簽發權杖驗證申請。自架 step-ca 的 SAN 簽發政策設在 CA 層級。有效期間與憑證樣板則可依 Provisioner 調整。
一般內部 PKI 會將 CA 分成根憑證機構(Root CA)與中繼憑證機構(Intermediate CA):
圖(二)用戶端沿憑證鏈驗證簽章,再核對伺服器名稱、憑證用途與有效期間。
圖中的簽章驗證是使用 CA 公鑰驗證簽章,不是查詢資料庫中是否保存相同憑證。名稱、時間與用途的比對資料則來自用戶端本身。
根憑證(Root Certificate)是信賴起源(Trust Anchor)。它的信任來自管理者透過可信管道,將正確的根憑證或指紋(Fingerprint)放進用戶端的信任儲存區(Trust Store)。自我簽署本身不會自動產生信任。若一開始匯入錯誤的根憑證,後續簽章即使都能驗證,整條信任鏈也會建立在錯誤的起點上。
根 CA 私鑰能簽發新的中繼 CA,外洩時會影響整條信任鏈。正式環境通常讓根 CA 離線,只在受控程序中簽發或更新中繼 CA。日常伺服器憑證則由線上中繼 CA(Online Intermediate CA)簽發。這樣無法消除中繼 CA 私鑰外洩的風險,但能縮小根 CA 私鑰暴露的時間與用途。
信任鏈在圖上是一條簽署關係,部署時則會分散成數個用途不同的檔案:
| 檔案 | 主要保存位置 | 是否屬於祕密 | 用途 |
|---|---|---|---|
| 伺服器私鑰 | 實際提供服務的伺服器 | 是 | 產生握手簽章,證明伺服器持有憑證對應的私鑰 |
| 伺服器憑證 | 伺服器 | 否 | 提供服務名稱、公鑰、用途與有效期間 |
| 中繼 CA 憑證 | 通常由伺服器隨伺服器憑證提供 | 否 | 將伺服器憑證接到用戶端已信任的根 CA |
| 根憑證 | 用戶端的信任儲存區或應用程式指定位置 | 否,但必須可靠取得 | 作為憑證路徑驗證的信賴起源 |
伺服器私鑰與憑證必須屬於同一組金鑰,否則無法完成握手簽章。根憑證不需要在每次 TLS 握手(TLS Handshake)時由伺服器傳送,因為用戶端必須事先信任它。
本系列主要使用 PEM 格式保存憑證與私鑰。PEM 是以文字形式封裝二進位資料的格式,可以從 -----BEGIN CERTIFICATE----- 或 -----BEGIN PRIVATE KEY----- 等標頭辨認內容。副檔名可能使用 .crt、.pem 或 .key,檔名本身無法決定內容與安全性,應查看標頭並限制私鑰權限。
PostgreSQL 用戶端的常見驗證程度如下:
| sslmode | 驗證範圍 |
|---|---|
require |
要求加密連線,但未完整驗證 CA 與連線名稱 |
verify-ca |
驗證憑證鏈是否通往受信任的根 CA |
verify-full |
驗證憑證鏈,並核對實際連線名稱 |
後續資料庫連線以 verify-full 為目標,同時確認連線已加密,而且對方是預期的資料庫服務。
CA 負責建立信任,不會轉送每一條 TLS 連線。簡化後的 TLS 1.3 伺服器驗證流程如下:

圖(三)TLS 1.3 先協商臨時金鑰並驗證伺服器憑證與握手簽章,成功後才使用本次 Session 的暫時金鑰保護應用資料。
臨時金鑰協商負責推導本次工作階段(Session)使用的共享祕密。伺服器私鑰則用來證明它持有憑證對應的私鑰。兩者用途不同,因此更新伺服器憑證不等於拿長期私鑰直接加密所有資料。

圖(四)信任根部署、憑證簽發與服務連線是三個分離的階段。根憑證指紋必須經可信管道核對。
部署到用戶端的是可以公開的根憑證,不是根 CA 私鑰。根 CA 私鑰由 CA 端保護,不會傳送給用戶端或服務主機。
只要憑證尚未到期、用戶端仍信任它的信任鏈,CA 暫時關機不會讓既有伺服器憑證立刻失效。受到影響的是新憑證簽發、續期及其他需要聯絡 CA 的管理作業。
單向 TLS(One-way TLS)以憑證驗證伺服器,用戶端身分可再由帳號密碼或其他機制驗證。雙向 TLS(Mutual TLS,mTLS)則要求雙方都提供憑證並互相驗證。

圖(五)單向 TLS 與雙向 TLS(mTLS)的驗證方向。
接下來部署的 PostgreSQL 採用單向 TLS:用戶端驗證資料庫伺服器憑證,資料庫伺服器再使用 SCRAM 驗證登入帳號與密碼,用戶端不需要提供憑證。etcd 則採用 mTLS,節點與管理端都要提供憑證並互相驗證。憑證確認連線端身分後,服務內部的角色(Role)與權限由 PostgreSQL 或 etcd 判斷。後面實際使用到 PostgreSQL 與 etcd 會再有更詳細的教學。
憑證具有明確的到期時間,私鑰與信任來源也可能需要更換。完整生命週期至少包含:
續期不會延長原憑證的到期時間,他會由 CA 簽發一張新的憑證。服務可以沿用原私鑰,也可以趁續期建立新金鑰。私鑰外洩或輪替政策要求更換時,必須使用新金鑰。
續期作業應依序監控 Not After、在到期前申請新憑證、驗證信任鏈與 SAN 等欄位、替換憑證並重新載入服務,最後再從用戶端確認新憑證與服務連線。
自動續期也不能只確認 CA 已回傳新檔案。服務若沒有重新載入,可能繼續提供舊憑證。
撤銷不代表每一個用戶端都會立刻得知憑證失效。驗證端還必須實際取得並檢查撤銷資訊。若環境沒有部署這項檢查,短效憑證、快速輪替、限制私鑰暴露與移除信任是必要控制。
TLS 失敗時,不應只把驗證功能關閉。錯誤通常可以對應到特定檢查位置:
| 常見現象 | 代表的檢查結果 | 優先確認 |
|---|---|---|
unknown ca/unable to get local issuer certificate |
無法接到受信任的根 CA | 用戶端是否匯入正確根憑證,伺服器是否提供必要的中繼 CA 憑證 |
hostname mismatch |
實際連線名稱不符合憑證 | DNS 名稱、連線參數與 SAN 是否一致 |
certificate has expired |
目前時間已超過 Not After |
憑證是否完成續期,服務是否已載入新憑證 |
certificate is not yet valid |
目前時間早於 Not Before |
用戶端、伺服器與 CA 的系統時間是否正確 |
unsupported certificate purpose |
憑證用途不符合目前連線 | EKU 是否允許 TLS 伺服器或 TLS 用戶端驗證 |
key values mismatch |
私鑰與憑證中的公鑰不屬於同一組 | 部署路徑、檔名與金鑰指紋 |
有效期間檢查依賴每台主機的系統時間。CA、伺服器或用戶端時間偏差過大時,剛簽發的憑證可能被判定尚未生效,舊憑證也可能提早顯示過期。時間同步因此屬於 PKI 的必要依賴,不能只在看到到期錯誤後才處理。
本次建立 ca01、根 CA、中繼 CA 與 step-ca 簽發服務,再簽發一張短效測試憑證,確認它能接回預先建立的信任根。今天的範圍是建立及驗證簽發基礎,不把伺服器憑證部署到 PostgreSQL 節點。
GitHub 實作文件:Day 19|從金鑰到信任鏈:使用 step-ca 為 PostgreSQL HA 建立專用內部 CA
| 項目 | 本次設定 |
|---|---|
| CA 主機 | ca01,10.77.30.10 |
| CA 結構 | 根 CA 與線上中繼 CA |
| 簽發服務 | step-ca,TCP 9000 |
| 簽發授權 | JWK Provisioner |
| 測試憑證 | test.lab.home,有效期間 10 分鐘 |
| 主機防火牆 | nftables 預設丟棄,只允許指定來源連入 TCP 9000 |
| 管理入口 | 建置完成後停用網路 SSH,保留 PVE Console |
| 儲存位置 | ca01 本機磁碟,不使用 Ceph |
CA 不使用 Ceph 儲存,避免簽發服務為了示範可用性而增加不必要的共享儲存依賴。這項選擇不會讓 CA 自動具備高可用。CA 設定、金鑰備份與復原需另行規劃。
先從 Debian 13 Template 建立 ca01,安裝 step-cli、step-ca 與 nftables,再建立專用服務帳號與 /var/lib/step-ca。CA 金鑰密碼與 JWK 簽發授權密碼分開保存,初始化後應建立根 CA、中繼 CA、加密私鑰與 step-ca 設定。
初始化完成後至少應存在以下檔案:
/var/lib/step-ca/config/ca.json
/var/lib/step-ca/certs/root_ca.crt
/var/lib/step-ca/certs/intermediate_ca.crt
/var/lib/step-ca/secrets/intermediate_ca_key

圖(六)ca01 已建立根憑證、中繼 CA 憑證、受限權限的私鑰與 step-ca 設定檔。
ca.json 是 step-ca 的主要設定,intermediate_ca_key 則是日常簽發使用的敏感私鑰。
密碼檔案(Password File)讓 systemd 能在無人輸入時解密中繼 CA 私鑰,其中保存明文密碼。這是 Lab 為了自動開機採用的折衷。檔案必須限制為 root:step-ca 640,不得放入 Git、截圖或一般共享位置。

圖(七)step-ca 維持 active 並監聽 TCP 9000,健康檢查與根憑證指紋皆可正常取得。
簽發一張只使用十分鐘的 test.lab.home 測試憑證,再使用 ca01 的根憑證驗證憑證鏈。檢查結果必須同時符合三個條件:Issuer 來自本次建立的 CA、SAN 包含 test.lab.home,而且有效期間符合短效測試設定。
這項測試證明 step-ca 不只是開啟 TCP 9000,也真的能完成授權、簽發與信任鏈驗證。由於這次測試直接在 ca01 執行,測試憑證與私鑰會暫存在 ca01 的 /var/lib/step-ca/tmp-test。完成檢查後立即刪除。它們不會部署到 PostgreSQL 節點,也不能當成下一篇的伺服器憑證。

圖(八)短效測試憑證通過信任鏈驗證,Issuer、有效期間與 SAN 均符合設定。
ca01 與後續 PostgreSQL 節點同在 VLAN 30。同 VLAN 流量不一定經過 OPNsense,因此要在 ca01 啟用 nftables 主機防火牆,只允許預定的 pg01~pg03 連入 TCP 9000。完成建置後關閉網路 SSH,只保留 PVE Console 作為管理路徑。

圖(九)ca01 重新開機後,step-ca 與 nftables 維持 active,SSH 維持 inactive,TCP 9000 與健康檢查也正常。

圖(十)nftables 預設丟棄入站流量,只有指定的 PostgreSQL 節點可以連入 TCP 9000。畫面另含 Day 29 才加入的 TCP 9100 監控規則。
| 正文敘述 | 驗證方式 | 目前結果 |
|---|---|---|
| CA 的日常簽發應使用中繼 CA 私鑰 | 核對根憑證、中繼 CA 憑證、私鑰與 ca.json |
必要檔案均已建立,敏感私鑰只允許服務帳號讀取 |
| 開啟連接埠不代表 CA 已具備完整簽發能力 | 檢查服務、監聽端點、健康狀態與根憑證指紋 | step-ca 為 active,監聽 10.77.30.10:9000,健康檢查正常 |
| 信任鏈從預先信任的根憑證開始 | 簽發短效憑證並使用根憑證驗證 | 驗證成功,Issuer、SAN 與 10 分鐘有效期間均符合設定 |
| 同 VLAN 的 CA 需要主機層存取控制 | 核對 nftables Input Chain | Input Policy 為 Drop,TCP 9000 只允許指定 PostgreSQL 節點來源 |
| 重新開機不應改變既定安全狀態 | 重新開機後檢查 step-ca、nftables、SSH 與健康端點 | step-ca 與 nftables 維持 active,SSH 維持 inactive,健康檢查正常 |
本次為了讓一台 VM 呈現完整流程,根 CA 與線上中繼 CA 的私鑰都放在 ca01。正式環境應進一步處理:
下一篇是 Day 20|準備 PostgreSQL HA 的三個資料庫節點:PostgreSQL 18、資料磁碟、TLS 與 SCRAM-SHA-256。我們會把信任根帶到 pg01~pg03,完成資料磁碟、資料庫安裝、伺服器憑證與存取控制,讓三台節點具備一致的安全基線。