跳转到内容

OpenSSH/日志和故障排除

100% developed
来自 Wikibooks,开放世界中的开放书籍

OpenSSH 客户端和服务器都提供了许多选择,可以选择日志写入的位置以及收集的信息量。

日志记录的先决条件是使用网络时间协议 (NTP) 或等效服务拥有准确的系统时钟,该服务提供与世界其他地区持续的时间同步。日志中的时间戳越准确,在机器或站点或服务提供商之间协调取证的速度就越快。如果您需要联系外部方(例如服务提供商),通常只有在时间非常精确的情况下才能取得进展。

服务器日志

[编辑 | 编辑源代码]

默认情况下,sshd(8) 使用日志级别 INFO 和系统日志设备 AUTH 将日志信息发送到系统日志。因此,查找来自 sshd(8) 的日志数据的地方在 /var/log/auth.log 中。这些默认值可以使用 SyslogFacilityLogLevel 指令覆盖。以下是授权日志中典型的服务器启动条目。

Mar 19 14:45:40 eee sshd[21157]: Server listening on 0.0.0.0 port 22.
Mar 19 14:45:40 eee sshd[21157]: Server listening on :: port 22.

在大多数情况下,默认的日志级别就足够了,但在测试新服务或活动时,有时需要更多信息。调试信息通常会写入 stderr。从 OpenSSH 7.6 开始,Match 块可以为特定条件设置备用日志级别。

下面的日志摘录显示了相同的基本服务器启动,但增加了详细信息。将下面的 DEBUG1 日志级别与上面的默认日志级别进行对比

debug1: sshd version OpenSSH_6.8, LibreSSL 2.1
debug1: private host key #0: ssh-rsa SHA256:X9e6YzNXMmr1O09LVoQLlCau2ej6TBUxi+Y590KVsds
debug1: private host key #1: ssh-dss SHA256:XcPAY4soIxU2IMtYmnErrVOjKEEvCc3l5hOctkbqeJ0
debug1: private host key #2: ecdsa-sha2-nistp256 SHA256:QIWi4La8svQSf5ZYow8wBHN4tF0jtRlkIaLCUQRlxRI
debug1: private host key #3: ssh-ed25519 SHA256:fRWrx5HwM7E5MRcMFTdH95KwaExLzAZqWlwULyIqkVM
debug1: rexec_argv[0]='/usr/sbin/sshd'
debug1: rexec_argv[1]='-d'
debug1: Bind to port 22 on 0.0.0.0.
Server listening on 0.0.0.0 port 22.
debug1: Bind to port 22 on ::.
Server listening on :: port 22.

以下是使用最详细级别 DEBUG3 的相同启动

debug2: load_server_config: filename /etc/ssh/sshd_config
debug2: load_server_config: done config len = 217
debug2: parse_server_config: config /etc/ssh/sshd_config len 217
debug3: /etc/ssh/sshd_config:52 setting AuthorizedKeysFile .ssh/authorized_keys
debug3: /etc/ssh/sshd_config:86 setting UsePrivilegeSeparation sandbox          
debug3: /etc/ssh/sshd_config:104 setting Subsystem sftp internal-sftp 
debug1: sshd version OpenSSH_6.8, LibreSSL 2.1
debug1: private host key #0: ssh-rsa SHA256:X9e6YzNXMmr1O09LVoQLlCau2ej6TBUxi+Y590KVsds
debug1: private host key #1: ssh-dss SHA256:XcPAY4soIxU2IMtYmnErrVOjKEEvCc3l5hOctkbqeJ0
debug1: private host key #2: ecdsa-sha2-nistp256 SHA256:QIWi4La8svQSf5ZYow8wBHN4tF0jtRlkIaLCUQRlxRI
debug1: private host key #3: ssh-ed25519 SHA256:fRWrx5HwM7E5MRcMFTdH95KwaExLzAZqWlwULyIqkVM
debug1: rexec_argv[0]='/usr/sbin/sshd'
debug1: rexec_argv[1]='-ddd'
debug2: fd 3 setting O_NONBLOCK
debug1: Bind to port 22 on 0.0.0.0.
Server listening on 0.0.0.0 port 22.
debug2: fd 4 setting O_NONBLOCK
debug1: Bind to port 22 on ::.

每次失败的登录尝试都会被记录,一旦指令 MaxAuthTries 中的值超过,连接就会断开。以下是日志摘录,显示了一些失败尝试后默认日志的外观

...
Mar 19 11:11:06 server sshd[54798]: Failed password for root from 122.121.51.193 port 59928 ssh2
Mar 19 11:11:06 server sshd[54798]: Failed password for root from 122.121.51.193 port 59928 ssh2
Mar 19 11:11:07 server sshd[54798]: Failed password for root from 122.121.51.193 port 59928 ssh2
Mar 19 11:11:08 server sshd[54798]: Failed password for root from 122.121.51.193 port 59928 ssh2
Mar 19 11:11:09 server sshd[54798]: Failed password for root from 122.121.51.193 port 59928 ssh2
Mar 19 11:11:10 server sshd[54798]: Failed password for root from 122.121.51.193 port 59928 ssh2
Mar 19 11:11:10 server sshd[54798]: error: maximum authentication attempts exceeded for root from 122.121.51.193 port 59928 ssh2 [preauth]
Mar 19 11:11:10 server sshd[54798]: Disconnecting authenticating user root 122.121.51.193 port 59928: Too many authentication failures [preauth]
...

通常最好不要允许 root 登录,至少不要通过密码身份验证进行 root 登录。阻止 root 的密码身份验证可以极大地简化日志分析,特别是它消除了耗时的疑问,即谁在试图登录以及为什么。需要完全 root 级权限的人可以通过 su(1) 获得常规活动权限。或者,对于需要 root 级权限的特定任务,可以通过为 sudo(8)doas(1) 创建的自定义条目授予这些权限。请注意,在这种情况下,只应允许特定服务和程序,而不是像 sudo(8) 中常见的错误配置那样,允许完全访问权限。或者,可以使用使用 forced-commands-only 创建的单用途密钥,因为有些人认为提供额外的提权方法(例如 su(1)sudo(8)doas(1))比通过与特定功能绑定的密钥或证书谨慎地提供远程 root 访问权限更危险。

成功登录

[编辑 | 编辑源代码]

默认情况下,服务器不会存储太多关于用户事务的信息。这是一件好事。认识到系统按预期运行时也是一件好事。以下是一个成功 SSH 登录的示例

Mar 14 19:50:59 server sshd[18884]: Accepted password for fred from 192.0.2.60 port 6647 ssh2

以下是一个使用密钥进行身份验证的示例。它显示密钥指纹作为 base64 中的 SHA256 哈希。

Mar 14 19:52:04 server sshd[5197]: Accepted publickey for fred from 192.0.2.60 port 59915 ssh2: RSA SHA256:5xyQ+PG1Z3CIiShclJ2iNya5TOdKDgE/HrOXr21IdOo

以下是一个使用用户证书成功进行身份验证的示例。证书的标识字符串为“foobar”,序列号为“9624”。在此示例中,证书使用 ECDSA,而密钥本身使用 Ed25519。证书本身是另一个不同的密钥,具有与身份验证密钥本身不同的 SHA256 指纹。

May 15 16:28:17 server sshd[50140]: Accepted publickey for fred from 192.0.2.60 port 44456 ssh2: ECDSA-CERT SHA256:qGl9KiyXrG6mIOo1CT01oHUvod7Ngs5VMHM14DTbxzI ID foobar (serial 9624) CA ED25519 SHA256:fZ6L7TlBLqf1pGWzkcQMQMFZ+aGgrtYgRM90XO0gzZ8

