7991 字
40 分钟

SSH 完全指南:从远程连接到安全加固 | 2026 最新实践

SSH(Secure Shell)是 Linux 服务器管理的基础工具——每次远程登录、文件传输、端口转发都离不开它。但大多数人的 SSH 使用停留在 ssh user@host 的层面,远未发挥其完整能力。

本文从零开始,系统讲解 SSH 的完整知识体系,包括:

  • SSH 基础连接与密钥认证
  • SSH 配置文件优化(告别每次输密码和 IP)
  • 端口转发与 SSH 隧道(本地/远程/动态三种模式)
  • SSH Agent 与密钥管理
  • 跳板机 ProxyJump 多级跳转
  • 安全加固最佳实践
  • rsync 远程文件同步
  • 常见错误排查与解决方案

1. SSH 基础连接#

1.1 安装与验证#

大多数 Linux/macOS 系统已内置 OpenSSH。Windows 10+ 也原生支持 SSH。

Terminal window
# 检查 SSH 客户端版本
ssh -V
# OpenSSH_9.8p1, OpenSSL 3.0.14 ...
# Ubuntu/Debian 安装(如未安装)
sudo apt update && sudo apt install openssh-client openssh-server
# CentOS/RHEL 安装
sudo yum install openssh-clients openssh-server
# Windows:PowerShell 安装
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

1.2 基本连接命令#

Terminal window
# 最简单的连接(默认端口 22)
ssh user@hostname
# 指定端口
ssh -p 2222 user@hostname
# 使用 IP 地址
ssh root@192.168.1.100
# 使用域名
ssh admin@server.example.com
# 首次连接会提示确认指纹
# The authenticity of host '192.168.1.100 (192.168.1.100)' can't be established.
# ED25519 key fingerprint is SHA256:abc123...
# Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

⚠️ 首次连接务必验证指纹:输入 yes 前应与服务器管理员确认指纹是否正确。如果指纹与预期不符,可能是中间人攻击。

1.3 SSH 连接流程#

SSH 连接的完整流程如下:

客户端 服务器
│ │
│── TCP 连接(端口 22)───────→│
│ │
│── 版本协商 ─────────────────→│ (交换 SSH 协议版本)
│←── 版本响应 ────────────────│
│ │
│── 密钥交换 ─────────────────→│ (Diffie-Hellman / ECDH)
│←── 密钥交换响应 ────────────│ (生成会话密钥)
│ │
│── 加密通信开始 ─────────────→│ (所有后续通信加密)
│ │
│── 用户认证请求 ─────────────→│ (密钥认证或密码认证)
│←── 认证结果 ────────────────│
│ │
│── 请求 Shell/命令执行 ──────→│
│←── Shell 会话/命令输出 ─────│

1.4 执行远程命令(不进入 Shell)#

Terminal window
# 执行单个命令后自动断开
ssh user@host "uname -a"
# 执行多条命令
ssh user@host "uptime; free -h; df -h"
# 执行带管道的命令
ssh user@host "ps aux | grep nginx | head -5"
# 执行本地脚本(通过 stdin 传递)
ssh user@host "bash -s" < /local/script.sh
# 使用 sudo 执行(需要 -t 分配伪终端)
ssh -t user@host "sudo systemctl restart nginx"

2. SSH 密钥认证#

密码认证是 SSH 最不安全的方式。密钥认证使用非对称加密,私钥保存在本地,服务器只存公钥——不存在暴力破解风险。

2.1 生成密钥对#

Terminal window
# 推荐:生成 Ed25519 密钥(最安全、最短、最快)
ssh-keygen -t ed25519 -C "your-email@example.com"
# Generating public/private ed25519 key pair.
# Enter file in which to save the key (~/.ssh/id_ed25519): [直接回车使用默认]
# Enter passphrase (empty for no passphrase): [输入密码保护私钥,推荐!]
# Enter same passphrase again:
# 生成 RSA 密钥(兼容性更好,老服务器可能不支持 Ed25519)
ssh-keygen -t rsa -b 4096 -C "your-email@example.com"
# 查看生成的密钥
ls ~/.ssh/
# id_ed25519 ← 私钥(绝不能泄露!)
# id_ed25519.pub ← 公钥(可以放到任何服务器)

密钥类型对比:

特性Ed25519RSA 4096RSA 2048
密钥长度256 bit4096 bit2048 bit
公钥长度68 字符~700 字符~370 字符
安全性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐(不够安全)
性能最快较慢中等
兼容性OpenSSH 6.5+所有版本所有版本
推荐度✅ 首选兼容场景❌ 不推荐

💡 passphrase vs no passphrase:给私钥设置 passphrase 后,每次使用需要输入一次密码(可通过 ssh-agent 缓解)。不设 passphrase 意味着私钥泄露 = 服务器沦陷。生产环境务必设置 passphrase。

2.2 上传公钥到服务器#

Terminal window
# 方法一:ssh-copy-id(推荐,最简单)
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@hostname
# /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "~/.ssh/id_ed25519.pub"
# Number of key(s) added: 1
# Now try logging into the machine, with: "ssh 'user@hostname'"
# and check to make sure that only the key(s) you wanted were added.
# 方法二:手动上传(ssh-copy-id 不可用时)
cat ~/.ssh/id_ed25519.pub | ssh user@host "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
# 方法三:在服务器上手动添加
# 登录服务器后:
echo "ssh-ed25519 AAAAC3Nza... your-email@example.com" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
chmod 700 ~/.ssh

2.3 验证密钥登录#

Terminal window
# 尝试密钥登录(添加 -v 查看详细过程)
ssh -v user@hostname
# 成功时会看到:
# debug1: Offering public key: ~/.ssh/id_ed25519 ED25519 SHA256:...
# debug1: Server accepts key: pkalg ed25519
# debug1: Authentication succeeded (publickey).
# 如果仍然要求密码,检查:
# 1. 服务器 ~/.ssh/authorized_keys 权限是否正确(600)
# 2. 服务器 ~/.ssh/ 目录权限是否正确(700)
# 3. 服务器 sshd_config 是否允许 PubkeyAuthentication

