操作系统安全与端侧 AI 推理部署:部署前防护与拓扑隔离实践

部署端侧推理服务时,吞吐量和显存占用只是验收的一部分。边缘节点还要考虑资源争抢、模型文件权限和进程对宿主机的影响范围。

下面以 ollama runllama.cpp 等推理服务为例,梳理 Linux 上可考虑的隔离措施。具体配置取决于硬件、驱动、容器运行时和业务可用性要求。

1. 资源隔离与防崩塌防护(cgroups v2 & Systemd)

边缘设备中模型推理属于高 CPU/GPU/NPU 消耗型任务。推理服务在处理超长 Prompt 时可能引发内存暴涨。如果缺乏 OS 层面的严格限制,系统可能会遭遇假死甚至崩溃。

内存与 CPU 软硬配额设定

除推理引擎自身的显存限制外,可在 cgroups v2 中为进程设置内存与 CPU 边界。数值需结合宿主机预留、并发和模型大小压测确定。也可评估 oom_score_adj,让内存极端紧张时的回收优先级符合运维预期。

  • 模型资产与敏感数据暴露:端侧部署通常对数据隐私有明确要求。若未对推理进程实施文件系统隔离,推理服务一旦受到攻击,攻击者可能利用接口权限读取宿主机的系统配置或提取模型权重文件。
  • 硬件驱动透传的提权风险:部分部署方案为向容器透传 GPU/NPU 硬件,直接启用 --privileged 标识。这会将底层内核驱动接口完全暴露给容器,提升了宿主机的提权风险。

2. 操作系统级别的三重安全防御堡垒

可以从资源隔离、系统调用过滤和文件访问控制三个层面检查部署配置:

第一重:cgroups v2 硬隔离与 OOM 保护

在推理引擎的显存限制之外,cgroups v2 还可约束内存和 CPU 使用。oom_score_adj 的取值应与系统服务一同评估,并在内存压力测试中确认其行为。

第二重:seccomp 过滤非必要系统调用

AI 推理进程主要执行内存读写、文件读取、网络通信及 GPU 驱动 IOCTL 调用。诸如 ptrace(进程调试)、kexec_load(加载新内核)、process_vm_writev 等高危系统调用需通过 Linux seccomp 过滤器予以屏蔽,收窄攻击面。

第三重:AppArmor / SELinux 隔离与模型只读挂载

模型权重在推理阶段通常只需要读取。可考虑以只读方式挂载模型目录,并借助 AppArmor、SELinux 或 namespace 限制进程的可访问路径;是否适用仍要看更新与缓存流程。

3. 端侧 AI 部署生产拓扑

合规的端侧部署拓扑需在物理硬件、内核隔离层、推理引擎与业务代理之间建立明确的隔离边界:

flowchart TD
    subgraph Host OS ["宿主机操作系统 (Linux Kernel 6.x)"]
        subgraph Hardware ["物理硬件层"]
            GPU["GPU / NPU 硬件 (PCIe 设备)"]
            RAM["系统物理内存"]
        end

        subgraph Isolation ["cgroups v2 & seccomp 隔离区"]
            subgraph Container ["推理服务沙箱 Container (Non-root)"]
                Engine["AI 推理引擎 (llama.cpp / vLLM)"]
                ModelFiles[("模型权重目录 (只读挂载 /ro)")]
            end
        end

        AppArmor["AppArmor 策略 / SELinux 约束"]
        Seccomp["seccomp Syscall 过滤器"]
        Proxy["内部 API Gateway / Nginx (仅绑定 127.0.0.1)"]
    end

    Client["外部业务请求"] --> Proxy
    Proxy -->|Unix Domain Socket| Engine
    Engine --> AppArmor
    Engine --> Seccomp
    Engine --> GPU
    Engine --> ModelFiles

如果推理服务不需要直接对外提供接口,应只在受控网络中开放,并通过网关处理鉴权、限流和审计。网关与引擎可使用 Unix Domain Socket 或受限 TCP 连接,关键是不要把未鉴权接口暴露到不可信网络。

4. 配置与健康检查示例

以下提供一套端侧部署治理脚手架。首先是 Linux 系统的安全防护 Shell 初始化脚本,用于创建无特权运行环境与 cgroups v2 资源配额:

#!/usr/bin/env bash
set -euo pipefail

# 1. 创建专用的无特权 AI 运行用户
if ! id -u ai-runner >/dev/null 2>&1; then
    sudo useradd -r -s /bin/false ai-runner
    echo "[+] 成功创建无特权用户: ai-runner"
fi

