SSH本地端口转发完全解析:像“挖掘隧道”一样安全访问远程数据库

SSH本地端口转发完全解析:像"挖掘隧道"一样安全访问远程数据库

关键词:SSH端口转发、本地转发、PostgreSQL远程连接、DBeaver、SSH隧道、网络安全

📑 目录

  1. 一个让你"安全感拉满"的数据库连接方案
  2. SSH 本地端口转发是什么?
  3. 核心命令解析:ssh -L 的每个参数都是什么意思?
  4. 四步走:一条数据包的完整旅程
  5. 实战演示:手动用命令连上远程 PostgreSQL
  6. DBeaver 中的"无感"实现(帮你自动建隧道)
  7. 排错指南:连不上怎么办?
  8. 总结与速查表

1. 一个让你"安全感拉满"的数据库连接方案

假设你是公司的数据工程师。你的任务是要在公司外网(比如家里或咖啡馆),连上部署在内网服务器上的 PostgreSQL 数据库。

但问题来了:

  • 生产数据库不能直接暴露在公网上(会被黑客扫端口)
  • 你也没有 VPN,或者 VPN 太慢不想用
  • 你的老板要求:"所有数据传输必须加密,否则就是安全红线"

此时,SSH 本地端口转发 就是工程师们的"瑞士军刀"。

💡 一句话理解:你在本地电脑上"挖"一个隧道口(端口 5433),所有对这个隧道口的访问,都会被 SSH 加密打包,通过远程服务器"中转",最终安全地送到内网的 PostgreSQL 数据库上。

2. SSH 本地端口转发是什么?

在 SSH 中,-L 参数专门用于"本地端口转发"(Local Forwarding)。它的语法是:

bash 复制代码
ssh -L [本地端口]:[目标主机]:[目标端口] user@跳板机IP

官方的描述是:它会把本地电脑的某个端口,通过 SSH 通道,映射到远程服务器能访问到的某个地址和端口上。

💡 一句话理解-L 就是建立一个 "本地入口"------你访问自己的 localhost:5433,就等于在安全地访问远程的 127.0.0.1:5432。

3. 核心命令解析:ssh -L 的每个参数都是什么意思?

假设我们执行这一条经典的连接命令:

bash 复制代码
ssh -N -f -L 5433:127.0.0.1:5432 aiuser@192.168.1.100

我们来拆解一下每一个参数的含义:

参数/字段 含义 在你的场景中
ssh 启动 SSH 客户端 系统命令
-N 不执行远程命令(纯粹做端口转发,不打开 Shell) 因为只是做隧道,不需要登录上去敲命令
-f 后台运行(Fork into background) 让隧道在后台静默运行,不占用当前终端窗口
-L 本地转发(Local) 核心参数,告诉 SSH 要做本地端口映射
5433 本地监听端口(你电脑上的端口) DBeaver 或你的 Python 代码连接 localhost:5433
127.0.0.1:5432 目标地址:端口(服务器上要访问的真实地址) 这里是告诉 SSH 服务端,到了远程机器后,去连接它自己(127.0.0.1)的 5432 端口(PostgreSQL 默认端口)
aiuser@192.168.1.100 跳板机(堡垒机)登录信息 你 SSH 登录远程服务器的用户名和 IP

4. 四步走:一条数据包的完整旅程

