SSH 的一些细节

一般的 HTTPS 认证如下:
1.浏览器连接 HTTPS 默认的 443 端口
2.服务器提供起 public key,(X509 证书)
3.浏览器 check 该证书颁发自一个受信任的 CA
4.浏览器 check 服务器证书上的 CN(X509 Common Name)跟你浏览的域名相匹配

上面这个步骤跟 ssh 的连接过程很类似,除了一点:ssh 没有 CA 的概念。下面描述一下 ssh 连接过程:
1.用户发起连接的请求
2.服务器收到请求,将自己的公钥发给用户
3.用户使用该公钥加密密码,发送给服务器
4.服务器私钥解密,验证密码正确性

但是使用上面密码的验证方式有一个缺点,如果在客户跟服务器通信的过程中被一个中间人截获(Man-in-middle),也就是说中间人截获了客户的登录请求,假冒服务器,将中间人的公钥发给了客户端。上面说了,ssh 是没有 CA 概念的,因此用户无法辨认此公钥的真伪,如果被中间人截获,那么用户跟真正服务器之间就没有信息可言了。

$ ssh -p 11111 jaseywang@10.18.10.31
The authenticity of host '[10.18.10.31]:11111 ([10.18.10.31]:11111)' can't be established.
RSA key fingerprint is a3:e4:dd:7a:13:fe:91:76:38:51:ee:9d:b8:6b:8a:03.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[10.18.10.31]:11111' (RSA) to the list of known hosts.
jaseywang@10.18.10.31's password:

上面这个是首次连接服务器时候出现的信息,就是无法确认该公钥是否是服务器的公钥,真假自辩。
key fingerprint 就是服务器 RSA 的公钥经过 MD5 后的一个散列。如果你输入 yes,ssh 客户端会将其公钥保存至 ~/.ssh/known_hosts 文件,这个文件从某种意义上来说就是一个"CA",经过你确认的,没问题的服务器的公钥才会保存在这个文件里面。当你下一次再次连接时,就不会显示上面那段话了,实际上在这之前,客户端会检查下面几个地方:
1.全局配置文件 /etc/ssh/ssh_known_hosts,这个可以通过 GlobalKnownHostFile 来修改
2.用户本地文件 $HOME/.ssh/known_hosts,这个可以通过 UserKnownHostsFile 参数来修改

如何验证该公钥就是你需要连接的服务器的公钥了?这个又是一个超出技术范畴的事件,可以把服务器的公钥发布在网络上,然后当你首次连接时进行匹配或者当你登录进去之后通过 ssh-keygen 检查其公钥,通常是在 /etc/ssh

$ ssh -p 11111 jaseywang@10.18.10.30
The authenticity of host '[10.18.10.30]:11111 ([10.18.10.30]:11111)' can't be established.
RSA key fingerprint is 17:2c:67:30:24:ab:79:4b:ae:fd:b2:b6:56:b8:28:71.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[10.18.10.30]:11111' (RSA) to the list of known hosts.
jaseywang@10.18.10.30's password:

$ ls
moduli  ssh_config  sshd_config  ssh_host_dsa_key  ssh_host_dsa_key.pub  ssh_host_rsa_key  ssh_host_rsa_key.pub
$ ssh-keygen  -l -f -v ssh_host_rsa_key.pub
2048 17:2c:67:30:24:ab:79:4b:ae:fd:b2:b6:56:b8:28:71 ssh_host_rsa_key.pub (RSA)
+–[ RSA 2048]—-+
|      ..+        |
|       o +       |
|      . . =      |
|     o   + .     |
|    o o.S .      |
|  . E+.o..       |
|   o ..o         |
|  . .o=          |
|   .o+=.         |
+—————–+

ssh 通常有三种方式来应对一个未被确认或者公钥被修改过的情况,通过 StrictHostKeyChecking 指令:
$ ssh -o  StrictHostKeyChecking=no -p 11111 jaseywang@10.18.10.30
Warning: Permanently added '[10.18.10.30]:11111' (RSA) to the list of known hosts.
jaseywang@10.18.10.30's password:

$ ssh -o  StrictHostKeyChecking=ask -p 11111 jaseywang@10.18.10.30
The authenticity of host '[10.18.10.30]:11111 ([10.18.10.30]:11111)' can't be established.
RSA key fingerprint is 17:2c:67:30:24:ab:79:4b:ae:fd:b2:b6:56:b8:28:71.
Are you sure you want to continue connecting (yes/no)?

$ ssh -o  StrictHostKeyChecking=yes -p 11111 jaseywang@10.18.10.30
No RSA host key is known for [10.18.10.30]:11111 and you have requested strict checking.
Host key verification failed.

这里就完全体现的安全跟方便的相对关系了,最安全的是 StrictHostKeyChecking=yes 选项,但是如果 known_hosts 事先没有此服务器的公钥的话,将拒绝登录。而我们会经常看到下面这个情景:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
69:38:ba:80:93:b8:2a:29:ec:b3:65:e2:40:da:78:54.
Please contact your system administrator.
Add correct host key in /root/.ssh/known_hosts to get rid of this message.
Offending key in /etc/ssh/ssh_known_hosts:153
Password authentication is disabled to avoid man-in-the-middle attacks.
Keyboard-interactive authentication is disabled to avoid man-in-the-middle attacks.
Permission denied (publickey,keyboard-interactive).

出现这个情况最可能是服务器重新生成了密钥对,或者重新安装了系统导致密钥对更换,或者该服务器的 IP 变更了。解决他就可以使用上面的方式了:
$ ssh -o StrictHostKeyChecking=no -p 11111 jaseywang@10.18.10.30
或者将其重定向到 /dev/null 中,也就是不将公钥记录到 known_hosts 里面:
$ ssh -o GlobalKnownHostsFile=/dev/null -p 11111 jaseywang@10.18.10.30

对于 ssh 的种类,主流的包括 V2 版本的 RSA/DSA,以及现在几乎不用的 V1 版本的 RSA。如果 public key 丢失,可以通过 ssh-keygen 还原:
$ pwd
/home/jaseywang/.ssh
$ ll
total 44
-rw-r–r– 1 jaseywang jaseywang  6541 Mar 14 18:24 config
-rw——- 1 jaseywang jaseywang  1675 Feb 26 20:09 id_rsa
-rw-r–r– 1 jaseywang jaseywang   401 Feb 26 20:09 id_rsa.pub
-rw-r–r– 1 jaseywang jaseywang 25380 Mar 23 03:39 known_hosts
$ ssh-keygen -y -f id_rsa
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDeNQGkMk4GTl3+5W2tYsAVQ/7stDx/2zAH3ETCo6jIOP0f07OV3CVOERW7AyhZmM0pQtrQsUzjCs7Jw9nJRHZDDUtdeTbFEA/eMHbtzeV0kNtyyutXKT9ymXf7z8CtSuJcd52SttubU6hsOBWz2rUkdz6HMvJhjBt38QW/
$ cat id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDeNQGkMk4GTl3+5W2tYsAVQ/7stDx5/2zAH3ETCo6jIOP0f07OV3CVOERW7AyhZmM0pQtrQsUzjCs7Jw9nJRHZDDUtdeTbFEAh/eMHbtzeV0kNtyyutXKT9ymXf7z8CtSuJcd52SttubU6hsOBWz2rUkdz6HMvJhjBt38QW/w

ref:
http://www.symantec.com/connect/articles/ssh-host-key-protection

  • http://mindsfree.info mazhechao

    很好的科普文章。最后两个长串在页面上“溢出”了。jasey居然用百度统计。。。