#!/usr/bin/env bash # ============================================================================= # Squid Manager - 一键部署脚本 # ============================================================================= # 功能: # 1. 安装系统依赖 (squid, python3, pip, gcc 等) # 2. 创建 Python 虚拟环境并安装 pip 依赖 # 3. 初始化数据库 & 默认管理员 # 4. 生成 gunicorn + systemd 服务文件并启动 # 5. 配置 squid (放行 localnet + 基本安全设置) # # 用法: # bash deploy.sh # 全自动部署 (默认 /opt/squid-manager) # INSTALL_DIR=/opt/sm bash deploy.sh # 自定义安装目录 # bash deploy.sh --no-squid # 不安装/配置 squid # bash deploy.sh --no-start # 不启动服务 (仅生成文件) # ============================================================================= set -euo pipefail # ---- 配置 ------------------------------------------------------------------- INSTALL_DIR="${INSTALL_DIR:-/opt/squid-manager}" SERVICE_NAME="${SERVICE_NAME:-squid-manager}" GUNICORN_HOST="${SQUIDMGR_HOST:-0.0.0.0}" GUNICORN_PORT="${SQUIDMGR_PORT:-5200}" GUNICORN_WORKERS="${SQUIDMGR_WORKERS:-2}" ADMIN_USER="${ADMIN_USER:-admin}" ADMIN_PASS="${ADMIN_PASS:-admin123}" INSTALL_SQUID=true START_SERVICE=true SKIP_EXISTING=false # 若服务已存在是否跳过 for arg in "$@"; do case "$arg" in --no-squid) INSTALL_SQUID=false ;; --no-start) START_SERVICE=false ;; --skip-existing) SKIP_EXISTING=true ;; -h|--help) sed -n '2,20p' "$0" exit 0 ;; *) echo "未知参数: $arg" >&2; exit 1 ;; esac done # ---- 工具函数 --------------------------------------------------------------- RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' info() { echo -e "${GREEN}[INFO]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } _generate_minimal_squid_conf() { # 生成一份最小可用的 Squid 5.x 配置 local out="$1" cat > "$out" << 'SQUIDEOF' # Minimal Squid 5.x configuration — generated by Squid Manager deploy.sh # 可通过 Web UI (/config/raw) 继续编辑和扩展。 http_port 3128 icp_port 3130 # Cache cache_mem 256 MB maximum_object_size 4096 KB cache_dir ufs /var/spool/squid 100 16 256 # ACLs acl localhost src 127.0.0.1/32 ::1 acl to_localhost dst 127.0.0.0/8 0.0.0.0/32 ::1 acl localnet src 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 169.254.0.0/16 fc00::/7 fe80::/10 acl Safe_ports port 80 443 21 70 210 1025-65535 acl SSL_ports port 443 563 acl CONNECT method CONNECT # Refresh patterns refresh_pattern ^ftp: 1440 20% 10080 refresh_pattern ^gopher: 1440 0% 1440 refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 refresh_pattern . 0 20% 4320 # Access rules http_access allow manager localhost http_access deny manager http_access deny !Safe_ports http_access deny CONNECT !SSL_ports http_access allow localhost http_access allow localnet http_access deny all # Logging access_log /var/log/squid/access.log squid cache_log /var/log/squid/cache.log # Misc coredump_dir /var/spool/squid visible_hostname squid-manager SQUIDEOF } must_run_as_root() { if [[ $EUID -ne 0 ]]; then error "此脚本必须以 root 运行 (sudo $0)" exit 1 fi } detect_os() { if [[ -f /etc/os-release ]]; then . /etc/os-release OS_ID="$ID" OS_VER="$VERSION_ID" elif command -v apt-get &>/dev/null; then OS_ID="debian" elif command -v yum &>/dev/null; then OS_ID="centos" else error "无法识别操作系统" exit 1 fi info "操作系统: $OS_ID $OS_VER" } pkg_install() { info "安装系统包: $*" case "$OS_ID" in debian|ubuntu) export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y -qq "$@" ;; centos|rhel|rocky|almalinux|ol) yum install -y -q "$@" ;; *) error "不支持的包管理器: $OS_ID" exit 1 ;; esac } # ---- 1. 前置检查 ------------------------------------------------------------ must_run_as_root detect_os info "安装目录: $INSTALL_DIR" info "服务名: $SERVICE_NAME" info "监听: $GUNICORN_HOST:$GUNICORN_PORT" # ---- 2. 安装系统依赖 -------------------------------------------------------- SYS_PACKAGES=( python3 python3-pip python3-venv python3-dev gcc libffi-dev libssl-dev curl ) if $INSTALL_SQUID; then SYS_PACKAGES+=(squid squidclient) fi pkg_install "${SYS_PACKAGES[@]}" # 确保 python3 / pip3 可用 PYTHON_BIN="$(command -v python3)" PIP_BIN="$(command -v pip3 || command -v pip)" info "Python: $PYTHON_BIN" info "Pip: $PIP_BIN" # ---- 3. 部署项目文件 -------------------------------------------------------- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" if [[ "$SCRIPT_DIR" != "$INSTALL_DIR" ]]; then if [[ -d "$INSTALL_DIR" ]] && $SKIP_EXISTING; then warn "安装目录已存在且 --skip-existing,跳过文件复制" else info "复制项目文件到 $INSTALL_DIR" mkdir -p "$INSTALL_DIR" # 复制源码(排除 .git / instance / backups 等运行时目录) rsync -a --delete \ --exclude='.git/' \ --exclude='__pycache__/' \ --exclude='*.pyc' \ --exclude='instance/*.db' \ --exclude='backups/' \ --exclude='uploads/' \ --exclude='ssl_certs/*.pem' \ --exclude='.env' \ "$SCRIPT_DIR/" "$INSTALL_DIR/" fi fi cd "$INSTALL_DIR" # 确保运行时目录存在 mkdir -p instance backups uploads ssl_certs chmod 700 instance ssl_certs # ---- 4. 创建虚拟环境 + 安装 pip 依赖 ---------------------------------------- VENV_DIR="$INSTALL_DIR/.venv" if [[ -d "$VENV_DIR" ]] && $SKIP_EXISTING; then warn "虚拟环境已存在且 --skip-existing,跳过依赖安装" else info "创建 Python 虚拟环境: $VENV_DIR" $PYTHON_BIN -m venv "$VENV_DIR" info "升级 pip + 安装 Python 依赖" "$VENV_DIR/bin/pip" install --upgrade pip setuptools wheel "$VENV_DIR/bin/pip" install -r requirements.txt fi PYTHON="$VENV_DIR/bin/python" GUNICORN="$VENV_DIR/bin/gunicorn" # ---- 5. 生成 .env 配置 ------------------------------------------------------ ENV_FILE="$INSTALL_DIR/.env" if [[ ! -f "$ENV_FILE" ]] || ! $SKIP_EXISTING; then info "生成 .env 配置文件" SECRET_KEY="$($PYTHON -c "import secrets; print(secrets.token_urlsafe(48))")" cat > "$ENV_FILE" </dev/null; then warn "现有 squid.conf 语法校验失败,备份后用最小可用配置替换" cp "$SQUID_CONF" "${SQUID_CONF}.broken.$(date +%s)" _generate_minimal_squid_conf "$SQUID_CONF" fi else info "squid.conf 不存在,生成最小可用配置" mkdir -p "$(dirname "$SQUID_CONF")" _generate_minimal_squid_conf "$SQUID_CONF" fi if [[ -f "$SQUID_CONF" ]]; then info "配置 Squid" # 备份原始配置 if [[ ! -f "${SQUID_CONF}.orig" ]]; then cp "$SQUID_CONF" "${SQUID_CONF}.orig" info "已备份原始配置到 ${SQUID_CONF}.orig" fi # 放行内网 (localnet) # 1) 确保 localnet ACL 存在 if grep -qE '^acl[[:space:]]+localnet' "$SQUID_CONF"; then info "localnet ACL 已存在" else _localnet_def="acl localnet src 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 169.254.0.0/16 fc00::/7 fe80::/10" # 找到第一个 http_access 行之前插入 if grep -n '^http_access' "$SQUID_CONF" | head -1 | cut -d: -f1 >/dev/null; then _insert_line=$(grep -n '^http_access' "$SQUID_CONF" | head -1 | cut -d: -f1) sed -i "${_insert_line}i${_localnet_def}" "$SQUID_CONF" else echo "$_localnet_def" >> "$SQUID_CONF" fi info "已添加 localnet ACL 定义" fi # 2) 确保 http_access allow localnet 在 deny all 之前 if grep -q '^http_access allow localnet' "$SQUID_CONF"; then info "http_access allow localnet 已存在" else if grep -q '^http_access deny all' "$SQUID_CONF"; then sed -i '/^http_access deny all/i http_access allow localnet' "$SQUID_CONF" else echo "http_access allow localnet" >> "$SQUID_CONF" echo "http_access deny all" >> "$SQUID_CONF" fi info "已添加 http_access allow localnet" fi # 3) 处理注释掉的老配置 if grep -q '^# http_access allow localnet' "$SQUID_CONF"; then sed -i 's/^# http_access allow localnet/http_access allow localnet/' "$SQUID_CONF" fi # visible_hostname if ! grep -q '^visible_hostname' "$SQUID_CONF"; then echo "visible_hostname $(hostname -f 2>/dev/null || hostname)" >> "$SQUID_CONF" fi # 语法校验 if squid -k parse -f "$SQUID_CONF" &>/dev/null; then info "Squid 配置语法校验通过" else warn "Squid 配置语法校验有警告,请检查 squid -k parse 输出" squid -k parse -f "$SQUID_CONF" 2>&1 | tail -10 fi # 初始化缓存目录 (首次启动需要 squid -z) if [[ ! -d /var/spool/squid/00 ]]; then info "初始化 Squid 缓存目录" squid -z 2>/dev/null || true chown -R proxy:proxy /var/spool/squid 2>/dev/null || true fi # 启动 squid 服务 if command -v systemctl &>/dev/null; then systemctl enable --now squid 2>/dev/null || { warn "squid 服务启动失败,请手动检查 (systemctl status squid)" } if systemctl is-active --quiet squid; then info "Squid 服务运行正常 ✓" fi fi else warn "未生成 squid.conf,跳过 squid 配置" fi fi # ---- 8. 生成 systemd 服务文件 ---------------------------------------------- SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service" info "生成 systemd 服务文件: $SERVICE_FILE" cat > "$SERVICE_FILE" <:${GUNICORN_PORT}" echo " 账号: ${ADMIN_USER} / ${ADMIN_PASS}" echo " 安装目录: ${INSTALL_DIR}" echo " 服务名: ${SERVICE_NAME}" echo " 配置文件: ${INSTALL_DIR}/.env" echo "" echo " 常用命令:" echo " systemctl status $SERVICE_NAME # 查看状态" echo " systemctl restart $SERVICE_NAME # 重启服务" echo " journalctl -u $SERVICE_NAME -f # 实时日志" echo "" echo " ⚠️ 请立即登录后修改默认密码!" echo "================================================================"