Files
lmvpn_server/README.md
T
kevin d674d40ca8 docs: 重写 README,聚焦 Linux 部署流程
- 新增一键部署、手动部署、反向代理(Caddy/Nginx)、服务管理、升级等章节
- 整合配置说明、JWT 密钥、Unix Socket 权限、首次登录初始化
- 顶部标注服务端部署仅测试 Linux 正常
- 保留目录说明与安全提示
2026-07-07 20:19:46 +08:00

367 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LMVPN
基于 WebSocket 隧道与 TUN 虚拟网卡的轻量级三层 VPN 系统,采用 Go + Vue 3 构建。服务端通过 WebSocket 与客户端建立控制通道,TUN 网卡与 WebSocket 之间双向搬运原始 IP 数据包,实现点对点与点对站点网络连接。
> **平台说明**:服务端部署**目前仅在 Linux 下测试功能正常**systemd + nftables/iptables NAT)。客户端协议规范见 [`docs/client-development.md`](docs/client-development.md)。
---
## 目录
- [环境要求](#环境要求)
- [一键部署(Linux,推荐)](#一键部署linux推荐)
- [首次登录与初始化](#首次登录与初始化)
- [手动部署](#手动部署)
- [配置说明](#配置说明)
- [反向代理(HTTPS/WSS](#反向代理httpswss)
- [服务管理](#服务管理)
- [升级](#升级)
- [目录说明](#目录说明)
- [常见问题排查](#常见问题排查)
- [相关文档](#相关文档)
---
## 环境要求
- **操作系统**:Linux(已测试),需 root 或 sudo 权限
- **Go**:≥ 1.26.4
- **Node.js**:≥ 22.18(用于构建前端)
- **内核**:支持 TUN 设备(`/dev/net/tun`),开启 `ip_forward`
- **防火墙工具**nftables(推荐)或 iptables
- **网络**:公网 IP,开放 Web 端口(或经反向代理)
---
## 一键部署(Linux,推荐)
仓库自带 `install_linux.sh`,一条命令完成构建、部署、网络配置与 systemd 服务安装。
```bash
git clone <仓库地址> lmvpn_server
cd lmvpn_server
sudo bash install_linux.sh
```
### 脚本执行流程
1. 拉取最新代码(`git fetch origin && git reset --hard origin/main`
2. 构建前端:`cd frontend && npm install && npm run build`(产物输出到 `../dist`
3. 编译后端:`go build -o lmvpn .`
4. 创建无 shell 的系统用户 `lmvpn`
5. 停止旧服务,部署到 `/opt/lmvpn`(二进制 + `dist/`),属主改为 `lmvpn`
6. 开启内核 IP 转发(IPv4 / IPv6),写入 `/etc/sysctl.d/99-lmvpn.conf`
7. 配置 NAT 与转发规则:自动检测出口网卡,优先 nft,回退 iptables
8. 安装 systemd 服务 `/etc/systemd/system/lmvpn.service`,以 `lmvpn` 用户运行并授予 `CAP_NET_ADMIN` / `CAP_NET_RAW`
9. 启动服务并打印状态
> ⚠️ 脚本中的 `git reset --hard origin/main` 会**丢弃所有本地未提交改动**。部署前请确保工作区干净,或先提交/暂存。
### 子网变量
脚本顶部有两个变量需与后台 VPN 设置保持一致:
```bash
VPN_SUBNET="192.168.77.0/24" # IPv4 子网
VPN_SUBNET6="fd00:dead:beef::/112" # IPv6 子网,留空则不配置 IPv6 NAT
```
若在管理后台修改了子网,需同步修改此处并重新执行脚本,或手动更新 NAT 规则,否则客户端无法上网。
---
## 首次登录与初始化
1. **获取初始密码**:首次启动时自动创建管理员 `admin`,密码为随机 16 位字符串,会
- 打印到 stdout(仅一次)
- 写入 `/opt/lmvpn/data/.initial_admin_password`(权限 0600
```bash
sudo cat /opt/lmvpn/data/.initial_admin_password
```
2. **登录**:浏览器访问 `http://<服务器IP>:8080`,使用 `admin` + 初始密码登录。
3. **立即修改密码**,并删除初始密码文件:
```bash
sudo rm /opt/lmvpn/data/.initial_admin_password
```
4. **启用 VPN**:进入「管理后台 → VPN 管理」,确认子网(默认 `192.168.77.0/24` + IPv6 `fd00:dead:beef::/112`),打开「启用」并保存。
5. **诊断检查**:VPN 管理页的「系统环境检测」面板(对应 `GET /api/admin/vpn/diag`)会检测 ip_forward、NAT、TUN 等,客户端无法上网时优先查看此处。
---
## 手动部署
如需自行控制部署细节,可参照以下步骤。
### 1. 构建产物
```bash
# 前端
cd frontend
npm install
npm run build # 产物输出到 ../dist
cd ..
# 后端
go build -o lmvpn .
```
### 2. 部署文件
```bash
sudo useradd -r -s /bin/false lmvpn
sudo mkdir -p /opt/lmvpn
sudo cp lmvpn /opt/lmvpn/
sudo cp -r dist /opt/lmvpn/
sudo chown -R lmvpn:lmvpn /opt/lmvpn
```
### 3. 开启内核转发
```bash
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv6.conf.all.forwarding=1 # IPv6 双栈时需要
cat <<'EOF' | sudo tee /etc/sysctl.d/99-lmvpn.conf
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF
sudo sysctl -p /etc/sysctl.d/99-lmvpn.conf
```
### 4. 配置 NATnft 示例)
将 `WAN_IFACE` 替换为出口网卡,`VPN_SUBNET` 替换为 VPN 子网:
```bash
WAN_IFACE=eth0
VPN_SUBNET=192.168.77.0/24
VPN_SUBNET6=fd00:dead:beef::/112
sudo nft add table inet lmvpn_nat
sudo nft 'add chain inet lmvpn_nat postrouting { type nat hook postrouting priority 100 ; }'
sudo nft add rule inet lmvpn_nat postrouting oifname "$WAN_IFACE" ip saddr "$VPN_SUBNET" masquerade
sudo nft add rule inet lmvpn_nat postrouting oifname "$WAN_IFACE" ip6 saddr "$VPN_SUBNET6" masquerade
sudo nft 'add chain inet lmvpn_nat forward { type filter hook forward priority 0 ; policy accept ; }'
sudo nft add rule inet lmvpn_nat forward ip saddr "$VPN_SUBNET" accept
sudo nft add rule inet lmvpn_nat forward ip daddr "$VPN_SUBNET" accept
```
### 5. 安装 systemd 服务
```bash
sudo tee /etc/systemd/system/lmvpn.service >/dev/null <<'EOF'
[Unit]
Description=LMVPN Server
After=network.target
[Service]
Type=simple
User=lmvpn
WorkingDirectory=/opt/lmvpn
ExecStart=/opt/lmvpn/lmvpn
Restart=on-failure
RestartSec=5
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now lmvpn
```
---
## 配置说明
配置文件位于 `data/config.yml`(相对工作目录,即 `/opt/lmvpn/data/config.yml`),首次启动时自动生成。
```yaml
web:
port: 8080 # TCP 监听端口,0 则不监听 TCP
sock: "/run/lmvpnweb.sock" # Unix Socket 路径,空则不监听 socket
sock_mode: "0666" # socket 文件权限
sock_group: "" # socket 文件属组,空=不修改
sock_dir_mode: "0755" # socket 目录权限
jwt_secret: "" # JWT 密钥,留空则按下面规则生成
database:
type: sqlite # sqlite 或 mysql
path: data/lmvpn.db # sqlite 文件路径
dsn: "" # mysql DSNtype=mysql 时必填)
```
> `web.port` 与 `web.sock` **至少配置一个**,两者都配则同时监听。生产环境建议仅留一个,由反向代理统一接入。
### JWT 密钥
按以下优先级加载:
1. 环境变量 `LMVPN_JWT_SECRET`
2. 配置文件 `web.jwt_secret`
3. 首次启动自动生成 32 字节随机密钥并写入配置文件
生产环境建议通过环境变量注入,避免密钥落盘:
```bash
# /etc/systemd/system/lmvpn.service 的 [Service] 段追加
Environment=LMVPN_JWT_SECRET=<你的密钥>
```
### Unix Socket 权限收紧
多租户/高安全场景建议收紧 socket 权限(将 lmvpn 进程用户加入反代用户组):
```yaml
web:
sock: "/run/lmvpnweb.sock"
sock_mode: "0660"
sock_group: "caddy"
sock_dir_mode: "0750"
```
> 若服务以非 root 用户运行且 socket 目录不可写,可将 `sock` 指向用户可写目录(如 `/opt/lmvpn/run/lmvpnweb.sock`),或设 `sock: ""` 仅用 TCP。
---
## 反向代理(HTTPS/WSS
生产环境**必须**通过反向代理提供 HTTPS/WSS。服务端默认监听 HTTP `:8080`,由反代终结 TLS。
### Caddy(自动 HTTPS
```caddyfile
lmvpn.example.com {
reverse_proxy 127.0.0.1:8080
}
```
Caddy 的 `reverse_proxy` 自动处理 WebSocket 升级,无需额外配置。
若使用 Unix Socket 接入:
```caddyfile
lmvpn.example.com {
reverse_proxy unix//run/lmvpnweb.sock
}
```
### Nginx
```nginx
server {
listen 443 ssl http2;
server_name lmvpn.example.com;
ssl_certificate /etc/ssl/certs/lmvpn.pem;
ssl_certificate_key /etc/ssl/private/lmvpn.key;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /ws {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
```
> 部署反代后,客户端应使用 `wss://lmvpn.example.com/ws` 连接。`/ws` 的长连接超时建议调大(如 3600s),避免空闲断连。
---
## 服务管理
```bash
sudo systemctl start lmvpn # 启动
sudo systemctl stop lmvpn # 停止
sudo systemctl restart lmvpn # 重启
sudo systemctl status lmvpn # 状态
sudo systemctl enable lmvpn # 开机自启
```
查看实时日志:
```bash
sudo journalctl -u lmvpn -f
```
---
## 升级
最简方式:重新执行一键脚本,它会自动拉取最新代码、重新构建并重启服务。
```bash
cd lmvpn_server
git pull
sudo bash install_linux.sh
```
> 脚本会 `git reset --hard origin/main`,本地改动会被覆盖。`/opt/lmvpn/data/` 下的配置与数据库不受影响。
---
## 目录说明
- `data/` — 运行时数据(`config.yml`、`lmvpn.db`、初始密码文件),gitignore
- `dist/` — 前端构建产物,由 Go 服务端托管,gitignore
- `frontend/` — 前端工程(Vue 3 + TypeScript + Vite
- `internal/` — Go 后端源码
- `config/` — 配置加载
- `db/` — 数据库初始化
- `handler/` — HTTP 请求处理
- `middleware/` — 中间件(认证、限流)
- `model/` — 数据模型
- `router/` — 路由
- `vpn/` — VPN 核心(认证、隧道、TUN、包转发)
- `docs/` — 文档
- `pytest/` — Python 测试脚本
- `install_linux.sh` — Linux 一键部署脚本
- `main.go` — 服务端入口
---
## 常见问题排查
| 现象 | 可能原因 | 排查建议 |
|------|----------|----------|
| 服务启动失败 | socket 目录不可写 / 端口占用 | `journalctl -u lmvpn`;设 `sock: ""` 或换可写目录 |
| 客户端连上但无法上网 | 未开 ip_forward / 未配 NAT / 子网不一致 | 管理后台诊断面板;核对脚本 `VPN_SUBNET` 与后台设置 |
| 登录提示「请求过于频繁」 | 触发限流(`/api/login` 5 次/分钟·IP) | 等待 1 分钟后重试 |
| 修改子网后客户端异常 | NAT 规则仍是旧子网 | 同步脚本变量并重跑 `install_linux.sh` |
| WebSocket 频繁断开 | 反代未透传 `Upgrade`/`Connection` 头 | 检查反代 `/ws` 配置;调大读超时 |
| 忘记管理员密码 | — | 删除 `data/lmvpn.db` 重新初始化(会丢失所有数据),或直接改库 |
---
## 相关文档
- [客户端开发协议规范](docs/client-development.md) — WebSocket 隧道协议、认证、握手、数据面、TUN 配置等完整规范
---
## 安全提示
- 配置文件 `data/config.yml` 权限为 0600,仅限运行用户读写
- `/api/login` 与 WebSocket 密码认证均限制每 IP 5 次/分钟
- WebSocket `/ws` 支持 JWT`?token=xxx`)与用户名/密码两种认证
- 生产环境务必经反向代理启用 HTTPS/WSS