Files
cellular-proxy/install.sh
T
Hermes 8835646d34 feat: cellular-proxy 一键安装 — 走代理一律数据出口
- sing-box 单进程 bind 数据网卡
- mixed HTTP+SOCKS + 中文静态面板
- install.sh 支持 curl | sudo bash
- 512MB 友好 MemoryMax 默认 96MB
2026-07-21 10:56:27 +00:00

249 lines
7.4 KiB
Bash
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
# cellular-proxy 一键安装
# 语义:走本代理的连接一律从数据网卡(流量)出口;系统默认仍可走 WiFi
#
# 用法示例:
# curl -fsSL 'https://gitea.chickliu.fun/Hermes/cellular-proxy/raw/branch/main/install.sh' | sudo bash
# curl -fsSL '...' | sudo bash -s -- --iface wwan0 --secret 'xxx' --user u --pass p
# sudo bash install.sh --iface usb0 --port 7890 --panel-port 9090
#
set -euo pipefail
REPO_OWNER="${REPO_OWNER:-Hermes}"
REPO_NAME="${REPO_NAME:-cellular-proxy}"
GITEA_BASE="${GITEA_BASE:-https://gitea.chickliu.fun}"
BRANCH="${BRANCH:-main}"
# 可用环境变量覆盖:CELLULAR_PROXY_REF=v1.0.0 或 commit sha
REF="${CELLULAR_PROXY_REF:-$BRANCH}"
INSTALL_DIR="${INSTALL_DIR:-/opt/cellular-proxy}"
WORK_DIR="${WORK_DIR:-/tmp/cellular-proxy-install-$$}"
# 可选 CLI 预填(也可装完再改 settings)
OPT_IFACE=""
OPT_SECRET=""
OPT_USER=""
OPT_PASS=""
OPT_PORT=""
OPT_PANEL_PORT=""
OPT_MEMORY=""
OPT_SKIP_START=0
OPT_LOCAL_DIR=""
log() { printf '[%s] %s\n' "$(date '+%F %T')" "$*" >&2; }
info() { log "INFO $*"; }
warn() { log "WARN $*"; }
err() { log "ERROR $*"; }
die() { err "$*"; exit 1; }
usage() {
cat <<'H'
cellular-proxy 一键安装
选项:
--iface NAME 数据网卡名(如 wwan0 / usb0
--secret STR 面板 PANEL_SECRET
--user USER 代理账号 PROXY_USER
--pass PASS 代理密码 PROXY_PASS
--port N 代理 mixed 端口(默认 7890
--panel-port N 面板端口(默认 9090)
--memory MB MemoryMax MB(默认 960=不限制)
--install-dir PATH 安装目录(默认 /opt/cellular-proxy
--local DIR 使用本地源码目录,不从 Gitea 下载
--ref REF 分支/tag/commit(默认 main
--skip-start 只安装不启动
-h, --help 帮助
环境变量:
GITEA_BASE REPO_OWNER REPO_NAME CELLULAR_PROXY_REF INSTALL_DIR
H
}
while [[ $# -gt 0 ]]; do
case "$1" in
--iface) OPT_IFACE="${2:-}"; shift 2 ;;
--secret) OPT_SECRET="${2:-}"; shift 2 ;;
--user) OPT_USER="${2:-}"; shift 2 ;;
--pass) OPT_PASS="${2:-}"; shift 2 ;;
--port) OPT_PORT="${2:-}"; shift 2 ;;
--panel-port) OPT_PANEL_PORT="${2:-}"; shift 2 ;;
--memory) OPT_MEMORY="${2:-}"; shift 2 ;;
--install-dir) INSTALL_DIR="${2:-}"; shift 2 ;;
--local) OPT_LOCAL_DIR="${2:-}"; shift 2 ;;
--ref) REF="${2:-}"; shift 2 ;;
--skip-start) OPT_SKIP_START=1; shift ;;
-h|--help) usage; exit 0 ;;
*) die "未知参数: $1--help 查看用法)" ;;
esac
done
need_root() {
if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
die "请用 root 运行:sudo bash install.sh ..."
fi
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || die "缺少命令: $1"
}
download() {
local url="$1" dest="$2"
if command -v curl >/dev/null 2>&1; then
curl -fL --retry 3 --connect-timeout 20 -o "$dest" "$url"
elif command -v wget >/dev/null 2>&1; then
wget -O "$dest" "$url"
else
die "需要 curl 或 wget"
fi
}
set_kv() {
# set_kv file KEY value — 存在则替换行,否则追加
local file="$1" key="$2" val="$3"
local tmp
tmp="$(mktemp)"
if grep -qE "^${key}=" "$file" 2>/dev/null; then
# 用 awk 避免 sed 对特殊字符敏感
awk -v k="$key" -v v="$val" '
BEGIN { done=0 }
index($0, k "=")==1 { print k "=" v; done=1; next }
{ print }
END { if (!done) print k "=" v }
' "$file" > "$tmp"
mv "$tmp" "$file"
else
printf '%s=%s\n' "$key" "$val" >> "$file"
rm -f "$tmp"
fi
}
fetch_source() {
mkdir -p "$WORK_DIR"
if [[ -n "$OPT_LOCAL_DIR" ]]; then
[[ -d "$OPT_LOCAL_DIR" ]] || die "本地目录不存在: $OPT_LOCAL_DIR"
info "使用本地源码: $OPT_LOCAL_DIR"
mkdir -p "$WORK_DIR/src"
if command -v rsync >/dev/null 2>&1; then
rsync -a --delete \
--exclude '.git' \
--exclude 'generated' \
--exclude 'config/settings.conf' \
"$OPT_LOCAL_DIR"/ "$WORK_DIR/src"/
else
cp -a "$OPT_LOCAL_DIR"/. "$WORK_DIR/src"/
rm -rf "$WORK_DIR/src/.git" "$WORK_DIR/src/generated" "$WORK_DIR/src/config/settings.conf" 2>/dev/null || true
fi
return
fi
local tarball="$WORK_DIR/src.tgz"
local url_archive="${GITEA_BASE}/${REPO_OWNER}/${REPO_NAME}/archive/${REF}.tar.gz"
info "下载源码: $url_archive"
if ! download "$url_archive" "$tarball"; then
die "下载失败。请检查网络或: git clone ${GITEA_BASE}/${REPO_OWNER}/${REPO_NAME}.git"
fi
mkdir -p "$WORK_DIR/extract"
tar -xzf "$tarball" -C "$WORK_DIR/extract"
local top
top="$(find "$WORK_DIR/extract" -mindepth 1 -maxdepth 1 -type d | head -1)"
[[ -n "$top" ]] || die "压缩包内容异常"
mkdir -p "$WORK_DIR/src"
if command -v rsync >/dev/null 2>&1; then
rsync -a "$top"/ "$WORK_DIR/src"/
else
cp -a "$top"/. "$WORK_DIR/src"/
fi
}
prepare_settings() {
local src="$WORK_DIR/src"
local conf="$src/config/settings.conf"
[[ -f "$src/config/settings.conf.example" ]] || die "缺少 settings.conf.example"
cp "$src/config/settings.conf.example" "$conf"
if [[ -n "$OPT_IFACE" ]]; then
set_kv "$conf" CELLULAR_IFACE "$OPT_IFACE"
fi
if [[ -n "$OPT_SECRET" ]]; then
set_kv "$conf" PANEL_SECRET "$OPT_SECRET"
else
# 随机 secret,避免默认 please-change-me 裸奔
local gen
gen="$(openssl rand -hex 12 2>/dev/null || head -c 16 /dev/urandom | xxd -p)"
set_kv "$conf" PANEL_SECRET "$gen"
warn "已生成随机 PANEL_SECRET(见 $INSTALL_DIR/etc/settings.conf"
fi
if [[ -n "$OPT_USER" ]]; then
set_kv "$conf" PROXY_USER "$OPT_USER"
fi
if [[ -n "$OPT_PASS" ]]; then
set_kv "$conf" PROXY_PASS "$OPT_PASS"
fi
if [[ -n "$OPT_PORT" ]]; then
set_kv "$conf" PROXY_MIXED_PORT "$OPT_PORT"
fi
if [[ -n "$OPT_PANEL_PORT" ]]; then
set_kv "$conf" PANEL_PORT "$OPT_PANEL_PORT"
fi
if [[ -n "$OPT_MEMORY" ]]; then
set_kv "$conf" MEMORY_MAX_MB "$OPT_MEMORY"
fi
set_kv "$conf" INSTALL_DIR "$INSTALL_DIR"
}
main() {
need_root
need_cmd tar
need_cmd awk
command -v curl >/dev/null 2>&1 || command -v wget >/dev/null 2>&1 || die "需要 curl 或 wget"
trap 'rm -rf "$WORK_DIR"' EXIT
info "1/4 获取源码"
fetch_source
info "2/4 写入 settings.conf"
prepare_settings
info "3/4 调用官方 install.sh(下载 sing-box + systemd"
# 临时把 install 行为调整:settings 已存在
export CONFIG_FILE="$WORK_DIR/src/config/settings.conf"
bash "$WORK_DIR/src/scripts/install.sh"
if [[ "$OPT_SKIP_START" -eq 1 ]]; then
systemctl stop cellular-proxy 2>/dev/null || true
info "已按 --skip-start 停止服务"
fi
info "4/4 完成"
local port panel secret
# shellcheck disable=SC1090
source "$INSTALL_DIR/etc/settings.conf" 2>/dev/null || true
port="${PROXY_MIXED_PORT:-7890}"
panel="${PANEL_PORT:-9090}"
secret="${PANEL_SECRET:- $INSTALL_DIR/etc/settings.conf}"
local lan
lan="$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1); exit}}' || true)"
cat <<EOM
========== 安装完成 ==========
语义: 走代理 → 数据流量;不走代理 → 系统默认(WiFi)
代理: HTTP/SOCKS ${lan:-<LAN-IP>}:${port}
面板: http://${lan:-<LAN-IP>}:${panel}/
密钥: ${secret}
配置: $INSTALL_DIR/etc/settings.conf
验证:
cpxy verify
curl --socks5-hostname 127.0.0.1:${port} https://ifconfig.me
管理:
cpxy status | logs | generate | restart
EOM
}
main "$@"