From f3cda67d9f5192de3e0e7883d35984f65eb46667 Mon Sep 17 00:00:00 2001 From: cnbugs Date: Sun, 9 Aug 2026 23:34:05 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20VPN=20verify:=20via-env=E2=86=92via-file,?= =?UTF-8?q?=20add=20pass=5Flen=20debug=20logging?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenVPN 2.7 Windows DCO passes username but NOT password as environment variable with via-env mode. Switched to via-file: server.conf: auth-user-pass-verify script via-file verify.sh reads credentials from temp file (): line 1 = username, line 2 = password Also added pass_len to debug log so we can immediately see if the password was actually received by the script. --- backend/pkg/openvpn/manager.go | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/backend/pkg/openvpn/manager.go b/backend/pkg/openvpn/manager.go index 28e2ffd..880b791 100644 --- a/backend/pkg/openvpn/manager.go +++ b/backend/pkg/openvpn/manager.go @@ -171,13 +171,13 @@ func (m *Manager) WriteServerConf(in *model.Instance, extraDir string) error { } if in.AuthMode == model.AuthCertPassword { // 双因素认证:证书 + 用户名密码 - // OpenVPN 以 via-env 方式调用 verify 脚本,环境变量: - // username= - // password=<用户输入的明文密码> + // OpenVPN 以 via-file 方式调用 verify 脚本,$1 为临时文件路径: + // 第 1 行 = VPN用户名(=证书CN) + // 第 2 行 = 用户输入的明文密码 // 脚本退出码 0 = 允许,非 0 = 拒绝 verifyScript := filepath.Join(m.InstanceDir(in.Name), "verify.sh") conf.WriteString("script-security 2\n") - conf.WriteString("auth-user-pass-verify \"" + verifyScript + "\" via-env\n") + conf.WriteString("auth-user-pass-verify \"" + verifyScript + "\" via-file\n") conf.WriteString("verify-client-cert require\n") _ = m.WriteVerifyScriptWithPort(in.Name, verifyScript, m.port) } @@ -507,16 +507,25 @@ func (m *Manager) WriteVerifyScript(instanceName, scriptPath string) error { // WriteVerifyScriptWithPort 生成带指定端口的认证脚本。 func (m *Manager) WriteVerifyScriptWithPort(instanceName, scriptPath string, port int) error { script := fmt.Sprintf(`#!/bin/bash -# OpenVPN auth-user-pass-verify script (via-env). +# OpenVPN auth-user-pass-verify script (via-file). # Generated by openvpn-manager — do NOT edit manually. -# Calls manager API to verify bcrypt password. Exit 0=allow, 1=deny. +# OpenVPN writes credentials to a temp file passed as $1: +# line 1 = username, line 2 = password LOG="/tmp/openvpn-verify.log" PORT=%d INSTANCE="%s" +CREDS_FILE="$1" -user="${username:-}" -pass="${password:-}" -echo "$(date '+%%Y-%%m-%%d %%H:%%M:%%S') VERIFY user=$user instance=$INSTANCE" >> "$LOG" +echo "$(date '+%%Y-%%m-%%d %%H:%%M:%%S') VERIFY instance=$INSTANCE creds_file=$CREDS_FILE" >> "$LOG" + +if [ -z "$CREDS_FILE" ] || [ ! -f "$CREDS_FILE" ]; then + echo "$(date '+%%Y-%%m-%%d %%H:%%M:%%S') DENY no credentials file" >> "$LOG" + exit 1 +fi + +user=$(sed -n '1p' "$CREDS_FILE") +pass=$(sed -n '2p' "$CREDS_FILE") +echo "$(date '+%%Y-%%m-%%d %%H:%%M:%%S') user=$user pass_len=${#pass}" >> "$LOG" if [ -z "$user" ] || [ -z "$pass" ]; then echo "$(date '+%%Y-%%m-%%d %%H:%%M:%%S') DENY empty credentials" >> "$LOG" @@ -531,7 +540,7 @@ CODE=$(curl -s -o /tmp/ovpn-verify-resp.txt -w "%%{http_code}" \ 2>>"$LOG" || echo "000") BODY=$(cat /tmp/ovpn-verify-resp.txt 2>/dev/null) -echo "$(date '+%%Y-%%m-%%d %%H:%%M:%%S') HTTP_CODE=$CODE BODY=$BODY" >> "$LOG" +echo "$(date '+%%Y-%%m-%%d %%H:%%M:%%S') HTTP=$CODE BODY=$BODY" >> "$LOG" if [ "$CODE" = "200" ]; then echo "$(date '+%%Y-%%m-%%d %%H:%%M:%%S') ALLOW" >> "$LOG"