SSH
ssh -p 22 ops@server.example.com 请求在目标服务器上以 ops 身份建立会话。命令中的对象可以直接拆开:
ssh -p 22 ops@server.example.com
│ │ │ └─ 目标主机名:直接连接时由客户端解析为地址
│ │ └───── 目标 Linux 账号:决定登录后的 UID、组和文件权限
│ └───────── SSH 服务的 TCP 端口,默认 22
└──────────────── OpenSSH 客户端程序
客户端 ssh → TCP 连接 → 服务端 sshd → 认证为 ops → shell 或指定命令SSH 是远程安全通信协议,OpenSSH 是常用实现。登录终端、执行命令、传文件和 TCP 转发都可以使用它。SSH 先验证服务器身份,再认证用户,最后才根据授权打开所需通道。连接参数和远程命令形式见 OpenSSH 客户端手册。
连接建立前,两端分别保存什么
主机密钥、用户密钥与会话密钥
两台机器之间有两次方向相反的身份核验。客户端需要确认服务器,服务器也需要确认登录者。
客户端 服务端
~/.ssh/known_hosts ← 记录信任的主机公钥 ← /etc/ssh/ssh_host_*_key.pub
/etc/ssh/ssh_host_*_key
服务器持有主机私钥并签名
~/.ssh/id_ed25519_ops → 用用户私钥签名 → /home/ops/.ssh/authorized_keys
~/.ssh/id_ed25519_ops.pub 允许登录 ops 的用户公钥
双方通过密钥交换产生会话密钥,用于加密连接中的数据主机私钥留在服务器上。握手时服务器用它证明自己的身份,客户端用已经信任的公钥或主机证书验证。known_hosts 记录的是“这个目标名称对应哪些可信主机密钥”;服务器重装、密钥轮换或地址被复用,都可能改变核验结果。
用户私钥由登录者保管。公钥认证时,客户端对包含本次会话标识的认证数据签名,服务器核对签名与账号授权。私钥不需要传给服务器,authorized_keys 也不是密码库。两端文件格式和密钥选项见 sshd 手册。
真正传输终端和文件数据时,使用密钥交换协商出的对称密钥。长期主机密钥负责身份认证,密钥交换负责建立共享秘密;把用户公钥复制过去,并不会使这把用户密钥直接承担整条连接的数据加密。SSH 传输层规范规定了密钥交换、服务器认证和加密保护。
从握手到多个通道
| 层次 | 主要动作 | 常见失败位置 |
|---|---|---|
| 传输层 | 协商算法、交换密钥、验证主机身份,建立加密与完整性保护 | 算法不兼容、主机密钥不受信任 |
| 用户认证层 | 对目标账号尝试公钥、密码或交互式认证 | 账号、密钥、PAM 或认证组合不满足要求 |
| 连接层 | 在连接中打开 session、SFTP 或转发通道 | shell、子系统、转发策略或目标服务失败 |
用户认证发生在受保护的传输之上。密码方式会把密码通过加密连接送到服务器验证;公钥方式验证签名;keyboard-interactive 则允许服务器发出一次或多次交互挑战,可接入 PAM 和多因素认证。服务器可以要求两种方法连续成功,不能把所有输入提示都当成“SSH 密码”。认证消息见 RFC 4252。
一条已认证的连接可以承载多个通道。每个通道有自己的编号、窗口和关闭状态,终端与文件数据在连接上复用;它们仍共同依赖底层 TCP。网络中断会影响这些通道,远程程序是否已经执行、文件是否写完,需要另外确认。RFC 4254给出了通道打开、流量控制、EOF 和关闭的语义。
安装与第一次可信登录
客户端和服务端分开准备
下面的 Linux 操作采用 Ubuntu 24.04、OpenSSH 9.6p1 发行线和 Bash。发行版安全更新会改变包修订号,以本机查询为准;上游在线手册还可能包含更新实现的选项,使用前可对照本机 man ssh、man sshd_config。
在客户端安装命令行工具,以有 sudo 权限的普通用户执行:
sudo apt update
sudo apt install openssh-client rsync
ssh -V
command -v ssh scp sftp ssh-keygen ssh-copy-id rsyncssh -V 输出客户端版本,后续路径输出用于确认实际调用的程序。它没有检查远程服务器版本。Ubuntu 的安装、认证与配置说明见 OpenSSH Server 文档。
服务器尚未开通 SSH 时,通过虚拟机控制台、物理控制台或已授权的云管理入口操作。先确认管理账号有可用的登录方式,再安装服务;初始化账号、公钥交付和安全组来源限制见云服务器与安全组。不要为了测试把管理端口向所有来源开放。
在服务器控制台执行:
sudo apt update
sudo apt install openssh-server
dpkg-query -W openssh-server
sudo /usr/sbin/sshd -t
systemctl status ssh.socket ssh.service --no-pager
sudo ss -lntp '( sport = :22 )'sshd -t 成功时不输出内容,非零退出时先按报错修复配置或主机密钥。ss 应显示预期地址上的监听。如果安装后没有启动,先看日志;确认采用 socket activation 的机器可用 sudo systemctl start ssh.socket,采用常驻服务的机器则启动 ssh.service,不必同时切换两种模式。
Ubuntu 24.04 支持由 ssh.socket 先监听,连接到来后再启动 ssh.service,所以尚未出现 sshd 进程未必是故障。监听也可能显示由 systemd 持有。修改 Port 或 ListenAddress 会牵涉 socket 生成配置,不能只重载 sshd 后就认定端口已改变;查看实际单元和监听再处理。版本差异见 Ubuntu 的 socket activation 说明。
RHEL 系的包名通常是 openssh-clients、openssh-server,服务名是 sshd.service。它还受 SELinux 和系统加密策略约束,不能把 Ubuntu 的服务命令与配置片段直接混用。Windows 原生 OpenSSH 的安装入口见 Microsoft 文档;PowerShell 可先运行 Get-Command ssh,Bash 数组、chmod 和这里的脚本应在 Linux/WSL 环境执行,Windows 私钥访问权限使用 NTFS ACL 管理。
企业内网优先通过批准的软件仓库安装。离线部署需要与目标发行版、架构匹配的包及依赖闭包;单独运行 apt download openssh-server 只下载指定包,无法保证离线安装成功。准备完离线仓库或介质后,仍在目标环境执行版本、配置和监听检查。
核对服务器,再认证用户
先从可信控制台读取服务器主机公钥指纹:
sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub -E sha256输出中的 SHA256:… 是该公钥的指纹。若服务器按策略只提供其他类型,应读取对应主机公钥,核对客户端实际提示的同一种算法。
客户端 Bash 中设置交付方给出的三个值;示例域名需要替换为已授权服务器:
SSH_HOST='server.example.com'
SSH_PORT=22
SSH_USER='ops'
ssh -p "$SSH_PORT" "$SSH_USER@$SSH_HOST"首次连接会显示目标和指纹并询问是否信任。把它与控制台指纹逐字符核对,一致后再接受;随后使用已经交付的用户私钥或初始化密码认证。指纹不一致时停止登录,先核对目标地址、算法和资产交付信息。只有一条未经核实的网络扫描结果时,仍缺少可信的首次身份依据。
交付的用户私钥不在客户端默认密钥位置时,在 ssh 命令中增加 -i 私钥文件路径。该文件应已通过批准的方式保存在本机并限制读取权限;不要为了初始化把已有管理私钥重新生成或上传到服务器。
登录后运行:
id -un
id
printf '%s\n' "$SSH_CONNECTION"id -un 应为交付的账号,例如 ops;id 展示实际 UID 和组。SSH_CONNECTION 的四个字段依次为客户端地址、客户端端口、服务端地址、服务端端口;经过 NAT 或跳板时,服务器看到的来源可能是中间节点。确认用户与入口一致后用 exit 退出。
known_hosts 的名称与端口
客户端已经接受的主机公钥保存在 ~/.ssh/known_hosts。它可以按主机名或地址记录,也可以对名称做哈希;直接搜索 IP 未必找得到,使用 ssh-keygen -F:
ssh-keygen -F "$SSH_HOST"
# 非默认端口的查找形式,例如 2222
ssh-keygen -F "[$SSH_HOST]:2222"ssh-keyscan 适合收集公钥,但没有验证对方身份的能力。自动化交付可以先扫描到临时文件,用 ssh-keygen -lf 文件 计算指纹,再与可信资产记录比对,匹配后才安装到专用 known_hosts。扫描工具的安全限制见 ssh-keyscan 手册。
对预先配置好主机信任的自动化,使用 StrictHostKeyChecking yes。accept-new 会自动接受此前未知的主机,仍拒绝已知密钥变化;它省略了首次人工确认,因此需要明确接受首次连接的信任风险。计划内换机的记录更新与异常变化处理见主机身份与认证故障。
用户密钥与客户端配置
生成公钥并授权账号
个人运维密钥在客户端生成,为不同用途使用独立文件。下面的目标文件必须尚不存在;已有同名密钥时换一个名称,不能覆盖。
install -d -m 700 "$HOME/.ssh"
OPS_KEY="$HOME/.ssh/id_ed25519_ops"
if test -e "$OPS_KEY" || test -e "$OPS_KEY.pub"; then
printf '密钥路径已存在,请另选名称\n' >&2
else
ssh-keygen -t ed25519 -a 64 -C 'ops-personal' -f "$OPS_KEY"
fi交互提示中的 passphrase 是保护本地私钥文件的口令,建议为个人密钥设置。它与服务器账号密码不同。-a 64 指定保护私钥时的 KDF 轮数,增加离线猜口令成本,也增加解锁开销;它不会改变网络会话加密算法。Ed25519 适合支持它的普通环境;受 FIPS 或组织加密政策约束时,按发行版允许的算法选择。RSA 密钥也可使用 SHA-2 签名,“RSA 密钥”和旧 ssh-rsa 的 SHA-1 签名算法需要分开判断。ssh-keygen 手册说明了密钥格式、口令与各算法选项。
已经能以目标账号登录时,在客户端安装公钥:
ssh-copy-id -i "$OPS_KEY.pub" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST"ssh-copy-id 依靠已有的密码或其他可用认证进入服务器,然后添加公钥。它无法替代首次账号交付。密码和密钥都不可用时,由管理员通过可信控制台把这份公钥安装到目标账号的 ~/.ssh/authorized_keys,保留已有授权,检查属主;不要传送私钥。
在目标账号自己的会话中检查:
id -un
ls -ld "$HOME" "$HOME/.ssh"
ls -l "$HOME/.ssh/authorized_keys"
chmod 700 "$HOME/.ssh"
chmod 600 "$HOME/.ssh/authorized_keys"这些命令只能处理当前账号的目录。管理员核查其他账号时使用其真实家目录,不能在 root 会话里把 $HOME 当成 ops 的家目录。家目录、.ssh 和授权文件若可被不受信任的用户写入,StrictModes 检查可能拒绝公钥;权限数值正确但属主错误同样需要修复。
从客户端验证公钥登录,并禁止连接复用:
ssh -S none -o IdentitiesOnly=yes -o PreferredAuthentications=publickey \
-i "$OPS_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" 'id -un'输出应为目标账号。此时仍可能提示本地私钥口令,但不会回退到服务器密码。IdentitiesOnly 限制客户端使用配置指定的身份,避免 agent 中大量无关密钥先被尝试。
agent 的使用与撤销
桌面会话可能已经启动 ssh-agent。先执行 ssh-add -l:列出指纹表示 agent 可访问;The agent has no identities 表示有 agent 但尚未加载密钥;Could not open a connection to your authentication agent 则表示当前 shell 没有可用连接。
已有 agent 时,加入这把密钥并限制驻留时间:
ssh-add -t 1h "$OPS_KEY"
ssh-add -l没有 agent 时,在一个专用子 shell 中创建临时 agent,避免覆盖其他终端的环境:
(
eval "$(ssh-agent -s)"
trap 'ssh-agent -k >/dev/null' EXIT
ssh-add -t 1h "$OPS_KEY"
ssh -S none -o BatchMode=yes -o IdentitiesOnly=yes \
-i "$OPS_KEY" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" 'id -un'
)子 shell 退出时关闭刚创建的 agent。agent 接收签名请求,使客户端不必每次重新输入口令;能访问该 agent socket 的进程也可能借用已加载的身份。驻留时限、确认选项和移除行为见 ssh-add 与 ssh-agent 手册。
从已有 agent 移除单把密钥用 ssh-add -d "$OPS_KEY",这不会删除磁盘上的私钥,也不会撤销服务器授权。撤销登录能力需要从各目标账号移除相应公钥,或撤销对应证书;已经认证的会话还需要另外处置。
Host 别名、取值顺序与展开检查
在客户端编辑 ~/.ssh/config,将下面专用条目放在通用 Host * 之前。替换域名、账号、端口和密钥路径,不要覆盖原有配置:
Host lab-server
HostName server.example.com
User ops
Port 22
IdentityFile ~/.ssh/id_ed25519_ops
IdentitiesOnly yes
StrictHostKeyChecking yes
ForwardAgent no
ServerAliveInterval 30
ServerAliveCountMax 3lab-server 是本地配置别名,HostName 才是连接目标。StrictHostKeyChecking yes 依赖前面已完成的主机信任。保活参数用于发现连接失去响应,不能修复链路,也不能判定远程任务是否成功。
chmod 600 "$HOME/.ssh/config"
ssh -G lab-server | grep -E '^(hostname|user|port|identityfile|identitiesonly|proxyjump|controlpath) '
ssh lab-server 'id -un'ssh -G 只展开配置,不发起网络连接。大多数参数使用首先取得的值:命令行优先,然后用户配置,再到系统配置。因此具体 Host 通常在前,通用默认值在后;不是“越靠后越覆盖”。IdentityFile 等少数指令可累加,不能把首值规则扩展到所有字段。匹配规则见 ssh_config 手册。
有多份客户端配置时,可用 ssh -F 文件名 lab-server 显式选择;指定替代用户配置后,系统级客户端配置也会被忽略。故障复现前先检查 ssh -G,否则命令中的别名可能仍继承跳板、复用连接或另一把密钥。
文件传输与远程执行
scp 和 SFTP:文件落到哪个账号的目录
从 OpenSSH 9.0 起,scp 默认通过 SFTP 传输文件。它与交互式 sftp 使用相同的 SSH 认证,但界面不同:scp 适合直接复制,sftp 适合查看远端目录和批处理。SFTP 也不同于 FTP/FTPS,不需要照搬 FTP 的数据端口配置。OpenSSH 发行说明与 scp 手册说明了默认协议变化。
下面用前面已配置的 lab-server 别名。两端均以普通用户操作,在各自临时目录练习,不覆盖应用目录。客户端 Bash 执行:
TRANSFER_DIR=$(mktemp -d -t ssh-transfer.XXXXXXXX)
printf 'ssh-file-ok\n' > "$TRANSFER_DIR/message.txt"
REMOTE_DIR=$(ssh lab-server 'mktemp -d -t ssh-receive.XXXXXXXX')
printf '本地:%s\n远程:%s\n' "$TRANSFER_DIR" "$REMOTE_DIR"
scp "$TRANSFER_DIR/message.txt" "lab-server:$REMOTE_DIR/message.txt"
sha256sum "$TRANSFER_DIR/message.txt"
ssh lab-server "sha256sum '$REMOTE_DIR/message.txt'"远程目录名由可信目标的 mktemp 产生,且这里的模板不含空格或 shell 特殊字符。两次 SHA-256 应相同;它们比较的是这两个文件的内容。正式制品还要与构建系统给出的可信摘要或签名比较,不能只依赖传输进度到达 100%。任一复制或摘要命令非零退出时,先停止后续使用,检查目标目录权限、空间和传输错误。
下载后再比较原文件:
scp "lab-server:$REMOTE_DIR/message.txt" "$TRANSFER_DIR/download.txt"
cmp "$TRANSFER_DIR/message.txt" "$TRANSFER_DIR/download.txt"cmp 返回 0 且无输出表示内容相同。未使用别名时,scp 的端口参数是大写 -P,例如 scp -P 2222 文件 用户@主机:路径;小写 -p 用来保留时间和模式,与 ssh 的 -p 不是同一职责。
交互式管理可以运行 sftp lab-server,提示符变为 sftp>。此时 pwd、ls、cd 对远端操作,lpwd、lls、lcd 对本地操作;put 上传,get 下载,exit 退出。需要脚本处理时,用批处理并检查退出码:sftp 手册列出了哪些命令失败会中止批处理。
sftp -b - lab-server <<EOF
put $TRANSFER_DIR/message.txt $REMOTE_DIR/from-sftp.txt
get $REMOTE_DIR/from-sftp.txt $TRANSFER_DIR/from-sftp.txt
EOF
cmp "$TRANSFER_DIR/message.txt" "$TRANSFER_DIR/from-sftp.txt"批处理应事先具备非交互认证,例如已解锁的 agent。不要给关键命令加忽略错误的 - 前缀。上面的路径取自无空格的临时目录;业务路径包含空格时,须按 SFTP 的路径引用规则处理,不能直接拼接不可信输入。
练习结束后,先核对两个目录变量的输出,再只删除刚创建的文件,最后删除空目录:
ssh lab-server "rm -- '$REMOTE_DIR/message.txt' '$REMOTE_DIR/from-sftp.txt' && rmdir -- '$REMOTE_DIR'"
rm -- "$TRANSFER_DIR/message.txt" "$TRANSFER_DIR/download.txt" "$TRANSFER_DIR/from-sftp.txt"
rmdir -- "$TRANSFER_DIR"rsync:目录内容、差异与删除
通过 SSH 使用 rsync 时,两端都必须安装 rsync。它会比较文件属性并按需要传输;远程传输可使用块差异算法。默认快速检查主要依据文件大小和修改时间,--checksum 会增加内容读取成本。压缩过的 JAR、ZIP 再加压缩通常收益有限,不能把 -z 当成所有链路的默认加速器。rsync 官方手册说明了比较算法和选项行为。
客户端先检查两端程序,再创建另一组临时目录:
rsync --version
ssh lab-server 'command -v rsync && rsync --version'
SYNC_DIR=$(mktemp -d -t ssh-sync.XXXXXXXX)
mkdir "$SYNC_DIR/source"
printf 'version-one\n' > "$SYNC_DIR/source/message.txt"
SYNC_REMOTE=$(ssh lab-server 'mktemp -d -t ssh-sync-target.XXXXXXXX')
rsync -rt --dry-run --itemize-changes \
"$SYNC_DIR/source/" "lab-server:$SYNC_REMOTE/"
rsync -rt --itemize-changes \
"$SYNC_DIR/source/" "lab-server:$SYNC_REMOTE/"第一次 dry-run 应列出待传文件,但不会创建远端文件;第二次才实际传输。-r 递归目录,-t 保留修改时间。备份需要更多属性时再选 -a:它展开为 -rlptgoD,并不包含 ACL、扩展属性和硬链接;普通接收用户也不能任意恢复属主和设备文件。
源路径末尾斜杠决定复制对象。假设目标目录已经存在:
rsync source/ host:target/ → target/message.txt
rsync source host:target/ → target/source/message.txt-P 等于 --partial --progress,保留中断的部分文件并显示进度;重新执行仍需按 rsync 的比较与传输规则工作。它不意味着任意文件都能无条件从同一个字节偏移续传。
删除操作与复制分开练习。在刚才的专用远端目录添加一个多余文件,然后只预览删除:
ssh lab-server "touch '$SYNC_REMOTE/extra.txt'"
rsync -rt --delete --dry-run --itemize-changes \
"$SYNC_DIR/source/" "lab-server:$SYNC_REMOTE/"应看到 *deleting extra.txt,远端文件仍然存在。--delete 会删除传输范围内目标端多出的文件;移除 --dry-run 之前,需要确认源目录、目标目录和排除规则,确保删掉的是可再生成或可恢复的副本。空源目录也可能把目标清空,不能只检查命令是否拼写正确。
这个练习不实际运行同步删除,清理限定到已知文件:
ssh lab-server "rm -- '$SYNC_REMOTE/message.txt' '$SYNC_REMOTE/extra.txt' && rmdir -- '$SYNC_REMOTE'"
rm -- "$SYNC_DIR/source/message.txt"
rmdir -- "$SYNC_DIR/source" "$SYNC_DIR"host:path 使用远程 shell,通常就是 SSH;host::module 或 rsync://host/module 默认连接 rsync daemon,常用端口 873。这后一种形式自身没有 SSH 提供的传输加密。两种认证、访问路径和加密保护不能混为一谈。
本地 shell、远端 shell 与退出码
SSH 的远程命令会交给目标账号的 shell 执行。客户端先处理引号、变量和重定向;然后 ssh 把命令发送到服务器,再由远端 shell 解释。以下单引号使 $HOME 留给远端展开:
ssh lab-server 'printf "%s\n" "$HOME"; id -un'输出应是远端家目录与账号。写成双引号时,$HOME 通常已经在本地展开。即使把命令拆成多个本地参数,ssh 也会将它们以空格连接后发送,不能据此把不可信参数当成安全的远端 argv。复杂操作用内容固定的脚本,输入另行校验;不要把接口参数、文件名或用户输入直接拼进远程 shell。
无须分配终端的脚本使用 -T;交互式编辑器才需要 -t。远程命令退出码通常成为 ssh 的退出码。下面是安全的负例:
if ssh -T lab-server 'exit 7'; then
printf '意外成功\n' >&2
else
REMOTE_RC=$?
printf 'ssh_exit=%s\n' "$REMOTE_RC"
fi应输出 ssh_exit=7。ssh 自身错误通常返回 255,但远程程序也可能退出为 255,要结合错误消息区分。网络断开时,服务器可能已经执行了变更,只是结果未送回;此时先查询目标状态,再决定是否重试。
远端连续命令可以用 && 明确依赖,也可显式选择 Bash 并启用失败处理:
ssh -T lab-server 'bash -se' <<'EOF'
set -u -o pipefail
id -un
test -d "$HOME"
printf 'remote-check-ok\n'
EOF这里 stdin 承载脚本,远程步骤不能同时随意读取 stdin。bash -e 也有条件语句、管道等语义限制;本地脚本的 set -e 不会自动传到远端。持续运行的服务交给 systemd 等进程管理器,交互式长任务可用终端复用器。保活或后台 SSH 进程不能替代服务托管。
多主机读取与连接复用
少量主机的只读巡检可以使用循环。客户端创建 hosts.txt,每行填一个已经验证的 SSH 别名,不放选项、空格或 shell 片段。在独立 Bash 脚本中执行:
#!/usr/bin/env bash
set -u
failures=0
while IFS= read -r host || test -n "$host"; do
case "$host" in
''|'#'*) continue ;;
-*|*[!a-zA-Z0-9._-]*) printf '非法别名:%s\n' "$host" >&2; exit 2 ;;
esac
printf '\n主机:%s\n' "$host"
if ssh -n -T -o BatchMode=yes -o ConnectTimeout=10 \
"$host" 'id -un && hostname'; then
printf '结果:成功\n'
else
rc=$?
printf '结果:失败,退出码 %s\n' "$rc" >&2
failures=$((failures + 1))
fi
done < hosts.txt
test "$failures" -eq 0-n 让 ssh 不读取循环的标准输入,否则远程命令可能把后面的主机行读走。脚本保留每台结果,最后有任一失败就非零退出;ConnectTimeout 约束连接建立过程,不是远程命令的总运行时限。批量变更还需要目标分组、幂等、分批与恢复策略,不能把这个只读循环直接改成全量重启脚本。
频繁执行短命令时,可以在特定 Host 中增加连接复用:
ControlMaster auto
ControlPath ~/.ssh/cm-%C
ControlPersist 5m它复用已经认证的连接,减少握手开销。控制 socket 所在目录应仅对本人可访问。ssh -O check lab-server 检查 master,ssh -O exit lab-server 关闭对应 master,可能影响共用它的任务;不要在传输途中随意关闭。
验证新密钥、撤销授权或服务端配置时,使用 ssh -S none ... 创建新连接。一次复用成功只说明旧连接仍可用,不能用来确认新认证规则已生效。
跳板与端口转发
ProxyJump 如何到达内网服务器
ProxyJump 先认证跳板机,再请求从跳板机连接目标 SSH 端口。目标服务器的 SSH 握手仍由本地客户端完成,目标私钥无需放到跳板机,客户端分别验证跳板和目标的主机身份。
本地客户端 ── SSH 连接 A ── 跳板机 ── TCP 转接 ── 内网目标 sshd
└──────────── SSH 连接 B:本地与目标端到端认证、加密 ──────────┘
跳板持有 A 的主机私钥;目标持有 B 的主机私钥
用户私钥与两份 known_hosts 信任记录由本地客户端管理在客户端配置中为两台服务器分别指定身份:
Host lab-jump
HostName jump.example.com
User jumpops
IdentityFile ~/.ssh/id_ed25519_jump
IdentitiesOnly yes
StrictHostKeyChecking yes
ForwardAgent no
Host lab-inner
HostName app.internal.example.com
User ops
IdentityFile ~/.ssh/id_ed25519_ops
IdentitiesOnly yes
StrictHostKeyChecking yes
ProxyJump lab-jump
ForwardAgent no两台主机的公钥应先通过可信渠道安装到本地信任库。随后 ssh lab-inner 'id -un'、scp 文件 lab-inner:路径 与 rsync 目录/ lab-inner:目录/ 均可使用该连接配置。目标名称通常由跳板侧在打开目标 TCP 连接时解析;本地浏览器能解析目标域名,对这条路径没有决定作用。
跳板账号需要允许到目标 SSH 端口的 TCP 转发。服务端可用 AllowTcpForwarding local 与 PermitOpen 限制目标;这里的 local 对应客户端发起的本地转发能力,也包括 -W/ProxyJump 使用的通道。命令行传给目标的密钥或端口选项,不会自动替跳板选择正确配置。
ProxyJump 转运的是目标 SSH 密文,单靠跳板 sshd 日志无法完整记录目标 shell 内的命令。需要命令或会话审计时,应在目标系统或专门会话审计产品实现。ForwardAgent yes 则会暴露可调用本地 agent 的 socket,跳板上有权限访问它的人可能借用签名能力;普通 ProxyJump 不需要开启它。
L、R、D 的监听位置
字母以发起 SSH 的客户端为参照,与哪台机器被称为“服务器”无关:
| 形式 | 监听在哪里 | 谁连接最终目标 | 常见任务 |
|---|---|---|---|
-L 本地地址:端口:目标:端口 | 客户端 | SSH 服务端 | 本地工具访问远端数据库或管理接口 |
-R 远端地址:端口:目标:端口 | SSH 服务端 | 客户端 | 授权远端系统临时访问本地测试服务 |
-D 本地地址:端口 | 客户端 SOCKS 代理 | SSH 服务端 | 支持 SOCKS 的应用按请求选择目标 |
-L 和显式目标的 -R 转发 TCP,不能直接转发 UDP。加密只覆盖客户端到 SSH 服务端这一段;服务端再连第三台数据库时,后半段仍应按数据库要求使用 TLS。默认只监听回环地址可以减少暴露,但同一机器上的其他进程仍可能访问这个监听。
本地转发的成功与目标失败
在已授权测试服务器的普通用户会话中安装可用的 Python 3 后,创建只含测试文本的目录,前台启动回环 HTTP 服务:
HTTP_DIR=$(mktemp -d -t ssh-http.XXXXXXXX)
printf 'ssh-tunnel-ok\n' > "$HTTP_DIR/index.html"
python3 -m http.server 18080 --bind 127.0.0.1 --directory "$HTTP_DIR"该终端保持运行。客户端另开终端,确认本地 28080 未被占用,再启动转发:
ssh -S none -N -T -o ExitOnForwardFailure=yes \
-L 127.0.0.1:28080:127.0.0.1:18080 lab-server第一个 127.0.0.1 是客户端监听地址;第二个是 SSH 服务端看到的回环目标。-N 不请求远程命令,终端没有 shell 提示符是正常的。在客户端第三个终端执行:
curl -q --noproxy '*' --fail-with-body --silent --show-error \
--connect-timeout 3 --max-time 5 http://127.0.0.1:28080/应输出 ssh-tunnel-ok。-q 必须作为 curl 第一个选项,禁止默认 curlrc;--noproxy '*' 避免代理环境改变回环路径。失败时结合转发终端的错误判断,curl 选项见 官方手册。
现在只在服务器 HTTP 终端按 Ctrl+C 停止测试 HTTP,再运行同一条 curl。隧道进程通常还在,但请求失败,转发终端可出现 connect failed: Connection refused。这说明 ExitOnForwardFailure 检查的是转发监听或请求建立失败,不能保证每次使用隧道时最终目标都可达。
重新启动同一 HTTP 命令,请求应恢复。最后在客户端转发终端按 Ctrl+C,再在服务器停止 Python;服务器原会话中执行 rm -- "$HTTP_DIR/index.html"、rmdir -- "$HTTP_DIR" 清理测试文件。
远程转发与动态代理
远程转发要求服务器允许相应通道。假设客户端有获准共享且只监听本机 18080 的测试服务:
ssh -S none -N -T -o ExitOnForwardFailure=yes \
-R 127.0.0.1:28080:127.0.0.1:18080 lab-server此时在服务器访问 127.0.0.1:28080,由客户端连接自己的 127.0.0.1:18080。服务器的 GatewayPorts no 默认限制远程监听到回环;要向其他机器公开,必须同时审查 GatewayPorts、监听地址、主机防火墙和云网络规则。不要为临时测试直接换成 0.0.0.0。
服务器还可用 PermitListen 限制 -R 请求的监听地址和端口。它不同于 PermitOpen:后者限制服务端为本地转发连接的目标。名称、回环地址和允许列表要按实际请求形式匹配。相关限制见 sshd_config 手册。
支持 SOCKS 的客户端程序可使用 -D,例如在批准的网络出口上建立本地代理:
ssh -S none -N -T -o ExitOnForwardFailure=yes -D 127.0.0.1:1080 lab-server应用必须显式配置 SOCKS 代理,系统其他流量不会自动进入它。curl 的 --socks5-hostname 把目标名称交给 SOCKS 端解析,--socks5 通常在客户端解析。测试时不要再带 --noproxy '*',否则会主动绕过这个代理;应使用 curl -q --noproxy '' --socks5-hostname 127.0.0.1:1080 ... 并选择明确获准的测试 URL。
SSH 的 SOCKS 支持用于 TCP 请求,不是自动接管 DNS 和 UDP 的全系统 VPN。完成后在隧道终端按 Ctrl+C,恢复应用原代理设置。三种转发都应在使用前确认访问授权,不能用来绕过组织网络策略。
服务端授权、变更与维护
配置文件存在,不代表该值生效
客户端的 ssh_config 决定如何连接,服务端的 sshd_config 决定接受什么。Ubuntu 常在主配置靠前位置包含 /etc/ssh/sshd_config.d/*.conf,通配文件按词法顺序读取。对于多数单值指令,先取得的值生效:在文件末尾补一行 PasswordAuthentication no,可能无法覆盖前面已经读取的 yes。
Match 又引入按连接条件求值的配置。匹配的 User、Address 等条件可以覆盖全局部分;若多个匹配块为同一参数赋值,通常使用第一个匹配值。某些指令支持累加,某些只能出现在全局部分,因此不要机械套用一条覆盖规则。
在服务器以管理员身份检查来源,再模拟真实连接:
sudo grep -nE '^[[:space:]]*(Include|Match|PasswordAuthentication|KbdInteractiveAuthentication|AuthenticationMethods|AllowUsers|AllowGroups)' \
/etc/ssh/sshd_config
sudo ls -l /etc/ssh/sshd_config.d
sudo /usr/sbin/sshd -t
SSHD_CLIENT_ADDR='192.0.2.10'
sudo /usr/sbin/sshd -T -C "user=ops,addr=$SSHD_CLIENT_ADDR,host=client.example.com,laddr=192.0.2.20,lport=22" \
| grep -E '^(passwordauthentication|kbdinteractiveauthentication|authenticationmethods|pubkeyauthentication|permitrootlogin|allowtcpforwarding|disableforwarding|forcecommand) '把示例地址替换为服务器实际看到的来源和本地地址;通过跳板时,addr 是跳板在目标侧呈现的地址。host 表示远端客户端主机名,涉及 Match Host 时也要与实际情况一致。-t 查语法与密钥合理性,-T -C 展示指定连接条件下的有效配置;两者职责不同。
配置片段需要沿 Include 路径继续查看。sshd -T 不会自动代表每个用户、来源和监听端口,也没有执行一次真实认证。参数与 Match 规则都以 sshd_config 为准。
关闭密码之前的变更顺序
只允许个人公钥登录的 Ubuntu 主机,可在全局配置中采用:
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
X11Forwarding no这组配置适用于已经验证普通管理用户、公钥及所需 sudo 权限的机器。PasswordAuthentication no 只关闭 password 方法;PAM 仍可能通过 keyboard-interactive 提示密码。采用 OTP 等 MFA 的主机,需要保留相应认证方式并配置组合,例如 AuthenticationMethods publickey,keyboard-interactive:pam,同时完成 PAM 集成与恢复账号设计。不要直接把上面的 KbdInteractive 设置覆盖到 MFA 主机。
PermitRootLogin prohibit-password 仍允许 root 公钥认证,而 no 禁止 root SSH 登录。AllowUsers、AllowGroups 是另一层账号限制,添加前必须包括所有仍需访问的管理、自动化与应急账号。端口转发按账号用途决定,不在全局样例里一律关闭,否则会切断刚配置的跳板或诊断隧道。
一次配置变更应保持以下顺序,操作期间保留已验证的管理会话 A 和可信控制台:
- 在客户端新建会话 B,以
ssh -S none验证普通用户公钥登录;确认需要的管理权限可用。 - 在服务器备份将要修改的文件,阅读 Include 与 Match 位置,只修改确定的生效来源。
- 执行语法检查与目标条件下的有效配置检查;有错误就恢复文件,不重载。
- 重载认证配置,再从客户端建立全新连接验证正确账号和应被拒绝的方式。
- 登录、所需 sudo、文件传输和必要转发都符合要求后,才退出会话 A。
以下示例只修改主文件,不改变端口、监听地址或 systemd 单元。在服务器会话 A 中执行:
SSHD_BACKUP=$(sudo mktemp /root/sshd-config-backup.XXXXXXXX)
sudo cp -a /etc/ssh/sshd_config "$SSHD_BACKUP"
printf '备份位置:%s\n' "$SSHD_BACKUP"
sudoedit /etc/ssh/sshd_config
sudo /usr/sbin/sshd -t备份路径记录在管理记录中。若实际生效的设置来自 include 文件,则对那个确定的文件另做备份、编辑和恢复,不能只备份主文件却改了整组片段。
sshd -t 成功且 -T -C 符合预期后,Ubuntu 已运行的服务重载:
sudo systemctl reload ssh.service
sudo journalctl -u ssh.service -n 30 --no-pager重载失败时看日志,不能用 reload sshd || reload ssh || restart 把服务名和失败原因一并掩盖。socket activation 的端口变更属于另一种操作,要检查 systemctl cat ssh.socket、生成配置和实际监听,并通过控制台安排重新加载单元及监听切换。
会话 B 使用 ssh -S none -o PreferredAuthentications=publickey lab-server 'id -un',应成功。验证关闭的 password 方法时,在客户端运行:
ssh -S none -o PubkeyAuthentication=no -o PreferredAuthentications=password \
-o NumberOfPasswordPrompts=1 lab-server true预期服务端不提供 password 方法,命令以认证失败退出;若仍出现服务器密码提示,取消输入,重新查有效配置和 Match,不重复猜密码。MFA 主机按实际认证组合验证,不能照搬这个拒绝目标。新登录失败时通过会话 A 恢复本次修改的主文件:
sudo cp -a "$SSHD_BACKUP" /etc/ssh/sshd_config
sudo /usr/sbin/sshd -t && sudo systemctl reload ssh.service再建新连接确认恢复。若会话 A 也不可用,就从预先验证的控制台恢复相同文件;因此控制台能否进入必须在变更前确认。
仅提供文件传输的 SFTP 账号
文件交换账号需要的权限通常比运维账号少。ForceCommand internal-sftp 可以强制文件传输,但它自身不关闭 TCP、agent 等转发;DisableForwarding yes 负责关闭这些额外通道。文件的读、写和删除仍受账号与目录权限控制,限制到 SFTP 并不意味着只能上传、不能下载。
下面是 Ubuntu 上新建专用测试账号 uploadlab 的完整结构。先确认账号与 /srv/sftp/uploadlab 尚不存在,不修改现有业务账号。客户端为它生成独立密钥,并通过可信管理渠道把公钥交给服务器管理员,保存为 /tmp/uploadlab.pub。服务器执行:
getent passwd uploadlab
sudo ls -ld /srv/sftp/uploadlab账号已有输出或目录存在时停止,另选名称并同步替换后面的路径。确认两者均不存在,且公钥文件与交付指纹一致后创建:
sudo useradd --no-create-home --home-dir /upload --shell /usr/sbin/nologin uploadlab
sudo install -d -o root -g root -m 755 /srv/sftp /srv/sftp/uploadlab
sudo install -d -o uploadlab -g uploadlab -m 750 /srv/sftp/uploadlab/upload
sudo install -d -o root -g root -m 755 /etc/ssh/authorized_keys
sudo install -o root -g root -m 644 /tmp/uploadlab.pub /etc/ssh/authorized_keys/uploadlab在已备份的主配置末尾加入这个匹配块,并确认此前没有优先匹配的冲突配置:
Match User uploadlab
ChrootDirectory /srv/sftp/%u
AuthorizedKeysFile /etc/ssh/authorized_keys/%u
ForceCommand internal-sftp -d /upload
DisableForwarding yes
PermitTTY no
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no/srv、/srv/sftp 和 chroot 根目录必须由 root 持有,且不能被组或其他用户写入。上传目录放在下面,才交给 uploadlab 写。进入 chroot 后,客户端看到的 /upload 对应宿主 /srv/sftp/uploadlab/upload;根目录如果也归 uploadlab,就会被 sshd 的安全检查拒绝。
这组授权文件由管理员持有,在 chroot 之前读取。它保存的是公钥,使用 root 属主和 644 让目标用户可读而不可写;若设为 root 属主的 600,sshd 以目标用户身份读取时会失败。它与用户自己持有、使用 600 的家目录 authorized_keys 属于不同布局。
Ubuntu 的 UsePAM yes 仍执行账号与会话检查;账号到期、PAM 限制等会影响公钥登录。其他发行版的锁定账号策略可能不同,不能遇到拒绝就统一解锁密码。按前面的变更流程用 -T -C user=uploadlab,... 验证、检查语法并重载。
客户端为 uploadlab 配置一个独立 lab-upload 别名,HostName 与主机相同,User 改为 uploadlab,IdentityFile 指向专用私钥。验证 sftp lab-upload 可以进入 /upload 并上传测试文件,尝试写 / 应失败;ssh -S none lab-upload id 不应执行 id;转发测试应在真正发起目标请求时被拒绝。只看到本地 -L 端口存在,还不足以说明服务端允许转发。
回退时先停止这份测试授权并恢复配置,确认普通管理账号仍可登录。保留上传文件直到明确其处理方式;不要把删除账号和递归清空上传目录绑成一条命令。
密钥轮换、证书与自动化权限
个人与自动化凭据分开分发,每份授权记录账号、用途、指纹和可到达的主机。轮换时先添加新公钥,以新密钥和 -S none 验证所需动作,再移除旧公钥并验证旧密钥失败。若私钥泄漏,要同步检查其他主机授权、agent、已有会话和自动化平台中的副本。
自动化账号只拥有完成任务所需的目录与命令权限。authorized_keys 可使用 from=、restrict、command= 等逐密钥约束;固定命令包装器应由管理员持有,不能把 SSH_ORIGINAL_COMMAND 不经校验地传给 eval。restrict 禁止转发和 PTY 等能力,但没有自动把 shell 命令变成只读,需要组合固定命令或受限子系统。
sudo 授权同样要追到命令实际读取的文件。如果允许用户重启一个以 root 运行、却加载该用户可写脚本的服务,授权表面上只有一条 systemctl 命令,实际仍可获得 root 执行能力。应用应使用独立低权限运行账号,unit、管理脚本和特权配置由管理员持有。脚本里使用 sudo -n 可在缺少非交互权限时立即失败;需要人工输入密码时再用交互终端。
主机数量增加后,可以让服务器信任用户 CA 公钥,而不是逐台分发每个人的公钥。OpenSSH 用户证书携带公钥、principals、有效期和权限选项,CA 对这些内容签名;客户端仍需要对应用户私钥完成认证。服务器通过 TrustedUserCAKeys 信任 CA,并用账号名或 AuthorizedPrincipalsFile 决定哪些 principals 可以登录该账号。
主机证书则解决另一方向的信任:客户端在 known_hosts 中用 @cert-authority 指定目标名称范围和 CA 公钥,验证服务器证书。SSH 证书不是网站 TLS 的 X.509 证书。CA 私钥不能分发到业务主机;签发、时间同步、短有效期、撤销和应急访问都需要独立管理。已有证书可用 ssh-keygen -L -f 文件-cert.pub 查看,撤销文件由 RevokedKeys 等配置接入;离职不应只等证书自然过期。
FIDO 安全密钥可降低可复制私钥的风险,但仍需要考虑设备丢失、触摸/PIN 交互和恢复方式。具体设备与 MFA 集成可查 Ubuntu OpenSSH 文档中的二因素认证入口,证书格式与签发/撤销参数查 ssh-keygen。这些选择服务于账号用途,不应让无人值守流水线依赖人工触摸才能发布。
日志、连接容量与升级
Ubuntu 上可以用 sudo journalctl -u ssh.service -n 100 --no-pager 查服务日志,再结合 sudo journalctl -t sshd -n 100 --no-pager 过滤认证消息;是否写 /var/log/auth.log 取决于日志配置。保留登录者、来源、目标账号、认证失败及所用公钥指纹等必要信息。提高 LogLevel 前检查日志量和隐私,详细 DEBUG 适合限时排查。
普通 sshd 日志主要记录认证与会话事件,无法完整还原一个交互 shell 中执行的所有命令。重要操作还要结合 sudo 日志、发布系统记录或受控会话审计。客户端 -vvv 日志也可能暴露主机、账号、路径和远程命令,分享前先脱敏。
并发失败时先区分服务端限制:MaxStartups 管未认证连接,MaxSessions 管单条网络连接中的 shell/登录/子系统会话数,MaxAuthTries 管每条连接的认证尝试次数。大量短连接会增加握手与认证负担,复用可降低这部分成本,但会共享故障与连接状态。先限制批量并发、确认认证延迟和拒绝日志,再决定是否调整容量。
升级优先使用受支持发行版的安全更新。发行版会回移补丁,单看 OpenSSH_9.6p1 字符串不能判断漏洞修复状态;应核对完整包版本与发行版安全公告。升级前保存配置、确认控制台和旧版本恢复渠道,升级后依次检查 sshd -t、有效配置、新登录、SFTP、跳板与必要转发。算法变化和协议兼容项可查 OpenSSH 发行说明,不要为了兼容单台旧设备降低全部主机的全局算法要求。
按最后成功的阶段排查
SSH 失败消息可以定位到已经完成的步骤。排查时保留目标、账号、端口和新连接条件,再看错误发生在哪一层:
| 客户端现象 | 已到达的位置 | 优先检查 |
|---|---|---|
Could not resolve hostname | 目标名称尚未解析 | HostName、当前执行侧解析器、跳板目标解析 |
Connection timed out / Connection refused | TCP 未正常建立 | 地址、端口、路由、监听与过滤 |
no matching ... found | 已进入 SSH 算法协商 | 错误指定的算法类别、双方允许集合 |
REMOTE HOST IDENTIFICATION HAS CHANGED | 正在验证服务器身份 | 可信主机指纹与资产变化 |
Permission denied (publickey) | 进入用户认证 | 账号、客户端身份、授权文件、有效策略 |
subsystem request failed | 认证后请求子系统 | SFTP 配置与 ForceCommand |
administratively prohibited | 请求转发通道 | 服务端转发许可、PermitOpen、密钥限制 |
解析、连接与握手
客户端先展开配置,再用同一别名发起新连接:
ssh -G lab-server | grep -E '^(hostname|user|port|proxyjump|proxycommand|identityfile) '
ssh -S none -vvv -o ConnectTimeout=10 lab-server true直接连接时可用 getent ahosts server.example.com 检查 Linux 系统解析结果。通过 ProxyJump 时,目标解析和 TCP 出口发生在跳板侧;需要在获准的跳板环境检查,而不是从办公电脑对内网目标反复 ping。
超时表示在给定时间内没有完成连接,不能仅凭它区分安全组、路由或主机过滤。拒绝表示收到了主动拒绝,可能来自目标没有监听,也可能是中间设备的 REJECT。通过控制台在服务器检查:
sudo ss -lntp '( sport = :22 )'
systemctl status ssh.socket ssh.service --no-pager
sudo journalctl -u ssh.service -n 60 --no-pager
sudo /usr/sbin/sshd -t本机无监听先修启动与配置;有正确监听但客户端超时,再沿实际来源检查路由、防火墙和云规则。ICMP 与 SSH TCP 可以采用不同策略,ping 成败不能替代这个判断。修复后仍用同一客户端、账号和端口建立新连接复测。
no matching key exchange method、no matching host key type 和 no matching cipher 分别涉及密钥交换、主机签名与数据加密,不能都通过更换用户私钥解决。ssh -Q kex、ssh -Q key、ssh -Q cipher 展示本机支持能力,ssh -G 与服务端 sshd -T 才展示相关配置;再由 -vvv 观察实际协商。优先升级旧端,确需临时兼容时限制到专用 Host 并记录撤销条件。
主机身份与认证故障
收到主机变化警告后,先从可信控制台重新读取主机公钥指纹,核对换机、地址复用和密钥轮换记录。没有可信解释就停止连接。确认新身份后,备份客户端信任库,并只处理准确的目标条目:
KNOWN_HOSTS_BACKUP=$(mktemp "$HOME/.ssh/known-hosts-backup.XXXXXXXX")
cp -p "$HOME/.ssh/known_hosts" "$KNOWN_HOSTS_BACKUP"
ssh-keygen -F "$SSH_HOST"
# 确认是该默认端口目标的计划内变更后执行
ssh-keygen -R "$SSH_HOST"
ssh -p "$SSH_PORT" "$SSH_USER@$SSH_HOST"非默认端口应使用 "[$SSH_HOST]:$SSH_PORT" 精确查询和移除,并按新的可信指纹重新接受。HostKeyAlias 或专用 UserKnownHostsFile 改变了存储位置时,对照 ssh -G 操作对应条目,不要清空整份信任库。
公钥被拒绝时,客户端先看 -vvv 的身份路径、Offering public key 和 Server accepts key。前者表示提出候选公钥,后者也未必代表全部认证完成,仍可能需要签名或第二因素。最终应看到完成认证并成功执行 id -un。
服务端按目标用户核查:
getent passwd ops
sudo namei -l /home/ops/.ssh/authorized_keys
sudo ssh-keygen -lf /home/ops/.ssh/authorized_keys -E sha256
sudo journalctl -t sshd -n 80 --no-pager家目录以 getent 输出为准。将授权指纹与客户端 ssh-keygen -lf "$OPS_KEY.pub" 对照,再查 -T -C 的认证策略与 AuthorizedKeysFile。如果日志是 bad ownership or modes,修复明确的属主或写权限,不递归改整个家目录;如果是账号/PAM 限制,按账号状态解决。修复后用同一密钥和 -S none 重测。
Too many authentication failures 常见于 agent 提供了过多候选密钥。先用 IdentitiesOnly=yes 和确定的 IdentityFile 收敛身份,再查看有效配置中的累加条目,不急着提高 MaxAuthTries。私钥权限过宽则在客户端修复该文件权限;Linux 可用 chmod 600 "$OPS_KEY",Windows 使用对应 ACL。
会话、传输与隧道故障
登录成功但 scp/SFTP 失败,先检查远端目录和空间,再检查子系统。服务器管理员运行 sudo /usr/sbin/sshd -T | grep '^subsystem ',应有可用的 SFTP 定义;常见配置为 Subsystem sftp internal-sftp,修改时只能保留一个相应定义并按前述流程验证。现代 scp 依赖 SFTP;scp -O 回到旧协议会重新引入远程 shell 的路径展开行为,只用于明确需要的短期兼容。
受限上传账号还要检查 chroot 的每层属主与模式,使用 sudo namei -l /srv/sftp/uploadlab/upload。能写 /upload、不能写根目录是预期;如果连会话都打不开,则查日志中的 chroot、家目录和 PAM 报错。rsync 需要远端 rsync 程序与命令执行能力,只提供 internal-sftp 的账号无法直接承接 rsync-over-SSH。
远程命令卡住时,先区分程序等待输入、sudo 等待密码、输出管道未关闭和网络失联。非交互脚本使用 BatchMode,sudo 使用 -n;需要交互时明确分配终端。长任务通过运行状态判断结果,不要一看到本地 SSH 超时就再次执行变更。
隧道错误需要定位到两段连接。bind: Address already in use 查监听所在机器的端口占用;administratively prohibited 查服务端 Match、密钥选项和转发许可;connect failed: Connection refused 则查最终目标所在侧的监听。使用 -L 时,从 SSH 服务端检查目标地址;使用 -R 时,从客户端检查目标地址。恢复后用同一应用请求复测,而不是只检查 ssh 进程还在。
权威资料与规范地址
协议消息、参数全集与平台差异可按下列主题查阅。
协议与身份
- SSH 传输层:https://www.rfc-editor.org/rfc/rfc4253
- SSH 用户认证:https://www.rfc-editor.org/rfc/rfc4252
- SSH 连接与通道:https://www.rfc-editor.org/rfc/rfc4254
- sshd 与授权文件:https://man.openbsd.org/sshd
- 密钥、证书与撤销:https://man.openbsd.org/ssh-keygen
- 主机公钥扫描:https://man.openbsd.org/ssh-keyscan
- agent 进程:https://man.openbsd.org/ssh-agent
- agent 密钥管理:https://man.openbsd.org/ssh-add
连接、配置与文件
- SSH 客户端与转发:https://man.openbsd.org/ssh
- 客户端配置:https://man.openbsd.org/ssh_config
- 服务端配置:https://man.openbsd.org/sshd_config
- scp:https://man.openbsd.org/scp
- SFTP:https://man.openbsd.org/sftp
- rsync:https://download.samba.org/pub/rsync/rsync.1
- curl:https://curl.se/docs/manpage.html
平台与兼容
- Ubuntu OpenSSH 安装与认证:https://ubuntu.com/server/docs/how-to/security/openssh-server/
- Ubuntu socket activation:https://discourse.ubuntu.com/t/sshd-now-uses-socket-based-activation-ubuntu-22-10-and-later/30189
- Windows OpenSSH 安装:https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse
- OpenSSH 发行说明:https://www.openssh.com/releasenotes.html