2.4 禁用密码认证(安全加固)#

Terminal window
# 在服务器上编辑 sshd_config
sudo vim /etc/ssh/sshd_config
# 修改以下配置:
PubkeyAuthentication yes # 启用密钥认证
PasswordAuthentication no # 禁用密码认证
ChallengeResponseAuthentication no # 禁用挑战响应
UsePAM no # 禁用 PAM 密码认证
# 重启 SSH 服务
sudo systemctl restart sshd
# ⚠️ 确保密钥登录成功后再禁用密码,否则将无法登录!

3. SSH 配置文件#

每次输入 ssh -p 2222 admin@192.168.1.100 -i ~/.ssh/work_key 太繁琐。SSH 配置文件让你用 ssh myserver 一步到位。

3.1 客户端配置 ~/.ssh/config#

Terminal window
# 创建或编辑配置文件
mkdir -p ~/.ssh && chmod 700 ~/.ssh
touch ~/.ssh/config && chmod 600 ~/.ssh/config

基本配置示例:

# === 通用默认配置 ===
Host *
AddKeysToAgent yes # 自动添加密钥到 ssh-agent
UseKeychain yes # macOS:使用 Keychain 存储 passphrase
ServerAliveInterval 60 # 每 60 秒发送心跳包
ServerAliveCountMax 3 # 3 次无响应断开
ConnectionAttempts 3 # 连接尝试次数
ConnectTimeout 10 # 连接超时 10 秒
# === 工作服务器 ===
Host dev
HostName 192.168.1.100
User developer
Port 2222
IdentityFile ~/.ssh/id_ed25519_work
ForwardAgent yes
Host prod
HostName prod.example.com
User admin
Port 22
IdentityFile ~/.ssh/id_ed25519_prod
# === 个人服务器 ===
Host home-server
HostName home.mydomain.com
User root
Port 2222
# === GitHub ===
Host github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
IdentitiesOnly yes # 只使用指定密钥
# === 多 IP 备用 ===
Host resilient
User admin
# 尝试多个 IP,第一个成功即连接
HostName %h
# OpenSSH 8.6+ 支持 Match exec
# 旧版可通过脚本实现

💡 配置生效顺序:SSH 先匹配 ~/.ssh/config,再匹配 /etc/ssh/ssh_config。Host 匹配是从上到下的,第一个匹配的配置生效——所以通配符 Host * 应放在文件末尾。

使用效果:

Terminal window
# 以前:
ssh -p 2222 developer@192.168.1.100 -i ~/.ssh/id_ed25519_work
# 现在只需:
ssh dev
# 同理:
ssh prod # → admin@prod.example.com:22
ssh home-server # → root@home.mydomain.com:2222

3.2 常用配置参数速查#

参数说明示例值
HostName目标主机地址192.168.1.100
User登录用户名root / admin
PortSSH 端口22 / 2222
IdentityFile指定密钥文件~/.ssh/id_ed25519_work
IdentitiesOnly仅用指定密钥yes
ForwardAgent转发 Agentyes(跳板机场景)
ProxyJump跳板机jump@jumphost
ServerAliveInterval心跳间隔60
ServerAliveCountMax心跳失败上限3
ConnectTimeout连接超时10
LocalForward本地端口转发8080:localhost:80
RemoteForward远程端口转发9090:localhost:8080
StrictHostKeyChecking主机指纹检查accept-new
RequestTTY请求伪终端yes / auto
SetEnv设置环境变量TERM=xterm-256color

3.3 服务器端配置 /etc/ssh/sshd_config#

Terminal window
# === 基础安全配置 ===
Port 22 # 修改默认端口可减少扫描攻击
PermitRootLogin prohibit-password # 允许 root 但仅密钥登录
PubkeyAuthentication yes # 启用密钥认证
PasswordAuthentication no # 禁用密码认证
MaxAuthTries 3 # 最大认证尝试次数
MaxSessions 5 # 最大并发会话
# === 安全增强 ===
AllowUsers admin deploy # 仅允许指定用户登录
AllowGroups ssh-users # 仅允许指定组登录
# DenyUsers root # 禁止指定用户(与 AllowUsers 互斥)
# === 日志与监控 ===
LogLevel VERBOSE # 详细日志
LoginGraceTime 60 # 登录超时 60 秒
SyslogFacility AUTH # 日志类别
# === 性能优化 ===
UseDNS no # 禁用 DNS 反查(加速登录)
TCPKeepAlive yes # TCP 心跳保活
Compression no # 禁用压缩(现代网络不需要)
# === 现代特性(OpenSSH 8.x+)===
# Include /etc/ssh/sshd_config.d/*.conf # 包含子配置

4. SSH 端口转发与隧道#

端口转发是 SSH 最强大也最被低估的功能。它可以在不开放端口的情况下,安全访问内网服务。

4.1 本地端口转发(Local Forwarding)#

场景:远程服务器背后有一个 MySQL 数据库(3306),你不能直接访问,但可以通过 SSH 连接到该服务器。

你的电脑 SSH 服务器 MySQL
│ │ │
│ localhost:3306 │ │
│ ──── SSH 隧道 ──────────→│── localhost:3306 ───→│
│ │ │
│ 访问 localhost:3306 │ │
│ 等于访问远程 MySQL │ │
Terminal window
# 基础命令:-L [本地端口]:[目标主机]:[目标端口] [SSH服务器]
ssh -L 3306:localhost:3306 user@remote-server
# 访问远程服务器背后的其他主机
ssh -L 3306:10.0.0.50:3306 user@remote-server
# remote-server 可以访问 10.0.0.50,但你的电脑不能
# 多个端口转发同时开启
ssh -L 3306:db.internal:3306 \
-L 8080:web.internal:80 \
-L 6379:redis.internal:6379 \
user@remote-server
# 在 config 中配置(推荐)
Host db-tunnel
HostName remote-server
User admin
LocalForward 3306 db.internal:3306
LocalForward 8080 web.internal:80

