224 lines
6.1 KiB
Bash
224 lines
6.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
|
|
|
|
ensure_torrc_d_include() {
|
|
local main="$TOR_ETC/torrc"
|
|
install -d -m 755 "$TOR_TORRC_D"
|
|
|
|
if [[ ! -f "$main" ]]; then
|
|
echo "# Created by TorPanel installer" > "$main"
|
|
fi
|
|
|
|
if grep -Eq '^[[:space:]]*%include[[:space:]]+/etc/tor/torrc\.d/\*\.conf' "$main"; then
|
|
return 0
|
|
fi
|
|
|
|
if sed -n 's/^[[:space:]]*#\s*%include[[:space:]]\+\/etc\/tor\/torrc\.d\/\*\.conf/%include \/etc\/tor\/torrc.d\/\*\.conf/p' "$main" | grep -q .; then
|
|
sed -i 's/^[[:space:]]*#\s*%include[[:space:]]\+\/etc\/tor\/torrc\.d\/\*\.conf/%include \/etc\/tor\/torrc.d\/\*\.conf/' "$main"
|
|
else
|
|
printf '\n%%include /etc/tor/torrc.d/*.conf\n' >> "$main"
|
|
fi
|
|
}
|
|
|
|
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"
|
|
echo -n '{"data":[]}' > "$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 "Ensuring main torrc includes torrc.d/*.conf"
|
|
ensure_torrc_d_include
|
|
ok "torrc.d include active"
|
|
|
|
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
|
|
ConditionPathExists=/run/tor/control.authcookie
|
|
|
|
[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=no
|
|
LockPersonality=yes
|
|
MemoryDenyWriteExecute=yes
|
|
RestrictRealtime=yes
|
|
RestrictSUIDSGID=yes
|
|
UMask=0077
|
|
ReadWritePaths=/var/lib/torpanel
|
|
ReadOnlyPaths=/run/tor /etc/tor
|
|
RestrictAddressFamilies=AF_UNIX
|
|
SystemCallFilter=@system-service
|
|
CapabilityBoundingSet=
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
Environment=PYTHONUNBUFFERED=1
|
|
Restart=on-failure
|
|
RestartSec=15s
|
|
|
|
[Install]
|
|
WantedBy=timers.target
|
|
UNIT
|
|
|
|
cat > "$TIMER" <<'TIMER'
|
|
[Unit]
|
|
Description=Run TorPanel collector every minute
|
|
|
|
[Timer]
|
|
OnCalendar=*-*-* *:*:00
|
|
AccuracySec=15s
|
|
Persistent=true
|
|
Unit=torpanel-collector.service
|
|
|
|
[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 start torpanel-collector.service
|
|
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." |