解决腾讯云 COS 客户端报 Request has expired 的问题
腾讯云对象存储服务 COS 客户端在 docker 内报错了,提示 “AccessDenied” 以及 “Request has expired"。明明各种配置参数都一样,为什么换个地方就运行不了了呢。
背景
我有一段 python 代码,是用于与腾讯云对象存储服务 COS 进行交互,从而实现对象搜索、对象下载等功能。
本地调试没问题后,想利用 docker 实现容器化部署,却报错了,提示 “AccessDenied” 以及 “Request has expired"。明明各种配置参数都一样,为什么换个地方就运行不了了呢。
结论
这里先给出根本原因和解决方案:
- 根本原因:容器的系统时间与真实时间不一致(一般是远远落后于真实时间),导致 COS 客户端在发起请求的时候,签名过期,请求失败。
- 解决方案:共有 3 类方案:
- 容器使用宿主机的本地时间设置:将宿主机的 /etc/localtime 文件挂载到容器的相同路径下,并以只读模式 (ro) 进行挂载即可。但我的情况比较特殊——使用的是 podman 而非 docker,容器内的时间实际上是与虚拟机同步的,修改虚拟机的时间设置比较麻烦,折腾了一小时没成功,最终放弃这种方案。
- 容器使用 NTP(网络时间协议)客户端与 NTP 服务器同步时间:在容器内安装和配置 NTP 客户端,然后使用 NTP 服务器同步时间。但需要装一堆东西,很麻烦,我的容器是 alpine 系统(一个面向安全的轻型 Linux 发行版),很多包都需要重新安装,折腾了一小时没成功,最终放弃这种方案。
- 传递 cos 签名超时参数:相比于前面两者比较治本的方法,这种方式有点头痛医头的感觉,并且是绕过了问题。因为如果你用 time.time 或其他方法读取时间,读到的还是错误的时间。但无所谓了,能解决问题就行,详见下文。😂如果你有好的方法,也欢迎在评论区回复,感谢~
过程分析
定位问题
首先,通过翻阅官方文档,可以看到一段解释说明。
看上去是时间设置的问题,对比本地和容器内的时间,可以知道已经不是简单的时区问题了。因为时区一般是差 N 小时,而这已经是差了好几天了。
解决方案
1. 容器使用宿主机的本地时间设置
需要注意的是,该方案不适用于 podman。
2. 容器使用 NTP(网络时间协议)客户端与 NTP 服务器同步时间
如何安装 NTP 客户端并设置,跟你的系统有关,并没有统一的方案。同样操作起来很麻烦……
3. 传递 cos 签名超时参数
折腾了半天无法解决这个问题后,我决定不再想着如何根治问题,而是针对于这个案例来定向解决。
首先,根据代码报错的位置,可知是请求的时候报错,具体是在 cos_client.py 的 1391 行。这段代码主要做的就是把 URL、params、headers 以及跟用户认证有关的内容,传递给 send_request 方法,发起请求。
跟签名过期有关的内容,一般要么放在 header 里,要么放在 auth 里,依次查看内容后,很容易看到答案就在 CosS2Auth 里。但麻烦的是,我们并没有办法传递或修改这个 expire 参数值。
那么只能把这段代码复现一下,唯一区别就是指定了 CosS2Auth 里的 expire 值。
下面以 list_objects 举个例子:
修改前,直接使用 cos_client 对象的 list_objects 函数
cos_client.list_objects(Bucket=bucket, Prefix=prefix, Delimiter="/", Marker=marker)
修改后,把 list_objects 的源代码拷贝出来,放在自己重新定义的 list_objects 函数里,但是额外在初始化 CosS2Auth 实例时传递 expire 值。
更多推荐
所有评论(0)