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

157 lines
4.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# 生成 sing-box:代理流量全部 bind 数据网卡出口
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# shellcheck source=lib.sh
source "$ROOT_DIR/scripts/lib.sh"
load_config
OUT_DIR="${1:-$ROOT_DIR/generated}"
mkdir -p "$OUT_DIR"
cell="$(resolve_cellular 2>/dev/null || true)"
if [[ -z "$cell" ]]; then
resolve_cellular >/dev/null || true
fi
src_ip="${CELLULAR_SOURCE_IP:-}"
if [[ -z "$src_ip" && -n "$cell" ]]; then
src_ip="$(detect_source_ip "$cell" || true)"
fi
if [[ -z "$cell" ]]; then
warn "CELLULAR_IFACE 为空,临时用 lo 生成配置(启动前务必修正)"
cell_for_cfg=lo
else
cell_for_cfg="$cell"
fi
if [[ "$PANEL_SECRET" == "please-change-me" || "$PANEL_SECRET" == "change-this-secret" ]]; then
warn "PANEL_SECRET 仍是默认值,请修改 settings.conf"
fi
if [[ "$PROXY_LISTEN_HOST" != "127.0.0.1" && "$PROXY_LISTEN_HOST" != "::1" && -z "$PROXY_USER" ]]; then
warn "代理监听 $PROXY_LISTEN_HOST 且未设置 PROXY_USER/PASS(局域网建议加鉴权)"
fi
export GEN_OUT="$OUT_DIR/config.json"
export GEN_CELL="$cell_for_cfg"
export GEN_SRC="$src_ip"
export GEN_PROXY_HOST="$PROXY_LISTEN_HOST"
export GEN_PROXY_PORT="$PROXY_MIXED_PORT"
export GEN_PROXY_USER="$PROXY_USER"
export GEN_PROXY_PASS="$PROXY_PASS"
export GEN_PANEL_HOST="$PANEL_LISTEN_HOST"
export GEN_PANEL_PORT="$PANEL_PORT"
export GEN_PANEL_SECRET="$PANEL_SECRET"
export GEN_DNS="$ENABLE_DNS"
export GEN_LOG="$LOG_LEVEL"
export GEN_UI_DIR="$INSTALL_DIR/ui"
python3 <<'PY'
import json, os
from pathlib import Path
cell = os.environ["GEN_CELL"]
src = os.environ.get("GEN_SRC") or ""
log_level = os.environ.get("GEN_LOG") or "warn"
ui_dir = os.environ.get("GEN_UI_DIR") or "/opt/cellular-proxy/ui"
users = []
u = os.environ.get("GEN_PROXY_USER") or ""
p = os.environ.get("GEN_PROXY_PASS") or ""
if u:
users.append({"username": u, "password": p})
inbound = {
"type": "mixed",
"tag": "mixed-in",
"listen": os.environ["GEN_PROXY_HOST"],
"listen_port": int(os.environ["GEN_PROXY_PORT"]),
}
if users:
inbound["users"] = users
# 唯一业务出口:强制绑定数据网卡
out_cell = {
"type": "direct",
"tag": "cellular",
"bind_interface": cell,
}
if src:
out_cell["inet4_bind_address"] = src
cfg = {
"log": {"level": log_level, "timestamp": True},
"inbounds": [inbound],
"outbounds": [
out_cell,
{"type": "block", "tag": "block"},
],
# 所有代理流量最终都走 cellular(数据)
"route": {
"rules": [],
"final": "cellular",
"auto_detect_interface": False,
},
"experimental": {
"clash_api": {
"external_controller": f"{os.environ['GEN_PANEL_HOST']}:{os.environ['GEN_PANEL_PORT']}",
"secret": os.environ["GEN_PANEL_SECRET"],
"default_mode": "rule",
"external_ui": ui_dir,
},
"cache_file": {
"enabled": True,
"path": "/var/lib/cellular-proxy/cache.db",
"store_fakeip": False,
},
},
}
if (os.environ.get("GEN_DNS") or "true").lower() == "true":
# DNS 也走数据出口,避免解析从 WiFi 出去
cfg["dns"] = {
"servers": [
{
"tag": "remote",
"address": "1.1.1.1",
"detour": "cellular",
},
],
"final": "remote",
"strategy": "ipv4_only",
}
Path(os.environ["GEN_OUT"]).write_text(
json.dumps(cfg, indent=2, ensure_ascii=False) + "\n", encoding="utf-8"
)
print("wrote", os.environ["GEN_OUT"])
PY
cat > "$OUT_DIR/runtime.env" <<EOF
INSTALL_DIR=${INSTALL_DIR}
LOG_DIR=${LOG_DIR}
CELLULAR_IFACE=${cell}
CELLULAR_SOURCE_IP=${src_ip}
PROXY_LISTEN_HOST=${PROXY_LISTEN_HOST}
PROXY_MIXED_PORT=${PROXY_MIXED_PORT}
PANEL_LISTEN_HOST=${PANEL_LISTEN_HOST}
PANEL_PORT=${PANEL_PORT}
EGRESS_CHECK_URL=${EGRESS_CHECK_URL}
EXPECTED_CELLULAR_PUBLIC_IP=${EXPECTED_CELLULAR_PUBLIC_IP}
MEMORY_MAX_MB=${MEMORY_MAX_MB}
REQUIRE_CELLULAR_IFACE=${REQUIRE_CELLULAR_IFACE}
EOF
cat > "$OUT_DIR/SUMMARY.txt" <<EOF
generated_at=$(date -Iseconds)
语义=走代理的连接一律从数据网卡出口
cellular_iface=${cell}
cellular_source_ip=${src_ip}
proxy=${PROXY_LISTEN_HOST}:${PROXY_MIXED_PORT} (HTTP+SOCKS mixed)
panel=http://<LAN-IP>:${PANEL_PORT}/ secret=见 settings PANEL_SECRET
memory_max_mb=${MEMORY_MAX_MB}
EOF
info "生成完成: $OUT_DIR"
cat "$OUT_DIR/SUMMARY.txt"