每天都在打补丁,每天都在失败:Windows Update 与那条被收掉的 HTTP/80

缘起

preprod 里有个维护窗口:nusa-preprod-windows-patch,每天凌晨 03:17(Asia/Singapore)跑 AWS-RunPatchBaseline,目标是两台带 PatchGroup=preprod-windows 的 PowerBI Gateway。

某天随手翻了一下执行历史,发现一件不太妙的事:

从 6 月 25 号到 7 月 24 号,最近三十来次执行,全部 Failed。

不是偶发超时,不是某一台掉线,是窗口级天天红。SSM Agent 在线,机器也跑着,任务确实打上去了——然后挂在 Windows 补丁搜索这一步。

现象

错误信息不长,但很「经典」:

1
2
An error occurred when attempting to search Windows Update.
Exception from HRESULT: 0x80072F8F

微软文档对 0x80072F8F 的第一反应通常是:

  1. 系统时间不准,SSL 证书日期校验失败;
  2. 或者证书链不被信任(常见于自家 WSUS / 自签证书)。

第一眼很容易往「时钟漂了」上想。毕竟这两台在私有子网,出网靠 NAT,SG 出站又收得很紧——万一 NTP 不通,时间漂一个月,证书校验挂掉,说得通。

于是先查时间线,再上机验证。

时间线对上了,但不是你想的那个故事

先把 Git 和维护窗口摆在一起看:

时间 发生了什么
6/23 03:17 最后一次补丁成功(当时还只有 gateway-1)
6/23 晚上 安全收紧:PowerBI SG 的 HTTP/80 从 0.0.0.0/0 收到 169.254.0.0/16
6/25 03:17 起,维护窗口开始天天 Failed
6/25 白天 又补了一刀:link-local 放开全协议,修好到 Amazon Time Sync 的 UDP/123;但公网 HTTP/80 没有恢复

成功补丁停在「收紧 HTTP」之前;失败从收紧之后的第一个维护窗口开始。相关性高得有点过分。

再看一眼当前 SG 实际放行什么:

  • VPC 内网:全通
  • 互联网:只有 TCP 443
  • link-local 169.254.0.0/16:全通(含 NTP)

而同仓库里 dev / qa 的 PowerBI Gateway SG,仍然保留着 0.0.0.0/0:80。preprod 成了特例。

上机诊断:时钟是无辜的,80 端口不是

在两台机上跑了只读探测(时间、NTP、443、80、几个 Microsoft URL)。结果两边几乎一字不差:

时间正常。

1
2
3
DateTime: 2026-07-24T04:42:42Z
NTP Source: 169.254.169.123
Last Successful Sync: 几分钟前

Amazon Time Sync 在干活,Stratum 正常。所以「证书日期无效」这条线,至少对现在不成立。

443 也正常。

1
2
3
TCP 443 -> sls.update.microsoft.com   : True
HTTPS -> www.microsoft.com : 200
HTTPS -> sls.update.microsoft.com : 404 # 裸路径 404 没关系,说明 TLS 握手成功了

80 全灭。

1
2
TCP 80  -> download.windowsupdate.com : False
HTTP -> windowsupdate.microsoft.com: timeout

Windows Update 官方要访问的那一串地址里,至今仍有大量 http://... 端点。你只放 443,等于让 WU Agent 去跑一场缺了半条腿的搜索。

于是 Patch Manager 的故事就变成了:

  1. 维护窗口准点触发;
  2. AWS-RunPatchBaseline 打到两台 Online 的 Windows;
  3. PatchWindows 调 IUpdateSearcher.Search;
  4. 搜索阶段触达需要 HTTP 的更新端点;
  5. 被 SG 丢掉;
  6. 表层报成 0x80072F8F;
  7. max_errors = 1,整窗 Failed。

调度没问题,NAT 没问题,Agent 没问题,时间也没问题。少的就是那条 TCP/80。

为什么错误码会骗人

0x80072F8F 在微软文案里被写成 SSL / 时钟问题,并不等于现场永远是时钟问题。

这次诊断里,HTTPS 到 sls.update.microsoft.com 已经能完成 TLS;真正稳定复现的,是 HTTP/80 被拒。结合 6/23 那次「只留 443、收掉公网 80」的变更,根因应落在出网上,而不是再去重装一堆根证书、或者怀疑系统日期跳到了 1970。

另外,6/23 到 6/25 之间,NTP(UDP/123 到 link-local)确实也曾一度不通——那几天如果刚好撞上时钟偏差,错误码看起来会更「像教科书」。但 6/25 修完 Time Sync 之后,失败还持续了整整一个月。说明真正让它日复一日挂着的,不是时钟,是 HTTP。

修复

很土,但有效:把 HTTP/80 加回去,跟 dev / qa 对齐,并在注释里写清楚为什么不能再「顺手」收掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Windows Update still requires HTTP (e.g. download.windowsupdate.com). Without this,
# AWS-RunPatchBaseline fails at Search with HRESULT 0x80072F8F.
resource "aws_vpc_security_group_egress_rule" "preprod_powerbi_gw_http" {
security_group_id = aws_security_group.preprod_powerbi_gateway.id
description = "Allow HTTP to internet (Windows Update)"
ip_protocol = "tcp"
from_port = 80
to_port = 80
cidr_ipv4 = "0.0.0.0/0"

tags = {
Name = "preprod-powerbi-gateway-egress-http"
Environment = var.env_name
ManagedBy = "terraform"
}
}

原则就一句:

安全收紧可以,但别把业务依赖的协议通道当成「多余的 80」一起砍。
Windows Update 不是只走 HTTPS 的现代 SPA。

Apply 之后,建议手动跑一次 Scan / Install 验证,别等第二天凌晨再看红绿。

一点感想

这事有个很典型的运维味道:

  • 监控面上,维护窗口「有在跑」;
  • 错误码面上,像是「证书 / 时间」;
  • Git 历史上,是一次「看起来更安全」的 egress 收紧;
  • 实例面上,443 绿灯会让人误以为「出网没问题」。

把这四层叠在一起,才能看到真相:
出网不是「有没有网」,而是「缺了哪一种协议、到哪一类域名」。

下次再看到 Patch Manager 天天 Failed、错误又是 0x80072F8F,我会先做三件事:

  1. 看最近有没有动过 Windows 实例的 SG egress;
  2. 看 HTTP/80 和 HTTPS/443 是否都通到 Microsoft Update 域名;
  3. 再用 w32tm 确认时间——而不是一上来就只信错误码的字面意思。

坑不深,但藏在「我们为了安全做对了的那次变更」里。