From 95dae08a8ed0928d5adf06f7a63c77494aa85b0a Mon Sep 17 00:00:00 2001 From: AlexChard Date: Sat, 29 Aug 2026 13:06:55 +0400 Subject: [PATCH] Add clean VPS variants --- clean-vps/README.md | 75 +++++++++++ clean-vps/install-with-panel.sh | 75 +++++++++++ clean-vps/install.sh | 219 ++++++++++++++++++++++++++++++++ 3 files changed, 369 insertions(+) create mode 100644 clean-vps/README.md create mode 100644 clean-vps/install-with-panel.sh create mode 100644 clean-vps/install.sh diff --git a/clean-vps/README.md b/clean-vps/README.md new file mode 100644 index 0000000..d4bddb2 --- /dev/null +++ b/clean-vps/README.md @@ -0,0 +1,75 @@ +# Telegram WEB Proxy на своём VPS + +В каталоге есть два варианта: + +- `install-with-panel.sh` — рекомендуемый вариант с минимальной веб-панелью, отдельными secrets, QR-кодами и отзывом доступа; +- `install.sh` — облегчённый вариант без панели и с одним общим secret. + +Оба варианта предназначены для отдельного VPS. Панельный вариант использует зафиксированный релиз `WEB PANEL PROXY v2.0.2`, проверяет SHA-256 архива до запуска и разворачивает официальный Telegram relay/MTProxy. + +## Что подготовить + +- VPS `x86_64`: Ubuntu 22.04/24.04/26.04 либо Debian 12/13; +- минимум 1 vCPU, 1 ГБ RAM и 10 ГБ диска; комфортнее 2 vCPU и 2 ГБ RAM; +- отдельный поддомен, например `proxy.example.com`; +- A-запись поддомена на публичный IPv4 VPS; +- открытые входящие TCP-порты 80 и 443; SSH-порт 22 — только для вашего IP, если это возможно; +- DNS-запись без CDN/проксирования (в Cloudflare — серое облако, режим `DNS only`). + +Не публикуйте AAAA-запись, если на VPS нет исправно работающего публичного IPv6. + +## Запуск + +Для варианта с панелью скопируйте `install-with-panel.sh` на VPS и выполните: + +```bash +sudo bash install-with-panel.sh +``` + +Установщик спросит домен, email, логин и пароль панели. Адрес панели создаётся случайным и сохраняется в `/root/telegram-web-panel-address.txt`. + +Для варианта без панели скопируйте `install.sh` и выполните: + +```bash +sudo bash install.sh --hostname proxy.example.com --email admin@example.com +``` + +Аргументы можно не указывать — скрипт спросит домен и email сам: + +```bash +sudo bash install.sh +``` + +Secret создаётся на сервере и не попадает в историю shell. После установки скрипт выводит: + +- hostname; +- 32-символьный secret; +- готовую ссылку `tg://webproxy?...`; +- альтернативную ссылку `https://t.me/webproxy?...`. + +Копия реквизитов сохраняется с правами `0600` в `/root/telegram-web-proxy-access.txt`. + +## Важные ограничения + +- WEB Proxy пока имеет статус proof of concept. +- Основной поддерживаемый клиент — Telegram Desktop 7.1.0 или новее: именно в 7.1.0 в настройки подключения добавлен тип WEB Proxy. +- Обычные мобильные релизы Telegram могут не поддерживать этот тип прокси. Экспериментальная Android-реализация существует отдельно; iOS заявлен как план. +- Это прокси только для Telegram, не VPN для всего устройства. +- Один hostname обслуживает один relay; для второго VPS используйте другой поддомен и другой secret. + +## Проверка после установки + +```bash +systemctl status caddy tproxy-firewall mtproxy tproxy-server --no-pager +curl -fsS http://127.0.0.1:8081/healthz +curl -fsS http://127.0.0.1:8081/readyz +curl -fsS https://proxy.example.com/ +ss -lntp +sudo nft list table inet tproxy_backend +``` + +Снаружи должны быть доступны только 80/443 (и ваш SSH-порт). Порты 2398, 8080, 8081 и 8888 наружу открывать нельзя. + +## Почему скрипт останавливается на занятом 80/443 + +Официальный автоматический установщик заменяет активную конфигурацию Caddy. Чтобы случайно не сломать существующие сайты, этот wrapper прекращает установку при обнаружении слушателей на 80/443 или существующего `/etc/caddy/Caddyfile`. Для занятого VPS нужна ручная интеграция с текущим reverse proxy. diff --git a/clean-vps/install-with-panel.sh b/clean-vps/install-with-panel.sh new file mode 100644 index 0000000..4e5e7a4 --- /dev/null +++ b/clean-vps/install-with-panel.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -Eeuo pipefail +umask 077 + +# Verified bootstrapper for WEB PANEL PROXY v2.0.2. +# Installs the Telegram WEB proxy plus a private management panel. + +readonly PANEL_VERSION="v2.0.2" +readonly ARCHIVE_URL="https://github.com/POLESNIESOVETI12/web-panel-proxy/archive/refs/tags/${PANEL_VERSION}.zip" +readonly ARCHIVE_SHA256="ac2258323b71bde13eb45314a0a70449561774697c5e4d3603791e459c55e7bb" + +WORK_DIR="" +die() { printf '\nERROR: %s\n' "$*" >&2; exit 1; } +cleanup() { [[ -z "${WORK_DIR}" || ! -d "${WORK_DIR}" ]] || rm -rf -- "${WORK_DIR}"; } +trap cleanup EXIT + +[[ "${EUID}" -eq 0 ]] || die "Run as root: sudo bash install-with-panel.sh" +[[ "$(uname -m)" == "x86_64" ]] || die "An x86_64 VPS is required." +[[ -r /etc/os-release ]] || die "Cannot identify the operating system." +# shellcheck disable=SC1091 +source /etc/os-release +case "${ID:-}" in + ubuntu) dpkg --compare-versions "${VERSION_ID:-0}" ge 22.04 || die "Ubuntu 22.04+ is required." ;; + debian) dpkg --compare-versions "${VERSION_ID:-0}" ge 12 || die "Debian 12+ is required." ;; + *) die "Supported systems: Ubuntu 22.04+ or Debian 12+." ;; +esac + +export DEBIAN_FRONTEND=noninteractive +if ! command -v curl >/dev/null 2>&1 || ! command -v unzip >/dev/null 2>&1; then + apt-get -o DPkg::Lock::Timeout=600 update + apt-get -o DPkg::Lock::Timeout=600 install -y --no-install-recommends curl unzip ca-certificates +fi +command -v sha256sum >/dev/null 2>&1 || die "sha256sum is required." + +WORK_DIR="$(mktemp -d /tmp/telegram-web-panel.XXXXXXXX)" +ARCHIVE="${WORK_DIR}/panel.zip" + +printf '\n==> Downloading WEB PANEL PROXY %s\n' "${PANEL_VERSION}" +curl --fail --silent --show-error --location \ + --proto '=https' --proto-redir '=https' --tlsv1.2 \ + --output "${ARCHIVE}" "${ARCHIVE_URL}" + +ACTUAL_SHA256="$(sha256sum "${ARCHIVE}" | awk '{print $1}')" +[[ "${ACTUAL_SHA256}" == "${ARCHIVE_SHA256}" ]] || + die "Downloaded archive checksum mismatch; installation was not started." + +unzip -q "${ARCHIVE}" -d "${WORK_DIR}/source" +SOURCE_DIR="${WORK_DIR}/source/web-panel-proxy-${PANEL_VERSION#v}" +for file in install-final.sh install-webproxy-core.sh install-panel.sh uninstall-web-proxy.sh; do + [[ -s "${SOURCE_DIR}/${file}" ]] || die "Installation package is incomplete: ${file}" +done +chmod 0700 "${SOURCE_DIR}"/*.sh + +printf '%s\n' \ + "" \ + "The installer will ask for:" \ + " 1. proxy hostname (A record must already point to this VPS);" \ + " 2. certificate email;" \ + " 3. panel login and password." \ + "" + +bash "${SOURCE_DIR}/install-final.sh" + +DOMAIN="$(sed -n 's/^Environment=TPROXY_HOSTNAME=//p' /etc/systemd/system/caddy.service.d/tproxy.conf 2>/dev/null | head -n1 || true)" +PANEL_PATH="$(sed -n 's/^Environment=WEBPROXY_PANEL_PATH=//p' /etc/systemd/system/tproxy-panel.service 2>/dev/null | head -n1 || true)" +if [[ -n "${DOMAIN}" && "${PANEL_PATH}" =~ ^/panel-[a-f0-9]+$ ]]; then + cat > /root/telegram-web-panel-address.txt < %s\n' "$*"; } +die() { printf '\nERROR: %s\n' "$*" >&2; exit 1; } + +cleanup() { + if [[ -n "${WORK_DIR}" && -d "${WORK_DIR}" ]]; then + rm -rf -- "${WORK_DIR}" + fi +} +trap cleanup EXIT +trap 'printf "\nInstallation stopped at line %s.\n" "$LINENO" >&2' ERR + +usage() { + cat <<'EOF' +Usage: + sudo bash install.sh [--hostname proxy.example.com] [--email admin@example.com] + +Options: + --hostname HOST Dedicated lowercase DNS name pointed to this VPS. + --email EMAIL ACME/Let's Encrypt contact email. + -h, --help Show this help. + +If hostname or email is omitted, the installer asks interactively. +The WEB proxy secret is generated locally and never placed in shell history. +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --hostname) HOSTNAME_ARG="${2:-}"; shift 2 ;; + --email) EMAIL="${2:-}"; shift 2 ;; + -h|--help) usage; exit 0 ;; + *) die "Unknown argument: $1" ;; + esac +done + +[[ "${EUID}" -eq 0 ]] || die "Run this script as root (sudo bash install.sh)." +[[ "$(uname -m)" == "x86_64" ]] || die "The official MTProxy build currently requires x86_64." +[[ -r /etc/os-release ]] || die "Cannot identify the operating system." +# shellcheck disable=SC1091 +source /etc/os-release +OS_MAJOR="${VERSION_ID%%.*}" +[[ "${OS_MAJOR}" =~ ^[0-9]+$ ]] || die "Cannot parse OS version: ${VERSION_ID:-unknown}." +if [[ "${ID:-}" == "ubuntu" ]]; then + (( OS_MAJOR >= 22 )) || die "Ubuntu 22.04 or newer is required." +elif [[ "${ID:-}" == "debian" ]]; then + (( OS_MAJOR >= 12 )) || die "Debian 12 or newer is required." +else + die "Supported systems: Ubuntu 22.04+ or Debian 12+ (detected ${PRETTY_NAME:-unknown})." +fi + +if [[ -z "${HOSTNAME_ARG}" ]]; then + read -r -p "Proxy hostname (for example proxy.example.com): " HOSTNAME_ARG +fi +HOSTNAME_ARG="${HOSTNAME_ARG,,}" +HOSTNAME_ARG="${HOSTNAME_ARG%.}" +if [[ ! "${HOSTNAME_ARG}" =~ ^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$ || "${HOSTNAME_ARG}" != *.* ]]; then + die "Hostname must be a lowercase ASCII DNS name, for example proxy.example.com." +fi +[[ "${HOSTNAME_ARG}" != *..* ]] || die "Hostname contains an empty DNS label." +IFS='.' read -r -a DNS_LABELS <<<"${HOSTNAME_ARG}" +for label in "${DNS_LABELS[@]}"; do + [[ ${#label} -le 63 && "${label}" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]] || + die "Invalid DNS label in hostname: ${label}" +done + +if [[ -z "${EMAIL}" ]]; then + read -r -p "Email for HTTPS certificate notices: " EMAIL +fi +[[ "${EMAIL}" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]] || die "Invalid email address." + +command -v curl >/dev/null 2>&1 || { + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends curl ca-certificates +} +command -v openssl >/dev/null 2>&1 || { + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends openssl +} + +say "Checking DNS and the VPS" +PUBLIC_IPV4="$(curl --fail --silent --show-error --max-time 10 -4 https://api.ipify.org || true)" +[[ "${PUBLIC_IPV4}" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || die "Could not determine this VPS public IPv4." + +DNS_IPV4S="$(getent ahostsv4 "${HOSTNAME_ARG}" 2>/dev/null | awk '{print $1}' | sort -u || true)" +[[ -n "${DNS_IPV4S}" ]] || die "${HOSTNAME_ARG} has no visible A record yet." +if ! grep -Fxq "${PUBLIC_IPV4}" <<<"${DNS_IPV4S}"; then + printf 'Public VPS IPv4: %s\nDNS currently returns:\n%s\n' "${PUBLIC_IPV4}" "${DNS_IPV4S}" >&2 + die "Point the hostname A record to this VPS and wait for DNS propagation." +fi + +if command -v ss >/dev/null 2>&1; then + OCCUPIED="$(ss -H -lnt '( sport = :80 or sport = :443 )' 2>/dev/null || true)" + if [[ -n "${OCCUPIED}" ]]; then + printf '%s\n' "${OCCUPIED}" >&2 + die "Ports 80/443 are already in use. Use a clean VPS or integrate manually; the official installer replaces Caddy configuration." + fi +fi + +if [[ -s /etc/caddy/Caddyfile ]]; then + die "An existing Caddy configuration was found. This safe installer will not overwrite it." +fi + +say "Downloading the pinned official tproxy-server source" +WORK_DIR="$(mktemp -d /tmp/telegram-web-proxy.XXXXXXXX)" +ARCHIVE="${WORK_DIR}/tproxy-server.tar.gz" +curl --fail --silent --show-error --location \ + --proto '=https' --proto-redir '=https' --tlsv1.2 \ + --output "${ARCHIVE}" "${TPROXY_ARCHIVE_URL}" +tar -tzf "${ARCHIVE}" >/dev/null +tar -xzf "${ARCHIVE}" -C "${WORK_DIR}" +SOURCE_DIR="${WORK_DIR}/tproxy-server-${TPROXY_COMMIT}" +[[ -x "${SOURCE_DIR}/deploy/install.sh" ]] || die "Official installer is missing from the downloaded archive." + +say "Creating a unique public cover site" +SITE_DIR="${WORK_DIR}/site" +mkdir -p "${SITE_DIR}" +SITE_ID="$(openssl rand -hex 6)" +cat >"${SITE_DIR}/index.html" < + + + + + Northwind Status + + + +