在 6.8 之前,密钥的指纹是十六进制的 MD5 哈希。

Jan 28 11:51:43 server sshd[5104]: Accepted publickey for fred from 192.0.2.60 port 60594 ssh2: RSA e8:31:68:c7:01:2d:25:20:36:8f:50:5d:f9:ee:70:4c

在 6.3 之前的旧版本 OpenSSH 中,密钥指纹完全缺失于身份验证日志记录中。

Jan 28 11:52:05 server sshd[1003]: Accepted publickey for fred from 192.0.2.60 port 20042 ssh2

以下是一个使用服务器的内部 sftp 子系统进行 SFTP 会话的密码身份验证示例。该子系统的日志记录设置为 INFO。

Mar 14 20:14:18 server sshd[19850]: Accepted password for fred from 192.0.2.60 port 59946 ssh2
Mar 14 20:14:18 server internal-sftp[11581]: session opened for local user fred from [192.0.2.60]

以下是一个使用 RSA 密钥进行身份验证的成功 SFTP 登录示例。

Mar 14 20:20:53 server sshd[10091]: Accepted publickey for fred from 192.0.2.60 port 59941 ssh2: RSA SHA256:LI/TSnwoLryuYisAnNEIedVBXwl/XsrXjli9Qw9SmwI
Mar 14 20:20:53 server internal-sftp[31070]: session opened for local user fred from [192.0.2.60]

借助 xinetd,可以记录其他数据,例如连接持续时间。

SSH 证书身份验证的日志问题

[编辑 | 编辑源代码]

通常,不会提供太多关于哪个证书失败的信息,只会说明为什么它身份验证失败。查找相关帐户或实际证书可能需要一些调查。通常不会公开客户端信息,所有调查都必须在服务器端进行。

如果通过其他方式(例如密码)再次尝试身份验证,那么在连接关闭时,将有一个日志条目记录了哪个帐户参与其中。这是因为连接序列的早期阶段,断开连接的进程 ID 相同,并且会包含帐户名称和来源地址,从而为寻求解决方案提供一些线索。有时甚至会包含帐户名称。

May  5 16:31:38 server sshd[252]: Connection closed by authenticating user fred 192.0.2.60 port 44470 [preauth]

但是,如果允许连接超时,而没有首先通过其他方式进行任何其他身份验证尝试,那么除了可能的时间之外,将无从得知。

May  5 16:33:00 server sshd[90593]: fatal: Timeout before authentication for 192.0.2.60 port 44718

以下是基于证书的登录尝试失败的一些常见日志条目示例。证书可能存在多个问题,但每次只记录一个错误。

已过期或尚未有效的证书

[编辑 | 编辑源代码]

尚未生效或已过期的证书会生成关于原因的日志条目,但不会生成相关帐户或证书。

May  5 16:35:20 server sshd[252]: error: Certificate invalid: expired

以上是已过期证书,以下是尚未生效的证书。

May  5 16:58:00 server sshd[90593]: error: Certificate invalid: not yet valid

两种类型的事件都不会提供更多信息。

有效证书但主体无效

[编辑 | 编辑源代码]

与已过期证书类似,不会提供太多关于实际帐户或证书的信息。这里使用错误的帐户尝试了证书,该帐户不在证书的主体中列出。

May  5 17:29:52 server sshd[98884]: error: Certificate invalid: name is not a listed principal
May  5 17:29:56 server sshd[98884]: Connection closed by authenticating user fred 192.0.2.60 port 45114 [preauth]

如果客户端故意关闭连接,则连接关闭条目中可能包含一些信息。

有效证书但源地址无效

[编辑 | 编辑源代码]

如果证书仅限于从特定地址或主机名连接,则如果连接来自不同的地址或主机,日志会抱怨并识别不正确的源地址。

May  5 17:48:54 server sshd[2420]: cert: Authentication tried for fred with valid certificate but not from a permitted source address (192.0.2.61).
May  5 17:48:54 server sshd[2420]: error: Refused by certificate options

但是,无法直接识别特定证书。

记录 SFTP 文件传输

[编辑 | 编辑源代码]

可以使用 **LogLevel** INFO 或 VERBOSE 记录 SFTP 文件传输。SFTP 服务器的日志级别可以在 sshd_config(5) 中设置,与常规 SSH 服务器设置分开。

Subsystem internal-sftp -l INFO

默认情况下,SFTP 消息也会出现在 auth.log 中,但可以通过重新配置系统记录器(通常是 **rsyslogd(8)** 或 **syslogd(8)**)将这些消息过滤到它们自己的文件。有时这可以通过将日志工具代码从默认的 AUTH 更改为 LOCAL0 到 LOCAL7,以及不太常用的 DAEMON 和 USER 来实现。

Subsystem internal-sftp -l INFO -f LOCAL6

如果分配了新的系统日志文件,请务必记住在日志轮换中也要包含它们。同样,可以使用 **Match** 指令更改特定连接的日志级别。

以下日志摘录是在使用日志级别 INFO 时生成的。会话以打开开始,以关闭结束。方括号中的数字是 SFTP 会话的进程 ID,是跟踪日志中会话的唯一方法。

Oct 22 11:59:45 server internal-sftp[4929]: session opened for local user fred from [192.0.2.33]
...
Oct 22 12:09:10 server internal-sftp[4929]: session closed for local user fred from [192.0.2.33]

下面是将名为 **foo** 的 928 字节小文件上传到用户“fred”的主目录的 SFTP 上传操作。

Oct 22 11:59:50 server internal-sftp[4929]: open "/home/fred/foo" flags WRITE,CREATE,TRUNCATE mode 0664
Oct 22 11:59:50 server internal-sftp[4929]: close "/home/fred/foo" bytes read 0 written 928

以及在同一会话中对目录 ** /var/www ** 的目录列表。

Oct 22 12:07:59 server internal-sftp[4929]: opendir "/var/www"
Oct 22 12:07:59 server internal-sftp[4929]: closedir "/var/www"

最后是下载与用户“fred”主目录中名为 **foo** 的相同 928 字节小文件。

Oct 22 12:08:03 server internal-sftp[4929]: open "/home/fred/foo" flags READ mode 0666
Oct 22 12:08:03 server internal-sftp[4929]: close "/home/fred/foo" bytes read 928 written 0

成功的传输将通过一条 close 消息记录。尝试下载(打开)不存在的文件将紧随其后出现一条 sent status No such file 消息,而不是一条 close 消息。存在但用户无权读取的文件将创建一条 sent status Permission denied 消息。

记录 Chrooted SFTP

[编辑 | 编辑源代码]

在 chroot 监狱(由 **ChrootDirectory** 定义)中使用内置 **sftp-subsystem** 记录日志需要在监狱内存在一个 **./dev/log** 节点。这可以通过在启动时让系统记录器(如 syslogd(8))在 chrooted 目录中添加额外的日志套接字来实现。在某些系统上,这只需简单地添加更多标志,例如“**-u -a /chroot/dev/log**”,在 ** /etc/rc.conf.local ** 中或任何等效的启动脚本中。

下面是使用 SFTP-subsystem 的日志级别 DEBUG3 登录到 chroot 监狱的 SFTP 登录示例(使用密码)。日志显示了文件上传。

