透明数据加密(TDE)概述
透明数据加密(Transparent Data Encryption,TDE)指的是:数据在写入磁盘之前被自动加密、读入内存时被自动解密,对应用与 SQL 层完全透明的一类「静态加密(encryption at rest)」能力。它的防御目标非常具体——当物理介质离开你的控制时,数据仍然是密文:硬盘被从退役服务器上拆走、整机失窃、备份磁带遗失、云快照被他人恢复,这些场景下没有密钥的一方拿到的是一堆无法解读的块。
必须先建立一个硬事实认知:社区版 PostgreSQL 不提供内置的 TDE。 这是社区长期 known 的能力缺口(相关讨论持续多年,涉及密钥管理、密钥体系与内核改造的复杂权衡),无论你用的是哪个大版本——包括最新的 PG18——在 postgresql.conf 里都找不到一个「打开就全库加密」的参数。因此本页的主题实际上有两个:一是理解 TDE 防什么、不防什么;二是在社区版上,用文件系统层 / 云基础设施层 / 发行版扩展这三类替代方案组合出等效的静态加密能力。
本页先厘清 TDE 的概念边界与社区版的现实,再逐一展开 LUKS、ZFS 原生加密、云盘加密、托管与发行版方案,讨论 WAL、备份与副本的加密联动、密钥管理与轮换的难度,最后给出 TDE 与列级加密的定位对比。实战部分是一套完整的 LUKS + PostgreSQL 部署流程。
前置知识
建议先阅读:审计策略设计
基础概念
数据安全里的「加密」至少有三层,TDE 只是其中之一,先把三者分开:
| 层次 | 英文 / 记法 | 保护什么 | PostgreSQL 的实现 |
|---|---|---|---|
| 传输加密 | encryption in transit | 网络上的数据(客户端 ↔ 服务器、主 ↔ 备) | SSL/TLS 连接(security 章节 SSL 相关页面) |
| 静态加密 | encryption at rest(TDE) | 落盘的数据、WAL、临时文件、备份 | 社区版无内置;文件系统 / 云盘 / 发行版方案 |
| 字段级加密 | column-level / field-level encryption | 单个敏感列的值 | pgcrypto 扩展、应用侧加密(下一页) |
TDE 防什么、不防什么
TDE 的威胁模型画得越清楚,选型越不容易翻车:
防的:
- 物理介质失窃或未擦除回收(退役硬盘、报废整机);
- 磁盘/卷的整盘拷贝(镜像、快照、冷备份文件被带走);
- 存储基础设施层的越权访问(云快照被他人账号恢复、共享存储上的裸卷被挂载)。
不防的(这一点比防什么更重要):
- SQL 注入与越权访问:数据在数据库内存(shared buffers)与 CPU 缓存中是明文,任何能通过 SQL 读到数据的人(合法账号、注入 payload、被攻陷的应用),TDE 拦不住;
- 超级用户与 DBA:数据库超级用户、操作系统 root 拥有完整的读取路径,TDE 不改变他们看到的东西;
- 内存转储与 swap:进程内存中的明文页可能被 core dump / 交换分区带走(应配合禁用 swap 或加密 swap、收紧 core dump);
- 运行中的数据库:文件系统在实例运行时始终以解密状态提供服务,「把正在运行的卷挂到另一台机器」属于边界场景,多数卷级方案在这一刻仍然有效(需要密钥),但「正在被 root 读取」永远无效。
一句话:TDE 防的是「拿走」,不防「看到」。 防「看到」要靠权限体系、RLS、脱敏与审计(本阶段前面的内容),两层合起来才是纵深。
社区版 PostgreSQL 的现实
把可选方案按「加密发生在哪一层」排开,这是本页的地图:
| 方案类别 | 代表 | 加密层 | 对 PostgreSQL 是否透明 |
|---|---|---|---|
| 文件系统 / 块设备层 | LUKS(dm-crypt)、ZFS 原生加密 | 卷 / 数据集 | 完全透明,PG 无感知 |
| 云盘加密 | 各云厂商的云盘加密(KMS 托管密钥) | 存储服务层 | 完全透明 |
| 云托管 TDE | RDS for PostgreSQL 的 TDE、Aurora 存储加密等 | 托管服务内建 | 完全透明(能力随厂商) |
| 发行版 / 扩展 | EDB 的 TDE、Percona 的 pg_tde 扩展等 | 数据库内 / 存储层 | 透明或半透明,随产品 |
| 列级加密 | pgcrypto、应用侧加密 | 字段层 | 不透明(应用必须感知) |
注意最后一行——列级加密不属于 TDE,它是另一种取舍(防「看到」而非防「拿走」),下一页专门展开。本页聚焦前四行的「落盘即密文」方案。
关于 PG18
PostgreSQL 18 没有内置 TDE,也没有「实例级加密开关」。网络上流传的「PG18 TDE 参数」均不可信;在内核引入完整的密钥管理框架之前,社区版用户的静态加密方案就是上表中的外部方案。
语法与用法
LUKS(Linux 统一密钥设置)方案
LUKS 是 Linux 上标准的块设备加密方案(底层是 dm-crypt),用法是先把物理卷加密、再在映射设备上建文件系统:
# 1. 对整块数据盘启用 LUKS(注意:会清空设备!先确认目标)
sudo cryptsetup luksFormat --type luks2 /dev/nvme1n1
# 2. 打开加密卷,映射为 /dev/mapper/pgdata-crypt
sudo cryptsetup luksOpen /dev/nvme1n1 pgdata-crypt
# 3. 在映射设备上创建文件系统并挂载
sudo mkfs.ext4 /dev/mapper/pgdata-crypt
sudo mount /dev/mapper/pgdata-crypt /var/lib/postgresql
# 4. 查看加密卷状态(算法、密钥槽等)
sudo cryptsetup luksDump /dev/nvme1n1
sudo cryptsetup status pgdata-crypt开机自动解锁通过 /etc/crypttab 配置(生产环境用密钥文件或网络取钥,见后文密钥管理):
# /etc/crypttab
pgdata-crypt /dev/nvme1n1 /etc/luks/pg-keyfile luks,discardZFS 原生加密方案
OpenZFS 在数据集(dataset)级别提供原生加密,粒度比 LUKS 更细,且支持逐数据集密钥:
# 1. 创建加密数据集(默认 aes-256-gcm),密钥格式用口令或 raw/hex
sudo zfs create -o encryption=on \
-o keyformat=passphrase \
-o mountpoint=/var/lib/postgresql \
tank/pgdata
# 2. 输入口令后数据集挂载可用;重启后需要加载密钥
sudo zfs load-key tank/pgdata && sudo zfs mount tank/pgdata
# 3. 查看加密属性
sudo zfs get encryption,keyformat,keystatus tank/pgdata云盘加密
云环境的首选通常是云盘加密——在控制台/API 开启即可,密钥由云 KMS 托管,对操作系统与 PostgreSQL 完全透明:
AWS: EBS encryption(可指定 customer managed key in KMS)
阿里云: 云盘加密(服务密钥或 BYOK 自带密钥)
GCP: Persistent Disk encryption(CMEK)创建云盘时选择加密并指定密钥,随后在盘上照常安装 PostgreSQL,无任何额外配置。
备份的加密联动
静态加密不能只覆盖主库数据目录,备份与 WAL 归档离开本机时必须自带加密:
# pgBackRest 配置片段:仓库端 AES-256 加密
repo1-cipher-type = aes-256-cbc
repo1-cipher-pass = <由密钥管理服务下发的口令># 带加密仓库做一次全量备份
sudo -u postgres pgbackrest --stanza=main --type=full backup详细说明
防线定位:整盘拷贝与介质回收
把 TDE 类方案放进攻防视角理解它的边界。攻击路径「偷走整块盘 / 整台机 / 整个快照」的共同特征是:攻击者拿到的是关闭状态或复制品,没有运行中的数据库进程,也没有密钥。卷级加密让这类路径的成本从「直接读数据」变成「需要先攻破密钥体系(KMS、口令、TPM)」——这是 TDE 的全部价值,也是云厂商合规问卷里「静态加密」一项的技术实质。
反过来,攻击路径「用合法连接把数据读出来」与卷级加密正交。这就是为什么 TDE 与本阶段其他内容是并列关系而不是替代关系:SSL 管传输、权限/RLS/脱敏管「看到」、TDE 管「拿走」、审计管「追溯」。
加密覆盖面:一张清单管全部数据面
「数据目录加密了」不等于「静态加密就位」。PostgreSQL 的落盘数据散布在多个位置,任何一处遗漏都会让防线出现直通缺口。按这张清单逐项核对:
| 数据面 | 位置 | 是否随数据目录自动覆盖 | 加密手段 |
|---|---|---|---|
| 基表与索引 | PGDATA/base/ | 是(同卷时) | 卷级加密 |
| 自定义表空间 | PGDATA/pg_tblspc/ 指向的外部目录 | 否——目录可在任何挂载点 | 外部目录所在卷单独加密 |
| WAL | pg_wal/ | 是(同卷时);独立卷须单独加密 | 卷级加密 |
| 临时文件 | 排序 / 物化的临时段 | 是(默认同卷);temp_tablespaces 指向独立表空间时例外 | 卷级加密 |
| WAL 归档 | archive_command 的目的地 | 否 | 目的地侧加密(桶加密 / 仓库加密) |
| 物理备份 | pg_basebackup / pgBackRest / 快照 | 否 | 仓库加密 / 快照所在存储加密 |
| 逻辑备份 | pg_dump 产物 | 否 | gpg 等应用层加密后外传 |
| 备库 / 容灾节点 | 各自的数据目录与 WAL | 否(是另一台机器的另一套盘) | 与主库同等加密等级 |
| 内存侧旁路 | swap 分区、core dump 目录 | 否 | 禁用 / 加密 swap、收紧 core dump |
最容易漏的两格:自定义表空间——CREATE TABLESPACE 的目录经常被规划在数据盘之外的独立卷上,「加密了数据盘」对它完全无效;备库——流复制落盘后就是完整的数据副本,备库卷的加密等级必须与主库对齐。
社区版的可选方案版图
选型决策树大致如下:
- 云上托管(RDS / Aurora / Cloud SQL 等)→ 直接用厂商的托管加密/TDE 能力,通常一个开关 + KMS 密钥选择,成本最低,合规材料最全。对应能力以各厂商文档为准。
- 云上自建(ECS/EC2 + 云盘) → 云盘加密(KMS 托管密钥),零运维、透明。
- 物理机 / 自管虚拟机 / 本地私有化 → LUKS(块设备,粒度粗、生态成熟)或 ZFS 原生加密(数据集级、支持校验与快照),按现有存储栈选择。
- 有发行版采购许可 / 能接受第三方扩展 → EDB 等商业发行版的 TDE,或 Percona 开源的
pg_tde扩展(开源、支持数据与 WAL 加密、对接 Vault/KMIP 类密钥管理,但相对较新,选型前需评估成熟度与社区反馈)。国内部分数据库发行版也提供透明加密能力,具体能力以厂商文档为准。
一个常见误区是用「Docker 镜像 + 加密容器目录」当 TDE——容器层加密多数只是 overlay 上的解密目录,威胁模型不成立,不要用。
LUKS / dm-crypt 方案细节
LUKS2 是当前推荐的头部格式(支持更强的校验与在线重加密)。工程要点:
- 性能:加密算法默认 AES-XTS,现代 CPU 的 AES-NI 指令让纯加密开销很小;顺序读写在现代硬件上通常接近裸盘,随机小 I/O 会有个位数到十几个百分点的损耗。用
cryptsetup benchmark在目标硬件上实测。 - 对齐与 discard:SSD/NVMe 上启用
discard(crypttab 中的discard或 mount 的discard)让 TRIM 穿透,避免写放大。 - 重加密:LUKS2 支持
cryptsetup reencrypt在线重加密(更换数据加密密钥、改变加密参数),对 TB 级数据是小时级 I/O 过程,要预留维护窗口。 - 备份密钥头:
cryptsetup luksHeaderBackup把 LUKS 头备份到安全位置——头部损坏等于整个卷报废。 - 粒度粗:整个卷一个密钥,无法对「某张表」单独加密;
pg_wal、临时文件、日志(若在同一卷)都自动被覆盖——这既是优点(无遗漏)也是缺点(无差别)。
LUKS 头备份与恢复演练
LUKS 头损坏(坏扇区、误操作、介质老化)等于整个卷报废——数据密文完好也无法解锁。上一节的 luksHeaderBackup 值得展开成一节,因为它是最便宜、也最容易被省略的保险:
# 1. 备份 LUKS 头(卷处于任意状态均可执行)
sudo cryptsetup luksHeaderBackup /dev/nvme1n1 \
--header-backup-file /root/backup/luks-header-pgdata.img
# 2. 核对:备份文件中可见密钥槽与算法信息
sudo cryptsetup luksDump /root/backup/luks-header-pgdata.img
# 3. 恢复演练(在测试卷上执行,确认备份可用,而不是假设可用)
sudo cryptsetup luksHeaderRestore /dev/nvme0n1 \
--header-backup-file /root/backup/luks-header-pgdata.img --batch-mode
sudo cryptsetup luksOpen /dev/nvme0n1 header-test # 用既有口令解锁成功三个管理要求:
- 头备份与密钥同级管理:头文件里没有密钥本体,但持有头备份 + 口令即可解锁,其访问控制与加密存储等级要对齐密钥。
- 恢复演练在隔离环境做:头文件与卷是一一对应的,把 A 卷的头恢复到 B 卷会破坏 B 卷的解锁状态;演练用测试卷或快照克隆。
- ZFS 的对应物不同:ZFS 原生加密没有可单独损坏的「头」,风险形态是包裹密钥丢失(数据集永久不可读),因此它的保险是密钥副本与 change-key 操作记录,而不是头文件备份。
把「头备份存在性 + 最近一次恢复演练日期」加进本页的验收清单,与密钥备份一起成为年度必查项——头备份的意义不在备份动作本身,而在「确认它真的能救回来」。
ZFS 原生加密方案细节
OpenZFS 的加密是数据集属性,粒度、快照继承和密钥层次都比 LUKS 灵活:
- 密钥两级结构:用户密钥(passphrase / raw / hex)包裹数据加密密钥(data encryption key)。
zfs change-key只换包裹密钥,不需要重写数据,密钥轮换成本低;真正的数据密钥更换需要新建数据集并迁移数据。 - 算法:
encryption=on默认aes-256-gcm(带认证的加密,可发现篡改)。 - 粒度:可以按数据集分别加密——
tank/pgdata(业务)与tank/pgdata_archive(归档)用不同密钥;子数据集自动继承。 - 边界:数据集名、大小、元数据结构不受加密保护;且加密数据集与某些 ZFS 特性(如部分 send/receive 场景对密钥的要求)有交互,跨机复制加密数据集要用
zfs send --raw才能保持密文。 - 性能:内核态实现,与 LUKS 同量级,GCM 模式在支持 AES-NI 的硬件上开销很小。
性能实测的方法与参考步骤
各家硬件差异太大,「量级」必须自己测。一套可复现的实测步骤:
# 1. 加密原语本身的吞吐(不涉及文件系统)
cryptsetup benchmark -c aes-xts-plain64
# 2. 加密卷上的 4k 随机读写(fio,队列深度与业务模型对齐)
fio --name=r4k --filename=/dev/mapper/pgdata-crypt --rw=randread \
--bs=4k --iodepth=32 --runtime=60 --time_based --direct=1
fio --name=w4k --filename=/dev/mapper/pgdata-crypt --rw=randwrite \
--bs=4k --iodepth=32 --runtime=60 --time_based --direct=1
# 3. 业务级对照:同一套压测脚本分别在加密卷 / 明文卷上执行
# 对比 TPS、P99 延迟与检查点耗时判读要点:cryptsetup benchmark 给出的是算法上限(AES-NI 机器上通常远超业务需求);真正决定体感的是 4k 随机小 I/O 与 fsync 路径的额外开销,以及加密与 RAID / 网络存储叠加后的表现。实测结论(含硬件型号、内核版本、fio 参数)写进选型文档——一年后有人问「当时为什么敢上」,这就是答案。
云盘加密与托管 TDE
云场景下「自己搞 LUKS」通常不如直接用云盘加密:
- 云盘加密的加解密发生在存储服务层(或宿主机 hypervisor),对 guest OS 透明,性能影响由厂商承担与优化;
- 密钥默认由云 KMS 的服务密钥托管,合规要求高时切换为 BYOK / CMEK(自带/客户管理密钥),密钥的创建、轮换、吊销都在你自己的 KMS 控制面;
- 注意「快照继承加密」与「跨区域复制快照」的密钥行为(复制后的快照用目标区域的密钥),这些细节决定异地容灾链路是否仍然加密;
- 托管数据库(RDS for PostgreSQL、Aurora 等)提供实例级/TDE 级加密开关,创建时必须选上——事后改加密需要重建实例,这是最容易遗漏的一步。
WAL、备份与副本的加密联动
数据目录加密了,但静态加密的「面」远不止数据目录:
- WAL:
pg_wal里的变更记录同样含业务数据。它与数据目录同卷时自动被覆盖;放在独立卷/独立盘时,那一块也要加密。 - WAL 归档目的地:归档到 NFS、S3、OSS 的 WAL 文件落在外面,目的地侧必须加密(桶默认加密 + 传输 TLS,或对归档工具启用仓库加密)。
- 物理备份与快照:
pg_basebackup的输出、卷快照、pgBackRest / WAL-G 的仓库,全部要加密(pgBackRest 用repo*-cipher-type,WAL-G 依赖对象存储加密或客户端加密选项)。 - 逻辑备份:
pg_dump的产物是明文 SQL/定制格式,落盘或传输前要自行加密(gpg、openssl、加密压缩包),或直接落进加密对象存储。 - 副本与备库:流复制把密文块原样复制,备库数据目录也要放在加密卷/加密云盘上——备库被偷走的可能性不比主库低。
最常见的缺口在备份链路
盘加密了、WAL 卷加密了,然后 pg_dump 的产物被随手 scp 到一台未加密的测试机——整条防线的价值瞬间归零。把「备份产物加密」写进备份流程与巡检项,而不是靠自觉。
密钥管理与轮换
密钥管理是 TDE 方案里比加密本身更难的部分:
- 密钥不能和密文放在一起:解锁口令写在数据盘上的脚本里,等于没加密。生产方案是密钥文件放在受保护的独立介质、KMS 下发、网络取钥(如 Vault),并在开机流程中接入(自动解锁带来的可用性收益与「无人值守自动解密」的取舍要明确写进策略)。
- 密钥丢失即数据丢失:密钥的备份与托管本身就是最高级别资产;换密钥前先验证备份密钥可用。
- 轮换成本分两档:换「包裹/用户密钥」(LUKS 换 keyslot 口令、ZFS
change-key)是轻操作;换「数据加密密钥」(LUKS2 在线重加密、ZFS 重建数据集)是重 I/O 事件。策略上按合规要求的周期规划:通常「包裹密钥按需轮换、数据密钥跟随重大事件(疑似泄露、组织调整)」。 - 集中化:KMS(云 KMS、HashiCorp Vault 的 transit/PKI 能力)提供密钥生命周期、审计与权限隔离,比散落在各主机的 keyfile 可管理得多。
开机自动解锁的三种取钥方式
重启后数据卷必须能解锁,服务才能拉起。三种主流取钥方式各有取舍:
| 方式 | 机制 | 优点 | 风险与代价 |
|---|---|---|---|
| 本地密钥文件 | crypttab 指向受保护路径的 keyfile | 简单、无外部依赖 | 密钥与本机共存;文件系统访问控制是唯一防线 |
| TPM 绑定 | 密钥封存于 TPM(如 systemd-cryptenroll 的 TPM2 支持) | 密钥不出芯片,可绑定平台状态 | 主板损坏即密钥不可用;需验证恢复路径 |
| 网络取钥 | 开机时向 KMS / Vault(或 NBDE 体系的 tang/clevis)请求解锁 | 密钥集中治理、可吊销、有审计 | 依赖网络与取钥服务可用性;开机时序更复杂 |
选型建议:单机或边缘环境用 TPM 绑定或本地密钥文件;集群与有 KMS 体系的组织优先网络取钥,把「机器可以丢,密钥不能丢」变成物理事实。无论哪种,都要演练「取钥链路故障时的手工解锁流程」——机房断网 30 分钟时 DBA 拿口令解锁的手册,要在事发前写好。
另外,「先解锁、后起库」的时序依赖要在 systemd 单元依赖或启动脚本里显式表达,避免「库先起、钥未到」的启动失败循环;取钥服务的变更窗口与数据库重启窗口解耦,否则一次取钥侧的例行变更可能变成数据库的意外停机。
介质退役与加密擦除
卷级加密给介质退役流程送上一个礼物:crypto-erase(加密擦除)——不需要擦写整个盘面,只需让密钥不可恢复,剩余密文就成了无意义的随机字节。
- LUKS:
cryptsetup luksErase <设备>销毁全部密钥槽(秒级操作),数据密文仍在盘上但永久不可解。执行前务必确认没有还在使用中的密钥槽备份! - ZFS:销毁数据集与全部快照后,数据加密密钥随数据一起消失;随后再对池做常规处置。
- 云盘:删除云盘与全部快照,并确认跨区域复制产生的快照副本一并删除——BYOK 场景下吊销 KMS 密钥同样能实现加密擦除。
合规上仍要注意:部分行业的介质退役规范要求物理销毁或多次覆写,加密擦除是否被接受以对应规范为准;两者并用(先 luksErase 再送物理销毁)是最稳妥的做法。退役记录(设备序列号、擦除方式、操作人、时间)要归档——它们是数据销毁合规的证据。
TDE 的局限:防不了 SQL 注入与越权
把「基础概念」里的结论展开成对抗场景,帮助团队建立正确预期:
- SQL 注入:注入的 payload 走的是正常 SQL 执行路径,数据在内存中是明文,TDE 无感。防线是参数化查询、最小权限、WAF。
- 越权/内部威胁:拥有 SELECT 权限的账号(或被滥用的 DBA)直接读到明文。防线是权限模型、RLS、列级加密与审计。
- 逻辑漏洞:把加密卷挂到另一台受控机器上(拥有解锁密钥的运维)依然是合法操作路径。防线是密钥的审批与审计。
- 内存侧:
core dump、swap、调试器附加都能带走内存明文。生产基线应禁用/加密 swap、关闭非必要的 core dump。
这些局限决定了 TDE 的验收标准应该是「介质/快照丢失演练通过」,而不是「攻击者拿不到数据」这种不可能的目标。
内存侧旁路的加固基线
「内存侧」不是一句带过的注脚,它有具体的系统配置清单:
# 1. 关闭 swap(物理机常用方案;或改用加密 swap)
sudo swapoff -a
sudo sh -c "sed -i '/ swap / s/^/#/' /etc/fstab"
# 2. systemd 服务单元中禁止 core dump([Service] 段)
# LimitCORE=0
# 3. 收紧内核的 dump 生成面
sudo sysctl -w fs.suid_dumpable=0# /etc/sysctl.d/99-hardening.conf(持久化)
fs.suid_dumpable = 0若确需 core 用于排障:落点指向加密卷上的受控目录,并配套「用完即删」的清理任务与访问审计。这类基线同时服务于 TDE 与列级加密——内存明文旁路对两种方案同等致命。把清单纳入主机加固基线与镜像模板,而不是逐台手工执行。
方案选型速查
详细展开之后,把四类方案的关键属性收进一张表,选型时快速对照:
| 维度 | LUKS2 | ZFS 原生加密 | 云盘加密 | 托管 / 发行版 TDE |
|---|---|---|---|---|
| 加密粒度 | 整卷 | 数据集 | 整盘 | 实例 / 表空间(随产品) |
| 对 PG 透明 | 完全 | 完全 | 完全 | 完全或接近 |
| 密钥轮换 | 包裹密钥轻;数据密钥需重加密 | 包裹密钥轻(change-key) | KMS 轮换(随厂商) | 随产品能力 |
| 运维负担 | 中(自管密钥与开机解锁) | 中(需 ZFS 存储栈) | 低 | 低~中 |
| 典型场景 | 物理机 / 自管虚拟机 | 已用 ZFS 的环境 | 云上自建 | 托管库 / 有商业许可 |
一句话决策:云上别自己造轮子(云盘加密 + KMS);自管环境跟着存储栈走(ext4 生态 → LUKS2,ZFS → 原生加密);有预算与合规硬要求再评估商业 TDE。任何选择都别忘了本页的另一半——覆盖面清单与密钥管理,方案本身只占工作量的一半。
与列级加密的对比定位
| 维度 | 卷级 TDE(LUKS / 云盘 / 发行版) | 列级加密(pgcrypto / 应用侧) |
|---|---|---|
| 粒度 | 整卷 / 整实例 | 单列、单字段 |
| 防的威胁 | 介质丢失、整盘拷贝 | 越权读取(含 DBA 视野外的字段)、日志与备份中的字段暴露 |
| 对应用透明 | 完全透明 | 不透明(密钥管理与加解密逻辑进应用或 SQL) |
| 对查询的影响 | 几乎无 | 等值查询受限(随机加密不可索引),见下一页 |
| 密钥体系 | 卷级,KMS 托管 | 字段/应用级,管理复杂度高 |
| 典型位置 | 基础设施团队负责 | 应用与 DBA 协作 |
两者不互斥:生产上的高敏感系统常常是「卷级 TDE 打底 + 个别 PII 列列级加密 + 权限/RLS/脱敏 + 审计」的组合。选型时先问威胁模型:怕「拿走」就上卷级,怕「看到」就上列级,两个都怕就都上。
静态加密的验收清单
方案落地后,用这份清单验收——全部为「实际执行并留证」的动作,不是口头确认:
□ 主库数据目录所在卷:lsblk 可见 crypt 映射 / zfs get encryption 显示加密算法
□ 全部自定义表空间:逐个核对所在卷的加密状态
□ pg_wal 独立卷(如有):加密状态同上
□ 备库与容灾节点:与主库同等检查
□ WAL 归档目的地:桶默认加密 + TLS-only 策略生效
□ 物理备份仓库:pgBackRest cipher 配置在位,抽一份备份验证可恢复
□ 逻辑备份产物:抽检为密文;明文中间产物已即时清理
□ swap / core dump 基线:sysctl 与 systemd 单元配置核对
□ 密钥管理:KMS 密钥清单、访问审计、备份密钥的恢复演练
□ 介质退役流程:crypto-erase 演练完成,记录模板在位执行频率:上线验收一次,之后并入季度演练(与审计策略的演练节奏对齐)。验收记录与策略文档互相引用,构成「静态加密确实在位」的证据链。
实战示例
示例一:LUKS + PostgreSQL 完整部署
目标:在一台本地物理机上为 PostgreSQL 数据盘启用 LUKS2 静态加密。
# 0. 前提:/dev/nvme1n1 是专用于 PostgreSQL 的数据盘,实例当前未部署
# 1. 创建加密卷(luks2 格式)
sudo cryptsetup luksFormat --type luks2 /dev/nvme1n1
# 按提示输入 YES 与解锁口令
# 2. 生成密钥文件并加入密钥槽(用于开机自动解锁)
sudo sh -c 'head -c 64 /dev/random | base64 > /etc/luks/pg-keyfile'
sudo chmod 600 /etc/luks/pg-keyfile
sudo cryptsetup luksAddKey /dev/nvme1n1 /etc/luks/pg-keyfile
# 3. 打开卷、建文件系统
sudo cryptsetup luksOpen /dev/nvme1n1 pgdata-crypt
sudo mkfs.ext4 /dev/mapper/pgdata-crypt
# 4. 配置开机解锁与挂载
sudo sh -c 'echo "pgdata-crypt /dev/nvme1n1 /etc/luks/pg-keyfile luks,discard" >> /etc/crypttab'
sudo sh -c 'echo "/dev/mapper/pgdata-crypt /var/lib/postgresql ext4 defaults,noatime 0 2" >> /etc/fstab'
sudo mount -a# 5. 初始化 PostgreSQL 数据目录到加密卷
sudo mkdir -p /var/lib/postgresql/18/main
sudo chown -R postgres:postgres /var/lib/postgresql
sudo -u postgres /usr/lib/postgresql/18/bin/initdb -D /var/lib/postgresql/18/main
sudo systemctl start postgresql# 6. 验证:数据目录确实位于加密映射设备上
df -h /var/lib/postgresql
lsblk /dev/nvme1n1
cryptsetup status pgdata-cryptdf: /dev/mapper/pgdata-crypt → 已挂载在加密映射设备上
lsblk: nvme1n1 └─ nvme1n1_crypt (crypt)# 7. 备份 LUKS 头(密钥资产的一部分,单独安全存放)
sudo cryptsetup luksHeaderBackup /dev/nvme1n1 --header-backup-file /root/luks-header-pg.img验证「拿不走」:直接 dd 读取 /dev/nvme1n1 的原始字节,看到的应是密文;把这块盘挂到另一台没有密钥的机器上,blkid 只能看到 LUKS 签名,读不出文件系统。
示例二:ZFS 加密数据集承载 PostgreSQL
# 1. 建池(略)后创建加密数据集
sudo zfs create -o encryption=on -o keyformat=passphrase \
-o mountpoint=/var/lib/postgresql tank/pgdata
# 2. 数据目录初始化与授权
sudo -u postgres initdb -D /var/lib/postgresql/18/main
sudo systemctl start postgresql
# 3. 验证加密状态
zfs get encryption,keystatus tank/pgdataNAME PROPERTY VALUE SOURCE
tank/pgdata encryption aes-256-gcm -
tank/pgdata keystatus available -# 4. 低成本更换包裹密钥(不重写数据)
sudo zfs change-key -o keyformat=passphrase tank/pgdata
# 5. 跨机迁移保持密文:raw send
sudo zfs snapshot tank/pgdata@migrate
sudo zfs send --raw tank/pgdata@migrate | ssh backup-host zfs receive tank/pgdata示例三:备份与 WAL 归档的加密检查清单
# pgBackRest:仓库加密开启后做全量备份
sudo -u postgres pgbackrest --stanza=main --type=full backup
# 校验备份仓库确为密文(在仓库存储侧查看文件特征)
file /var/lib/pgbackrest/backup/main/*/backup_manifest-- WAL 归档是否启用(归档目的地侧的加密另行核对)
SELECT name, setting FROM pg_settings
WHERE name IN ('archive_mode', 'archive_command'); name | setting
----------------+--------------------------------------------------
archive_mode | on
archive_command| test ! -f /pgarchive/%f && cp %p /pgarchive/%f巡检项:归档目录 /pgarchive 若在 NFS/S3 上,目的地桶策略必须开启默认加密与 TLS-only 访问;pg_dump 产物走 gpg --encrypt 后再外传。每一项在备份演练时逐一勾验。
示例四:介质退役的加密擦除演练
场景:一台退役的数据库服务器,本地 NVMe 盘承载过加密的 PostgreSQL 数据目录,按流程执行 crypto-erase 并留证。
# 1. 停实例、卸载、关闭加密映射
sudo systemctl stop postgresql
sudo umount /var/lib/postgresql
sudo cryptsetup close pgdata-crypt
# 2. 销毁全部密钥槽(不可逆!确认无仍在使用的共享密钥)
sudo cryptsetup luksErase /dev/nvme1n1
# 3. 验证:任何已知口令/密钥文件都无法解锁
sudo cryptsetup luksOpen /dev/nvme1n1 verify-slot # 应报错:无可用密钥槽# 退役记录(归档到资产管理台账)
设备序列号: S4XXNX0R123456
擦除方式: LUKS2 crypto-erase(luksErase,密钥槽全部销毁)
验证方式: 解锁尝试失败;无密钥状态下文件系统不可识别
操作人: dba_ops / 复核: sec_oncall
时间: 2026-10-07 15:30 CST两个必须交代的边界:其一,luksErase 之后所有密钥槽(口令槽与密钥文件槽)全部失效,若这台机器上还有其他加密卷共用同一密钥,先解耦再擦除;其二,密钥文件副本(含备份的 LUKS 头)要同步销毁——密文与密钥必须同时退场,只擦一半等于没擦。若行业规范要求物理销毁,crypto-erase 作为前置步骤仍然有价值:擦除先行、销毁随后,双保险。
示例五:pg_dump 产物的 gpg 加密外传
「最常见缺口在备份链路」的具体解法——逻辑备份产物离开主机前的标准加密动作:
# 1. 备份并加密(收件人是数据团队在 gpg 中登记的公钥)
sudo -u postgres pg_dump -Fc appdb > /tmp/appdb-$(date +%F).dump
gpg --batch --yes --encrypt -r dba-team@example.com \
--output /tmp/appdb-$(date +%F).dump.gpg /tmp/appdb-$(date +%F).dump
# 2. 立即删除明文中间产物
shred -u /tmp/appdb-$(date +%F).dump
# 3. 外传(TLS 通道的对象存储 / scp),本地只留密文待清理# 4. 目标端解密还原(持有对应私钥的机器)
gpg --decrypt /tmp/appdb-2026-10-07.dump.gpg > /tmp/appdb.dump
pg_restore -d appdb /tmp/appdb.dump要点:公钥加密让备份机不需要持有解密能力(私钥只在还原侧);--batch --yes 让动作可脚本化;明文中间产物即时清除。把这套动作封装进备份脚本,而不是做成「记得手动做」的口头流程——审计章节的原则在这里同样成立:靠流程文档不如靠脚本默认。
注意事项
社区版没有内置 TDE,不要等也不要编。 任何「PG18 某参数打开即加密」的说法都不成立;规划静态加密时直接从卷级/云层/发行版方案中选型。
密钥与密文同盘等于没加密。 解锁口令写在与数据同卷的文件、密钥文件放进被加密目录的子目录,是最常见的自欺配置。密钥必须独立保管并纳入密钥管理制度。
实例运行中数据在内存是明文。 TDE 的验收目标是介质与快照场景;SQL 层的越权与注入需要权限、RLS、脱敏、审计组合应对,不要把安全预期押在 TDE 上。
备份与归档是静态加密最常漏掉的面。 WAL 归档目的地、pg_dump 产物、逻辑订阅下游、测试环境恢复副本,任何一处明文落盘都会抵消主库加密的全部价值。
备库与容灾节点的卷也要加密。 流复制传输的是 WAL 字节流(受传输加密保护),但备库落盘后就是明文态数据目录——备库所在卷的加密等级必须与主库一致。
换数据加密密钥是重 I/O 事件。 LUKS2 在线重加密与 ZFS 数据集迁移都要按 TB 级 I/O 预算窗口;策略上区分「包裹密钥轻轮换」与「数据密钥重轮换」两种操作。
性能损耗用实测说话。 AES-NI 时代卷级加密的损耗通常可接受,但低配虚拟机、高随机小 I/O 场景下可能有可感知影响;上生产前用 cryptsetup benchmark 与业务模型压测。
云盘加密在创建时就要开启。 绝大多数云的云盘/实例加密不支持事后原地开启,需要重建实例并迁移数据——把它放进实例初始化基线,而不是上线后补救项。
swap 与 core dump 是旁路。 加密卷之外的交换分区、可写的 core dump 目录都能带走内存明文;生产基线关闭或加密 swap、收紧 core dump 路径。
自定义表空间是最常见的覆盖缺口。 CREATE TABLESPACE 的目录常在数据盘之外的独立卷上,「加密了 PGDATA」对它无效;每次新增表空间都要回答「它落在哪个卷、那个卷加密了吗」。
加密不是备份,备份不是加密。 卷加密防不了误删与逻辑损坏(rm、DROP TABLE、坏块照样穿透加密层);备份策略与静态加密是两件必须同时存在的事,不要因为「盘已加密」就放松备份演练。
快照链与加密继承要逐一验证。 快照是否继承加密、跨区域复制后的快照用哪把密钥、还原出来的卷是否仍加密——这些行为随云厂商与存储栈不同,用一次实际还原验证,而不是看控制台的状态图标。
头备份不是密钥替代品。 头文件 + 口令二者缺一不可,只有头没有口令(或相反)都等于没有保险;两者的存放位置、访问审计、恢复演练要分别管理。
宿主级快照回滚可能损坏加密卷上的数据库。 卷内快照(LUKS 之上)与宿主快照(LUKS 之下)语义不同:宿主快照回滚会把整个卷状态倒回,对运行中的 PostgreSQL 是数据损坏风险——用卷内快照,或在平台层明确禁止对数据库宿主做快照回滚。
介质退役时要同步销毁头备份与密钥副本。 crypto-erase 销毁了盘上的密钥槽,但如果 LUKS 头备份与密钥副本还躺在运维工作站上,介质退役就没有真正完成。
加密集群的扩容要检查新节点的加密状态。 新增备库、迁移节点、临时扩容的实例,若初始化流程跳过加密步骤,集群里就多了一个明文副本——把「卷加密」设为节点初始化的不可跳过项。
raw 流与非 raw 流不能在同一增量链上混用。 ZFS 的 zfs send --raw 增量链要求全程 raw,混用普通流会因加密元数据不匹配而失败;备份链路的 send 类型要固定并写入文档。
介质的加密状态要进资产台账。 「这块盘是否加密过」是退役与故障处置时要回答的第一个问题;台账缺这一列,crypto-erase 流程就无从谈起。
最佳实践
- 先写威胁模型再选方案。 防介质丢失/整盘拷贝 → 卷级方案;防越权看到字段 → 列级加密;两者并列使用,互不替代。
- 云上优先用云盘加密与托管 TDE。 零运维、密钥由 KMS 托管、合规材料现成;自建 LUKS 留给物理机与非托管环境。
- 自管环境按存储栈选 LUKS2 或 ZFS 加密。 用 ext4/LVM 生态选 LUKS2;已在用 ZFS 的直接用数据集级原生加密,享受轻量密钥轮换与 raw send。
- 把「备份/归档/副本全部加密」写成流程并演练验证。 每次备份演练逐项核对仓库密文特征、归档桶加密策略、备库卷加密状态。
- 密钥集中托管(KMS / Vault)并独立备份。 密钥的创建、下发、轮换、吊销有记录有审批;LUKS 头与密钥文件是最高级别资产,丢失即数据报废。
- 区分包裹密钥与数据密钥的轮换节奏。 包裹密钥(口令/keyslot)按需轻量轮换;数据密钥重轮换规划维护窗口,避免临时起意。
- 开机自动解锁的取舍写进文档。 无人值守重启的可用性收益 vs. 密钥在线的风险;至少保证密钥来源(keyfile/KMS)有独立的访问审计。
- 生产基线同步处理 swap 与 core dump。 禁用或加密 swap、core dump 落点为空/受控目录,堵住内存明文的旁路。
- 上线前做性能实测。
cryptsetup benchmark+ 业务级压测,把卷级加密的损耗量化后纳入容量规划,而不是拍脑袋。 - 把静态加密纳入合规证据链。 加密状态巡检(卷/快照/备份三处)、密钥管理审计记录,与审计策略文档一起构成合规答复的支撑材料。
- 高危字段仍然考虑列级加密。 即使上了卷级 TDE,对身份证号、支付凭证这类「怕 DBA 与日志」的字段,pgcrypto/应用侧加密仍然值得,见下一页。
- 定期做「介质丢失」演练。 模拟拿走一块盘/一份快照,确认无密钥状态下恢复不出数据——这是 TDE 唯一有效的验收方式。
下一节
继续学习:列级加密(pgcrypto)
参考链接
- PostgreSQL 官方文档:Database File Layout(静态加密的作用对象)
- PostgreSQL 官方文档:Reliability and the Write-Ahead Log
- PostgreSQL 官方文档:Backup and Restore
- PostgreSQL 官方文档:Continuous Archiving and Point-in-Time Recovery(WAL 归档)
- cryptsetup / LUKS 官方仓库
- OpenZFS 官方文档(ZFS 原生加密)
- Percona pg_tde:开源 TDE 扩展
- EDB Postgres 文档中心(发行版 TDE 能力)
- pgBackRest 官方文档(仓库加密)