§1概述和核心原则
RoamSwitch 服务器版 是一款无头、常驻自主网络安全和完整性保护套件,专为跨云提供商(AWS、GCP、Azure、裸机 VPS)和本地数据中心的 Linux 服务器基础设施而构建。
与专注于移动性和 Wi-Fi 周边适应的客户端工作站版本不同,服务器版本建立在五个毫不妥协的原则之上:入站默认丢弃、关键路径文件完整性监控 (FIM)、抢占式内核 LPE 和权限升级强化、eBPF 运行时威胁遥测 和带有断线前紧急警报的自主气隙主机隔离。
在代码层面贯彻从守护进程本体中完全排除外部通信库的 Zero Telemetry 原则(默认对外的只有接收签名数据(内核 CVE 数据、Web 规则,以及在 Ubuntu 服务器上用于操作系统软件包 CVE 比对的 Ubuntu 软件包 CVE 数据;清单每天确认一次,CVE 数据本体约每 30 天一次),且可以关闭;其余所有例外都是运维人员明确选择启用的,见 §1 核心设计原则),机密信息(机器人令牌和配置)由权限 0600 保护。
1.1 比较:客户端版与服务器版
| 尺寸 | 客户端版(桌面/移动) | 服务器版(无头/基础设施) |
|---|---|---|
| 部署 | 笔记本电脑、移动开发者工作站 | 云虚拟机、裸机节点、容器主机 |
| 网络行为 | 基于 Wi-Fi SSID/网关 MAC 的动态配置文件切换 | 静态入站默认丢弃。仅公开明确允许的端口 |
| 用户界面 | GTK3 桌面 GUI + 托盘指示器 | 完全无头。CUI 命令 + 交互式设置向导 + 终端 UI(roamswitch-server-tui) |
| 事件通知 | 桌面通知 (libnotify) | 直接推送到 Telegram Bot、LINE Messaging API 和 Webhooks (Slack/Discord) |
| 主要防御 | 公共 Wi-Fi ARP 欺骗、BadUSB、勒索软件文档加密 | 外部侦察投放、FIM 篡改检测、容器逃逸、内核 LPE (CVE-2026-53362) |
1.2 核心架构原则
- Zero Telemetry(零遥测):默认不向外部发送任何有关用户或环境的信息。守护进程本体没有 HTTP/TLS 外部通信库,只有在运维人员配置的通知和转发时,才会把系统自带的
curl(Telegram/LINE/Webhook;令牌和 URL 等机密通过标准输入的配置传递,不出现在命令行参数中)和openssl(TLS syslog 转发)作为子进程运行。默认启用的对外通信,除包管理器的更新外,只有下面这一项。(1) 在cve_kernel_map_updates_enabled(默认 true)开启期间,roamswitch-updater从 lafine.net 匿名接收签名数据。接收的是用于容器隔离审计的内核 CVE 数据、Web 应用层检测规则(Web 规则),以及在 Ubuntu 服务器上用于操作系统软件包 CVE 比对的 Ubuntu 软件包 CVE 数据。签名清单及其签名每天确认一次(2 次 GET),内核 CVE 数据本体和 Ubuntu 软件包 CVE 数据只在距上次更新约 30 天后才获取(尚无 Ubuntu 数据的服务器在下一次每日确认时获取),Web 规则只在清单版本更新时才获取。它只是不含查询字符串和 Cookie 的 HTTPS GET,能表明软件身份的请求头只有固定的User-Agent: RoamSwitch-Updater,只采用能通过 Ed25519 签名验证的数据,不会发送任何有关本主机的信息。运维人员可以用roamswitch server config set cve_kernel_map_updates_enabled false、设置向导或服务器 TUI 将其关闭;现有server.conf中明确写了 false 时,会原样保持;关闭后审计仍基于内置基线运行,但 Ubuntu 服务器上的操作系统软件包 CVE 比对会提示“本地 CVE 数据未获取”,且不返回结果。除此之外的例外,都是运维人员明确选择启用的。(2) 事件转发(forward_enabled,默认 false)只发送到运维人员配置的 syslog/JSON Lines 文件/签名 Webhook 目的地。(3) 仅当启用npm_audit_signatures_enabled(默认 false)且运维人员执行roamswitch audit-npm-signatures <DIR> --confirm时,才由npm自身访问 registry.npmjs.org。(4) 仅当启用sensor_pairing_enabled(默认 false)时,才会连接运维人员输入的 Sensor 地址(Sensor 是另一款产品,不适用 Zero Telemetry)。(5) Fleet(Business 层)只有设备端代码,未注册的设备不发送任何内容,也不解析名称;管理服务尚未部署或发布,且设计上没有远程命令通道。(6) 随附的独立工具accessscope(访问日志的安全诊断)的 LLM 联动默认关闭。仅当启用时(在命令行中即指定了--llm),诊断摘要、依据日志行和环境信息(查找日志的功能)才可能通过运维人员所选的 LLM 命令发送到外部服务。守护进程(roamswitch-server)不参与这一通信,AccessScope 也可以 root 运行 LLM 分析(此时 LLM 以 root 权限运行,建议用无特权的用户运行),且不属于 Zero Telemetry 的范围。同捆的另一个独立工具personalsoc(检查日志与防御状况)同样如此:仅在指定--llm时,诊断摘要才可能通过运维人员所选的 LLM 命令发送到外部服务(不属于 Zero Telemetry 的范围)。pathscope命令不会调用 LLM。(7) 仅当启用clamav_enabled(默认 false)时,守护进程才会运行freshclam来接收 ClamAV 签名(在签名数据库尚不存在时的首次,以及在指定了clamav_scan_dirs期间每隔clamav_freshclam_interval_secs(默认 7200 秒)一次)。 - 入站默认丢弃 (Inbound Default Drop)会通过
nftables丢弃发往允许端口以外(TCP)的新入站连接,甚至不返回 RST。端口扫描只能看到被允许的端口(环回、已建立的连接、ICMP 和 ICMPv6 会放行)。 - 故障关闭与自我修复如下。默认拒绝与隔离规则保留在内核中,因此即使守护进程停止或重启也会保持(
Restart=always)。Docker 发布端口保护每 60 秒、出站(Egress)封锁列表与端口扫描拦截每 300 秒重新检查一次,并重新安装已消失的规则。降级(DEGRADED)状态的隔离规则会在每 15 秒一次的评估中检查,若已消失则重新安装。如果默认拒绝规则本身被外部删除,则不会自动重新应用,因此请通过诊断“入站默认拒绝”(roamswitch server status)进行确认。 - 通过 0600 权限保护:配置文件
/etc/roamswitch/server.conf在创建和更新时会被自动强制设为权限 0600。令牌和 Webhook URL 等机密在传给curl时也通过标准输入的配置传递,不会放在命令行参数中(其他用户可通过/proc/<pid>/cmdline看到)。
§2威胁格局和对手模型
现代面向互联网的服务器面临着无情的自动扫描、容器突破向量和零日内核权限升级。
- 自动侦察和数据库暴露:互联网范围内的扫描仪定位无意暴露的内部数据库端口(MySQL 3306、Redis 6379、Postgres 5432).
- 关键路径篡改和后门插入:入侵者通过修改核心身份验证二进制文件(
/bin/login、/usr/bin/sudo、/etc/pam.d/*、/etc/shadow)建立持久性。 - 内核权限升级 (CVE-2026-53362 Frag Gap):非特权攻击者武器化用户命名空间 (
unshare -U -r) 以触发内核内存损坏并升级到 root. - Container Socket Breakout:配置错误的容器,已安装
/var/run/docker.sock或--privileged标志,在主机操作系统上执行 root 操作. - 进程内存窥探:攻击者跨进程附加
ptrace或读取/proc/$pid/mem以提取私钥或凭据。
§3系统架构
RoamSwitch 服务器版作为本机 Rust 二进制文件运行,旨在实现最小内存和零 CPU 开销。
┌─────────────────────────────────────────────────────────────┐
│ 用户与管理员空间 │
│ │
│ roamswitch (CLI) roamswitch-mcp (AI Agent) │
│ · server status(34 项诊断) · 安全审计工具集 │
│ · server config / setup · 标准输入输出 JSON-RPC │
│ · fim verify / update │
│ │ │ │
└────────┼───────────────────────────────┼────────────────────┘
│ (CUI 命令 & 配置生效) │ (stdio)
┌────────▼───────────────────────────────▼────────────────────┐
│ 系统基座 (systemd + root 权限) │
│ │
│ roamswitch-server-daemon │
│ ├── CriticalPathFim (SHA-256 后台巡检) │
│ ├── eBPF / Falco 联动模块 │
│ ├── Notifier (Telegram / LINE / Webhook 即时通知) │
│ └── Air-Gap Controller (展开 nftables 紧急封锁规则) │
│ │
│ 配置管理: /etc/roamswitch/server.conf (权限 0600) │
│ 完整性数据库: /var/lib/roamswitch/fim.db │
└─────────────────────────────────────────────────────────────┘
§4核心防御机制
4.1 边界安全(入站默认丢弃)
nftables 对 inet roamswitch_server 表的 input 链应用 policy drop。仅允许发往管理员指定端口(ssh_ports、allowed_ports)的新入站 TCP 连接,发往未允许端口的通信会超时(被丢弃)。环回、已建立的连接、ICMP 和 ICMPv6 予以允许。
即使触发 Air-Gap(紧急隔离),维护用 SSH 端口(ssh_ports)和已登记的维护来源 IP(whitelist_ips)的管理通道(新建及已建立的连接)仍会保留在规则中,避免无法进行远程恢复的局面(preserve_ssh_on_isolation,默认 true)。其他已建立的连接(例如攻击者已建立的 C2 或反向 shell)会被 default-drop 阻断。如果未指定管理来源(whitelist_ips 与 guard.yaml 中的允许项均为空),隔离期间维护 SSH 端口仍会接受来自任意来源的新连接,服务器启动时会输出警告。设为 preserve_ssh_on_isolation=false 时,SSH 也会一并阻断。1.10.4 及之前的服务器版存在缺陷,隔离规则会丢弃 SSH 的应答,因此隔离期间运维人员的 SSH 无法使用。该缺陷已在 1.10.5 中修复。
4.2 关键路径文件完整性监控(FIM)
监控与系统启动、认证、网络和系统管理相关的最关键文件的 SHA-256 哈希完整性。范围包括:固定的 23 处路径(/etc/shadow、/etc/passwd、/etc/sudoers、/etc/ssh/sshd_config、/bin/login、/usr/bin/sudo、/usr/sbin/sshd、/usr/lib/systemd/systemd 等);/etc/pam.d、/etc/sudoers.d、/etc/ssh/sshd_config.d、/etc/cron.d 与 /etc/cron.{hourly,daily,weekly,monthly}、/etc/systemd/system、/etc/ld.so.conf.d、/etc/profile.d、/root/.ssh 这 12 个目录下的全部文件(同时检测新文件的出现。/root/.ssh 中的 known_hosts 和 known_hosts.old 因每次 SSH 连接都会被改写,故予以排除);RoamSwitch 自身的二进制;每个可登录用户的 authorized_keys;以及不应存在的 /etc/ld.so.preload(检测其出现)。在正规的软件包更新之后,正常基线会自动更新:Debian 系通过 /etc/apt/apt.conf.d/99roamswitch-fim 钩子(apt upgrade 等),RHEL 系和 openSUSE 通过监视 /var/lib/rpm 的 systemd.path 单元(roamswitch-fim-update.path),执行 roamswitch fim update。
入侵路径的确定(谁、通过哪次 SSH 登录做了修改)
在通知文件变更的消息(关键路径 FIM 和锁文件 FIM)中,附上做出修改的进程(账户、可执行文件、命令),以及该进程所属的 SSH 登录(来源 IP 和端口、登录时间、认证方式、公钥指纹)(默认启用,fim_intrusion_context_enabled)。读到事件的当下,就会为写入进程的 /proc/<pid>(可执行文件、用户、loginuid、sessionid、cgroup、父进程链)拍下快照,因为 tee、sed 这类进程转瞬即逝。已经退出而无法取得快照的进程,由执行记录器(启用时)的记录补全。与登录的关联,优先使用 SSH 防护的会话表,没有时则从 logind 和 journald 的 Accepted 行中查找。即使停用 SSH 防护也能工作。不会向外部发送任何内容。
只信任内核报告的值:可执行文件取自 /proc/<pid>/exe(不是 argv[0]),登录取自 loginuid(登录时只设置一次,并会跨越 sudo、su 继承)和 cgroup 的 session-N.scope,再加上父进程链。comm、命令行、环境变量这类进程自己可以改的值,只用于显示,不用于判定。只是自称 sshd 的进程,不会被当作 SSH 登录。通知会区分三种答案:找到了(来源、时间、认证方式、密钥);“没有找到 SSH 登录”(查明是 systemd 服务、容器、本地登录会话或不属于任何登录会话的进程);“无法查明登录路径”(进程已不存在且执行记录器中也没有、logind 没有响应、变更由定期扫描发现因而不知道进程,等等)。无法查明的情况,绝不会写成“不是登录”。即使查询失败,通知也一定会发出。
/etc/ssh/sshd_config: modified
进程: root(登录的账户: admin) / /usr/bin/tee / 命令: tee /etc/ssh/sshd_config (pid 4121)
SSH 登录: 这是 admin 于 2026-10-17 03:12:08 从 203.0.113.25:51544 登录的会话(认证: publickey, 密钥 SHA256:AbCdEf0123456789)
/etc/sudoers: modified
无法查明登录路径(这是定期检查发现的变更,不知道是哪个进程做的)
把新文件放到同一名字之下的编辑方式也能检测到,例如 install、sed -i,以及同一目录中的 cp 加 mv。按文件设置的标记挂在 inode 上,所以这类编辑以前不会产生事件。现在,监视对象文件所在的目录也会被设置标记,同一目录中另一个名字的文件被关闭时,几毫秒后会重新查询名字。限制:在其他目录写好再用 mv 移入的文件,要等定期扫描(fim_check_interval_secs)才会发现。这时不知道写入的进程是谁,通知中也会如此说明。
实测在 Ubuntu 24.04 虚拟机上进行,运维人员的来源放在单独的网络命名空间(netns)中。无论是 sudo tee、用 nohup 脱离的进程(注销之后仍能确定来源 IP,并附上“该会话已经结束”)、sudo -i 的进程链,还是极短命的 cat,通知中都给出了 IP、用户和认证方式。本地登录则显示“没有找到 SSH 登录”。用 exec -a "sshd: …" 伪装名称的进程,没有被当作 SSH。
除通知外,记录还会保存在事件时间线(类型 fim)和 /var/lib/roamswitch/fim_intrusions.json(仅 root 可读,最新 100 条)中。MCP 的 verify_fim 会以新增字段 recentIntrusionContext 返回(仅以 root 运行时)。是否被篡改的判定不变。限制:停用执行记录器且写入进程极其短命时,无法得知可执行文件,此时会显示父进程的可执行文件和命令行。持久化监视(persistence_watch)的通知不含进程信息,因此不附带入侵路径。入侵路径只显示到做出修改的进程及其登录为止,入侵的入口本身(被利用的漏洞,或密钥是如何被盗的)无法得知。要关闭,请设置 fim_intrusion_context_enabled=false。
4.3 内核和容器强化
Frag Gap (CVE-2026-53362) 缓解:通过 sysctl 强制执行 user.max_user_namespaces = 0,中和 LPE 向量。 Yama LSM 保护 强制执行 kernel.yama.ptrace_scope = 2,防止未经授权的内存检查。容器工作负载经过 docker.sock 暴露审计。在服务器版中,禁止非特权命名空间默认开启(harden_userns_enabled)。这会使 rootless Podman 等无法运行,如果需要使用,请设为 false。
Docker 发布的端口(-p)存在绕过主机标准防火墙的已知问题,因此在 protect_docker_ports=true(默认)下,会阻断来自外部接口、且 DNAT 之前的目的端口不在允许端口(ssh_ports、allowed_ports)中的流量(回程流量、回环及容器间流量不受影响)。在 iptables/iptables-nft 后端,规则放入 DOCKER-USER 链(IPv4;启用 Docker IPv6 时也通过 ip6tables);在 Docker 29 及之后等原生 nftables 后端(没有 DOCKER-USER 的配置)下,放入专用的 inet roamswitch_docker_guard 表(forward 钩子,优先级 -10),后者用一套规则集同时覆盖 IPv4 和 IPv6。守护进程会定期复查并重新安装消失的规则。此外还有一个常驻防护,通过 docker events 实时检测 --privileged 容器的启动或 /var/run/docker.sock 的挂载,并作为容器逃逸风险立即通知(无需配置,仅通知,不自动阻断)。对于处理会交给他人的文件(如邮件中继或共享文件夹)的主机,可选择启用 File Scan Guard(内置 YARA + 可选 ClamAV)提供定期扫描和自动隔离。
容器执行防护(container_exec_guard_enabled,默认 true)。在每个运行中 Docker 容器的 overlay2 根目录上设置 fanotify FAN_OPEN_EXEC(通知类),用内置 YARA 规则检查被执行文件的内容并发出通知。由于先订阅 docker events,再用 docker ps 枚举运行中的容器,守护进程启动时已在运行的容器也在覆盖范围内。被执行文件的内容从 fanotify 传来的文件描述符读取,因此即使文件在执行后立即自我删除也能检查。仅通知,不会阻止或拒绝执行(Server Edition 根本没有许可类 fanotify)。Docker 29 去掉了 docker events 消息中已弃用的 Status,因此直到 1.11.0,订阅都会立即失败,防护一直无法使用订阅,每 5 秒重复重连一次。自 1.11.1 起,改为使用在旧版 Docker 上取值也相同的 Action。订阅持续失败时,重连间隔会从 5 秒起逐次翻倍,最长 5 分钟,同一原因的警告只记录第一次。
4.4 eBPF 运行时威胁遥测和防膨胀调整
利用现代 eBPF (CO-RE / BTF) 探测器在几毫秒内拦截权限升级和容器突破尝试。为了消除 Falco 的标准痛点 — 日志膨胀、磁盘耗尽和 CPU 开销 —RoamSwitch 标准化了优化配置文件 (/etc/falco/config.d/99-roamswitch-optimized.yaml):
- 严重性过滤 (
priority: warning):将嘈杂的操作遥测(信息/通知)抑制 95%,暴露严格可操作的安全事件。 - 内核级预删除 (
drop_failed_exit: true):直接在内核探测中丢弃失败的系统调用退出,将 CPU 上下文切换减少 50% 以上。 - 内存占用优化 (
cpus_for_each_buffer: 2):将环形缓冲区从 8MB 压缩到 2MB,将内存使用严格限制在 30–50MB。 - 直连 UNIX 套接字 IPC:不经过磁盘 I/O,通过内存中的套接字(
/run/roamswitch/events.sock)即时联动。 - 日志自动上限 (
/etc/logrotate.d/roamswitch-falco)通过maxsize 50M和 7 代压缩轮换(每天执行)抑制 Falco 日志的膨胀。
eBPF 隔离的准确度与速度(不把一切交给 Falco 的规则)
eBPF 隔离的准确度和速度,取决于传感器(Falco 或 Tetragon)的规则。实际上,Falco 0.45.0 的标准规则(29 条)里,没有检测主机上反向 shell 的规则:针对反向 shell 的规则只用于容器,其中一部分还会被 RoamSwitch 的 priority: warning 条件丢弃。RoamSwitch 不以规则本身的质量为前提,而是用三件事来弥补:测量隔离所花的时间、定期确认告警通路是否畅通,以及自己也检测反向 shell。
隔离延迟的测量:对每个 eBPF 事件,记录从 Falco 检测到守护进程读到告警的时间(detect_to_receive_ms)、从读到告警到处置完成的时间(receive_to_action_ms,在隔离调用返回之后测量;主机 Air-Gap 则测到规则集加载完毕),以及两者之和(detect_to_action_ms)。这些值会作为新增字段出现在 ebpf_incidents.json 和 MCP 的 get_ebpf_incidents 中。隔离进程时的通知会附上一行“从检测到处置 N 毫秒”。实测(Ubuntu 24.04 虚拟机,Falco 0.45.0,modern eBPF):从检测到接收 1~2 毫秒,从接收到进程隔离加 SIGKILL 41~62 毫秒(合计 42~64 毫秒),主机 Air-Gap 合计 109 毫秒(在 150 毫秒的目标之内)。数值属于该虚拟机。Tetragon 在接收时打时间戳,因此没有 detect_to_receive_ms。
心跳检测(ebpf_heartbeat_interval_secs。默认 600 秒,0 为停用,启用时取 60~86400):软件包内置了 RoamSwitch 自己的 Falco 规则 96-roamswitch-heartbeat.yaml。守护进程会定期打开一个仅 root 可用的一次性路径(/run/roamswitch/heartbeat/ 下的随机名),并测量该规则的告警经由 Falco 输出返回所需的时间。一次往返就能同时确认规则已加载、Falco 在运行、输出通路畅通。只有连续两次确认都没有返回时(第一次之后 1 分钟再确认一次),才会发出“规则未加载,或输出未送达”的警告,且 24 小时内最多通知一次。心跳的记录行会被直接丢弃,不进入隔离、通知、允许列表和事件记录。不会向外部发送任何内容。实测往返为 2~6 毫秒;移除规则后约 164 秒出现警告,恢复后约 75 秒回到正常。
状态保存在 /var/lib/roamswitch/ebpf_heartbeat.json,由 MCP get_ebpf_incidents 的 heartbeat 字段和诊断 K-04(eBPF 运行时代理,诊断项目数仍为 34)读取。即使 Falco 在运行,只要心跳没有返回,K-04 就不合格。限制:确认的只是一条规则(open 系列系统调用)的往返,无法得知其他规则是否单独损坏。在两次确认的间隔(默认 10 分钟)内损坏的部分,要到下一次确认才会发现。对 Tetragon 不执行。在 Falco 未运行或规则文件缺失的主机上,不做任何确认,也不发任何警告(停止由原有的通知负责)。Falco 每次确认都会向 syslog 写一行警告(默认设置下每天 144 行)。
反向 shell 检测(RoamSwitch 自带)
不依赖 Falco 的规则,RoamSwitch 自己每 2 秒读取一次 /proc,查找标准输入和标准输出连到“本机向外部地址建立的同一条 TCP 连接”的 shell 和解释器(sh、bash、python、perl、ruby、php、node 等)。bash -i >& /dev/tcp/…、python、perl、nc -e、socat exec: 都在检测范围内。只有连续两次都看到的才会上报,且每个进程只报一次。默认只统计指向外部公网地址的连接;指向私有地址(RFC 1918、CGNAT、链路本地)的连接,只有设置 reverse_shell_watch_include_private=true 时才计入(因为 ssh 的 ProxyCommand、容器和本地开发中正常的情况很多)。不检查回环地址。由外部接入的连接(inetd 或 socat TCP-LISTEN 所 accept 的连接)也不在范围内。实测(Ubuntu 24.04 虚拟机)中,bash 的 /dev/tcp、python、perl、nc -e、socat exec: 都在连接建立后约 2.4 秒被检测到。ssh、带 ProxyCommand 的 ssh、git over ssh 以及 nc 客户端不会被检测。
检测结果会作为名为 RoamSwitch Reverse Shell 的 Critical 级 eBPF 事件,进入与 Falco 告警相同的通路(允许列表、通知冷却、guard.yaml 按严重度的动作、事件记录)。默认会隔离该进程并将其终止(kill)。root 所有的反向 shell 无法按进程隔离,因此与其他 Critical 事件一样,会一直进行到整台主机的 Air-Gap。如果担心正常程序被误检,有两条退路:reverse_shell_watch_notify_only=true(作为 Warning 只发通知,不隔离也不 kill,默认 false),以及加入允许列表 sudo roamswitch server allowlist add "RoamSwitch Reverse Shell" <进程名>。设置键有三个:reverse_shell_watch_enabled(默认 true)、reverse_shell_watch_include_private(默认 false)、reverse_shell_watch_notify_only(默认 false)。
限制:检测按名称(进程名和可执行文件名)筛选,因此名字不是 shell 或解释器、却把标准输入输出接到套接字的自制二进制文件,无法被发现。经由回环地址的连接(例如 ssh -R 的折返)和短于 2 秒的连接不在检测范围,主机进程空间中看不到的进程同样无法检测。该检测仅限 Server Edition,Mac 和 Linux 客户端版没有。
4.5 带有预切断警报的紧急气隙隔离
在发生严重篡改时切断网络接口之前,守护进程会同步向 Telegram、LINE 和 Webhooks 发送紧急警报。警报会自动附加服务器 主机名 和 主 IP 地址。
🚨 [RoamSwitch IMMINENT AIR-GAP] CRITICAL: IMMINENT AIR-GAP HOST ISOLATION (cve_2026_53362_lpe)
🚨 CRITICAL SECURITY INCIDENT DETECTED: cve_2026_53362_lpe
Host: production-node-01 (IP: 192.168.1.10)
Process: exploit_payload (PID: 4921)
ACTION: Initiating host Air-Gap network isolation (SSH preservation active).
WARNING: Outbound internet connectivity will be cut immediately after this message.
Timestamp: 2026-09-06 18:00:00
⚠️ Network connectivity will be severed immediately.解除条件取决于触发源的置信度。低置信度触发(例如 eBPF 的 Error 级及以下)会在短计时器(guard.yaml 的 server_safeguards.safety_timer_secs,默认 300 秒)到期后完全恢复。高置信度触发(eBPF 的 Critical/Emergency/Alert、同一规则反复触发升级为主机隔离、金丝雀被篡改等)不会悄悄恢复。短计时器到期后会复核原因(被冻结的 PID 是否仍存活、规则是否再次触发等),只有确认原因已消失时才会附带通知地解除。如果无法确认且没有 ACK,到达上限(isolation_hard_cap_secs,默认 3600 秒,0 = 无上限)后转入 DEGRADED。DEGRADED 保留维护 SSH 端口和维护来源 IP 的管理通道(新建及已建立),新的外向连接仅允许发往维护来源 IP,并且绝不会因时间流逝而自动放开。解除方式只有:确认原因已消失的复核、roamswitch server ack(DEGRADED 中的 ACK 表示解除)或 roamswitch emergency-restore。处于 FULL 时已 ACK 的隔离视为由运维人员接管,既不会由计时器恢复,也不会在确认原因已消失时自动解除,更不会转入 DEGRADED,而是一直保持到被 emergency-restore 解除。每次转换都会记录到事件时间线并发出通知。
进程执行记录器 & 事件转发
通过 netlink 进程连接器记录 exec 的仅限本地的记录器默认启用(与客户端版共用)。它把可执行文件路径、命令行参数(限制长度,控制字符转义)、父进程谱系、uid、容器 ID、可执行文件的 SHA-256 等,以带哈希链的 JSON Lines(0600)保存在 /var/lib/roamswitch/exec_log(默认 200 MB/14 天)。哈希链可以检测篡改,但无法防止篡改(取得 root 的攻击者可以重写整条链)。仅通知的关联规则 EXR-001~010(由 Web 服务启动 shell、从 /tmp 或 memfd 执行、攻击工具链等)单独不会隔离。仅当运维人员明确启用 exec_rules_isolation_optin(默认 false)时,高严重度规则才会被当作主机隔离的高置信度触发。
事件转发(forward_enabled,默认 false)只发送到运维人员配置的目的地:syslog(RFC 5424 或 CEF,UDP/TCP/TLS)、JSON Lines 文件,或带 HMAC-SHA256 签名的 Webhook。TLS syslog 通过系统的 openssl s_client,Webhook 通过系统的 curl(机密通过标准输入的配置传递,不出现在命令行参数中),守护进程没有 TLS/HTTP crate。发送的仅限于:出现在事件时间线和通知历史中的检测事件(eBPF、隔离状态转换、FIM、金丝雀等)以及执行规则通知的主机名、事件 ID、时间、来源、严重度、规则、处置、PID、进程名和摘要字段,外加执行记录的分段封印哈希。不包含执行记录的原始日志。
DNS 隧道/数据外泄检测
启用 dns_tunnel_detect_enabled(默认 true)后,会通过专用的 NFQUEUE 监控本机自身发出的 DNS 查询(仅限 UDP/TCP 53 端口)。当标签长度超过 40 字节且香农熵超过 3.8,或查询记录的 qtype 为 TXT/NULL 时(仅 ClamAV 签名更新所使用的发往 clamav.net 的 TXT 查询不计入),该查询会被计为 iodine、dnscat2 等 DNS 隧道工具所特有的“可疑形状”,仅当最近 60 秒内此类查询达到 10 次或以上时才发出通知(同一域名的重复通知间隔 300 秒;突发持续期间依次延长为 600 秒、1200 秒,最长间隔 1800 秒)。检查对象仅限 DNS(UDP/TCP 53 端口),不检查其他流量。仅通知,不会自动阻断。底层 nft 规则带有 bypass 标志,因此即使监控线程停止或无响应,DNS 查询也会照常放行。域名解析不会因该功能出现故障而中断。此设置的更改需要执行 roamswitch server restart 后才会生效(不支持热加载)。
已知限制:以连字符分隔的长主机名(例如 prod-us-east-1-web-server-node-0042),以及邮件服务器在短时间内验证多个不同发件域名的 SPF/DKIM TXT 记录这类在服务器场景中尤为常见的正常处理,此前经实测确认会因不区分目标域名的单一全局计数器统计 TXT/NULL 突发查询而引发误报。这两点均已于 2026-09-23 修复。已通过可配置的 entropy 允许清单,以及按 apex domain(对 QNAME 末尾两个标签的简单近似,并非严格的 Public Suffix List 实现)为单位分离突发计数器的键的方式解决。允许清单默认内置 16 个主流 CDN/云服务和 PaaS 后缀(cloudfront.net、akamaiedge.net、akamaized.net、edgekey.net、edgesuite.net、fastly.net、azureedge.net、googleusercontent.com、amazonaws.com、r2.dev、workers.dev、vercel.app、netlify.app、supabase.co、firebaseapp.com、web.app),运维人员可通过 ServerConfig.dns_tunnel_entropy_allowlist(server.conf 中以逗号分隔的键,也可在 TUI 中编辑)添加自有内部域名。允许清单只会豁免基于标签长度+熵值的可疑判定,TXT/NULL 的 qtype 判定无论是否有允许清单都会适用,从而防止攻击者在允许清单域名下伪装隧道(唯一的例外是 ClamAV 签名更新所使用的发往 clamav.net 的 TXT 查询。第三方无法在该域名下创建子域名,因此不计入)。突发计数器现已按 apex domain 分离键,消除了因单一全局计数器不区分目标域名而产生的误报(60 秒滑动窗口和 10 次阈值的判定逻辑本身未因此次修复而改变。重复通知的间隔此后已改为在突发持续期间从 300 秒逐步延长至最长 1800 秒)。iodine、dnscat2 等真实恶意流量只会在单一攻击者控制的域名下轮换标签和 qtype,因此按 apex domain 为单位收窄并不会降低检测能力。
SSH 暴力破解的检测与封禁(ssh_guard)
RoamSwitch 自带的暴力破解防御,由 ssh_guard_enabled(默认 true)启用。它通过 journalctl -f 从 journald 实时读取 sshd 日志(仅在没有 journald 的环境才跟踪 auth.log/secure),按来源地址统计失败次数,对达到阈值(默认 600 秒内 6 次)的来源,在 inet roamswitch_sshguard 表中只丢弃其发往 SSH 端口的新连接(ct state new)。封禁的同时,也会切断该来源已经建立到 SSH 的连接(删除其 conntrack 条目,并增加一条丢弃应答方向数据包的规则)。只有既没有已认证会话、也没有公钥登录成功记录、也不在豁免之列的来源才会被封禁,且在切断前会再确认一次,因此已认证的会话和豁免的来源不会被切断。封禁从 1 小时开始,每次再犯延长为 4 倍,上限 7 天,到期由 nftables 的超时处理。不会启动 Air-Gap(隔离),也不会向外部通信(只在本机运行 journalctl、nft、loginctl 等命令)。当 fail2ban(sshd 的 jail 已启用)或 CrowdSec(firewall bouncer 正在运行)已经在保护 sshd 时,它会让出封禁,只做检测和通知。
日志行中含有攻击者可以决定的字符串(用户名)。因此来源地址只从行尾的 from <IP> port <N> 读取,即使在用户名中写入伪造的来源地址,第三方也不会被封禁。对于 journald,只读取发送方凭据(_UID、_COMM)属于 root 的 sshd 的行,忽略用 logger -t sshd 写入的行。跟踪文件(auth.log)没有能保证发送方的字段,能使用 syslog 的本地用户可以伪造行,所以不作为默认方式。为避免把运维人员锁在门外,网关、DNS、本机自身、回环地址、whitelist_ips、ssh_guard_allow、已认证 SSH 会话的来源地址、30 天内用公开密钥认证成功过的来源地址,以及已配对的 Sensor 都不会被封禁。私有地址不豁免。公开密钥的失败(Failed publickey)不计入。
实测(Ubuntu 24.04 虚拟机,真实的 journald 和 systemd):从第 6 次失败到封禁约 0.2 秒(0.17 至 0.24 秒)。运维人员的来源地址没有被封禁,在用户名中写入伪造来源地址的尝试也没有让第三方被封禁。真实的 hydra(4 并发)在 6 次失败后被拦停。同时确认了:服务器重启后封禁会连同剩余时间一起恢复,封禁可以解除,以及 fail2ban 在保护 sshd 时只做检测和通知。这些数值来自这一台虚拟机环境。加入切断连接后的实测中,封禁之后仍然通过的失败次数,加入前为 16~24 次,加入后为 0~4 次。仅删除 conntrack 条目并不能切断连接,加上丢弃应答方向数据包的规则之后才生效。
Web 应用层检测规则(Web Shell 与注入代码)
Server Edition 此前没有在写入时触发检查的路径,只有对 clamav_scan_dirs 的定期扫描。现在它用仅通知的 fanotify(FAN_CLASS_NOTIF,绝不会使其他进程停滞)监视从 Web 服务器(Apache、nginx、Caddy)配置中找到的文档根目录,在 PHP、JavaScript、JSP、ASP 等文件写入并关闭后立即用 Web 应用层检测规则(Web Shell、被注入的混淆代码、伪装成图片的 PHP、.htaccess 滥用)检查。为弥补可能丢失的事件和启动前已存在的文件,每 6 小时还会定期扫描。规则分三层(内置、每日更新程序验证签名后安装、/etc/roamswitch/rules.d/*.json 中的自定义规则);18 条规则不含从真实样本提取的签名,只描述公开记载的手法。
在 Server Edition 中,自动隔离默认仅限 critical。high 只发送通知并触发检测后钩子,不会触碰运行中站点的文件。通知(Telegram、LINE、Webhook)为 10 种语言,每次检测都会写明规则名称以及文件是已隔离还是保留。规则的获取并入与每日更新程序相同的一次获取,不会新增通信目标。关闭 cve_kernel_map_updates_enabled 后完全不进行网络通信,使用内置规则运行。在真实的 fanotify 监视下,写入的 Web Shell 在 2 秒内被隔离,无害文件被保留。
检测之后,若在 /etc/roamswitch/hooks/post-detection 放置可执行文件,检测内容(JSON)会通过标准输入传给它(仅执行 root 所有、不可写且父目录也安全的文件;不经过 shell,环境变量极少,30 秒后终止)。可选的账户处置(默认关闭)会停用被隔离文件所有者的登录 shell。它不会处理 root、UID 低于 account_min_uid(默认 1000)的账户、已登录的用户和守护进程自身的用户,并会记录原 shell,可用 roamswitch web-rules unlock-account 恢复。误报实测:WordPress 核心 2,409 个文件、phpMyAdmin 3,356 个文件、npm 依赖 14,743 个文件均为 0 条。局限:只读取开头 256 KB(写入/执行时)和 4 MB(扫描),无法跟踪变量名拼接('ev'.'al')这类拆分;因没有真实样本,未附带针对已知恶意软件的个别签名。
蜜令牌(凭据诱饵)
守护进程启动时,会在攻击者及自动化窃取凭据工具实际会扫描的已知真实路径(/root/.aws/credentials、/root/.ssh/id_rsa、/root/.docker/config.json、/root/.pypirc、/root/.env.backup。因守护进程以 root 身份运行,故位于 /root 下)幂等地部署包含 ROAMSWITCH_HONEYTOKEN 标记的伪造凭据文件(默认开启;可用 honeytokens_enabled 关闭,部署位置可用 honeytokens_home 更改)。若该路径下已存在真实文件,则绝不覆盖(部署前始终先检查文件是否存在)。对蜜令牌的真实访问还可能触发带上限的级联部署,追加相关诱饵文件(如额外的 AWS 配置文件、SSH config、kubeconfig 等)。截至 1.10.4 的发布软件包未包含此功能所需的 roamswitch-honeytokens,因此通过软件包安装的环境中该功能并未运行。自 1.10.5 起已随附,服务器版中 systemd 沙箱阻止写入 /root 的问题也已在 1.10.5 中修复。自 1.11.1 起,RoamSwitch 自身执行的 docker 命令,会把一个空的专用目录指定为 DOCKER_CONFIG 后启动,因此不会触发诱饵 /root/.docker/config.json。其他程序读取时,仍会像以前一样检测。
后台监视进程(roamswitch-honeytokens)使用通知类的 fanotify(不会阻止任何操作)监视每个诱饵文件的读取与打开操作。由于事件会携带访问来源的 PID,因此可以当场识别访问来源的可执行文件(在无法使用 fanotify 的环境中,会切换为 inotify 加 fuser)。由于任何合法进程本来都没有理由读取这些文件,因此除 RoamSwitch 自身的二进制,以及针对 SSH 诱饵文件的 OpenSSH 客户端(ssh、scp、sftp、ssh-add、ssh-agent 等,不含 ssh-keygen)之外的访问,都被视为入侵者在得手后进行侦察(MITRE ATT&CK T1552, Unsecured Credentials)的强烈信号,并发出通知。无法识别访问来源可执行文件的访问,同样不会被放行,而是视为检测事件。该功能仅用于通知,不会对访问来源进程执行冻结或阻断等额外处置。
已知限制:/root/.ssh/id_rsa 诱饵文件的正文包含可读文本,其 base64 主体仍不是有效的 OpenSSH 私钥格式。不过大多数密钥/凭据扫描工具仅通过匹配 -----BEGIN ... PRIVATE KEY----- 头部的正则表达式来标记文件,因此我们判断这对实际检测率的影响有限。另外,在无法使用 fanotify 而切换为 inotify 的环境中,识别访问来源进程(调用 fuser)时,对于读取文件后立即退出的极短命进程,可能无法获取其 PID 或进程名;此时仅会记为“未知”,不影响对访问本身的检测(事件触发和通知不受影响)。
证据保全包(自动取证采集)
证据采集会在以下情形启动。eBPF 运行时威胁传感器的事件促成了对相关进程的单进程网络隔离并成功;该隔离失败并升级为主机级 Air-Gap;或执行了阻断整个主机的 Air-Gap。勒索软件金丝雀机制检测到篡改、并冻结进程和隔离主机的路径也会启动。在阻断整个主机的路径上,采集会在阻断之前启动,且不会延迟阻断。1.10.4 及之前仅在单进程隔离成功时才会启动,1.10.5 起扩大了范围。如果无法确定目标进程的 PID,则不采集 /proc 快照。roamswitch-incident-capture 启动后,会将证据包保存到 <状态目录>/incident-evidence/<时间戳>-<短 ID>/。采集内容包括:在默认路由接口上通过 tshark 进行的、限定 30 秒的抓包(若未安装 tshark 则仅记录为跳过),以及目标进程的 /proc/<pid> 快照(status、cmdline、maps,以及将疑似敏感信息的值替换为 [REDACTED] 的 environ、已打开文件描述符的链接目标列表(不采集其内容本身),以及祖先进程链)。单个证据项采集失败不会使整个证据包中止,只会记录该情况并继续采集其余内容。截至 1.10.4 的发布软件包未包含此功能所需的 roamswitch-incident-capture,因此通过软件包安装的环境中该功能并未运行。自 1.10.5 起已随附。
采集结果汇总在 manifest.json 中,其中记录了证据包内所有证据项的 SHA-256 哈希(用于事后检测篡改)。证据包不会自动删除。只有在运维人员明确执行 sudo roamswitch-incident-capture prune [--keep-days N](默认保留期 30 天)时,旧的证据包才会被删除。系统还提供 sudo roamswitch-incident-capture snapshot [--pid PID] [--reason TEXT] [--json] 命令,可随时手动采集。
采集到的证据在采集时并不会被校验。若需事后确认是否遭到篡改,请运行 sudo roamswitch-incident-capture verify <证据包目录> [--json]。该命令会对证据包内当前的所有文件重新计算哈希,并与 manifest.json 中记录的值进行比对,将差异分别报告为「已修改」「缺失」或「未记录」(存在但清单中未记录的文件)。
已知限制:此验证会把 manifest.json 的 SHA-256 与保存在证据包之外的记录进行比对,记录在两处:证据包之外的记录链(evidence_anchor.log,每一行都连接到上一行,并设置仅追加属性)和系统日志(标签 roamswitch-evidence)。采集后改写 manifest.json 会使 verify 失败;修改或删除记录中的某一行,也会因链断裂而失败。但如果拥有 root 权限的攻击者能同时改写这条链和系统日志的转发目的地,则无法防范。建议把系统日志转发到另一台机器。尚未实现签名。没有该记录的证据包(由旧版本采集)会显示为“无记录”,表示没有外部佐证。
§534 检查安全诊断套件
服务器版执行 34 项专门的安全健康审核:
| # | 类别 | 检查标题 | 标准与逻辑 |
|---|---|---|---|
| 1 | 授权和访问 | SSH Root 登录和密码验证 | 验证 PermitRootLogin no。若密码认证同样被禁用,则报告为仅限公钥认证;即使密码认证已启用,只要禁止 root 登录,也判定为合格 |
| 2 | 授权和访问 | 空密码帐户 | 确保 /etc/shadow 中没有用户具有空白密码哈希 |
| 3 | 授权和访问 | Sudoers 特权卫生 | 审计 /etc/sudoers 和 /etc/sudoers.d 中是否存在无需密码即授予全部权限的 NOPASSWD: ALL |
| 4 | 网络 | 入站默认拒绝 | 审核 nftables 的默认入站丢弃策略 |
| 5 | 核心与完整性 | 非特权用户命名空间(碎片间隙) | 通过 user.max_user_namespaces=0(或 kernel.unprivileged_userns_clone=0)阻断 LPE 攻击路径 |
| 6 | 核心与完整性 | eBPF BTF 支持 | 通过 /sys/kernel/btf/vmlinux 验证内核 BTF 类型信息 |
| 7 | 核心与完整性 | eBPF LSM 活动 | 检查内核 lsm= 启动参数中的 bpf |
| 8 | 网络 | XDP 启动时闸门 | 用 XDP 默认拒绝闸门覆盖 Link Guard 的 nftables 表加载之前的启动窗口期(实验性功能,默认不加载,需主动启用)。检查是否存在 pinned link;未加载不视为不合格 |
| 9 | 核心与完整性 | eBPF 运行时安全代理 | 确认 Falco / Tetragon 等代理是否在运行。Falco 运行时,还会确认 RoamSwitch 自己规则的告警是否返回(心跳检测),未返回则不合格(若缺少 RoamSwitch 的 Falco 文件,会一并给出文件名) |
| 10 | 核心与完整性 | 统一事件时间线 | 检查将 Link Guard、Canary、eBPF Guard 和 Resource Guard 的事件按时间顺序整合的时间线是否处于可记录状态(已有事件记录,或存储位置可写),并显示最近一次遏制耗时 |
| 11 | 集装箱 | Docker Socket 安装审核 | 检测 /var/run/docker.sock 的容器安装 |
| 12 | 集装箱 | Docker 特权容器 | 高风险 --privileged 容器工作负载的审核 |
| 13 | 集装箱 | 容器运行时隔离级别 (runc/gVisor/Kata) | 检查默认容器运行时。基于 runc 的运行时(共享主机内核)判为不合格;检测到 gVisor(runsc)或 Kata Containers(每容器虚拟机隔离)则合格。未检测到 Docker/containerd 时视为不适用 |
| 14 | 集装箱 | Docker 防火墙绕过防护 (DOCKER-USER) | 验证 RoamSwitch 自身的规则是否确实存在于始终最先执行的 DOCKER-USER 链(或其专用的 Docker 防护 nftables 表)中,防止已发布的容器端口通过 Docker 自身规则绕过防火墙的默认拒绝。仅在 server.conf 中启用 protect_docker_ports 时才评估 |
| 15 | 核心与完整性 | ClamAV 实际检测测试(EICAR) | 守护进程在启动 2 分钟后及此后每 6 小时,从自身运行环境(例如 systemd 沙箱内)扫描一个无害的 EICAR 测试文件,确认 ClamAV 确实能够检测。仅在 server.conf 中启用 clamav_enabled 时才评估 |
| 16 | 核心与完整性 | fanotify 事件丢失(队列溢出) | 检查是否因 fanotify 事件队列溢出而被内核丢弃了 FIM 和容器执行监控事件。溢出期间的文件写入和执行会逃过实时监控;如果频繁发生,请增大 fs.fanotify.max_queued_events |
| 17 | 核心与完整性 | 运行中内核的已知 CVE 暴露 | 将运行中的内核版本与本地已知 CVE 映射(内置基线,或经过签名验证的更新)进行比对。检查本身不进行任何对外通信,映射更新按 cve_kernel_map_updates_enabled(默认 true,可以关闭)获取。无法确定时按“无法判定”处理,而不是判为不合格 |
| 18 | 诚信 | 关键路径文件完整性 (FIM) | 对 23 个固定的关键文件以及 12 个目录下的全部文件等进行 SHA-256 哈希完整性验证 |
| 19 | 核心与完整性 | Yama过程保护 | 通过确认 kernel.yama.ptrace_scope 为 2 或 3,防止内存窃听 |
| 20 | 网络 | IP 转发已禁用 | 验证 net.ipv4.ip_forward = 0 |
| 21 | 网络 | TCP SYN Cookie 保护 | 验证 net.ipv4.tcp_syncookies = 1 |
| 22 | 网络 | 反向路径过滤(rp_filter) | 通过确认 net.ipv4.conf.all.rp_filter 为 1(严格)或 2(宽松,适用于多宿主配置),丢弃伪造数据包 |
| 23 | 核心与完整性 | 核心转储限制 | 通过 fs.suid_dumpable = 0 防止进程内存泄漏 |
| 24 | 运营 | 自动安全更新 | 确认已通过 unattended-upgrades(Debian / Ubuntu)或 dnf-automatic(RHEL / Fedora)等配置了补丁自动应用 |
| 25 | 运营 | NTP时间同步 | 通过 chrony 或 systemd-timesyncd 检查同步 |
| 26 | 运营 | SSH 暴力保护 | RoamSwitch 自带的 SSH 防护(ssh_guard)正在运行并执行封禁时,或通过 fail2ban / crowdsec 验证主动监控 |
| 27 | 核心与完整性 | 禁用非特权 eBPF | 通过确认 kernel.unprivileged_bpf_disabled 为 1 或 2,防止 Spectre/LPE |
| 28 | 网络 | 忽略 ICMP 重定向 | 通过 accept_redirects = 0 防止路由中毒 |
| 29 | 网络 | 忽略 ICMP 回显广播 | 缓解 Smurf 放大器 DoS 攻击 |
| 30 | 核心与完整性 | 共享内存强化 | 确认 /dev/shm 至少带有 nodev、nosuid、noexec 中的一个挂载选项 |
| 31 | 授权和访问 | 不存在遗留的不安全协议 | 验证未安装明文监听服务(telnetd、rshd、tftpd 等)和明文客户端(telnet、rsh、tftp 等)(服务与客户端分开显示)。 |
| 32 | 核心与完整性 | SUID 进程核心转储受限 | 防止 SUID 二进制崩溃时的凭证泄漏 |
| 33 | 核心与完整性 | 辅助程序(诱饵与取证)已安装 | 守护进程按名称启动凭据诱饵(蜜罐令牌)程序和取证包程序,缺失时会静默地什么都不做。此项检查这两个程序是否已安装。 |
| 34 | 网络 | Web 公开目录中的敏感文件暴露 | 从 Apache / nginx / Caddy 配置中找出文档根目录,并通过向服务器自身发送请求,检查其中的 .git、.env、.DS_Store、phpinfo、SQL 转储、备份和私钥能否通过 Web 服务器实际读取。只有响应与磁盘上文件的开头一致时才判定为暴露,因此对所有路径都返回 200 的站点,或已用 403 拒绝的文件,都不会误报。不会报告文件内容,只显示路径。没有 Web 服务器的环境为不适用;无法确认时按“无法判定”处理,不扣分。 |
在适用的情况下,为 34 个检查项中的每一项都标注 NIST CSF 2.0 的功能/类别标识符(例如 PR.DS-01、DE.CM-09),并且仅对能够做出可靠对应的项目,一并标注 CIS Controls v8 的控制编号(例如 10.1、4.1)。对应关系不明确的项目不会被强行标注,而是保持未映射状态。CLI(roamswitch server status)与 MCP(get_security_report)的输出中均包含此对应表,方便需要满足合规要求的运维人员与审计人员,将各检查项机械式地对应到现有框架。
§6自查(Docker 环境下的可复现验证)
针对从官方 APT 仓库安装的 roamswitch-server 软件包,以 Docker 中的独立容器作为外部攻击者,实施模拟内部与外部攻击的自查。2026-09-07 在 v1.3.2 上的实测为全部 18 项 PASS。2026-09-25 使用 1.10 系列源码构建的软件包(arm64、Docker Desktop)再次实施,19 项检查 PASS。此次重新实施发现了 6 个产品缺陷并已全部修复(已包含在 1.10.5 中)。具体包括:软件包缺少辅助可执行文件,FIM 将蜜罐诱饵报告为新文件,确认(ack)被忽略,隔离期间管理用 SSH 的应答被丢弃导致管理员被锁在外面,即使设置 preserve_ssh_on_isolation=false 端口 22 仍保持开放,以及 systemd 单元的沙箱阻止了诱饵文件的写入。您可以在自己的机器上复现此验证。步骤和脚本请参阅下文“自行复现的方法”。
| # | 场景 | 攻击向量 | 确认结果 |
|---|---|---|---|
| PENT-S1 | Nmap SYN 隐形扫描 | 跨端口 22、80、443、3306、8080 进行探测 | nftables Default Drop 使未允许的 3306 (MySQL) 和 8080 等端口完全不响应并被丢弃(Filtered)。100% 阻断 |
| PENT-S2 | 不允许的端口探测 | 卷曲直接连接到端口 3306 和 8080 | 通过入站默认拒绝,仅允许的 80 端口正常响应,其余端口超时被阻断 |
| PENT-S3 | Egress / C2 阻止清单 | 尝试向恶意 IP 情报源中登记的地址发起出站连接 | 通过 egress_ip_blocklist_enabled(默认 true、nftables)仅阻断已登记的地址。与未登记主机的通信不受影响(并非一律阻断) |
| PENT-S4 | FIM 篡改攻击 | 将后门有效负载注入 /bin/login |
SHA-256 Critical Path FIM 即时检测到篡改。正规还原后,基线能够正确重新同步 |
| PENT-S5 | guard.yaml 按严重程度分级的遏制 |
注入引用真实进程的 Falco Critical 事件 | 通过 on_critical: isolate_process + kill_process,对相关进程进行网络隔离后再执行 SIGKILL |
| PENT-S6 | 受保护进程安全机制 | 注入引用真实 sshd PID 的同类事件 |
受保护进程列表(sshd/systemd/容器运行时等)使 sshd 持续运行。仅记录一条拒绝日志作为误报 |
| PENT-S7 | 安全定时器 | 让 Air-Gap 事件保持未确认状态(无操作员 ACK) | 通过 safety_timer_secs,无响应时自动恢复。一旦已确认(ACK),则隔离将持续至执行 emergency-restore |
| PENT-S8 | 内核 LPE 碎片间隙 | 以普通用户权限执行 unshare -U -r(已在真实 VM 上确认) |
没有本产品时允许的 unshare -U -r,在启动后被拒绝。user.max_user_namespaces 和 kernel.unprivileged_userns_clone 为 0,Yama 的 ptrace_scope 为 2,非特权 eBPF 已禁用(2026-09-25,Ubuntu 24.04 的 VM,已在 v1.10.4 的公开版和修复版两者上确认) |
注:PENT-S7 的实测值为 v1.3.2 时的数据。当前版本中,高置信度触发器造成的隔离在确认原因消失后解除,而运维人员已确认(ack)的隔离会保持到运维人员解除为止(确认被忽略的缺陷已在 2026-09-25 的重新运行中发现并修复)。低置信度触发器仍由同一定时器恢复。详见 §4。
验证类型 — PENT-S8 确认内核功能(禁止用户命名空间)已生效。PENT-S1、S2 确认 RoamSwitch 所设置的 nftables 规则,实际丢弃数据包的是内核。检验 RoamSwitch 自身逻辑的项目是 PENT-S3(订阅源应用)、S4(FIM)、S5、S6(遏制与受保护进程)和 S7(安全定时器)。
SSH 锁定防护机制
preserve_ssh_on_isolation=true(默认)的设计是在 Air-Gap(on_emergency)期间保留管理用 SSH。但 2026-09-25 的重新运行表明,1.10.4 及之前的隔离规则会丢弃 SSH 的应答,导致隔离期间运维人员被锁在外面;此前的检查只是在隔离解除之后才成功。该问题已在 1.10.5 中修复,并在隔离原因仍然存在的条件下,验证了隔离期间 SSH 可以通过。
验证范围的局限性(如实披露)
内核加固(禁用非特权 User Namespace、提升 Yama ptrace_scope)无法在容器内进行端到端验证。由于这些 sysctl 并非按容器命名空间隔离,特权容器(--privileged)中的写入操作影响的并非验证用的沙箱,而是正在运行本次验证的真实机器本身。因此,本次验证仅确认该 sysctl 写入操作在(面对只读文件系统时)能够优雅降级而不崩溃,至于其值是否真正生效,则需要在本产品实际运行的真实机器或虚拟机上进行确认。该确认已于 2026-09-25 在 Ubuntu 24.04 的 VM 上进行过一次(请参阅 PENT-S8 一行)。
自行复现
上述所有验证结果,均可通过官方支持仓库(roamswitch-support)中公开的 Docker 测试套件,仅使用官方发行包在您自己的机器上 100% 复现(无需访问源代码,也无需信任我们)。
# 获取并复现验证套件(从官方 APT 仓库自动安装 roamswitch-server)
git clone https://github.com/lafine1211/roamswitch-support.git
cd roamswitch-support/test/docker/server
./run.sh
在 Docker 的默认桥接网络上构建 Target、Attacker、C2 诱饵共 3 个一次性容器,自动验证 18 个项目并输出报告。v1.3.2 的完整执行结果已公开于 RESULTS-PENTEST-SERVER-2026-09-07.ja.md(出于透明考虑,也记录了搭建过程中发现并修复的测试工具自身问题)。2026-09-25 重新实施的记录见 RESULTS-PENTEST-2026-09-25.ja.md。
§7运营、部署和生命周期
7.1 无头 CUI 工具和设置向导
通过 CLI 完成远程管理:
roamswitch server status:输出 34 个检查项的诊断报告(支持 10 种语言,遵循执行用户的设置或操作系统的区域设置)。roamswitch server setup:端口和通知的交互式终端向导。roamswitch server config show / set <key> <val>:Ansible/Terraform 的可编写脚本的自动化。roamswitch server test-notify [all|telegram|line|webhook]:端到端通知探测。roamswitch server config set language <ja|en|zh-Hans|zh-Hant|ko|de|fr|es|it|pt|system>:守护进程的定期诊断报告(每小时一次)和推送通知(Telegram/LINE/Webhook,包括测试发送)的语言(每台主机一种语言)。system跟随操作系统区域设置,无法识别的区域设置会回退为ja。roamswitch server status的显示语言不遵循此设置,而是遵循执行用户的设置或操作系统的区域设置。roamswitch server bans [--json]/sudo roamswitch server unban <IP|IPv6 的 /64|--all>:查看 SSH 暴力破解防护的状态,以及封禁列表与解除。MCP 的get_ssh_guard_incidents是只读的,不能解除封禁。
Server TUI(roamswitch server tui)
这是一个可通过 SSH、在 80x24 终端中仅用键盘操作 Server Edition 全部功能的终端界面(roamswitch server tui 或 roamswitch-server-tui)。共有 11 个屏幕:仪表盘、防御、事件、通知与转发、Sensor、设置、服务、工具、日志审计、信息与帮助、Fleet。设置编辑器由 server.conf 和 guard.yaml 的键定义(模式)生成,会校验类型、范围和枚举值,显示是否需要重启,保存前展示差异,并以带时间戳的备份和原子写入保存(令牌等机密值以掩码显示、输入时不回显)。恢复隔离、ACK、更新 FIM 基线、停止服务等影响较大的操作会显示写明影响的确认界面,影响特别大的还要求输入 yes(键入式确认)。非 root 时为只读,并提示所需的 sudo roamswitch ... 命令。没有配置重新加载(reload),需要重启服务才能生效。界面支持 10 种语言。
7.2 包装
通过面向 Debian / Ubuntu 的 .deb 软件包,以及面向 RHEL / Fedora 和 openSUSE 的 .rpm 软件包提供。
§8结论
RoamSwitch Server Edition 以单一集成软件包为云端和企业 Linux 基础设施提供“零遥测(默认的对外通信只有可以关闭的、仅接收签名数据的一项)”“基于入站默认拒绝的坚固边界防护”“关键路径篡改的即时检测”“内核权限提升的预防性阻断”。
针对攻击者瞄准的主要阶段(初期侦察、入侵、权限提升、持久化),将多层检测与自主封锁(Air-Gap)相结合的 Linux 服务器安全套件。