我常买 Linux VPS,有一套统一的初始化配置,一条条命令复制粘贴执行很有感觉。其中包括利用 NTP 进行时钟同步。但是有些服务商会屏蔽 NTP 协议,可能是出于安全原因。于是得另寻他法。
稍微搜了一下,发现有两条路,都是利用现有 HTTP 服务进行同步的。一种利用响应体(response body),一种利用响应头(response header)。很有意思。
利用响应体
如果一个网页的内容是某种格式的时间,那么我们可以直接传给 date 程序设置系统时间。如 Slime 给出了命令:
curl http://time.akamai.com/?iso | xargs date -s
著名 CDN 服务商 Akamai 在这个地址下以 ISO 格式响应当前时间,用命令获取它,通过管道作为 date -s 的参数传递过去。其中 curl、xargs、date 都是 Linux 服务器常备的程序或命令,所以可以毫无压力地使用。
利用响应头
上述获取响应体设置时间的方式固然巧妙,Akamai 算是大厂,服务可用性也很高,但你知道其实几乎所有 HTTP 服务都会在响应头返回时间吗?可以用浏览器开发者工具、抓包工具或者 curl 命令来验证,比如请求闪星空间首页:
$ curl -I --http1.1 https://shansing.com
HTTP/1.1 200 OK
Server: nginx
Date: Sun, 29 Aug 2021 11:30:02 GMT
Content-Type: text/html; charset=UTF-8
...
其中有 Date header,且为当前时间。多试几个网站或网页,能得到一样的结果。这不是巧合。RFC 2616 规定,服务器除出错或没有时钟等情形外,必须(MUST)按此特定格式返回 Date header,且应当(SHOULD)尽可能贴合消息生成的时间,我们一般就可以理解为当前时间。获取这个值也能帮助校正服务器时间。
已经有程序写好了,叫 htpdate。根据说明编译安装
wget -O htpdate-1.2.6.tar.gz https://github.com/angeloc/htpdate/archive/refs/tags/v1.2.6.tar.gz
tar zxvf htpdate-1.2.6.tar.gz
cd htpdate-1.2.6
make && make install
然后就可以执行了。指定多个 URL 可以提高准确性,比如:
htpdate -a -t http://www.google.com http://www.baidu.com http://www.cloudflare.com
更多用法可执行 man htpdate
查看(英文)。
结语
NTP 其实会考虑网络时延,它假设收发所用时间是相等的,以此减少网络传输带来的延迟。但本文介绍的两种方法都不考虑延迟:获取响应体的方式很明显没有考虑,而 htpdate 尽管相对完善,可以使系统时间平滑改变(-a)也可以突变(-s),但设计上也是忽略网络延迟的。
所以,在要求不严格的情况下,可以用这些方式替代 NTP 同步系统时间。但如果有更高要求,服务器又屏蔽了 UDP 123 端口连出,也许可以考虑 NTP 代理、自建 chrony 之类的解决方案。
如果不需要多个域名校准,倒是可以直接一行 bash 实现类似功能
date -s "$(curl http://www.baidu.com -I -s | grep Date | awk '{first = $1; $1 = ""; print $0;}')" && hwclock --systohc
如果要支持 HTTP/2,你还得忽略
Date
的大小写。直接都用小写就完事了,1.x 对大小写不敏感