MacOS Ventura 系统 ssh 不再支持 ssh-rsa 的原因及解决办法

陪她去流浪 桃子 2022年12月04日 编辑 微信上查看 阅读次数:5144

刚升级到 MacOS Ventura 系统后,如果尝试 ssh 登录到服务器,大概率会发现,突然登录不上了:

1
2
3
$ ssh 192.1681.86
Unable to negotiate with 192.168.1.86 port 22: no matching host key type found.
Their offer: ssh-rsa

如果想直接看解决办法,可以跳到解决办法

问题排查

从报错信息很容易推断出来:是本地的 ssh 不接受服务器 sshd 提供的公钥类型了。

所以猜测是 MacOS 的 ssh 客户端也随系统升级了,并且修改了默认的一些参数配置。 查看新旧系统的 ssh 版本情况,确实发现,版本有所升级,从 8.x 版本升级到了 9.x 版本:

1
2
3
4
5
$ ssh -V
OpenSSH_8.6p1, LibreSSL 3.3.6

$ ssh -V
OpenSSH_9.0, LibreSSL 3.3.6

由于 MacOS 使用的是 OpenSSH 这个开源的 ssh 套件。所以直接上 OpenSSH 官网查看版本记录, 然后发现,OpenSSH 从 8.8 版本 就废弃掉了 rsa/sha1 算法组合。原文如下:

Potentially-incompatible changes

This release disables RSA signatures using the SHA-1 hash algorithm by default. This change has been made as the SHA-1 hash algorithm is cryptographically broken, and it is possible to create chosen-prefix hash collisions for <USD$50K [1]

For most users, this change should be invisible and there is no need to replace ssh-rsa keys. OpenSSH has supported RFC8332 RSA/SHA-256/512 signatures since release 7.2 and existing ssh-rsa keys will automatically use the stronger algorithm where possible.

Incompatibility is more likely when connecting to older SSH implementations that have not been upgraded or have not closely tracked improvements in the SSH protocol. For these cases, it may be necessary to selectively re-enable RSA/SHA1 to allow connection and/or user authentication via the HostkeyAlgorithms and PubkeyAcceptedAlgorithms options. For example, the following stanza in ~/.ssh/config will enable RSA/SHA1 for host and user authentication for a single destination host:

Host old-host
    HostkeyAlgorithms +ssh-rsa
    PubkeyAcceptedAlgorithms +ssh-rsa

We recommend enabling RSA/SHA1 only as a stopgap measure until legacy implementations can be upgraded or reconfigured with another key type (such as ECDSA or Ed25519).

[1] "SHA-1 is a Shambles: First Chosen-Prefix Collision on SHA-1 and Application to the PGP Web of Trust" Leurent, G and Peyrin, T (2020) https://eprint.iacr.org/2020/014.pdf

简单来说就是,OpenSSH 不再认为 RSA签名 + SHA1 哈希算法的组合满足密码学安全要求了,所以默认弃用。 至于如何不安全,可以参考原文后面引用到的一篇论文:SHA-1 is a Shambles...

原文也提到了如何临时规避这个问题,见下文的解决办法

乍一看,OpenSSH 8.8 版本发布日期为:2021-09-26。 MacOS Ventura 使用的 9.0 版本发布日期为:2022-04-08,而 MacOS 本身的公开发行版本是 2022-10-24。 不得不说,苹果这操作真是有够激进啊! 激进带来的一个“后果”就是:会 broke 掉很多不激进的工具链。就像我司一样,几乎所有升级到 MacOS Ventura 系统后的员工 ssh 相关的工具全部炸了。

当然,这符合苹果一贯的作风,我也比较能接受(我在开发 iOS App 的过程中已经严重的感受到了……)。激进是好事,能跟上最新的安全标准,要不然,还不知道公司啥时候才有动力去升级呢。

简单解决办法

打开 ~/.ssh/config 文件,在文件最开始添加以下配置并保存:

Host *
	HostkeyAlgorithms +ssh-rsa
	PubkeyAcceptedAlgorithms +ssh-rsa

以上是最简单粗暴的解决办法。 简单粗暴的原因是,上述配置对所有的 Host 都生效了,没有作细粒度的控制。这需要比较熟悉 ssh 和 ssh_config,本文不详述。

如果需要临时解决,可以直接在命令行上传递参数修改(命令行参数的优先级大于 ssh_config):

