192 lines
5.1 KiB
Bash
192 lines
5.1 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/torpanel"
|
|
PANEL_PUBLIC="$PANEL_ROOT/public"
|
|
STATE_DIR="/var/lib/torpanel"
|
|
LOG_DIR="/var/log/torpanel"
|
|
ETC_APP="/etc/torpanel"
|
|
|
|
TOR_ETC="/etc/tor"
|
|
TOR_TORRC_D="$TOR_ETC/torrc.d"
|
|
TOR_PANEL_CONF="$TOR_TORRC_D/99-torpanel.conf"
|
|
|
|
NGX_SITE_AVAIL="/etc/nginx/sites-available/torpanel"
|
|
NGX_SITE_ENABL="/etc/nginx/sites-enabled/torpanel"
|
|
SUDOERS_FILE="/etc/sudoers.d/torpanel"
|
|
|
|
COLLECTOR_SRC="$SCRIPT_DIR/bin/torpanel-collect.py"
|
|
COLLECTOR_BIN="/usr/local/bin/torpanel-collect.py"
|
|
SVC="/etc/systemd/system/torpanel-collector.service"
|
|
TIMER="/etc/systemd/system/torpanel-collector.timer"
|
|
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
|
|
echo -e "${C_BOLD}Installing TorPanel...${C_RESET}"
|
|
|
|
info "Updating apt and installing packages"
|
|
apt-get update -y >/dev/null
|
|
apt-get install -y --no-install-recommends \
|
|
tor tor-geoipdb nginx rsync \
|
|
php-fpm php-cli php-json php-curl php-zip php-common php-opcache \
|
|
python3 >/dev/null
|
|
ok "Packages installed"
|
|
|
|
PHPV=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
|
|
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"
|
|
touch "$STATE_DIR/stats.json"
|
|
rsync -a --delete "$SCRIPT_DIR/web/" "$PANEL_PUBLIC/"
|
|
chown -R www-data:www-data "$PANEL_ROOT" "$STATE_DIR" "$LOG_DIR"
|
|
chmod 750 "$PANEL_ROOT" "$STATE_DIR" "$LOG_DIR"
|
|
chown root:www-data "$ETC_APP"; chmod 770 "$ETC_APP"
|
|
ok "Web files deployed"
|
|
|
|
info "Writing Nginx site"
|
|
cat > "$NGX_SITE_AVAIL" <<'NGINX'
|
|
server {
|
|
listen 80 default_server;
|
|
server_name _;
|
|
|
|
root /var/www/torpanel/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 "Writing torrc defaults"
|
|
install -d "$TOR_TORRC_D"
|
|
cat > "$TOR_PANEL_CONF" <<'TORRC'
|
|
## --- Managed by TorPanel ---
|
|
SocksPort 0
|
|
ORPort 9001
|
|
ExitRelay 0
|
|
ExitPolicy reject *:*
|
|
Nickname RaspberryRelay
|
|
ContactInfo contact@admin.com
|
|
BandwidthRate 5 MB
|
|
BandwidthBurst 10 MB
|
|
AccountingMax 100 GB
|
|
AccountingStart month 1 00:00
|
|
ControlPort 0
|
|
ControlSocket /run/tor/control
|
|
CookieAuthentication 1
|
|
CookieAuthFileGroupReadable 1
|
|
# --- End TorPanel block ---
|
|
TORRC
|
|
ok "torrc written"
|
|
|
|
info "Setting permissions for Tor managed config"
|
|
chown root:www-data "$TOR_TORRC_D"; chmod 775 "$TOR_TORRC_D"
|
|
chown root:www-data "$TOR_PANEL_CONF"; chmod 664 "$TOR_PANEL_CONF"
|
|
ok "Permissions applied"
|
|
|
|
info "Granting www-data access to Tor cookie"
|
|
usermod -aG debian-tor www-data || true
|
|
ok "Permissions set"
|
|
|
|
info "Allowing www-data to control tor (limited)"
|
|
cat > "$SUDOERS_FILE" <<'SUD'
|
|
www-data ALL=NOPASSWD:/bin/systemctl reload tor, /bin/systemctl restart tor, /bin/systemctl start tor, /bin/systemctl stop tor
|
|
SUD
|
|
chmod 440 "$SUDOERS_FILE"
|
|
ok "Sudoers entry created"
|
|
|
|
info "Installing collector"
|
|
install -m 0755 "$COLLECTOR_SRC" "$COLLECTOR_BIN"
|
|
chown www-data:www-data "$COLLECTOR_BIN"
|
|
cat > "$SVC" <<'UNIT'
|
|
[Unit]
|
|
Description=TorPanel minute collector
|
|
After=tor.service
|
|
Wants=tor.service
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
User=www-data
|
|
Group=www-data
|
|
SupplementaryGroups=debian-tor
|
|
ExecStart=/usr/bin/env python3 /usr/local/bin/torpanel-collect.py
|
|
NoNewPrivileges=yes
|
|
ProtectSystem=strict
|
|
ProtectHome=yes
|
|
ProtectClock=yes
|
|
ProtectHostname=yes
|
|
ProtectKernelLogs=yes
|
|
ProtectKernelModules=yes
|
|
ProtectKernelTunables=yes
|
|
PrivateTmp=yes
|
|
PrivateDevices=yes
|
|
PrivateUsers=yes
|
|
LockPersonality=yes
|
|
MemoryDenyWriteExecute=yes
|
|
RestrictRealtime=yes
|
|
RestrictSUIDSGID=yes
|
|
UMask=0077
|
|
ReadWriteDirectories=/var/lib/torpanel
|
|
ReadOnlyPaths=/run/tor /etc/tor
|
|
RestrictAddressFamilies=AF_UNIX
|
|
SystemCallFilter=@system-service
|
|
CapabilityBoundingSet=
|
|
|
|
[Install]
|
|
WantedBy=timers.target
|
|
UNIT
|
|
|
|
cat > "$TIMER" <<'TIMER'
|
|
[Unit]
|
|
Description=Run TorPanel collector every minute
|
|
[Timer]
|
|
OnBootSec=30sec
|
|
OnUnitActiveSec=60sec
|
|
AccuracySec=15sec
|
|
Persistent=true
|
|
[Install]
|
|
WantedBy=timers.target
|
|
TIMER
|
|
ok "Systemd units installed"
|
|
|
|
info "Restarting services"
|
|
systemctl daemon-reload
|
|
systemctl enable --now tor
|
|
systemctl enable "$PHP_FPM_SVC" nginx >/dev/null
|
|
systemctl restart "$PHP_FPM_SVC"
|
|
systemctl restart nginx
|
|
systemctl enable --now torpanel-collector.timer
|
|
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."
|
|
echo -e " Tip: forward ${C_BOLD}TCP/9001${C_RESET} to your Pi for a publicly reachable relay." |