Jan 28 12:42:41 server sshd[26299]: Connection from 192.0.2.60 port 47366
Jan 28 12:42:42 server sshd[26299]: Failed none for fred from 192.0.2.60 port 47366 ssh2
Jan 28 12:42:44 server sshd[26299]: Accepted password for fred from 192.0.2.60 port 47366 ssh2
Jan 28 12:42:44 server sshd[26299]: User child is on pid 21613
Jan 28 12:42:44 server sshd[21613]: Changed root directory to "/home/fred"
Jan 28 12:42:44 server sshd[21613]: subsystem request for sftp
Jan 28 12:42:44 server internal-sftp[2084]: session opened for local user fred from [192.0.2.60]
Jan 28 12:42:58 server internal-sftp[2084]: open "/docs/somefile.txt" flags WRITE,CREATE,TRUNCATE mode 0644
Jan 28 12:42:58 server internal-sftp[2084]: close “/docs/somefile.txt” bytes read 0 written 400

请记住,SFTP 是一个单独的子系统,与文件创建模式一样,日志级别和日志工具在 sshd_config(5) 中的设置与 SSH 服务器分开。

Subsystem internal-sftp -l ERROR

记录客户端连接的稳定性

[编辑 | 编辑源代码]

当在服务器的配置中设置 **ClientAliveInterval** 时,服务器会定期探测已建立连接的客户端。在正常的日志级别下,这些探测不会在日志中记录,除非出现错误。

如果 **ClientAliveInterval** 被连续超过 **ClientAliveCountMax** 允许的次数,则客户端将被正式宣布断开连接,并且连接将被断开。在默认的日志级别 INFO 下,将记录一条简短的消息,标识已断开的客户端。

Sep  6 14:42:08 eee sshd[83709]: packet_write_poll: Connection from 192.0.2.97 port 57608: Host is down

在日志级别 DEBUG 下,服务器将记录客户端对轮询的响应,表明会话仍然处于连接状态。

Sep  6 14:27:52 eee sshd[9075]: debug1: Got 100/147 for keepalive

日志级别 DEBUG2 和 DEBUG3 将提供有关连接的更多信息。但是,即使在日志级别 DEBUG3 下,特定被轮询的客户端也不会在日志消息中直接标识,必须从守护进程的进程 ID 中推断出来(如果需要此类信息)。

Sep  6 14:30:59 eee sshd[73960]: debug2: channel 0: request [email protected] confirm 1
Sep  6 14:30:59 eee sshd[73960]: debug3: send packet: type 98
Sep  6 14:30:59 eee sshd[73960]: debug3: receive packet: type 100
Sep  6 14:30:59 eee sshd[73960]: debug1: Got 100/22 for keepalive

同样,当 **ClientAliveCountMax** 被超过时,在客户端最后一次未能响应之后,连接将断开。以下是如何在将日志级别设置为 DEBUG2 时的情况。

Sep  6 14:17:55 eee sshd[15780]: debug2: channel 0: request [email protected] confirm 1
Sep  6 14:17:55 eee sshd[15780]: debug1: Got 100/22 for keepalive
Sep  6 14:18:37 eee sshd[15780]: debug2: channel 0: request [email protected] confirm 1
Sep  6 14:18:37 eee sshd[15780]: packet_write_poll: Connection from 192.0.2.97 port 57552: Host is down
Sep  6 14:18:37 eee sshd[15780]: debug1: do_cleanup
Sep  6 14:18:37 eee sshd[48675]: debug1: do_cleanup
Sep  6 14:18:37 eee sshd[48675]: debug1: session_pty_cleanup: session 0 release /dev/ttyp0

指令 **ClientAliveInterval** 和 **ClientAliveCountMax** 通常适用于连接到服务器的所有客户端。但是,它们可以在 **Match** 块内使用,因此仅适用于特定连接。

记录被吊销的密钥

[编辑 | 编辑源代码]

如果使用 **RevokedKeys** 指令指向已被吊销的公钥列表,则 sshd(8) 将在尝试使用被吊销的密钥进行访问时记录日志条目。无论使用的是公钥的明文列表,还是已生成的二进制密钥吊销列表 (KRL),该条目都将相同。

如果允许密码身份验证,并且用户尝试使用密码身份验证,则在密钥身份验证失败后,将记录密码身份验证的记录。

Mar 14 20:36:40 server sshd[29235]: error: Authentication key RSA SHA256:jXEPmu4thnubqPUDcKDs31MOVLQJH6FfF1XSGT748jQ revoked by file /etc/ssh/ssh_revoked_keys
...
Mar 14 20:36:45 server sshd[29235]: Accepted password for fred from 192.0.2.10 port 59967 ssh2

如果不允许密码身份验证,则 sshd(8) 将在密钥失败后立即关闭连接。

Mar 14 20:38:27 server sshd[29163]: error: Authentication key RSA SHA256:jXEPmu4thnubqPUDcKDs31MOVLQJH6FfF1XSGT748jQ revoked by file /etc/ssh/ssh_revoked_keys
...

尝试使用被吊销的密钥的帐户仍然是个谜,因此,您需要使用 ssh-keygen -lf 从旧密钥存档中按指纹查找密钥,并阅读密钥的注释。但是,如果有效帐户在密钥尝试失败后取消了连接而没有尝试密码,则通常的消息仍将发布到日志中。

Mar 14 20:44:04 server sshd[14352]: Connection closed by authenticating user fred 192.0.2.237 port 55051 [preauth]
...

这可能会提供一些线索,并允许使用简短的 AWK 脚本进行过滤(如果所有消息都在同一个日志文件中)。

$ awk '/revoked by file/ {
        pid[$5]++; key[$5]=$9; hash[$5]=$10; next;
    }
    pid[$5] && /closed by authenticating user/ {
        print key[$5], hash[$5], $10, $11;
        delete key[$5]; delete hash[$5]; delete pid[$5];
    }' /var/log/authlog

同样,如果客户端没有尝试登录,只是超时,则消息将只说明这一点。

Mar 18 21:40:25 server sshd[9942]: fatal: Timeout before authentication for 198.51.100.236 port 53728
...

在客户端方面,如果尝试使用被吊销的密钥,不会发出任何警告或错误。它只会失败,并将尝试下一个密钥或方法。

暴力攻击和 Hail Mary 攻击

[编辑 | 编辑源代码]

在服务器连接到网络后,几乎立即看到登录尝试失败是相当常见的。暴力攻击(一台机器对几个帐户进行锤击以尝试找到有效的密码)变得越来越少见。部分原因是数据包过滤器(如 Linux 的 NFables 和 BSD 的 PF)可以限制来自单个主机的连接尝试的数量和速率。服务器配置指令 **MaxStartups** 可以限制同时进行的未经身份验证的连接数量。

...
Mar 18 18:54:44 server sshd[54939]: Failed password for root from 201.179.249.231 port 52404 ssh2
Mar 18 18:54:48 server sshd[54939]: Failed password for root from 201.179.249.231 port 52404 ssh2
Mar 18 18:54:49 server sshd[54939]: Failed password for root from 201.179.249.231 port 52404 ssh2
Mar 18 18:54:49 server sshd[54939]: error: maximum authentication attempts exceeded for root from 201.179.249.231 port 52404 ssh2 [preauth]
Mar 18 18:54:49 server sshd[54939]: Disconnecting authenticating user root 201.179.249.231 port 52404: Too many authentication failures [preauth]
...

请注意,从 OpenSSH 7.5 开始,当尝试使用有效的用户名时,日志中会出现“正在验证用户”。当尝试使用无效的用户名时,也会记录此信息。

