OpenSSH

出自 ArchWiki

OpenSSH (OpenBSD Secure Shell) 是一组计算机程序,它使用 安全外壳 (SSH) 协议通过计算机网络提供加密的通信会话。它是作为 SSH Communications Security 提供的专有安全外壳软件套件的开源替代品而创建的。 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 移除了 sshd.socket,该套接字使用 systemd 的套接字激活,因为它容易受到拒绝服务攻击。有关详细信息,请参阅 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 身份验证器 以设置 Google 身份验证器。

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

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

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

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

现在您将无法通过 SSH 以 root 身份登录,但仍然可以使用您的普通用户登录并使用 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 文件上的不可变位。为了防止用户重命名 ~/.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 记录

安全外壳指纹记录 (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 标志禁用伪终端分配。

最好添加 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 连接非常有用,尽管 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 标志选择密码。为了获得永久效果,请将 Ciphers 选项放入您的 ~/.ssh/config 中,并按新的首选顺序排列密码,例如
    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 unit 文件

/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) 中找到。当然,如果需要,您可以使此 unit 更复杂(有关详细信息,请参阅 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 是第 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 条目之一中。

“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

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

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

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

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

HostKey /etc/ssh/ssh_host_ed25519_key

子系统请求失败

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 被拒绝

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 的完整路径名来简单地解决此问题,该 shell 也将在跳转服务器上有效,或者通过在您的 ~/.ssh/config 文件中为每个服务器设置特定的 SHELL 变量。

另请参阅