#!/bin/bash # 3x-ui-agent 节点一键安装脚本 # 用法: bash install-agent.sh --master http://MASTER_IP:2053 --token YOUR_AGENT_TOKEN [--name my-node] # 要求: Debian/Ubuntu/Alpine/CentOS (amd64/arm64), root 运行 # # 测试用环境变量(一般无需设置): # XUI_ROOT 安装路径前缀(假根目录验证用),默认空 = 真实路径 # XUI_INIT_SYSTEM 强制 init 系统: systemd | openrc | none,默认 auto 探测 # XUI_URL agent 二进制下载地址覆盖 set -e RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[0;33m'; NC='\033[0m' info() { echo -e "${GREEN}[INFO]${NC} $1"; } warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } [[ $EUID -ne 0 ]] && error "请用 root 运行" MASTER=""; TOKEN=""; NAME="agent-node" while [[ $# -gt 0 ]]; do case $1 in --master) MASTER="$2"; shift 2;; --token) TOKEN="$2"; shift 2;; --name) NAME="$2"; shift 2;; *) error "未知参数: $1";; esac done [[ -z "$MASTER" ]] && error "缺少 --master 参数" [[ -z "$TOKEN" ]] && error "缺少 --token 参数 (从主控面板 API 获取)" ARCH=$(uname -m) case "$ARCH" in x86_64|amd64) BIN_ARCH="amd64";; aarch64|arm64) BIN_ARCH="arm64";; *) error "不支持的架构: $ARCH (仅支持 amd64/arm64)";; esac # 二进制下载地址(本仓 Gitea Releases,tag 沿用 v3.7.0-agent.1) XUI_URL="${XUI_URL:-https://git.881218.xyz/rogue/3x-ui-agent/releases/download/v3.7.0-agent.1/x-ui-linux-${BIN_ARCH}}" XRAY_VER="26.6.27" ROOT="${XUI_ROOT:-}" XUI_HOME="${ROOT}/usr/local/x-ui" CONF_DIR="${ROOT}/etc/x-ui" AGENT_BIN="${XUI_HOME}/x-ui" info "创建目录..." mkdir -p "${XUI_HOME}/bin" "${CONF_DIR}" info "下载 agent 二进制..." curl -fL -o "${AGENT_BIN}" "$XUI_URL" || error "agent 下载失败" chmod +x "${AGENT_BIN}" info "下载 xray v${XRAY_VER}..." cd "${XUI_HOME}/bin" case "$BIN_ARCH" in amd64) XRAY_ZIP="Xray-linux-64.zip";; arm64) XRAY_ZIP="Xray-linux-arm64-v8a.zip";; esac curl -fL -o "$XRAY_ZIP" "https://github.com/XTLS/Xray-core/releases/download/v${XRAY_VER}/${XRAY_ZIP}" || error "xray 下载失败" unzip -o -q "$XRAY_ZIP" xray && mv xray "xray-linux-${BIN_ARCH}" && chmod +x "xray-linux-${BIN_ARCH}" rm -f "$XRAY_ZIP" info "下载 geo 文件..." curl -fL -o geoip.dat "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" || error "geoip 下载失败" curl -fL -o geosite.dat "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" || error "geosite 下载失败" cd - >/dev/null info "写入 agent 配置..." cat > "${CONF_DIR}/agent.json" </dev/null) || code="000" case "$code" in 404) echo "" echo -e "${RED}============================================================${NC}" echo -e "${RED} 主控返回 404:注册接口不存在,安装中止。${NC}" echo -e "${RED} 主控地址大概率少了面板基础路径(如 /rogue)。${NC}" echo -e "${RED} 当前地址: ${MASTER}${NC}" echo -e "${RED} 若面板实际地址带基础路径,请改用 --master ${MASTER%/}/rogue${NC}" echo -e "${RED} 之类的完整地址(以面板真实基础路径为准)改对后重跑本脚本。${NC}" echo -e "${RED}============================================================${NC}" exit 1 ;; 000) echo "" echo -e "${RED}============================================================${NC}" echo -e "${RED} 连不上主控,安装中止。${NC}" echo -e "${RED} 请检查:网络是否通、域名解析是否正常、主控地址与端口是否写对、${NC}" echo -e "${RED} 以及主控 HTTPS 证书是否有效(agent 不接受自签证书)。${NC}" echo -e "${RED} 目标: $url${NC}" echo -e "${RED}============================================================${NC}" exit 1 ;; *) info "主控探测通过 (HTTP $code,注册路由存在)" ;; esac } probe_master detect_init_system() { case "${XUI_INIT_SYSTEM:-auto}" in systemd|openrc|none) echo "${XUI_INIT_SYSTEM}"; return;; esac if [ -d /run/systemd/system ] && command -v systemctl >/dev/null 2>&1; then echo systemd; return fi if command -v rc-update >/dev/null 2>&1 && command -v rc-service >/dev/null 2>&1; then echo openrc; return fi echo none } # 以 systemd 服务方式安装并启动(开机自启) start_via_systemd() { local unit_dir="${ROOT}/etc/systemd/system" mkdir -p "$unit_dir" cat > "${unit_dir}/x-ui-agent.service" <<'EOF' [Unit] Description=3x-ui agent After=network.target [Service] ExecStart=/usr/local/x-ui/x-ui agent Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF info "已生成 systemd 服务文件: ${unit_dir}/x-ui-agent.service" if [ -n "$ROOT" ]; then info "(XUI_ROOT 模式:跳过 systemctl 实际操作)" return 0 fi systemctl stop x-ui-agent 2>/dev/null || true pkill -f "${AGENT_BIN} agent" 2>/dev/null || true; sleep 1 systemctl daemon-reload systemctl enable x-ui-agent systemctl restart x-ui-agent sleep 2 if systemctl is-active --quiet x-ui-agent; then info "agent 已由 systemd 托管启动(开机自启)" echo "" echo " 主控: $MASTER" echo " 节点名: $NAME" echo " 日志: journalctl -u x-ui-agent -f" echo " agent 会在 30 秒内从主控拉取配置" else error "agent 服务启动失败,查看: systemctl status x-ui-agent" fi } # 以 OpenRC 服务方式安装并启动(开机自启,Alpine 等) start_via_openrc() { local init_dir="${ROOT}/etc/init.d" mkdir -p "$init_dir" cat > "${init_dir}/x-ui-agent" <<'EOF' #!/sbin/openrc-run name="x-ui-agent" description="3x-ui lightweight node agent" command="/usr/local/x-ui/x-ui" command_args="agent" command_background="yes" pidfile="/run/x-ui-agent.pid" output_log="/var/log/x-ui-agent.log" error_log="/var/log/x-ui-agent.log" depend() { need net } EOF chmod +x "${init_dir}/x-ui-agent" info "已生成 OpenRC 服务文件: ${init_dir}/x-ui-agent" if [ -n "$ROOT" ]; then info "(XUI_ROOT 模式:跳过 rc-update/rc-service 实际操作)" return 0 fi rc-service x-ui-agent stop 2>/dev/null || true pkill -f "${AGENT_BIN} agent" 2>/dev/null || true; sleep 1 rc-update add x-ui-agent default rc-service x-ui-agent restart sleep 2 if rc-service x-ui-agent status >/dev/null 2>&1; then info "agent 已由 OpenRC 托管启动(开机自启)" echo "" echo " 主控: $MASTER" echo " 节点名: $NAME" echo " 日志: tail -f /var/log/x-ui-agent.log" echo " agent 会在 30 秒内从主控拉取配置" else error "agent 服务启动失败,查看: rc-service x-ui-agent status" fi } # 无服务管理器时回落 nohup(无开机自启,明确警告) start_via_nohup() { warn "未检测到 systemd / OpenRC,退回 nohup 方式启动" warn "注意:nohup 方式没有开机自启,机器重启后 agent 不会自动恢复,需手动重跑本脚本!" pkill -f "${AGENT_BIN} agent" 2>/dev/null || true; sleep 1 # -config 显式给出(生产环境即其默认路径 /etc/x-ui/agent.json,行为不变), # 这样 XUI_ROOT 假根验证时 agent 也能找到刚写下的配置。 nohup "${AGENT_BIN}" agent -config "${CONF_DIR}/agent.json" > /tmp/agent.log 2>&1 & sleep 8 if grep -q "Starting 3x-ui agent" /tmp/agent.log 2>/dev/null; then info "agent 启动成功!" echo "" echo " 主控: $MASTER" echo " 节点名: $NAME" echo " 日志: tail -f /tmp/agent.log" echo " agent 会在 30 秒内从主控拉取配置" else error "启动失败,查看日志: cat /tmp/agent.log" fi } info "启动 agent..." case "$(detect_init_system)" in systemd) start_via_systemd;; openrc) start_via_openrc;; *) start_via_nohup;; esac