From 4c635858c7b6b186a59929be7afc84e2379dea6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=91=D0=BE=D0=B1?= =?UTF-8?q?=D1=8B=D1=80=D0=B5=D0=B2?= Date: Sat, 6 Jun 2026 12:49:05 +0300 Subject: [PATCH] feat: expose web admin with auth by default --- README.md | 29 ++++++++++++++--------------- admin-web/server.py | 23 +++++++++++++++++------ install.sh | 8 ++++++-- install_pcatelegram_web_bot.sh | 31 ++++++++++++++++++++++++++++++- lib/common.sh | 29 ++++++++++++++++++++++++++++- 5 files changed, 95 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 8042e46..549241b 100644 --- a/README.md +++ b/README.md @@ -50,33 +50,32 @@ ssh root@SERVER 'chmod +x /opt/pcatelegram_web/install.sh /opt/pcatelegram_web/i | `PCATELEGRAM_WEB_BASE` | `https://raw.githubusercontent.com/andrey271192/PCAtelegram_web/main` | база загрузки файлов | | `PCATELEGRAM_WEB_INSTALL_DIR` | `/opt/pcatelegram_web` | путь установки | | `GITHUB_TOKEN` / `GH_TOKEN` | пусто | token для private GitHub raw downloads | -| `PCATELEGRAM_WEB_ADMIN_HOST` | `127.0.0.1` | bind web-admin | +| `PCATELEGRAM_WEB_ADMIN_HOST` | `0.0.0.0` | bind web-admin | | `PCATELEGRAM_WEB_ADMIN_PORT` | `1984` | port web-admin | | `PCATELEGRAM_WEB_ADMIN_USER` | `admin` | Basic Auth login | -| `PCATELEGRAM_WEB_ADMIN_PASSWORD` | пусто | Basic Auth password; обязателен при public bind | +| `PCATELEGRAM_WEB_ADMIN_PASSWORD` | автогенерация | Basic Auth password | ## Web-admin -После первого запуска `pcatelegram_web` локальная web-admin панель ставится как `pcatelegram_web-admin.service`. - -Безопасный локальный доступ через SSH tunnel: +После первого запуска `pcatelegram_web` web-admin панель ставится как `pcatelegram_web-admin.service` и слушает публично: ```bash -ssh -L 1984:127.0.0.1:1984 root@SERVER -open http://127.0.0.1:1984/ +http://SERVER:1984/ ``` -Для public bind сначала задайте пароль через systemd override или reverse proxy: +Логин по умолчанию: `admin`. Пароль генерируется при установке и хранится на сервере: -```ini -[Service] -Environment=PCATELEGRAM_WEB_ADMIN_HOST=0.0.0.0 -Environment=PCATELEGRAM_WEB_ADMIN_PORT=1984 -Environment=PCATELEGRAM_WEB_ADMIN_USER=admin -Environment=PCATELEGRAM_WEB_ADMIN_PASSWORD=change-me +```bash +sudo cat /root/pcatelegram_web-admin.password ``` -Без HTTPS public Basic Auth гонит пароль открытым текстом. Для постоянного доступа лучше reverse proxy с TLS. +Для своего пароля передайте env до установки: + +```bash +PCATELEGRAM_WEB_ADMIN_PASSWORD='strong-password' bash bootstrap.sh +``` + +Без HTTPS Basic Auth гонит пароль открытым текстом. Для постоянного доступа лучше reverse proxy с TLS, но порт `1984` открыт по умолчанию по запросу проекта. ## Проверки diff --git a/admin-web/server.py b/admin-web/server.py index 8f2870c..86768cb 100644 --- a/admin-web/server.py +++ b/admin-web/server.py @@ -2,9 +2,8 @@ """ PCAtelegram_web local web admin. -The service is intentionally bound to 127.0.0.1:1984. Operators reach it -through an SSH tunnel by default. If exposed through a reverse proxy or public -bind, enable PCATELEGRAM_WEB_ADMIN_PASSWORD. +The service is publicly reachable by default and protected with Basic Auth. +Installers generate /root/pcatelegram_web-admin.password for credentials. """ from __future__ import annotations @@ -49,7 +48,7 @@ SHARED_443_CONFIG = Path(os.getenv("PCATELEGRAM_WEB_SHARED_443", "/opt/pcatelegr BACKUP_SCHEDULE_FILE = Path(os.getenv("PCATELEGRAM_WEB_BACKUP_SCHEDULE", "/opt/pcatelegram_web/backup_schedule.json")) BACKUP_RESTORE_LOG = Path(os.getenv("PCATELEGRAM_WEB_BACKUP_RESTORE_LOG", "/var/log/pcatelegram_web-restore.log")) -HOST = os.getenv("PCATELEGRAM_WEB_ADMIN_HOST", "127.0.0.1") +HOST = os.getenv("PCATELEGRAM_WEB_ADMIN_HOST", "0.0.0.0") PORT = int(os.getenv("PCATELEGRAM_WEB_ADMIN_PORT", "1984")) ADMIN_USER = os.getenv("PCATELEGRAM_WEB_ADMIN_USER", "admin") ADMIN_PASSWORD = os.getenv("PCATELEGRAM_WEB_ADMIN_PASSWORD", "") @@ -70,6 +69,17 @@ TRAFFIC_WINDOWS = { } +def load_admin_password_file() -> str: + path = Path(os.getenv("PCATELEGRAM_WEB_ADMIN_AUTH_FILE", "/root/pcatelegram_web-admin.password")) + try: + for line in path.read_text(encoding="utf-8").splitlines(): + if line.startswith("password="): + return line.split("=", 1)[1].strip() + except OSError: + return "" + return "" + + def utc_now() -> str: return datetime.now(timezone.utc).isoformat() @@ -1288,7 +1298,7 @@ class AdminHandler(BaseHTTPRequestHandler): print("%s - %s" % (self.address_string(), fmt % args)) def auth_enabled(self) -> bool: - return bool(ADMIN_PASSWORD) + return bool(ADMIN_PASSWORD or load_admin_password_file()) def is_authorized(self) -> bool: if not self.auth_enabled(): @@ -1301,7 +1311,8 @@ class AdminHandler(BaseHTTPRequestHandler): username, password = decoded.split(":", 1) except Exception: return False - return hmac.compare_digest(username, ADMIN_USER) and hmac.compare_digest(password, ADMIN_PASSWORD) + expected_password = ADMIN_PASSWORD or load_admin_password_file() + return hmac.compare_digest(username, ADMIN_USER) and hmac.compare_digest(password, expected_password) def require_auth(self) -> bool: if self.is_authorized(): diff --git a/install.sh b/install.sh index cd43f08..f543590 100755 --- a/install.sh +++ b/install.sh @@ -902,8 +902,9 @@ install_admin_web() { chmod 755 "$ADMIN_WEB_DIR/server.py" "$ADMIN_WEB_DIR/static" rm -f "$ADMIN_WEB_DIR/token" 2>/dev/null || true - local python_bin + local python_bin admin_password python_bin=$(command -v python3) + admin_password=$(ensure_admin_web_password) cat > "/etc/systemd/system/${ADMIN_WEB_SERVICE}.service" << SVCEOF [Unit] Description=PCAtelegram_web v${PCATELEGRAM_WEB_VERSION} Local Web Admin @@ -917,6 +918,8 @@ Restart=always RestartSec=5 Environment=PCATELEGRAM_WEB_ADMIN_HOST=$ADMIN_WEB_HOST Environment=PCATELEGRAM_WEB_ADMIN_PORT=$ADMIN_WEB_PORT +Environment=PCATELEGRAM_WEB_ADMIN_USER=${PCATELEGRAM_WEB_ADMIN_USER:-admin} +Environment=PCATELEGRAM_WEB_ADMIN_PASSWORD=$admin_password [Install] WantedBy=multi-user.target @@ -928,7 +931,8 @@ SVCEOF if type install_stats_collector &>/dev/null; then install_stats_collector >/dev/null 2>&1 || log_warning "stats collector was not started; open Web Admin traffic page and use Repair" fi - log_success "Web admin installed: ${ADMIN_WEB_HOST}:${ADMIN_WEB_PORT}" + log_success "Web admin installed: http://$(get_server_ip):${ADMIN_WEB_PORT}/" + log_info "Web admin login saved: $ADMIN_WEB_AUTH_FILE" } auto_install_admin_web_if_possible() { diff --git a/install_pcatelegram_web_bot.sh b/install_pcatelegram_web_bot.sh index 98e9a19..3d72785 100755 --- a/install_pcatelegram_web_bot.sh +++ b/install_pcatelegram_web_bot.sh @@ -15,6 +15,32 @@ PCATELEGRAM_WEB_DIR="/opt/pcatelegram_web" ADMIN_WEB_DIR="/opt/pcatelegram_web-admin" ADMIN_WEB_SERVICE="pcatelegram_web-admin" ADMIN_WEB_PORT="1984" +ADMIN_WEB_HOST="0.0.0.0" +ADMIN_WEB_AUTH_FILE="/root/pcatelegram_web-admin.password" + +ensure_admin_web_password() { + local pw="" + if [ -n "${PCATELEGRAM_WEB_ADMIN_PASSWORD:-}" ]; then + pw="$PCATELEGRAM_WEB_ADMIN_PASSWORD" + elif [ -f "$ADMIN_WEB_AUTH_FILE" ] && [ -s "$ADMIN_WEB_AUTH_FILE" ]; then + pw=$(sed -n 's/^password=//p' "$ADMIN_WEB_AUTH_FILE" | head -1) + fi + if [ -z "$pw" ]; then + if command -v openssl >/dev/null 2>&1; then + pw=$(openssl rand -base64 24 | tr -d '\n') + else + pw=$(head -c 24 /dev/urandom | base64 | tr -d '\n') + fi + fi + umask 077 + cat > "$ADMIN_WEB_AUTH_FILE" </dev/null | awk '{print $1}'):${ADMIN_WEB_PORT}/ +EOF + chmod 600 "$ADMIN_WEB_AUTH_FILE" 2>/dev/null || true + printf '%s' "$pw" +} if [ "$EUID" -ne 0 ]; then echo -e "${RED}Запустите с sudo.${NC}" @@ -128,6 +154,7 @@ if [ -f "$SCRIPT_DIR/admin-web/server.py" ]; then rm -f "$ADMIN_WEB_DIR/token" 2>/dev/null || true PYTHON_BIN=$(command -v python3) + ADMIN_WEB_PASSWORD=$(ensure_admin_web_password) cat > "/etc/systemd/system/${ADMIN_WEB_SERVICE}.service" << EOF [Unit] Description=PCAtelegram_web v2.5.0 Local Web Admin @@ -139,8 +166,10 @@ WorkingDirectory=$ADMIN_WEB_DIR ExecStart=$PYTHON_BIN $ADMIN_WEB_DIR/server.py Restart=always RestartSec=5 -Environment=PCATELEGRAM_WEB_ADMIN_HOST=127.0.0.1 +Environment=PCATELEGRAM_WEB_ADMIN_HOST=$ADMIN_WEB_HOST Environment=PCATELEGRAM_WEB_ADMIN_PORT=$ADMIN_WEB_PORT +Environment=PCATELEGRAM_WEB_ADMIN_USER=${PCATELEGRAM_WEB_ADMIN_USER:-admin} +Environment=PCATELEGRAM_WEB_ADMIN_PASSWORD=$ADMIN_WEB_PASSWORD [Install] WantedBy=multi-user.target diff --git a/lib/common.sh b/lib/common.sh index da6656e..948be9e 100755 --- a/lib/common.sh +++ b/lib/common.sh @@ -20,8 +20,9 @@ LOG_FILE="/var/log/pcatelegram_web.log" BOT_DIR="/opt/pcatelegram_web-bot" ADMIN_WEB_DIR="/opt/pcatelegram_web-admin" ADMIN_WEB_SERVICE="pcatelegram_web-admin" -ADMIN_WEB_HOST="127.0.0.1" +ADMIN_WEB_HOST="0.0.0.0" ADMIN_WEB_PORT="1984" +ADMIN_WEB_AUTH_FILE="/root/pcatelegram_web-admin.password" # ── V1 совместимость ───────────────────────────────────────────────────────── V1_CONTAINER_NAME="mtproto-proxy" @@ -738,3 +739,29 @@ init_dirs() { mkdir -p "$PCATELEGRAM_WEB_DIR" "$BACKUP_DIR" /etc/telemt 2>/dev/null touch "$LOG_FILE" 2>/dev/null } + +ensure_admin_web_password() { + local pw="" + if [ -n "${PCATELEGRAM_WEB_ADMIN_PASSWORD:-}" ]; then + pw="$PCATELEGRAM_WEB_ADMIN_PASSWORD" + elif [ -f "$ADMIN_WEB_AUTH_FILE" ] && [ -s "$ADMIN_WEB_AUTH_FILE" ]; then + pw=$(sed -n 's/^password=//p' "$ADMIN_WEB_AUTH_FILE" | head -1) + fi + + if [ -z "$pw" ]; then + if command -v openssl >/dev/null 2>&1; then + pw=$(openssl rand -base64 24 | tr -d '\n') + else + pw=$(head -c 24 /dev/urandom | base64 | tr -d '\n') + fi + fi + + umask 077 + cat > "$ADMIN_WEB_AUTH_FILE" </dev/null || hostname -I 2>/dev/null | awk '{print $1}'):${ADMIN_WEB_PORT}/ +EOF + chmod 600 "$ADMIN_WEB_AUTH_FILE" 2>/dev/null || true + printf '%s' "$pw" +}