OpenSSH

来自 ArchWiki
(重定向自 Sshd

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 port_number 行来监听多个端口。
  • 可以通过从 /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 凭据(Integration Key,Secret Key,API Hostname)的说明。

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 进行访问,并可能充当针对暴力破解攻击的安全措施,因为现在攻击者除了密码外,还必须猜测帐户名。

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

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

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

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

限制

某些自动化任务(例如远程全系统备份)需要完全 root 访问权限。为了以安全的方式允许这些操作,而不是禁用通过 SSH 进行的 root 登录,可以仅允许 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 文件上设置 immutable 位。为了防止用户重命名 ~/.ssh 目录并创建新的 ~/.ssh 目录和 authorized_keys 文件,也在 ~/.ssh 目录上设置 immutable 位。要添加或删除密钥,您必须从 authorized_keys 中删除 immutable 位,并使其临时可写。

提示: 建议通过例如 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/Certificate-based Authentication 中找到。

证书部署自动化

许多开源工具可以提供 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 标志禁用伪终端分配。

最好添加详细模式 (-v) 标志,因为这样您就可以从输出中验证它是否实际连接。

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

只有与 Web 浏览器或其他使用此新创建的 SOCKS 隧道的程序结合使用时,上述步骤才有用。由于 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 连接非常有用,尽管 vncserver 实用程序非常有用,但它明确表示缺乏安全性。

远程转发允许远程主机通过 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)的连接将通过隧道发送到本地机器,然后再发送到端口 6667 上的 irc.libera.chat,因此,在此示例中,即使端口 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 服务。有关如何防止隧道超时的信息,请参阅#保持连接。如果您希望在启动时启动隧道,您可能需要将 unit 重写为系统服务。

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 连接。只有当无法通过 DNS 查找 internal.example.com 时,片段 !exec "host 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 是一个 Layer 2 数据包嗅探实用程序,而 TCP/UDP 是 Layer 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,您就快完成了。您仍然需要配置您的客户端以使用其他端口而不是默认端口。解决此问题的方法有很多,但让我们在这里介绍其中两种。

Read from socket failed: connection reset by peer

当连接到较旧的 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]: No such file or directory" / ssh_exchange_identification 问题

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

"Terminal unknown" 或 "Error opening terminal" 错误消息

如果您在登录时收到上述错误,则表示服务器无法识别您的终端。像 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 的控制序列完全匹配,否则您可能会遇到奇怪的行为和图形故障。

Connection closed by x.x.x.x [preauth]

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

HostKey /etc/ssh/ssh_host_ed25519_key

subsystem request failed

OpenSSH 8.8 起,scp 使用 SFTP 作为数据传输的默认协议,方法是请求名为 sftp 的子系统。如果您在详细模式下运行 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 refused

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

No matching key exchange method found by 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: No such file or directory"

如果您没有将 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 变量。

参见