192 lines
5.7 KiB
Bash
192 lines
5.7 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
C_RESET="\033[0m"; C_DIM="\033[2m"; C_BOLD="\033[1m"
|
|
C_RED="\033[31m"; C_GRN="\033[32m"; C_BLU="\033[34m"; C_YEL="\033[33m"
|
|
info(){ echo -e "${C_BLU}➜${C_RESET} $*"; }
|
|
ok(){ echo -e "${C_GRN}✓${C_RESET} $*"; }
|
|
warn(){ echo -e "${C_YEL}!${C_RESET} $*"; }
|
|
fail(){ echo -e "${C_RED}✗${C_RESET} $*"; }
|
|
if [[ $EUID -ne 0 ]]; then fail "Run as root (sudo)."; exit 1; fi
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
PANEL_ROOT="/var/www/snowpanel"
|
|
PANEL_PUBLIC="$PANEL_ROOT/public"
|
|
STATE_DIR="/var/lib/snowpanel"
|
|
LOG_DIR="/var/log/snowpanel"
|
|
ETC_APP="/etc/snowpanel"
|
|
|
|
NGX_SITE_AVAIL="/etc/nginx/sites-available/snowpanel"
|
|
NGX_SITE_ENABL="/etc/nginx/sites-enabled/snowpanel"
|
|
SUDOERS_FILE="/etc/sudoers.d/snowpanel"
|
|
|
|
COLLECTOR_SRC="$SCRIPT_DIR/bin/snowpanel-collect.py"
|
|
COLLECTOR_BIN="/usr/local/bin/snowpanel-collect.py"
|
|
LOGDUMP_SRC="$SCRIPT_DIR/bin/snowpanel-logdump"
|
|
LOGDUMP_BIN="/usr/local/bin/snowpanel-logdump"
|
|
|
|
SHAPER_SRC="$SCRIPT_DIR/bin/snowpanel-shaper"
|
|
SHAPER_BIN="/usr/local/bin/snowpanel-shaper"
|
|
SHAPER_SVC="/etc/systemd/system/snowpanel-shaper.service"
|
|
SHAPER_PATH="/etc/systemd/system/snowpanel-shaper.path"
|
|
|
|
SVC="/etc/systemd/system/snowpanel-collector.service"
|
|
TIMER="/etc/systemd/system/snowpanel-collector.timer"
|
|
SF_DROPIN_DIR="/etc/systemd/system/snowflake-proxy.service.d"
|
|
SF_ACCOUNTING="$SF_DROPIN_DIR/10-accounting.conf"
|
|
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
|
|
echo -e "${C_BOLD}Installing SnowPanel...${C_RESET}"
|
|
|
|
info "Updating apt and installing packages"
|
|
apt-get update -y >/dev/null
|
|
apt-get install -y --no-install-recommends snowflake-proxy nginx rsync php-fpm php-cli php-json php-curl php-zip php-common php-opcache python3 iproute2 iptables >/dev/null
|
|
ok "Packages installed"
|
|
|
|
PHPV=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;' 2>/dev/null || echo "8.2")
|
|
PHP_FPM_SVC="php${PHPV}-fpm"
|
|
PHP_SOCK="/run/php/php${PHPV}-fpm.sock"
|
|
ln -sf "$PHP_SOCK" /run/php/php-fpm.sock || true
|
|
|
|
info "Preparing directories"
|
|
install -d "$PANEL_PUBLIC" "$STATE_DIR" "$LOG_DIR" "$ETC_APP"
|
|
install -d -m 0755 /usr/local/bin
|
|
chown -R www-data:www-data "$PANEL_ROOT" || true
|
|
chown -R www-data:www-data "$STATE_DIR" "$LOG_DIR" || true
|
|
chmod 750 "$PANEL_ROOT" "$STATE_DIR" "$LOG_DIR"
|
|
ok "Directories ready"
|
|
|
|
info "Deploying web"
|
|
rsync -a --delete "$SCRIPT_DIR/web/" "$PANEL_PUBLIC/"
|
|
chown -R www-data:www-data "$PANEL_PUBLIC"
|
|
ok "Web deployed"
|
|
|
|
info "Seeding state"
|
|
echo -n '{"data":[]}' > "$STATE_DIR/stats.json"
|
|
chown www-data:www-data "$STATE_DIR/stats.json"
|
|
chmod 640 "$STATE_DIR/stats.json"
|
|
ok "State seeded"
|
|
|
|
info "Writing Nginx site"
|
|
cat > "$NGX_SITE_AVAIL" <<'NGINX'
|
|
server {
|
|
listen 80 default_server;
|
|
server_name _;
|
|
root /var/www/snowpanel/public;
|
|
index index.php index.html;
|
|
location / {
|
|
try_files $uri $uri/ /index.php?$args;
|
|
}
|
|
location ~ \.php$ {
|
|
include snippets/fastcgi-php.conf;
|
|
fastcgi_pass unix:/run/php/php-fpm.sock;
|
|
}
|
|
location ~ /\. {
|
|
deny all;
|
|
}
|
|
}
|
|
NGINX
|
|
rm -f /etc/nginx/sites-enabled/default || true
|
|
ln -sf "$NGX_SITE_AVAIL" "$NGX_SITE_ENABL"
|
|
ok "Nginx site enabled"
|
|
|
|
info "Installing helpers"
|
|
install -m 0755 "$COLLECTOR_SRC" "$COLLECTOR_BIN"
|
|
install -m 0755 "$LOGDUMP_SRC" "$LOGDUMP_BIN"
|
|
install -m 0755 "$SHAPER_SRC" "$SHAPER_BIN"
|
|
install -d -m 0750 -o www-data -g www-data "$STATE_DIR"
|
|
ok "Helpers installed"
|
|
|
|
info "Granting sudoers"
|
|
cat > "$SUDOERS_FILE" <<SUD
|
|
www-data ALL=NOPASSWD:/usr/local/bin/snowpanel-logdump
|
|
www-data ALL=NOPASSWD:/bin/systemctl start snowflake-proxy, /bin/systemctl stop snowflake-proxy, /bin/systemctl restart snowflake-proxy
|
|
www-data ALL=NOPASSWD:/bin/systemctl restart snowpanel-shaper
|
|
SUD
|
|
chmod 440 "$SUDOERS_FILE"
|
|
ok "Sudoers set"
|
|
|
|
info "Enabling systemd IP accounting"
|
|
mkdir -p "$SF_DROPIN_DIR"
|
|
cat > "$SF_ACCOUNTING" <<EOF
|
|
[Service]
|
|
IPAccounting=yes
|
|
EOF
|
|
ok "Accounting drop-in written"
|
|
|
|
info "Creating collector units"
|
|
cat > "$SVC" <<'UNIT'
|
|
[Unit]
|
|
Description=SnowPanel minute collector
|
|
After=snowflake-proxy.service
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
User=www-data
|
|
Group=www-data
|
|
ExecStart=/usr/bin/env python3 /usr/local/bin/snowpanel-collect.py
|
|
UNIT
|
|
cat > "$TIMER" <<'TIMER'
|
|
[Unit]
|
|
Description=Run SnowPanel collector every minute
|
|
|
|
[Timer]
|
|
OnCalendar=*-*-* *:*:00
|
|
Persistent=true
|
|
Unit=snowpanel-collector.service
|
|
|
|
[Install]
|
|
WantedBy=timers.target
|
|
TIMER
|
|
ok "Collector units created"
|
|
|
|
info "Creating shaper units"
|
|
cat > "$SHAPER_SVC" <<'UNIT'
|
|
[Unit]
|
|
Description=SnowPanel traffic shaper for snowflake-proxy
|
|
After=network-online.target snowflake-proxy.service
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/usr/local/bin/snowpanel-shaper apply
|
|
ExecStop=/usr/local/bin/snowpanel-shaper clear
|
|
RemainAfterExit=yes
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
UNIT
|
|
|
|
cat > "$SHAPER_PATH" <<'PATHUNIT'
|
|
[Unit]
|
|
Description=Watch SnowPanel limits and reapply shaper
|
|
|
|
[Path]
|
|
PathChanged=/var/lib/snowpanel/app.json
|
|
PathChanged=/etc/snowpanel/app.json
|
|
PathChanged=/etc/snowpanel/limits.json
|
|
PathChanged=/var/lib/snowpanel/limits.json
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
PATHUNIT
|
|
ok "Shaper units created"
|
|
|
|
info "Restarting services"
|
|
systemctl daemon-reload
|
|
systemctl enable "$PHP_FPM_SVC" nginx >/dev/null 2>&1 || true
|
|
systemctl restart "$PHP_FPM_SVC" || true
|
|
systemctl restart nginx
|
|
systemctl enable --now snowflake-proxy >/dev/null 2>&1 || true
|
|
systemctl restart snowflake-proxy || true
|
|
systemctl enable --now snowpanel-collector.timer
|
|
systemctl start snowpanel-collector.service || true
|
|
systemctl enable --now snowpanel-shaper.service snowpanel-shaper.path
|
|
systemctl restart snowpanel-shaper.service || true
|
|
ok "Services running"
|
|
|
|
IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
|
echo
|
|
echo -e "${C_BOLD}All set!${C_RESET}"
|
|
echo -e "URL: ${C_GRN}http://$IP/${C_RESET}"
|
|
echo -e " First run: you'll see a setup page to create the admin user." |