13 min更新 服务器

用 Docker 部署 UniVPN,让多个 VPN 连接共存

把 UniVPN 放进独立容器,终端输入短信;默认选 Full 按原始 IP 使用 SSH 和浏览器,Lite 则通过 SOCKS5 逐应用接入。

目录13 节

Windows 的 UniVPN 客户端一次只能连接一套环境。我需要保留原来的 VPN,同时访问另一套内网,又不想多开一台 Windows 虚拟机,于是把额外的 UniVPN 放进 Docker。

每套 UniVPN 独占一个容器,日常使用默认选 Full。 它通过 Windows WireGuard 网卡接入,连上后像普通 VPN 一样,SSH、浏览器直接填写原始内网 IP。只有明确不想安装宿主机网络组件、并愿意逐应用配置代理时,再选 Lite。

配套项目是 JavaZeroo/univpn-docker-gateway,预构建镜像位于 Docker Hub。本文对应 0.4.0:lite / 0.4.0-lite 是代理版,full / 0.4.0-full 是原始 IP 接入版;latest 和 0.4.0 继续指向 Full。

默认推荐 Full,明确需要应用代理时再选 Lite

Lite 的容器依赖少一些,但使用步骤不一定更少。它提供 SOCKS5 代理,SSH 客户端和浏览器都得显式配置;Full 把入口放在网卡和路由层,应用通常不需要额外设置。

对比项Full:默认推荐Lite:按需选择
使用体验原始 IP,SSH、浏览器按平常方式使用每个应用配置 SOCKS5
支持范围指定 IPv4 目标的 TCP / UDPIPv4 TCP CONNECT;无 UDP、BIND、目标域名解析
宿主机依赖WireGuard 与专属网卡,首次可能需要 UAC不安装 WireGuard,不改 Windows 网卡或系统代理
主要优点符合普通 VPN 的使用习惯,适合多个日常工具容器部署集中,可由不同应用选择不同 VPN
需要接受的代价网段重叠会影响系统选路,需要管理宿主机入口OpenSSH 需额外代理程序,浏览器需处理代理范围

下面统一以示例服务器 10.80.0.11 的 SSH 22 端口和 http://10.80.0.11:8080/ 说明。服务端也要准备好:HTTP 服务监听内网地址或 0.0.0.0:8080,SSH 与 HTTP 端口在远端网络策略中可达。VPN 不会替服务器启动服务或开放端口。

两版都不用为远端的 22、8080 分别增加 Docker 端口映射。 目标属于允许的 CIDR、服务可达即可:Full 通过目标路由访问,Lite 的 SSH 和 HTTP 共用同一个 SOCKS5 入口。

架构设计总览:两种入口接入独立容器

两版的远端连接都由官方 UniVPN 引擎负责。区别在本机到容器这一段:Lite 发布 localhost TCP 代理端口,Full 发布 localhost UDP 端口并连接 Windows WireGuard 网卡。

业务数据 认证控制;每个实例选择一版

01 Windows 宿主机

Lite:逐应用设置 SOCKS5Full:按目标 IP 选路应用显式配置代理仅接管配置的目标 CIDR支持 SOCKS5 的应用数字 IPv4 · 仅 TCP CONNECTlocalhost TCP 1080不安装 WireGuard,不改网卡普通 Windows 应用使用原始 IP · TCP / UDPWindows WireGuard专属 univpn-* 服务与网卡经 localhost UDP 接入容器
Lite 只有 Docker 入口。Full 的 WireGuard 程序安装或复用发生在 Windows 宿主机。

02 Docker Desktop Linux VM

Lite 容器的数据面Full 容器的数据面两版共用的官方引擎仅 VPN 出口同一容器内转发只允许指定 VPN 出口Python 标准库 SOCKS5目标 CIDR 与真实就绪检查出站 socket 绑定 VPN 接口无 UDP / BIND / 域名 DNSwg0:WireGuard 内核接口由容器 wireguard-tools 管理iptables FORWARD + SNAT目标白名单 · 健康失败时 DROPUniVPNCS → cnem_vnic每套 VPN 独立网络命名空间
两条路径代表不同版本。每套 VPN 一个容器,Lite 无 WireGuard 工具,两版均无桌面。