使用后:

Terminal window
# 连接远程 MySQL,就像连接本地一样
mysql -h 127.0.0.1 -P 3306 -u dbuser -p
# 浏览器访问远程内部网站
curl http://localhost:8080

💡 绑定地址安全:默认本地转发绑定 localhost(仅本机可访问)。使用 -L 0.0.0.0:3306:... 会让所有本网段机器都能访问这个隧道——慎用

4.2 远程端口转发(Remote Forwarding)#

场景:你在本地开发了 Web 服务(8080),想让远程服务器上的同事临时访问。

你的电脑 SSH 服务器 同事
│ │ │
│ localhost:8080 │ remote:9090 │
│ ──── SSH 隧道 ──────────→│── 需开启 GatewayPorts→│
│ │ │
│ 本地服务 8080 │ 通过 remote:9090 访问 │
Terminal window
# 基础命令:-R [远程端口]:[目标主机]:[目标端口] [SSH服务器]
ssh -R 9090:localhost:8080 user@remote-server
# 远程服务器上访问 localhost:9090 即可访问你的本地服务
curl http://localhost:9090
# 让远程其他人也能访问(需服务器开启 GatewayPorts)
# 在远程服务器 sshd_config 中添加:
# GatewayPorts clientspecified
ssh -R 0.0.0.0:9090:localhost:8080 user@remote-server
# 现在任何人都可以通过 remote-server:9090 访问

4.3 动态端口转发(SOCKS 代理)#

场景:你想通过 SSH 服务器安全访问多个远程服务,不想为每个服务都开一条隧道。

Terminal window
# 创建 SOCKS5 代理
ssh -D 1080 user@remote-server
# 配置浏览器/应用使用 SOCKS5 代理
# 代理地址:localhost,端口:1080
# curl 使用 SOCKS 代理
curl --socks5 localhost:1080 http://internal-site.example.com
# 在 config 中配置
Host socks-proxy
HostName remote-server
User admin
DynamicForward 1080

💡 动态转发 = 轻量级 VPN:所有流量都通过 SSH 服务器转发,适合临时安全访问内部资源。比 VPN 更轻量,但性能不如 VPN。

4.4 端口转发三种模式对比#

特性本地转发 (-L)远程转发 (-R)动态转发 (-D)
方向本地→远程→目标远程→本地→目标本地→远程→任意
场景访问远程内网服务让远程访问本地服务SOCKS5 代理
目标固定端口固定端口动态任意端口
安全仅本机可访问仅远程本机可访问仅本机可访问
等价于临时 VPN 规则反向临时 VPN轻量级 VPN
推荐度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

4.5 保持隧道持久化#

Terminal window
# 方法一:autossh(自动重连)
autossh -M 0 -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" \
-N -L 3306:db.internal:3306 user@remote-server
# 方法二:SSH + f 模式(后台运行)
ssh -f -N -L 3306:db.internal:3306 user@remote-server
# -f: 后台运行
# -N: 不执行远程命令(仅做隧道)
# 方法三:systemd 服务(推荐生产环境)
sudo vim /etc/systemd/system/ssh-tunnel.service

systemd 服务配置:

[Unit]
Description=SSH Tunnel to Internal MySQL
After=network.target
[Service]
Type=simple
User=admin
ExecStart=/usr/bin/ssh -N -L 3306:db.internal:3306 admin@remote-server
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Terminal window
# 启用服务
sudo systemctl enable ssh-tunnel
sudo systemctl start ssh-tunnel
sudo systemctl status ssh-tunnel

5. SSH Agent 与密钥管理#

当你有多个密钥且都设置了 passphrase 时,每次连接都要输入密码——ssh-agent 解决这个问题。

5.1 启动 ssh-agent#

Terminal window
# 启动 ssh-agent
eval "$(ssh-agent -s)"
# Agent pid 12345
# macOS:使用 Keychain 集成(推荐)
# 在 ~/.ssh/config 中添加:
# Host *
# UseKeychain yes
# AddKeysToAgent yes
# Linux:在 .bashrc/.zshrc 中自动启动
if [ -z "$SSH_AUTH_SOCK" ]; then
eval "$(ssh-agent -s)"
fi

5.2 添加密钥到 Agent#

Terminal window
# 添加默认密钥
ssh-add
# Enter passphrase for ~/.ssh/id_ed25519: [输入一次即可]
# Identity added: ~/.ssh/id_ed25519 (your-email@example.com)
# 添加指定密钥
ssh-add ~/.ssh/id_ed25519_work
ssh-add ~/.ssh/id_ed25519_github
# 查看已添加的密钥
ssh-add -l
# 256 SHA256:abc123... your-email@example.com (ED25519)
# 256 SHA256:xyz789... work@company.com (ED25519)
# 删除指定密钥
ssh-add -d ~/.ssh/id_ed25519_work
# 删除所有密钥
ssh-add -D
# 锁定 Agent(临时禁用所有密钥)
ssh-add -x
# Enter lock password: [设置锁定密码]
# 解锁 Agent
ssh-add -X
# Enter lock password: [输入锁定密码]

5.3 macOS Keychain 集成#

macOS 的 ssh-agent 可以将 passphrase 存入 Keychain,重启后自动恢复。

Terminal window
# macOS 自动存储 passphrase 到 Keychain
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
# 从 Keychain 中删除
ssh-add --apple-delete-keychain ~/.ssh/id_ed25519
# 配置文件中启用(推荐)
# ~/.ssh/config:
Host *
UseKeychain yes
AddKeysToAgent yes

5.4 Agent Forwarding(转发 Agent)#

场景:你通过跳板机连接目标服务器,想在目标服务器上用 git pull——但目标服务器没有你的 GitHub 密钥。

