feat: frps-manager v1.0 - frp 0.70.1 multi-tenant web management system
- Multi-tenant client/proxy management with Flask+SQLite - Frps server control (start/restart/token rotation) - Generate frpc.ini with shared server token + admin API - Dashboard API integration (live client/proxy status) - RBAC: admin/tenant_admin/user roles - Audit logging for all operations - Systemd service files included - Requires legacy INI format (frp 0.70 TOML auth broken)
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env bash
|
||||
# frps-manager one-shot deploy script.
|
||||
# Installs frps + frps-manager as systemd services on a Debian/Ubuntu or RHEL box.
|
||||
# Run as root (or with sudo).
|
||||
#
|
||||
# Usage: sudo bash scripts/deploy.sh [--dry-run] [--no-systemd] [--dev]
|
||||
# What it does:
|
||||
# 1. Detects distro + package manager, installs system deps
|
||||
# 2. Downloads the latest stable frps binary if runtime/bin/frps is missing
|
||||
# 3. Creates a Python venv, installs requirements
|
||||
# 4. Initializes the SQLite database
|
||||
# 5. Generates a default frps.ini (admin user, default tenant, bindPort 7000)
|
||||
# 6. Installs + starts systemd services (frps + frps-manager on :5390)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DRY_RUN=0
|
||||
NO_SYSTEMD=0
|
||||
DEV=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run) DRY_RUN=1 ;;
|
||||
--no-systemd) NO_SYSTEMD=1 ;;
|
||||
--dev) DEV=1 ;;
|
||||
-h|--help)
|
||||
grep '^#' "$0" | sed 's/^# \?//'
|
||||
exit 0 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
log() { echo "[deploy] $*"; }
|
||||
die() { echo "[deploy][FATAL] $*" >&2; exit 1; }
|
||||
|
||||
# --- distro detection -------------------------------------------------------
|
||||
if [ -f /etc/os-release ]; then
|
||||
. /etc/os-release
|
||||
OS_ID="$ID"
|
||||
OS_VER="$VERSION_ID"
|
||||
else
|
||||
die "Cannot detect /etc/os-release"
|
||||
fi
|
||||
case "$OS_ID" in
|
||||
ubuntu|debian|raspbian) PKG=apt; SUDO=sudo ;;
|
||||
centos|rhel|rocky|almalinux|opencloudos|openanolis) PKG=yum; SUDO=sudo ;;
|
||||
fedora) PKG=dnf; SUDO=sudo ;;
|
||||
*)
|
||||
log "Unknown distro $OS_ID — assuming apt-compatible"
|
||||
PKG=apt; SUDO=sudo ;;
|
||||
esac
|
||||
[ "$(id -u)" -eq 0 ] && SUDO=""
|
||||
|
||||
log "Detected: $OS_ID $OS_VER (pkg manager: $PKG)"
|
||||
|
||||
# --- frps download ----------------------------------------------------------
|
||||
if [ ! -x runtime/bin/frps ]; then
|
||||
log "frps binary missing — fetching v0.70.1 from GitHub"
|
||||
if [ "$DRY_RUN" = 1 ]; then
|
||||
log "DRY: would download frp_0.70.1_linux_amd64.tar.gz"
|
||||
else
|
||||
TMPDIR=$(mktemp -d)
|
||||
trap "rm -rf $TMPDIR" EXIT
|
||||
ARCH="$(uname -m)"
|
||||
case "$ARCH" in
|
||||
x86_64) FRPS_ARCH=amd64 ;;
|
||||
aarch64|arm64) FRPS_ARCH=arm64 ;;
|
||||
armv7l|armv7) FRPS_ARCH=arm ;;
|
||||
*) die "Unsupported arch: $ARCH (please build/download frps manually)" ;;
|
||||
esac
|
||||
URL="https://github.com/fatedier/frp/releases/download/v0.70.1/frp_0.70.1_linux_${FRPS_ARCH}.tar.gz"
|
||||
log "Downloading $URL"
|
||||
curl -fSL --max-time 120 -o "$TMPDIR/frp.tgz" "$URL" \
|
||||
|| die "Failed to download frps"
|
||||
tar xzf "$TMPDIR/frp.tgz" -C "$TMPDIR"
|
||||
mkdir -p runtime/bin
|
||||
cp "$TMPDIR"/frp_0.70.1_linux_${FRPS_ARCH}/frps runtime/bin/frps
|
||||
chmod +x runtime/bin/frps
|
||||
log "Installed runtime/bin/frps"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- system deps ------------------------------------------------------------
|
||||
log "Installing system dependencies"
|
||||
if [ "$DRY_RUN" = 0 ]; then
|
||||
case "$PKG" in
|
||||
apt)
|
||||
$SUDO apt-get update -y >/dev/null
|
||||
$SUDO apt-get install -y python3 python3-venv python3-pip curl ca-certificates >/dev/null
|
||||
;;
|
||||
yum|dnf)
|
||||
$SUDO $PKG install -y python3 python3-pip python3-venv curl ca-certificates >/dev/null
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# --- venv -------------------------------------------------------------------
|
||||
if [ ! -d venv ]; then
|
||||
log "Creating Python venv"
|
||||
if [ "$DRY_RUN" = 0 ]; then
|
||||
python3 -m venv venv
|
||||
venv/bin/pip install --upgrade pip >/dev/null
|
||||
venv/bin/pip install -r requirements.txt
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- db init ----------------------------------------------------------------
|
||||
log "Initializing database"
|
||||
if [ "$DRY_RUN" = 0 ]; then
|
||||
venv/bin/python init_db.py
|
||||
fi
|
||||
|
||||
# --- frps systemd -----------------------------------------------------------
|
||||
if [ "$NO_SYSTEMD" = 0 ]; then
|
||||
log "Installing systemd unit frps.service"
|
||||
if [ "$DRY_RUN" = 0 ]; then
|
||||
$SUDO cp runtime/frps.service /etc/systemd/system/frps.service
|
||||
$SUDO systemctl daemon-reload
|
||||
$SUDO systemctl enable frps.service
|
||||
$SUDO systemctl restart frps.service || true
|
||||
sleep 2
|
||||
if ! systemctl is-active --quiet frps.service; then
|
||||
log "frps.service not active — check 'journalctl -u frps'"
|
||||
else
|
||||
log "frps.service is active"
|
||||
fi
|
||||
fi
|
||||
|
||||
log "Installing systemd unit frps-manager.service (port 5390)"
|
||||
if [ "$DRY_RUN" = 0 ]; then
|
||||
$SUDO cp runtime/frps-manager.service /etc/systemd/system/frps-manager.service
|
||||
$SUDO systemctl daemon-reload
|
||||
$SUDO systemctl enable frps-manager.service
|
||||
$SUDO systemctl restart frps-manager.service || true
|
||||
sleep 2
|
||||
if ! systemctl is-active --quiet frps-manager.service; then
|
||||
log "frps-manager.service not active — check 'journalctl -u frps-manager'"
|
||||
else
|
||||
log "frps-manager.service is active on http://0.0.0.0:5390"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log "Skipped systemd (--no-systemd)"
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
|
||||
================================================================
|
||||
frps-manager deployed!
|
||||
|
||||
Web UI: http://<this-host>:5390
|
||||
Default: admin / admin (CHANGE IMMEDIATELY under "改密")
|
||||
|
||||
frps bind: 0.0.0.0:7000
|
||||
frps dashboard: 127.0.0.1:7500 (admin / admin123)
|
||||
frps vhost_http: 0.0.0.0:80 (set FRPS_VHOST_HTTP_PORT in service to override)
|
||||
frps vhost_https: 0.0.0.0:443
|
||||
|
||||
To customize, edit:
|
||||
/etc/systemd/system/frps.service (frps CLI flags)
|
||||
/etc/systemd/system/frps-manager.service (Web UI port / env)
|
||||
|
||||
To upgrade later:
|
||||
cd $PROJECT_DIR
|
||||
sudo systemctl stop frps-manager frps
|
||||
git pull
|
||||
venv/bin/pip install -r requirements.txt
|
||||
sudo systemctl start frps frps-manager
|
||||
================================================================
|
||||
EOF
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
# Local dev launcher — runs frps-manager on :5300 with gunicorn using the
|
||||
# project venv. Use this when iterating without systemd.
|
||||
|
||||
set -euo pipefail
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||||
|
||||
mkdir -p runtime/conf runtime/logs instance
|
||||
|
||||
if [ ! -x runtime/bin/frps ]; then
|
||||
echo "[dev] runtime/bin/frps missing — running deploy.sh to fetch it"
|
||||
bash scripts/deploy.sh --no-systemd --dry-run || true
|
||||
fi
|
||||
|
||||
if [ ! -d venv ]; then
|
||||
echo "[dev] venv missing — create one and install requirements"
|
||||
python3 -m venv venv
|
||||
venv/bin/pip install -r requirements.txt
|
||||
fi
|
||||
|
||||
# Ensure frps service is *not* taking port 7000/7500 while we develop
|
||||
if systemctl is-active --quiet frps.service 2>/dev/null; then
|
||||
echo "[dev] stopping existing frps.service so dev frps can take port 7000"
|
||||
sudo systemctl stop frps.service
|
||||
fi
|
||||
|
||||
# Start frps under the dev user (not as a daemon) if not already running
|
||||
if ! ss -tlnp 2>/dev/null | grep -q ':7000 '; then
|
||||
echo "[dev] starting frps from runtime/conf/frps.toml"
|
||||
mkdir -p runtime/conf
|
||||
if [ ! -s runtime/conf/frps.toml ]; then
|
||||
venv/bin/python -c "
|
||||
import os
|
||||
os.environ.setdefault('FRPS_CONFIG', os.path.abspath('runtime/conf/frps.toml'))
|
||||
from app import write_frps_config, FRPS_CONFIG
|
||||
write_frps_config()
|
||||
print('wrote', os.environ.get('FRPS_CONFIG'))
|
||||
"
|
||||
fi
|
||||
nohup runtime/bin/frps -c runtime/conf/frps.toml > runtime/logs/frps.log 2>&1 &
|
||||
echo $! > runtime/logs/frps.pid
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
# Initialize DB (idempotent)
|
||||
venv/bin/python init_db.py
|
||||
|
||||
echo "[dev] launching gunicorn on :5300"
|
||||
exec venv/bin/gunicorn \
|
||||
--workers 2 \
|
||||
--bind 0.0.0.0:5300 \
|
||||
--reload \
|
||||
--access-logfile runtime/logs/web-access.log \
|
||||
--error-logfile runtime/logs/web-error.log \
|
||||
wsgi:app
|
||||
Reference in New Issue
Block a user