diff --git a/INSTALLER_GUIDE.md b/INSTALLER_GUIDE.md index 1c15ab1..a2ef5a2 100644 --- a/INSTALLER_GUIDE.md +++ b/INSTALLER_GUIDE.md @@ -19,6 +19,12 @@ sudo ./IGProxy ## Экраны мастера +Перед установкой ядра первым вариантом предлагается последний стабильный релиз +telemt. Закреплённая версия остаётся отдельным запасным вариантом. На старых +дистрибутивах, где GNU-бинарнику не хватает новой glibc, мастер автоматически +переходит на официальную статическую musl-сборку той же версии и сохраняет +проверку SHA-256. + ### 1. Роль сервера - **Центр управления + прокси** — первый сервер сети. Здесь работают единый diff --git a/README.md b/README.md index 40e8bdc..7448279 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,11 @@ sudo ./IGProxy - проверяет новые службы и сайт; - автоматически откатывает изменения при ошибке. +По умолчанию мастер предлагает последний стабильный релиз ядра telemt. +Если GNU-сборка не запускается на старой glibc, IGProxy показывает сообщение +системного загрузчика и автоматически проверяет официальную статическую +musl-сборку той же версии. SHA-256 проверяется для каждого скачанного архива. + ## После установки Открыть серверное меню: diff --git a/install.sh b/install.sh index 406ead1..2929f02 100644 --- a/install.sh +++ b/install.sh @@ -503,8 +503,8 @@ MaxFileSec=1day" } # ── Выбор версии ядра telemt ───────────────────────────────────────────────── -# Дефолт — протестированный пин (TELEMT_PINNED_VERSION), а не latest, чтобы битый -# свежий релиз ядра не ломал установки. Сохраняем выбор в config.json telemt_version. +# Дефолт — последний стабильный релиз. Протестированный пин остаётся запасным +# вариантом для ручного отката и недоступного GitHub API. choose_telemt_version() { local meta latest latest_meta pin_meta pin_d pin_f lat_d lat_f src ans v meta=$(_telemt_releases_meta 2>/dev/null || echo "") @@ -521,28 +521,28 @@ choose_telemt_version() { [ -n "$cur" ] && [ "$cur" = "$latest" ] && m2=" ${GREEN}← установлена${NC}" src=""; if [ -t 0 ]; then src=/dev/stdin; elif [ -r /dev/tty ]; then src=/dev/tty; fi if [ -z "$src" ]; then - TELEMT_VERSION_PREF="${TELEMT_PINNED_VERSION}" - bot_update_config_field telemt_version "${TELEMT_PINNED_VERSION}" >/dev/null 2>&1 || true + TELEMT_VERSION_PREF="latest" + bot_update_config_field telemt_version "latest" >/dev/null 2>&1 || true return 0 fi echo "" >&2 echo -e " ${BOLD}Версия ядра telemt${NC}" >&2 echo -e " ${DIM}Установлено сейчас: ${cur:-нет} · настройка: ${curpref:-рекомендованная}${NC}" >&2 - echo -e " ${GREEN}1)${NC} Рекомендованная — ${TELEMT_PINNED_VERSION}${pin_d:+ · $pin_d}${pin_f:+ · $pin_f} ${DIM}(протестирована)${NC}${m1}" >&2 - echo -e " ${GREEN}2)${NC} Последняя — ${latest}${lat_d:+ · $lat_d}${lat_f:+ · $lat_f}${m2}" >&2 + echo -e " ${GREEN}1)${NC} Последняя стабильная — ${latest}${lat_d:+ · $lat_d}${lat_f:+ · $lat_f} ${DIM}(рекомендуется)${NC}${m2}" >&2 + echo -e " ${GREEN}2)${NC} Проверенная запасная — ${TELEMT_PINNED_VERSION}${pin_d:+ · $pin_d}${pin_f:+ · $pin_f}${m1}" >&2 echo -e " ${GREEN}3)${NC} Выбрать из списка / ввести тег" >&2 printf ' > ' >&2; read -r ans < "$src" || ans=1 case "$ans" in - 2) TELEMT_VERSION_PREF="latest"; bot_update_config_field telemt_version "latest" >/dev/null 2>&1 || true - log_info "Версия ядра: последняя (${latest}, ${lat_f:-?}, ${lat_d:-?})" >&2 ;; + 2) TELEMT_VERSION_PREF="${TELEMT_PINNED_VERSION}"; bot_update_config_field telemt_version "${TELEMT_PINNED_VERSION}" >/dev/null 2>&1 || true + log_info "Версия ядра: запасная (${TELEMT_PINNED_VERSION}, ${pin_f:-?}, ${pin_d:-?})" >&2 ;; 3) echo " Доступные версии (новые сверху):" >&2 printf '%s\n' "$meta" | head -12 | awk -F'|' '{printf " %-9s · %s · %s\n",$1,$2,$3}' >&2 printf ' Введите тег (напр. %s): ' "$TELEMT_PINNED_VERSION" >&2; read -r v < "$src" || v="" v=$(echo "$v" | tr -d '[:space:]') if [ -n "$v" ]; then TELEMT_VERSION_PREF="$v"; bot_update_config_field telemt_version "$v" >/dev/null 2>&1 || true; log_info "Версия ядра: ${v}" >&2 - else TELEMT_VERSION_PREF="${TELEMT_PINNED_VERSION}"; bot_update_config_field telemt_version "${TELEMT_PINNED_VERSION}" >/dev/null 2>&1 || true; fi ;; - *) TELEMT_VERSION_PREF="${TELEMT_PINNED_VERSION}"; bot_update_config_field telemt_version "${TELEMT_PINNED_VERSION}" >/dev/null 2>&1 || true - log_info "Версия ядра: рекомендованная (${TELEMT_PINNED_VERSION})" >&2 ;; + else TELEMT_VERSION_PREF="latest"; bot_update_config_field telemt_version "latest" >/dev/null 2>&1 || true; fi ;; + *) TELEMT_VERSION_PREF="latest"; bot_update_config_field telemt_version "latest" >/dev/null 2>&1 || true + log_info "Версия ядра: последняя стабильная (${latest})" >&2 ;; esac return 0 } diff --git a/lib/common.sh b/lib/common.sh index 9a1cdec..4406247 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -3,7 +3,7 @@ # Colors, logging, spinner, system helpers, v1 compat, i18n-aware # ── Version ─────────────────────────────────────────────────────────────────── -GOTELEGRAM_VERSION="2.15.7" +GOTELEGRAM_VERSION="2.15.8" GOTELEGRAM_NAME="IGProxy" # ── Пути ────────────────────────────────────────────────────────────────────── diff --git a/lib/telemt.sh b/lib/telemt.sh index 8436da3..0943be2 100644 --- a/lib/telemt.sh +++ b/lib/telemt.sh @@ -28,6 +28,7 @@ _telemt_releases_meta() { get_telemt_download_url() { local version="${1:-}" + local libc_flavor="${2:-gnu}" local api="$TELEMT_RELEASE_API" if [ -n "$version" ] && [ "$version" != "latest" ]; then api="https://api.github.com/repos/${TELEMT_GITHUB}/releases/tags/${version}" @@ -47,11 +48,17 @@ get_telemt_download_url() { *) arch_pattern="${arch}" ;; esac + case "$libc_flavor" in + gnu|musl) ;; + *) return 1 ;; + esac + echo "$resp" | jq -r ".assets[].browser_download_url" 2>/dev/null \ | grep -iE "$arch_pattern" \ | grep -i "linux" \ | grep -v "sha256" \ - | grep "gnu" \ + | grep -vi -- "-v3-" \ + | grep -i -- "linux-${libc_flavor}" \ | head -1 } @@ -69,25 +76,37 @@ is_telemt_installed() { } # Целевая версия ядра: из config.json telemt_version, иначе глобал TELEMT_VERSION_PREF, -# иначе пин. "latest" -> резолвится в актуальный тег. Возвращает КОНКРЕТНЫЙ тег. +# иначе последний стабильный релиз. Пин остаётся аварийным fallback. telemt_target_version() { local v v=$(read_config_or_default telemt_version "" 2>/dev/null) [ -z "$v" ] && v="${TELEMT_VERSION_PREF:-}" case "$v" in - ""|recommended|pinned) echo "${TELEMT_PINNED_VERSION:-3.4.25}" ;; - latest) get_latest_telemt_version ;; + ""|latest|recommended) + get_latest_telemt_version 2>/dev/null || echo "${TELEMT_PINNED_VERSION:-3.4.25}" + ;; + pinned) echo "${TELEMT_PINNED_VERSION:-3.4.25}" ;; *) echo "$v" ;; esac } +telemt_system_libc() { + if ldd --version 2>&1 | grep -qi musl; then + printf 'musl\n' + else + printf 'gnu\n' + fi +} + # ── Скачивание и установка ─────────────────────────────────────────────────── download_telemt() { local version="${1:-$(telemt_target_version)}" + local libc_flavor="${2:-$(telemt_system_libc)}" + local allow_musl_fallback="${3:-1}" local url - url=$(get_telemt_download_url "$version") + url=$(get_telemt_download_url "$version" "$libc_flavor") if [ -z "$url" ]; then - log_error "Не найден бинарник telemt для архитектуры $(get_arch)" + log_error "Не найден ${libc_flavor}-бинарник telemt для архитектуры $(get_arch)" return 1 fi @@ -97,7 +116,7 @@ download_telemt() { tmp_file="$tmp_dir/asset" checksum_file="$tmp_dir/asset.sha256" extract_dir="$tmp_dir/extract" - log_info "Скачивание telemt ${version}: $url" + log_info "Скачивание telemt ${version} (${libc_flavor}): $url" if ! curl -L -s --max-time 120 -o "$tmp_file" "$url"; then log_error "Ошибка скачивания telemt" @@ -180,13 +199,32 @@ download_telemt() { rm -rf -- "$tmp_dir" return 1 } - if ! install -m 755 "$extracted" "$staged_bin" || \ - ! "$staged_bin" --version 2>/dev/null | grep -Fq "$version"; then - log_error "Скачанный бинарник telemt не прошёл проверку запуска/версии" + local probe_output="" probe_rc=0 + if ! install -m 755 "$extracted" "$staged_bin"; then + log_error "Не удалось подготовить бинарник telemt к запуску" rm -f -- "$staged_bin" rm -rf -- "$tmp_dir" return 1 fi + if probe_output=$("$staged_bin" --version 2>&1); then + probe_rc=0 + else + probe_rc=$? + fi + if [ "$probe_rc" -ne 0 ] || ! printf '%s\n' "$probe_output" | grep -Fq "$version"; then + probe_output=$(printf '%s' "${probe_output:-нет сообщения от загрузчика}" \ + | tr '\r\n\t' ' ' | sed -E 's/[[:space:]]+/ /g' | cut -c1-320) + log_warning "Сборка telemt (${libc_flavor}) не запустилась: ${probe_output} (code: ${probe_rc})." + rm -f -- "$staged_bin" + rm -rf -- "$tmp_dir" + if [ "$libc_flavor" = "gnu" ] && [ "$allow_musl_fallback" = "1" ]; then + log_info "Пробую совместимую статическую musl-сборку telemt той же версии." + download_telemt "$version" "musl" "0" + return $? + fi + log_error "Скачанный бинарник telemt не прошёл проверку запуска/версии" + return 1 + fi if [ -x "$TELEMT_BIN" ]; then backup_tmp=$(mktemp "${TELEMT_BIN}.rollback.XXXXXX") || { rm -f -- "$staged_bin" diff --git a/tests/test_installer_wizard.py b/tests/test_installer_wizard.py index 9c57f2e..e3654ac 100644 --- a/tests/test_installer_wizard.py +++ b/tests/test_installer_wizard.py @@ -516,13 +516,21 @@ def test_selected_ports_are_checked_against_firewall(self): self.assertNotIn("ufw allow ${port}/tcp >/dev/null", wizard) self.assertNotIn("firewall-cmd --permanent --add-port=${port}/tcp >/dev/null", wizard) - def test_telemt_3425_is_pinned_with_verified_download_and_split_mss(self): + def test_telemt_latest_is_default_with_verified_musl_fallback(self): common = (ROOT / "lib" / "common.sh").read_text(encoding="utf-8") telemt = (ROOT / "lib" / "telemt.sh").read_text(encoding="utf-8") + install = INSTALL.read_text(encoding="utf-8") config = (ROOT / "lib" / "telemt_config.sh").read_text(encoding="utf-8") self.assertIn('TELEMT_PINNED_VERSION="3.4.25"', common) - self.assertIn('${TELEMT_PINNED_VERSION:-3.4.25}', telemt) + self.assertIn('""|latest|recommended)', telemt) + self.assertIn('TELEMT_VERSION_PREF="latest"', install) + self.assertIn('bot_update_config_field telemt_version "latest"', install) + self.assertIn('local libc_flavor="${2:-gnu}"', telemt) + self.assertIn('"linux-${libc_flavor}"', telemt) + self.assertIn('download_telemt "$version" "musl" "0"', telemt) + self.assertIn("Пробую совместимую статическую musl-сборку", telemt) + self.assertIn('"$staged_bin" --version 2>&1', telemt) self.assertIn('"${url}.sha256"', telemt) self.assertIn('sha256sum "$tmp_file"', telemt) self.assertIn("mktemp -d /tmp/telemt-download.XXXXXX", telemt) @@ -532,6 +540,29 @@ def test_telemt_3425_is_pinned_with_verified_download_and_split_mss(self): self.assertIn('local client_mss_bulk="1400"', config) self.assertIn('client_mss_bulk = \\"${client_mss_bulk}\\"', config) + def test_telemt_target_defaults_to_latest_but_preserves_explicit_pin(self): + telemt = (ROOT / "lib" / "telemt.sh").as_posix() + script = f""" +TELEMT_BIN=/tmp/unused-telemt +TELEMT_PINNED_VERSION=3.4.25 +TELEMT_VERSION_PREF= +source {telemt!r} +read_config_or_default() {{ printf '%s\\n' "${{CONFIG_PREF:-}}"; }} +get_latest_telemt_version() {{ printf '9.9.9\\n'; }} +CONFIG_PREF= +telemt_target_version +CONFIG_PREF=pinned +telemt_target_version +""" + result = subprocess.run( + ["bash", "-c", script], + text=True, + capture_output=True, + check=False, + ) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(result.stdout.splitlines(), ["9.9.9", "3.4.25"]) + if __name__ == "__main__": unittest.main()