487 lines
25 KiB
Bash
487 lines
25 KiB
Bash
#!/usr/bin/env bash
|
||
# 3X-UI_KIT (форк hacker07) — локальные дополнения.
|
||
#
|
||
# Этот файл наш: автор его не знает и не правит, поэтому обновления автора его не ломают.
|
||
# kit подключает его одной строкой из kit.sh (шов в tools/fork_apply.py) и передаёт сюда
|
||
# аргументы командной строки. Всё, что мы дописываем к kit, живёт ЗДЕСЬ.
|
||
#
|
||
# Команды: kit local <команда> (справка: kit local help)
|
||
#
|
||
# Часть команд повторяет поведение проекта StressOzz/vps-setup (добавить порт SSH, отключить
|
||
# ответ на ping, сменить пароль root). Там нет файла лицензии, поэтому код свой, а поведение
|
||
# сделано безопаснее, чем в источнике:
|
||
# * конфиг sshd проверяется (`sshd -t`) ДО перезапуска, новый порт сначала открывается в ufw;
|
||
# * прежний порт продолжает работать, пока новый не проверен (`--close-old` — отдельный шаг);
|
||
# * у каждой правки есть --dry-run и копия затронутого файла в /root/kit-local-backup-<дата>/;
|
||
# * apt full-upgrade из источника НЕ выполняется (он тянет систему вперёд и может сломать
|
||
# закреплённые версии панели и ядра) — вместо него автообновления безопасности.
|
||
#
|
||
# Правила работы с файлом: только функции, никакого кода на верхнем уровне (он выполняется при
|
||
# каждом запуске kit), и никаких зависимостей от функций автора: наш вызов идёт в самом начале
|
||
# kit.sh, когда авторские say/warn ещё не объявлены.
|
||
|
||
KL_BACKUP_DIR="/root/kit-local-backup-$(date +%Y%m%d)"
|
||
KL_SSHD_CONF="/etc/ssh/sshd_config"
|
||
KL_SSHD_DROPIN="/etc/ssh/sshd_config.d/99-kit-local-port.conf"
|
||
KL_SOCKET_DROPIN="/etc/systemd/system/ssh.socket.d/99-kit-local-port.conf"
|
||
KL_UFW_RULES=/etc/ufw/before.rules
|
||
KL_UFW6_RULES=/etc/ufw/before6.rules
|
||
KL_SYSCTL_DROPIN=/etc/sysctl.d/99-kit-local-icmp.conf
|
||
|
||
kl_say() { printf '\033[1m==>\033[0m %s\n' "$*"; }
|
||
kl_ok() { printf ' ✓ %s\n' "$*"; }
|
||
kl_warn() { printf ' ! %s\n' "$*" >&2; }
|
||
kl_step() { printf '\n\033[1m%s\033[0m\n' "$*"; }
|
||
kl_die() { printf '✗ %s\n' "$*" >&2; return 1; }
|
||
kl_have() { command -v "$1" >/dev/null 2>&1; }
|
||
kl_dry() { [[ ${KL_DRY:-no} == yes ]]; }
|
||
kl_run() { if kl_dry; then printf ' (dry-run) %s\n' "$*"; return 0; fi; "$@"; }
|
||
|
||
# Разбор аргументов: --dry-run поднимаем в KL_DRY, остальное складываем в KL_REST.
|
||
kl_args() {
|
||
KL_DRY=no; KL_REST=()
|
||
local a
|
||
for a in "$@"; do
|
||
case $a in
|
||
--dry-run) KL_DRY=yes ;;
|
||
*) KL_REST+=("$a") ;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
kl_backup() { # файл → копия рядом с релизом, не в /tmp (там её переживёт только до перезагрузки)
|
||
[[ -e $1 ]] || return 0
|
||
local dst="$KL_BACKUP_DIR/$(printf '%s' "$1" | tr '/' '_')"
|
||
if kl_dry; then printf ' (dry-run) сохранил бы %s → %s\n' "$1" "$dst"; return 0; fi
|
||
install -d -m 700 "$KL_BACKUP_DIR"
|
||
cp -p "$1" "$dst" && printf ' ✓ копия: %s\n' "$dst"
|
||
return 0
|
||
}
|
||
|
||
kl_need_root() { [[ ${EUID:-0} -eq 0 ]] || kl_die "нужен root"; }
|
||
|
||
# ------------------------------------------------------------------ справка
|
||
|
||
kit_local_help() {
|
||
cat <<'EOF'
|
||
3X-UI KIT — дополнения форка (hacker07):
|
||
|
||
Состояние
|
||
kit local эта справка
|
||
kit local version версия kit, источник обновлений, дата сборки нашего файла
|
||
kit local channel откуда берутся обновления и чем проверяется подпись
|
||
kit local seams какие места форк правит в файлах автора
|
||
kit local masking состояние маскировки (у REALITY должен быть свой домен)
|
||
kit local hardening сводка по защите сервера и что ещё можно включить
|
||
|
||
Защита сервера (поведение взято из StressOzz/vps-setup, сделано безопаснее)
|
||
kit local ssh 2222 добавить порт SSH (прежние продолжают работать)
|
||
kit local ssh --status какие порты SSH слушаются сейчас
|
||
kit local ssh --close-old 2222 закрыть прежние порты после проверки нового
|
||
kit local ping off|on отключить/вернуть ответ на ICMP echo (ping) через ufw
|
||
kit local ping status состояние ответа на ping
|
||
kit local passwd [юзер] сменить пароль (спросит дважды, в логи не попадает)
|
||
kit local passwd --generate сгенерировать пароль и показать один раз
|
||
kit local upgrades on|off|status автообновления безопасности
|
||
|
||
У любой правки можно добавить --dry-run: покажет план и ничего не изменит.
|
||
Копии изменённых файлов — в /root/kit-local-backup-<дата>/.
|
||
|
||
Оговорки:
|
||
* прежний порт SSH сам не закрывается: сначала проверьте новый из отдельного окна;
|
||
* ping отключаем правилом ufw (видно в `ufw status`), а не общесистемным sysctl: отменяется
|
||
одной командой и не глушит диагностику для внутренних интерфейсов;
|
||
* apt full-upgrade из источника не делаем: вместо него — обновления безопасности.
|
||
EOF
|
||
}
|
||
|
||
kit_local_version() {
|
||
printf 'kit: %s (форк hacker07)\n' "${KIT_VERSION:-?}"
|
||
printf 'источник: %s\n' "${KIT_RAW:-?}"
|
||
local lib=${KIT_LOCAL_LIB:-/usr/local/lib/kit/kit-local.sh}
|
||
printf 'наш файл: %s\n' "$lib"
|
||
[[ -r $lib ]] && printf 'собран: %s\n' "$(date -r "$lib" '+%Y-%m-%d %H:%M' 2>/dev/null || echo '?')"
|
||
local latest
|
||
latest=$(cat /etc/kit/latest-version 2>/dev/null || true)
|
||
[[ -n $latest ]] && printf 'последняя: %s\n' "$latest"
|
||
return 0
|
||
}
|
||
|
||
kit_local_channel() {
|
||
printf 'файлы релиза: %s\n' "${KIT_RAW:-?}"
|
||
printf 'подпись: id=%s namespace=%s\n' "${KIT_SIG_ID:-?}" "${KIT_SIG_NS:-?}"
|
||
local n=0 k
|
||
for k in "${KIT_SIGNERS[@]:-}"; do [[ -n $k ]] && n=$((n + 1)); done
|
||
printf 'ключей в KIT_SIGNERS: %d (должен быть только ключ форка)\n' "$n"
|
||
if [[ $n -eq 0 ]]; then
|
||
printf 'ВНИМАНИЕ: список ключей пуст — обновление не пройдёт проверку подписи.\n'
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
kit_local_seams() {
|
||
local bin=/usr/local/bin/kit
|
||
[[ -r $bin ]] || { printf 'нет %s\n' "$bin"; return 1; }
|
||
grep -n '3X-UI_KIT (форк hacker07)' "$bin" 2>/dev/null \
|
||
|| printf 'метки форка в %s не найдены — сборка не наша?\n' "$bin"
|
||
return 0
|
||
}
|
||
|
||
kit_local_masking() {
|
||
kl_have kit || { printf 'нет команды kit\n'; return 1; }
|
||
kit net site 2>/dev/null || printf 'посмотреть: kit net site\n'
|
||
return 0
|
||
}
|
||
|
||
# ------------------------------------------------------------------ SSH: порты
|
||
|
||
kl_ssh_ports() {
|
||
{ sshd -T 2>/dev/null | awk '$1 == "port" {print $2}'
|
||
kl_have ss && ss -H -ltnp 2>/dev/null | awk '/sshd/ {n = split($4, a, ":"); print a[n]}'
|
||
[[ -n ${SSH_CONNECTION:-} ]] && awk '{print $4}' <<<"$SSH_CONNECTION"
|
||
[[ -n ${SSH_CLIENT:-} ]] && awk '{print $2}' <<<"$SSH_CLIENT"
|
||
} 2>/dev/null | grep -E '^[0-9]+$' | sort -un | tr '\n' ' '
|
||
}
|
||
|
||
kl_ssh_socket_active() { kl_have systemctl && systemctl is-active --quiet ssh.socket 2>/dev/null; }
|
||
|
||
kl_ufw_active() { kl_have ufw && ufw status 2>/dev/null | grep -q '^Status: active'; }
|
||
|
||
kl_ssh_status() {
|
||
local ports; ports=$(kl_ssh_ports)
|
||
printf 'порты SSH сейчас: %s\n' "${ports:-не удалось определить}"
|
||
if kl_ssh_socket_active; then
|
||
printf 'слушает: systemd (ssh.socket — так в Ubuntu 24.04)\n'
|
||
[[ -f $KL_SOCKET_DROPIN ]] && grep -H 'ListenStream' "$KL_SOCKET_DROPIN"
|
||
else
|
||
printf 'слушает: sshd по %s\n' "$KL_SSHD_CONF"
|
||
[[ -f $KL_SSHD_DROPIN ]] && grep -H '^Port' "$KL_SSHD_DROPIN"
|
||
fi
|
||
kl_ufw_active && printf 'ufw: %s\n' "$(ufw status 2>/dev/null | awk '$2 == "ALLOW" && $1 ~ /\/tcp$/ {printf "%s ", $1}')"
|
||
return 0
|
||
}
|
||
|
||
kit_local_ssh() {
|
||
kl_args "$@"
|
||
local arg=${KL_REST[0]:-} second=${KL_REST[1]:-} close_old=no new
|
||
[[ $arg == --close-old ]] && { close_old=yes; new=$second; }
|
||
[[ $second == --close-old ]] && close_old=yes
|
||
|
||
if [[ -z $arg || $arg == --status ]]; then kl_ssh_status; return 0; fi
|
||
kl_need_root || return 1
|
||
[[ $close_old == no ]] && new=$arg
|
||
[[ $new =~ ^[0-9]+$ ]] && ((new >= 1 && new <= 65535)) \
|
||
|| { kl_die "порт — число 1..65535, например: kit local ssh 2222"; return 1; }
|
||
|
||
local cur; cur=$(kl_ssh_ports)
|
||
|
||
if [[ $close_old == yes ]]; then
|
||
kl_say "закрываю прежние порты SSH, оставляю только $new"
|
||
printf ' было: %s\n' "${cur:-?}"
|
||
if [[ -z $cur ]]; then
|
||
kl_warn "не удалось определить текущие порты SSH — закройте прежний порт вручную (файл sshd или ssh.socket)"
|
||
return 1
|
||
fi
|
||
if kl_ssh_socket_active; then
|
||
cat <<EOF
|
||
! SSH слушает systemd (ssh.socket): закрытие старых портов делается правкой
|
||
$KL_SOCKET_DROPIN — первой строкой там должен идти «ListenStream=» (сброс списка),
|
||
затем «ListenStream=$new», после чего: systemctl daemon-reload && systemctl restart ssh.socket.
|
||
Автоматически не делаю: ошибка здесь запирает доступ к серверу.
|
||
EOF
|
||
return 0
|
||
fi
|
||
local p
|
||
for p in $cur; do
|
||
[[ $p == "$new" ]] && continue
|
||
kl_backup "$KL_SSHD_CONF"
|
||
if kl_dry; then
|
||
printf ' (dry-run) закрыл бы порт %s в %s и снял правило ufw %s/tcp\n' "$p" "$KL_SSHD_CONF" "$p"
|
||
continue
|
||
fi
|
||
sed -i -E "s|^[[:space:]]*Port[[:space:]]+$p[[:space:]]*$|# Port $p (закрыт kit local $(date +%F))|" "$KL_SSHD_CONF"
|
||
kl_ufw_active && ufw delete allow "$p/tcp" >/dev/null 2>&1
|
||
kl_ok "порт $p закрыт"
|
||
done
|
||
kl_run sshd -t || { kl_warn "sshd -t не принял конфиг — верните файл из $KL_BACKUP_DIR"; return 1; }
|
||
kl_run systemctl restart ssh
|
||
kl_say "остался только порт $new. Проверьте вход в НОВОМ окне, это не закрывая."
|
||
return 0
|
||
fi
|
||
|
||
kl_say "добавляю порт SSH $new (прежние: ${cur:-?})"
|
||
if [[ " $cur " == *" $new "* ]]; then kl_warn "порт $new уже слушается — ничего не меняю"; return 0; fi
|
||
if kl_have ss && ss -H -ltn 2>/dev/null | awk '{print $4}' | grep -qE "[:.]$new\$"; then
|
||
kl_die "порт $new занят другой службой — выберите другой"; return 1
|
||
fi
|
||
|
||
if kl_ufw_active; then
|
||
kl_run ufw allow "$new/tcp" && kl_ok "ufw: открыт $new/tcp — сделано до перезапуска, чтобы не запереть себя"
|
||
else
|
||
kl_warn "ufw не активен: проверьте, что порт $new открыт у хостера"
|
||
fi
|
||
|
||
if kl_ssh_socket_active; then
|
||
kl_backup "$KL_SOCKET_DROPIN"
|
||
if kl_dry; then
|
||
printf ' (dry-run) записал бы в %s: [Socket] + ListenStream=%s (старый порт остаётся)\n' "$KL_SOCKET_DROPIN" "$new"
|
||
else
|
||
install -d -m 755 "$(dirname "$KL_SOCKET_DROPIN")"
|
||
printf '# 3X-UI KIT (форк): дополнительный порт SSH, добавлен %s\n[Socket]\nListenStream=%s\n' "$(date +%F)" "$new" >"$KL_SOCKET_DROPIN"
|
||
kl_ok "добавлен $KL_SOCKET_DROPIN"
|
||
fi
|
||
kl_run systemctl daemon-reload
|
||
kl_run systemctl restart ssh.socket
|
||
else
|
||
kl_backup "$KL_SSHD_DROPIN"
|
||
if kl_dry; then
|
||
printf ' (dry-run) записал бы в %s: Port %s (старый порт остаётся)\n' "$KL_SSHD_DROPIN" "$new"
|
||
else
|
||
install -d -m 755 /etc/ssh/sshd_config.d
|
||
printf '# 3X-UI KIT (форк): дополнительный порт SSH, добавлен %s\nPort %s\n' "$(date +%F)" "$new" >"$KL_SSHD_DROPIN"
|
||
kl_ok "добавлен $KL_SSHD_DROPIN"
|
||
fi
|
||
kl_run sshd -t || { kl_warn "sshd -t не принял конфиг — убираю наш файл, ничего не перезапускаю"; kl_dry || rm -f "$KL_SSHD_DROPIN"; return 1; }
|
||
kl_run systemctl restart ssh || kl_run systemctl restart sshd
|
||
fi
|
||
|
||
if ! kl_dry; then
|
||
sleep 2
|
||
if ss -H -ltn 2>/dev/null | awk '{print $4}' | grep -qE "[:.]$new\$"; then kl_ok "sshd слушает $new"
|
||
else kl_warn "порт $new пока не слушается — смотрите: journalctl -u ssh -n 30"; fi
|
||
fi
|
||
cat <<EOF
|
||
|
||
Дальше обязательно:
|
||
1) во ВТОРОМ окне проверьте вход: ssh -p $new root@<IP сервера>
|
||
2) заработало — закройте прежние порты: kit local ssh --close-old $new
|
||
3) поменяйте порт у себя в ~/.ssh/config и в мониторинге, если он там есть
|
||
EOF
|
||
return 0
|
||
}
|
||
|
||
# ------------------------------------------------------------------ ping (ICMP echo)
|
||
|
||
kl_ping_state() {
|
||
if kl_have ufw && [[ -f $KL_UFW_RULES ]]; then
|
||
grep -qE '^-A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT' "$KL_UFW_RULES" && echo allow || echo deny
|
||
elif [[ -f $KL_SYSCTL_DROPIN ]]; then
|
||
grep -q 'icmp_echo_ignore_all = 1' "$KL_SYSCTL_DROPIN" && echo deny || echo allow
|
||
else
|
||
echo "?"
|
||
fi
|
||
}
|
||
|
||
kit_local_ping() {
|
||
kl_args "$@"
|
||
local want=${KL_REST[0]:-status}
|
||
case $want in
|
||
status | "")
|
||
printf 'ответ на ping: %s\n' "$(kl_ping_state)"
|
||
if kl_have ufw && [[ -f $KL_UFW_RULES ]]; then
|
||
printf ' ufw before.rules: echo-request %s\n' "$(grep -qE '^# -A ufw-before-input -p icmp --icmp-type echo-request' "$KL_UFW_RULES" && echo 'закрыт (строка закомментирована)' || echo 'открыт')"
|
||
[[ -f $KL_UFW6_RULES ]] && printf ' ufw before6.rules: echo-request %s\n' "$(grep -qE '^# -A ufw6-before-input' "$KL_UFW6_RULES" && echo 'закрыт' || echo 'открыт')"
|
||
fi
|
||
printf ' sysctl icmp_echo_ignore_all=%s\n' "$(sysctl -n net.ipv4.icmp_echo_ignore_all 2>/dev/null || echo '?')"
|
||
return 0 ;;
|
||
off | on) ;;
|
||
*) kl_die "kit local ping off|on|status"; return 1 ;;
|
||
esac
|
||
kl_need_root || return 1
|
||
|
||
if kl_ufw_active; then
|
||
kl_backup "$KL_UFW_RULES"; kl_backup "$KL_UFW6_RULES"
|
||
if kl_dry; then
|
||
printf ' (dry-run) %s в %s и before6.rules правило «icmp echo-request», затем ufw reload\n' \
|
||
"$([[ $want == off ]] && echo 'закомментировал бы' || echo 'вернул бы')" "$KL_UFW_RULES"
|
||
return 0
|
||
fi
|
||
local f
|
||
for f in "$KL_UFW_RULES" "$KL_UFW6_RULES"; do
|
||
[[ -f $f ]] || continue
|
||
if [[ $want == off ]]; then
|
||
sed -i -E 's|^(-A ufw6?-before-input -p icmp6? --icmp6?-type echo-request -j ACCEPT)|# \1 # kit local: закрыт|' "$f"
|
||
else
|
||
sed -i -E 's|^# (-A ufw6?-before-input -p icmp6? --icmp6?-type echo-request -j ACCEPT).*$|\1|' "$f"
|
||
fi
|
||
done
|
||
ufw reload >/dev/null 2>&1 || kl_warn "ufw reload не прошёл — проверьте: ufw status"
|
||
rm -f "$KL_SYSCTL_DROPIN"
|
||
if [[ $want == off ]]; then
|
||
kl_ok "сервер больше не отвечает на ping"
|
||
kl_warn "ICMP-мониторинг этого сервера из дома работать перестанет (проба «ping» даст сбой)"
|
||
kl_warn "вернуть: kit local ping on"
|
||
else
|
||
kl_ok "сервер снова отвечает на ping"
|
||
fi
|
||
return 0
|
||
fi
|
||
|
||
kl_warn "ufw не активен — использую общесистемный запрет ICMP echo (это шире: глушит ping и для внутренних интерфейсов)"
|
||
if kl_dry; then
|
||
printf ' (dry-run) записал бы %s с icmp_echo_ignore_all=%s\n' "$KL_SYSCTL_DROPIN" "$([[ $want == off ]] && echo 1 || echo 0)"
|
||
return 0
|
||
fi
|
||
printf 'net.ipv4.icmp_echo_ignore_all = %s\n' "$([[ $want == off ]] && echo 1 || echo 0)" >"$KL_SYSCTL_DROPIN"
|
||
sysctl -p "$KL_SYSCTL_DROPIN" >/dev/null 2>&1
|
||
kl_ok "готово (файл $KL_SYSCTL_DROPIN); вернуть: kit local ping on"
|
||
return 0
|
||
}
|
||
|
||
# ------------------------------------------------------------------ пароль
|
||
|
||
kit_local_passwd() {
|
||
kl_args "$@"
|
||
local user=${KL_REST[0]:-root} gen=no
|
||
[[ $user == --generate ]] && { user=root; gen=yes; }
|
||
[[ ${KL_REST[1]:-} == --generate ]] && gen=yes
|
||
kl_need_root || return 1
|
||
id "$user" >/dev/null 2>&1 || { kl_die "нет пользователя $user"; return 1; }
|
||
|
||
if [[ $gen == yes ]]; then
|
||
if kl_dry; then printf ' (dry-run) сгенерировал бы пароль пользователю %s и показал бы один раз\n' "$user"; return 0; fi
|
||
local pw; pw=$(openssl rand -base64 24 | tr -d '/+=' | cut -c1-20)
|
||
printf '%s:%s\n' "$user" "$pw" | chpasswd || { kl_die "chpasswd не сработал"; return 1; }
|
||
cat <<EOF
|
||
|
||
Пароль пользователя $user сменён. Показываю ОДИН раз:
|
||
|
||
$pw
|
||
|
||
Перенесите в CREDENTIALS.md и очистите прокрутку терминала (clear).
|
||
EOF
|
||
else
|
||
kl_say "меняю пароль $user — спросит дважды, в историю команд не попадёт"
|
||
if kl_dry; then printf ' (dry-run) запустил бы passwd %s\n' "$user"; return 0; fi
|
||
passwd "$user" || return 1
|
||
fi
|
||
kl_dry || passwd -S "$user" 2>/dev/null
|
||
sshd -T 2>/dev/null | grep -qi '^passwordauthentication no' \
|
||
&& kl_warn "в sshd вход по паролю выключен — пароль пригодится только для консоли хостера"
|
||
return 0
|
||
}
|
||
|
||
# ------------------------------------------------------------------ автообновления безопасности
|
||
|
||
kit_local_upgrades_status() {
|
||
printf 'автообновления безопасности: %s\n' "$([[ -f /etc/apt/apt.conf.d/20auto-upgrades ]] && echo включены || echo выключены)"
|
||
kl_have systemctl && printf ' служба unattended-upgrades: %s\n' "$(systemctl is-active unattended-upgrades 2>/dev/null || echo 'нет')"
|
||
[[ -f /etc/apt/apt.conf.d/20auto-upgrades ]] && sed -n '1,4p' /etc/apt/apt.conf.d/20auto-upgrades | sed 's/^/ /'
|
||
if [[ -f /var/log/unattended-upgrades/unattended-upgrades.log ]]; then
|
||
printf ' последние запуски:\n'
|
||
tail -n 3 /var/log/unattended-upgrades/unattended-upgrades.log | cut -c1-110 | sed 's/^/ /'
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
kit_local_upgrades() {
|
||
kl_args "$@"
|
||
local want=${KL_REST[0]:-status}
|
||
case $want in
|
||
status | "") kit_local_upgrades_status; return 0 ;;
|
||
on | off) ;;
|
||
*) kl_die "kit local upgrades on|off|status"; return 1 ;;
|
||
esac
|
||
kl_need_root || return 1
|
||
|
||
if [[ $want == off ]]; then
|
||
kl_say "выключаю автообновления безопасности"
|
||
kl_run rm -f /etc/apt/apt.conf.d/20auto-upgrades /etc/apt/apt.conf.d/52-kit-local-upgrades
|
||
kl_run systemctl disable --now unattended-upgrades
|
||
kl_ok "готово (пакет оставлен)"
|
||
return 0
|
||
fi
|
||
|
||
kl_say "включаю автообновления безопасности (только security, без перезагрузки)"
|
||
if ! kl_dry && ! dpkg -s unattended-upgrades >/dev/null 2>&1; then
|
||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq unattended-upgrades \
|
||
|| { kl_die "пакет не поставился"; return 1; }
|
||
fi
|
||
if kl_dry; then
|
||
printf ' (dry-run) записал бы /etc/apt/apt.conf.d/20auto-upgrades и 52-kit-local-upgrades, включил бы службу\n'
|
||
return 0
|
||
fi
|
||
cat >/etc/apt/apt.conf.d/20auto-upgrades <<'EOF'
|
||
APT::Periodic::Update-Package-Lists "1";
|
||
APT::Periodic::Unattended-Upgrade "1";
|
||
APT::Periodic::Download-Upgradeable-Packages "1";
|
||
APT::Periodic::AutocleanInterval "7";
|
||
EOF
|
||
cat >/etc/apt/apt.conf.d/52-kit-local-upgrades <<'EOF'
|
||
// 3X-UI KIT (форк): только обновления безопасности, никакой самовольной перезагрузки.
|
||
Unattended-Upgrade::Automatic-Reboot "false";
|
||
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
|
||
EOF
|
||
systemctl enable --now unattended-upgrades >/dev/null 2>&1 || kl_warn "служба не включилась — проверьте: systemctl status unattended-upgrades"
|
||
kl_ok "включено: обновления безопасности ставятся сами"
|
||
kl_ok "панель и ядро это не затрагивает — они ставятся не из apt, а из своих релизов"
|
||
kl_warn "обновление ядра применится только после перезагрузки, сам сервер не перезагружается"
|
||
return 0
|
||
}
|
||
|
||
# ------------------------------------------------------------------ сводка
|
||
|
||
kit_local_hardening() {
|
||
kl_args "$@"
|
||
local ports; ports=$(kl_ssh_ports)
|
||
kl_step "Защита сервера — состояние"
|
||
printf 'порты SSH: %s\n' "${ports:-?}$(kl_ssh_socket_active && echo ' — через systemd ssh.socket')"
|
||
printf 'ответ на ping: %s\n' "$(kl_ping_state)"
|
||
printf 'ufw: %s\n' "$(kl_ufw_active && echo 'активен' || echo 'не активен')"
|
||
kl_ufw_active && printf 'открытые порты: %s\n' "$(ufw status 2>/dev/null | awk '$2 == "ALLOW" {printf "%s ", $1}')"
|
||
printf 'автообновления: %s\n' "$([[ -f /etc/apt/apt.conf.d/20auto-upgrades ]] && echo включены || echo выключены)"
|
||
printf 'вход по паролю в sshd: %s\n' "$(sshd -T 2>/dev/null | awk '/^passwordauthentication/ {print $2}' || echo '?')"
|
||
printf 'смена пароля root: %s\n' "$(chage -l root 2>/dev/null | awk -F': ' '/Last password change/ {print $2}')"
|
||
printf 'продление сертификата: %s\n' "$(crontab -l 2>/dev/null | grep -c acme) задач acme в cron"
|
||
|
||
cat <<'EOF'
|
||
|
||
Чего здесь НЕТ и почему (в отличие от vps-setup):
|
||
* apt full-upgrade — тянет систему вперёд и может сломать закреплённые панель и ядро;
|
||
вместо него включены обновления безопасности;
|
||
* автоматическая перезагрузка после правок — сервер не должен вставать без вашего решения;
|
||
* общесистемный icmp_echo_ignore_all — правило ufw видно в `ufw status` и отменяется одной
|
||
командой, не глушит диагностику внутренних интерфейсов.
|
||
|
||
Что включить при желании:
|
||
kit local ssh 2222 добавить порт SSH
|
||
kit local ping off не отвечать на ping
|
||
kit local upgrades on обновления безопасности
|
||
kit local passwd --generate сменить пароль root
|
||
EOF
|
||
return 0
|
||
}
|
||
|
||
kit_local_cmd() {
|
||
case ${1:-help} in
|
||
help | -h | --help) kit_local_help ;;
|
||
version | -v) kit_local_version ;;
|
||
channel) kit_local_channel ;;
|
||
seams) kit_local_seams ;;
|
||
masking | mask) kit_local_masking ;;
|
||
ssh | ssh-port) shift; kit_local_ssh "$@" ;;
|
||
ping | icmp) shift; kit_local_ping "$@" ;;
|
||
passwd | pass) shift; kit_local_passwd "$@" ;;
|
||
upgrades | auto) shift; kit_local_upgrades "$@" ;;
|
||
hardening | hard) shift; kit_local_hardening "$@" ;;
|
||
*)
|
||
printf 'Неизвестная команда форка: %s\n' "$1" >&2
|
||
kit_local_help >&2
|
||
return 2
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# Точка входа: вызывается из kit.sh с аргументами командной строки.
|
||
# Если команда не наша — возвращаем 0, и kit работает ровно как раньше.
|
||
kit_local_dispatch() {
|
||
case ${1:-} in
|
||
local)
|
||
shift
|
||
kit_local_cmd "$@"
|
||
exit $?
|
||
;;
|
||
*) return 0 ;;
|
||
esac
|
||
}
|