端口转发详解

本地端口转发(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、远程开发等场景中特别有用。


上一章节SSH安全实践
下一章节SSH故障排除