Git 的 HTTPS 凭据和 SSH 密钥
摘要
- 本文写
git连接 GitHub 等远程仓库时的两种认证:HTTPS 远程用凭据助手保存令牌,SSH 远程用密钥。macOS 的凭据助手按钥匙串osxkeychain来写。本文按 Git 2.56.0。 - 令牌怎么创建见 创建 GitHub Personal Access Token。两个账号各用一把密钥见 同一台电脑使用两个 GitHub 账号。
credential.helper所在的配置文件见 Git 的用户配置 ~/.gitconfig。 - gitcredentials,Adding a new SSH key to your GitHub account。
走哪一种由远程地址决定
GitHub 从 2021 年 8 月起,HTTPS 的 git push、git pull 不再接受账号密码,密码栏要填 Personal Access Token。每次手动粘贴令牌太麻烦,所以要么把令牌交给凭据助手保存,要么改用 SSH 密钥。
1 | git remote -v |
地址是 https://github.com/... 时走凭据助手。地址是 git@github.com:... 时走 SSH 密钥,不读凭据助手。同一台机器上两种可以并存,每个仓库按自己的远程地址选择。
不要把令牌写进地址
令牌也可以直接写在 HTTPS 地址里。@ 前面的用户名要填,GitHub 实际用令牌认证。这样 git 每次都带上令牌,不再提问:
1 | git clone https://用户名:ghp_xxxxxx@github.com/用户名/仓库名.git |
这种写法把令牌明文留在好几个地方:
-
整条地址写入该仓库的
.git/config。打包或复制这个目录时,令牌一起带走。 -
git remote -v会打印令牌原文,截图或贴日志时容易泄露。 -
执行过的
git clone、git remote set-url会留在 shell 历史里。 -
令牌过期或被删除后,每个这样配置的仓库都要逐个改地址。凭据助手只要更新一条记录。
已经这样配置的仓库,把地址改回不带令牌的写法,再交给下面的凭据助手。这条改写该仓库的 .git/config。
1 | git remote set-url origin https://github.com/用户名/仓库名.git |
令牌已经出现在截图、日志或别人能读到的目录里时,只改地址不够。还要到 GitHub 删除这张令牌,重新生成一张,见 创建 GitHub Personal Access Token。
Git 2.37 起,可以禁止地址里带凭据。下面这条写入 ~/.gitconfig;之后 clone、fetch、push 遇到 remote.<name>.url 里有用户名和密码时,直接报错。取值 warn 只警告,默认的 allow 不检查。写在 remote.<name>.pushurl 里的凭据不在检查范围内。
1 | git config --global transfer.credentialsInUrl die |
HTTPS:凭据助手
令牌要有对应的权限
钥匙串里已经存了令牌,只说明 GitHub 认得出这张令牌。仓库操作还要看令牌上勾了什么。创建时怎么勾,见 创建 GitHub Personal Access Token。
-
克隆、拉取、推送普通文件:细粒度令牌要有 Contents 的 write,只读仓库用 read。经典令牌勾
repo。 -
推送
.github/workflows下的文件:细粒度令牌还要 Workflows 的 write。经典令牌在repo之外再勾workflow。 -
推送被拒绝、报错里出现
workflow或403时,先看这张令牌的权限。助手里有令牌,和这张令牌能改工作流文件,是两件事。
权限不够时,到 GitHub 上改这张令牌的权限,或另建一张再更新钥匙串。已经发出去的令牌字符串不会因为后来补勾权限而换掉。
选择助手
--global 写入 ~/.gitconfig,这个用户的所有仓库都会使用。设置后执行一次 git pull,按提示填写用户名和令牌;之后由助手提供,不再提问。
1 | # macOS,存入钥匙串 |
Windows 安装 Git Credential Manager 后,通常已经自动配置好助手。
store 把凭据以明文写进 ~/.git-credentials,任何能读这个文件的人都能拿到令牌。只在个人机器上、并且清楚这个风险时使用。
1 | git config --global credential.helper store |
从 store 换成钥匙串
git config --global credential.helper osxkeychain 只改 ~/.gitconfig 里的 credential.helper。~/.git-credentials 不会被删除或清空。助手改成 osxkeychain 之后,Git 不再读这个明文文件,但里面的令牌还在磁盘上。确认钥匙串里已经能推送之后,再自己删掉 ~/.git-credentials。
已有仓库不用逐个重配。钥匙串按协议和主机名保存,默认不区分仓库路径。同一个 https://github.com 只在钥匙串里还没有这项时问一次,之后这个主机上的其他 HTTPS 仓库共用这一条。Git 不会把 ~/.git-credentials 里的旧记录搬进钥匙串。
查看已保存的账号
钥匙串和 ~/.git-credentials 都要看。git credential-osxkeychain 不能列出全部记录。打开「钥匙串访问」,搜索 github.com(或其他主机名)。每条的帐户是用户名,勾选「显示密码」才看得到令牌。
知道主机名时,可以先只看用户名。带 -g 的那条,以及 get、cat,会把令牌打在终端上。
1 | # 只打印 github.com 这条的用户名 |
store 留下的文件还在时,一行一条,格式是 https://用户名:令牌@主机:
1 | cat ~/.git-credentials |
gh auth status 只列出 gh 登录的账号和存放位置,不打印令牌。
清掉旧凭据
没有提问就报 Authentication failed 时,钥匙串里多半已有旧的 github.com 条目。清掉后再操作一次,就会重新提问。这条只删除钥匙串里的对应项,不动 ~/.git-credentials。
1 | printf "protocol=https\nhost=github.com\n\n" | git credential-osxkeychain erase |
和 gh 的关系
以前执行过 gh auth setup-git 时,github.com 的 HTTPS 可能仍由 gh 提供凭据,钥匙串那一条不会被用到。在仓库里看实际生效的助手和它所在的文件:
1 | git config --show-origin --get-regexp 'credential\..*helper' |
SSH:密钥
生成密钥
推荐 ed25519,密钥短、速度快。生成的是 ~/.ssh/id_ed25519(私钥)和 ~/.ssh/id_ed25519.pub(公钥)。已有同名文件时,命令会询问是否覆盖;覆盖后,旧公钥对应的服务器都要重新添加。
1 | # -C 只是备注,便于在服务器上辨认这把密钥,不必是邮箱 |
服务器只支持 RSA 时:
1 | ssh-keygen -b 4096 -t rsa -C "your_label" |
把公钥加到服务器
把 ~/.ssh/id_ed25519.pub 的整行复制到服务器的 SSH 公钥设置页。私钥 ~/.ssh/id_ed25519 留在本机,不要上传。
-
GitHub:头像 → Settings → SSH and GPG keys → New SSH key。
-
Coding:个人设置 → SSH 公钥。
-
Gitee:设置 → 安全设置 → SSH 公钥。
逐个测试。GitHub 成功时输出 Hi 用户名! You've successfully authenticated。
1 | ssh -T git@github.com |
账号密钥没有分项权限
加在个人账号 SSH and GPG keys 里的认证密钥,能做的事和这个账号在网页上能做的 Git 操作相同。账号对仓库有写权限,用这把密钥就可以 git push,包括 .github/workflows。密钥上不能再勾 Contents、workflow 这种分项。要按接口收权限,用 创建 GitHub Personal Access Token 里的令牌。
ssh-keygen 时设置的口令只锁本机私钥。GitHub 不读取这句口令,也不用它区分只读和可写。
只想让一把密钥访问一个仓库时,把公钥加到该仓库的 Settings → Deploy keys,不要加到个人账号。
-
不勾 Allow write access:只能克隆和拉取。
-
勾上之后:可以往这一个仓库推送。
-
同一把公钥不能同时作为多个仓库的 Deploy key。
-
Deploy key 仍然只区分这一个仓库的只读和可写,不能拿来调 REST API。
使用 SSH 地址
新克隆时使用 git@ 开头的地址:
1 | git clone git@github.com:hanqunfeng/reactive-redis-cache-annotation-spring-boot-starter.git |
已经用 HTTPS 克隆的仓库,改远程地址即可。这条写入该仓库的 .git/config。
1 | git remote set-url origin git@github.com:用户名/仓库名.git |
ssh -T 能打出用户名,只说明这把密钥被服务器接受。推送仍然失败时,先看 git remote -v 的地址是不是 git@ 开头,以及这个账号对仓库有没有写权限。