iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
IT Operation

從裸機到叢集:以 Proxmox VE、Ceph、OPNsense 與 PostgreSQL HA 建立可驗證的安全私有雲系列 第 19

Day 19|從金鑰到信任鏈:使用 step-ca 為 PostgreSQL HA 建立專用內部 CA

  • 分享至 

  • xImage
  •  

今天終於來到了第三條主線:可以切換、也可以復原的服務。這條主線會逐步建立 PostgreSQL、共識系統、自動故障切換、固定服務入口、備份與監控,最後再用故障演練驗證整條服務路徑。

網路規則可以限制哪些來源連向服務,卻無法單獨證明連線另一端的身分。若用戶端(Client)只確認 TCP 連得上或 TLS 已加密,可能把帳號、查詢與資料送給錯誤的主機。建立資料庫節點前,必須先準備一個可共同信任的身分驗證基礎。

Day 17 以 OpenVPN 內建 CA 簡單說明憑證的用途。本篇會從金鑰、數位簽章與 X.509 憑證開始,完整建立公開金鑰基礎建設與內部 CA。PostgreSQL 的安裝、TLS 設定與存取控制留到下一篇處理。

今天要解決的問題

  1. 為什麼內部服務需要自己的 CA,不能讓每台主機各自建立憑證?
  2. 加密、雜湊與數位簽章分別保護什麼?
  3. 一把公開金鑰如何成為可以驗證的服務身分?
  4. 根憑證、中繼 CA 與伺服器憑證如何形成信任鏈?
  5. TLS 用戶端實際驗證哪些內容,CA 是否位於每一條連線中間?
  6. 單向 TLS 與雙向 TLS 分別驗證誰?
  7. step-ca 如何控制誰能申請憑證,以及可以申請哪些名稱?
  8. 私鑰、續期、撤銷、備份與 CA 故障應如何納入生命週期?

本文閱讀方式

  • 完整理解技術與底層原理:依序閱讀全文。
  • 掌握完成部署所需的基本概念:優先閱讀標有「⭐」的章節。
  • 快速了解當天的部署內容:閱讀文末的 Lab/實作章節。
  • 跟著系列建立完整系統:依照 GitHub 詳細部署文件 的天數順序操作。

⭐ 為什麼內部服務需要自己的 CA

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 驗證申請並簽發憑證

圖(一)服務主機在本機建立私鑰與 CSR,CA 驗證申請後簽發並回傳憑證。私鑰不會離開服務主機。

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 為目標,同時確認連線已加密,而且對方是預期的資料庫服務。

憑證如何參與 TLS 連線

CA 負責建立信任,不會轉送每一條 TLS 連線。簡化後的 TLS 1.3 伺服器驗證流程如下:

TLS 1.3 從臨時金鑰協商、伺服器憑證驗證到應用資料加密的流程

圖(三)TLS 1.3 先協商臨時金鑰並驗證伺服器憑證與握手簽章,成功後才使用本次 Session 的暫時金鑰保護應用資料。

臨時金鑰協商負責推導本次工作階段(Session)使用的共享祕密。伺服器私鑰則用來證明它持有憑證對應的私鑰。兩者用途不同,因此更新伺服器憑證不等於拿長期私鑰直接加密所有資料。

信任根部署、憑證簽發與實際 TLS 服務連線的三階段關係

圖(四)信任根部署、憑證簽發與服務連線是三個分離的階段。根憑證指紋必須經可信管道核對。

部署到用戶端的是可以公開的根憑證,不是根 CA 私鑰。根 CA 私鑰由 CA 端保護,不會傳送給用戶端或服務主機。

只要憑證尚未到期、用戶端仍信任它的信任鏈,CA 暫時關機不會讓既有伺服器憑證立刻失效。受到影響的是新憑證簽發、續期及其他需要聯絡 CA 的管理作業。

單向 TLS 與雙向 TLS 驗證不同對象

單向 TLS(One-way TLS)以憑證驗證伺服器,用戶端身分可再由帳號密碼或其他機制驗證。雙向 TLS(Mutual TLS,mTLS)則要求雙方都提供憑證並互相驗證。

單向 TLS 與雙向 TLS 驗證對象的差異

圖(五)單向 TLS 與雙向 TLS(mTLS)的驗證方向。

接下來部署的 PostgreSQL 採用單向 TLS:用戶端驗證資料庫伺服器憑證,資料庫伺服器再使用 SCRAM 驗證登入帳號與密碼,用戶端不需要提供憑證。etcd 則採用 mTLS,節點與管理端都要提供憑證並互相驗證。憑證確認連線端身分後,服務內部的角色(Role)與權限由 PostgreSQL 或 etcd 判斷。後面實際使用到 PostgreSQL 與 etcd 會再有更詳細的教學。

憑證生命週期從私鑰產生時開始

憑證具有明確的到期時間,私鑰與信任來源也可能需要更換。完整生命週期至少包含:

  1. 產生金鑰:私鑰在實際使用它的主機上建立,限制擁有者與檔案權限。
  2. 申請與簽發:簽發授權方式驗證申請資格。CA 層級政策限制可簽發名稱,Provisioner 設定有效期間,憑證樣板控制用途。
  3. 部署與驗證:服務載入新憑證,用戶端使用正確的根憑證驗證名稱與信任鏈。
  4. 監控與續期:在到期前換發憑證,驗證設定後重新載入服務。
  5. 重新產生金鑰:私鑰外洩、演算法政策改變或輪替週期到達時,建立新金鑰對(Key Pair),不只沿用原私鑰續期。
  6. 撤銷與汰換:停止後續使用已不可信的憑證,更新依賴端與信任資料。
  7. 備份與復原:保護 CA 設定、簽署金鑰(Signing Key)與必要的簽發狀態,定期驗證能否還原。