...
Mar 18 18:55:05 server sshd[38594]: Invalid user ubnt from 201.179.249.231 port 52471
Mar 18 18:55:05 server sshd[38594]: Failed password for invalid user ubnt from 201.179.249.231 port 52471 ssh2
Mar 18 18:55:09 server sshd[38594]: error: maximum authentication attempts exceeded for invalid user ubnt from 201.179.249.231 port 52471 ssh2 [preauth]
Mar 18 18:55:09 server sshd[38594]: Disconnecting invalid user ubnt 201.179.249.231 port 52471: Too many authentication failures [preauth]
...

处理来自单个机器或网络的暴力攻击的方法是自定义服务器主机的数据包过滤器,以限制攻击,甚至暂时阻止超载最大连接数量或速率的机器。或者,您还可以联系攻击者网络块的所有者,提供 IP 地址以及攻击的确切日期和时间。

在撰写本文时,一种常见的攻击类型是分布在大量受损机器上的攻击,每台机器在攻击服务器中只扮演着很小的角色。

要处理 Hail Mary 攻击,请联系攻击者网络块的所有者。如果提供确切的时间和地址,则可以使用带有从日志中剪切粘贴的摘录的格式信。或者,网络或系统管理员团队可以协同工作,汇集数据以识别并封锁参与攻击的受损主机。

对无效用户的失败的 None

[编辑 | 编辑源代码]

SSH 协议指定了许多可能的身份验证方法[1]。方法 passwordkeyboard-interactivepublickey 相当常见。一个鲜为人知的身份验证方法是 none,它只有在服务器不需要进一步的身份验证时才会成功,例如,如果设置了 **PermitEmptyPassword**,并且帐户实际上没有密码[2]。包括 OpenSSH 在内的一些 SSH 客户端会先要求 none 身份验证,然后使用剩余的可能身份验证方法列表来决定如果它不起作用下一步该怎么做。

...
Aug 10 19:09:05 server sshd[93126]: Failed none for invalid user admin from 125.64.94.136 port 27586 ssh2
...

换句话说,这是一个尝试使用 none 身份验证方法的暴力攻击。它是一种攻击,只有在明确设置为空密码并且还专门设置了允许访问的帐户才能进入,方法是同时启用 none 身份验证方法和服务器上的 **PermitEmptyPasswords** 配置指令。大多数暴力攻击只尝试 password 身份验证,其中一些攻击甚至会检查 password 方法,如果不可用,则放弃。其他攻击者可能会无谓地反复锤击,即使该方法不可用。

看似来自 127.0.0.1、::1 或其他本地主机地址的连接

[edit | edit source]

当 SSH 服务器通过反向隧道访问另一台机器时,传入的连接将看起来来自本地主机地址,通常是127.0.0.1::1

Mar 23 14:16:16 server sshd[9265]: Accepted password for fred from 127.0.0.1 port 40426 ssh2

如果反向隧道另一端的端口对公众开放,它将被探测并可能受到攻击。由于反向隧道,攻击也似乎来自服务器自己的回环地址。

Mar 23 14:20:17 server sshd[5613]: Invalid user cloud from ::1 port 57404
Mar 23 14:20:21 server sshd[5613]: Failed password for invalid user cloud from ::1 port 57404 ssh2
Mar 23 14:20:26 server sshd[5613]: Failed password for invalid user cloud from ::1 port 57404 ssh2
Mar 23 14:20:32 server sshd[5613]: Failed password for invalid user cloud from ::1 port 57404 ssh2
Mar 23 14:20:35 server sshd[5613]: Connection closed by invalid user cloud ::1 port 57404 [preauth]

因此,通常的应对措施(如 SSHGuard 或 Fail2Ban 或其他类似的入侵检测系统)无法使用,因为本地主机地址被隧道用于所有登录尝试,无论其真实来源如何。

一个部分解决方案是将传入连接绑定到不同的 IP 地址。回环接口将需要一个额外的永久地址,一个别名,为此。该别名可以在建立反向隧道时分配。

$ ssh -R 2022:127.2.2.1:22 [email protected]

因此,它将指定通过该隧道传入的所有登录的源地址。因此,以这种方式,别名将显示在日志中,而不是使用反向隧道时默认的回环地址。

Mar 23 18:00:13 server sshd[8525]: Invalid user cloud from 127.2.2.1 port 17271
Mar 23 18:00:15 server sshd[8525]: Failed password for invalid user cloud from 127.2.2.1 port 17271 ssh2
Mar 23 18:00:19 server sshd[8525]: Failed password for invalid user cloud from 127.2.2.1 port 17271 ssh2
Mar 23 18:01:23 server sshd[8525]: Failed password for invalid user cloud from 127.2.2.1 port 17271 ssh2
Mar 23 18:01:26 server sshd[8525]: Connection closed by invalid user cloud 127.2.2.1 port 17271 [preauth]

如果端口不需要对开放的互联网可用,一个完整的解决方案就是确保它们无法从外部访问。这可以通过在创建反向隧道时不在客户端使用-g选项,或者通过将sshd_config(5)中的GatewayPorts指令恢复为默认的no,或者两者都做来完成。系统的内置数据包过滤器也可以使用。然后,即使将转发端口从外部关闭,ProxyJump选项仍然可以用来跳过跳转主机并使用设置进行 SSH 访问。然而,由于这些端口有时需要对外部世界可用,因此这种方法并不总是一个选择。

客户端日志记录

[edit | edit source]

OpenSSH 客户端通常将日志信息发送到stderr-y选项可用于将输出发送到系统日志,由syslogd(8)或类似程序管理。客户端日志详细程度可以通过更改ssh_config(5)中的LogLevel指令来增加或减少,并通过SyslogFacility指令更改日志设施。两者都需要使用-y运行时选项,如果没有它,它们什么也不做。

或者,可以使用-E选项将日志输出发送到指定文件,而不是stderr,而不是使用-y选项。当在自动脚本中运行ssh(1)时,使用系统日志或单独的日志文件是很有用的。以下是一个连接到交互式 shell 的示例,其中包含正常级别的客户端日志记录。

$ ssh -l fred server.example.org    
[email protected]‘s password: 
Last login: Thu Jan 27 13:21:57 2011 from 192.168.11.1

相同连接在第一个详细级别给出大量的调试信息,多 42 行。

