Initial commit: OpenVPN Manager v1.0

OpenVPN Web management console with multi-instance support, client cert
issuance, traffic/connection auditing, certificate expiry reminders,
auto backup/restore.

Stack:
- Backend: Go 1.21+ (Gin + JWT)
- Frontend: Vue 3 + Element Plus + ECharts + Vite
- Storage: JSON file (db.json) + filesystem (pki/, instances/, clients/, backups/)

Features:
- Multi-instance OpenVPN management (independent port/proto/subnet/PKI)
- One-click client certificate issuance with .ovpn (embedded certs)
- Certificate expiry reminders (30-day threshold)
- Connection log parsing (status-version 3)
- Auto backup/restore (tar.gz)
- Audit log for all write operations
- JWT auth (12h TTL)
- One-line install.sh for Ubuntu/Debian/RHEL/Fedora
This commit is contained in:
cnbugs
2026-08-09 20:32:37 +08:00
commit 77f8b59290
34 changed files with 6063 additions and 0 deletions
+198
View File
@@ -0,0 +1,198 @@
#!/usr/bin/env bash
# openvpn-manager 一键部署脚本
# 适用: Ubuntu 20.04+/Debian 11+/CentOS Stream 9+
# 行为: 装依赖 → 编译前端 → 编译 Go 二进制 → 装到 /opt/openvpn-manager → 写 systemd
# 用法: sudo ./install.sh [卸载参数 -u] [--port 8089] [--user admin] [--pass xxxx]
set -euo pipefail
APP_NAME="openvpn-manager"
INSTALL_DIR="/opt/openvpn-manager"
SERVICE_NAME="openvpn-manager"
# 默认配置(可被命令行覆盖)
PORT=8089
ADMIN_USER="admin"
ADMIN_PASS="admin123"
JWT_SECRET="$(openssl rand -hex 32 2>/dev/null || head -c 64 /dev/urandom | xxd -p -c 64)"
usage() {
cat <<EOF
用法: $0 [选项]
-u, --uninstall 卸载(停止服务, 删除 /opt/openvpn-manager 与 systemd 单元)
--port PORT Web 监听端口 (默认 8089)
--user USER 管理员用户名 (默认 admin)
--pass PASS 管理员密码 (默认 admin123)
--dir DIR 安装目录 (默认 /opt/openvpn-manager)
-h, --help 显示本帮助
示例:
sudo $0
sudo $0 --port 9090 --user root --pass 'StrongPass!2026'
sudo $0 -u
EOF
}
uninstall() {
echo "==> 停止并禁用服务"
systemctl stop "$SERVICE_NAME" 2>/dev/null || true
systemctl disable "$SERVICE_NAME" 2>/dev/null || true
rm -f "/etc/systemd/system/${SERVICE_NAME}.service"
systemctl daemon-reload
echo "==> 删除安装目录 $INSTALL_DIR"
rm -rf "$INSTALL_DIR"
echo "==> 完成(已保留源码目录, 如需彻底清理请手动 rm -rf 源码目录)"
}
# ---------- 参数解析 ----------
while [[ $# -gt 0 ]]; do
case "$1" in
-u|--uninstall) uninstall; exit 0 ;;
--port) PORT="$2"; shift 2 ;;
--user) ADMIN_USER="$2"; shift 2 ;;
--pass) ADMIN_PASS="$2"; shift 2 ;;
--dir) INSTALL_DIR="$2"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) echo "未知参数: $1"; usage; exit 1 ;;
esac
done
# ---------- 权限检查 ----------
if [[ $EUID -ne 0 ]]; then
echo "请使用 root 运行: sudo $0"
exit 1
fi
# ---------- 发行版识别 ----------
. /etc/os-release 2>/dev/null || true
echo "==> 操作系统: ${PRETTY_NAME:-unknown}"
SCRIPTPATH="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
SRC_DIR="$( cd -- "$SCRIPTPATH/.." &> /dev/null && pwd )"
echo "==> 源码目录: $SRC_DIR"
echo "==> 安装目录: $INSTALL_DIR"
# ---------- 包管理器 ----------
if command -v apt-get >/dev/null 2>&1; then
PKG="apt-get"
PKG_INSTALL="apt-get install -y --no-install-recommends"
PKG_UPDATE="apt-get update -qq"
DEPS=(curl ca-certificates openssl openvpn easy-rsa nodejs npm golang-go git)
elif command -v dnf >/dev/null 2>&1; then
PKG="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf makecache -q"
DEPS=(curl ca-certificates openssl openvpn easy-rsa nodejs npm golang git)
elif command -v yum >/dev/null 2>&1; then
PKG="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum makecache fast -q"
DEPS=(curl ca-certificates openssl openvpn easy-rsa nodejs npm golang git)
else
echo "不支持的发行版(需 apt/dnf/yum 之一)"
exit 1
fi
echo "==> 使用包管理器: $PKG"
echo "==> 更新包索引"
$PKG_UPDATE || true
echo "==> 安装系统依赖"
$PKG_INSTALL "${DEPS[@]}" || {
echo "依赖安装失败, 请检查网络/源"; exit 1;
}
# ---------- 校验版本 ----------
echo "==> 检查工具版本"
node -v
npm -v
go version
openssl version
openvpn --version | head -1
# 若 Go < 1.21, 警告但不中断(本项目最低 1.21)
GO_VER=$(go version | awk '{print $3}' | sed 's/go//')
echo "==> Go 版本: $GO_VER"
# ---------- 构建前端 ----------
echo "==> 构建前端 (npm install + build)"
cd "$SRC_DIR/frontend"
npm install --include=dev --no-audit --no-fund
npm run build
# ---------- 构建后端 ----------
echo "==> 编译 Go 后端二进制"
cd "$SRC_DIR/backend"
go build -ldflags "-s -w" -o "$SRC_DIR/bin/openvpn-manager" ./cmd/server
# ---------- 安装 ----------
echo "==> 安装到 $INSTALL_DIR"
mkdir -p "$INSTALL_DIR"/{bin,data,dist}
cp -r "$SRC_DIR/bin/openvpn-manager" "$INSTALL_DIR/bin/"
cp -r "$SRC_DIR/dist/"* "$INSTALL_DIR/dist/"
# 写入环境变量文件(便于 systemd 引用)
cat > "$INSTALL_DIR/.env" <<EOF
OVPNMGR_HOST=0.0.0.0
OVPNMGR_PORT=$PORT
OVPNMGR_ADMIN_USER=$ADMIN_USER
OVPNMGR_ADMIN_PASS=$ADMIN_PASS
OVPNMGR_JWT_SECRET=$JWT_SECRET
OVPNMGR_OPENVPN_BIN=$(command -v openvpn)
OVPNMGR_EASYRSA_BIN=$(command -v easyrsa || echo easyrsa)
EOF
chmod 600 "$INSTALL_DIR/.env"
# 渲染 systemd 单元(替换环境变量)
sed -e "s|Environment=OVPNMGR_PORT=.*|Environment=OVPNMGR_PORT=$PORT|" \
-e "s|Environment=OVPNMGR_ADMIN_USER=.*|Environment=OVPNMGR_ADMIN_USER=$ADMIN_USER|" \
-e "s|Environment=OVPNMGR_ADMIN_PASS=.*|Environment=OVPNMGR_ADMIN_PASS=$ADMIN_PASS|" \
-e "s|Environment=OVPNMGR_JWT_SECRET=.*|Environment=OVPNMGR_JWT_SECRET=$JWT_SECRET|" \
-e "s|/opt/openvpn-manager|$INSTALL_DIR|g" \
"$SRC_DIR/systemd/${SERVICE_NAME}.service" > "/etc/systemd/system/${SERVICE_NAME}.service"
systemctl daemon-reload
systemctl enable "$SERVICE_NAME"
systemctl restart "$SERVICE_NAME"
# ---------- 健康检查 ----------
sleep 2
HEALTH_URL="http://127.0.0.1:${PORT}/api/health"
echo "==> 健康检查 $HEALTH_URL"
for i in 1 2 3 4 5; do
if curl -fsS "$HEALTH_URL" >/dev/null 2>&1; then
echo "✓ 服务已就绪"
break
fi
if [[ $i -eq 5 ]]; then
echo "✗ 健康检查失败, 查看: journalctl -u $SERVICE_NAME -n 50"
exit 1
fi
sleep 1
done
# ---------- 完成提示 ----------
cat <<EOF
================================================================
OpenVPN Manager 安装完成
================================================================
访问地址 : http://<服务器IP>:${PORT}
用户名 : ${ADMIN_USER}
密码 : ${ADMIN_PASS}
安装目录 : ${INSTALL_DIR}
数据目录 : ${INSTALL_DIR}/data
配置单元 : /etc/systemd/system/${SERVICE_NAME}.service
常用命令:
systemctl status ${SERVICE_NAME} # 查看状态
systemctl restart ${SERVICE_NAME} # 重启
systemctl stop ${SERVICE_NAME} # 停止
journalctl -u ${SERVICE_NAME} -f # 跟踪日志
${INSTALL_DIR}/bin/openvpn-manager --help
卸载:
sudo $0 -u
================================================================
EOF