跳到主要内容

SSH 隧道

小林
后端开发工程师, 专注Go开发、微服务和云原生

SSH 隧道, 打通内外网的神器

standing.png

本地端口转发

第一个场景, 我们在云服务器上有些中间件和服务出于安全性考虑不便把端口暴露到公网, 但是又想在本地方便访问服务器上的资源, 这时候就可以建立一个本地端口转发隧道(Local Port Forwarding)

ssh -L [本地绑定地址:]本地端口:目标主机地址:目标端口 用户名@SSH服务器

简单示例

我们在服务器上用 Python 开启一个简单的 HTTP 服务, 绑定 8000 端口

mkdir ssh-demo && cd ssh-demo
echo "Hello, world!" > index.html
python3 -m http.server

在本地机器执行下面命令

ssh -C -f -N -L 12345:localhost:8000 user01@x.x.x.x
提示

user01 和 x.x.x.x 改成自己的用户名和服务器 IP

我们把本地端口跟远程服务绑定, 访问 http://localhost:12345 即可看到隧道成功建立

img

参数说明

  • -L 将本地端口转发到远程服务器, 本地绑定地址可省略, 目标主机地址不止可以是 localhost, 也可以是内网另一服务器地址, 只要建立SSH隧道的跳板机能访问即可
  • -N 表示不执行远程命令, 如果不加这个参数建立隧道的同时会登录上服务器, 加上则会一直阻塞住
  • -f 后台运行, 如果只加 -N 参数会一直阻塞, 再加上这个参数执行完命令就直接返回
  • -C 启用压缩, 建议加上, 可以提高传输效率
  • -i 用于指定私钥路径, 我们上面的命令没有使用这个参数, 这是因为我们已经在服务器添加公钥并且本地的私钥文件存放在默认路径, 如果不在默认路径需要用这个参数指定

远程端口转发

第二个场景, 我们在本地写了个服务, 希望能够在公网访问, 这时候就能够用SSH隧道建立远程端口转发(Remote Port Forwarding), 这可比去研究各种内网穿透工具简单多了

ssh -R [远程绑定地址:]远程端口:本地主机地址:本地端口 用户名@SSH服务器

简单示例

同样的我们在本地用 Python 开启一个简单的 HTTP 服务, 同样绑定 8000 端口

mkdir ssh-demo && cd ssh-demo
echo "Hello, world!" > index.html
python3 -m http.server

在本地执行下面命令

ssh -C -f -N -R 3000:localhost:8000 user01@x.x.x.x

参数说明

  • -R 将远程端口转发到本地服务
  • 其他参数同上, 就不赘述了
提示

远程绑定的端口一定要在安全组中打开才能在公网中访问

img

我们这里将服务绑定到已经在安全组打开的 3000 端口, 注意端口不要被占用, 尝试访问公网地址 http://x.x.x.x:3000

问题排查

没想到居然翻车了, 在公网居然没法访问, 尝试在服务器上运行 curl localhost:3000 是通的, 难道地址需要改成 0.0.0.0?

折腾来折腾去也是不行, 都开始怀疑是不是被云服务器厂商给限制了, 问一下AI吧

原来有个重要的步骤没有执行, 服务器的 SSH 服务需要配置允许远程转发, 在服务器执行下面命令

echo "GatewayPorts yes" > /etc/ssh/sshd_config.d/gateway-ports.conf

重启 ssh

sudo service ssh restart
危险

不要执行 sudo systemctl restart sshd, 我用 AI 告诉我的这段命令重启 SSH 服务差点搞得 SSH 都连不上

这次访问成功

img