$ ssh -v -l fred server.example.org
OpenSSH_6.8, LibreSSL 2.1
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: Connecting to server.example.org [198.51.100.20] port 22.
debug1: Connection established.
debug1: key_load_public: No such file or directory
debug1: identity file /home/fred/.ssh/id_rsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/fred/.ssh/id_rsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/fred/.ssh/id_dsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/fred/.ssh/id_dsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/fred/.ssh/id_ecdsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/fred/.ssh/id_ecdsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/fred/.ssh/id_ed25519 type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/fred/.ssh/id_ed25519-cert type -1
debug1: Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_6.8
debug1: Remote protocol version 2.0, remote software version OpenSSH_6.7
debug1: match: OpenSSH_6.7 pat OpenSSH* compat 0x04000000
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: server->client aes128-ctr [email protected] none
debug1: kex: client->server aes128-ctr [email protected] none
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: Server host key: ecdsa-sha2-nistp256 SHA256:CEXGTmrVgeY1qEiwFe2Yy3XqrWdjm98jKmX0LK5mlQg
debug1: Host '198.51.100.20' is known and matches the ECDSA host key.
debug1: Found key in /home/fred/.ssh/known_hosts:2
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug1: SSH2_MSG_NEWKEYS received
debug1: Roaming not allowed by server
debug1: SSH2_MSG_SERVICE_REQUEST sent
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug1: Next authentication method: publickey
debug1: Trying private key: /home/fred/.ssh/id_rsa
debug1: Trying private key: /home/fred/.ssh/id_dsa
debug1: Trying private key: /home/fred/.ssh/id_ecdsa
debug1: Trying private key: /home/fred/.ssh/id_ed25519
debug1: Next authentication method: keyboard-interactive
debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug1: Next authentication method: password
debug1: Authentication succeeded (password).
Authenticated to server.example.org ([198.51.100.20]:22).
debug1: channel 0: new [client-session]
debug1: Requesting [email protected]
debug1: Entering interactive session.
debug1: client_input_global_request: rtype [email protected] want_reply 0
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug1: client_input_channel_req: channel 0 rtype [email protected] reply 0
debug1: channel 0: free: client-session, nchannels 1
debug1: fd 2 clearing O_NONBLOCK
Last login: Sat Mar 14 21:31:33 2015 from 192.0.2.111

...

相同登录使用最大详细级别,-vvv,给出大约 150 行调试信息。请记住,调试信息发送到stderr而不是stdout。这只会将会话捕获到文件中,调试信息只发送到屏幕,而不是输出日志。

$ ssh -vvv -l fred  somehost.example.org  | tee ~/ssh-output.log

工具tee(1)就像一个 T 管,将输出发送到两个方向,一个到stdout,另一个到文件。

以下将同时捕获调试信息和会话文本。

$ ssh -vvv -l fred  somehost.example.org  2>&1  | tee ~/ssh-output.log

单独捕获客户端调试信息

[edit | edit source]

常规管道和重定向仅适用于stdout,因此如果未使用-E捕获调试输出,则必须将stderr上的输出发送到 stdout,如果要同时捕获它和实际会话。这是通过额外的重定向2>&1来捕获stderr完成的。注意空格,或空格的缺乏。

在运行时或动态更改客户端调试级别

[edit | edit source]

在运行时,在建立新连接时,只需使用-v选项。

$ sftp -v -o "IdentityFile=~/.ssh/weblog.key_rsa" [email protected]

客户端上的调试详细程度可以像服务器一样增加。

$ sftp -vvv -o "IdentityFile=~/.ssh/weblog.key_rsa" [email protected]

额外的信息可以用来准确地查看正在发送到服务器或从服务器请求的内容。

事后,一旦连接建立,转义序列~v~V可以用来动态增加或减少详细程度。通过它们,可以在已建立的连接中更改客户端的详细程度。当增加时,客户端会将其日志级别提高到 VERBOSE、DEBUG、DEBUG2 和 DEBUG3,按此顺序,如果从默认的 INFO 开始。相反,当降低日志级别时,客户端将从默认的 INFO 开始,依次降至 ERROR、FATAL 和 QUIET。

调试和故障排除

[edit | edit source]

在故障排除时,服务器日志是你的好帮手。可能需要暂时提高那里的日志级别以获取更多信息。然后,在问题修复后,还需要将它们恢复到正常状态,以避免隐私问题或过度使用磁盘空间。

例如,SFTP 子系统日志记录默认设置为 ERROR,仅报告错误。要跟踪客户端执行的事务,请将日志级别更改为 INFO 或 VERBOSE。

Subsystem internal-sftp  -l INFO

注意。再次,使用更高的日志级别将违反用户的隐私,此外还会占用大量的磁盘空间,因此通常不应在生产环境中使用,一旦找出更改。在收集期间,更高的日志消息应该真正发送到单独的日志文件。

默认情况下,一些系统只将正常消息发送到常规系统日志文件,并忽略更高的消息。一些系统默认情况下保存所有消息。如果更高的系统日志消息没有显示在任何系统日志中,则可能是前者导致的。无论哪种方式,请检查系统日志配置,并确保额外的消息只发送到单独的日志文件,而不是与常规系统日志混合。如有必要,更改配置。这有助于保持日志整洁,并保护隐私。系统日志设置位于系统日志守护程序的配置文件中,其确切名称会根据安装的内容而有所不同,但常见的有syslog.conf(5)和 rsyslog.conf(5)。注意,以下机器配置将 AUTH 设施的更详细的 DEBUG 消息发送到与常规 AUTH 消息不同的单独日志文件。

$ grep '^auth\.' /etc/syslog.conf 
auth.info                                               /var/log/authlog
auth.debug                                              /var/log/authdebug

有关日志设施和日志级别的信息,请参见syslog(3)。最好限制收集调试信息的时间,并主动监控收集过程。但是,如果它运行了很长时间,尤其是如果它无人看管即使是短时间,请务必记住将特殊日志文件添加到日志轮转计划中,以防止它填满分区。

Match 块可以通过为特定情况设置日志级别来提供帮助,并避免所有内容都被密集记录的情况。

此外,OpenSSH 手册页写得非常好,很多时候可以通过在正确的手册页中找到正确的部分来解决问题。至少,重要的是要浏览所有四个主要程序手册页及其配置,并熟悉至少各节标题。

然后,一旦在手册页中找到正确的部分,就仔细阅读它,并熟悉其内容。其他 OpenSSH 手册页也是如此,具体取决于活动。请务必使用适用于您系统的 OpenSSH 版本以及相应的手册页,最好是安装在您系统上的手册页,以避免不匹配。在某些情况下,客户端和服务器的版本不同,因此必须分别查找每个版本的手册页。在发布新版本时,查看 OpenSSH 的发行说明也是一个好主意。

除了以下几个例外,故障排除的具体示例通常在与特定活动相关的食谱部分中给出。因此,例如,对身份验证密钥问题的排序是在公钥身份验证本身部分完成的。

调试使用 sudo(8) 的脚本、配置或密钥

[编辑 | 编辑源代码]

通常,只有在编写和测试脚本、新配置、一些新密钥或同时进行所有操作时,才需要更改日志级别。在使用sudo(8)时,尤其需要查看客户端发送的精确信息,以便将正确的模式输入到/etc/sudoers中以确保安全。使用最低的详细程度,客户端发送到远程服务器的精确字符串将显示在调试输出中。

$ rsync -e "ssh -v -i /home/webmaint/.ssh/bkup_key -l webmaint" \
        -a server.example.org:/var/www/ var/backup/www/
...
debug1: Authentication succeeded (publickey).
Authenticated to server.example.org ([192.0.2.20]:22).
debug1: channel 0: new [client-session]
debug1: Requesting [email protected]
debug1: Entering interactive session.
debug1: Sending command: rsync --server --sender -vlogDtpre.if . /var/www/
receiving incremental file list
...

假设帐户“webmaint”位于组“webmasters”中,sudoers需要类似以下内容:

%webmasters ALL=(ALL) NOPASSWD: /usr/local/bin/rsync --server \
--sender -vlogDtpre.if . /var/www/

同样的方法可以用来调试新的服务器配置或密钥登录。一旦将设置调整到需要运行的方式,就可以将sshd(8)的日志级别设置为INFO,将internal-sftp的日志级别设置为ERROR。此外,一旦将脚本留在了完全自动模式下运行,就可以使用syslog(3)系统模块来设置客户端日志信息,而不是使用stderr,方法是在启动时设置-y选项。

调试服务器配置

[编辑 | 编辑源代码]

