git 使用 ssh 常见错误之 - Permission denied (publickey)

in OS with 0 comment

起因

由于换新的服务器,之前搭建的 Gogs 也是旧版本(一直懒得升级),于是就重新拉取了docker 镜像,搭建服务的过程一切都顺利。在配置系统时,看见启用两步验证,手机也刚好安装了 Google Authenticator,结果悲剧了迁出代码的时候需要加上临时凭证,感觉一下子就麻烦了起来(通过 https 的方式接取推送代码)。

想了一想,能不能鱼和熊掌兼得,既有 Web 安全登录,又可以顺利拉取推送代码(这是刚需,不然玩个寂寞)。于是就和标题里面的 Permission denied 展开搏斗(主要是自己技术不精)。

使用 docker 搭建 Gogs,请参阅:Docker for Gogs

场景还原

我使用的是 win10 系统,安装了 git,使用的 shell 是 git-bash

首先,使用 ED25519 算法生成的公私钥文件(名为 gogs 的私钥,gogs.pub 的公钥)。

ssh-keygen -t ed25519 -f gogs -C "your@email.com"

其次,按照文档说明在站点的个人设置里配置好了公钥。并在系统盘,用户目录下, 配置了%userprofile%/.ssh/config 文件,添加了如下配置:

Host gogs
    Hostname 10.0.0.10
    User git
    Port 10022
    IdentityFile ~/.ssh/gogs
    IdentitiesOnly yes

关于配置选项请参阅:list of client coinfiguration-options

.ssh 目录主要存储 ssh 服务相关文件,config 文件是用来配置经常访问的主机连接信息(如 IP 地址、用户名、私钥)。

使用 config 文件的好处是,平时我们使用 ssh 连接远程主机时通常使用下面两种命令:

  1. 密码形式

    ssh git@10.0.0.10 -p 10022 # 回车后会提示输入密码
  2. 密钥文件形式

    ssh -i ~/.ssh/gogs git@10.0.010 -p 10022

上面的命令每次输入,就会变得繁琐,然而配置 config 文件后,命令就可以简化为:

ssh gogs
# 语法: ssh <host>

接下来,尝试使用 git bash 尝试推送代码,结果就报了 Permission denied 错误。

原因及解决方案

经过一番搜索后,得出结论:问题是出在 ssh-agent 身上。

ssh-agent 是客户端 ssh 的默认代理,其作用是管理私钥,负责 ssh 连接的整个认证过程(本质就是一个验证身份的程序)。

关于 ssh-agent 的详细解释,请参阅:ssh-agent 详解

为什么这样说呢?因为在使用 ssh-keygen 时如果一路回车下去,命令最终会生成如 id_xxx 的公私钥文件(xxx 指的是算法),这些文件通常会作为 ssh-agent 的默认文件。这样的公私钥也不会有问题,问题是我自定义了公私钥文件名称。

这里摘自 ssh-agent 文档:在 Linux 系统下,ssh-agent 通常会在登录系统时自动配置并运行。默认会使用用户目录下 .ssh 目录中的私钥文件,在简单模式下,只会加载 ~/.ssh/id_rsa, .ssh/id_dsa, ~/.ssh/id_ecdsa, ~/.ssh/id_ed25519, and ~/.ssh/identity 等文件。如果要使用其它文件私钥文件,需要使用 ssh-add 添加。

这也就解释了,为什么名 gogs 私钥文件一直没有起作用的原因。

接下来,只需要添加 gogs 私钥文件给 ssh-agent 就好了:

ssh-add ~/.ssh/gogs

如果执行 ssh-add 命令时出现 Could not open a connection to your authentication agent 错误,请尝试运行命令(这个在 ssh-agent 里也有提到):

eval `ssh-agent`

再次尝试推送,就可以了。

总结

解决方案主要参考了 Github Error: Permission denied (publickey) 的文档,其中最主要部分是如何查找错误,其主要命令如下:

ssh -vT git@10.0.0.10 -p 10022

参数 -T 是调试命令,-v 则是输出详情,结合起来就可以看到连接远程主机的整个过程,以及过程中尝试查找的私钥文件,最后选择了哪个私钥文件与远程主机交互。

下面是 Github 文档里的输出示例,其中最后的 -1 代表没有找到此文件。
$ ssh -vT git@github.com
> ...
> debug1: identity file /Users/you/.ssh/id_rsa type -1
> debug1: identity file /Users/you/.ssh/id_rsa-cert type -1
> debug1: identity file /Users/you/.ssh/id_dsa type -1
> debug1: identity file /Users/you/.ssh/id_dsa-cert type -1
> ...
> debug1: Authentications that can continue: publickey
> debug1: Next authentication method: publickey
> debug1: Trying private key: /Users/you/.ssh/id_rsa
> debug1: Trying private key: /Users/you/.ssh/id_dsa
> debug1: No more authentication methods to try.
> Permission denied (publickey).

运行这个命令来验证我们的私钥是否被加载。私钥没有被正确加载就是 Permission denied (publickey) 错误的一个最大的原因。

Comments are closed.