事情是这样的 #
2026 年 1 月 21 日,目标集群 集群告警了。
Prometheus 报错:Warning: Error fetching server time: Detected 38.116000175476074 seconds time difference between your browser and the server. 38 秒,说多不多说少不少,但足够让 Teleport 的安全校验挂掉。
更离谱的是,target02 直接失联了——Teleport 握手失败,根本连不上 SSH。
排查过程 #
第一步:以为是网络问题 #
在 target01 上探测跳板机的连通性:
# TCP 扫描(正常)
for port in 22 80 8888; do nc -zv -w 2 100.64.0.5 $port; done
# UDP 扫描(有猫腻)
nc -uvz -w 2 100.64.0.5 8888 # 显示 success,但收不到回程包TCP 通,UDP 看起来通但数据包丢了。熟悉的配方,隔离环境的老朋友们都知道这意味着什么。
第二步:抓包确认 #
在跳板机(jump-host)开启抓包,同时从 target01 发送 UDP 包:
# jump-host 执行
sudo tcpdump -i any udp port 8888 -n
# target01 执行
nc -u 100.64.0.5 8888 <<< "test"结果:跳板机侧完全没有捕获到任何 UDP 包。UDP 在隔离网络层被拦截了。
第三步:检查代理 #
curl -v http://100.64.0.5:8888
# 响应:< Proxy-Agent: gost/2.12.08888 端口是 gost 代理,但它主要处理 TCP 隧道。UDP?这儿不接待。
真正的凶手:时钟漂移 #
绕了一圈,问题根源浮出水面——目标节点的时钟已经飞了:
- target01:时钟滞后约 38 秒,Prometheus 图表数据断点、查询偏移
- target02:时钟偏移更严重,Teleport 安全校验直接 fail
没有 NTP(隔离环境 UDP 114 端口通不过),chrony 也在那儿干瞪眼。
解决方案:HTTP Date Header 手动同步 #
步骤一:禁用失效的 chrony #
systemctl stop chronyd
systemctl disable chronyd步骤二:手动对齐时钟 #
跳板机 80 端口的 HTTP 响应头里有个 Date 字段,虽然精度是秒级,但总比没有强:
# 提取 HTTP Header 中的 Date 字段并设置系统时间
HTTP_DATE=$(curl -sI http://100.64.0.5 | grep -i "^Date:" | cut -d" " -f2-)
[ -n "$HTTP_DATE" ] && date -s "$HTTP_DATE"没错,2026 年了,我还在用 curl + date -s 同步时钟。上次这么干还是在 OpenWrt 上,那已经是十多年前的事了。
步骤三:定时任务持久化 #
在 target01 和 target02 上配置每小时自动同步:
(crontab -l 2>/dev/null; echo '0 * * * * HTTP_DATE=$(curl -sI http://100.64.0.5 | grep -i "^Date:" | cut -d" " -f2-) && [ -n "$HTTP_DATE" ] && date -s "$HTTP_DATE"') | crontab -教训 #
-
隔离环境没有 NTP 是常态。UDP 被拦截时,HTTP 是最后的安全通道。
-
非标端口的陷阱。8888 虽然在 gost 中配置了,但 目标 客户端没同步配置隧道,直接访问这个 UDP 端口必然失败。
-
Prometheus 对时间极度敏感。大规模集群里,时间同步的鲁棒性比想象中重要得多。
事后回想这套流程:禁用 chrony → curl HTTP Header → date 设置 → crontab 定时。整个链路充满了"能跑就行"的务实感。
下次再有隔离环境部署,或许该考虑把 HTTP Date 同步做成 systemd timer 服务?