Files
tamabox/outputs/edgeone-origin-whitelist-guide.md
T
2026-09-08 10:10:44 +08:00

300 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# EdgeOne 回源端口 iptables 白名单自动维护指南
> **交付物**:`scripts/edgeone-origin-whitelist.sh`(Linux / iptables + ipset)
> **适用**:TamaBox 源站对公网开放了 EO 回源端口(免备案双线架构的 EO 分支),想把「开放 IP + 端口」收敛为「仅 EdgeOne 可访问」。
> **更新日期**:2026-09-03
---
## 0. 先泼一盆冷水:官方清单是付费功能
你要的「定期获取 EdgeOne IP 列表」——**免费套餐下官方不提供可程序化获取的渠道**:
| 事实 | 说明 |
|---|---|
| 官方唯一清单来源 = 「源站防护 / Origin ACL」 | EO 控制台 → 安全防护 → 源站防护,启用后展示回源 IPv4/IPv6 网段,变更前 14/7/3/1 天通知 |
| **仅付费套餐支持** | 官方文档明确「origin protection 仅支持绑定付费套餐的站点,free edition 不支持」(腾讯文档 1552/76086)——这就是你在控制台找不到入口的原因 |
| 自动化 API 存在但同样付费 | `DescribeOriginACL`(teo.intl.tencentcloudapi.com,国际站),每 3 天低频轮询,`NextOriginACL` 非空即同步并调确认接口(文档 71118) |
**所以本脚本的现实定位**:不依赖官方 API,以「本地/远程 CIDR 清单」为骨架,免费档立刻可用;日后升级付费套餐,只需把 API 拉取结果落到清单文件,脚本零改动继续工作(见 §7)。
> ### 🔴 0.1 2026-09-03 实测定稿:免费档请直接用「段级放行」,IP 级采种已证明不可行
>
> 曾设计 learn/auto 从连接表逐个收集 EO 出口 IP 作白名单,**服务器实测(tcpdump 取证)否决了该路线**:
>
> - EO 回源出口是**节点池,规模数百上千**,且**同一时刻在多段轮换**——一次 23 秒采样就看到 13 个不同出口(分布在 `43.146/43.152/43.168/43.174/43.175` 五段),其中 9 个因不在白名单被 REJECT → 表现为境内**间歇性 521**(EO 边缘回源失败)。
> - 结论:**IP 级清单 = 打地鼠**,永远追不上。免费档唯一现实可行方案是**按腾讯云大段放行**(脚本原生支持 CIDR,无需任何改动):
>
> ```bash
> # /etc/edgeone-origin/cidr.txt(2026-09-03 实测生效配置,521 消失)
> 43.146.0.0/16
> 43.152.0.0/16
> 43.168.0.0/16
> 43.174.0.0/16
> 43.175.0.0/16
> ```
>
> - learn/auto 在段级模式下**降级为「新段预警」辅助**:auto 采到清单外的 IP → `whois` 反查归属 → 若属腾讯云 EO 新段,把整段 `/16` 补进清单。首次采种不再需要 learn(§3.1 的逐 IP 采种流程整体退役)。
> - 代价说明:`/16` 段内任意腾讯云主机可直连该回源端口(比全开安全、比精确 ACL 松),对 TamaBox 这类自身带鉴权的应用风险中等、可接受(§8)。
---
## 1. 脚本做什么
把回源端口从「全网开放」收敛为白名单:
```
INPUT 链(默认仅 2 条规则,其余不动)
├─ -p tcp --dport 18080 -i lo -> ACCEPT ← cloudflared Tunnel 本机回连(回环放行)
└─ -p tcp --dport 18080 -i eth0 -> EO_IN ← 仅公网网卡 eth0 入站才判定
EO_IN 自建链(一次性建立,内容恒定)
├─ EO_ORIGIN 集合命中 -> ACCEPT ← EdgeOne 回源(白名单)
└─ 其余 -> REJECT tcp-reset ← 直连者秒断
(网卡名不是 eth0 请改脚本头 EO_IFACE;留空=不限网卡,回环由 EO_IN 内 127.0.0.0/8 兜底)
```
**两个关键设计,保证不会把自己锁死、不会误伤 CF 分支:**
1. **更新零窗口**:白名单存于 ipset 集合 `EO_ORIGIN`,更新走「临时集合填充 → `ipset swap` 原子替换 → 销毁临时集」;iptables 规则永远引用同名集合、从不重建。同步过程中不存在「规则被清空」的瞬间。
2. **cloudflared 不受影响**:Tunnel 是 cloudflared 从本机 `127.0.0.1` 连回源端口,INPUT 里有独立的 `-i lo` ACCEPT 放行回环(与 eth0 网卡限定解耦),所以**即使防火墙收紧,CF 分支依然正常工作**——这正是裸 iptables 白名单最容易踩的坑(有人把 18080 一刀切 REJECT,Tunnel 分支跟着全挂)。
其余保障:清单为空/拉取失败/全非法 → **拒绝更新、保留现网规则**(fail-safe);支持 `--dry-run` 演练;全程日志 `/var/log/eo-origin-whitelist.log`。
---
## 2. 部署前确认两件事
1. **确认你的实际回源端口**(脚本默认 `18080`,务必改成实际值):
```bash
ss -tlnp | grep -iE 'tamabox|LISTEN' # 找 TamaBox 监听的端口
```
或看 `conf/app.ini` 的 `[server] port`。改脚本文件头 `EO_PORT=18080` 那一行,或运行时 `sudo EO_PORT=xxxx ./edgeone-origin-whitelist.sh sync`。
2. **确认系统有 ipset**(白名单原子替换依赖它):
```bash
command -v ipset || sudo apt-get install -y ipset # Debian/Ubuntu
command -v ipset || sudo dnf install -y ipset # RHEL/CentOS/Fedora
```
3. **确认公网网卡名**(脚本默认只对 `eth0` 生效):
```bash
ip route get 1.1.1.1 | awk '{print $5; exit}' # 输出如 eth0 / ens3 / enp1s0
```
输出不是 `eth0` 时,改脚本头 `EO_IFACE=` 或运行时覆盖:`sudo EO_IFACE=ens3 ./edgeone-origin-whitelist.sh sync`。⚠️ 网卡名不对 → 跳转规则永不命中 → 端口等于裸奔。
---
## 3. 初始化:生成种子清单(免费档必经步骤)
> 🔴 **先读 §0.1(实测定稿)**:免费档**不要**再做逐 IP 采种(EO 出口是海量轮换节点池,IP 级白名单实测导致间歇 521)。直接按 §0.1 的五个 `/16` 段建清单即可;learn/auto 只留作「新段预警」,见 §3.1 末注。
### 3.1 首选:腾讯云大段放行(实测定稿)
按 §0.1 直接放行 EO 所属的腾讯云大段(免费档没有官方回源段 API,而逐 IP 采种已被实测否决)。清单内容(2026-09-03 服务器实测生效):
```bash
sudo mkdir -p /etc/edgeone-origin
# 每行一个 CIDR,# 开头为注释。以下为实测生效配置:
sudo tee /etc/edgeone-origin/cidr.txt >/dev/null <<'EOF'
# EdgeOne 回源段(腾讯云,2026-09-03 实测;境内 EO 分支访问验证正常)
43.146.0.0/16
43.152.0.0/16
43.168.0.0/16
43.174.0.0/16
43.175.0.0/16
EOF
```
> 段怎么来的:用 `auto`(或 tcpdump 抓 SYN)持续观察回源出口,把出现过的 IP 归并到所属腾讯云段(whois 查 ASN/归属确认),整段放行。上面五个 `/16` 来自多次实测采样(新加坡/腾讯云 EO 出口遍布这些段)。
>
> 🔴 段不是永恒不变的:EO 可能启用清单外的新段(罕见、以月计)。每月跑一次 `auto` 看采样里有没有清单外的 IP,`whois <IP>` 确认属腾讯云 EO 后把整段补进 cidr.txt 再 sync(详见 §4.4)。
### 3.2 备选:向 EO 索取回源段
免费档控制台无源站防护页面,但可尝试**提交工单索取当前回源 IP 段**(客服可能以"尽力提供"形式给出);拿到后把 CIDR 逐行放进清单文件即可,效果等同。日后升级付费套餐启用源站防护,官方段可直接用(§7)。
### 3.3 首次启用(务必先 dry-run)
```bash
# 1. 建清单文件
sudo mkdir -p /etc/edgeone-origin
sudo cp 你整理好的清单 /etc/edgeone-origin/cidr.txt # 每行一个 IP/CIDR,# 注释
# 2. 演练:只看将要执行的动作,不触碰系统
sudo ./edgeone-origin-whitelist.sh sync --dry-run
# 3. 正式启用
sudo ./edgeone-origin-whitelist.sh sync
```
启用后**立刻验证**(另开一个 SSH 或终端,别把自己关外面):
```bash
# 服务器本机验证(必须能通:Tunnel 回环放行)
curl -sI --max-time 5 http://127.0.0.1:18080/ | head -3
# 从一台"与 EO 无关"的外网机器验证(必须被拒)
nc -zv <你的服务器IP> 18080 # 期望 refused 或超时
# 经 EO 访问验证(必须能通)
# ⚠️ 必须用【境内网络】访问——境外访问会被 DNSPod 解析到 CF 分支(走 Tunnel 回环),
# 验证到的是 CF 而不是 EO!境内手机 4G 访问最方便
curl -sI --max-time 10 https://box.tama.guru/ | head -3
```
### 3.4 撤掉原来的全局放行
若你之前是用 ufw 放行的(`ufw allow 18080`),现在它已冗余(脚本的 REJECT 会先于它拦截非白名单),建议删掉避免困惑:
```bash
sudo ufw delete allow 18080 # 脚本 sync 时也会检测并提示
```
> ⚠️ 云厂商安全组若也是「全放行 18080」:本机 iptables 白名单已是最终防线,直连者到本机 INPUT 即被拒,**不强制改安全组**;但建议安全组层面至少把 18080 的源收敛为 EO 大段(升级付费拿到官方段后可做),纵深更稳。
---
## 4. 定时维护(核心诉求)
### 4.1 清单会变吗?
EO 官方口径:**回源网段平均 3–6 个月更新一次**,变更前 14/7/3/1 天通知。付费档启用源站防护后段变更有 API 兜底(§7);免费档请按 §7 的「工单/客服」或定期(建议每月一次)核对。
### 4.2 用 cron(最简单)
段级模式(§0.1)下清单是 5 个 `/16` 段、几乎不变 → 定时只需**周期 sync 兜底 + 月度人工归段核对**,auto 不再作日常主线(改为按需「新段预警」观测,见 4.4):
```bash
sudo crontab -e
# ★ 推荐:每 3 天一次 sync(全量重建:清单里没有的旧条目自动剔除)
17 4 */3 * * /usr/local/bin/edgeone-origin-whitelist.sh sync >> /var/log/eo-origin-whitelist.log 2>&1
# 想更保险可改每天:把 */3 改成 *(即 17 4 * * * …)——sync 开销秒级,零负担
# 新段预警观测(不设定时,需要时手动跑):
# sudo /usr/local/bin/edgeone-origin-whitelist.sh auto
# 输出「新增」→ whois 确认属腾讯云新段 → 整段写进 cidr.txt → 再 sync
```
把脚本放到固定路径:`sudo cp scripts/edgeone-origin-whitelist.sh /usr/local/bin/ && sudo chmod +x /usr/local/bin/edgeone-origin-whitelist.sh`
### 4.3 用 systemd timer(更规范,推荐)
`/etc/systemd/system/eo-origin-whitelist.service`:
```ini
[Unit]
Description=Sync EdgeOne origin whitelist
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/edgeone-origin-whitelist.sh sync
```
推荐**每 3 天一次**(段级清单近乎静态,sync 纯兜底):
```ini
# A. 日历式(推荐):每月 1/4/7/10/13/16/19/22/25/28/31 号 04:17
# /etc/systemd/system/eo-origin-whitelist.timer
[Unit]
Description=EdgeOne origin whitelist sync (3-day)
[Timer]
OnCalendar=*-*-1/3 04:17:00
Persistent=true
[Install]
WantedBy=timers.target
```
> 想每天跑:`OnCalendar=*-*-* 04:17:00`;想严格 72h 间隔:`OnBootSec=5min` + `OnUnitActiveSec=3d`。
### 4.4 失效的条目会自动剔除吗?(段级模式)
**会——且已内建,不需要任何额外参数。** 关键是理解 sync 的语义:
| 你可能以为 | 实际机制 |
|---|---|
| sync 是把清单「增量添加」,旧条目越积越多 | ❌ 错。sync 是**全量重建**:每次按当前清单文件从零填充临时集合 → `ipset swap` 原子替换 → 旧的 `EO_ORIGIN` 整个被新集合顶掉 |
所以**只要某段/某 IP 不在「当前清单文件」里,下一次 sync 它就自动消失**——剔除是 sync 的默认行为,零残留。反证见 §5:「临时 `ipset add` 放行的 IP,下次 sync 就没了」正是同一机制。
段级模式下清单近乎静态(5 个 `/16`),日常维护极简,只剩一件事:**防 EO 启用清单外新段**。
```bash
# ① 月度新段预警:跑一次 auto(或 tcpdump 抓 SYN),看采样里有没有清单外的 IP
sudo ./edgeone-origin-whitelist.sh auto
# auto 会把「清单外的新 IP」增量合并进 cidr.txt(只增不删),并提示新增了哪些
# ② 归段:whois <新IP>(/ ipinfo.io/<IP>),确认属腾讯云 EO(Tencent/EdgeOne 字样)
# ③ 把新 IP 归并成所属整段后写入清单(若 auto 已把裸 IP 加进 cidr.txt,可顺手整理成段):
sudo nano /etc/edgeone-origin/cidr.txt # 示例:43.175.x.x → 加一行 43.175.0.0/16
sudo ./edgeone-origin-whitelist.sh sync # 立即生效;不手动跑则下次定时 sync 也会生效
```
三个要点:
1. **别删到清单为空**:fail-safe 会让 sync 拒绝执行(宁可保留旧白名单,不可裸奔)。
2. **旧段留在清单里没有安全风险**:那些地址属于腾讯云,不会"变成攻击者",顶多白名单略宽(/16 级本就宽);真正要紧的只是**新段漏加 → 偶发 521**(见 §8 免费档清单质量)。
3. **想彻底全自动**(连月度核对都不想做):升级付费套餐启用「源站防护」,把 `DescribeOriginACL` 输出定时写入 cidr.txt(§7)——清单每次都是官方最新全集,sync 自动带上新段、自动剔除已退出的段,脚本零改动。
---
## 5. 常用操作速查
```bash
sudo ./edgeone-origin-whitelist.sh sync # 周期入口(定时任务用这个,段级清单全量重建)
sudo ./edgeone-origin-whitelist.sh auto # 新段预警:采集当前回源 IP → 合并进清单 → 有新增即 sync
sudo ./edgeone-origin-whitelist.sh learn # 看当前谁在回源(即时观测,不必用于日常维护)
sudo ./edgeone-origin-whitelist.sh show # 当前白名单条目 + iptables 规则
sudo ./edgeone-origin-whitelist.sh sync --dry-run # 演练
sudo ./edgeone-origin-whitelist.sh flush # 回滚:移除本脚本全部规则/集合
tail -20 /var/log/eo-origin-whitelist.log # 看更新历史
```
**临时放行某个 IP**(不用改清单,立即生效;下次 sync 会随清单重建而消失——想持久就写进清单文件):
```bash
sudo ipset add EO_ORIGIN x.x.x.x
```
**手动移除某个 IP**:
```bash
sudo ipset del EO_ORIGIN x.x.x.x
```
**修改回源端口**(低概率操作):改 `EO_PORT` 后先 `flush` 再 `sync`(避免 INPUT 残留旧端口跳转)。
**修改生效网卡**:改 `EO_IFACE`(或 `sudo EO_IFACE=ens3 ./edgeone-origin-whitelist.sh sync`)后直接 sync 即可——从「不限网卡」升级为「限定网卡」时会自动删除旧的无限定跳转;改完用 `show` 确认 INPUT 规则带 `-i <网卡>`。
---
## 6. 排错
| 现象 | 原因与处理 |
|---|---|
| sync 报「清单为空…保持现网白名单不变(fail-safe)」 | 正常保护:空清单绝不上线。成因=清单文件不存在 / 内容全非法 / 无有效条目。先用 `learn` 采种并人工审阅后写入 `/etc/edgeone-origin/cidr.txt`(裸 IP 每行即可,已支持直接入清单);脚本报错会自带下一步指引。若真配了 `EO_CIDR_URL` 且拉取失败 + 本地无条目,会单独报「来源全部失败」 |
| 收紧后 CF 分支(海外)访问挂 | 几乎肯定是 cloudflared 连的不是 `127.0.0.1`。确认 Tunnel 配置里 service 指向 `http://127.0.0.1:PORT`(脚本对 127.0.0.0/8 放行;若 cloudflared 在**另一台机器**则需把那台机器出口 IP 加进白名单——但那属于另一架构,需单独设计) |
| 经 EO 访问 502/回源失败(境内间歇性 521) | ① 端口填错(脚本拦的不是 EO 回源那个端口);② 🔴 **清单太细**——EO 回源出口是海量轮换节点池(实测 23 秒 13 个出口、分布多个 /16),逐 IP 白名单必然间歇漏放 → EO 报 521。解法=按 §0.1 放行腾讯云大段(43.146/43.152/43.168/43.174/43.175 的 /16,2026-09-03 实测 521 消失);③ 服务器重启后 ipset/iptables 规则丢失 → 见下 |
| sync 报 `Set EO_ORIGIN doesn't exist` | 🔴 iptables-nft 要求「被引用的 ipset 集合必须先于规则存在」。旧版脚本先建链(内含引用集合的规则)后建集合,**首次运行必失败**。已修复(集合创建挪到建链之前);旧版升级后重跑 `sync` 即可自愈 |
| sync 报 `RULE_APPEND failed (Invalid argument)` 且 EO_IN 链**缺 REJECT 兜底行**(只剩 127/集合两条 ACCEPT) | 🔴 iptables-nft 下 `--reject-with tcp-reset` **必须带 `-p tcp`**(nft 的 reject 语句需确认匹配 TCP 才能构造 RST,无 -p 直接 EINVAL)。旧版 REJECT 行无 `-p tcp` 必然失败 → 链尾无兜底 → **非白名单直连仍通,白名单形同虚设**(集合/127 两条 ACCEPT 无此限制,能正常加入)。已修复(REJECT 加 `-p tcp`);部署新版后重跑 `sync`,验证 `iptables -S EO_IN` 三条齐全:`-s 127.0.0.0/8 ACCEPT` / `-m set --match-set EO_ORIGIN src ACCEPT` / `-p tcp ... REJECT --reject-with tcp-reset` |
| 服务器重启后白名单没了 | ipset 与 iptables 规则是内存态。用 `iptables-persistent` + `ipset-persistent`(Debian/Ubuntu),或把 sync 加进开机自启(systemd timer `Persistent=true` 只补跑错过的任务,不保证开机即跑——建议加一条 `@reboot` cron 或 oneshot service 开机执行一次 sync) |
| 直连还是能通 | 先 `iptables -S INPUT` 看是否还有更靠前的全局 ACCEPT 匹配 18080;`ufw status` 若显示 allow 18080 先删;云安全组全放行但本机已 REJECT 的情况下直连应被拒——若仍通说明规则没插在 INPUT 最前或被其它链绕过(如 nftables 后端混用,`iptables -S` 与实际生效不一致时改用 `nft list ruleset` 排查) |
| auto 立即结束、没等 `EO_SAMPLE_SECONDS` 窗口(日志同秒出现「采样到 0 个」) | 🔴 旧版 tcpdump filter 误写为 `"... and 'tcp[tcpflags] & tcp-syn != 0'"`——双引号内的单引号是**字面字符**,会原样传给 tcpdump,pcap 编译器不认识引号 → syntax error **秒退**(手动执行时单引号由 shell 吃掉,脚本里没这一步);且旧版 `awk '{print $2}'` 取到的是协议名 `IP` 而非源地址(源在 `>` 前一字段)。两个缺陷叠加 = 抓了也白抓、还秒退。已修复(filter 去引号 + 按 `>` 前字段取源 IP,窗口会真实阻塞)。部署新版后:`sudo edgeone-origin-whitelist.sh auto` 应看到「tcpdump 采样 Ns … 请稍候」并等待 N 秒;`EO_SAMPLE_SECONDS` 非数字会打印提示并跳过 |
| learn 输出为空 | ① **没有活跃回源连接**——EO 回源是短连接、错过即无,且**必须境内网络访问才走 EO 路径**(境外访问走 CF Tunnel,对端 127.0.0.1 会被脚本过滤,永远为空);② 端口核对:`ss -tlnp \| grep LISTEN` 确认 TamaBox 监听端口 = 脚本 `EO_PORT`;③ 访问后 60 秒内 `ss -Htan \| grep :<端口>` 看 TIME-WAIT 痕迹;④ 最稳:`tcpdump -ni <网卡> tcp port <端口> and 'tcp[tcpflags] & tcp-syn != 0'` 抓 SYN——触发一次境内访问,出现的源 IP 即 EO 回源 IP。新版脚本空结果时会自带以上提示;`auto` 的采样含 conntrack(窗口比 learn 宽),仍采不到时排查思路同上 |
---
## 7. 升级付费套餐后:接官方 API 全自动(零脚本改动)
启用「源站防护」拿到 ZoneId 后,可用腾讯云 Python SDK / tccli 周期调用 `DescribeOriginACL`,把返回的 `CurrentOriginACL.EntireAddresses.IPv4[]` 写入 `/etc/edgeone-origin/cidr.txt`,本脚本照常 sync 即可——**脚本无需任何改动**(它只认"每行一个 CIDR 的文件")。
> 也可让脚本直接拉取:把 API 结果发布到一个私有 HTTP URL,配置 `EO_CIDR_URL=...`(文件 + URL 会取并集)。注意变更时先核对 `NextOriginACL` 计划生效时间,建议同步时机放在 `PlannedActiveTime` 前完成,避免 EO 强制切换后回源失败。
---
## 8. 边界与已知限制
- **默认仅公网网卡 eth0**:白名单只判定 `EO_IFACE` 指定网卡的入站流量(默认 `eth0`);回环由独立的 `-i lo` ACCEPT 放行(供 Tunnel 本机回连),与网卡限定解耦。网卡名不是 eth0 时务必配置,否则跳转永不命中、端口裸奔。
- **IPv6 天然无风险、无需任何 ip6tables 规则**:脚本只管理 IPv4;TamaBox 监听 `0.0.0.0`(仅 IPv4、无 AAAA),IPv6 上不存在该回源端口,v6 用户访问由 CDN 边缘走 IPv4 回源,一切正常。只有日后改为监听 `[::]:PORT` 并向 EO 开放 v6 回源,才需扩展 inet6 集合 + ip6tables 镜像(脚本头注释 5 有说明)。
- **免费档清单质量(段级视角)**:清单是 5 个腾讯云 `/16` 段(§0.1),EO 出口怎么轮换都在段内 → 正常无感。唯一风险是 EO 启用清单外的**新段**(罕见、以月计)→ 表现为个别请求偶发 521;对策=月度跑一次 `auto` 看有无清单外新 IP,`whois` 确认归段后补段(§4.4)。这是免费档没有官方清单的固有代价,可接受则用,不可接受就升级付费档走 §7。
- **脚本不触碰 ufw/云安全组**,只管理自己创建的链与集合;`flush` 也只删自己的东西,可放心回滚。
- 本机为 1C1G 低配:脚本每次运行仅秒级、无常驻进程,ipset 查表为 O(1) 哈希,不影响转发性能。