03 远端 VPN 与业务网段

由远端网关提供内网接入远端 UniVPN 网关沿用密码 / 短信认证两版使用同一原生认证代码已授权目标 CIDRLite:SSH / HTTP 等 IPv4 TCPFull:IPv4 TCP / UDP目标看到容器 VPN 来源地址
业务服务器无需部署 WireGuard。选了哪版,就按哪版的协议范围验收。

共用控制面:终端认证与原会话短信续交

用户终端所选版本的 VPN 容器用户终端Lite:docker run · Full:univpn.cmd容器 vpnctl / Python 服务本地 Unix IPC → CSDK/RPC官方 UniVPNCS密码与短信续交到同一会话
账号、密码和短信只在真实终端输入,不写配置;后台探针决定数据入口是否放行。
用户终端Lite:docker run · Full:univpn.cmd容器 vpnctl / Python 服务本地 Unix IPC → CSDK/RPC官方 UniVPNCS密码与短信续交到同一会话
真实终端输入认证信息,后台探针决定是否放行;凭据不写配置。

Full 才需要宿主机 WireGuard 和 UAC;专属 univpn-* 不修改旧 tunnel 配置,但重叠目标会参与 Windows 选路。Lite 需要逐应用指定 SOCKS5。

图中的 Lite 与 Full 是两个可选版本,每个实例只运行所选版本。Lite 不安装 WireGuard,也不修改 Windows 网卡或路由。 Full 的 WireGuard 程序与专属服务安装在 Windows 宿主机,容器里只包含 Linux 工具和 wg0 内核接口;不需要第二个 WireGuard 服务容器。

Full 连接后直接使用 SSH 和浏览器

Full 只需预装 Docker Desktop。在 Windows 新建空文件夹,在该文件夹打开 PowerShell:

powershell
docker run --rm -v "${PWD}:/output" javazero/univpn-docker-gateway:full export-client
.\univpn\univpn.cmd

第一条把 Windows 启动器导出到当前文件夹的 univpn/ 子目录。第二条在同一个 PowerShell 窗口运行它;.\ 表示当前目录。启动器按需准备官方 WireGuard、生成配置和配对密钥,再启动容器,收取登录信息并续交短信。

已有签名有效的 WireGuard 安装会复用,不升级或重装。首次安装 Windows 程序与创建专属 univpn-* 隧道可能分别需要 UAC;若安装器要求重启,启动器会停止流程并告知用户。执行策略例外只限本次 PowerShell 进程。

填写网关、精确目标 CIDR 和 SSH 探针后,容器先完成认证、隧道与真实探针检查,再安装或核验 Windows 网卡。最后由 Windows 普通连接检查目标 SSH banner。连接后可以直接使用:

powershell
ssh your-user@10.80.0.11
curl.exe --noproxy "*" http://10.80.0.11:8080/

浏览器直接打开 http://10.80.0.11:8080/,WindTerm 等 SSH 客户端填目标 IP 和 22 端口。若浏览器、SSH 或其他应用已有代理配置,需让这个内网目标绕过应用代理;网卡路由不会取消应用自己的代理。上面的 --noproxy 只让这次 curl 请求直接连接。

Full 通过目标路由承载 TCP / UDP;探针使用 SSH,不代表转发只支持 SSH。WireGuard 位于宿主机的 C:\Program Files\WireGuard,容器中的 wireguard-tools 不能代替 Windows 网卡。

启动器不修改旧 tunnel 配置,但目标 CIDR 重叠仍可能改变 Windows 实际选路,需要核对。不能把独立名称理解成所有流量都互不影响。Full 快速开始包含首次使用细节。

Lite 在容器终端启动并提供 SOCKS5 代理

先启动 Windows x64 的 Docker Desktop,使用 Linux 容器 / WSL2。在 PowerShell 执行:

