From 807f4c2448c8eb024ded103d516d2ac0586e0f7f Mon Sep 17 00:00:00 2001 From: Nexus Deploy Date: Sat, 6 Jun 2026 06:59:45 +0800 Subject: [PATCH] fix(install): correct Redis URL auth for 1Panel root user Password must use redis://:pass@ or redis://root:pass@ host; wizard adds redis_user field defaulting to root on 1Panel Docker installs. Co-authored-by: Cursor --- deploy/README-1panel.md | 2 +- ...026-06-06-install-redis-url-1panel-root.md | 33 +++++++++++++++++++ server/api/install.py | 24 +++++++++----- tests/test_security_unit.py | 23 +++++++++++++ web/app/install.html | 21 ++++++++---- 5 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 docs/changelog/2026-06-06-install-redis-url-1panel-root.md diff --git a/deploy/README-1panel.md b/deploy/README-1panel.md index 754f1d02..d985d7ed 100644 --- a/deploy/README-1panel.md +++ b/deploy/README-1panel.md @@ -119,7 +119,7 @@ Docker 栈**仅含 Nexus 容器**。MySQL / Redis 在 **1Panel 应用商店**安 1. **应用商店** → 安装 **MySQL**、**Redis** 2. **数据库** → 创建库 `nexus`、用户 `nexus`(仅授权 `nexus` 库),记下密码 3. `nx update` 后打开安装向导步骤 3(主机应已预填 `1Panel-mysql-…`) -4. 填写 MySQL 密码;Redis 若设了密码一并填写 +4. 填写 MySQL 密码;Redis:**用户名 `root`** + 应用参数中的密码(见 1Panel 文档) ### 常见错误 1045(Access denied for 'nexus'@'172.18.x.x') diff --git a/docs/changelog/2026-06-06-install-redis-url-1panel-root.md b/docs/changelog/2026-06-06-install-redis-url-1panel-root.md new file mode 100644 index 00000000..6df0df17 --- /dev/null +++ b/docs/changelog/2026-06-06-install-redis-url-1panel-root.md @@ -0,0 +1,33 @@ +# 2026-06-06 — 修复安装向导 Redis URL 与 1Panel root 用户 + +## 摘要 + +步骤 3 Redis 连接失败根因:`_build_redis_url` 生成 `redis://pass@host`(密码被当成用户名);1Panel Redis ACL 用户名为 `root`。 + +## 修复 + +- URL 密码格式改为 `redis://:pass@host` 或 `redis://root:pass@host` +- 向导新增 Redis 用户名,Docker/1Panel 预填 `root` +- 错误提示说明 1Panel 用法 + +## 涉及文件 + +- `server/api/install.py` +- `web/app/install.html` +- `tests/test_security_unit.py` +- `deploy/README-1panel.md` + +## 验证 + +```bash +pytest tests/test_security_unit.py -k build_redis_url -q +bash deploy/sync-install-wizard-to-container.sh +``` + +步骤 3:Redis 用户 `root`、密码 `redis_WP3NyN`、主机 `1Panel-redis-xxxx` → 检测通过。 + +## 已有 .env 时手动修正 + +```bash +NEXUS_REDIS_URL="redis://root:你的密码@1Panel-redis-xxxx:6379/0" +``` diff --git a/server/api/install.py b/server/api/install.py index bac6fc6e..572643c7 100644 --- a/server/api/install.py +++ b/server/api/install.py @@ -62,6 +62,7 @@ class InitDbRequest(BaseModel): redis_host: str = "127.0.0.1" redis_port: str = "6379" redis_db: str = "0" + redis_user: str = "" redis_pass: str = "" api_port: str = "8600" timezone: str = "Asia/Shanghai" @@ -92,11 +93,17 @@ def _make_db_url(req: InitDbRequest | CreateAdminRequest) -> str: def _build_redis_url(req: InitDbRequest) -> str: - url = "redis://" - if req.redis_pass: - url += f"{quote_plus(req.redis_pass)}@" - url += f"{req.redis_host}:{req.redis_port}/{req.redis_db}" - return url + """Build redis:// URL. Password-only auth must use redis://:pass@host (leading colon).""" + user = (req.redis_user or "").strip() + password = (req.redis_pass or "").strip() + auth = "" + if user and password: + auth = f"{quote_plus(user)}:{quote_plus(password)}@" + elif password: + auth = f":{quote_plus(password)}@" + elif user: + auth = f"{quote_plus(user)}@" + return f"redis://{auth}{req.redis_host}:{req.redis_port}/{req.redis_db}" def _read_1panel_hosts_file() -> dict[str, str]: @@ -236,10 +243,10 @@ def _mysql_connect_error_detail(host: str, port: int) -> str: def _redis_auth_error_detail() -> str: if os.environ.get("NEXUS_DOCKER_WRITE_ENV") == "1": return ( - "Redis 密码错误或用户被禁用(1Panel Redis 请在步骤 3 填写应用参数中的密码;" - "无密码则留空)。主机应为 1Panel-redis-xxxx,不是 127.0.0.1。" + "Redis 认证失败。1Panel Redis:用户名填 root,密码填应用参数中的随机密码;" + "主机为 1Panel-redis-xxxx(不是 127.0.0.1)。无 ACL 的旧版 Redis 用户名可留空。" ) - return "Redis 密码错误或需要 AUTH;请核对 Redis 密码(无密码则留空)。" + return "Redis 密码错误或需要 AUTH;请核对用户名/密码(无密码则留空)。" def _redis_connect_error_detail(host: str, port: int) -> str: @@ -344,6 +351,7 @@ def _docker_wizard_defaults() -> dict[str, str] | None: "redis_host": redis_host, "redis_port": "6379", "redis_db": "0", + "redis_user": "root", } diff --git a/tests/test_security_unit.py b/tests/test_security_unit.py index 0a91b79f..c76992b6 100644 --- a/tests/test_security_unit.py +++ b/tests/test_security_unit.py @@ -211,6 +211,29 @@ async def test_install_test_credentials_all_pass(monkeypatch, tmp_path): assert len(out["checks"]) == 2 +def test_build_redis_url_password_with_leading_colon(): + from server.api.install import InitDbRequest, _build_redis_url + + req = InitDbRequest( + db_pass="db", + redis_host="1Panel-redis-x", + redis_pass="redis_WP3NyN", + ) + assert _build_redis_url(req) == "redis://:redis_WP3NyN@1Panel-redis-x:6379/0" + + +def test_build_redis_url_1panel_root_user(): + from server.api.install import InitDbRequest, _build_redis_url + + req = InitDbRequest( + db_pass="db", + redis_host="1Panel-redis-x", + redis_user="root", + redis_pass="redis_WP3NyN", + ) + assert _build_redis_url(req) == "redis://root:redis_WP3NyN@1Panel-redis-x:6379/0" + + def test_install_reject_repeat_init_when_env_exists(monkeypatch, tmp_path): from fastapi import HTTPException from server.api import install as install_api diff --git a/web/app/install.html b/web/app/install.html index 9476e417..624865ab 100644 --- a/web/app/install.html +++ b/web/app/install.html @@ -197,14 +197,21 @@
- - -
-
- - Redis 用户名 +

1Panel Redis 填 root;无用户留空

+
+
+ + +
+
+
+ +

1Panel → Redis → 参数中的密码;无密码留空

+
@@ -636,6 +643,7 @@ function installWizard() { redis_host: '127.0.0.1', redis_port: '6379', redis_db: '0', + redis_user: '', redis_pass: '', api_port: '8600', timezone: 'Asia/Shanghai', @@ -674,6 +682,7 @@ function installWizard() { if (defaults.db_port) this.form.db_port = defaults.db_port; if (defaults.redis_port) this.form.redis_port = defaults.redis_port; if (defaults.redis_db) this.form.redis_db = defaults.redis_db; + if (defaults.redis_user && !this.form.redis_user) this.form.redis_user = defaults.redis_user; }, async refreshDockerDefaults() {