Skip to content

透明数据加密(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 托管密钥)存储服务层完全透明
云托管 TDERDS for PostgreSQL 的 TDE、Aurora 存储加密等托管服务内建完全透明(能力随厂商)
发行版 / 扩展EDB 的 TDE、Percona 的 pg_tde 扩展等数据库内 / 存储层透明或半透明,随产品
列级加密pgcrypto、应用侧加密字段层不透明(应用必须感知)

注意最后一行——列级加密不属于 TDE,它是另一种取舍(防「看到」而非防「拿走」),下一页专门展开。本页聚焦前四行的「落盘即密文」方案。

关于 PG18

PostgreSQL 18 没有内置 TDE,也没有「实例级加密开关」。网络上流传的「PG18 TDE 参数」均不可信;在内核引入完整的密钥管理框架之前,社区版用户的静态加密方案就是上表中的外部方案。

语法与用法 ​

LUKS(Linux 统一密钥设置)方案 ​

LUKS 是 Linux 上标准的块设备加密方案(底层是 dm-crypt),用法是先把物理卷加密、再在映射设备上建文件系统:

bash
# 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 配置(生产环境用密钥文件或网络取钥,见后文密钥管理):

ini
# /etc/crypttab
pgdata-crypt  /dev/nvme1n1  /etc/luks/pg-keyfile  luks,discard

ZFS 原生加密方案 ​

OpenZFS 在数据集(dataset)级别提供原生加密,粒度比 LUKS 更细,且支持逐数据集密钥:

bash
# 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 完全透明:

text
AWS:    EBS encryption(可指定 customer managed key in KMS)
阿里云:  云盘加密(服务密钥或 BYOK 自带密钥)
GCP:    Persistent Disk encryption(CMEK)

创建云盘时选择加密并指定密钥,随后在盘上照常安装 PostgreSQL,无任何额外配置。

备份的加密联动 ​

静态加密不能只覆盖主库数据目录,备份与 WAL 归档离开本机时必须自带加密:

ini
# pgBackRest 配置片段:仓库端 AES-256 加密
repo1-cipher-type = aes-256-cbc
repo1-cipher-pass = <由密钥管理服务下发的口令>
bash
# 带加密仓库做一次全量备份
sudo -u postgres pgbackrest --stanza=main --type=full backup

详细说明 ​

防线定位:整盘拷贝与介质回收 ​

把 TDE 类方案放进攻防视角理解它的边界。攻击路径「偷走整块盘 / 整台机 / 整个快照」的共同特征是:攻击者拿到的是关闭状态或复制品,没有运行中的数据库进程,也没有密钥。卷级加密让这类路径的成本从「直接读数据」变成「需要先攻破密钥体系(KMS、口令、TPM)」——这是 TDE 的全部价值,也是云厂商合规问卷里「静态加密」一项的技术实质。

反过来,攻击路径「用合法连接把数据读出来」与卷级加密正交。这就是为什么 TDE 与本阶段其他内容是并列关系而不是替代关系:SSL 管传输、权限/RLS/脱敏管「看到」、TDE 管「拿走」、审计管「追溯」。

加密覆盖面:一张清单管全部数据面 ​

「数据目录加密了」不等于「静态加密就位」。PostgreSQL 的落盘数据散布在多个位置,任何一处遗漏都会让防线出现直通缺口。按这张清单逐项核对:

数据面位置是否随数据目录自动覆盖加密手段
基表与索引PGDATA/base/是(同卷时)卷级加密
自定义表空间PGDATA/pg_tblspc/ 指向的外部目录否——目录可在任何挂载点外部目录所在卷单独加密
WALpg_wal/是(同卷时);独立卷须单独加密卷级加密
临时文件排序 / 物化的临时段是(默认同卷);temp_tablespaces 指向独立表空间时例外卷级加密
WAL 归档archive_command 的目的地否目的地侧加密(桶加密 / 仓库加密)
物理备份pg_basebackup / pgBackRest / 快照否仓库加密 / 快照所在存储加密
逻辑备份pg_dump 产物否gpg 等应用层加密后外传
备库 / 容灾节点各自的数据目录与 WAL否(是另一台机器的另一套盘)与主库同等加密等级
内存侧旁路swap 分区、core dump 目录否禁用 / 加密 swap、收紧 core dump

最容易漏的两格:自定义表空间——CREATE TABLESPACE 的目录经常被规划在数据盘之外的独立卷上,「加密了数据盘」对它完全无效;备库——流复制落盘后就是完整的数据副本,备库卷的加密等级必须与主库对齐。

社区版的可选方案版图 ​

