操作系统安全加固与可信计算:LSM、内核加固、TPM 与容器逃逸防护

操作系统安全的本质是纵深防御:从内核自身的加固(KASLR、栈保护、模块签名),到强制访问控制层 LSM(SELinux/AppArmor),再到硬件信任根(TPM 可信启动与远程证明),以及容器场景的逃逸防护(seccomp、Capabilities、最小权限)。本文先画出攻击面全景,再逐层拆解每一道防线的工作原理与配置方法,最后给出生产环境可落地的加固清单与逃逸事件处置路径。

操作系统安全不是"装个杀毒软件",而是一层套一层的纵深防御(Defense in Depth)。内核的漏洞一旦被利用,用户态的防护全部失效——所以真正可靠的边界,从内核自身、到强制访问控制、到硬件信任根、再到容器逃逸防护,每一层都必须尽责。

本文先画出操作系统攻击面全景,然后逐层深入:LSM(SELinux/AppArmor)如何把 root 的"全知全能"收窄为最小权限;内核加固(KASLR、栈保护、模块签名)如何提高利用成本;TPM 与可信启动如何让"开机即可信"成为可验证的事实;最后落到云原生场景最关心的容器逃逸防护与处置路径。


攻击面全景:操作系统都有哪些门?

防御的前提是知道门在哪。操作系统的主要攻击面包括:

攻击面风险点典型利用
内核漏洞本地提权(LPE)、任意代码执行Dirty Pipe、Dirty COW
系统调用接口语义绕过、TOCTOU低权限调用危险 syscall
驱动/固件内存损坏、DMA 攻击IOMMU 缺失时的 DMA 攻击
用户态服务越权、配置错误SUID 二进制提权
启动链预启动 RootkitBootkit 替换 Bootloader
容器边界逃逸到宿主挂载宿主目录、capabilities 滥用

每类攻击面都对应专门防线。下面按"内核 → LSM → 信任根 → 容器"的顺序逐层建立防线。


LSM:Linux 安全模块框架

Linux 本身只有传统的 DAC(自主访问控制):权限由文件属主决定,root 拥有全部权力。root 一旦被攻破,系统就失守。LSM(Linux Security Module) 引入 MAC(强制访问控制):即使你是 root,也必须先通过安全策略的授权检查。

LSM 框架如何工作

LSM 在内核的关键路径(open、exec、mount、socket、ptrace…)上插入钩子函数(hook)。每个钩子调用已注册的 LSM 实现,任何一方拒绝,操作即被禁止:

sys_open() → ... → security_file_open() → SELinux 检查 → 允许/拒绝
                                                  └→ AppArmor 检查

内核通过 security_hook_heads 链表串联多个 LSM(如 integrity、capability、selinux),组合出"能力检查 + MAC 检查"的双层门禁。

SELinux:标签与策略(TE/AVC)

SELinux 用**安全标签(Label)**标记主体(进程)与客体(文件、端口、socket),策略规则形如"主体类型 A 对客体类型 B 能否执行动作 C"。它的核心是三块:

  • 类型强制(TE,Type Enforcement):按 type 划分主体与客体,规则定义 type 间许可。
  • AVC(Access Vector Cache):缓存检查结果,避免每条路径重复查策略。
  • 布尔值(Boolean):运行时开关常用策略子集。
# 查看/修改进程的 SELinux 上下文
ls -Z /etc/shadow        # system_u:object_r:shadow_t:s0
ps -eZ | grep sshd       # system_u:system_r:sshd_t:s0

# 常用运维命令
getenforce               # Enforcing / Permissive / Disabled
sestatus
setsebool -P httpd_can_network_connect on   # 临时/持久化布尔

RHEL/Fedora 系默认启用 SELinux。其策略语言强大但难写,因此生产上常见"Enforcing + 放行白名单"的折中,或用 audit2allow 从审计日志生成放行规则。

AppArmor:路径约束

AppArmor 与 SELinux 哲学不同:按路径(file path) 而非标签定义权限。它更适合直觉理解,是 Ubuntu/Debian 的默认选择。配置是一份"该程序能碰什么"的声明:

