Dockerizing VPN Clients
将 VPN 客户端封装到 Docker 容器中,提供 SOCKS5 代理出口。支持两种模式:GUI 客户端使用 VNC,CLI 客户端使用 Web 管理界面。
适用场景
- 需要把现有 VPN 客户端封装到 Docker 中运行
- 需要通过容器提供 SOCKS5 代理出口
- 需要为 GUI 客户端提供 VNC 访问,或为 CLI 客户端提供轻量 Web 管理界面
- 需要复用仓库里的 Dockerfile / 启动脚本模板快速搭建原型
不适用
- 只需要普通 HTTP 代理、反向代理或零信任隧道,不需要 VPN 客户端
- 运行环境无法提供
NET_ADMIN、/dev/net/tun或 NAT 能力 - 需要生产级鉴权、审计、密钥管理,但当前方案只有最小原型级管理界面
- 需要直接容器化 WireGuard/OpenVPN 服务端,而不是客户端
快速工作流
- 先判断客户端是 GUI 还是 CLI,选定模式 A / B
- 确认运行环境具备
NET_ADMIN、/dev/net/tun、iptables 和目标架构支持 - 从
templates/复制对应 Dockerfile、启动脚本和配置文件 - 先打通
VPN -> tun0 -> danted链路,再验证 VNC 或 Web 管理界面 - 最后做容器启动、代理可用性和 NAT 转发验证
Decision Flow
VPN 客户端是否有 GUI?
├─ YES → 模式 A: VNC + Supervisor
│ ├─ xvfb 虚拟显示
│ ├─ x11vnc/tigervnc 远程访问
│ └─ 轻量桌面 (XFCE4/flwm)
│
└─ NO → 模式 B: Web 管理 + lighttpd
├─ lighttpd 提供 Web UI
├─ CGI 脚本封装 VPN 命令
└─ 前端展示状态/控制连接
Core Components
| 组件 | 作用 | 工具选择 | |------|------|----------| | VPN 客户端 | 建立 VPN 隧道,创建 tun0 | 原厂客户端 | | 代理层 | SOCKS5 代理出口 | danted (端口 1080) | | 管理层 | 用户交互界面 | VNC / lighttpd+CGI | | 网络层 | NAT 转发 | iptables MASQUERADE |
Implementation Steps
模式 A: GUI 客户端 (VNC)
-
Dockerfile 基础设置
- 基础镜像:
debian:12-slim - 安装:
xvfb,x11vnc,supervisor,xfce4,dante-server - 设置环境变量:
DISPLAY=:0,VNC_PASSWORD
- 基础镜像:
-
Supervisor 进程管理
[program:xvfb] command=Xvfb :0 -screen 0 1024x768x24 priority=1 [program:x11vnc] command=x11vnc -display :0 -forever -rfbport 5900 -rfbauth /root/.vnc/passwd priority=2 [program:vpn] command=/opt/start.sh priority=3 -
端口暴露: 5900 (VNC), 1080 (SOCKS5)
模式 B: CLI 客户端 (Web 管理)
-
Dockerfile 基础设置
- 基础镜像:
debian:12-slim - 安装:
lighttpd,dante-server,curl,iproute2,iptables - 复制 Web 资源到
/var/www/html/和/var/www/cgi-bin/
- 基础镜像:
-
lighttpd 配置
server.modules = ("mod_indexfile", "mod_cgi", "mod_alias") server.port = 8080 cgi.assign = ( ".cgi" => "" ) $HTTP["url"] =~ "^/cgi-bin/" { alias.url = ( "/cgi-bin/" => "/var/www/cgi-bin/" ) } -
CGI API 端点
?action=status- 查询连接状态?action=connect- 执行连接?action=disconnect- 断开连接
-
端口暴露: 8080 (Web), 1080 (SOCKS5)
Common Patterns
等待 tun0 后启动 danted
(while true; do
sleep 5
if [ -d /sys/class/net/tun0 ] && ! pgrep -x danted > /dev/null 2>&1; then
su daemon -s /bin/sh -c "/usr/sbin/danted -f /run/danted.conf"
fi
done) &
动态检测网络接口
externals=""
for iface in $({ ip -f inet -o addr; ip -f inet6 -o addr; } | sed -E 's/^[0-9]+: ([^ ]+) .*/\1/'); do
externals="${externals}external: $iface\\n"
done
sed s/^#external-lines/"$externals"/ -i /run/danted.conf
NAT 配置 (幂等)
update-alternatives --set iptables /usr/sbin/iptables-legacy 2>/dev/null || true
iptables -t nat -C POSTROUTING -o tun0 -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
安全规则
# 拒绝 tun0 侧主动连接
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -i tun0 -p tcp -j DROP
CGI JSON 响应
echo "Content-Type: application/json"
echo ""
echo '{"success": true, "data": {...}}'
Templates Quick Reference
| 文件 | 用途 | 模式 |
|------|------|------|
| Dockerfile.gui | GUI 模式镜像 | GUI |
| Dockerfile.cli | CLI 模式镜像 | CLI |
| supervisord.conf | Supervisor 进程管理 | GUI |
| start-gui.sh | GUI 模式启动脚本 | GUI |
| start.sh | CLI 模式启动脚本 | CLI |
| danted.conf | SOCKS5 代理配置 | 通用 |
| lighttpd.conf | Web 服务器配置 | CLI |
| api.cgi | CGI API 脚本 | CLI |
所有模板位于 templates/ 目录。
Docker Run 示例
# GUI 模式
docker run -d --name vpn \
--cap-add NET_ADMIN \
--device /dev/net/tun \
-p 5900:5900 \
-p 1080:1080 \
-v ./config:/opt/vpn/conf \
vpn-client:gui
# CLI 模式
docker run -d --name vpn \
--cap-add NET_ADMIN \
--device /dev/net/tun \
-p 8080:8080 \
-p 1080:1080 \
-v ./config:/opt/vpn/conf \
vpn-client:cli
Checklist
实现前检查:
- [ ] 确认 VPN 客户端类型 (GUI/CLI)
- [ ] 确认客户端支持的架构 (amd64/arm64)
- [ ] 获取 VPN 客户端安装包
实现后验证:
- [ ] 容器启动无错误
- [ ] VPN 可成功连接 (tun0 创建)
- [ ] danted 在 tun0 创建后自动启动
- [ ] SOCKS5 代理可用 (
curl --socks5 localhost:1080 ...) - [ ] 管理界面可访问 (VNC:5900 或 Web:8080)
- [ ] NAT 转发正常工作
Anti-Patterns
| 错误做法 | 正确做法 | |----------|----------| | 在 tun0 创建前启动 danted | 后台循环检测 tun0 后启动 | | 直接使用 iptables-nft | 切换到 iptables-legacy | | 硬编码网络接口 | 动态检测所有接口 | | 同步等待 VPN 连接 | 后台重试,不阻塞启动 |
Scan to join WeChat group