1.1.1: kit check и kit fix, заголовки Happ, безопасная загрузка ядра, инструкция XKeen
- kit check и kit fix: проверка служб, сертификата, подписки, сайта маскировки, прав на файлы; безопасная починка - kit-sub: пропускает заголовки Happ (Routing и др.) до клиентов - ядро Xray: запасная загрузка через зеркало только с проверкой SHA256, tools/release.sh сверяет суммы с официальными - установка со своим доменом: при отказе Let's Encrypt предлагается самоподписанный сертификат - README: инструкция XKeen, команды kit, новый баннер; правки инструкций и changelog Co-authored-by: xlok1e <124461726+xlok1e@users.noreply.github.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
xlok1e
Claude Sonnet 5.5
parent
49b27f380a
commit
099117c2a7
+234
-7
@@ -4,14 +4,14 @@
|
||||
#
|
||||
# kit user add имя [--gb 50] [--days 30] [--devices 3]
|
||||
# kit user list | link имя | limit имя [--gb N] [--days N] | off имя | on имя | del имя
|
||||
# kit update [--auto | --manual] | kit backup | kit version
|
||||
# kit update [--auto | --manual] | kit backup | kit check | kit fix | kit version
|
||||
|
||||
set -Eeuo pipefail
|
||||
export LC_ALL=C.UTF-8 # ширина колонок по символам, а не байтам
|
||||
|
||||
KIT_VERSION="1.1"
|
||||
KIT_VERSION="1.1.1"
|
||||
KIT_RAW="https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/main"
|
||||
# Файлы новой версии берём из её тега (v1.2 и т.д.), а не из меняющейся ветки main.
|
||||
# Файлы новой версии берём из её тега, а не из меняющейся ветки main.
|
||||
kit_ref_raw() { echo "https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/v$1"; }
|
||||
|
||||
XUI_ENV=/etc/x-ui/install-result.env
|
||||
@@ -271,7 +271,7 @@ cmd_version() {
|
||||
# Что нового в версии $1 – из CHANGELOG.md, без разметки.
|
||||
changelog_of() {
|
||||
curl -fsS -m 5 "$KIT_RAW/CHANGELOG.md" 2>/dev/null | awk -v v="## v$1" '
|
||||
index($0, v) == 1 { on = 1; t = substr($0, length(v) + 1); sub(/^[: ]+/, "", t); if (t != "") print t; next } on && /^## / { exit } on' | sed 's/\*\*//g; s/`//g' | grep -v '^[[:space:]]*$' | head -40 || true
|
||||
index($0, v) == 1 { on = 1; t = substr($0, length(v) + 1); sub(/^[: ]+/, "", t); if (t != "") print t; next } on && /^## / { exit } on' | sed -E 's/ ?Спасибо \[[^]]*\]\([^)]*\)[^.]*\.//g; s/\[([^]]*)\]\([^)]*\)/\1/g; s/\*\*//g; s/`//g' | grep -v '^[[:space:]]*$' | head -40 || true
|
||||
}
|
||||
|
||||
# Скачивает релиз $1 в каталог $2 и проверяет: подпись SHA256SUMS нашим ключом, версию
|
||||
@@ -483,9 +483,233 @@ cmd_update() {
|
||||
if [[ -n $news ]]; then echo; echo "${B}Что нового в $latest${N}"; echo "$news"; fi
|
||||
}
|
||||
|
||||
# ---------- проверка и починка ----------
|
||||
|
||||
# kit check только читает и показывает, что с сервером. kit fix чинит безопасное: перезапускает
|
||||
# упавшие службы, возвращает права на файлы, включает автообновление, перечитывает сертификат.
|
||||
CHECK_BAD=0; CHECK_WARN=0; CHECK_FIX=(); CHECK_QUIET=no
|
||||
c_ok() { [[ $CHECK_QUIET == yes ]] || printf '%s\n' "${G}✅${N} $*"; }
|
||||
c_info() { [[ $CHECK_QUIET == yes ]] || printf '%s\n' "${D}ℹ $*${N}"; }
|
||||
c_warn() { printf '%s\n' "${Y}⚠${N} $*"; CHECK_WARN=$((CHECK_WARN + 1)); }
|
||||
c_bad() { printf '%s\n' "${R}❌${N} ${*:2}"; CHECK_BAD=$((CHECK_BAD + 1)); [[ -z $1 ]] || CHECK_FIX+=("$1"); } # код-починки сообщение
|
||||
|
||||
# Сайт маскировки отвечает по TLS 1.3 и HTTP/2 (как требует REALITY)? Проверка с самого сервера.
|
||||
sni_alive() { echo | timeout 8 openssl s_client -connect "$1:443" -servername "$1" -tls1_3 -alpn h2 2>/dev/null | grep -q 'ALPN protocol: h2'; }
|
||||
|
||||
check_services() {
|
||||
local pid u
|
||||
if systemctl is-active -q x-ui; then c_ok "служба x-ui работает"; else c_bad svc:x-ui "служба x-ui не работает (journalctl -u x-ui -n 50)"; fi
|
||||
if [[ ${SINGLE:-no} == yes ]]; then
|
||||
if systemctl is-active -q nginx; then c_ok "nginx работает"; else c_bad svc:nginx "nginx не работает (journalctl -u nginx -n 50)"; fi
|
||||
fi
|
||||
if [[ -f /usr/local/lib/kit-sub/kit_sub.py ]]; then
|
||||
if systemctl is-active -q kit-sub; then
|
||||
c_ok "служба kit-sub работает"
|
||||
pid=$(systemctl show -p MainPID --value kit-sub 2>/dev/null || true)
|
||||
u=$(ps -o user= -p "$pid" 2>/dev/null | tr -d ' ' || true)
|
||||
if [[ -n $u && $u != root ]]; then c_ok "kit-sub работает не от root (пользователь $u)"; else c_bad "" "kit-sub работает от root – это небезопасно, обновите kit: kit update --force"; fi
|
||||
else
|
||||
c_bad svc:kit-sub "служба kit-sub не работает (journalctl -u kit-sub -n 50)"
|
||||
fi
|
||||
fi
|
||||
if auto_enabled; then c_ok "автообновление включено"
|
||||
elif [[ -f $KIT_MANUAL ]]; then c_info "автообновление выключено вами (включить: kit update --auto)"
|
||||
else c_bad timer "автообновление не включено"; fi
|
||||
}
|
||||
|
||||
check_versions() {
|
||||
local xv; xv=$(xray_version || true)
|
||||
c_info "kit $KIT_VERSION, панель 3X-UI $(/usr/local/x-ui/x-ui -v 2>/dev/null | head -1 || echo '?'), ядро Xray ${xv:-?}"
|
||||
if [[ -n $xv ]] && newer "$xv" v26.6.27; then
|
||||
c_warn "ядро Xray $xv новее проверенного (26.6.27): клиенты на Mihomo и sing-box могут не подключаться к REALITY"
|
||||
fi
|
||||
if [[ -s $KIT_LATEST ]] && newer "$(cat "$KIT_LATEST")" "$KIT_VERSION"; then
|
||||
c_info "вышла версия $(cat "$KIT_LATEST"): kit update"
|
||||
fi
|
||||
}
|
||||
|
||||
check_cert() {
|
||||
local cert end left
|
||||
cert=$(awk '$1 == "ssl_certificate" {sub(/;$/, "", $2); print $2; exit}' /etc/nginx/conf.d/kit.conf 2>/dev/null || true)
|
||||
[[ -n $cert ]] || cert=$(jq -r '.cert // empty' /etc/kit-sub/config.json 2>/dev/null || true)
|
||||
[[ -n $cert && -f $cert ]] || { c_info "сертификат для проверки не найден (режим без TLS)"; return 0; }
|
||||
if ! openssl x509 -in "$cert" -noout -checkend 0 >/dev/null 2>&1; then
|
||||
c_bad cert "сертификат $cert просрочен"
|
||||
else
|
||||
end=$(openssl x509 -in "$cert" -noout -enddate 2>/dev/null | cut -d= -f2)
|
||||
left=$((($(date -d "$end" +%s) - $(date +%s)) / 86400))
|
||||
if openssl x509 -in "$cert" -noout -checkend 86400 >/dev/null 2>&1; then c_ok "сертификат действителен ещё ~$left дн."
|
||||
else c_warn "сертификат истекает меньше чем через сутки: проверьте продление (acme.sh --cron)"; fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_exposure() {
|
||||
local bind f m bad=0
|
||||
# Панель наружу не торчит: за nginx или в режиме «только SSH-туннель».
|
||||
if [[ ${SINGLE:-no} == yes || ! -f /usr/local/lib/kit-sub/kit_sub.py ]]; then
|
||||
bind=$(ss -ltnH "sport = :$XUI_PANEL_PORT" 2>/dev/null | awk '{print $4}' || true)
|
||||
if [[ -z $bind ]]; then c_bad "" "панель не слушает порт $XUI_PANEL_PORT"
|
||||
elif grep -qv '^127\.0\.0\.1:' <<<"$bind"; then c_bad "" "панель слушает наружу ($(tr '\n' ' ' <<<"$bind")): в настройках 3X-UI укажите адрес 127.0.0.1"
|
||||
else c_ok "панель слушает только localhost"; fi
|
||||
fi
|
||||
if [[ ${SINGLE:-no} == yes ]]; then
|
||||
if ss -ltnH 'sport = :443' 2>/dev/null | grep -q .; then c_ok "порт 443 слушается"; else c_bad svc:nginx "порт 443 никто не слушает"; fi
|
||||
if command -v ufw >/dev/null && ufw status 2>/dev/null | grep -q '^Status: active'; then
|
||||
if ufw status | grep -Eq '^443/tcp +ALLOW'; then c_ok "ufw пропускает 443/tcp"; else c_bad "" "ufw не пропускает 443/tcp: ufw allow 443/tcp"; fi
|
||||
fi
|
||||
fi
|
||||
for f in /etc/x-ui/install-result.env /etc/kit/kit.env /etc/kit-sub/config.json /root/3x-ui.txt /root/cert/*/privkey.pem; do
|
||||
[[ -f $f ]] || continue
|
||||
m=$(stat -c %a "$f")
|
||||
[[ $m == 600 ]] || { c_bad perms "права на $f: $m (нужно 600)"; bad=1; }
|
||||
done
|
||||
((bad)) || c_ok "файлы с паролями и ключами доступны только root"
|
||||
}
|
||||
|
||||
check_subscription() {
|
||||
local cfg=/etc/kit-sub/config.json port path scheme=http sid ua code size body up sub_cert
|
||||
[[ -f $cfg ]] || { c_info "подписка не установлена (режим без сертификата: используйте отдельные ссылки из /root/3x-ui.txt)"; return 0; }
|
||||
port=$(jq -r '.port' "$cfg"); path=$(jq -r '.path' "$cfg")
|
||||
# Частая поломка: команда «x-ui cert» или меню панели включает TLS у встроенной подписки 3X-UI,
|
||||
# а kit-sub ходит к ней по http – подписка перестаёт отвечать.
|
||||
up=$(jq -r '.upstream // empty' "$cfg")
|
||||
sub_cert=$(api POST setting/all '{}' 2>/dev/null | jq -r '.subCertFile // empty' 2>/dev/null || true)
|
||||
if [[ $up == http://* && -n $sub_cert ]]; then
|
||||
c_bad subtls "у встроенной подписки 3X-UI включён TLS (сертификат $sub_cert), а kit-sub ходит к ней по http – подписка не отвечает"
|
||||
fi
|
||||
body=$(mktemp)
|
||||
[[ -n $(jq -r '.cert // empty' "$cfg") ]] && scheme=https
|
||||
sid=$(clients 2>/dev/null | jq -r '[.[] | .subId // empty | select(. != "")][0] // empty' 2>/dev/null || true)
|
||||
[[ -n $sid ]] || { c_info "пользователей нет, подписку проверить нечем"; return 0; }
|
||||
for ua in "Happ/1.0" "HiddifyNext/2.0" "v2rayN/7.0" "clash-verge/v2"; do
|
||||
: >"$body"
|
||||
code=$(curl -sgk -m 10 -A "$ua" -o "$body" -w '%{http_code}' "$scheme://127.0.0.1:$port$path$sid" 2>/dev/null || true)
|
||||
size=$(wc -c <"$body" | tr -d ' ')
|
||||
if [[ $code == 200 && ${size:-0} -gt 0 ]]; then c_ok "подписка отвечает для $ua ($size байт)"; else c_bad svc:kit-sub "подписка для $ua: HTTP ${code:-нет ответа}"; fi
|
||||
done
|
||||
rm -f "$body"
|
||||
if [[ ${SINGLE:-no} == yes ]]; then
|
||||
code=$(curl -sk -m 10 -A "Happ/1.0" -o /dev/null -w '%{http_code}' "https://127.0.0.1$path$sid" 2>/dev/null || true)
|
||||
if [[ $code == 200 ]]; then c_ok "подписка отвечает и через nginx (443)"; else c_bad svc:nginx "подписка через nginx: HTTP ${code:-нет ответа} (ищите причину выше)"; fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_masking() {
|
||||
local list line sni target host ips
|
||||
list=$(api GET inbounds/list 2>/dev/null) || { c_bad "" "не удалось получить подключения из панели (панель не отвечает?)"; return 0; }
|
||||
while IFS=$'\t' read -r name sni target; do
|
||||
[[ -n $sni ]] || continue
|
||||
if [[ $target == 127.0.0.1:* ]]; then
|
||||
ips=$(getent ahostsv4 "$sni" 2>/dev/null | awk '{print $1}' | sort -u || true); host=${HOST:-}
|
||||
if [[ -n $ips ]] && grep -qx "$host" <<<"$ips"; then c_ok "$name: свой домен $sni указывает на этот сервер"
|
||||
else c_bad "" "$name: свой домен $sni не указывает на этот сервер (A-запись: ${ips:-нет}, нужен $host)"; fi
|
||||
elif sni_alive "$sni"; then
|
||||
c_ok "$name: сайт маскировки $sni отвечает по TLS 1.3 и HTTP/2"
|
||||
else
|
||||
c_bad "" "$name: сайт маскировки $sni не отвечает по TLS 1.3 и HTTP/2 – смените его в панели 3X-UI"
|
||||
fi
|
||||
done < <(jq -r '.[] | select(.protocol == "vless") | (.streamSettings | if type == "string" then fromjson else . end) as $s
|
||||
| select($s.security == "reality") | [.remark, ($s.realitySettings.serverNames[0] // ""), ($s.realitySettings.target // "")] | @tsv' <<<"$list" 2>/dev/null || true)
|
||||
c_info "Проверка идёт с самого сервера: доступность из вашей сети она не покажет."
|
||||
}
|
||||
|
||||
check_system() {
|
||||
local used
|
||||
if command -v timedatectl >/dev/null; then
|
||||
if [[ $(timedatectl show -p NTPSynchronized --value 2>/dev/null || true) == yes ]]; then c_ok "время синхронизировано"
|
||||
else c_warn "время не синхронизировано: при сильном сдвиге TLS и REALITY не работают (kit fix включит синхронизацию)"; CHECK_FIX+=(ntp); fi
|
||||
fi
|
||||
used=$(df -P / | awk 'NR==2 {gsub("%", "", $5); print $5}')
|
||||
if ((${used:-0} >= 95)); then c_warn "диск заполнен на ${used}%"; else c_ok "место на диске: занято ${used:-?}%"; fi
|
||||
}
|
||||
|
||||
run_checks() {
|
||||
CHECK_BAD=0; CHECK_WARN=0; CHECK_FIX=()
|
||||
check_services; check_versions; check_cert; check_exposure; check_subscription; check_masking; check_system
|
||||
}
|
||||
|
||||
cmd_check() {
|
||||
echo "${B}kit check${N} – проверка сервера (ничего не меняет)"
|
||||
echo
|
||||
run_checks
|
||||
echo
|
||||
if ((CHECK_BAD == 0)); then
|
||||
echo "${G}${B}Всё в порядке.${N}$( ((CHECK_WARN)) && echo " Предупреждений: $CHECK_WARN." || true)"
|
||||
else
|
||||
echo "${R}${B}Проблем: $CHECK_BAD.${N} Безопасные исправления: ${B}kit fix${N} (сначала можно посмотреть: kit fix --dry-run)."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
fix_action() { # код
|
||||
case $1 in
|
||||
svc:x-ui) say "Перезапускаю x-ui"; systemctl restart x-ui; sleep 3 ;;
|
||||
svc:nginx)
|
||||
if nginx -t >/dev/null 2>&1; then say "Перезапускаю nginx"; systemctl restart nginx
|
||||
else warn "Конфиг nginx не проходит проверку (nginx -t) – не трогаю, чтобы не сломать сервер."; fi ;;
|
||||
svc:kit-sub) say "Перезапускаю kit-sub"; systemctl restart kit-sub; sleep 2 ;;
|
||||
subtls)
|
||||
say "Убираю сертификат у встроенной подписки 3X-UI (TLS снимает nginx, kit-sub ходит по http)"
|
||||
local all upd
|
||||
all=$(api POST setting/all '{}')
|
||||
upd=$(jq -c '.subCertFile = "" | .subKeyFile = ""' <<<"$all")
|
||||
api POST setting/update "$upd" >/dev/null
|
||||
systemctl restart x-ui; sleep 4 ;;
|
||||
timer) say "Включаю автообновление"; auto_on ;;
|
||||
perms)
|
||||
say "Возвращаю права 600 на файлы с паролями и ключами"
|
||||
local f
|
||||
for f in /etc/x-ui/install-result.env /etc/kit/kit.env /etc/kit-sub/config.json /root/3x-ui.txt /root/cert/*/privkey.pem; do
|
||||
[[ -f $f ]] && chmod 600 "$f"
|
||||
done ;;
|
||||
cert)
|
||||
if nginx -t >/dev/null 2>&1; then say "Перечитываю сертификат в nginx"; systemctl reload nginx
|
||||
else warn "Конфиг nginx не проходит проверку – сертификат не перечитываю."; fi
|
||||
warn "Если сертификат всё ещё просрочен, запустите продление: ~/.acme.sh/acme.sh --cron" ;;
|
||||
ntp) say "Включаю синхронизацию времени"; timedatectl set-ntp true ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Что чинить сейчас: если найдена причина (TLS у подписки), перезапуски kit-sub и nginx – лишь
|
||||
# следствия: сначала лечим причину, потом проверяем заново.
|
||||
fix_plan() {
|
||||
local c
|
||||
while IFS= read -r c; do
|
||||
if [[ $c == svc:kit-sub || $c == svc:nginx ]] && printf '%s\n' "${CHECK_FIX[@]}" | grep -qx subtls; then continue; fi
|
||||
echo "$c"
|
||||
done < <(printf '%s\n' "${CHECK_FIX[@]}" | sort -u)
|
||||
}
|
||||
|
||||
cmd_fix() {
|
||||
local dry=no c pass plan
|
||||
case ${1:-} in --dry-run) dry=yes ;; "") ;; *) die "kit fix [--dry-run]" ;; esac
|
||||
echo "${B}kit fix${N} – безопасные исправления (службы, права, автообновление, сертификат, время)"
|
||||
echo
|
||||
CHECK_QUIET=yes; run_checks; CHECK_QUIET=no
|
||||
if ((${#CHECK_FIX[@]} == 0)); then
|
||||
if ((CHECK_BAD == 0)); then echo "${G}Чинить нечего: всё в порядке.${N}"; return 0; fi
|
||||
echo; echo "${Y}Автоматически эти проблемы не исправить, они описаны выше. Подробности: kit check${N}"; return 1
|
||||
fi
|
||||
if [[ $dry == yes ]]; then
|
||||
while IFS= read -r c; do echo " будет сделано: $c"; done < <(fix_plan)
|
||||
return 0
|
||||
fi
|
||||
for pass in 1 2; do
|
||||
plan=$(fix_plan)
|
||||
[[ -n $plan ]] || break
|
||||
while IFS= read -r c; do fix_action "$c"; done <<<"$plan"
|
||||
CHECK_QUIET=yes; run_checks; CHECK_QUIET=no
|
||||
((${#CHECK_FIX[@]})) || break
|
||||
done
|
||||
echo; echo "${B}Повторная проверка${N}"; echo
|
||||
run_checks
|
||||
echo
|
||||
if ((CHECK_BAD == 0)); then echo "${G}${B}Готово: всё в порядке.${N}"; else echo "${R}Осталось проблем: $CHECK_BAD.${N} Часть из них нужно решать вручную (см. выше)."; return 1; fi
|
||||
}
|
||||
|
||||
# ---------- резервная копия ----------
|
||||
|
||||
# Всё, что нужно, чтобы поднять тот же сервер в другом месте: база панели (пользователи,
|
||||
# Что входит в копию: база панели (пользователи,
|
||||
# ключи, подключения), настройки kit и kit-sub, nginx, сайт-заглушка, свои сертификаты.
|
||||
# Сертификаты Let's Encrypt (на IP и на свой домен) не берём: на новом сервере он выпускается заново.
|
||||
BACKUP_PATHS=(/etc/x-ui/install-result.env /etc/kit/kit.env /etc/kit-sub/config.json
|
||||
@@ -527,7 +751,6 @@ PY
|
||||
echo "В ней ключи и пароли от сервера, храните её как пароль. Скачать к себе (на компьютере):"
|
||||
echo " ${B}scp root@$HOST:$out .${N}"
|
||||
echo
|
||||
echo "${D}Восстановление из копии на новом VPS появится в версии 1.2.${N}"
|
||||
}
|
||||
|
||||
usage() {
|
||||
@@ -545,7 +768,9 @@ ${B}kit${N} $KIT_VERSION – управление сервером 3X-UI KIT
|
||||
Сервер:
|
||||
kit update обновить kit и подписку kit-sub сейчас (пользователи и ссылки не меняются)
|
||||
kit update --manual выключить автообновление (--auto – включить обратно)
|
||||
kit backup резервная копия сервера (восстановление из неё – в версии 1.2)
|
||||
kit backup резервная копия сервера (подключения, ключи, пользователи)
|
||||
kit check проверить сервер: службы, сертификат, подписка, сайт маскировки, права
|
||||
kit fix [--dry-run] исправить безопасное: перезапустить службы, права, автообновление, сертификат
|
||||
kit version версия kit, панели и ядра
|
||||
EOF
|
||||
}
|
||||
@@ -560,6 +785,8 @@ case "${1:-} ${2:-}" in
|
||||
"user del") shift 2; cmd_del "$@" ;;
|
||||
"update "*) shift; cmd_update "$@" ;;
|
||||
"backup "*) cmd_backup ;;
|
||||
"check "*) cmd_check ;;
|
||||
"fix "*) shift; cmd_fix "$@" ;;
|
||||
"version "*|"--version "*|"-v "*) cmd_version ;;
|
||||
*) usage; update_hint ;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user