RoamSwitch服务器版操作手册
云 VPS(AWS、GCP、Azure、Linode、DigitalOcean)、裸机数据中心和容器主机的官方安装、部署和操作参考。
1. 概述及要求
RoamSwitch 服务器版是一个 无头(零 GUI 依赖)自主防御和完整性监控套件 ,专为暴露于公共互联网的 Linux 服务器而设计。
与基于 Wi-Fi 状态动态切换配置文件的客户端版本不同,服务器版本强制执行 入站默认丢弃、关键路径文件完整性监控 (FIM)、预防性内核 LPE 缓解(Frag Gap)、eBPF 运行时威胁检测、SSH 锁定预防和自动多通道紧急警报(Telegram / LINE / Webhook)。
cve_kernel_map_updates_enabled,默认开启)。获取的是内核 CVE 数据、Web 规则,以及在 Ubuntu 服务器上用于操作系统软件包 CVE 比对的 Ubuntu 软件包 CVE 数据。roamswitch-updater 从 lafine.net 以 HTTPS GET 接收带 Ed25519 签名的数据(签名清单每天确认一次,CVE 数据本体约每 30 天一次,Web 规则只在清单版本更新时获取),不含查询字符串和 Cookie,能表明软件身份的请求头只有固定的 User-Agent: RoamSwitch-Updater,也不会发送任何有关本主机的信息。可用 roamswitch server config set cve_kernel_map_updates_enabled false(或设置向导、服务器 TUI)关闭,关闭后审计仍基于内置基线运行,但 Ubuntu 服务器上的操作系统软件包 CVE 比对会显示“本地 CVE 数据未获取”。除此之外的对外通信,仅限运维人员明确启用的项目:通知(Telegram / LINE / Webhook)、npm 签名验证(npm_audit_signatures_enabled。仅在执行命令时),以及事件转发(forward_enabled,默认关闭。§16)。事件转发只会发送到运维人员指定的自己组织的目的地(SIEM、syslog、Webhook),TLS 的 syslog 通过系统的 openssl 发送,结构化 Webhook 通过 curl 子进程发送。此外,Sensor 配对(sensor_pairing_enabled,默认关闭)和随附的独立工具 accessscope 的 LLM 联动(默认关闭;在命令行中仅当指定 --llm 时)也只有在运维人员启用时才会对外连接。例外列表见 Server Edition 安全设计文档的 Zero Telemetry 一节。系统要求
- 支持的发行版:Ubuntu 22.04 / 24.04 LTS、Debian 12+、AlmaLinux / Rocky Linux / RHEL 9+、Fedora 39+、openSUSE Leap 15.5+、Raspberry Pi OS(64 位)
- 架构:
x86_64(amd64) 或aarch64(arm64) - Kernel:Linux 5.10+(推荐
nftables、cgroups v2、eBPF BTF) - Footprint:20–30 MB 驻留内存,空闲/正常工作负载期间 <0.1% CPU
2. 安装步骤
使用官方签名的存储库可确保通过标准系统包管理器自动解决依赖关系并进行无缝更新。
2.1 APT(Ubuntu / Debian / Raspberry Pi 操作系统)
# 1. 注册仓库签名密钥
curl -fsSL https://lafine.net/apt/roamswitch-archive-keyring.asc \
| sudo gpg --dearmor -o /usr/share/keyrings/roamswitch-archive-keyring.gpg
# 2. 添加仓库
echo "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/roamswitch-archive-keyring.gpg] https://lafine.net/apt stable main" \
| sudo tee /etc/apt/sources.list.d/roamswitch.list
# 3. 安装
sudo apt update && sudo apt install roamswitch-server
2.2 DNF / RPM(Fedora / RHEL / AlmaLinux / Rocky Linux)
# 1. 导入 GPG 密钥
sudo rpm --import https://lafine.net/rpm/RPM-GPG-KEY-roamswitch
# 2. 添加仓库配置文件
sudo curl -fsSL -o /etc/yum.repos.d/roamswitch.repo https://lafine.net/rpm/fedora/roamswitch.repo
# 3. 安装
sudo dnf install roamswitch-server
2.3 openSUSE(zypper)
sudo rpm --import https://lafine.net/rpm/RPM-GPG-KEY-roamswitch
sudo zypper addrepo https://lafine.net/rpm/opensuse/roamswitch.repo
sudo zypper refresh && sudo zypper install roamswitch-server
roamswitch) 和服务器版本 (roamswitch-server) 声明相互的包冲突。在无头服务器环境中始终选择 roamswitch-server。3. 初始设置和服务验证
3.1 服务状态
安装后,roamswitch-server.service 立即启动并在系统引导时启用。
sudo systemctl status roamswitch-server.service
3.2 交互式设置向导
使用以下命令启动交互式设置向导,交互式配置端口、维护来源 IP、通知渠道和遏制操作(按 Enter 采用各项默认值)。
sudo roamswitch server setup
- 端口:分别以逗号分隔输入 SSH 维护端口(默认
22)和公开服务端口(默认80,443)。 - 指定维护来源 IP:指定 SSH 跳板机或监控源的 IP / CIDR(例如
203.0.113.50/32)。向导中为必填项,不能留空。若要允许所有 IP 的 SSH,请输入ANYWHERE(此时隔离期间管理用 SSH 也会向全世界开放)。 - 通知渠道设置:分别启用 Telegram Bot、LINE Messaging API、通用 Webhook(Slack / Discord / Teams)。
- eBPF 重大事件时的响应:选择
isolate(隔离主机)/freeze(冻结该进程)/alert_only(仅通知)。保存后会确认发送测试通知并重启服务。 - CVE 数据自动更新(默认
y):选择是否让容器隔离审计所使用的内核 CVE 数据库、Web 规则,以及在 Ubuntu 服务器上用于操作系统软件包 CVE 比对的 Ubuntu 软件包 CVE 数据,从 lafine.net 匿名获取(签名清单每天确认一次,CVE 数据本体约每 30 天一次,Web 规则只在清单版本更新时获取;仅 HTTPS GET,不含查询字符串和 Cookie,能表明软件身份的请求头只有固定的User-Agent: RoamSwitch-Updater,并验证 Ed25519 签名)。不会发送任何有关本主机的信息。这是服务器版默认开启的唯一对外通信,选n即可停止(审计本身仍会基于内置基线运行,但 Ubuntu 服务器上的操作系统软件包 CVE 比对会显示“本地 CVE 数据未获取”)。如果现有的server.conf中已将其关闭,默认答案仍为n。 - File Scan Guard 设置:设置是否为邮件中继、共享文件夹、上传目标启用病毒扫描(§13)及目标目录。可选功能,默认关闭。
- Resource Guard 设置:设置是否启用检测网络公开服务持续 RSS 增长(内存泄漏/DoS)或崩溃循环的资源枯竭防护。
- 常驻核心防护说明:显示无需通过向导、始终生效的核心防护功能列表,如入站默认拒绝、FIM、eBPF 集成等(无需输入)。
- 调查代理协作(investigation)设置:设置是否将 eBPF 重大事件,或关键路径 FIM/依赖锁文件篡改检测(均为仅通知判定时)的初步调查自动委托给外部 AI 代理 CLI(Claude Code / agy / Codex CLI / OpenCode,或自定义命令)(§10.1)。默认关闭。
-100 开头的负数(例如 -1001234567890)。可在 curl -s "https://api.telegram.org/bot<token>/getUpdates" 响应中的 "chat":{"id":...} 字段确认正确的值。3.3 34项服务器安全审计
sudo roamswitch status --server
对防火墙规则、Frag Gap 缓解、Yama LSM 限制、权限和 Docker 端口暴露执行即时 34 项检查审核,产生 0-100 的安全评分和等级(S 到 C)。
为便于合规报告,每个检查项目现在还会标注 NIST CSF 2.0 的功能/类别(适用时必定标注),以及在存在高置信度映射时的 CIS Controls v8 管控编号。
4. 防火墙和端口规则
配置文件 /etc/roamswitch/server.conf 出于保护敏感信息的目的,强制要求权限为 0600(仅 root 可读写)。格式为扁平的 key=value,形如 [network] 的分区标题行会被忽略。
# --- 防火墙 ---
ssh_ports=22 # SSH 管理端口(紧急隔离时也保留)
allowed_ports=80,443 # 公开服务端口
whitelist_ips=203.0.113.10/32 # 维护来源 IP / CIDR(留空 = 全部允许,旧名 admin_source_ips 也可)
protect_docker_ports=true # DOCKER-USER 保护(阻止容器绕过防火墙)
preserve_ssh_on_isolation=true # 紧急隔离时也保留 SSH 管理通道(设为 false 则完全阻断)
# --- eBPF 运行时防护 ---
action_on_critical=isolate # isolate | freeze(SIGSTOP+网络阻断) | alert_only
ebpf_socket_path=/run/roamswitch/events.sock
# --- Egress / C2 阻断与 DNS 黑洞 ---
egress_ip_blocklist_enabled=true # 默认 true。软件包自带的种子仅包含 JPCERT/CC 公布的 3 个攻击服务器;在安装情报源之前不会阻断其他任何内容
metadata_watch_enabled=true # 默认 true。非 root 程序/容器连接云元数据时通知(仅记录、不阻断;用于发现 SSRF)
metadata_watch_allow_users= # 正常使用元数据服务的账户(名称或数字 ID,逗号分隔;root 始终豁免)
dns_sinkhole_enabled=false # 默认 false(避免破坏内部 DNS/split-horizon 环境)
dns_sinkhole_provider=quad9 # quad9 | cloudflare | adguard | cleanbrowsing
# --- 容器隔离 CVE 数据(默认开启) ---
cve_kernel_map_updates_enabled=true # 默认 true。默认开启的唯一对外通信(签名的内核 CVE 数据、Web 规则,以及 Ubuntu 服务器上的 Ubuntu 软件包 CVE 数据;清单每天一次,CVE 数据本体约每 30 天一次;匿名·仅接收·验证签名)。设为 false 即停止
# --- 通知 ---
telegram_enabled=false
telegram_bot_token=
telegram_chat_id=
line_enabled=false
webhook_enabled=false
webhook_url=
webhook_block_private_addresses=false # opt-in SSRF guard
webhook_allow_addresses=
# --- 系统 ---
language=ja
fim_check_interval_secs=300 # 旧名 scan_interval 也可
isolation_hard_cap_secs=3600 # 高置信度隔离进入降级模式前的秒数(0 = 保持到解除)
如果直接编辑了配置,需要重启服务才能生效(roamswitch-server 不支持 reload):
sudo roamswitch server restart
4.1 按严重级别设置策略(guard.yaml,可选)
如果仅靠 action_on_critical 无法满足更精细的控制需求,可创建 /etc/roamswitch/guard.yaml(分发模板位于 /etc/roamswitch/guard.yaml.example)。若该文件不存在,会根据 server.conf 中的 action_on_critical 自动合成等效策略,不影响现有运行。
policies:
on_warning:
action: log_and_notify
on_critical:
server_action: isolate_process
kill_process: true
fallback_to_host_isolation_after_retries: 3 # 同一进程再次发生时自动升级为主机隔离
on_emergency:
action: isolate_host_all
kill_process: true
server_safeguards:
maintenance_ssh_ports: [22]
safety_timer_secs: 300 # 低置信度隔离自动恢复前的秒数(0 为禁用)。不适用于高置信度隔离(见下文)
隔离的自动解除(按置信度):如果仅靠固定计时器解除,会被只需等待的攻击者突破。因此,隔离的触发条件按置信度分类。(1) 低置信度(eBPF 的 Error / Warning 及以下、通用的端口异常等):经过较短的计时器(safety_timer_secs,默认 300 秒)后全部恢复。(2) 高置信度(金丝雀篡改、勒索软件式的大量改写、eBPF 的 Critical / Emergency / Alert、同一规则的重复升级):不会静默恢复。在较短的计时器之后仍会持续重新确认原因,仅在确认原因消失时才会带通知解除。如果无法确认、也没有 ack,并达到上限(server.conf 的 isolation_hard_cap_secs,默认 3600 秒,0 = 无上限),则进入降级(DEGRADED)模式。(3) 降级模式:除维护 SSH 端口和白名单 IP 的管理路径之外,新的通信仍保持阻断,绝不会因时间流逝而自动放开。解除的方式只有:重新确认原因消失、明确的 ack(降级期间的 ack 表示解除),或 sudo roamswitch emergency-restore。无论哪种隔离,管理路径(maintenance_ssh_ports 和 whitelist_ips)以外的已建立连接(攻击者已经建立的 C2 或反向 Shell 等)都会被阻断。每次状态转换都会记录到事件时间线,并以 10 种语言发出通知。
sudo roamswitch server ack
roamswitch server ack 在 FULL 隔离期间会保持隔离严格不变并做出确认应答(视为运维人员已接手,停止进入降级或自动解除),在降级期间则会解除隔离。
4.2 Egress / C2 阻断与 DNS 黑洞
设置 egress_ip_blocklist_enabled=true(默认)后,会通过 nftables 阻断发往已知恶意 IP 的出站连接。情报源优先使用 /var/lib/roamswitch/threatfeed/malicious_ips.txt(由更新机制管理),不存在时使用软件包自带的种子(/usr/share/roamswitch/threatfeed/);二者择一,不会合并。种子仅包含 JPCERT/CC 在 2026 年 10 月 15 日的报告(针对 Web 服务和 API 的攻击)中公布的 3 个攻击服务器地址(210.149.87.120, 118.26.36.16, 69.10.51.162)。租用服务器的地址可能被重新分配给他人,因此若正常的通信目标被阻断,请检查该文件中对应的行。
将 dns_sinkhole_enabled=true 设置为启用后,会强制通过过滤型 DNS 解析器(Quad9 / Cloudflare / AdGuard / CleanBrowsing)解析域名,阻断对已知恶意域名的解析。因可能破坏内部 DNS 或 split-horizon 环境,默认值为 false。
metadata_watch_enabled 開啟(預設)時,只有當非 root 程式或容器新建到雲端中繼資料位址(169.254.169.254、169.254.170.2、100.100.100.200、fd00:ec2::254)的連線時,nftables 表 inet roamswitch_metadata_watch 才會寫入一筆核心記錄,伺服器常駐程式依帳號(容器依來源位址)每天通知一次。只記錄與通知,不阻斷。攻擊者傳入自己伺服器上的 URL(以 302 轉送到內部位址)時,存取記錄只會留下外部 URL,因此只有這條對外流量看得到第二跳。正常使用雲端角色的應用程式同樣會被回報,可在 metadata_watch_allow_users 中加入其帳號予以豁免(容器無法豁免)。
4.3 入站端口扫描检测防护
通过将 nftables 日志规则与 journald 进行比对,检测在短时间内连接了大量不同端口的来源 IP(默认启用)。高置信度的扫描来源可在一定时间内自动封锁。与 RoamSwitch Sensor 一起使用时(与 §10.1 是不同的功能——已配对 Sensor 的全端口审计探测会作为误报对策被排除),Sensor 一侧的 IP 会自动排除在外。
# --- 入站端口扫描检测(默认启用) ---
port_scan_detect_enabled=true # false 表示禁用
port_scan_detect_min_distinct_ports=15 # 连接到不少于此数量的不同端口时判定为扫描
port_scan_detect_window_secs=300 # 判定的统计窗口(秒)
port_scan_block_enabled=true # 自动封锁检测到的扫描来源 IP
port_scan_block_duration_secs=600 # 自动封锁的持续时间(秒)
可通过 roamswitch port-scan 命令查看检测历史。
4.4 DNS 隧道检测防护
仅针对本机自身发出的 DNS 流量(UDP/TCP 53 端口),通过专用的 NFQUEUE 进行检查。凡标签长度超过 40 字节且香农熵超过 3.8 的查询,或查询类型为 TXT/NULL 记录的查询,均视为可疑形态,且仅当最近 60 秒内此类查询达到 10 次时才发出通知(同一条件下的重复通知设有 300 秒冷却期)。其目的是检测 iodine、dnscat2 等 DNS 隧道/数据外泄工具所特有的查询模式。底层 nftables 规则携带与其他 DNS 类防护相同的 bypass 标志,因此即使本防护停止或无响应,也绝不会妨碍 DNS 域名解析。本功能仅用于通知,不会自动进行阻断。与其他配置项相同,dns_tunnel_detect_enabled 的更改需重启服务后才会生效。
# --- DNS 隧道检测(默认启用) ---
dns_tunnel_detect_enabled=true # false 表示禁用
4.5 SSH 暴力破解防护
在 sshd 的登录失败写入日志的瞬间读取,并按来源地址计数(默认启用)。达到设定次数(默认 600 秒内 6 次)的来源地址,会通过 nftables 的 inet roamswitch_sshguard 表,只丢弃其发往 SSH 端口的新连接。首次封禁 1 小时,同一来源每再犯一次延长为 4 倍,上限 7 天。封禁的同时,也会切断该来源已经建立到 SSH 的连接(删除其 conntrack 条目,并增加一条丢弃应答方向数据包的规则)。只有既没有已认证会话、也没有公钥登录成功记录、也不在豁免之列的来源才会被封禁,且在切断前会再确认一次,因此已认证的会话和豁免的来源不会被切断。不会向外部发送任何数据,只在本机运行 journalctl、nft、loginctl 等命令。
日志通过 journalctl -f 实时读取。journald 会记录写入者进程,因此 sshd 以外写入的行会被忽略。没有 journald 的环境会跟踪 /var/log/auth.log 或 /var/log/secure,但只要是能使用 syslog 的本地用户,就能伪造这些文件中的行,导致第三方被封禁;有 journald 的环境请保持 ssh_guard_log_source 的默认值 auto。计入的失败包括密码错误、不存在的用户、报出用户名后在认证前断开、超过尝试次数以及 PAM 认证失败。公开密钥的失败(Failed publickey)不计入,因为持有多把密钥的合法客户端每次都会产生。来源地址取自行尾的 from <IP> port <N>。攻击者可以在用户名中写入任意字符串,但写入伪造的来源地址并不会让第三方被封禁。IPv6 按 /64 合并计数,封禁也以 /64 为单位(若该 /64 中含有网关等必须保护的地址,则只封禁来源的那一个地址)。
不会封禁的对象包括:网关、DNS 解析器、本服务器自身、回环地址、whitelist_ips、ssh_guard_allow、已通过认证且当前仍打开 SSH 会话的来源地址、最近 30 天内(ssh_guard_trust_key_days)用公开密钥认证成功过的来源地址(密码登录成功不计),以及已配对的 Sensor。这些来源即使失败次数累积,也只留下记录。私有地址不豁免,因为内部被攻陷的设备同样可能成为暴力破解的发起方。仅在认证之前保持连接打开的来源,同样不豁免。
当 fail2ban(sshd 的 jail 已启用)或 CrowdSec(firewall bouncer 正在运行)已经在保护 sshd 时,为避免对同一地址重复封禁,本防护会让出封禁,只做检测和通知(ssh_guard_yield_to_fail2ban,默认 true)。ssh_guard_block_enabled=false 时同样只检测和通知。仅在以下情况通知:封禁时(只检测时为达到阈值时),以及失败累积的来源地址登录成功时(严重。可能已被暴力破解成功,不会切断会话)。不会为每一次失败通知,1 小时内超过 10 个的来源地址会合并为一条通知。
用 roamswitch server bans 查看封禁列表(支持 --json;列表只有 root 能读取,所以要加 sudo),用 sudo roamswitch server unban <IP | IPv6 的 /64 | --all> 解除。解除后,该来源地址的失败计数也会清零。服务器重启后,封禁会连同剩余时间一起恢复。roamswitch emergency-restore 不会解除 SSH 封禁(恢复网络并不构成放回曾在猜测密码的对象的理由);恢复菜单(roamswitch recover)和卸载则会连同整个表一起删除。MCP 的 get_ssh_guard_incidents 是只读的,没有解除封禁的工具。在 Server TUI 中,可在“事件”界面的“SSH 暴力破解”里查看并解除封禁(以 root 启动时。§17)。
roamswitch server bans
sudo roamswitch server unban 203.0.113.9
sudo roamswitch server unban --all
设置项共有 10 个,都以 ssh_guard_ 开头(§18.1)。例如用 sudo roamswitch server config set ssh_guard_max_failures 10 修改,生效需要执行 sudo roamswitch server restart。要停止整个防护,设置 ssh_guard_enabled=false;只检测和通知,设置 ssh_guard_block_enabled=false。诊断(34 项)中的“SSH 暴力保护”,在本防护运行并执行封禁时也会通过。
局限:只反复产生公开密钥失败的尝试不计入。分散在大量来源地址上的暴力破解按来源逐个计数,因此在某个来源达到阈值之前不会被阻止。根本的对策是关闭密码认证,只允许公开密钥认证。
5. SSH 锁定预防故障保护
由于防火墙配置错误而导致的意外锁定是无法容忍的操作灾难。 RoamSwitch 服务器版包含多层保护措施:
- 已建立/相关无条件通过:在规则重新加载期间,活动会话永远不会被删除。
- Air-Gap 隔离时保留 SSH:即使在检测到异常并紧急隔离的情况下,通过
preserve_ssh_on_isolation = true,仅维护 SSH 端口和管理来源 IP(whitelist_ips)的管理路径会保持新建和已建立的通信,无需现场处置即可调查原因并恢复。除此之外的已建立连接(攻击者已经建立的 C2、数据外传、反向 Shell)都会被阻断。如果未指定管理来源(whitelist_ips与 guard.yaml 中的允许项均为空),隔离期间维护 SSH 端口仍会接受来自任意来源的新连接,服务器启动时会输出警告。设为preserve_ssh_on_isolation=false时,SSH 也会一并阻断。1.10.4 及之前的服务器版存在缺陷,隔离规则会丢弃 SSH 的应答,因此隔离期间运维人员的 SSH 无法使用。该缺陷已在 1.10.5 中修复。 - Atomic nftables 加载并回滚:规则语法错误立即触发零停机回滚到之前已验证的规则集。
6.容器保护(Docker/Podman)
Docker 的默认行为会创建绕过标准主机防火墙的自定义 iptables 规则,将容器端口(例如 -p 8080:8080)直接暴露到互联网。
当 protect_docker_ports=true(默认)时,RoamSwitch 会按照 Docker 的防火墙方式,使发往容器的流量除了目标为 ssh_ports 和 allowed_ports 中所含端口的之外,都无法从外部到达。在 iptables / iptables-nft 方式下,会在 DOCKER-USER 链的开头插入检查规则(除 IPv4 外,如果启用了 Docker 的 IPv6,也会通过 ip6tables)。在 Docker 29 及以上的原生 nftables 方式(不存在 DOCKER-USER)下,会在早于 Docker 链的 forward 钩子上创建专用的 inet roamswitch_docker_guard 表(一个 inet 表同时处理 IPv4 / IPv6)。判定依据是 DNAT 之前的原始目标端口(ctorigdstport),且只针对来自外部接口的通信,因此不会影响容器发出的出站通信和容器之间的通信。可防止意外的开发用数据库容器等被暴露到外部。
此防护具有幂等性,并且每分钟自我修复一次。即使 dockerd 在 RoamSwitch 之后才启动、Docker 重启或重新加载导致规则被改写、手动执行了 nft flush 或 nftables 表丢失,也会在不触动当前有效防护的前提下重新设置。
此外,监视 docker events 的实时防护会在使用 --privileged 启动的容器,或挂载了 /var/run/docker.sock 的容器(容器逃逸风险)启动后立即检测并通知。另外,容器 exec 防护(container_exec_guard_enabled,默认开启)会使用 YARA 检查容器内二进制的执行(即使执行后删除文件的规避手法,由于是从已打开的文件描述符读取内容,同样可以检查)。该防护也会在启动时给已经在运行的容器打上标记,并自动跟进新容器。两者都只做通知,不会自动阻断。在 Docker 29 上也能运行(1.11.1 起支持)。事件订阅持续失败时,重连间隔会从 5 秒逐步延长到最长 5 分钟。
7. 关键路径文件完整性监控(FIM)
使用 SHA-256 监控 150 多个重要文件(/bin/login、/usr/bin/sudo、/usr/sbin/sshd 等重要二进制,/etc/shadow、/etc/sudoers 等认证配置,以及 systemd 单元)。
监控范围不只是单个文件。在重要目录(/etc/pam.d、/etc/sudoers.d、/etc/ssh/sshd_config.d、/etc/cron.*、/etc/systemd/system、/etc/ld.so.conf.d、/etc/profile.d、/root/.ssh(known_hosts 除外))中,还会检测是否出现了新文件;所有可登录账户的 authorized_keys、/etc/ld.so.preload(正常情况下不存在,出现即为本地 rootkit 的迹象)以及 RoamSwitch 自身的二进制也在监控之列。snapd 自动生成的临时单元(snap-*.mount 等)不在监控范围内。新文件检测仅在包含此范围的基线上有效,因此升级后请执行一次 sudo roamswitch fim update。以非 root 身份执行 fim verify 时无法读取的文件,会按「因无法读取而跳过」处理,而不是视为篡改。
7.1 手动哈希验证
sudo roamswitch fim verify
7.2 事件驱动检测(即时)
受监控文件通过 fanotify(FAN_CLASS_NOTIF,写入完成事件)被逐一监视,无需等待下一次周期性扫描,篡改发生的瞬间即会重新校验哈希。周期性扫描(默认 fim_check_interval_secs=300 秒间隔)仍会作为漏检事件的后备保障并存。把新文件放到同一名字之下的编辑(install、sed -i 等)也能检测到,通知中会附上做出修改的进程和 SSH 登录(§7.5)。
7.3 操作系统更新时自动基线同步
在 Debian/Ubuntu 上,安装软件包时会自动配置 /etc/apt/apt.conf.d/99roamswitch-fim 的 DPkg::Post-Invoke 钩子。在 Fedora/RHEL/Rocky/AlmaLinux/openSUSE 上,监控 /var/lib/rpm 的 systemd.path 单元(roamswitch-fim-update.path)承担相同职责。
在 Debian/Ubuntu 上,/etc/apt/apt.conf.d/99roamswitch-fim 附加一个 DPkg::Post-Invoke 挂钩。通过 sudo apt upgrade 进行的合法软件包更新会自动刷新 FIM 基线,从而消除误报警报。
sudo roamswitch fim update
守护进程首次启动并建立基线时,如果包管理器(apt/dpkg、dnf/rpm、zypper 或 pacman)正在工作,会先等待其结束,最长 120 秒,再建立基线。即使与 Apache 等在同一次 apt install 中安装,dpkg 正在处理的临时文件(如 *.dpkg-new)也不会进入基线,因此不会在随后的校验中被误判为篡改(1.11.18 及以后)。超过 120 秒仍未结束时,会记录警告并照常建立基线。
7.4 蜜罐凭据(Honeytokens)
守护进程启动时,利用其以 root 权限运行的特性,在 /root 下幂等地部署三种诱饵凭据文件(蜜罐凭据):.aws/credentials、.ssh/id_rsa、.docker/config.json。若该路径已存在真实文件,则绝不覆盖。后台监视进程通过 inotify 检测对这些文件的读取访问,一旦检测到即视为入侵后的凭据侦察行为(MITRE ATT&CK T1552:Unsecured Credentials),立即发送 Critical 级别通知并记录到事件时间线。默认开启,可以用配置项 honeytokens_enabled(设为 false 会停止部署和监视,已放置的诱饵会保留)和 honeytokens_home(部署位置,默认 /root)更改。截至 1.10.4 的发布软件包未包含此功能所需的 roamswitch-honeytokens,因此通过软件包安装的环境中该功能并未运行。自 1.10.5 起已随附,服务器版中 systemd 沙箱阻止写入 /root 的问题已在 1.10.10 中修复。自 1.11.1 起,RoamSwitch 自身执行的 docker 命令,会把一个空的专用目录(root 下为 /run/roamswitch/docker-config)指定为 DOCKER_CONFIG 后启动,因此不会触发诱饵 .docker/config.json(此前,守护进程的监视和 roamswitch status --server 可能读取诱饵而造成误检)。其他程序读取时,仍会像以前一样检测。
若检测到对任一蜜罐凭据的真实访问,考虑到攻击者可能进一步深入探查,RoamSwitch 会额外部署与之相关的新诱饵(例如:触及 .aws/credentials 后会部署 .aws/config;触及 .ssh/id_rsa 后会部署 .ssh/config;触及 .docker/config.json 后会部署 .kube/config),每次触发最多 2 个,全部触发合计最多 6 个。若在事件调查期间发现 /root 下出现这些陌生文件增多,属于预期行为。
7.5 入侵路径(谁、通过哪次 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)才会发现。这时不知道写入的进程是谁,通知中也会如此说明。
除通知外,记录还会保存在事件时间线(类型 fim)和 /var/lib/roamswitch/fim_intrusions.json(仅 root 可读,最新 100 条)中。MCP 的 verify_fim 会以新增字段 recentIntrusionContext 返回(仅以 root 运行时)。是否被篡改的判定不变。限制:停用执行记录器且写入进程极其短命时,无法得知可执行文件,此时会显示父进程的可执行文件和命令行。持久化监视(persistence_watch)的通知不含进程信息,因此不附带入侵路径。入侵路径只显示到做出修改的进程及其登录为止,入侵的入口本身(被利用的漏洞,或密钥是如何被盗的)无法得知。要关闭,请设置 fim_intrusion_context_enabled=false。
8. eBPF 运行时防护和 Falco 集成
挂钩内核空间 eBPF 事件 (Falco / Tetragon) 以检测 Frag Gap 内核权限升级、容器逃逸和未经授权的反向 shell。
8.1 抗膨胀架构
为避免传统安全工具中常见的「syslog 爆炸式膨胀」和「磁盘空间被占满」问题,RoamSwitch 通过 UNIX 域套接字 /run/roamswitch/events.sock 与 Falco 直接对接。该套接字以仅 root 可用(0600)的方式创建,能写入的只有以 root 运行的 Falco(无需额外的权限设置)。
- 捆绑的
/etc/falco/config.d/99-roamswitch-optimized.yaml将事件直接流式传输到守护程序中,磁盘 I/O 为零。Falco 的输出在该配置中通过socat传给套接字,因此 Server Edition 的 deb / rpm 软件包会把socat作为推荐软件包(Recommends)安装(不使用 Falco 时不需要)。 /etc/logrotate.d/roamswitch-falco确保任何后备文件输出的每日干净轮换。
8.2 自主收容
一旦检测到严重攻击,RoamSwitch 会立即发出 SIGSTOP 来冻结违规进程 PID 并应用 nftables 气隙隔离。
8.3 入侵证据自动保全(Forensic Evidence Capture)
当 eBPF 运行时防护执行了针对进程的遏制操作(以 SIGSTOP 冻结并切断网络)时,会自动创建证据保全包。按照 §4.1 的默认策略,Critical 级别事件会走此路径;而对整台主机进行隔离的 Emergency 级别事件目前不会生成此类证据包。证据包内容包括:在默认路由接口上进行的、尽力而为的约 30 秒定界数据包捕获(若未安装 tshark,则仅跳过此项并记录该情况),以及(在已知目标 PID 时)一份 /proc/<pid> 快照,包含 status、cmdline、maps、environ(名称中包含 KEY/TOKEN/SECRET/PASSWORD 的值会被替换为 [REDACTED])、已打开文件描述符的链接目标列表,以及直至 PID 1 的祖先进程链。随包附有记录了每个已获取文件 SHA-256 哈希值的 manifest.json,可供日后进行篡改验证。
存储位置为 /var/lib/roamswitch/incident-evidence/<时间戳>-<ID>/。证据包不会自动清理。为避免占用磁盘空间,如需明确删除超过保留期限(默认 30 天)的旧证据包,请执行:
sudo roamswitch-incident-capture prune [--keep-days N]
此外,您也可以在无需等待遏制动作触发的任意时刻手动获取快照:
sudo roamswitch-incident-capture snapshot [--pid PID] [--reason TEXT] [--json]
8.4 隔离速度的测量与告警通路的心跳检测
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 行)。
8.5 反向 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 客户端版没有。
9. 紧急警报(Telegram / LINE / Webhook)
当发生端口异常、FIM 篡改或 eBPF 遏制事件时,立即向外部通知通道发送警报:
[notifications]
language = ja
# Telegram:Bot Token 与 Chat ID
telegram_bot_token = "123456789:ABCdefGHIjklMNOpqrSTUvwxYZ"
telegram_chat_id = "-1001234567890"
# LINE:Messaging API 频道访问令牌与用户 ID
line_channel_access_token = "YOUR_LINE_ACCESS_TOKEN"
line_user_id = "U1234567890abcdef1234567890abcdef"
# 通用 Webhook(Slack、Discord、Teams、自建监控平台)
webhook_url = "https://hooks.slack.com/services/T00/B00/XXXXX"
即使未配置这些外部通知渠道,已发送的通知也会在本地保留最近1周。可通过 roamswitch notifications 查看。
拒绝向内部地址发送 Webhook(可选启用):设置 webhook_block_private_addresses=true(默认 false = 保持原行为)后,若通知 Webhook 的目标是环回、私有(10/8、172.16/12、192.168/16)、链路本地(169.254/16,含云元数据 169.254.169.254)、ULA(fc00::/7)或 CGNAT(100.64/10)地址(主机名则解析结果中有一个命中即可),就拒绝发送,并把原因写入日志和 roamswitch notify-test 的错误。2130706433、0x7f.1 等其他写法以及 IPv4-mapped IPv6 同样会被识别,且已验证的地址会固定给 curl,可防止 DNS rebinding。内部 SIEM 等合法的内部目标用 webhook_allow_addresses=10.20.0.0/16,fd00::/8(以逗号分隔的 CIDR)放行。事件转发 Webhook 有同样的一对键 forward_webhook_block_private_addresses / forward_webhook_allow_addresses。即使该选项为 false,目标是云元数据地址时也会记录警告日志(不会阻止发送)。若设置了 HTTP 代理环境变量,解析与连接由代理完成,不在此检查范围内。
10.AI代理/MCP集成(模型上下文协议)
RoamSwitch 服务器版包括 roamswitch-mcp,允许 AI 代理(Claude、Gemini、Cursor)监控基础设施健康状况和状态。
配置示例(`claude_desktop_config.json`)
{
"mcpServers": {
"roamswitch": {
"command": "/usr/bin/roamswitch-mcp",
"args": []
}
}
}
10.1 调查代理协作(investigation,将初步自动调查委托给 AI 代理)
§10 的 roamswitch-mcp 面向「人类通过 AI 客户端发起的查询」,而investigation 协作是另一种机制:由守护进程自身在检测到事件时自主启动外部 AI 代理 CLI。其对象是被判定为仅通知(未触发自动隔离或进程冻结)的 eBPF 重大事件,以及关键路径 FIM、依赖锁文件篡改检测。启用后,守护进程会先生成本地启发式一次判定报告(误报可能性、依据、下一步调查建议),随后附带该报告以子进程方式启动已配置的 AI 代理 CLI,委托其进行进一步调查与总结。默认关闭。
# 追加到 /etc/roamswitch/guard.yaml(与 server.conf 是不同的文件)
investigation:
enabled: true
command: "claude" # "claude" | "agy" | "codex" | "opencode" | 任意自定义命令
args: ["-p"] # 提示词会自动作为末尾参数追加
timeout_secs: 180
同样内容也可在 sudo roamswitch server setup 的交互式向导(§3.2 最后一步)中设置。可选择 Claude Code(claude -p)/ agy(agy -p --dangerously-skip-permissions)/ Codex CLI(codex exec)/ OpenCode(opencode run)预设,或任意自定义命令。再次运行时会根据已保存的设置自动选中对应预设。
run_as 的设置不会被向导(CLI 和 TUI)保存,守护进程也不会启动它。以跳过权限确认的模式(如 --dangerously-skip-permissions)启动工具,会提高恶意输入使代理执行非预期操作的风险。选择 agy 时,向导会显示此提示。在存在不可信日志输入路径的环境中,建议使用不跳过权限确认的工具设置。在网络中断期间(如 Air-Gap 触发时等,相当于 isolate_host_all 的状态)不会调用 investigation 协作。
11. CLI 命令备忘单
| 命令 | 特权 | 描述 |
|---|---|---|
sudo roamswitch status --server |
root | 显示 34 项服务器安全诊断的评分和状态(使用 --json 以 JSON 输出相同内容) |
sudo roamswitch ports |
root | 审计开放的监听端口和绑定的进程 |
sudo roamswitch fim verify |
root | 验证关键路径系统文件的 SHA-256 完整性 |
roamswitch notifications |
用户 | 显示最近一周的通知历史(所有已发送的告警)(同样支持 --json / --export 文件) |
sudo roamswitch server setup |
root | 启动交互式初始配置向导 |
sudo roamswitch fim update |
root | 刷新 FIM SHA-256 基线数据库 |
sudo roamswitch server config set <key> <value> |
root | 修改并保存单个配置键(列表见 §18) |
sudo roamswitch emergency-restore |
根 | 解除全部 eBPF/防火墙紧急隔离,恢复到初始基线 |
roamswitch port-scan |
普通用户 | 显示入站端口扫描检测防护(§4.3)的检测历史(来源 IP、是否自动封锁及封锁时长) |
roamswitch sensor [key|list|pair|unpair|request-audit|results] |
普通用户/root | 与 RoamSwitch Sensor 通过配对码方式建立相互信任。使用 pair --addr <IP> --code <配对码> --fingerprint <SHA-256> 进行配对(指纹为 Sensor 屏幕上显示的值),使用 repin 在 Sensor 重新生成证书后更新固定值,使用 request-audit 请求主动漏洞审计(通过 results 获取结果)。需要 sensor_pairing_enabled: true(默认关闭)。详情请参阅 Sensor 运维手册 |
roamswitch server tui |
普通用户/root | 启动可操作全部功能的 TUI(普通用户为只读,root 可进行全部操作。§17) |
sudo roamswitch server ack |
root | 对当前的紧急隔离做出确认应答(FULL 隔离期间保持严格隔离,降级期间则解除。§4.1) |
roamswitch server ebpf |
用户 | 显示 eBPF 运行时防护当前的隔离状态和已检测事件的历史 |
roamswitch server bans [--json] |
普通用户/root | 显示 SSH 暴力破解防护(§4.5)的状态,以及当前被封禁的来源地址(含剩余时间)。支持 --json。封禁列表只有 root 能读取 |
sudo roamswitch server unban <IP | IPv6 /64 | --all> |
root | 解除 SSH 暴力破解防护的封禁,并清零该来源地址的失败计数(IPv6 以 /64 指定,--all 解除全部) |
sudo roamswitch server allowlist [list|add <rule> <process> [--exe <path>]|remove <rule> <process>] |
root | 管理误报的永久允许列表。加上 --exe 后会与可执行文件的路径绑定,无法通过伪造进程名来规避(仅按名称登记可被伪造,并会显示警告) |
roamswitch frozen [list|resume <PID>|resume all] |
普通用户/root | 列出被防护以 SIGSTOP 冻结的进程,并在属于误报时解除(SIGCONT)。使用 resume all 可全部解除 |
sudo roamswitch events search|tree|export|verify|status |
root | 搜索执行记录器(--since 2h --pid --exe --user --parent --text --json)、进程树、导出、哈希链验证、状态显示(§15) |
sudo roamswitch forward status|test|queue |
root | 查看事件转发的状态、发送测试事件、显示重发队列(§16) |
roamswitch timeline / notifications [--json] [--export FILE] |
用户 | 显示统一事件时间线(实验性)/通知历史。使用 --json 输出 JSON 数组,使用 --export 文件 输出 JSON Lines |
roamswitch audit-agent-setup [--json] |
用户 | 以只读方式检查提供给 AI 智能体的指示文件、技能和 OpenClaw 设置中的危险内容(仅限主目录,不进行网络通信,--json 输出 JSON)。也可在 Server TUI 的“工具”中运行 |
12. 故障排除和常见问题解答
Q1. RoamSwitch 会意外断开我的 SSH 会话吗?
不会。RoamSwitch 在正常情况下会无条件放行已知的 ESTABLISHED/RELATED 连接状态和 ssh_ports(默认 22)。此外,即使因严重攻击触发了 Air-Gap 紧急隔离,preserve_ssh_on_isolation=true(默认)也会保留维护 SSH 端口和管理来源 IP 的管理会话(除此之外的已建立连接会被阻断)。1.10.4 及之前的服务器版存在缺陷,隔离规则会丢弃 SSH 的应答,因此隔离期间运维人员的 SSH 无法使用。该缺陷已在 1.10.5 中修复。
Q2。如果不小心被锁定,如何重新获得访问权限?
通过云服务商的 Web 管理控制台(VNC/串行控制台)登录,执行 sudo roamswitch emergency-restore 解除紧急隔离,或执行 sudo systemctl stop roamswitch-server 停止服务。
Q3。如何为新添加的 Web 服务(例如 Nginx)公开端口?
执行 sudo roamswitch server config set allowed_ports 80,443,3000(或直接编辑 /etc/roamswitch/server.conf),然后执行 sudo roamswitch server restart。
13. File Scan Guard(保护邮件中继、共享文件夹与上传目的地)
仅当此主机存在用于保存要交给他人的文件的目录(如邮件中继、Samba/NFS 共享、文件上传目的地)时才需要关注的可选功能。内置 YARA 引擎无需外部依赖即可持续扫描 clamav_scan_dirs,设置 clamav_enabled=true 后还会加入 ClamAV 作为第二重判断。确认的威胁会自动隔离至 /var/lib/roamswitch/quarantine,并通知运维人员。
# --- File Scan Guard(ClamAV,可选启用) ---
# 内置 YARA 引擎始终无条件扫描 clamav_scan_dirs。
clamav_enabled=false # 设为 true 可启用 clamdscan 进行额外检查
clamav_scan_dirs= # 扫描目标的绝对路径(以逗号分隔)
clamav_scan_interval_secs=86400 # 扫描间隔(秒,最小 3600)
clamav_freshclam_interval_secs=7200 # 特征库自动更新间隔(秒,最小 1800)
也可通过交互式向导 sudo roamswitch server setup 进行配置,并可通过 get_file_scan_guard_status MCP 工具查看配置与隔离状态。
14. Log Audit(日志模式异常检测与通知)
通过定期扫描 journalctl 将日志消息模板化,检测新模式(前所未见的日志形态)与频率异常(统计学激增,Z-score > 3.0)。由于无需额外守护进程或外部依赖,这是一种轻量机制,默认已启用。检测到异常时会通过与 File Scan Guard 和 FIM 相同的通知渠道(Telegram / LINE / Webhook)发送通知。日志中可能出现的 API 密钥或令牌,会在写入通知正文之前自动脱敏。
# --- Log Audit(默认启用) ---
log_audit_enabled=true # 设为 false 可禁用
log_audit_interval_secs=1800 # 扫描间隔(秒,最小 300)
也可通过交互式向导 sudo roamswitch server setup 进行配置,并可通过 roamswitch audit-logs CLI 命令或 audit_security_logs MCP 工具查看最新的检测结果。
访问日志诊断工具 AccessScope(独立工具):Server Edition 的 deb / rpm 中随附一个独立工具的命令 accessscope,用于诊断 Web 服务器和 SSH 的访问日志(不含界面应用)。它是只读的,分析既不使用 LLM,也不使用网络。通过 LLM 分析是可选的,默认关闭(启用后也可以 root 运行,但 LLM 会以 root 权限运行,建议用无特权的用户运行)。accessscope scan --auto 诊断本主机的日志,accessscope discover 列出日志的位置。详情请参阅 AccessScope 页面。 同一 Server Edition 的 deb / rpm 还包含另外两个独立工具 PersonalSOC(检查本主机的日志与防御状况)和 PathScope(分析提权路径)的命令 personalsoc 与 pathscope,不含界面应用。这两个命令本身不进行网络通信。PersonalSOC 的 LLM 分析仅在指定 --llm 时运行。
15. 执行记录器与关联规则(本地,仅通知)
通过 netlink 的 proc connector 记录进程的执行(exec)(可执行文件、参数、父进程谱系、UID、cwd、容器 ID、SHA-256)。默认开启,记录以带哈希链的分段 JSON Lines(0600,默认 200 MB / 14 天)保存在本主机仅 root 可访问的目录 /var/lib/roamswitch/exec_log 中,不会发送到外部。它能够检测篡改(events verify),但不能防止篡改。如果无法打开 proc connector,则退化为低频的 /proc 扫描。记录处理运行在与 fanotify 许可事件循环不同的线程上,并将自身的 CPU 使用率控制在预算(默认 3%)以内。
在本地搜索和导出(由于是仅 root 可读的日志,通常需要 sudo)。
sudo roamswitch events status
sudo roamswitch events search --since 2h --exe curl
sudo roamswitch events tree 4242
sudo roamswitch events export --since 1d > exec-events.jsonl
sudo roamswitch events verify
关联规则 EXR-001〜007(仅通知,附带 MITRE ATT&CK 技术 ID):EXR-001 由 Web 服务启动的 Shell / EXR-002 从 /tmp、已删除文件、memfd 执行 / EXR-003 下载后立即执行 / EXR-004 使用 base64、eval 的解释器单行命令 / EXR-005 非 setuid 来源的 uid 0 获取 / EXR-006 执行 cron、systemd 刚刚放置的二进制 / EXR-007 ld.so.preload 变更后的执行。各规则采用安静的默认值,单独不会触发隔离。可通过 exec_rules_disabled(例如 EXR-003,EXR-004)单独禁用,也可通过 exec_rule_service_users 调整 EXR-001 的目标用户。
只有明确将 exec_rules_isolation_optin(默认 false)设为 true 时,严重程度高的规则才会被视为高置信度的隔离触发条件(§4.1)。默认情况下,规则只做通知。也可以通过只读的 MCP 工具 search_exec_events / get_process_tree 查看。
16. 事件转发(SIEM / syslog / Webhook,需主动开启)
可以将检测事件转发到运维人员指定的自己组织的目的地。默认关闭,在设置 forward_enabled=true 并配置至少一个目的地之前不会发送任何内容,也没有内置任何厂商的目的地。转发对象包括事件时间线、通知历史、eBPF 防护、隔离的状态转换、FIM、金丝雀、exec 规则的告警,但不会转发 exec 的原始日志本身。目的地有以下 3 种。
- syslog(
forward_syslog_url:udp://host:514/tcp://host:port/tls://host:port):RFC 5424 或 CEF(forward_syslog_format)。TCP / TLS 的分帧为octet(RFC 6587,默认)或lf。TLS 验证可通过forward_tls_ca_file指定 CA,通信则交由系统的openssl s_client处理。 - JSON Lines 文件(
forward_file_path):每行一个事件。通过forward_file_max_mb(默认 50)和forward_file_keep(默认 5)进行轮转。 - 结构化 Webhook(
forward_webhook_url):以 POST 发送 JSON,并在X-RoamSwitch-Signature头中附上基于共享密钥(forward_webhook_secret,或指向0600文件的forward_webhook_secret_file)的 HMAC-SHA256 签名。发送通过系统curl子进程进行,密钥不会出现在参数中。
未能发送的事件(TCP / TLS / Webhook)会进入磁盘上的重发队列(0600,上限由 forward_queue_max_mb 默认 20 和 forward_queue_max_age_hours 默认 72 决定),即使重启后也会按指数退避重发。可通过 forward_min_severity(默认 low)设置发送的最低严重程度。为避免阻塞热路径,发送在专用线程中进行,队列溢出时会从最旧的开始丢弃。
状态查看、连通性测试和重发队列查看使用以下命令(更改配置后请重启服务)。
# /etc/roamswitch/server.conf
forward_enabled=true
forward_syslog_url=tls://siem.example.org:6514
forward_syslog_format=rfc5424
forward_tls_ca_file=/etc/ssl/certs/corp-ca.pem
forward_min_severity=medium
sudo roamswitch server restart
sudo roamswitch forward status
sudo roamswitch forward test
sudo roamswitch forward queue
17. Server TUI(可操作全部功能的终端 UI)
roamswitch-server-tui(也可通过 roamswitch server tui 启动)是一个 TUI,只需通过 SSH 的键盘操作,就能处理分散在 CLI 动词和两个配置文件(server.conf 与 guard.yaml)中的 Server Edition 的所有功能(可在 80x24 下运行,支持 10 种语言)。如果 CLI 动词或配置键增加了而 TUI 中没有对应项,开发时的测试就会失败。
- 权限模式:以普通用户启动时为只读(更改和特权操作被禁用,取而代之的是显示应当执行的
sudo roamswitch …命令)。以 root 启动时可以进行操作,影响较大的操作会先显示内容,最危险的操作则要求输入yes。不会启动守护进程,自身也不会打开网络套接字。 - 界面(用数字键切换):
1仪表板(健康状况、隔离状态)/2防御(eBPF、隔离、金丝雀、FIM、Docker、Egress、DNS、执行记录器、规则、事件搜索、订阅源、XDP 门控)/3事件(各类事件(含 SSH 暴力破解的封禁列表与解除)、时间线、通知历史、冻结的进程、隔离 Vault、允许列表)/4通知、转发 /5Sensor /6配置 /7服务 /8工具 /9日志审计 /0信息、帮助。按?查看当前界面的按键列表,按L切换 10 种语言,按r重新加载,按q退出(如有未保存的配置更改会进行确认)。 - 配置编辑器:由 schema 生成,可以在校验类型、范围和枚举值的同时,编辑
server.conf与guard.yaml的全部 146 个键(§18)。保存前可以确认差异(密钥已被遮盖),保存时会创建带时间戳的备份并以原子方式写入。需要重启的键,以及设置错误会失去管理路径或削弱防御的键,都会明确标示。调查代理联动(§10.1)的配置,也可以在选择预设并预览之后写入。
启动:
sudo roamswitch-server-tui # full control
roamswitch-server-tui # read-only
roamswitch server tui # same as roamswitch-server-tui
18. 配置键一览(server.conf / guard.yaml)
roamswitch-server 不支持配置的重新加载(reload)。所有键的生效都需要重启服务(sudo roamswitch server restart)。server.conf 的各个键可通过 roamswitch server config set <键> <值> 或 §17 的编辑器修改。guard.yaml 通过编辑器或手动编辑来修改。
18.1 server.conf(109 个键)
| 键 | 类型 / 可取值 | 默认 | 重启 |
|---|---|---|---|
language | system | ja | en | zh-hans | zh-hant | ko | de | fr | es | it | pt | ja | 需要 |
ssh_ports | port list | 22 | 需要 |
allowed_ports | port list | 80,443 | 需要 |
whitelist_ips | list | — | 需要 |
preserve_ssh_on_isolation | bool | true | 需要 |
telegram_enabled | bool | false | 需要 |
telegram_bot_token | string | — | 需要 |
telegram_chat_id | string | — | 需要 |
line_enabled | bool | false | 需要 |
line_channel_access_token | string | — | 需要 |
line_user_id | string | — | 需要 |
webhook_enabled | bool | false | 需要 |
webhook_url | URL | — | 需要 |
webhook_block_private_addresses | bool | false | 需要 |
webhook_allow_addresses | CIDR list | — | 需要 |
action_on_critical | isolate | freeze | alert_only | isolate | 需要 |
ebpf_socket_path | abs path | /run/roamswitch/events.sock | 需要 |
ebpf_heartbeat_interval_secs | 0 (off) | int 60–86400 | 600 | 需要 |
isolation_hard_cap_secs | int ≥ 0 | 3600 | 需要 |
fim_check_interval_secs | int ≥ 10 | 300 | 需要 |
fim_intrusion_context_enabled | bool | true | 需要 |
protect_docker_ports | bool | true | 需要 |
dns_sinkhole_enabled | bool | false | 需要 |
dns_sinkhole_provider | quad9 | cloudflare | adguard | cleanbrowsing | quad9 | 需要 |
egress_ip_blocklist_enabled | bool | true | 需要 |
metadata_watch_enabled | bool | true | 需要 |
metadata_watch_allow_users | list | — | 需要 |
cve_kernel_map_updates_enabled | bool | true | 需要 |
clamav_enabled | bool | false | 需要 |
clamav_scan_dirs | abs path list | — | 需要 |
clamav_scan_interval_secs | int ≥ 3600 | 86400 | 需要 |
clamav_freshclam_interval_secs | int ≥ 1800 | 7200 | 需要 |
lockfile_watch_dirs | abs path list | — | 需要 |
npm_audit_signatures_enabled | bool | false | 需要 |
log_audit_enabled | bool | true | 需要 |
log_audit_interval_secs | int ≥ 300 | 1800 | 需要 |
resource_guard_enabled | bool | true | 需要 |
resource_guard_rss_sample_interval_secs | int ≥ 10 | 60 | 需要 |
resource_guard_rss_window_secs | int ≥ 300 | 1800 | 需要 |
resource_guard_rss_min_samples | int ≥ 3 | 10 | 需要 |
resource_guard_rss_min_growth_pct | int ≥ 10 | 50 | 需要 |
resource_guard_rss_recovery_drop_pct | int ≥ 1 | 10 | 需要 |
resource_guard_rss_min_kb | int ≥ 0 | 20000 | 需要 |
resource_guard_crash_loop_window_secs | int ≥ 60 | 300 | 需要 |
resource_guard_crash_loop_min_restarts | int ≥ 2 | 3 | 需要 |
resource_guard_correlation_window_secs | int ≥ 60 | 600 | 需要 |
resource_guard_zombie_min_count | int ≥ 0 | 20 | 需要 |
resource_guard_load_min_normalized | float ≥ 0.0 | 0.5 | 需要 |
resource_guard_load_enabled | bool | true | 需要 |
resource_guard_cpu_enabled | bool | true | 需要 |
resource_guard_cpu_sample_interval_secs | int ≥ 0 | 60 | 需要 |
resource_guard_cpu_window_secs | int ≥ 0 | 600 | 需要 |
resource_guard_cpu_min_samples | int ≥ 0 | 8 | 需要 |
resource_guard_cpu_pct_threshold | float 0.0–1000.0 | 80.0 | 需要 |
container_exec_guard_enabled | bool | true | 需要 |
port_scan_detect_enabled | bool | true | 需要 |
port_scan_detect_min_distinct_ports | int ≥ 0 | 15 | 需要 |
port_scan_detect_window_secs | int ≥ 0 | 300 | 需要 |
port_scan_block_enabled | bool | true | 需要 |
port_scan_block_duration_secs | int ≥ 0 | 600 | 需要 |
ssh_guard_enabled | bool | true | 需要 |
ssh_guard_block_enabled | bool | true | 需要 |
ssh_guard_max_failures | int 2–1000 | 6 | 需要 |
ssh_guard_window_secs | int 10–86400 | 600 | 需要 |
ssh_guard_ban_secs | int 60–2592000 | 3600 | 需要 |
ssh_guard_ban_max_secs | int 60–31536000 | 604800 | 需要 |
ssh_guard_allow | IP/CIDR list | — | 需要 |
ssh_guard_log_source | auto | journal | abs path | auto | 需要 |
ssh_guard_yield_to_fail2ban | bool | true | 需要 |
ssh_guard_trust_key_days | int 0–3650 | 30 | 需要 |
dns_tunnel_detect_enabled | bool | true | 需要 |
reverse_shell_watch_enabled | bool | true | 需要 |
reverse_shell_watch_include_private | bool | false | 需要 |
reverse_shell_watch_notify_only | bool | false | 需要 |
sensor_pairing_enabled | bool | false | 需要 |
sensor_colocated | bool | false | 需要 |
forward_enabled | bool | false | 需要 |
forward_syslog_url | URL | — | 需要 |
forward_syslog_format | rfc5424 | cef | rfc5424 | 需要 |
forward_syslog_framing | octet | lf | octet | 需要 |
forward_tls_ca_file | abs path | — | 需要 |
forward_file_path | abs path | — | 需要 |
forward_file_max_mb | int 1–10000 | 50 | 需要 |
forward_file_keep | int 1–100 | 5 | 需要 |
forward_webhook_url | URL | — | 需要 |
forward_webhook_secret | string | — | 需要 |
forward_webhook_secret_file | abs path | — | 需要 |
forward_webhook_block_private_addresses | bool | false | 需要 |
forward_webhook_allow_addresses | CIDR list | — | 需要 |
forward_min_severity | info | low | medium | high | critical | low | 需要 |
forward_queue_max_mb | int 1–10000 | 20 | 需要 |
forward_queue_max_age_hours | int 1–8760 | 72 | 需要 |
exec_recorder_enabled | bool | true | 需要 |
exec_recorder_retention_mb | int 1–100000 | 200 | 需要 |
exec_recorder_retention_days | int 1–3650 | 14 | 需要 |
exec_recorder_hash_chain | bool | true | 需要 |
exec_recorder_hash_exe | bool | true | 需要 |
exec_recorder_cpu_budget_pct | int 1–50 | 3 | 需要 |
exec_rules_enabled | bool | true | 需要 |
exec_rules_disabled | string | — | 需要 |
exec_rule_service_users | string | — | 需要 |
exec_rule_window_secs | int 5–3600 | 120 | 需要 |
exec_rules_isolation_optin | bool | false | 需要 |
honeytokens_enabled | bool | true | 需要 |
honeytokens_home | abs path | /root | 需要 |
harden_userns_enabled | bool | true | 需要 |
dns_tunnel_entropy_allowlist | list | — | 需要 |
fleet_forwarding_allowed_hosts | list | — | 需要 |
fleet_forwarding_allow_plaintext | bool | false | 需要 |
18.2 guard.yaml(37 个键)
| 键 | 类型 / 可取值 | 默认 | 重启 |
|---|---|---|---|
sensor.type | falco | tetragon | falco | 需要 |
sensor.transport | string | unix_socket | 需要 |
sensor.socket_path | abs path | /run/roamswitch/events.sock | 需要 |
sensor.reconnect_interval_ms | int 100–3600000 | 1000 | 需要 |
policies.on_warning.action | log_and_notify | isolate_process | isolate_host_all | log_and_notify | 需要 |
policies.on_warning.server_action | log_and_notify | isolate_process | isolate_host_all | — | 需要 |
policies.on_warning.kill_process | bool | false | 需要 |
policies.on_warning.freeze_process | bool | false | 需要 |
policies.on_warning.fallback_to_host_isolation_after_retries | int 0–1000 | — | 需要 |
policies.on_error.action | log_and_notify | isolate_process | isolate_host_all | isolate_process | 需要 |
policies.on_error.server_action | log_and_notify | isolate_process | isolate_host_all | — | 需要 |
policies.on_error.kill_process | bool | false | 需要 |
policies.on_error.freeze_process | bool | false | 需要 |
policies.on_error.fallback_to_host_isolation_after_retries | int 0–1000 | — | 需要 |
policies.on_critical.action | log_and_notify | isolate_process | isolate_host_all | isolate_process | 需要 |
policies.on_critical.server_action | log_and_notify | isolate_process | isolate_host_all | — | 需要 |
policies.on_critical.kill_process | bool | true | 需要 |
policies.on_critical.freeze_process | bool | false | 需要 |
policies.on_critical.fallback_to_host_isolation_after_retries | int 0–1000 | 3 | 需要 |
policies.on_emergency.action | log_and_notify | isolate_process | isolate_host_all | isolate_host_all | 需要 |
policies.on_emergency.server_action | log_and_notify | isolate_process | isolate_host_all | — | 需要 |
policies.on_emergency.kill_process | bool | true | 需要 |
policies.on_emergency.freeze_process | bool | false | 需要 |
policies.on_emergency.fallback_to_host_isolation_after_retries | int 0–1000 | — | 需要 |
server_safeguards.allow_established_connections | bool | true | 需要 |
server_safeguards.maintenance_ssh_ports | port list | 22 | 需要 |
server_safeguards.whitelist_cidrs | list | — | 需要 |
server_safeguards.whitelist_dns_servers | list | — | 需要 |
server_safeguards.safety_timer_secs | int ≥ 0 | 300 | 需要 |
adaptive_security.enabled | bool | true | 需要 |
adaptive_security.untrusted_network_actions.elevate_falco_profile | string | — | 需要 |
adaptive_security.untrusted_network_actions.auto_enable_userns_restriction | bool | false | 需要 |
investigation.enabled | bool | false | 需要 |
investigation.command | abs path | — | 需要 |
investigation.args | list | — | 需要 |
investigation.run_as | string | — | 需要 |
investigation.timeout_secs | int 1–86400 | 180 | 需要 |
19. Web Shell 与注入代码检测 (Web Rules)
此功能用于发现在 Web 服务器上运行的 PHP、JavaScript、JSP、ASP Web Shell 与注入代码、伪装成图片的 PHP 以及 .htaccess 滥用(自 Linux 1.11.23 起)。无需设置:它会从 Apache、nginx、Caddy 的配置中自动找到文档根目录,并在文件于其中写入并关闭后立即检查(使用仅通知的 fanotify,不会使其他进程停滞)。为弥补可能丢失的事件和启动前已存在的文件,每 6 小时还会定期扫描。不需要 ClamAV。
行为由 /etc/roamswitch/web_rules.conf 决定(root 所有,权限 0644;他人可写的文件会被忽略)。修改后用 sudo roamswitch server restart 使其生效。模板可用 roamswitch web-rules example conf 输出。
| 键 | 含义(默认值) |
|---|---|
quarantine | 自动隔离级别。off 不隔离,critical 仅隔离 critical,high 隔离 critical 与 high(默认:critical)。在 critical 级别下发现 high 时,只发送通知并触发钩子,不触碰文件。 |
account_action | 是否停用被检测文件所有者的登录 shell。off / revoke_shell(默认:off)。仅针对已隔离的 critical 或 high。不会处理 root、UID 低于 account_min_uid 的账户、已登录的用户以及守护进程自身。原 shell 会被记录,可用 sudo roamswitch web-rules unlock-account <用户> 恢复。 |
account_min_uid | account_min_uid:账户处置可作用的最小 UID(默认 1000,不能低于 100)。 |
scan_dirs, auto_docroots, watch, scan_interval_secs | scan_dirs(逗号分隔的绝对路径)、auto_docroots(默认 true;设为 false 则不再从 Web 服务器配置自动发现)、watch(默认 true;设为 false 则关闭写入监视,仅保留定期扫描)、scan_interval_secs(默认 21600,最小 3600)。 |
# /etc/roamswitch/web_rules.conf
quarantine=critical
account_action=off
account_min_uid=1000
#scan_dirs=/srv/sites/a,/srv/sites/b
auto_docroots=true
watch=true
scan_interval_secs=21600
检测到时会发送通知(Telegram、LINE、Webhook),通知中写明规则名称以及文件是已隔离还是保留。若只想查看而不移动任何文件,请使用 sudo roamswitch web-scan <文件夹>(仅检查;加 --quarantine 才隔离)或 Server TUI 的“工具”界面。自定义规则放在 /etc/roamswitch/rules.d/*.json;若要在检测后调用自己的脚本,请放在 /etc/roamswitch/hooks/post-detection(检测内容以 JSON 通过标准输入传入;仅执行 root 所有、他人不可写且父目录也安全的文件,不经过 shell,30 秒后终止)。规则更新并入每日只接收更新。关闭 cve_kernel_map_updates_enabled 后完全不进行网络通信,使用内置规则运行。