李守中

SSH 密钥管理

Table of Contents

1 密钥是什么

密钥 key 是一个非常大的数字,通过加密算法得到。对称加密只需要一个密钥。非对称加密需要两个密钥成对使用,分为公钥 public key 和私钥 private key

SSH 密钥登录采用的是非对称加密,每个用户凭各自生成的密钥对登录。公钥和私钥一一对应,每一个私钥都有且仅有一个对应的公钥,反之亦然。

私钥自己持有,不能泄露;公钥可以对外发送。

如果数据被公钥加密,那么有且只有对应的私钥才能解密;如果数据被私钥加密 (这个过程一般称为 签名),也只有使用对应的公钥解密。

2 密钥登录的过程

SSH 密钥登录要经历以下过程:

  1. 客户端通过 ssh-keygen 程序生成自己的公钥和私钥。
  2. 手动将客户端的公钥放入远程服务器的指定位置。
  3. 客户端向服务器发起 SSH 登录的请求。
  4. 服务器收到用户 SSH 登录请求,向用户发送随机数据,等待用户证明自己的身份。
  5. 客户端用私钥对将服务器发来的数据数据进行签名,然后发还给服务器。
  6. 服务器使用对应的公钥解密客户端发回的数据,与原始数据比较。如果一致,允许用户登录。

3 用 ssh-key 命令生成密钥

3.1 基本用法

密钥登录前需要生成公钥和私钥。OpenSSH 提供了指令程序 ssh-keygen 用来生成密钥。

ssh-keygen -t <encryption-method>-t 参数指定了密钥的加密算法。可选 dsa, rsa, ecdsa, ecdsa-sk, ed25519, ed25519-sk 等。

虽然 dsarsa 用的比较多,但 rsa 兼容性更好。而 ecdsa 在相同兼容性的情况下,性能最好。

比如要生成一个 ecdsa 密钥,执行 ssh-keygen -t ecdsa 后,会要求用户回答一些问题:

Generating public/private dsa key pair.
Enter file in which to save the key (/home/username/.ssh/id_dsa):  press ENTER
Enter passphrase (empty for no passphrase): ********
Enter same passphrase again: ********
Your identification has been saved in /home/username/.ssh/id_dsa.
Your public key has been saved in /home/username/.ssh/id_dsa.pub.
The key fingerprint is:
14:ba:06:98:a8:98:ad:27:b5:ce:55:85:ec:64:37:19 [email protected]

示例中,第一个问题询问密钥保存路径,默认是 ~/.ssh/id_dsa 文件,这个是私钥的文件名,对应的公钥文件 ~/.ssh/id_dsa.pub 是自动生成的。

如果选择 rsa 算法,密钥文件默认是 ~/.ssh/id_rsa (私钥) 和 ~/.ssh/id_rsa.pub (公钥)。

第二个问题,询问是否给私钥文件设密码 (passphrase)。这样即使入侵者拿到私钥,还需要破解密码才能使用。如果为了方便不设密码,直接按回车键密码就会为空。后面还会让你再输入一次密码,两次输入必须一致。注意,这里 密码 的英文单词是 passphrase ,避免与 Linux 账户的密码单词 password 混淆。

最后生成私钥和公钥,屏幕上给出公钥的指纹以及当前的用户名和主机名作为注释,用于识别密钥的来源。

公钥文件和私钥文件都是文本文件,可以用文本编辑器看一下它们的内容。公钥文件的内容类似下面这样。

ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAvpB4lUbAaEbh9u6HLig7amsfywD4fqSZq2ikACIUBn3GyRPfeF93l/weQh702ofXbDydZAKMcDvBJqRhUotQUwqV6HJxqoqPDlPGUUyo8RDIkLUIPRyqypZxmK9aCXokFiHoGCXfQ9imUP/w/jfqb9ByDtG97tUJF6nFMP5WzhM= [email protected]