Terminal window
# 方法一:命令行启用
ssh -A user@jumphost
# 然后在跳板机上:
git pull # 可以使用你本地的密钥!
# 方法二:配置文件启用
Host jumphost
HostName jump.example.com
ForwardAgent yes
# ⚠️ 安全风险:Agent Forwarding 让跳板机可以"借用"你的密钥
# 如果跳板机不可信,不要启用 ForwardAgent!
# 仅在你信任的跳板机上使用

⚠️ Agent Forwarding 安全警告:跳板机的 root 用户可以访问你转发过去的 Agent socket,理论上可以在你连接期间使用你的密钥认证其他服务器。只在信任的服务器上启用 ForwardAgent。


6. 跳板机与 ProxyJump#

生产环境中,目标服务器通常不直接暴露,需要通过跳板机(Bastion Host)中转。

6.1 ProxyJump(推荐方式)#

OpenSSH 7.3+ 引入 -J 参数,是最简洁的跳板机方式。

Terminal window
# 单跳板机
ssh -J jumpuser@jumphost targetuser@targethost
# 指定端口
ssh -J jumpuser@jumphost:2222 targetuser@targethost:22
# 多级跳板机(链式跳转)
ssh -J jump1@host1,jump2@host2 targetuser@targethost
# 在 config 中配置(推荐)
Host target
HostName target.internal.example.com
User admin
ProxyJump jump@jumphost.example.com
# 然后直接:
ssh target

6.2 ProxyCommand(传统方式)#

OpenSSH 7.3 之前的版本使用 ProxyCommand,语法更复杂。

Terminal window
# 传统方式
ssh -o ProxyCommand="ssh -W %h:%p jumpuser@jumphost" targetuser@targethost
# config 中配置
Host target
HostName target.internal.example.com
User admin
ProxyCommand ssh -W %h:%p jump@jumphost.example.com

6.3 多跳板机配置示例#

~/.ssh/config
# === 一级跳板:DMZ ===
Host dmz
HostName dmz.example.com
User jump
Port 22
IdentityFile ~/.ssh/id_ed25519_dmz
# === 二级跳板:内网入口 ===
Host internal-jump
HostName 10.0.1.50
User jump
ProxyJump dmz
IdentityFile ~/.ssh/id_ed25519_internal
# === 目标服务器 ===
Host db-server
HostName 10.0.2.100
User admin
ProxyJump internal-jump
IdentityFile ~/.ssh/id_ed25519_db
Host web-server
HostName 10.0.2.200
User deploy
ProxyJump internal-jump
IdentityFile ~/.ssh/id_ed25519_web
Terminal window
# 使用效果
ssh db-server # → dmz → internal-jump → db-server
ssh web-server # → dmz → internal-jump → web-server

💡 ProxyJump vs ProxyCommand:ProxyJump 更简洁(-J 一行搞定),ProxyCommand 更灵活(可以嵌入自定义脚本)。95% 的场景用 ProxyJump 即可。

6.4 SCP/SFTP 通过跳板机传文件#

Terminal window
# SCP 通过跳板机
scp -J jump@jumphost localfile.txt target@targethost:/remote/path/
# SCP 多级跳板
scp -J jump1@host1,jump2@host2 localfile.txt target@targethost:/remote/path/
# rsync 通过跳板机(使用 SSH config)
rsync -avz --progress localdir/ db-server:/remote/dir/
# rsync 会自动使用 config 中的 ProxyJump 配置

7. SSH 安全加固#

7.1 禁用密码认证#

这是最重要的安全措施——99% 的 SSH 攻击都是密码暴力破解。

Terminal window
# 编辑 /etc/ssh/sshd_config
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM no # 禁用 PAM(否则密码认证可能仍生效)
# 重启服务
sudo systemctl restart sshd
# 验证密码登录已被禁用
ssh -o PubkeyAuthentication=no user@host
# 应该立即拒绝:Permission denied (publickey).

7.2 修改默认端口#

修改端口可以减少 95% 的自动化扫描攻击(但无法防止针对性攻击)。

Terminal window
# sshd_config
Port 2222 # 或其他非标准端口
# 客户端连接需要指定端口
ssh -p 2222 user@host
# 或在 ~/.ssh/config 中配置
Host myserver
HostName server.example.com
Port 2222

7.3 禁止 root 直接登录#

Terminal window
# sshd_config
PermitRootLogin prohibit-password # 允许 root 密钥登录,禁止密码登录
# 或完全禁止:
PermitRootLogin no # root 必须先普通用户登录再 su/sudo

7.4 限制登录用户和来源#

Terminal window
# 仅允许指定用户
AllowUsers admin deploy@10.0.0.* # deploy 仅允许从 10.0.0 网段登录
# 仅允许指定组
AllowGroups ssh-users admin-group
# 使用 Match 条件精细化控制
Match Address 192.168.1.*
PasswordAuthentication yes # 内网允许密码登录
Match Address *,!192.168.1.*
PasswordAuthentication no # 外网仅密钥登录

7.5 fail2ban 防暴力破解#

Terminal window
# 安装 fail2ban
sudo apt install fail2ban # Ubuntu/Debian
sudo yum install fail2ban # CentOS/RHEL
# 创建 SSH 专用配置
sudo vim /etc/fail2ban/jail.d/sshd.local
[sshd]
enabled = true
port = 22
filter = sshd
logpath = /var/log/auth.log
maxretry = 3 # 3 次失败后封禁
findtime = 600 # 10 分钟内
bantime = 3600 # 封禁 1 小时
# 逐渐加重:3600 → 86400 → 604800
Terminal window
# 启动 fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# 查看封禁状态
sudo fail2ban-client status sshd
# 手动解封
sudo fail2ban-client set sshd unbanip 192.168.1.50

7.6 双因素认证(2FA)#

