服务器

版本:25.03

内核完整性度量(IMA)

概述

IMA介绍

IMA,全称 Integrity Measurement Architecture(完整性度量架构),是内核中的一个子系统,能够基于自定义策略对通过execve()mmap()open()等系统调用访问的文件进行度量,度量结果可被用于本地/远程证明,或者和已有的参考值比较以控制对文件的访问

IMA的运行模式主要包含以下两种:

  • 度量(measure):提供了对文件的完整性状态观测功能,访问受保护文件时,会往度量日志(位于内核内存中)增加度量记录。如果系统包含TPM芯片,还可以往TPM芯片PCR寄存器中扩展度量摘要值,以保证度量信息不被篡改。度量场景并不提供对文件访问的控制,它记录的文件信息可传递给上层应用软件,进一步用于远程证明。
  • 评估(appraise):提供了对文件的完整性校验功能,从根本上杜绝了未知的/被篡改的文件的访问。通过哈希、签名、HMAC等密码学技术对文件的内容进行完整性验证,如果验证失败,则不允许任何进程对该文件进行访问。该特性为系统提供了底层韧性设计,在系统被破坏时牺牲一部分功能(被篡改的部分文件),避免攻击造成的影响进一步升级。

可以看到,IMA度量模式相当于一个“只记录不干涉”的观察员,IMA评估模式相当于一位严格的保安人员,它的职责是拒绝对所有“人证不一”的文件访问。

EVM介绍

EVM,全称 Extended Verification Module(扩展验证模块),是对IMA功能的扩展,在通过IMA实现对于文件内容的完整性保护的基础上,使用EVM可以更进一步地实现对于文件扩展属性(如UID、security.ima 、security.selinux等属性)的保护。

IMA摘要列表介绍

IMA Digest Lists(IMA摘要列表)是openEuler对内核原生完整性保护机制的增强,旨在对原生的IMA/EVM机制的以下痛点进行优化:

TPM扩展导致文件访问性能下降:

IMA度量模式下,每次触发度量都需要访问TPM芯片,TPM属于低速芯片,通常采用几十MHz时钟频率的SPI协议与CPU通信,导致系统调用性能下降:

非对称运算导致文件访问性能下降:

IMA评估模式下,需要使用签名机制保护不可变文件,每次触发文件校验都需要进行签名验证,而非对称运算相对复杂 ,同样导致系统调用性能下降:

复杂的部署方式导致效率和安全性下降:

IMA评估模式下,需要通过fix模式进行部署,即系统首先需要进入fix模式进行IMA/EVM扩展属性标记,然后再切换为校验模式启动。同时在受保护的文件升级时,需要重启进入fix模式,完成文件和扩展属性更新。一方面降低了部署效率,另一方面需要在运行环境中访问密钥,降低了安全性:

IMA摘要列表旨在通过一个哈希列表文件管理一系列文件的基准摘要值,即将若干文件(如一个软件包中的所有可执行文件)的基准摘要值汇总到单个文件中进行管理。基准摘要值可包含文件内容摘要(对应IMA模式)和文件扩展属性摘要(对应EVM模式),这个文件就是IMA摘要列表文件。

开启IMA摘要列表功能后,内核维护一个哈希白名单池,用于存放导入的IMA摘要列表文件中的摘要值,并通过securityfs对外提供IMA摘要列表文件的导入/删除/查询等接口。

在度量模式下,导入内核的摘要列表文件需要进行度量和TPM扩展才可添加至白名单池,后续如果度量的目标文件的摘要值和白名单池匹配,则不进行额外的度量日志记录以及TPM扩展;在评估模式下,导入内核的摘要列表文件需要通过签名验证才可添加至白名单池,后续将访问的目标文件的摘要值和白名单池中的摘要值进行匹配即可判断评估结果。

相比Linux原生IMA/EVM机制,IMA摘要列表扩展从安全性、性能、易用性三个方面进行了改良,以实现更好的落地效果:

  • 安全性:IMA摘要列表可以随软件包一起发布,软件包安装时同步导入摘要列表,确保了基准值来自于软件发行商(如openEuler社区),避免在运行环境生成基准值的流程,实现了完整的信任链。
  • 性能:IMA摘要列表机制以摘要列表为单位进行度量/校验,降低TPM访问和非对称运算频率为1/n(n为平均单个摘要列表管理的文件哈希数量),可一定程度提升系统调用性能和系统启动性能。
  • 易用性:IMA摘要列表机制可以实现“开箱即用”,即完成系统安装后直接进入评估模式,且允许在评估模式下直接安装/升级软件包,而无需进入fix模式进行文件标记,从而实现快速部署和平滑升级。

需要注意的是,IMA摘要列表相比原生IMA/EVM,将度量/评估的基准值在内核内存中进行维护,也引入了一个假设,即内核内存不可被未授权篡改,这就使得IMA摘要列表也依赖于其他安全机制(如内核模块安全启动和内存动态度量等)以保护内核内存的完整性。

但无论社区原生IMA机制还是IMA摘要列表扩展,都只是系统安全链中的一环,无法孤立地保证系统的安全性,安全自始至终都是一个构建纵深防御的系统工程。

接口说明

内核启动参数说明

openEuler IMA/EVM机制提供的内核启动参数及说明如下:

