From 326abb3a441c855239a5a47793b944d941f7a22d Mon Sep 17 00:00:00 2001 From: Sora39831 <540587985@qq.com> Date: Wed, 22 Apr 2026 10:10:04 +0800 Subject: [PATCH] fix: avoid admin credential prompt on fresh mariadb install --- ...2-fix-mariadb-admin-empty-password-auth.md | 51 +++++++++++++++++++ install.sh | 48 ++++++++++++++--- tests/mariadb_admin_empty_password_test.sh | 25 +++++++++ x-ui.sh | 46 ++++++++++++++--- 4 files changed, 158 insertions(+), 12 deletions(-) create mode 100644 docs/Tasktracking/2026-04-22-fix-mariadb-admin-empty-password-auth.md create mode 100644 tests/mariadb_admin_empty_password_test.sh diff --git a/docs/Tasktracking/2026-04-22-fix-mariadb-admin-empty-password-auth.md b/docs/Tasktracking/2026-04-22-fix-mariadb-admin-empty-password-auth.md new file mode 100644 index 00000000..d04790cb --- /dev/null +++ b/docs/Tasktracking/2026-04-22-fix-mariadb-admin-empty-password-auth.md @@ -0,0 +1,51 @@ +Task Record: + +Date: 2026-04-22 +Related Module: MariaDB admin auth in installer and menu script +Change Type: Fix + +Background + +During local MariaDB setup, when socket auth failed the script asked for admin credentials. +On hosts where admin/root password is empty, the previous command construction always appended `-p`, which can cause authentication checks to fail for empty-password accounts. +This led to false "管理员账号连接失败" and blocked install/uninstall flows. + +Changes + +Updated MariaDB connection execution in both `install.sh` and `x-ui.sh`: +- Build MariaDB client command with optional password flag. +- Append `-p` only when password is non-empty. +- Apply this to server-connection check, database-connection check, and admin SQL execution. +- Prompt text updated to indicate admin password can be empty. +- Added retry logic (up to 10 seconds) before declaring socket auth failure. +- Added automatic fallback to `root@127.0.0.1` with empty password before prompting admin credentials. +- For freshly installed local MariaDB in script flow, set an install marker and prefer non-interactive auto init path. + +Added `tests/mariadb_admin_empty_password_test.sh` for static regression checks. + +Impact + +Affected files: +- `install.sh` +- `x-ui.sh` +- `tests/mariadb_admin_empty_password_test.sh` + +No API/database schema change. +Install, DB switching, and uninstall paths are more compatible with empty-password admin setups. + +Verification + +Commands: +- `bash -n install.sh` +- `bash -n x-ui.sh` +- `bash tests/mariadb_admin_empty_password_test.sh` +- `bash tests/install_uninstall_resilience_test.sh` +- `bash tests/panel_port_prompt_test.sh` +- `bash tests/mariadb_install_switch_test.sh` + +Result: +- All checks passed. + +Risks And Follow-Up + +Current tests are static assertions. Full runtime verification still depends on real MariaDB environment variants (socket auth, TCP auth, empty password, root-password mode). diff --git a/install.sh b/install.sh index a26daa1c..24eae7a6 100644 --- a/install.sh +++ b/install.sh @@ -222,6 +222,7 @@ ensure_mariadb_client_ready() { ensure_local_mariadb_ready() { if ! has_local_mariadb_service; then install_local_mariadb_server || return 1 + LOCAL_MARIADB_JUST_INSTALLED="1" fi ensure_mariadb_client_ready || return 1 start_mariadb_service || true @@ -231,15 +232,27 @@ ensure_local_mariadb_ready() { test_mariadb_server_connection() { local host="$1" port="$2" user="$3" pass="$4" local bin + local -a cmd bin=$(mariadb_cli_bin) || return 1 - "$bin" -h "$host" -P "$port" -u "$user" -p"$pass" -e "SELECT 1;" >/dev/null 2>&1 + cmd=("$bin" -h "$host" -P "$port" -u "$user") + if [[ -n "$pass" ]]; then + cmd+=("-p$pass") + fi + cmd+=(-e "SELECT 1;") + "${cmd[@]}" >/dev/null 2>&1 } test_mariadb_database_connection() { local host="$1" port="$2" dbname="$3" user="$4" pass="$5" local bin + local -a cmd bin=$(mariadb_cli_bin) || return 1 - "$bin" -h "$host" -P "$port" -u "$user" -p"$pass" -D "$dbname" -e "SELECT 1;" >/dev/null 2>&1 + cmd=("$bin" -h "$host" -P "$port" -u "$user" -D "$dbname") + if [[ -n "$pass" ]]; then + cmd+=("-p$pass") + fi + cmd+=(-e "SELECT 1;") + "${cmd[@]}" >/dev/null 2>&1 } is_safe_mariadb_identifier() { @@ -254,6 +267,7 @@ LOCAL_MARIADB_ADMIN_MODE="" LOCAL_MARIADB_ADMIN_USER="" LOCAL_MARIADB_ADMIN_PASS="" LOCAL_MARIADB_ADMIN_PORT="3306" +LOCAL_MARIADB_JUST_INSTALLED="0" try_local_mariadb_socket_admin() { local bin @@ -263,10 +277,26 @@ try_local_mariadb_socket_admin() { ensure_local_mariadb_admin_access() { local port="${1:-3306}" + local i LOCAL_MARIADB_ADMIN_PORT="$port" - if try_local_mariadb_socket_admin; then - LOCAL_MARIADB_ADMIN_MODE="socket" + # Fresh installs may need a few seconds before local socket auth is ready. + for ((i = 0; i < 10; i++)); do + if try_local_mariadb_socket_admin; then + LOCAL_MARIADB_ADMIN_MODE="socket" + return 0 + fi + sleep 1 + done + + # Common default on fresh installs: root can connect via TCP without password. + if test_mariadb_server_connection "127.0.0.1" "$port" "root" ""; then + LOCAL_MARIADB_ADMIN_MODE="password" + LOCAL_MARIADB_ADMIN_USER="root" + LOCAL_MARIADB_ADMIN_PASS="" + if [[ "$LOCAL_MARIADB_JUST_INSTALLED" == "1" ]]; then + echo -e "${green}检测到新安装 MariaDB,已自动使用 root 免密权限初始化数据库。${plain}" + fi return 0 fi @@ -274,7 +304,7 @@ ensure_local_mariadb_admin_access() { echo -e "${yellow}无法通过 root socket 直接连接本地 MariaDB,请输入管理员账号信息。${plain}" read -rp "MariaDB 管理员用户名 [root]: " admin_user admin_user="${admin_user:-root}" - read -rsp "MariaDB 管理员密码: " admin_pass + read -rsp "MariaDB 管理员密码(可留空): " admin_pass echo if ! test_mariadb_server_connection "127.0.0.1" "$port" "$admin_user" "$admin_pass"; then @@ -290,6 +320,7 @@ ensure_local_mariadb_admin_access() { run_local_mariadb_admin_sql() { local sql="$1" local bin + local -a cmd bin=$(mariadb_cli_bin) || return 1 case "$LOCAL_MARIADB_ADMIN_MODE" in @@ -297,7 +328,12 @@ run_local_mariadb_admin_sql() { "$bin" -e "$sql" >/dev/null 2>&1 || "$bin" -uroot -e "$sql" >/dev/null 2>&1 ;; password) - "$bin" -h "127.0.0.1" -P "$LOCAL_MARIADB_ADMIN_PORT" -u "$LOCAL_MARIADB_ADMIN_USER" -p"$LOCAL_MARIADB_ADMIN_PASS" -e "$sql" >/dev/null 2>&1 + cmd=("$bin" -h "127.0.0.1" -P "$LOCAL_MARIADB_ADMIN_PORT" -u "$LOCAL_MARIADB_ADMIN_USER") + if [[ -n "$LOCAL_MARIADB_ADMIN_PASS" ]]; then + cmd+=("-p$LOCAL_MARIADB_ADMIN_PASS") + fi + cmd+=(-e "$sql") + "${cmd[@]}" >/dev/null 2>&1 ;; *) return 1 diff --git a/tests/mariadb_admin_empty_password_test.sh b/tests/mariadb_admin_empty_password_test.sh new file mode 100644 index 00000000..5d811e91 --- /dev/null +++ b/tests/mariadb_admin_empty_password_test.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +assert_contains() { + local file="$1" + local pattern="$2" + if ! grep -Fq "$pattern" "$file"; then + echo "missing pattern in $file: $pattern" >&2 + return 1 + fi +} + +assert_contains "install.sh" "MariaDB 管理员密码(可留空)" +assert_contains "install.sh" "if [[ -n \"\$pass\" ]]; then" +assert_contains "install.sh" "if [[ -n \"\$LOCAL_MARIADB_ADMIN_PASS\" ]]; then" +assert_contains "install.sh" "test_mariadb_server_connection \"127.0.0.1\" \"\$port\" \"root\" \"\"" +assert_contains "install.sh" "LOCAL_MARIADB_JUST_INSTALLED=\"1\"" + +assert_contains "x-ui.sh" "MariaDB 管理员密码(可留空)" +assert_contains "x-ui.sh" "if [[ -n \"\$pass\" ]]; then" +assert_contains "x-ui.sh" "if [[ -n \"\$LOCAL_MARIADB_ADMIN_PASS\" ]]; then" +assert_contains "x-ui.sh" "test_mariadb_server_connection \"127.0.0.1\" \"\$port\" \"root\" \"\"" +assert_contains "x-ui.sh" "LOCAL_MARIADB_JUST_INSTALLED=\"1\"" + +echo "mariadb admin empty-password flow looks correct" diff --git a/x-ui.sh b/x-ui.sh index 8e1bfea6..9059f304 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -2655,6 +2655,7 @@ ensure_local_mariadb_ready() { echo -e "${yellow}未检测到本地 MariaDB 服务${plain}" confirm "是否安装本地 MariaDB?" "y" || return 1 install_local_mariadb_server || return 1 + LOCAL_MARIADB_JUST_INSTALLED="1" fi ensure_mariadb_client_ready || return 1 start_mariadb_service || true @@ -2664,15 +2665,27 @@ ensure_local_mariadb_ready() { test_mariadb_server_connection() { local host="$1" port="$2" user="$3" pass="$4" local bin + local -a cmd bin=$(mariadb_cli_bin) || return 1 - "$bin" -h "$host" -P "$port" -u "$user" -p"$pass" -e "SELECT 1;" >/dev/null 2>&1 + cmd=("$bin" -h "$host" -P "$port" -u "$user") + if [[ -n "$pass" ]]; then + cmd+=("-p$pass") + fi + cmd+=(-e "SELECT 1;") + "${cmd[@]}" >/dev/null 2>&1 } test_mariadb_database_connection() { local host="$1" port="$2" dbname="$3" user="$4" pass="$5" local bin + local -a cmd bin=$(mariadb_cli_bin) || return 1 - "$bin" -h "$host" -P "$port" -u "$user" -p"$pass" -D "$dbname" -e "SELECT 1;" >/dev/null 2>&1 + cmd=("$bin" -h "$host" -P "$port" -u "$user" -D "$dbname") + if [[ -n "$pass" ]]; then + cmd+=("-p$pass") + fi + cmd+=(-e "SELECT 1;") + "${cmd[@]}" >/dev/null 2>&1 } is_safe_mariadb_identifier() { @@ -2687,6 +2700,7 @@ LOCAL_MARIADB_ADMIN_MODE="" LOCAL_MARIADB_ADMIN_USER="" LOCAL_MARIADB_ADMIN_PASS="" LOCAL_MARIADB_ADMIN_PORT="3306" +LOCAL_MARIADB_JUST_INSTALLED="0" try_local_mariadb_socket_admin() { local bin @@ -2696,10 +2710,24 @@ try_local_mariadb_socket_admin() { ensure_local_mariadb_admin_access() { local port="${1:-3306}" + local i LOCAL_MARIADB_ADMIN_PORT="$port" - if try_local_mariadb_socket_admin; then - LOCAL_MARIADB_ADMIN_MODE="socket" + for ((i = 0; i < 10; i++)); do + if try_local_mariadb_socket_admin; then + LOCAL_MARIADB_ADMIN_MODE="socket" + return 0 + fi + sleep 1 + done + + if test_mariadb_server_connection "127.0.0.1" "$port" "root" ""; then + LOCAL_MARIADB_ADMIN_MODE="password" + LOCAL_MARIADB_ADMIN_USER="root" + LOCAL_MARIADB_ADMIN_PASS="" + if [[ "$LOCAL_MARIADB_JUST_INSTALLED" == "1" ]]; then + echo -e "${green}检测到新安装 MariaDB,已自动使用 root 免密权限初始化数据库。${plain}" + fi return 0 fi @@ -2707,7 +2735,7 @@ ensure_local_mariadb_admin_access() { echo -e "${yellow}无法通过 root socket 直接连接本地 MariaDB,请输入管理员账号信息。${plain}" read -rp "MariaDB 管理员用户名 [root]: " admin_user admin_user="${admin_user:-root}" - read -rsp "MariaDB 管理员密码: " admin_pass + read -rsp "MariaDB 管理员密码(可留空): " admin_pass echo if ! test_mariadb_server_connection "127.0.0.1" "$port" "$admin_user" "$admin_pass"; then @@ -2723,6 +2751,7 @@ ensure_local_mariadb_admin_access() { run_local_mariadb_admin_sql() { local sql="$1" local bin + local -a cmd bin=$(mariadb_cli_bin) || return 1 case "$LOCAL_MARIADB_ADMIN_MODE" in @@ -2730,7 +2759,12 @@ run_local_mariadb_admin_sql() { "$bin" -e "$sql" >/dev/null 2>&1 || "$bin" -uroot -e "$sql" >/dev/null 2>&1 ;; password) - "$bin" -h "127.0.0.1" -P "$LOCAL_MARIADB_ADMIN_PORT" -u "$LOCAL_MARIADB_ADMIN_USER" -p"$LOCAL_MARIADB_ADMIN_PASS" -e "$sql" >/dev/null 2>&1 + cmd=("$bin" -h "127.0.0.1" -P "$LOCAL_MARIADB_ADMIN_PORT" -u "$LOCAL_MARIADB_ADMIN_USER") + if [[ -n "$LOCAL_MARIADB_ADMIN_PASS" ]]; then + cmd+=("-p$LOCAL_MARIADB_ADMIN_PASS") + fi + cmd+=(-e "$sql") + "${cmd[@]}" >/dev/null 2>&1 ;; *) return 1