## 背景
SSH为Secure Shell Protocol的简写,用于在不安全的网络上进行安全的远程登录和其它安全网络服务。作为代码工作人员,我们日常登录服务器时就使用了SSH协议,本文旨在于对SSH 2.0的理论和OpenSSH这个具体实现的常识做一些总结。
## 理论知识
SSH协议存在两个版本,分别是SSH v1和SSH v2,其对应的实现中,提供的SSH协议版本存在三种取值:
1. SSH-1,代表SSH实现支持的协议版本为v1;
2. SSH-2,代表SSH实现支持的协议版本为v1;
3. SSH-1.99,仅用于服务端,代表该服务端支持SSH v2并同时兼容SSH v1。
下文中将针对所有出现SSH都指代SSH 2.0(SSH v2),SSH v1将不再提及。
### SSH协议的组成部分
SSH协议的核心标准总共有5篇RFC文档(RFC4250-RFC4254),其对应的访问链接和其它的能力扩展相关RFC访问链接均可以通过OpenSSH提供的[Specifications](https://www.openssh.com/specs.html)页面查看。
RFC 4251中,对SSH的组成定义为3个主要部分:
1. The Transport Layer Protocol [SSH-TRANS]:SSH协议的底层传输层,一般运行在TCP协议之上。主要负责服务端认证,以及消息的机密性和完整性;
2. The User Authentication Protocol [SSH-USERAUTH]:运行在SSH-TRANS之上,完成服务端对客户端的认证;
3. The Connection Protocol [SSH-CONNECT]:运行在SSH-USERAUTH之上,在一个加密通道之上复用为多个逻辑通道,给上层的应用协议使用;
下面结合我们日常使用SSH时,经常接触的指纹、公钥、密码等概念,来简单学习[SSH-TRANS]和[SSH-USERAUTH]两个协议。
### SSH-TRANS
基于RFC 4253的定义,The Transport Layer Protocol [SSH-TRANS]主要负责服务端认证,以及消息的机密性和完整性。结合日常工作,涉及到的关键点如下:
1. kex_algorithms,协商通过什么样的交换算法来实现加密密钥的交换。比如说,ecdh-sha2-*,diffie-hellman-group-exchange-sha256等等,即DHE和ECDHE交换中使用的不同曲线等参数;
2. server_host_key_algorithms,协商什么样的Server Host Key是可以被客户端接受的。如ssh-ed25519,ssh-rsa等等;
3. encryption_algorithms:采用什么用的加密算法来保证消息的机密性。如AEAD_AES_256_GCM,aes192-ctr,CHACHA20-POLY1305等;
4. mac_algorithms:采用什么样的mac算法来保证消息的完整性。如hmac-sha2-512,hmac-sha2-256等;
所有涉及到的参数均可在[IANA SSH Parameter](https://www.iana.org/assignments/ssh-parameters/ssh-parameters.xhtml)页面中找到。
### SSH-USERAUTH
RFC4252,定义了SSH协议支持的认证方式,以及对应认证请求报文格式和交换流程。目前SSH支持的认证方式为:
1. Password:通过明文密码方式进行认证,最容易理解的模式,和电脑登录一样的模式;
2. PublicKey:采用公钥验证方式,通过在服务端内置一个文件来指明接受的公钥列表,然后客户端使用私钥来签发服务端返回的挑战码来实现认证登录;目前推荐使用的公钥算法有:rsa、ecdsa、ed25519。
3. hostbased,不常用,不推荐使用。
### 关键信息Host Keys
Host Key是服务端在密钥交换环节中,用于向客户端证明自己身份的一个永久公私钥对。每个服务端都应该有一个Host Key,同时可能存在多个使用了不同的算法Host Key对,以及多个服务端可能使用同一个Host Key。
基于Host Key,客户端对服务端的身份验证有两个模式:
1. 客户端存在一个本地数据库,用于保存host name和public host key的映射关系;这种方式的好处是没有中央管理设施和没有第三方参与,坏处则是客户端和服务端数量上涨以后难以维护;
2. 基于可信CA来认证host name和host key的关系,客户端基于CA的根证书来验证服务端的host key是不是被CA签发过;
## 服务器具体实现
以Ubuntu 22.04为例,其对应的OpenSSH版本如下:
查看`/etc/ssh/`目录,可见如下信息:
其中以ssh_host前缀的文件,就是上文中的Host Key部分:
而`/etc/ssh/sshd_config`文件则是服务端针对SSH的配置,可以参考[sshd_config](https://man.openbsd.org/sshd_config.5),配置了服务端支持的kex_algorithms,encryption_algorithms,以及使用的Host Key。
在服务端中,每个用户下的`~/.ssh/authorized_keys`文件,则表明了,服务器上每个用户支持的可以登录的公钥列表。单个用户支持多个公钥。
## 客户段具体实现
客户端中的`/etc/ssh/ssh_config`文件类似于服务端的`/etc/ssh/sshd_config`文件,同样配置了客户端支持的kex_algorithms,encryption_algorithms等配置信息,具体配置可以参考[ssh](https://man.openbsd.org/ssh.1)配置。
对比服务端中每个用户的`~/.ssh/authorized_keys`文件,客户端中的每个用户下的`~/ssh`目录下包含了多个公私钥对,当公钥放置于服务端用户目录下的`~/.ssh/authorized_keys`文件中,客户端就可以通过私钥登录到服务端。
客户端中每个用户目录下`~/.ssh/known_hosts`文件就是客户端采用Host Key信任模式中第一种方式的数据库,基于首次信任(Trust of first use,TOFU)的原则,客户端第一次链接到服务端时,会提示客户端是否信任该服务端的指纹来决定是否继续SSH连接。
当我们使用云上ECS的时候,如果出现同一个IP通过解绑重绑方式复用到不同ECS机器时(或者服务器重新初始化系统,对应的Server Host Key文件就会重新生成),就会出现服务器指纹不匹配的问题,这个时候可以通过手动删除`~/.ssh/known_hosts`文件中的指纹或者通过 `ssh-key-gen -R 主机`来清除对应主机的指纹来实现重新连接。
## 总结
本文目标旨在于解释一些SSH协议使用中的常识信息,其对应的内容均可以在OpenSSH的manual pages找到,其服务端和客户端对应的配置命令分别为sshd和ssh。基于上述内容对SSH协议有一个基本的了解后,还需要学习SSH协议中使用的对称加密和非对称加密算法的应用,可以通过`ssh -v`选项来展示SSH链接过程的详细信息来进一步了解。
## 参考文档
1. OpenSSH: SSH Speifications: [https://www.openssh.com/specs.html](https://www.openssh.com/specs.html)
2. IANA SSH Parameter:[https://www.iana.org/assignments/ssh-parameters/ssh-parameters.xhtml](https://www.iana.org/assignments/ssh-parameters/ssh-parameters.xhtml)
3. SSH详解:[https://segmentfault.com/a/1190000011395818](https://segmentfault.com/a/1190000011395818)
4. Introduction SSH:[https://www.techtarget.com/searchsecurity/tip/An-introduction-to-SSH2](https://www.techtarget.com/searchsecurity/tip/An-introduction-to-SSH2)
5. OpenSSH manual Pages:[https://www.openssh.com/manual.html](https://www.openssh.com/manual.html)