基础服务类系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.html
本文对SSH连接验证机制进行了非常详细的分析,还详细介绍了ssh客户端工具的各种功能,相信能让各位对ssh有个全方位较透彻的了解,而不是仅仅只会用它来连接远程主机。
另外,本人翻译了ssh客户端命令的man文档,如本文有不理解的地方,可以参考man文档手册:ssh中文手册。
SSH系列文章:
1.1 非对称加密基础知识
对称加密:加密和解密使用一样的算法,只要解密时提供与加密时一致的密码就可以完成解密。例如QQ登录密码,银行卡密码,只要保证密码正确就可以。
非对称加密:通过公钥(public key)和私钥(private key)来加密、解密。公钥加密的内容可以使用私钥解密,私钥加密的内容可以使用公钥解密。一般使用公钥加密,私钥解密,但并非绝对如此,例如CA签署证书时就是使用自己的私钥加密。在接下来介绍的SSH服务中,虽然一直建议分发公钥,但也可以分发私钥。
所以,如果A生成了(私钥A,公钥A),B生成了(私钥B,公钥B),那么A和B之间的非对称加密会话情形包括:
(1).A将自己的公钥A分发给B,B拿着公钥A将数据进行加密,并将加密的数据发送给A,A将使用自己的私钥A解密数据。
(2).A将自己的公钥A分发给B,并使用自己的私钥A加密数据,然后B使用公钥A解密数据。
(3).B将自己的公钥B分发给A,A拿着公钥B将数据进行加密,并将加密的数据发送给B,B将使用自己的私钥B解密数据。
(4).B将自己的公钥B分发给A,并使用自己的私钥B加密数据,然后A使用公钥B解密数据。
虽然理论上支持4种情形,但在SSH的身份验证阶段,SSH只支持服务端保留公钥,客户端保留私钥的方式,所以方式只有两种:客户端生成密钥对,将公钥分发给服务端;服务端生成密钥对,将私钥分发给客户端。只不过出于安全性和便利性,一般都是客户端生成密钥对并分发公钥。后文将给出这两种分发方式的示例。
1.2 SSH概要
(1).SSH是传输层和应用层上的安全协议,它只能通过加密连接双方会话的方式来保证连接的安全性。当使用ssh连接成功后,将建立客户端和服务端之间的会话,该会话是被加密的,之后客户端和服务端的通信都将通过会话传输。
(2).SSH服务的守护进程为sshd,默认监听在22端口上。
(3).所有ssh客户端工具,包括ssh命令,scp,sftp,ssh-copy-id等命令都是借助于ssh连接来完成任务的。也就是说它们都连接服务端的22端口,只不过连接上之后将待执行的相关命令转换传送到远程主机上,由远程主机执行。
(4).ssh客户端命令(ssh、scp、sftp等)读取两个配置文件:全局配置文件/etc/ssh/ssh_config和用户配置文件~/.ssh/config。实际上命令行上也可以传递配置选项。它们生效的优先级是:命令行配置选项 > ~/.ssh/config > /etc/ssh/ssh_config。
(5).ssh涉及到两个验证:主机验证和用户身份验证。通过主机验证,再通过该主机上的用户验证,就能唯一确定该用户的身份。一个主机上可以有很多用户,所以每台主机的验证只需一次,但主机上每个用户都需要单独进行用户验证。
(6).ssh支持多种身份验证,最常用的是密码验证机制和公钥认证机制,其中公钥认证机制在某些场景实现双机互信时几乎是必须的。虽然常用上述两种认证机制,但认证时的顺序默认是gssapi-with-mic,hostbased,publickey,keyboard-interactive,password。注意其中的主机认证机制hostbased不是主机验证,由于主机认证用的非常少(它所读取的认证文件为/etc/hosts.equiv或/etc/shosts.equiv),所以网络上比较少见到它的相关介绍。总的来说,通过在ssh配置文件(注意不是sshd配置文件)中使用指令PreferredAuthentications改变认证顺序不失为一种验证的效率提升方式。
(7).ssh客户端其实有不少很强大的功能,如端口转发(隧道模式)、代理认证、连接共享(连接复用)等。
(8).ssh服务端配置文件为/etc/ssh/sshd_config,注意和客户端的全局配置文件/etc/ssh/ssh_config区分开来。
(9).很重要却几乎被人忽略的一点,ssh登录时会请求分配一个伪终端。但有些身份认证程序如sudo可以禁止这种类型的终端分配,导致ssh连接失败。例如使用ssh执行sudo命令时sudo就会验证是否要分配终端给ssh。
1.3 SSH认证过程分析
假如从客户端A(172.16.10.5)连接到服务端B(172.16.10.6)上,将包括主机验证和用户身份验证两个过程,以RSA非对称加密算法为例。
[root@xuexi ~]# ssh 172.16.10.6
服务端B上首先启动了sshd服务程序,即开启了ssh服务,打开了22端口(默认)。
1.3.1 主机验证过程
当客户端A要连接B时,首先将进行主机验证过程,即判断主机B是否是否曾经连接过。
判断的方法是读取~/.ssh/known_hosts文件和/etc/ssh/known_hosts文件,搜索是否有172.16.10.6的主机信息(主机信息称为host key,表示主机身份标识)。如果没有搜索到对应该地址的host key,则询问是否保存主机B发送过来的host key,如果搜索到了该地址的host key,则将此host key和主机B发送过来的host key做比对,如果完全相同,则表示主机A曾经保存过主机B的host key,无需再保存,直接进入下一个过程——身份验证,如果不完全相同,则提示是否保存主机B当前使用的host key。
询问是否保存host key的过程如下所示:
[root@xuexi ~]# ssh 172.16.10.6 The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established. RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf. Are you sure you want to continue connecting (yes/no)? yes
或者windows端使用图形界面ssh客户端工具时:
在说明身份验证过程前,先看下known_hosts文件的格式。以~/.ssh/known_hosts为例。
[root@xuexi ~]# cat ~/.ssh/known_hosts 172.16.10.6 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC675dv1w+GDYViXxqlTspUHsQjargFPSnR9nEqCyUgm5/32jXAA3XTJ4LUGcDHBuQ3p3spW/eO5hAP9eeTv5HQzTSlykwsu9He9w3ee+TV0JjBFulfBR0weLE4ut0PurPMbthE7jIn7FVDoLqc6o64WvN8LXssPDr8WcwvARmwE7pYudmhnBIMPV/q8iLMKfquREbhdtGLzJRL9DrnO9NNKB/EeEC56GY2t76p9ThOB6ES6e/87co2HjswLGTWmPpiqY8K/LA0LbVvqRrQ05+vNoNIdEfk4MXRn/IhwAh6j46oGelMxeTaXYC+r2kVELV0EvYV/wMa8QHbFPSM6nLz
该文件中,每行一个host key,行首是主机名,它是搜索host key时的索引,主机名后的内容即是host key部分。以此文件为例,它表示客户端A曾经试图连接过172.16.10.6这个主机B,并保存了主机B的host key,下次连接主机B时,将搜索主机B的host key,并与172.16.10.6传送过来的host key做比较,如果能匹配上,则表示该host key确实是172.16.10.6当前使用的host key,如果不能匹配上,则表示172.16.10.6修改过host key,或者此文件中的host key被修改过。
那么主机B当前使用的host key保存在哪呢?在/etc/ssh/ssh_host*文件中,这些文件是服务端(此处即主机B)的sshd服务程序启动时重建的。以rsa算法为例,则保存在/etc/ssh/ssh_host_rsa_key和/etc/ssh/ssh_host_rsa_key.pub中,其中公钥文件/etc/ssh/ssh_host_rsa_key.pub中保存的就是host key。
[root@xuexi ~]# cat /etc/ssh/ssh_host_rsa_key.pub # 在主机B上查看 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC675dv1w+GDYViXxqlTspUHsQjargFPSnR9nEqCyUgm5/32jXAA3XTJ4LUGcDHBuQ3p3spW/eO5hAP9eeTv5HQzTSlykwsu9He9w3ee+TV0JjBFulfBR0weLE4ut0PurPMbthE7jIn7FVDoLqc6o64WvN8LXssPDr8WcwvARmwE7pYudmhnBIMPV/q8iLMKfquREbhdtGLzJRL9DrnO9NNKB/EeEC56GY2t76p9ThOB6ES6e/87co2HjswLGTWmPpiqY8K/LA0LbVvqRrQ05+vNoNIdEfk4MXRn/IhwAh6j46oGelMxeTaXYC+r2kVELV0EvYV/wMa8QHbFPSM6nLz
发现/etc/ssh/ssh_host_rsa_key.pub文件内容和~/.ssh/known_hosts中该主机的host key部分完全一致,只不过~/.ssh/known_hosts中除了host key部分还多了一个主机名,这正是搜索主机时的索引。
综上所述,在主机验证阶段,服务端持有的是私钥,客户端保存的是来自于服务端的公钥。注意,这和身份验证阶段密钥的持有方是相反的。
实际上,ssh并非直接比对host key,因为host key太长了,比对效率较低。所以ssh将host key转换成host key指纹,然后比对两边的host key指纹即可。指纹格式如下:
[root@xuexi ~]# ssh 172.16.10.6 The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established. RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf. Are you sure you want to continue connecting (yes/no)? yes
host key的指纹可由ssh-kegen计算得出。例如,下面分别是主机A(172.16.10.5)保存的host key指纹,和主机B(172.16.10.6)当前使用的host key的指纹。可见它们是完全一样的。
[root@xuexi ~]# ssh-keygen -l -f ~/.ssh/known_hosts 2048 f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf 172.16.10.6 (RSA)