操作系统内核加固:从启动到运行的三道防线

内核加固这事儿,说白了就是给操作系统的心脏装三道锁。第一道锁管着谁能往内核里塞代码,第二道锁管着进程能碰什么资源,第三道锁管着内核自己的行为参数。这三道锁缺一不可,我在生产环境里见过太多只做其中一两项然后出事的案例了。

内核模块签名:不让陌生代码进内核

Linux 内核从 3.7 开始支持模块签名验证。你想想看,如果任何人都能往内核里 insmod 一个模块,那 rootkit 简直如入无人之境。我早年做安全审计时遇到过一台服务器,攻击者就是通过加载一个伪造的网卡驱动模块,把所有流量都镜像了一份出去。

启用模块签名的步骤其实不复杂:

  1. 生成签名密钥对
  2. 配置内核编译选项
  3. 对模块进行签名
  4. 启用强制验证

核心配置:内核需要开启 CONFIG_MODULE_SIG=y,并且设置 CONFIG_MODULE_SIG_FORCE=y 才能真正阻止未签名模块加载。

# 生成签名密钥
openssl req -new -x509 -newkey rsa:2048 -keyout signing_key.pem \
    -out signing_key.pem -days 3650 -nodes -subj "/CN=Kernel Signing Key/"

# 对已编译的模块签名
perl scripts/sign-file sha512 signing_key.pem signing_key.pem \
    /lib/modules/$(uname -r)/kernel/drivers/net/xxx.ko

# 验证签名状态
modinfo /lib/modules/$(uname -r)/kernel/drivers/net/xxx.ko | grep sig

个人经验:我建议在构建系统时就集成签名流程,而不是事后手动签名。曾经有个项目因为手动签名漏掉了几个模块,结果内核升级后系统直接无法加载网卡驱动,半夜被叫起来排查。

SELinux 与 AppArmor:强制访问控制的两种流派

传统的 Linux 权限模型是 DAC(自主访问控制),说白了就是文件所有者说了算。但一旦进程被提权,DAC 就形同虚设。这时候就需要 MAC(强制访问控制)上场了。

SELinux 和 AppArmor 是当前主流的两种实现。我个人的习惯是:RHEL/CentOS 系用 SELinux,Ubuntu/Debian 系用 AppArmor。不是说不能混用,而是生态整合度更高。

特性 SELinux AppArmor
标签方式 基于文件系统扩展属性(security.selinux) 基于路径(文件名路径匹配)
策略复杂度 高,需要理解类型强制(TE)模型 中,配置文件更接近自然语言
默认策略 targeted(仅保护关键服务) complain(仅记录不阻止)
适用场景 高安全环境、多级安全(MLS)需求 通用服务器、快速部署
学习曲线 陡峭 平缓

SELinux 实战配置

嗯,这里要注意:SELinux 有三种模式——enforcing(强制)、permissive(宽容)、disabled(禁用)。我见过太多人直接 setenforce 0 或者干脆在配置里改成 disabled,这其实是在自废武功。

# 查看当前模式
getenforce

# 临时切换(重启失效)
setenforce 1   # 强制模式
setenforce 0   # 宽容模式

# 永久配置
vim /etc/selinux/config
SELINUX=enforcing
SELINUXTYPE=targeted

# 查看进程上下文
ps -eZ | grep nginx

# 查看文件上下文
ls -Z /var/www/html/index.html

# 修改文件上下文
semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"
restorecon -Rv /web

避坑指南:我曾经在迁移一个 Web 应用时,因为没处理好文件上下文,导致 Nginx 无法读取静态资源。当时查了半天日志,发现全是 "Permission denied",但 ls -l 看权限又是对的。最后用 ausearch -m avc 才定位到是 SELinux 在拦截。所以记住:SELinux 的审计日志在 /var/log/audit/audit.log,用 ausearch 或 sealert 来分析。

AppArmor 配置示例

AppArmor 的配置相对直观。每个配置文件对应一个可执行文件,定义了它能访问哪些路径。

# 安装工具
apt install apparmor-utils

# 查看状态
aa-status

# 生成配置文件(学习模式)
aa-genprof /usr/sbin/nginx

# 手动编写配置(/etc/apparmor.d/usr.sbin.nginx)
#include <tunables/global>

