openssh8.8默认禁止ssh-rsa加密算法
问题源自于最近无意间在工作机上升级了openssh版本(后续才发现是版本问题), 导致所有基于ssh方式的git操作全部失效, 一直提示请输入密码; 在我输入了无数次个人gitlab密码仍然失败后,第一直觉是我的ssh密钥对出了问题, 重新生成上传了新的公钥,还是同样的提示, 经过前辈的指点, 使用 ssh -vT命令查看了一下详细的日志信息, 最终发现了问题所在 在解析日志之前, 先了解一下ssh密钥登录的原理:
ssh
如果说到ssh登录, 则都会提到的三层协议
- 传输层协议[SSH-TRANS]:提供服务器验证、完整性和保密性功能,建立在传统的TCP/IP协议之上。
- 验证协议[SSH-USERAUTH]:向服务器验证客户端用户,有基于用户名密码和公钥两种验证方式,建立在传输层协议[SSH-TRANS]之上。
- 连接协议[SSH-CONNECT]:将加密隧道复用为若干逻辑信道。它建立在验证协议之上。
常说的ssh只是一种抽象的协议标准, 实际开发中我们使用的是开源openssh库, 该库是对ssh这一抽象协议标准的实现
ssh是基于非对称加密的一种通信加密协议,常用于做登录校验;
一般有公钥登录和口令登录两种方式: 口令登录和公钥登录
公钥登录
过程分为两个步骤。
- 会话密钥(session key)生成
- 客户端和服务端互相发送ssh协议版本以及openssh版本, 并约定协议版本
- 客户端和服务端互相发送支持的加密算法并约定使用的算法类型
- 服务端生成非对称密钥,并将公钥以及公钥指纹发送到客户端
- 客户端和服务端分别使用DH算法计算出获取会话密钥,后续所有流程都会使用会话密钥加密传输
- 认证
- 客户端将公钥指纹信息 使用上述的q(会话密钥)加密发送到服务端
- 服务端拿到后解密, 并去authorized_keys中匹配对应的公钥
- 服务器生成随机数 x,并用该公钥加密后生成结果 S(x),发送给客户端
- 客户端使用私钥解密 S(x) 得到 x
- 客户端将解密出的随机数用会话密钥加密,将得到的值进行MD5运算 n(q+x)。然后将这个值发送给服务器
- 服务器端对原始随机数也使用会话密钥加密后计算MD5 m(q+x)
- 服务器比较 m(q+x) 和 n(q+x),两者相同则认证成功
在认证阶段, 由于ssh是将所有用户的公钥保存在authorized_keys, 那么是如何匹配本次链接请求组使用的公钥呢?
答案是在认证阶段, 会传递本机公钥的指纹信息, 服务器端根据指纹信息比对出是否存在对应的客户端公钥, 后续会使用该公钥进行身份认证
由于ssh是基于TCP实现的, 所以可以使用wireshark抓包观察数据交互过程
- (88~90)是tcp连接的三次握手
- (91~93)是协议版本约定
- (98~99)是加密算法的约定
- (102~105)是DH算法生成会话密钥, 以及约定使用会话密钥加密后续通讯
- 后续认证阶段由于都是加密通讯, 所以无法直观分析, 感兴趣的同学可以查看openssh的源码实现
口令登录:
- 会话密钥生成(相同)
- 认证: 在上一步会话密钥生成后, 客户端使用该密钥对密码进行加密, 传输给服务器服务器使用该密钥解密获取到密码, 进行校验
以上是ssh协议的原理,我们了解到在验证阶段会用到客户端的公钥, openssh作为实现会判断公钥生成算法类型, 由于实现中不再支持ssh-rsa, 会尝试进行另一种允许的口令登录方式, 具体可以通过日志查看
日志解析 & 解决方案
ssh -vT git@gitb.xxxx.com
# 日志如下
# 版本信息
OpenSSH_8.8p1, OpenSSL 1.1.1m 14 Dec 2021
# 读取配置文件
debug1: Reading configuration data /Users/clownfish/.ssh/config
debug1: Reading configuration data /usr/local/etc/ssh/ssh_config
...
# 查找身份文件, 成功返回0, 失败返回-1, 由于本地只有默认的id_ras 所以只有这一项返回0
debug1: identity file /Users/clownfish/.ssh/id_rsa type 0
debug1: identity file /Users/clownfish/.ssh/id_rsa-cert type -1
...
# 版本号
debug1: Local version string SSH-2.0-OpenSSH_8.8
debug1: Remote protocol version 2.0, remote software version OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.8
...
# 查找到host
debug1: Found key in /Users/clownfish/.ssh/known_hosts:5
debug1: Will attempt key: /Users/clownfish/.ssh/id_rsa RSA SHA256:7KTOiN2jUDgc5SJm22GnEk5TpshjTBk/lU9stwJYx48
... # Will attempt key 尝试其他类型密钥
# 认证支持两种方式, 公钥和口令
debug1: Authentications that can continue: publickey,password
debug1: Next authentication method: publickey
debug1: Offering public key: /Users/clownfish/.ssh/id_rsa RSA SHA256:7KTOiN2jUDgc5SJm22GnEk5TpshjTBk/lU9stwJYx48
# 针对上文找到的public key 没有相互支持的签名算法
debug1: send_pubkey_test: no mutual signature algorithm
... # Trying private key 尝试其他私有key
# 尝试口令登录
debug1: Next authentication method: password
... # 一直提示输入密码
找到关键字no mutual signature supported.去查了一下发现是openssh8.8版本问题不再支持ssh-rsa, (openssh 8.8 release notes中说明默认会自动转换),
但是链接到版本较低的server时,还是要手工处理
从日志中可以看到我们server的版本是6.6
那么解决办法也就有了, 要么重新生成其他算法的秘钥对上传, 要么修改配置再次开启支持, 这里只针对第二种 在config中做如下配置:
Host * # 第一行说明对所有主机生效
PubkeyAcceptedKeyTypes=+ssh-rsa # 第二行是将ssh-rsa加会允许使用的范围, 没配置会提示no mutual signature supported.表示找不到匹配的签名算法
# HostKeyAlgorithms +ssh-rsa # 第三行是指定所有主机使用的都是ssh-rsa算法的key, 我个人测试可以不写,如果仍不生效可以打开测试
再次测试发现可以正常登录
另外开局提到的,git认证提示输入的密码,其实应该是登录服务器git用户的密码,而不是指的gitlab中的个人账号密码;因为git使用ssh目的仅仅是登录校验,不用于访问数据,由于个人对server端了解的较少, 所以在这里也坑了很久, 希望了解的同学多多指教
扩展
ssh know_hosts 防止中间人攻击
ssh基于非对称加密方式,亟需解决的一个问题就是防止中间人攻击, https中是基于CA认证实现的, ssh则是基于指纹认证; 即在首次发起链接时,会提示以下内容:
The authenticity of host 'xxxxxx' can't be established.
RSA key fingerprint is 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d.
Are you sure you want to continue connecting (yes/no)?
在输入yes后,提示
Warning: Permanently added 'xxxx' (RSA) to the list of known hosts.
konw_hosts文件存储在~/.ssh/konw_hosts, 里面记录了确认后的主机host和对应的加密算法, 以及对应的公钥信息, 后续再次登录主机时,则不会再提示以上can't be established的内容
RSA key fingerprint is 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d. 这个是公钥指纹, 由于公钥信息太长, 这里对公钥做了一个摘要,叫做指纹信息,
ssh config 常用参数
ssh有三种方式来配置,分别是命令行参数, 用户配置文件, 和系统配置文件, 优先级是命令行参数 > user config > system config
- 命令行参数,如-p 222, -i /path/to/identity_file 等选项来设置SSH的端口号或认证证书位置
- 用户的配置, 路径为~/.ssh/config 默认是不存在,手动创建即可
- 系统配置, 路径为/etc/ssh/ssh_config
配置文件用来针对ssh一些参数做配置, 例如指定HostHostName、Port、User、IdentityFile(认证证书文件, 绝对路径), 更多参数配置使用man ssh_config可以查看
以下是常用的一些配置参数:
- ssh config 文件中的一些常用变量意义:
- %d,本地用户目录 ~
- %u,本地用户
- %l,本地主机名
- %h,远程主机名
- %r,远程用户名
- 禁用密码登录: 如果你对服务器安全要求很高,那么禁用密码登录是必须的。因为使用密码登录服务器容易受到暴力破解的攻击,有一定的安全隐患。那么你需要编辑服务器的系统配置文件
/etc/ssh/sshd_config:PasswordAuthentication no ChallengeResponseAuthentication no - 远程服务当本地用: 通过 LocalForward 将本地端口上的数据流量通过 ssh 转发到远程主机的指定端口。感觉你是使用的本地服务,其实你使用的远程服务。如远程服务器上运行着 MySQL,端口 3306(未暴露端口给外部)。那么,你可以:
Host db HostName db.example.com LocalForward 3306 localhost:3306 - 多连接共享: 在你打开多个 shell 窗口时需要连接同一台服务器,如果你不想每次都输入用户名,密码,或是等待连接建立,那么你需要添加如下配置到 `~/.ssh/config
ControlMaster auto ControlPath /tmp/%r@%h:%p - 代理登录: 有的时候你可能没法直接登录到某台服务器,而需要使用一台中间服务器进行中转,如公司内网服务器。首先确保你已经为服务器配置了公钥访问,并开启了agent forwarding,那么你需要添加如下配置到
~/.ssh/config:Host gateway HostName proxy.example.com User root Host db HostName db.internal.example.com # 目标服务器地址 User root # 用户名 # IdentityFile ~/.ssh/id_ecdsa # 认证文件 ProxyCommand ssh gateway netcat -q 600 %h %p # 代理命令
git ssh的原理
告知git获取私钥:git使用ssh-agent来进行ssh协议传输,默认情况下ssh-agent会读取~/.ssh目录下的id_rsa或id_dsa文件。
告知git服务器获取公钥:这一步对于大部分git用户是不需要知道的,只需把公钥给git管理员即可。
大部分git服务器提供的SSH访问方式如下:在主机上建立一个 git 账户,让每个需要写权限的人发送一个 SSH 公钥,然后将其加入 git 账户的 ~/.ssh/authorized_keys 文件。 这样一来,所有人都将通过 git 账户访问主机。所以我们在使用ssh访问git时, 请求连接都是git@gitlab.xxx.com 以git作为用户访问
git ssh的多账号访问方式
我们在创建ssh公私钥时, 默认都是id_rsa, 可以指定新的公私钥文件名,以防止覆盖已有的id_rsa文件, 在Enter file in which to save the key步骤时指定完整的文件路径
ssh-keygen -t rsa -C "xxx@gmail.com"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/helloworld/.ssh/id_rsa): /Users/helloworld/.ssh/test_rsa
在gitlab/github 仓库中设置test_rsa对应的公钥信息后, 由于ssh默认是查询id_rsa私钥做登录认证, 所以需要指定host使用哪个私钥进行校验
根据上面对ssh config的理解, 有三种方式可以设置:
- 一种是通过ssh -i参数指定identity_file认证文件, 但是git命令是底层调用的ssh, 没办法传递-i参数到ssh,
- 第二种在config中配置host和私钥登录的对应关系(这里最好是设置在user config), 示例如下:
Host github.com
HostName github.com
Port 22
User git
IdentityFile ~/.ssh/test_rsa
配置好以后, 再次使用ssh做github登录验证时, 则使用本地的test_rsa私钥做校验, 现在就可以同时使用多个ssh密钥对登录不同的git仓库
总结
工作中经常需要用到ssh作为git的登录校验方式, 所以花时间学习了下大概原理, 不过由于是个初学者, 所以很多地方可能存在漏洞或者错误, 所以如果您发现了问题, 请联系我多多指教, 不胜感激.
另外欢迎加好友讨论各种iOS问题, 互相学习
openssh release notes
gitlab Docs
ssh配置文件详解
ssh 双钥认证原理
使用wireshark分析ssh口令登录细节
密钥登录原理篇
ssh PublicKey fingerprint
ssh RFC 中文版