1
$ ssh -o HostkeyAlgorithms=+ssh-rsa -o PubkeyAcceptedAlgorithms=+ssh-rsa ...

当然,并不是所有的 ssh 行为都是手输入的,很多工具也会在代码内执行 ssh。比如,实现 ProxyCommand。这也需要同时修改这些工具传递给 ssh 的参数。(我司的 ssh 工具就 broke 在这里了)

意外发现

程序员遇到的大难题之一,内存泄露(或者相关)的问题,没想到会在最新版本的 OpenSSH 版本记录中多次修复:

This release contains fixes for three minor memory safety problems. None are believed to be exploitable, but we report most memory safety problems as potential security vulnerabilities out of caution.

  • ssh-keyscan(1): fix a one-byte overflow in SSH- banner processing. Reported by Qualys

  • ssh-keygen(1): double free() in error path of file hashing step in signing/verify code; GHPR333

  • ssh-keysign(8): double-free in error path introduced in openssh-8.9

一处溢出、两处重复释放!

谁能想到,几乎跑在全世界所有 MacOS/Linux/Windows 上的一款开源软件,竟然也会有如此多的内存问题! 一想到当年学 C 语言之初也是这样,是不是立马舒服多了?🤪

我其实是有点好奇的,然后我就再深挖了一下作者为什么会写出这样的代码,便找到了写出此 BUG 的代码位置(有删减)(openssh/openssh-portable@d637c4a):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@@ -484,7 +508,8 @@ hash_file(int fd, int hashalg, u_char *hash, size_t hashlen)
                        error("%s: read: %s", __func__, strerror(errno));
                        ssh_digest_free(ctx);
                        errno = oerrno;
-                       return SSH_ERR_SYSTEM_ERROR;
+                       r = SSH_ERR_SYSTEM_ERROR;
+                       goto out;
                }
        }
+ out:
+       sshbuf_free(b);
        ssh_digest_free(ctx);

可以看出,它原来是先释放(ssh_digest_free)后直接返回错误码(return SSH_ERR_SYSTEM_ERROR)。 后面修改时,把错误码保存到 r 中,并 gotoout,在 out 的地方再一次 ssh_digest_free。由此带来 double-free 问题。

所以:写代码容易,维护难?😅

更好的解决办法

上面提到的简单解决办法不宜推广使用,因为不安全。

新版本的 OpenSSH 并不是禁用了整个 RSA 和 SHA 算法(看名字 ssh-rsa 挺误解的),只是禁用了和 SHA1 的组合。 其它组合,比如 RSA/SHA-256/512,都是可以继续用的。可以升级服务端的 SSHD 解决此问题。

更换服务器和本地的公钥类型也可以。 OpenSSH 支持的公钥加密类型很多,可以通过以下命令获取到:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
$ ssh -Q HostkeyAlgorithms
ssh-ed25519
[email protected]
[email protected]
[email protected]
ssh-rsa
rsa-sha2-256
rsa-sha2-512
ssh-dss
ecdsa-sha2-nistp256
ecdsa-sha2-nistp384
ecdsa-sha2-nistp521
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]

我个人非常喜欢 ed25519,因为非常简短,并且在相同密钥长度的情况下提供了比 RSA 更高的安全性。老实说,早就该推广了。 也不是没有推广,只是大家的习惯太难改变了……

生成 ed25519 和 RSA 的区别非常简单,只需要 -t ed25519 即可:

1
2
3
4
$ ssh-keygen -t ed25519
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/tao/.ssh/id_ed25519): key
...

然后看看生成的 Keys:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ cat key.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKVbraJiv9DqLw2nJZij5re7b3cA9vOfDfVsEXaIykmS tao@nuc

$ cat key
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACClW62iYr/Q6i8NpyWYo+a3u293APbznw31bBF2iMpJkgAAAJD1B/Hy9Qfx
8gAAAAtzc2gtZWQyNTUxOQAAACClW62iYr/Q6i8NpyWYo+a3u293APbznw31bBF2iMpJkg
AAAEDlMj0HE1tbHzKl/4UAp3uixJ4R0e8hT8AQDKLPae/alqVbraJiv9DqLw2nJZij5re7
b3cA9vOfDfVsEXaIykmSAAAAB3Rhb0BudWMBAgMEBQY=
-----END OPENSSH PRIVATE KEY-----

比 RSA 简短多了吧?

参考文章:

标签:ssh · macOS · ed25519 · RSA