secu-tools/unifi-network-list-sync

GitHub: secu-tools/unifi-network-list-sync

一款纯 Bash/Zsh 脚本工具,用于将 UniFi Network 防火墙地址组与 Firehol、Spamhaus 等公共 IP 黑名单自动同步,实现轻量化的网络威胁拦截。

Stars: 2 | Forks: 0

# unifi-network-list-sync [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/secu-tools/unifi-network-list-sync/actions/workflows/ci.yml) Bash 脚本,用于保持 UniFi Network 防火墙地址组(UI 中的“network lists”)与公共 IP 黑名单/白名单同步,并附带了一些小型的设备管理辅助工具。直接与 UniFi Network 控制器的本地 REST API 通信 - 无需云依赖,无需 Python,仅需 bash 和 标准 Unix 工具。 已在 UniFi Network 10 版本上测试。较早或未来的主要版本 可能具有不同的本地 API。
功能 - 仅限 API key 身份验证 - 无需用户名/密码,无需 Ubiquiti 云帐户。 - 读取纯文本源(每行一个 IP/CIDR)和 JSON 源(AWS、GCP)。 - 处理单个 IP 和 CIDR 范围,支持 IPv4 和 IPv6。 - 无损压缩:将相邻/重叠的条目合并为 涵盖完全相同地址的最小 CIDR 块。 - 在上传前对每个列表进行去重和规范化。 - 大列表自动拆分到多个防火墙组中。 - 高效的重新同步:每个列表每次运行最多获取和处理一次 (而不是每个站点一次),报告无更改的源(HTTP Last-Modified/ETag)将被完全跳过,而发生了更改但仍 压缩为与以前完全相同的地址集的源永远不会 被重新推送到路由器。运行之间不会在磁盘上保留任何列表内容 - 只有每个列表的小时间戳/哈希会保留(参见 下文的 `config/.lists_state.json`)。 - 多站点:一次运行即可同步每个已配置的 UniFi 站点。 - 在 bash 或 zsh 下运行;支持 Linux、macOS、WSL 或 UniFi OS 控制台。 - 安全防护:配置验证、运行锁,损坏/空的源或 推送失败绝不会清除您现有的组。
## 免责声明 本项目面向在将 bash 脚本指向自己的网络设备之前, 习惯于先阅读脚本的高级用户。 示例配置指向由第三方发布的列表。本 仓库仅提供其 URL - 数据由*您*在运行时下载, 并受各提供商自身条款的约束。请审查它们并 仅保留您想要的条目。 ## 要求 您需要在任何可以访问控制器的 Linux/macOS/WSL 主机(或 UniFi OS 控制台)上安装 `bash` 或 `zsh`、`curl`、`jq` 和 `awk`。每个脚本 都会在启动时检查这些依赖,并提示任何缺失的内容。
常见发行版的安装命令 - Debian / Ubuntu / WSL: `sudo apt-get update && sudo apt-get install -y curl jq` - Fedora / RHEL / CentOS 8+: `sudo dnf install -y jq` - RHEL / CentOS 7 (已 EOL,如果可以请升级): `sudo yum install -y epel-release && sudo yum install -y jq` - 在 7 中,`jq` 从未出现在基础/额外仓库中,仅存在于 EPEL 中 - Arch Linux: `sudo pacman -S --needed jq` - Alpine Linux: `sudo apk add bash curl jq` (Alpine 的默认 shell 是 `ash`,而不是 bash 或 zsh,因此这里确实需要安装 `bash`) - openSUSE: `sudo zypper install -y jq findutils` (其最小镜像缺失 `find`) - macOS (Homebrew): `brew install jq` 在原装 UniFi OS 控制台上(已用 Dream Machine 确认),`bash`、 `curl`、`awk` 和 `jq` 都已存在 - 无需额外安装。
## 安装说明 1. 在 UniFi Network 应用程序中创建 API key:打开 Integrations 面板(插头图标,位于左侧边栏底部),点击 “Create New API Key”,并立即复制 - 它仅显示一次。 这是本地控制器 key;Ubiquiti/UI.com 云登录在此处不 起作用。 ![在 UniFi Network 应用程序中查找 Integrations 并创建新 API key 的位置](https://static.pigsec.cn/wp-content/uploads/repos/cas/3c/3c5a628531cd66a35fe8053b4d0a5b5ada490d42d47baca8ccc52fcac9ae62f7.jpg) 2. 复制站点模板并填写 `name`、`host` 和 `api_key` (参见[配置](#configuration)): cp docs/examples/site.example.json config/sites/my-site.json 3. 选择适合您使用场景的列表注册表(参见 [列表](#lists-configlistsjson))并复制它: cp docs/examples/lists.outgoing.example.json config/lists.json 4. 检查配置:`./validate_config.sh` 5. 检查连通性:`./get_firewall_groups.sh my-site` 应该以 JSON 格式打印 您的控制器当前的防火墙组。 6. 运行它:`./sync_lists.sh`(或对单个站点使用 `./sync_lists.sh my-site`)。 这些组将显示在 Settings -> Firewall & Security -> Firewall Groups 下。 7. 安排定时任务(参见[定时任务](#scheduling)),然后创建使用这些组的防火墙 规则。 ## 配置 整个 `config/` 文件夹都是真实的、每次安装专属的数据,并且被 git-ignored(站点文件包含有效的 API key)。模板位于 [docs/examples/](docs/examples/)。 ### 站点 (`config/sites/*.json`) 每个 UniFi 站点/控制器一个文件,从 [docs/examples/site.example.json](docs/examples/site.example.json) 复制而来: - `name` - 您选择的标签;在您的站点文件中必须是唯一的,也是您在 命令行中传递的名称。 - `host` - 包含协议的控制器基础 URL,例如 `https://192.168.1.1`。 - `api_key` - 来自安装步骤 1 的 API key。 - `verify_cert` - 设为 `true` 以验证控制器的 TLS 证书。 默认为 `false`(大多数本地控制器是自签名的)。 - `group_max_members` - 每个防火墙组的最大成员数,默认为 `5000`。 调低是安全的;不建议调高 - 如果使用更大的组,UniFi 网关可能会 变得缓慢或不稳定。 在采用本项目之前已经(手动或使用其他工具)创建了防火墙组?请运行 `./sync_groups.sh my-site` 而不是从 `"groups": {}` 开始 - 它会在控制器上查找名称和类型与 `config/lists.json` 中某个类别所期望的相匹配的组(例如 `"Firehol Level 1-1"`、`"Firehol Level 1-2"` 等),并将它们注册到您的 `groups` 中,这样 `sync_lists.sh` 就会复用它们,而不会在旁边创建重复的组。请参阅 [sync_groups.sh](#scripts)。 ### 列表 (`config/lists.json`) 要同步的列表注册表,由所有站点共享。包含两个随时可用的 模板 - 选择适合您使用场景的模板,或者将它们合并(它们故意有重叠的部分;最终请选择适合您使用情况的配置): - [docs/examples/lists.outgoing.example.json](docs/examples/lists.outgoing.example.json) - 家庭使用。阻止前往已知恶意目的地的**出站**流量(LAN 到 WAN):网络钓鱼网站、僵尸网络命令和控制服务器、 恶意软件基础设施。 - [docs/examples/lists.incoming.example.json](docs/examples/lists.incoming.example.json) - 用于托管服务器(Web、游戏、邮件等)。阻止来自已知攻击源的**入站**流量(WAN 到服务器):扫描器、暴力破解者、 蜜罐标记的 IP。还包括 Cloudflare/AWS/GCP 范围,可用作白名单。 既公开托管内容,又希望为局域网的其他部分提供出站保护?将两个文件的 `lists` 数组合并到一个 `config/lists.json` 中(删除重复的 `category` 条目 - 有一些是故意重叠的),并创建两个防火墙规则,每个方向一个。 每个条目: ``` { "category": "firehol_level1", "display_name": "Firehol Level 1", "name_template": "Firehol Level 1-%d", "group_type": "address-group", "min_groups": 2, "url": "https://iplists.firehol.org/files/firehol_level1.netset", "preprocess": "cut -d' ' -f1" } ``` - `category` - 内部键,在整个文件中唯一。 - `display_name` - 显示在日志输出中。 - `name_template` - UniFi 组名;`%d` 用于为多个组编号(例如 `"Firehol Level 1-%d"` -> `"Firehol Level 1-1"`、`"-2"` 等)。没有 `%d` 的模板(例如 `"Firehol Webserver"`)将按原样用于第一个组;如果列表的数量超过了单个组的容量限制,溢出的部分会自动获得名为 `"Firehol Webserver 2"`、`"Firehol Webserver 3"` 等的独立组 - 任何内容都不会被静默丢弃或强行塞入过大的组中,也无需更改配置。 - `group_type` - `"address-group"` (IPv4) 或 `"ipv6-address-group"` (IPv6)。 - `min_groups` - 始终保持至少注册了这么多数量的组,即使列表当前可以容纳在更少的组中。 - `url` - 用于下载列表的 http(s) URL。 - `preprocess` - 用于纯文本源的可选文本过滤器,例如 `cut -d' ' -f1` 用于保留第一个字段。它不是自由格式的 shell 命令: 仅允许使用 `cat`、`cut`、`grep`、`tr`、`head`、`tail`(通过 `|` 链接),并且会直接拒绝 shell 元字符。制表符分隔的源(例如 DShield 的 `block.txt`,见下文)可以使用 `cut` 和 `tr` 将两列合并为一个 `ip/prefix` 值,在 JSON 中将制表符写为字面量 `\t` 转义:`"cut -f1,3 | tr '\t' '/'"`。 - `json_path` - 用于 JSON 源而不是 `preprocess`(两者是 互斥的):以点分隔的字段名称,告诉同步程序 地址嵌套的位置,例如 AWS 的 `ip-ranges.json` 对应 `"prefixes.ip_prefix"`。系统会自动遍历每一级的数组,并且 会跳过缺少该字段的条目,因此无论某个级别 是对象、对象数组还是字符串数组,只要一个路径就可以起作用。 有关这两个模板中每个条目的实际用途(来源、大小、建议的方向),请参阅[示例列表](#example-lists)。 ### 日志 (`config/logging.json`, 可选) 将 [docs/examples/logging.example.json](docs/examples/logging.example.json) 复制到 `config/logging.json` 并设置 `"enabled": true`,即可将每次 运行的输出同时写入带有时间戳的日志文件中。字段包括: `log_dir` (默认 `logs`)、`log_file_prefix` (默认 `update`) 和 `keep_runs` (保留多少个旧的运行日志;较早的日志会自动清理)。如果没有此文件,输出仅会发送到 stdout/stderr。 ## 示例列表
每个示例列表是什么? `Recommendation`: `Server` = 在您的入站规则上阻止 (WAN 到 server); `Outgoing` = 在您的出站规则上阻止 (LAN 到 WAN); `Both` = 两者皆可,出现在两个示例文件中; `Allowlist` = 允许规则素材,不作为阻止对象; `(optional)` = 优先级较低(设计上更宽泛/噪音更大,或者不是实际的攻击证据,例如 Tor、bogons)- 将其视为额外的一层,而不是必需品。“Size”是指经过本项目自身的去重/合并后的大致 CIDR 块数量。 **注意:** Cloudflare、AWS 和 GCP 条目(下方的 `Allowlist`)是 主要云/CDN 提供商的地址空间,而不是攻击者 IP。直接阻止 它们将中断访问许多由相同提供商托管的不相关的合法服务。 **警告:** 一些条目非常庞大(数万个 地址),特别是 `firehol_level2`/`firehol_level3`、`blocklist_de_all` 和 `cins_army`。庞大的组可能会减慢 UniFi 网关的速度 - 请先在非关键站点上测试,并保留控制器配置的备份。 | 类别 | 建议 | 描述 | 大小 | | --- | --- | --- | --- | | `emerging_threats_compromised` | Both | 已确认被入侵或具有其他恶意行为的主机 | 小 | | `firehol_level1` | Both | 最高可信度的汇总(包含 Spamhaus DROP、DShield 等) | ~小 | | `ipsum_level5` | Both | 从许多公共黑名单中汇总;至少被 5 个独立来源标记 | 小 | | `spamhaus_drop` | Both | 完全处于犯罪控制之下的网络块("Don't Route Or Peer");几乎零误报 | 小 | | `spamhaus_dropv6` | Both | Spamhaus DROP 的 IPv6 对应版本 | 极小 | | `binarydefense_artillery` | Server | 来自蜜罐/传感器网络的威胁情报封禁列表 | 小 | |blocklist_de_all` | Server | 实时滥用报告(SSH、邮件、Web、FTP、IRC 等),fail2ban 风格 | 大 | | `cins_army` | Server | 信任度较差且尚未在其他地方被标记的 IP | 大 | | `dshield_top_attackers` | Server | 过去 3 天内攻击次数排名前 20 的 /24 子网 | 极小 | | `firehol_level2` | Server | 比 Level 1 更广泛的汇总;来源更多,但仍然经过筛选 | 中 | | `firehol_level3` | Server (可选) | 最广泛的汇总级别;数量庞大、可信度较低的来源 - 请参阅上面的警告 | 大 | | `firehol_webserver` | Server | 专门被发现用于攻击 Web 服务器的 IP | 小 | | `greensnow` | Server | 针对暴力破解/扫描的社区蜜罐网络报告 | 小 | | `team_cymru_bogons_ipv4` | Server (可选) | 未分配/保留的 IPv4 空间,永远不应作为合法来源 | 小 | | `tor_exit_nodes` | Server (可选) | 活跃的 Tor 出口节点 - 匿名流量,本质上不具有恶意 | 小 | | `feodo_tracker` | Outgoing | 活跃的僵尸网络命令和控制服务器 | 极小 | | `firehol_webclient` | Outgoing | 专门被发现用于攻击客户端软件(浏览器等)的 IP | 极小 | | `phishing_database` | Outgoing | 活跃的钓鱼网站托管 IP,不断进行重新验证 | 中 | | `aws_ipv4` / `aws_ipv6` | Allowlist | Amazon 官方发布的 IP 范围 | 小 | | `cloudflare_ipv4` / `cloudflare_ipv6` | Allowlist | Cloudflare 官方发布的边缘 IP 范围 | 极小 | | `gcp_ipv4` / `gcp_ipv6` | Allowlist | Google 官方发布的 IP 范围 | 小 |
## 脚本 - `sync_lists.sh [site_name] [--force]` - 将每个列表与每个 已配置的站点进行同步,或者如果指定了名称,则仅同步一个站点。这是您需要 安排定时任务的脚本。正常运行会跳过重新获取未发生更改的源,并在实际没有任何更改时跳过 向路由器的重新推送; `--force` 会绕过这两者并无条件地重新同步所有内容(参见 [什么是 config/.lists_state.json?](#what-is-configlists_statejson))。 - `sync_groups.sh ` - 根据名称和类型将已存在的 UniFi 防火墙 组与 `config/lists.json` 类别进行匹配,并将其 注册到该站点的 `groups` 字段中。适用于在已经设置了组的控制器上采用本项目的 管理员 - 参见 [站点](#sites-configsitesjson)。可随时安全地重新运行;它本身绝不会创建、 重命名或删除组。 - `get_firewall_groups.sh ` - 以 JSON 格式转储站点的防火墙组。 - `get_device_list.sh ` - 以 JSON 格式转储站点的设备清单。 - `set_device_disabled.sh ` - 启用 或禁用 UniFi 设备。 - `validate_config.sh` - 使用具体的错误 消息验证每个配置文件。 上述每个接受 `site_name` 的脚本都要求提供该参数,除了 `sync_lists.sh`,如果省略它会同步每个已配置的站点(或者如果指定了名称,则仅同步一个站点)- 每个站点都是不同的,因此绝不会有任何操作在未被要求的情况下静默地 为您选择一个或对所有站点采取行动。 ## 定时任务 将 cron (Linux/macOS/WSL) 或 Task Scheduler (Windows) 指向不带参数的 `sync_lists.sh`,例如每天 03:00 运行一次: ``` 0 3 * * * /path/to/sync_lists.sh ``` 每天一次就足够了 - 这些列表不会每时每刻都在变化,并且 某些提供商会限制过于频繁的获取器的速率。锁机制可以防止重叠运行,如果出现任何失败,脚本将以非零状态退出,以便您的 调度程序可以对失败的运行发出警报。 ## 常见问题
组创建失败,提示名称冲突 UniFi 不允许同一站点上的两个防火墙组共用一个名称。 如果本项目想要创建的组(例如 "Firehol Level 1-2")已经 存在于控制器上 - 手动创建的,或者是由其他设置遗留下来的 - 运行将打印: ``` [ERROR] Could not create firewall group 'Firehol Level 1-2': a group with this name already exists on the UniFi controller (api.err.FirewallGroupExisted) ``` 在 UniFi Network 应用程序中,转到 Settings -> Firewall & Security -> Firewall Groups 并重命名或删除冲突的组,然后重新运行 `sync_lists.sh` - 它会从之前中断的地方继续。
同步后 UniFi 显示 "Gateway Configuration Failed" 在使用默认的 `group_max_members` 为 5000 的情况下不应该发生这种情况 - 特别是因为列表在上传前已经过 CIDR 压缩。如果您看到了这个错误,很可能是您调高了 `group_max_members` 或者您的网关面临 内存压力:将 `group_max_members` 调回 5000(或更低),禁用 最大的列表,然后重新运行。
我的组包含的条目少于源列表 这是预期的。在上传之前,条目会被去重并无损地合并为 CIDR 块(例如,两个相邻的 /25 网络会合并为一个 /24)。该组涵盖的地址与源列表完全相同 - 不多也不少 - 这只是 一种更紧凑的表示形式,可以使网关保持快速运行。
我的 Ubiquiti 帐户登录无法用作 api_key `api_key` 是在 UniFi Network 应用程序的 Integrations 面板中创建的**本地控制器 API key**(参见安装步骤 1)。Ubiquiti 云(UI.com / SSO)凭据完全是两码事,将无法使用。
为什么我会看到文件权限警告? `config/` 保存着有效的 API key,因此如果 `config/` 或脚本本身可以被其他用户访问,每个脚本都会发出警告(但不会停止)。使用 `chmod -R go-rwx config` 和 `chmod -R go-wx` 修复指定的路径。在没有 POSIX 权限的文件系统(例如 Windows/NTFS)上会跳过此检查,因为在那儿是毫无意义的。
什么是 config/.lists_state.json? 用于每个列表的记录(HTTP Last-Modified/ETag 加上内容校验和),以便 重新运行时可以跳过未更改的源,并在结果与上次相同时跳过 向路由器的重新推送。绝不会保留任何实际的列表 内容,仅保留这些小的验证器/哈希值。 随时可以安全删除 - 下次运行时会重建它并 完全重新同步所有内容,就像使用 `--force` 一样(参见[脚本](#scripts))。这同样适用于新添加的站点,或者是错过了更新需要立即同步的站点。它像 `config/` 的其余部分一样被 git-ignored。
我可以比每天更频繁地同步吗? 一些提供商会对频繁的获取器进行速率限制或封禁(例如,Spamhaus 要求自动下载之间至少间隔一小时)。对于这些源,每天同步一次就足够了;在缩短时间间隔之前,请检查每个提供商的条款。
## 开发 测试套件是纯 bash 的 - 除了已经 需要的工具之外没有其他框架。从项目根目录运行它: ``` bash tests/run_all.sh ``` 或单个文件:`bash tests/run_all.sh test_cidr_merge.sh` 要在 zsh 而不是 bash 下运行相同的套件: `TEST_SHELL=zsh bash tests/run_all.sh` 测试针对本地固件和模拟的 API 调用运行 - 不会触碰真实的网络或 控制器。`.github/workflows/ci.yml` 会在每次推送和 pull request 时在 Ubuntu 和 macOS 上运行套件以及 shellcheck;macOS 作业也会在 zsh 下重新运行套件。 ## 许可证 MIT License - 见 [LICENSE](LICENSE)。 Copyright (c) 2026 Jack L. (Cpt-JackL) (https://jack-l.com) GitHub Repository: https://github.com/secu-tools/unifi-network-list-sync
标签:Bash, certspotter, Docker 部署, IP黑名单, UniFi, 应用安全, 数字取证, 网络设备管理, 网络运维, 自动化脚本, 防火墙策略