fix(1panel): grant nexus@% for Docker MySQL 1045 on install
Add fix-1panel-mysql-grant.sh and clearer install wizard errors when 1Panel MySQL only allows nexus@localhost. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
| `install-nexus-fresh.sh` | 全新空库安装 | `bash deploy/install-nexus-fresh.sh --skip-clone` |
|
||||
| **`update.sh`** | **一键更新** | `curl -fsSL ".../update.sh" \| bash` |
|
||||
| `nexus-1panel.sh` | 底层 install / upgrade | `bash deploy/nexus-1panel.sh upgrade` |
|
||||
| `fix-1panel-mysql-grant.sh` | 修复 1045 nexus@Docker | `bash deploy/fix-1panel-mysql-grant.sh` |
|
||||
| **`nx`** | **统一运维菜单** | `nx` |
|
||||
|
||||
档位:`--profile 1c4g` · `2c8g`(默认)· `4c16g`
|
||||
@@ -120,6 +121,26 @@ Docker 栈**仅含 Nexus 容器**。MySQL / Redis 在 **1Panel 应用商店**安
|
||||
3. `nx update` 后打开安装向导步骤 3(主机应已预填 `1Panel-mysql-…`)
|
||||
4. 填写 MySQL 密码;Redis 若设了密码一并填写
|
||||
|
||||
### 常见错误 1045(Access denied for 'nexus'@'172.18.x.x')
|
||||
|
||||
TCP 已通,但 MySQL 用户多为 **`nexus@localhost`**,Docker 内 Nexus 从 **172.x** 连接会被拒绝。
|
||||
|
||||
在服务器执行(按提示输入 root 密码与 nexus 密码):
|
||||
|
||||
```bash
|
||||
cd /opt/nexus && bash deploy/fix-1panel-mysql-grant.sh
|
||||
```
|
||||
|
||||
或一次性:
|
||||
|
||||
```bash
|
||||
MYSQL_ROOT_PASSWORD='1Panel里MySQL的root密码' \
|
||||
NEXUS_DB_PASSWORD='向导里要填的nexus密码' \
|
||||
bash /opt/nexus/deploy/fix-1panel-mysql-grant.sh
|
||||
```
|
||||
|
||||
然后安装向导步骤 3:**库名/用户 `nexus`**,主机为 `1Panel-mysql-xxxx`,密码与上面一致。
|
||||
|
||||
若仍报 `Can't connect to MySQL/Redis on 'host.docker.internal'`:说明 Nexus **未接入 1panel-network** 或未探测到容器名,在服务器执行:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
# Grant nexus@'%' on 1Panel App Store MySQL for Docker (1panel-network) install wizard.
|
||||
# Fixes: Access denied for user 'nexus'@'172.18.x.x' (1045)
|
||||
#
|
||||
# Usage:
|
||||
# MYSQL_ROOT_PASSWORD='root密码' NEXUS_DB_PASSWORD='nexus密码' bash deploy/fix-1panel-mysql-grant.sh
|
||||
# bash deploy/fix-1panel-mysql-grant.sh # prompts
|
||||
set -euo pipefail
|
||||
|
||||
NEXUS_DB_NAME="${NEXUS_DB_NAME:-nexus}"
|
||||
NEXUS_DB_USER="${NEXUS_DB_USER:-nexus}"
|
||||
|
||||
info() { echo -e "\033[0;32m[INFO]\033[0m $*"; }
|
||||
warn() { echo -e "\033[1;33m[WARN]\033[0m $*"; }
|
||||
error() { echo -e "\033[0;31m[ERROR]\033[0m $*" >&2; exit 1; }
|
||||
|
||||
pick_mysql_container() {
|
||||
local name
|
||||
if [[ -n "${MYSQL_CONTAINER:-}" ]]; then
|
||||
echo "$MYSQL_CONTAINER"
|
||||
return 0
|
||||
fi
|
||||
if docker network inspect 1panel-network >/dev/null 2>&1; then
|
||||
name="$(docker network inspect 1panel-network --format '{{range .Containers}}{{.Name}} {{end}}' \
|
||||
| tr ' ' '\n' | grep -iE '^1Panel-mysql-' | head -1 || true)"
|
||||
[[ -n "$name" ]] && echo "$name" && return 0
|
||||
fi
|
||||
name="$(docker ps --format '{{.Names}}' | grep -iE '^1Panel-mysql-' | head -1 || true)"
|
||||
[[ -n "$name" ]] && echo "$name" && return 0
|
||||
error "未找到 1Panel MySQL 容器(可设 MYSQL_CONTAINER=名称)"
|
||||
}
|
||||
|
||||
prompt_secret() {
|
||||
local var="$1" prompt="$2"
|
||||
if [[ -n "${!var:-}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
read -r -s -p "$prompt: " "$var"
|
||||
echo ""
|
||||
if [[ -z "${!var:-}" ]]; then
|
||||
error "$var 不能为空"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
[[ "${EUID:-$(id -u)}" -eq 0 ]] || error "请用 root 运行"
|
||||
|
||||
local mysql_c
|
||||
mysql_c="$(pick_mysql_container)"
|
||||
info "MySQL 容器: $mysql_c"
|
||||
|
||||
prompt_secret MYSQL_ROOT_PASSWORD "1Panel MySQL root 密码"
|
||||
prompt_secret NEXUS_DB_PASSWORD "nexus 用户密码(与安装向导步骤 3 一致)"
|
||||
|
||||
info "创建库/用户并授权 ${NEXUS_DB_USER}@'%' → ${NEXUS_DB_NAME}.* ..."
|
||||
|
||||
docker exec -i "$mysql_c" mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" <<SQL
|
||||
CREATE DATABASE IF NOT EXISTS \`${NEXUS_DB_NAME}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE USER IF NOT EXISTS '${NEXUS_DB_USER}'@'%' IDENTIFIED BY '${NEXUS_DB_PASSWORD}';
|
||||
ALTER USER '${NEXUS_DB_USER}'@'%' IDENTIFIED BY '${NEXUS_DB_PASSWORD}';
|
||||
GRANT ALL PRIVILEGES ON \`${NEXUS_DB_NAME}\`.* TO '${NEXUS_DB_USER}'@'%';
|
||||
FLUSH PRIVILEGES;
|
||||
SELECT user, host FROM mysql.user WHERE user = '${NEXUS_DB_USER}';
|
||||
SQL
|
||||
|
||||
info "完成。请在安装向导步骤 3 使用: 库/用户=${NEXUS_DB_NAME}/${NEXUS_DB_USER},密码与上面一致"
|
||||
info "若已改 install.py,执行: bash deploy/sync-install-wizard-to-container.sh"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -0,0 +1,27 @@
|
||||
# 2026-06-06 — 1Panel MySQL 1045 授权修复
|
||||
|
||||
## 摘要
|
||||
|
||||
安装向导步骤 3 报 `Access denied for user 'nexus'@'172.18.x.x'`:1Panel 常见仅 `nexus@localhost`,Docker 网段连接被拒。新增 `fix-1panel-mysql-grant.sh` 与安装 API 明确错误提示。
|
||||
|
||||
## 动机
|
||||
|
||||
验收脚本已通过但 init-db 1045;需在仓库内提供可重复修复路径,避免手工 SQL 易错。
|
||||
|
||||
## 涉及文件
|
||||
|
||||
- `deploy/fix-1panel-mysql-grant.sh` — 创建/授权 `nexus@'%'`
|
||||
- `server/api/install.py` — 1045 友好错误与 connection-check
|
||||
- `deploy/README-1panel.md` — 1045 排障节
|
||||
|
||||
## 验证
|
||||
|
||||
```bash
|
||||
MYSQL_ROOT_PASSWORD='...' NEXUS_DB_PASSWORD='...' bash deploy/fix-1panel-mysql-grant.sh
|
||||
# 安装向导步骤 3 初始化成功
|
||||
bash deploy/sync-install-wizard-to-container.sh # 同步 install.py 到运行中容器
|
||||
```
|
||||
|
||||
## 迁移 / 重启
|
||||
|
||||
无 DB 迁移。`install.py` 变更需 `sync-install-wizard-to-container.sh` 或 `nx update --no-cache`。
|
||||
@@ -196,6 +196,22 @@ def _service_tcp_reachable(host: str, port: int, timeout: float = 3.0) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _mysql_auth_error_detail(db_user: str) -> str:
|
||||
"""1045 Access denied — common on 1Panel when user is only nexus@localhost."""
|
||||
if os.environ.get("NEXUS_DOCKER_WRITE_ENV") != "1":
|
||||
return (
|
||||
f"MySQL 拒绝用户 {db_user!r} 的密码或主机权限。"
|
||||
"请核对库名/用户名/密码,并确认该用户允许从应用主机连接(非仅 localhost)。"
|
||||
)
|
||||
return (
|
||||
f"MySQL 拒绝用户 {db_user!r}(错误 1045)。Nexus 在 Docker 内通过 1panel-network 连接,"
|
||||
"来源 IP 为容器网段(如 172.18.x.x),不是 localhost。\n"
|
||||
"请在 1Panel MySQL 中确保:① 库 nexus、用户 nexus 已创建 ② 密码与向导一致 "
|
||||
"③ 存在 nexus@'%' 或允许 Docker 网段(仅 nexus@localhost 会失败)。\n"
|
||||
"服务器执行: cd /opt/nexus && bash deploy/fix-1panel-mysql-grant.sh"
|
||||
)
|
||||
|
||||
|
||||
def _mysql_connect_error_detail(host: str, port: int) -> str:
|
||||
if os.environ.get("NEXUS_DOCKER_WRITE_ENV") != "1":
|
||||
return ""
|
||||
@@ -265,6 +281,11 @@ async def _test_mysql_url(db_url: str) -> tuple[bool, str]:
|
||||
await engine.dispose()
|
||||
return True, "✓ MySQL 连接正常"
|
||||
except Exception as e:
|
||||
err = str(e)
|
||||
if "1045" in err or "Access denied" in err:
|
||||
return False, _mysql_auth_error_detail(
|
||||
urlparse(db_url).username or "nexus"
|
||||
)
|
||||
return False, f"✗ MySQL 连接失败: {e}"
|
||||
|
||||
|
||||
@@ -805,6 +826,8 @@ async def init_db(req: InitDbRequest):
|
||||
except Exception as e:
|
||||
await engine.dispose()
|
||||
err = str(e)
|
||||
if "1045" in err or "Access denied" in err:
|
||||
raise HTTPException(400, _mysql_auth_error_detail(req.db_user)) from e
|
||||
if "2003" in err or "Can't connect to MySQL" in err:
|
||||
extra = _mysql_connect_error_detail(req.db_host, db_port)
|
||||
if extra:
|
||||
|
||||
Reference in New Issue
Block a user