前言
GitHub Workflow 是一个非常强大的工具,可以帮助我们自动化很多工作,它能够在检测到我们推送的提交、合并请求等事件时,自动新建一个虚机来执行我们预先编写好的脚本。
我的很多项目都使用了这个功能来帮助我执行自动化流程,比如这篇博客的渲染就是由 GitHub Workflow 来完成的。在我推送 Markdown 格式的博客内容后,它会首先签出到我最新的提交,然后调用 hugo
的工具链来渲染成 HTML 页面,然后将打包好的文件夹压缩,通过 scp
指令发送到我位于日本东京的生产服务器 tokyo-neko
上,再执行相关操作后,网站上就会显示我写的博文——整个流程小于 1 分钟。而我要做的,只是专注于写作,然后 git push。
此外,我使用它来自动发布 hugging-face-api的新版本到 PyPi 平台上,以及自动构建 AstrBot Docker 镜像。
而本篇文章要记录的,就是由于对 GitHub Workflow 中某个插件的不熟悉导致的 AstrBot Docker 端更新业务的大规模异常。
问题的发现
起因是群里有部分用户反馈 Docker 的 AstrBot 更新后无法正常运行。
排查之后,推送了一个修复版本,但是在测试尝试更新的时候,发现可视化面板页面一直显示 “正在检查更新”。
用户也反馈遇到了这个问题。
于是我使用 docker logs
查看了日志,发现显示了这么一句话:
1 | Username for 'https://github.com': |
这是 AstrBot 在调用 git 指令时,需要输入用户名的提示。
然而,AstrBot 是 GitHub 上的一个 Public 的项目,拉取代码是不需要输入用户名的。
问题的分析
我猜测是 GitHub Workflow 在签出仓库时,将我的账号的某些令牌数据写入了某个 .git/ 下的配置文件,然后令牌过期了,导致了这个问题。下面的实验也佐证了我的猜测。
我重新构建了一次 Workflow,代码签出到早期版本的 AstrBot。然后在测试机上拉取该新构建的镜像来运行,执行更新操作,发现可以正常更新——说明肯定是有一个令牌的机制导致了这个问题。
对 .git 文件夹的深入分析
.git 文件夹是所有使用 git 进行版本管理的项目都会有的,它存储了 git 的一些配置信息,比如分支信息、提交信息等。由于以 . 开头,因此他在大多数文件资源管理器中是隐藏的。具体来说,.git 文件夹包含有如下文件:
- HEAD:指向当前签出的分支的引用,一般内容是
ref: refs/heads/main
- COMMIT_EDITMSG:存储了最近一次提交的 message 文本。
- FETCH_HEAD:存储了最近一次 fetch 的信息:commit_id、分支名、仓库地址(remotes)等。
- ORIG_HEAD:存储了最近一次 HEAD 的 commit_id。
- discritpion:存储了仓库的描述信息。
- config:存储了 git 的配置信息
- hooks/:存储了 git 的钩子脚本,用于在 git 的生命周期中执行一些操作,比如 pre-commit、pre-push 等。默认情况下,这个文件夹下有一些示例脚本:
pre-commit.sample pre-push.sample ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30- index:存储了暂存区的信息,是一个二进制文件。
- logs/:存储了 git 的日志信息,比如 HEAD 的变更记录。
- refs/:存储了 git 的引用信息,比如分支、标签等。
- objects/:存储了 git 的对象信息,比如 commit、tree、blob 等,是 git 的核心。
![](/images/AstrBot维护记录:由GitHubWorkflow引发的大规模更新失败/image-2.png)
而我们问题的罪魁祸首就出现在这个 .git 文件夹下的 config 文件中。
config 文件内容以 ini 格式存储,包含了 git 的一些配置信息。
比如我的博客项目的 config 文件内容如下:
```ini
[core] // 核心配置
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[submodule "themes/paper"] // 子模块配置
url = https://github.com/nanxiaobei/hugo-paper
active = true
[remote "origin"] // 远程仓库配置
url = https://github.com/Soulter/SoulterBlogv3.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"] // 分支配置,记录了当前分支的一些信息
remote = origin
merge = refs/heads/main
而在我调研异常版本的 AstrBot 的 config 文件中,发现了这么一段:
http "https://github.com/"
节下的内容是我在 GitHub 上的令牌,这个令牌是使用 GitHub Workflow 的 actions/checkout@v2
这一个插件时,自动在 .git/config 文件中写入的。
而我在 docker build 指令中,直接将 AstrBot 下的所有文件打包进了镜像,包括了 .git 文件夹。
这就导致了用户在使用该镜像时,与 git 之间的交互都走的是我的账号!当令牌过期后,git 将该令牌发送给 github,而 github 识别到这是一个过期令牌,就返回需要重新验证账户的响应。这是一个巨大的安全隐患,如果被恶意利用,我的账号可能就不复存在了。
解决方案
直接使用 git clone 指令拉取代码,以代替 GitHub Workflow 的 actions/checkout@v2
这一个插件。