Terminal window
# 安装 libpam-google-authenticator
sudo apt install libpam-google-authenticator
# 为用户生成 OTP 密钥
google-authenticator
# 会显示 QR 码,用 Authenticator App 扫码
# 配置 sshd 使用 2FA
sudo vim /etc/pam.d/sshd
# 添加:
# auth required pam_google_authenticator.so nullok
sudo vim /etc/ssh/sshd_config
# 修改:
# ChallengeResponseAuthentication yes
# AuthenticationMethods publickey,keyboard-interactive:pam
# (密钥 + OTP 双因素)

7.7 安全配置速查表#

措施安全等级实施难度说明
禁用密码认证⭐⭐⭐⭐⭐最重要,防暴力破解
密钥 passphrase⭐⭐⭐⭐⭐私钥泄露仍需密码
Ed25519 密钥⭐⭐⭐⭐⭐比 RSA 更安全更快
修改默认端口⭐⭐⭐⭐减少扫描,非根本解决
fail2ban⭐⭐⭐⭐自动封禁暴力破解 IP
禁止 root 登录⭐⭐⭐⭐减少攻击面
AllowUsers 限制⭐⭐⭐⭐仅允许必要用户
2FA 双因素⭐⭐⭐⭐⭐密钥+OTP双重保护
跳板机架构⭐⭐⭐⭐⭐生产环境标准架构

8. SCP 与 SFTP 文件传输#

8.1 SCP(简单复制)#

Terminal window
# 本地→远程
scp localfile.txt user@host:/remote/path/
scp -r localdir/ user@host:/remote/path/ # 递归复制目录
# 远程→本地
scp user@host:/remote/file.txt ./localpath/
scp -r user@host:/remote/dir/ ./localpath/
# 指定端口
scp -P 2222 file.txt user@host:/remote/path/ # 注意是大写 -P!
# 通过跳板机
scp -J jump@jumphost file.txt target@target:/path/

8.2 SFTP(交互式传输)#

Terminal window
# 连接 SFTP
sftp user@host
# 常用命令
sftp> pwd # 查看远程当前目录
sftp> ls # 列出远程文件
sftp> cd /remote/path # 切换远程目录
sftp> lpwd # 查看本地当前目录
sftp> lls # 列出本地文件
sftp> lcd /local/path # 切换本地目录
sftp> put localfile.txt # 上传文件
sftp> get remotefile.txt # 下载文件
sftp> put -r localdir/ # 上传目录
sftp> get -r remotedir/ # 下载目录
sftp> mkdir newdir # 创建远程目录
sftp> rm file.txt # 删除远程文件
sftp> exit # 退出

8.3 SCP vs SFTP vs rsync 对比#

特性SCPSFTPrsync
用途快速复制交互式传输增量同步
增量传输✅(仅传输差异部分)
断点续传
压缩✅(-C)✅(-z)
交互式
大文件慢(全量)✅推荐
镜像同步
适用场景小文件快速复制手动浏览传输大文件/定期同步

💡 生产环境推荐 rsync:大文件传输、增量同步、断点续传都用 rsync。SCP 适合小文件快速复制。


9. rsync 远程同步#

rsync 是最高效的远程文件同步工具——只传输文件的差异部分,节省大量带宽和时间。

9.1 基础用法#

Terminal window
# 本地→远程同步
rsync -avz /local/dir/ user@host:/remote/dir/
# 远程→本地同步
rsync -avz user@host:/remote/dir/ /local/dir/
# 参数说明:
# -a: archive(保留权限/时间/符号链接等,等于 -rlptgoD)
# -v: verbose(显示过程)
# -z: compress(传输时压缩)
# --progress: 显示进度
# --delete: 删除目标中源没有的文件(完全镜像)

9.2 重要注意事项:斜杠的含义#

Terminal window
# ⚠️ 源路径末尾斜杠的含义不同!
# 有斜杠:复制目录内容到目标
rsync -avz /source/dir/ /target/ # /target/ 包含 dir/ 的内容
# 无斜杠:复制整个目录到目标下
rsync -avz /source/dir /target/ # /target/dir/ 包含 dir/ 的内容

⚠️ 这是 rsync 最容易犯的错/source/dir//source/dir 的行为完全不同。建议始终在源路径末尾加斜杠,行为更直观。

9.3 排除与包含#

Terminal window
# 排除文件
rsync -avz --exclude='*.log' --exclude='node_modules' \
/source/ user@host:/target/
# 排除列表文件
rsync -avz --exclude-from='/path/to/exclude-list.txt' /source/ user@host:/target/
# exclude-list.txt 内容:
# *.log
# node_modules/
# .git/
# __pycache__/
# *.pyc
# 包含优先级(先包含再排除)
rsync -avz --include='*.py' --exclude='*' /source/ user@host:/target/
# 仅同步 .py 文件

9.4 增量备份策略#

Terminal window
# 使用 --link-dest 创建硬链接备份(节省空间)
rsync -avz --delete --link-dest=/backup/2026-07-01/ \
/source/ /backup/2026-07-02/
# 效果:
# - 未修改的文件 → 硬链接到昨天的备份(不占额外空间)
# - 已修改的文件 → 新副本
# - 删除的文件 → 仅在旧备份中保留
# 自动化每日备份脚本
#!/bin/bash
DATE=$(date +%Y-%m-%d)
YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
rsync -avz --delete \
--link-dest=/backup/$YESTERDAY/ \
/data/ /backup/$DATE/

9.5 大文件传输优化#

Terminal window
# 限速传输(避免占满带宽)
rsync -avz --bwlimit=1000 /source/ user@host:/target/
# 1000 = 1000 KB/s ≈ 1 MB/s
# 部分传输续传
rsync -avz --partial --partial-dir=.rsync-partial /source/ user@host:/target/
# 显示详细进度
rsync -avz --progress --stats /source/ user@host:/target/
# 仅检查大小和修改时间(快速模式)
rsync -avz --size-only /source/ user@host:/target/
# 不校验文件内容,仅通过大小和mtime判断是否需要传输

