本节介绍如何使用
plugin/audit_null
MySQL 源代码分发目录中的示例插件编写服务器端审计插件。该audit_null.c
目录中的源文件实现了一个名为NULL_AUDIT
.
在服务器内部,可插入审计接口在MySQL 源代码分发目录中sql_audit.h
的
sql_audit.cc
文件中
实现。sql
此外,当可审计事件发生时,服务器中的几个地方会调用审计接口,以便在必要时通知已注册的审计插件有关该事件的信息。要查看此类调用发生的位置,请搜索服务器源文件以查找名称为
. 服务器操作会出现审核通知,例如:
mysql_audit_
xxx
()
客户端连接和断开连接事件
将消息写入一般查询日志(如果启用了日志)
将消息写入错误日志
向客户端发送查询结果
要编写审计插件,请在插件源文件中包含以下头文件。根据插件功能和要求,可能还需要其他 MySQL 或通用头文件。
Press CTRL+C to copy#include <mysql/plugin_audit.h>
plugin_audit.h
includes
plugin.h
,因此您无需明确包含后一个文件。plugin.h
定义MYSQL_AUDIT_PLUGIN
服务器插件类型和声明插件所需的数据结构。
plugin_audit.h
定义特定于审计插件的数据结构。
与任何 MySQL 服务器插件一样,审计插件具有通用插件描述符(请参阅
第 4.4.2.1 节,“服务器插件库和插件描述符”)和特定于类型的插件描述符。在
audit_null.c
中, 的一般描述符
audit_null
如下所示:
Press CTRL+C to copymysql_declare_plugin(audit_null) { MYSQL_AUDIT_PLUGIN, /* type */ &audit_null_descriptor, /* descriptor */ "NULL_AUDIT", /* name */ "Oracle Corp", /* author */ "Simple NULL Audit", /* description */ PLUGIN_LICENSE_GPL, audit_null_plugin_init, /* init function (when loaded) */ audit_null_plugin_deinit, /* deinit function (when unloaded) */ 0x0003, /* version */ simple_status, /* status variables */ NULL, /* system variables */ NULL, 0, } mysql_declare_plugin_end;
第一个成员 ,MYSQL_AUDIT_PLUGIN
将此插件标识为审计插件。
audit_null_descriptor
指向特定于类型的插件描述符,稍后描述。
name
成员 ( ) 表示在or
NULL_AUDIT
等语句中用于引用插件的
名称。这也是
或
显示的名称。
INSTALL PLUGIN
UNINSTALL PLUGIN
INFORMATION_SCHEMA.PLUGINS
SHOW PLUGINS
通用插件描述符也指
simple_status
,一个将几个状态变量暴露给SHOW
STATUS
语句的结构:
Press CTRL+C to copystatic struct st_mysql_show_var simple_status[]= { { "Audit_null_called", (char *) &number_of_calls, SHOW_INT }, { "Audit_null_general_log", (char *) &number_of_calls_general_log, SHOW_INT }, { "Audit_null_general_error", (char *) &number_of_calls_general_error, SHOW_INT }, { "Audit_null_general_result", (char *) &number_of_calls_general_result, SHOW_INT }, { "Audit_null_general_status", (char *) &number_of_calls_general_status, SHOW_INT }, { "Audit_null_connection_connect", (char *) &number_of_calls_connection_connect, SHOW_INT }, { "Audit_null_connection_disconnect", (char *) &number_of_calls_connection_disconnect, SHOW_INT }, { "Audit_null_connection_change_user", (char *) &number_of_calls_connection_change_user, SHOW_INT }, { 0, 0, 0} };
加载插件时,audit_null_plugin_init
初始化函数将状态变量设置为零。该audit_null_plugin_deinit
函数在卸载插件时执行清理。在操作期间,插件会为其收到的每个通知递增第一个状态变量。它还根据事件类和子类递增其他事件。实际上,第一个变量是事件子类计数的总和。
通用插件描述符中的audit_null_descriptor
值指向特定类型的插件描述符。对于审计插件,此描述符具有以下结构:
Press CTRL+C to copystruct st_mysql_audit { int interface_version; void (*release_thd)(MYSQL_THD); void (*event_notify)(MYSQL_THD, unsigned int, const void *); unsigned long class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; };
审计插件的特定类型插件描述符具有以下成员:
interface_version
:按照惯例,特定于类型的插件描述符以给定插件类型的接口版本开头。服务器interface_version
在加载插件时检查插件是否与其兼容。对于审计插件,interface_version
成员的值为MYSQL_AUDIT_INTERFACE_VERSION
(在 中定义plugin_audit.h
)。release_thd
:服务器调用的一个函数,用于通知插件它正在与其线程上下文分离。NULL
如果没有这样的功能, 应该 是这样。event_notify
:服务器调用的函数,用于通知插件发生了可审计事件。这个功能不应该是NULL
;这没有意义,因为不会进行审计。class_mask
:一个位掩码,指示插件想要接收通知的事件类。如果此值为 0,则服务器不会将任何事件传递给插件。
服务器同时使用event_notify
和
release_thd
函数。它们在特定线程的上下文中被调用,并且线程可能执行产生多个事件通知的活动。服务器第一次调用
event_notify
线程时,它会创建插件到线程的绑定。此绑定存在时无法卸载插件。当线程不再有事件发生时,服务器通过调用release_thd
函数,然后销毁绑定。例如,当客户端发出一条语句时,处理该语句的线程可能会通知审计插件有关语句生成的结果集以及正在记录的语句。在这些通知发生后,服务器在让线程休眠之前释放插件,直到客户端发出另一个语句。
这种设计使插件能够在第一次调用函数时为给定线程分配所需的资源,并在
event_notify
函数中释放它们release_thd
:
Press CTRL+C to copyevent_notify function: if memory is needed to service the thread allocate memory ... rest of notification processing ... release_thd function: if memory was allocated release memory ... rest of release processing ...
这比在通知函数中反复分配和释放内存更有效。
对于NULL_AUDIT
审计插件,特定类型的插件描述符如下所示:
Press CTRL+C to copystatic struct st_mysql_audit audit_null_descriptor= { MYSQL_AUDIT_INTERFACE_VERSION, /* interface version */ NULL, /* release_thd function */ audit_null_notify, /* notify function */ { (unsigned long) MYSQL_AUDIT_GENERAL_CLASSMASK | MYSQL_AUDIT_CONNECTION_CLASSMASK } /* class mask */ };
服务器调用audit_null_notify()
以将审计事件信息传递给插件。该插件没有任何
release_thd
功能。
事件类掩码表示对“通用”和“连接”
类的所有事件感兴趣。plugin_audit.h
为这些类定义符号及其相应的类掩码:
Press CTRL+C to copy#define MYSQL_AUDIT_GENERAL_CLASS 0 #define MYSQL_AUDIT_GENERAL_CLASSMASK (1 << MYSQL_AUDIT_GENERAL_CLASS) #define MYSQL_AUDIT_CONNECTION_CLASS 1 #define MYSQL_AUDIT_CONNECTION_CLASSMASK (1 << MYSQL_AUDIT_CONNECTION_CLASS)
在特定于类型的插件描述符中,event_notify
函数原型的第二个和第三个参数表示事件类和指向事件结构的通用指针:
Press CTRL+C to copyvoid (*event_notify)(MYSQL_THD, unsigned int, const void *);
不同类中的事件可能具有不同的结构,因此通知函数应该使用事件类值来确定如何解释指向事件结构的指针。
如果服务器使用 的事件类调用通知函数MYSQL_AUDIT_GENERAL_CLASS
,它会将事件结构作为指向结构的指针传递
mysql_event_general
:
Press CTRL+C to copystruct mysql_event_general { unsigned int event_subclass; int general_error_code; unsigned long general_thread_id; const char *general_user; unsigned int general_user_length; const char *general_command; unsigned int general_command_length; const char *general_query; unsigned int general_query_length; struct charset_info_st *general_charset; unsigned long long general_time; unsigned long long general_rows; };
审计插件可以解释
mysql_event_general
成员如下:
-
event_subclass
: 事件子类,以下值之一:Press CTRL+C to copy#define MYSQL_AUDIT_GENERAL_LOG 0 #define MYSQL_AUDIT_GENERAL_ERROR 1 #define MYSQL_AUDIT_GENERAL_RESULT 2 #define MYSQL_AUDIT_GENERAL_STATUS 3
general_error_code
: 错误代码。这是一个类似于mysql_errno()
C API 函数返回的值;0 表示“没有错误。”general_thread_id
:发生事件的线程的 ID。general_user
:事件的当前用户。general_user_length
: 的长度general_user
,以字节为单位。general_command
:用于一般查询日志事件,操作类型。例子:Connect
,Query
,Shutdown
。对于错误日志事件,错误消息。这是一个类似于mysql_error()
C API 函数返回的值;空字符串表示“没有错误。” 对于结果事件,这是空的。general_command_length
: 的长度general_command
,以字节为单位。general_query
:记录或产生结果的 SQL 语句。general_query_length
: 的长度general_query
,以字节为单位。general_charset
: 事件的字符集信息。general_time
:一个TIMESTAMP
值,表示调用通知函数之前的时间。general_rows
:对于一般查询日志事件,为零。对于错误日志事件,发生错误的行号。对于结果事件,结果中的行数加一。对于不产生结果集的语句,该值为 0。此编码使不产生结果集的语句与产生空结果集的语句区分开来。例如,对于DELETE
语句,此值为 0。对于 aSELECT
,结果始终为 1 或更多,其中 1 表示空结果集。general_host
:对于一般查询日志事件,一个字符串表示客户端主机名。general_sql_command
:对于一般查询日志事件,一个字符串,指示执行的操作类型,例如connect
或drop_table
。general_external_user
:对于一般查询日志事件,表示外部用户的字符串(如果没有则为空)。general_ip
: 对于一般查询日志事件,一个字符串表示客户端的 IP 地址。
、general_host
、
和
成员是 MySQL 5.6.14 中的新成员general_sql_command
。
这些是将字符串及其长度配对的结构。例如,如果
是指向一般事件的指针,您可以
按如下方式访问值的成员:
general_external_user
general_ip
MYSQL_LEX_STRING
event_general
general_host
Press CTRL+C to copyevent_general->general_host.length event_general->general_host.str
如果服务器使用 的事件类调用通知函数MYSQL_AUDIT_CONNECTION_CLASS
,它会将事件结构作为指向结构的指针传递,该结构与
mysql_event_connection
结构类似,并且解释方式与
mysql_event_general
结构大致相同。
NULL_AUDIT
插件通知功能非常简单
。它递增全局事件计数器,确定事件类,然后查看事件子类以确定要递增哪个子类计数器:
Press CTRL+C to copystatic void audit_null_notify(MYSQL_THD thd __attribute__((unused)), unsigned int event_class, const void *event) { /* prone to races, oh well */ number_of_calls++; if (event_class == MYSQL_AUDIT_GENERAL_CLASS) { const struct mysql_event_general *event_general= (const struct mysql_event_general *) event; switch (event_general->event_subclass) { case MYSQL_AUDIT_GENERAL_LOG: number_of_calls_general_log++; break; case MYSQL_AUDIT_GENERAL_ERROR: number_of_calls_general_error++; break; case MYSQL_AUDIT_GENERAL_RESULT: number_of_calls_general_result++; break; case MYSQL_AUDIT_GENERAL_STATUS: number_of_calls_general_status++; break; default: break; } } else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS) { const struct mysql_event_connection *event_connection= (const struct mysql_event_connection *) event; switch (event_connection->event_subclass) { case MYSQL_AUDIT_CONNECTION_CONNECT: number_of_calls_connection_connect++; break; case MYSQL_AUDIT_CONNECTION_DISCONNECT: number_of_calls_connection_disconnect++; break; case MYSQL_AUDIT_CONNECTION_CHANGE_USER: number_of_calls_connection_change_user++; break; default: break; } } }
要编译和安装插件库文件,请使用第 4.4.3 节“编译和安装插件库”中的说明。要使库文件可用,请将其安装在插件目录(由
plugin_dir
系统变量命名的目录)中。对于NULL_AUDIT
插件,它是在您从源代码构建 MySQL 时编译和安装的。它也包含在二进制发行版中。构建过程会生成一个名称为
adt_null.so
(.so
后缀可能因平台而异)的共享对象库。
要在运行时注册插件,请使用此语句,.so
根据需要调整平台的后缀:
Press CTRL+C to copyINSTALL PLUGIN NULL_AUDIT SONAME 'adt_null.so';
有关插件加载的其他信息,请参阅 安装和卸载插件。
要验证插件安装,请检查
INFORMATION_SCHEMA.PLUGINS
表或使用SHOW PLUGINS
语句。请参阅获取服务器插件信息。
安装审计插件时,它会公开状态变量,指示已调用插件的事件:
Press CTRL+C to copymysql> SHOW STATUS LIKE 'Audit_null%'; +-----------------------------------+-------+ | Variable_name | Value | +-----------------------------------+-------+ | Audit_null_called | 1388 | | Audit_null_connection_change_user | 0 | | Audit_null_connection_connect | 22 | | Audit_null_connection_disconnect | 21 | | Audit_null_general_error | 1 | | Audit_null_general_log | 513 | | Audit_null_general_result | 415 | | Audit_null_general_status | 416 | +-----------------------------------+-------+
Audit_null_called
计算所有事件,其他变量计算事件子类的实例。例如,前面的SHOW
STATUS
语句使服务器向客户端发送结果,并在启用该日志的情况下将消息写入常规查询日志。因此,重复发出该语句的客户端导致
每次递增,并且Audit_null_called
递增
。(在 MySQL 5.6.24 之前,只有启用了通用查询日志,才会收到通用查询日志的事件通知。从 5.6.24 开始,无论是否启用该日志,都会收到通知。)
Audit_null_general_result
Audit_null_general_log
要在测试后禁用插件,请使用以下语句卸载它:
Press CTRL+C to copyUNINSTALL PLUGIN NULL_AUDIT;