端口转发详解
本地端口转发(Local Port Forwarding)
本地端口转发将本地端口的流量转发到远程服务器的指定端口,常用于访问远程服务。
# 基本语法
ssh -L [本地IP:]本地端口:目标主机:目标端口 用户@SSH服务器
# 示例:访问远程数据库
ssh -L 3306:localhost:3306 user@database-server
# 现在可以通过 localhost:3306 访问远程数据库
# 绑定到特定本地IP
ssh -L 192.168.1.100:8080:web-server:80 user@gateway
# 多个端口转发
ssh -L 3306:db1:3306 -L 5432:db2:5432 user@server
实际应用场景
# 1. 数据库访问
ssh -L 3306:mysql-server:3306 user@bastion
mysql -h localhost -P 3306 -u dbuser -p
# 2. Web服务调试
ssh -L 8080:internal-web:80 user@gateway
# 浏览器访问 http://localhost:8080
# 3. 远程桌面
ssh -L 5900:desktop-server:5900 user@gateway
# VNC客户端连接 localhost:5900
# 4. 开发服务器
ssh -L 3000:dev-server:3000 -L 8080:dev-server:8080 user@dev-gateway
远程端口转发(Remote Port Forwarding)
远程端口转发将远程服务器的端口流量转发到本地,常用于让远程服务器访问本地服务。
# 基本语法
ssh -R [远程IP:]远程端口:本地主机:本地端口 用户@SSH服务器
# 示例:让远程服务器访问本地Web服务
ssh -R 8080:localhost:80 user@remote-server
# 远程服务器可以通过 localhost:8080 访问本地的80端口
# 绑定到远程服务器的所有接口
ssh -R 0.0.0.0:8080:localhost:80 user@remote-server
实际应用场景
# 1. 本地开发服务器共享
ssh -R 3000:localhost:3000 user@public-server
# 远程用户可以访问你的本地开发服务器
# 2. 内网服务暴露
ssh -R 8080:internal-service:80 user@public-gateway
# 通过公网服务器访问内网服务
# 3. 文件共享
python3 -m http.server 8000 # 本地启动文件服务器
ssh -R 8000:localhost:8000 user@remote-server
# 远程服务器可以下载本地文件
动态端口转发(SOCKS代理)
动态端口转发创建一个SOCKS代理,可以代理多种协议的流量。
# 创建SOCKS代理
ssh -D 1080 user@proxy-server
# 指定绑定地址
ssh -D 127.0.0.1:1080 user@proxy-server
# 后台运行
ssh -D 1080 -f -N user@proxy-server
配置应用程序使用SOCKS代理
# 1. 浏览器配置
# Firefox: 设置 -> 网络设置 -> 手动代理配置
# SOCKS主机: localhost 端口: 1080
# 2. curl使用SOCKS代理
curl --socks5 localhost:1080 http://example.com
# 3. git使用SOCKS代理
git config --global http.proxy socks5://localhost:1080
# 4. 系统级代理(Linux)
export http_proxy=socks5://localhost:1080
export https_proxy=socks5://localhost:1080
SSH隧道和跳板机
多级跳板连接
# 传统方式(逐级连接)
ssh user1@jump1
ssh user2@jump2 # 在jump1上执行
ssh user3@target # 在jump2上执行
# 使用ProxyJump(推荐)
ssh -J user1@jump1,user2@jump2 user3@target
# 配置文件方式
# ~/.ssh/config
Host target
HostName target-server
User target-user
ProxyJump jump1,jump2
Host jump1
HostName jump1-server
User jump1-user
Host jump2
HostName jump2-server
User jump2-user
复杂网络拓扑
# 场景:通过跳板机访问内网多台服务器
# ~/.ssh/config
# 跳板机配置
Host bastion
HostName bastion.company.com
User admin
Port 2222
IdentityFile ~/.ssh/bastion_key
# 内网服务器配置
Host web-*
ProxyJump bastion
User webadmin
IdentityFile ~/.ssh/web_key
Host web-01
HostName 10.0.1.10
Host web-02
HostName 10.0.1.11
Host db-*
ProxyJump bastion
User dbadmin
IdentityFile ~/.ssh/db_key
Host db-master
HostName 10.0.2.10
LocalForward 3306 localhost:3306
Host db-slave
HostName 10.0.2.11
文件传输高级技巧
SCP高级用法
# 保持文件属性
scp -p file.txt user@server:/path/
# 递归复制目录
scp -r directory/ user@server:/path/
# 压缩传输
scp -C large-file.tar user@server:/path/
# 限制带宽(KB/s)
scp -l 1000 file.txt user@server:/path/
# 通过跳板机传输
scp -o ProxyJump=bastion file.txt user@target:/path/
# 服务器间直接传输
scp user1@server1:/path/file.txt user2@server2:/path/
SFTP高级操作
# 批量操作脚本
echo -e "cd /remote/path\nput *.txt\nquit" | sftp user@server
# 使用配置文件
sftp -F ~/.ssh/config server-alias
# 恢复中断的传输
sftp -r user@server
sftp> reput interrupted-file.txt
# 同步目录
sftp user@server
sftp> mirror local-dir remote-dir
rsync over SSH
# 基本同步
rsync -avz -e ssh local-dir/ user@server:/remote-dir/
# 删除目标中多余的文件
rsync -avz --delete -e ssh local-dir/ user@server:/remote-dir/
# 排除特定文件
rsync -avz --exclude='*.log' --exclude='tmp/' -e ssh local-dir/ user@server:/remote-dir/
# 显示进度
rsync -avz --progress -e ssh large-file user@server:/path/
# 断点续传
rsync -avz --partial --progress -e ssh large-file user@server:/path/
# 通过跳板机同步
rsync -avz -e "ssh -J bastion" local-dir/ user@target:/remote-dir/
X11转发和图形应用
基本X11转发
# 启用X11转发
ssh -X user@server
# 可信X11转发(更快但安全性较低)
ssh -Y user@server
# 运行图形应用
xclock
firefox
gedit
X11转发优化
# 压缩X11流量
ssh -X -C user@server
# 配置文件设置
# ~/.ssh/config
Host graphics-server
HostName server.example.com
ForwardX11 yes
ForwardX11Trusted yes
Compression yes
解决X11转发问题
# 检查DISPLAY变量
echo $DISPLAY
# 检查X11认证
xauth list
# 手动设置DISPLAY
export DISPLAY=localhost:10.0
# 测试X11连接
xeyes
SSH会话管理
连接复用
# 启用连接复用
# ~/.ssh/config
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# 创建socket目录
mkdir -p ~/.ssh/sockets
# 手动建立主连接
ssh -M -S ~/.ssh/sockets/master user@server
# 使用已有连接
ssh -S ~/.ssh/sockets/master user@server
会话保持工具
使用screen
# 在远程服务器上启动screen
ssh user@server
screen -S work-session
# 分离会话
Ctrl+A, D
# 重新连接
ssh user@server
screen -r work-session
# 列出所有会话
screen -ls
使用tmux
# 启动tmux会话
ssh user@server
tmux new-session -s work
# 分离会话
Ctrl+B, D
# 重新连接
ssh user@server
tmux attach-session -t work
# 列出会话
tmux list-sessions
SSH脚本自动化
批量操作脚本
#!/bin/bash
# 批量执行命令
SERVERS=("web1" "web2" "web3")
COMMAND="uptime"
for server in "${SERVERS[@]}"; do
echo "=== $server ==="
ssh "$server" "$COMMAND"
echo
done
并行执行
#!/bin/bash
# 并行执行命令
SERVERS=("web1" "web2" "web3")
COMMAND="df -h"
for server in "${SERVERS[@]}"; do
{
echo "=== $server ==="
ssh "$server" "$COMMAND"
echo
} &
done
wait # 等待所有后台任务完成
文件分发脚本
#!/bin/bash
# 批量文件分发
SOURCE_FILE="config.txt"
TARGET_PATH="/etc/myapp/"
SERVERS=("web1" "web2" "web3")
for server in "${SERVERS[@]}"; do
echo "Copying to $server..."
scp "$SOURCE_FILE" "$server:$TARGET_PATH"
echo "Restarting service on $server..."
ssh "$server" "sudo systemctl restart myapp"
done
SSH API和编程接口
Python paramiko库
import paramiko
# 创建SSH客户端
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
client.connect('hostname', username='user', key_filename='/path/to/key')
# 执行命令
stdin, stdout, stderr = client.exec_command('ls -la')
print(stdout.read().decode())
# 文件传输
sftp = client.open_sftp()
sftp.put('local_file.txt', '/remote/path/file.txt')
sftp.get('/remote/path/file.txt', 'downloaded_file.txt')
# 关闭连接
sftp.close()
client.close()
Go语言SSH客户端
package main
import (
"golang.org/x/crypto/ssh"
"io/ioutil"
"log"
)
func main() {
// 读取私钥
key, err := ioutil.ReadFile("/path/to/private/key")
if err != nil {
log.Fatal(err)
}
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
log.Fatal(err)
}
// 配置SSH客户端
config := &ssh.ClientConfig{
User: "username",
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
// 连接服务器
client, err := ssh.Dial("tcp", "hostname:22", config)
if err != nil {
log.Fatal(err)
}
defer client.Close()
// 执行命令
session, err := client.NewSession()
if err != nil {
log.Fatal(err)
}
defer session.Close()
output, err := session.Output("ls -la")
if err != nil {
log.Fatal(err)
}
log.Printf("Output: %s", output)
}
性能优化
连接优化
# ~/.ssh/config
Host *
# 启用连接复用
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# 启用压缩
Compression yes
# 快速加密算法
Ciphers aes128-gcm@openssh.com,aes256-gcm@openssh.com
# 禁用不需要的功能
ForwardX11 no
ForwardAgent no
# 保持连接活跃
ServerAliveInterval 60
ServerAliveCountMax 3
大文件传输优化
# 使用并行传输
rsync -avz --progress --partial -e "ssh -c aes128-gcm@openssh.com" large-file user@server:/path/
# 分块传输
split -b 100M large-file chunk_
for chunk in chunk_*; do
scp "$chunk" user@server:/tmp/
done
# 在服务器上合并
ssh user@server "cat /tmp/chunk_* > /path/large-file && rm /tmp/chunk_*"
小结
SSH的高级功能为系统管理和开发工作提供了强大的工具。通过掌握端口转发、隧道技术、文件传输优化、会话管理等高级技巧,可以:
- 安全地访问内网资源
- 高效地进行文件传输和同步
- 实现复杂的网络拓扑连接
- 自动化批量操作
- 优化连接性能
这些技术在云计算、DevOps、远程开发等场景中特别有用。