参数名称取值功能
ima_appraiseenforce-evmIMA评估强制校验模式(EVM开启)
log-evmIMA评估日志模式(EVM开启)
enforceIMA评估强制校验模式
logIMA评估日志模式
off关闭IMA评估
ima_appraise_digest_listdigest基于摘要列表进行IMA+EVM评估(比较文件内容和扩展属性)
digest-nometadata基于摘要列表进行IMA评估(只比较文件内容)
evmx509直接开启基于可移植签名的EVM(无论EVM证书是否加载)
complete启动后不允许通过securityfs接口修改EVM模式
allow_metadata_writes允许修改文件元数据,EVM不做拦截
ima_hashsha256/sha1/...声明IMA度量哈希算法
ima_templateima声明IMA度量模板(d|n)
ima-ng声明IMA度量模板(d-ng|n-ng),默认使用该模板
ima-sig声明IMA度量模板(d-ng|n-ng|sig)
ima_policyexec_tcb度量所有执行、映射方式访问的文件,以及加载的内核模块、固件、内核等文件
tcb在exec_tcb策略的基础上,额外度量以uid=0或euid=0身份访问的文件
secure_boot评估所有加载的内核模块、固件、内核等文件,并指定使用IMA签名模式
appraise_exec_tcb在secure_boot策略的基础上,额外评估所有执行、映射方式访问的文件
appraise_tcb评估访问的所有属主为0的文件
appraise_exec_immutable与appraise_exec_tcb策略配合使用,可执行文件的扩展属性不可变
ima_digest_list_pcr10在PCR 10中扩展基于摘要列表的IMA度量结果,禁用原生IMA度量
11在PCR 11中扩展基于摘要列表的IMA度量结果,禁用原生IMA度量
+11在PCR 11中扩展基于摘要列表的IMA度量结果,在PCR 10中扩展原生IMA度量结果
ima_digest_db_sizenn[M]设置内核摘要列表上限(0M~64M),不做配置的情况下默认为16MB(不做配置指的是不写该参数,但注意不能将值留空,如ima_digest_db_size=)
ima_capacity-1~2147483647设置内核度量日志条数上限,不做配置的情况下默认为100000条,配置-1表示无上限
initramtmpfs在initrd中支持tmpfs,以携带文件扩展属性

根据用户实际场景诉求,建议采取如下参数组合:

1) 原生IMA度量:

sh
# 原生IMA度量+自定义策略
无需配置,默认开启
# 原生IMA度量+TCB默认策略
ima_policy="tcb"

2) 基于摘要列表的IMA度量:

sh
# 摘要列表IMA度量+自定义策略
ima_digest_list_pcr=11 ima_template=ima-ng initramtmpfs
# 摘要列表IMA度量+默认策略
ima_digest_list_pcr=11 ima_template=ima-ng ima_policy="exec_tcb" initramtmpfs

3) 基于摘要列表的IMA评估,只保护文件内容:

sh
# IMA评估+日志模式
ima_appraise=log ima_appraise_digest_list=digest-nometadata ima_policy="appraise_exec_tcb" initramtmpfs
# IMA评估+强制校验模式
ima_appraise=enforce ima_appraise_digest_list=digest-nometadata ima_policy="appraise_exec_tcb" initramtmpfs

4) 基于摘要列表的IMA评估,保护文件内容和扩展属性:

sh
# IMA评估+日志模式
ima_appraise=log-evm ima_appraise_digest_list=digest ima_policy="appraise_exec_tcb|appraise_exec_immutable" initramtmpfs evm=x509 evm=complete
# IMA评估+强制校验模式
ima_appraise=enforce-evm ima_appraise_digest_list=digest ima_policy="appraise_exec_tcb|appraise_exec_immutable" initramtmpfs evm=x509 evm=complete

说明

以上四种参数都可以单独配置使用,但只有基于摘要列表的度量和评估模式可以组合使用,即2)和3)搭配或2)和4)搭配。

securityfs接口说明

openEuler IMA提供的securityfs接口位于/sys/kernel/security目录下,接口名及说明如下:

路径权限说明
ima/policy600IMA策略查询/导入接口
ima/ascii_runtime_measurement440查询IMA度量日志,以字符串形式输出
ima/binary_runtime_measurement440查询IMA度量日志,以二进制形式输出
ima/runtime_measurement_count440查询IMA度量日志条数
ima/violations440查询异常IMA度量日志数量
ima/digests_count440显示系统哈希表中的总摘要数量(IMA+EVM)
ima/digest_list_data200摘要列表新增接口
ima/digest_list_data_del200摘要列表删除接口
evm660查询/设置EVM模式

其中,/sys/kernel/security/evm 的接口的取值有以下三种:

  • 0:EVM 未初始化;
  • 1:使用 HMAC(对称加密)方式校验扩展属性完整性;
  • 2:使用公钥验签(非对称加密)方式校验扩展属性完整性;
  • 6:关闭扩展属性完整性校验。

摘要列表管理工具说明

digest-list-tools软件包提供IMA摘要列表文件生成和管理的工具,主要包含如下几个命令行工具:

gen_digest_lists工具

用户可通过调用gen_digest_lists命令行工具生成摘要列表。命令参数定义如下:

