Add clean VPS variants

This commit is contained in:
2026-08-29 13:06:55 +04:00
parent 471cc1594f
commit 95dae08a8e
3 changed files with 369 additions and 0 deletions
+75
View File
@@ -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.
+75
View File
@@ -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 <<EOF
Panel URL: https://${DOMAIN}${PANEL_PATH}/login
Configuration: /etc/web-proxy-panel
Panel data: /var/lib/tproxy-panel
Diagnostics: systemctl status caddy mtproxy tproxy-server tproxy-panel --no-pager
EOF
chmod 0600 /root/telegram-web-panel-address.txt
printf '\nPanel address saved to /root/telegram-web-panel-address.txt\n'
fi
+219
View File
@@ -0,0 +1,219 @@
#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
# One-command installer for Telegram's experimental WEB proxy server.
# Intended for a clean, dedicated Ubuntu 22.04+/Debian 12+ x86_64 VPS.
readonly TPROXY_COMMIT="52a5feb7fac38f68da5afef9cedd9b3bfc8473ca"
readonly TPROXY_ARCHIVE_URL="https://github.com/telegramdesktop/tproxy-server/archive/${TPROXY_COMMIT}.tar.gz"
readonly RESULT_FILE="/root/telegram-web-proxy-access.txt"
HOSTNAME_ARG=""
EMAIL=""
WORK_DIR=""
say() { printf '\n==> %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" <<EOF
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Northwind Status</title>
<link rel="stylesheet" href="/styles.css">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
</head>
<body><main><p class="tag">SERVICE NODE ${SITE_ID}</p><h1>Systems operational</h1><p>This endpoint provides encrypted application transport and status information.</p><a href="/about">Service information</a></main></body>
</html>
EOF
cat >"${SITE_DIR}/about.html" <<EOF
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Service information</title><link rel="stylesheet" href="/styles.css"></head><body><main><p class="tag">NODE ${SITE_ID}</p><h1>Service information</h1><p>This privately operated endpoint is monitored for availability.</p><a href="/">Return to status</a></main></body></html>
EOF
cat >"${SITE_DIR}/404.html" <<EOF
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Not found</title><link rel="stylesheet" href="/styles.css"></head><body><main><p class="tag">404</p><h1>Page not found</h1><a href="/">Return to status</a></main></body></html>
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
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="16" fill="#0b1827"/><path d="M17 34l10 10 21-25" fill="none" stroke="#61d3a5" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/></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}" <<EOF
Telegram WEB proxy
Created: $(date --iso-8601=seconds)
Hostname: ${HOSTNAME_ARG}
Secret: ${SECRET}
Desktop link: ${WEB_LINK}
HTTPS-form link: ${HTTPS_LINK}
Public cover site: https://${HOSTNAME_ARG}/
Pinned tproxy-server commit: ${TPROXY_COMMIT}
EOF
chmod 0600 "${RESULT_FILE}"
cat <<EOF
============================================================
Telegram WEB proxy is ready.
Hostname : ${HOSTNAME_ARG}
Secret : ${SECRET}
Open in Telegram Desktop:
${WEB_LINK}
Alternative link form:
${HTTPS_LINK}
Saved root-only at: ${RESULT_FILE}
Public site: https://${HOSTNAME_ARG}/
============================================================
Important: WEB proxy support is experimental. Use a compatible
Telegram Desktop build; normal mobile releases may not support it.
EOF