openGauss源码解析(202)
openGauss源码解析:安全管理源码解析(13)
9.5 审计与追踪
审计机制和审计追踪机制能够对用户的日常行为进行记录和分析,实现规避风险、提高安全性。
9.5.1 审计日志设计
审计内容的记录方式通常有两种:记录到数据库的表中、记录到OS文件中。openGauss采用记录到OS文件中(即审计日志)的方式来保存审计结果,审计日志文件夹受操作系统权限保护,默认只有初始化用户可以读写,从数据库安全角度出发,保证了审计结果的可靠性。日志文件的存储目录由audit_directory参数指定。
openGauss审计日志每条记录包括time、type、result、userid、username、database、client_conninfo、object_name、detail_info、node_name、thread_id、local_port、remote_port共13个字段。图9-23为审计日志的单条记录示例。
图9-23 审计记录示例
对审计日志文件进行读写的函数的代码主要位于“pgaudit.cpp”文件中,其中主要包括两类函数:审计文件的读、写、更新函数;审计记录的增、删、查接口。
首先介绍审计文件的数据结构,如图9-24所示。
openGauss的审计日志采用文件的方式存储在指定目录中。通过查看目录,可以发现日志主要包括两类文件:形如0_adt的审计文件以及名为index_table索引文件。
图9-24 审计文件结构
以adt结尾的审计文件中,每一条审计记录对应一个AuditData结构体。数据结构AuditData代码如下:
typedef struct AuditData {
AuditMsgHdr header; /* 记录文件头,存储记录的标识、大小等信息 */
AuditType type; /* 审计类型 */
AuditResult result; /* 执行结果 */
char varstr[1]; /* 二进制格式存储的具体审计信息 */
} AuditData;
其中AuditMsgHdr记录着审计记录的标识信息,数据结构AuditMsgHdr的代码如下:
typedef struct AuditMsgHdr {
char signature[2]; /* 审计记录标识,目前固定为AUDIT前两个字符’A’和’U’ */
uint16 version; /* 版本信息,目前固定为0 */
uint16 fields; /* 审计记录字段数,目前为13 */
uint16 flags; /* 记录有效性标识,如果被删除则标记为DEAD */
pg_time_t time; /* 审计记录创建时间 */
uint32 size; /* 审计信息占字节长度 */
} AuditMsgHdr;
AuditData的其他结构存储着审计记录的审计信息,AuditType为审计类型,目前有38种类型。AuditResult为执行的结果,有AUDIT_UNKNOWN、AUDIT_OK、AUDIT_FAILED三种结果。其余的各项信息,均通过二进制的方式写入到varstr中。
审计日志有关的另一个文件为索引文件index_table,其中记录着审计文件的数量、审计日志文件编号、审计文件修改日期等信息。其数据结构AuditIndexTable代码如下:
typedef struct AuditIndexTable {
uint32 maxnum; /* 审计目录下审计文件个数的最大值 */
uint32 begidx; /* 审计文件开始编号 */
uint32 curidx; /* 当前使用的审计文件编号 */
uint32 count; /* 当前审计文件的总数 */
pg_time_t last_audit_time; /* 最后一次写入审计记录的时间 */
AuditIndexItem data[1]; /* 审计文件指针 */
} AuditIndexTable;
索引文件中每一个AuditIndexItem对应一个审计文件,其数据结构AuditIndexItem的代码如下:
typedef struct AuditIndexItem {
pg_time_t ctime; /* 审计文件创建时间 */
uint32 filenum; /* 审计文件编号 */
uint32 filesize; /* 审计文件占空间大小 */
} AuditIndexItem;
审计文件的读、写类函数如auditfile_open、auditfile_rotate等函数实现较简单,读者可以直接阅读源码。
下面主要介绍日志文件的结构和日志记录的增、删、查接口。
审计记录的写入接口为audit_report函数。该函数的原型为:
void audit_report(AuditType type, AuditResult result, const char* object_name, const char* detail_info);
其中入参type、result、object_name、detail_info分别对应审计日志记录中的相应字段,审计日志中的其余9个字段均为函数在执行时从全局变量中获取。
audit_report函数的执行主要分为3个部分,首先会检查审计的各项开关,判断是否需要审计该操作;然后根据传入的参数、全局变量中的参数以及当前时间,生成审计日志所需的信息并拼接成字符串;最后调用审计日志文件读写接口,将审计日志写入文件中。
审计记录查询接口为pg_query_audit函数,该函数为数据库内置函数,可供用户直接调用,调用形式为:
SELECT * FROM pg_query_audit (timestamptz startime,timestamptz endtime, audit_log);
入参为需要查询审计记录的起始时间和终止时间以及审计日志文件所在的物理路径。当不指定audit_log时,默认查看连接当前实例的审计日志信息。
审计记录的删除接口为pg_delete_audit函数,该函数为数据库内置函数,可供用户直接调用,调用形式为:
SELECT * FROM pg_delete_audit (timestamptz startime,timestamptz endtime);
入参为需要被删除审计记录的起始时间和终止时间。该函数通过调用pgaudit_delete_file函数来将审计日志文件中,startime与endtime之间的审计记录标记为AUDIT_TUPLE_DEAD,达到删除审计日志的效果,而不实际删除审计记录的物理数据。带来的效果是执行该函数审计日志文件大小不会减小。