Libvirt 被广泛应用于管理 Linux® 上的虚拟化环境。它提供了各种丰富的功能,包括客户机生命周期管理、资源分配、资源管理(使用 cgroups),以及通过 SELinux 实现安全强制 (security enforcement) 等。所有这些操作均是由 libvirt 执行,主要处理被分配给客户机或从客户机释放的主机资源。
从 0.9.0 版本开始,libvirt 就能够与主机上的 Linux 审计子系统通信,发出有关若干操作的记录。这是一项重要特性,允许系统管理员、审计员,甚至其他应用程序访问虚拟化环境中详细的变更历史,包括客户机生命周期操作,以及主机资源到客户机的分配变化。
Linux 审计子系统能够处理来自不同来源的许多种操作,而不仅仅是 libvirt 发出的与虚拟化有关的事件。审计系统常用于跟踪文件变更、来自 SELinux 或 AppAmor 的安全拒绝 (security denial),以及系统调用。这些不同的事件包含对虚拟机审计有用的信息。
例如,SELinux 策略可能禁止访问某个分配给客户机的资源,这将生成一个 AVC (Access Vector Cache) 拒绝记录。尽管该事件不是由 libvirt 发出,但是它与一个客户机关联。类似地,许多事件都可以与客户机关联,其中包含由内核或用户空间内的程序生成的异常事件。
在下一节中,我们将快速了解审计系统如何工作,libvirt 如何与之交互,以及如何从审计日志获取与客户机相关的信息。
Linux 内核负责生成大部分审计事件,例如系统调用事件、文件监视事件和安全事件。但是用户应用程序也可以生成事件。
Linux 审计系统的核心组件就是内核。由其他组件生成的所有事件首先将被发送给内核。在内核中,将应用一系列规则来决定是否应当丢弃某个事件。图 1 展示了构成 Linux 审计系统的组件。
图 1. 审计系统组件