上面示例中,末尾的 [email protected] 是公钥的注释,用来识别不同的公钥,表示这是主机 shell.isp.com 的用户 username 的公钥。注释不是必需项。

自动生成的公钥文件通常以 .pub 结尾。

给密钥文件正确的权限: chmod 600 ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa.pub 否则 ssh 将不使用这些密钥文件。

3.2 配置项

ssh-keygen 命令的常用配置项有下面这些:

  • -b 指定密钥的二进制位数。这个值越大,密钥就越不容易破解,对应地,加密数据的算力开销也会增加。一般来说,rsa 算法的 -b 至少应该是 1024,通常是 2048,个人喜欢用 4096。
  • -C 为密钥文件指定新的注释,格式为 <user_name>@<host>

ssh-keygen -t rsa -b 4096 -C "<comment>" 命令生成一个 4096 位 RSA 加密算法的密钥对,并加了注释。

  • -f 指定生成的私钥文件: ssh-keygen -t dsa -f <key-file> 在当前目录生成私钥文件 <key-file> 和公钥文件 <key-file>.pub
  • -F 检查某个主机名是否在 known_hosts 文件里面: ssh-keygen -F <host>
  • -N 指定私钥的密码 passphrase: ssh-keygen -t dsa -N <password>
  • -p 重新指定私钥密码。与 -N 的不同之处在于,新密码不在命令中指定,而是执行后再输入。ssh 先要求输入旧密码,然后要求输入两遍新密码。
  • -R 将指定的主机公钥指纹移出 ~/.ssh/known_hosts 文件: ssh-keygen -R example.com
  • -t 指定生成密钥的加密算法,一般为 dsarsa

4 手动上传公钥

生成密钥以后,公钥必须上传到服务器,才能正常使用密钥对登录。

OpenSSH 规定,用户公钥保存在服务器的 ~/.ssh/authorized_keys 文件。要登陆的用户的密钥必须保存在该用户主目录 ~/.ssh/authorized_keys 文件中。只要把公钥添加到这个文件之中,就相当于公钥上传到服务器了。每个公钥占据一行。如果该文件不存在,可以手动创建。

用户可以手动编辑该文件,把公钥粘贴进去,也可以在本机计算机上,执行下面的命令:

cat ~/.ssh/id_rsa.pub | ssh <user_name>@<host> "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

<user_name>@<host> 替换成所要登录的用户名和主机名。

ssh 为了安全,对属主的目录和文件权限有所要求。如果权限不对,则 ssh 密钥或公钥不生效:

  • 用户家目录权限为 755 或者 700,不能是 77x 或 777,即保证 other 用户不能有 w 权限。
  • ~/.ssh 目录权限一般为 755 或者 700。
  • 公钥文件 (rsa_id.pub 及 ~/.ssh/authorized_keys) 权限一般为 644 或 600。
  • 私钥权限必须为 600。

只要公钥上传到服务器,下次登录时,OpenSSH 就会自动采用密钥登录,不再提示输入密码。

5 用 ssh-copy-id 命令自动上传公钥

OpenSSH 自带一个 ssh-copy-id 命令,可以自动将公钥记录到远程服务器的 ~/.ssh/authorized_keys 文件中。如果文件不存在则自动创建该文件。

在本地计算机执行 ssh-copy-id -i <key-file> <user_name>@<host> 将本地的公钥拷贝到服务器。其中 -i 参数指定公钥文件, user_name 是所要登录的账户名, <host> 是服务器地址。如果省略用户名,默认为当前的本机用户名。

注意,公钥文件可以不指定路径和 .pub 后缀名,ssh-copy-id 会自动在 ~/.ssh 目录里面寻找。比如执行 ssh-copy-id -i id_rsa <user_name>@<host> 公钥文件会自动匹配到 ~/.ssh/id_rsa.pub 。

ssh-copy-id 采用密码登录,系统会提示输入远程服务器的密码。