选型决策树大致如下:

  1. 云上托管(RDS / Aurora / Cloud SQL 等)→ 直接用厂商的托管加密/TDE 能力,通常一个开关 + KMS 密钥选择,成本最低,合规材料最全。对应能力以各厂商文档为准。
  2. 云上自建(ECS/EC2 + 云盘) → 云盘加密(KMS 托管密钥),零运维、透明。
  3. 物理机 / 自管虚拟机 / 本地私有化 → LUKS(块设备,粒度粗、生态成熟)或 ZFS 原生加密(数据集级、支持校验与快照),按现有存储栈选择。
  4. 有发行版采购许可 / 能接受第三方扩展 → 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 值得展开成一节,因为它是最便宜、也最容易被省略的保险:

bash
# 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 的硬件上开销很小。

性能实测的方法与参考步骤 ​

各家硬件差异太大,「量级」必须自己测。一套可复现的实测步骤:

bash
# 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 的验收标准应该是「介质/快照丢失演练通过」,而不是「攻击者拿不到数据」这种不可能的目标。

内存侧旁路的加固基线 ​

「内存侧」不是一句带过的注脚,它有具体的系统配置清单:

bash
# 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
ini
# /etc/sysctl.d/99-hardening.conf(持久化)
fs.suid_dumpable = 0

若确需 core 用于排障:落点指向加密卷上的受控目录,并配套「用完即删」的清理任务与访问审计。这类基线同时服务于 TDE 与列级加密——内存明文旁路对两种方案同等致命。把清单纳入主机加固基线与镜像模板,而不是逐台手工执行。

方案选型速查 ​

详细展开之后,把四类方案的关键属性收进一张表,选型时快速对照:

维度LUKS2ZFS 原生加密云盘加密托管 / 发行版 TDE
加密粒度整卷数据集整盘实例 / 表空间(随产品)
对 PG 透明完全完全完全完全或接近
密钥轮换包裹密钥轻;数据密钥需重加密包裹密钥轻(change-key)KMS 轮换(随厂商)随产品能力
运维负担中(自管密钥与开机解锁)中(需 ZFS 存储栈)低低~中
典型场景物理机 / 自管虚拟机已用 ZFS 的环境云上自建托管库 / 有商业许可

一句话决策:云上别自己造轮子(云盘加密 + KMS);自管环境跟着存储栈走(ext4 生态 → LUKS2,ZFS → 原生加密);有预算与合规硬要求再评估商业 TDE。任何选择都别忘了本页的另一半——覆盖面清单与密钥管理,方案本身只占工作量的一半。

与列级加密的对比定位 ​

维度卷级 TDE(LUKS / 云盘 / 发行版)列级加密(pgcrypto / 应用侧)
粒度整卷 / 整实例单列、单字段
防的威胁介质丢失、整盘拷贝越权读取(含 DBA 视野外的字段)、日志与备份中的字段暴露
对应用透明完全透明不透明(密钥管理与加解密逻辑进应用或 SQL)
对查询的影响几乎无等值查询受限(随机加密不可索引),见下一页
密钥体系卷级,KMS 托管字段/应用级,管理复杂度高
典型位置基础设施团队负责应用与 DBA 协作

两者不互斥:生产上的高敏感系统常常是「卷级 TDE 打底 + 个别 PII 列列级加密 + 权限/RLS/脱敏 + 审计」的组合。选型时先问威胁模型:怕「拿走」就上卷级,怕「看到」就上列级,两个都怕就都上。

静态加密的验收清单 ​

方案落地后,用这份清单验收——全部为「实际执行并留证」的动作,不是口头确认:

text
□ 主库数据目录所在卷:lsblk 可见 crypt 映射 / zfs get encryption 显示加密算法
□ 全部自定义表空间:逐个核对所在卷的加密状态
□ pg_wal 独立卷(如有):加密状态同上
□ 备库与容灾节点:与主库同等检查
□ WAL 归档目的地:桶默认加密 + TLS-only 策略生效
□ 物理备份仓库:pgBackRest cipher 配置在位,抽一份备份验证可恢复
□ 逻辑备份产物:抽检为密文;明文中间产物已即时清理
□ swap / core dump 基线:sysctl 与 systemd 单元配置核对
□ 密钥管理:KMS 密钥清单、访问审计、备份密钥的恢复演练
□ 介质退役流程:crypto-erase 演练完成,记录模板在位

执行频率:上线验收一次,之后并入季度演练(与审计策略的演练节奏对齐)。验收记录与策略文档互相引用,构成「静态加密确实在位」的证据链。

实战示例 ​

示例一:LUKS + PostgreSQL 完整部署 ​

目标:在一台本地物理机上为 PostgreSQL 数据盘启用 LUKS2 静态加密。

