# install.sh 问题评估与分级 ## 说明 本文对 `install.sh` 及其直接依赖的服务文件进行静态审查,输出逻辑问题、潜在 Bug 与风险分级。 - 审查对象:`install.sh`、`x-ui.service.arch`、`x-ui.service.debian`、`x-ui.service.rhel`、`x-ui.rc` - 审查方式:静态阅读 + `bash -n install.sh` - 结论范围:以脚本逻辑为主,不包含联网下载内容真实性、外部服务可用性与目标主机现场状态 ## 分级标准 | 级别 | 含义 | |---|---| | P0 / 致命 | 高概率导致安装结果错误、服务不可用、错误安全结论、数据错写或危险操作 | | P1 / 高 | 在常见环境下容易触发,导致功能失败、行为不一致或明显错误 | | P2 / 中 | 不是每次触发,但会造成兼容性、稳健性、可维护性或边界行为问题 | | P3 / 低 | 风险较低,主要是误导、死代码、风格不一致或隐含维护成本 | ## 总览 | 级别 | 数量 | 重点 | |---|---:|---| | P0 / 致命 | 8 | 端口检测失效、SSL 失败仍报成功、MariaDB 新装数据错位、目录漂移 | | P1 / 高 | 12 | 自定义目录不完整支持、安装失败继续、弱口令、服务路径错误 | | P2 / 中 | 18 | 输入校验不足、平台兼容性差、下载/解析脆弱、引号缺失 | | P3 / 低 | 8 | 死代码、注释偏差、提示文案不准确、维护性问题 | --- ## P0 / 致命 | ID | 位置 | 问题 | 影响 | 建议 | |---|---|---|---|---| | P0-01 | `install.sh:63`, `install.sh:67` | `is_port_in_use()` 中 `awk ... END {exit 1}` 覆盖前面的成功退出码,导致 `ss`/`netstat` 分支几乎总返回“端口未占用” | 端口冲突检测失效,ACME 独立监听端口选择可能错误,安装过程可能与现有服务抢端口 | 改为纯 `grep`/`ss` 判断,或让 `awk` 使用状态变量并在 `END` 中按变量返回 | | P0-02 | `install.sh:955`, `install.sh:966`, `install.sh:969` | SSL 配置函数失败后,安装摘要仍打印 HTTPS 地址和“SSL 已启用并配置” | 用户被误导为已启用 HTTPS,可能错误暴露面板 | 必须检查 `prompt_and_setup_ssl` 返回值,失败时终止安装摘要或明确显示失败 | | P0-03 | `install.sh:587`, `install.sh:589` | `ssl_cert_issue()` 返回值未检查,且用 `acme.sh --list | tail -1` 取最后一张证书推断本次签发结果 | 可能把旧证书/无关证书当成本次成功结果写入面板域名 | 直接在 `ssl_cert_issue()` 内返回明确域名或通过全局/输出参数传递,不要读列表尾行 | | P0-04 | `install.sh:859`, `install.sh:863`, `install.sh:867`, `install.sh:869` | 提示称“留空或 rd 自动生成”,实际用户名/密码默认是 `admin/admin` | 新装面板默认弱口令,安全风险极高 | 留空时应真正随机生成密码,用户名至少提示确认 | | P0-05 | `install.sh:890`, `install.sh:914-920`, `install.sh:991` | 新装流程先按默认 SQLite 写面板配置,再切换 `dbType=mariadb`,但没有执行 SQLite→MariaDB 数据迁移 | 最终服务连接 MariaDB 后,用户刚设置的账号/端口等可能不在实际使用库里 | 先确定数据库类型,再初始化与写配置;若从 SQLite 切 MariaDB,必须执行显式迁移 | | P0-06 | `install.sh:157`, `install.sh:1106`, `install.sh:1135-1176` | `install_acme()` 进入家目录后不恢复;`config_after_install()` 里一旦调用它,会污染后续当前工作目录 | 可能找不到解压包中的 service 文件,导致安装行为与预期不一致 | 在函数内保存并恢复目录,或完全使用绝对路径 | | P0-07 | `x-ui.service.arch:10-11`, `install.sh:11`, `install.sh:1135-1159` | Arch 服务文件硬编码 `/usr/lib/x-ui`,而脚本默认安装到 `/usr/local/x-ui` | Arch 系统默认安装后服务大概率无法启动 | 安装时模板渲染 `ExecStart`/`WorkingDirectory`,不要硬编码 | | P0-08 | `install.sh:1078-1084` | 依赖环境变量的目录删除缺乏安全防护,直接 `rm -rf ${xui_folder}/` | 若 `XUI_MAIN_FOLDER` 被误设,可能删除错误目录 | 删除前校验路径非空、非根目录、符合预期前缀,并统一加引号 | ## P1 / 高 | ID | 位置 | 问题 | 影响 | 建议 | |---|---|---|---|---| | P1-01 | `install.sh:1041` | 用 `${xui_folder%/x-ui}` 推导工作目录,隐式假设安装目录以 `/x-ui` 结尾 | 自定义目录时下载/解压位置可能错误 | 单独定义工作目录,不要从安装目录字符串裁剪推导 | | P1-02 | `install.sh:1088-1091` | 解压与 `cd x-ui` 不检查失败 | 后续 chmod、复制 service、配置命令可能在错误目录执行 | 每一步关键文件操作都要显式检查退出码 | | P1-03 | `install.sh:1106` | `config_after_install` 失败后仍继续安装 service 并启动 | 配置失败被掩盖,最终系统状态不可预测 | 将其纳入主流程错误链,失败立即退出 | | P1-04 | `install.sh:76-104`, `install.sh:1232-1234` | `install_base()` 失败后脚本仍继续执行 | 缺依赖状态下继续安装,错误位置后移且更难排查 | 主流程必须检查基础依赖安装结果 | | P1-05 | `install.sh:1071`, `install.sh:1176-1188` | 指定版本安装时,管理脚本和 fallback service 仍从 `main` 分支下载 | 脚本/服务文件与二进制版本不匹配,可能出现不兼容 | 统一按所选 tag 下载同版本配套文件 | | P1-06 | `install.sh:1048`, `install.sh:1065`, `install.sh:1071`, `install.sh:1180-1186` | 多处强制 `curl -4` | IPv6-only 主机无法安装或更新 | 优先正常双栈请求,失败后再回退到 `-4` | | P1-07 | `install.sh:981-988` | 已有安装路径中,只要 `cert` 字段非空就认定已配置 SSL,并打印 HTTPS 地址 | 可能在没有有效证书/私钥时误导用户 | 同时校验 `cert`、`key`、文件存在性和可读性 | | P1-08 | `install.sh:429-435` | 证书重复判断只比较 `acme.sh --list` 最后一条记录 | 已有当前域名证书时仍可能重复签发 | 遍历列表精确匹配域名,或直接查询目标证书目录 | | P1-09 | `install.sh:384-386` | IP 证书流程中,`x-ui cert` 失败只告警不失败,仍打印“安装并配置成功” | 证书文件虽存在,但面板未实际启用证书 | 写入路径失败应判定整体失败 | | P1-10 | `install.sh:611-618`, `install.sh:623-625` | IP 证书模式停止面板后,失败路径没有统一保证服务恢复 | 安装中断后面板可能保持停止状态 | 使用 `trap` 或统一清理/恢复逻辑 | | P1-11 | `install.sh:1137-1203`, `x-ui.service.debian:10-11`, `x-ui.service.rhel:10-11`, `x-ui.rc:3`, `x-ui.rc:12` | 虽允许 `XUI_MAIN_FOLDER` 覆盖,但 service/init 模板大量硬编码 `/usr/local/x-ui` | 自定义安装目录时服务启动失败 | 安装时根据变量生成 service/init 文件 | | P1-12 | `install.sh:994-1015` | 用 `tr`/`grep`/`sed` 解析 GitHub Releases JSON,逻辑脆弱 | API 结构变化、字段顺序变化、错误响应时容易取错版本 | 使用 `jq`,或服务端返回最小化 API 请求并严格校验 JSON | ## P2 / 中 | ID | 位置 | 问题 | 影响 | 建议 | |---|---|---|---|---| | P2-01 | `install.sh:158` | `curl ... | sh` 缺少 `pipefail`,下载失败时可能误报 acme 安装成功 | 安装状态不可信 | 启用 `set -o pipefail`,或先下载再执行 | | P2-02 | `install.sh:177`, `install.sh:249`, `install.sh:396`, `install.sh:732` | 用 `command -v ~/.acme.sh/acme.sh` 检查路径式命令,可用但不规范 | 兼容性与可读性较差 | 直接使用 `[ -x ~/.acme.sh/acme.sh ]` | | P2-03 | `install.sh:193`, `install.sh:321`, `install.sh:463`, `install.sh:743` | 设置默认 CA 的命令结果多数未检查 | 前置动作失败时后续错误定位困难 | 对关键前置步骤逐一校验 | | P2-04 | `install.sh:263-266`, `install.sh:46-50` | IPv4/IPv6 校验过于宽松:IPv4 不校验每段范围,IPv6 只看是否含 `:` | 非法地址可能进入证书流程 | 使用更严格的地址校验函数 | | P2-05 | `install.sh:451-455` | 手动 SSL 端口输入为空时不会自然采用默认值,而是走“无效输入”分支 | 交互体验与提示不一致 | 先 `WebPort="${WebPort:-80}"` 再校验 | | P2-06 | `install.sh:452` | 手动 SSL 端口校验未先判断是否纯数字 | 异常输入由 shell 算术比较隐式处理,不稳健 | 先正则校验再做范围比较 | | P2-07 | `install.sh:881-888` | 面板端口输入没有有效校验,也不检测占用 | 可能写入非法端口或冲突端口 | 校验数字范围并复用端口占用检测 | | P2-08 | `install.sh:886` | 随机端口不检查是否已被占用 | 启动服务时可能冲突 | 生成后循环检测空闲端口 | | P2-09 | `install.sh:890-897` | 只验证端口写入成功,不验证用户名、密码、`webBasePath` 是否确实写入 | 配置失败可能部分隐藏 | 对关键配置项逐项校验 | | P2-10 | `install.sh:899-920` | MariaDB 连接信息读取与写入缺少完整校验,账号/密码/库名可以为空 | 后续数据库初始化失败 | 按数据库类型做必填校验与连通性预检查 | | P2-11 | `install.sh:929-945` | `worker` 仅校验 `dbType=mariadb`,不校验 MariaDB 是否可连接 | worker 模式可能被写入无效配置 | 在设置前做配置与连接校验 | | P2-12 | `install.sh:846-855`, `install.sh:955` | 获取公网 IP 失败后不提示手动输入,IP 证书默认路径会直接失败 | 默认交互路径容易走向失败 | IP 获取失败时要求用户手输或默认跳过 IP 证书 | | P2-13 | `install.sh:707-729` | Cloudflare 域名未用 `is_domain` 校验,API Key 非静默输入 | 容易误输,且敏感信息暴露在终端 | 域名校验 + `read -rsp` 输入密钥 | | P2-14 | `install.sh:751-752` | Cloudflare 凭证 `export` 后未清理 | 后续子进程可见环境变量 | 完成后 `unset CF_Key CF_Email` | | P2-15 | `install.sh:491` | 允许用户输入任意 `reloadcmd`,续期时将以高权限执行 | 误操作或恶意输入可能造成风险 | 至少明确安全提示,并对常见场景提供模板而非完全任意命令 | | P2-16 | `install.sh:91-92` | Arch 依赖安装执行两次 `pacman -Syu`,且第一次无 `--noconfirm` | 交互阻塞、重复更新、耗时增加 | 合并为一次清晰的安装命令 | | P2-17 | `install.sh:1088-1100` | ARM 分支先重命名 `bin/xray-linux-$(arch)`,后面又对原文件名 `chmod` | 可能输出错误信息并污染日志 | 重命名后按最终文件名处理权限 | | P2-18 | `install.sh:1048`, `install.sh:1065`, `install.sh:1071` 等多处 | 路径、URL、命令参数大量未加引号 | 自定义目录、异常字符或空格路径下行为不稳定 | 统一为变量引用加引号 | ## P3 / 低 | ID | 位置 | 问题 | 影响 | 建议 | |---|---|---|---|---| | P3-01 | `install.sh:9` | `cur_dir` 未使用 | 增加噪音 | 删除死代码 | | P3-02 | `install.sh:168-236` | `setup_ssl_certificate()` 未被调用,且参数 `server_ip`、`existing_port`、`existing_webBasePath` 未使用 | 阅读成本高,容易误导维护者 | 删除死函数或接入统一 SSL 流程 | | P3-03 | `install.sh:52-54` | `is_ip()` 未被调用 | 维护噪音 | 删除或实际复用 | | P3-04 | `install.sh:39` | 不支持架构时删除当前 `install.sh` | 行为危险且无必要 | 直接报错退出,不做文件删除 | | P3-05 | `install.sh:578` | 注释“非 1、3、4 默认为 2”表述不完整 | 容易造成维护误解 | 注释改为“除 1/3/4 外均视为 2” | | P3-06 | `install.sh:827-832` | “全新安装”的判定只看两个文件是否存在,注释与语义不够精确 | 可读性一般 | 把注释改成“基于配置/数据库文件存在性判断” | | P3-07 | `install.sh:1044` | `[ $# == 0 ]` 属于 bash 风格写法,可运行但不如 `-eq` 清晰 | 风格一致性较差 | 改用 `[ $# -eq 0 ]` | | P3-08 | `install.sh:1234` | `install_x-ui $1` 未加引号 | 边界输入下会参数拆分 | 改为 `install_x-ui "$1"` | ## 优先修复建议 建议按以下顺序修复: 1. 先修 `P0-01`、`P0-02`、`P0-05`、`P0-06`、`P0-07` 2. 再修 `P1-01`、`P1-03`、`P1-05`、`P1-11` 3. 然后补齐输入校验、错误处理和引号问题 4. 最后清理死代码、注释和交互文案 ## 建议的验收点 - 端口占用检测在 `ss`、`netstat`、`lsof` 三种环境下都能正确判定 - SSL 任一路径失败时,最终摘要不再显示成功状态 - SQLite / MariaDB 两种新装流程都能得到一致且可登录的实际配置 - 自定义 `XUI_MAIN_FOLDER` 后,服务文件仍能正确启动 - Arch、Debian/RHEL、Alpine 三类服务安装路径与执行路径一致 - 在公网 IP 获取失败、80 端口不可用、证书签发失败等场景下,脚本能给出准确结果而非伪成功