DevStation

版本:26.09

使用手册 ​

本文档是 Censorguard的系统使用手册,覆盖核心概念、策略编写、 运行时开关、进程纳管、策略热更新、观测与故障排查。构建与安装见 构建指南;DeepSeek Harness 集成见 DSH 插件指南。

1. 核心概念 ​

作用域模型:策略引擎基于 eBPF LSM + 自建 PID 树。只有被纳管的进程及其 全部子孙才受策略约束,系统其它进程完全不受影响。fork 出的子进程自动继承 所属域;纳管发生在 exec 屏障上,子进程 exec 前即被拦截,无竞态窗口。

域与策略组:每个被纳管的进程属于一个域(domain),域绑定一个策略组 (group)。进程实际受 全局基线(rules)+ 所在组规则 叠加约束。策略编译为 inode/地址等内核可直接匹配的形式,写进双 bank(one active bank switch): 新策略全部写完后单次切换,失败 reload 不产生"部分新部分旧"的窗口。

fail-closed 语义:enable_* 开关关闭某维度时该维度放行(fail-open); 但策略拒绝永远即时生效。审计默认只记 DENY,ALLOW 需显式打开。

运行模型:

text
YAML -> Rust typed snapshot -> inactive policy bank
                              -> one active_bank switch
tgid -> domain/logical slot -> active bank inner maps
syscall -> LSM/tracepoint -> allow or -EPERM -> ringbuf audit

2. 组件与二进制 ​

二进制角色
censorguardd守护进程:加载 eBPF、维护进程树域、执行策略(root)
censorguardctl控制入口:策略发布、域绑定、进程纳管、运行时开关
censorguard-audit观测入口:订阅事件流(DENY/ALLOW)
censorguard-grpc非特权 gRPC 适配器:127.0.0.1:50051 → ui.sock/events.sock,供 DSH 插件使用
censorguard-exec启动器:供外部宿主登记进程

daemon 暴露五条 Unix socket,各有角色 ACL(SO_PEERCRED 鉴权):

socket默认路径角色用途
ctl.sock/run/censorguard/ctl.sockAdminroot 级控制(reload/bind/apply)
events.sock/run/censorguard/events.sockAudit事件流订阅
launch.sock/run/censorguard/launch.sockLauncher以域身份启动命令
dsh.sock/run/censorguard/dsh.sockDshDSH 自助 attach(普通用户)
ui.sock/run/censorguard/ui.sockUiDSH 委托面:白名单组策略读写、开关切换

daemon 从不监听网络端口;gRPC 适配器只绑 127.0.0.1。

3. 策略配置(一行式,唯一合法格式) ​

策略 YAML 只接受三个顶层键,旧格式字段(enable_file、policy_groups、 file_rules 等)一律被严格拒绝(报 unknown field):

yaml
rules:                    # 全局基线,对所有被纳管进程生效,可省略
  - file deny+audit /etc/shadow
  - exec deny /usr/bin/nc
groups:                   # 命名策略组,每组只有 rules,可省略
  web:
    rules:
      - net deny 0.0.0.0/0
      - net allow api.github.com:443
domains:                  # 域 → 组绑定,可省略;省略 group 表示仅受基线约束
  - name: dsh-web
    group: web

3.1 规则语法 ​

一行式:file|exec|net 动作 目标 [掩码]

以下示例展示的都是 rules: 列表内的单行写法(规则语法与所在位置无关): 同一个 rules: 列表既可以出现在顶层(全局基线),也可以出现在 groups.<name>.rules(组规则),行内语法完全一致。只写 rules: 一个键 的文件同样合法(如 config/base.yaml)。

  • 动作:allow、deny、allow+audit、deny+audit(+audit 表示命中 ALLOW 也上报;DENY 永远上报)。
  • file 掩码可选:none(默认,全部操作)、ro、rw、noattr、 [read,write,delete,rename,attr]。
  • exec 目标可带参数前缀:exec deny /usr/bin/git push 只拦 push 子命令。
  • net 支持 IPv4/IPv6 地址、CIDR、端口、域名(域名由 daemon 做 DNS 展开 与周期刷新)。
