文章

老旧的SSH客户端连不上服务器:no hostkey alg

这篇博客记录一下如何解决旧版本的 SSH 客户端在连接服务器时出现 no hostkey alg 的问题。

问题描述

使用 SSH 连接服务器时,连接无法建立:

1
2
$ ssh GUYUHAO-UBUNTU
no hostkey alg

我的 SSH 客户端版本:

1
2
$ ssh -V
OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013

我的 SSHD 服务器版本:

1
2
3
$ sshd -V
unknown option -- V
OpenSSH_8.9p1 Ubuntu-3ubuntu0.6, OpenSSL 3.0.2 15 Mar 2022

可见差别是有点大,底层 OpenSSL 库都不一样了(1 vs 3)。

解决过程

  1. 报错信息是什么意思?

    老版本的 SSH 错误信息真的很坑爹,no hostkey alg 中的 algalgorithm 的缩写,意思是没有找到合适的算法。

    OpenSSL 从 1.0 升级到 3.0 后,由于密码学研究的进步,发现了许多旧算法的安全隐患,因此同时弃用了一些旧算法。而老版本的 SSH 客户端只支持旧算法,因此无法连接新版本的 SSHD 服务器。

    为了解决这个问题,简单的办法就是更新客户端或者服务器到最新版本。但是有时候这并不是我们能控制的,在我这里,我只能控制服务器,所以下面就是要给服务器额外添加一些算法的支持。

  2. 让 SSH 客户端打印出详细的调试信息,找出客户端支持哪些算法:

    使用 ssh -vvv GUYUHAO-UBUNTU 打印调试信息,3 个 v 表示 3 级调试信息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    
    OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013
    debug1: Reading configuration data /.../.ssh/config
    debug1: Applying options for GUYUHAO-UBUNTU
    debug1: Reading configuration data /etc/ssh/ssh_config
    debug2: ssh_connect: needpriv 0
    debug1: Executing proxy command: exec /usr/bin/nss_yhpc_ssh_proxy 172.16.170.177 22
    debug1: permanently_drop_suid: 11257
    debug1: identity file /.../.ssh/identity type -1
    debug1: identity file /.../.ssh/identity-cert type -1
    debug1: identity file /.../.ssh/id_rsa type -1
    debug1: identity file /.../.ssh/id_rsa-cert type -1
    debug1: identity file /.../.ssh/id_dsa type -1
    debug1: identity file /.../.ssh/id_dsa-cert type -1
    debug1: identity file /.../.ssh/id_ecdsa type -1
    debug1: identity file /.../.ssh/id_ecdsa-cert type -1
    debug1: Remote protocol version 2.0, remote software version OpenSSH_8.9p1 Ubuntu-3ubuntu0.6
    debug1: match: OpenSSH_8.9p1 Ubuntu-3ubuntu0.6 pat OpenSSH*
    debug1: Enabling compatibility mode for protocol 2.0
    debug1: Local version string SSH-2.0-OpenSSH_5.3
    debug2: fd 5 setting O_NONBLOCK
    debug2: fd 4 setting O_NONBLOCK
    debug1: SSH2_MSG_KEXINIT sent
    debug3: Wrote 864 bytes for a total of 885
    debug1: SSH2_MSG_KEXINIT received
    debug2: kex_parse_kexinit: diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1
    debug2: kex_parse_kexinit: ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com,ssh-rsa-cert-v00@openssh.com,ssh-dss-cert-v00@openssh.com,ssh-rsa,ssh-dss
    debug2: kex_parse_kexinit: aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se
    debug2: kex_parse_kexinit: aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se
    debug2: kex_parse_kexinit: hmac-sha1,umac-64@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha1-96
    debug2: kex_parse_kexinit: hmac-sha1,umac-64@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha1-96
    debug2: kex_parse_kexinit: none,zlib@openssh.com,zlib
    debug2: kex_parse_kexinit: none,zlib@openssh.com,zlib
    debug2: kex_parse_kexinit:
    debug2: kex_parse_kexinit:
    debug2: kex_parse_kexinit: first_kex_follows 0
    debug2: kex_parse_kexinit: reserved 0
    debug2: kex_parse_kexinit: curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,sntrup761x25519-sha512@openssh.com,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,kex-strict-s-v00@openssh.com
    debug2: kex_parse_kexinit: rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp256,ssh-ed25519
    debug2: kex_parse_kexinit: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
    debug2: kex_parse_kexinit: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
    debug2: kex_parse_kexinit: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
    debug2: kex_parse_kexinit: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
    debug2: kex_parse_kexinit: none,zlib@openssh.com
    debug2: kex_parse_kexinit: none,zlib@openssh.com
    debug2: kex_parse_kexinit:
    debug2: kex_parse_kexinit:
    debug2: kex_parse_kexinit: first_kex_follows 0
    debug2: kex_parse_kexinit: reserved 0
    debug2: mac_setup: found hmac-sha1
    debug1: kex: server->client aes128-ctr hmac-sha1 none
    debug2: mac_setup: found hmac-sha1
    debug1: kex: client->server aes128-ctr hmac-sha1 none
    no hostkey alg
    

    注意上面输出信息中 debug2: kex_parse_kexinit: 开头的行,这些行列出了客户(或服务器)端支持的算法。注意上面其实打印出了两组 debug2: kex_parse_kexinit: 哪个是客户端的,哪个是服务器的呢?这就是老版本的 SSH 客户端坑爹的地方,而在新版本中打印得非常清楚,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    ...
    ...
    debug2: local client KEXINIT proposal
    debug2: KEX algorithms: curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,sntrup761x25519-sha512@openssh.com,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,ext-info-c,kex-strict-c-v00@openssh.com
    debug2: host key algorithms: ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ssh-ed25519@openssh.com,sk-ecdsa-sha2-nistp256@openssh.com,rsa-sha2-512,rsa-sha2-256
    debug2: ciphers ctos: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
    debug2: ciphers stoc: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
    debug2: MACs ctos: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
    debug2: MACs stoc: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
    debug2: compression ctos: none,zlib@openssh.com,zlib
    debug2: compression stoc: none,zlib@openssh.com,zlib
    debug2: languages ctos:
    debug2: languages stoc:
    debug2: first_kex_follows 0
    debug2: reserved 0
    debug2: peer server KEXINIT proposal
    debug2: KEX algorithms: curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,sntrup761x25519-sha512@openssh.com,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,kex-strict-s-v00@openssh.com
    debug2: host key algorithms: rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp256,ssh-ed25519
    debug2: ciphers ctos: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
    debug2: ciphers stoc: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
    debug2: MACs ctos: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
    debug2: MACs stoc: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
    debug2: compression ctos: none,zlib@openssh.com
    debug2: compression stoc: none,zlib@openssh.com
    debug2: languages ctos:
    debug2: languages stoc:
    debug2: first_kex_follows 0
    debug2: reserved 0
    ...
    ...
    

    根据 debug2: local client KEXINIT proposaldebug2: peer server KEXINIT proposal 可以看出:前一组是客户端,后一组是服务器。并且,新版本中也把不同 kex_parse_kexinit 的算法类型也打印出来了,每组中的 kex_parse_kexinit 依次为:

    • KEX algorithms
    • host key algorithms
    • ciphers ctos
    • ciphers stoc
    • MACs ctos
    • MACs stoc
    • compression ctos
    • compression stoc
    • languages ctos
    • languages stoc

    报错信息 no hostkey alg 就是指两组的 host key algorithms 交集为空,协商不出一个双方都支持的算法。

  3. 给服务器添加支持的算法

    通过上面的调试信息,我们可以看到客户端支持的算法: ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com,ssh-rsa-cert-v00@openssh.com,ssh-dss-cert-v00@openssh.com,ssh-rsa,ssh-dss ,这些算法确实没有一个出现在服务器支持的算法列表中。

    下面介绍怎么给服务器添加 ssh-rsa 算法的支持:

    1. 编辑 /etc/ssh/sshd_config 文件,添加 HostKeyAlgorithms 选项:

      1
      
      HostKeyAlgorithms +ssh-rsa
      
    2. 重启 SSH 服务:

      1
      
      $ sudo systemctl restart sshd
      

    这样就可以了,再次连接服务器,应该就可以成功了。

后记:SSL/TLS 的握手过程

参考资料:https://www.cloudflare.com/zh-cn/learning/ssl/what-happens-in-a-tls-handshake/

其中,协商双方支持的算法属于 ClientHelloServerHello 阶段的事情。

本文由作者按照 CC BY 4.0 进行授权