参数名称取值功能
-d<path>指定生成摘要列表文件存放的位置,需为有效目录。
-fcompact指定生成摘要列表文件的格式,当前仅支持compact格式。
-i<option arg>:<option value>指定生成摘要列表的目标文件范围,具体参数定义如下。
I:<path>指定需要生成摘要列表的文件绝对路径,如指定目录,则会执行递归生成。
E:<path>指定需要排除的路径或目录。
F:<path>指定路径或目录,为该路径或目录下所有文件生成摘要列表(同时指定e:参数时,忽略e:选项的筛选效果)。
e:仅对可执行文件生成摘要列表。
l:policy从系统SELinux策略匹配文件安全上下文,而不是直接从文件扩展属性中读取安全上下文。
i:当生成metadata类型的摘要列表时,被计算的扩展属性信息包含文件的摘要值(必须指定)。
M:允许显式指定文件的扩展属性信息(需要结合rpmbuild命令使用)。
u:将“L:”参数所指定的列表文件名作为生成摘要列表的文件名(需要结合rpmbuild命令使用)。
L:<path>指定列表文件的路径,列表文件中包含需要生成摘要列表的信息数据(需要结合rpmbuild命令使用)。
-oadd指定生成摘要列表的操作,当前仅支持add操作,即将摘要列表添加到文件中。
-p-1指定将摘要列表写入文件中的位置,当前仅支持指定-1。
-tfile只针对文件内容生成摘要列表。
metadata针对对文件的内容和扩展属性分别生成摘要列表。
-TNA不添加该参数,则生成摘要列表文件,添加该参数则生成TLV摘要列表文件。
-A<path>指定相对根目录,将文件路径截去指定的前缀进行路径匹配和SELinux标签匹配。
-mimmutable指定生成摘要列表文件的modifiers属性,当前仅支持指定immutable。摘要列表在enforce/enforece-evm模式下,摘要列表只能以只读模式打开。
-hNA打印帮助信息。

参考使用示例:

  • 场景1:为单个文件生成摘要列表/TLV摘要列表。

    sh
    gen_digest_lists -t metadata -f compact -i l:policy -o add -p -1  -m immutable -i I:/usr/bin/ls -d ./ -i i: gen_digest_lists -t metadata -f compact -i l:policy -o add -p -1  -m immutable -i I:/usr/bin/ls -d ./ -i i: -T
  • 场景2: 为单个文件生成摘要列表/TLV摘要列表,并指定相对根目录。

    sh
    gen_digest_lists -t metadata -f compact -i l:policy -o add -p -1  -m immutable -i I:/usr/bin/ls -A /usr/ -d ./ -i i: gen_digest_lists -t metadata -f compact -i l:policy -o add -p -1  -m immutable -i I:/usr/bin/ls -A /usr/ -d ./ -i i: -T
  • 场景3:为目录下的文件递归生成摘要列表/TLV摘要列表。

    sh
    gen_digest_lists -t metadata -f compact -i l:policy -o add -p -1  -m immutable -i I:/usr/bin/ -d ./ -i i: 
    gen_digest_lists -t metadata -f compact -i l:policy -o add -p -1  -m immutable -i I:/usr/bin/ -d ./ -i i: -T
  • 场景4:为目录下的可执行文件递归生成摘要列表/TLV摘要列表。

    sh
    gen_digest_lists -t metadata -f compact -i l:policy -o add -p -1  -m immutable -i I:/usr/bin/ -d ./ -i i: -i e:gen_digest_lists -t metadata -f compact -i l:policy -o add -p -1  -m immutable -i I:/usr/bin/ -d ./ -i i: -i e: -T
  • 场景5:为目录下的文件递归生成摘要列表/TLV摘要列表,排除部分子目录。

    sh
    gen_digest_lists -t metadata -f compact -i l:policy -o add -p -1  -m immutable -i I:/usr/ -d ./ -i i: -i E:/usr/bin/gen_digest_lists -t metadata -f compact -i l:policy -o add -p -1  -m immutable -i I:/usr/ -d ./ -i i: -i E:/usr/bin/ -T
  • 场景6:rpmbuild回调脚本中,通过读取rpmbuild传入的列表文件生成摘要列表。

    sh
    gen_digest_lists -i M: -t metadata -f compact -d $DIGEST_LIST_DIR -i l:policy \
     -i i: -o add -p -1 -m immutable -i L:$BIN_PKG_FILES -i u: \
     -A $RPM_BUILD_ROOT -i e: \
     -i E:/usr/src \
     -i E:/boot/efi \
     -i F:/lib \
     -i F:/usr/lib \
     -i F:/lib64 \
     -i F:/usr/lib64 \
     -i F:/lib/modules \
     -i F:/usr/lib/modules \
     -i F:/lib/firmware \
     -i F:/usr/lib/firmware
    
    gen_digest_lists -i M: -t metadata -f compact -d $DIGEST_LIST_DIR.tlv \
     -i l:policy -i i: -o add -p -1 -m immutable -i L:$BIN_PKG_FILES -i u: \
     -T -A $RPM_BUILD_ROOT -i e: \
     -i E:/usr/src \
     -i E:/boot/efi \
     -i F:/lib \
     -i F:/usr/lib \
     -i F:/lib64 \
     -i F:/usr/lib64 \
     -i F:/lib/modules \
     -i F:/usr/lib/modules \
     -i F:/lib/firmware \
     -i F:/usr/lib/firmware

manage_digest_lists工具

manage_digest_lists命令行工具主要用于将二进制格式的TLV摘要列表文件解析转换为可读的文本形式。命令参数定义如下:

参数名称取值功能
-d<path>指定TLV摘要列表文件存放的目录。
-f<filename>指定TLV摘要列表文件名。
-pdump指定操作类型,当前仅支持dump,表示解析打印TLV摘要列表操作。
-vNA打印详细信息。
-hNA打印帮助信息。

参考使用示例:

查看TLV摘要列表信息:

sh
manage_digest_lists -p dump -d /etc/ima/digest_lists.tlv/

文件格式说明

IMA策略文件语法说明

IMA策略文件为文本文件,一个文件中可包含若干条按照换行符\n分隔的规则语句,每条规则语句都必须以 action 关键字代表的动作开头,后接筛选条件

sh
<action关键字> <筛选条件1> [筛选条件2] [筛选条件3]...