bash
# 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
bash
# 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
bash
# 6. 验证:数据目录确实位于加密映射设备上
df -h /var/lib/postgresql
lsblk /dev/nvme1n1
cryptsetup status pgdata-crypt
text
df: /dev/mapper/pgdata-crypt  → 已挂载在加密映射设备上
lsblk: nvme1n1 └─ nvme1n1_crypt (crypt)
bash
# 7. 备份 LUKS 头(密钥资产的一部分,单独安全存放)
sudo cryptsetup luksHeaderBackup /dev/nvme1n1 --header-backup-file /root/luks-header-pg.img

验证「拿不走」:直接 dd 读取 /dev/nvme1n1 的原始字节,看到的应是密文;把这块盘挂到另一台没有密钥的机器上,blkid 只能看到 LUKS 签名,读不出文件系统。

示例二:ZFS 加密数据集承载 PostgreSQL ​

bash
# 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/pgdata
text
NAME         PROPERTY     VALUE        SOURCE
tank/pgdata  encryption   aes-256-gcm  -
tank/pgdata  keystatus    available    -
bash
# 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 归档的加密检查清单 ​

bash
# pgBackRest:仓库加密开启后做全量备份
sudo -u postgres pgbackrest --stanza=main --type=full backup

# 校验备份仓库确为密文(在仓库存储侧查看文件特征)
file /var/lib/pgbackrest/backup/main/*/backup_manifest
sql
-- WAL 归档是否启用(归档目的地侧的加密另行核对)
SELECT name, setting FROM pg_settings
WHERE name IN ('archive_mode', 'archive_command');
text
     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 并留证。

bash
# 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   # 应报错:无可用密钥槽
text
# 退役记录(归档到资产管理台账)
设备序列号: S4XXNX0R123456
擦除方式:   LUKS2 crypto-erase(luksErase,密钥槽全部销毁)
验证方式:   解锁尝试失败;无密钥状态下文件系统不可识别
操作人:     dba_ops / 复核: sec_oncall
时间:       2026-10-07 15:30 CST

两个必须交代的边界:其一,luksErase 之后所有密钥槽(口令槽与密钥文件槽)全部失效,若这台机器上还有其他加密卷共用同一密钥,先解耦再擦除;其二,密钥文件副本(含备份的 LUKS 头)要同步销毁——密文与密钥必须同时退场,只擦一半等于没擦。若行业规范要求物理销毁,crypto-erase 作为前置步骤仍然有价值:擦除先行、销毁随后,双保险。

示例五:pg_dump 产物的 gpg 加密外传 ​

「最常见缺口在备份链路」的具体解法——逻辑备份产物离开主机前的标准加密动作:

bash
# 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),本地只留密文待清理
bash
# 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 流程就无从谈起。

最佳实践 ​

  1. 先写威胁模型再选方案。 防介质丢失/整盘拷贝 → 卷级方案;防越权看到字段 → 列级加密;两者并列使用,互不替代。
  2. 云上优先用云盘加密与托管 TDE。 零运维、密钥由 KMS 托管、合规材料现成;自建 LUKS 留给物理机与非托管环境。
  3. 自管环境按存储栈选 LUKS2 或 ZFS 加密。 用 ext4/LVM 生态选 LUKS2;已在用 ZFS 的直接用数据集级原生加密,享受轻量密钥轮换与 raw send。
  4. 把「备份/归档/副本全部加密」写成流程并演练验证。 每次备份演练逐项核对仓库密文特征、归档桶加密策略、备库卷加密状态。
  5. 密钥集中托管(KMS / Vault)并独立备份。 密钥的创建、下发、轮换、吊销有记录有审批;LUKS 头与密钥文件是最高级别资产,丢失即数据报废。
  6. 区分包裹密钥与数据密钥的轮换节奏。 包裹密钥(口令/keyslot)按需轻量轮换;数据密钥重轮换规划维护窗口,避免临时起意。
  7. 开机自动解锁的取舍写进文档。 无人值守重启的可用性收益 vs. 密钥在线的风险;至少保证密钥来源(keyfile/KMS)有独立的访问审计。
  8. 生产基线同步处理 swap 与 core dump。 禁用或加密 swap、core dump 落点为空/受控目录,堵住内存明文的旁路。
  9. 上线前做性能实测。 cryptsetup benchmark + 业务级压测,把卷级加密的损耗量化后纳入容量规划,而不是拍脑袋。
  10. 把静态加密纳入合规证据链。 加密状态巡检(卷/快照/备份三处)、密钥管理审计记录,与审计策略文档一起构成合规答复的支撑材料。
  11. 高危字段仍然考虑列级加密。 即使上了卷级 TDE,对身份证号、支付凭证这类「怕 DBA 与日志」的字段,pgcrypto/应用侧加密仍然值得,见下一页。
  12. 定期做「介质丢失」演练。 模拟拿走一块盘/一份快照,确认无密钥状态下恢复不出数据——这是 TDE 唯一有效的验收方式。

下一节

继续学习:列级加密(pgcrypto)

参考链接 ​