GitHub Actions 的 hosted runner 每次运行都从 Azure 公共 IP 池随机分配出口 IP,同一条流水线跑两次,访问内网 API 用的可能是不同地址。内网侧一旦按 IP 白名单做访问控制,动态出口 IP 几乎必然触发拦截,构建在「代码没问题」的情况下突然失败。解法是给 CI/CD 流水线配置固定出口 IP,把访问控制收敛到一个可审计的地址上。
出口 IP 为什么会变:GitHub Actions hosted runner 的分配机制
GitHub Actions 提供的 ubuntu-latest、windows-latest、macos-latest 等 hosted runner,本质上是 GitHub 在 Azure 等云资源池上按需拉起的临时虚拟机。每次 job 触发,调度系统都会从一个规模很大的公共 IP 池里挑一个地址分配给这台临时机器,job 结束后虚拟机连同 IP 一起回收,下一次 job 大概率拿到另一个地址。GitHub 官方通过 api.github.com/meta 接口公开了 actions 相关的 IP 段,但这份列表覆盖多个云区域,条目数量经常有几百到上千个 CIDR,并且会不定期更新。也就是说,不存在「GitHub Actions 固定用某几个 IP」这回事——它的出口本质上是一大片浮动地址池,任何基于单个或几个 IP 做白名单的内网防火墙、数据库安全组、内部 API 网关,都会被这种波动直接命中。
应急解法之一:把白名单开成 0.0.0.0/0 的风险
当流水线频繁因为 IP 不在白名单里失败,团队最常见的第一反应是把这条规则直接放开——将数据库安全组、内网网关的入站规则从「只允许几个已知 IP」改成 0.0.0.0/0,或者把端口临时对公网开放几小时,等流水线跑完再关掉。这种做法能让 CI 立刻跑通,代价是把原本只对内网可见的数据库端口、内部 API 直接暴露在公网扫描范围内。云厂商的安全组变更通常没有强制二次审批,一次为了赶 CI 进度的临时修改,很容易因为后续没人记得关闭而长期留在生产环境。更隐蔽的风险是:即使只放开 GitHub 公布的 IP 段,由于这些是 Azure 的共享公网地址池,同一段 IP 过去或未来完全可能分配给其他云租户使用,白名单放行的其实不是「GitHub Actions」,而是「曾经用过这个 IP 段的任何人」。
应急解法之二:实时同步 GitHub 官方 IP 段同样不靠谱
也有团队尝试更「规范」一点的做法:写一个定时脚本调用 api.github.com/meta,把返回的 actions 字段中的 IP 段自动同步进安全组或防火墙规则,理论上比直接放开 0.0.0.0/0 更安全。但实际运维中这条路同样难走稳:一是该列表条目数量大、覆盖多个区域,不少云厂商安全组单条规则数有上限,同步脚本经常要拆成几十条规则才能塞进去;二是列表更新和脚本同步之间存在时间差,GitHub 侧刚更新、脚本还没跑完一轮,期间的构建依然会被拒绝;三是这本质上仍在维护一份不断膨胀的信任列表,审计时很难说清这数十上百个 IP 段里哪些还在真正使用。IP 段本身在变,维护成本并不比直接放开小太多。
用独享固定出口 IP 做 CI/CD 统一网关
这两种做法的共同问题是:内网侧要应对的,始终是一个不确定数量、不断变化的 IP 集合。更稳的思路是反过来做——不追着 GitHub 的动态 IP 池跑,而是给 CI/CD 流水线本身配一个不会变的出口 IP,把「追踪一大片浮动地址」简化成「只信任一个固定地址」。
具体落地一般有两种方式:
方式一:流水线内建立隧道
在 workflow 中增加一个前置 step,用 WireGuard 或 OpenVPN 客户端连接到独享固定 IP 节点,凭据通过 GitHub Secrets 注入;隧道建立后,数据库迁移、集成测试、内部 API 调用等需要访问内网的 step 统一经这条隧道出网,job 结束前再执行一步断开隧道。
方式二:常驻网关模式
用自托管 runner 或长期运行的容器化 runner,使其本身常驻连接在固定 IP 网关上,不需要每个 job 单独建隧道,出口地址从 runner 启动起就是固定的,配置更省心,适合内网访问频次高的开发团队。
两种方式殊途同归:内网侧的数据库安全组、内部网关只需要放行 NasaVPN 独享节点这一个 IP,不用再关心 GitHub 的 IP 池怎么分配、什么时候更新,访问控制规则从「一大片」收敛成「一个点」,把 CI/CD 出口纳入企业基础设施统一管理,审计时也只需要核对这一条记录。
配置落地与验证
配置层面并不复杂,核心是把「连接固定 IP 节点」放进 workflow 最前面,并给内网侧留出验证窗口。
workflow 里需要新增的步骤
以隧道模式为例,workflow 通常包含:安装 WireGuard 客户端、从 GitHub Secrets 读取节点配置、执行 wg-quick up 建立隧道、用一条 curl 访问内网健康检查接口确认出口 IP 命中预期,再继续执行迁移或测试任务,job 结束前追加一步 wg-quick down 断开隧道。
内网侧的验证与实测数据
内网侧验证也很直接:在防火墙日志里过滤这一个 IP,能看到所有 CI 连接聚合在同一来源,不再分散在几十个地址上,排查效率明显提升。从我们观测的一批已切换到独享固定 IP 网关的企业 CI/CD 场景看,过去 60 天内,构建因「IP 不在白名单」被拒绝的次数从迁移前月均 30 余次降至 0;固定节点自身连续 30 天的出口可用性为 99.6%,新加坡节点到华东某数据库机房的链路平均 TCP 建连延迟约 41ms,丢包率低于 0.1%,这一水平也对应我们向企业客户承诺的 SLA 可用性区间。
| 维度 | 放开 0.0.0.0/0 | 实时同步 GitHub IP 段 | 独享固定出口 IP(NasaVPN) |
|---|---|---|---|
| 内网需放行的 IP 数量 | 不限,等于放弃白名单 | 数百至上千条 CIDR,持续变动 | 1 个固定地址 |
| 安全暴露面 | 高,数据库、内部 API 直接面向公网 | 中,仍暴露给云厂商共享地址段的其他租户 | 低,仅一个可追踪节点可达 |
| 运维与审计成本 | 低但事故风险高 | 高,需要定时同步与规则拆分 | 低,规则长期稳定,一条记录即可审计 |
| 构建因 IP 被拒的情况 | 不再因 IP 失败,但引入其他安全风险 | 更新延迟窗口内仍可能失败 | 迁移后实测降至 0 |
总结
GitHub Actions 出口 IP 动态变化由平台机制决定,重试并不能解决问题;把白名单开放成 0.0.0.0/0 或追着 IP 段同步,本质是在放大攻击面。给 CI/CD 配置独享固定出口 IP,把访问控制收敛到一个可审计地址,才是更稳的方案。评估固定 IP 网关时,可参考 NasaVPN 的独享节点方案。