action表示该条策略具体的动作,一条策略只能选一个 action,具体的action见后表(实际书写时可忽略 action 字样,例如直接书写 dont_measure,不需要写成 action=dont_measure):

筛选条件支持如下几种类型:

  • func:表示被度量或评估的文件类型,常和 mask 匹配使用,一条策略只能选一个 func。

    • FILE_CHECK 只能同 MAY_EXEC、MAY_WRITE、MAY_READ 匹配使用。
    • MODULE_CHECK、MMAP_CHECK、BPRM_CHECK 只能同 MAY_EXEC 匹配使用。
    • 匹配关系以外的组合不会产生效果。
  • mask:表示文件在做什么操作时将被度量或评估,一条策略只能选一个 mask。

  • fsmagic:表示文件系统类型的十六进制魔数,定义在 /usr/include/linux/magic.h 文件中(默认情况下度量所有文件系统,除非使用 dont_measure/dont_appraise 标记不度量某文件系统)。

  • fsuuid:表示系统设备 uuid 的 16 位的十六进制字符串。

  • objtype:表示文件安全类型,一条策略只能选一个文件类型,objtype 相比 func 而言,划分的粒度更细,比如 obj_type=nova_log_t 表示SELinux类型为 nova_log_t 的文件。

  • uid:表示哪个用户(用用户 id 表示)对文件进行操作,一条策略只能选一个 uid。

  • fowner:表示文件的属主(用用户 id 表示)是谁,一条策略只能选一个 fowner。

关键字的具体取值及说明如下:

关键字说明
actionmeasure开启IMA度量
dont_measure禁用IMA度量
appraise开启IMA评估
dont_appraise禁用IMA评估
audit开启审计
funcFILE_CHECK将要被打开的文件
MODULE_CHECK将要被装载的内核模块文件
MMAP_CHECK将要被映射到进程内存空间的动态库文件
BRPM_CHECK将要被执行的文件(不含通过 /bin/hash 等程序打开的脚本文件)
POLICY_CHECK将要被导入的IMA策略文件
FIRMWARE_CHECK将要被加载到内存中的固件
DIGEST_LIST_CHECK将要被加载到内核中的摘要列表文件
KEXEC_KERNEL_CHECK将要切换的 kexec 内核
maskMAY_EXEC执行文件
MAY_WRITE写文件
MAY_READ读文件
MAY_APPEND扩展文件属性
fsmagicfsmagic=xxx表示文件系统类型的十六进制魔数
fsuuidfsuuid=xxx表示系统设备 uuid 的 16 位的十六进制字符串
fownerfowner=xxx文件属主的用户 id
uiduid=xxx操作文件的用户 id
obj_typeobj_type=xxx_t表示文件的类型(基于 SELinux 标签)
pcrpcr=选择 TPM 中用于扩展度量值的 PCR(默认为 10)
appraise_typeimasig基于签名进行IMA评估
meta_immutable基于签名进行文件扩展属性的评估(支持摘要列表)

使用说明

说明

原生IMA/EVM为Linux开源特性,本章节仅简单介绍基本使用方式,其他资料可参考开源WIKI: https://sourceforge.net/p/linux-ima/wiki/Home/

原生IMA使用说明

IMA度量模式

用户需要配置度量策略,开启IMA度量功能,具体步骤如下:

步骤1: 用户可通过配置启动参数或手动配置的方式,指定度量策略。通过启动参数配置IMA策略的示例如下:

sh
ima_policy="tcb"

手动配置IMA策略的示例如下:

sh
echo "measure func=BPRM_CHECK" > /sys/kernel/security/ima/policy

步骤2: 重启系统,用户可实时检查度量日志获取当前的度量情况:

sh
cat /sys/kernel/security/ima/ascii_runtime_measurements

IMA评估模式

用户需要首先进入fix模式,完成文件的IMA标记后,再开启log或enforce模式。具体步骤如下:

步骤1: 配置启动参数,重启后进入fix模式:

sh
ima_appraise=fix ima_policy=appraise_tcb

步骤2: 为所有需要评估的文件生成IMA扩展属性:

对于不可变文件(如二进制程序文件)可以使用签名模式,将文件摘要值的签名写入IMA扩展属性中。举例如下(其中/path/to/ima.key指的是和IMA证书匹配的签名私钥):

sh
find /usr/bin -fstype ext4 -type f -executable -uid 0 -exec evmctl -a sha256 ima_sign --key /path/to/ima.key '{}' \;

对于可变文件(如数据文件)可以使用哈希模式,将文件的摘要值写入IMA扩展属性中。IMA支持自动标记机制,即在fix模式下仅需触发文件访问,即可自动生成IMA扩展属性:

sh
find / -fstype ext4 -type f -uid 0 -exec dd if='{}' of=/dev/null count=0 status=none \;

可通过如下命令检查文件是否被成功标记了IMA扩展属性(security.ima):

sh
getfattr -m - -d /sbin/init

步骤3: 配置启动参数,修改IMA评估为log或enforce模式后,重启系统:

sh
ima_appraise=enforce ima_policy=appraise_tcb

IMA摘要列表使用说明

前置条件

IMA摘要列表特性使用前,用户需安装ima-evm-utilsdigest-list-tools软件包:

sh
yum install ima-evm-utils digest-list-tools

机制介绍

摘要列表文件

在安装openEuler发布的RPM包后,默认会在/etc/ima目录下生成摘要列表文件。根据文件名的不同,存在如下几种文件:

/etc/ima/digest_lists/0-metadata_list-compact-<RPM_NAME>