yaml
rules:
  - file deny+audit /var/lib/censorguard [write,delete,rename]
  - exec deny+audit /usr/bin/rm -rf /
  - net deny 2001:db8::/32                 # 裸 IPv6/CIDR 无端口
  - net allow+audit [::1]:443              # 带端口必须 [IPv6/CIDR]:PORT
  - net deny api.example.com:8443          # 域名规则,DNS 自动展开

3.2 inode 陷阱 ​

file/exec 规则在编译期解析为 inode。策略加载后重建目标文件会改变 inode,需 reload 策略才能重新命中。规则"不生效"时先用 policy-dump 确认规范化策略,再检查目标文件是否被重建过。

完整示例见 config/base.yaml、config/standard-dev.yaml、 config/high-priv.yaml、config/policy.dsh-default.yaml(DSH 默认组)。

4. 运行时开关(替代旧配置字段) ​

enable_file/enable_exec/enable_net(默认全开)与 audit_file/audit_exec/audit_net(默认全关)是运行时开关,不写在 配置文件里:

bash
sudo censorguardctl set --audit-file on                    # 打开文件 ALLOW 审计
sudo censorguardctl set --enable-net off --audit-exec on   # 可一次多个
  • 等价接口:RPC v2 set_switches;DSH WebUI 设置页(经 ui.sock 委托面)。
  • ctl 设置需 root(SO_PEERCRED 鉴权)。
  • 不持久化:daemon 重启恢复默认;reload 保持当前值。
  • 任一 audit_* 打开时,该类 ALLOW 事件全量上报;enable_*=off 时对应 维度 fail-open(放行)。

5. 启动 daemon ​

5.1 systemd 方式(生产推荐) ​

bash
sudo systemctl enable --now censorguardd.service
sudo systemctl status censorguardd        # 日志出现 [READY] 即就绪

unit 内置 --spawn-grpc:daemon 自动派生并守护 gRPC 适配器 (127.0.0.1:50051),DSH 插件无需额外配置即可连接。状态目录为 /var/lib/censorguard(策略 revision 快照,跨重启恢复)。

5.2 手动方式(测试/调试) ​

bash
# socket 父目录必须属 root 且 group/other 不可写(/tmp 会被拒绝)
sudo install -d -m 0755 /run/agsec
sudo censorguardd \
    --config /run/agsec/policy.yaml \
    --bpf-object /usr/lib/censorguard/enforce.bpf.o \
    --ctl-sock /run/agsec/ctl.sock \
    --event-sock /run/agsec/events.sock

常用参数:--duration N(N 秒后自动退出)、--state-dir DIR、 --socket-group NAME、--launch-sock PATH、--dsh-sock PATH、 --ui-sock PATH、--spawn-grpc/--grpc-bin/--grpc-listen(派生适配器)、 --check-config(只校验不加载)。socket 在 /run/censorguard 之外时 状态目录是临时的,退出自动清理。

6. 纳管进程 ​

bash
# 在域里启动命令(推荐,带执行屏障,无竞态)
sudo censorguardctl spawn --domain dsh-web -- curl https://example.com

# 纳管已在运行的进程(--seed 回填已有子孙)
sudo censorguardctl attach --pid 12345 --domain dsh-web --seed

spawn 透明地以子进程退出码退出(子进程 exit 3 → ctl 退出码 3; 被策略拒绝 → 非零)。跨 UID attach 需要 root。

纳管后整棵进程树(fork 自动继承 + pending 降级层)都在 eBPF LSM 强制 之下,不经任何插件通道启动的子进程也逃不掉。

7. 策略热更新 ​

bash
# 整份替换(原子双 bank 切换,失败自动回滚)
sudo censorguardctl reload --file /path/to/policy-v2.yaml