# /etc/apparmor.d/usr.sbin.nginx
abi <abi/4.0>,
include <tunables/global>
profile nginx /usr/sbin/nginx {
  include <abstractions/base>
  capability net_bind_service,
  network inet tcp,
  /etc/nginx/conf.d/ r,
  /var/log/nginx/*.log w,
  /usr/share/nginx/html/ r,
}
apparmor_status                 # 查看已加载 profile 与状态
aa-enforce /usr/sbin/nginx      # 强制模式
aa-complain /usr/sbin/nginx     # 仅记录不阻止(调试用)

SELinux vs AppArmor

维度SELinuxAppArmor
控制模型标签 + TE 策略路径 + profile
学习成本高(策略语言复杂)低(直觉)
精细度极高(类型/角色/布尔)中(路径/能力)
默认发行版RHEL/Fedora/CentOSUbuntu/Debian
适合严格合规环境快速加固

无论哪个,核心价值相同:把进程权限收窄到它真的需要的最小集合——即最小权限原则的落地。


内核加固:把利用成本提高到"不值得"

LSM 管的是"授权",内核加固管的是"让漏洞不好利用"。内核层面常见的加固手段:

内存布局随机化与隔离

  • KASLR(Kernel Address Space Layout Randomization):随机化内核代码/数据的加载地址,让攻击者无法预测符号地址。
  • PTI / KAISER:隔离用户态与内核态页表,缓解 Meltdown 类侧信道。
  • KPTR 隐藏:kptr_restrict 限制 /proc/kallsyms 暴露内核符号给普通用户。
# 查看内核保护措施
cat /proc/cmdline | tr ' ' '\n' | grep -E "kaslr|pti|nopti"
sysctl kernel.kptr_restrict   # 2 表示非特权用户不可见

编译期与运行时防护

  • 栈保护:-fstack-protector-strong(CONFIG_STACKPROTECTOR_STRONG)在栈帧加金丝雀值,检测溢出。
  • CFI(控制流完整性):间接调用点校验目标地址,阻止函数指针劫持(Clang CFI / ARM PAC)。
  • CONFIG_HARDENED_USERCOPY:校验拷贝的目标是否属于对象边界。
  • CONFIG_RANDOMIZE_KSTACK_OFFSET:每次中断随机化内核栈偏移。

模块签名与锁定内核

  • CONFIG_MODULE_SIG:只加载有合法签名(内核签名密钥)的模块,阻断"加载恶意内核模块"这条提权捷径。
  • CONFIG_SECURITY_LOCKDOWN_LSM:一旦启用 lockdown 模式,禁止加载未签名模块、禁止读写内核内存设备(/dev/mem、/dev/kmem)。
# 查看模块签名状态
cat /proc/sys/kernel/modules_disabled     # 1 表示禁止加载任何模块
modinfo --signature nf_tables.ko | head   # 查看模块签名

sysctl 加固基线

一组高频加固 sysctl(写入 /etc/sysctl.d/99-hardening.conf):

# 限制 dmesg 访问、隐藏内核符号
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2

# 限制 ptrace(防注入)
kernel.yama.ptrace_scope = 2

# 关闭非必要的核心转储/重定向
fs.suid_dumpable = 0
kernel.core_uses_pid = 1

# 网络层:禁 IP 转发除非需要
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0

加固不是一键脚本,而是一份与业务形态相关的权衡清单——过度加固会破坏功能,需在合规与可用性之间取舍。


TPM 与可信计算:让"可信"可验证

前几层防线假设"软件没问题"。但软件可能被提前替换——Bootkit 可以在内核加载前注入恶意代码。可信计算(Trusted Computing) 用硬件信任根回答这个问题。

TPM 芯片

TPM(Trusted Platform Module) 是主板上的独立安全芯片,具备:

  • 密钥生成与存储(且私钥永不离开芯片)。
  • PCR(Platform Configuration Register):只支持扩展(extend)的度量寄存器,记录软件状态摘要。
  • 签名与加密运算。

可信启动链(Trusted Boot)

可信启动不是"验证签名"(那是 Secure Boot),而是逐级度量:每级启动组件在把控制权交给下一级之前,先把自己(及被加载的下一级)的哈希 extend 进 TPM 的 PCR:

固件 → extend PCR[0]
 Bootloader → extend PCR[4]
 内核 → extend PCR[?]
  initramfs → ...

PCR 有一个关键性质:PCR[n+1] = H(旧值 || 新度量)。因此PCR 的最终值只取决于整条启动链的完整历史——任何人替换了任意一级,PCR 值都会不同。系统运行时,你(或远程验证方)可以请求 TPM 签名一个 PCR 摘要(Quote),以证明这台机器是用可信组件启动的。

远程证明(Remote Attestation)

远程证明是可信计算最有价值的应用:一台服务器向另一台(验证方)证明"我的启动链是干净的"。流程:

验证方 → 随机数 Nonce
服务器:TPM 对 (PCR 摘要 || Nonce) 签名 → 返回 Quote
验证方:用服务器 TPM 的公钥验签,比对 PCR 摘要是否等于基准值

现代发行版结合 IMA(Integrity Measurement Architecture) 还能度量到每个文件,形成更细粒度的可信证明。工具如 tpm2-tools、vTPM(虚拟机)、measured boot(测量启动)都是这条技术栈的实现。

# tpm2 查看 PCR 值
tpm2_pcrread sha256:0,4,5
# 打印启动日志中 TPM 度量事件(EFI 变量)
ls /sys/kernel/security/tpm0/binary_bios_measurements

Secure Boot vs 可信启动

维度Secure BootTrusted Boot(TPM 度量)
机制签名验证(谁签的)哈希度量(是什么)
防什么未签名/篡改组件加载记录整条启动链历史
谁来信任固件的 db 数据库TPM 硬件信任根
证明无(只防本地加载)可远程证明

两者互补:Secure Boot 挡住恶意 Bootloader 加载,Trusted Boot 让"整条链"可被验证。详见 https://plumephp.com/os-boot-process/ 中 Secure Boot 与启动链的讨论。


容器逃逸防护:云原生场景的最后防线

容器与宿主机共享内核,容器的安全边界完全依赖内核自身的安全机制。攻破容器的内核漏洞,等于直接攻破宿主。防护从四个维度叠加。

最小权限:Capabilities 与 rootless

容器默认去掉危险 capabilities,--privileged(等价于 CAP_SYS_ADMIN + 全设备访问)是逃逸的最快捷径,生产禁止默认开启:

# 查看进程 capabilities
capsh --print
grep CapEff /proc/self/status

# Docker 最小化
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE --user 10001:10001 nginx

seccomp:系统调用过滤

seccomp 在内核层过滤系统调用,是容器逃逸的高性价比防线。默认 docker-default profile 已禁止约 44 个高危 syscall(mount、ptrace、kexec_load…)。

# 用 strace 收集应用实际用到的 syscall,生成白名单
strace -f -e trace=network -o /tmp/syscalls.log ./app
# 据此生成 seccomp profile 后
docker run --security-opt seccomp=./profile.json nginx

seccomp 的过滤由 seccomp(2) 与 BPF 过滤器(SECCOMP_RET_ERRNO / SECCOMP_RET_TRAP)实现——底层正是 https://plumephp.com/os-ebpf-observability/ 讨论的 BPF 指令集,机制一脉相承。

LSM 与只读根文件系统

  • 容器内启用 AppArmor/SELinux profile(Docker 默认 docker-default AppArmor)。
  • --read-only 让容器根文件系统只读,攻击者无法持久化写入。
  • 禁用 --mount type=bind 对宿主敏感路径(/、/etc、/proc)的写挂载。

逃逸事件处置路径

当检测到逃逸迹象(异常 mount、unshare、可疑进程——可用 https://plumephp.com/os-ebpf-observability/ 的 execsnoop/mount 追踪),处置三板斧:

# 1) 立即冻结容器
docker pause <ctr>          # 或 kubectl rollout 隔离

# 2) 捕获证据(进程、网络、文件改动)
docker inspect <ctr> > evidence.json
ps aux | grep <ctr_pid>
ss -tnp | grep <ctr_pid>

# 3) 隔离宿主、下线重建,审计宿主内核漏洞与内核版本
uname -r
# 升级内核 / 启用 lockdown / 更新 seccomp 策略
# 纵深防御基线组合
docker run \
  --user 10001:10001 \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  --security-opt seccomp=default.json \
  --security-opt apparmor=docker-default \
  --read-only \
  --tmpfs /tmp \
  -p 8080:80 nginx

结语

操作系统安全是一场"拉高攻击成本"的持久战。从 LSM 把 root 收窄为最小权限,到 KASLR/CFI/模块签名让漏洞难以利用,再到 TPM 让"可信"变成可以远程验证的事实,最后到 seccomp/Capabilities 守住容器边界——每一层都假设"上一层已被攻破",每一层都让攻击者多付出一份代价。

对工程师而言,正确的姿态不是追求"绝对安全",而是建立可度量的纵深:知道自己有哪些攻击面、每一层防线在哪、加固到什么程度、如何验证与审计。这套体系配合 eBPF 的实时观测(https://plumephp.com/os-ebpf-observability/)与容器的资源隔离(https://plumephp.com/os-virtualization-containers/),才能构成真正完整的 OS 安全视图。


延伸阅读

  1. Linux Kernel Documentation: Documentation/admin-guide/LSM/ 与 security/lsm_hooks.h
  2. SELinux 项目文档:github.com/SELinuxProject/selinux
  3. TPM2 规范与 tpm2-software/tpm2-tools
  4. Docker/OCI Runtime specs(seccomp、capabilities、Linux namespace 配置)
  5. NSA/CIS Linux Benchmark(加固基线:CIS Benchmarks)

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「os」更多文章

  1. 虚拟化与容器隔离:从 KVM/QEMU 到 namespace 与 cgroup
  2. 系统启动全流程:从 BIOS/UEFI、GRUB 到内核与 systemd
  3. 实时操作系统:RTOS 内核设计、优先级反转与 Linux PREEMPT_RT