为IMA摘要列表文件,通过gen_digest_lists命令生成(生成方法详见gen_digest_lists工具),该文件为二进制格式,包含header信息以及一连串SHA256哈希值,分别代表合法的文件内容摘要值和文件扩展属性摘要值。该文件被度量或评估后,最终被导入内核,并以该文件中的白名单摘要值为基准进行IMA摘要列表度量或评估。

/etc/ima/digest_lists/0-metadata_list-rpm-<RPM_NAME>

为RPM摘要列表文件,实际为RPM包的头信息。RPM包安装后,如果IMA摘要列表文件不包含签名,则会把RPM头信息写入该文件中,并将头信息的签名写入security.ima扩展属性中。这样通过签名可验证RPM头信息的真实性,由于RPM头信息又包含了摘要列表文件的摘要值,则可实现摘要列表的间接验证。

/etc/ima/digest_lists/0-parser_list-compact-libexec

为IMA PARSER摘要列表文件,存放/usr/libexec/rpm_parser文件的摘要值。该文件用于实现RPM摘要列表->IMA摘要列表的信任链,内核IMA摘要列表机制会对该文件执行后产生的进程进行特殊校验,如果确定是rpm_parser程序,则会信任其导入的所有摘要列表而无需校验签名。

/etc/ima/digest_lists.sig/0-metadata_list-compact-<RPM_NAME>.sig

为IMA摘要列表的签名文件,若RPM包中包含此文件,则在RPM包安装阶段,会将该文件的内容写入对应的RPM摘要列表文件的security.ima扩展属性,从而在IMA摘要列表导入内核阶段进行签名验证。

/etc/ima/digest_lists.tlv/0-metadata_list-compact_tlv-<RPM_NAME>

为TLV摘要列表文件,通常在对目标文件生成IMA摘要列表文件时一并生成,存放目标文件的完整性信息(文件内容摘要值、文件扩展属性等)。该文件的功能是协助用户查询/恢复目标文件的完整性信息。

摘要列表文件签名方式

在IMA摘要列表评估模式下,IMA摘要列表文件需要经过签名验证才可导入内核,并用于后续的文件白名单匹配。IMA摘要列表文件支持如下几种签名方式:

1) IMA扩展属性签名

即原生的IMA签名机制,将签名信息按照一定格式,存放在security.ima扩展属性中。可通过evmctl命令生成并添加:

sh
evmctl ima_sign --key /path/to/ima.key -a sha256 <DIGEST_LIST_PATH>

也可添加-f参数,将签名信息和头信息存入独立的文件中:

sh
evmctl ima_sign -f --key /path/to/ima.key -a sha256 <DIGEST_LIST_PATH>

在开启IMA摘要列表评估模式下,可直接将摘要列表文件路径写入内核接口,实现摘要列表的导入/删除。该过程会自动触发评估,基于security.ima扩展属性完成对摘要列表文件内容的签名验证:

sh
# 导入IMA摘要列表文件
echo <DIGEST_LIST_PATH> > /sys/kernel/security/ima/digest_list_data
# 删除IMA摘要列表文件
echo <DIGEST_LIST_PATH> > /sys/kernel/security/ima/digest_list_data_del

2) IMA摘要列表追加签名(openEuler 24.03 LTS版本默认)

openEuler 24.03 LTS版本开始支持IMA专用签名密钥,并采用CMS签名。由于签名信息包含证书链,可能由于长度超出限制而无法写入文件的security.ima扩展属性中,因此采用类似内核模块的追加签名的方式:

其签名机制为:

(1) 将CMS签名信息追加到IMA摘要列表文件末尾;

(2) 填充结构体并添加到签名信息末尾,结构体定义如下:

sh
struct module_signature {
u8 algo;  /* Public-key crypto algorithm [0] */
u8 hash;  /* Digest algorithm [0] */
u8 id_type; /* Key identifier type [PKEY_ID_PKCS7] */
u8 signer_len; /* Length of signer's name [0] */
u8 key_id_len; /* Length of key identifier [0] */
u8 __pad[3];
__be32 sig_len; /* Length of signature data */
};

(3) 添加魔鬼字符串"~Module signature appended~\n"

此步骤的参考脚本如下:

shell
#!/bin/bash
DIGEST_FILE=$1 # IMA摘要列表文件路径
SIG_FILE=$2 # IMA摘要列表签名信息保存路径
OUT=$3 #完成签名信息添加后的摘要列表文件输出路径

cat $DIGEST_FILE $SIG_FILE > $OUT
echo -n -e "\x00\x00\x02\x00\x00\x00\x00\x00" >> $OUT
echo -n -e $(printf "%08x" "$(ls -l $SIG_FILE | awk '{print $5}')") | xxd -r -ps >> $OUT
echo -n "~Module signature appended~" >> $OUT
echo -n -e "\x0a" >> $OUT

3) 复用RPM签名(openEuler 22.03 LTS版本默认)

openEuler 22.03 LTS版本支持复用RPM签名机制实现IMA摘要列表文件的签名。旨在解决版本无专用IMA签名密钥的问题。用户无需感知该签名流程,当RPM包中含有IMA摘要列表文件,而不包含IMA摘要列表的签名文件时,会自动使用该签名机制。其核心原理是通过RPM包的头信息实现对IMA摘要列表的验证。

对于openEuler发布的RPM包,每一个包文件可以包含两部分:

  • RPM头信息: 存放RPM包属性字段,如包名、文件摘要值列表等,通过RPM头签名保证其完整性;
  • RPM文件: 实际安装至系统中的文件,也包括构建阶段生成的IMA摘要列表文件。

