MySQL8 中文参考(三十一)
原文:
dev.mysql.com/doc/refman/8.0/en/audit-log-logging-configuration.html
8.4.5.5 配置审计日志特性
本节描述了如何配置审计日志特性,例如审计日志插件写入事件的文件、写入事件的格式、是否启用日志文件压缩和加密以及空间管理。
-
审计日志文件命名约定
-
选择审计日志文件格式
-
启用审计日志刷新任务
-
为异常检测添加查询统计信息
-
压缩审计日志文件
-
加密审计日志文件
-
手动解压和解密审计日志文件
-
MySQL 8.0.17 之前的审计日志文件加密
-
审计日志文件空间管理
-
审计日志编写策略
注意
此处描述的加密功能适用于 MySQL 8.0.17,除了比较当前加密功能与以前更有限功能的部分之外;请参阅 MySQL 8.0.17 之前的审计日志文件加密。
有关影响审计日志的函数和系统变量的额外信息,请参阅审计日志函数和审计日志选项和变量。
审计日志插件还可以根据事件内容或事件来源的帐户控制写入审计日志文件的审计事件。请参阅第 8.4.5.7 节,“审计日志过滤”。
审计日志文件命名约定
要配置审计日志文件名,请在服务器启动时设置audit_log_file系统变量。默认名称是服务器数据目录中的audit.log。为了最佳安全性,将审计日志写入仅对 MySQL 服务器和有合法查看日志原因的用户可访问的目录。
该插件将audit_log_file值解释为由可选的前导目录名、基本名称和可选后缀组成。如果启用了压缩或加密,则有效文件名(实际用于创建日志文件的名称)与配置文件名不同,因为它具有额外的后缀:
-
如果启用了压缩,插件会添加一个后缀为
.gz。 -
如果启用了加密,插件会添加一个后缀为
.*pwd_id*.enc,其中pwd_id指示用于日志文件操作的加密密码。审计日志插件将加密密码存储在密钥环中;请参阅加密审计日志文件。
有效的审计日志文件名是通过将适用的压缩和加密后缀添加到配置文件名中得到的名称。例如,如果配置的audit_log_file值为audit.log,则有效文件名是以下表中显示的值之一。
| 启用功能 | 有效文件名 |
|---|---|
| 无压缩或加密 | audit.log |
| 压缩 | audit.log.gz |
| 加密 | audit.log.*pwd_id*.enc |
| 压缩,加密 | audit.log.gz.*pwd_id*.enc |
pwd_id指示用于加密或解密文件的密码的 ID。pwd_id格式为pwd_timestamp-seq,其中:
-
pwd_timestamp是一个以*YYYYMMDD*T*hhmmss*格式表示的 UTC 值,指示密码创建的时间。 -
seq是一个序列号。序列号从 1 开始,对于具有相同pwd_timestamp值的密码递增。
以下是一些示例pwd_id密码 ID 值:
20190403T142359-1
20190403T142400-1
20190403T142400-2
为了构建用于在密钥环中存储密码的相应密钥环 ID,审计日志插件将pwd_id值的前缀添加为audit_log-。对于刚刚显示的示例密码 ID,相应的密钥环 ID 是:
audit_log-20190403T142359-1
audit_log-20190403T142400-1
audit_log-20190403T142400-2
当前由审计日志插件用于加密的密码的 ID 是具有最大pwd_timestamp值的密码。如果多个密码具有该pwd_timestamp值,则当前密码 ID 是具有最大序列号的密码。例如,在前面的密码 ID 集合中,有两个具有最大时间戳20190403T142400,因此当前密码 ID 是具有最大序列号(2)的密码。
审计日志插件根据有效的审计日志文件名在初始化和终止期间执行某些操作:
-
在初始化期间,插件会检查是否已存在具有审计日志文件名的文件,并在有时重命名它。(在这种情况下,插件假定之前的服务器调用以审计日志插件运行而异常退出。)然后插件会写入一个新的空审计日志文件。
-
在终止期间,插件会重命名审计日志文件。
-
文件重命名(无论是在插件初始化还是终止期间)都遵循自动基于大小的日志文件轮换的通常规则;请参阅手动审计日志文件轮换(MySQL 8.0.31 之前)。
选择审计日志文件格式
要配置审计日志文件格式,请在服务器启动时设置audit_log_format系统变量。这些格式可用:
-
NEW:新式 XML 格式。这是默认值。 -
OLD:旧式 XML 格式。 -
JSON:JSON 格式。将审计日志写入 JSON 数组。只有此格式支持可选的查询时间和大小统计信息,这些信息从 MySQL 8.0.30 开始提供。
有关每种格式的详细信息,请参阅第 8.4.5.4 节,“审计日志文件格式”。
启用审计日志刷新任务
从 MySQL 8.0.34 开始,MySQL 企业审计提供了设置刷新间隔以自动处理内存缓存的功能。使用audit_log_flush_interval_seconds系统变量配置的刷新任务默认值为零,这意味着未安排运行任务。
当任务配置为运行时(值为非零),MySQL 企业审计尝试在其初始化时调用 scheduler 组件,并配置其内存缓存的定期定期刷新:
-
如果审计日志找不到调度程序注册服务的实现,则不会安排刷新并继续加载。
-
审计日志实现了
dynamic_loader_services_loaded_notification服务,并监听mysql_scheduler的新注册,以便审计日志可以将其计划任务注册到新加载的调度程序中。 -
审计日志仅注册到加载的第一个调度程序实现中。
类似地,MySQL 企业审计在其去初始化时调用scheduler组件,并取消其已计划的定期刷新。它保持对调度程序注册服务的活动引用,直到计划任务被注销,确保在有活动计划任务时无法卸载scheduler组件。执行调度程序及其任务的所有结果都写入服务器错误日志。
要安排审计日志刷新任务:
-
确认
scheduler组件已加载并启用。该组件默认启用(ON)(参见component_scheduler.enabled)。SELECT * FROM mysql.components; +--------------+--------------------+----------------------------+ | component_id | component_group_id | component_urn | +--------------+--------------------+----------------------------+ | 1 | 1 | file://component_scheduler | +--------------+--------------------+----------------------------+ -
如果尚未安装
audit_log插件,请安装它(参见 Section 8.4.5.2, “Installing or Uninstalling MySQL Enterprise Audit”)。 -
使用
audit_log_flush_interval_seconds启动服务器,并将值设置为大于 59 的数字。该值的上限因平台而异。例如,要配置刷新任务每两分钟重复一次:$> mysqld --audit_log_flush_interval_seconds=120更多信息,请参阅
audit_log_flush_interval_seconds系统变量。
添加用于异常值检测的查询统计信息
在 MySQL 8.0.30 及更高版本中,您可以通过可选数据字段扩展 JSON 格式的日志文件,以显示查询时间、发送和接收的字节数、返回给客户端的行数以及检查的行数。对于符合条件的查询,此数据在慢查询日志中可用,并且在审计日志的上下文中,类似地有助于检测活动分析的异常值。只有当审计日志处于 JSON 格式(audit_log_format=JSON)时,扩展数据字段才能添加,这不是默认设置。
查询统计信息通过您设置的组件服务传递到审计日志,作为审计日志过滤函数。这些服务分别命名为mysql_audit_print_service_longlong_data_source和mysql_audit_print_service_double_data_source。您可以为每个输出项选择任一数据类型。对于查询时间,longlong以微秒输出值,而double以秒输出值。
您可以使用audit_log_filter_set_filter()审计日志函数将查询统计信息添加为 JSON 过滤语法的service元素,如下所示:
SELECT audit_log_filter_set_filter('QueryStatistics',
'{ "filter": { "class": { "name": "general", "event": { "name": "status", "print" : '
'{ "service": { "implementation": "mysql_server", "tag": "query_statistics", "element": [ '
'{ "name": "query_time", "type": "double" }, '
'{ "name": "bytes_sent", "type": "longlong" }, '
'{ "name": "bytes_received", "type": "longlong" }, '
'{ "name": "rows_sent", "type": "longlong" }, '
'{ "name": "rows_examined", "type": "longlong" } ] } } } } } }');
要填充bytes_sent和bytes_received字段,系统变量log_slow_extra必须设置为ON。如果系统��量值为OFF,则这些字段的日志文件中将写入空值。
如果您想停止收集查询统计信息,请使用audit_log_filter_set_filter()审计日志函数来移除过滤器,例如:
SELECT audit_log_filter_remove_filter('QueryStatistics');
压缩审计日志文件
可以为任何日志格式启用审计日志文件压缩。
要配置审计日志文件压缩,请在服务器启动时设置audit_log_compression系统变量。允许的值为NONE(无压缩;默认值)和GZIP(GNU Zip 压缩)。
如果同时启用了压缩和加密,则压缩会在加密之前发生。要手动恢复原始文件,首先解密,然后解压缩。请参阅手动解压缩和解密审计日志文件。
加密审计日志文件
可以为任何日志格式启用审计日志文件加密。加密基于用户定义的密码(除了审计日志插件生成的初始密码)。要使用此功能,必须启用 MySQL 密钥环,因为审计日志使用它进行密码存储。可以使用任何密钥环组件或插件;有关说明,请参阅第 8.4.4 节,“MySQL 密钥环”。
要配置审计日志文件加密,请在服务器启动时设置audit_log_encryption系统变量。允许的值为NONE(无加密;默认)和AES(AES-256-CBC 密码加密)。
要在运行时设置或获取加密密码,请使用这些审计日志函数:
-
要设置当前的加密密码,请调用
audit_log_encryption_password_set()。此函数将新密码存储在密钥环中。如果启用了加密,它还会执行一个日志文件旋转操作,重命名当前日志文件,并开始一个新的使用密码加密的日志文件。文件重命名遵循自动基于大小的日志文件旋转的通常规则;请参阅手动审计日志文件旋转(MySQL 8.0.31 之前)。如果
audit_log_password_history_keep_days系统变量不为零,则调用audit_log_encryption_password_set()也会导致旧的存档审计日志加密密码过期。有关审计日志密码历史的信息,包括密码存档和过期,请参阅该变量的描述。 -
要获取当前的加密密码,请调用不带参数的
audit_log_encryption_password_get()。要通过 ID 获取密码,请传递一个指定当前密码或存档密码的密钥环 ID 的参数。要确定存在哪些审计日志密钥环 ID,请查询性能模式
keyring_keys表:mysql> SELECT KEY_ID FROM performance_schema.keyring_keys WHERE KEY_ID LIKE 'audit_log%' ORDER BY KEY_ID; +-----------------------------+ | KEY_ID | +-----------------------------+ | audit_log-20190415T152248-1 | | audit_log-20190415T153507-1 | | audit_log-20190416T125122-1 | | audit_log-20190416T141608-1 | +-----------------------------+
有关审计日志加密函数的其他信息,请参阅审计日志函数。
当审计日志插件初始化时,如果发现日志文件加密已启用,则会检查密钥环是否包含审计日志加密密码。如果没有,则插件会自动生成一个随机初始加密密码并将其存储在密钥环中。要查找此密码,请调用audit_log_encryption_password_get()。
如果同时启用了压缩和加密,则压缩会在加密之前发生。要手动恢复原始文件,请先解密,然后解压缩。请参阅手动解压缩和解密审计日志文件。
手动解压缩和解密审计日志文件
审计日志文件可以使用标准工具进行解压缩和解密。这应该仅针对已关闭(已归档)且不再使用的日志文件进行,而不是当前审计日志插件正在写入的日志文件。您可以通过审计日志插件将时间戳添加到基本名称后的文件名中来识别已归档的日志文件。
在本讨论中,假设audit_log_file设置为audit.log。在这种情况下,已归档的审计日志文件具有以下表中显示的名称之一。
| 启用功能 | 已归档文件名 |
|---|---|
| 无压缩或加密 | audit.*timestamp*.log |
| 压缩 | audit.*timestamp*.log.gz |
| 加密 | audit.*timestamp*.log.*pwd_id*.enc |
| 压缩,加密 | audit.*timestamp*.log.gz.*pwd_id*.enc |
如审计日志文件命名约定所述,pwd_id格式为pwd_timestamp-seq。因此,已归档的加密日志文件的名称实际上包含两个时间戳。第一个时间戳表示文件旋转时间,第二个时间戳表示加密密码创建时间。
考虑以下一组已归档的加密日志文件名称:
audit.20190410T205827.log.20190403T185337-1.enc
audit.20190410T210243.log.20190403T185337-1.enc
audit.20190415T145309.log.20190414T223342-1.enc
audit.20190415T151322.log.20190414T223342-2.enc
每个文件名都有一个唯一的旋转时间戳。相比之下,密码时间戳不是唯一的:
-
前两个文件具有相同的密码 ID 和序列号(
20190403T185337-1)。它们具有相同的加密密码。 -
后两个文件具有相同的密码 ID(
20190414T223342),但不同的序列号(1,2)。这些文件具有不同的加密密码。
要手动解压缩压缩的日志文件,请使用gunzip,gzip -d或等效命令。例如:
gunzip -c audit.*timestamp*.log.gz > audit.*timestamp*.log
要手动解密加密的日志文件,请使用openssl命令。例如:
openssl enc -d -aes-256-cbc -pass pass:*password* -md sha256
-in audit.*timestamp*.log.*pwd_id*.enc
-out audit.*timestamp*.log
要执行该命令,你必须获取*password*,即加密密码。为此,请使用audit_log_encryption_password_get()。例如,如果审计日志文件名为audit.20190415T151322.log.20190414T223342-2.enc,密码 ID 为20190414T223342-2,密钥环 ID 为audit-log-20190414T223342-2。像这样检索密钥环密码:
SELECT audit_log_encryption_password_get('audit-log-20190414T223342-2');
如果审计日志既启用了压缩又启用了加密,压缩会在加密之前发生。在这种情况下,文件名会添加.gz和.*pwd_id*.enc后缀,对应这些操作发生的顺序。要手动恢复原始文件,请按相反顺��执行操作。也就是说,首先解密文件,然后解压缩:
openssl enc -d -aes-256-cbc -pass pass:*password* -md sha256
-in audit.*timestamp*.log.gz.*pwd_id*.enc
-out audit.*timestamp*.log.gz
gunzip -c audit.*timestamp*.log.gz > audit.*timestamp*.log
MySQL 8.0.17 之前的审计日志文件加密
本节介绍了 MySQL 8.0.17 之前和之后审计日志文件加密功能的差异,这是在实现密码历史记录(包括密码归档和过期)时的情况。还指出了审计日志插件如何处理从低于 8.0.17 版本升级到 MySQL 8.0.17 或更高版本的情况。
| 功能 | 在 MySQL 8.0.17 之前 | 截至 MySQL 8.0.17 |
|---|---|---|
| 密码数量 | 仅单个密码 | 允许多个密码 |
| 加密日志文件名称 | .enc 后缀 | .*pwd_id*.enc 后缀 |
| 密码密钥环 ID | audit_log | audit_log-*pwd_id* |
| 密码历史记录 | 否 | 是 |
在 MySQL 8.0.17 之前,没有密码历史记录,因此设置新密码会使旧密码无法访问,导致 MySQL 企业审计无法读取用旧密码加密的日志文件。如果你预期需要手动解密这些文件,你必须保留以前的密码记录。
如果在从低版本升级到 MySQL 8.0.17 或更高版本时启用了审计日志文件加密,则审计日志插件执行以下升级操作:
-
在插件初始化期间,插件会检查具有
audit_log密钥环 ID 的加密密码。如果找到一个,插件会使用audit_log-*pwd_id*格式中的密钥环 ID 复制密码,并将其用作当前的加密密码。(有关*pwd_id*语法的详细信息,请参阅审计日志文件命名约定.) -
现有加密的日志文件具有后缀
.enc。插件不会将这些重命名为后缀为.*pwd_id*.enc,但只要具有audit_logID 的密钥保留在密钥环中,就可以读取它们。 -
当密码清理发生时,如果插件过期任何具有
audit_log-*pwd_id*格式的密钥环 ID 的密码,它也会过期具有audit_log密钥环 ID 的密码(如果存在)。此时,具有后缀.enc而不是.*pwd_id*.enc的加密日志文件变得无法被插件读取,因此可以假定你不再需要它们。
审计日志文件的空间管理
审计日志文件有可能变得非常庞大,占用大量磁盘空间。如果您正在收集自 MySQL 8.0.30 起可用的可选查询时间和大小统计信息,则会增加空间要求。查询统计仅支持 JSON 格式。
为管理使用的空间,采用以下方法:
-
日志文件轮换。这涉及通过重命名当前日志文件来轮换它,然后使用原始名称打开一个新的当前日志文件。轮换可以手动执行,也可以配置为自动执行。
-
如果启用了自动轮换,则对已轮换的 JSON 格式日志文件进行修剪。修剪可以基于日志文件的年龄(自 MySQL 8.0.24 起),或组合日志文件大小(自 MySQL 8.0.26 起)进行。
要配置审计日志文件空间管理,请使用以下系统变量:
-
如果
audit_log_rotate_on_size为 0(默认值),则禁用自动日志文件轮换。-
除非手动执行,否则不会发生轮换。
-
要轮换当前文件,请使用以下方法之一:
-
在 MySQL 8.0.31 之前,手动重命名文件,然后启用
audit_log_flush来关闭它,并使用原始名称打开一个新的当前日志文件。此文件轮换方法和audit_log_flush变量在 MySQL 8.0.31 中已弃用。使用此文件轮换方法,不会发生对已轮换的 JSON 格式日志文件进行修剪;
audit_log_max_size和audit_log_prune_seconds不起作用。 -
从 MySQL 8.0.31 开始,运行
SELECT audit_log_rotate();来重命名文件,并使用原始名称打开一个新的审计日志文件。使用此文件轮换方法,如果
audit_log_max_size或audit_log_prune_seconds的值大于 0,则会对已轮换的 JSON 格式日志文件进行修剪。
参见手动审计日志文件轮换(MySQL 8.0.31 之前)。
-
-
-
如果
audit_log_rotate_on_size大于 0,则启用自动审计日志文件轮换:-
当对当前日志文件进行写入导致其大小超过
audit_log_rotate_on_size值时,以及在某些其他条件下,会自动进行轮换;参见自动审计日志文件轮换。当自动轮换发生时,审计日志插件会重命名当前日志文件,并使用原始名称打开一个新的当前日志文件。 -
如果
audit_log_max_size或audit_log_prune_seconds的值大于 0,则会对旋转的 JSON 格式日志文件进行修剪。 -
audit_log_flush没有效果。
-
注意
对于 JSON 格式的日志文件,在运行时更改audit_log_format_unix_timestamp系统变量的值时也会发生旋转。然而,这并不是为了空间管理目的,而是为了使给定的 JSON 格式日志文件中的所有记录要么包含time字段,要么不包含。
注意
旋转(重命名)的日志文件不会自动删除。例如,基于大小的日志文件旋转,重命名的日志文件具有唯一名称并且会无限累积。它们不会在名称序列的末尾旋转。为避免过度使用空间:
-
从 MySQL 8.0.24 开始(针对 JSON 格式日志文件):按照审计日志文件修剪中描述的方式启用日志文件修剪。
-
否则(对于非 JSON 文件,或者在 MySQL 8.0.24 之前的所有日志格式):定期删除旧文件,必要时首先备份它们。如果备份的日志文件已加密,还需将相应的加密密码备份到安全位置,以便以后解密文件时使用。
以下部分详细描述了日志文件旋转和修剪。
-
手动审计日志文件旋转(MySQL 8.0.31 之前)
-
手动审计日志文件旋转(从 MySQL 8.0.31 开始)
-
自动审计日志文件旋转
-
审计日志文件修剪
手动审计日志文件旋转(MySQL 8.0.31 之前)
注意
从 MySQL 8.0.31 开始,audit_log_flush变量和这种审计日志文件旋转方法已被弃用;预计在未来的 MySQL 版本中将不再支持。
如果audit_log_rotate_on_size为 0(默认值),除非手动执行,否则不会发生日志轮换。在这种情况下,当audit_log_flush值从禁用更改为启用时,审计日志插件会关闭并重新打开日志文件。日志文件重命名必须在服务器外部完成。假设日志文件名为audit.log,并且您希望保留最近的三个日志文件,循环使用名称audit.log.1到audit.log.3。在 Unix 上,执行以下手动旋转:
-
从命令行,重命名当前的日志文件:
mv audit.log.2 audit.log.3 mv audit.log.1 audit.log.2 mv audit.log audit.log.1此策略会覆盖当前的
audit.log.3内容,限制已归档的日志文件的数量和它们使用的空间。 -
此时,插件仍在写入当前的日志文件,该文件已重命名为
audit.log.1。连接到服务器并刷新日志文件,以便插件关闭它并重新打开一个新的audit.log文件:SET GLOBAL audit_log_flush = ON;audit_log_flush是特殊的,其值保持为OFF,因此您无需在再次启用它以执行另一个刷新之前显式禁用它。
注意
如果启用了压缩或加密功能,则日志文件名包括表示已启用功能的后缀,以及如果启用了加密则包括密码 ID。如果文件名包括密码 ID,请确保在手动重命名任何文件时保留 ID,以便确定用于解密操作的密码。
注意
对于 JSON 格式日志记录,手动重命名审计日志文件会使它们对于日志读取功能不可用,因为审计日志插件无法再确定它们是日志文件序列的一部分(参见第 8.4.5.6 节,“读取审计日志文件”)。考虑设置audit_log_rotate_on_size大于 0 以使用基于大小的轮换。
手动审计日志文件旋转(从 MySQL 8.0.31 开始)
如果audit_log_rotate_on_size为 0(默认值),除非手动执行,否则不会发生日志轮换。
要手动旋转审计日志文件,请运行SELECT audit_log_rotate();以重命名当前的审计日志文件并打开一个新的审计日志文件。文件将根据审计日志文件命名约定中描述的约定进行重命名。
使用audit_log_rotate()函数需要AUDIT_ADMIN特权。
管理已归档的日志文件(已重命名的文件)的数量和它们使用的空间是一个手动任务,涉及从文件系统中删除不再需要的已归档审计日志文件。
使用audit_log_rotate()函数重命名的审计日志文件的内容可以通过audit_log_read()函数读取。
自动审计日志文件旋转
如果audit_log_rotate_on_size大于 0,则设置audit_log_flush无效。相反,每当对当前日志文件的写入导致其大小超过audit_log_rotate_on_size值时,审计日志插件会自动重命名当前日志文件,并使用原始名称打开一个新的当前日志文件。
在以下情况下也会自动基于大小进行旋转:
-
在插件初始化期间,如果具有审计日志文件名的文件已经存在(参见审计日志文件命名约定)。
-
在插件终止期间。
-
当调用
audit_log_encryption_password_set()函数设置加密密码时,如果启用了加密。(如果禁用加密,则不会发生旋转。)
插件通过在基本名称后插入时间戳来重命名原始文件。例如,如果文件名为audit.log,插件将将其重命名为类似audit.20210115T140633.log的值。时间戳是*YYYYMMDD*T*hhmmss*格式的 UTC 值。对于 XML 日志记录,时间戳表示旋转时间。对于 JSON 日志记录,时间戳是写入文件的最后一个事件的时间。
如果日志文件已加密,则原始文件名已包含指示加密密码创建时间的时间戳(请参见审计日志文件命名约定)。在这种情况下,旋转后的文件名包含两个时间戳。例如,名为audit.log.20210110T130749-1.enc的加密日志文件将重命名为类似audit.20210115T140633.log.20210110T130749-1.enc的值。
审计日志文件修剪
如果启用了自动日志文件旋转,则审计日志插件支持对旋转的 JSON 格式审计日志文件进行修剪。要使用此功能:
-
将
audit_log_format设置为JSON。(此外,还考虑更改audit_log_file;请参见选择审计日志文件格式。) -
将
audit_log_rotate_on_size设置为大于 0,以指定自动日志文件旋转发生的字节大小。 -
默认情况下,不会对自动旋转的 JSON 格式日志文件进行修剪。要启用修剪,请将以下系统变量之一设置为大于 0 的值:
-
将
audit_log_max_size设置为大于 0,以指定旋转日志文件的组合大小上限,超过该大小的文件将被修剪。audit_log_max_size自 MySQL 8.0.26 版本起可用。 -
将
audit_log_prune_seconds设置为大于 0,以指定旋转日志文件在多少秒后将被修剪。audit_log_prune_seconds自 MySQL 8.0.24 版本起可用。
非零值的
audit_log_max_size优先于非零值的audit_log_prune_seconds。如果两者在插件初始化时都设置为大于 0,则会向服务器错误日志写入警告。如果客户端在运行时都设置为大于 0,则会向客户端返回警告。注意
写入错误日志的警告被写入为注释,这些是信息消息。为确保这些消息出现在错误日志中并且不被丢弃,请确保错误日志的详细程度足以包含信息消息。例如,如果您正在使用基于优先级的日志过滤,如第 7.4.2.5 节,“基于优先级的错误日志过滤(log_filter_internal)”中所述,将
log_error_verbosity系统变量设置为 3。 -
如果启用了 JSON 格式日志文件的修剪,则会按以下方式进行:
-
当自动旋转发生时;有关发生此情况的条件,请参阅自动审计日志文件旋转。
-
当全局
audit_log_max_size或audit_log_prune_seconds系统变量���运行时设置时。
对于基于组合旋转日志文件大小进行修剪,如果组合大小大于audit_log_max_size指定的限制,则审计日志插件将删除最旧的文件,直到它们的组合大小不超过限制。
对于基于旋转日志文件年龄进行修剪,修剪点是当前时间减去audit_log_prune_seconds的值。在旋转的 JSON 格式日志文件中,每个文件名的时间戳部分表示写入文件的最后一个事件的时间戳。审计日志插件使用文件名时间戳来确定哪些文件仅包含早于修剪点的事件,并将其删除。
撰写审计日志策略
审计日志插件可以使用多种策略进行日志写入。无论采用哪种策略,记录都是尽力而为的,没有一致性的保证。
要指定写入策略,请在服务器启动时设置 audit_log_strategy 系统变量。默认情况下,策略值为 ASYNCHRONOUS,插件会异步记录到缓冲区,如果缓冲区已满则等待。可以告诉插件不要等待 (PERFORMANCE) 或使用文件系统缓存同步记录 (SEMISYNCHRONOUS),或者在每次写入请求后强制输出使用 sync() 调用 (SYNCHRONOUS)。
对于异步写入策略,audit_log_buffer_size 系统变量是以字节为单位的缓冲区大小。在服务器启动时设置此变量以更改缓冲区大小。插件使用一个单一的缓冲区,在初始化时分配并在终止时移除。插件不会为非异步写入策略分配此缓冲区。
异步记录策略具有以下特点:
-
对服务器性能和可扩展性的影响最小。
-
阻塞生成审计事件的线程的时间尽可能短;即分配缓冲区的时间加上将事件复制到缓冲区的时间。
-
输出到缓冲区。一个单独的线程处理从缓冲区到日志文件的写入。
使用异步记录时,如果在写入文件时出现问题或插件未能干净地关闭(例如,在服务器主机意外退出的情况下),日志文件的完整性可能会受到影响。为了降低这种风险,请将 audit_log_strategy 设置为使用同步记录。
PERFORMANCE 策略的一个缺点是当缓冲区已满时会丢弃事件。对于负载较重的服务器,审计日志可能会丢失事件。
原文:
dev.mysql.com/doc/refman/8.0/en/audit-log-file-reading.html
8.4.5.6 读取审计日志文件
审计日志插件支持提供 SQL 接口以读取 JSON 格式审计日志文件的函数。(此功能不适用于其他格式的日志文件。)
当审计日志插件初始化并配置为 JSON 记录时,它使用包含当前审计日志文件的目录作为搜索可读取审计日志文件的位置。插件从audit_log_file系统变量的值中确定文件位置、基本名称和后缀,然后查找与以下模式匹配的文件,其中[...]表示可选文件名部分:
*basename*[.*timestamp*].*suffix*[.gz][[.*pwd_id*].enc]
如果文件名以.enc结尾,则文件已加密,读取其未加密内容需要从密钥环获取解密密码。审计日志插件确定解密密码的密钥环 ID 如下:
-
如果
.enc之前是*pwd_id*,密钥环 ID 是audit_log-*pwd_id*。 -
如果
.enc之前不是*pwd_id*,则文件具有在实施审计日志加密密码历史之前的旧名称。密钥环 ID 是audit_log。
有关加密审计日志文件的更多信息,请参阅加密审计日志文件。
插件会忽略那些被手动重命名且不符合模式的文件,以及使用密码加密但密钥环中不再存在密码的文件。插件会打开每个剩余的候选文件,验证文件是否实际包含JSON审计事件,并使用每个文件的第一个事件的时间戳对文件进行排序。结果是一系列文件,可以通过日志读取函数进行访问:
-
audit_log_read()从审计日志中读取事件或关闭读取过程。 -
audit_log_read_bookmark()返回最近写入的审计日志事件的书签。此书签适合传递给audit_log_read()以指示从何处开始读取。
audit_log_read() 接受一个可选的JSON字符串参数,成功调用任一函数后返回的结果是一个JSON字符串。
要使用函数读取审计日志,请遵循以下原则:
-
调用
audit_log_read()从给定位置或当前位置开始读取事件,或关闭读取:-
要初始化审核日志读取序列,请传递指示开始位置的参数。一种方法是传递由
audit_log_read_bookmark()返回的书签:SELECT audit_log_read(audit_log_read_bookmark()); -
要从序列中的当前位置继续读取,请调用
audit_log_read()而不指定位置:SELECT audit_log_read(); -
要显式关闭读取序列,请传递一个
JSONnull参数:SELECT audit_log_read('null');不需要显式关闭读取。当会话结束或通过调用带有指示开始位置参数的
audit_log_read()来初始化新的读取序列时,读取会在隐式关闭。
-
-
成功调用
audit_log_read()读取事件将返回一个包含审核事件数组的JSON字符串:-
如果返回数组的最终值不是
JSONnull值,则在刚刚读取的事件后还有更多事件,并且可以再次调用audit_log_read()来读取更多事件。 -
如果返回数组的最终值是
JSONnull值,则当前读取序列中没有更多事件可读取。
每个非
null数组元素都表示为JSON哈希。例如:[ { "timestamp": "2020-05-18 13:39:33", "id": 0, "class": "connection", "event": "connect", ... }, { "timestamp": "2020-05-18 13:39:33", "id": 1, "class": "general", "event": "status", ... }, { "timestamp": "2020-05-18 13:39:33", "id": 2, "class": "connection", "event": "disconnect", ... }, null ]有关 JSON 格式审核事件内容的更多信息,请参阅 JSON 审计日志文件格式。
-
-
未指定位置的
audit_log_read()调用在以下任何条件下都会产生错误:-
通过向
audit_log_read()传递位置参数来初始化读取序列。 -
当前读取序列中没有更多事件可读取;也就是说,
audit_log_read()先前返回了以JSONnull值结尾的数组。 -
通过向
audit_log_read()传递一个JSONnull值,最近的读取序列已被关闭。
要在这些条件下读取事件,需要首先通过调用带有指定位置参数的
audit_log_read()来初始化读取序列。 -
要指定传递给audit_log_read()的位置,请包含指示从何处开始读取的参数。例如,传递一个书签,这是一个包含timestamp和id元素的JSON哈希,用于唯一标识特定事件。以下是通过调用audit_log_read_bookmark()函数获得的示例书签:
mysql> SELECT audit_log_read_bookmark();
+-------------------------------------------------+
| audit_log_read_bookmark() |
+-------------------------------------------------+
| { "timestamp": "2020-05-18 21:03:44", "id": 0 } |
+-------------------------------------------------+
将当前书签传递给audit_log_read()会初始化从书签位置开始的事件读取:
mysql> SELECT audit_log_read(audit_log_read_bookmark());
+-----------------------------------------------------------------------+
| audit_log_read(audit_log_read_bookmark()) |
+-----------------------------------------------------------------------+
| {"timestamp":"2020-05-18 22:41:24","id":0,"class":"connection", ... |
+-----------------------------------------------------------------------+
传递给[audit_log_read()的参数是可选的。如果存在,则可以是JSON null值以关闭读取序列,或者是JSON哈希。
在传递给audit_log_read()的哈希参数中,项目是可选的,用于控制读取操作的方面,例如开始读取的位置或要读取的事件数量。以下项目是重要的(其他项目将被忽略):
-
start:要读取的第一个事件在审计日志中的位置。位置以时间戳给出,并且读取从时间戳值之后或同时发生的第一个事件开始。start项目具有以下格式,其中*value*是一个文字时间戳值:"start": { "timestamp": "*value*" }start项目自 MySQL 8.0.22 起允许使用。 -
timestamp,id:读取的第一个事件在审计日志中的位置。timestamp和id项目一起构成一个书签,唯一标识特定事件。如果audit_log_read()参数包括任一项目,则必须同时包括两者才能完全指定位置,否则会出现错误。 -
max_array_length:从日志中读取的最大事件数。如果省略此项目,默认情况下是读取到日志末尾或直到读取缓冲区已满为止。
要指定传递给audit_log_read()的起始位置,请传递一个包含start项目或由timestamp和id项目组成的书签的哈希参数。如果哈希参数同时包括start项目和书签,则会出现错误。
如果哈希参数未指定起始位置,则从当前位置继续读取。
如果时间戳值不包含时间部分,则假定时间部分为00:00:00。
audit_log_read()接受的示例参数:
-
从给定时间戳之后或同时发生的第一个事件开始读取事件:
audit_log_read('{ "start": { "timestamp": "2020-05-24 12:30:00" } }') -
类似于前一个示例,但最多读取 3 个事件:
audit_log_read('{ "start": { "timestamp": "2020-05-24 12:30:00" }, "max_array_length": 3 }') -
从
2020-05-24 00:00:00之后或同时发生的第一个事件开始读取事件(时间戳不包含时间部分,因此假定为00:00:00):audit_log_read('{ "start": { "timestamp": "2020-05-24" } }') -
从具有确切时间戳和事件 ID 的事件开始读取事件:
audit_log_read('{ "timestamp": "2020-05-24 12:30:00", "id": 0 }') -
与前一个示例类似,但最多读取 3 个事件:
audit_log_read('{ "timestamp": "2020-05-24 12:30:00", "id": 0, "max_array_length": 3 }') -
从读取序列的当前位置读取事件:
audit_log_read() -
从当前位置开始读取最多 5 个事件:
audit_log_read('{ "max_array_length": 5 }') -
关闭当前读取序列:
audit_log_read('null')
从任一日志读取函数返回的JSON字符串可以根据需要进行操作。假设调用以获取书签产生此值:
mysql> SET @mark := audit_log_read_bookmark();
mysql> SELECT @mark;
+-------------------------------------------------+
| @mark |
+-------------------------------------------------+
| { "timestamp": "2020-05-18 16:10:28", "id": 2 } |
+-------------------------------------------------+
使用该参数调用audit_log_read()可以返回多个事件。要限制audit_log_read()最多读取 N 个事件,请向字符串添加一个具有该值的 max_array_length 项。例如,要读取单个事件,请修改字符串如下:
mysql> SET @mark := JSON_SET(@mark, '$.max_array_length', 1);
mysql> SELECT @mark;
+----------------------------------------------------------------------+
| @mark |
+----------------------------------------------------------------------+
| {"id": 2, "timestamp": "2020-05-18 16:10:28", "max_array_length": 1} |
+----------------------------------------------------------------------+
修改后的字���串,当传递给audit_log_read()时,将产生一个包含最多一个事件的结果,无论有多少事件可用。
在 MySQL 8.0.19 之前,审计日志函数返回的字符串返回值是二进制字符串。要将二进制字符串用于需要非二进制字符串的函数(例如操作JSON值的函数),请将其转换为非二进制字符串。例如,在将书签传递给JSON_SET()之前,将其转换为utf8mb4如下:
SET @mark = CONVERT(@mark USING utf8mb4);
该语句即使对于 MySQL 8.0.19 及更高版本也可以使用;对于这些版本,它基本上是一个无操作,并且是无害的。
如果从mysql客户端内调用审计日志函数,则根据--binary-as-hex的值,二进制字符串结果将以十六进制表示。有关该选项的更多信息,请参见 Section 6.5.1, “mysql — The MySQL Command-Line Client”。
要限制audit_log_read()读取的字节数量,请设置audit_log_read_buffer_size系统变量。从 MySQL 8.0.12 开始,此变量默认为 32KB,并且可以在运行时设置。每个客户端应适当设置其对audit_log_read()的使用的会话值audit_log_read_buffer_size。
每次调用audit_log_read()都会返回尽可能多的可用事件,适合缓冲区大小。不适合缓冲区大小的事件将被跳过并生成警告。考虑这种行为时,应考虑以下因素来评估应用程序的适当缓冲区大小:
-
在调用
audit_log_read()和每次调用返回的事件数量之间存在权衡:-
使用更小的缓冲区大小,调用会返回更少的事件,因此需要更多的调用。
-
使用更大的缓冲区大小,调用会返回更多事件,因此需要更少的调用。
-
-
使用较小的缓冲区大小,例如默认大小为 32KB,事件超过缓冲区大小并因此被跳过的可能性更大。
在 MySQL 8.0.12 之前,audit_log_read_buffer_size默认为 1MB,影响所有客户端,只能在服务器启动时更改。
有关审计日志读取函数的更多信息,请参见 Audit Log Functions。
8.4.5.7 审计日志过滤
注意
要使此处描述的审计日志过滤工作,必须安装审计日志插件和相应的审计表和函数。如果安装了插件但没有为基于规则的过滤所需的审计表和函数,插件将以传统过滤模式运行,描述在第 8.4.5.10 节“传统模式审计日志过滤”中。传统模式(在 MySQL 8.0.34 中已弃用)是 MySQL 5.7.13 之前引入基于规则的过滤之前的过滤行为。
-
审计日志过滤属性
-
审计日志过滤函数的约束
-
使用审计日志过滤函数
审计日志过滤属性
审计日志插件具有通过过滤控制记录审计事件的能力:
-
可以使用以下特征过滤审计事件:
-
用户帐户
-
审计事件类
-
审计事件子类
-
诸如指示操作状态或执行的 SQL 语句等审计事件字段
-
-
审计过滤是基于规则的:
-
审计规则定义创建了一组审计规则。根据刚才描述的特征,可以配置定义以包括或排除事件进行日志记录。
-
过滤规则具有阻止(中止)符合条件事件执行的能力,除了现有的事件记录功能之外。
-
可以定义多个过滤器,并且任何给定的过滤器可以分配给任意数量的用户帐户。
-
可以定义一个默认过滤器,用于任何没有明确分配过滤器的用户帐户。
审计日志过滤用于从 MySQL 8.0.30 实现组件服务。要获取该版本提供的可选查询统计信息,您可以设置它们作为一个使用服务组件的过滤器,该组件实现将统计信息写入审计日志的服务。有关设置此过滤器的说明,请参见添加查询统计信息以进行异常检测。
有关编写过滤规则的信息,请参见第 8.4.5.8 节“编写审计日志过滤器定义”。
-
-
可以使用基于函数调用的 SQL 接口定义和修改审计过滤器。要显示审计过滤器,请查询
mysql.audit_log_filter表。 -
审计过滤器定义存储在
mysql系统数据库的表中。 -
在给定会话中,只读的
audit_log_filter_id系统变量的值指示是否为会话分配了过滤器。
注意
默认情况下,基于规则的审计日志过滤不记录任何用户的可审计事件。要记录所有用户的所有可审计事件,请使用以下语句,创建一个简单的过滤器以启用日志记录并将其分配给默认账户:
SELECT audit_log_filter_set_filter('log_all', '{ "filter": { "log": true } }');
SELECT audit_log_filter_set_user('%', 'log_all');
%分配的过滤器用于来自未明确分配过滤器的任何账户的连接(最初对所有账户都是真的)。
如前所述,审计过滤控制的 SQL 接口是基于函数的。以下列表简要总结了这些功能:
-
audit_log_filter_set_filter(): 定义过滤器。 -
audit_log_filter_remove_filter(): 移除过滤器。 -
audit_log_filter_set_user(): 开始过滤用户账户。 -
audit_log_filter_remove_user(): 停止过滤用户账户。 -
audit_log_filter_flush(): 刷新手动更改的过滤器表以影响正在进行的过滤。
有关使用示例和有关过滤功能的完整详细信息,请参见 Using Audit Log Filtering Functions 和 Audit Log Functions。
审计日志过滤功能的约束
审计日志过滤功能受到以下约束:
-
要使用任何过滤功能,必须启用
audit_log插件,否则会出错。此外,审计表必须存在,否则会出错。要安装audit_log插件及其附带的功能和表,请参见 Section 8.4.5.2, “Installing or Uninstalling MySQL Enterprise Audit”。 -
要使用任何过滤功能,用户必须具有
AUDIT_ADMINSUPER权限,否则会出错。要向用户账户授予其中一个权限,请使用此语句:GRANT *privilege* ON *.* TO *user*;或者,如果您希望避免授予
AUDIT_ADMIN或SUPER权限,同时仍允许用户访问特定的过滤功能,“包装”存储程序可以被定义。这种技术在使用通用密钥环函数中描述了在密钥环函数的上下文中,可以用于过滤函数。 -
如果安装了
audit_log插件但未创建相应的审计表和函数,则该插件将以传统模式运行。插件在服务器启动时将这些消息写入错误日志:[Warning] Plugin audit_log reported: 'Failed to open the audit log filter tables.' [Warning] Plugin audit_log reported: 'Audit Log plugin supports a filtering, which has not been installed yet. Audit Log plugin will run in the legacy mode, which will be disabled in the next release.'在 MySQL 8.0.34 之后不再支持的传统模式中,过滤只能基于事件账户或状态进行。详情请参阅第 8.4.5.10 节,“传统模式审计日志过滤”。
-
理论上,具有足够权限的用户可能会错误地在审计日志过滤器中创建一个“中止”项目,阻止自己和其他管理员访问系统。从 MySQL 8.0.28 开始,
AUDIT_ABORT_EXEMPT权限可用于允许用户账户的查询始终执行,即使“中止”项目会阻止它们。因此,具有此权限的账户可以用于在审计配置错误后恢复对系统的访问。查询仍然记录在审计日志中,但由于权限的存在,不会被拒绝,而是被允许执行。在 MySQL 8.0.28 或更高版本中创建的具有
SYSTEM_USER权限的账户在创建时会自动分配AUDIT_ABORT_EXEMPT权限。在进行 MySQL 8.0.28 或更高版本的升级过程时,如果没有现有账户被分配该权限,则具有SYSTEM_USER权限的现有账户也会被分配AUDIT_ABORT_EXEMPT权限。
使用审计日志过滤功能
在使用审计日志函数之前,请根据第 8.4.5.2 节,“安装或卸载 MySQL 企业审计”中提供的说明安装它们。使用任何这些函数都需要AUDIT_ADMIN或SUPER权限。
审计日志过滤功能通过提供一个接口来创建、修改和删除过滤定义并将过滤器分配给用户账户,实现了过滤控制。
过滤器定义是JSON值。有关在 MySQL 中使用 JSON 数据的信息,请参见第 13.5 节,“JSON 数据类型”。本节显示了一些简单的过滤器定义。有关过滤器定义的更多信息,请参见第 8.4.5.8 节,“编写审计日志过滤器定义”。
当连接到达时,审计日志插件通过在当前过滤器分配中搜索用户账户名来确定新会话使用哪个过滤器:
-
如果用户被分配了过滤器,则审计日志将使用该过滤器。
-
否则,如果不存在特定于用户的过滤器分配,但是为默认账户(
%)分配了过滤器,则审计日志将使用默认过滤器。 -
否则,审计日志不会从会话中选择任何审计事件进行处理。
如果会话期间发生更改用户操作(参见 mysql_change_user()),会话的过滤器分配将根据相同规则更新,但针对新用户。
默认情况下,没有账户被分配过滤器,因此不会对任何账户进行可审计事件的处理。
假设您想要更改默认设置为仅记录与连接相关的活动(例如,查看连接、更改用户和断开连接事件,但不记录用户在连接时执行的 SQL 语句)。为实现此目的,请定义一个过滤器(此处命名为 log_conn_events),仅允许记录 connection 类中的事件,并将该过滤器分配给默认账户,表示为 % 账户名:
SET @f = '{ "filter": { "class": { "name": "connection" } } }';
SELECT audit_log_filter_set_filter('log_conn_events', @f);
SELECT audit_log_filter_set_user('%', 'log_conn_events');
现在审计日志将对没有明确定义过滤器的任何账户的连接使用此默认账户过滤器。
要明确为特定用户账户或多个账户分配过滤器,请定义过滤器,然后将其分配给相关账户:
SELECT audit_log_filter_set_filter('log_all', '{ "filter": { "log": true } }');
SELECT audit_log_filter_set_user('user1@localhost', 'log_all');
SELECT audit_log_filter_set_user('user2@localhost', 'log_all');
现在 user1@localhost 和 user2@localhost 启用了完整记录。其他账户的连接将继续使用默认账户过滤器进行过滤。
要取消用户账户与当前过滤器的关联,要么取消分配过滤器,要么分配不同的过滤器:
-
要取消用户账户的过滤器分配:
SELECT audit_log_filter_remove_user('user1@localhost');账户当前会话的过滤保持不受影响。如果账户有默认账户过滤器,则后续连接将使用该过滤器进行过滤,否则不记录。
-
要为用户账户分配不同的过滤器:
SELECT audit_log_filter_set_filter('log_nothing', '{ "filter": { "log": false } }'); SELECT audit_log_filter_set_user('user1@localhost', 'log_nothing');账户当前会话的过滤保持不受影响。后续连接将使用新过滤器进行过滤。对于此处显示的过滤器,这意味着来自
user1@localhost的新连接不会记录。
对于审计日志过滤,用户名和主机名的比较是区分大小写的。这与用于权限检查的主机名比较不区分大小写不同。
要移除过滤器,请执行以下操作:
SELECT audit_log_filter_remove_filter('log_nothing');
移除过滤器还会取消分配给它的任何用户,包括这些用户的当前会话。
刚刚描述的过滤函数立即影响审计过滤,并更新存储过滤器和用户帐户的 mysql 系统数据库中的审计日志表(参见 Audit Log Tables)。也可以直接修改审计日志表,使用诸如 INSERT、UPDATE 和 DELETE 等语句,但这些更改不会立即影响过滤。要刷新更改并使其生效,请调用 audit_log_filter_flush():
SELECT audit_log_filter_flush();
警告
audit_log_filter_flush() 应仅在直接修改审计表后使用,以强制重新加载所有过滤器。否则,应避免使用此函数。实际上,这是使用 UNINSTALL PLUGIN 和 INSTALL PLUGIN 卸载和重新加载 audit_log 插件的简化版本。
audit_log_filter_flush() 影响所有当前会话,并将它们从先前的过滤器中分离。当前会话将不再记录,除非它们断开连接并重新连接,或执行更改用户操作。
要确定过滤器是否分配给当前会话,请检查只读的 audit_log_filter_id 系统变量的会话值。如果值为 0,则未分配过滤器。非零值表示分配的过滤器的内部维护 ID:
mysql> SELECT @@audit_log_filter_id;
+-----------------------+
| @@audit_log_filter_id |
+-----------------------+
| 2 |
+-----------------------+
原文:
dev.mysql.com/doc/refman/8.0/en/audit-log-filter-definitions.html
8.4.5.8 编写审计日志过滤器定义
过滤器定义是JSON值。有关在 MySQL 中使用JSON数据的信息,请参见第 13.5 节,“JSON 数据类型”。
过滤器定义采用以下形式,其中*actions*表示过滤如何进行:
{ "filter": *actions* }
以下讨论描述了过滤器定义中允许的结构。
-
记录所有事件
-
记录特定事件类
-
记录特定事件子类
-
包容性和排他性日志记录
-
测试事件字段值
-
阻止特定事件的执行
-
逻辑运算符
-
引用预定义变量
-
引用预定义函数
-
替换事件字段值
-
替换用户过滤器
记录所有事件
要明确启用或禁用所有事件的记录,请在过滤器中使用log项:
{
"filter": { "log": true }
}
log值可以是true或false。
前面的过滤器启用了所有事件的记录。这等同于:
{
"filter": { }
}
记录行为取决于log值以及是否指定了class或event项:
-
如果指定了
log,则使用给定的值。 -
如果未指定
log,则如果未指定class或event项,则记录为true,否则为false(在这种情况下,class或event可以包括自己的log项)。
记录特定事件类
要记录特定类别的事件,请在过滤器中使用class项,其��name字段表示要记录的类别的名称:
{
"filter": {
"class": { "name": "connection" }
}
}
name值可以是connection、general或table_access,分别用于记录连接、一般或表访问事件。
上述过滤器启用了connection类中事件的记录。它等同于以下过滤器,其中log项目被明确指定:
{
"filter": {
"log": false,
"class": { "log": true,
"name": "connection" }
}
}
要启用多个类别的记录,将class值定义为JSON数组元素,命名这些类别:
{
"filter": {
"class": [
{ "name": "connection" },
{ "name": "general" },
{ "name": "table_access" }
]
}
}
注意
当在过滤器定义中同一级别出现给定项目的多个实例时,可以将项目值合并为数组值中的单个项目实例。上述定义可以写成这样:
{
"filter": {
"class": [
{ "name": [ "connection", "general", "table_access" ] }
]
}
}
记录特定事件子类
要选择特定的事件子类,使用包含命名子类的name项目的event项目。由event项目选择的事件的默认操作是记录它们。例如,此过滤器启用了命名事件子类的记录:
{
"filter": {
"class": [
{
"name": "connection",
"event": [
{ "name": "connect" },
{ "name": "disconnect" }
]
},
{ "name": "general" },
{
"name": "table_access",
"event": [
{ "name": "insert" },
{ "name": "delete" },
{ "name": "update" }
]
}
]
}
}
event项目还可以包含显式的log项目,以指示是否记录符合条件的事件。此event项目选择多个事件,并明确指示它们的记录行为:
"event": [
{ "name": "read", "log": false },
{ "name": "insert", "log": true },
{ "name": "delete", "log": true },
{ "name": "update", "log": true }
]
event项目还可以指示是否阻止符合条件的事件,如果包含abort项目。详情请参见阻止特定事件的执行。
表 8.35,“事件类和子类组合”描述了每个事件类的允许子类值。
表 8.35 事件类和子类组合
| 事件类 | 事件子类 | 描述 |
|---|---|---|
connection | connect | 连接初始化(成功或失败) |
connection | change_user | 会话期间使用不同用户/密码重新认证 |
connection | disconnect | 连接终止 |
general | status | 一般操作信息 |
message | internal | 内部生成的消息 |
message | user | 由audit_api_message_emit_udf()生成的消息 |
table_access | read | 表读取语句,如SELECT或INSERT INTO ... SELECT |
table_access | delete | 表删除语句,如DELETE或TRUNCATE TABLE |
table_access | insert | 表插入语句,如INSERT或REPLACE |
table_access | update | 表更新语句,如UPDATE |
| 事件类 | 事件子类 | 描述 |
表 8.36,“每个事件类和子类组合的记录和中止特性”描述了每个事件子类是否可以被记录或中止。
表 8.36 每个事件类和子类组合的记录和中止特性
| 事件类 | 事件子类 | 可以被记录 | 可以被中止 |
|---|---|---|---|
connection | connect | 是 | 否 |
connection | change_user | 是 | 否 |
connection | disconnect | 是 | 否 |
general | status | 是 | 否 |
message | internal | 是 | 是 |
message | user | 是 | 是 |
table_access | read | 是 | 是 |
table_access | delete | 是 | 是 |
table_access | insert | 是 | 是 |
table_access | update | 是 | 是 |
| 事件类 | 事件子类 | 可以被记录 | 可以被中止 |
包容性和独占性记录
可以定义包容性或独占性模式的过滤器:
-
包容性模式仅记录明确指定的项目。
-
独占模式记录除明确指定的项目之外的所有内容。
要执行包容性记录,全局禁用记录并为特定类启用记录。此过滤器记录connection类中的connect和disconnect事件,以及general类中的事件:
{
"filter": {
"log": false,
"class": [
{
"name": "connection",
"event": [
{ "name": "connect", "log": true },
{ "name": "disconnect", "log": true }
]
},
{ "name": "general", "log": true }
]
}
}
要执行独占性记录,全局启用记录并为特定类禁用记录。此过滤器记录除general类中的事件之外的所有内容:
{
"filter": {
"log": true,
"class":
{ "name": "general", "log": false }
}
}
此过滤器记录connection类中的change_user事件,message事件和table_access事件,因为不记录其他所有内容:
{
"filter": {
"log": true,
"class": [
{
"name": "connection",
"event": [
{ "name": "connect", "log": false },
{ "name": "disconnect", "log": false }
]
},
{ "name": "general", "log": false }
]
}
}
测试事件字段值
要基于特定事件字段值启用记录,请在指示字段名称及其预期值的log项内指定一个field项:
{
"filter": {
"class": {
"name": "general",
"event": {
"name": "status",
"log": {
"field": { "name": "general_command.str", "value": "Query" }
}
}
}
}
}
每个事件包含特定于事件类的字段,可以从过滤器内部访问以执行自定义过滤。
connection类中的事件指示会话期间发生的与连接相关的活动,例如用户连接到服务器或从服务器断开连接。表 8.37,“连接事件字段”指示了connection事件的允许字段。
表 8.37 连接事件字段
| 字段名称 | 字段类型 | 描述 |
|---|---|---|
status | 整数 | 事件状态:0:成功否则:失败 |
connection_id | 无符号整数 | 连接 ID |
user.str | 字符串 | 在认证期间指定的用户名 |
user.length | 无符号整数 | 用户名称长度 |
priv_user.str | 字符串 | 已认证的用户名称(帐户用户名) |
priv_user.length | 无符号整数 | 已认证的用户名称长度 |
external_user.str | 字符串 | 外部用户名称(由第三方认证插件提供) |
external_user.length | 无符号整数 | 外部用户名称长度 |
proxy_user.str | string | 代理用户名称 |
proxy_user.length | unsigned integer | 代理用户名称长度 |
host.str | string | 连接用户主机 |
host.length | unsigned integer | 连接用户主机长度 |
ip.str | string | 连接用户 IP 地址 |
ip.length | unsigned integer | 连接用户 IP 地址长度 |
database.str | string | 连接时指定的数据库名称 |
database.length | unsigned integer | 数据库名称长度 |
connection_type | integer | 连接类型:0 或 "::undefined": 未定义 1 或 "::tcp/ip": TCP/IP2 或 "::socket": Socket3 或 "::named_pipe": 命名管道 4 或 "::ssl": 带加密的 TCP/IP5 或 "::shared_memory": 共享内存 |
| 字段名称 | 字段类型 | 描述 |
"::*xxx*" 值是符号伪常量,可以代替文字数字值。 它们必须被引用为字符串,并且区分大小写。
general 类中的事件指示操作的状态代码及其详细信息。 表 8.38,“通用事件字段” 指示 general 事件的允许字段。
表 8.38 通用事件字段
| 字段名称 | 字段类型 | 描述 |
|---|---|---|
general_error_code | integer | 事件状态:0: OK 否则: 失败 |
general_thread_id | unsigned integer | 连接/线程 ID |
general_user.str | string | 认证期间指定的用户名 |
general_user.length | unsigned integer | 用户��称长度 |
general_command.str | string | 命令名称 |
general_command.length | unsigned integer | 命令名称长度 |
general_query.str | string | SQL 语句文本 |
general_query.length | unsigned integer | SQL 语句文本长度 |
general_host.str | string | 主机名 |
general_host.length | unsigned integer | 主机名长度 |
general_sql_command.str | string | SQL 命令类型名称 |
general_sql_command.length | unsigned integer | SQL 命令类型名称长度 |
general_external_user.str | string | 外部用户名称(由第三方认证插件提供) |
general_external_user.length | unsigned integer | 外部用户名称长度 |
general_ip.str | string | 连接用户 IP 地址 |
general_ip.length | unsigned integer | 连接用户 IP 地址长度 |
| 字段名称 | 字段类型 | 描述 |
general_command.str 指示命令名称:Query、Execute、Quit 或 Change user。
一个 general 事件,其中 general_command.str 字段设置为 Query 或 Execute,包含 general_sql_command.str 设置为指定 SQL 命令类型的值:alter_db、alter_db_upgrade、admin_commands 等。 可以在此语句显示的性能模式工具的最后组件中看到可用的 general_sql_command.str 值:
mysql> SELECT NAME FROM performance_schema.setup_instruments
WHERE NAME LIKE 'statement/sql/%' ORDER BY NAME;
+---------------------------------------+
| NAME |
+---------------------------------------+
| statement/sql/alter_db |
| statement/sql/alter_db_upgrade |
| statement/sql/alter_event |
| statement/sql/alter_function |
| statement/sql/alter_instance |
| statement/sql/alter_procedure |
| statement/sql/alter_server |
...
table_access 类中的事件提供有关对表的特定类型访问的信息。Table 8.39, “Table-Access Event Fields” 指示了table_access事件的允许字段。
Table 8.39 Table-Access Event Fields
| Field Name | Field Type | 描述 |
|---|---|---|
connection_id | 无符号整数 | 事件连接 ID |
sql_command_id | integer | SQL 命令 ID |
query.str | string | SQL 语句文本 |
query.length | 无符号整数 | SQL 语句文本长度 |
table_database.str | string | 与事件相关联的数据库名 |
table_database.length | 无符号整数 | 数据库名长度 |
table_name.str | string | 与事件相关联的表名 |
table_name.length | 无符号整数 | 表名长度 |
以下列表显示了哪些语句产生哪些表访问事件:
-
read事件:-
SELECT -
INSERT ... SELECT(用于SELECT子句中引用的表) -
REPLACE ... SELECT(用于SELECT子句中引用的表) -
UPDATE ... WHERE(用于WHERE子句中引用的表) -
HANDLER ... READ
-
-
delete事件:-
DELETE -
TRUNCATE TABLE
-
-
insert事件:-
INSERT -
INSERT ... SELECT(用于INSERT子句中引用的表) -
REPLACE -
REPLACE ... SELECT(用于REPLACE子句中引用的表) -
LOAD DATA -
LOAD XML
-
-
update事件:-
UPDATE -
UPDATE ... WHERE(用于UPDATE子句中引用的表)
-
阻止特定事件的执行
event 项可以包括一个abort项,指示是否阻止符合条件的事件执行。abort使得可以编写规则来阻止特定 SQL 语句的执行。
重要
理论上,具有足够权限的用户可能会错误地在审计日志过滤器中创建一个abort项,从而阻止自己和其他管理员访问系统。从 MySQL 8.0.28 开始,AUDIT_ABORT_EXEMPT权限可用于允许用户帐户的查询始终执行,即使abort项会阻止它们。具有此权限的帐户因此可以用于在审计配置错误后恢复对系统的访问。查询仍然记录在审计日志中,但不会被拒绝,而是由于权限而被允许。
在 MySQL 8.0.28 或更高版本中创建的带有SYSTEM_USER权限的帐户在创建时会自动分配AUDIT_ABORT_EXEMPT权限。在进行 MySQL 8.0.28 或更高版本的升级过程时,如果没有现有帐户被分配该权限,则现有帐户也会被分配AUDIT_ABORT_EXEMPT权限。
abort项必须出现在event项内。例如:
"event": {
"name": *qualifying event subclass names*
"abort": *condition*
}
对于由name项选择的事件子类,abort操作为真或假,取决于*condition*的评估。如果条件评估为真,则阻止事件。否则,事件继续执行。
*condition*规范可以简单到true或false,也可以更复杂,以至于评估取决于事件特征。
此过滤器阻止INSERT、UPDATE和DELETE语句:
{
"filter": {
"class": {
"name": "table_access",
"event": {
"name": [ "insert", "update", "delete" ],
"abort": true
}
}
}
}
这个更复杂的过滤器仅针对特定表(finances.bank_account)阻止相同的语句:
{
"filter": {
"class": {
"name": "table_access",
"event": {
"name": [ "insert", "update", "delete" ],
"abort": {
"and": [
{ "field": { "name": "table_database.str", "value": "finances" } },
{ "field": { "name": "table_name.str", "value": "bank_account" } }
]
}
}
}
}
}
由过滤器匹配并阻止的语句会向客户端返回错误:
ERROR 1045 (28000): Statement was aborted by an audit log filter
并非所有事件都可以被阻止(参见表 8.36,“每个事件类和子类组合的日志和中止特性”)。对于无法被阻止的事件,审计日志会向错误日志写入警告,而不是阻止它。
尝试定义一个过滤器,其中abort项出现在event项之外时,会出现错误。
逻辑运算符
逻辑运算符(and、or、not)允许构建复杂条件,从而编写更高级的过滤配置。以下log项仅记录具有特定值和长度的general_command字段的general事件:
{
"filter": {
"class": {
"name": "general",
"event": {
"name": "status",
"log": {
"or": [
{
"and": [
{ "field": { "name": "general_command.str", "value": "Query" } },
{ "field": { "name": "general_command.length", "value": 5 } }
]
},
{
"and": [
{ "field": { "name": "general_command.str", "value": "Execute" } },
{ "field": { "name": "general_command.length", "value": 7 } }
]
}
]
}
}
}
}
}
引用预定义变量
要在log条件中引用预定义变量,请使用variable项,该项接受name和value项并测试命名变量与给定值的相等性:
"variable": {
"name": "*variable_name*",
"value": *comparison_value*
}
如果*variable_name具有值comparison_value*,则为真,否则为假。
示例:
{
"filter": {
"class": {
"name": "general",
"event": {
"name": "status",
"log": {
"variable": {
"name": "audit_log_connection_policy_value",
"value": "::none"
}
}
}
}
}
}
每个预定义变量对应一个系统变量。通过编写测试预定义变量的过滤器,您可以通过设置相应的系统变量来修改过滤器操作,而无需重新定义过滤器。例如,通过编写测试audit_log_connection_policy_value预定义变量值的过滤器,您可以通过更改audit_log_connection_policy系统变量的值来修改过滤器操作。
audit_log_*xxx*_policy系统变量用于废弃的传统模式审计日志(参见第 8.4.5.10 节,“传统模式审计日志过滤”)。使用基于规则的审计日志过滤时,这些变量仍然可见(例如,使用SHOW VARIABLES),但除非编写包含引用它们的结构的过滤器,否则对它们的更改不会产生任何效果。
以下列表描述了variable项的允许的预定义变量:
-
audit_log_connection_policy_value此变量对应于
audit_log_connection_policy系统变量的值。该值是一个无符号整数。表 8.40,“audit_log_connection_policy_value Values”显示了允许的值以及相应的audit_log_connection_policy值。表 8.40 audit_log_connection_policy_value Values
值 对应的 audit_log_connection_policy 值 0或"::none"NONE1或"::errors"ERRORS2或"::all"ALL"::*xxx*"值是象征性伪常量,可以代替文字数字值。它们必须被引用为字符串,并且区分大小写。 -
audit_log_policy_value此变量对应于
audit_log_policy系统变量的值。该值是一个无符号整数。表 8.41,“audit_log_policy_value Values”显示了允许的值以及相应的audit_log_policy值。表 8.41 audit_log_policy_value Values
值 对应的 audit_log_policy 值 0或"::none"NONE1或"::logins"LOGINS2或"::all"ALL3或"::queries"QUERIES"::*xxx*"值是象征性伪常量,可以代替文字数字值。它们必须被引用为字符串,并且区分大小写。 -
audit_log_statement_policy_value此变量对应于
audit_log_statement_policy系统变量的值。该值是一个无符号整数。表 8.42,“audit_log_statement_policy_value Values”显示了允许的值以及相应的audit_log_statement_policy值。表 8.42 audit_log_statement_policy_value Values
值 对应的 audit_log_statement_policy 值 0或"::none"NONE1或"::errors"ERRORS2或"::all"ALL"::*xxx*"值是象征性伪常量,可以代替文字数字值。它们必须被引用为字符串,并且区分大小写。
引用预定义函数
要在 log 条件中引用预定义函数,请使用 function 项,该项接受 name 和 args 项分别指定函数名称及其参数:
"function": {
"name": "*function_name*",
"args": *arguments*
}
name 项应仅指定函数名称,不包括括号或参数列表。
args 项必须满足以下条件:
-
如果函数不需要参数,则不应提供
args项。 -
如果函数需要参数,则需要一个
args项,并且参数必须按照函数描述中列出的顺序给出。参数可以是预定义变量、事件字段或字符串或数字常量。
如果参数数量不正确或参数类型与函数所需的正确数据类型不符,将会出现错误。
示例:
{
"filter": {
"class": {
"name": "general",
"event": {
"name": "status",
"log": {
"function": {
"name": "find_in_include_list",
"args": [ { "string": [ { "field": "user.str" },
{ "string": "@"},
{ "field": "host.str" } ] } ]
}
}
}
}
}
}
前面的过滤器根据当前用户是否在 audit_log_include_accounts 系统变量中找到来确定是否记录 general 类 status 事件。该用户是使用事件中的字段构建的。
以下列表描述了 function 项的允许预定义函数:
-
audit_log_exclude_accounts_is_null()检查
audit_log_exclude_accounts系统变量是否为NULL。在定义与传统审计日志实现对应的过滤器时,此函数可能会有所帮助。参数:
无。
-
audit_log_include_accounts_is_null()检查
audit_log_include_accounts系统变量是否为NULL。在定义与传统审计日志实现对应的过滤器时,此函数可能会有所帮助。参数:
无。
-
debug_sleep(millisec)休眠指定的毫秒数。此函数用于性能测量。
debug_sleep()仅适用于调试版本。参数:
millisec:指定要休眠的毫秒数的无符号整数。
-
find_in_exclude_list(account)检查账户字符串是否存在于审计日志排除列表中(
audit_log_exclude_accounts系统变量的值)。参数:
account:指定用户账户名称的字符串。
-
find_in_include_list(account)检查账户字符串是否存在于审计日志包含列表中(
audit_log_include_accounts系统变量的值)。参数:
account:指定用户账户名称的字符串。
-
query_digest([str])此函数具有不同的行为,具体取决于是否给出参数:
-
没有参数时,
query_digest返回与当前事件中语句文本对应的语句摘要值。 -
有参数时,
query_digest返回一个布尔值,指示参数是否等于当前语句摘要。
参数:
str:此参数是可选的。如果提供,则指定要与当前事件中语句的摘要进行比较的语句摘要。
例子:
此
function项不包含参数,因此query_digest将当前语句摘要作为字符串返回:"function": { "name": "query_digest" }此
function项包含一个参数,因此query_digest返回一个布尔值,指示参数是否等于当前语句摘要:"function": { "name": "query_digest", "args": "SELECT ?" }此函数是在 MySQL 8.0.26 中添加的。
-
-
string_find(text, substr)检查
substr值是否包含在text值中。此搜索区分大小写。参数:
-
text:要搜索的文本字符串。 -
substr:要在*text*中搜索的子字符串。
-
事件字段值的替换
截至 MySQL 8.0.26,审计过滤器定义支持替换某些审计事件字段,以便记录的事件包含替换值而不是原始值。此功能使记录的审计记录可以包含语句摘要而不是文字语句,这对于可能暴露敏感值的 MySQL 部署非常有用。
审计事件中的字段替换工作原理如下:
-
字段替换在审计过滤器定义中指定,因此必须按照第 8.4.5.7 节,“审计日志过滤”中描述的方式启用审计日志过滤。
-
并非所有字段都可以被替换。表 8.43,“可替换事件字段”显示了哪些字段可以在哪些事件类中被替换。
表 8.43 可替换事件字段
事件类 字段名称 generalgeneral_query.strtable_accessquery.str -
替换是有条件的。过滤器定义中的每个替换规范都包括一个条件,使得可根据条件结果更改或保持替换字段不变。
-
如果发生替换,替换规范将使用允许用于此目的的函数指示替换值。
如表 8.43,“可替换事件字段”所示,目前唯一可替换的字段是包含语句文本的字段(出现在general和table_access类事件中)。此外,唯一允许用于指定替换值的函数是query_digest。这意味着唯一允许的替换操作是将语句文字文本替换为其对应的摘要。
因为字段替换发生在早期的审计阶段(在过滤期间),所以无论稍后写入的日志格式是 XML 还是 JSON 输出(即,审计日志插件生成的日志格式),都需要选择是写入语句文字文本还是摘要值。
字段替换可以在不同级别的事件粒度上进行:
-
要为类中的所有事件执行字段替换,请在类级别过滤事件。
-
要更细粒度地执行替换,可以包括额外的事件选择项。例如,您可以仅针对给定事件类的特定子类执行字段替换,或仅在具有特定特征的字段的事件中执行。
在过滤器定义中,通过包含print项来指定字段替换,其语法如下:
"print": {
"field": {
"name": "*field_name*",
"print": *condition*,
"replace": *replacement_value*
}
}
在print项中,其field项使用这三个项目来指示替换是如何发生的:
-
name:替换(可能)发生的字段。*field_name*必须是 Table 8.43,“可替换事件字段”中显示的字段之一。 -
print:确定是保留原始字段值还是替换它的条件:-
如果*
condition*评估为true,则该字段保持不变。 -
如果*
condition*评估为false,则发生替换,使用replace项的值。
要无条件替换字段,请指定条件如下:
"print": false -
-
replace:当print条件评估为false时要使用的替换值。使用function项指定*replacement_value*。
例如,此过滤器定义适用于general类中的所有事件,将语句文字替换为其摘要:
{
"filter": {
"class": {
"name": "general",
"print": {
"field": {
"name": "general_query.str",
"print": false,
"replace": {
"function": {
"name": "query_digest"
}
}
}
}
}
}
}
前面的过滤器使用这个print项来无条件地通过其摘要值替换general_query.str中包含的语句文字:
"print": {
"field": {
"name": "general_query.str",
"print": false,
"replace": {
"function": {
"name": "query_digest"
}
}
}
}
print项可以以不同的方式编写以实现不同的替换策略。刚刚显示的replace项使用这个function结构指定替换文本,以返回表示当前语句摘要的字符串:
"function": {
"name": "query_digest"
}
query_digest函数也可以以另一种方式使用,作为返回布尔值的比较器,从而使其在print条件中可用。为此,提供一个参数,指定一个比较语句摘要:
"function": {
"name": "query_digest",
"args": "*digest*"
}
在这种情况下,query_digest根据当前语句摘要是否与比较摘要相同返回true或false。以这种方式使用query_digest使得过滤器定义能够检测与特定摘要匹配的语句。以下结构中的条件仅对具有等于SELECT ?的摘要的语句为真,因此仅对不匹配摘要的语句进行替换:
"print": {
"field": {
"name": "general_query.str",
"print": {
"function": {
"name": "query_digest",
"args": "SELECT ?"
}
},
"replace": {
"function": {
"name": "query_digest"
}
}
}
}
仅为匹配摘要的语句执行替换,使用not来反转条件:
"print": {
"field": {
"name": "general_query.str",
"print": {
"not": {
"function": {
"name": "query_digest",
"args": "SELECT ?"
}
}
},
"replace": {
"function": {
"name": "query_digest"
}
}
}
}
假设您希望审计日志仅包含语句摘要而不是文字语句。为实现此目的,您必须对包含语句文本的所有事件执行替换;即general和table_access类别中的事件。较早的过滤器定义显示了如何无条件地替换general事件的语句文本。要对table_access事件执行相同操作,请使用类似但将类别从general更改为table_access,字段名称从general_query.str更改为query.str的过滤器:
{
"filter": {
"class": {
"name": "table_access",
"print": {
"field": {
"name": "query.str",
"print": false,
"replace": {
"function": {
"name": "query_digest"
}
}
}
}
}
}
}
结合general和table_access过滤器会产生一个执行所有包含语句文本事件替换的单一过滤器:
{
"filter": {
"class": [
{
"name": "general",
"print": {
"field": {
"name": "general_query.str",
"print": false,
"replace": {
"function": {
"name": "query_digest"
}
}
}
}
},
{
"name": "table_access",
"print": {
"field": {
"name": "query.str",
"print": false,
"replace": {
"function": {
"name": "query_digest"
}
}
}
}
}
]
}
}
仅对类别中的某些事件执行替换,向过滤器添加指示更具体替换发生时间的项目。以下过滤器适用于table_access类中的事件,但仅对insert和update事件执行替换(保持read和delete事件不变):
{
"filter": {
"class": {
"name": "table_access",
"event": {
"name": [
"insert",
"update"
],
"print": {
"field": {
"name": "query.str",
"print": false,
"replace": {
"function": {
"name": "query_digest"
}
}
}
}
}
}
}
}
该过滤器对应于列出的账户管理语句的general类事件执行替换(效果是隐藏语句中的凭据和数据值):
{
"filter": {
"class": {
"name": "general",
"event": {
"name": "status",
"print": {
"field": {
"name": "general_query.str",
"print": false,
"replace": {
"function": {
"name": "query_digest"
}
}
}
},
"log": {
"or": [
{
"field": {
"name": "general_sql_command.str",
"value": "alter_user"
}
},
{
"field": {
"name": "general_sql_command.str",
"value": "alter_user_default_role"
}
},
{
"field": {
"name": "general_sql_command.str",
"value": "create_role"
}
},
{
"field": {
"name": "general_sql_command.str",
"value": "create_user"
}
}
]
}
}
}
}
}
有关可能的general_sql_command.str值的信息,请参阅测试事件字段值。
替换用户过滤器
在某些情况下,过滤器定义可以动态更改。为此,在现有的filter中定义一个filter配置。例如:
{
"filter": {
"id": "main",
"class": {
"name": "table_access",
"event": {
"name": [ "update", "delete" ],
"log": false,
"filter": {
"class": {
"name": "general",
"event" : { "name": "status",
"filter": { "ref": "main" } }
},
"activate": {
"or": [
{ "field": { "name": "table_name.str", "value": "temp_1" } },
{ "field": { "name": "table_name.str", "value": "temp_2" } }
]
}
}
}
}
}
}
当子过滤器中的activate项评估为true时,将激活新的过滤器。在顶层filter中使用activate是不允许的。
通过在子过滤器中使用ref项引用原始过滤器id,可以将新过滤器替换为原始过滤器。
所示的过滤器的操作方式如下:
-
main过滤器等待table_access事件,无论是update还是delete。 -
如果在
temp_1或temp_2表上发生update或deletetable_access事件,则将过滤器替换为内部过滤器(没有id,因为没有必要显式引用它)。 -
如果命令结束被标记(
general/status事件),则会向审计日志文件写入条目,并将过滤器替换为main过滤器。
该过滤器用于记录更新或删除temp_1或temp_2表中任何内容的语句,例如:
UPDATE temp_1, temp_3 SET temp_1.a=21, temp_3.a=23;
该语句生成多个table_access事件,但审计日志文件仅包含general / status条目。
注意
在定义中使用的任何id值仅与该定义相关。它们与audit_log_filter_id系统变量的值无关。
8.4.5.9 禁用审计日志
audit_log_disable变量在 MySQL 8.0.28 中引入,允许禁用所有连接和已连接会话的审计日志记录。audit_log_disable变量可以在 MySQL 服务器选项文件中设置,在命令行启动字符串中设置,或者在运行时使用SET语句设置;例如:
SET GLOBAL audit_log_disable = true;
将audit_log_disable设置为 true 会禁用审计日志插件。当audit_log_disable重新设置为false时(默认设置),插件将重新启用。
使用audit_log_disable = true启动审计日志插件会生成警告(ER_WARN_AUDIT_LOG_DISABLED),并显示以下消息:Audit Log is disabled. Enable it with audit_log_disable = false. 将audit_log_disable设置为 false 也会生成警告。当audit_log_disable设置为 true 时,审计日志函数调用和变量更改会生成会话警告。
设置audit_log_disable的运行时值需要AUDIT_ADMIN权限,除了通常需要设置全局系统变量运行时值的SYSTEM_VARIABLES_ADMIN权限(或已弃用的SUPER权限)。
原文:
dev.mysql.com/doc/refman/8.0/en/audit-log-legacy-filtering.html
8.4.5.10 遗留模式审计日志过滤
注意
此部分描述了遗留审计日志过滤,适用于安装了audit_log插件但没有规则过滤所需的审计表和函数的情况。
遗留模式审计日志过滤已在 MySQL 8.0.34 中弃用。
审计日志插件可以过滤审计事件。这使您可以控制基于事件来源账户或事件状态将审计事件写入审计日志文件。连接事件和语句事件的状态过滤是分开进行的。
-
按账户进行遗留事件过滤
-
按状态进行遗留事件过滤
按账户进行遗留事件过滤
要根据发起账户过滤审计事件,请在服务器启动或运行时设置以下系统变量中的一个(而不是两个)。这些已弃用的变量仅适用于遗留审计日志过滤。
-
audit_log_include_accounts:要包含在审计日志中的账户。如果设置了此变量,则只对这些账户进行审计。 -
audit_log_exclude_accounts:要排除在审计日志中的账户。如果设置了此变量,则除了这些账户外,所有其他账户都会被审计。
任一变量的值可以是NULL,或包含一个或多个以*user_name*@*host_name*格式逗号分隔的账户名字符串。默认情况下,这两个变量都是NULL,在这种情况下,不进行账户过滤,对所有账户进行审计。
对audit_log_include_accounts或audit_log_exclude_accounts的修改仅影响在修改后创建的连接,而不影响现有连接。
示例:要仅为user1和user2本地主机账户启用审计日志记录,请像这样设置audit_log_include_accounts系统变量:
SET GLOBAL audit_log_include_accounts = 'user1@localhost,user2@localhost';
audit_log_include_accounts或audit_log_exclude_accounts只能同时有一个为非NULL:
-
如果设置了
audit_log_include_accounts,服务器会将audit_log_exclude_accounts设置为NULL。 -
如果您尝试设置
audit_log_exclude_accounts,除非audit_log_include_accounts为NULL,否则会出现错误。在这种情况下,您必须首先通过将其设置为NULL来清除audit_log_include_accounts。
-- This sets audit_log_exclude_accounts to NULL
SET GLOBAL audit_log_include_accounts = *value*;
-- This fails because audit_log_include_accounts is not NULL
SET GLOBAL audit_log_exclude_accounts = *value*;
-- To set audit_log_exclude_accounts, first set
-- audit_log_include_accounts to NULL
SET GLOBAL audit_log_include_accounts = NULL;
SET GLOBAL audit_log_exclude_accounts = *value*;
如果检查任一变量的值,请注意SHOW VARIABLES将NULL显示为空字符串。要将NULL显示为NULL,请改用SELECT:
mysql> SHOW VARIABLES LIKE 'audit_log_include_accounts';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| audit_log_include_accounts | |
+----------------------------+-------+
mysql> SELECT @@audit_log_include_accounts;
+------------------------------+
| @@audit_log_include_accounts |
+------------------------------+
| NULL |
+------------------------------+
如果用户名或主机名需要引用,因为它包含逗号、空格或其他特殊字符,请使用单引号引用。如果变量值本身用单引号引用,则每个内部单引号都要加倍或用反斜杠转义。以下语句分别为本地root账户启用审计日志记录,并且虽然引用样式不同,但是等效的:
SET GLOBAL audit_log_include_accounts = 'root@localhost';
SET GLOBAL audit_log_include_accounts = '''root''@''localhost''';
SET GLOBAL audit_log_include_accounts = '\'root\'@\'localhost\'';
SET GLOBAL audit_log_include_accounts = "'root'@'localhost'";
如果启用了ANSI_QUOTES SQL 模式,则最后一个语句将无效,因为在该模式下,双引号表示标识符引用,而不是字符串引用。
按状态进行传统事件过滤
要根据状态过滤审计事件,请在服务器启动或运行时设置以下系统变量。这些已弃用的变量仅适用于传统审计日志过滤。对于 JSON 审计日志过滤,不同的状态变量适用;请参阅 Audit Log Options and Variables。
-
audit_log_connection_policy:连接事件的记录策略 -
audit_log_statement_policy:语句事件的记录策略
每个变量都可以取值ALL(记录所有相关事件;这是默认值)、ERRORS(仅记录失败事件)或NONE(不记录事件)。例如,要记录所有语句事件但仅记录失败的连接事件,请使用以下设置:
SET GLOBAL audit_log_statement_policy = ALL;
SET GLOBAL audit_log_connection_policy = ERRORS;
另一个策略系统变量audit_log_policy可用,但不像audit_log_connection_policy和audit_log_statement_policy那样提供更多控制。它只能在服务器启动时设置。
注意
自 MySQL 8.0.34 起,audit_log_policy传统模式系统变量已弃用。
在运行时,它是一个只读变量。它可以取ALL(记录所有事件;这是默认值)、LOGINS(记录连接事件)、QUERIES(记录语句事件)或NONE(不记录事件)的值。对于这些值中的任何一个,审计日志插件都会记录所有选定的事件,不区分成功或失败。在启动时使用audit_log_policy的工作方式如下:
-
如果您未设置
audit_log_policy或将其设置为默认值ALL,则对于audit_log_connection_policy或audit_log_statement_policy的任何显式设置均按照指定的方式应用。如果未指定,则它们默认为ALL。 -
如果您将
audit_log_policy设置为非ALL值,则该值优先,并用于设置audit_log_connection_policy和audit_log_statement_policy,如下表所示。如果您还将这些变量中的任一设置为非默认值ALL,服务器将向错误日志写入消息,指示它们的值被覆盖。启动时的 audit_log_policy 值 结果的 audit_log_connection_policy 值 结果的 audit_log_statement_policy 值 LOGINSALLNONEQUERIESNONEALLNONENONENONE