# 2. 配置 cgroups v2 资源限制组
CGROUP_PATH="/sys/fs/cgroup/ai_inference"
if [ ! -d "$CGROUP_PATH" ]; then
    sudo mkdir -p "$CGROUP_PATH"
    # 限制内存最大使用 16GB
    echo "17179869184" | sudo tee "$CGROUP_PATH/memory.max"
    # 限制 CPU 使用份额
    echo "400000 100000" | sudo tee "$CGROUP_PATH/cpu.max"
    echo "[+] cgroups v2 资源配额设置完成"
fi

# 3. 设置模型权重目录权限 (仅允许 ai-runner 读取)
MODEL_DIR="/var/models/llm_weights"
sudo mkdir -p "$MODEL_DIR"
sudo chown -R root:ai-runner "$MODEL_DIR"
sudo chmod -R 750 "$MODEL_DIR"
echo "[+] 模型目录安全权限配置完成"

其次是运行在宿主机的 Python 健康度与安全状态巡检程序,用于监控推理进程的资源占用、oom_score_adj 以及模型文件挂载状态:

import os
import sys
import psutil
import logging
from pathlib import Path

logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")

class InferenceEnvironmentChecker:
    def __init__(self, target_process_name: str, model_dir_path: str):
        self.process_name = target_process_name
        self.model_dir = Path(model_dir_path)

    def check_oom_score(self, pid: int) -> bool:
        """检查进程 oom_score_adj 是否配置合规"""
        try:
            oom_adj_file = Path(f"/proc/{pid}/oom_score_adj")
            if oom_adj_file.exists():
                score = int(oom_adj_file.read_text().strip())
                # 生产环境建议推理进程 score 设置在 100~300 之间,高于核心系统进程(0)
                logging.info(f"进程 [{pid}] 的 oom_score_adj 当前为: {score}")
                return True
        except Exception as e:
            logging.warning(f"读取 PID {pid} 的 oom_score_adj 失败: {e}")
        return False

    def verify_model_dir_permissions(self) -> bool:
        """检查模型权重目录权限是否符合安全规范"""
        if not self.model_dir.exists():
            logging.error(f"模型目录不存在: {self.model_dir}")
            return False
            
        # 验证是否包含写权限漏洞
        mode = oct(self.model_dir.stat().st_mode)
        logging.info(f"模型目录 [ {self.model_dir} ] 权限位: {mode}")
        
        # 确保其他用户无全局写权限 (Other Write bit)
        if self.model_dir.stat().st_mode & 0o002:
            logging.error("安全警告:模型目录包含全局可写权限 (Other Writeable)!")
            return False
        return True

    def audit_system_resources(self) -> None:
        """审计宿主机物理内存与推理进程状态"""
        mem = psutil.virtual_memory()
        logging.info(f"系统总内存: {mem.total / (1024**3):.2f} GB, 已用: {mem.percent}%")
        
        found = False
        for proc in psutil.process_iter(['pid', 'name', 'username']):
            if self.process_name in proc.info['name']:
                found = True
                pid = proc.info['pid']
                logging.info(f"找到目标推理进程 [{proc.info['name']}], PID: {pid}, 运行用户: {proc.info['username']}")
                self.check_oom_score(pid)
                
        if not found:
            logging.warning(f"未在当前系统中找到运行中的推理进程: {self.process_name}")

if __name__ == "__main__":
    checker = InferenceEnvironmentChecker(
        target_process_name="llama",
        model_dir_path="/var/models/llm_weights"
    )
    logging.info("开始端侧 AI 部署环境安全检查...")
    checker.verify_model_dir_permissions()
    checker.audit_system_resources()

5. 上线前安全排查 CheckList

在端侧 AI 推理服务正式交付运行前,应对照以下标准检查表逐项确认:

  1. [ ] 用户权限:推理进程是否以无特权用户(如 ai-runner)运行?禁止以 root 用户直接启动推理守护进程。
  2. [ ] 内存隔离:cgroups v2 是否配置了 memory.maxmemory.high?防止显存与内存溢出导致系统死机。
  3. [ ] 文件安全:模型权重目录是否以只读模式挂载?配置文件中是否剥离了所有的硬编码密钥。
  4. [ ] 端口收拢:推理引擎端口(如 11434 / 8000)是否仅绑定至 127.0.0.1 或使用 Unix Socket?避免向外网公开暴露未鉴权的 HTTP 接口。
  5. [ ] 系统调用限制:seccomp 过滤器是否开启,且有效拦截高危 syscall。

上线前应在目标硬件上验证资源上限、驱动访问和故障恢复行为。隔离配置不是一次性动作,模型、驱动或运行时升级后都需要复核。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