SSH 密钥登录
SSH 密钥登录
SSH 默认采用密码登录,这种方法有很多缺点,简单的密码不安全,复杂的密码不容易记忆,每次手动输入也很麻烦。密钥登录是比密码登录更好的解决方案。
密钥是什么
密钥(key)是一个非常大的数字,通过加密算法得到。对称加密只需要一个密钥,非对称加密需要两个密钥成对使用,分为公钥(public key)和私钥(private key)。
SSH 密钥登录采用的是非对称加密,每个用户通过自己的密钥登录。其中,私钥必须私密保存,不能泄漏;公钥则是公开的,可以对外发送。它们的关系是,公钥和私钥是一一对应的,每一个私钥都有且仅有一个对应的公钥,反之亦然。
如果数据使用公钥加密,那么只有使用对应的私钥才能解密,其他密钥都不行;反过来,如果使用私钥加密(这个过程一般称为“签名”),也只有使用对应的公钥解密。
密钥登录的过程
SSH 密钥登录分为以下的步骤。
预备步骤,客户端通过ssh-keygen
生成自己的公钥和私钥。
第一步,手动将客户端的公钥放入远程服务器的指定位置。
第二步,客户端向服务器发起 SSH 登录的请求。
第三步,服务器收到用户 SSH 登录的请求,会生成一个随机数 R,使用客户端的公钥对 R 进行加密,然后发送给客户端,要求客户端证明自己的身份。
第四步,客户端收到服务器发来的数据,使用自己的私钥进行解密,得到随机数 R,再加上当前和服务器会话的 sessionKey,使用私钥对数据进行签名,然后再发还给服务器。
第五步,服务器收到客户端发来的加密签名后,需要进行验签,服务器此刻有 R,也有 sessionKey,使用客户端的的公钥验签。如果一致,就允许用户登录。
ssh-keygen
命令:生成密钥
基本用法
密钥登录时,首先需要生成公钥和私钥。OpenSSH 提供了一个工具程序ssh-keygen
命令,用来生成密钥。
直接输入ssh-keygen
,程序会询问一系列问题,然后生成密钥。
|
|
通常做法是使用-t
参数,指定密钥的加密算法。
|
|
上面示例中,-t
参数用来指定密钥的加密算法,一般会选择 DSA 算法或 RSA 算法。如果省略该参数,默认使用 RSA 算法。
输入上面的命令以后,ssh-keygen
会要求用户回答一些问题。
|
|
上面示例中,执行ssh-keygen
命令以后,会出现第一个问题,询问密钥保存的文件名,默认是~/.ssh/id_dsa
文件,这个是私钥的文件名,对应的公钥文件~/.ssh/id_dsa.pub
是自动生成的。用户的密钥一般都放在主目录的.ssh
目录里面。
如果选择rsa
算法,生成的密钥文件默认就会是~/.ssh/id_rsa
(私钥)和~/.ssh/id_rsa.pub
(公钥)。
接着,就会是第二个问题,询问是否要为私钥文件设定密码保护(passphrase)。这样的话,即使入侵者拿到私钥,还是需要破解密码。如果为了方便,不想设定密码保护,可以直接按回车键,密码就会为空。后面还会让你再输入一次密码,两次输入必须一致。注意,这里“密码”的英文单词是 passphrase,这是为了避免与 Linux 账户的密码单词 password 混淆,表示这不是用户系统账户的密码。
最后,就会生成私钥和公钥,屏幕上还会给出公钥的指纹,以及当前的用户名和主机名作为注释,用来识别密钥的来源。
公钥文件和私钥文件都是文本文件,可以用文本编辑器看一下它们的内容。公钥文件的内容类似下面这样。
|
|
上面示例中,末尾的username@shell.isp.com
是公钥的注释,用来识别不同的公钥,表示这是哪台主机(shell.isp.com
)的哪个用户(username
)的公钥,不是必需项。
注意,公钥只有一行。因为它太长了,所以上面分成三行显示。
下面的命令可以列出用户所有的公钥。
|
|
生成密钥以后,建议修改它们的权限,防止其他人读取。
|
|
配置项
ssh-keygen
的命令行配置项,主要有下面这些。
(1)-b
-b
参数指定密钥的二进制位数。这个参数值越大,密钥就越不容易破解,但是加密解密的计算开销也会加大。
一般来说,-b
至少应该是1024
,更安全一些可以设为2048
或者更高。
(2)-C
-C
参数可以为密钥文件指定新的注释,格式为username@host
。
下面命令生成一个 4096 位 RSA 加密算法的密钥对,并且给出了用户名和主机名。
|
|
(3)-f
-f
参数指定生成的私钥文件。
|
|
上面命令会在当前目录生成私钥文件mykey
和公钥文件mykey.pub
。
(4)-F
-F
参数检查某个主机名是否在known_hosts
文件里面。
|
|
(5)-N
-N
参数用于指定私钥的密码(passphrase)。
|
|
(6)-p
-p
参数用于重新指定私钥的密码(passphrase)。它与-N
的不同之处在于,新密码不在命令中指定,而是执行后再输入。ssh 先要求输入旧密码,然后要求输入两遍新密码。
(7)-R
-R
参数将指定的主机公钥指纹移出known_hosts
文件。
|
|
(8)-t
-t
参数用于指定生成密钥的加密算法,一般为dsa
或rsa
上传公钥
生成密钥以后,公钥必须上传到服务器,才能使用公钥登录。
OpenSSH 规定,用户公钥保存在服务器的~/.ssh/authorized_keys
文件。你要以哪个用户的身份登录到服务器,密钥就必须保存在该用户主目录的~/.ssh/authorized_keys
文件。只要把公钥添加到这个文件之中,就相当于公钥上传到服务器了。每个公钥占据一行。如果该文件不存在,可以手动创建。
手动上传
用户可以手动编辑该文件,把公钥粘贴进去,
|
|
也可以在本机计算机上,执行下面的命令。
|
|
上面示例中,user@host
要替换成你所要登录的用户名和主机名。
注意,~/.ssh/authorized_keys
文件的权限要设为644
,即只有文件所有者才能写。如果权限设置不对,SSH 服务器会拒绝读取该文件。然后要求用户输入密码登录。
具体请看
文件权限问题
小节
|
|
只要公钥上传到服务器,下次登录时,OpenSSH 就会自动采用密钥登录,不再提示输入密码。
|
|
上面例子中,SSH 客户端使用私钥之前,会要求用户输入密码(passphrase),用来解开私钥。
如何理解~/.ssh/known_hosts
文件和~/.ssh/authorized_keys
文件的区别?
-
别人通过 SSH 连我的时候,只有当发起方的公钥已经保存在
~/.ssh/authorized_keys
文件中的时候,才可以免密登录 -
当我通过 SSH 连别人的时候,目标服务器的公钥(具体来说是目标服务器的公钥的指纹),会保存在
~/.ssh/known_hosts
文件中。当目标服务器的公钥有所变化的时候,给与发起连接的一方提醒,防止有人恶意冒充目标服务器。具体请看《SSH 客户端》
ssh-copy-id 命令:自动上传公钥
OpenSSH 自带一个ssh-copy-id
命令,可以自动将公钥拷贝到远程服务器的~/.ssh/authorized_keys
文件。如果~/.ssh/authorized_keys
文件不存在,ssh-copy-id
命令会自动创建该文件。
用户在本地计算机执行下面的命令,就可以把本地的公钥拷贝到服务器。
|
|
上面命令中,-i
参数用来指定公钥文件,user
是所要登录的账户名,host
是服务器地址。如果省略用户名,默认为当前的本机用户名。执行完该命令,公钥就会拷贝到服务器。
注意,公钥文件可以不指定路径和.pub
后缀名,ssh-copy-id
会自动在~/.ssh
目录里面寻找。
|
|
上面命令中,公钥文件会自动匹配到~/.ssh/id_rsa.pub
。
甚至也可以不指定-i
参数,默认会上传~/.ssh/id_rsa.pub
。
|
|
在 Windows 环境下使用时,则默认使用/c/Users/<user_name>/.ssh/id_rsa.pub
在 Windows 环境下,PowerShell 中不自带
ssh-copy-id
命令,如果你安装了 Git Bash,可以在 Git Bash 中执行ssh-copy-id
命令。
|
|
ssh-copy-id
会采用密码登录,系统会提示输入远程服务器的密码。上传之后,下次登录时,OpenSSH 就会自动采用密钥登录,不再提示输入密码。
注意,ssh-copy-id
是直接将公钥添加到authorized_keys
文件的末尾。如果authorized_keys
文件的末尾不是一个换行符,会导致新的公钥添加到前一个公钥的末尾,两个公钥连在一起,使得它们都无法生效。所以,如果authorized_keys
文件已经存在,使用ssh-copy-id
命令之前,务必保证authorized_keys
文件的末尾是换行符(假设该文件已经存在)。
ssh-agent 命令,ssh-add 命令
基本用法
如果私钥设置了密码,那么每次使用都必须输入密码,有时让人感觉非常麻烦。比如,连续使用scp
命令远程拷贝文件时,每次都要求输入密码。
ssh-agent
命令就是为了解决这个问题而设计的,它让用户在整个 Bash 对话(session)之中,只在第一次使用 SSH 命令时输入密码,然后将私钥保存在内存中,后面都不需要再输入私钥的密码了。
第一步,使用下面的命令新建一次命令行对话。
|
|
上面命令中,如果你使用的命令行环境不是 Bash,可以用其他的 Shell 命令代替。比如zsh
和fish
。
如果想在当前对话启用ssh-agent
,可以使用下面的命令。
|
|
上面命令中,ssh-agent
会先自动在后台运行,并将需要设置的环境变量输出在屏幕上,类似下面这样。
|
|
eval
命令的作用,就是运行上面的ssh-agent
命令的输出,设置环境变量。
第二步,在新建的 Shell 对话里面,使用ssh-add
命令添加默认的私钥(比如~/.ssh/id_rsa
,或~/.ssh/id_dsa
,或~/.ssh/id_ecdsa
,或~/.ssh/id_ed25519
)。
|
|
上面例子中,添加私钥时,会要求输入密码。以后,在这个对话里面再使用密钥时,就不需要输入私钥的密码了,因为私钥已经加载到内存里面了。
如果添加的不是默认私钥,ssh-add
命令需要显式指定私钥文件。
|
|
上面的命令中,my-other-key-file
就是用户指定的私钥文件。
第三步,使用 ssh 命令正常登录远程服务器。
|
|
上面命令中,remoteHost
是远程服务器的地址,ssh 使用的是默认的私钥。这时如果私钥设有密码,ssh 将不再询问密码,而是直接取出内存里面的私钥。
如果要使用其他私钥登录服务器,需要使用 ssh 命令的-i
参数指定私钥文件。
|
|
最后,如果要退出ssh-agent
,可以直接退出子 Shell(按下 Ctrl + d),也可以使用下面的命令。
|
|
ssh-add
命令
ssh-add
命令用来将私钥加入ssh-agent
,它有如下的参数。
(1)-d
-d
参数从内存中删除指定的私钥。
|
|
(2)-D
-D
参数从内存中删除所有已经添加的私钥。
|
|
(3)-l
-l
参数列出所有已经添加的私钥。
|
|
关闭密码登录
为了安全性,启用密钥登录之后,最好关闭服务器的密码登录。
对于 OpenSSH,具体方法就是打开服务器 sshd 的配置文件/etc/ssh/sshd_config
,将PasswordAuthentication
这一项设为no
。
|
|
修改配置文件以后,不要忘了重新启动 sshd,否则不会生效。
关于
/etc/ssh/sshd_config
的配置项的具体意义,请看《SSH 服务器端》的sshd 配置项
小节
配置密钥登录过程中可能出现的问题
文件权限问题
~/.ssh/authorized_keys
文件的权限要设为644
,即只有文件所有者才能写。如果权限设置不对,SSH 服务器会拒绝读取该文件。然后要求用户输入密码登录。
此时在/var/log/secure
文件中会出现错误日志
|
|
其实这也很好理解,~/.ssh/authorized_keys
文件关系到 SSH 免密登录,如果权限给太多了,系统会认为文件不安全,所以拒绝使用。
此外,用户家目录权限为 755 或者 700,不能是 77x。
~/.ssh
目录的权限必须是 700。
ssh、sshd 的配置文件和公钥私钥文件的权限应该为 600
还有《SSH 客户端》中提到的~/.ssh/known_hosts
文件的权限也应该是 644
如果发现免密登录出现问题,第一个从文件权限的角度排查。
同时这个问题也告诉我们,不要无脑给任何文件 777 权限,一些关键文件,给 777 权限会有重大安全隐患。
无法修改 authorized_keys
当我们使用ssh-copy-id
或者cat <pub key you upload> >> authorized_keys
将公钥追加到authorized_keys
中的时候,可能会报错
|
|
于是乎很熟悉的给文件加写的权限:
|
|
依然报错
|
|
此时就只能通过chattr
命令来修改权限了
chmod 的底层实现是 chattr 命令,用此命的功能更为强大,甚至可以锁定文件,即使 root 用户也操作不了此文件。
通过lsattr
查看权限
|
|
放开权限,减号(-)代表去掉的意思,反之加号(+)代表增加的意思。
|
|
再次赋权
|
|
然后发现就可以写入了,成功写入之后,记得恢复权限
|
|
配置 /etc/ssh/sshd_config
想要配置免密登录,还需要配置/etc/ssh/sshd_config
|
|
以上内容在配置文件里都有对应行,但被注释了起来,可通过删除注释符号设置,也可直接追加到文件末尾。
然后执行systemctl restart sshd
重启sshd
服务即可。
关于
/etc/ssh/sshd_config
的配置项的具体意义,请看《SSH 服务器端》的sshd 配置项
小节