[Linux基础]-18-SSH端口转发
引言
篇文章介绍三种SSH端口转发方式,分别是动态端口转(fan)发(qiang),本地端口转发和远程端口转发,分别适用于不同网络环境。
文章目录
0×1.SSH动态端口转发
SSH动态端口转发工作原理介绍:
配置SSH动态端口转发,将在本地创建一个 SOCKS 代理服务端口,指向远程SSH服务器,这样就在本地和远程SSH服务器之间建立了一条SSH安全通道;在本地浏览器配置SOCKS代理指向这个本地端口,浏览器的所有访问请求将通过此端口转发给远程SSH服务器,SSH服务器接收这些请求后,代替请求者去访问这些需要浏览的页面,然后将页面数据通过已经建立的SSH安全通道返回给请求者,从而让本地能够显示这些页面内容,SSH动态端口转发能保证本地到SSH服务器的数据安全传输,但不能保证SSH服务器到实际浏览页面所在服务器之间的数据安全传输,除非它们之间也使用了加密连接(如 HTTPS)。
SSH动态端口转发实例:
实验需求:
1)已经在墙外的SSH服务器
2)火狐浏览器
⚡使用密码连接远程服务器(不太安全)
#1.打开终端,配置去往SSH服务器的SOCKS代理端口
#-D代表这是一个SOCKS代理端口,本地端口号8899
#987是远程SSH服务器中的用户,192.168.1.233是SSH服务器的IP地址
# 参数解释(针对动态端口转发场景):
# -D 8899 核心参数:开启动态端口转发,本地监听端口为8899(SOCKS代理端口)
# -f 后台执行命令(端口转发必用,避免占用当前终端)
# -N 不执行远程命令(端口转发必用,仅做转发不登录Shell)
# -q 安静模式,不显示警告和诊断信息(可选,减少干扰)
# -T 禁用伪tty分配(推荐,因不执行命令无需终端)
# -n 重定向标准输入从/dev/null(后台运行-f时**必须**搭配,防止后台进程因无输入报错)
# 完整命令示例:
987@hk987:~$ ssh -qTfnN -D 8899 987@192.168.1.233
#2.输入SSH服务器上987用户对应的密码,SSH连接完成,此时已经在本地和SSH服务器之间开启了一条数据转发通道。
⚡使用密钥连接(安全)
# 在本地生成公私钥对,一路回车即可,也可以根据提示设置一个密码保护私钥
ssh-keygen -t hk987 -C "your_email@example.com"
# 将公钥添加到服务器
# 你需要把本地的公钥内容,放到服务器的 ~/.ssh/authorized_keys 文件里
# 查看刚才生成的公钥内容,假设公钥名称是hk987.pub
cat ~/.ssh/hk987.pub
# 这里有个一键命令(假设你现在还能用密码登录服务器):
# 在本地执行,替换用户名your_user和服务器IP your_server_ip(假设你刚才生成的公钥保存在~/.ssh/987.pub)
ssh-copy-id -i ~/.ssh/hk987.pub your_user@your_server_ip
#-i 后面指定本地私钥文件
# 完整命令示例:
987@hk987:~$ ssh -qTfnN -i ~/.ssh/987.pub -D 8899 987@192.168.1.233
# 如果服务ssh端口改成了2333,可以用-p指定端口
987@hk987:~$ ssh -qTfnN -i ~/.ssh/987.pub -p 2333 -D 8899 987@192.168.1.233
打开火狐浏览器(chrome也可以),在设置里面搜索"proxy";在proxy设置里面,选择手动配置代理,然后再SOCKS主机里面,填入127.0.0.1,端口填上面我们设置的8899,下面选择SOCKS V5,确定
这样配置之后,浏览器就能随时转(fan)发(qiang)访问任何网页了,不需要使用时只要在proxy设置里面选择不使用代理服务器,就能恢复普通访问。
0×2.SSH本地端口转发
假设有三台计算机A(192.168.1.100)、B(192.168.1.101)、C(192.168.1.102),A作为客户端,C是服务端,A想要SSH连接C,必须先SSH连接到B,通过B才能SSH连接到C,但每次这样操作十分麻烦,这时就可以在B上配置SSH本地端口转发,让B的指定端口的数据直接发给C,之后,A只需要SSH连接B的这个指定端口,就能直接连接到C,请看下面的演示:
# 1. 在B上配置SSH本地端口转发
# 参数解释:
# -L 核心参数:指定本地端口转发,完整格式为 [绑定地址]:本地端口:目标主机:目标端口
# (省略绑定地址时默认仅监听127.0.0.1,加 -g 后监听0.0.0.0,允许远程主机连接)
# -g 允许远程主机(如A)连接到B的本地转发端口(必加,否则只有B自己能连23333)
# -N 不执行远程命令(仅做端口转发,不登录Shell,必用)
# -n 重定向标准输入从/dev/null(后台运行 -f 时推荐搭配,防止进程挂起)
# -f 后台执行命令(必用,避免占用B的当前终端)
#
# 命令拆解:
# 23333:192.168.1.102:22 → 将B的23333端口,转发到目标主机C的22端口(SSH服务端口)
# 987C@192.168.1.102 → B通过SSH连接到C(建立转发的“跳板通道”)
987B@B.hk987.xyz:~$ ssh -gNnf -L 23333:192.168.1.102:22 987C@192.168.1.102
#首次连接会接收一个Key,输入yes,随后输入SSH服务器上对应用户名的密码即可完成连接
#2.在A上连接B的23333端口(IP虽然是B的,但@前面的用户名要是C中的用户,想登陆C中哪个账户的SSH,就写上对应的用户名,输入的连接密码也应该是C中对应账户的密码)
987A@A.hk987.xyz:~$ ssh -p 23333 987C@192.168.1.101
#注意:如果开启了iptables等防火墙,请将对23333入站连接设置成允许,否则很有可能连接失败
这种方法可以用来将数据转发到指定的任意端口,而不局限于22号端口。
0×3.SSH远程端口转发
假设有两台计算机 A(192.168.1.100,内网)、B(10.10.10.10,外网),B 不能直接访问 A,但 A 能直接访问 B。此时可通过SSH 远程端口转发,将 A 的端口映射到 B 上,实现从 B(或其他外网机器)访问 A:
# 1.A和B都需要安装openssh-server,下面是ubuntu的安装命令,其他发行版自查安装命令
987@hk987.xyz:~$ sudo apt-get install openssh-server
# 2.关键配置:修改 B 的 SSH 服务设置(重要!)
# 默认情况下,B 的 SSH 服务会限制远程端口转发仅绑定到localhost(只有 B 自己能访问)
# 需修改 B 的配置文件,允许外部访问转发端口:
# 在B上编辑SSH服务配置
sudo vim /etc/ssh/sshd_config
# 找到GatewayPorts项,修改为:
GatewayPorts clientspecified # 或 yes(允许所有远程端口绑定到0.0.0.0)
# 保存后重启 B 的 SSH 服务:
sudo systemctl restart sshd
#3.在A上执行远程端口转发
# 参数解释:
# -R 核心参数:开启远程端口转发,格式为 [绑定地址]:远程端口:本地主机:本地端口
# 0.0.0.0:22222 → 让B的22222端口绑定到0.0.0.0(允许所有外网访问)
# localhost:22 → 将流量转发到A的22端口(SSH服务)
# -f 后台执行命令
# -N 不执行远程命令(仅做端口转发)
# -n 重定向标准输入从/dev/null(配合-f使用,防止进程挂起)
# 首次连接需输入 B 的987B用户密码,验证后转发通道建立
987A@A.hk987.xyz:~$ ssh -fNn -R 0.0.0.0:22222:localhost:22 987B@10.10.10.10
#4.连接完成后,在B上将会开启22222号端口,B可以使用自己的这个端口访问到A的22号端口
# 虽然是访问的localhost,但是@前的登陆用户名应该是A的用户,因为访问B的22222就等于访问A的22
987B@B.hk987.xyz:~$ ssh -p 22222 987A@localhost
# 5.从第三台外网机器 C 访问 A
# 直接连接 B 的公网 IP 和22222端口,用户名用 A 的用户:
987C@C.hk987.xyz:~$ ssh -p 22222 987A@10.10.10.10
# 如果无权限修改/etc/ssh/sshd_config文件,就只能在B上再做个本地端口转发来实现远程端口转发相同的功能
#x1.如果想让其他计算机可以通过B的22222号端口访问A,还需要在B上再添加一条本地转发,指定一个对外开放的端口(例如23333),将访问此端口的数据转发给自己的22222号端口
987B@B.hk987.xyz:~$ ssh -gNnf -L 23333:localhost:22222 987B@localhost
#x2.此时在第三台计算机上,只需要访问B的23333号端口即可连上A的22号端口
# 访问B的23333号端口就等于访问B的22222号端口,而此端口又将数据转发给A的22号端口,所以@前面要用A的账户名
987C@C.hk987.xyz:~$ ssh -p 23333 987A@10.10.10.10
0×4.SSH转发实现内网穿透
功能描述:
内网机器 C(被控端)主动连接外网服务器 S;
将 C 的 22 端口映射到 S 的指定端口;
外网机器直接连接 S 的该端口,即可访问 C 的 SSH 服务
# 1.安装必要软件
# C 和 S 都需安装openssh-server
sudo apt update && sudo apt install openssh-server -y
# 2. 配置外网服务器 S(关键!)
# 修改 S 的 SSH 服务配置,允许远程端口转发绑定到外网接口:
# 编辑S的sshd_config
sudo vim /etc/ssh/sshd_config
# 找到以下项修改或添加:
# 允许远程端口转发绑定到指定地址(或直接设为yes)
GatewayPorts clientspecified
# 保存后重启 S 的 SSH 服务:
sudo systemctl restart sshd
# 3. 配置 SSH 密钥认证(替代明文密码)
# 在内网机器 C上生成密钥对,并将公钥上传到 S,实现无密码登录:
# . 在C上生成密钥(一路回车即可)
ssh-keygen -t ed25519 -f ~/.ssh/intranet_tunnel_key
# . 将公钥上传到S的普通用户(假设S的普通用户是`987`)
ssh-copy-id -i ~/.ssh/intranet_tunnel_key.pub 987@S的公网IP
# 4.下面的脚本保存为c.sh,放在内网机器 C 上
#!/bin/bash
# ----- 1. 检查命令行参数(IP 和 端口) -----
if [ -z "$1" ] || [ -z "$2" ]; then
echo "Usage: bash $0 <外网服务器IP> <映射到外网的端口>"
echo "Example: bash $0 10.10.10.10 2333"
exit 1
fi
# ----- 2. 交互式询问外网服务器用户名 -----
read -p "请输入外网服务器的登录用户名: " SERVER_USER
# 检查用户名是否为空
if [ -z "$SERVER_USER" ]; then
echo "错误:用户名不能为空,请重新运行脚本。"
exit 1
fi
# ----- 3. 建立 SSH 远程端口转发隧道 -----
echo "正在建立隧道,使用用户 '$SERVER_USER' 连接服务器 $1 ..."
ssh -i ~/.ssh/intranet_tunnel_key -fNn -o ServerAliveInterval=60 -o ServerAliveCountMax=3 \
-R 0.0.0.0:$2:localhost:22 $SERVER_USER@$1
# ----- 4. 检查执行结果 -----
if [ $? -eq 0 ]; then
echo "✅ 隧道建立成功!"
echo "📡 连接方式:ssh -p $2 <内网机器C的用户名>@$1"
else
echo "❌ 隧道建立失败,请检查网络、密钥或用户名是否正确。"
fi
C主机隧道启动使用方法:
# 1. 给脚本加执行权限(如果还没加)
chmod +x c.sh
# 2. 运行脚本,只需传入 IP 和 端口
# 启动隧道:参数1=S的公网IP,参数2=映射到S的外网端口
bash c.sh 10.10.10.10 2333
# 3. 脚本会提示你输入用户名:
# 请输入外网服务器的登录用户名: 987 <-- 这里输入你的实际用户名
从任何外网机器通过S连接内网 C,实现内网穿透
# 直接连接 S 的公网 IP 和映射端口,用户名用C的本地用户:
ssh -p 2333 C的用户名@10.10.10.10
注释:添加ServerAliveInterval和ServerAliveCountMax=3,防止隧道因网络波动断开;ServerAliveInterval=60每 60 秒 向服务器发送一次心跳包,避免连接因 “空闲” 被防火墙切断。ServerAliveCountMax=3 连续 3 次(即 3×60=180 秒)没收到服务器回应,就自动断开隧道,避免进程挂死。