Skip to content

Latest commit

 

History

History
105 lines (62 loc) · 7.06 KB

20240308_01.md

File metadata and controls

105 lines (62 loc) · 7.06 KB

DB吐槽大会,第98期 - 文件删了不报错还能启动、能查询! PG有隐患

作者

digoal

日期

2024-03-08

标签

PostgreSQL , PolarDB , DuckDB , 备份 , 恢复 , 数据文件 , 隐患


背景

挖个坑

今天是女神节, 分析一下老白一篇关于PG数据文件被删除还能使用的文章:
https://mp.weixin.qq.com/s/4wX0KOiRXq0JJRLoSmgxhA

对于老白提到的场景, 原因是这样的:

  • 1 数据库启动状态下删了一个文件,只要未来有该文件的IO都会报错并导致停库,例如bgwriter或checkpoint写脏页,或者未命中buffer的读请求,理论上不可能没有察觉。
    • 但是存在一种情况, 删除的文件没有被修改过(没有被DML过、也没有被vacuum过、也没有修改过hint bit). 那目前来看没法被数据库主动察觉.
  • 2 如果数据库是正常重启肯定会刷脏,所以也应该会报错并发现问题。
    • 情况如2.
  • 3 immediate重启则会涉及恢复,开启了fpw是可以完全恢复的。这个问题可能和部分表空间恢复的场景类似,一个没有备份的表空间,在进行部分恢复时,元数据没有被剥开,整体恢复可以正常,但是由于原数据在系统表里,也就没备份数据但是备份了元数据,查询到没备份的表空间的表时才会报错。启动时检查元数据和数据文件的完整性可能可以作为一个功能选项更好。

情况与我之前写的部分表空间备份与恢复方法的文章类似:

  • 备份global文件(主要包含全局信息: 用户、数据库、表空间定义, schema定义, 控制文件等.) + wal归档 + 指定表空间文件.

详细参考:

部分表空间备份与恢复其实有一个比较诡异的现象:

  • 在回放wal时, 由于有FPW, 未备份的表空间的数据文件也能正常的回放. 被修改过的块都能恢复出来.
  • 但是没有被修改过的块呢? 在file中是空洞的存在, 取决于file write接口, offset write?

导致的问题: 当我们读未备份的表, 数据就有可能是不准的. 整个逻辑是这样的:

  • 没有备份的表空间其实是不完整的.
  • 但全局数据是完整的, 没有备份的表空间对应的数据结构还在, 例如某个表a.
  • 如果我们去查a表, 它的数据当然可能是不准确的.

回过头来看老白的文章里提到的场景, 和上面部分表空间备份与恢复的情况类似.

  • PG规定了单个数据文件的大小上限, 当数据超过一个数据文件大小, 会写append文件, 文件suffix按数字编号, 如17889, 17889.1, 17889.2 .
  • 我们删除一个在最近一次检查点之后没有被DML过、也没有被vacuum过、也没有修改过hint bit的数据文件的append文件.
  • 如果进行count操作, 是发现不了的.
  • 但是索引查询引用到被删除的文件内的坏块时可以被发现.

填坑

1、发现坏块: 检查坏块, 开启checksum, 有现成的工具. pg_checksums
2、发现文件被删: 检查元数据和数据文件映射的完备性. 这个通常可以启动时检查, 目前社区没有该功能, 需要提patch. 或者运行过程中检查, 发现完备性问题, 应该要有报错.

pg_class   
pg_relation_filenode   
pg_relation_filepath   
pg_filenode.map   

再挖坑

Oracle, 默认未开启fpw. 也有比较难发现的隐藏风险.

如果服务器崩溃, 可能出现partial write的块. 这种数据块也是极难发现, 只有读到时通过块的checksum才会校验出错误. 恢复则需要用到过去的全量备份和redo. 如果你的备份保留时间不够长, 当你发现时可能备份已经被清理了, 那就恢复不回来了.

黑盒产品(如O)自发觉的功能有可行基础,因为你不知道它怎么组织数据的, 你无法模拟它的行为来修改内容.

开源的产品, 理论上都可以模拟符合数据库逻辑的操作行为。开源产品要么使用加密数据文件,没有得到密钥无法模拟,被篡改可以被发现。不过发现篡改的成本比较高,增加了额外的IO和计算。

操作系统被黑之后, 可以写点“坏tuple”进去,checksum也可以刷新,非常逼真。通过checksum就无法发现被篡改.

老白说oracle段页式这方面好很多,BMB里有所有extent的信息bnb坏了也会报错. 实际上即使你把checksum放宽到page+extent+file级别, 都是可以被修改的, 只要代码公开, 攻击者就可以根据你设置的checksum位置去进行篡改, 而且对于产品来说, checksum越多性能肯定越差.

所以开源产品要根本上解决这个场景存在的问题, 至少要上TDE(透明加密) 以及 自发觉。 就是结合客户端给的密钥进行加密, 校验时也需要这个密钥, 所以一旦被篡改就会被发现. 自发觉则需要主动发现问题及报错.

好消息, PolarDB for PostgreSQL开源版本支持了TDE.

digoal's wechat