OpenSSH

来自 ArchWiki
(重定向自 X11 转发

OpenSSH (OpenBSD 安全 Shell) 是一组计算机程序,它使用 安全 Shell (SSH) 协议,在计算机网络上提供加密的通信会话。它被创建为 SSH Communications Security 提供的专有安全 Shell 软件套件的开源替代品。OpenSSH 是作为 OpenBSD 项目的一部分开发的,该项目由 Theo de Raadt 领导。

OpenSSH 有时会与名称相似的 OpenSSL 混淆;但是,这两个项目有不同的目的,由不同的团队开发,名称相似仅仅是因为目标相似。

安装

安装 openssh 软件包。

客户端使用

要连接到服务器,请运行

$ ssh -p port user@server-address

如果服务器仅允许公钥身份验证,请遵循 SSH 密钥

配置

此文章或章节需要扩充。

原因: openssh 9.4p1-2 在 /etc/ssh/ssh_config 中添加了 Include /etc/ssh/ssh_config.d/*.conf。 现在可以使说明使用 drop-in 文件。(在 Talk:OpenSSH 中讨论)

可以配置客户端以存储常用选项和主机。所有选项都可以全局声明或限制为特定主机。例如

~/.ssh/config
# global options
User user

# host-specific options
Host myserver
    Hostname server-address
    Port     port

使用这样的配置,以下命令是等效的

$ ssh -p port user@server-address
$ ssh myserver

有关更多信息,请参阅 ssh_config(5)

某些选项没有命令行开关等效项,但您可以使用 -o 在命令行上指定配置选项。例如 -oKexAlgorithms=+diffie-hellman-group1-sha1

服务端使用

此文章或章节需要扩充。

原因: openssh 9.4p1-2 在 /etc/ssh/sshd_config 中添加了 Include /etc/ssh/sshd_config.d/*.conf。 现在可以使说明使用 drop-in 文件。(在 Talk:OpenSSH 中讨论)

sshd 是 OpenSSH 服务器守护进程,使用 /etc/ssh/sshd_config 配置,并由 sshd.service 管理。每当更改配置时,在重新启动服务之前,请使用测试模式下的 sshd 以确保它可以干净地启动。有效的配置不会产生任何输出。

# sshd -t

配置

要仅允许某些用户访问,请添加此行

AllowUsers    user1 user2

要仅允许某些组访问

AllowGroups   group1 group2

要添加友好的欢迎消息(例如来自 /etc/issue 文件),请配置 Banner 选项

Banner /etc/issue

公共和私有主机密钥由 sshdgenkeys 服务/etc/ssh 中自动生成,即使 sshd_config 中的 HostKeyAlgorithms 选项仅允许某些算法,也会在丢失时重新生成。基于 ed25519、ecdsa 和 rsa 算法提供了三对密钥。要使 sshd 使用特定的密钥,请指定以下选项

HostKey /etc/ssh/ssh_host_ed25519_key

如果要将服务器暴露于 WAN,建议将默认端口从 22 更改为更高的随机端口,例如这样

Port 39901
提示
  • 为了帮助选择尚未分配给常用服务的备用端口,请查看 TCP 和 UDP 端口号列表。您还可以在本地 /etc/services 中找到端口信息。从默认端口 22 更改端口将减少由自动身份验证尝试引起的日志条目数量,但不会消除它们。有关相关信息,请参阅 端口敲门
  • 建议完全禁用密码登录。这将大大提高安全性,有关更多信息,请参阅 #强制公钥认证。有关更多建议的安全方法,请参阅 #保护
  • OpenSSH 可以通过在配置文件中包含多行 Port 端口号 来侦听多个端口。
  • 可以通过删除要替换的密钥对从 /etc/ssh 中删除,并以 root 身份运行 ssh-keygen -A 来生成新的(或丢失的)主机密钥对。

守护进程管理

启动/启用 sshd.service。它将使 SSH 守护进程永久处于活动状态,并为每个传入连接 fork。

注意: openssh 8.0p1-3 移除了使用 systemd 套接字激活的 sshd.socket,因为它容易受到拒绝服务攻击。 有关详细信息,请参阅 FS#62248。 如果在更新到 openssh 8.0p1-3 时启用了 sshd.socket,则 sshd.socketsshd@.service 单元将被复制到 /etc/systemd/system/重新启用。 这只是为了不破坏现有设置而进行的;仍然建议用户迁移到 sshd.service
警告: 如果您继续使用 sshd.socket,请注意其问题
  • sshd.socket 单元可能会失败(例如,由于内存不足的情况),并且无法在套接字单元上指定 Restart=always。 请参阅 systemd 问题 11553
  • 使用套接字激活可能会导致拒绝服务,因为过多的连接可能会导致拒绝进一步激活服务。 请参阅 FS#62248
注意: 使用 sshd.socket 会否定 ListenAddress 设置,因此它将允许通过任何地址进行连接。 要实现设置 ListenAddress 的效果,您必须通过 编辑 sshd.socket 来指定 ListenStream 的端口 IP(例如 ListenStream=192.168.1.100:22)。 您还必须在 [Socket] 下添加 FreeBind=true,否则设置 IP 地址将具有与设置 ListenAddress 相同的缺点:如果网络未及时启动,套接字将无法启动。
提示: 使用套接字激活时,将为每个连接启动 sshd@.service 的瞬态实例(具有不同的实例名称)。 因此,sshd.socket 和守护进程的常规 sshd.service 都不允许监视日志中的连接尝试。 可以通过以 root 身份运行 journalctl -u "sshd@*" 或以 root 身份运行 journalctl /usr/bin/sshd 来查看套接字激活的 SSH 实例的日志。

保护

允许通过 SSH 进行远程登录对于管理目的很有用,但可能会对服务器的安全性构成威胁。 SSH 访问通常是暴力破解攻击的目标,需要对其进行适当限制,以防止第三方获得对您服务器的访问权限。

ssh-audit 提供服务器和客户端配置的自动分析。 还有其他一些关于该主题的优秀指南和工具,例如

强制公钥认证

如果客户端无法通过公钥进行身份验证,则默认情况下,SSH 服务器会回退到密码身份验证,从而允许恶意用户尝试通过 暴力破解密码来获得访问权限。 防止此攻击最有效的方法之一是完全禁用密码登录,并强制使用 SSH 密钥。 这可以通过在守护进程配置文件中设置以下选项来实现

/etc/ssh/sshd_config.d/20-force_publickey_auth.conf
PasswordAuthentication no
AuthenticationMethods publickey
警告: 在将此添加到您的配置之前,请确保所有需要 SSH 访问的帐户都在相应的 authorized_keys 文件中设置了公钥身份验证。 有关更多信息,请参阅 SSH 密钥#将公钥复制到远程服务器

双因素认证和公钥

可以将 SSH 设置为需要多种身份验证方式;您可以使用 AuthenticationMethods 选项告诉需要哪些身份验证方法。 这使您可以使用公钥以及双因素授权。

认证提供程序

请参阅 Google Authenticator 以设置 Google Authenticator。

对于 Duo安装 duo_unixAUR,它将提供 pam_duo.so 模块。 阅读 Duo Unix 文档,以获取有关如何设置必要的 Duo 凭据(集成密钥、密钥、API 主机名)的说明。

PAM 设置

此文章或章节的事实准确性存在争议。

原因:[1] 以来,发行版默认设置为 KbdInteractiveAuthentication no。 后来,由于 FS#79285,调整了默认值的词法顺序,以允许优先级更高的用户代码片段,这些代码片段将与 20-pam.conf 下方的代码片段匹配。 但是,BBS 线程 似乎仅通过在 99-archlinux.conf 默认值之后排序自定义值来解决。(在 Talk:OpenSSH 中讨论)

要将 PAM 与 OpenSSH 一起使用,请编辑以下文件

/etc/ssh/sshd_config.d/20-pam.conf
KbdInteractiveAuthentication yes
AuthenticationMethods publickey keyboard-interactive:pam

然后,您可以使用公钥 PAM 设置要求的用户身份验证登录。

另一方面,如果您想同时验证公钥 PAM 设置要求的用户身份验证,请使用逗号而不是空格来分隔 AuthenticationMethods

/etc/ssh/sshd_config.d/20-pam.conf
KbdInteractiveAuthentication yes
AuthenticationMethods publickey,keyboard-interactive:pam

使用所需的公钥 pam 身份验证,您可能希望禁用密码要求

/etc/pam.d/sshd
auth      required  pam_securetty.so     #disable remote root
#Require google authenticator
auth      required  pam_google_authenticator.so
#But not password
#auth      include   system-remote-login
account   include   system-remote-login
password  include   system-remote-login
session   include   system-remote-login

防止暴力破解攻击

暴力破解是一个简单的概念:一个人不断尝试使用大量随机用户名和密码组合登录到网页或服务器登录提示(如 SSH)。

有关 iptables,请参阅 ufw#使用 ufw 进行速率限制简单状态防火墙#暴力破解攻击

自 9.8 以来,实现了一种类似于 fail2ban 的基本保护:选项 PerSourcePenalties 设置为合理的默认值。针对客户端在其源地址上强制执行各种条件的惩罚,从而导致在一段时间内拒绝连接。

或者,您可以使用自动化脚本来保护自己免受暴力破解攻击,该脚本会阻止任何试图暴力破解进入的人。

  • 仅允许来自受信任位置的传入 SSH 连接
  • 使用 fail2bansshguard 自动阻止密码验证失败次数过多的 IP 地址。
  • 使用 pam_shield 阻止在一定时间内执行过多登录尝试的 IP 地址。与 fail2bansshguard 相比,此程序不考虑登录成功或失败。

限制 root 登录

此文章或章节已过时。

原因: 在当前版本中,上游默认已禁用 Root 登录。我不清楚本节和子节的哪些部分是多余的。(在 Talk:OpenSSH 中讨论)

通常认为允许 root 用户不受限制地通过 SSH 登录是不好的做法。有两种方法可以限制 SSH root 访问权限以提高安全性。

拒绝

Sudo 有选择地为需要 root 权限的操作提供 root 权限,而无需针对 root 帐户进行身份验证。这允许锁定 root 帐户,以防止通过 SSH 访问,并可能充当针对暴力破解攻击的安全措施,因为现在攻击者除了密码之外还必须猜测帐户名。

可以通过编辑守护进程配置文件中的“身份验证”部分来配置 SSH 以拒绝使用 root 用户进行远程登录。只需将 PermitRootLogin 设置为 no

/etc/ssh/sshd_config.d/20-deny_root.conf
PermitRootLogin no

接下来,重启 SSH 守护进程。

现在您将无法通过 root 用户通过 SSH 登录,但仍然可以使用您的普通用户登录并使用 susudo 进行系统管理。

限制

某些自动化任务(例如远程全系统备份)需要完全 root 访问权限。为了以安全的方式允许这些操作,可以仅允许为选定的命令进行 root 登录,而不是禁用通过 SSH 进行 root 登录。这可以通过编辑 ~root/.ssh/authorized_keys 来实现,方法是在所需的密钥前加上前缀,例如,如下所示

command="rrsync -ro /" ssh-ed25519 ...

这将允许使用此特定密钥进行的任何登录仅执行引号之间指定的命令。

可以通过将以下内容添加到 sshd_config 来补偿在登录时暴露 root 用户名而增加的攻击面

PermitRootLogin forced-commands-only

此设置不仅会限制 root 可以通过 SSH 执行的命令,而且还会禁用密码的使用,从而强制 root 帐户使用公钥身份验证。

稍微宽松的替代方案将允许 root 执行任何命令,但通过强制执行公钥身份验证,使暴力破解攻击变得不可行。对于此选项,请设置

PermitRootLogin prohibit-password

锁定 authorized_keys 文件

警告: 锁定此文件仅防止用户错误和特定的简单人身攻击。它提供任何针对恶意程序或漏洞的保护。首先,使用多因素身份验证、防火墙并实践纵深防御以防止漏洞。

如果出于某种原因,您认为相关用户不应能够添加或更改现有密钥,则可以阻止他们操作该文件。

在服务器上,使 authorized_keys 文件对用户只读,并拒绝所有其他权限

$ chmod 400 ~/.ssh/authorized_keys

为了防止用户简单地将权限更改回原样,authorized_keys 文件上设置不可变位。为了防止用户重命名 ~/.ssh 目录并创建新的 ~/.ssh 目录和 authorized_keys 文件,也在 ~/.ssh 目录上设置不可变位。要添加或删除密钥,您必须从 authorized_keys 中删除不可变位并使其暂时可写。

提示: 建议通过例如 auditd 记录对任何 authorized_keys 文件的更改。

SSH 证书

虽然常见的 SSH 密钥和手动指纹验证可能很容易用于由单个管理员管理的一些主机,但是这种身份验证方法根本无法扩展。当多个用户需要通过 SSH 访问大量服务器时,手动验证每个主机的 ssh 公钥指纹几乎不可能安全可靠地完成。

此问题的解决方案是使用 SSH 证书,该证书通过信任链提供公钥身份的自动验证,该信任链比 SSH 的默认首次使用时信任方法扩展性更好。SSH 证书基本上只是普通的公共 SSH 密钥,但带有来自受信任证书颁发机构的额外签名,用于验证密钥身份。

为您的基础设施创建主机证书颁发机构密钥
$ ssh-keygen -t ed25519 -f ~/.ssh/ca_host_key -C 'Host certificate authority for *.example.com'

私有证书颁发机构密钥应安全存储,最好存储在智能卡或硬件令牌上,以防止密钥提取,例如 NitrokeyYubiKey

签署服务器的公共 SSH 主机密钥

将公共服务器密钥复制到包含私有证书颁发机构密钥的本地系统以对其进行签名

$ ssh-keygen -h -s ~/.ssh/ca_key -I certLabel -n server01.example.com ./ssh_host_ed25519_key.pub
移动新证书并配置 sshd 以使用它

生成的证书 ssh_host_ed25519_key-cert.pub 应复制到服务器上的 /etc/ssh/

/etc/ssh/sshd_config.d/20-ed25519_key.conf
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
配置所有客户端信任证书颁发机构
~/.ssh/known_hosts
@cert-authority  *.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKL8gB/pjuff005YNazwMCqJpgsXAbQ3r4VStd/CRKwU Host certificate authority for *.example.com
警告: 当服务器未提供证书进行身份验证时,默认情况下使用公钥身份验证作为回退。
SSH 用户证书

根据用户数量和部署方法,SSH 用户密钥也可以与证书一起使用。对于拥有许多 ssh 用户的组织,强烈建议使用证书来安全地管理用户密钥部署。

用户证书的部署基本上与服务器身份的部署相同。更多详细信息和说明可以在 Wikibooks:OpenSSH/Cookbook/基于证书的身份验证 中找到。

证书部署自动化

许多开源工具可以提供 SSH 证书的自动化部署。流行的例子有

SSHFP 记录

安全 Shell 指纹记录 (SSHFP) 是域名系统中的可选资源记录,它将 SSH 密钥与主机名关联。它可以通过使用 DNSSEC 而不是部署受信任的 CA 证书来验证公共服务器上的 SSH 指纹,这允许即使是未经管理的客户端也可以验证密钥指纹的有效性。

生成记录条目

要生成存储在 DNS 记录中所需的十六进制密钥指纹,请在目标服务器上创建哈希。

$ ssh-keygen -r host.example.com

这将读取指定域的所有可用 SSH 密钥,并输出有效的 SSHFP 记录,然后可以将其存储在受影响域的 DNS 条目中。

客户端配置

为了自动检索和信任存储为 SSHFP 记录的 SSH 密钥指纹,请将以下内容添加到您的 ssh 客户端配置文件中

~/.ssh/config
# global options
Match all
    VerifyHostKeyDNS yes

如果目标主机具有有效的 SSHFP 记录,并且该记录已通过有效的 DNSSEC 签名验证,则指纹将自动被接受,而无需提示用户验证主机身份。如果 DNS 记录未通过 DNSSEC 验证,则会提示用户验证指纹。

生成 SSHFP 记录

要确定特定域的 SSH 指纹,请使用 ssh-keyscan 以有效的 DNS 记录格式检索 ssh 指纹。(请注意,默认情况下,每种可用密钥类型的指纹都以 SHA1 和 SHA256 形式提供。)

$ ssh-keyscan -D github.com
; github.com:22 SSH-2.0-babeld-57ca1323
; github.com:22 SSH-2.0-babeld-57ca1323
github.com IN SSHFP 1 1 6f4c60375018bae0918e37d9162bc15ba40e6365
github.com IN SSHFP 1 2 b8d895ced92c0ac0e171cd2ef5ef01ba3417554a4a6480d331ccc2be3ded0f6b
; github.com:22 SSH-2.0-babeld-57ca1323
github.com IN SSHFP 3 1 3358ab5dd3e306c461c840f7487e93b697e30600
github.com IN SSHFP 3 2 a764003173480b54c96167883adb6b55cf7cfd1d415055aedff2e2c8a8147d03
; github.com:22 SSH-2.0-babeld-57ca1323
github.com IN SSHFP 4 1 e9619e2ed56c2f2a71729db80bacc2ce9ccce8d4
github.com IN SSHFP 4 2 f83898df0bef57a4ee24985ba598ac17fccb0c0d333cc4af1dd92be14bc23aa5
; github.com:22 SSH-2.0-babeld-57ca1323

由于 SSHFP 记录将密钥指纹存储为十六进制值,而 SSH 指纹的常见输出是公钥的 base64 编码 SHA256 哈希,因此有必要将记录转换回 base64 格式,以便将其与 known_hosts 文件或其他常用 SHA256 格式存储指纹的文档中的值进行比较。

$ echo "SSHFP-fingerprint" | xxd -r -p | base64

github.com 的示例,使用密钥类型 ed25519 的 sha256 指纹的十六进制值

$ echo "f83898df0bef57a4ee24985ba598ac17fccb0c0d333cc4af1dd92be14bc23aa5" | xxd -r -p | base64
+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU=

与 known_hosts 条目比较

$ ssh-keygen -l -f ~/.ssh/known_hosts
从 DNS 手动检索 SSHFP 记录
$ dig SSHFP targetdomain.tld +short

技巧与诀窍

加密 SOCKS 隧道

此文章或章节需要语言、wiki 语法或风格改进。请参阅 Help:Style 以供参考。

原因: 写得像博客文章。(在 Talk:OpenSSH 中讨论)

这对于连接到各种不安全无线连接的笔记本电脑用户非常有用。您唯一需要的是在某个安全位置(例如您的家或工作场所)运行的 SSH 服务器。使用像 DynDNS 这样的动态 DNS 服务可能很有用,这样您就不必记住您的 IP 地址。

步骤 1:启动连接

您只需执行此单个命令即可启动连接

$ ssh -TND 4711 user@host

其中 user 是您在 host 上运行的 SSH 服务器上的用户名。它会要求您输入密码,然后您就连接上了。N 标志禁用交互式提示,D 标志指定本地端口以进行侦听(您可以根据需要选择任何端口号)。T 标志禁用伪 tty 分配。

添加 verbose (-v) 标志很好,因为这样您就可以从该输出中验证它是否真的已连接。

步骤 2(变体 A):配置您的浏览器(或其他程序)

上述步骤仅在与使用此新创建的 SOCKS 隧道的 Web 浏览器或其他程序结合使用时才有用。由于 SSH 当前同时支持 SOCKS v4 和 SOCKS v5,因此您可以使用其中任何一种。

  • 对于 Firefox:在偏好设置 > 常规中,导航到页面底部,然后点击设置...,它位于网络设置标题的右侧。接下来,在新弹出的半窗口中,勾选手动代理配置选项,并在SOCKS 主机文本字段中输入 localhost,以及在旁边的端口文本字段中输入端口号(在上面的示例中为 4711)。
Firefox 不会自动通过 socks 隧道发出 DNS 请求。可以通过进一步向下滚动,勾选使用 SOCKS v5 时代理 DNS 来缓解这种潜在的隐私问题。显然,这仅在使用 SOCKS v5 而不是 v4 时才有效。
重启 Firefox 以激活这些设置。
  • 对于 Chromium:您可以将 SOCKS 设置设置为环境变量或命令行选项。例如,将以下函数之一添加到您的 .bashrc
function secure_chromium {
    port=4711
    export SOCKS_SERVER=localhost:$port
    export SOCKS_VERSION=5
    chromium &
    exit
}

或者

function secure_chromium {
    port=4711
    chromium --proxy-server="socks://127.0.0.1:$port" &
    exit
}

现在打开终端并执行

$ secure_chromium

享受您的安全隧道吧!

步骤 2(变体 B):设置本地 TUN 接口

此变体前期稍微复杂一些,但最终您不必手动为每个应用程序逐个配置以使用 SOCKS 代理。它涉及设置本地 TUN 接口并通过它路由流量。

请参阅 VPN over SSH#设置 badvpn 和隧道接口

X11 转发

X11 转发是一种机制,允许在远程系统上运行的 X11 程序的图形界面显示在本地客户端机器上。对于 X11 转发,远程主机不需要安装完整的 X11 系统;但是,它至少需要安装 xauthxauth 是一个实用程序,用于维护服务器和客户端用于 X11 会话身份验证的 Xauthority 配置(来源)。

警告: X11 转发具有重要的安全隐患,至少应通过阅读 ssh(1)sshd_config(5)ssh_config(5) 手册页的相关章节来了解这些隐患。另请参阅 这个 StackExchange 问题

设置

远程
  • 安装 xorg-xauth 软件包
  • /etc/ssh/sshd_config
    • X11Forwarding 设置为 yes
    • 验证 AllowTcpForwardingX11UseLocalhost 选项是否设置为 yes,以及 X11DisplayOffset 是否设置为 10(如果没有任何更改,这些是默认值,请参阅 sshd_config(5)
  • 然后重启 sshd 守护进程
客户端
  • 安装 xorg-xauth 软件包
  • 通过在命令行上指定 -X 开关进行机会性连接,或在客户端配置中将 ForwardX11 设置为 yes,来启用 ForwardX11 选项。
提示: 如果 GUI 绘制效果不佳或收到错误,您可以启用 ForwardX11Trusted 选项(命令行上的 -Y 开关);这将防止 X11 转发受到 X11 SECURITY 扩展 控制。如果您这样做,请务必阅读本节开头的警告

用法

正常登录到远程机器,如果在客户端配置文件中未启用 ForwardX11,则指定 -X 开关

$ ssh -X user@host

如果您在尝试运行图形应用程序时收到错误,请尝试 ForwardX11Trusted

$ ssh -Y user@host

如果输出 X11 forwarding request failed,请重新配置您的远程机器。一旦 X11 转发请求成功,您就可以在远程服务器上启动任何 X 程序,它将被转发到您的本地会话

$ xclock

包含 Can't open display 的错误输出表明 DISPLAY 设置不正确。

请注意某些应用程序,因为它们会检查本地机器上是否正在运行实例。Firefox 就是一个例子:要么关闭正在运行的 Firefox 实例,要么使用以下启动参数在本地机器上启动远程实例

$ firefox --no-remote

如果您在连接时收到 “X11 forwarding request failed on channel 0” 错误(并且服务器 /var/log/errors.log 显示 “Failed to allocate internet-domain X11 display socket”),请确保已安装 xorg-xauth 软件包。如果其安装不起作用,请尝试

  • 服务器上的 sshd_config 中启用 AddressFamily any 选项,或者
  • 服务器上的 sshd_config 中将 AddressFamily 选项设置为 inet。

将其设置为 inet 可能会修复 Ubuntu 客户端在 IPv4 上的问题。

为了以 SSH 服务器上的另一个用户身份运行 X 应用程序,您需要 xauth add 从 SSH 登录用户的 xauth list 中获取的身份验证行。

提示: 这里一些 有用的 链接,用于解决 X11 Forwarding 问题。

转发其他端口

除了 SSH 内置的 X11 支持外,它还可以通过本地转发或远程转发来安全地隧道任何 TCP 连接。

本地转发在本地机器上打开一个端口,连接到该端口的连接将被转发到远程主机,然后再转发到给定的目标。通常,转发目标将与远程主机相同,从而为同一台机器提供安全 shell 和,例如,安全 VNC 连接。本地转发通过 -L 开关及其随附的转发规范以 <隧道端口>:<目标地址>:<目标端口> 的形式完成。

因此

$ ssh -L 1000:mail.google.com:25 192.168.0.100

将使用 SSH 登录并在 192.168.0.100 上打开 shell,还将创建从本地机器的 TCP 端口 1000 到 mail.google.com 端口 25 的隧道。一旦建立,到 localhost:1000 的连接将连接到 Gmail SMTP 端口。对于 Google 来说,任何此类连接(尽管不一定是通过连接传输的数据)都将看起来源自 192.168.0.100,并且此类数据在本地机器和 192.168.0.100 之间是安全的,但在 192.168.0.100 和 Google 之间则不是,除非采取其他措施。

类似地

$ ssh -L 2000:192.168.0.100:6001 192.168.0.100

将允许连接到 localhost:2000,这些连接将被透明地发送到远程主机上的端口 6001。前面的示例对于使用 vncserver 实用程序(tightvnc 软件包的一部分)的 VNC 连接非常有用,尽管它非常有用,但明确声明其缺乏安全性。

远程转发允许远程主机通过 SSH 隧道和本地机器连接到任意主机,提供本地转发的功能反转,并且适用于远程主机由于防火墙而连接受限的情况。它通过 -R 开关和转发规范以 <隧道端口>:<目标地址>:<目标端口> 的形式启用。

因此

$ ssh -R 3000:irc.libera.chat:6667 192.168.0.200

将在 192.168.0.200 上启动 shell,并且从 192.168.0.200 到自身端口 3000(远程主机的 localhost:3000)的连接将通过隧道发送到本地机器,然后再发送到 irc.libera.chat 端口 6667,因此,在本例中,即使端口 6667 通常对其阻塞,也允许在远程主机上使用 IRC 程序。

本地和远程转发都可以用于提供安全“网关”,允许其他计算机利用 SSH 隧道,而无需实际运行 SSH 或 SSH 守护程序,方法是在转发规范中为隧道的起始端提供绑定地址,例如 <隧道地址>:<隧道端口>:<目标地址>:<目标端口><隧道地址> 可以是隧道起始端机器上的任何地址。地址 localhost 允许通过 localhost 或环回接口进行连接,而空地址或 * 允许通过任何接口进行连接。默认情况下,转发仅限于来自隧道“开始”处的机器的连接,即 <隧道地址> 设置为 localhost。本地转发不需要额外的配置;但是,远程转发受远程服务器的 SSH 守护程序配置的限制。有关远程转发和本地转发的更多信息,请参阅 sshd_config(5) 中的 GatewayPorts 选项和 ssh(1) 中的 -L address 选项。

跳转主机

在某些情况下,可能无法直接连接到您的目标 SSH 守护程序,并且需要使用跳转服务器(或 堡垒服务器)。因此,我们尝试将两个或多个 SSH 隧道连接在一起,并假设您的本地密钥已针对链中的每个服务器获得授权。这可以通过 SSH 代理转发 (-A) 和伪终端分配 (-t) 来实现,后者使用以下语法转发您的本地密钥

$ ssh -A -t -l user1 bastion1 \
  ssh -A -t -l user2 intermediate2 \
  ssh -A -t -l user3 target

可以使用 ProxyCommand 选项自动执行此操作

$ ssh -o ProxyCommand="ssh -W %h:%p bastion.example.org" targetserver.example.org

一种更简单、更安全的方法是使用带有 -J 标志的 ProxyJump 选项

$ ssh -J user1@bastion1,user2@intermediate2 user3@target

-J 指令中的多个主机可以用逗号分隔;它们将按列出的顺序连接。user...@ 部分不是必需的,但可以使用。-J 的主机规范使用 ssh 配置文件,因此如果需要,可以在那里设置特定于每个主机的选项。

ProxyCommand 和 ProxyJump 选项之间的主要区别在于,后者不需要跳转主机上的 shell。因此,这也意味着跳转服务器不需要访问用户的登录凭据或 SSH 代理转发。使用 ProxyJump 选项,ssh 客户端通过跳转服务器直接连接到目标服务器,从而在客户端和目标服务器之间建立端到端加密通道。

配置文件中 -J 标志的等效项是 ProxyJump 选项;有关详细信息,请参阅 ssh_config(5)

通过中继的反向 SSH

此文章或章节需要语言、wiki 语法或风格改进。请参阅 Help:Style 以供参考。

原因: SSH 隧道化的概念是经典的,因此一些详细解释的参考资料会很好。例如 [2],其中包含其他场景。(在 Talk:OpenSSH 中讨论)

其思想是客户端通过另一个中继连接到服务器,而服务器使用反向 SSH 隧道连接到同一个中继。当服务器位于 NAT 之后时,这很有用,而中继是用户可以访问的公共可访问的 SSH 服务器,用作代理。因此,先决条件是客户端的密钥已针对中继和服务器获得授权,并且服务器也需要针对中继进行授权以进行反向 SSH 连接。

以下配置示例假设 user1 是客户端上使用的用户帐户,user2 是中继上的用户帐户,user3 是服务器上的用户帐户。首先,假设我们将使用端口 2222,服务器需要建立反向隧道,使用

ssh -R 2222:localhost:22 -N user2@relay

这也可以通过启动脚本、systemd 服务、autosshsidedoorAUR 自动完成。

在客户端,连接通过以下方式建立

ssh -t user2@relay ssh user3@localhost -p 2222
注意: ssh user3@relay -p 2222 将要求您在中继服务器的防火墙中打开此端口,并允许来自其他地址的连接到此端口。

在relay的 ~/.ssh/authorized_keys 中也可以通过包含 command 字段来定义建立反向隧道的远程命令,如下所示

command="ssh user3@localhost -p 2222" ssh-ed25519 KEY2 user1@client

在这种情况下,连接通过以下方式建立

ssh user2@relay

或者,您可以向 ssh 配置添加一个条目,该条目同时指定 RemoteCommandRequestTTY

~/.ssh/config
Host jump-destination
    Hostname relay
    User user2
    RemoteCommand ssh user3@localhost -p 2222
    RequestTTY yes

这将减少连接到

ssh jump-destination
注意: 客户端终端中的 SCP 自动完成功能将不起作用,甚至 SCP 传输本身在某些配置下也无法工作。

多路复用

SSH 守护程序通常在端口 22 上监听。但是,许多公共互联网热点通常会阻止所有不在常规 HTTP/S 端口(分别为 80 和 443)上的流量,从而有效地阻止 SSH 连接。对此的直接解决方案是让 sshd 额外监听白名单端口之一

/etc/ssh/sshd_config
Port 22
Port 443

但是,端口 443 很可能已被提供 HTTPS 内容的 Web 服务器使用,在这种情况下,可以使用多路复用器,例如 sslh,它在多路复用端口上监听,并且可以智能地将数据包转发到多个服务。

加速 SSH

有几个客户端配置选项可以全局或针对特定主机加速连接。有关这些选项的完整描述,请参阅 ssh_config(5)

  • 使用更快的密码:在具有 AESNI 指令的现代 CPU 上,aes128-gcm@openssh.comaes256-gcm@openssh.com 应该比 openssh 的默认首选密码(通常为 chacha20-poly1305@openssh.com)提供显着更好的性能。可以使用 -c 标志选择密码。为了获得永久效果,请在您的 ~/.ssh/config 中使用新的首选顺序的密码放置 Ciphers 选项,例如
    Ciphers aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
  • 启用或禁用压缩:压缩可以提高慢速连接的速度;通过 Compression yes 选项或 -C 标志启用压缩。但是,使用的压缩算法是相对较慢的 gzip(1),它在快速网络上会成为瓶颈。为了加速连接,应该在本地或快速网络上使用 Compression no 选项。
  • 连接共享:您可以使用以下选项使到同一主机的​​所有会话共享单个连接
    ControlMaster auto
    ControlPersist yes
    ControlPath ~/.ssh/sockets/socket-%r@%h:%p
    
其中 ~/.ssh/sockets 可以是其他用户不可写的任何目录。
  • ControlPersist 指定在初始客户端连接关闭后,主连接应在后台等待新客户端的时间。可能的值可以是
    • no,在最后一个客户端断开连接后立即关闭连接,
    • 以秒为单位的时间,
    • yes,永远等待,连接永远不会自动关闭。
  • 可以通过使用 AddressFamily inet 选项或 -4 标志绕过 IPv6 查找来缩短登录时间。
  • 最后,如果您打算将 SSH 用于 SFTP 或 SCP,高性能 SSH/SCP 可以通过动态增加 SSH 缓冲区大小来显着提高吞吐量。安装软件包 openssh-hpnAUR 以使用带有此增强功能的 OpenSSH 修补版本。

使用 SSHFS 挂载远程文件系统

请参阅 SSHFS 文章,将可通过 SSH 访问的远程系统挂载到本地目录,这样您就可以使用任何工具(复制、重命名、使用 vim 编辑等)对挂载的文件执行任何操作。通常首选 sshfs 而不是 shfs,后者自 2004 年以来未更新。

保持连接

默认情况下,如果 SSH 会话空闲一段时间,它会自动注销。为了保持会话连接,如果一段时间内未收到任何数据,客户端可以向服务器发送保持活动信号,或者服务器可以定期发送消息(如果它没有收到客户端的消息)。

  • 服务器端,ClientAliveInterval 设置超时时间(以秒为单位),如果在超时时间后未收到来自客户端的任何数据,sshd 将发送响应请求。默认值为 0,不发送消息。例如,要每 60 秒向客户端请求一次响应,请在您的服务器配置中设置 ClientAliveInterval 60 选项。另请参阅 ClientAliveCountMaxTCPKeepAlive 选项。
  • 客户端端,ServerAliveInterval 控制从客户端发送到服务器的响应请求之间的间隔。例如,要每 120 秒向服务器请求一次响应,请将 ServerAliveInterval 120 选项添加到您的客户端配置中。另请参阅 ServerAliveCountMaxTCPKeepAlive 选项。
注意: 为了确保会话保持活动状态,客户端或服务器只需一方发送保持活动请求。如果一方控制服务器和客户端,一个合理的选择是仅配置需要持久会话的客户端,并为其他客户端和服务器保留其默认配置,并将 ServerAliveInterval 设置为正值。

使用 systemd 自动重启 SSH 隧道

systemd 可以在启动/登录时自动启动 SSH 连接,并且在连接失败时重启它们。这使其成为维护 SSH 隧道的有用工具。

以下服务可以使用您的ssh 配置中的连接设置在登录时启动 SSH 隧道。如果连接因任何原因关闭,它将等待 10 秒钟,然后重新启动它

~/.config/systemd/user/tunnel.service
[Unit]
Description=SSH tunnel to myserver

[Service]
Type=simple
Restart=always
RestartSec=10
ExecStart=/usr/bin/ssh -F %h/.ssh/config -N myserver

然后启用启动 Systemd/User 服务。请参阅 #保持连接 了解如何防止隧道超时。如果您希望在启动时启动隧道,您可能需要将单元文件重写为系统服务。

Autossh - 自动重启 SSH 会话和隧道

当会话或隧道无法保持活动状态时,例如由于不良的网络条件导致客户端断开连接,您可以使用 autossh 自动重启它们。

用法示例

$ autossh -M 0 -o "ServerAliveInterval 45" -o "ServerAliveCountMax 2" username@example.com

SSHFS 结合使用

$ sshfs -o reconnect,compression=yes,transform_symlinks,ServerAliveInterval=45,ServerAliveCountMax=2,ssh_command='autossh -M 0' username@example.com: /mnt/example 

通过 代理设置 设置的 SOCKS 代理连接

$ autossh -M 0 -o "ServerAliveInterval 45" -o "ServerAliveCountMax 2" -NCD 8080 username@example.com 

使用 -f 选项,可以使 autossh 在后台进程中运行。但是,以这种方式运行意味着无法交互式输入密码。

一旦您在会话中键入 exit,或者 autossh 进程收到 SIGTERM、SIGINT 或 SIGKILL 信号,会话将结束。

通过 systemd 在启动时自动运行 autossh

如果您想自动启动 autossh,您可以创建一个 systemd 单元文件

/etc/systemd/system/autossh.service
[Unit]
Description=AutoSSH service for port 2222
After=network.target

[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 0 -NL 2222:localhost:2222 -o TCPKeepAlive=yes foo@bar.com

[Install]
WantedBy=multi-user.target

在这里,AUTOSSH_GATETIME=0 是一个环境变量,用于指定 ssh 必须启动多长时间 autossh 才能将其视为成功连接,将其设置为 0 autossh 也会忽略 ssh 的首次运行失败。这在启动时运行 autossh 时可能很有用。其他环境变量可在 autossh(1) 中找到。当然,如果需要,您可以使此单元更复杂(有关详细信息,请参阅 systemd 文档),并且显然您可以为 autossh 使用您自己的选项,但请注意,暗示 AUTOSSH_GATETIME=0-f 不适用于 systemd。

之后记得启动和/或启用该服务。

您可能还需要禁用 ControlMaster,例如

ExecStart=/usr/bin/autossh -M 0 -o ControlMaster=no -NL 2222:localhost:2222 -o TCPKeepAlive=yes foo@bar.com
提示: 维护多个 autossh 进程也很容易,以保持多个隧道处于活动状态。只需创建多个具有不同名称的服务文件即可。

SSH 守护进程失败时的替代服务

对于专门依赖 SSH 的远程或无头服务器,SSH 守护进程启动失败(例如,在系统升级后)可能会阻止管理访问。systemd 通过 OnFailure 选项提供了一个简单的解决方案。

假设服务器运行 sshd,而 telnet 是首选的故障安全替代方案。创建一个如下所示的文件。不要启用 telnet.socket

/etc/systemd/system/sshd.service.d/override.conf
[Unit]
OnFailure=telnet.socket

就是这样。当 sshd 运行时,Telnet 不可用。如果 sshd 启动失败,则可以打开 telnet 会话进行恢复。

基于主机的终端背景颜色

为了更好地区分您何时在不同的主机上,您可以设置基于主机类型的不同背景颜色

此解决方案有效,但并非通用(仅限 ZSH)。

特定于网络的配置

您可以使用特定于您连接到的网络的特定主机配置,使用 Match exec

例如,当使用 nmcli(1) 时,并且连接配置为(手动或通过 DHCP)使用搜索域

Match exec "nmcli | grep domains: | grep example.com"
  CanonicalDomains example.com
  # Should you use a different username on this network
  #User username
  # Use a different known_hosts file (for private network or synchronisation)
  #UserKnownHostsFile <network>_known_hosts

Match host ... exec "..." 的另一个示例:考虑连接到 internal.example.com 需要堡垒/代理(通过 ProxyJump),除非您已经通过 VPN 连接。片段 !exec "host internal.example.com" 仅在无法通过 DNS 查找 internal.example.com 时应用。在 [3] 中讨论了各种替代方案。

Match host internal.example.com !exec "host internal.example.com"
  ProxyJump bastion.example.com
Host internal.example.com
  User foobar

私有网络主机密钥验证

由于不同网络上的不同服务器可能共享一个通用的私有 IP 地址,因此您可能希望以不同的方式处理它们。

此文章或章节的事实准确性存在争议。

原因: 最好的解决方案不需要警告在实践中使用其他方案。(在 Talk:OpenSSH 中讨论)

最好的解决方案是使用#特定于网络的配置来使用不同的 UserKnownHostsFile,具体取决于您所在的网络。第二种解决方案,最好在您处理新的/原型网络时用作默认设置,是简单地忽略私有网络的主机密钥

Host 10.* 192.168.*.* 172.31.* 172.30.* 172.2?.* 172.1?.*
    # Disable HostKey verification
    # Trust HostKey automatically
    StrictHostKeyChecking no
    # Do not save the HostKey
    UserKnownHostsFile=/dev/null
    # Do not display: "Warning: Permanently Added ..."
    LogLevel Error

此文章或章节的事实准确性存在争议。

原因: 即使您使用主机名访问服务器,known_hosts 文件也会记录 IP 地址。(在 Talk:OpenSSH 中讨论)
警告: 在生产环境中,请确保使用主机名访问主机和/或使用特定于网络的 known_hosts 文件。

登录时运行命令

如果您正在使用交互式会话,则有多种方法可以在登录时执行命令

  • 使用远程主机上的 authorized_keys 文件(请参阅 sshd(8) § AUTHORIZED_KEYS FILE FORMAT
  • 如果服务器已启用 PermitUserRC 选项,则使用远程主机上的 ~/.ssh/rc
  • 使用远程主机上的 shell 配置文件,例如 .bashrc

代理转发

SSH 代理转发允许您在连接到服务器时使用本地密钥。建议仅为选定的主机启用代理转发。

~/.ssh/config
Host myserver.com
    ForwardAgent yes

接下来,配置 SSH 代理 并使用 ssh-add 添加您的本地密钥。

如果您现在连接到远程服务器,您将能够使用您的本地密钥连接到其他服务。

生成新密钥

可以通过以下方式生成新的服务器私钥

  1. 删除所有密钥,例如
    # rm /etc/ssh/ssh_host_*_key*
  2. 重启 sshdgenkeys.service 或以 root 身份运行 ssh-keygen -A

以非特权用户身份运行 sshd

您可能希望在容器中或用于测试等以非特权用户身份运行 sshd

由于非特权用户无法读取 /etc/ssh 中的主机密钥,因此必须生成新的主机密钥

$ ssh-keygen -q -N "" -t rsa -b 4096 -f /path/to/host/keys/ssh_host_rsa_key
$ ssh-keygen -q -N "" -t ecdsa -f /path/to/host/keys/ssh_host_ecdsa_key
$ ssh-keygen -q -N "" -t ed25519 -f /path/to/host/keys/ssh_host_ed25519_key

创建一个 sshd_config 文件。下面的示例使用高于 1024 的端口,提供主机密钥的新路径并禁用 PAM

/path/to/sshd_config
Port 2022
HostKey /path/to/host/keys/ssh_host_rsa_key
HostKey /path/to/host/keys/ssh_host_ecdsa_key
HostKey /path/to/host/keys/ssh/ssh_host_ed25519_key
UsePAM no

使用创建的配置运行 sshd-D 标志禁用守护进程模式,-e 将输出重定向到 stderr 以方便监控。

$ sshd -f /path/to/sshd_config -D -e

故障排除

检查清单

在您深入了解之前,请检查这些简单的问题。

  1. 配置目录 ~/.ssh 及其内容应仅可由用户访问(在客户端和服务器上都检查这一点),并且用户的主目录应仅可由用户写入
    $ chmod go-w ~
    $ chmod 700 ~/.ssh
    $ chmod 600 ~/.ssh/*
    $ chown -R $USER ~/.ssh
    
  2. 检查客户端的公钥(例如 id_ed25519.pub)是否在服务器上的 ~/.ssh/authorized_keys 中。
  3. 检查您是否在服务器配置中使用 AllowUsersAllowGroups 限制了 SSH 访问。
  4. 检查用户是否设置了密码。有时尚未登录到服务器的新用户没有密码。
  5. 附加 LogLevel DEBUG/etc/ssh/sshd_config
  6. 以 root 身份运行 journalctl -xe 以获取可能的(错误)消息。
  7. 重启 sshd 并在客户端和服务器上注销/登录。

连接被拒绝或超时问题

端口转发

如果您位于 NAT 模式/路由器之后(除非您在 VPS 或公共寻址的主机上,否则很可能如此),请确保您的路由器正在将传入的 ssh 连接转发到您的机器。使用 $ ip addr 找到服务器的内部 IP 地址,并将您的路由器设置为将 SSH 端口上的 TCP 转发到该 IP。portforward.com 可以提供帮助。

SSH 是否正在运行并监听?

ss 实用程序使用以下命令行显示所有正在监听 TCP 端口的进程

$ ss --tcp --listening

如果以上命令未显示系统正在监听端口 ssh,则 SSH 未运行:检查日志中是否有错误等。

是否有防火墙规则阻止连接?

Iptables 可能会阻止端口 22 上的连接。使用以下命令检查:

# iptables -nvL

并查找可能正在 INPUT 链上丢弃数据包的规则。然后,如有必要,使用类似以下的命令取消阻止端口:

# iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT

有关配置防火墙的更多帮助,请参阅防火墙

流量甚至到达您的计算机了吗?

在您遇到问题的计算机上启动流量转储

# tcpdump -lnn -i any port ssh and tcp-syn

这应该显示一些基本信息,然后等待任何匹配的流量发生,然后再显示它。现在尝试您的连接。如果您在尝试连接时没有看到任何输出,则您计算机外部的某些东西正在阻止流量(例如,硬件防火墙、NAT 路由器等)。

您的 ISP 或第三方阻止默认端口?

注意: 如果您知道您没有运行任何防火墙,并且您知道您已将路由器配置为 DMZ 或已将端口转发到您的计算机,但仍然不起作用,请尝试此步骤。您将在此处找到诊断步骤和可能的解决方案。

在某些情况下,您的 ISP 可能会阻止默认端口(SSH 端口 22),因此无论您尝试什么(打开端口、强化堆栈、防御洪水攻击等)最终都将无济于事。为了确认这一点,请在所有接口 (0.0.0.0) 上创建一个服务器并远程连接。

如果您收到类似于此的错误消息

ssh: connect to host www.inet.hr port 22: Connection refused

这意味着端口被 ISP 阻止,而是服务器未在该端口上运行 SSH(请参阅 隐蔽式安全)。

但是,如果您收到类似于此的错误消息

ssh: connect to host 111.222.333.444 port 22: Operation timed out 

这意味着某些东西正在端口 22 上拒绝您的 TCP 流量。基本上,该端口是隐形的,可能是由于您的防火墙或第三方干预(例如 ISP 阻止和/或拒绝端口 22 上的传入流量)。如果您知道您没有在计算机上运行任何防火墙,并且您知道 Gremlins 没有在您的路由器和交换机中生长,那么您的 ISP 正在阻止流量。

为了再次检查,您可以在您的服务器上运行 Wireshark 并监听端口 22 上的流量。由于 Wireshark 是一个第 2 层数据包嗅探实用程序,而 TCP/UDP 是第 3 层及以上(请参阅 IP 网络堆栈),如果您在远程连接时没有收到任何内容,则第三方很可能正在阻止到您的服务器的该端口上的流量。

诊断

安装 tcpdump 或带有 wireshark-cli 软件包的 Wireshark。

对于 tcpdump

# tcpdump -ni interface "port 22"

对于 Wireshark

$ tshark -f "tcp port 22" -i interface

其中 interface 是 WAN 连接的网络接口(请参阅 ip a 进行检查)。如果您在尝试远程连接时未收到任何数据包,则可以非常确定您的 ISP 正在阻止端口 22 上的传入流量。

可能的解决方案

解决办法是使用 ISP 未阻止的其他端口。打开 /etc/ssh/sshd_config 并配置文件以使用不同的端口。例如,添加

Port 22
Port 1234

还要确保文件中其他的 "Port" 配置行都被注释掉。仅仅注释掉 "Port 22" 并添加 "Port 1234" 并不能解决问题,因为这样 sshd 将只监听端口 1234。同时使用这两行可以在两个端口上运行 SSH 服务器。

重启 服务器 sshd.service,您就快完成了。您仍然需要配置您的客户端以使用其他端口而不是默认端口。有很多解决方案可以解决这个问题,但让我们在这里介绍其中两种。

从套接字读取失败:连接被对端重置

在连接到较旧的 ssh 服务器时,新版本的 OpenSSH 有时会失败并显示上述错误消息。可以通过为该主机设置各种客户端选项来解决此问题。有关以下选项的更多信息,请参阅 ssh_config(5)

问题可能是 ecdsa-sha2-nistp*-cert-v01@openssh 椭圆曲线主机密钥算法。可以通过将 HostKeyAlgorithms 设置为排除这些算法的列表来禁用它们。在客户端,客户端想要使用的 HostKeyAlgorithms 也可以通过在 HostKeyAlgorithms 列表前加上 - 来设置,以从默认集合中删除指定的算法(包括通配符)(请参阅 ssh_config(5) 手册页)。您可以使用 ssh -v server_to_connect_to 在包含 kex: host key algorithm: 的行中检查实际使用的主机密钥算法。

如果这不起作用,可能是密码列表太长。将 Ciphers 选项设置为较短的列表(少于 80 个字符应该足够)。同样,您也可以尝试缩短 MACs 列表。

另请参阅 OpenSSH 错误论坛上的讨论

"[您的 shell]: 没有那个文件或目录" / ssh_exchange_identification 问题

一个可能的原因是某些 SSH 客户端需要在 $SHELL 中找到绝对路径(例如,whereis -b [您的 shell] 返回的路径),即使 shell 的二进制文件位于 $PATH 条目之一中。

"终端未知" 或 "打开终端时出错" 错误消息

如果您在登录时收到上述错误,则表示服务器无法识别您的终端。像 nano 这样的 Ncurses 应用程序可能会失败并显示消息 "Error opening terminal"。

正确的解决方案是在服务器上安装客户端终端的 terminfo 文件。这会告诉服务器上的控制台程序如何正确地与您的终端交互。您可以使用 $ infocmp 获取有关当前 terminfo 的信息,然后找出 哪个软件包拥有它

如果您无法正常安装它,您可以将您的 terminfo 复制到服务器上的主目录

$ ssh myserver mkdir -p  ~/.terminfo/${TERM:0:1}
$ scp /usr/share/terminfo/${TERM:0:1}/$TERM myserver:~/.terminfo/${TERM:0:1}/

从服务器登录和注销后,问题应该得到解决。

TERM hack

注意: 这应该只作为最后的手段使用。

或者,您可以简单地在服务器上的环境中设置 TERM=xterm(例如,在 .bash_profile 中)。这将消除错误并允许 ncurses 应用程序再次运行,但您可能会遇到奇怪的行为和图形故障,除非您的终端的控制序列与 xterm 的完全匹配。

连接被 x.x.x.x [preauth] 关闭

如果您在 sshd 日志中看到此错误,请确保您已设置有效的 HostKey

HostKey /etc/ssh/ssh_host_ed25519_key

子系统请求失败

OpenSSH 8.8 起,scp 使用 SFTP 作为数据传输的默认协议,方法是请求名为 sftp 的子系统。如果您在 verbose 模式下运行 scpscp -v,您可以确定您的客户端正在使用哪个子系统(例如,Sending subsystem: <subsystem-name>)。诸如 subsystem request failed on channel 0 之类的错误可以通过配置服务器的 Subsystem 设置来修复:sshd_config(5) § Subsystem。服务器配置应类似于以下示例。

/etc/ssh/sshd_config
...
Subsystem subsystem-name /path/to/subsystem-executable
...

id_dsa 被拒绝

OpenSSH 7.0 弃用了 DSA 公钥,因为其安全性较低,并且 OpenSSH 9.8 在构建时默认不支持 DSA 密钥。2025 年的第一个 OpenSSH 版本将完全删除 DSA 支持。目前,如果您绝对必须使用它们,您需要重新构建 openssh,同时将 --enable-dsa-keys 传递给 configure[4]

OpenSSH 7.0 找不到匹配的密钥交换方法

OpenSSH 7.0 弃用了 diffie-hellman-group1-sha1 密钥算法,因为它很弱,并且在所谓的 Logjam 攻击的理论范围内(请参阅 https://www.openssh.com/legacy.html)。如果特定主机需要该密钥算法,ssh 将产生如下错误消息

Unable to negotiate with 127.0.0.1: no matching key exchange method found.
Their offer: diffie-hellman-group1-sha1

解决这些故障的最佳方法是升级/配置服务器以不使用已弃用的算法。如果不可能,您可以强制客户端使用客户端选项 KexAlgorithms +diffie-hellman-group1-sha1 重新启用该算法。

断开 SSH 连接时 tmux/screen 会话被终止

如果您的进程在会话结束时被终止,则可能是您正在使用套接字激活,并且当 systemd 注意到 SSH 会话进程退出时将其终止。在这种情况下,有两种解决方案。一种是通过使用 ssh.service 而不是 ssh.socket 来避免使用套接字激活。另一种是在 ssh@.service 的 Service 部分中设置 KillMode=process

KillMode=process 设置对于经典的 ssh.service 也可能有用,因为它避免在服务器停止或重启时终止 SSH 会话进程或 screentmux 进程。

SSH 会话停止响应

SSH 响应 流控制命令 XONXOFF。当您按下 Ctrl+s 时,它将冻结/挂起/停止响应。使用 Ctrl+q 恢复您的会话。

Broken pipe (管道破裂)

如果您尝试创建连接,导致 packet_write_waitBroken pipe 响应,您应该在调试模式下重新尝试连接,并查看输出是否以错误结尾

debug3: send packet: type 1
packet_write_wait: Connection to A.B.C.D port 22: Broken pipe

上面的 send packet 行表明回复数据包从未收到。因此,可以推断出这是一个 QoS 问题。为了减少数据包被丢弃的可能性,请设置 IPQoS

/etc/ssh/ssh_config
Match all
    IPQoS reliability

reliability (0x04) 服务类型应该可以解决问题,0x00throughput (0x08) 也是如此。

终止无响应的 SSH 连接

如果客户端会话不再响应,并且无法通过指示正在运行的程序(例如shell)来终止,您仍然可以通过依次按下 Enter~. 来终止会话。

~ 是伪终端转义字符(请参阅 ssh(1) § ESCAPE CHARACTERS),可以根据客户端会话添加多次以终止。例如,如果您从 A 连接到 B,然后从 B 连接到 C,并且从 B 到 C 的会话冻结,您可以通过按下 Enter 并键入 ~~. 来终止它,这将使您在 B 上处于工作会话中。

警告:远程主机标识已更改!

如果客户端警告 ssh 服务器的密钥已更改,您应该通过经过身份验证的(不一定是加密的)通道验证新提供的密钥是否真的属于服务器运营商。然后使用 ssh-keygen -R $SSH_HOSTknown_hosts 文件中删除旧密钥,并接受新密钥,就像它是新服务器一样。

连接到没有相应 terminfo 条目的远程主机

当连接到没有您的终端的 terminfo 条目的主机时,例如,当使用 terminfo 条目未与 ncurses 一起提供的终端模拟器(例如 kittyrxvt-unicode),或者当连接到具有有限 terminfo 数据库的主机(例如运行 OpenWrt 的系统)时,依赖于 terminfo(5) 的软件会发生各种问题。

正确的解决方案是在主机上放置相应的 terminfo 条目。如果这不可行,另一种方法是将 TERM 设置为远程主机支持且与终端兼容的值。

自 OpenSSH 8.7 起,可以使用简单的配置片段将自定义 TERM 环境变量传递给远程主机

~/.ssh/config
Host example.com
  SetEnv TERM=xterm-256color

通过跳板机连接失败,并显示 "bash: 没有那个文件或目录"

如果您没有将 SHELL 环境变量设置为完整的有效路径(在跳板服务器上),则连接将失败,并显示类似于此错误消息的消息

bash: No such file or directory
kex_exchange_identification: Connection closed by remote host
Connection closed by UNKNOWN port 65535

您可以通过将 SHELL 设置为在跳板服务器上也有效的 shell 的完整路径名,或者通过在 ~/.ssh/config 文件中为每个服务器设置特定的 SHELL 变量来轻松解决此问题。

参见