# 组级热更新(组文档只有 rules 一个键)
printf 'rules:\n  - exec deny /usr/bin/id\n' | \
    sudo censorguardctl policy apply --name web --stdin

# 换绑 / 解绑(空组 = 仅受基线约束)
sudo censorguardctl bind dsh-web --group web
sudo censorguardctl bind dsh-web --group ''

组级 apply 带 revision 乐观锁:传 --expected-revision N 与当前不符则 409 冲突。全量 reload 支持 SIGHUP(systemctl reload censorguardd)。 已应用策略以 revision 快照持久化在 /var/lib/censorguard/revisions/, daemon 重启自动恢复最新 revision。

8. 观测 ​

bash
sudo censorguard-audit launch --socket /run/censorguard/events.sock            # 全量事件流
sudo censorguard-audit launch --socket /run/censorguard/events.sock --kind 2   # 只看 exec (1=file 2=exec 3=net 4=guard)

censorguardctl status        # 开关/版本/bank/reload 状态
censorguardctl tree          # 域进程树
censorguardctl policy-dump   # 当前生效的规范化策略

事件字段要点:每事件带单调 sequence(从 1 起)、daemon_boot_id(区分 daemon 重启)与 dropped_before(本订阅者累计丢失)。事件无持久化,是 实时观察窗。

一个动态链接程序可能产生多个 FILE ALLOW(动态加载器、libc、locale 和 配置文件的读取),这表示文件打开检查点,不表示执行了多个可执行文件; 只关心执行事件用 --kind 2 过滤。

9. 测试 ​

bash
make check                                   # 静态检查(fmt/clippy/bash -n/工具语法)
make test                                    # Rust 单元与编译器测试(不需要 root)
cd plugins/dsh-censorguard && pnpm test    # DSH 插件测试(不需要 root)
sudo bash scripts/test/integration-basic.sh  # 基础验收(14 用例,root + BPF LSM)
sudo make integration                        # 全量 30+ 场景(root + BPF LSM)

集成测试要求内核启用 BPF LSM(cat /sys/kernel/security/lsm 含 bpf)。 integration-basic.sh 是门禁:全部隔离在临时目录,不碰 /run 与生产 state。场景覆盖:文件 read/write/delete/rename/chmod、link/symlink/mkdir/ rmdir/mknod、chown/xattr/ACL、预打开 fd、命令与参数级 exec、多域隔离、 execveat、IPv4/IPv6 与 DNS、reload/bind/SIGHUP/回滚、PID generation/ fork/draining/churn、事件丢弃压力、审计开关、文件压力、graceful shutdown 等。

10. 故障排查 ​

现象原因与处理
socket parent must be owned by uid 0 ...socket 父目录权限不合(如 /tmp 1777)。建专用目录:sudo install -d -m 0755 /run/agsec
启动报 unknown field 'enable_file' 等配置是旧格式,按 §3 改写成一行式;或 /var/lib/censorguard 里有旧格式 revision 快照,停机后 sudo rm -rf /var/lib/censorguard
daemon 反复重启,日志报 unknown argument "--spawn-grpc"旧二进制 + 新 systemd unit 不匹配:重新构建并同步安装二进制
Text file busy 复制二进制失败二进制正在运行,先 systemctl stop 再复制
ctl 报 No such file or directorydaemon 未启动或 socket 路径不一致,先确认日志 [READY]
policy apply/bind/set 报权限错误需要 root(SO_PEERCRED),用 sudo
规则不命中file/exec 规则按 inode 编译,目标文件重建后需 reload(§3.2);用 policy-dump 确认规范化策略
看不到 ALLOW 事件默认只记 DENY;set --audit-<维度> on 或给规则加 +audit
DSH attach 报 references uninstalled groupDSH 策略组未下发,root 执行 sudo censorguardctl policy apply --name censorguard-dsh-default --file config/policy.dsh-default.yaml