http-tunnel 是一个基于 WebSocket 的 TCP 隧道系统,用于把“本地访问虚拟 IP:端口”转发到远端内网真实服务。
典型场景是:用户只访问固定地址(如 10.2.2.123:30080),由 agent 通过隧道连到服务端,再由服务端转发到内网目标地址。
主要解决的痛点:
- 研发团队常见限制:只能开通 HTTP/HTTPS 端口,不能额外开放自定义 TCP 端口
- 内网 TCP 服务(如管理端口、私有 API、调试端口)无法被外部直接访问
- 终端用户不希望配置复杂网络环境,只希望访问固定地址即可连通目标服务
- 多个内网目标需要统一出口与统一认证,降低运维复杂度
本项目的做法:
- 复用已开通的 HTTP/HTTPS 端口承载 WebSocket 隧道
- 在隧道内传输内部 TCP 流量,实现“HTTP 口复用的 TCP 代理”
核心组件:
go/cmd/htunnel-server:服务端,维护 agent 连接并按vip_map转发到真实 IPgo/cmd/htunnel-agent:客户端 agent,建立隧道并承接本地流量go/cmd/htunnel-ui:桌面端连接器(账号密码登录、自动获取 token 和路由)go/internal/protocol/tunnel.proto:隧道消息协议定义(Protobuf)
- Agent 与 Server 通过 WebSocket 长连接通信(
ws/wss) - 使用 JWT 做 Agent 认证(HS256)
- 服务端支持虚拟 IP 到真实内网 IP 的一对一静态映射
- Agent 内置 SOCKS5,并可托管
tun2socks进程实现虚拟网卡流量接入
二选一:
- 本机运行:
cd go
go run ./cmd/htunnel-server -config configs/server.yaml- Docker 部署:
docker pull loongtall/htunnel-server:latest
docker run --rm -p 8082:8082 \
-v $(pwd)/go/configs/server.yaml:/app/configs/server.yaml:ro \
loongtall/htunnel-server:latest- 如果要固定版本,可把
latest换成具体 tag(如v2.1.3)
编辑 go/configs/server.yaml,至少确认三项:
listen.addr:服务监听地址(默认:8082)auth.users:客户端登录账号密码auth.users.<name>.route_cidrs:该用户下发到客户端的网段(用于本地路由)network.vip_map:虚拟 IP 到真实目标 IP 的映射network.allow_direct_ip_fallback:是否允许未命中vip_map时直连目标 IP(默认false)false:只允许访问白名单映射(更安全,推荐生产)true:vip_map未命中时直接访问dst_vip(更灵活,但权限边界更宽)
示例:
auth:
users:
alice:
password: "pass123"
route_cidrs:
- "10.2.2.123/32"
network:
allow_direct_ip_fallback: false
vip_map:
"10.2.2.123": "172.28.52.13"说明:如果用户未配置 route_cidrs,服务端会默认把 vip_map 中的 VIP 按 /32 下发给客户端。
- 从 GitHub Release 页面下载对应平台压缩包并解压
Release:https://github.com/llongtao/http-tunnel/releases - 进入解压目录,执行:
./start.shstart.sh默认优先启动 UI(需要管理员权限),若 UI 不存在则回退启动 agent- 当
auth.token为空时,agent 会使用auth.username/password调用/api/agent/login自动获取 token 和路由配置
go/configs/server.yaml:服务端配置样例go/configs/agent.yaml:agent 配置样例
cd go
go mod tidy
go run ./cmd/htunnel-server -config configs/server.yaml另开一个终端运行 agent:
cd go
go run ./cmd/htunnel-agent -config configs/agent.yaml构建镜像(项目根目录):
docker build -t htunnel-server:latest .运行容器:
docker run --rm -p 8082:8082 \
-v $(pwd)/go/configs/server.yaml:/app/configs/server.yaml:ro \
htunnel-server:latest客户端支持原生桌面界面(非浏览器页面),推荐流程:
- 填服务端地址 + 账号密码
- 点击“连接”(自动向服务端登录并获取 token)
- 自动把
agent_id设置为用户名(或服务端返回值) - 自动下发
route_cidrs到本地配置 - token 未过期时,桌面端启动后会自动连接
启动桌面端:
cd go
sudo go run -tags "desktop,production" ./cmd/htunnel-ui -config configs/agent.yamlmacOS 如果出现 Undefined symbols ... _OBJC_CLASS_$_UTType,请加上:
cd go
sudo CGO_LDFLAGS='-framework UniformTypeIdentifiers' go run -tags "desktop,production" ./cmd/htunnel-ui -config configs/agent.yaml或使用根目录命令:
make run-ui用户不需要单独下载 tun2socks。发布时可直接打一个自包含包:
cd go
./scripts/release.shWindows 可用:
cd go
.\scripts\release.ps1产物位于 go/dist/,每个平台目录都包含:
bin/htunnel-agentbin/htunnel-ui(INCLUDE_UI=1默认启用)bin/tun2socksbin/tap-windows-installer.exe(可选,若存在会被打进 windows 包并被start-*.cmd自动调用)drivers/windows/tap.win10(可选,若存在会被打进 windows 包并被start-*.cmd自动调用devcon + OemVista.inf)configs/agent.yamlstart-agent.sh/start-agent.ps1/start-agent.cmdstart-ui.sh/start-ui.ps1/start-ui.cmdstart.sh/start.ps1/start.cmd(默认优先启动 UI,不存在 UI 时回退到 agent)
示例(mac):
cd go/dist/htunnel-agent-darwin-arm64
./start-ui.sh示例(windows,管理员 PowerShell):
cd go\dist\htunnel-agent-windows-amd64
.\start-ui.ps1说明:UI 启动即要求管理员权限(避免连接时权限不足)。
建议优先使用 start-ui.cmd / start.cmd,脚本会自动申请管理员权限,且在缺少 TAP/Wintun 时会尝试运行包内 bin\tap-windows-installer.exe(若已打包)。
直接双击 bin\htunnel-ui.exe 可能会因权限或配置错误而直接退出。
若 Windows UI 仍然启动即退出,请确认系统已安装 Microsoft Edge WebView2 Runtime(Wails 桌面框架依赖)。
注意:tun2socks 依赖 cgo,请在目标平台本机打包(mac 打 mac 包,windows 打 windows 包)。
go/configs/server.yaml:
network:
allow_direct_ip_fallback: false
vip_map:
"10.2.2.123": "192.168.0.25"含义:访问 10.2.2.123:<port> 会被转发到 192.168.0.25:<port>。
可选:若将 allow_direct_ip_fallback 设为 true,当 vip_map 中不存在目标时,服务端会直接把 dst_vip 当作真实 IPv4 访问。
服务端配置 JWT 参数 + 账号:
auth:
jwt:
secret: "change-me"
issuer: "htunnel"
audience: "htunnel-agent"
expire_hour: 24
users:
alice:
password: "pass123"
route_cidrs:
- "10.2.2.123/32"桌面端调用 POST /api/agent/login:
- 入参:
username、password - 出参:
token、agent_id、ws_url、route_cidrs
ws_url 的生成逻辑(服务端):
- 若配置了
listen.public_ws_url,直接返回该地址(推荐) - 否则根据请求头推断:
X-Forwarded-Proto->ws/wssX-Forwarded-Host/Host-> 域名X-Forwarded-Port-> 端口(仅当 Host 不带端口时)
反向代理场景最佳实践:
- 最稳妥:在
server.yaml显式配置listen.public_ws_url - 同时让代理透传
X-Forwarded-Proto、X-Forwarded-Host、X-Forwarded-Port public_ws_url建议写外网可访问地址,例如:
listen:
public_ws_url: "wss://tunnelv2.sms-uat.gree.com:30443/websocket/message"agent 仍支持直接写 token(兼容旧方式):
server:
base_url: "http://127.0.0.1:8082"
auth:
token: "<jwt-token>"
username: "alice"
password: "pass123"
password_enc: "<encrypted-by-ui>"说明:桌面 UI 会把密码加密后写入 password_enc,不再持久化明文 password。
当通过命令行直接启动 agent 时,若 token 为空,会尝试使用 username/password 自动登录获取 token。
可用内置工具生成测试 token:
cd go
go run ./cmd/gen-jwt -secret change-me -issuer htunnel -audience htunnel-agent -agent agent-01agent 通过启动 tun2socks 接入虚拟网卡流量:
tun:
enabled: true
binary: ""
name: "utun9"
addr: "10.2.2.2"
gateway: "10.2.2.1"
mask: "255.255.255.0"
route_cidrs:
- "10.2.2.123/32"Windows 注意:
name: "utun9"仅适用于 macOS 示例。- Windows 下
tun.name为空或是utun*时,会自动探测现有 TAP/Wintun 网卡并自动填充。 - 若探测不到,
start-*.cmd会尝试执行bin\tap-windows-installer.exe自动安装驱动(如果该文件已随包提供)。 - 可用管理员 PowerShell 查看可选网卡名:
Get-NetAdapter | Format-Table -Auto Name, InterfaceDescription, Status - 如果仍没有 TAP/Wintun 网卡,请先安装对应驱动(例如 OpenVPN TAP-Windows 或 Wintun)。
开发环境可直接安装到 go/bin(中国网络可用镜像):
make install-tun2socks或手动执行:
cd go
mkdir -p bin
GOPROXY=https://goproxy.cn,direct GOBIN=$(pwd)/bin \
go install -tags socks github.com/eycorsican/go-tun2socks/cmd/tun2socks@v1.16.11binary 为空时,agent 会按以下顺序自动查找:
- 可执行文件所在目录的
bin/tun2socks - 配置文件目录下的
bin/tun2socks - 当前目录的
bin/tun2socks - 系统
PATH
并通过 auto_route_commands 配置系统路由命令(需管理员权限)。
建议同时配置 auto_route_cleanup_commands,agent 退出时自动清理临时路由,避免残留。
GitHub Actions 已切换为 Go 流水线:.github/workflows/go.yml。
新增 agent 打包流水线:.github/workflows/package-agent.yml,目前产物包含:
windows-amd64darwin-amd64darwin-arm64
当推送 v* tag(如 v2.1.0)时,会自动把上述产物作为附件上传到 GitHub Release。
新增服务端镜像发布流水线:.github/workflows/publish-server-image.yml。
- 触发:推送
v*tag 或手动触发 - 产物:
loongtall/htunnel-server:<tagname>(例如v2.1.0)和loongtall/htunnel-server:latest - 需要仓库 Secret:
DOCKERHUB_TOKEN(Docker Hub Access Token)