内核中的审计规则可以通过 auditctl
命令修改。一些具有特殊功能的应用程序,如 libvirt
daemon,被认为是可信任的应用程序,能够向内核发送审计事件。在应用了规则后,内核向 audit
daemon 发送审计事件。然后 audit
daemon 将记录写入到磁盘,并使用 auditspd daemon 分配定制操作。
其他组件(ausearch、aureport、aulast 和 auvirt)均为用户空间工具,用于解析审计日志并以更简单的形式呈现信息。auvirt 工具专门用于呈现与虚拟化有关的事件和关联事件。
需要注意审计事件和审计记录之间的区别。审计事件是由受审计的用户或应用程序所发起的一个操作,例如:文件打开事件就是一个可以受审计的事件。此外,审计记录包含仅与事件的某个方面有关的信息。因此,一个事件可能跨越审计日志中的一个或多个记录。以文件打开为例,该事件可以生成两条记录,一条记录包含与被调用的开放 syscall 有关的信息,另一条记录包含与所访问的文件有关的记录。(参见 清单 1 获得示例)。
清单 1. 打开文件事件的审计记录(使用独立区域显示以清晰呈现)
type=SYSCALL msg=audit(1328292445.908:40194): arch=c000003e syscall=2 success=yes exit=3 a0=7fffe3d2e85c a1=941 a2=1b6 a3=7fffe3d2c570 items=3 ppid=26377 pid=26677 auid=500 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts8 ses=1 comm="touch" exe="/bin/touch" subj=unconfined_u:unconfined_r:unconfined_t:s0 key=(null) type=CWD msg=audit(1328292445.908:40194): cwd="/home/mhcerri" type=PATH msg=audit(1328292445.908:40194): item=0 name="/tmp/file" inode=1196 dev=fd:01 mode=0100775 ouid=500 ogid=501 rdev=00:00 obj=unconfined_u:object_r:user_tmp_t:s0 |
注意,每条记录都有一个不同的类型,所有三条记录都包含 "1328292445.908:40194" 字符串。这是事件标识符。冒号前的头两个数字为事件时间戳,第一个为 UNIX® 事件(单位为秒),第二个为毫秒值。第三个数字是生成的序列号。还需注意,审计记录中使用的格式有些复杂,因此审计系统提供了一些工具帮助您显示审计日志。
Libvirt 实际上生成三种类型的记录。它们都包含一条通用记录,“uuid” 字段。这是一个 Universal Unique Identifier,标识所有记录中 libvirt 发出的客户机。libvirt 使用的记录类型为:
- VIRT_CONTROL:在虚拟机生命周期的每个阶段生成。它将在客户机启动或停止时生成,并且包含运行原因和客户机名称等字段。
- VIRT_MACHINE_ID:它是在 libvirt 尝试向客户机分配安全标签时生成的。在对 QEMU 使用 sVirt 时,该记录将包含 QEMU 流程的安全标签和分配给客户机的磁盘安全标签。
- VIRT_RESOURCE:该类型的记录在客户机的资源分配出现变化时生成,例如,当从客户机中移除磁盘,或向客户机连接一个新的网络设备。
auvirt 工具将搜索审计日志,查找由 libvirt 生成的记录,展示虚拟机会话列表。它还查找其他一些事件,如主机宕机、与客户机有关的 AVC 拒绝事件,以及与 QEMU 有关的异常事件。清单 2 展示了一个 auvirt 输出例子。第一个字段为客户机名称。第二个为启动客户机的用户。第三个为客户机运行的时间范围。注意,示例中 “Arch” 的执行已失败,而另一个仍然保持运行。
清单 2. Auvirt 输出
# auvirt Fedora root Tue Jan 24 16:28 - 16:28 (00:00) Fedora root Wed Jan 25 10:31 - 10:34 (00:02) CentOS root Wed Jan 25 10:34 - 13:50 (03:16) Fedora root Wed Jan 25 13:51 - 13:54 (00:03) Fedora root Wed Jan 25 13:54 - 13:55 (00:00) Arch root Wed Jan 25 13:55 - failed Arch root Wed Jan 25 13:56 |
可以通过下面的选项将输出限制到一个特定的时间范围:
--start <date> [<time>]
:为将要使用的时间期限确定一个开始时间--end <date> [<time>]
:为将要使用的时间期限确定一个结束时间
如果忽略 <time>
,工具将把开始和结束时间默认设置为 “00:00:00” 和 “23:59:59”。
清单 3. 按时间过滤 auvirt 输出
# auvirt --start 01/25/2012 12:00:00 --end 01/25/2012 Fedora root Wed Jan 25 13:51 - 13:54 (00:03) Fedora root Wed Jan 25 13:54 - 13:55 (00:00) Arch root Wed Jan 25 13:55 - failed Arch root Wed Jan 25 13:56 |
也可以将结果限制到一个客户机。该客户机可以通过客户机名称或客户机的 UUID 指定。默认情况下,为了保持输出简单,工具不会显示 UUID,但是您可以通过 --show-uuid
选项在输出中包含它。
清单 4. 按客户机名称和 UUID 过滤 auvirt 输出(使用独立区域显示以清晰呈现)
# auvirt --vm Fedora Fedora root Tue Jan 24 16:28 - 16:28 (00:00) Fedora root Wed Jan 25 10:31 - 10:34 (00:02) Fedora root Wed Jan 25 13:51 - 13:54 (00:03) Fedora root Wed Jan 25 13:54 - 13:55 (00:00) # auvirt --show-uuid --uuid cabf9532-99f1-3756-930f-59e8f19d4144 Arch cabf9532-99f1-3756-930f-59e8f19d4144 root Wed Jan 25 13:55 - failed Arch cabf9532-99f1-3756-930f-59e8f19d4144 root Wed Jan 25 13:56 |
默认情况下,auvirt 将显示一个简化的输出。要展示所有与虚拟化有关的事件,可以使用 --all-events
选项。使用该选项后,auvirt 将展示每个起始和结束事件、资源分配和相关事件的记录。清单 5 展示了使用 --all-events
选项的例子。
清单 5. 详细的 auvirt 输出(为了清晰显示进行了编辑,参见下载部分获得完整的输出)
# auvirt --vm CentOS --all-events down root Wed Jan 25 08:34 res CentOS root Wed Jan 25 10:34 - 13:50 (03:16) cgroup allow major rw pty [...] avc CentOS root Wed Jan 25 10:34 relabelto denied libvirtd CentOS.img system_u:object_r:svirt_image_t:s0:c6,c883 [...] res CentOS root Wed Jan 25 10:34 - 13:50 (03:16) disk start /var/lib/libvirt/images/CentOS.img res CentOS root Wed Jan 25 10:34 - 13:50 (03:16) net start 52:54:00:DB:AE:B4 res CentOS root Wed Jan 25 10:34 - 13:50 (03:16) mem start 1048576 res CentOS root Wed Jan 25 10:34 - 13:50 (03:16) vcpu start 1 start CentOS root Wed Jan 25 10:34 - 13:50 (03:16) [...] stop CentOS root Wed Jan 25 13:50 |
第一个字段表示事件类型,包含以下值:start、stop、res、avc、anom 和 down(表示主机宕机)。
资源分配和 AVC 记录包含额外的字段,提供相关的信息。资源分配中的额外字段包括:
- 资源类型:描述可分配给客户机的不同的资源类型,如磁盘、虚拟 CPU、内存和网络设备等。此外,libvirt 可以通过 Cgroups 设置为限制每个客户机使用的资源,这也被记录为一个资源事件。该字段的可能值包括:vcpu、mem、disk、net 和 cgroup。
- 原因:表示哪个操作引起资源分配事件。例如,当客户机启动或运行时,可以将一个磁盘分配给客户机,对于这些情况,原因值可以为 “start” 或 “attach”。对于 Cgroups 事件,原因字段表示是否允许或拒绝某个资源,它的值可以为 “allow” 或 “deny”。
- 资源:指分配给客户机的资源。路径用于磁盘资源,兆字节用于内存,MAC 地址用于网络设备,VCPU 数量用于虚拟 CPU。对于 Cgroups 事件,该值由三个部分组成:类型、ACL 和资源。
auvirt 报告的 AVC 事件表示客户机执行的访问没有被拒绝,而主机中另一个进程对客户机资源进行的访问被拒绝。AVC 事件包含的额外字段包括:
- 操作:指流程执行的操作被拒绝。例子包括:read、write、open、relabelto 和 relabelfrom。
- 操作结果:通常为拒绝。
- 程序:尝试执行拒绝操作的流程的程序名。
- 目标:拒绝操作的目标。例如,如果主机内部的一个流程试图访问客户机的磁盘映像文件,并且访问被 SELinux 拒绝,那么该字段将包含磁盘映像文件名。
- 上下文:对于
relabelto
和relabelfrom
操作,额外又包含了字段,表示相关的安全上下文:relabelto
的目标上下文和relabelfrom
的源上下文。
Auvirt 提供了用户友好的输出,不必包含审计日志中的所有事件信息。有时,这些额外的信息可能有用,需要访问原始数据来获得更详细的信息。Auvirt 包含 --proof
选项,可用于显示审计事件标识符(用于生成输出中的每条记录)。
清单 6. Auvirt --proof 选项
# auvirt --vm Fedora -ts 01/23/2012 00:00 -te 01/23/2012 17:30 --proof Fedora root Mon Jan 23 17:02 - 17:27 (00:24) Proof: 1327345338.087:41631, 1327346831.548:41702 |
清单 6 显示了 auvirt 示例,显示了用于创建记录的开始和停止事件标识符。通过这些信息,您可以使用 ausearch 工具从审计日志中提取原始记录(参见 清单 7)。
清单 7. 使用 ausearch 获得原始记录(使用独立区域显示以清晰呈现)
# ausearch -a 41631 --start 01/23/2012 17:02:00 --end 01/23/2012 17:27:59 ---- time->Mon Jan 23 17:02:18 2012 type=VIRT_CONTROL msg=audit(1327345338.087:41631): user pid=2433 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:initrc_t:s0 msg='virt=kvm op=start reason=booted vm="Fedora" uuid=7bcae45c-c179-6cea-a185-c7abe50520c7: exe="/usr/sbin/libvirtd" hostname=? addr=? terminal=? res=success' |
该示例使用了 -a
选项,将输出限制到带有给定序列号的记录,还使用了 --start
和 --end
选项,它们与 auvirt 选项的工作方式相同。注意,原始记录包含了额外的信息,如原因、可执行文件名称和 pid。