在调试模式下运行服务器会提供有关连接的大量信息,以及有关服务器配置的一小部分信息。服务器的调试级别(-d)可以提高一次、两次(-dd)或三次(-ddd)。

$ /usr/sbin/sshd -d

请注意,在这种情况下,服务器不会分离并成为守护进程,因此当SSH连接终止时,它也会终止。必须重新启动服务器才能从客户端进行后续连接。尽管在某些方面这很麻烦,但它确实确保了会话数据是唯一的集合,而不是多个会话的混合,因此可能是不同的配置。或者,另一个选项(-e)是在调试时将调试数据发送到stderr以保持系统日志清洁。

在OpenSSH的最新版本中,还可以直接将系统日志中的调试数据记录到单独的文件,并防止系统日志产生噪音。从OpenSSH 6.3开始,选项-E将调试数据追加到特定的日志文件,而不是将其发送到系统日志。这便于调试实时系统,而不会弄乱系统日志。

$ /usr/sbin/sshd -E /home/fred/sshd.debug.log

在旧版本的OpenSSH中,如果需要将输出保存到文件,同时还要在屏幕上实时查看它,可以使用tee(1)

$ /usr/sbin/sshd -ddd 2>&1 | tee /tmp/foo

这将通过捕获sshd(8)发送到stderr的内容,将输出保存到文件foo中。这适用于旧版本的OpenSSH,但上面提到的-E选项更可取。

如果服务器是远程的,并且需要降低被锁定的风险,可以在第二个sshd(8)实例上进行配置文件的实验,使用单独的配置文件并监听高端口,直到设置经过测试为止。

$ /usr/sbin/sshd -dd -p 22222 -f /home/fred/sshd_config.test

可以对配置文件进行扩展测试(-T)。如果存在语法错误,它将被报告,但请记住,即使是正确的配置也可能导致你被锁定。扩展测试模式可以单独使用,但也可以指定要与-C一起使用的特定连接参数。sshd(8)将根据传递给它的参数处理配置文件,并输出结果。特别有用的是,Match指令的结果将被显示。因此,-T选项可以与-C选项一起使用,以准确显示哪些配置将用于各种连接。

在将特定的连接参数传递给sshd(8)进行评估时,userhostaddr是扩展测试所需的最小参数。以下内容将打印出如果用户fred尝试从地址192.0.2.15登录到主机server.example.org时将应用的配置。

$ /usr/sbin/sshd -T -C user=fred,host=server.example.org,addr=192.0.2.15

还可以传递另外两个参数,laddrlport。它们指的是服务器连接到的IP号和端口。

$ /usr/sbin/sshd -T -C user=fred,host=server.example.org,addr=192.0.2.15,laddr=192.0.2.2,lport=2222

这五个变量应该能够描述任何可能的传入连接。

调试客户端配置

[编辑 | 编辑源代码]

有时,在调试服务器配置时,还需要跟踪客户端。从OpenSSH 6.8开始,-G选项使ssh(1)在评估HostMatch块后打印其配置,然后退出。这允许查看客户端实际用于特定连接的确切配置选项。

$ ssh -G -l fred server.example.org

客户端配置通过三种方式确定。第一种是运行时选项,然后是帐户自己的配置文件,最后是系统范围的客户端配置文件。优先级按此顺序排列,找到的第一个值将被使用。对于sftp(1),这些选项也会传递给ssh(1)

无效或过时的密码或MAC

[编辑 | 编辑源代码]

正确的客户端将显示失败的详细信息。对于错误的消息认证码(MAC),当尝试将像hmac-md5-96这样的错误MAC强加于服务器时,正确的客户端可能会显示类似以下内容