9.6 通过 SSH config 使用 rsync#

Terminal window
# rsync 自动使用 SSH config 配置
# 如果你的 config 中有:
Host db-server
HostName 10.0.2.100
User admin
ProxyJump dmz,internal-jump
# 直接使用:
rsync -avz /local/data/ db-server:/remote/data/
# rsync 会自动走跳板机链路

10. SSH 高级技巧#

10.1 SSH Multiplexing(连接复用)#

每次 SSH 连接都要建立加密通道,耗时约 1-2 秒。Multiplexing 让后续连接复用已有通道,几乎零延迟。

~/.ssh/config
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600 # 最后一个会话关闭后保持 600 秒
# 创建 socket 目录
mkdir -p ~/.ssh/sockets
# 第一次连接:正常建立(约 1-2 秒)
ssh dev
# 第二次连接:复用通道(约 0.05 秒)
ssh dev # 几乎瞬间连接!
# 查看已有连接
ssh -S ~/.ssh/sockets/admin@dev-2222 -O check dev
# Master running (pid=12345)
# 强制关闭所有连接
ssh -S ~/.ssh/sockets/admin@dev-2222 -O exit dev

💡 ControlPersist 600:即使你退出 SSH 会话,连接通道仍保持 10 分钟。10 分钟内再次连接几乎是瞬间。这对于 rsync、scp、git 等频繁短连接场景尤其有用。

10.2 SSH 会话保活(防断开)#

Terminal window
# 客户端配置
Host *
ServerAliveInterval 60 # 每 60 秒发送心跳
ServerAliveCountMax 3 # 3 次无响应断开(即 180 秒)
TCPKeepAlive yes # TCP 层保活
# 服务器端配置
ClientAliveInterval 60
ClientAliveCountMax 3
# 使用 mosh 替代 SSH(不稳定网络推荐)
# mosh 基于 UDP,自动重连,漫游切换网络
mosh user@host
# 安装 mosh
sudo apt install mosh # Ubuntu/Debian
brew install mosh # macOS
# 服务器也需要安装 mosh

10.3 SSH 证书登录(大规模环境)#

在管理数十台服务器时,逐台部署公钥很繁琐。SSH 证书可以一次签发,所有信任该 CA 的服务器都接受。

Terminal window
# 1. 创建 CA 密钥
ssh-keygen -t ed25519 -f ~/.ssh/ca_key -C "SSH CA"
# 2. 签发用户证书(有效期 1 天)
ssh-keygen -s ~/.ssh/ca_key -I "admin-2026-07-02" \
-V +1d -n admin,deploy \
~/.ssh/id_ed25519.pub
# 生成 ~/.ssh/id_ed25519-cert.pub
# 3. 服务器信任 CA 公钥
# 在每台服务器 /etc/ssh/sshd_config 中:
TrustedUserCAKeys /etc/ssh/ca_pub_key
# 将 ca_key.pub 内容写入 /etc/ssh/ca_pub_key
# 4. 客户端自动使用证书
# SSH 自动查找 id_ed25519-cert.pub 与 id_ed25519 配对
ssh admin@server # 使用证书认证,无需部署公钥

10.4 使用 Match 条件精细化配置#

Terminal window
# /etc/ssh/sshd_config 中的 Match 规则
# 内网允许密码,外网仅密钥
Match Address 192.168.0.0/16,10.0.0.0/8,172.16.0.0/12
PasswordAuthentication yes
Match Address *,!192.168.0.0/16,!10.0.0.0/8
PasswordAuthentication no
# 特定用户限制
Match User deploy
AllowTcpForwarding no # 禁止端口转发
X11Forwarding no # 禁止 X11 转发
PermitTTY yes
ForceCommand /usr/bin/rrsync /data/deploy/ # 强制命令(仅允许 rsync)
# 特定时间限制(OpenSSH 8.2+)
# 注意:Match 不支持时间条件,需通过 cron 调整 sshd 配置实现

10.5 SSH Reverse Shell(应急访问)#

当需要从服务器反向连接到你的电脑(服务器在你防火墙后面时):

Terminal window
# 在你的电脑上监听
nc -lvnp 4444
# 在远程服务器上反向连接(通过 SSH 隧道更安全)
ssh -R 2222:localhost:22 user@your-computer
# 然后你通过 localhost:2222 连回远程服务器

⚠️ Reverse Shell 仅用于应急,生产环境应使用 VPN 或跳板机架构。

10.6 SSH 隐藏服务(Tor)#

Terminal window
# 通过 Tor 连接 SSH(隐私最大化)
# 需要安装 tor 和 nc/torsocks
# 方法一:ProxyCommand + tor
ssh -o ProxyCommand="torsocks nc %h %p" user@onion-address.onion
# 方法二:SOCKS 代理
ssh -o ProxyCommand="nc -X 5 -x localhost:9050 %h %p" user@onion-address.onion

11. Windows SSH 使用指南#

11.1 Windows 原生 SSH#

Terminal window
# 检查 OpenSSH 是否安装
Get-WindowsCapability -Online | Where-Object Name -like "OpenSSH*"
# 安装 SSH 客户端
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
# 安装 SSH 服务器
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
# 启动 SSH 服务器
Start-Service sshd
Set-Service -Name sshd -StartupType Automatic
# Windows SSH 配置文件位置
# 客户端:C:\Users\你的用户名\.ssh\config
# 服务器:C:\ProgramData\ssh\sshd_config

11.2 Windows SSH 密钥#

Terminal window
# 生成密钥
ssh-keygen -t ed25519 -C "your-email@example.com"
# 默认保存到 C:\Users\你的用户名\.ssh\id_ed25519
# 上传公钥到 Linux 服务器
type C:\Users\你的用户名\.ssh\id_ed25519.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
# Windows ssh-agent 服务
Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
ssh-add C:\Users\你的用户名\.ssh\id_ed25519

