PostgreSQL 终于有了开源透明数据加密:pg_tde 实战指南

2026-05-29 30 预计阅读时间:1 分钟
来源:postgr.es AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:9 分钟

数据库磁盘文件被偷走怎么办?这是很多合规审计场景下的硬问题。MySQL、SQL Server、Oracle 早就有透明数据加密(TDE)方案,而 PostgreSQL 长期以来只能靠文件系统层加密或自编译补丁来凑合——要么不够细粒度,要么维护成本太高。pg_tde 的出现填补了这个空白:它是第一个面向 PostgreSQL 的开源 TDE 扩展,以 extension 形式加载,不需要改应用代码,也不需要重新编译数据库。

透明数据加密到底加密了什么

"透明"的意思是:对上层 SQL 查询完全透明,应用不需要改一行代码;对底层存储则完全不透明——磁盘上的数据文件是加密的。即使有人直接拷走了 .dat 文件或整个数据目录,没有密钥就无法还原表内容。

pg_tde 当前加密的范围:

  • 表数据文件(relation files)——这是最核心的目标,业务数据存在这里
  • 临时文件——排序、哈希等操作产生的中间结果
  • WAL 日志——正在逐步覆盖,确保事务日志也不裸露在磁盘上

它不加密的是:系统目录(pg_class 等元数据)、控制文件、配置文件。这意味着攻击者能看到表结构定义,但看不到实际行数据。对于大多数合规要求(如 PCI-DSS、HIPAA),这已经足够。

密钥管理:TDE 的命门

TDE 的安全性完全取决于密钥管理。pg_tde 采用两层密钥架构:

  1. 主密钥(Master Key):存在外部密钥管理服务(KMS)中,如 HashiCorp Vault、AWS KMS 等。数据库本身不持久保存主密钥。
  2. 表加密密钥(Table Key):每个被加密的表有一个独立密钥,用主密钥加密后存储在 PostgreSQL 的内部目录中。

启动数据库时,pg_tde 从 KMS 获取主密钥,解密各表的加密密钥,再用于读写加密。如果 KMS 不可达,数据库将无法启动或无法访问加密表——这正是期望的行为:密钥和密文不在同一个地方。

安装与启用:从零开始

以下示例基于 Ubuntu 22.04 + PostgreSQL 16 + HashiCorp Vault。如果你用其他平台或 KMS,替换对应步骤即可。

第一步:安装 pg_tde 扩展

# 从 Percona 仓库安装(以 Ubuntu/Debian 为例)
sudo apt update
sudo apt install -y percona-postgresql-16-pg-tde

# 或者从源码编译
git clone https://github.com/Percona/pg_tde.git
cd pg_tde
# 确保已安装 pg_config 对应版本的 dev 包
make
sudo make install

第二步:配置 PostgreSQL 加载扩展

修改 postgresql.conf

# 在 shared_preload_libraries 中加入 pg_tde
# 注意:TDE 扩展必须在启动时加载,不能只靠 CREATE EXTENSION
sudo -u postgres sed -i \
  "s/^#shared_preload_libraries = ''/shared_preload_libraries = 'pg_tde'/" \
  /etc/postgresql/16/main/postgresql.conf

# 重启 PostgreSQL
sudo systemctl restart postgresql

第三步:配置密钥管理

pg_tde 需要知道主密钥从哪里取。创建密钥提供者配置:

-- 连接到目标数据库
psql -U postgres -d mydb

-- 创建扩展
CREATE EXTENSION pg_tde;

-- 注册 Vault 密钥提供者
-- 参数根据你的 Vault 实例调整
SELECT pg_tde_add_key_provider_vault(
  'my_vault',
  'http://127.0.0.1:8200',   -- Vault 地址
  'secret/data/pg_tde',      -- 存储密钥的 Vault path
  's.xxxxxxxx'               -- Vault token
);

-- 设置默认主密钥
SELECT pg_tde_set_default_master_key(
  'my_vault',
  'pg_tde_master_key'        -- Vault 中主密钥的名字
);

第四步:创建加密表

这才是关键一步——不是加密整个数据库,而是按表选择:

-- 创建加密表:在 CREATE TABLE 语句中加上 USING tde_am
-- tde_am 是 pg_tde 提供的专用访问方法(Access Method)
CREATE TABLE payment_cards (
    id         SERIAL PRIMARY KEY,
    card_number TEXT NOT NULL,
    holder_name TEXT NOT NULL,
    expiry      TEXT NOT NULL
) USING tde_am;

-- 普通表仍然用默认的 heap AM,不受影响
CREATE TABLE audit_log (
    id      SERIAL PRIMARY KEY,
    event   TEXT NOT NULL,
    ts      TIMESTAMP DEFAULT now()
);

-- 验证加密表
SELECT amname FROM pg_am WHERE amname = 'tde_am';
-- 应返回 tde_am

SELECT relname, relam
FROM pg_class
WHERE relname = 'payment_cards';
-- relam 应指向 tde_am 的 OID

插入和查询的 SQL 完全不变:

INSERT INTO payment_cards (card_number, holder_name, expiry)
VALUES ('4111111111111111', 'Zhang San', '12/26');

SELECT * FROM payment_cards;
-- 正常返回明文结果,应用无感知

验证磁盘上确实是密文

# 找到加密表的物理文件路径
psql -U postgres -d mydb -c \
  "SELECT pg_relation_filepath('payment_cards');"
# 输出类似: base/16384/16390

# 用 hexdump 查看文件内容
sudo hexdump -C /var/lib/postgresql/16/main/base/16384/16390 | head -20
# 你应该看到乱码/不可读字节,而非明文 "4111111111111111"

按表加密 vs 全库加密:选哪个

pg_tde 采用按表加密策略,这是一个有意的设计选择:

维度 按表加密(pg_tde) 全库加密(文件系统层)
粒度 只加密敏感表,减少性能开销 所有文件一视同仁
密钥轮换 单表密钥可独立轮换 全库轮换需要重写所有数据
性能影响 非加密表零开销 所有表都有加密/解密开销
合规针对性 精确满足"敏感数据加密"条款 过度加密反而增加运维复杂度

如果你的合规要求是"所有客户数据必须加密存储",按表加密更精准;如果要求是"整个存储介质不可读",文件系统层 LUKS 加密可能更简单。两者也可以叠加使用。

上线前的检查清单

  • KMS 高可用:Vault 或 KMS 必须比数据库更可靠。数据库重启时如果拿不到主密钥,整个加密表集合将不可访问。建议 KMS 前面放 HA,或使用缓存机制。
  • 密钥轮换流程:提前演练。pg_tde 支持主密钥轮换,但需要停写或在线重加密,确认你的业务能接受哪种方式。
  • 备份策略调整pg_dump 导出的是解密后的明文,备份文件本身需要加密保护(GPG、Vault encrypt 等)。物理备份(pg_basebackup)则是密文,恢复时需要同样的 KMS 配置。
  • 监控告警:KMS 连接失败必须立即告警,否则数据库重启后你会面临"数据还在但密钥拿不到"的窘境。
  • 测试恢复:在 staging 环境完整演练一次"数据目录丢失 → 从备份恢复 → 从 KMS 取密钥 → 数据库上线"的全流程,不要等到生产出事才发现流程有断点。

pg_tde 目前仍在积极开发中,WAL 加密、索引加密等能力正在完善。如果你的场景是"敏感字段必须磁盘加密且应用不能改代码",它已经是 PostgreSQL 生态里最值得试的方案。先在非生产环境跑通上述流程,再决定是否推进。


相关推荐