false-systems/syva

GitHub: false-systems/syva

基于 eBPF 和 LSM 钩子的 Kubernetes 内核级容器隔离加固工具,通过 zone 策略在内核层面强制执行容器间访问边界。

Stars: 1 | Forks: 0

# Syva **您的容器共享同一个内核。Syva 让内核在它们之间强制执行边界。** 如今,容器隔离是结构性的 —— namespaces 和 cgroups 建立了围墙。但是,没有任何东西检查容器 A 是否正在访问容器 B 的文件、向其发送信号或附加调试器。内核并不知道这些容器不应该交互。 Syva 解决了这个问题。它将小程序加载到内核中(通过 eBPF),拦截安全敏感操作 —— 打开文件、执行二进制文件、发送信号 —— 并检查调用者是否被允许触碰目标。如果不允许,操作在发生之前即被拒绝。 无 sidecar。无代理。无运行时替换。每个节点部署一个代理,标记您的容器,完成。 ## Syva 的功能 想象同一节点上的两组容器: ``` Node ═══════════════════════════════════════════════════ Zone: "frontend" Zone: "database" ┌─────────────────┐ ┌─────────────────┐ │ │ │ │ │ nginx │ │ postgres │ │ react-app │ │ redis │ │ │ │ │ └─────────────────┘ └─────────────────┘ │ │ │ open("/db/data") ──────X │ │ kill(pg_pid, 9) ───────X │ │ ptrace(redis_pid) ─────X │ │ │ X = denied by Syva in the kernel ``` 如果没有 Syva,这些容器可以通过共享内核进行交互 —— 通过 `/proc` 读取彼此的文件、发送信号、附加调试器。有了 Syva,每一个此类操作都会触及一个内核检查点,该检查点首先验证 zone 成员身份。 ## 工作原理 —— 分步说明 **步骤 1:您标记您的容器。** 向您的 pod 或容器添加注解: ``` metadata: annotations: syva.dev/zone: "frontend" ``` **步骤 2:您编写 zone 策略。** 每个 zone 一个 TOML 文件。文件名即 zone 名称。 ``` /etc/syva/policies/ ├── frontend.toml └── database.toml ``` **步骤 3:Syva 监控 containerd。** 当容器启动时,Syva 读取其注解,查找匹配的策略,并将条目写入内核级 map: ``` Container starts │ ▼ Syva sees the containerd event │ ▼ Reads annotation: syva.dev/zone = "frontend" │ ▼ Writes to BPF map: cgroup_id → zone "frontend" │ ▼ Now the kernel knows this container belongs to "frontend" ``` **步骤 4:内核执行。** 从此时起,该容器的每一个敏感操作都会经过 Syva 的内核钩子: ``` Process in "frontend" zone calls open() │ ▼ Kernel hits the file_open hook │ ▼ Syva's eBPF program runs: ┌─────────────────────────────────────┐ │ 1. What zone is the caller in? │ → "frontend" │ 2. What zone owns this file? │ → "database" │ 3. Are these zones allowed to talk? │ → No │ 4. DENY. Return -EACCES. │ └─────────────────────────────────────┘ │ ▼ Process gets "Permission denied" Event logged to ring buffer ``` 此检查发生在**内核内部**,每次调用时都会执行。没有到用户空间的往返。路径中没有守护进程。 ## 五个内核钩子 Syva 拦截五种操作。它们共同涵盖了容器通过共享内核进行交互的主要方式: ``` ┌─────────────────────────────────────────────────────────────┐ │ │ │ open() Can this process read/write this file? │ │ │ │ exec() Can this process run this binary? │ │ │ │ ptrace() Can this process debug/inspect that process? │ │ │ │ kill() Can this process send a signal to that process? │ │ │ │ cgroup_attach() Can this process escape its zone? │ │ │ └─────────────────────────────────────────────────────────────┘ Each hook: caller zone ──?── target zone same zone → allow different zone, no policy rule → DENY different zone, explicit allow → allow ``` 每次拒绝都会记录调用者 PID、两个 zone ID 以及特定于钩子的上下文(文件 inode、目标 cgroup 等)。 ## Zone 策略 策略目录中的每个 `.toml` 文件定义一个 zone。文件名即 zone 名称 —— `agent-sandbox.toml` 创建 zone `agent-sandbox`。 ``` # database.toml [capabilities] allowed = ["CAP_NET_BIND_SERVICE", "CAP_CHOWN"] [resources] cpu_shares = 1024 memory_limit = 536870912 # 512Mi in bytes pids_max = 512 [network] mode = "bridged" allowed_zones = ["frontend"] # frontend can talk to database allowed_egress = ["0.0.0.0/0:443"] [filesystem] writable_paths = ["/data", "/tmp", "/var/log"] [syscalls] deny = ["mount", "umount2", "pivot_root"] ``` **默认:拒绝一切。** 仅当两个 zone 在 `allowed_zones` 中互相列出时,才会发生跨 zone 通信。 ## 部署 ### 在 Kubernetes 上 ``` # 1. 创建策略作为 ConfigMap kubectl create configmap syva-policies \ --from-file=frontend.toml \ --from-file=database.toml \ -n syva-system # 2. 部署 DaemonSet (每个节点一个代理) kubectl apply -f deploy/syva-daemonset.yaml # 3. 标记您的 pods # 添加到您的 pod spec: # annotations: # syva.dev/zone: "frontend" ``` Syva 作为具有 `CAP_BPF`、`CAP_SYS_ADMIN` 和 `CAP_PERFMON` 权限的特权 DaemonSet 运行。它挂载 `/sys/fs/bpf`、`/sys/fs/cgroup` 和 containerd socket。 ### 独立运行 ``` syva --policy-dir /etc/syva/policies ``` 默认使用 `/run/containerd/containerd.sock`。使用 `--containerd-sock` 进行覆盖。 ### 验证 ``` syva status ``` ``` syva: ACTIVE pin path: /sys/fs/bpf/syva hooks: file_open: allow=48201 deny=3 error=0 bprm_check: allow=892 deny=0 error=0 ptrace_check: allow=12 deny=1 error=0 task_kill: allow=340 deny=0 error=0 cgroup_attach: allow=28 deny=0 error=0 ``` ## 可观测性 每次拒绝都作为结构化事件发出: ``` WARN DENY hook=file_open pid=1847 caller_zone=2 target_zone=3 context=8421376 WARN DENY hook=ptrace pid=992 caller_zone=1 target_zone=2 context=0 ``` 这些事件来自 BPF 环形缓冲区(1MB,约 21K 事件),并通过 `tracing` 进行日志记录。允许操作仅在每 CPU 计数器中跟踪 —— 常见路径没有每次事件的开销。 ## Syva 如何处理内核差异 eBPF 程序读取内核结构字段(`task_struct->cgroups`,`file->f_inode`)。这些字段的字节偏移量在不同内核版本之间会发生变化。Syva 通过以下方式处理: ``` Startup │ ├─ Load BTF from /sys/kernel/btf/vmlinux ├─ Run pahole to get real offsets for this kernel ├─ Inject offsets into eBPF programs as globals ├─ Load and attach programs │ ├─ Self-test: open a file, compare two ways of reading cgroup_id │ ├─ BPF helper (known correct) │ └─ Offset chain (what we just configured) │ ├─ Match? → enforcement active └─ Mismatch? → refuse to load (no silent failure) ``` 如果未安装 `pahole`,则使用 Linux 6.1+ 的默认值。 ## 架构 ``` ┌───────────────────────────────────────────────────────────┐ │ syva │ │ │ │ Policy Loader containerd Watcher eBPF Mgr │ │ reads TOML files gRPC event stream aya + BTF │ │ │ │ │ │ │ └───────────────────────┼───────────────────┘ │ │ │ │ │ BPF map read/write │ └────────────────────────────────┼──────────────────────────┘ │ ┌────────────────────────────────▼──────────────────────────┐ │ Linux kernel │ │ │ │ ZONE_MEMBERSHIP cgroup_id → zone (who is where) │ │ ZONE_POLICY zone → caps, flags (what's allowed) │ │ INODE_ZONE_MAP inode → zone (who owns what) │ │ ZONE_ALLOWED_COMMS (zone, zone) → ok (who can talk) │ │ ENFORCEMENT_EVENTS ring buffer (what happened) │ │ │ │ 5 LSM hooks checking every open/exec/kill/ptrace/cgroup │ └───────────────────────────────────────────────────────────┘ ``` | Crate | 用途 | |-------|------------| | `syva` | 代理二进制文件。加载 eBPF,监控 containerd,管理 map。 | | `syva-ebpf` | 5 个内核程序。单独构建,目标为 `bpfel-unknown-none`。 | | `syva-ebpf-common` | 内核和用户空间之间共享的类型(`#[repr(C)]`,`no_std`)。 | | `xtask` | 构建助手:`cargo xtask build-ebpf` | ## 构建 ``` cargo build # agent + shared types (Linux only) cargo xtask build-ebpf # kernel programs (requires nightly Rust) cargo test # all tests ``` **要求:** Linux 6.1+,`CONFIG_BPF_LSM=y`,`CONFIG_DEBUG_INFO_BTF=y`,使用 `lsm=lockdown,capability,bpf` 引导。 ## 限制 **软件强制执行,而非硬件。** Syva 使用内核内存中的 BPF map。这是纵深防御,而不是虚拟机监控程序边界。内核漏洞利用可能会绕过它。 **仅限附加。** eBPF LSM 钩子可以拒绝,但绝不能覆盖现有的 MAC 策略(SELinux、AppArmor)。Syva 堆叠在其之上。 **无热重载。** 策略在启动时加载。重启代理以应用更改。 **每个节点一个实例。** Syva 将 BPF map 固定在 `/sys/fs/bpf/syva/`。第二个实例将拒绝启动。 ## 许可证 Apache-2.0
标签:API集成, DevSecOps, Docker镜像, Host Security, JSONLines, K8s 策略引擎, Kubernetes 安全, LSM, Python安全, Runtime Enforcement, Streamlit, Web截图, 上游代理, 入侵防御, 内核级防护, 可观测性, 可视化界面, 子域名突变, 安全合规, 容器安全, 微隔离, 模块化开发, 模型鲁棒性, 系统调用拦截, 网络代理, 网络安全, 行为监控, 访问控制, 进程隔离, 通知系统, 隐私保护, 零信任