v1.2.26: лимит Let-Encrypt называется прямо; готовый сертификат переиспользуется вместо нового заказа
This commit is contained in:
@@ -1,3 +1,14 @@
|
|||||||
|
## v1.2.26: лимит Let's Encrypt виден прямо, готовый сертификат переиспользуется
|
||||||
|
|
||||||
|
* **Причина отказа теперь называется честно.** Let's Encrypt разрешает 5 сертификатов на точный набор имён
|
||||||
|
за 7 суток; при повторных установках лимит выбирается, и acme.sh отвечает `rateLimited`, а установщик
|
||||||
|
писал про «A-запись, порт 80 или Cloudflare» — то есть отправлял искать причину не там. Теперь в выводе
|
||||||
|
дата, после которой повтор сработает, и подсказка взять другое имя (`node3.…` — лимит считается на набор имён).
|
||||||
|
* **Готовый сертификат переиспользуется.** Если у acme.sh уже лежит годный сертификат на этот домен,
|
||||||
|
новый не заказывается — лишний заказ и упирался в лимит. Заодно не тратится время на повторные попытки.
|
||||||
|
* Итоговая ошибка установщика больше не повторяет догадки про порт и DNS: причина печатается выше,
|
||||||
|
а лог остаётся в /var/log/kit-domain-cert.log.
|
||||||
|
|
||||||
## v1.2.25: вопрос про панель возвращён, блок про защиту переписан понятнее
|
## v1.2.25: вопрос про панель возвращён, блок про защиту переписан понятнее
|
||||||
|
|
||||||
* **Снова спрашиваем, где открывать панель и подписку.** У автора этот вопрос задавался только когда
|
* **Снова спрашиваем, где открывать панель и подписку.** У автора этот вопрос задавался только когда
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
KIT_VERSION="1.2.25" # версия сборки форка; должна совпадать с VERSION релиза
|
KIT_VERSION="1.2.26" # версия сборки форка; должна совпадать с VERSION релиза
|
||||||
GITEA="${KIT_FORK_BASE:-https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS}"
|
GITEA="${KIT_FORK_BASE:-https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS}"
|
||||||
NS="3x-ui-kit-release"
|
NS="3x-ui-kit-release"
|
||||||
SIG_ID="releases@3x-ui-kit"
|
SIG_ID="releases@3x-ui-kit"
|
||||||
|
|||||||
+34
-11
@@ -30,7 +30,7 @@ wait_apt_idle() {
|
|||||||
XUI_VERSION="v3.9.0"
|
XUI_VERSION="v3.9.0"
|
||||||
# SHA256 установщика 3X-UI этой версии: тег могут передвинуть, а хеш – нет (проверено 2026-09-30).
|
# SHA256 установщика 3X-UI этой версии: тег могут передвинуть, а хеш – нет (проверено 2026-09-30).
|
||||||
XUI_INSTALL_SHA256="18616fe26c8f6c92db6daa2dcd7cd53c5143ee69ecfd26d8ea6dc9b2c78607a6"
|
XUI_INSTALL_SHA256="18616fe26c8f6c92db6daa2dcd7cd53c5143ee69ecfd26d8ea6dc9b2c78607a6"
|
||||||
KIT_VERSION="1.2.25"
|
KIT_VERSION="1.2.26"
|
||||||
# kit и kit-sub берём из того же релиза, что и этот скрипт, а не из меняющейся ветки main.
|
# kit и kit-sub берём из того же релиза, что и этот скрипт, а не из меняющейся ветки main.
|
||||||
KIT_RAW="https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/tag/v$KIT_VERSION"
|
KIT_RAW="https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/tag/v$KIT_VERSION"
|
||||||
# Ядро Xray для панели. С 26.7.x клиенты на Mihomo и sing-box (Hiddify, FlClash,
|
# Ядро Xray для панели. С 26.7.x клиенты на Mihomo и sing-box (Hiddify, FlClash,
|
||||||
@@ -286,20 +286,43 @@ issue_domain_cert() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
[[ -x $acme ]] || { warn "Не нашёл acme.sh, которым установщик 3X-UI получает сертификаты: не могу выпустить сертификат для $DOMAIN."; return 1; }
|
[[ -x $acme ]] || { warn "Не нашёл acme.sh, которым установщик 3X-UI получает сертификаты: не могу выпустить сертификат для $DOMAIN."; return 1; }
|
||||||
|
# Наш шов: лимит Let's Encrypt считается на точный набор имён (5 сертификатов за 7 суток). Если у acme.sh
|
||||||
|
# уже лежит годный сертификат на этот домен — берём его и не заказываем новый, лишний заказ упирается в лимит.
|
||||||
|
local need_issue=yes
|
||||||
|
if [[ -s /root/.acme.sh/${DOMAIN}_ecc/fullchain.cer ]] \
|
||||||
|
&& openssl x509 -in "/root/.acme.sh/${DOMAIN}_ecc/fullchain.cer" -noout -checkhost "$DOMAIN" 2>/dev/null | grep -q 'does match' \
|
||||||
|
&& openssl x509 -in "/root/.acme.sh/${DOMAIN}_ecc/fullchain.cer" -noout -checkend 86400 >/dev/null 2>&1; then
|
||||||
|
ok "у acme.sh уже есть годный сертификат для ${B}$DOMAIN${N} — беру его, новый не заказываю"
|
||||||
|
need_issue=no
|
||||||
|
fi
|
||||||
port_busy 80 tcp && { warn "Порт 80/tcp занят: Let's Encrypt не сможет проверить домен $DOMAIN."; return 1; }
|
port_busy 80 tcp && { warn "Порт 80/tcp занят: Let's Encrypt не сможет проверить домен $DOMAIN."; return 1; }
|
||||||
say "Получаю сертификат Let's Encrypt для ${B}$DOMAIN${N}"
|
say "Получаю сертификат Let's Encrypt для ${B}$DOMAIN${N}"
|
||||||
install -m 600 /dev/null "$log"
|
install -m 600 /dev/null "$log"
|
||||||
# Сбои бывают временными (например, ошибка TLS при скачивании готового сертификата): до трёх попыток.
|
# Сбои бывают временными (например, ошибка TLS при скачивании готового сертификата): до трёх попыток.
|
||||||
local try
|
if [[ $need_issue == yes ]]; then
|
||||||
for try in 1 2 3; do
|
local try
|
||||||
rc=0
|
for try in 1 2 3; do
|
||||||
"$acme" --issue -d "$DOMAIN" --standalone --httpport 80 --server letsencrypt --keylength ec-256 ${rc_force:-} >>"$log" 2>&1 || rc=$?
|
rc=0
|
||||||
[[ $rc == 0 || $rc == 2 ]] && break
|
"$acme" --issue -d "$DOMAIN" --standalone --httpport 80 --server letsencrypt --keylength ec-256 ${rc_force:-} >>"$log" 2>&1 || rc=$?
|
||||||
local rc_force="--force"
|
[[ $rc == 0 || $rc == 2 ]] && break
|
||||||
sleep 10
|
local rc_force="--force"
|
||||||
done
|
sleep 10
|
||||||
|
done
|
||||||
|
else
|
||||||
|
rc=2 # сертификат взяли готовый: для кода ниже это то же, что «уже свежий»
|
||||||
|
fi
|
||||||
# 2 – сертификат уже свежий, выпускать заново не нужно.
|
# 2 – сертификат уже свежий, выпускать заново не нужно.
|
||||||
[[ $rc == 0 || $rc == 2 ]] || { warn "Let's Encrypt не выдал сертификат для $DOMAIN. Обычно дело в одном из трёх: A-запись ещё не обновилась, порт 80 закрыт у хостера или домен за проксированием Cloudflare. Лог: $log"; return 1; }
|
[[ $rc == 0 || $rc == 2 ]] || {
|
||||||
|
# Наш шов: лимит Let's Encrypt — частая причина, и она не про сервер. Говорим прямо и с датой повтора.
|
||||||
|
if grep -qE 'rateLimited|too many certificates' "$log"; then
|
||||||
|
local till
|
||||||
|
till=$(grep -oE 'retry after [0-9-]+ [0-9:]+ UTC' "$log" | tail -1 | sed 's/^retry after //')
|
||||||
|
warn "Лимит Let's Encrypt: на имя ${B}$DOMAIN${N} за 7 суток уже выпущено 5 сертификатов${till:+ — повтор возможен после $till}. С вашим сервером всё в порядке: DNS, порты и ufw не при чём. Подождите до этой даты либо возьмите другое имя (например node3.…): лимит считается на точный набор имён."
|
||||||
|
else
|
||||||
|
warn "Let's Encrypt не выдал сертификат для $DOMAIN. Обычно дело в одном из трёх: A-запись ещё не обновилась, порт 80 закрыт у хостера или домен за проксированием Cloudflare. Лог: $log"
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
# Каталог и ключ – только для root (nginx читает их от root).
|
# Каталог и ключ – только для root (nginx читает их от root).
|
||||||
install -d -m 700 "$DOMAIN_CERT_DIR"
|
install -d -m 700 "$DOMAIN_CERT_DIR"
|
||||||
(umask 077; "$acme" --install-cert -d "$DOMAIN" --ecc --fullchain-file "$DOMAIN_CERT_DIR/fullchain.pem" \
|
(umask 077; "$acme" --install-cert -d "$DOMAIN" --ecc --fullchain-file "$DOMAIN_CERT_DIR/fullchain.pem" \
|
||||||
@@ -2140,7 +2163,7 @@ PY
|
|||||||
# позже (после сборки nginx), поэтому на свежем сервере проверка Let's Encrypt доходила до сервера и
|
# позже (после сборки nginx), поэтому на свежем сервере проверка Let's Encrypt доходила до сервера и
|
||||||
# «проглатывалась» — в логе acme это «Timeout during connect (likely firewall problem)».
|
# «проглатывалась» — в логе acme это «Timeout during connect (likely firewall problem)».
|
||||||
command -v ufw >/dev/null 2>&1 && ufw allow 80/tcp >/dev/null 2>&1 || true
|
command -v ufw >/dev/null 2>&1 && ufw allow 80/tcp >/dev/null 2>&1 || true
|
||||||
[[ -n $DOMAIN ]] && { issue_domain_cert || die "Не удалось получить сертификат для $DOMAIN: нужен свободный порт 80, открытый в ufw (и у хостера), и A-запись домена на этот сервер. Лог: /var/log/kit-domain-cert.log"; }
|
[[ -n $DOMAIN ]] && { issue_domain_cert || die "Без сертификата для $DOMAIN продолжать нельзя — причина выше, подробности в /var/log/kit-domain-cert.log"; }
|
||||||
setup_nginx
|
setup_nginx
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
# Код выхода 1 при любом запуске (и при `__update_binary`) – старый `hy2 update` на нём
|
# Код выхода 1 при любом запуске (и при `__update_binary`) – старый `hy2 update` на нём
|
||||||
# останавливается и не заменяет команду hy2.
|
# останавливается и не заменяет команду hy2.
|
||||||
|
|
||||||
KIT_VERSION="1.2.25"
|
KIT_VERSION="1.2.26"
|
||||||
|
|
||||||
cat >&2 <<'MSG'
|
cat >&2 <<'MSG'
|
||||||
Отдельный установщик Hysteria2 больше не поддерживается.
|
Отдельный установщик Hysteria2 больше не поддерживается.
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@
|
|||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
export LC_ALL=C.UTF-8 # ширина колонок по символам, а не байтам
|
export LC_ALL=C.UTF-8 # ширина колонок по символам, а не байтам
|
||||||
|
|
||||||
KIT_VERSION="1.2.25"
|
KIT_VERSION="1.2.26"
|
||||||
KIT_RAW="https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/branch/main"
|
KIT_RAW="https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/branch/main"
|
||||||
# Файлы новой версии берём из её тега, а не из меняющейся ветки main.
|
# Файлы новой версии берём из её тега, а не из меняющейся ветки main.
|
||||||
kit_ref_raw() { echo "https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/tag/v$1"; }
|
kit_ref_raw() { echo "https://git.rukavishnikoff.ru/hacker07/3X-UI_autoVPS/raw/tag/v$1"; }
|
||||||
|
|||||||
+22
-1
@@ -289,6 +289,27 @@ def main():
|
|||||||
' names=$(nearby_scan "$base" | grep -vE "^(${me})$" || true)\n [[ -n $names ]] || continue\n out=$(while IFS= read -r n; do\n [[ $n =~ $SNI_DYN_RE || $n =~ $SNI_BRAND_RE || $n =~ $SNI_BAD_RE ]] && continue\n printf \'%s\\n\' "$n"\n done <<<"$names" | xargs -P 8 -I{} bash -c \'same_net "$1" && sni_alive "$1" && sni_trusted "$1" && sni_quality "$1" && printf "%s\\n" "$1"\' _ {} 2>/dev/null | head -8)\n [[ -n $out ]] && { printf \'%s\\n\' "$out"; return 0; }\n',
|
' names=$(nearby_scan "$base" | grep -vE "^(${me})$" || true)\n [[ -n $names ]] || continue\n out=$(while IFS= read -r n; do\n [[ $n =~ $SNI_DYN_RE || $n =~ $SNI_BRAND_RE || $n =~ $SNI_BAD_RE ]] && continue\n printf \'%s\\n\' "$n"\n done <<<"$names" | xargs -P 8 -I{} bash -c \'same_net "$1" && sni_alive "$1" && sni_trusted "$1" && sni_quality "$1" && printf "%s\\n" "$1"\' _ {} 2>/dev/null | head -8)\n [[ -n $out ]] && { printf \'%s\\n\' "$out"; return 0; }\n',
|
||||||
'авто-шов: replace, строк 6',
|
'авто-шов: replace, строк 6',
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
'scripts/3x-ui.sh',
|
||||||
|
' [[ -x $acme ]] || { warn "Не нашёл acme.sh, которым установщик 3X-UI получает сертификаты: не могу выпустить сертификат для $DOMAIN."; return 1; }\n',
|
||||||
|
False,
|
||||||
|
' [[ -x $acme ]] || { warn "Не нашёл acme.sh, которым установщик 3X-UI получает сертификаты: не могу выпустить сертификат для $DOMAIN."; return 1; }\n # Наш шов: лимит Let\'s Encrypt считается на точный набор имён (5 сертификатов за 7 суток). Если у acme.sh\n # уже лежит годный сертификат на этот домен — берём его и не заказываем новый, лишний заказ упирается в лимит.\n local need_issue=yes\n if [[ -s /root/.acme.sh/${DOMAIN}_ecc/fullchain.cer ]] \\\n && openssl x509 -in "/root/.acme.sh/${DOMAIN}_ecc/fullchain.cer" -noout -checkhost "$DOMAIN" 2>/dev/null | grep -q \'does match\' \\\n && openssl x509 -in "/root/.acme.sh/${DOMAIN}_ecc/fullchain.cer" -noout -checkend 86400 >/dev/null 2>&1; then\n ok "у acme.sh уже есть годный сертификат для ${B}$DOMAIN${N} — беру его, новый не заказываю"\n need_issue=no\n fi\n',
|
||||||
|
'авто-шов: insert, строк 1',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'scripts/3x-ui.sh',
|
||||||
|
' local try\n for try in 1 2 3; do\n rc=0\n "$acme" --issue -d "$DOMAIN" --standalone --httpport 80 --server letsencrypt --keylength ec-256 ${rc_force:-} >>"$log" 2>&1 || rc=$?\n [[ $rc == 0 || $rc == 2 ]] && break\n local rc_force="--force"\n sleep 10\n done\n',
|
||||||
|
False,
|
||||||
|
' if [[ $need_issue == yes ]]; then\n local try\n for try in 1 2 3; do\n rc=0\n "$acme" --issue -d "$DOMAIN" --standalone --httpport 80 --server letsencrypt --keylength ec-256 ${rc_force:-} >>"$log" 2>&1 || rc=$?\n [[ $rc == 0 || $rc == 2 ]] && break\n local rc_force="--force"\n sleep 10\n done\n else\n rc=2 # сертификат взяли готовый: для кода ниже это то же, что «уже свежий»\n fi\n',
|
||||||
|
'авто-шов: replace, строк 8',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'scripts/3x-ui.sh',
|
||||||
|
' [[ $rc == 0 || $rc == 2 ]] || { warn "Let\'s Encrypt не выдал сертификат для $DOMAIN. Обычно дело в одном из трёх: A-запись ещё не обновилась, порт 80 закрыт у хостера или домен за проксированием Cloudflare. Лог: $log"; return 1; }\n',
|
||||||
|
False,
|
||||||
|
' [[ $rc == 0 || $rc == 2 ]] || {\n # Наш шов: лимит Let\'s Encrypt — частая причина, и она не про сервер. Говорим прямо и с датой повтора.\n if grep -qE \'rateLimited|too many certificates\' "$log"; then\n local till\n till=$(grep -oE \'retry after [0-9-]+ [0-9:]+ UTC\' "$log" | tail -1 | sed \'s/^retry after //\')\n warn "Лимит Let\'s Encrypt: на имя ${B}$DOMAIN${N} за 7 суток уже выпущено 5 сертификатов${till:+ — повтор возможен после $till}. С вашим сервером всё в порядке: DNS, порты и ufw не при чём. Подождите до этой даты либо возьмите другое имя (например node3.…): лимит считается на точный набор имён."\n else\n warn "Let\'s Encrypt не выдал сертификат для $DOMAIN. Обычно дело в одном из трёх: A-запись ещё не обновилась, порт 80 закрыт у хостера или домен за проксированием Cloudflare. Лог: $log"\n fi\n return 1\n }\n',
|
||||||
|
'авто-шов: replace, строк 1',
|
||||||
|
),
|
||||||
(
|
(
|
||||||
'scripts/3x-ui.sh',
|
'scripts/3x-ui.sh',
|
||||||
" _ _ _ _ _ _ _\n (_) |_ ___ _ __ ___ | |_| | ___ _| |__ _ __(_) ___| | __\n | | __/ __| '_ \\ / _ \\| __| |/ / | | | '_ \\| '__| |/ __| |/ /\n | | |_\\__ \\ | | | (_) | |_| <| |_| | |_) | | | | (__| <\n |_|\\__|___/_| |_|\\___/ \\__|_|\\_\\\\__,_|_.__/|_| |_|\\___|_|\\_\\\n",
|
" _ _ _ _ _ _ _\n (_) |_ ___ _ __ ___ | |_| | ___ _| |__ _ __(_) ___| | __\n | | __/ __| '_ \\ / _ \\| __| |/ / | | | '_ \\| '__| |/ __| |/ /\n | | |_\\__ \\ | | | (_) | |_| <| |_| | |_) | | | | (__| <\n |_|\\__|___/_| |_|\\___/ \\__|_|\\_\\\\__,_|_.__/|_| |_|\\___|_|\\_\\\n",
|
||||||
@@ -517,7 +538,7 @@ def main():
|
|||||||
'scripts/3x-ui.sh',
|
'scripts/3x-ui.sh',
|
||||||
' [[ -n $DOMAIN ]] && { issue_domain_cert || die "Не удалось получить сертификат для $DOMAIN: порт 80 должен быть свободен, а A-запись домена указывать на этот сервер. Лог: /var/log/kit-domain-cert.log"; }\n',
|
' [[ -n $DOMAIN ]] && { issue_domain_cert || die "Не удалось получить сертификат для $DOMAIN: порт 80 должен быть свободен, а A-запись домена указывать на этот сервер. Лог: /var/log/kit-domain-cert.log"; }\n',
|
||||||
False,
|
False,
|
||||||
' # Наш шов: открываем 80 в ufw ПРЯМО ПЕРЕД выпуском сертификата. Установщик открывает список портов\n # позже (после сборки nginx), поэтому на свежем сервере проверка Let\'s Encrypt доходила до сервера и\n # «проглатывалась» — в логе acme это «Timeout during connect (likely firewall problem)».\n command -v ufw >/dev/null 2>&1 && ufw allow 80/tcp >/dev/null 2>&1 || true\n [[ -n $DOMAIN ]] && { issue_domain_cert || die "Не удалось получить сертификат для $DOMAIN: нужен свободный порт 80, открытый в ufw (и у хостера), и A-запись домена на этот сервер. Лог: /var/log/kit-domain-cert.log"; }\n',
|
' # Наш шов: открываем 80 в ufw ПРЯМО ПЕРЕД выпуском сертификата. Установщик открывает список портов\n # позже (после сборки nginx), поэтому на свежем сервере проверка Let\'s Encrypt доходила до сервера и\n # «проглатывалась» — в логе acme это «Timeout during connect (likely firewall problem)».\n command -v ufw >/dev/null 2>&1 && ufw allow 80/tcp >/dev/null 2>&1 || true\n [[ -n $DOMAIN ]] && { issue_domain_cert || die "Без сертификата для $DOMAIN продолжать нельзя — причина выше, подробности в /var/log/kit-domain-cert.log"; }\n',
|
||||||
'авто-шов: replace, строк 1',
|
'авто-шов: replace, строк 1',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|||||||
Reference in New Issue
Block a user