为了让你更直观地理解,我们追踪一条数据从 DBeaver 发出,到数据库返回的全过程:
#mermaid-svg-BfIgtaIWiToyzVJJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BfIgtaIWiToyzVJJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BfIgtaIWiToyzVJJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BfIgtaIWiToyzVJJ .error-icon{fill:#552222;}#mermaid-svg-BfIgtaIWiToyzVJJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BfIgtaIWiToyzVJJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BfIgtaIWiToyzVJJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BfIgtaIWiToyzVJJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BfIgtaIWiToyzVJJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BfIgtaIWiToyzVJJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BfIgtaIWiToyzVJJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BfIgtaIWiToyzVJJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BfIgtaIWiToyzVJJ .marker.cross{stroke:#333333;}#mermaid-svg-BfIgtaIWiToyzVJJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BfIgtaIWiToyzVJJ p{margin:0;}#mermaid-svg-BfIgtaIWiToyzVJJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BfIgtaIWiToyzVJJ .cluster-label text{fill:#333;}#mermaid-svg-BfIgtaIWiToyzVJJ .cluster-label span{color:#333;}#mermaid-svg-BfIgtaIWiToyzVJJ .cluster-label span p{background-color:transparent;}#mermaid-svg-BfIgtaIWiToyzVJJ .label text,#mermaid-svg-BfIgtaIWiToyzVJJ span{fill:#333;color:#333;}#mermaid-svg-BfIgtaIWiToyzVJJ .node rect,#mermaid-svg-BfIgtaIWiToyzVJJ .node circle,#mermaid-svg-BfIgtaIWiToyzVJJ .node ellipse,#mermaid-svg-BfIgtaIWiToyzVJJ .node polygon,#mermaid-svg-BfIgtaIWiToyzVJJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BfIgtaIWiToyzVJJ .rough-node .label text,#mermaid-svg-BfIgtaIWiToyzVJJ .node .label text,#mermaid-svg-BfIgtaIWiToyzVJJ .image-shape .label,#mermaid-svg-BfIgtaIWiToyzVJJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-BfIgtaIWiToyzVJJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BfIgtaIWiToyzVJJ .rough-node .label,#mermaid-svg-BfIgtaIWiToyzVJJ .node .label,#mermaid-svg-BfIgtaIWiToyzVJJ .image-shape .label,#mermaid-svg-BfIgtaIWiToyzVJJ .icon-shape .label{text-align:center;}#mermaid-svg-BfIgtaIWiToyzVJJ .node.clickable{cursor:pointer;}#mermaid-svg-BfIgtaIWiToyzVJJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BfIgtaIWiToyzVJJ .arrowheadPath{fill:#333333;}#mermaid-svg-BfIgtaIWiToyzVJJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BfIgtaIWiToyzVJJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BfIgtaIWiToyzVJJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BfIgtaIWiToyzVJJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BfIgtaIWiToyzVJJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BfIgtaIWiToyzVJJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BfIgtaIWiToyzVJJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BfIgtaIWiToyzVJJ .cluster text{fill:#333;}#mermaid-svg-BfIgtaIWiToyzVJJ .cluster span{color:#333;}#mermaid-svg-BfIgtaIWiToyzVJJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BfIgtaIWiToyzVJJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BfIgtaIWiToyzVJJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-BfIgtaIWiToyzVJJ .icon-shape,#mermaid-svg-BfIgtaIWiToyzVJJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BfIgtaIWiToyzVJJ .icon-shape p,#mermaid-svg-BfIgtaIWiToyzVJJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BfIgtaIWiToyzVJJ .icon-shape .label rect,#mermaid-svg-BfIgtaIWiToyzVJJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BfIgtaIWiToyzVJJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BfIgtaIWiToyzVJJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BfIgtaIWiToyzVJJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} DBeaver 连接 localhost:5433
SSH 客户端拦截连接
数据通过 SSH 加密通道发送
SSH 服务端接收并解密
SSH 服务端发起连接 127.0.0.1:5432
PostgreSQL 返回数据
数据原路返回:加密通道 → SSH客户端 → 本地5433端口
DBeaver 收到数据

关键点总结

  • 对本地程序(DBeaver)来说 :它认为自己就是在连接 localhost:5433,是一个标准的本地数据库连接,完全不知道远程服务器存在。
  • 对服务器 PostgreSQL 来说:它看到的是 SSH 服务端在本地(127.0.0.1)发起的连接,以为是本机程序在访问它,完全不知道外面有 DBeaver。
  • 对所有中间网络流量来说:数据包全是 SSH 协议的加密密文,即使被黑客抓包,也无法破解里面的 SQL 语句。

5. 实战演示:手动用命令连上远程 PostgreSQL

5.1 命令示例

bash 复制代码
# 在终端中执行以下命令
ssh -N -f -L 5433:127.0.0.1:5432 aiuser@your-server-ip

# 输入 SSH 密码或自动使用密钥登录(免密登录配置好后更丝滑)

5.2 验证隧道是否建立成功

bash 复制代码
# 查看本地端口是否处于监听状态
netstat -tlnp | grep 5433
# 或
lsof -i :5433

如果看到监听状态(LISTEN),说明隧道已成功建立。

5.3 在 DBeaver 中连接

  1. 新建 PostgreSQL 连接
  2. 主机 :填写 localhost
  3. 端口 :填写 5433
  4. 用户名/密码:填写你的 PostgreSQL 真实用户名密码(不是 SSH 密码)
  5. 测试连接 → 成功!