powershell
docker run --rm -it --log-driver none --name univpn-lite `
  --cap-add NET_ADMIN --device /dev/net/tun `
  -p 127.0.0.1:1080:1080 `
  -v univpn-lite-config:/config `
  -v univpn-vendor-cache:/var/cache/univpn `
  javazero/univpn-docker-gateway:lite

首次在容器终端填写网关 host:port、允许访问的 IPv4 CIDR,以及其中一台 SSH 探针的 IP:port。例如只访问 10.80.0.11/32 时,可默认使用该地址的 22 端口。网络配置保存到具名卷里的 /config/lite/vpn.json,不用准备本机 JSON 文件。

随后在同一终端输入 VPN 账号、密码与本次短信。连接后保持终端运行,Ctrl+C 停止;网络配置和已验证的厂商缓存会保留,凭据不写配置,也不会自动重发短信。

Lite 的 SSH 需要代理客户端

以 Windows OpenSSH 为例,先准备 Nmap 的 Ncat,确保 ncat 在本机 PATH 中;它不是 Windows 内置命令,也不因为启动了容器就自动装好。Ncat 的官方用法支持 --proxy 指定代理地址、--proxy-type socks5 选择协议。Ncat 代理说明

将下面一段加入 ~/.ssh/config,Windows 对应 $HOME\.ssh\config。放在通用的 Host * 规则之前,不要覆盖已有配置:

ssh-config
Host demo-lite
    HostName 10.80.0.11
    User your-user
    Port 22
    ProxyCommand ncat --proxy 127.0.0.1:1080 --proxy-type socks5 %h %p

然后运行 ssh demo-lite。这里 %h 和 %p 分别替换为目标主机和端口,OpenSSH 通过 Ncat 的输入输出完成连接;SSH 用户、密钥和服务器校验仍由 OpenSSH 处理。OpenSSH ProxyCommand 与占位符

直接执行 ssh your-user@10.80.0.11 不会匹配 demo-lite 这一段,因此也不会自动走该代理。支持 SOCKS5 的图形 SSH 客户端可以改用它自己的会话代理设置,地址同样是 127.0.0.1:1080。

Lite 的 HTTP 通过 curl 或专用浏览器配置访问

curl 显式选择 SOCKS5,仍访问同一个服务:

powershell
curl.exe --proxy socks5h://127.0.0.1:1080 http://10.80.0.11:8080/

这里只支持数字 IPv4,socks5h 不代表代理能解析真正的目标域名。

浏览器可用 Firefox 的手动代理:在设置中搜索“代理”,打开连接设置,选择手动配置;HTTP / HTTPS 代理留空,SOCKS 主机填 127.0.0.1、端口 1080,选择 SOCKS v5,然后打开 http://10.80.0.11:8080/。确认目标未列入“不使用代理”的范围。Firefox 连接设置、Mozilla 的 SOCKS 配置定义

这会作用于该浏览器 profile 的代理连接,不是仅代理当前网页。本项目拒绝目标 CIDR 以外的请求,所以同一 profile 打开的普通公网网站可能失败。建议通过 about:profiles 新建一份专用 profile,只用于这套内网,不改 Windows 系统代理。Firefox profile 管理

Lite 使用 Python 标准库实现 SOCKS5 CONNECT-only / IPv4 TCP,不支持 UDP ASSOCIATE、BIND、IPv6 或域名 DNS。规范的数字 IPv4 即使放在 SOCKS5 的域名字段中,也可以使用。代理仅允许配置的目标 CIDR,不能把它当作通用公网代理。Lite 使用说明记录了完整范围。

两版的数据面不同,认证和真实就绪判断共用

容器拥有独立网络命名空间,UniVPN 创建的 cnem_vnic、路由与过滤规则留在对应实例。Docker Desktop 的 Linux 容器位于虚拟机内部,不能直接把容器 IP 当作 Windows 路由下一跳,所以需要代理端口或 WireGuard 入口。

Lite:应用代理转发 IPv4 TCP

应用显式选择代理SOCKS5 握手与目标请求白名单与探针就绪后连接仅允许 VPN 接口配置了 SOCKS5 的应用数字 IPv4 目标 · 仅 TCP CONNECTlocalhost TCP 1080Docker 发布到本机回环地址容器内 Python SOCKS5目标 CIDR、就绪状态与路由检查无 UDP / BIND / 域名解析出站 socket 绑定 cnem_vnicVPN 失效不能回落到 eth0UniVPN 网关 → 授权内网数据经过 Python 用户态代理
不安装 WireGuard。每个应用选择代理,裸 SSH 或 HTTP 直连不会自动进入容器。

Full:网卡与路由转发 IPv4 包

Windows 默认选路本机 WireGuard 隧道容器内核转发就绪放行,失效 DROP普通 Windows 应用原始 IP 与端口 · TCP / UDPWindows WireGuard 网卡仅接管 AllowedIPs 中的目标Docker 容器内的 wg0通过 localhost UDP 接入与 UniVPN 同一网络命名空间iptables FORWARD 与 SNAT目标 CIDR / 源地址 / VPN 出口cnem_vnic → UniVPN 网关 → 内网数据不经过 Python 用户态代理
宿主机需要 WireGuard。SNAT 使回复沿原 VPN 和本机 WireGuard 返回,支持 TCP / UDP。

Lite 的业务连接经过 Python SOCKS5 服务。它检查目标 CIDR、真实就绪状态和目标路由,并把出站 socket 绑定到 cnem_vnic。VPN 不可用时关闭代理流量,不能改从 eth0 发出。

Full 的业务包经过 wg0、iptables FORWARD 与 SNAT,不经 Python 用户态代理。SNAT 将来源换成容器的 VPN 地址,让回包沿原路返回。专属转发链只允许配置的目标和 VPN 出口;后台探针失败后切换为 DROP。

两版都由终端前端调用 vpnctl,Python 服务再通过 CSDK/RPC 控制官方 UniVPNCS。验证码续交到原引擎、原认证会话。守卫检查认证、接口、路由和真实 SSH 响应,状态查询读取缓存;轮询和探针超时会带来检测延迟。认证成功、容器 Up 或端口存在,都不能代替数据面验证。

证书默认验证 CA 链和主机名。自签证书需要事先独立核实叶证书 SHA256,再显式批准;独立 TLS 预检不等于引擎侧连接 pinning。密码、短信不保存到参数、环境变量或配置中,也不使用桌面自动化登录。

技术栈分为共同部分与版本专用部分

部署两版都不需要 Git、本机 Python、手工生成密钥或本地构建。共同技术栈是 Docker Desktop / WSL2、Ubuntu、Python 标准库与官方引擎,WireGuard 仅属于 Full;Lite 若使用 OpenSSH,还需本机 Ncat 等代理程序。

运行 Linux 容器两版共同的运行平台共同宿主机:Windows x64Docker CLI · PowerShell 终端Docker Desktop / WSL2Lite:docker run · Full:Compose共享 Linux 内核独立网络命名空间 · TUN容器权限:NET_ADMIN
两版都仍有 Docker Desktop / WSL2 平台开销,Lite 不等于直接运行在 Windows 的轻量进程。
公开镜像不含厂商二进制不执行厂商安装脚本共同镜像基础:Ubuntu / amd64Python 标准库 · tini · 网络工具 · CA原生认证代码 + 官方引擎首次官方下载与逐层 SHA256 校验具名卷只缓存已校验的 ZIP每个容器提取自己的运行副本密码和短信不写配置
无需本机 Python 或构建镜像。两版均无 Qt、X11、VNC 或额外 Python 第三方库。
逐应用选择 localhost 代理配置与启动均在容器终端Lite 的应用依赖客户端需要支持 SOCKS5Python 标准库 SOCKS5IPv4 TCP CONNECT · 精确目标 CIDR终端 wizard + Docker 配置卷无 Windows 脚本 / 网卡无 wireguard-tools
lite / 0.4.0-lite:不解析目标域名,不支持 UDP、BIND 或 IPv6。
配对密钥与本机 UDP 隧道用户态工具管理内核接口Full 的 Windows 依赖启动器 · WireGuard 程序与网卡Full 容器:wireguard-toolswg0 与 iptables 过滤 / SNATLinux 内核 WireGuard / netfilter同一 VPN 容器内转发无需额外 WireGuard 服务容器
full / 0.4.0-full:原始 IP 接入;latest 与 0.4.0 兼容指向 Full。宿主机安装可能需要 UAC。
共同依赖Lite 额外部分Full 额外部分
Ubuntu 22.04 amd64、Python 标准库、tini、iproute2、iptables、CA、TUNPython SOCKS5 服务,配置保存于 Docker 卷Windows WireGuard 与启动器;容器 wireguard-tools 和内核 wg0
原生认证控制器、官方 UniVPNCS 与辅助组件发布 localhost TCP 代理端口发布 localhost UDP,生成配对配置与专属 Windows 服务

Dockerfile.public 不包含厂商二进制。首次从官方获取固定 10781.21.0.0831,依次校验 ZIP、内嵌 run 文件及六个运行文件的 SHA256,只提取白名单文件,不执行安装脚本。具名卷保存已验证的 ZIP 缓存,各容器加载自己的运行副本;失败不使用未校验文件。镜像说明记录了固定来源与摘要。

容器使用 bridge 网络、NET_ADMIN 与 /dev/net/tun,不使用 privileged 或 host 网络。两版都没有 Qt、X11、Xvfb、DBus、VNC 或额外 Python 第三方依赖;Lite 也没有 wireguard-tools 和 Windows 脚本。厂商程序遵循自身许可,项目自写代码采用 GPL-3.0-only。

多实例分别选择代理端口或接入网段

Lite 新增一套 VPN 时,更换容器名、配置卷和本机发布端口,例如将 univpn-lite、univpn-lite-config、127.0.0.1:1080:1080 改成 univpn-work、univpn-work-config、127.0.0.1:1081:1080。应用选择对应的 SOCKS5 端口,就选择了对应 VPN;厂商缓存卷可以复用。

Full 新增一套 VPN 时,使用不同名称、UDP 端口和接入网段:

powershell
.\univpn\univpn.cmd -Name work -UdpPort 51830 -AccessSubnet 10.204.0.0/30

以后加 -Name work 即可操作这个 profile。接入网段需避开局域网、其他 VPN 和 Docker 网络。Windows 按目的 IP 选路,Full 不能同时区分两套 VPN 中相同的目标地址;Lite 可以由不同应用选择各自代理,但也不能绕过网关的账号并发限制。

两版的停止方式与短信续交命令

Lite 前台 Ctrl+C 停止,--rm 移除本次容器,配置和缓存卷仍保留。运行期间可在另一终端用 docker exec univpn-lite vpnctl status 检查状态,或用 docker exec -it univpn-lite vpnctl sms 续交现有短信。

sms 只续交尚未过期的现有挑战。失败、过期或取消后,停止当前容器,再显式执行启动命令发起新尝试。

Full 使用同一个 Windows 入口:

powershell
.\univpn\univpn.cmd                   # 连接;有效短信挑战会续交
.\univpn\univpn.cmd -Mode Status      # 状态和最佳路由
.\univpn\univpn.cmd -Mode Stop        # 停容器,保留网卡、密钥和缓存
.\univpn\univpn.cmd -Mode Remove      # 移除本实例网卡与容器,保留文件和缓存

Full 的 Stop 保留 Windows 路由,目标可能继续指向已停止的入口;需释放入口时用 Remove。删除新网卡后,旧 VPN 可能重新接管重叠目标,需要用最佳路由区分回退。两版都不自动重新登录或重发短信。

已有 Full 链路实测与新增 Lite 入口分别验收

2026-10-29 的原数据链验收使用 Windows 普通 TCP / UDP socket 和 HTTP 客户端,没有绑定接口、源地址或应用代理,并交叉核对最佳路由、WireGuard 计数与远端 VPN 来源。

验证范围证据与边界
共用原生认证路径无 GUI 的真实短信续交成功,同一引擎会话完成认证与隧道启动
Full 原始 IP 数据链4/4 SSH banner;双向各 1 MiB HTTP 长度与摘要匹配;32、1200、4096 字节 UDP 正确往返
Full 探针故障与恢复SSH、HTTP、UDP 全部阻断,恢复后再次通过,原认证保留
Full 宿主机准备与启动器一次性 Windows CI 实装 WireGuard 通过,本机复用既有安装;配置、密钥和只读预检通过,完整新用户接入仍需单独验收
公开镜像组件准备官方冷缓存下载、无网络新容器复用缓存,以及隔离官方引擎启动通过
Lite 新 SOCKS5 入口不能沿用 Full 数据链结论;仍待真实 VPN 登录后的端到端验收

原 Full 数据链使用 4 个精确 /32,UniVPN MTU 为 1300,WireGuard 为 1280;大 UDP 分片往返只能证明当次路径可用。探针故障没有断开厂商 VPN 会话,不能替代真实断线、重新认证或 Docker 重启恢复。DNS、IPv6、服务重建后的路由优先级也未完整验收;Lite 明确不提供 UDP 和目标域名解析。完整实测记录按各层记录证据。

依赖数量与实际资源占用分别判断

Lite 省的是 Windows 组件的安装与配置。Full 多了宿主机网卡、路由与授权步骤,不能因此推断其内存明显更高;两版仍运行同一官方引擎,也都依赖 Docker Desktop / WSL2。选择 Lite 还需接受逐应用设置代理的成本。

镜像大小、空闲未认证容器、已认证但无业务流量的容器、实际传输负载,以及整个平台占用需要分别测量。未认证的空闲样本不能代表认证后的常驻占用,更不能当作整机资源承诺。

2026-10-29 在同一 Docker Desktop 27.3.1 / Linux amd64 环境,用两版 0.4.0 镜像各启动一个隔离容器,限制均为 384 MiB、0.5 CPU、128 个进程。官方引擎处于未认证的 idle 状态,无业务流量;Full 的测试 wg0 已启动,Lite 的 SOCKS5 端口已监听。

容器后台服务5 次工作集样本(MiB)中位数
Lite34.00 / 34.00 / 34.00 / 34.00 / 34.0034.00 MiB
Full35.97 / 35.97 / 35.97 / 35.97 / 35.9735.97 MiB

数据来自 docker stats;此处 cgroup v1 的命令行口径扣除了 total_inactive_file。两边均使用后台 serve 入口、相同官方缓存,禁用健康检查,并在无外部网络的独立命名空间采样。测量后移除了测试容器,未重新登录或重启现有 VPN。Docker stats 的内存口径

本次空闲样本仅相差约 2 MiB,不能据此宣称 Lite 的实际使用内存明显更低。 它不包含 Docker Desktop / WSL2、Windows WireGuard,也不包含 Lite 常用前台 run 入口的额外 Python 管理进程;认证和传输时的开销仍需分别测量。

因此,Lite 的“轻”主要指少安装宿主机网络组件,代价是逐应用配置。Full 多了网卡、路由与权限准备,却更符合日常 VPN 的使用习惯。是否值得选择 Lite,主要看能否接受这些配置成本,而不是这次约 2 MiB 的差值。

旧 CLI 菜单与手工转发保留为参考

最初参考 jesusdf/huawei-vpn,用 expect 驱动厂商 CLI,再以 socat 暴露 SSH 端口。旧菜单在本次环境里没有短信续交流程,后续通过独立 Python 前端与官方 CSDK/RPC 路径解决,不再需要图形桌面。

旧 CLI 对照记录和开发者构建说明仍保留。首次使用按自己的需求选择 Lite 或 Full,无需克隆项目、手写多份 JSON 或逐个映射 SSH 端口。

评论