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里配置这些选项,有两种方式:
- 通过内核的defconfig文件:直接在
board/你的板子/linux.config里加上这些选项。 - 通过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 模式。
坑三:内核模块签名导致驱动加载失败
如果你启用了强制模块签名,但忘了给驱动模块签名,那驱动就加载不了。我遇到过有人把密钥文件弄丢了,结果所有新模块都加载不了,只能重新编译内核。所以,密钥一定要备份!
总结一下:内核安全配置,说白了就是“加锁”。锁加多了,系统会变慢,但更安全。锁加少了,系统跑得快,但容易被攻破。你需要根据实际场景找到平衡点。我个人建议,至少把栈保护、地址随机化、模块签名这三个打开。它们是性价比最高的安全选项。
好了,这一章的内容就到这里。下一章,咱们聊聊文件系统的安全加固。到时候我会分享一些关于只读文件系统和完整性校验的实战经验。