SSH 密钥管理
Table of Contents
1 密钥是什么
密钥 key 是一个非常大的数字,通过加密算法得到。对称加密只需要一个密钥。非对称加密需要两个密钥成对使用,分为公钥 public key 和私钥 private key 。
SSH 密钥登录采用的是非对称加密,每个用户凭各自生成的密钥对登录。公钥和私钥一一对应,每一个私钥都有且仅有一个对应的公钥,反之亦然。
私钥自己持有,不能泄露;公钥可以对外发送。
如果数据被公钥加密,那么有且只有对应的私钥才能解密;如果数据被私钥加密 (这个过程一般称为 签名),也只有使用对应的公钥解密。
2 密钥登录的过程
SSH 密钥登录要经历以下过程:
- 客户端通过
ssh-keygen
程序生成自己的公钥和私钥。 - 手动将客户端的公钥放入远程服务器的指定位置。
- 客户端向服务器发起 SSH 登录的请求。
- 服务器收到用户 SSH 登录请求,向用户发送随机数据,等待用户证明自己的身份。
- 客户端用私钥对将服务器发来的数据数据进行签名,然后发还给服务器。
- 服务器使用对应的公钥解密客户端发回的数据,与原始数据比较。如果一致,允许用户登录。
3 用 ssh-key 命令生成密钥
3.1 基本用法
密钥登录前需要生成公钥和私钥。OpenSSH 提供了指令程序 ssh-keygen
用来生成密钥。
ssh-keygen -t <encryption-method>
中 -t
参数指定了密钥的加密算法。可选 dsa
, rsa
, ecdsa
, ecdsa-sk
, ed25519
, ed25519-sk
等。
虽然 dsa
和 rsa
用的比较多,但 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
指定生成密钥的加密算法,一般为 dsa 或 rsa 。
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 以使新配置生效。