SSH 公钥登录配置详解(iTerm2 为例)

SSH 服务的远程登录服务方式,通常有密码登录公钥登录两种。公钥登录方式的好处就不赘述了,本文详解 SSH 公钥登录的配置过程,以及其中常见的坑。
Note:本文所有实验均在 Mac 下的 iTerm2 中操作。

配置单个主机的 SSH 公钥登录,步骤非常简单,三步即可:

  1. 生成密钥对
  2. 拷贝公钥文件到服务器
  3. SSH 直接公钥登录服务器

步骤详解

ssh-keygen 生成密钥对

ssh-keygen 命令可以按照指定的参数生成密钥对,其常用的参数有以下几个:

-t : 加密算法,可选 [dsa | ecdsa | ed25519 | rsa];
-b :密钥长度(256/512/1024/2048……),建议至少1024;
-f : 输出的密钥对文件名;
-N :密钥口令。

比如我要为 test 主机生成一对密钥文件,密钥长度 2048、加密算法 RSA,就可以用以下命令:

1
~ ➜ ssh-keygen -f test -t rsa -b 2048

执行此命令会提示 Enter passphrase (empty for no passphrase):,如果需要密钥口令,就要填上并记下来,如果不需要就直接摁回车键直到结束。命令执行完成后,会在当前目录生成两个文件:私钥文件 test 和 公钥文件 test.pub 。如果不指定 ssh-keygen 命令的 -f 参数,则会默认生成 ~/.ssh/id_ras~/.ssh/id_ras.pub 两个文件 。

ssh-copy-id 拷贝公钥到服务器

ssh-copy-id 命令负责把上述步骤生成的公钥文件内容拷贝到服务器,确切说,是把 xxx.pub 文件中的内容附加到服务器端的 ~/.ssh/authorized_keys 文件中。

ssh-copy-id 命令使用方式如下:

1
~ ➜ ssh-copy-id [-i <identityfile>] [-p <port>] [username@]hostname

其中 -i 即指定上述步骤生成的密钥对中的 公钥文件,意思是把公钥文件 xxx.pub 传到服务端 hostname,把 xxx.pub 中的内容附加到 hostname:/home/username/.ssh/authorized_keys 文件中。如果不用 -i 指定公钥文件,则默认指定 id_ras.pub

举例来说,我想通过公钥登录我 192.168.1.2 上的 jiayu 账户,那么只需把上述步骤中生成的 test.pub 文件上传到该服务器即可:

1
~ ➜ ssh-copy-id -i test jiayu@192.168.1.2

运行这条命令会让输入一次账户密码,然后才能成功上传到服务端。

另外,还有好多人在这一步会舍弃 ssh-copy-id 命令,转而手动上传公钥文件到服务端,然后 cat xxx.pub >> ~/.ssh/authorized_keys ,个人不提倡这种做法, ssh-copy-id 这条专用命令就是为了免去麻烦又容易失误的手动操作。

直接登录远程服务器

经过上面两步骤,没什么意外的话就可以用密钥登录远程服务器了。不过这里还有个小细节需要说一下,我们平时用密码 SSH 登录远程服务器,是用 ssh username@host -p <port> 命令然后输入密码。如果我们只有一台远程服务器需要用 SSH 公钥登录的方式,那么最初执行 ssh-keygen 命令的时候就可以不指定 -f 参数,默认生成密钥对文件 id_ras/id_isa.pub ,然后执行命令 ssh username@host -p <port> 不用输入密码即可登录,SSH 命令会默认调用 id_rsa 尝试连接。

但如果执行 ssh-keygen 命令的时候指定了 -f test 参数,那么这时要登录远程服务器就要在 SSH 命令中也指定私钥文件:

1
~ ➜ ssh -i test jiayu@192.168.1.2

成功登录,配置完成,即可删除本地的公钥文件 xxx.pub,只留下相应的私钥文件。

踩坑

有的朋友会发现,严格按照上述步骤操作,也无法成功连接服务端。此处常见的坑有 2 个:

  1. 服务端 ~/.ssh/ 目录和 ~/.ssh/authorized_keys 文件的权限问题;
  2. 服务端 ~/.ssh/authorized_keys 文件内容格式问题。

服务端相关目录和文件权限

个人见过的情况,把服务端 ~/.ssh/ 目录权限设置为 700 ,把 ~/.ssh/authorized_keys 文件权限设置为 600 即可。

authorized_keys 中 pubkey 内容要换行

有时候,服务端 SSH 目录和文件权限都没问题,仍然连接失败。此时给 SSH 命令启用 -v 选项打出连接时的详细信息,会发现客户机已经把私钥发送到服务器端,但在服务器端没有验证成功。

并且,在服务端的 SSH 服务日志 /var/log/secure 文件中可以查到类似以下内容的错误信息:

1
error: key_read: uudecode AAAAB3N failed

此时就是服务端 ~/.ssh/authorized_keys 文件内容格式出了问题。根源在于 ssh-copid 命令默认把公钥文件内容直接附加到服务端 authorized_keys 末尾,而没有换行

解决方案也很简单:Vim 打开服务端 ~/.ssh/authorized_keys 文件,为每个 pubkey 内容手动隔开一行即可。

配置多服务器多公钥登录

前面举的例子都是针对单一服务器 SSH 公钥登录配置,如果我有多台服务器都需要配置成 SSH 公钥登录,该怎么配置?

这里有个便捷的方式,两步即可完成配置:

  1. 为每台服务器按照上面的 三步走 生成密钥对文件,配置 SSH 公钥登录后,整理好本地所有的私钥文件,最好所有的私钥文件统一放到同一目录下,比如 ~/.ssh/priKeys/
  2. 编写本地 SSH 配置文件: ~/.ssh/config

假如我的服务端主机与主机别名(方便自己记忆而起的别名)列表如下:

1
2
3
4
192.168.120.1 jiayu1
192.168.120.2 jiayu2
192.168.110.3 jiayu3
192.168.110.4 jiayu4

那么我的本地 ~/.ssh/config 文件配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Host jiayu1 #别名
Hostname 192.168.120.1 #服务器地址
Port 22 #服务器 SSH 服务端口
User root #服务端的用户名
IdentityFile ~/.ssh/priKeys/jiayutest1 #对应本地的私钥文件路径
Host jiayu2
Hostname 192.168.120.2
Port 22
User root
IdentityFile ~/.ssh/priKeys/jiayutest2
Host jiayu3
Hostname 192.168.110.3
Port 22
User root
IdentityFile ~/.ssh/priKeys/jiayutest3
Host jiayu4
Hostname 192.168.110.4
Port 22
User root
IdentityFile ~/.ssh/priKeys/jiayutest4

各配置项的意义见上面的注释内容,这样一来,我就可以用 主机别名 直接登录远程服务器了。比如通过 SSH 公钥登录 root@192.168.110.4 ,以下命令即可:

1
~ ➜ ssh jiayu4

这样,ssh 命令会自动根据 ~/.ssh/config 中的配置项,寻找到 jiayutest 这一个私钥文件,然后拿它去登录 root@192.168.110.4