/usr/sbin/nginx {
  #include <abstractions/base>
  #include <abstractions/nameservice>

  /etc/nginx/** r,
  /var/log/nginx/** w,
  /var/www/html/** r,
  /run/nginx.pid rw,
  /proc/*/status r,
}

# 加载配置
apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx

# 切换到强制模式
aa-enforce /usr/sbin/nginx

我的建议:初次配置 AppArmor 时先用 complain 模式跑几天,收集审计日志,确认没有误拦后再切 enforce。我在一个金融项目中就是这么做的,避免了因为策略过严导致业务中断的尴尬。

内核参数安全优化:调整系统行为基线

内核参数通过 sysctl 接口暴露,分布在 /proc/sys/ 下。优化这些参数相当于给系统打了一针安全疫苗。我习惯把安全相关的参数分成三类:网络防护、内存防护、通用防护。

网络层加固

# /etc/sysctl.d/99-security.conf

# 防 SYN 洪水攻击
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_synack_retries = 2

# 防 IP 欺骗
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# 不响应 ICMP 重定向
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0

# 不发送 ICMP 重定向
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# 忽略源路由包
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0

# 记录可疑包
net.ipv4.conf.all.log_martians = 1

内存与进程防护

# 防缓冲区溢出利用
kernel.randomize_va_space = 2

# 限制 ptrace 范围(防止进程注入)
kernel.yama.ptrace_scope = 1

# 限制 core dump
fs.suid_dumpable = 0
kernel.core_uses_pid = 1

# 限制 dmesg 访问
kernel.dmesg_restrict = 1

# 限制 kptr 暴露
kernel.kptr_restrict = 2

关键参数说明:kernel.randomize_va_space 设置为 2 表示启用完整的 ASLR(地址空间布局随机化),包括堆、栈、mmap 基址和 VDSO 页的随机化。这是对抗缓冲区溢出利用的基础防线。

应用配置

# 立即生效
sysctl -p /etc/sysctl.d/99-security.conf

# 查看当前值
sysctl net.ipv4.tcp_syncookies
sysctl kernel.randomize_va_space

# 持久化验证
sysctl --system

注意:有些参数修改后可能影响应用行为。比如 net.ipv4.tcp_syncookies 在极端高并发下可能轻微影响性能。我建议在测试环境先压测一轮,确认没有副作用再上生产。曾经有个团队在生产环境直接改了 net.core.somaxconn,结果导致 Redis 连接队列溢出,服务大面积超时。

知识体系总览

下面这张图概括了内核加固的三个层次和它们之间的关系。你可以把它当作检查清单来用。

操作系统内核加固知识体系 第一道防线:内核模块签名 防止未授权内核模块加载 生成签名密钥对 编译时/运行时签名 强制验证(CONFIG_MODULE_SIG_FORCE) 第二道防线:强制访问控制(MAC) 限制进程对文件、网络、资源的访问 SELinux(类型强制/多级安全) AppArmor(路径匹配/易上手) 第三道防线:内核参数安全优化 通过 sysctl 调整系统行为基线 网络防护(SYN Cookie / rp_filter) 内存防护(ASLR / kptr_restrict) 通用防护(ptrace_scope / dmesg) 三层防线层层递进,缺一不可。建议按顺序实施并持续审计。

落地检查清单

最后,我整理了一份快速检查清单。每次新服务器上线前,对着跑一遍,能省掉很多事后救火的麻烦。

检查项 验证命令 期望结果
模块签名是否启用 cat /proc/sys/kernel/modules_disabled 0(未禁用模块加载)且签名强制开启
SELinux 模式 getenforce Enforcing
AppArmor 状态 aa-status 关键服务处于 enforce 模式
ASLR 级别 cat /proc/sys/kernel/randomize_va_space 2
SYN Cookie cat /proc/sys/net/ipv4/tcp_syncookies 1
rp_filter cat /proc/sys/net/ipv4/conf/all/rp_filter 1
ptrace_scope cat /proc/sys/kernel/yama/ptrace_scope 1
dmesg_restrict cat /proc/sys/kernel/dmesg_restrict 1

最后说一句:内核加固不是一次性工作。每次内核升级、应用变更、配置调整后,都应该重新验证一遍。我习惯把这些检查项写进 CI/CD 的自动化测试里,确保任何变更都不会意外削弱安全基线。