續期會換成一張新憑證

續期不會延長原憑證的到期時間,他會由 CA 簽發一張新的憑證。服務可以沿用原私鑰,也可以趁續期建立新金鑰。私鑰外洩或輪替政策要求更換時,必須使用新金鑰。

續期作業應依序監控 Not After、在到期前申請新憑證、驗證信任鏈與 SAN 等欄位、替換憑證並重新載入服務,最後再從用戶端確認新憑證與服務連線。

自動續期也不能只確認 CA 已回傳新檔案。服務若沒有重新載入,可能繼續提供舊憑證。

撤銷不代表每一個用戶端都會立刻得知憑證失效。驗證端還必須實際取得並檢查撤銷資訊。若環境沒有部署這項檢查,短效憑證、快速輪替、限制私鑰暴露與移除信任是必要控制。

從錯誤訊息判斷信任鏈斷在哪裡

TLS 失敗時,不應只把驗證功能關閉。錯誤通常可以對應到特定檢查位置:

常見現象 代表的檢查結果 優先確認
unknown caunable 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 設定、金鑰備份與復原需另行規劃。

驗證一:ca01 已建立可重複啟動的簽發服務

先從 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 設定檔擁有者及權限

圖(六)ca01 已建立根憑證、中繼 CA 憑證、受限權限的私鑰與 step-ca 設定檔。

ca.json 是 step-ca 的主要設定,intermediate_ca_key 則是日常簽發使用的敏感私鑰。

密碼檔案(Password File)讓 systemd 能在無人輸入時解密中繼 CA 私鑰,其中保存明文密碼。這是 Lab 為了自動開機採用的折衷。檔案必須限制為 root:step-ca 640,不得放入 Git、截圖或一般共享位置。

step-ca 服務正常運作、監聽 TCP 9000,健康檢查與根憑證指紋皆可取得

圖(七)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 節點,也不能當成下一篇的伺服器憑證。

短效測試憑證通過信任鏈驗證,且簽發者、有效期間與 SAN 符合設定

圖(八)短效測試憑證通過信任鏈驗證,Issuer、有效期間與 SAN 均符合設定。

驗證三:CA 只保留必要入口並維持重新開機後的設定

ca01 與後續 PostgreSQL 節點同在 VLAN 30。同 VLAN 流量不一定經過 OPNsense,因此要在 ca01 啟用 nftables 主機防火牆,只允許預定的 pg01~pg03 連入 TCP 9000。完成建置後關閉網路 SSH,只保留 PVE Console 作為管理路徑。

ca01 重新開機後 step-ca 與 nftables 維持 active,SSH 維持 inactive

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

nftables Input Policy 為 Drop,只有指定 PostgreSQL 節點可以連入 TCP 9000

圖(十)nftables 預設丟棄入站流量,只有指定的 PostgreSQL 節點可以連入 TCP 9000。畫面另含 Day 29 才加入的 TCP 9100 監控規則。

Lab 驗證對照

正文敘述 驗證方式 目前結果
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。正式環境應進一步處理:

  • 根 CA 保持離線,線上 CA 只保存中繼 CA 私鑰。
  • 將線上 CA 放入獨立的 PKI 或安全 VLAN。
  • 使用 HSM 或 KMS 保護高價值簽署金鑰。
  • 對 CA 設定、資料庫與金鑰資料執行加密備份及還原演練。
  • 若簽發服務也要求高可用,另外設計受支援的多 CA 架構。只替單台 ca01 開啟 PVE HA,無法消除應用程式與金鑰層的單點故障。

今天完成了什麼

  • 分清楚加密、雜湊、數位簽章與憑證在信任建立過程中的工作。
  • 理解根憑證、中繼 CA、CSR、SAN、EKU 與 TLS 驗證之間的關係。
  • 建立 ca01、根 CA、中繼 CA 與可重複啟動的 step-ca 簽發服務。
  • 簽發短效測試憑證,並以根憑證驗證信任鏈、Issuer、SAN 與有效期間。
  • 使用 nftables 限制 TCP 9000 的來源,停用網路 SSH,並確認重新開機後設定生效。
  • 建立後續簽發服務憑證所需的信任基礎。PostgreSQL TLS 與資料庫存取控制尚未部署。

下一篇預告

下一篇是 Day 20|準備 PostgreSQL HA 的三個資料庫節點:PostgreSQL 18、資料磁碟、TLS 與 SCRAM-SHA-256。我們會把信任根帶到 pg01~pg03,完成資料磁碟、資料庫安裝、伺服器憑證與存取控制,讓三台節點具備一致的安全基線。


參考資料


上一篇
Day 18|在網路邊界發現並阻擋威脅:OPNsense Suricata IDS/IPS 的運作與實測
下一篇
Day 20|準備 PostgreSQL HA 的三個資料庫節點:PostgreSQL 18、資料磁碟、TLS 與 SCRAM-SHA-256
系列文
從裸機到叢集:以 Proxmox VE、Ceph、OPNsense 與 PostgreSQL HA 建立可驗證的安全私有雲20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言