1.1: kit update и kit version для доставки исправлений на серверы 1.0

kit update скачивает kit и kit-sub из тега релиза, переводит kit-sub на работу
без root, убирает cron меню x-ui и исправляет ссылку подписки в панели.
При сбое новой kit-sub возвращает прежнюю.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7GrBSRPVXhrz3RAprWDnt
This commit is contained in:
itsnotkubrick
2026-09-30 18:22:52 +00:00
co-authored by Claude Opus 5.5
parent fdae4588ea
commit 7167c8158d
3 changed files with 195 additions and 10 deletions
+12 -4
View File
@@ -31,13 +31,21 @@
- `SECURITY.md`: как сообщить об уязвимости, не публикуя её на весь интернет.
- Проверка кода на каждый коммит: ShellCheck, Python, тесты генераторов.
**Новая команда: `kit update`**
- Обновляет kit и подписку до новой версии, а пользователи, ссылки и подписки остаются прежними.
Раньше, чтобы получить исправление, приходилось сносить всё и раздавать ссылки заново.
«Мам, удали старый VPN, вот новый» больше не повторится.
- Если новая подписка вдруг не запустится, `kit update` молча вернёт старую. Ну, не молча,
предупредит.
**Как обновиться**
- Новые установки получают всё сразу.
- Сервер 3X-UI KIT из версии 1.0 (там ещё нет `kit update`), один раз:
`curl -fsSL https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/v1.1/scripts/kit.sh -o /usr/local/bin/kit && chmod 755 /usr/local/bin/kit && kit update`
- Дальше хватит `kit update`, он сам подскажет, когда выйдет новая версия.
- Hysteria2: `hy2 update`.
- Серверу 3X-UI из версии 1.0 ничего не угрожает: критическая ошибка была только в установщике
Hysteria2 и срабатывала при установке. Перевести подписку на работу без root поможет
`kit update` в версии 1.2. Пока можно просто продолжать пользоваться.
- Новые установки получают всё сразу.
## v1.0
+20
View File
@@ -106,6 +106,26 @@ kit user add sasha --gb 50 --days 30
Трафик и лимиты общие на все протоколы: 50 ГБ — это 50 ГБ суммарно, через что бы пользователь ни подключался.
## Обновление
```bash
kit update
```
Обновляет команду `kit` и подписку `kit-sub` до новой версии 3X-UI KIT. Пользователи, ссылки
и подписки не меняются, раздавать ничего заново не нужно. Файлы берутся из тега релиза
на GitHub, а если новая подписка не запустится, `kit update` вернёт прежнюю.
`kit version` покажет версию kit, панели и ядра Xray.
Сервер ставили версией 1.0? В ней команды `kit update` ещё нет, поэтому один раз:
```bash
curl -fsSL https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/v1.1/scripts/kit.sh -o /usr/local/bin/kit && chmod 755 /usr/local/bin/kit && kit update
```
Панель 3X-UI обновляется отдельно, через `x-ui`. Ядро Xray в панели не обновляйте до последней
версии: с Xray 26.7 и новее Hiddify, FlClash и Clash Verge не подключаются к REALITY.
## Параметры
| Параметр | Что делает |
+163 -6
View File
@@ -1,15 +1,22 @@
#!/usr/bin/env bash
# kit — пользователи 3X-UI KIT: один пользователь сразу на всех протоколах.
# kit — управление сервером 3X-UI KIT: пользователи и обновление.
# https://github.com/itsnotkubrick/3X-UI_KIT
#
# kit user add имя [--gb 50] [--days 30] [--devices 3]
# kit user list | link имя | limit имя [--gb N] [--days N] | off имя | on имя | del имя
# kit update | kit version
set -Eeuo pipefail
export LC_ALL=C.UTF-8 # ширина колонок по символам, а не байтам
KIT_VERSION="1.1"
KIT_RAW="https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/main"
# Файлы новой версии берём из её тега (v1.2 и т.д.), а не из меняющейся ветки main.
kit_ref_raw() { echo "https://raw.githubusercontent.com/itsnotkubrick/3X-UI_KIT/v$1"; }
XUI_ENV=/etc/x-ui/install-result.env
KIT_ENV=/etc/kit/kit.env
KIT_LATEST=/etc/kit/latest-version
if [[ -t 1 ]]; then
G=$'\e[32m'; Y=$'\e[33m'; R=$'\e[31m'; B=$'\e[1m'; D=$'\e[2m'; N=$'\e[0m'
@@ -17,6 +24,7 @@ else
G=; Y=; R=; B=; D=; N=
fi
say() { printf '%s\n' "${G}==>${N} $*"; }
warn() { printf '%s\n' "${Y}!${N} $*" >&2; }
die() { printf '%s\n' "${R}✗${N} $*" >&2; exit 1; }
[[ $EUID -eq 0 ]] || die "Запустите от root: sudo -i, затем команду ещё раз."
@@ -212,27 +220,176 @@ cmd_del() {
say "Пользователь $name удалён, его подписка больше не работает."
}
# ---------- версия и обновление ----------
xray_version() {
local b
for b in /usr/local/x-ui/bin/xray-linux-*; do [[ -x $b ]] && "$b" version 2>/dev/null | awk 'NR==1 {print "v" $2}'; return; done
}
remote_version() { curl -fsS -m "${1:-5}" "$KIT_RAW/VERSION" 2>/dev/null | tr -d '[:space:]'; }
newer() { [[ $1 != "$2" && $(printf '%s\n%s\n' "$1" "$2" | sort -V | tail -1) == "$1" ]]; } # $1 новее $2?
# Раз в сутки узнаём последнюю версию (не дольше 3 секунд) и подсказываем обновиться.
update_hint() {
local latest=""
if [[ ! -f $KIT_LATEST ]] || (($(date +%s) - $(stat -c %Y "$KIT_LATEST") > 86400)); then
latest=$(remote_version 3) || true
[[ $latest =~ ^[0-9]+(\.[0-9]+)+$ ]] && echo "$latest" >"$KIT_LATEST" || touch "$KIT_LATEST"
fi
latest=$(cat "$KIT_LATEST" 2>/dev/null || true)
if [[ -n $latest ]] && newer "$latest" "$KIT_VERSION"; then
echo
echo "${Y}↑ Доступна версия $latest${N} (у вас $KIT_VERSION). Обновить: ${B}kit update${N}"
fi
}
cmd_version() {
echo "3X-UI KIT $KIT_VERSION"
echo "${D}панель 3X-UI $(/usr/local/x-ui/x-ui -v 2>/dev/null | head -1 || echo '?'), ядро Xray $(xray_version)${N}"
update_hint
}
# Что нового в версии $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
}
# Юнит kit-sub: без root (DynamicUser), конфиг и сертификат – через LoadCredential.
# Сертификат Let's Encrypt на IP продлевается раз в несколько дней, поэтому при отдельном
# порте kit-sub перезапускается раз в сутки и берёт свежий.
kit_sub_unit() { # путь-к-сертификату путь-к-ключу (пусто – за nginx)
cat <<UNIT
[Unit]
Description=kit-sub: подписка с учётом приложения (3X-UI KIT)
After=network-online.target x-ui.service
Wants=network-online.target
[Service]
ExecStart=/usr/bin/python3 /usr/local/lib/kit-sub/kit_sub.py
Restart=on-failure
RestartSec=5
DynamicUser=yes
LoadCredential=config.json:/etc/kit-sub/config.json
${1:+LoadCredential=cert.pem:$1}
${2:+LoadCredential=key.pem:$2}
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=true
PrivateDevices=true
ProtectProc=invisible
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
MemoryMax=64M
[Install]
WantedBy=multi-user.target
UNIT
}
# Сервер ещё не получил исправления 1.1 (например, kit обновили вручную из 1.0)?
needs_migration() {
[[ -f /etc/cron.d/kit-xui-menu ]] && return 0
[[ -f /etc/systemd/system/kit-sub.service ]] && ! grep -q '^DynamicUser=yes' /etc/systemd/system/kit-sub.service && return 0
return 1
}
cmd_update() {
local force=${1:-} latest tmp
latest=$(remote_version) || true
[[ $latest =~ ^[0-9]+(\.[0-9]+)+$ ]] || die "Не удалось узнать последнюю версию: GitHub недоступен с сервера. Попробуйте позже."
echo "$latest" >"$KIT_LATEST"
if ! newer "$latest" "$KIT_VERSION" && [[ $force != --force ]] && ! needs_migration; then
say "У вас последняя версия: $KIT_VERSION."
echo "${D}Переустановить файлы kit той же версии: kit update --force${N}"
return
fi
if [[ $latest == "$KIT_VERSION" ]]; then say "Применяю исправления версии $latest"; else say "3X-UI KIT $KIT_VERSION → $latest"; fi
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' RETURN
curl -fsSL --retry 3 -o "$tmp/kit.sh" "$(kit_ref_raw "$latest")/scripts/kit.sh" && bash -n "$tmp/kit.sh" \
|| die "Не удалось скачать новую версию kit – сервер не тронут."
# Подписка kit-sub: ставим новую, при сбое возвращаем старую.
if [[ -f /usr/local/lib/kit-sub/kit_sub.py ]]; then
curl -fsSL --retry 3 -o "$tmp/kit_sub.py" "$(kit_ref_raw "$latest")/scripts/kit-sub.py" \
&& python3 -c "import ast,sys; ast.parse(open(sys.argv[1]).read())" "$tmp/kit_sub.py" \
|| die "Не удалось скачать kit-sub – сервер не тронут."
cp /usr/local/lib/kit-sub/kit_sub.py "$tmp/kit_sub.old"
cp /etc/systemd/system/kit-sub.service "$tmp/kit-sub.service.old"
install -m 644 "$tmp/kit_sub.py" /usr/local/lib/kit-sub/kit_sub.py
# С 1.1 kit-sub работает без root: переписываем юнит под DynamicUser и LoadCredential.
local c k
c=$(jq -r '.cert // empty' /etc/kit-sub/config.json); k=$(jq -r '.key // empty' /etc/kit-sub/config.json)
kit_sub_unit "$c" "$k" >/etc/systemd/system/kit-sub.service
[[ -n $c ]] && echo '19 4 * * * root systemctl restart kit-sub >/dev/null 2>&1' >/etc/cron.d/kit-sub-cert
systemctl daemon-reload
systemctl restart kit-sub
sleep 3
if systemctl is-active -q kit-sub; then
say "Подписка kit-sub обновлена"
else
install -m 644 "$tmp/kit_sub.old" /usr/local/lib/kit-sub/kit_sub.py
install -m 644 "$tmp/kit-sub.service.old" /etc/systemd/system/kit-sub.service
rm -f /etc/cron.d/kit-sub-cert
systemctl daemon-reload
systemctl restart kit-sub
warn "Новая kit-sub не запустилась – вернул прежнюю. Лог: journalctl -u kit-sub -n 30"
fi
fi
# Исправления для установок 1.0.
local all uri
all=$(api POST setting/all)
uri=${SUB_BASE:-}
if [[ $uri == https://* && $(jq -r '.subURI // ""' <<<"$all") != "$uri" ]]; then
api POST setting/update "$(jq -c --arg u "$uri" '.subURI = $u' <<<"$all")" >/dev/null
say "Ссылка подписки в панели: $uri…"
fi
# 1.0 ставил cron, который каждый день правил файлы x-ui, – убираем.
rm -f /etc/cron.d/kit-xui-menu
# Через rename: bash дочитывает текущий kit по ходу работы, его файл трогать нельзя.
install -m 755 "$tmp/kit.sh" /usr/local/bin/kit.new && mv -f /usr/local/bin/kit.new /usr/local/bin/kit
echo
echo "${G}✓ Готово: 3X-UI KIT $latest.${N} Пользователи, ссылки и подписки не менялись."
local news
news=$(changelog_of "$latest")
if [[ -n $news ]]; then echo; echo "${B}Что нового в $latest${N}"; echo "$news"; fi
}
usage() {
cat <<EOF
${B}kit${N} — пользователи: один пользователь сразу на всех протоколах
${B}kit${N} $KIT_VERSION – управление сервером 3X-UI KIT
Пользователи (один пользователь сразу на всех протоколах):
kit user add имя [--gb 50] [--days 30] [--devices 3] добавить и показать подписку
kit user list трафик, срок, статус
kit user link имя [--all] подписка и QR; --all — ещё vpn:// и tg://
kit user limit имя [--gb N] [--days N] [--devices N] изменить лимиты (0 — без ограничений)
kit user link имя [--all] подписка и QR; --all – ещё vpn:// и tg://
kit user limit имя [--gb N] [--days N] [--devices N] изменить лимиты (0 – без ограничений)
kit user off имя / kit user on имя выключить и включить
kit user del имя удалить
Сервер:
kit update обновить kit и подписку kit-sub (пользователи и ссылки не меняются)
kit version версия kit, панели и ядра
EOF
}
case "${1:-} ${2:-}" in
"user add") shift 2; cmd_add "$@" ;;
"user list") cmd_list ;;
"user list") cmd_list; update_hint ;;
"user link") shift 2; cmd_link "$@" ;;
"user limit") shift 2; cmd_limit "$@" ;;
"user off") cmd_toggle "${3:-}" false ;;
"user on") cmd_toggle "${3:-}" true ;;
"user del") shift 2; cmd_del "$@" ;;
*) usage ;;
"update "*) cmd_update "${2:-}" ;;
"version "*|"--version "*|"-v "*) cmd_version ;;
*) usage; update_hint ;;
esac