SSH 完整指南¶
适用人群:需要登录服务器、传输文件、访问内网服务或使用 Git 远程仓库的读者
前置要求:会使用终端,了解用户名、主机名、IP 地址和端口
最后更新:2026-07-21
SSH(Secure Shell)是在不可信网络上建立加密通道的协议。远程终端只是它最常见的用途;文件传输、Git 认证、跳板机和端口转发都建立在同一套身份与加密机制上。本章先解释连接时双方验证什么,再建立一套可以长期维护的密钥和配置方法,最后提供安全加固、故障定位和命令速查。
1. 一次 SSH 连接中发生了什么¶
执行 ssh user@server.example.com 后,客户端先与目标 TCP 端口建立连接,然后协商协议和加密算法。服务器用主机密钥证明身份,客户端再用密码、私钥或其他方式证明用户身份。认证通过后,双方在加密通道中传输终端、文件或转发流量。
sequenceDiagram
participant C as SSH 客户端
participant S as SSH 服务端
C->>S: 建立 TCP 连接
C->>S: 协商算法与会话密钥
S-->>C: 提供主机公钥证明身份
C->>C: 对照 known_hosts 验证指纹
C->>S: 使用用户私钥完成签名认证
S->>S: 对照 authorized_keys 验证公钥
S-->>C: 建立加密会话
这条链中有两种不同的信任:
| 问题 | 由什么回答 | 常见位置 |
|---|---|---|
| “服务器真的是目标服务器吗?” | 服务器主机密钥 | 客户端 ~/.ssh/known_hosts |
| “当前用户有登录权限吗?” | 用户公钥或其他认证方式 | 服务端 ~/.ssh/authorized_keys |
私钥只保存在持有者设备上,并通过签名证明身份,不会被发送给服务器。公钥可以分发,但仍应标明用途并及时撤销。会话数据使用对称加密,因为它比公钥运算更适合大量数据;公钥算法主要用于身份和密钥交换。
不要跳过主机指纹验证
第一次连接出现的指纹应通过云控制台、管理员或其他可信渠道核对。直接输入 yes 只表示保存当前指纹,不表示它一定正确。
2. 建立第一条可靠连接¶
现代 macOS、Linux 和 Windows 通常已经包含 OpenSSH 客户端:
服务器端在 Linux 上通常由 sshd 提供:
sudo systemctl status ssh # Debian / Ubuntu
sudo systemctl status sshd # RHEL / Fedora
sudo ss -lntp | grep sshd
最常用连接参数如下:
ssh user@server.example.com
ssh -p 2222 user@server.example.com
ssh -i ~/.ssh/id_ed25519_work user@server.example.com
-p 指定服务端口,-i 指定身份私钥。连接成功后执行 exit 或按 Ctrl-D 退出。
不进入交互终端也可以执行命令:
远程命令的引号在远端 shell 中解释。命令包含用户输入时不要直接字符串拼接,否则可能形成命令注入。自动化优先使用结构化远程 API 或经过严格限定的脚本。
第一次连接与指纹变化¶
第一次连接会看到类似提示:
The authenticity of host 'server.example.com' can't be established.
ED25519 key fingerprint is SHA256:...
核对后确认,记录会写入 known_hosts。如果以后出现 REMOTE HOST IDENTIFICATION HAS CHANGED,可能是服务器重装、主机名指向了另一台机器,也可能是中间人攻击。先确认变更来源,确认合法后再删除旧记录:
不要为了“让错误消失”关闭 StrictHostKeyChecking 或清空整个 known_hosts。
3. 密钥认证与权限¶
新密钥优先使用 Ed25519,并设置 passphrase。passphrase 能在私钥文件被复制后增加一道保护。
-t 选择算法,-a 增加私钥口令派生轮数,-C 添加便于识别的注释。默认生成:
一个密钥应对应可识别的设备或用途。工作、个人托管平台和高权限生产环境不要共用同一私钥。轮换时先添加新公钥并验证,再移除旧公钥,避免锁在服务器外。
部署公钥¶
Linux/macOS 客户端可使用:
如果没有 ssh-copy-id,通过可信渠道把 .pub 文件的一整行追加到远端用户的 ~/.ssh/authorized_keys。服务端权限通常应为:
客户端私钥和配置权限:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 600 ~/.ssh/config
chmod 644 ~/.ssh/id_ed25519.pub
OpenSSH 会拒绝使用其他用户可读的私钥。不要使用 chmod 777 解决权限错误。
authorized_keys 的限制选项¶
服务端可以在单个公钥前限制来源、命令和转发能力:
自动备份账户可以使用 command="..." 固定允许执行的程序。具体选项需按当前 OpenSSH 手册验证,修改前保留另一个已登录会话作为恢复通道。
4. 用 SSH Config 管理连接¶
命令参数一多就应写入 ~/.ssh/config。Host 是本地别名,HostName 才是真实地址:
Host dev-api
HostName 203.0.113.20
User ubuntu
Port 22
IdentityFile ~/.ssh/id_ed25519_work
IdentitiesOnly yes
ServerAliveInterval 30
ServerAliveCountMax 3
之后只需:
配置从上到下累计匹配,较具体规则通常放在前面,通用默认放在最后:
Host *.corp.example.com
User deploy
IdentityFile ~/.ssh/id_ed25519_work
IdentitiesOnly yes
Host *
AddKeysToAgent yes
ServerAliveInterval 30
ServerAliveCountMax 3
查看某个别名最终生效的配置是排错利器:
高频参数:
| 参数 | 作用 |
|---|---|
HostName |
真实主机名或 IP |
User |
远端用户 |
Port |
SSH 服务端口 |
IdentityFile |
私钥文件 |
IdentitiesOnly yes |
只尝试明确配置的身份 |
ProxyJump |
通过跳板机连接 |
ForwardAgent |
转发本地 agent,默认不要开启 |
LocalForward |
固定本地端口转发 |
ServerAliveInterval |
客户端保活间隔 |
5. ssh-agent:保管已解锁的密钥¶
ssh-agent 在内存中保存已解锁私钥的使用能力,避免每次连接重复输入 passphrase。它不会把私钥复制到远端。
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519_work
ssh-add -l
ssh-add -d ~/.ssh/id_ed25519_work
ssh-add -D
桌面系统通常已集成 agent 或系统密钥链,不要在每个 shell 配置里反复启动新 agent。Too many authentication failures 往往不是密码错误,而是 agent 中密钥太多;使用 IdentitiesOnly yes 和明确的 IdentityFile。
Agent forwarding 允许远端通过本地 agent 继续认证下一台主机,但被攻陷的远端在会话期间可能滥用该能力。优先使用 ProxyJump,只有明确需要时才对单个可信主机启用 ForwardAgent yes。
6. 文件传输:scp、sftp 与 rsync¶
scp 适合少量文件:
# 上传
scp ./report.txt dev-api:/tmp/
# 下载
scp dev-api:/var/log/app.log ./
# 目录递归与非标准端口
scp -r -P 2222 ./dist user@host:/srv/app/
注意 scp 使用大写 -P 指端口,而 ssh 使用小写 -p。
sftp 提供交互式文件操作:
大量目录同步优先使用 rsync,它只传输差异并可保留属性:
--delete 会删除目标端多余文件,先加 --dry-run 预览。传输敏感数据前确认目标主机和路径,避免通配符在错误一端展开。
7. 跳板机与内网访问¶
生产主机通常没有公网入口,只允许通过堡垒机进入。ProxyJump 让客户端端到端连接目标主机,无需在跳板机保存私钥:
长期配置:
Host bastion
HostName bastion.example.com
User ops
IdentityFile ~/.ssh/id_ed25519_work
Host prod-api
HostName 10.0.1.20
User ubuntu
IdentityFile ~/.ssh/id_ed25519_prod
IdentitiesOnly yes
ProxyJump bastion
目标主机指纹仍记录在本地 known_hosts。多级跳转可以用逗号连接,但链路越长越应依赖组织统一的访问控制、短期证书和审计,而不是无限扩展个人配置。
8. 端口转发:安全地访问受限服务¶
端口转发把 TCP 连接放进 SSH 加密通道。使用前先明确“监听在哪一端、最终目标从哪台机器访问”。
本地转发 -L¶
把远端网络可见的服务映射到本地。访问内网 PostgreSQL:
随后本地程序连接 127.0.0.1:5433。参数含义是:
使用 127.0.0.1 限制只有本机能访问,不要无意绑定 0.0.0.0。
远程转发 -R¶
让远端一侧访问本地服务:
远端访问 127.0.0.1:9000 会被转到本地 3000 端口。服务端是否允许以及能否绑定非回环地址由 sshd_config 控制。不要用它绕过组织网络政策。
动态转发 -D¶
这会创建 SOCKS 代理,由客户端决定目标地址。它适合经过授权的调试,不等同于通用 VPN,也不保证应用的所有 DNS 查询都进入隧道。
常用控制参数:
-N 不执行远程命令,-T 不分配终端,ExitOnForwardFailure 在监听失败时直接退出。
9. GitHub 与多账号密钥¶
代码托管平台保存你的公钥,Git 客户端用对应私钥签名。生成独立密钥后,把 .pub 内容添加到平台账户:
ssh-keygen -t ed25519 -C 'github-personal' \
-f ~/.ssh/id_ed25519_github_personal
cat ~/.ssh/id_ed25519_github_personal.pub
ssh -T git@github.com
个人与工作账号使用 Host 别名隔离:
Host github-personal
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github_personal
IdentitiesOnly yes
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github_work
IdentitiesOnly yes
仓库地址中的主机部分必须使用对应别名:
git clone git@github-personal:username/project.git
git remote set-url origin git@github-work:company/project.git
git remote -v
SSH 身份决定平台账户,Git 提交作者由仓库中的 user.name 和 user.email 决定,两者要分别配置:
10. 服务端安全加固¶
加固前确认云控制台或另一个已登录会话可用于恢复。先测试配置,再平滑重载:
不同发行版可能使用 /etc/ssh/sshd_config 及 sshd_config.d/。核心原则如下:
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
MaxAuthTries 3
AllowUsers deploy ops
只有在所有管理员都验证过密钥登录并有恢复通道后,才能关闭密码认证。修改端口只能减少扫描噪声,不是安全边界。真正的保护来自密钥或短期证书、最小用户权限、防火墙、补丁、速率限制和审计。
私钥不应部署到服务器;自动化系统使用专用身份、有限权限和可轮换凭据。离职、设备丢失或权限变化时及时移除对应公钥并检查日志。
11. 分层排错方法¶
SSH 连接失败时按照“名称解析 → TCP → 主机身份 → 用户认证 → 会话”的顺序检查,不要一开始就重建密钥。
名称和端口¶
Connection timed out:常见于路由、防火墙、安全组或地址错误。Connection refused:目标可达,但对应端口没有服务监听或端口写错。
查看详细握手过程¶
关注这些阶段:
Connecting to ... TCP 连接
Server host key ... 服务器主机密钥
Offering public key ... 客户端提交公钥身份
Server accepts key ... 服务端接受该身份
Authenticated to ... 用户认证成功
Permission denied (publickey)¶
ssh -G dev-api | grep -E '^(user|hostname|port|identityfile) '
ssh-add -l
ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519_work user@host
在服务器控制台检查目标用户、authorized_keys 内容与权限,并查看 sshd 日志。公钥放在错误用户家目录、私钥未被实际选中、家目录权限过宽都是高频原因。
连接经常断开¶
配置客户端保活:
这不能修复网络问题,只能更快发现失效连接并通过空闲设备。长任务应在远端使用 tmux 或 systemd 管理,不能依赖一个永不断开的 SSH 终端。
12. 命令与参数速查¶
连接和认证¶
| 目的 | 命令或参数 |
|---|---|
| 基础连接 | ssh user@host |
| 指定端口 | ssh -p 2222 user@host |
| 指定密钥 | ssh -i KEY user@host |
| 跳板机 | ssh -J bastion user@target |
| 远程命令 | ssh host 'command' |
| 详细调试 | ssh -vvv host |
| 查看生效配置 | ssh -G host |
| 仅检查认证 | ssh -T host |
密钥和主机记录¶
| 目的 | 命令 |
|---|---|
| 生成 Ed25519 密钥 | ssh-keygen -t ed25519 -a 64 |
| 显示公钥指纹 | ssh-keygen -lf KEY.pub |
| 查找主机记录 | ssh-keygen -F host |
| 删除主机记录 | ssh-keygen -R host |
| 添加 agent 密钥 | ssh-add KEY |
| 查看 agent 密钥 | ssh-add -l |
文件和隧道¶
| 目的 | 命令 |
|---|---|
| 上传文件 | scp file host:/path/ |
| 下载文件 | scp host:/path/file ./ |
| 目录同步 | rsync -av src/ host:/dst/ |
| 本地转发 | ssh -N -L local:target:port host |
| 远程转发 | ssh -N -R remote:target:port host |
| SOCKS 代理 | ssh -N -D 127.0.0.1:1080 host |
13. 延伸阅读¶
- OpenSSH
ssh手册:客户端参数的权威说明 - OpenSSH
ssh_config手册:客户端配置字段 - OpenSSH
sshd_config手册:服务端策略与默认值 - GitHub SSH 连接指南:托管平台密钥配置
- Mozilla OpenSSH Guidelines:服务端安全建议