Files
cellular-proxy/scripts/install.sh
T
Hermes 2d1168ef9e feat: 全自动安装 — 探测数据网卡并绑定出口
- 驱动/名称/次默认路由/非默认口 评分探测
- install.sh 无参数:探测→写配置→装内核→启动→验证
- cpxy rebind 重插卡后重新绑定
- 自动生成 PANEL_SECRET;可选 apt 装依赖
2026-07-21 11:11:57 +00:00

205 lines
7.4 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# shellcheck source=lib.sh
source "$ROOT_DIR/scripts/lib.sh"
AUTO_DETECT="${AUTO_DETECT:-true}"
AUTO_VERIFY="${AUTO_VERIFY:-true}"
SKIP_START="${SKIP_START:-false}"
if [[ ! -f "$ROOT_DIR/config/settings.conf" ]]; then
cp "$ROOT_DIR/config/settings.conf.example" "$ROOT_DIR/config/settings.conf"
warn "已创建 config/settings.conf(将自动探测数据网卡并生成密钥)"
fi
load_config
need_root
info "1/6 自动探测并绑定数据网卡"
"$ROOT_DIR/scripts/detect.sh" || true
cell=""
if [[ -n "${CELLULAR_IFACE:-}" ]] && iface_exists "$CELLULAR_IFACE"; then
cell="$CELLULAR_IFACE"
info "使用配置的数据网卡: $cell"
elif [[ "$AUTO_DETECT" == "true" ]]; then
cell="$(detect_cellular_iface "" || true)"
fi
if [[ -z "$cell" ]]; then
die "未能自动探测数据网卡。
请插入/拨通数据模块后重试,或手动:
1) ./scripts/detect.sh
2) 编辑 config/settings.conf 设置 CELLULAR_IFACE=网卡名
3) sudo ./scripts/install.sh"
fi
src_ip="$(detect_source_ip "$cell" || true)"
persist_cellular_to_settings "$ROOT_DIR/config/settings.conf" "$cell" "$src_ip"
# 重新加载
CELLULAR_IFACE="$cell"
CELLULAR_SOURCE_IP="$src_ip"
info "已绑定 CELLULAR_IFACE=$cell source_ip=${src_ip:-auto}"
# 随机面板密钥(若仍是默认)
if [[ "$PANEL_SECRET" == "please-change-me" || "$PANEL_SECRET" == "change-this-secret" || -z "$PANEL_SECRET" ]]; then
gen="$(openssl rand -hex 12 2>/dev/null || head -c 16 /dev/urandom | xxd -p | tr -d '\n')"
tmp="$(mktemp)"
awk -v k="PANEL_SECRET" -v v="$gen" '
BEGIN { done=0 }
index($0, k "=")==1 { print k "=" v; done=1; next }
{ print }
END { if (!done) print k "=" v }
' "$ROOT_DIR/config/settings.conf" > "$tmp"
mv "$tmp" "$ROOT_DIR/config/settings.conf"
PANEL_SECRET="$gen"
warn "已自动生成 PANEL_SECRET(见 settings.conf"
fi
# 再 load 一次保证变量一致
load_config
CELLULAR_IFACE="$cell"
CELLULAR_SOURCE_IP="${src_ip:-$CELLULAR_SOURCE_IP}"
info "2/6 下载/安装 sing-box + UI"
"$ROOT_DIR/scripts/fetch-binaries.sh"
info "3/6 生成配置(代理出口 bind = $cell"
"$ROOT_DIR/scripts/generate.sh" "$ROOT_DIR/generated"
ensure_dirs
mkdir -p /var/lib/cellular-proxy
install -m 0644 "$ROOT_DIR/generated/config.json" "$INSTALL_DIR/etc/config.json"
install -m 0644 "$ROOT_DIR/generated/runtime.env" "$INSTALL_DIR/etc/runtime.env"
install -m 0644 "$ROOT_DIR/config/settings.conf" "$INSTALL_DIR/etc/settings.conf"
cp -a "$ROOT_DIR/scripts" "$INSTALL_DIR/"
cp -a "$ROOT_DIR/ui" "$INSTALL_DIR/" 2>/dev/null || true
if ! "$INSTALL_DIR/bin/sing-box" check -c "$INSTALL_DIR/etc/config.json"; then
die "sing-box 配置校验失败"
fi
# 确认 bind_interface 写进配置
if ! grep -q "\"bind_interface\": \"$cell\"" "$INSTALL_DIR/etc/config.json" \
&& ! grep -q "\"bind_interface\": \"$cell\"" "$INSTALL_DIR/etc/config.json" 2>/dev/null; then
# JSON 可能无空格差异
if ! python3 - "$INSTALL_DIR/etc/config.json" "$cell" <<'PY'
import json,sys
c=json.load(open(sys.argv[1]))
cell=sys.argv[2]
ok=any(o.get("tag")=="cellular" and o.get("bind_interface")==cell for o in c.get("outbounds",[]))
sys.exit(0 if ok else 1)
PY
then
die "配置未正确绑定数据网卡 $cell"
fi
fi
info "配置已确认 bind_interface=$cell"
install -m 0755 /dev/stdin "$INSTALL_DIR/bin/cpxy" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
BASE="$(cd "$(dirname "$0")/.." && pwd)"
export CONFIG_FILE="${CONFIG_FILE:-$BASE/etc/settings.conf}"
cmd="${1:-help}"
shift || true
case "$cmd" in
detect) exec "$BASE/scripts/detect.sh" "$@" ;;
generate)
# 重新探测:若 settings 里网卡丢失则自动补
# shellcheck source=/dev/null
source "$BASE/scripts/lib.sh"
load_config
if [[ -z "${CELLULAR_IFACE:-}" ]] || ! iface_exists "${CELLULAR_IFACE:-}"; then
cell="$(detect_cellular_iface "" || true)"
if [[ -n "$cell" ]]; then
src="$(detect_source_ip "$cell" || true)"
persist_cellular_to_settings "$CONFIG_FILE" "$cell" "$src"
CELLULAR_IFACE="$cell"
fi
fi
"$BASE/scripts/generate.sh" "$BASE/generated"
install -m 0644 "$BASE/generated/config.json" "$BASE/etc/config.json"
install -m 0644 "$BASE/generated/runtime.env" "$BASE/etc/runtime.env"
install -m 0644 "$CONFIG_FILE" "$BASE/etc/settings.conf" 2>/dev/null || true
"$BASE/bin/sing-box" check -c "$BASE/etc/config.json"
systemctl restart cellular-proxy 2>/dev/null || true
;;
rebind)
# shellcheck source=/dev/null
source "$BASE/scripts/lib.sh"
load_config
cell="$(detect_cellular_iface "" || true)"
[[ -n "$cell" ]] || die "未能探测数据网卡"
src="$(detect_source_ip "$cell" || true)"
persist_cellular_to_settings "$CONFIG_FILE" "$cell" "$src"
info "已重新绑定 $cell"
exec "$0" generate
;;
start) systemctl start cellular-proxy ;;
stop) systemctl stop cellular-proxy ;;
restart) systemctl restart cellular-proxy ;;
status) systemctl status cellular-proxy --no-pager || true ;;
verify) exec "$BASE/scripts/verify.sh" "$@" ;;
logs) journalctl -u cellular-proxy -n "${1:-80}" -f ;;
help|*)
cat <<H
cpxy — cellular-proxy
语义: 走代理的连接一律从数据网卡出口
cpxy detect | rebind | generate | start | stop | restart | status
cpxy verify | logs [N]
H
;;
esac
EOF
ln -sfn "$INSTALL_DIR/bin/cpxy" /usr/local/bin/cpxy
info "4/6 安装 systemd"
MEM="${MEMORY_MAX_MB:-96}"
install -m 0644 "$ROOT_DIR/systemd/cellular-proxy.service" /etc/systemd/system/cellular-proxy.service
sed -i "s|@INSTALL_DIR@|$INSTALL_DIR|g" /etc/systemd/system/cellular-proxy.service
sed -i "s|@LOG_DIR@|$LOG_DIR|g" /etc/systemd/system/cellular-proxy.service
if [[ "$MEM" != "0" && -n "$MEM" ]]; then
sed -i "s|@MEMORY_MAX@|${MEM}M|g" /etc/systemd/system/cellular-proxy.service
else
sed -i '/MemoryMax=@MEMORY_MAX@/d' /etc/systemd/system/cellular-proxy.service
fi
systemctl daemon-reload
info "5/6 启动服务"
if [[ "$SKIP_START" == "true" ]]; then
systemctl enable cellular-proxy.service
systemctl stop cellular-proxy.service 2>/dev/null || true
info "已按 SKIP_START 跳过启动"
else
systemctl enable cellular-proxy.service
systemctl restart cellular-proxy.service
sleep 2
if ! systemctl is-active --quiet cellular-proxy.service; then
err "服务未处于 active"
systemctl --no-pager --full status cellular-proxy.service || true
journalctl -u cellular-proxy -n 40 --no-pager || true
die "启动失败,请检查数据网卡 $cell 是否 up 且有 IP"
fi
systemctl --no-pager --full status cellular-proxy.service || true
fi
info "6/6 自动验证出口"
if [[ "$SKIP_START" != "true" && "$AUTO_VERIFY" == "true" ]]; then
if "$ROOT_DIR/scripts/verify.sh"; then
info "出口验证通过:代理已从数据网卡出"
else
warn "自动验证未通过(可能数据侧暂无公网/与 WiFi 同出口)。请稍后: cpxy verify"
warn "当前绑定: CELLULAR_IFACE=$cell ip=${src_ip:-}"
fi
fi
echo
info "安装完成(全自动探测 + 绑定)"
echo " 数据网卡: $cell (${src_ip:-no-ipv4})"
echo " 语义: 不走代理 → 系统默认(WiFi);走代理 → 数据流量"
echo " 代理: HTTP/SOCKS ${PROXY_LISTEN_HOST}:${PROXY_MIXED_PORT}"
echo " 面板: http://<LAN-IP>:${PANEL_PORT}/"
echo " 密钥: $PANEL_SECRET"
echo " 配置: $INSTALL_DIR/etc/settings.conf"
echo " 管理: cpxy rebind | verify | logs | status"