SSH 不只是登录:tunnel / agent / 跳板机 / config 模板
这是 Linux 系列的第 20 篇——网络章节收尾。前面讲了"网络怎么通",这一篇讲怎么用 SSH 这一个工具把你和服务器之间的一切都打通。
0. SSH 的本质:一条加密的字节流通道
你以为 SSH 只是个 telnet 升级版?它实际是一种多路复用的加密通道协议:
- 默认在通道里跑一个 PTY(伪终端)→ 这就是"登录"
- 也能在通道里跑任意 TCP 端口 → "tunnel"
- 也能跑 X11 协议 → 远程图形界面
- 也能跑 socket 转发 → "agent forwarding"
理解这一点,下面所有"花式用法"都是同一通道的不同切片。
1. 基础 + ~/.ssh/config
最基本:
$ ssh user@server
$ ssh -p 22022 user@server
$ ssh -i ~/.ssh/my_key user@server
敲多了非常累。~/.ssh/config 给每台机器起别名:
# ~/.ssh/config
Host openclaw
HostName 101.37.211.203
User root
Port 22
IdentityFile ~/.ssh/id_openclaw
Host web-prod
HostName web.example.com
User deploy
Port 22022
IdentityFile ~/.ssh/deploy_key
# 通配符
Host *.internal
User admin
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
# 全局默认
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
配完:
$ ssh openclaw # 等价于 ssh -p 22 -i ~/.ssh/id_openclaw [email protected]
$ ssh web-prod
$ ssh db.internal # 匹配 *.internal
scp / rsync 也认这些别名:
$ scp file openclaw:/tmp/
$ rsync -av ./ web-prod:/var/www/
强烈建议第一台服务器就给配上——一年节省的打字量惊人。
常用 config 字段速记
| 字段 | 用途 |
|---|---|
HostName |
真实 IP / 域名 |
User |
远端用户 |
Port |
SSH 端口 |
IdentityFile |
用哪个私钥 |
IdentitiesOnly yes |
只用 IdentityFile,不试别的 |
ProxyJump |
跳板(见下面) |
LocalForward |
本地端口转发 |
RemoteForward |
远端端口转发 |
ServerAliveInterval |
心跳间隔(防 NAT idle 断) |
ServerAliveCountMax |
连续几次心跳失败就断连 |
ControlMaster auto + ControlPath ~/.ssh/cm-%r@%h:%p |
连接复用:下次 ssh 同一台秒进 |
Compression yes |
压缩(慢网络有用,快网络可能反而慢) |
2. SSH 密钥 + agent
# 生成新密钥(推荐 ed25519,比 RSA 短且强)
$ ssh-keygen -t ed25519 -C 'my-key 2026'
# 默认存 ~/.ssh/id_ed25519 + .pub
# 把公钥放到服务器
$ ssh-copy-id user@server
# 或手动:cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
ssh-agent:少敲一次密码
私钥设了 passphrase 后,每次 ssh 都问一遍——烦。ssh-agent 帮你缓存:
# 启动 agent
$ eval $(ssh-agent -s)
# 加密钥
$ ssh-add ~/.ssh/id_ed25519
Enter passphrase: ...
Identity added.
# 看现在 agent 里有哪些
$ ssh-add -l
之后 ssh 不再要密码。
Mac 用户:macOS 内置 keychain 集成,配 config:
Host *
AddKeysToAgent yes
UseKeychain yes # macOS-only
IdentityFile ~/.ssh/id_ed25519
ssh-add 一次 → 重启都还在。
Agent forwarding:从 A→B→git 都不用复制 key
$ ssh -A server
$ ssh server # 配 ForwardAgent yes 后等价
意思是"server 上的 ssh 进程能用我本地 agent 里的密钥"。用途:
你本地 → ssh → server → 在 server 上 git clone [email protected]:...
↑ 用你本地的 GitHub key(而不是 server 上的)
安全提示:-A 让远端 root 能用你的所有密钥——只对你信任的服务器开。
Host github-builder
ForwardAgent yes # 只对这一台开
3. Port forward(tunnel):SSH 通道里跑别的协议
Local forward(-L):本地端口 → 远端可达的目标
最高频。"我本地 localhost:5432 想访问 server 后面的 postgres":
$ ssh -L 5432:db-internal:5432 jump-host
读法:
-L 本地端口 : 远端可达的目标 host : 目标端口
│ │ │
5432 db-internal 5432
发生:
本地 psql localhost:5432
↓ TCP
你电脑的 ssh 进程
↓ SSH tunnel(加密)
jump-host 的 sshd
↓ TCP
db-internal:5432
你 psql -h localhost -p 5432 就连到了藏在内网的数据库,不暴露 db 到公网。
Remote forward(-R):远端端口 → 本地可达的目标
反过来——把本地服务暴露给远端:
# 本地跑了个 dev server 在 8080
$ python -m http.server 8080 &
# 让 server 上的人能访问
$ ssh -R 8080:localhost:8080 server
# server 上:curl localhost:8080 → 实际访问你本地的 8080
用途:
- 给同事临时演示本地服务
- 在没公网 IP 的家用机器上跑服务,通过有公网 IP 的 VPS 暴露
- ngrok / cloudflare tunnel 的免费替代
Dynamic forward(-D):SOCKS 代理
把整条 SSH 连接变成 SOCKS5 代理:
$ ssh -D 1080 jump-host
之后浏览器配 SOCKS5 = localhost:1080,所有流量从 jump-host 出去。等于一个临时 VPN,常用于:
- 翻看墙内被墙的资源(合规场景)
- 让外部测试看你内网的服务
4. Jump host:跳板机穿多层
很多公司架构:
你本地 → bastion(堡垒机/跳板机,公网 IP) → 内网服务器
(没公网,只有 bastion 能 ssh)
传统做法:先 ssh bastion,进去后 ssh internal-server。麻烦。
ProxyJump(SSH 7.3+)一键穿透:
$ ssh -J bastion internal-server
或写到 config:
Host bastion
HostName bastion.example.com
User me
Host *.internal
User me
ProxyJump bastion
之后:
$ ssh db.internal # 自动经 bastion → db.internal
$ scp file db.internal:/tmp/
$ rsync -av ./ db.internal:/path/
多层跳板:
Host hop2
ProxyJump hop1
Host target
ProxyJump hop2
走 你 → hop1 → hop2 → target,配一次终生用。
5. 连接复用(control master):第二次秒进
每次 ssh 都要 TCP 握手 + TLS 握手 + 鉴权 = 几百毫秒。配复用:
Host *
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 10m
第一次连后台开个长连接(保留 10 分钟),后续 ssh / scp / rsync 同一台直接复用——0.05 秒就进。
坑:
- 长连接没断开时你改不了 user / port(用同一通道)
- 想强制重连:
ssh -O exit <host>先关掉控制连接 - 通道挂了所有依赖它的 session 一起死
6. 文件传输:scp / rsync / sftp 都走 ssh
# scp(简单,但 OpenSSH 9 起用 sftp 底层,部分 flag 变了)
$ scp file.txt server:/tmp/
$ scp -r dir/ server:/tmp/
$ scp server:/etc/hosts ./remote-hosts
$ scp -P 22022 file server:/tmp/ # 注意 P 大写
$ scp -J bastion file internal:/tmp/ # 跳板
# rsync(智能、增量、可断点续传,**强烈推荐**)
$ rsync -avP file server:/tmp/
$ rsync -avP -e 'ssh -p 22022' file server:/tmp/ # 改端口
# sftp(交互式)
$ sftp server
sftp> ls
sftp> put file.txt
sftp> get remote.txt
sftp> bye
99% 场景 rsync 优于 scp(断点续传 + 增量 + 进度条)。
7. 远程执行命令 / 脚本
# 远端跑一条命令立刻退出
$ ssh server 'uptime'
# 多条
$ ssh server 'date; uptime; df -h'
# 跑本地脚本到远端(不用 scp 上去)
$ ssh server 'bash -s' < my-script.sh
# 带变量 / 参数
$ ssh server "echo Hello $USER" # 双引号让 $USER 在本地展开
$ ssh server 'echo Hello $USER' # 单引号让 $USER 在远端展开
# heredoc 跑一坨远端命令
$ ssh server 'bash -s' <<'EOF'
set -e
cd /var/www/app
git pull
systemctl restart myapp
EOF
# 在多台机器上并行(用 GNU parallel)
$ parallel -j 5 ssh {} uptime ::: web1 web2 web3 web4 web5
8. 实战:一些超有用的高阶用法
A. SSH 当 git 跳板(写代码不用复制 key 上 server)
Host github.com
User git
IdentityFile ~/.ssh/github_key
Host devbox
HostName 1.2.3.4
ForwardAgent yes
server 上 git clone / git pull 用的是你本地的 GitHub key——server 上根本没 key 文件,最安全。
B. SSH config 加 alias 触发某条命令
Host k8s-shell
HostName k8s-bastion
User me
RemoteCommand kubectl exec -it api-pod -- /bin/bash
RequestTTY yes
ssh k8s-shell → SSH 进 bastion → 立刻自动 kubectl exec 进 pod。一条命令穿 3 层。
C. 端口转发后自动后台 + 不开 shell
$ ssh -fNT -L 5432:db:5432 jump-host
-fbackground-N不执行命令(只建 tunnel)-T不分配 TTY
适合后台开几条 tunnel 不打扰你的终端。
D. 用 SSH 当 SOCKS 代理 + autossh 自动重连
$ sudo apt install autossh
$ autossh -M 0 -fNT -D 1080 jump-host
autossh 在网络抖动时自动重连——比 ssh 直接跑稳得多。
E. tmux + ssh 防断网
跑长任务时,先 ssh,再开 tmux:
$ ssh server
$ tmux new -s work
(跑长任务)
Ctrl+B d # 脱离 tmux,回到原 shell
# 几小时后
$ ssh server
$ tmux attach -t work # 回到之前的会话
ssh 断了 tmux 不死。
9. SSH 安全清单(公网服务器必看)
# /etc/ssh/sshd_config (server 端,不是 client 的 ~/.ssh/config)
# 强制 key 登录,禁密码(关键!防爆破)
PasswordAuthentication no
PubkeyAuthentication yes
# 禁 root 直接 ssh(要先 ssh 普通用户再 sudo)
PermitRootLogin no
# 或者只允许 key 登录 root(保留紧急救援能力)
PermitRootLogin prohibit-password
# 限制能 ssh 的用户
AllowUsers alice bob deploy
# 改默认端口(防扫描器,但不是真安全;安全靠 key)
Port 22022
# 缩短超时,干净退出空闲连接
ClientAliveInterval 60
ClientAliveCountMax 3
# 禁用 X11 / agent forward 等用不到的
X11Forwarding no
改完:
$ sudo sshd -t # 语法测试(不重启)
$ sudo systemctl restart sshd
测试前别关现有 ssh 会话——万一改错了,新 ssh 进不来你还有老连接救场。
10. 现在做一件事
# 1. 生成一对密钥(如果还没)
$ ssh-keygen -t ed25519
# 2. 配 ~/.ssh/config 给你常连的服务器起别名
$ vim ~/.ssh/config
# 3. 把 ssh-add 调成 auto(加到 ~/.zshrc 或 ~/.bashrc)
$ eval $(ssh-agent -s) ; ssh-add ~/.ssh/id_ed25519
# Mac:直接配 UseKeychain yes
# 4. 给你最频繁的服务器开 ControlMaster
# 测一下:连过一次后再 ssh 同一台
$ time ssh server exit
$ time ssh server exit # 第二次应该 < 0.1s
# 5. 试一次 port forward 把远端服务"借"到本地
$ ssh -L 6379:localhost:6379 server
# 另一个 terminal:redis-cli -h localhost
SSH 是日常运维的 80% 入口——把它调到顺手,你之后做任何远程操作都飞快。
下一篇:observability——
top / iotop / iostat / vmstat / strace,机器在干什么——CPU / 内存 / 磁盘 / 网络的实时观测。
$ ls related/
-
现代 Linux 网络工具集:ip / ss / dig / curl / nc 2026-07-01
Linux 系列第 18 篇。`ifconfig` / `netstat` / `route` / `nslookup` 这套上世纪的工具今天还能用,但已经被 `ip` / `ss` / `dig` 替代。这一篇拆 5 件现代工具的常用语法,并给一份"网络出问题怎么 5 分钟排查到根因"的清单。
-
systemd 全攻略:systemctl / journalctl / .service 文件 2026-06-30
Linux 系列第 17 篇。开机自启服务、看日志、定时任务、依赖管理——这些以前要拼接 init 脚本 + crontab + rsyslog 的活,现代 Linux 都让 systemd 一个工具搞定。这一篇拆 systemctl 怎么用、写一个 .service 文件、看 journalctl、配 timer。
-
信号机制:Ctrl+C 按下去机器内部发生了什么 2026-06-29
Linux 系列第 16 篇。信号(signal)是 Unix 进程间最古老的通信手段——内核能在不打扰进程"主线程"的情况下捅它一下,让它去做点别的。这一篇拆 SIGTERM / SIGKILL / SIGHUP / SIGCHLD 这些常见信号的用途、进程怎么 catch、为什么 SIGKILL 不能拦截、信号编号和发行版的关系。