花了点时间,搞明白了 git / github 的认证机制。
git 的传输协议与认证机制
git 支持三种传输协议,HTTPS, SSH 和 Git。Git 已基本淘汰,不考虑。
当我们 clone 时,从 <repo> 的形式即可确定使用的传输协议:
- HTTPS:
https://github.com/bytecodealliance/wasmtime.git - SSH:
git@github.com:bytecodealliance/wasmtime.git
对于 HTTPS 协议,认证使用标准的 HTTP Basic Auth 机制。它通过 Authorization header 携带认证信息,其值为 base64_encode({username}:{password})。我们要么手动输入 username 和 password,要么借助 git 的 credential helper 机制自动化这个过程。
HTTP 的认证是一个 challenge–response 过程,第一次请求不带 Authorization header。如果这个仓库允许公开访问,那么无需输入认证信息,直接访问即可。如果需要认证,就返回 401。git 提示用户手动输入 username 和 password,或通过 credential helper 自动获取,并添加 Authorization header,再次发起请求。
具体到 GitHub,它的 public 仓库允许任意访问,无需认证。 而且,GitHub 已禁止直接使用密码,password 是用各种 token 替代,比如 OAuth token,PAT (personal access token)。
对于 SSH 协议,则通过 SSH key 认证。我们需要先将自己的 pubkey 上传到 git server。即使 public repo,也需要 SSH key。
clone 之后,之后 pull/push 操作时,认证机制也是类似,即根据 remote url 确定传输协议。git remove -v 即可查看 remote url。
这里有个问题:认证机制与传输协议是一一绑定的,并不正交。username+password 只能用于 HTTPS 协议,SSH key 只能用于 SSH 协议。 这有时会让人不爽。比如,在自己电脑上习惯了使用 SSH key,但如果我不小心使用 HTTPS 协议 clone 私有仓库,git 就(可能)会提示输入用户名和密码。
GitHub 的 deploy key 与 PAT
Deploy key 是 SSH key。它需要每个仓库单独设置,而且多个仓库的 deploy key 不允许相同。Deploy key 安全性好,但使用起来不方便。在代码仓库的 Settings - Deploy Keys 页面设置 deploy key。
前面提到 GitHub 禁止直接使用密码,PAT (personal access token) 是其替代机制,可视为替代密码。git 通过 HTTPS (git over HTTPS) 协议访问代码仓库时,可用 PAT 进行认证。PAT 也可用于访问 GitHub API,比如处理 issue/pr 等。
在 Settings - Developer Settings - Personal access tokens 页面设置 PAT,推荐使用 Fine-grained PAT。PAT 安全而且灵活。我们可以指定 PAT 的过期时间,允许访问哪些 repo,以及指定权限。生成的 PAT 前缀为 github_pat_。
借助 git 的 credential helper 机制,可以做到自动输入 username 和 PAT 作为 password,免去手敲之苦。
在 Mac 上可这样操作:
# 查看设置的 crendential helper
# Mac 上 git 已自动设置好了,见 /Applications/Xcode.app/Contents/Developer/usr/share/git-core/gitconfig
$ git config credential.helper
osxkeychain
# 如果未设置,则设置
git config --global credential.helper osxkeychain
# 用 HTTPS 协议 clone 一个私有 repo
# 输入 username,及 PAT,即可。
$ git clone https://github.com/{user}/{private_repo}.git
打开 Keychain 程序,搜索 github.com,就能找到刚刚存储的 username 和 PAT 了。
在 Ubuntu 上操作如下:
# 启用 credential store,git 将会保存到 ~/.git-credentials 文件
git config --global credential.helper store
# 用 HTTPS 协议 clone 一个私有 repo
# 输入 username,及 PAT,即可。
$ git clone https://github.com/{user}/{private_repo}.git
打开 ~/.git-credentials 会发现保存的是明文,内容形如 https://{username}:{PAT}@github.com。
此后,就不需要再手动输入了。
总的来说,PAT 是非常好的机制。当需要在远程服务器上构建并部署项目时,以及需要授权 code agent 访问 github 仓库时,都可选择 PAT。
GitHub 的命令行工具 gh
gh 是 GitHub 的官方 cli 工具,它几乎覆盖了 GitHub 的所有功能,包括 repo, issure, pr, release, project, workflow, gist 等等。
我们可以通过 gh 登录 GitHub,并将获得的 token 给 git 命令使用。
$ git auth login
? Where do you use GitHub? GitHub.com
? What is your preferred protocol for Git operations on this host? HTTPS
? Authenticate Git with your GitHub credentials? Yes
? How would you like to authenticate GitHub CLI? Login with a web browser
回答这 4 个问题,然后在浏览器中完成授权,即登录成功。这是一个 OAuth 授权流程,生成的是 OAuth token,以 gho_ 为前缀。
第 3 个问题,Authenticate Git with your GitHub credentials?,如果回答为 yes,就会配置 git 的 credential helper,让 git 命令也自动使用 gh 的 token。
# 查看登录状态
$ gh auth status
github.com
✓ Logged in to github.com account yfaming (keyring)
- Active account: true
- Git operations protocol: https
- Token: gho_************************************
- Token scopes: 'gist', 'read:org', 'repo', 'workflow'
# 查看 token
$ gh auth token
...
# 退出登录
$ gh auth logout
gh auth login 时,我们也可以使用事先生成的 PAT。第 4 个问题,How would you like to authenticate GitHub CLI?,选择 Paste an authentication token,即可。
总结
git 支持 HTTPS 和 SSH 协议,不同协议使用不同认证方式。
我们可以自己生成 PAT,也可用 gh 登录生成 OAuth token。借助 git 的 credentials helper 机制,我们可以让 HTTPS 协议下的 git 自动使用这些 token,而不必手动输入。