起因:要学习IMA(Linux的一个文件度量)系统,毕设也需要,想着就把环境配了,结果一弄就弄了两天……

引子

天真的我,以为可以从docker上面弄,先创了一个容器,然后找如何开启IMA。但是发现,IMA是类似于一种“基础架构”的东西,其主要停留在OS层面。

而Docker主要是对OS的虚拟,所以,我的底层linux os没有开启IMA,自然docker里面也开启不了IMA

遂打开了wsl(因为我的docker和wsl用的同一个内核(似乎)),准备从wsl层面操作。

结果,仍然无法开启IMA,因为 WSL2的内核是修改过的,无法使用 ubuntu上游的内核头文件和modules文件,因此,我们需要手动编译并安装一个版本。

然后就开启了我的内核升级之路。

内核升级

PS:由于本文是后面来写的,在操作过程中没有截图,懒得复现,所以很多截图缺失

准备工作

首先,运行uname -a可以查看对应的内核版本。

到WSL git仓库(https://github.com/microsoft/WSL2-Linux-Kernel/),找一个版本下载

下载linux内核src

接着,打开wsl,安装编译过程中需要的库

1
sudo apt install build-essential flex bison dwarves libssl-dev libelf-dev cpio

下载、解压内核

然后,打开wsl,解压到linux环境下的某个地方(比如我创建了一个/my_kernel)文件夹

1
2
3
# /mnt/d/linux-msft-wsl-5.10.16.3.tar.gz是下载到windows的路径(挂载/mnt)
# .标识解压到当前文件夹
tar -xf /mnt/d/linux-msft-wsl-5.10.16.3.tar.gz .

解压约30s(解压前约200M,解压后约1G),注意:一定不能在windows中以任何方式(包括重命名)打开这个压缩包,或者直接在windows中进行解压操作,也不能把解压路径设置在windows环境内!

因为该压缩包含有一些 除了大小写外,名字相同的文件,而windows是大小写不敏感的,会进行覆盖,所以如果用windows操作,实际的文件会有缺失。

解压后,cd进入到这个目录中

1
cd WSL2-Linux-Kernel-linux-msft-wsl-****(版本号)

配置项

微软在解压文件夹WSL2-Linux-Kernel-linux-msft-wsl-****(版本号)Microsoft/config-wsl内置了一些配置项说明,我们可以根据自己需要配置哪些需要编译入内核。

这个工具内置了一个简易可视化来修改WSL2内核配置,添加或修改内核组件,输入

1
​​​​​​make menuconfig KCONFIG_CONFIG=Microsoft/config-wsl

配置内核编译项

在这一步可以选择众多的内核选项可以配置,界面分多级菜单,可以通过“/”来进行关键字搜索。上下键选择配置项,如果前面为[ ]表明没有编入内核,[*]表明被编入内核,< >表明没有编入内核modules,<*>表明编入内核modules。按空格进行切换

内核模块化和编入内核(也称为内置或静态编译)是 Linux 内核编译时的两种不同包含方式,它们决定了内核功能的不同加载和管理方式:

  • 内核模块化(模块化内核):模块化内核允许特定的功能作为模块加载到内核中。这些模块在系统运行时可以被动态加载或卸载
  • 编入内核(内置内核):编入内核意味着特定的功能被直接编译到内核映像中,成为内核的一部分,不能在系统运行时动态加载或卸载。

如果没有标识这是一个大类,空格敲击进入该子类进行进一步配置(同上)。

左右键是对最下方的选择,如果修改后,请移动到save,然后按键盘enter。修改完后移动到exit,然后键盘enter就行(enter后可能会提示你保存,enter保存确认即可)。

此时编译项就被保存了,你可以使用

1
cat Microsoft/config-wsl

查看编译项的编码版本。或者重新运行上面的指令,也能重新唤起可视化界面。

编译

配置好自己需要的定制内核后,编译,-j参数用于多线程编译,自动获取cpu内核参数来配置

1
make KCONFIG_CONFIG=Microsoft/config-wsl bzImage -j $(grep "cpu cores" /proc/cpuinfo | wc -l)

编译完成后内核文件bzImage,在当前目录的./arch/x86/boot/内可以找到。

编译大概需要15分钟左右,最后会出现下图这个 (Kernel:arch/x86/boot/bzImage is ready) 就表明初步成了

编译成功标识

替换内核

方式1:直接简单粗暴替换

首先将我们编译好的内核放到windows环境中,方便操作

1
cp ./arch/x86/boot/bzImage /mnt/d

这样,bzImage就保存到了我们的D盘里面了。同名。

在替换内核之前,首先需要关闭正在运行的WSL实例,在Power shell中使用如下命令即可:

1
wsl --shutdown

然后我跟着网上的:接着进入Win中的C:\Windows\System32\lxss\tools文件夹下,将kernel文件替换为刚刚编译的那个即可(记得重命名bzImage为kernel,保持名字为kernel)(重要!当然最好把旧的那个备份下,如果后续出现打不开wsl,直接换回去即可)

然后我就被坑了,因为我的kernel根本不再这里,我的在C:\Program Files\WSL\tools

然后打开wsl进行验证即可

方式2:配置.wslconfig

在Windows用户目录下(例如我的在C:\Users\41987)的.wslconfig文件,如果没有就创建一个。

填写:

1
2
3
​[wsl2]
kernel=C:\\Your\\WSL2\\Kernel\\Path\\bzImage
// 一定要用双斜杠,本行请删除

关闭WSL2并重新打开即可。

关于.wslconfig的详细配置,可参考Advanced settings configuration in WSL

验证内核

重启后,如果能正常进入系统,请输入uname -a

验证内核

出现的内容和准备工作不一样,版本号能对上即可。时间为你编译的时间。

安装module,完成全部内容

虽然别的教程没有提这几步,但是我还是做了,不然不知道为什么还是启动不了ima。出现sed: can't read modules.order: No such file or directory类似的,反正找不到modules.order可参考这里——

编译完成后,需要安装新的内核模组并更新引导加载程序(比如GRUB)

回到刚刚的WSL2-Linux-Kernel-linux-msft-wsl-****(版本号)目录,完成如下编译——

1
2
3
4
5
6
# 编译内核模块
make modules KCONFIG_CONFIG=Microsoft/config-wsl -j $(grep "cpu cores" /proc/cpuinfo | wc -l)
#安装内核模块
make install_modules KCONFIG_CONFIG=Microsoft/config-wsl -j $(grep "cpu cores" /proc/cpuinfo | wc -l)
# 安装完整内核
make install KCONFIG_CONFIG=Microsoft/config-wsl -j $(grep "cpu cores" /proc/cpuinfo | wc -l)

注:如果上述方式还是会有报错,可以在编译内核的环节时直接全部make(如下),但也有可能出错(我出错了),具体可参考文章

1
sudo make V=1 all -j10

试试IMA与EVM

初步开启:试试IMA

在 WSL2 上开启 IMA 可能需要以下步骤:

  • 启用内核的 IMA 支持:这通常需要在内核配置中启用 IMA 相关的选项。由于 WSL2 使用的是微软提供的 Linux 内核,因此可能需要使用自定义内核或者等待微软在将来的更新中支持这一特性。(上述已完成)

  • 安装 IMA 工具:在 Linux 发行版中,可以通过包管理器安装 IMA 工具,例如 ima-evm-utils。在 Ubuntu 中,可以使用以下命令安装:

1
2
3
sudo apt-get install ima-evm-utils
sudo apt-get install attr
#用于获取文件扩展属性
  • 配置 IMA:需要配置 IMA 以测量和报告系统完整性。可以在.wslconfig中进行如下配置——
    1
    2
    3
    4
    [wsl2]
    kernel=D:\\wsl\\my_kernel\\bzImage
    kernelCommandLine=ima_tcb ima_appraise_tcb ima_appraise=fix
    # 新增上面一行,标识Linux 内核的启动参数,本行注释请删除

ima_tcbima_appraise_tcbima_appraise=fix 是 Linux 内核的启动参数,它们与 IMA(Integrity Measurement Architecture,完整性度量架构)和 EVM(Extended Verification Module,扩展验证模块)相关,用于增强系统的安全性。下面是每个参数的含义:

  1. ima_tcb
    • “TCB” 代表 “Trusted Computing Base”,即可信计算基础。
    • ima_tcb 参数启用了 IMA 的度量策略,它会度量所有执行、映射方式访问的文件,以及加载的内核模块、固件、内核等文件。
    • 这个参数确保内核使用默认的可信计算基础测量策略和实例步骤,强制禁止访问之前和当前测量结果不匹配的文件 。
  2. ima_appraise_tcb
    • ima_appraise_tcb 参数是 IMA 评估策略的一部分,它在 ima_tcb 的基础上额外度量以 uid=0 或 euid=0 身份访问的文件。
    • 这个参数用于评估访问的所有属主为0的文件,即系统关键文件,确保它们的完整性 。
  3. ima_appraise=fix
    • ima_appraise=fix 参数将 IMA 设置为修复模式(fix mode)。
    • 在这个模式下,系统可以在没有参考值的情况下启动,允许更新受保护文件的参考值。
    • 这个参数通常用于系统初始化阶段,以便为文件系统上的文件生成或更新 IMA 测量值 。

这些参数通常在系统启动时通过内核命令行参数设置,可以通过修改 GRUB 配置来实现。在 WSL2 环境中,可以通过编辑 /etc/wsl.conf 文件来设置这些参数,从而影响 WSL2 的内核行为。这些设置有助于提高系统的安全性,确保关键文件未被篡改。

配置后,请完成wsl的重启!!

  • 验证IMA配置

在wsl中,输入

1
2
3
4
5
6
cat /proc/cmdline

#以下是响应
initrd=\initrd.img WSL_ROOT_INIT=1 panic=-1 nr_cpus=16 bonding.max_bonds=0 dummy.numdummies=0 fb_tunnels=none console=hvc0 debug pty.legacy_count=0 ima_tcb ima_appraise_tcb ima_appraise=fix

# 出现的最后和我们添加的一致,表明正确。

然后验证是否在开机时就运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 dmesg | grep -i -e EVM -e IMA -w
# dmesg是显示和控制内核环缓冲区的命令

#响应示例
[ 0.194564] device-mapper: core: CONFIG_IMA_DISABLE_HTABLE is disabled. Duplicate IMA measurements will not be recorded in the IMA log.
#====出现以下内容表明IMA已经开始运行了====
[ 0.238684] ima: No TPM chip found, activating TPM-bypass!
[ 0.238854] ima: Allocated hash algorithm: sha256
[ 0.239273] ima: No architecture policies found
#====出现以上内容表明IMA已经开始运行了====
[ 0.239659] evm: Initialising EVM extended attributes:
[ 0.239831] evm: security.selinux
[ 0.239956] evm: security.SMACK64 (disabled)
[ 0.240117] evm: security.SMACK64EXEC (disabled)
[ 0.240319] evm: security.SMACK64TRANSMUTE (disabled)
[ 0.240631] evm: security.SMACK64MMAP (disabled)
[ 0.240848] evm: security.apparmor
[ 0.240995] evm: security.ima
[ 0.241144] evm: security.capability
[ 0.241302] evm: HMAC attrs: 0x1


# dmesg | grep audit可以查看审计日志噢

使用

例如:查看ima值

1
2
3
4
5
6
7
8
sudo  getfattr -m . -d /home/jjq/my_kernel/WSL2-Linux-Kernel-linux-msft-wsl-6.6.36.6

# 响应——

[sudo] password for jjq:
getfattr: Removing leading '/' from absolute path names
# file: home/jjq/my_kernel/WSL2-Linux-Kernel-linux-msft-wsl-6.6.36.6
security.ima=0sAZNOOsDxZUJ5l65LIc7CKyKAEgxK

例如:验证

1
2
3
4
5
 sudo evmctl ima_verify /home/jjq/my_kernel/WSL2-Linux-Kernel-linux-msft-wsl-6.6.36.6
security.ima has no signature
#因为我还木有启用evm

# 更多关于ima的用法,请使用evmctl -h

进一步开启:主要是EVM

准备您的系统以启用 IMA 和 EVM——

  • 导入keyutils

    1
    sudo apt-get install keyutils
  • securityfs 文件系统挂载到 /sys/kernel/security/

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    sudo mount -t securityfs securityfs /sys/kernel/security/

    # ===以下为验证是否挂载成功
    jjq@jjq:~$ cd /sys/kernel/security/
    jjq@jjq:/sys/kernel/security$ ls
    evm ima integrity lockdown lsm safesetid
    # 文件中出现了ima!
    jjq@jjq:/sys/kernel/security$ cd ima
    jjq@jjq:/sys/kernel/security/ima$ ls
    ascii_runtime_measurements binary_runtime_measurements policy runtime_measurements_count violations

在Linux系统中,securityfs 文件系统是一个虚拟的文件系统,它被用来支持内核安全模块,如SELinux、AppArmor等。这个文件系统通常在系统启动时自动挂载到 /sys/kernel/security/ 目录。这个目录包含了安全相关的文件和子系统,例如IMA(完整性度量架构)和EVM(扩展验证模块)。

如果你想让securityfs在系统启动时自动挂载,你可以添加一个条目到/etc/fstab文件中。打开/etc/fstab文件,添加以下行:

1
none    /sys/kernel/security    securityfs    defaults    0    0

保存并关闭文件,下次系统启动时securityfs应该会自动挂载。

  • .wslconfig修改为
    1
    2
    3
    4
    [wsl2]
    kernel=D:\\wsl\\my_kernel\\bzImage
    kernelCommandLine=ima_tcb ima_appraise_tcb ima_appraise=fix evm=fix
    # 即最后加了evm=fix这个参数

重启(如不行,记得使用power shell 运行wsl --shutdown )后,可以再Bash用cat /proc/cmdline验证命令行是否包含新参数

为 EVM 创建并设置公共和私有密钥对——

  • 为 EVM 创建新密钥环:
1
evm_kr_id=$(keyctl newring _evm @u)

命令创建 _evm 密钥环,并将它连接到 @u 系统用户密钥环。

然后,_ evm 的密钥环 ID 分配给 evm_kr_id 变量,以便后续处理。(在此期间请勿关闭bash)

  • 另外,还可查看新创建的密钥环:这个命令的输出显示了键环的详细信息,包括它们的键环的唯一标识符(ID)、访问权限、所有者、会话ID以及键环的名称。
    1
    2
    3
    4
    5
    6
    7
    8
    keyctl show

    # 以下是输出样例
    Session Keyring
    # Session Keyring:表示这是一个会话键环的上下文,会话键环是在用户登录时创建的,并且在用户注销时销毁
    1025767139 --alswrv 0 0 keyring: _ses
    548660789 --alswrv 0 65534 \_ keyring: _uid.0
    456142548 --alswrv 0 0 \_ keyring: _evm

针对访问权限:

  • a 表示任何人都可以读取键环中的密钥,
  • l 表示任何人都可以列出键环中的密钥,
  • s 表示任何人都可以搜索键环中的密钥,
  • w 表示任何人都可以写入键环,
  • r 表示任何人都可以读取密钥的权限,
  • v 表示任何人都可以查看密钥的存在性。
  • 为密钥创建一个目录:
1
sudo mkdir -p /etc/keys/
  • 创建私钥:在 /etc/keys/privkey.pem文件中生成 1024 位 RSA 私钥:
1
sudo openssl genrsa -out /etc/keys/privkey.pem 1024
  • 创建对应公钥:使用之前创建的 /etc/keys/privkey.pem 私钥,将对应的 RSA 公钥派生到 /etc/keys/pubkey.pem 文件中:
1
2
3
4
sudo openssl rsa -pubout -in /etc/keys/privkey.pem -out /etc/keys/pubkey.pem

# 我的输出
writing RSA key
  • 将公钥导入到专用的 EVM 密钥环中:
1
2
3
4
evmctl import --rsa /etc/keys/pubkey.pem $evm_kr_id

# 我的输出
604951908

这个命令将 /etc/keys/pubkey.pem公钥导入到 _evm密钥环中。然后,_evm密钥环附加到内核密钥环。密钥序列号位于上例的输出。

  • 另外,还可查看新导入的密钥

    1
    2
    3
    4
    5
    6
    7
    8
    keyctl show

    # 输出样例(不是我的)
    Session Keyring
    1025767139 --alswrv 0 0 keyring: _ses
    548660789 --alswrv 0 65534 \_ keyring: _uid.0
    456142548 --alswrv 0 0 \_ keyring: _evm
    1054989579 --alswrv 0 0 \_ user: FA0EF80BF06F80AC
  • 创建一个内核主密钥保护 EVM 密钥

1
2
3
4
dd if=/dev/urandom bs=1 count=32 2>/dev/null | keyctl padd user kmk-user @u

# 我的输出
790025544

内核主密钥(kmk)完全保留在内核空间内存中。内核主密钥 kmk 的 32 字节长值是从 /dev/urandom 文件中随机字节生成的,并放置在用户(@u)密钥环中。

根据 kmk 密钥创建一个加密的 EVM 密钥——

  • 创建 kmk 密钥
1
2
3
4
keyctl add encrypted evm-key "new user:kmk 64" @u

# 我的输出
54211731

命令使用 kmk 生成并加密 64 字节用户密钥(名为 evm-key)并将其放置在用户(@u)密钥环中。密钥序列号位于上例的输出部分。

必须为用户密钥 evm-key 命名,因为这是 EVM 子系统预期并可使用的名称。

  • 可以通过bash检查
    1
    2
    3
    4
    5
    6
    7
    8
    9
     keyctl show
    # 我的输出
    Session Keyring
    192193409 --alswrv 1000 65534 keyring: _uid_ses.1000
    119356623 --alswrv 1000 65534 \_ keyring: _uid.1000
    614097528 --alswrv 1000 1000 \_ keyring: _evm
    604951908 --alswrv 1000 1000 | \_ user: $A(�
    54211731 --alswrv 1000 1000 \_ encrypted: evm-key
    790025544 --alswrv 1000 1000 \_ user: kmk-user

激活 EVM——

1
echo 1 > /sys/kernel/security/evm
失败了

IMA策略修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ sudo cat /sys/kernel/security/ima/policy


dont_measure fsmagic=0x9fa0
dont_measure fsmagic=0x62656572
dont_measure fsmagic=0x64626720
dont_measure fsmagic=0x1021994
dont_measure fsmagic=0x1cd1
dont_measure fsmagic=0x42494e4d
dont_measure fsmagic=0x73636673
dont_measure fsmagic=0xf97cff8c
dont_measure fsmagic=0x43415d53
dont_measure fsmagic=0x27e0eb
dont_measure fsmagic=0x63677270
dont_measure fsmagic=0x6e736673
dont_measure fsmagic=0xde5e81e4
measure func=MMAP_CHECK mask=MAY_EXEC
measure func=BPRM_CHECK mask=MAY_EXEC
measure func=FILE_CHECK mask=^MAY_READ euid=0
measure func=FILE_CHECK mask=^MAY_READ uid=0
measure func=MODULE_CHECK
measure func=FIRMWARE_CHECK
measure func=POLICY_CHECK
  • 修改策略

    1
    sudo echo /home/jjq/ima_proj/ima_policy/test_policy_1031 | sudo tee /sys/kernel/security/integrity/ima/policy

    其他

  • docker desktop不支持内核6.6,我退回了5.5

  • 在当前police下,只改文件不会触发重新度量,必须echo/vim后查看一下(cat or 再次vim),而且要是root模式(sudo su)

参考文章