在RPM包安装阶段,如果RPM进程检测到包中的摘要列表文件不包含签名,则在/etc/ima目录下创建一个RPM摘要列表文件,将RPM头信息写入文件内容,将RPM头签名写入文件的security.ima扩展属性中。后续可通过RPM摘要列表间接实现IMA摘要列表的验证和导入。

IMA摘要列表导入

在开启IMA度量模式下,导入IMA摘要列表文件无需经过签名验证,可直接将路径写入内核接口,实现摘要列表的导入/删除:

sh
# 导入IMA摘要列表文件
echo <DIGEST_LIST_PATH> > /sys/kernel/security/ima/digest_list_data
# 删除IMA摘要列表文件
echo <DIGEST_LIST_PATH> > /sys/kernel/security/ima/digest_list_data_del

在开启IMA评估模式下,导入摘要列表必须通过签名验证。根据签名方式不同,可分为两种导入方式。

直接导入方式

对于已包含签名信息的IMA摘要列表文件(IMA扩展属性签名或IMA摘要列表追加签名),可直接将路径写入内核接口,实现摘要列表的导入/删除。该过程会自动触发评估,基于security.ima扩展属性完成对摘要列表文件内容的签名验证:

sh
# 导入IMA摘要列表文件
echo <DIGEST_LIST_PATH> > /sys/kernel/security/ima/digest_list_data
# 删除IMA摘要列表文件
echo <DIGEST_LIST_PATH> > /sys/kernel/security/ima/digest_list_data_del

调用upload_digest_lists导入方式

对于复用RPM签名的IMA摘要列表文件,需要调用upload_digest_lists命令实现导入。具体命令如下(注意指定的路径为对应的RPM摘要列表):

sh
# 导入IMA摘要列表文件
upload_digest_lists add <RPM_DIGEST_LIST_PATH>
# 删除IMA摘要列表文件
upload_digest_lists del <RPM_DIGEST_LIST_PATH>

该流程相对复杂,需要满足以下前置条件:

  1. 系统已导入openEuler发布的digest_list_tools软件包中的摘要列表(包含IMA摘要列表和IMA PARSER摘要列表);

  2. 已配置对应用程序执行的IMA评估策略(BPRM_CHECK策略)。

操作指导

RPM构建自动生成摘要列表

openEuler RPM工具链支持%__brp_digest_list宏定义,配置格式如下:

sh
%__brp_digest_list /usr/lib/rpm/brp-digest-list %{buildroot}

当配置了该宏定义后,当用户调用rpmbuild命令进行软件包构建时,在RPM打包阶段会调用/usr/lib/rpm/brp-digest-list脚本进行摘要列表的生成和签名等流程。openEuler默认针对可执行程序、动态库、内核模块等关键文件生成摘要列表。用户也可以通过修改脚本,自行配置生成摘要列表的范围和指定签名密钥。如下示例使用用户自定义的签名密钥/path/to/ima.key进行摘要列表签名。

sh
...... (line 66)
DIGEST_LIST_TLV_PATH="$DIGEST_LIST_DIR.tlv/0-metadata_list-compact_tlv-$(basename $BIN_PKG_FILES)"
[ -f $DIGEST_LIST_TLV_PATH ] || exit 0

chmod 644 $DIGEST_LIST_TLV_PATH
echo $DIGEST_LIST_TLV_PATH

evmctl ima_sign -f --key /path/to/ima.key -a sha256 $DIGEST_LIST_PATH &> /dev/null
chmod 400 $DIGEST_LIST_PATH.sig
mkdir -p $DIGEST_LIST_DIR.sig
mv $DIGEST_LIST_PATH.sig $DIGEST_LIST_DIR.sig
echo $DIGEST_LIST_DIR.sig/0-metadata_list-compact-$(basename $BIN_PKG_FILES).sig
IMA摘要列表度量

用户可通过如下功能开启IMA摘要列表度量:

步骤1: 用户需要配置启动参数度量策略,开启IMA度量功能,具体步骤同原生IMA度量,不同的是需要单独配置度量所使用的TPM PCR寄存器,启动参数示例如下:

sh
ima_policy=exec_tcb ima_digest_list_pcr=11

步骤2: 用户导入IMA摘要列表,以bash软件包的摘要列表为例:

sh
echo /etc/ima/digest_lists/0-metadata_list-compact-bash-5.1.8-6.oe2203sp1.x86_64 > /sys/kernel/security/ima/digest_list_data

可查询到IMA摘要列表的度量日志:

sh
cat /sys/kernel/security/ima/ascii_runtime_measurements

导入IMA摘要列表后,如果后续度量的文件摘要值包含在IMA摘要列表中,则不会额外记录度量日志。

IMA摘要列表评估
默认策略启动场景

用户可在启动参数中配置ima_policy参数指定IMA默认策略,则在内核启动阶段,IMA初始化完成后立即启用默认策略进行评估。用户可通过如下功能开启IMA摘要列表评估:

步骤1: 执行dracut命令将摘要列表文件写入initrd:

sh
dracut -f -e xattr

步骤2: 配置启动参数和IMA策略,典型的配置如下:

sh
# 基于摘要列表的IMA评估log/enforce模式,只保护文件内容,配置默认策略为appraise_exec_tcb
ima_appraise=log ima_appraise_digest_list=digest-nometadata ima_policy="appraise_exec_tcb" initramtmpfs module.sig_enforce
ima_appraise=enforce ima_appraise_digest_list=digest-nometadata ima_policy="appraise_exec_tcb" initramtmpfs module.sig_enforce
# 基于摘要列表的IMA评估log/enforce模式,保护文件内容和扩展属性,配置默认策略为appraise_exec_tcb+appraise_exec_immutable
ima_appraise=log-evm ima_appraise_digest_list=digest ima_policy="appraise_exec_tcb|appraise_exec_immutable" initramtmpfs evm=x509 evm=complete module.sig_enforce
ima_appraise=enforce-evm ima_appraise_digest_list=digest ima_policy="appraise_exec_tcb|appraise_exec_immutable" initramtmpfs evm=x509 evm=complete module.sig_enforce

