4、内核安全配置:内核安全子系统概述、启用SELinux/AppArmor、配置内核安全选项

说到嵌入式安全,内核这一层是绕不过去的坎。我个人的习惯是,拿到一块新板子,先不急着跑应用,而是先把内核安全选项过一遍。你想想看,内核是整个系统的基石,如果它本身就有漏洞,上层做得再花哨也没用。

这一章,咱们就聊聊内核安全子系统的那些事。说白了,就是怎么让Linux内核变得更“硬”,不容易被攻破。

4.1 内核安全子系统概述

Linux内核的安全机制,其实是一套组合拳。它不是靠某一个功能单打独斗,而是多个子系统协同工作。

我把它分成三个层次:

  • 强制访问控制(MAC):比如SELinux、AppArmor。它们比传统的“用户-组-其他”权限模型更细粒度。
  • 能力机制(Capabilities):把root用户的超级权限拆成一个个小权限。比如你只需要开个socket,没必要把整个root权限都交出去。
  • 内核自我保护:比如栈保护、地址随机化、内核模块签名等。这些是防止内核自身被攻击的。

我在项目中遇到过一种情况:客户觉得只要开了防火墙就万事大吉了。结果呢?攻击者通过一个内核漏洞直接提权,防火墙形同虚设。嗯,从那以后,我每次做方案都会强调内核安全的重要性。

核心观点:内核安全不是可选项,而是必选项。尤其在物联网设备上,攻击者往往直接瞄准内核漏洞。

4.2 启用SELinux与AppArmor

SELinux和AppArmor,这两个都是强制访问控制的实现。但它们的理念不太一样。

SELinux:它给每个进程、每个文件都打上标签(安全上下文)。然后通过一套策略规则,决定谁可以访问谁。配置起来比较复杂,但粒度非常细。

AppArmor:它走的是“路径匹配”路线。你给某个程序写一个配置文件,告诉它“你只能读这些文件,只能写这些文件”。配置相对简单,容易上手。

在Buildroot里怎么选?我个人建议:

  • 如果你的设备对安全性要求极高(比如金融、军工),选SELinux。虽然配置麻烦,但值得。
  • 如果是普通的物联网设备,AppArmor就够用了。配置简单,维护成本低。

在Buildroot中启用它们,其实不复杂。你需要在 make menuconfig 里找到对应的选项:

Target packages → 
  [*] selinux   (或者 apparmor)

然后,内核配置里也要打开支持:

Security options → 
  [*] Enable different security models
  [*]   SELinux support
  [*]   AppArmor support

我曾经在一个项目里同时启用了SELinux和AppArmor,结果发现它们互相冲突。后来查了文档才知道,这两个东西不能同时跑。你只能二选一。

注意:SELinux和AppArmor不能同时启用。它们会争夺安全模块的控制权,导致系统行为不可预测。

4.3 配置内核安全选项

除了SELinux和AppArmor,内核本身还有很多安全开关。这些开关默认可能是关闭的,或者处于“宽松”模式。我们需要手动把它们拧紧。

我整理了一份常用的内核安全选项清单,你可以对照着检查:

选项 说明 建议值
CONFIG_SECURITY_DMESG_RESTRICT 限制非root用户查看dmesg日志 y
CONFIG_STRICT_KERNEL_RWX 内核内存区域读写执行权限分离 y
CONFIG_STACKPROTECTOR_STRONG 强栈保护,防止栈溢出攻击 y
CONFIG_RANDOMIZE_BASE 内核地址空间随机化(KASLR) y
CONFIG_MODULE_SIG 强制内核模块签名验证 y
CONFIG_SYN_COOKIES 防止SYN Flood攻击 y
CONFIG_LEGACY_PTYS 禁用过时的伪终端设备 n

在Buildroot里配置这些选项,有两种方式:

  1. 通过内核的defconfig文件:直接在 board/你的板子/linux.config 里加上这些选项。
  2. 通过Buildroot的Kernel配置界面:运行 make linux-menuconfig,手动勾选。

我个人更推荐第一种方式。因为这样配置是可复现的,换个人来编译,结果是一样的。

举个例子,如果你想启用内核模块签名验证,可以在linux.config里加上:

CONFIG_MODULE_SIG=y
CONFIG_MODULE_SIG_FORCE=y
CONFIG_MODULE_SIG_SHA512=y
CONFIG_MODULE_SIG_HASH="sha512"

然后,你还需要生成一个签名密钥。这个密钥要妥善保管,一旦丢了,以后就没法加载新模块了。

小技巧:内核模块签名密钥建议放在一个安全的地方,比如HSM(硬件安全模块)里。如果只是测试,可以放在构建服务器的某个目录下,但生产环境千万别这么干。

4.4 避坑指南

讲几个我踩过的坑,希望能帮你省点时间。

坑一:开了SELinux,系统起不来

我曾经在一个项目里,兴冲冲地编译了带SELinux的内核,结果系统启动到一半就卡住了。后来发现,是因为文件系统没有打上正确的安全上下文标签。解决办法是:在根文件系统里预先设置好标签,或者在启动脚本里用 restorecon 修复。

坑二:AppArmor配置文件写错了

AppArmor的配置文件语法比较严格。少了一个花括号,或者路径写错了,程序就直接崩溃。我建议先在开发板上用 aa-complain 模式调试,等配置没问题了再切换到 aa-enforce 模式。

坑三:内核模块签名导致驱动加载失败

如果你启用了强制模块签名,但忘了给驱动模块签名,那驱动就加载不了。我遇到过有人把密钥文件弄丢了,结果所有新模块都加载不了,只能重新编译内核。所以,密钥一定要备份!

总结一下:内核安全配置,说白了就是“加锁”。锁加多了,系统会变慢,但更安全。锁加少了,系统跑得快,但容易被攻破。你需要根据实际场景找到平衡点。我个人建议,至少把栈保护、地址随机化、模块签名这三个打开。它们是性价比最高的安全选项。

好了,这一章的内容就到这里。下一章,咱们聊聊文件系统的安全加固。到时候我会分享一些关于只读文件系统和完整性校验的实战经验。