跳转至

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 客户端:

ssh -V

服务器端在 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 退出。

不进入交互终端也可以执行命令:

ssh server 'hostname && uptime'
ssh server 'journalctl -u nginx --since "10 minutes ago"'

远程命令的引号在远端 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,可能是服务器重装、主机名指向了另一台机器,也可能是中间人攻击。先确认变更来源,确认合法后再删除旧记录:

ssh-keygen -F server.example.com
ssh-keygen -R server.example.com

不要为了“让错误消失”关闭 StrictHostKeyChecking 或清空整个 known_hosts

3. 密钥认证与权限

新密钥优先使用 Ed25519,并设置 passphrase。passphrase 能在私钥文件被复制后增加一道保护。

ssh-keygen -t ed25519 -a 64 -C 'work-laptop-2026'

-t 选择算法,-a 增加私钥口令派生轮数,-C 添加便于识别的注释。默认生成:

~/.ssh/id_ed25519       私钥:不可分享
~/.ssh/id_ed25519.pub   公钥:可以部署到服务器或平台

一个密钥应对应可识别的设备或用途。工作、个人托管平台和高权限生产环境不要共用同一私钥。轮换时先添加新公钥并验证,再移除旧公钥,避免锁在服务器外。

部署公钥

Linux/macOS 客户端可使用:

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server.example.com

如果没有 ssh-copy-id,通过可信渠道把 .pub 文件的一整行追加到远端用户的 ~/.ssh/authorized_keys。服务端权限通常应为:

chmod 700 ~/.ssh
chmod 600 ~/.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 的限制选项

服务端可以在单个公钥前限制来源、命令和转发能力:

from="203.0.113.0/24",restrict ssh-ed25519 AAAAC3... backup-laptop

自动备份账户可以使用 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

之后只需:

ssh dev-api
scp report.txt dev-api:/tmp/

配置从上到下累计匹配,较具体规则通常放在前面,通用默认放在最后:

Host *.corp.example.com
    User deploy
    IdentityFile ~/.ssh/id_ed25519_work
    IdentitiesOnly yes

Host *
    AddKeysToAgent yes
    ServerAliveInterval 30
    ServerAliveCountMax 3

查看某个别名最终生效的配置是排错利器:

ssh -G dev-api | less
ssh -G dev-api | grep -E '^(hostname|user|port|identityfile|proxyjump) '

高频参数:

参数 作用
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 提供交互式文件操作:

sftp dev-api
sftp> pwd
sftp> lpwd
sftp> ls
sftp> put report.txt
sftp> get app.log
sftp> exit

大量目录同步优先使用 rsync,它只传输差异并可保留属性:

rsync -av --progress ./dist/ dev-api:/srv/app/
rsync -av --delete ./dist/ dev-api:/srv/app/

--delete 会删除目标端多余文件,先加 --dry-run 预览。传输敏感数据前确认目标主机和路径,避免通配符在错误一端展开。

7. 跳板机与内网访问

生产主机通常没有公网入口,只允许通过堡垒机进入。ProxyJump 让客户端端到端连接目标主机,无需在跳板机保存私钥:

ssh -J bastion.example.com ubuntu@10.0.1.20

长期配置:

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
ssh prod-api
scp config.yaml prod-api:/tmp/

目标主机指纹仍记录在本地 known_hosts。多级跳转可以用逗号连接,但链路越长越应依赖组织统一的访问控制、短期证书和审计,而不是无限扩展个人配置。

8. 端口转发:安全地访问受限服务

端口转发把 TCP 连接放进 SSH 加密通道。使用前先明确“监听在哪一端、最终目标从哪台机器访问”。

本地转发 -L

把远端网络可见的服务映射到本地。访问内网 PostgreSQL:

ssh -N -L 127.0.0.1:5433:db.internal:5432 bastion

随后本地程序连接 127.0.0.1:5433。参数含义是:

本地监听地址:本地端口:远端可见的目标主机:目标端口

使用 127.0.0.1 限制只有本机能访问,不要无意绑定 0.0.0.0

远程转发 -R

让远端一侧访问本地服务:

ssh -N -R 127.0.0.1:9000:127.0.0.1:3000 server

远端访问 127.0.0.1:9000 会被转到本地 3000 端口。服务端是否允许以及能否绑定非回环地址由 sshd_config 控制。不要用它绕过组织网络政策。

动态转发 -D

ssh -N -D 127.0.0.1:1080 bastion

这会创建 SOCKS 代理,由客户端决定目标地址。它适合经过授权的调试,不等同于通用 VPN,也不保证应用的所有 DNS 查询都进入隧道。

常用控制参数:

ssh -N -T \
  -o ExitOnForwardFailure=yes \
  -L 127.0.0.1:8080:internal-web:80 \
  bastion

-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.nameuser.email 决定,两者要分别配置:

git config user.name 'Your Name'
git config user.email 'you@company.example'

10. 服务端安全加固

加固前确认云控制台或另一个已登录会话可用于恢复。先测试配置,再平滑重载:

sudo sshd -t
sudo systemctl reload sshd

不同发行版可能使用 /etc/ssh/sshd_configsshd_config.d/。核心原则如下:

PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
MaxAuthTries 3
AllowUsers deploy ops

只有在所有管理员都验证过密钥登录并有恢复通道后,才能关闭密码认证。修改端口只能减少扫描噪声,不是安全边界。真正的保护来自密钥或短期证书、最小用户权限、防火墙、补丁、速率限制和审计。

私钥不应部署到服务器;自动化系统使用专用身份、有限权限和可轮换凭据。离职、设备丢失或权限变化时及时移除对应公钥并检查日志。

sudo journalctl -u sshd --since '1 hour ago'
sudo journalctl -u ssh --since '1 hour ago'
last -a

11. 分层排错方法

SSH 连接失败时按照“名称解析 → TCP → 主机身份 → 用户认证 → 会话”的顺序检查,不要一开始就重建密钥。

名称和端口

getent hosts server.example.com
nc -vz server.example.com 22
  • Connection timed out:常见于路由、防火墙、安全组或地址错误。
  • Connection refused:目标可达,但对应端口没有服务监听或端口写错。

查看详细握手过程

ssh -vvv dev-api

关注这些阶段:

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 日志。公钥放在错误用户家目录、私钥未被实际选中、家目录权限过宽都是高频原因。

连接经常断开

配置客户端保活:

Host unstable-server
    ServerAliveInterval 30
    ServerAliveCountMax 3

这不能修复网络问题,只能更快发现失效连接并通过空闲设备。长任务应在远端使用 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. 延伸阅读