11.3 Windows sshd 特殊配置#

C:\ProgramData\ssh\sshd_config
# Windows SSH 服务器配置差异
# 默认 Shell 改为 PowerShell
# 在注册表中设置:
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force
# Windows 用户密钥认证位置
# 普通用户:C:\Users\用户名\.ssh\authorized_keys
# 管理员组:C:\ProgramData\ssh\administrators_authorized_keys
# ⚠️ 管理员不使用用户目录下的 authorized_keys!
# 设置 administrators_authorized_keys 权限
$acl = Get-Acl C:\ProgramData\ssh\administrators_authorized_keys
$acl.SetSecurityDescriptorSddlForm("D:(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)")
Set-Acl C:\ProgramData\ssh\administrators_authorized_keys $acl

12. 常见错误排查#

12.1 Permission denied (publickey)#

最常见错误,通常是权限问题。

Terminal window
# 1. 检查服务器端权限
# authorized_keys 必须是 600
ls -la ~/.ssh/authorized_keys
# -rw------- 1 user user ... ← 正确
# 如果权限不对:
chmod 600 ~/.ssh/authorized_keys
chmod 700 ~/.ssh/
# 2. 检查服务器 sshd_config
grep PubkeyAuthentication /etc/ssh/sshd_config
# PubkeyAuthentication yes ← 必须是 yes
# 3. 检查服务器 authorized_keys 内容
# 公钥必须完整(一行一个密钥)
cat ~/.ssh/authorized_keys
# 4. 检查客户端密钥文件权限
ls -la ~/.ssh/id_ed25519
# -rw------- 1 user user ... ← 私钥必须是 600
chmod 600 ~/.ssh/id_ed25519
# 5. 调试模式连接
ssh -vvv user@host
# 查看详细的认证过程,定位失败原因

12.2 Connection refused#

Terminal window
# 1. SSH 服务是否运行
sudo systemctl status sshd
sudo systemctl start sshd
# 2. 端口是否监听
sudo ss -tlnp | grep :22
# LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=12345))
# 3. 防火墙是否放行
sudo ufw status # Ubuntu
sudo ufw allow 22/tcp
sudo firewall-cmd --list-all # CentOS
sudo firewall-cmd --add-service=ssh --permanent
sudo firewall-cmd --reload
# 4. 端口是否被修改
grep Port /etc/ssh/sshd_config
# Port 2222 ← 可能不是 22

12.3 Connection timed out#

Terminal window
# 1. 网络是否可达
ping server.example.com
traceroute server.example.com
# 2. 端口是否可达
nc -zv server.example.com 22 -w 5
# Connection to server.example.com 22 port [tcp/ssh] succeeded!
# 3. DNS 解析是否正确
dig server.example.com
nslookup server.example.com
# 4. 客户端增加超时
ssh -o ConnectTimeout=30 user@host

12.4 Host key verification failed#

Terminal window
# 服务器指纹变更(可能是重装系统、IP变更或中间人攻击)
# 1. 确认是合法变更(与管理员确认)
# 2. 删除旧的 known_hosts 条目
ssh-keygen -R server.example.com
# 或手动编辑 ~/.ssh/known_hosts 删除对应行
# 3. 重新连接(会提示新的指纹)
ssh user@server.example.com
# 首次连接自动接受新指纹(慎用)
ssh -o StrictHostKeyChecking=accept-new user@host
# accept-new: 仅对新主机自动接受,已知主机变更仍警告
# 完全跳过指纹检查(危险!仅临时调试用)
ssh -o StrictHostKeyChecking=no user@host

12.5 SSH 连接缓慢#

Terminal window
# 原因一:DNS 反向解析
# sshd 尝试反查客户端 IP 的域名,可能超时
# 解决:服务器 sshd_config
UseDNS no
# 原因二:GSSAPI 认证尝试
# 客户端默认尝试 GSSAPI,失败后才用密钥
# 解决:客户端 ~/.ssh/config
GSSAPIAuthentication no
# 原因三:多个密钥逐一尝试
# SSH 会尝试所有密钥,直到成功或全部失败
# 解决:指定密钥
IdentitiesOnly yes
IdentityFile ~/.ssh/id_ed25519
# 综合加速配置
Host *
GSSAPIAuthentication no
Compression no
ConnectTimeout 5
IdentitiesOnly yes

12.6 断开连接后任务中断#

Terminal window
# 方法一:nohup(最简单)
nohup python train.py > train.log 2>&1 &
# SSH 断开后进程继续运行
# 方法二:tmux/screen(推荐)
tmux new -s work
python train.py
# Ctrl+B D 分离会话(任务继续运行)
# 重新连接:
tmux attach -t work
# 方法三:disown(临时补救)
python train.py &
disown -h %1 # 从 Shell 中脱离,SSH 断开不影响

12.7 错误排查速查表#

错误常见原因解决方法
Permission denied (publickey)权限错误/密钥未部署chmod 600/700,ssh-copy-id
Connection refusedsshd 未运行/防火墙systemctl start sshd,ufw allow
Connection timed out网络不通/端口错误ping/traceroute,检查端口
Host key verification failed指纹变更ssh-keygen -R,确认后重连
Connection slowDNS 反查/GSSAPIUseDNS no,GSSAPIAuthentication no
Broken pipe网络断开ServerAliveInterval,tmux
Too many authentication failures多密钥逐一尝试IdentitiesOnly yes
no matching key exchange method客户端/服务器版本差异升级 OpenSSH 或指定算法

13. SSH 实用配置模板#

13.1 完整 ~/.ssh/config 模板#

