外网开发访问内网服务(SSH 跳板 + 端口转发)
大约 5 分钟
外网开发访问内网服务(SSH 跳板 + 端口转发)
0. 目标(我们要实现什么)
我们的开发环境满足以下约束:
- 我们人在 外网(家 / 咖啡店 / 热点)
- 外网 只能直连一台跳板机
- 内网的 GitLab / Git / 数据库 / Redis / 各类后端服务 均不允许外网直连
通过 SSH 隧道(端口转发),我们要实现:
- 浏览器访问内网 GitLab →
https://localhost:PORT - Git clone / pull / push →
ssh://git@localhost:PORT/xxx.git - 后端在本机启动,但能访问内网 DB / Redis / 依赖服务 →
localhost:PORT
1. SSH 是什么(一句话)
SSH 是一种加密的远程连接协议,用来安全登录服务器,并建立加密隧道把远程服务“映射”到本机端口。
SSH 解决三件事:
- 加密:通信内容不可被窃听
- 鉴权:证明“我们是谁”(用户 Key)
- 身份校验:证明“我们连的是谁”(Host Key)
2. 两种完全不同的 Key(必须分清)
2.1 Host Key(服务器的身份证)
什么时候见到?
第一次执行:
ssh -p 2222 git@localhost会看到:
The authenticity of host '[localhost]:2222' can't be established.
ED25519 key fingerprint is SHA256:xxxx含义:
- 这是 Git 服务器的 Host Key 指纹
- 我们输入
yes后,它会写入:
~/.ssh/known_hosts作用:
- 下次不再提示
- 防止中间人攻击
2.2 用户 Key(我们的门禁卡)
我们本机的一对密钥:
~/.ssh/id_ed25519 (私钥,不能外泄)
~/.ssh/id_ed25519.pub (公钥)流程:
- 公钥加到 GitLab(用户设置 → SSH Keys)
- GitLab 以后通过它识别我们的身份
验证成功的标志:
ssh -T -p 2222 git@localhost返回:
Welcome to GitLab, @your_name!3. 什么是跳板机,为什么需要它
- 内网资源 不对外暴露
- 跳板机是 唯一允许外网 SSH 连接的入口
- 我们 先连跳板机
- 再让跳板机 代我们访问内网服务
逻辑结构:
我们本机
|
| SSH
v
跳板机
|
| 内网访问
v
GitLab / DB / Redis / 服务4. 端口转发(Port Forwarding)是什么
我们使用的是:SSH 本地端口转发(-L)
一句话理解
把“内网的某个服务端口”,映射成“我们本机的 localhost:端口”。
标准语法
ssh -L <本地端口>:<内网主机>:<内网端口> user@jump_host示例(Git SSH)
ssh -L 2222:git.internal.example.com:22 user@jump_host含义:
本机监听
localhost:2222所有流量:
- 加密 → 跳板机
- 再转发 → 内网 Git 的 22 端口
所以:
ssh -p 2222 git@localhost本质是在连内网 Git
5. 转发脚本在做什么(拆解“魔法”)
示例转发配置(脱敏):
本机端口 → 内网目标
--------------------------------
2222 → git.internal:22
10443 → git.internal:443
20030 → db.internal:20030
6301 → redis.internal:6301脚本通常还会做这些增强(推荐):
- 启动前检查端口是否已监听
- SSH 掉线自动重连
- 写 PID / 日志,方便 stop
- ServerAliveInterval 防止“假死连接”
6. 外网访问内网 GitLab(网页)
6.1 HTTPS 转发原理
localhost:10443 → git.internal:443所以我们可以访问:
https://localhost:104436.2 为什么浏览器能打开,IDEA 有时不行
原因:证书校验
- 证书签发给:
git.internal.example.com - 我们访问的是:
https://localhost:10443
浏览器:
- 允许我们“忽略风险继续访问”
IDEA / 某些客户端:
- 校验证书更严格 → 可能拒绝
推荐解决方案(仅本机)
/etc/hosts
127.0.0.1 git.internal.example.com然后用:
https://git.internal.example.com:104437. 外网拉代码(Git clone / pull / push)
7.1 确认隧道正常
ssh -T -p 2222 git@localhost7.2 把公钥加到 GitLab
cat ~/.ssh/id_ed25519.pub复制 → GitLab → 用户设置 → SSH Keys → Add
7.3 使用正确的仓库地址
原内网地址(不可用):
git@git.internal.example.com:group/project.git外网通过转发使用:
ssh://git@localhost:2222/group/project.git新克隆
git clone ssh://git@localhost:2222/group/project.git已有仓库
git remote set-url origin ssh://git@localhost:2222/group/project.git
git pull --rebase8. 为什么之前会提示输入密码
原因只有一个:
GitLab 没接受我们的公钥
SSH 流程是:
- 尝试 publickey
- 失败 → 回退到 password
解决:
- 确认公钥已加
- 用下面命令强制验证:
ssh -T -p 2222 -o IdentitiesOnly=yes git@localhost9. IDEA 中如何图形化更新项目
9.1 修改远端
Git → 管理远程 → origin
ssh://git@localhost:2222/group/project.git9.2 使用系统 SSH(推荐)
设置 → 版本控制 → Git
SSH 可执行文件:原生这样 IDEA 会使用:
- ~/.ssh/config
- ~/.ssh/id_ed25519
- ssh-agent
10. GitLab 集成(MR / Issues)说明
这是 GitLab API 集成,不是 git 拉代码必须的。
要成功:
使用 Personal Access Token (PAT)
勾选 scope:
apiread_user
服务地址需证书可接受(见第 6 节)
不做 MR / Issues,可完全忽略此功能。
11. 后端在外网如何跑
核心思想:
服务跑在我们本机,依赖“伪装成”本地端口。
示例依赖映射
localhost:20030 → 内网 PostgreSQL
localhost:6301 → 内网 Redis后端配置示例
db:
host: 127.0.0.1
port: 20030
redis:
host: 127.0.0.1
port: 6301启动
export APP_CONFIG=etc/dev.local.conf
python wsgi.py server
python wsgi.py worker12. 前端在外网的通用思路
- 前端本地启动(npm / pnpm / yarn)
- API Base URL 指向本机后端:
http://localhost:5010若需直连内网服务:
- 同样通过 SSH 转发映射为 localhost
13. 常见问题速查表
| 现象 | 原因 | 解决 |
|---|---|---|
| Are you sure you want to continue connecting | Host Key 校验 | 输入 yes |
| Permission denied (publickey) | 公钥未加 | 加 SSH Key |
| 一直要密码 | key 未生效 | ssh -T 验证 |
| HOST IDENTIFICATION HAS CHANGED | known_hosts 冲突 | ssh-keygen -R |
| IDEA 拉代码 OK,GitLab 集成失败 | PAT / 证书问题 | 不影响 git |
总结一句话
我们只连跳板机一次,用 SSH 把整个内网“折叠”成 localhost。
工程里的关键补充点
安全与合规边界
- 只转发必要端口,避免把内网全量暴露
- 跳板机权限最小化,避免一人权限即全网权限
稳定性与可维护
- 自动重连要有退避策略,避免断线风暴
- 脚本日志要有轮转,避免磁盘占满
证书与域名
- 浏览器可忽略证书风险,但客户端不一定允许
- 本地 hosts 映射要谨慎维护,避免污染真实域名
性能与排障
- 本地端口冲突是最常见问题,先检查占用进程
- 出现卡顿时先看跳板机负载与带宽
