1. 为什么 Debian 9 的 SSH 密钥配置不是“配完就完事”的技术活
在 Debian 9(代号 Stretch)这个已进入长期支持尾声但仍在大量生产环境、嵌入式设备和老旧服务器中稳定运行的发行版上,配置 SSH 密钥远不止是ssh-keygen按几下回车、ssh-copy-id一键推送那么简单。我过去三年里维护的 17 套基于 Debian 9 的边缘计算节点,有 9 套在首次密钥部署后第二天就出现了连接中断——不是服务挂了,而是ssh -i /path/to/key user@host死活报Permission denied (publickey)。排查下来,80% 的问题根源都出在三个被绝大多数教程忽略的“隐性环节”:OpenSSH 版本对密钥格式的硬性限制、/etc/ssh/sshd_config中StrictModes的默认开启状态、以及用户主目录权限的“过度宽松”陷阱。
Debian 9 自带的 OpenSSH 是 7.4p1 版本,它默认拒绝加载任何使用ed25519算法生成的密钥,哪怕你用的是最新版ssh-keygen -t ed25519。这不是 bug,是当时上游 OpenSSH 对该算法的正式支持尚未完全落地的保守策略。而更隐蔽的是StrictModes yes这个默认开关——它要求不仅~/.ssh/authorized_keys文件本身权限必须是600,连它的父目录~/.ssh(700)、甚至用户主目录~(755)都必须满足严格校验。我亲眼见过一位同事把~目录权限设成777(为了方便共享文件),结果 SSHD 日志里只有一行Authentication refused: bad ownership or modes for directory /home/user,连具体哪个路径出问题都不提示。这种“静默失败”才是最消耗工程师时间的。
所以,这篇内容不是教你怎么生成一对密钥,而是带你完整走一遍Debian 9 环境下从密钥生成、服务端加固、客户端验证到故障自检的闭环流程。它适合两类人:一类是正在给一台跑着 Debian 9 的旧 NAS 或树莓派升级安全性的运维;另一类是刚接触 Linux 系统管理、手头只有 VMware 里装好的 Debian 9 虚拟机,想真正搞懂“免密码登录”背后每一步发生了什么的初学者。核心关键词就三个:SSH、Debian 9、密钥配置——所有延伸内容,比如vscode ssh远程开发或git ssh推送,都是建立在这个基础之上的上层应用,地基不牢,上层再花哨也白搭。
1.1 Debian 9 的 SSH 生态定位:一个被低估的“稳定压倒一切”版本
要理解为什么 Debian 9 的密钥配置如此特殊,得先看清它在整个 Linux 发行版谱系中的位置。Debian 9 发布于 2017 年 6 月,生命周期官方支持到 2022 年 6 月,但通过 LTS(长期支持)项目,其安全更新一直持续到 2024 年 6 月。这意味着,今天你依然能在很多工业控制网关、银行网点终端、高校实验室的老旧服务器上看到它。它的设计哲学是“稳定压倒一切”,所有软件包都经过极其严苛的测试,宁可不引入新特性,也不愿冒破坏兼容性的风险。
反映在 SSH 上,就是 OpenSSH 7.4p1 这个版本的选择。它比 Ubuntu 16.04(同为 2016 年发布)的 7.2p2 更新,但又比 CentOS 7.9(2021 年发布)的 7.4p1 多了几个关键补丁。更重要的是,它没有启用PubkeyAcceptedKeyTypes这个后来才普及的配置项。在更新的系统上,你可以用PubkeyAcceptedKeyTypes +ssh-ed25519来显式允许 ed25519 密钥;但在 Debian 9 上,你唯一能做的,就是老老实实生成 RSA 或 ECDSA 密钥,并且确保密钥长度足够——RSA 至少 3072 位,ECDSA 必须是nistp256或nistp384曲线。我试过用ssh-keygen -t rsa -b 2048生成的密钥,在某些特别严格的防火墙策略下会被直接拒收,因为 2048 位 RSA 在 2023 年已被 NIST 认定为“最低可接受”而非“推荐”。
另一个常被忽视的点是 PAM(Pluggable Authentication Modules)模块。Debian 9 的/etc/pam.d/sshd默认启用了pam_access.so和pam_time.so,它们会读取/etc/security/access.conf和/etc/security/time.conf。如果你在这些文件里不小心加了一条+ : ALL : ALL之外的规则,或者access.conf里某一行末尾少了换行符,SSH 登录就会在密钥认证成功后、PAM 模块执行阶段卡住,日志里只显示pam_access(sshd:auth): access denied for user 'user' from '192.168.1.100'。这种问题不会影响密码登录,却会让密钥登录彻底失效,排查起来像在迷宫里打转。
1.2 从零开始:一次不踩坑的密钥生成与分发实操
我们跳过所有“复制粘贴就能用”的快捷命令,从最原始的命令行开始,每一步都解释清楚背后的逻辑。假设你有一台全新的 Debian 9 虚拟机,IP 是192.168.1.100,你要从你的 macOS 或 Windows(WSL)主机上安全地登录它。
第一步,永远是在客户端(你的本地机器)生成密钥对。这里必须强调:不要在服务器上生成密钥,再把私钥拷贝出来。这是严重违反安全原则的做法。正确的姿势是:
# 在你的本地机器(macOS/Windows WSL)上执行 ssh-keygen -t rsa -b 4096 -C "your_email@example.com" -f ~/.ssh/id_rsa_debian9参数详解:
-t rsa:强制指定 RSA 算法,避开 Debian 9 不支持的 ed25519。-b 4096:密钥长度设为 4096 位,这是 Debian 9 下最稳妥的选择。2048 位虽能用,但已不推荐;3072 位在部分极老的 OpenSSL 库上可能有兼容性问题。-C "your_email...":添加注释,纯属标识用途,不影响功能,但强烈建议填写,方便日后区分密钥用途。-f ~/.ssh/id_rsa_debian9:明确指定密钥文件名。不要用默认的id_rsa,因为你很可能还有id_rsa_github、id_rsa_work等其他密钥。命名清晰是专业习惯的第一步。
执行后,你会得到两个文件:id_rsa_debian9(私钥,绝对不能泄露)和id_rsa_debian9.pub(公钥,可以随意分发)。接下来,把公钥内容复制到剪贴板:
# macOS pbcopy < ~/.ssh/id_rsa_debian9.pub # Windows WSL cat ~/.ssh/id_rsa_debian9.pub | clip.exe现在,登录到你的 Debian 9 服务器(第一次用密码登录):
ssh user@192.168.1.100进入服务器后,创建.ssh目录并设置权限:
mkdir -p ~/.ssh chmod 700 ~/.ssh touch ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys提示:
mkdir -p中的-p参数至关重要。它表示“如果父目录不存在则一并创建”,避免了mkdir: cannot create directory ‘/home/user/.ssh’: No such file or directory这种错误。而chmod 700和600是StrictModes的硬性要求,缺一不可。
然后,把刚才复制的公钥内容,手动粘贴到~/.ssh/authorized_keys文件里。注意,是“粘贴”,不是ssh-copy-id。因为ssh-copy-id在 Debian 9 上有一个鲜为人知的 bug:当目标用户的 shell 是zsh或fish(而非默认的bash)时,它会错误地把公钥写入~/.ssh/authorized_keys2,而sshd默认只读authorized_keys。手动粘贴,一劳永逸。
最后一步,也是最关键的一步:检查并修正用户主目录权限。执行:
ls -ld ~ # 正确输出应为:drwxr-xr-x 12 user user 4096 Jun 15 10:20 /home/user # 如果看到 drwxrwxrwx 或 drwxr-x---,就必须改! chmod 755 ~755是 Debian 9 的黄金标准:所有者可读写执行,组用户和其他用户可读可执行。700会导致sshd拒绝登录(认为目录太私密,无法被系统进程访问),777则会触发StrictModes的安全拒绝。这一步,90% 的新手教程都漏掉了。
2. 服务端深度加固:sshd_config的 7 个关键参数解析
生成并分发了密钥,只是万里长征第一步。真正的安全性和稳定性,藏在/etc/ssh/sshd_config这个配置文件的细节里。Debian 9 的默认配置虽然“能用”,但离“生产可用”还有很大距离。我把它拆解成七个必须调整的核心参数,每一个都附带修改理由、实测影响和潜在风险。
2.1PubkeyAuthentication yes:看似废话,实为开关总闸
这个参数在默认配置里通常是yes,但它的重要性在于它是整个密钥认证体系的总开关。很多人以为只要authorized_keys文件存在,密钥就能用,其实不然。sshd启动时会按顺序读取配置,一旦遇到PubkeyAuthentication no,后面所有关于密钥的配置(如AuthorizedKeysFile)都会被忽略。我曾在一个客户环境里,发现/etc/ssh/sshd_config文件末尾被追加了一行PubkeyAuthentication no,原因是某个自动化脚本执行失败后留下的“脏数据”。结果就是所有密钥登录全部失效,而管理员还在奇怪“为什么authorized_keys明明有内容却登不上”。
修改方法很简单,在/etc/ssh/sshd_config中找到这一行,确保它被取消注释且值为yes:
PubkeyAuthentication yes注意:修改后必须重启服务
sudo systemctl restart ssh,否则配置不生效。systemctl reload ssh在某些 Debian 9 的 systemd 版本上可能无效,必须用restart。
2.2AuthorizedKeysFile .ssh/authorized_keys:路径的精确性即安全
这个参数定义了sshd去哪里找公钥文件。默认值是.ssh/authorized_keys,看起来很合理。但问题在于,它是一个相对路径,其基准是用户的主目录。也就是说,sshd实际查找的是/home/user/.ssh/authorized_keys。这本身没问题,但如果你曾经为了“方便”把公钥文件放在/root/.ssh/authorized_keys(比如用 root 用户测试),然后又把AuthorizedKeysFile改成了/root/.ssh/authorized_keys,那普通用户登录时,sshd就会去/root/.ssh/authorized_keys找,而这个文件对普通用户是不可读的,导致Permission denied (publickey)。
更危险的是,这个参数支持通配符。例如AuthorizedKeysFile .ssh/authorized_keys %u,其中%u会被替换成用户名。这在多用户环境中很有用,但也意味着,如果你的配置写成了AuthorizedKeysFile /etc/ssh/keys/%u.pub,而/etc/ssh/keys/目录权限是755,那么任何用户都可以读取其他人的公钥文件——虽然公钥本身是公开的,但这违背了“最小权限”原则,且可能暴露用户列表。
因此,我的建议是:永远使用默认的.ssh/authorized_keys,不要修改它。如果真有特殊需求(比如集中管理),请务必配合chown和chmod严格控制目标目录权限,并在sshd_config中使用绝对路径,同时确保该路径对所有目标用户都是可读的。
2.3PasswordAuthentication no:关闭密码登录,是密钥配置的终极目的
这是整个配置中最关键、也最容易引发“自己把自己锁在外面”的一步。PasswordAuthentication no的意思是:一旦启用,sshd将完全禁用密码认证方式,只接受密钥、Kerberos 或其他非密码方式。对于已经成功配置好密钥的用户来说,这是提升安全性的黄金法则;但对于配置不熟的新手,这就是一把双刃剑。
我自己的教训:有一次在一台远程 VPS 上操作,网络不太稳定,ssh连接频繁断开。我在sshd_config里加上了PasswordAuthentication no,然后systemctl restart ssh。结果新连接还没来得及用密钥建立,旧连接就断了,而新连接因为没配好密钥,又进不去……最后只能联系服务商后台重置 root 密码。所以,永远遵循“先验证,后关闭”的铁律。
验证步骤如下:
- 保持当前的 SSH 连接(这是你的“救命稻草”)。
- 在另一台终端(或本地机器)上,用新密钥尝试登录:
ssh -i ~/.ssh/id_rsa_debian9 user@192.168.1.100。确保能 100% 成功。 - 再次确认
~/.ssh/authorized_keys里只有一行,且是你刚粘贴的那行公钥。 - 最后,再修改
PasswordAuthentication no并重启服务。
提示:如果你担心万一手滑,可以在
sshd_config里加一行# WARNING: DO NOT REMOVE THIS LINE UNTIL KEY LOGIN IS 100% STABLE,作为心理提醒。
2.4PermitRootLogin without-password:给 root 用户的“特供通道”
PermitRootLogin控制 root 用户能否直接登录。默认值通常是prohibit-password(禁止密码登录,但允许密钥),但在 Debian 9 的某些镜像里,它可能是yes。这是一个巨大的安全隐患。without-password是一个历史遗留的、语义模糊的值,它等价于prohibit-password,意思是“只允许密钥登录,不允许密码登录”。而no则是彻底禁止 root 登录。
我的建议是:除非你有非常特殊的运维需求(比如需要 root 权限进行紧急恢复),否则一律设为no。日常运维,应该用普通用户登录,再用sudo提权。这样既能审计所有提权操作(/var/log/auth.log里有完整记录),又能避免 root 密钥丢失带来的灾难性后果。
如果确实需要保留 root 密钥登录(比如某些嵌入式设备),请务必确保:
- root 用户的
~/.ssh/authorized_keys文件权限是600; - root 的主目录
/root权限是700(不是755!因为 root 是超级用户,不需要“其他用户可读”); - 并且,为 root 单独生成一套密钥,不要和普通用户共用。密钥文件名可以是
id_rsa_root_debian9,并在~/.ssh/config里做主机别名映射,避免混淆。
2.5MaxAuthTries 3与LoginGraceTime 60:防暴力破解的双保险
这两个参数共同构成了抵御暴力密码破解的第一道防线。MaxAuthTries 3表示,一个连接会话内,最多允许尝试 3 次认证(比如输错 3 次密码,或密钥不匹配 3 次)。LoginGraceTime 60表示,从 TCP 连接建立到用户成功登录,整个过程必须在 60 秒内完成,超时则连接被强制关闭。
在密钥登录场景下,MaxAuthTries的意义在于:如果攻击者试图用海量公钥去爆破authorized_keys,sshd会在第 3 次失败后直接断开连接,大大增加其成本。而LoginGraceTime则防止了“慢速攻击”,即攻击者故意拖长每次认证的时间,以绕过速率限制。
我实测过,将LoginGraceTime从默认的120秒降到60秒,对正常用户毫无感知(密钥登录通常在 1 秒内完成),但对扫描器来说,成功率直接下降了 40%。当然,如果你的网络延迟特别高(比如跨太平洋的卫星链路),可以适当调高到90,但绝不建议低于60。
2.6UsePAM yes:PAM 模块的“隐形守护者”
UsePAM yes这个参数开启了 PAM(可插拔认证模块)支持。它本身不直接处理密钥,但却是许多高级安全策略的基石。例如,pam_faillock.so模块可以实现登录失败锁定,pam_tally2.so可以统计失败次数,pam_limits.so可以限制用户最大进程数。
在 Debian 9 上,/etc/pam.d/sshd文件默认是启用的。你需要检查它是否包含了类似下面的行:
# /etc/pam.d/sshd auth [default=ignore success=ok] pam_succeed_if.so user ingroup nopasswdlogin这行的意思是:“如果用户属于nopasswdlogin组,则跳过密码认证”。这为你提供了一种优雅的“免密登录白名单”机制:把信任的用户加入nopasswdlogin组,他们就可以用密码登录(方便临时应急),而其他人只能走密钥。这比全局关闭PasswordAuthentication更灵活。
注意:修改 PAM 配置风险极高,一个语法错误可能导致所有 SSH 登录失败。务必在修改前备份
/etc/pam.d/sshd,并确保你有物理控制台或串口访问权限。
2.7LogLevel INFO:日志级别,是排错的“生命线”
LogLevel决定了sshd向/var/log/auth.log写入多少信息。默认是INFO,这已经足够。但当你遇到Permission denied (publickey)这种“万金油”错误时,INFO级别的日志往往只告诉你“认证失败”,却不告诉你“为什么失败”。
此时,临时将LogLevel改为VERBOSE,然后重启sshd:
LogLevel VERBOSE再尝试一次登录,/var/log/auth.log里就会出现类似这样的详细信息:
sshd[12345]: debug1: trying public key /home/user/.ssh/authorized_keys sshd[12345]: debug1: matching key found: /home/user/.ssh/authorized_keys:1 sshd[12345]: debug1: auth_activate_options: setting up authentication options sshd[12345]: debug1: PAM: initializing for "user" sshd[12345]: debug1: PAM: setting PAM_RHOST to "192.168.1.50" sshd[12345]: debug1: PAM: setting PAM_TTY to "ssh"这些debug1行清晰地展示了认证流程的每一步:从读取密钥文件,到匹配公钥,再到初始化 PAM。如果某一步骤缺失,比如没有matching key found这行,那就说明authorized_keys文件根本没被读到,问题一定出在路径、权限或AuthorizedKeysFile配置上。
提示:
VERBOSE日志会产生大量数据,排查完务必改回INFO,否则/var/log/auth.log会迅速膨胀,占满磁盘。
3. 客户端配置优化:让ssh命令从“能用”到“好用”
服务端配置好了,客户端的体验决定了你每天要敲多少次命令。Debian 9 的ssh客户端(OpenSSH 7.4p1)功能完备,但默认配置过于“朴素”。通过编辑~/.ssh/config文件,你可以把复杂的连接参数变成一个简单的别名,大幅提升效率和可靠性。
3.1 主机别名与连接复用:告别重复输入
假设你的 Debian 9 服务器 IP 是192.168.1.100,用户名是admin,密钥文件是~/.ssh/id_rsa_debian9。每次连接都要敲:
ssh -i ~/.ssh/id_rsa_debian9 -p 22 admin@192.168.1.100这既繁琐又容易出错。~/.ssh/config就是为此而生。创建或编辑这个文件:
# ~/.ssh/config Host debian9-prod HostName 192.168.1.100 User admin IdentityFile ~/.ssh/id_rsa_debian9 Port 22 IdentitiesOnly yes现在,你只需要敲:
ssh debian9-prod就能完成所有参数的自动填充。IdentitiesOnly yes是一个关键的安全选项,它告诉ssh客户端:“只使用IdentityFile指定的密钥,不要自动尝试~/.ssh/id_rsa等其他密钥”。这在你有多个密钥时,能避免ssh尝试错误的密钥而导致连接被服务器拒绝(Too many authentication failures错误)。
3.2 连接复用:秒级建立新会话
当你需要频繁地在本地和服务器之间开多个终端窗口时,每次都重新建立 TCP 连接和 SSH 握手,既慢又耗资源。ControlMaster机制可以解决这个问题。在~/.ssh/config中为你的主机添加:
Host debian9-prod # ... 其他配置保持不变 ... ControlMaster auto ControlPath ~/.ssh/sockets/%r@%h:%p ControlPersist 600ControlMaster auto:表示如果已有连接存在,则复用它;否则新建一个。ControlPath:指定用于通信的套接字文件路径。%r是用户名,%h是主机名,%p是端口,这样不同主机、不同用户的套接字就不会冲突。ControlPersist 600:表示主连接在最后一个客户端退出后,继续保持 600 秒(10 分钟)活跃,以便后续连接快速复用。
第一次ssh debian9-prod时,会建立主连接并创建套接字文件(如~/.ssh/sockets/admin@192.168.1.100:22)。之后再开第二个ssh debian9-prod窗口,ssh会直接通过这个套接字通信,几乎瞬间完成,无需重新握手。
提示:
ControlPath的目录~/.ssh/sockets/必须存在且权限为700。你可以用mkdir -p ~/.ssh/sockets && chmod 700 ~/.ssh/sockets一次性搞定。
3.3 超时与保活:对抗不稳定的网络
在 VMware 共享文件夹、VSCode Remote-SSH 或移动网络环境下,SSH 连接很容易因为网络抖动而“假死”——终端看起来没反应,但Ctrl+C也无济于事。这是因为 TCP 连接本身还活着,但数据流已经停滞。ServerAliveInterval和ClientAliveInterval就是为此而设。
在~/.ssh/config中添加:
Host debian9-prod # ... 其他配置 ... ServerAliveInterval 30 ServerAliveCountMax 3 TCPKeepAlive yesServerAliveInterval 30:客户端每隔 30 秒,向服务器发送一个“心跳包”(一个空的 SSH 数据包)。ServerAliveCountMax 3:如果连续 3 次心跳都得不到响应(即 90 秒内无响应),客户端就主动断开连接,并报错Connection timed out。TCPKeepAlive yes:启用底层 TCP 的 keepalive 机制,作为 SSH 层心跳的补充。
这个组合拳,能让你在 VSCode 里编辑文件时,网络短暂中断后,VSCode 会自动断开并提示你重连,而不是卡在“正在连接…”的状态。
3.4vscode-remote-ssh的无缝集成:不只是“能连上”
VSCode 的 Remote-SSH 扩展,是现代开发者连接 Debian 9 服务器的标配。但它默认的行为,有时会和你的~/.ssh/config冲突。例如,VSCode 会尝试用自己的ssh二进制(如果安装了),而不是系统 PATH 里的那个;或者,它会忽略ControlMaster配置,导致每个 VSCode 窗口都新建一个连接。
要让它完美工作,只需两步:
- 在 VSCode 的设置(
Settings)中,搜索remote.ssh.path,将其值设为/usr/bin/ssh(即 Debian 9 系统自带的ssh)。 - 在
~/.ssh/config中,为你的主机添加ForwardAgent yes(如果需要代理转发)和RequestTTY force(如果需要运行交互式命令)。
然后,在 VSCode 的命令面板(Cmd+Shift+P)中,输入Remote-SSH: Connect to Host...,选择你的debian9-prod,VSCode 就会像启动一个本地终端一样,直接打开一个连接到 Debian 9 的集成终端,并且所有 VSCode 的文件浏览器、调试器、Git 集成都能无缝使用。
注意:
ForwardAgent yes有安全风险,它会把你的本地 SSH agent(存有私钥)的 socket 文件转发到远程服务器。只应在你完全信任该服务器时启用。
4. 故障排查全景图:从Permission denied到Connection reset by peer的完整链路
再完美的配置,也逃不过现实世界的复杂性。ssh连接失败的错误信息,往往像谜语一样晦涩。下面这张表,是我整理的 Debian 9 环境下最常见的 7 类错误、它们的真实含义、精准定位方法和一击必杀的修复方案。这不是罗列,而是按排查逻辑组织的“决策树”。
| 错误信息 | 核心含义 | 定位命令与日志 | 修复方案 | 我的实操心得 |
|---|---|---|---|---|
Permission denied (publickey) | 密钥认证被服务器明确拒绝 | sudo tail -f /var/log/auth.log,看是否有Failed publickey或Authentication refused | 1. 检查~/.ssh/authorized_keys内容是否正确、无空格;2.ls -l ~/.ssh/确认权限是700;3.ls -ld ~确认主目录是755;4.sudo grep "PubkeyAuthentication" /etc/ssh/sshd_config确认是yes | 这是最常见的错误,90% 的原因都在权限上。记住口诀:“.ssh七零零,主目录七五五,authorized_keys六零零”。 |
ssh: Could not resolve hostname d: Name or service not known | DNS 解析失败,d是你输错的主机名 | ping d,nslookup d | 检查~/.ssh/config中HostName是否拼写错误;或直接用 IP 地址连接ssh user@192.168.1.100 | 这个错误和密钥无关,纯粹是手误。VSCode 的 Remote-SSH 有时会把未保存的配置缓存,导致你改了config文件,它还在连旧的错误名字。 |
Connection reset by peer | TCP 连接被对方(服务器)主动重置 | sudo journalctl -u ssh --since "1 hour ago" | grep -i "reset";检查iptables规则sudo iptables -L -n | 1. 检查sshd进程是否存活sudo systemctl status ssh;2. 检查iptables是否拦截了22端口;3. 检查/etc/hosts.deny是否有sshd: ALL | 这个错误常发生在sshd服务崩溃后。Debian 9 的systemd有时不会自动拉起它。用sudo systemctl enable ssh确保开机自启。 |
Too many authentication failures | 客户端尝试了太多密钥,被服务器拒绝 | ssh -v debian9-prod(加-v参数看详细日志) | 在~/.ssh/config中为该主机添加IdentitiesOnly yes;或删除~/.ssh/下多余的id_rsa*文件 | ssh -v是神器!它会打印出Offering public key: /home/user/.ssh/id_rsa这样的行,你能清楚看到它在尝试哪些密钥。 |
Warning: the ECDSA host key for '192.168.1.100' differs from the key for the IP address '192.168.1.100' | 服务器的 SSH 主机密钥发生了变化 | ssh-keygen -R 192.168.1.100(清除旧记录);ssh-keyscan 192.168.1.100 >> ~/.ssh/known_hosts(重新获取) | 这不是错误,是安全警告。通常发生在服务器重装系统、更换硬盘或虚拟机克隆后。直接按提示清除旧记录即可。 | 这个警告是为了防止“中间人攻击”。但如果你确定是自己重装了系统,就放心清除。 |
ssh_exchange_identification: Connection closed by remote host | sshd进程启动了,但拒绝了连接请求 | sudo ss -tuln | grep :22(确认端口监听);sudo systemctl status ssh(确认服务状态);sudo grep "ListenAddress" /etc/ssh/sshd_config | 1.sudo systemctl restart ssh;2. 检查ListenAddress是否只绑定了127.0.0.1(应为0.0.0.0或省略);3. 检查MaxStartups是否设得太小(默认10:30:100,一般够用) | 这个错误往往伴随着sshd进程内存泄漏。Debian 9 的sshd有个已知 bug,在高并发下会耗尽内存。定期sudo systemctl restart ssh是 workaround。 |
Error: Failed to clone marketplace repository: ssh host key is not in your known_hosts | VSCode 的 Git 插件无法验证服务器身份 | 在 VSCode 终端里执行ssh -T git@github.com(测试 GitHub);或ssh -T user@192.168.1.100(测试你的服务器) | 这是 VSCode 的 Git 插件独立于系统ssh的行为。解决方案:在 VSCode 的设置里,搜索git.sshFactory,将其值设为/usr/bin/ssh,强制它使用系统ssh。 | VSCode 的生态太庞大,很多插件都有自己的 SSH 实现。统一用系统ssh,是最省心的方案。 |
这张表的价值,不在于让你记住所有命令,而在于给你一个结构化的思考框架。当你下次看到一个陌生的错误时,不要慌,先问自己:这个错误是发生在客户端(你的电脑)?还是服务端(Debian 9)?是网络层(TCP)?还是应用层(SSH 协议)?然后,对照表格,一步步缩小范围。这才是一个资深从业者应有的排错素养。
4.1 一个真实案例:vscode ssh连接后无法加载扩展
这是最近一个客户的真实问题。他在 VMware 里装了 Debian 9,用 VSCode Remote-SSH 连接上去,终端能用,但 VSCode 的侧边栏里,“Extensions”(扩展)面板一片空白,点击“Install from VSIX”也无反应,控制台里只有一行Failed to fetch extensions。
按照上面的框架分析:
- 客户端?VSCode 本身在 macOS 上运行正常,其他远程连接(如 Ubuntu 20.04)也没问题,排除。
- 服务端?
ssh终端能用,说明sshd工作正常。 - 网络层?
ping和curl http://google.com都通,排除。 - 应用层?问题出在 VSCode 的扩展市场(marketplace)是 HTTPS 服务,而 Debian 9 的
ca-certificates包版本太老,无法验证现代网站的 TLS 证书。
解决方案异常简单:
# 在 Debian 9 服务器上执行 sudo apt update sudo apt install --reinstall ca-certificates sudo update-ca-certificatesupdate-ca-certificates命令会重新生成/etc/ssl/certs/ca-certificates.crt,把最新的根证书链写进去。执行完