2b0f144abc
The most common OpenVPN deployment scenario is letting remote users
access the server's LAN (office/home network). This requires:
1. net.ipv4.ip_forward=1
2. iptables MASQUERADE on the LAN-facing interface
3. push 'route <LAN-net>' so clients know to send LAN traffic through VPN
Previously this was only mentioned in the FAQ row 'ping不通服务端',
with no actionable instructions. Users (including the project's own
first deployment) hit this exact issue and had to figure it out from
forum posts.
Changes:
README.md:
- New section 'IP 转发与内网访问' before 防火墙与公网暴露
- Covers: enabling ip_forward (immediate + persistent via sysctl.d),
MASQUERADE rules for one/multiple LAN interfaces, push routes,
verification commands, troubleshooting table, full checklist
- FAQ table: add explicit row for 'gateway reachable, LAN not'
- Features table: add '内网转发' row
- TOC: link new section
scripts/install.sh:
- Auto-enable ip_forward on install (idempotent, persistent via
/etc/sysctl.d/99-openvpn-manager.conf)
- Skip silently in containerized environments (no /proc/sys write)
- Print reminder about manual MASQUERADE rule with link to docs
217 lines
7.3 KiB
Bash
Executable File
217 lines
7.3 KiB
Bash
Executable File
#!/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
|
|
|
|
# ---------- 启用 IP 转发 (客户端访问服务端 LAN 的前提) ----------
|
|
echo "==> 启用 IPv4 转发 (客户端访问服务端 LAN 需要)"
|
|
if [[ -w /proc/sys/net/ipv4/ip_forward ]]; then
|
|
echo 1 > /proc/sys/net/ipv4/ip_forward
|
|
# 持久化(下次重启不丢)
|
|
CONF_FILE="/etc/sysctl.d/99-openvpn-manager.conf"
|
|
if [[ ! -f "$CONF_FILE" ]] || ! grep -q "^net.ipv4.ip_forward=1" "$CONF_FILE" 2>/dev/null; then
|
|
echo "net.ipv4.ip_forward=1" > "$CONF_FILE"
|
|
sysctl -p "$CONF_FILE" >/dev/null 2>&1 || true
|
|
fi
|
|
echo " ✓ IP 转发已启用 (持久化到 $CONF_FILE)"
|
|
echo " 提示: 如需让客户端访问服务端 LAN 网段, 还需手动添加 iptables NAT 规则:"
|
|
echo " sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o <LAN网卡> -j MASQUERADE"
|
|
echo " 详见 README \"IP 转发与内网访问\" 章节"
|
|
else
|
|
echo " ! 无法写入 /proc/sys/net/ipv4/ip_forward (容器环境?) — 跳过"
|
|
echo " 如需客户端访问服务端 LAN, 请在宿主机启用: sysctl -w net.ipv4.ip_forward=1"
|
|
fi
|
|
|
|
# ---------- 完成提示 ----------
|
|
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 |