SERVICE NODE ${SITE_ID}

Systems operational

This endpoint provides encrypted application transport and status information.

Service information
+ +EOF +cat >"${SITE_DIR}/about.html" <Service information

NODE ${SITE_ID}

Service information

This privately operated endpoint is monitored for availability.

Return to status
+EOF +cat >"${SITE_DIR}/404.html" <Not found

404

Page not found

Return to status
+EOF +cat >"${SITE_DIR}/styles.css" <<'EOF' +:root{color-scheme:dark;font-family:ui-sans-serif,system-ui,sans-serif;background:#07111f;color:#e8f0f8}*{box-sizing:border-box}body{margin:0;min-height:100vh;display:grid;place-items:center;background:radial-gradient(circle at 20% 20%,#173b5c 0,transparent 35%),#07111f}main{width:min(680px,calc(100% - 40px));padding:48px;border:1px solid #294158;border-radius:20px;background:#0b1827cc;box-shadow:0 28px 80px #0008}h1{font-size:clamp(2rem,7vw,4rem);letter-spacing:-.04em;margin:.2em 0}p{line-height:1.7;color:#a9bbcb}.tag{font-size:.75rem;letter-spacing:.18em;color:#61d3a5}a{color:#7ed7ff} +EOF +cat >"${SITE_DIR}/favicon.svg" < +EOF + +SECRET="$(openssl rand -hex 16)" + +say "Installing Caddy, the relay and official MTProxy" +printf '%s\n' "${SECRET}" | "${SOURCE_DIR}/deploy/install.sh" \ + --hostname "${HOSTNAME_ARG}" \ + --email "${EMAIL}" \ + --site-dir "${SITE_DIR}" + +say "Verifying services and HTTPS" +for unit in caddy tproxy-firewall mtproxy tproxy-server; do + systemctl is-active --quiet "${unit}" || { + systemctl --no-pager --full status "${unit}" || true + die "Service ${unit} is not running." + } +done +curl --fail --silent --show-error --max-time 10 http://127.0.0.1:8081/healthz >/dev/null +curl --fail --silent --show-error --max-time 10 http://127.0.0.1:8081/readyz >/dev/null +HTTPS_OK=0 +for _ in 1 2 3 4 5 6; do + if curl --fail --silent --show-error --location --max-time 15 "https://${HOSTNAME_ARG}/" >/dev/null; then + HTTPS_OK=1 + break + fi + sleep 5 +done +[[ "${HTTPS_OK}" -eq 1 ]] || die "Services are running, but public HTTPS verification failed. Check DNS, provider firewall and Caddy logs." + +WEB_LINK="tg://webproxy?server=${HOSTNAME_ARG}&secret=${SECRET}" +HTTPS_LINK="https://t.me/webproxy?server=${HOSTNAME_ARG}&secret=${SECRET}" +cat >"${RESULT_FILE}" <