6. DBeaver 中的"无感"实现(帮你自动建隧道)

你不需要手动在终端敲命令。DBeaver 的 SSH 隧道配置 已经把这个过程封装好了,底层用了 Java 的 JSCH 库 来模拟 ssh -L

6.1 配置步骤

在 DBeaver 中新建 PostgreSQL 连接后:

  1. 切换到 SSH 标签页
  2. 勾选 "使用 SSH 隧道"
  3. 填写 SSH 主机、端口、用户名、认证方式(密码或私钥)
  4. 回到 主要 标签页,主机填写 localhost,端口填写一个本地空闲端口(如 5433)
  5. 点击 测试连接

6.2 底层原理

DBeaver 做的事情和命令完全一样:

bash 复制代码
# DBeaver 内部等价于执行:
ssh -N -f -L 5433:127.0.0.1:5432 aiuser@192.168.1.100
  • 创建隧道:DBeaver 在后台启动一个 SSH 连接
  • 设置转发:自动建立端口转发
  • 销毁隧道:关闭 DBeaver 连接时自动关闭 SSH 隧道

7. 排错指南:连不上怎么办?

序号 症状 检查点
1 ssh: connect to host ...: Connection refused 检查跳板机 IP 是否正确,SSH 服务是否运行(systemctl status sshd
2 bind: Address already in use 本地 5433 端口被占用,换一个端口(如 -L 5434:127.0.0.1:5432)或杀掉占用进程
3 channel 3: open failed: connect failed: Connection refused 目标 PostgreSQL 服务没开(systemctl status postgresql),或地址写错了
4 隧道已建立,但 DBeaver 连不上 检查 DBeaver 是否连的是 localhost:5433;PostgreSQL 的 pg_hba.conf 是否允许 127.0.0.1 连接

8. 总结与速查表

核心知识点回顾

  • SSH 本地端口转发:在本地电脑上"挖"一个隧道口(端口),所有对这个隧道口的访问,都会被 SSH 加密传到远程服务器,再转发到目标服务。
  • -L 语法ssh -L [本地端口]:[目标主机]:[目标端口] user@跳板机
  • 无感连接 :DBeaver 等工具内部使用 JSCH 库,后台自动完成 ssh -L 命令,无需手动操作。

命令速查表

场景 命令
最简转发(前台运行) ssh -L 5433:127.0.0.1:5432 aiuser@192.168.1.100
后台守护进程(不登录) ssh -N -f -L 5433:127.0.0.1:5432 aiuser@192.168.1.100
查看端口占用 lsof -i :5433
关闭隧道 kill [进程ID]

一图胜千言

(下面的 ASCII 图就是你的笔记原图,保留它你就记住了所有细节)

复制代码
本地 DBeaver/程序
    ↓ 连接 localhost:5433
SSH 客户端拦截这个连接
    ↓ 通过已建立的 SSH 加密通道转发
到达服务器
    ↓ SSH 服务端在服务器上发起连接
连接服务器本地的 PostgreSQL (127.0.0.1:5432)
    ↓ PostgreSQL 数据库返回数据
数据原路返回(加密通道 → SSH客户端 → 本地5433端口)
    ↓
DBeaver/程序收到数据
相关推荐
OceanBase数据库官方博客1 小时前
让 DRP全域数据智能流转OceanBase AI 数据库支撑央国企落地穿透式监
数据库·人工智能·oceanbase
笃行3502 小时前
零代码完成MongoDB迁移,KingbaseES是怎么做到的
数据库
Leighteen2 小时前
时区的坑:为什么存进数据库的时间,差了 8 小时
数据库
陈天伟教授2 小时前
TraeWork初体验-生成研究报告
大数据·数据库·人工智能
大黄说说2 小时前
SQL Server 执行计划怎么看?快速定位 SQL 慢的根源
java·linux·数据库
专注API从业者2 小时前
告别人工盯品!Open Claw 搭建京东商品全自动监控与数据分析系统(附完整可运行代码)
大数据·数据库·数据分析
数据知道4 小时前
SSRF 漏洞实战:内网探测、云元数据窃取一条龙
网络·安全·网络安全
molaoye4 小时前
win10下安装MySQL 8.0.*实录
数据库·mysql
yunwei374 小时前
eBPF 教程:精准隔离已建立的 TCP 连接
linux·安全·开源