注意: ssh-copy-id 直接将公钥添加到 ~/.ssh/authorized_keys 文件的末尾。如果文件的末尾不是一个换行符,会导致新的公钥添加到前一个公钥的末尾,两个公钥连在一起,使得两条公钥都无法生效。

所以,如果 ~/.ssh/authorized_keys 文件已经存在,使用 ssh-copy-id 命令之前,务必保证文件的末尾是换行符 (即,文件末尾有一个空行)。

6 ssh-agent 与 ssh-add 命令

6.1 基本用法

私钥设置了密码以后,每次使用都必须输入密码,有时让人感觉非常麻烦。比如,连续使用 scp 命令远程拷贝文件时,每次都要求输入密码。

ssh-agent 命令就是为了解决这个问题而设计的,它让用户在整个 Bash 对话 (session) 之中,只在第一次使用 SSH 命令时输入密码,然后将私钥保存在内存中,后面都不需要再输入私钥的密码了。

第一步,推荐的方式是用 ssh-agent $SHELL 新建一个子 SHELL,ssh-agent 进程会随子 SHELL 退出结束。

如果想在当前对话启用 ssh-agent,可以使用 :

# linux 命令,注意是反引号
$ eval `ssh-agent`
# windows 命令
$ eval $(ssh-agent)

ssh-agent 会先自动在后台运行,并将需要设置的环境变量输出在屏幕上,类似下面这样。

SSH_AUTH_SOCK=/tmp/ssh-AUBdzk7V7WAR/agent.5861; export SSH_AUTH_SOCK;
SSH_AGENT_PID=5862; export SSH_AGENT_PID;
echo Agent pid 5862;

eval 的作用,就是运行上面的,执行 ssh-agent 命令后的输出,设置环境变量。

注意,不建议以这种方式在当前对话启用 ssh-agent。 使用 eval 启动的 ssh-agent 进程必须被手动杀死,不会随 SHELL 退出。

第二步,在新建的 Shell 对话里面,使用 ssh-add </path/to/private-key-file> 命令添加默认的私钥文件。

$ ssh-add ~/.ssh/id_rsa
Enter passphrase for /home/you/.ssh/id_dsa: ********
Identity added: /home/you/.ssh/id_dsa (/home/you/.ssh/id_dsa)

上面例子中,添加私钥时,会要求输入密码。以后,在这个对话里面再使用密钥时,就不需要输入私钥的密码了,因为私钥已经加载到内存里面了。

第三步 ssh <user_name>@<remote_host> 正常登录远程服务器。

ssh 使用的是默认的私钥。这时如果私钥有密码,ssh 不再询问密码,直接取出内存里面的私钥。

如果要使用其他私钥登录服务器,需要指定私钥文件 ssh –i ~/.ssh/<key_file> <remote_host>

如果要退出 ssh-agent,可以 Ctrl+d 直接退出子 Shell,也可以执行 ssh-agent -k 退出。

6.2 ssh-add 命令

ssh-add 命令用来将私钥加入 ssh-agent,它有如下的参数。

  • -d 从内存中删除指定的私钥: ssh-add -d name-of-key-file
  • -D 从内存中删除所有已经添加的私钥: ssh-add -D
  • -l 列出所有已经添加的私钥: ssh-add -l

6.3 多主机配置实例

# gitlab
Host git.iboxpay.com
    HostName git.iboxpay.com
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/id_rsa

# github
Host github2.com
    HostName github.com
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/feygh

# github
Host github.com
    HostName github.com
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/id_rsa

7 关闭密码登录

为了安全性,启用密钥登录之后,最好关闭服务器的密码登录。

对于 OpenSSH 需要编辑 etc/ssh/sshd_config 文件,将 /PasswordAuthentication 这一项设为 no

修改配置文件以后,重新启动 sshd 以使新配置生效。



Last Update: 2023-05-27 Sat 16:16

Contact: [email protected]     Generated by: Emacs 27.1 (Org mode 9.3)

若正文中无特殊说明,本站内容遵循: 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议