这次你从 “git push 提示登录失败、Permission denied (publickey)” 一路排查到成功推送,其实已经把 SSH 的关键知识都走了一遍。

一、为什么 git push 会要求“登录”?

现在 GitHub 已经不再接受账号+密码的方式,推荐两种认证方式:

  • SSH key(最常用,也最省心)
  • Personal Access Token(PAT) + HTTPS

你遇到的是 SSH 场景下的错误:

git@github.com: Permission denied (publickey).

含义很简单:

“我只接受用公钥认证,但你给我的这些钥匙里,没有一把是我认识的。”

也就是说:本地用的私钥,和 GitHub 那边保存的公钥没配上。


二、SSH 密钥对:私钥 + 公钥

在本地生成 SSH 密钥时,我们用的命令类似:

ssh-keygen -t ed25519 -C "your_email@example.com"

各部分含义:

  • ssh-keygen:生成 SSH 密钥对的工具
  • -t ed25519:使用 ed25519 算法(比传统 RSA 更推荐)
  • -C "...":给这把钥匙加一个“备注”,通常写邮箱,方便以后识别

生成后会在 ~/.ssh 下出现一对文件:

  • 私钥:例如 id_ed25519
  • 公钥:例如 id_ed25519.pub

关系可以理解为:

  • 私钥:只在你自己电脑上保存,绝对不能对外泄露
  • 公钥:可以复制到 GitHub 或服务器,用来识别“这台机器”

认证过程大致是:

  1. 你用私钥在本地做签名
  2. GitHub 用保存的公钥验证签名
  3. 如果能验证通过,就说明你确实拥有对应的私钥,身份 OK

三、~/.ssh 目录里的常见文件

典型情况下,你的 ~/.ssh 下会有这些文件(名字略有不同没关系):

  • 私钥
    • id_ed25519
    • github
  • 公钥
    • id_ed25519.pub
    • github.pub
  • 配置文件
    • config
  • 信任主机列表
    • known_hosts
    • known_hosts.old

它们的作用分别是:

  • id_ed25519 / github:本机私钥,SSH 认证时用
  • id_ed25519.pub / github.pub:公钥,要复制到 GitHub 等远端服务
  • config:SSH 的“路由表”,告诉 SSH:
    • 访问哪个 Host 时,用哪把私钥、哪个用户名等
  • known_hosts
    • 记录“我信任哪些远程主机指纹”
    • 第一次连接某个主机时会问 yes/no,你选 yes 后就写入这里
    • 以后对比指纹,防止中间人攻击

四、~/.ssh/config:SSH 的“自动配置表”

~/.ssh/config 文件的作用,是给 SSH 定义按 Host 维度的规则,例如:

Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/github
  IdentitiesOnly yes

这段规则可以这样理解:

  • 当你访问 github.com
    • HostName:实际连接的主机是 github.com
    • User:用 git 这个用户名(GitHub SSH 固定是 git)
    • IdentityFile:只用 ~/.ssh/github 这把私钥
    • IdentitiesOnly yes:不要乱试别的钥匙

有了这段配置后:

  • 你只需要写 ssh -T git@github.com 或直接 git push
  • SSH 会自动:
    • 识别这是 github.com
    • 只拿出 ~/.ssh/github 私钥去认证
  • 只要这把私钥对应的公钥已经添加到 GitHub 账号中,认证就能一次通过

五、没有配置 Host 时,SSH 默认怎么做?

如果你完全不写 Host github.com 这类配置:

  • SSH 会按照默认规则,从“默认密钥文件 + ssh-agent 中的 key”里依次尝试几把私钥
  • 让远端服务一个个试图用它保存的公钥来验证
  • 如果在限定次数内,有一把能配上,就通过;都配不上,就 Permission denied (publickey)

问题在于:

  • 你可能有多把 key(私钥很多),但 GitHub 只认识其中一把
  • SSH 不一定会刚好选对那把;或者试错次数超过远端限制,直接被拒

所以我们在 config 里针对 github.com 写清楚 IdentityFile,就是为了:

“访问 GitHub 就只用这把你已经在 GitHub 配过的私钥。”

这也是你在补全 config 后,问题立刻解决的根本原因。


六、ssh -T git@github.com 和那句“does not provide shell access”

当你执行:

ssh -T git@github.com

如果配置正确,会看到类似提示:

Hi your-username! You've successfully authenticated, but GitHub does not provide shell access.

这句话的含义是:

  • 认证结果:你已经用 SSH key 成功通过身份验证 ✅
  • 限制说明:GitHub 不像一台普通 Linux 服务器,不允许你登录进去拿到 shell,只能通过 Git 协议进行拉取 / 推送等操作

对我们来说,只要看到这句话,就可以认为 SSH 配置成功,可以继续 git push 了。


七、SSH + GitHub 的完整闭环

总结一下从配置到推送的完整流程:

  1. 本地生成 SSH 密钥对

    ssh-keygen -t ed25519 -C "your_email@example.com"
    # 在提示保存路径时,如果已有 id_ed25519,想新生成一把:
    # 输入一个新文件名,比如:/Users/you/.ssh/github
    
  2. 把公钥添加到 GitHub

    cat ~/.ssh/github.pub
    
    • 复制输出内容
    • 打开 GitHub → Settings → SSH and GPG keys → New SSH key
    • 粘贴并保存
  3. ~/.ssh/config 中为 GitHub 指定这把 key

    Host github.com
      HostName github.com
      User git
      IdentityFile ~/.ssh/github
      IdentitiesOnly yes
    
  4. 确认远程仓库使用 SSH 地址

    cd /path/to/your/repo
    git remote -v
    # 确认是:
    # origin  git@github.com:username/repo.git (fetch)
    # origin  git@github.com:username/repo.git (push)
    
  5. 测试 SSH 连接

    ssh -T git@github.com
    

    出现 successfully authenticated, but GitHub does not provide shell access 即成功。

  6. 正常使用 git 推送

    git push
    

八、几个容易踩坑的小点

  • ssh-keygen 提示保存路径时不要随便回车

    • 已经有 id_ed25519 时,直接回车会覆盖旧 key
    • 想要多一把独立的 key,一定要输入新文件名(带绝对路径)
  • ssh-keygen 中输入 ~/.ssh/xxx 有时会报 “No such file or directory”

    • 有些环境下,这个输入不会自动展开 ~
    • 最稳妥的写法是:/Users/你的用户名/.ssh/xxx
  • 多个 key 混在一起时一定用 config 指定 Host

    • 否则 SSH 默认“轮询尝试”,一旦超过服务器允许的 key 尝试次数,会直接拒绝

通过这次实战,你已经把 SSH + GitHub 这一块的核心知识都走通了。以后只要记住这几点:

  • 一对密钥:本地保私钥,远程存公钥
  • config 决定“去哪用哪把钥匙”
  • 看到 “successfully authenticated, but GitHub does not provide shell access” 就说明 SSH OK,可以放心 git push

如果之后你在别的机器上也要配 GitHub,只要复刻这一整套流程,很快就能搞定。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