重启系统即可开启IMA摘要列表评估功能,启动过程中自动完成IMA策略生效和IMA摘要列表文件导入。

无默认策略启动场景

用户可在启动参数中不配置ima_policy参数,代表系统启动阶段无默认策略,IMA评估机制等待用户导入策略后生效启用。

步骤1: 配置启动参数,典型的配置如下:

sh
# 基于摘要列表的IMA评估log/enforce模式,只保护文件内容,无默认策略
ima_appraise=log ima_appraise_digest_list=digest-nometadata initramtmpfs
ima_appraise=enforce ima_appraise_digest_list=digest-nometadata initramtmpfs
# 基于摘要列表的IMA评估log/enforce模式,保护文件内容和扩展属性,无默认策略
ima_appraise=log-evm ima_appraise_digest_list=digest initramtmpfs evm=x509 evm=complete
ima_appraise=enforce-evm ima_appraise_digest_list=digest initramtmpfs evm=x509 evm=complete

重启系统,此时由于系统无策略,IMA评估并不生效。

步骤2: 导入IMA策略,将策略文件的全路径写入内核接口:

sh
echo /path/to/policy > /sys/kernel/security/ima/policy

策略中需要包含一些固定规则,用户可参考如下策略模板:
openEuler 22.03 LTS版本的策略模板如下(复用RPM签名场景):

sh
# 不评估securityfs文件系统的访问行为
dont_appraise fsmagic=0x73636673
# 其他用户自定义的dont_appraise规则
......
# 评估导入的IMA摘要列表文件
appraise func=DIGEST_LIST_CHECK appraise_type=imasig
# 评估/usr/libexec/rpm_parser进程打开的所有文件
appraise parser appraise_type=imasig
# 评估执行的应用程序(触发对/usr/libexec/rpm_parser执行的评估,也可以新增其他限制条件,如SELinux标签等)
appraise func=BPRM_CHECK appraise_type=imasig
# 其他用户自定义的appraise规则
......

openEuler 24.03 LTS版本的策略模板如下(IMA扩展属性签名或追加签名场景):

sh
# 用户自定义的dont_appraise规则
......
# 评估导入的IMA摘要列表文件
appraise func=DIGEST_LIST_CHECK appraise_type=imasig|modsig
# 其他用户自定义的appraise规则
......

步骤3: 导入IMA摘要列表文件,对于不同签名方式的摘要列表,需要使用不同的导入方式。

openEuler 22.03 LTS的摘要列表导入方式如下(复用RPM签名的IMA摘要列表):

sh
# 导入digest_list_tools软件包的摘要列表
echo /etc/ima/digest_lists/0-metadata_list-compact-digest-list-tools-0.3.95-13.x86_64 > /sys/kernel/security/ima/digest_list_data
echo /etc/ima/digest_lists/0-parser_list-compact-libexec > /sys/kernel/security/ima/digest_list_data
# 导入其他的RPM摘要列表
upload_digest_lists add /etc/ima/digest_lists
# 检查导入的摘要列表条数
cat /sys/kernel/security/ima/digests_count

openEuler 24.03 LTS的摘要列表导入方式如下(追加签名的IMA摘要列表):

sh
find /etc/ima/digest_lists -name "0-metadata_list-compact-*" -exec echo {} > /sys/kernel/security/ima/digest_list_data \;
软件升级场景

开启IMA摘要列表功能后,对于覆盖在IMA保护范围内的文件,在升级更新场景需要同步更新摘要列表。对于openEuler发布的RPM包,在包安装、升级、卸载的同时,将自动完成RPM包中的摘要列表的添加、更新和删除,不需要用户手动操作。对于用户维护的非RPM格式的软件包,则需要手动完成摘要列表的导入。

用户证书导入

用户可以通过导入自定义证书,从而针对非openEuler发布的软件进行度量或评估。openEuler IMA评估模式支持从如下两种密钥环中获取证书进行签名校验:

  • builtin_trusted_keys密钥环:内核编译时预置的根证书;
  • ima密钥环:通过initrd中的/etc/keys/x509_ima.der导入,需要为builtin_trusted_keys密钥环中任意一本证书的子证书。

将根证书导入builtin_trusted_keys密钥环的步骤如下:

步骤1: 生成根证书,以openssl命令为例:

sh
echo 'subjectKeyIdentifier=hash' > root.cfg
openssl genrsa -out root.key 4096
openssl req -new -sha256 -key root.key -out root.csr -subj "/C=AA/ST=BB/O=CC/OU=DD/CN=openeuler test ca" 
openssl x509 -req -days 3650 -extfile root.cfg -signkey root.key -in root.csr -out root.crt
openssl x509 -in root.crt -out root.der -outform DER

步骤2: 获取openEuler kernel源码,以最新的OLK-5.10分支为例:

sh
git clone https://gitee.com/openeuler/kernel.git -b OLK-5.10

步骤3: 进入源码目录,并将根证书拷贝至目录下:

sh
cd kernel
cp /path/to/root.der .

修改config文件的CONFIG_SYSTEM_TRUSTED_KEYS选项:

sh
CONFIG_SYSTEM_TRUSTED_KEYS="./root.crt"