# ======================================================
# SSH 配置文件 - 2026 最佳实践
# ======================================================
# === 通用默认配置(放在文件末尾)===
Host *
# 连接优化
ServerAliveInterval 60
ServerAliveCountMax 3
ConnectTimeout 10
ConnectionAttempts 2
GSSAPIAuthentication no
IdentitiesOnly yes
# macOS Keychain
UseKeychain yes
AddKeysToAgent yes
# Multiplexing(连接复用)
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# 安全
StrictHostKeyChecking accept-new
# === GitHub ===
Host github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
IdentitiesOnly yes
Host github.com-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_work_github
IdentitiesOnly yes
# === 工作环境 ===
Host dev
HostName dev.internal.company.com
User developer
Port 2222
IdentityFile ~/.ssh/id_ed25519_work
ForwardAgent yes
Host prod
HostName prod.company.com
User admin
IdentityFile ~/.ssh/id_ed25519_prod
RequestTTY auto
# === 跳板机 ===
Host dmz
HostName dmz.company.com
User jump
IdentityFile ~/.ssh/id_ed25519_dmz
Host internal-*
ProxyJump dmz
IdentityFile ~/.ssh/id_ed25519_internal
Host db-server
HostName 10.0.2.100
User dbadmin
ProxyJump dmz
LocalForward 3306 localhost:3306
Host web-server
HostName 10.0.2.200
User deploy
ProxyJump dmz
# === 个人服务器 ===
Host home-nas
HostName nas.myhome.com
User admin
Port 2222
IdentityFile ~/.ssh/id_ed25519_home
# === 临时调试隧道 ===
Host db-tunnel
HostName prod.company.com
User admin
LocalForward 3306 db.internal:3306
LocalForward 6379 redis.internal:6379
RequestTTY no

13.2 生产级 sshd_config 模板#

Terminal window
# /etc/ssh/sshd_config - 生产环境安全加固版
# === 基础配置 ===
Port 22 # 可改为非标准端口
Protocol 2 # 仅 SSH v2(v1 已废弃)
AddressFamily any # IPv4+IPv6
# === 认证配置 ===
PermitRootLogin prohibit-password # root 仅密钥登录
PubkeyAuthentication yes # 启用密钥认证
PasswordAuthentication no # 禁用密码认证
ChallengeResponseAuthentication no
MaxAuthTries 3 # 最多 3 次尝试
MaxSessions 5 # 最多 5 个并发会话
LoginGraceTime 60 # 60 秒内必须完成认证
# === 用户限制 ===
AllowUsers admin deploy # 仅允许指定用户
# === 安全配置 ===
StrictModes yes # 严格检查权限
PermitEmptyPasswords no # 禁止空密码
HostbasedAuthentication no # 禁止主机认证
IgnoreRhosts yes # 忽略 .rhosts
X11Forwarding no # 禁止 X11 转发(除非需要)
AllowTcpForwarding no # 禁止端口转发(除非需要)
PermitTunnel no # 禁止隧道
AllowAgentForwarding no # 禁止 Agent 转发(除非需要)
# === 日志 ===
LogLevel VERBOSE # 详细日志
SyslogFacility AUTH
# === 性能 ===
UseDNS no # 禁用 DNS 反查
TCPKeepAlive yes
Compression no # 禁用压缩
# === 现代算法(OpenSSH 9.x+)===
# KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256
# Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
# MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
# === Match 规则(条件配置)===
Match Address 192.168.0.0/16,10.0.0.0/8
PasswordAuthentication yes # 内网允许密码(临时维护用)
AllowTcpForwarding yes # 内网允许端口转发
Match User deploy
AllowTcpForwarding yes # deploy 用户允许端口转发
ForceCommand /usr/bin/rrsync /data/deploy/ # 仅允许 rsync

14. 最佳实践清单#

14.1 10 条核心最佳实践#

  1. ✅ 使用 Ed25519 密钥——比 RSA 更安全更短更快
  2. ✅ 设置 passphrase——私钥泄露仍需密码保护
  3. ✅ 禁用密码认证——99% 的 SSH 攻击是暴力破解密码
  4. ✅ 配置 ~/.ssh/config——别名连接,告别每次输 IP 和端口
  5. ✅ 启用 ServerAliveInterval——60 秒心跳,防止连接断开
  6. ✅ 使用 ControlMaster——连接复用,第二次连接零延迟
  7. ✅ 跳板机架构 + ProxyJump——生产环境标准,内网服务器不直接暴露
  8. ✅ rsync 替代 SCP——增量传输、断点续传、带宽控制
  9. ✅ 安装 fail2ban——自动封禁暴力破解 IP
  10. ✅ 定期轮换密钥——建议每 90-180 天更换密钥对

14.2 不同场景的推荐配置#

场景推荐配置
个人开发服务器密钥认证 + config 别名 + ServerAlive
团队共享服务器密钥认证 + AllowUsers + fail2ban + 禁用 root
生产环境跳板机 + 2FA + 禁密码 + 禁转发 + 日志
临时调试远程端口转发 + tmux + 限时认证
大规模服务器群SSH 证书认证 + Puppet/Ansible 批量部署

总结#

SSH 是 Linux 服务器管理的基石,但大多数人的使用仅停留在基础连接层面。掌握以下关键能力可以让你的 SSH 使用效率和安全等级大幅提升:

能力提升效果
密钥认证消除暴力破解风险,登录更便捷
SSH config告别每次输 IP/端口/用户名
端口转发安全访问内网服务,无需 VPN
ProxyJump多级跳板机一步直达目标
ControlMaster连接复用,几乎零延迟
rsync高效增量同步,节省带宽和时间
安全加固禁密码 + fail2ban + 限制用户

核心规律:

  • 密钥认证 > 密码认证(永远)
  • config 别名 > 命令行参数(效率)
  • rsync > SCP > SFTP(大文件)
  • ProxyJump > ProxyCommand(简洁)
  • 跳板机 > 直接暴露(安全)

推荐阅读#

SSH 完全指南:从远程连接到安全加固 | 2026 最新实践
https://971918.xyz/posts/docs/ssh-complete-guide/
作者
九所长
发布于
2026-07-02
许可协议
CC BY-NC-SA 4.0