hexbytedev/hexwall

GitHub: hexbytedev/hexwall

hexwall 是一款服务器出站流量防护工具,通过监控并终止绕过 DNS 的直接 IP 可疑连接,弥补 Pi-hole 无法检测硬编码 IP 数据外泄的盲区。

Stars: 2 | Forks: 0

# hexwall [![Go Reference](https://pkg.go.dev/badge/github.com/hexbytedev/hexwall)][2] [![CodeQL Advanced](https://static.pigsec.cn/wp-content/uploads/repos/cas/73/73721a0ab8ff3d64fd3a57205cc9130f873189e35218be05a6bc861800c54d84.svg)][4] [![golangci-lint](https://static.pigsec.cn/wp-content/uploads/repos/cas/84/841fe7acbd916d699e3b66ef8be20360f2db9b9bcecfab1aad2fb806e45e1cd7.svg)][6] [![Dependency Review](https://static.pigsec.cn/wp-content/uploads/repos/cas/b1/b15cde1db1847f5f456bf59b25038725a992f13ef67da95748c5bbf4ed7fbab7.svg)][8] [![Dependency Graph](https://static.pigsec.cn/wp-content/uploads/repos/cas/1d/1dcf91009ccc659bee16e095fe751ec649c2dd0e55b4d98fd4caf0fb67b70a09.svg)][10] [![Dependabot Updates](https://static.pigsec.cn/wp-content/uploads/repos/cas/ab/ab0fb91c230209c3d027fa03649b629f10ebbab45d690ad212e1b8e38f31f910.svg)][12] [![Go](https://static.pigsec.cn/wp-content/uploads/repos/cas/31/31c96a8fe552af584b5983afc39ddf2e71ca6806185aad0eda564c77331e5b3e.svg)][14] `hexwall` 是一个出站数据外泄制动器,专为已受到 Pi-hole 保护的服务器设计。当供应链攻击、恶意软件植入或受损的软件包已经在机器内部运行时,它们可以完全绕过 DNS,直接将窃取的数据发送到硬编码的 IP 地址。Pi-hole 无法看到这一跳。`somo` 可以看到实时连接。`hexwall` 利用这种可见性来标记,并在 `enforce` 模式下,于数据离开服务器之前终止可疑的直接 IP 连接。 它会监控活动的网络连接,并终止任何连接到不受 Pi-hole 历史记录、内置本地允许列表或近期已建立的流量信任的 IP 的连接——从而填补了 Pi-hole 为直接 IP 连接留下的漏洞。 简而言之:`Pi-hole + somo + hexwall` 为您提供了一个针对受损后出站流量的实用遏制层,尤其是供应链攻击中偶尔使用的那种直接 IP 数据外泄行为。 ## 概览 | 情况 | 未使用 Pi-hole DNS 的服务器 | 仅使用 Pi-hole 的服务器 | 使用 Pi-hole + somo + hexwall 的服务器 | | --- | --- | --- | --- | | 通过 DNS 解析的恶意域名 | ❌ 暴露风险 | ✅ 可在 DNS 层拦截 | ✅ 可在 DNS 层拦截 | | 恶意软件直接连接到硬编码 IP | ❌ 暴露风险 | ❌ Pi-hole 无法察觉 | ✅ 直接 IP 连接可见且受检查 | | 识别哪个进程拥有该连接 | ❌ 无内置可见性 | ❌ 无进程可见性 | ✅ `somo` 显示 PID/程序 | | 受损后阻止出站数据外泄 | ❌ 无遏制层 | ⚠️ 仅在攻击者仍使用 DNS 时有效 | ✅ 可终止可疑的已建立连接 | | 绕过 DNS 的供应链恶意软件 | ❌ 高风险 | ❌ 仍存在暴露风险 | ✅ 更强的遏制能力 | | 整体出站防护态势 | ❌ 薄弱 | ⚠️ 部分 | 🛡️ 三者中最强 | ## 问题所在 Pi-hole 在 DNS 层运行。它可以拦截域名,但对完全绕过 DNS 的连接(即进程直接拨打硬编码 IP 地址)毫无可见性。恶意软件和受损的软件包通常利用这种技术进行回连,而不会触发任何基于 DNS 的拦截。 hexwall 基于相反的假设运作:**如果某个 IP 不受信任,则该连接默认为可疑。** 当 IP 符合内置的 CIDR 允许列表、在过去一小时内从 Pi-hole 查询历史记录中刷新过,或者在过去 60 秒内已被识别为已建立的连接时,即被授予信任。在启动时,它会立即从 Pi-hole 刷新受信任的 IP,然后每 30 秒刷新一次,同时每 10 秒扫描一次连接。任何不在此信任集合内的 IP 都会根据外部欺诈 API 进行检查,结果按每个 IP 在本地缓存 6 小时。确认的威胁将被记录(watch 模式)或终止(enforce 模式)。 ## 依赖项 | 依赖项 | 角色 | 必需 | | ----------------------------------------- | --------------------------------------------------- | --------------------------- | | [`somo`](https://github.com/theopfr/somo) | 列出已建立的 TCP/UDP 连接;按 PID 终止 | 必须位于 `$PATH` 中 | | Pi-hole FTL database | 受信任 DNS 历史记录的来源 | 自动检测或通过 `--db` | | `deghostapi.hexbyte.dev` | 针对未知 IP 的欺诈/威胁分类 | 需要网络访问权限 | **构建依赖:** `modernc.org/sqlite`(纯 Go 编写的 SQLite,无需 CGo 或系统 libsqlite3)。 ## 二进制文件与部署 CI 发布了适用于以下平台的轻量级 Linux 二进制文件: - `linux/amd64` - `linux/arm64` 您可以从该项目的 GitHub Releases 页面下载它们: - [GitHub Releases](https://github.com/hexbytedev/hexwall/releases) 对于生产环境的 Linux 服务器,请从 Releases 下载匹配的二进制文件,将其放置在您的部署期望的 `./app/hexwall` 路径下,并使用包含的演示 Docker Compose 文件 [`docker-compose.yml`](./docker-compose.yml) 在服务器上启动它。 对于 macOS 和 Windows,CI 不发布预构建的二进制文件。请克隆仓库并在部署前手动构建二进制文件。 ### 安装 `somo` 如果 `somo` 尚未在您的 `$PATH` 中,这是在 Ubuntu 或 Debian 上安装并验证它的最快方法: ``` curl https://sh.rustup.rs -sSf | sh sudo apt install build-essential cargo install somo sudo ln -s "$HOME/.cargo/bin/somo" /usr/local/bin/somo sudo somo ``` 这些步骤的作用: - 使用 `rustup` 安装 Rust 工具 - 安装构建 `somo` 所需的编译器工具链 - 使用 Cargo 构建并安装 `somo` - 通过 `/usr/local/bin` 在系统范围内公开 `somo` - 运行一次 `sudo somo` 以确认它可以查看到实时连接和 PID ## Pi-hole 数据库位置 自动检测按以下顺序进行: 1. `/etc/pihole/pihole-FTL.db` — 裸机 / 软件包安装 2. 名称或镜像包含 `pihole` 的正在运行的 Docker 容器,检查其 `/etc/pihole` 挂载点 如果两者均未找到,则启动失败。请使用 `--db` 手动指定路径。 ## CLI 标志 | 标志 | 默认值 | 描述 | | ------------ | ------------------- | ---------------------------------------------------------------- | | `--db` | _(自动检测)_ | `pihole-FTL.db` 的路径 | |`--hexwall-db`| `./hexwall.db` | 本地 hexwall 数据库的路径(首次运行时创建) | | `--mode` | `watch` | `watch` — 仅检测并记录;`enforce` — 检测、记录并终止 | | `--debug` | `false` | 启用详细的每个连接扫描日志 | `--mode` 接受 `watch` 或 `enforce`(不区分大小写)。任何其他值都会导致错误退出。 ### 调试日志 当启用 `--debug` 时,每个扫描周期会记录其遇到的每个连接及其状态: - **allowed** — IP 位于本地信任缓存或允许列表中 - **unrecognized-clean** — IP 不在缓存中,但欺诈 API 返回了干净的判定(或者针对私有/保留范围返回了 403) - **vulnerable** — IP 不在缓存中,欺诈 API 将其标记为滥用者/攻击者/威胁 当禁用调试时(默认),仅记录未被允许的结果。此外,空扫描(somo 返回零连接)无论调试模式如何都会被记录。 ## 连接何时被终止 只有当以下所有条件都满足时,连接才会被终止: 1. `--mode enforce` 处于活动状态。 2. `somo` 报告该连接当前已建立。 3. 远程 IP 不受信任。 4. 远程 IP 当前的欺诈判定(无论是实时获取的还是从本地 6 小时缓存中复用的)将其标记为 `is_abuser`、`is_attacker` 或 `is_threat`。 当以下任何一项成立时,该 IP 即被视为受信任: - 它匹配内置的 CIDR 允许列表。 - 它在过去一小时内从 Pi-hole 历史记录中刷新过。 - 它在过去 60 秒内已被视为已建立的允许连接。 在以下情况下,连接不会被终止: - `--mode watch` 处于活动状态。该连接仅被记录为 `would kill`。 - 该 IP 位于内置允许列表中。 - 该 IP 仍受近期 Pi-hole 刷新或近期已建立连接活动的信任。 - 该 IP 在过去 6 小时内已有缓存的干净欺诈判定。 - 欺诈 API 返回 HTTP `403`,这被视为干净/私有/保留地址。 - 欺诈 API 返回了报告,但 `is_abuser`、`is_attacker` 或 `is_threat` 均不为 true。 - 欺诈查询本身失败。 当确实发生终止时,hexwall 会首先在本地 `killed_connections` 审计表中记录该事件,然后要求 `somo` 终止拥有该连接的 PID。 ### 欺诈 API 缓存行为 欺诈查询按每个 IP 在本地 SQLite 数据库中缓存 6 小时。 - 如果不受信任的 IP 具有未超过 6 小时的缓存欺诈判定,hexwall 将复用该缓存结果,并再次调用欺诈 API。 - 如果缓存的判定超过 6 小时,下一次扫描将再次调用欺诈 API 并刷新缓存时间戳。 - 干净和值得终止的欺诈判定都会被缓存。 - HTTP `403` 响应被视为干净/私有/保留地址,并作为不予终止的结果进行缓存。 - 欺诈查询失败不会被缓存。 此缓存作为 Unix 时间戳(`INTEGER` / int64 风格的秒数)存在于本地 hexwall 数据库中,并在重启后依然保留。 ## Pi-hole 历史记录如何成为受信任的 IP Pi-hole 信任基于近期的 DNS 历史记录建立,而不是直接信任当前每一个连接。刷新流程如下: 1. 从 `pihole-FTL.db` 读取 `queries` 表。 2. 选择过去一小时内出现的、非空的不同域名。 3. 将它们统一转换为小写并去除首尾空格。 4. 通过系统 DNS 解析器解析每个域名,限制并发数,并为每个域名设置 1 秒的查询超时。 5. 忽略无法解析的域名。这对于被拦截的域名、过期的记录及类似情况是正常的。 6. 对已解析的 IP 进行去重。如果多个域名解析到同一个 IP,则按排序顺序为该 IP 存储第一个域名。 7. 将每个解析出的 IP 插入或更新到本地 `allowed_ips` 表中,设置或刷新 `last_refreshed`。 重要细节: - 启动时会在首次监控扫描之前立即执行此刷新,以便在开始强制执行之前,近期的 Pi-hole 历史记录中出现的正常流量就已经获得信任。 - 刷新每 30 秒重复一次。 - 来自 Pi-hole 刷新的信任自最近一次成功的插入或更新起持续 1 小时。 - 现有行在刷新时会保留其原始的 `first_approved` 和 `last_established` 值;仅更新存储的域名和 `last_refreshed` 时间戳。 - 监控器还会在发现已受信任的连接依然处于已建立状态时更新 `last_established`,从而为长期允许的连接延长信任期。 这意味着,只有在以下所有情况发生后,IP 才会从 Pi-hole 历史记录中获得信任: 1. Pi-hole 在过去一小时内记录了针对它的域名查询。 2. 该域名在刷新周期内仍然可以解析。 3. 某个解析出的 IP 已被写入本地 `allowed_ips` 缓存中。 如果在 Pi-hole 历史记录中出现过某个域名,但在刷新期间不再解析,则不会为其添加任何 IP,因此单凭该域名无法产生任何信任。
标签:EVTX分析, 日志审计, 请求拦截