MySQL审核日志记录配置
本节介绍如何配置审核日志记录特征,例如审核日志插件向其写入事件的文件,写入事件的格式以及是否启用日志文件压缩和加密。
- 审核日志文件名
- 审核日志文件格式
- 审核日志文件压缩
- 审核日志文件加密
- 审核日志文件手动解压缩和解密
- MySQL 8.0.17之前的审核日志文件加密
- 审核日志文件空间管理和名称轮换
- 审核日志写入策略
- 审核日志文件读取
注意此处描述的加密功能自MySQL 8.0.17起适用,但本节将当前的加密功能与以前的更多受限功能进行了比较(请参阅 MySQL 8.0.17之前的审核日志文件加密)。
有关影响审核日志记录的用户定义函数和系统变量的其他信息,请参阅审核日志函数和审核日志选项和变量。
审核日志插件还可以根据事件内容或事件源自的帐户,控制将哪些审核事件写入审核日志文件。请参见“MySQL审核日志过滤”。
审核日志文件名
要配置审核日志文件名,请 udit_log_file
在服务器启动时设置系统变量。默认情况下,该名称 udit.log
位于服务器数据目录中。出于安全原因,请将审核日志文件写入只有MySQL服务器和有正当理由参见日志的用户才能访问的目录。
该插件将 udit_log_file
值解释为由基本名称和可选后缀组成。如果启用了压缩或加密,则有效文件名(实际上用于创建日志文件的名称)与配置的文件名不同,因为它具有其他后缀:
- 如果启用了压缩,则插件会添加后缀
.gz
。 - 如果启用了加密,则插件会添加后缀,其中指示用于日志文件操作的加密密码。
.pwd_id.enc
pwd_id
有效的审核日志文件名是将适用的压缩和加密后缀添加到配置的文件名后得到的名称。例如,如果配置的 udit_log_file
值为 udit.log
,则有效文件名是下表中显示的值之一。
启用的功能 | 有效文件名 |
---|---|
没有压缩或加密 | udit.log |
压缩 | udit.log.gz |
加密 | udit.log.pwd_id.enc |
压缩,加密 | udit.log.gz.pwd_id.enc |
pwd_id
表示用于加密或解密文件的密码的ID。pwd_id
格式为pwd_timestamp-seq
,其中:
pwd_timestamp
是UTC值,其格式指示创建密码的时间。YYYYMMDDThhmmss
seq
是一个序列号。序列号从1开始,对于具有相同pwd_timestamp
值的密码,序列号增加。
以下是一些示例密码ID值:
20190403T142359-1 20190403T142400-1 20190403T142400-2
审核日志插件将加密密码存储在密钥环中(请参见“ MySQL密钥环”)。密钥环中的审核日志密码的ID基于pwd_id
值,前缀为 udit_log-
。对于刚刚显示的示例密码ID,相应的密钥环ID为:
udit_log-20190403T142359-1 audit_log-20190403T142400-1 audit_log-20190403T142400-2
审核日志插件当前用于加密的密码是最大的密码pwd_timestamp
。如果多个密码具有pwd_timestamp
最大值,则当前密码为序列号最大的密码。例如,在前面的一组密码ID中,其中两个时间戳最大20190403T142400
,因此当前密码是序列号(2)最大的一个。
审核日志插件根据有效的审核日志文件名在初始化和终止期间执行某些操作:
- 在初始化期间,插件会检查是否已存在带有审核日志文件名的文件,如果存在,则将其重命名。(在这种情况下,插件假定在运行审核日志插件的情况下意外退出了先前的服务器调用。)然后,插件将写入新的空审核日志文件。
- 在终止期间,插件将重命名审核日志文件。
- 文件重命名(无论是在插件初始化还是终止期间)均根据自动日志文件轮换的常规规则进行;请参阅自动审核日志文件轮换。
审核日志文件格式
要配置审核日志文件格式,请 udit_log_format
在服务器启动时设置系统变量。默认情况下,格式为NEW
(新型XML格式)。有关每种格式的详细信息,请参见“MySQL审核日志文件格式”。
如果您进行更改 udit_log_format
,建议您也进行更改 udit_log_file
。否则,将存在两组具有相同基本名称但格式不同的日志文件。
审核日志文件压缩
可以为任何日志记录格式启用审核日志文件压缩。
要配置审核日志文件压缩,请 udit_log_compression
在服务器启动时设置系统变量。允许值为NONE
(不压缩;默认值)和GZIP
(GNU Zip压缩)。
如果同时启用了压缩和加密,则压缩将在加密之前进行。要手动恢复原始文件,请先将其解密,然后再将其解压缩。请参见审核日志文件手动解压缩和解密。
审核日志文件加密
可以为任何日志记录格式启用审核日志文件加密。加密基于用户定义的密码(审核日志插件生成的初始密码除外)。要使用此功能,必须启用MySQL密钥环,因为审核日志记录将其用于密码存储。任何钥匙圈插件都可以使用;有关说明,请参见“ MySQL密钥环”。
要配置审核日志文件加密,请 udit_log_encryption
在服务器启动时设置系统变量。允许的值为NONE
(不加密;默认值)和AES
(AES-256-CBC密码加密)。
要设置或获取加密密码,请使用以下用户定义的函数(UDF):
- 要设置当前的加密密码,请调用
udit_log_encryption_password_set()
。此功能将新密码存储在密钥环中,如果启用了加密,它还将执行日志文件轮换操作,重命名当前日志文件,并开始使用该密码加密的新日志文件。文件重命名是根据自动循环日志文件的通常规则进行的;请参阅自动审核日志文件轮换。 要获取当前的加密密码,请
udit_log_encryption_password_get()
不带参数调用。要通过ID获得密码,请传递一个参数,该参数指定当前密码或已归档密码的密钥环ID。要确定存在哪些审核日志密钥环ID,请查询“性能模式”
keyring_keys
表:mysql>
SELECT *FROM performance_schem .keyring_keysWHERE KEY_ID LIKE 'audit_log%'ORDER BY KEY_ID; +----------------------------- +----------- +---------------- + | KEY_ID | KEY_OWNER | BACKEND_KEY_ID | +----------------------------- +----------- +---------------- + | audit_log -20190415T152248 -1 | | | | audit_log -20190415T153507 -1 | | | | audit_log -20190416T125122 -1 | | | | audit_log -20190416T141608 -1 | | | +----------------------------- +----------- +---------------- +
有关审核日志加密功能的其他信息,请参阅审核日志功能。
审核日志插件初始化时,如果发现启用了日志文件加密,则会检查密钥环是否包含审核日志加密密码。如果不是,该插件会自动生成一个随机的初始加密密码,并将其存储在密钥环中。要发现此密码,请调用 udit_log_encryption_password_get()
。
如果同时启用了压缩和加密,则压缩将在加密之前进行。要手动恢复原始文件,请先将其解密,然后再将其解压缩。请参见审核日志文件手动解压缩和解密。
审核日志文件手动解压缩和解密
审计日志文件可以使用标准工具解压缩和解密。仅应对已关闭(存档)且不再使用的日志文件进行此操作,而不应对审计日志插件当前正在写入的日志文件进行此操作。您可以识别存档的日志文件,因为审核日志插件已将其重命名为在基本名称之后的文件名中包含时间戳。
对于此讨论,假定将 udit_log_file
其设置为 udit.log
。在这种情况下,存档的审核日志文件具有下表中显示的名称之一。
启用的功能 | 存档文件名 |
---|---|
没有压缩或加密 | udit.timestamp.log |
压缩 | udit.timestamp.log.gz |
加密 | udit.timestamp.log.pwd_id.enc |
压缩,加密 | udit.timestamp.log.gz.pwd_id.enc |
如审核日志文件名中所述,pwd_id
格式为pwd_timestamp-seq
。因此,已存档的加密日志文件的名称实际上包含两个时间戳。第一个指示旋转时间,第二个指示创建密码的时间。
考虑以下一组归档的加密日志文件名:
udit.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
如果同时为审计日志记录启用了压缩和加密,则压缩将在加密之前进行。在这种情况下,文件名已添加.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 | udit_log | udit_log-pwd_id |
密码记录 | 没有 | 是 |
密码过期 | 没有 | 是 |
在MySQL 8.0.17之前,没有密码历史记录,因此设置新密码将使旧密码不可访问,从而使MySQL企业审计无法读取使用旧密码加密的日志文件。如果您需要手动解密这些文件,则必须记录以前的密码。
如果从较低版本升级到MySQL 8.0.17或更高版本时启用了审计日志文件加密,则审计日志插件将执行以下升级操作:
- 插件初始化期间,插件会检查密钥环ID为的加密密码
udit_log
。如果找到一个,则插件使用格式为密钥环ID的密码重复该密码,并将其用作当前的加密密码。udit_log-pwd_id
- 现有的加密日志文件的后缀为
.enc
。插件不会将它们重命名为具有后缀,但是只要ID为的密钥保留在密钥环中就可以读取它们。.pwd_id.enc
udit_log
- 发生密码清理时,如果插件使具有密钥环ID 格式的任何密码失效,则也将使具有密钥环ID的密码失效(如果存在)。(在这一点上,有一个后缀加密的日志文件,而不是成为插件无法读取,所以假设你不再需要他们。)
udit_log-pwd_id
udit_log
.enc
.pwd_id.enc
审核日志文件空间管理和名称轮换
审核日志文件可能会变得很大,并占用大量磁盘空间。为了管理其日志文件使用的空间,审核日志插件提供了手动或自动旋转日志文件的功能。轮换功能使用 udit_log_flush
和 udit_log_rotate_on_size
系统变量:
- 默认情况下,
udit_log_rotate_on_size=0
除非手动执行,否则不会发生日志轮换。在这种情况下,请udit_log_flush
在手动重命名之后使用来关闭并重新打开当前日志文件。 - 如果
udit_log_rotate_on_size
大于0,则对当前日志文件的写入导致其大小超过该值时,将自动旋转。审核日志插件将关闭文件,将其重命名,然后打开一个新的日志文件。启用自动旋转后,udit_log_flush
无效。 - 自动旋转还会在其他几种情况下发生,这将在后面介绍。
注意重命名的日志文件不会自动删除。例如,使用基于大小的日志文件旋转时,重命名的日志文件不会在名称序列的末尾旋转。相反,它们具有唯一的名称并且会无限期地累积。为避免过多使用空间,请定期删除旧文件,并在必要时首先对其进行备份。如果备份的日志文件已加密,则还应将相应的加密密码备份到安全的地方,以备日后解密时使用。
以下讨论更详细地描述了日志文件轮换方法。
- 手动审核日志文件轮换
- 自动审核日志文件轮换
手动审核日志文件轮换
如果 udit_log_rotate_on_size=0
(默认),除非手动执行,否则不会发生日志轮换。在这种情况下,当 udit_log_flush
值从禁用更改为启用时,审核日志插件将关闭并重新打开日志文件。日志文件重命名必须在服务器外部进行。假设日志文件名 udit.log
,并要保持三个最近的日志文件,通过名称循环 udit.log.1
通过 udit.log.3
。在Unix上,手动执行旋转,如下所示:
在命令行中,重命名当前日志文件:
mv audit.log.2 audit.log.3 mv audit.log.1 audit.log.2 mv audit.log audit.log.1
该策略将覆盖当前
udit.log.3
内容,从而限制了归档日志文件的数量及其使用的空间。此时,该插件仍在写入当前的日志文件,该文件已重命名为
udit.log.1
。连接到服务器并刷新日志文件,以便插件将其关闭并重新打开一个新udit.log
文件:SET GLOBAL audit_log_flush =ON ;udit_log_flush
特殊之处在于它的值保持不变,OFF
因此您无需在再次启用它执行另一次刷新之前显式禁用它。
注意如果启用了压缩或加密,则日志文件名将包括表示启用的功能的后缀,以及如果启用了加密的密码ID。如果文件名包含密码ID,请确保将该ID保留在您手动重命名的任何文件的名称中,以便可以确定用于解密操作的密码。
注意对于JSON格式的日志记录,手动重命名审核日志文件使它们无法用于日志读取功能,因为审核日志插件不再可以确定它们是日志文件序列的一部分(请参阅审核日志文件读取)。考虑设置
udit_log_rotate_on_size
大于0来使用基于尺寸的旋转。
自动审核日志文件轮换
如果 udit_log_rotate_on_size
大于0,则设置 udit_log_flush
无效。取而代之的是,每当写入当前日志文件导致其大小超过该 udit_log_rotate_on_size
值时,审核日志插件就会关闭该文件,对其重命名并打开一个新的日志文件。
在以下情况下也会发生自动旋转:
- 在插件初始化期间,如果已经存在带有审核日志文件名的文件(请参阅审核日志文件名)。
- 在插件终止期间。
udit_log_encryption_password_set()
调用该功能以设置加密密码时,如果启用了加密。(如果禁用加密,则不会旋转。)
该插件通过在基本名称之后插入时间戳来重命名原始文件。例如,如果文件名为 udit.log
,则插件会将其重命名为值 udit.20190115T140633.log
。时间戳是格式的UTC值。时间戳指示XML日志记录的循环时间,以及上一次写入JSON日志文件的事件的时间戳。YYYYMMDDThhmmss
审核日志写入策略
审核日志插件可以使用多种策略中的任何一种来进行日志写入。无论采用何种策略,日志记录都是在尽力而为的基础上进行的,并且不能保证一致性。
要指定写策略,请 udit_log_strategy
在服务器启动时设置系统变量。默认情况下,策略值为,ASYNCHRONOUS
并且插件异步记录到缓冲区,等待缓冲区是否已满。可以告诉插件不要等待(PERFORMANCE
)或同步登录,可以使用文件系统缓存(SEMISYNCHRONOUS
)或sync()
在每次写请求(SYNCHRONOUS
)之后通过调用强制输出。
对于异步写入策略, udit_log_buffer_size
系统变量是缓冲区大小(以字节为单位)。在服务器启动时设置此变量以更改缓冲区大小。插件使用单个缓冲区,它在初始化时分配,在终止时删除。插件不会为非异步写策略分配此缓冲区。
异步日志记录策略具有以下特征:
- 对服务器性能和可伸缩性的影响最小。
- 阻塞在最短时间内生成审核事件的线程;也就是说,分配缓冲区的时间加上将事件复制到缓冲区的时间。
- 输出进入缓冲区。一个单独的线程处理从缓冲区到日志文件的写入。
使用异步日志记录,如果在写入文件时发生问题,或者插件无法完全关闭(例如,在服务器主机意外退出的情况下),则可能会损害日志文件的完整性。为了降低这种风险,请设置 udit_log_strategy
为使用同步日志记录。
PERFORMANCE
策略的一个缺点是,当缓冲区已满时,它将丢弃事件。对于负载很重的服务器,审核日志可能缺少事件。
审核日志文件读取
审核日志插件可启用书签和读取JSON格式的审核日志文件。(这些功能不适用于以其他日志格式编写的文件。)
审核日志插件初始化并配置为JSON日志记录时,它将使用包含审核日志文件(由 udit_log_file
值确定)的目录作为搜索可读审核日志文件的位置。为此,它使用值 udit_log_file
来确定文件的基本名称和后缀值,然后查找名称与以下模式匹配的[...]
文件,其中表示可选的文件名部分:
basename[.timestamp].suffix[.gz][[.pwd_id].enc]
如果文件名以结尾,则文件被加密.enc
。插件确定读取它所需的解密密码的密钥环ID,如下所示:
- 如果
.enc
以开头pwd_id
,则密钥环ID为。udit_log-pwd_id
- 如果
.enc
之前没有pwd_id
,则该文件的旧名称来自实施密码历史记录之前的名称。密钥环ID为udit_log
。
该插件会打开与该模式匹配的每个文件,检查该文件是否确实包含JSON审核记录,并使用每个文件的第一条记录中的时间戳对文件进行排序,以构建要与日志读取功能一起使用的文件列表。
该插件不能包含在手动重命名且与前面的模式不匹配的序列文件中,或使用密钥环中不再可用的密码加密的序列文件中。
要从审核日志中读取事件,请使用以下用户定义的函数(UDF):
udit_log_read_bookmark()
返回JSON
表示最近写入的审核日志事件的书签的字符串。该书签适合于传递以udit_log_read()
指示该功能从何处开始阅读。书签示例:{ "timestamp": "2019-10-03 21:03:44", "id": 0 }
udit_log_read()
从审核日志中读取事件,并返回JSON
包含审核事件数组的字符串。
udit_log_read()
使用当前书签的示例调用:
mysql>SELECT audit_log_read( udit_log_read_bookmark()); +----------------------------------------------------------------------- + | audit_log_read(audit_log_read_bookmark()) | +----------------------------------------------------------------------- + | [ {"timestamp":"2019 -10 -03 22:41:24","id":0,"class":"connection", ... | +----------------------------------------------------------------------- +
udit_log_read()
返回值中的每个事件都是JSON
哈希,除了最后一个数组元素可能是一个值,该值指示没有后续事件可读取。例如:JSON
null
[ { "timestamp": "2019-10-03 22:08:08", "id": 10, "class": "general", "event": "status", ... }, { "timestamp": "2019-10-03 22:08:08", "id": 11, "class": "connection", "event": "disconnect", ... }, { "timestamp": "2019-10-03 13:39:33", "id": 0, "class": "connection", "event": "connect", ... }, { "timestamp": "2019-10-03 13:39:33", "id": 1, "class": "general", "event": "status", ... }, { "timestamp": "2019-10-03 13:39:33", "id": 2, "class": "connection", "event": "disconnect", ... }, null ]
udit_log_read()
像这样使用:
- 对于
udit_log_read()
会话中的第一个呼叫,请传递一个书签,以指示从何处开始阅读。 - 如果返回的数组的最终值不是一个值,则在刚刚读取的事件之后会有更多事件,可以在不使用书签参数或不使用书签参数的情况下调用它们。不带参数的情况下,读取继续进行下一个未读事件。使用书签参数时,将从书签继续读取。
JSON
null
udit_log_read()
- 如果返回的数组的最终值是一个值,则不再有其他事件需要读取,并且下一次调用必须包含书签参数。
JSON
null
udit_log_read()
书签是一个JSON
散列,指示在何处读取多少内容。以下各项在书签值中很重要(其他项将被忽略):
timestamp
,id
:第一个事件的审计日志中的位置读取。这两个项目都必须存在才能完全指定位置。max_array_length
:要从日志中读取的最大事件数。如果省略,则默认为读取到日志末尾或读取缓冲区已满,以先到者为准。
从任一日志读取函数返回的结果都是字符串,可以根据需要进行操作。假设书签具有以下值:
mysql>SET @mark := audit_log_read_bookmark(); mysql>SELECT @mark; +------------------------------------------------- + | @mark | +------------------------------------------------- + | { "timestamp": "2019 -10 -03 16:10:28", "id": 2 } | +------------------------------------------------- +
udit_log_read()
使用该书签进行调用可以返回多个事件。要限制 udit_log_read()
读取单个事件,请在书签中添加max_array_length
一个值为1 的项目。例如,按如下方式转换前面的书签:
mysql>SET @mark := JSON_SET(@mark, '$.max_array_length', 1); mysql>SELECT @mark; +---------------------------------------------------------------------- + | @mark | +---------------------------------------------------------------------- + | {"id": 2, "timestamp": "2019 -10 -03 16:10:28", "max_array_length": 1} | +---------------------------------------------------------------------- +
修改后的书签传递给时 udit_log_read()
,将产生单个审核记录的结果。
在MySQL 8.0.19之前,审核日志UDF的字符串返回值是二进制字符串。要将二进制字符串与需要非二进制字符串的函数一起使用(例如,操作JSON
值的函数),请执行转换为utf8mb4
。例如,在将书签传递到之前JSON_SET()
,将其转换如下:
SET @mark = CONVERT(@markUSING utf8mb4);
即使对于MySQL 8.0.19及更高版本,也可以包含该语句,因为对于那些版本,它基本上是无操作且无害的。
要对 udit_log_read()
读取的字节数设置限制,请设置 udit_log_read_buffer_size
系统变量。从MySQL 8.0.12开始,此变量的默认值为32KB,可以在运行时设置。每个客户端应为其设置 udit_log_read_buffer_size
适当的会话值 udit_log_read()
。MySQL 8.0.12之前的版本 udit_log_read_buffer_size
的默认值为1MB,会影响所有客户端,并且只能在服务器启动时进行更改。
每次调用都会 udit_log_read()
返回缓冲区大小内适合的尽可能多的可用项目,而跳过不适合缓冲区大小的项目。鉴于此行为,在评估应用程序的适当缓冲区大小时,请考虑以下因素:
- 在呼叫数量
udit_log_read()
和每次呼叫返回的项目之间需要权衡。使用较小的缓冲区大小,调用返回较少的项目,因此需要更多的调用。缓冲区越大,调用返回的项越多,因此需要的调用越少。 - 使用较小的缓冲区大小(例如默认大小为32KB),项目将有更大的机会超过缓冲区大小并
udit_log_read()
跳过它们。跳过的项目会生成警告。
有关审核日志读取功能的其他信息,请参见审核日志功能。