no matching mac found: client hmac-md5-96 server [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1

对于错误的密码,当尝试将arcfour密码强加于服务器时,正确的客户端可能会显示类似以下内容

no matching cipher found: client arcfour server [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected]

有时,在排查客户端问题时,需要查看服务器日志。OpenSSH 6.7 中删除了不安全的MAC,在OpenSSH 7.2 中删除了不安全的密码,但一些第三方客户端可能仍然尝试使用它们来建立连接。在这种情况下,客户端可能不会提供太多信息,除了服务器意外关闭网络连接的模糊消息之外。但是,服务器日志将显示发生了什么事情。

fatal: no matching mac found: client hmac-sha1,hmac-sha1-96,hmac-md5 server [email protected],[email protected],[email protected],hmac-sha2-512,hmac-sha2-256,hmac-ripemd160 [preauth]

更新的版本将显示更简单的错误消息,用于错误的MAC。

fatal: Unable to negotiate with 192.0.2.37 port 55044: no matching MAC found. Their offer: hmac-md5-96 [preauth]

错误的密码将像这样报告

fatal: Unable to negotiate with 192.0.2.37 port 55046: no matching cipher found. Their offer: arcfour [preauth]

服务器日志中的错误消息可能不会说明哪些MAC或密码实际上可用。对于这些情况,可以使用扩展测试模式来显示服务器设置,特别是允许的MAC或密码。在最基本的用法中,扩展测试模式只是-T,就像/usr/sbin/sshd -T | grep -E 'cipher|macs'一样,没有其他选项。有关更多详细信息和选项,请参见上面有关“调试服务器配置”部分的内容。

一种解决方案是将客户端升级到能够处理正确密码和MAC的版本。另一种选择是切换到不同的客户端,能够处理现代密码或MAC的客户端。

调试基于密钥的身份验证

[编辑 | 编辑源代码]

公钥身份验证失败的最常见原因似乎是以下两种情况中的任何一种

  • 将公钥添加到服务器上的authorized_keys过程中出现错误。
  • 涉及的文件和目录的权限不正确,可能是客户端或服务器上的。这些目录是用于密钥的目录,通常是~/.ssh/,或其父目录,或者authorized_keys文件,或者私钥本身。

截至撰写本文时,看起来邮件列表和论坛上描述的几乎所有基于密钥的身份验证失败问题,都可以通过解决这两种情况中的任何一种或两种来解决。因此,当遇到错误消息“Permission denied (publickey,keyboard-interactive)”或类似消息时,请参阅公钥身份验证部分。然后查看sshd(8)的手册页及其关于授权密钥的部分。通常,尽管并不总是如此,但当私钥的权限不正确时,将很明显。

$ ssh -i ~/.ssh/fred-193.ed25519 [email protected]
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0664 for '/home/fred/.ssh/fred-193.ed25519' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "/home/fred/.ssh/fred-193.ed25519": bad permissions

除了授权密钥文件中的公钥被篡改以及权限错误外,还有一种非常罕见的第三种情况,即公钥和私钥文件不匹配,并非来自同一密钥对。如《公钥认证》一节所述,公钥和私钥需要匹配,并且属于同一密钥对。这是因为,即使在 SSH 客户端使用私钥进行加密之前,它也会查看所提议的私钥的文件名,然后发送与该名称匹配的公钥(如果存在)。如果客户端上的公钥与服务器上 authorized_keys 文件中的公钥不匹配,则连接将被拒绝,并出现“Permission denied (publickey,keyboard-interactive)”或类似的错误。仅此一项就是给公钥文件取唯一的描述性名称的充分理由。请注意,如上所述,除了客户端机器上公钥文件管理不当之外,通常还有其他原因会导致相同错误消息。

每个密钥对的两个部分的文件名必须保持一致,以便内容匹配。关于解决方案,从长远来看,需要更加仔细地管理密钥及其文件名。短期解决方案是删除有问题的公钥文件,或者使用私钥重新生成一个新的公钥文件,覆盖有问题的文件。再次强调,这是一个不常见的情况,并不是导致该错误的常见原因。

SSH 身份验证失败次数过多

[编辑 | 编辑源代码]

当身份验证代理中存在多个密钥时,客户端将以不可预测的顺序尝试将它们与服务器进行匹配。如果客户端恰好在找到正确密钥之前尝试了足够多的错误密钥,并且超过了服务器的 MaxAuthTries 限制,服务器自然会断开连接,并显示关于身份验证失败次数过多的错误消息。

"Received disconnect from 203.0.113.110 port 22:2: Too many authentication failures 
Authentication failed."

在提高详细程度后,将显示测试和拒绝的密钥。

$ ssh -v 203.0.113.110
...
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug1: Authentications that can continue: publickey,keyboard-interactive
debug1: Next authentication method: publickey
debug1: Offering RSA public key: /home/fred/.ssh/key.06.rsa
debug1: Authentications that can continue: publickey,keyboard-interactive
debug1: Offering RSA public key: /home/fred/.ssh/key.02.rsa
debug1: Authentications that can continue: publickey,keyboard-interactive
debug1: Offering RSA public key: /home/fred/.ssh/key.03.rsa
debug1: Authentications that can continue: publickey,keyboard-interactive
debug1: Offering RSA public key: /home/fred/.ssh/key.04.rsa
debug1: Authentications that can continue: publickey,keyboard-interactive
debug1: Offering RSA public key: /home/fred/.ssh/key.01.rsa
debug1: Authentications that can continue: publickey,keyboard-interactive
debug1: Offering RSA public key: /home/fred/.ssh/key.05.rsa
Received disconnect from 203.0.113.110 port 22:2: Too many authentication failures
Authentication failed.

代理中的每个密钥都带有一个注释,说明密钥文件是由用户提供的,是在配置文件中还是作为运行时参数提供的。客户端优先使用在配置文件中指定的并且当前位于代理中的密钥。然后它将按照提供的顺序尝试这些密钥。[3]

如果您遇到“身份验证失败次数过多”错误,则有两种解决方案。

解决此错误的一种方法是使用 ssh-add(1) 命令和 -d 选项从代理中逐一删除密钥,直到只剩下正确的密钥。请参考每个密钥的文件系统路径,例如:ssh-add -d ~/.ssh/some.key.rsa 由于要删除的私钥是在代理中根据相应的公钥查找的,因此两个文件都必须存在。如果没有匹配的公钥文件,则无法从身份验证代理中单独删除私钥。相反,可以使用 -D 选项一次性删除所有密钥。但是,当频繁使用多个远程系统并且需要代理保持充足的密钥时,这样做并不总是可行。这可能不是最实用的方法。

解决此错误的另一种方法,也是最实用的方法,是使用 IdentitiesOnly 配置指令结合 IdentityFile 配置指令,限制客户端只尝试特定密钥。后者明确指向正确的密钥。这两种指令都可以作为运行时选项添加到客户端的配置文件中。作为运行时选项,它们可以像这样使用

$ ssh -o IdentitiesOnly=yes -i ~/.ssh/server14.example.org.rsa -l fred server14.example.org

或者,这两个选项可以添加到客户端配置文件中,例如:

Host server14 server14.example.org
        HostName server14.example.org
        IdentitiesOnly yes
        IdentityFile /home/fred/.ssh/server14.example.org.rsa
        User fred

这样,无论是使用短名称还是完全限定的域名,都可以访问服务器,只要名称在 Host 指令下列出即可。

$ ssh server14

请记住,选项是从客户端配置文件中根据首个匹配项进行选择的。由于首个匹配项优先,因此特定规则必须放在更通用的规则之前。

签名失败 ... 代理拒绝操作

[编辑 | 编辑源代码]

如前所述,当尝试基于密钥的身份验证时,客户端文件或目录的权限错误是身份验证失败的常见原因。然而,并非所有错误都明确地说明这一点。以下是一个误导性错误消息的示例,实际上是由权限错误引起的

$ ssh -i ~/.ssh/key-ed25519 [email protected]
sign_and_send_pubkey: signing failed for ED25519 "/home/fred/.ssh/key-ed25519" from agent: agent refused operation
[email protected]: Permission denied (publickey).

解决此问题的办法是确保没有任何其他帐户能够读取或写入私钥,并且其他帐户也无法写入 .ssh 目录或其父目录。

调试 chroot SFTP 帐户

[编辑 | 编辑源代码]

最常见的问题似乎是目录权限错误。chroot 目录及其上方的所有目录都必须由 root 拥有,并且任何其他用户或组都不可写入。即使这些目录的组成员身份不必是 root,如果其中任何一个不是 root,那么它也不允许组写入。如果未使用正确的拥有权,将导致无法使用受影响的帐户登录。从客户端尝试登录时,将显示以下错误消息

$ sftp [email protected]
[email protected]'s password: 
packet_write_wait: Connection to 192.0.2.206: Broken pipe
Couldn't read packet: Connection reset by peer

服务器端显示的错误消息更加清晰

Aug  4 23:52:38 server sshd[7075]: fatal: bad ownership or modes for chroot directory component "/home/fred/"

检查 chroot 目标和其上方的所有目录的目录权限。如果发现任何一个目录权限错误,则必须修复该目录,使其由 root 拥有,并且任何其他用户都不可写入。修复方法有很多,这里提供两种方法来设置 chroot 权限。

  • 一种快速修复权限的方法是将目录的拥有权和组成员身份都更改为 root。对于 chroot 目标上方的所有目录,操作相同。
$ ls -lhd /home/ /home/fred/
drwxr-xr-x 3 root  root  4.0K Aug  4 20:47 /home/
drwxr-xr-x 8 root  root  4.0K Aug  4 20:47 /home/fred/

这将与 ChrootDirectory 指令设置为 %h 配合使用,但存在一些缺点,当添加文件或目录时会很快变得明显。

  • 另一种简单的修复权限的方法是更改帐户的主目录和 ChrootDirectory 指令。将帐户的主目录安排在由 root 拥有的唯一目录下,例如使用用户名本身
$ ls -lhd /home/ /home/fred/ /home/fred/fred/
drwxr-xr-x 3 root  root  4.0K Aug  4 20:47 /home/
drwxr-x--- 3 root  fred  4.0K Aug  4 20:47 /home/fred/
drwxr-x--- 8 fred  fred  4.0K Aug  4 20:47 /home/fred/fred/

然后将帐户 chroot 到父目录,并将其与 SFTP 服务器的 -d 选项结合使用,从用户名令牌开始的备用起始目录。

ChrootDirectory /home/%u
ForceCommand internal-sftp -d %u

然后,当帐户连接时,它将只能看到自己的目录,而看不到系统中的其他部分。

调试 RC 脚本干扰 SFTP 会话

[编辑 | 编辑源代码]

如果在 stdin 上(来自客户端或服务器)存在任何多余数据,则 SFTP 连接将断开。这方面的一个常见错误是如果 /etc/ssh/sshrc~/.ssh/rc 将任何数据发送到 stdout,而不是保持静默。在这种情况下,输出(将是服务器上的 stdout)将由客户端在 stdin 上接收,但不匹配任何正确的协议,因此导致客户端断开连接。因此,即使在使用 RC 脚本的情况下,服务器的响应也必须保持 8 位整洁,否则将发生错误。

$ sftp server.example.org
Received message too long 1400204832

该消息将是主要线索。通过 -v 选项提高 SFTP 客户端的详细程度不会提供更多相关信息。

此外,服务器上的标准日志只会显示客户端断开了连接,而不会提供任何断开连接的原因信息。在更高级别的日志记录中,可能会注意到一些多余的读取和相应的丢弃操作,但这仅此而已。以下是 DEBUG3 详细程度下记录的日志示例,显示了这种情况。

...
debug2: subsystem request for sftp by user fred
debug1: subsystem: exec() /usr/libexec/sftp-server
Starting session: subsystem 'sftp' for fred from 198.51.100.38 port 37446 id 0
...
debug2: channel 0: read 13 from efd 12
debug3: channel 0: discard efd
debug2: channel 0: read 12 from efd 12
debug3: channel 0: discard efd
debug2: channel 0: read 15 from efd 12
debug3: channel 0: discard efd
debug2: channel 0: read 18 from efd 12
debug3: channel 0: discard efd
...

再次强调,在使用 SFTP 时,任何 RC 脚本都不允许在 stdout 上产生任何输出,否则会破坏连接。如果 RC 脚本确实产生了输出,则必须将其重定向到系统日志、文件或发送到 stderr 而不是 stdout。常规的交互式 SSH 连接不会因使用 stdout 而受到干扰,客户端只会显示发送的内容。有关更多信息,请参见 sshd(8) 手册页中的“SSHRC”部分。

相同的限制适用于通过 stdinstdout 运行的 SSH 服务的任何其他部分,例如 ProxyJump 或某些 ProxyCommand 的用法。因此,另一个潜在干扰的示例是在使用 LocalCommand 与客户端指定在成功连接到服务器后在本地机器上执行的命令时。它产生的任何输出也需要重定向到 stderr。如果 LocalCommand 最终干扰了 ProxyJump,则连接将似乎挂起,停留在 stdout 被使用的那一步。

调试 SSH 代理拥有正确的私钥但不使用它时的情况

[编辑 | 编辑源代码]

在旧版本的 OpenSSH 中,私钥加载到代理时,公钥也必须在客户端可用。如果没有,代理将无法使用私钥,除非进行其他安排。一个症状是,虽然通过运行时参数指定密钥可以工作,但同一个密钥无法通过代理工作。

升级到更新的 OpenSSH 版本是更好的选择。否则,一种解决方法是在客户端的运行时参数中或在 ssh_config(5) 文件中指定私钥。客户端会找到相应名称的公钥文件。重要的是,客户端仍然会使用代理中的密钥,但使用指定的匹配公钥文件,因此私钥文件不必包含任何内容,甚至可以为空。

$ ssh -i some_key_ed25519 [email protected]

但是,如果不想在文件系统上访问私钥,或者私钥仅在代理中,而本身无法通过文件系统访问,那么可以直接指定公钥。

$ ssh -i some_key_ed25519.pub [email protected]

无论哪种方式,另一种方法是使用 **IdentityFile** 配置指令在 ssh_config(5) 中命名密钥。如果包含公钥的文件丢失,可以使用 ssh-keygen(1) 以及 **-y** 选项从私钥重新生成它。

SSH 客户端错误消息和常见原因

[编辑 | 编辑源代码]

作为以上内容的复述,以下是客户端的一些错误消息,以及这些消息的常见原因[4]。无论是错误列表还是错误原因都不是详尽的。对原因的建议只涉及到一些常见的理由。没有比检查实际日志更好的方法了,尤其是在服务器上。服务器日志文件通常在大多数系统上是 ** /var/log/auth.log ** 或者类似名称。有时您会发现信息在文件 ** /var/log/secure ** 中。

无关联地址

$ ssh nonesuch.example.org 
ssh: Could not resolve hostname nonesuch.example.org: no address associated with name

目标主机名在 DNS 中不存在。是否拼写正确?

操作超时

$ ssh 198.51.100.89 
ssh: connect to host 198.51.100.89 port 22: Operation timed out

没有与该 IP 地址关联的系统,或者存在导致故障的包过滤器。

连接超时

$ ssh 198.51.100.89 
ssh: connect to host 198.51.100.89 port 22: Connection timed out

您无法从这里到达那里。目标机器可能与网络断开连接,或者它所在的网络不可访问。

连接拒绝

$ ssh www.example.org
ssh: connect to host www.example.org port 22: Connection refused

目标系统存在,但尝试的端口上没有 SSH 服务可用。目标是否正确?可能是错误的系统,SSH 可能根本没有运行,SSH 可能在其他端口上监听,或者包过滤器可能阻止连接。

权限被拒绝

$ ssh www.example.org
[email protected]: Permission denied (publickey,keyboard-interactive).

这通常是用户名错误、身份验证方法错误、SSH 密钥或 SSH 证书错误或目标系统上授权密钥文件的文件权限错误导致的问题。如果是基于 SSH 密钥的身份验证,请参阅有关 公钥身份验证 的章节,以获取更详细的介绍。如果它与 SSH 证书相关,请参阅相应的 基于证书的身份验证 章节。它也可能是目标系统中使用 **AllowGroups**、**DenyGroups** 或类似配置指令的服务器端设置问题。任何这些可能性只能通过检查来自 sshd(8) 的日志输出来识别和解决。

未找到匹配的主机密钥类型

$ ssh www.example.org
Unable to negotiate with 198.51.100.89 port 22: no matching host key type found. Their offer: ssh-rsa,ssh-dss

该服务器上的 SSH 守护进程非常过时。联系系统管理员进行升级并让他们更新。使用客户端上的 **-v** 选项可以查看更多详细信息。

$ ssh -v [email protected]
...
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: diffie-hellman-group-exchange-sha256
debug1: kex: host key algorithm: (no match)
Unable to negotiate with 198.51.100.89 port 22: no matching host key type found. Their offer: ssh-rsa,ssh-dss

在上面的 shell 会话中,服务器非常需要更新,因为它尝试使用的密钥交换算法已过时。再次强调,解决方案是更新过时的软件。如果绝对必须使用特定版本的旧操作系统一段时间,请考虑获取 SSH 守护进程的移植版本。许多 GNU/Linux 发行版甚至有专门的移植版本库。

如果您想查看客户端支持哪些密钥交换算法,请尝试使用 **-Q** 选项。

$ ssh -Q kex
diffie-hellman-group1-sha1
diffie-hellman-group14-sha1
diffie-hellman-group14-sha256
diffie-hellman-group16-sha512
diffie-hellman-group18-sha512
diffie-hellman-group-exchange-sha1
diffie-hellman-group-exchange-sha256
ecdh-sha2-nistp256
ecdh-sha2-nistp384
ecdh-sha2-nistp521
curve25519-sha256
[email protected]
[email protected]

 

  1. "RFC 4252: The Secure Shell (SSH) Authentication Protocol". IETF. 2006. Retrieved 2021-08-10.
  2. Tucker, Darren (2021-08-10). "Re: ssh authlog: Failed none for invalid user". OpenBSD. https://marc.info/?l=openbsd-misc&m=162858437916966&w=2. Retrieved 2021-08-10. 
  3. Tucker, Darren (2019-04-01). "IdentityFile vs IdentitiesOnly". openssh-unix-dev mailing list. https://lists.mindrot.org/pipermail/openssh-unix-dev/2019-April/037700.html. Retrieved 2019-04-04. 
  4. "SSH Troubleshooting Guide". IT Tavern. 2023-01-17. Retrieved 2023-01-26.
华夏公益教科书