步骤4: 编译安装内核(步骤略,注意需要为内核模块生成摘要列表)。

步骤5: 重启后检查证书导入成功:

sh
keyctl show %:.builtin_trusted_keys

将子证书导入ima密钥环的步骤如下,注意需要提前将根证书导入builtin_trusted_keys密钥环:

步骤1: 基于根证书生成子证书,以openssl命令为例:

sh
echo 'subjectKeyIdentifier=hash' > ima.cfg
echo 'authorityKeyIdentifier=keyid,issuer' >> ima.cfg
echo 'keyUsage=digitalSignature' >> ima.cfg
openssl genrsa -out ima.key 4096
openssl req -new -sha256 -key ima.key -out ima.csr -subj "/C=AA/ST=BB/O=CC/OU=DD/CN=openeuler test ima"
openssl x509 -req -sha256 -CAcreateserial -CA root.crt -CAkey root.key -extfile ima.cfg -in ima.csr -out ima.crt
openssl x509 -outform DER -in ima.crt -out x509_ima.der

步骤2: 将IMA证书拷贝到/etc/keys目录下:

sh
mkdir -p /etc/keys/
cp x509_ima.der /etc/keys/

步骤3: 打包initrd,将IMA证书和摘要列表置入initrd镜像中:

sh
echo 'install_items+=" /etc/keys/x509_ima.der "' >> /etc/dracut.conf
dracut -f -e xattr

步骤4: 重启后检查证书导入成功:

sh
keyctl show %:.ima

典型使用场景

根据运行模式的不同,IMA摘要列表可应用于可信度量场景和用户态安全启动场景。

可信度量场景

可信度量场景主要基于IMA摘要列表度量模式,由内核+硬件可信根(如TPM)共同完成对关键文件的度量,再结合远程证明工具链完成对当前系统的文件可信状态的证明:

运行阶段

  • 软件包部署时同步导入摘要列表,IMA对摘要列表进行度量并记录度量日志(同步扩展TPM);

  • 应用程序执行时触发IMA度量,若文件摘要值匹配白名单则忽略,否则记录度量日志(同步扩展TPM) 。

证明阶段(业界通用流程)

  • 远程证明服务器下发证明请求,客户端回传IMA度量日志以及经过签名的TPM PCR值;

  • 远程证明服务器依次校验PCR(校验签名)、度量日志(PCR回放)、文件度量信息(比对本地基准值)的正确性,上报结果至安全中心;

  • 安全管理中心采取对应操作,如事件通知、节点隔离等。

用户态安全启动场景

用户态安全启动场景主要基于IMA摘要列表评估模式,与安全启动类似,旨在对执行的应用程序或访问的关键文件执行完整性校验,如果校验失败,则拒绝访问:

运行阶段

  • 应用部署时导入摘要列表,内核验签通过后,加载摘要值到内核哈希表中作为白名单;
  • 应用程序执行时触发IMA校验,计算文件hash值,若与基线值一致,则允许访问,否则记录日志或拒绝访问 。

附录

内核编译选项说明

原生IMA/EVM提供的编译选项及说明如下:

编译选项功能
CONFIG_INTEGRITYIMA/EVM 总编译开关
CONFIG_INTEGRITY_SIGNATURE使能IMA签名校验
CONFIG_INTEGRITY_ASYMMETRIC_KEYS使能IMA非对称签名校验
CONFIG_INTEGRITY_TRUSTED_KEYRING使能 IMA/EVM 密钥环
CONFIG_INTEGRITY_AUDIT编译 IMA audit 审计模块
CONFIG_IMAIMA 总编译开关
CONFIG_IMA_WRITE_POLICY允许在运行阶段更新IMA策略
CONFIG_IMA_MEASURE_PCR_IDX允许指定IMA度量 PCR 序号
CONFIG_IMA_LSM_RULES允许配置 LSM 规则
CONFIG_IMA_APPRAISEIMA 评估总编译开关
IMA_APPRAISE_BOOTPARAM启用IMA评估启动参数
CONFIG_EVMEVM 总编译开关

openEuler IMA摘要列表特性提供的编译选项及说明如下(openEuler内核编译默认开启):

编译选项功能
CONFIG_DIGEST_LIST开启IMA摘要列表特性开关

IMA摘要列表根证书说明

openEuler 22.03版本使用RPM密钥对IMA摘要列表进行签名,为保证IMA功能开箱可用,openEuler内核编译时默认将RPM根证书(PGP证书)导入内核。当前包含旧版本使用的OBS证书和openEuler 22.03 LTS SP1版本切换的openEuler证书:

shell
# cat /proc/keys | grep PGP
1909b4ad I------     1 perm 1f030000     0     0 asymmetri private OBS b25e7f66: PGP.rsa b25e7f66 []
2f10cd36 I------     1 perm 1f030000     0     0 asymmetri openeuler <openeuler@compass-ci.com> fb37bc6f: PGP.rsa fb37bc6f []

由于当前内核不支持导入PGP子公钥,而切换后的openEuler证书采用子密钥签名,因此openEuler内核编译前对证书进行了预处理,抽取子公钥并导入内核,具体处理流程可见内核软件包代码仓内的process_pgp_certs.sh脚本文件:https://gitee.com/src-openeuler/kernel/blob/openEuler-22.03-LTS-SP1/process_pgp_certs.sh

openEuler 24.03及之后的版本支持IMA专用证书,详见证书签名文档相关章节。

如果用户不使用IMA摘要列表功能或使用其他密钥实现签名/验签,则可将相关代码移除,自行实现内核根证书配置。