From 8dc95a9964d1df4b5243782c3b388919d9acfde0 Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Wed, 15 Jul 2026 11:58:08 +0700 Subject: [PATCH 01/12] Add optional Russian locale and safe reset script (#1) --- .github/workflows/bash-syntax.yml | 24 +++ README.md | 26 ++- ochenstarik-server-1.sh | 80 +++++++ ochenstarik-server-uninstall.sh | 342 ++++++++++++++++++++++++++++++ 4 files changed, 470 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/bash-syntax.yml create mode 100644 ochenstarik-server-uninstall.sh diff --git a/.github/workflows/bash-syntax.yml b/.github/workflows/bash-syntax.yml new file mode 100644 index 0000000..70064e2 --- /dev/null +++ b/.github/workflows/bash-syntax.yml @@ -0,0 +1,24 @@ +name: Bash syntax + +on: + push: + branches: ['**'] + pull_request: + +permissions: + contents: read + +jobs: + syntax: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Check every installer + run: | + set -e + for script in ./*.sh; do + echo "Checking ${script}" + bash -n "${script}" + done diff --git a/README.md b/README.md index 3bf9567..b4ef7fe 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,14 @@ | Файл | Назначение | Можно запускать отдельно | | --- | --- | --- | -| `ochenstarik-server-1.sh` | Предлагает выбрать часовой пояс, создаёт swap-файл 2 ГБ и задаёт `vm.swappiness=20` | Да | +| `ochenstarik-server-1.sh` | Предлагает выбрать часовой пояс и русификацию терминала, создаёт swap-файл 2 ГБ и задаёт `vm.swappiness=20` | Да | | `ochenstarik-server-2.sh` | Предлагает выбрать будущий SSH-порт и режим IPv4/IPv6, обновляет систему, ставит пакеты и настраивает UFW | Да | | `ochenstarik-server-user-3.sh` | Создаёт администратора, переносит SSH на порт из этапа 2, проверяет права и включает fail2ban | После установки необходимых пакетов; проще всего сначала запустить скрипт 2 | | `ochenstarik-server-tg-4.sh` | Отправляет уведомления в Telegram при успешном входе по SSH | После установки OpenSSH и `curl`; обычно после скрипта 2 или 3 | | `ochenstarik-server-vpn-5.sh` | Устанавливает Xray и направляет системный трафик через VLESS + REALITY | Да; зависимости устанавливаются автоматически | | `ochenstarik-server-panel-warp-6.sh` | Устанавливает 3x-ui и Cloudflare WARP, настраивает и проверяет их порты | Ubuntu 22.04/24.04/26.04 или Debian 12/13 | | `ochenstarik-server-backup-7.sh` | Создаёт неизменяемый первичный снимок и настраивает выбранные расписания резервного копирования | Да; зависимости устанавливаются автоматически | +| `ochenstarik-server-uninstall.sh` | Удаляет настройки, службы и данные проекта для повторной установки с начала | Да; запускать только из сохранённой SSH-сессии | Файлы `*.env.example` являются только примерами. Не записывайте настоящие пароли, токены и приватные SSH-ключи в Git. @@ -69,7 +70,11 @@ bash -n ./ИМЯ-СКРИПТА.sh Файл: `ochenstarik-server-1.sh`. -Сначала показывает нумерованное меню популярных часовых поясов, включая `Asia/Novosibirsk`, `Europe/Moscow` и `UTC`. Нужную зону достаточно выбрать по номеру. Для остальных зон предусмотрен полный список с последовательным выбором региона и часового пояса — вводить имя вручную не требуется. Затем скрипт создаёт `/swapfile` размером 2 ГБ, добавляет его в `/etc/fstab` и устанавливает `vm.swappiness=20`. Повторный запуск предусмотрен. +Сначала показывает нумерованное меню популярных часовых поясов, включая `Asia/Novosibirsk`, `Europe/Moscow` и `UTC`. Нужную зону достаточно выбрать по номеру. Для остальных зон предусмотрен полный список с последовательным выбором региона и часового пояса — вводить имя вручную не требуется. + +После выбора времени скрипт спрашивает, нужна ли русификация терминала. При согласии он устанавливает доступные пакеты русской локали, создаёт `ru_RU.UTF-8` и задаёт русский язык для новых терминальных сессий. Прежний `/etc/default/locale` и список только что установленных языковых пакетов сохраняются в `/etc/ochenstarik-server`, поэтому отдельный скрипт очистки может вернуть исходную локаль. Без явного согласия языковые настройки не меняются. + +Затем скрипт создаёт `/swapfile` размером 2 ГБ, добавляет его в `/etc/fstab` и устанавливает `vm.swappiness=20`. Повторный запуск предусмотрен. ```bash curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-1.sh @@ -321,6 +326,23 @@ sudo ./ochenstarik-server-backup-7.sh VPN, 3x-ui, WARP, Telegram-уведомления и автоматические расписания резервного копирования являются необязательными этапами. +## Удаление и повторная установка с начала + +Файл: `ochenstarik-server-uninstall.sh`. + +Скрипт удаляет управляемые правила UFW, настройки IPv4/IPv6, SSH drop-in, fail2ban-конфигурацию, Telegram-hook, Xray, 3x-ui, WARP, расписания резервного копирования, swap и служебные файлы проекта. Перед изменением SSH открывается порт `22/tcp`. Правило порта, через который работает текущая SSH-сессия, сохраняется, чтобы не потерять доступ. + +Удаление администратора и самих архивов резервных копий требует отдельных подтверждений. Общие системные пакеты, обновления Ubuntu и программы вроде OpenSSH, UFW и `curl` не удаляются: они могли существовать до запуска проекта и необходимы серверу. Пакеты русификации удаляются только если первый скрипт зафиксировал, что до русификации их не было. + +```bash +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-uninstall.sh +chmod 700 ochenstarik-server-uninstall.sh +bash -n ochenstarik-server-uninstall.sh +sudo ./ochenstarik-server-uninstall.sh +``` + +Не закрывайте текущую SSH-сессию. После очистки сначала проверьте отдельное подключение через порт 22, затем снова запускайте нужные установочные скрипты. Старое правило текущего SSH-порта можно удалить только после успешной проверки нового входа. + ## Безопасность - Всегда проверяйте скачанные скрипты перед запуском от `root`. diff --git a/ochenstarik-server-1.sh b/ochenstarik-server-1.sh index 606b72b..b7db1b0 100644 --- a/ochenstarik-server-1.sh +++ b/ochenstarik-server-1.sh @@ -5,11 +5,90 @@ readonly SWAPFILE="/swapfile" readonly SWAPSIZE="2G" readonly SWAPPINESS="20" readonly SYSCTL_FILE="/etc/sysctl.d/60-hermes-swap.conf" +readonly CONFIG_DIR="/etc/ochenstarik-server" +readonly LOCALE_BACKUP="${CONFIG_DIR}/locale-before-russian.conf" +readonly LOCALE_ABSENT_MARKER="${CONFIG_DIR}/locale-before-russian.absent" +readonly LOCALE_PACKAGES="${CONFIG_DIR}/russian-locale-installed-packages.list" log() { printf '[+] %s\n' "$*"; } warn() { printf '[!] %s\n' "$*" >&2; } die() { printf '[x] %s\n' "$*" >&2; exit 1; } +ask_yes_no() { + local prompt="$1" default_answer="${2:-no}" answer suffix + [[ "$default_answer" == yes || "$default_answer" == no ]] \ + || die "Invalid default answer: $default_answer" + [[ "$default_answer" == yes ]] && suffix='[Y/n]' || suffix='[y/N]' + + while :; do + read -rp "$prompt $suffix " answer || die "Input was interrupted" + answer="${answer:-$default_answer}" + case "${answer,,}" in + y|yes|д|да) return 0 ;; + n|no|н|нет) return 1 ;; + *) warn "Ответьте yes/no или да/нет" ;; + esac + done +} + +package_is_installed() { + dpkg-query -W -f='${Status}' "$1" 2>/dev/null | grep -Fqx 'install ok installed' +} + +install_russian_terminal_locale() { + local package_name + local -a packages=(locales) newly_installed=() + + ask_yes_no "Установить русскую локаль терминала ru_RU.UTF-8?" no || { + log "Русификация терминала пропущена" + return 0 + } + + for package_name in apt-get apt-cache dpkg-query; do + command -v "$package_name" >/dev/null 2>&1 \ + || die "Required command not found: $package_name" + done + + log "Обновление списка пакетов для русификации" + apt-get update + + install -d -m 700 -o root -g root "$CONFIG_DIR" + if [[ ! -e "$LOCALE_BACKUP" && ! -e "$LOCALE_ABSENT_MARKER" ]]; then + if [[ -f /etc/default/locale && ! -L /etc/default/locale ]]; then + cp -a -- /etc/default/locale "$LOCALE_BACKUP" + chmod 600 "$LOCALE_BACKUP" + else + : > "$LOCALE_ABSENT_MARKER" + chmod 600 "$LOCALE_ABSENT_MARKER" + fi + fi + + for package_name in language-pack-ru manpages-ru; do + if apt-cache show "$package_name" >/dev/null 2>&1; then + packages+=("$package_name") + fi + done + for package_name in "${packages[@]}"; do + package_is_installed "$package_name" || newly_installed+=("$package_name") + done + + log "Установка пакетов русской локали" + export DEBIAN_FRONTEND=noninteractive + apt-get install -y "${packages[@]}" + locale-gen ru_RU.UTF-8 + update-locale LANG=ru_RU.UTF-8 LANGUAGE=ru_RU:ru LC_MESSAGES=ru_RU.UTF-8 + + touch "$LOCALE_PACKAGES" + for package_name in "${newly_installed[@]}"; do + grep -Fqx -- "$package_name" "$LOCALE_PACKAGES" \ + || printf '%s\n' "$package_name" >> "$LOCALE_PACKAGES" + done + chown root:root "$LOCALE_PACKAGES" + chmod 600 "$LOCALE_PACKAGES" + + log "Русская локаль включена. Она применится при следующем входе в терминал" +} + choose_numbered_option() { local prompt="$1" result_variable="$2" answer index shift 2 @@ -86,6 +165,7 @@ for command_name in awk blkid fallocate grep mkswap swapon sysctl timedatectl; d done choose_timezone +install_russian_terminal_locale log "Configuring ${SWAPSIZE} swap file" if [[ ! -e "$SWAPFILE" ]]; then diff --git a/ochenstarik-server-uninstall.sh b/ochenstarik-server-uninstall.sh new file mode 100644 index 0000000..5057d6c --- /dev/null +++ b/ochenstarik-server-uninstall.sh @@ -0,0 +1,342 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +readonly CONFIG_DIR="/etc/ochenstarik-server" +readonly MANAGED_PORTS_CONFIG="${CONFIG_DIR}/ufw-managed-ports.conf" +readonly IP_FAMILY_CONFIG="${CONFIG_DIR}/ip-family.conf" +readonly SSH_PORT_CONFIG="${CONFIG_DIR}/ssh-port.conf" +readonly IPV6_SYSCTL_FILE="/etc/sysctl.d/99-zz-ochenstarik-disable-ipv6.conf" +readonly SWAPFILE="/swapfile" +readonly SWAP_SYSCTL_FILE="/etc/sysctl.d/60-hermes-swap.conf" +readonly SSHD_DROPIN="/etc/ssh/sshd_config.d/00-hermes-hardening.conf" +readonly FAIL2BAN_JAIL="/etc/fail2ban/jail.d/hermes.local" +readonly SETUP_ENV="/root/setup-data/env.txt" +readonly SETUP_HASH="/root/setup-data/password.hash" +readonly TELEGRAM_CONFIG="${CONFIG_DIR}/telegram.conf" +readonly TELEGRAM_SCRIPT="/usr/local/libexec/ochenstarik-ssh-login-telegram.sh" +readonly TELEGRAM_LEGACY_SCRIPT="/usr/local/libexec/ssh-login-telegram.sh" +readonly TELEGRAM_LOG="/var/log/ochenstarik-ssh-login-telegram.log" +readonly TELEGRAM_LOGROTATE="/etc/logrotate.d/ochenstarik-ssh-login-telegram" +readonly PAM_SSHD="/etc/pam.d/sshd" +readonly XRAY_STATE_DIR="/etc/ochenstarik-xray" +readonly XRAY_CONFIG_DIR="/usr/local/etc/xray" +readonly XRAY_SHARE_DIR="/usr/local/share/xray" +readonly XRAY_LOG_DIR="/var/log/xray" +readonly XRAY_HELPER="/usr/local/sbin/ochenstarik-xray-routing" +readonly XRAY_SERVICE="/etc/systemd/system/ochenstarik-xray-routing.service" +readonly BACKUP_CONFIG="${CONFIG_DIR}/backup.conf" +readonly BACKUP_RUNNER="/usr/local/sbin/ochenstarik-server-backup" +readonly LOCALE_BACKUP="${CONFIG_DIR}/locale-before-russian.conf" +readonly LOCALE_ABSENT_MARKER="${CONFIG_DIR}/locale-before-russian.absent" +readonly LOCALE_PACKAGES="${CONFIG_DIR}/russian-locale-installed-packages.list" + +log() { printf '[+] %s\n' "$*"; } +warn() { printf '[!] %s\n' "$*" >&2; } +die() { printf '[x] %s\n' "$*" >&2; exit 1; } + +ask_yes_no() { + local prompt="$1" default_answer="${2:-no}" answer suffix + [[ "$default_answer" == yes || "$default_answer" == no ]] \ + || die "Некорректный ответ по умолчанию" + [[ "$default_answer" == yes ]] && suffix='[Y/n]' || suffix='[y/N]' + while :; do + read -rp "$prompt $suffix " answer || die "Ввод прерван" + answer="${answer:-$default_answer}" + case "${answer,,}" in + y|yes|д|да) return 0 ;; + n|no|н|нет) return 1 ;; + *) warn "Ответьте yes/no или да/нет" ;; + esac + done +} + +service_exists() { + command -v systemctl >/dev/null 2>&1 && systemctl cat "$1" >/dev/null 2>&1 +} + +stop_disable_service() { + local unit="$1" + service_exists "$unit" || return 0 + systemctl disable --now "$unit" >/dev/null 2>&1 || true + systemctl reset-failed "$unit" >/dev/null 2>&1 || true +} + +is_valid_port() { + [[ "$1" =~ ^[0-9]{1,5}$ ]] && ((10#$1 >= 1 && 10#$1 <= 65535)) +} + +current_ssh_port() { + local port="" + if [[ -n "${SSH_CONNECTION:-}" ]]; then + port="${SSH_CONNECTION##* }" + is_valid_port "$port" && printf '%s' "$((10#$port))" + fi +} + +remove_ufw_rules() { + local rule port protocol active_port + command -v ufw >/dev/null 2>&1 || return 0 + active_port="$(current_ssh_port)" + + if LANG=C ufw status 2>/dev/null | grep -q '^Status: active'; then + ufw allow 22/tcp >/dev/null + log "Порт 22/tcp открыт перед сбросом SSH" + fi + + [[ -f "$MANAGED_PORTS_CONFIG" && ! -L "$MANAGED_PORTS_CONFIG" ]] || return 0 + while IFS= read -r rule || [[ -n "$rule" ]]; do + [[ "$rule" =~ ^([0-9]{1,5})/(tcp|udp)$ ]] || continue + port="$((10#${BASH_REMATCH[1]}))" + protocol="${BASH_REMATCH[2]}" + if [[ "$protocol" == tcp && "$port" == 22 ]]; then + continue + fi + if [[ -n "$active_port" && "$protocol" == tcp && "$port" == "$active_port" ]]; then + warn "Правило текущего SSH-порта ${port}/tcp оставлено для защиты соединения" + continue + fi + while ufw --force delete allow from 0.0.0.0/0 to any port "$port" proto "$protocol" \ + >/dev/null 2>&1; do :; done + while ufw --force delete allow from ::/0 to any port "$port" proto "$protocol" \ + >/dev/null 2>&1; do :; done + while ufw --force delete allow "${port}/${protocol}" >/dev/null 2>&1; do :; done + done < "$MANAGED_PORTS_CONFIG" + ufw reload >/dev/null 2>&1 || true + log "Управляемые правила UFW удалены" +} + +restore_ipv6_defaults() { + rm -f -- "$IPV6_SYSCTL_FILE" + if command -v sysctl >/dev/null 2>&1; then + sysctl -w net.ipv6.conf.all.disable_ipv6=0 >/dev/null 2>&1 || true + sysctl -w net.ipv6.conf.default.disable_ipv6=0 >/dev/null 2>&1 || true + fi + if [[ -f /etc/default/ufw && ! -L /etc/default/ufw ]]; then + if grep -q '^IPV6=' /etc/default/ufw; then + sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw + else + printf '\nIPV6=yes\n' >> /etc/default/ufw + fi + fi + command -v ufw >/dev/null 2>&1 && ufw reload >/dev/null 2>&1 || true +} + +reset_ssh_and_user() { + local ssh_service="" configured_user="" login_user="${SUDO_USER:-}" + + if [[ -f "$SETUP_ENV" && ! -L "$SETUP_ENV" ]]; then + configured_user="$(sed -n 's/^NEW_USERNAME=//p' "$SETUP_ENV" | head -n1 | tr -d '\r')" + fi + + rm -f -- "$SSHD_DROPIN" "$FAIL2BAN_JAIL" + if command -v sshd >/dev/null 2>&1; then + sshd -t || die "После удаления drop-in конфигурация SSH не прошла проверку" + fi + for ssh_service in ssh sshd; do + if service_exists "$ssh_service"; then + systemctl restart "$ssh_service" + log "SSH перезапущен; проверьте новый вход через порт 22" + break + fi + done + service_exists fail2ban && systemctl restart fail2ban >/dev/null 2>&1 || true + + if [[ -n "$configured_user" && "$configured_user" != root ]] && id "$configured_user" >/dev/null 2>&1; then + rm -f -- "/etc/sudoers.d/${configured_user}" + if ask_yes_no "Удалить созданного администратора ${configured_user} вместе с домашней папкой?" no; then + if [[ -n "$login_user" && "$configured_user" == "$login_user" ]]; then + warn "Нельзя удалить пользователя текущей sudo/SSH-сессии: ${configured_user}" + else + read -rp "Для удаления введите имя пользователя ${configured_user}: " confirmation + if [[ "$confirmation" == "$configured_user" ]]; then + userdel --remove "$configured_user" + log "Пользователь ${configured_user} удалён" + else + warn "Имя не совпало; пользователь оставлен" + fi + fi + else + warn "Пользователь ${configured_user} и его членство в группах оставлены" + fi + fi + + rm -f -- "$SETUP_ENV" "$SETUP_HASH" + rmdir /root/setup-data >/dev/null 2>&1 || true +} + +remove_telegram() { + if [[ -f "$PAM_SSHD" && ! -L "$PAM_SSHD" ]]; then + cp -a -- "$PAM_SSHD" "${PAM_SSHD}.bak.before-ochenstarik-uninstall.$(date +%F-%H%M%S)" + sed -i "\|${TELEGRAM_SCRIPT}|d;\|${TELEGRAM_LEGACY_SCRIPT}|d" "$PAM_SSHD" + fi + rm -f -- "$TELEGRAM_CONFIG" "$TELEGRAM_SCRIPT" "$TELEGRAM_LEGACY_SCRIPT" \ + "$TELEGRAM_LOG" "$TELEGRAM_LOGROTATE" + find "$CONFIG_DIR" -maxdepth 1 -type f -name 'telegram.conf.bak.*' -delete 2>/dev/null || true + command -v sshd >/dev/null 2>&1 && sshd -t \ + || warn "Не удалось проверить SSH после удаления Telegram PAM-hook" +} + +remove_xray() { + stop_disable_service ochenstarik-xray-rollback.timer + stop_disable_service ochenstarik-xray-rollback.service + stop_disable_service ochenstarik-xray-routing.service + [[ ! -x "$XRAY_HELPER" ]] || "$XRAY_HELPER" stop >/dev/null 2>&1 || true + if command -v nft >/dev/null 2>&1; then + nft list table ip ochenstarik_xray >/dev/null 2>&1 \ + && nft delete table ip ochenstarik_xray || true + nft list table ip6 ochenstarik_xray6 >/dev/null 2>&1 \ + && nft delete table ip6 ochenstarik_xray6 || true + fi + stop_disable_service xray.service + stop_disable_service xray@.service + rm -f -- "$XRAY_HELPER" "$XRAY_SERVICE" /etc/systemd/system/xray.service \ + /etc/systemd/system/xray@.service /usr/local/bin/xray + rm -rf -- "$XRAY_STATE_DIR" "$XRAY_CONFIG_DIR" "$XRAY_SHARE_DIR" "$XRAY_LOG_DIR" +} + +remove_panel_and_warp() { + stop_disable_service x-ui.service + if command -v warp-cli >/dev/null 2>&1; then + warp-cli --accept-tos disconnect >/dev/null 2>&1 || true + warp-cli --accept-tos registration delete >/dev/null 2>&1 || true + fi + stop_disable_service warp-svc.service + + rm -f -- /etc/systemd/system/x-ui.service /usr/bin/x-ui /usr/local/bin/x-ui + rm -rf -- /usr/local/x-ui /etc/x-ui + + if command -v apt-get >/dev/null 2>&1 && dpkg-query -W -f='${Status}' cloudflare-warp \ + 2>/dev/null | grep -Fqx 'install ok installed'; then + DEBIAN_FRONTEND=noninteractive apt-get purge -y cloudflare-warp + fi + rm -f -- /etc/apt/sources.list.d/cloudflare-client.list \ + /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg +} + +read_backup_root() { + local value="/var/backups/ochenstarik-server" + if [[ -f "$BACKUP_CONFIG" && ! -L "$BACKUP_CONFIG" ]]; then + [[ "$(stat -c '%u' "$BACKUP_CONFIG")" == 0 ]] \ + || die "Файл $BACKUP_CONFIG должен принадлежать root" + value="$(bash -c 'set -efu; source "$1"; printf "%s" "$BACKUP_ROOT"' \ + _ "$BACKUP_CONFIG")" + fi + [[ "$value" == /* && "$value" != / ]] || die "Опасный путь резервных копий: $value" + case "$value" in + /var|/var/backups|/home|/root|/mnt|/media|/etc|/usr|/opt) + warn "Общий каталог ${value} не будет очищен автоматически" + printf '' + return 0 + ;; + esac + printf '%s' "$value" +} + +remove_backup_automation() { + local schedule backup_root + for schedule in daily weekly monthly; do + stop_disable_service "ochenstarik-backup-${schedule}.timer" + done + rm -f -- /etc/systemd/system/ochenstarik-backup@.service \ + /etc/systemd/system/ochenstarik-backup-daily.timer \ + /etc/systemd/system/ochenstarik-backup-weekly.timer \ + /etc/systemd/system/ochenstarik-backup-monthly.timer "$BACKUP_RUNNER" + + backup_root="$(read_backup_root)" + if [[ -n "$backup_root" && -d "$backup_root" ]] \ + && ask_yes_no "Удалить все архивы из ${backup_root}?" no; then + read -rp 'Для необратимого удаления введите УДАЛИТЬ БЭКАПЫ: ' confirmation + if [[ "$confirmation" == 'УДАЛИТЬ БЭКАПЫ' ]]; then + if command -v chattr >/dev/null 2>&1; then + find "$backup_root" -xdev -type f -exec chattr -i -- {} + 2>/dev/null || true + fi + rm -rf -- "$backup_root/initial" "$backup_root/daily" \ + "$backup_root/weekly" "$backup_root/monthly" + log "Архивы проекта удалены" + else + warn "Фраза не совпала; архивы оставлены" + fi + else + warn "Архивы оставлены; они не помешают повторной установке" + fi + rm -f -- "$BACKUP_CONFIG" +} + +restore_locale() { + local package_name + local -a packages=() + if [[ -f "$LOCALE_BACKUP" && ! -L "$LOCALE_BACKUP" ]]; then + cp -a -- "$LOCALE_BACKUP" /etc/default/locale + log "Предыдущая системная локаль восстановлена" + elif [[ -f "$LOCALE_ABSENT_MARKER" && ! -L "$LOCALE_ABSENT_MARKER" ]]; then + rm -f -- /etc/default/locale + log "Созданный скриптом файл локали удалён" + fi + + if [[ -f "$LOCALE_PACKAGES" && ! -L "$LOCALE_PACKAGES" ]]; then + while IFS= read -r package_name || [[ -n "$package_name" ]]; do + [[ "$package_name" =~ ^[a-z0-9][a-z0-9+.-]*$ ]] && packages+=("$package_name") + done < "$LOCALE_PACKAGES" + if ((${#packages[@]} > 0)) && ask_yes_no \ + "Удалить пакеты русификации, которые ранее отсутствовали?" yes; then + DEBIAN_FRONTEND=noninteractive apt-get purge -y "${packages[@]}" + fi + fi + rm -f -- "$LOCALE_BACKUP" "$LOCALE_ABSENT_MARKER" "$LOCALE_PACKAGES" +} + +remove_swap() { + [[ -e "$SWAP_SYSCTL_FILE" || -e "$SWAPFILE" ]] || return 0 + ask_yes_no "Отключить и удалить ${SWAPFILE}?" yes || return 0 + swapoff "$SWAPFILE" >/dev/null 2>&1 || true + if [[ -f /etc/fstab && ! -L /etc/fstab ]]; then + cp -a -- /etc/fstab "/etc/fstab.bak.before-ochenstarik-uninstall.$(date +%F-%H%M%S)" + sed -i '\|^[[:space:]]*/swapfile[[:space:]].*[[:space:]]swap[[:space:]]|d' /etc/fstab + fi + rm -f -- "$SWAP_SYSCTL_FILE" + if [[ -f "$SWAPFILE" && ! -L "$SWAPFILE" ]]; then + rm -f -- "$SWAPFILE" + else + warn "${SWAPFILE} не является обычным файлом и не удалён" + fi + sysctl --system >/dev/null 2>&1 || true +} + +[[ "$EUID" -eq 0 ]] || die "Запустите скрипт от имени root" + +cat <<'WARNING' + +Этот скрипт удалит настройки, службы и данные, созданные проектом lightweight-server. +Он не откатывает обновления Ubuntu и не удаляет общие системные пакеты (OpenSSH, +UFW, curl, мультимедийные библиотеки и другие), потому что они могли существовать +до установки и нужны для доступа к серверу. Их наличие не мешает установке заново. + +Сначала будет открыт SSH-порт 22. Не закрывайте текущую сессию, пока не проверите +новый вход после очистки. +WARNING + +read -rp 'Для продолжения введите СБРОСИТЬ СЕРВЕР: ' final_confirmation +[[ "$final_confirmation" == 'СБРОСИТЬ СЕРВЕР' ]] || die "Очистка отменена" + +remove_ufw_rules +restore_ipv6_defaults +reset_ssh_and_user +remove_telegram +remove_xray +remove_panel_and_warp +remove_backup_automation +restore_locale +remove_swap + +rm -f -- "$SSH_PORT_CONFIG" "$IP_FAMILY_CONFIG" "$MANAGED_PORTS_CONFIG" +find "$CONFIG_DIR" -maxdepth 1 -type f -name '*.bak.*' -delete 2>/dev/null || true +rmdir "$CONFIG_DIR" >/dev/null 2>&1 || true +systemctl daemon-reload >/dev/null 2>&1 || true + +log "Очистка компонентов lightweight-server завершена" +printf '\nПроверьте вход в новой сессии: ssh -p 22 root@IP_СЕРВЕРА\n' +printf 'После проверки можно снова запускать скрипты установки по порядку.\n' +if [[ -n "$(current_ssh_port)" && "$(current_ssh_port)" != 22 ]]; then + printf 'Правило текущего старого SSH-порта %s/tcp оставлено. Удалите его после проверки нового входа.\n' \ + "$(current_ssh_port)" +fi From a6f53d9ad5806a71348f5d096396ed835032b735 Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Wed, 15 Jul 2026 13:27:24 +0700 Subject: [PATCH 02/12] Add sequential installation wizard (#2) --- README.md | 14 ++- ochenstarik-server-install.sh | 162 ++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 ochenstarik-server-install.sh diff --git a/README.md b/README.md index b4ef7fe..fc68712 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ | Файл | Назначение | Можно запускать отдельно | | --- | --- | --- | +| `ochenstarik-server-install.sh` | Единый пошаговый мастер: установить, пропустить или завершить на каждом этапе | Да; рекомендуемый способ полной настройки | | `ochenstarik-server-1.sh` | Предлагает выбрать часовой пояс и русификацию терминала, создаёт swap-файл 2 ГБ и задаёт `vm.swappiness=20` | Да | | `ochenstarik-server-2.sh` | Предлагает выбрать будущий SSH-порт и режим IPv4/IPv6, обновляет систему, ставит пакеты и настраивает UFW | Да | | `ochenstarik-server-user-3.sh` | Создаёт администратора, переносит SSH на порт из этапа 2, проверяет права и включает fail2ban | После установки необходимых пакетов; проще всего сначала запустить скрипт 2 | @@ -312,7 +313,18 @@ zstd -dc /ПУТЬ/К/АРХИВУ.tar.zst | tar --acls --xattrs --numeric-owner ## Полная установка -Если нужны все модули, запускайте их по очереди: +Рекомендуемый способ — единый мастер. Он показывает этапы по порядку и перед каждым предлагает установить его, пропустить либо завершить работу. Если остальные файлы находятся рядом, мастер использует их. Если какой-либо этап отсутствует, мастер безопасно загрузит его из основной ветки, проверит `bash -n` и только потом запустит. + +```bash +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh +chmod 700 ochenstarik-server-install.sh +bash -n ochenstarik-server-install.sh +sudo ./ochenstarik-server-install.sh +``` + +После ошибки этап можно повторить, пропустить или завершить весь мастер. Повторный запуск мастера разрешён: каждый модуль поддерживает повторную настройку. + +Отдельные скрипты сохранены. При необходимости их по-прежнему можно запускать вручную по очереди: ```bash sudo ./ochenstarik-server-1.sh diff --git a/ochenstarik-server-install.sh b/ochenstarik-server-install.sh new file mode 100644 index 0000000..0c38652 --- /dev/null +++ b/ochenstarik-server-install.sh @@ -0,0 +1,162 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +readonly REPO_RAW_BASE="https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main" +readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" + +declare -a STEP_FILES=( + "ochenstarik-server-1.sh" + "ochenstarik-server-2.sh" + "ochenstarik-server-user-3.sh" + "ochenstarik-server-tg-4.sh" + "ochenstarik-server-vpn-5.sh" + "ochenstarik-server-panel-warp-6.sh" + "ochenstarik-server-backup-7.sh" +) + +declare -a STEP_TITLES=( + "Часовой пояс, русификация терминала и swap" + "Базовые пакеты, будущий SSH-порт, IPv4/IPv6 и UFW" + "Администратор, перенос SSH и fail2ban" + "Telegram-уведомления о входах по SSH" + "Системный VPN через Xray" + "Панель 3x-ui и Cloudflare WARP" + "Первичный снимок и расписания резервного копирования" +) + +TEMP_FILE="" + +log() { printf '[+] %s\n' "$*"; } +warn() { printf '[!] %s\n' "$*" >&2; } +die() { printf '[x] %s\n' "$*" >&2; exit 1; } + +cleanup() { + if [[ -n "$TEMP_FILE" && "$TEMP_FILE" == "$SCRIPT_DIR"/.ochenstarik-server-* \ + && -f "$TEMP_FILE" && ! -L "$TEMP_FILE" ]]; then + rm -f -- "$TEMP_FILE" + fi +} +trap cleanup EXIT + +ensure_step_script() { + local filename="$1" result_variable="$2" target + target="${SCRIPT_DIR}/${filename}" + + if [[ -e "$target" ]]; then + [[ -f "$target" && ! -L "$target" ]] \ + || die "Отказ от запуска: $target должен быть обычным файлом" + else + command -v curl >/dev/null 2>&1 \ + || die "Не найден curl: установите его командой apt-get install -y curl ca-certificates" + [[ -d "$SCRIPT_DIR" && ! -L "$SCRIPT_DIR" && -w "$SCRIPT_DIR" ]] \ + || die "Каталог $SCRIPT_DIR недоступен для безопасной загрузки" + + log "Файл ${filename} отсутствует; загружаю его из основного репозитория" + TEMP_FILE="$(mktemp "${SCRIPT_DIR}/.ochenstarik-server-download.XXXXXX")" + chmod 600 "$TEMP_FILE" + curl -fL --retry 5 --retry-delay 5 --connect-timeout 30 \ + --proto '=https' --tlsv1.2 "${REPO_RAW_BASE}/${filename}" -o "$TEMP_FILE" + bash -n "$TEMP_FILE" || die "Загруженный файл ${filename} не прошёл проверку Bash" + chmod 700 "$TEMP_FILE" + mv -- "$TEMP_FILE" "$target" + TEMP_FILE="" + fi + + chmod 700 "$target" + bash -n "$target" || die "Синтаксическая ошибка в $target" + printf -v "$result_variable" '%s' "$target" +} + +choose_step_action() { + local step_number="$1" title="$2" result_variable="$3" answer + while :; do + printf '\n============================================================\n' + printf 'Этап %s из %s: %s\n' "$step_number" "${#STEP_FILES[@]}" "$title" + printf ' 1) Установить / запустить этот этап\n' + printf ' 2) Пропустить и перейти к следующему\n' + printf ' 3) Завершить мастер\n' + read -rp 'Выберите действие: ' answer || die "Ввод прерван" + case "$answer" in + 1|2|3) printf -v "$result_variable" '%s' "$answer"; return 0 ;; + *) warn "Введите 1, 2 или 3" ;; + esac + done +} + +choose_after_failure() { + local result_variable="$1" answer + while :; do + printf '\nЭтап завершился с ошибкой.\n' + printf ' 1) Запустить этот этап повторно\n' + printf ' 2) Пропустить и перейти к следующему\n' + printf ' 3) Завершить мастер\n' + read -rp 'Выберите действие: ' answer || die "Ввод прерван" + case "$answer" in + 1|2|3) printf -v "$result_variable" '%s' "$answer"; return 0 ;; + *) warn "Введите 1, 2 или 3" ;; + esac + done +} + +print_summary() { + local completed="$1" skipped="$2" + printf '\n============================================================\n' + printf 'Мастер установки завершён.\n' + printf 'Успешно выполнено этапов: %s\n' "$completed" + printf 'Пропущено этапов: %s\n' "$skipped" + printf '\nМастер можно запустить повторно: каждый этап поддерживает повторную настройку.\n' + printf 'Для полного сброса используйте ochenstarik-server-uninstall.sh.\n' +} + +[[ "$EUID" -eq 0 ]] || die "Запустите мастер от имени root: sudo ./ochenstarik-server-install.sh" +((${#STEP_FILES[@]} == ${#STEP_TITLES[@]})) || die "Некорректное описание этапов" + +cat <<'INTRO' + +Единый мастер установки lightweight-server + +Этапы будут показаны по порядку. Каждый из них можно выполнить, пропустить +или завершить весь мастер. Не закрывайте текущую SSH-сессию во время изменения +порта SSH и проверяйте новый вход во втором терминале. +INTRO + +completed=0 +skipped=0 + +for index in "${!STEP_FILES[@]}"; do + step_number="$((index + 1))" + action="" + choose_step_action "$step_number" "${STEP_TITLES[index]}" action + case "$action" in + 2) + skipped="$((skipped + 1))" + continue + ;; + 3) + print_summary "$completed" "$skipped" + exit 0 + ;; + esac + + script_path="" + ensure_step_script "${STEP_FILES[index]}" script_path + while :; do + log "Запуск этапа ${step_number}: ${STEP_TITLES[index]}" + if bash -- "$script_path"; then + completed="$((completed + 1))" + log "Этап ${step_number} успешно завершён" + break + fi + + warn "Этап ${step_number} вернул ошибку" + failure_action="" + choose_after_failure failure_action + case "$failure_action" in + 1) continue ;; + 2) skipped="$((skipped + 1))"; break ;; + 3) print_summary "$completed" "$skipped"; exit 1 ;; + esac + done +done + +print_summary "$completed" "$skipped" From 0cca8d9408b5746487ea591f24f504fa892a395c Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Wed, 15 Jul 2026 13:41:59 +0700 Subject: [PATCH 03/12] Add language and program selection to step one (#4) --- .github/workflows/bash-syntax.yml | 7 +- README.md | 26 ++- ochenstarik-server-1.sh | 289 ++++++++++++++++++++++++------ ochenstarik-server-2.sh | 9 +- ochenstarik-server-install.sh | 2 +- ochenstarik-server-uninstall.sh | 61 +++++-- tests/test-step1-selection.sh | 57 ++++++ 7 files changed, 370 insertions(+), 81 deletions(-) create mode 100644 tests/test-step1-selection.sh diff --git a/.github/workflows/bash-syntax.yml b/.github/workflows/bash-syntax.yml index 70064e2..222bc1b 100644 --- a/.github/workflows/bash-syntax.yml +++ b/.github/workflows/bash-syntax.yml @@ -18,7 +18,10 @@ jobs: - name: Check every installer run: | set -e - for script in ./*.sh; do + while IFS= read -r -d '' script; do echo "Checking ${script}" bash -n "${script}" - done + done < <(find . -type f -name '*.sh' -print0) + + - name: Test step 1 selections + run: bash tests/test-step1-selection.sh diff --git a/README.md b/README.md index fc68712..aaade69 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ | Файл | Назначение | Можно запускать отдельно | | --- | --- | --- | | `ochenstarik-server-install.sh` | Единый пошаговый мастер: установить, пропустить или завершить на каждом этапе | Да; рекомендуемый способ полной настройки | -| `ochenstarik-server-1.sh` | Предлагает выбрать часовой пояс и русификацию терминала, создаёт swap-файл 2 ГБ и задаёт `vm.swappiness=20` | Да | -| `ochenstarik-server-2.sh` | Предлагает выбрать будущий SSH-порт и режим IPv4/IPv6, обновляет систему, ставит пакеты и настраивает UFW | Да | +| `ochenstarik-server-1.sh` | Предлагает выбрать часовой пояс, язык терминала и наборы программ, создаёт swap-файл 2 ГБ | Да | +| `ochenstarik-server-2.sh` | Предлагает будущий SSH-порт и IPv4/IPv6, обновляет систему, ставит обязательные пакеты и настраивает UFW | Да | | `ochenstarik-server-user-3.sh` | Создаёт администратора, переносит SSH на порт из этапа 2, проверяет права и включает fail2ban | После установки необходимых пакетов; проще всего сначала запустить скрипт 2 | | `ochenstarik-server-tg-4.sh` | Отправляет уведомления в Telegram при успешном входе по SSH | После установки OpenSSH и `curl`; обычно после скрипта 2 или 3 | | `ochenstarik-server-vpn-5.sh` | Устанавливает Xray и направляет системный трафик через VLESS + REALITY | Да; зависимости устанавливаются автоматически | @@ -67,13 +67,27 @@ chmod 700 ./*.sh bash -n ./ИМЯ-СКРИПТА.sh ``` -## 1. Часовой пояс и настройка swap +## 1. Часовой пояс, язык, программы и swap Файл: `ochenstarik-server-1.sh`. Сначала показывает нумерованное меню популярных часовых поясов, включая `Asia/Novosibirsk`, `Europe/Moscow` и `UTC`. Нужную зону достаточно выбрать по номеру. Для остальных зон предусмотрен полный список с последовательным выбором региона и часового пояса — вводить имя вручную не требуется. -После выбора времени скрипт спрашивает, нужна ли русификация терминала. При согласии он устанавливает доступные пакеты русской локали, создаёт `ru_RU.UTF-8` и задаёт русский язык для новых терминальных сессий. Прежний `/etc/default/locale` и список только что установленных языковых пакетов сохраняются в `/etc/ochenstarik-server`, поэтому отдельный скрипт очистки может вернуть исходную локаль. Без явного согласия языковые настройки не меняются. +После выбора времени скрипт показывает меню языков терминала: оставить текущий, русский, английский, немецкий, французский, испанский, итальянский, португальский, польский, украинский, турецкий, китайский, японский, корейский либо выбрать другую локаль из полного системного списка. Выбранный язык применяется к новым терминальным сессиям. Исходный `/etc/default/locale` сохраняется для последующего восстановления. + +Затем можно отметить несколько наборов программ номерами через пробел, например `1 2 4 9`. Пустой ввод устанавливает все наборы, а `0` пропускает дополнительные программы: + +1. Терминал и повседневная работа — Git, wget/curl, jq/yq, ripgrep, fd, fzf, lnav, редакторы, MC, tmux/screen, htop/btop, ncdu, rsync и strace. +2. Диагностика сети — DNS, ping, MTR, traceroute, tcpdump, nmap, netcat, HTTPie, whois, ethtool и socat. +3. Мониторинг системы и дисков — iotop, sysstat, SMART, lm-sensors, atop, nethogs, vnStat и psmisc. +4. Безопасность и обслуживание — auditd, AppArmor tools, Lynis, needrestart, unattended-upgrades, debsums, ACL и extended attributes. +5. Разработка и автоматизация — компилятор, ShellCheck, Python pip/venv и pipx. +6. Архивы и данные — 7-Zip, ZIP/RAR, SQLite, csvkit, клиенты PostgreSQL и Redis. +7. Документы, PDF и OCR — Poppler, qpdf, Ghostscript, OCRmyPDF/Tesseract, LibreOffice, Pandoc, antiword и catdoc. +8. Изображения, аудио и видео — ImageMagick, ExifTool, WebP, FFmpeg и MediaInfo. +9. Резервное копирование — restic, BorgBackup и rclone. + +Скрипт проверяет доступность каждого пакета в подключённых репозиториях. Недоступные для конкретной версии или набора репозиториев пакеты пропускаются с предупреждением. Список программ, которых не было до запуска, сохраняется в `/etc/ochenstarik-server/step1-installed-packages.list` для безопасной очистки. Затем скрипт создаёт `/swapfile` размером 2 ГБ, добавляет его в `/etc/fstab` и устанавливает `vm.swappiness=20`. Повторный запуск предусмотрен. @@ -95,7 +109,7 @@ sysctl vm.swappiness Файл: `ochenstarik-server-2.sh`. -При каждом запуске скрипт предлагает три действия: полную установку или обновление, изменение режима IP без переустановки пакетов либо добавление открытых портов. При полной установке он запрашивает SSH-порт для этапа 3, обновляет систему и устанавливает OpenSSH, UFW, fail2ban, инструменты обработки документов, изображений, аудио и видео. +При каждом запуске скрипт предлагает три действия: полную установку или обновление, изменение режима IP без переустановки пакетов либо добавление открытых портов. При полной установке он запрашивает SSH-порт для этапа 3, обновляет систему и устанавливает только обязательные компоненты: OpenSSH, UFW, fail2ban, `sudo`, TLS-сертификаты и сетевые инструменты. Дополнительные программы выбираются на этапе 1. Для входящих подключений можно выбрать: @@ -344,7 +358,7 @@ VPN, 3x-ui, WARP, Telegram-уведомления и автоматически Скрипт удаляет управляемые правила UFW, настройки IPv4/IPv6, SSH drop-in, fail2ban-конфигурацию, Telegram-hook, Xray, 3x-ui, WARP, расписания резервного копирования, swap и служебные файлы проекта. Перед изменением SSH открывается порт `22/tcp`. Правило порта, через который работает текущая SSH-сессия, сохраняется, чтобы не потерять доступ. -Удаление администратора и самих архивов резервных копий требует отдельных подтверждений. Общие системные пакеты, обновления Ubuntu и программы вроде OpenSSH, UFW и `curl` не удаляются: они могли существовать до запуска проекта и необходимы серверу. Пакеты русификации удаляются только если первый скрипт зафиксировал, что до русификации их не было. +Удаление администратора и самих архивов резервных копий требует отдельных подтверждений. Обязательные системные пакеты второго этапа и обновления Ubuntu сохраняются, чтобы не потерять доступ к серверу. Языковые пакеты и дополнительные программы удаляются только если первый скрипт зафиксировал, что до его запуска их не было. ```bash curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-uninstall.sh diff --git a/ochenstarik-server-1.sh b/ochenstarik-server-1.sh index b7db1b0..51fd1ab 100644 --- a/ochenstarik-server-1.sh +++ b/ochenstarik-server-1.sh @@ -6,53 +6,116 @@ readonly SWAPSIZE="2G" readonly SWAPPINESS="20" readonly SYSCTL_FILE="/etc/sysctl.d/60-hermes-swap.conf" readonly CONFIG_DIR="/etc/ochenstarik-server" -readonly LOCALE_BACKUP="${CONFIG_DIR}/locale-before-russian.conf" -readonly LOCALE_ABSENT_MARKER="${CONFIG_DIR}/locale-before-russian.absent" -readonly LOCALE_PACKAGES="${CONFIG_DIR}/russian-locale-installed-packages.list" +readonly LOCALE_BACKUP="${CONFIG_DIR}/locale-before-selection.conf" +readonly LOCALE_ABSENT_MARKER="${CONFIG_DIR}/locale-before-selection.absent" +readonly LOCALE_PACKAGES="${CONFIG_DIR}/locale-installed-packages.list" +readonly LEGACY_LOCALE_BACKUP="${CONFIG_DIR}/locale-before-russian.conf" +readonly LEGACY_LOCALE_ABSENT_MARKER="${CONFIG_DIR}/locale-before-russian.absent" +readonly LEGACY_LOCALE_PACKAGES="${CONFIG_DIR}/russian-locale-installed-packages.list" +readonly PROGRAM_PACKAGES="${CONFIG_DIR}/step1-installed-packages.list" + +readonly -a PROGRAM_GROUP_TITLES=( + "Терминал и повседневная работа" + "Диагностика сети" + "Мониторинг системы и дисков" + "Безопасность и обслуживание Ubuntu" + "Разработка и автоматизация" + "Архивы, таблицы и локальные данные" + "Документы, PDF и OCR" + "Изображения, аудио и видео" + "Резервное копирование и синхронизация" +) + +readonly -a PROGRAM_GROUP_DESCRIPTIONS=( + "git, wget, curl, jq/yq, ripgrep/fd/fzf, lnav, редакторы, mc, tmux, htop/btop, ncdu, rsync" + "DNS/ping/MTR/traceroute, tcpdump, nmap, netcat, HTTPie, whois, ethtool и socat" + "iotop, sysstat, SMART, датчики, atop, nethogs, vnStat и инструменты процессов" + "auditd, AppArmor tools, Lynis, needrestart, автоматические обновления, debsums, ACL/xattr" + "компилятор, ShellCheck, Python pip/venv и pipx" + "7-Zip/ZIP/RAR, SQLite, csvkit, клиенты PostgreSQL и Redis" + "Poppler, qpdf, Ghostscript, OCRmyPDF/Tesseract, LibreOffice, Pandoc, antiword/catdoc" + "ImageMagick, ExifTool, WebP, FFmpeg и MediaInfo" + "restic, BorgBackup и rclone" +) + +readonly -a PROGRAM_GROUP_PACKAGES=( + "git wget curl ca-certificates jq yq ripgrep fd-find fzf lnav tree less nano vim mc tmux screen htop btop ncdu lsof strace rsync" + "dnsutils iputils-ping mtr-tiny traceroute tcpdump nmap netcat-openbsd httpie whois ethtool socat" + "iotop sysstat smartmontools lm-sensors atop nethogs vnstat psmisc" + "auditd apparmor-utils lynis needrestart unattended-upgrades debsums acl attr" + "build-essential shellcheck python3-pip python3-venv pipx" + "p7zip-full unzip zip unrar sqlite3 csvkit postgresql-client redis-tools" + "poppler-utils qpdf ghostscript ocrmypdf tesseract-ocr tesseract-ocr-eng tesseract-ocr-rus libreoffice pandoc antiword catdoc" + "imagemagick libimage-exiftool-perl webp ffmpeg mediainfo" + "restic borgbackup rclone" +) + +APT_UPDATED=no log() { printf '[+] %s\n' "$*"; } warn() { printf '[!] %s\n' "$*" >&2; } die() { printf '[x] %s\n' "$*" >&2; exit 1; } -ask_yes_no() { - local prompt="$1" default_answer="${2:-no}" answer suffix - [[ "$default_answer" == yes || "$default_answer" == no ]] \ - || die "Invalid default answer: $default_answer" - [[ "$default_answer" == yes ]] && suffix='[Y/n]' || suffix='[y/N]' - - while :; do - read -rp "$prompt $suffix " answer || die "Input was interrupted" - answer="${answer:-$default_answer}" - case "${answer,,}" in - y|yes|д|да) return 0 ;; - n|no|н|нет) return 1 ;; - *) warn "Ответьте yes/no или да/нет" ;; - esac - done -} - package_is_installed() { dpkg-query -W -f='${Status}' "$1" 2>/dev/null | grep -Fqx 'install ok installed' } -install_russian_terminal_locale() { - local package_name - local -a packages=(locales) newly_installed=() - - ask_yes_no "Установить русскую локаль терминала ru_RU.UTF-8?" no || { - log "Русификация терминала пропущена" - return 0 - } - - for package_name in apt-get apt-cache dpkg-query; do - command -v "$package_name" >/dev/null 2>&1 \ - || die "Required command not found: $package_name" +ensure_package_tools() { + local command_name + for command_name in apt-get apt-cache dpkg-query; do + command -v "$command_name" >/dev/null 2>&1 \ + || die "Required command not found: $command_name" done +} - log "Обновление списка пакетов для русификации" +ensure_apt_updated() { + [[ "$APT_UPDATED" == no ]] || return 0 + ensure_package_tools + log "Обновление списка пакетов" apt-get update + APT_UPDATED=yes +} +record_new_packages() { + local destination="$1" package_name + shift install -d -m 700 -o root -g root "$CONFIG_DIR" + [[ ! -L "$destination" ]] || die "Отказ от записи через ссылку: $destination" + touch "$destination" + for package_name in "$@"; do + grep -Fqx -- "$package_name" "$destination" \ + || printf '%s\n' "$package_name" >> "$destination" + done + chown root:root "$destination" + chmod 600 "$destination" +} + +migrate_legacy_locale_state() { + local package_name + install -d -m 700 -o root -g root "$CONFIG_DIR" + [[ ! -L "$LOCALE_BACKUP" && ! -L "$LOCALE_ABSENT_MARKER" \ + && ! -L "$LOCALE_PACKAGES" ]] || die "Файл состояния локали не должен быть ссылкой" + if [[ -f "$LEGACY_LOCALE_BACKUP" && ! -L "$LEGACY_LOCALE_BACKUP" \ + && ! -e "$LOCALE_BACKUP" ]]; then + mv -- "$LEGACY_LOCALE_BACKUP" "$LOCALE_BACKUP" + fi + if [[ -f "$LEGACY_LOCALE_ABSENT_MARKER" \ + && ! -L "$LEGACY_LOCALE_ABSENT_MARKER" && ! -e "$LOCALE_ABSENT_MARKER" ]]; then + mv -- "$LEGACY_LOCALE_ABSENT_MARKER" "$LOCALE_ABSENT_MARKER" + fi + if [[ -f "$LEGACY_LOCALE_PACKAGES" && ! -L "$LEGACY_LOCALE_PACKAGES" ]]; then + touch "$LOCALE_PACKAGES" + while IFS= read -r package_name || [[ -n "$package_name" ]]; do + [[ -z "$package_name" ]] || grep -Fqx -- "$package_name" "$LOCALE_PACKAGES" \ + || printf '%s\n' "$package_name" >> "$LOCALE_PACKAGES" + done < "$LEGACY_LOCALE_PACKAGES" + rm -f -- "$LEGACY_LOCALE_PACKAGES" + fi +} + +save_original_locale() { + migrate_legacy_locale_state + if [[ ! -e "$LOCALE_BACKUP" && ! -e "$LOCALE_ABSENT_MARKER" ]]; then if [[ -f /etc/default/locale && ! -L /etc/default/locale ]]; then cp -a -- /etc/default/locale "$LOCALE_BACKUP" @@ -62,31 +125,156 @@ install_russian_terminal_locale() { chmod 600 "$LOCALE_ABSENT_MARKER" fi fi +} - for package_name in language-pack-ru manpages-ru; do +choose_terminal_language() { + local answer selected_locale selected_language primary_language package_name index + local current_locale="${LANG:-не задана}" + local -a titles=( + "Не менять язык терминала" + "Русский — ru_RU.UTF-8" + "English (US) — en_US.UTF-8" + "Deutsch — de_DE.UTF-8" + "Français — fr_FR.UTF-8" + "Español — es_ES.UTF-8" + "Italiano — it_IT.UTF-8" + "Português (Brasil) — pt_BR.UTF-8" + "Polski — pl_PL.UTF-8" + "Українська — uk_UA.UTF-8" + "Türkçe — tr_TR.UTF-8" + "简体中文 — zh_CN.UTF-8" + "日本語 — ja_JP.UTF-8" + "한국어 — ko_KR.UTF-8" + "Другая локаль из полного списка" + ) + local -a locales=( + "" ru_RU.UTF-8 en_US.UTF-8 de_DE.UTF-8 fr_FR.UTF-8 es_ES.UTF-8 + it_IT.UTF-8 pt_BR.UTF-8 pl_PL.UTF-8 uk_UA.UTF-8 tr_TR.UTF-8 + zh_CN.UTF-8 ja_JP.UTF-8 ko_KR.UTF-8 "" + ) + local -a language_values=( + "" ru_RU:ru en_US:en de_DE:de fr_FR:fr es_ES:es it_IT:it + pt_BR:pt pl_PL:pl uk_UA:uk tr_TR:tr zh_CN:zh ja_JP:ja ko_KR:ko "" + ) + local -a language_packages=( + "" "language-pack-ru manpages-ru" language-pack-en language-pack-de + language-pack-fr language-pack-es language-pack-it language-pack-pt + language-pack-pl language-pack-uk language-pack-tr language-pack-zh-hans + language-pack-ja language-pack-ko "" + ) + local -a supported_locales packages=(locales) available_packages=() \ + newly_installed=() extra_packages=() + + while :; do + printf '\nВыберите язык новых терминальных сессий (сейчас: %s):\n' "$current_locale" + for index in "${!titles[@]}"; do + printf ' %d) %s\n' "$((index + 1))" "${titles[index]}" + done + read -rp 'Номер языка [1 — не менять]: ' answer || die "Ввод прерван" + answer="${answer:-1}" + [[ "$answer" =~ ^[0-9]+$ ]] && ((answer >= 1 && answer <= ${#titles[@]})) && break + warn "Введите номер от 1 до ${#titles[@]}" + done + + ((answer == 1)) && { log "Язык терминала не изменён"; return 0; } + ensure_apt_updated + save_original_locale + + if ((answer == ${#titles[@]})); then + package_is_installed locales || newly_installed+=(locales) + apt-get install -y locales + mapfile -t supported_locales < <(awk '{print $1}' /usr/share/i18n/SUPPORTED | sort -u) + choose_numbered_option "Выберите локаль из полного списка:" selected_locale \ + "${supported_locales[@]}" + primary_language="${selected_locale%%_*}" + selected_language="${selected_locale%%.*}:${primary_language}" + packages+=("language-pack-${primary_language}") + else + selected_locale="${locales[answer - 1]}" + selected_language="${language_values[answer - 1]}" + read -r -a extra_packages <<< "${language_packages[answer - 1]}" + packages+=("${extra_packages[@]}") + fi + + for package_name in "${packages[@]}"; do + [[ -n "$package_name" ]] || continue if apt-cache show "$package_name" >/dev/null 2>&1; then - packages+=("$package_name") + available_packages+=("$package_name") + package_is_installed "$package_name" || newly_installed+=("$package_name") + else + warn "Языковой пакет недоступен и будет пропущен: $package_name" fi done - for package_name in "${packages[@]}"; do - package_is_installed "$package_name" || newly_installed+=("$package_name") - done - log "Установка пакетов русской локали" export DEBIAN_FRONTEND=noninteractive - apt-get install -y "${packages[@]}" - locale-gen ru_RU.UTF-8 - update-locale LANG=ru_RU.UTF-8 LANGUAGE=ru_RU:ru LC_MESSAGES=ru_RU.UTF-8 + ((${#available_packages[@]} == 0)) || apt-get install -y "${available_packages[@]}" + locale-gen "$selected_locale" + update-locale LANG="$selected_locale" LANGUAGE="$selected_language" \ + LC_MESSAGES="$selected_locale" + record_new_packages "$LOCALE_PACKAGES" "${newly_installed[@]}" - touch "$LOCALE_PACKAGES" - for package_name in "${newly_installed[@]}"; do - grep -Fqx -- "$package_name" "$LOCALE_PACKAGES" \ - || printf '%s\n' "$package_name" >> "$LOCALE_PACKAGES" + log "Выбрана локаль ${selected_locale}. Она применится при следующем входе" +} + +choose_and_install_programs() { + local answer token group_index package_name + local -a selected_groups=() group_packages=() requested=() available=() newly_installed=() + local -A selected=() seen=() + + printf '\nВыберите наборы программ для установки:\n' + for group_index in "${!PROGRAM_GROUP_TITLES[@]}"; do + printf ' %d) %s\n %s\n' "$((group_index + 1))" \ + "${PROGRAM_GROUP_TITLES[group_index]}" "${PROGRAM_GROUP_DESCRIPTIONS[group_index]}" done - chown root:root "$LOCALE_PACKAGES" - chmod 600 "$LOCALE_PACKAGES" + printf ' 0) Не устанавливать дополнительные программы\n' + printf '\nВведите номера через пробел, например: 1 2 4 9.\n' + read -rp 'Выбор [Enter — установить все наборы]: ' answer || die "Ввод прерван" + answer="${answer//,/ }" - log "Русская локаль включена. Она применится при следующем входе в терминал" + if [[ -z "${answer//[[:space:]]/}" ]]; then + for group_index in "${!PROGRAM_GROUP_TITLES[@]}"; do + selected_groups+=("$group_index") + done + elif [[ "$answer" =~ ^[[:space:]]*0[[:space:]]*$ ]]; then + log "Установка дополнительных программ пропущена" + return 0 + else + for token in $answer; do + [[ "$token" =~ ^[0-9]+$ ]] \ + || die "Некорректный номер набора: $token" + ((token >= 1 && token <= ${#PROGRAM_GROUP_TITLES[@]})) \ + || die "Номер набора вне диапазона: $token" + group_index="$((token - 1))" + [[ -n "${selected[$group_index]:-}" ]] || selected_groups+=("$group_index") + selected[$group_index]=yes + done + fi + + ensure_apt_updated + for group_index in "${selected_groups[@]}"; do + log "Выбран набор: ${PROGRAM_GROUP_TITLES[group_index]}" + read -r -a group_packages <<< "${PROGRAM_GROUP_PACKAGES[group_index]}" + for package_name in "${group_packages[@]}"; do + [[ -n "${seen[$package_name]:-}" ]] && continue + seen[$package_name]=yes + requested+=("$package_name") + done + done + + for package_name in "${requested[@]}"; do + if apt-cache show "$package_name" >/dev/null 2>&1; then + available+=("$package_name") + package_is_installed "$package_name" || newly_installed+=("$package_name") + else + warn "Пакет отсутствует в подключённых репозиториях и пропущен: $package_name" + fi + done + + ((${#available[@]} > 0)) || die "Ни один выбранный пакет не найден" + export DEBIAN_FRONTEND=noninteractive + apt-get install -y "${available[@]}" + record_new_packages "$PROGRAM_PACKAGES" "${newly_installed[@]}" + log "Выбранные программы установлены" } choose_numbered_option() { @@ -165,7 +353,8 @@ for command_name in awk blkid fallocate grep mkswap swapon sysctl timedatectl; d done choose_timezone -install_russian_terminal_locale +choose_terminal_language +choose_and_install_programs log "Configuring ${SWAPSIZE} swap file" if [[ ! -e "$SWAPFILE" ]]; then diff --git a/ochenstarik-server-2.sh b/ochenstarik-server-2.sh index 65c138f..2969f69 100644 --- a/ochenstarik-server-2.sh +++ b/ochenstarik-server-2.sh @@ -326,14 +326,9 @@ if [[ "$ACTION" == install ]]; then apt-get update apt-get upgrade -y - log "Устанавливаю серверные пакеты и средства обработки документов" + log "Устанавливаю обязательные пакеты SSH, брандмауэра и защиты" apt-get install -y \ - sudo ufw fail2ban curl ca-certificates openssl openssh-server logrotate iproute2 \ - poppler-utils qpdf ghostscript ocrmypdf \ - tesseract-ocr tesseract-ocr-eng tesseract-ocr-rus \ - libreoffice pandoc antiword catdoc imagemagick libimage-exiftool-perl webp \ - ffmpeg mediainfo p7zip-full unzip zip unrar jq yq csvkit sqlite3 \ - python3-pip python3-venv mc + sudo ufw fail2ban curl ca-certificates openssl openssh-server logrotate iproute2 for command_name in ip sed sysctl ufw; do require_command "$command_name" diff --git a/ochenstarik-server-install.sh b/ochenstarik-server-install.sh index 0c38652..9c9240b 100644 --- a/ochenstarik-server-install.sh +++ b/ochenstarik-server-install.sh @@ -15,7 +15,7 @@ declare -a STEP_FILES=( ) declare -a STEP_TITLES=( - "Часовой пояс, русификация терминала и swap" + "Часовой пояс, язык терминала, набор программ и swap" "Базовые пакеты, будущий SSH-порт, IPv4/IPv6 и UFW" "Администратор, перенос SSH и fail2ban" "Telegram-уведомления о входах по SSH" diff --git a/ochenstarik-server-uninstall.sh b/ochenstarik-server-uninstall.sh index 5057d6c..07e4b72 100644 --- a/ochenstarik-server-uninstall.sh +++ b/ochenstarik-server-uninstall.sh @@ -26,9 +26,13 @@ readonly XRAY_HELPER="/usr/local/sbin/ochenstarik-xray-routing" readonly XRAY_SERVICE="/etc/systemd/system/ochenstarik-xray-routing.service" readonly BACKUP_CONFIG="${CONFIG_DIR}/backup.conf" readonly BACKUP_RUNNER="/usr/local/sbin/ochenstarik-server-backup" -readonly LOCALE_BACKUP="${CONFIG_DIR}/locale-before-russian.conf" -readonly LOCALE_ABSENT_MARKER="${CONFIG_DIR}/locale-before-russian.absent" -readonly LOCALE_PACKAGES="${CONFIG_DIR}/russian-locale-installed-packages.list" +readonly LOCALE_BACKUP="${CONFIG_DIR}/locale-before-selection.conf" +readonly LOCALE_ABSENT_MARKER="${CONFIG_DIR}/locale-before-selection.absent" +readonly LOCALE_PACKAGES="${CONFIG_DIR}/locale-installed-packages.list" +readonly LEGACY_LOCALE_BACKUP="${CONFIG_DIR}/locale-before-russian.conf" +readonly LEGACY_LOCALE_ABSENT_MARKER="${CONFIG_DIR}/locale-before-russian.absent" +readonly LEGACY_LOCALE_PACKAGES="${CONFIG_DIR}/russian-locale-installed-packages.list" +readonly PROGRAM_PACKAGES="${CONFIG_DIR}/step1-installed-packages.list" log() { printf '[+] %s\n' "$*"; } warn() { printf '[!] %s\n' "$*" >&2; } @@ -263,26 +267,52 @@ remove_backup_automation() { } restore_locale() { - local package_name + local package_name package_file local -a packages=() if [[ -f "$LOCALE_BACKUP" && ! -L "$LOCALE_BACKUP" ]]; then cp -a -- "$LOCALE_BACKUP" /etc/default/locale log "Предыдущая системная локаль восстановлена" + elif [[ -f "$LEGACY_LOCALE_BACKUP" && ! -L "$LEGACY_LOCALE_BACKUP" ]]; then + cp -a -- "$LEGACY_LOCALE_BACKUP" /etc/default/locale + log "Предыдущая системная локаль восстановлена" elif [[ -f "$LOCALE_ABSENT_MARKER" && ! -L "$LOCALE_ABSENT_MARKER" ]]; then rm -f -- /etc/default/locale log "Созданный скриптом файл локали удалён" + elif [[ -f "$LEGACY_LOCALE_ABSENT_MARKER" \ + && ! -L "$LEGACY_LOCALE_ABSENT_MARKER" ]]; then + rm -f -- /etc/default/locale + log "Созданный скриптом файл локали удалён" fi - if [[ -f "$LOCALE_PACKAGES" && ! -L "$LOCALE_PACKAGES" ]]; then + for package_file in "$LOCALE_PACKAGES" "$LEGACY_LOCALE_PACKAGES"; do + [[ -f "$package_file" && ! -L "$package_file" ]] || continue while IFS= read -r package_name || [[ -n "$package_name" ]]; do - [[ "$package_name" =~ ^[a-z0-9][a-z0-9+.-]*$ ]] && packages+=("$package_name") - done < "$LOCALE_PACKAGES" - if ((${#packages[@]} > 0)) && ask_yes_no \ - "Удалить пакеты русификации, которые ранее отсутствовали?" yes; then - DEBIAN_FRONTEND=noninteractive apt-get purge -y "${packages[@]}" - fi + [[ "$package_name" =~ ^[a-z0-9][a-z0-9+.-]*$ ]] \ + && [[ ! " ${packages[*]} " =~ " ${package_name} " ]] \ + && packages+=("$package_name") + done < "$package_file" + done + if ((${#packages[@]} > 0)) && ask_yes_no \ + "Удалить языковые пакеты, которые ранее отсутствовали?" yes; then + DEBIAN_FRONTEND=noninteractive apt-get purge -y "${packages[@]}" fi - rm -f -- "$LOCALE_BACKUP" "$LOCALE_ABSENT_MARKER" "$LOCALE_PACKAGES" + rm -f -- "$LOCALE_BACKUP" "$LOCALE_ABSENT_MARKER" "$LOCALE_PACKAGES" \ + "$LEGACY_LOCALE_BACKUP" "$LEGACY_LOCALE_ABSENT_MARKER" \ + "$LEGACY_LOCALE_PACKAGES" +} + +remove_step1_programs() { + local package_name + local -a packages=() + [[ -f "$PROGRAM_PACKAGES" && ! -L "$PROGRAM_PACKAGES" ]] || return 0 + while IFS= read -r package_name || [[ -n "$package_name" ]]; do + [[ "$package_name" =~ ^[a-z0-9][a-z0-9+.-]*$ ]] && packages+=("$package_name") + done < "$PROGRAM_PACKAGES" + if ((${#packages[@]} > 0)) && ask_yes_no \ + "Удалить дополнительные программы, установленные первым этапом?" yes; then + DEBIAN_FRONTEND=noninteractive apt-get purge -y "${packages[@]}" + fi + rm -f -- "$PROGRAM_PACKAGES" } remove_swap() { @@ -307,9 +337,9 @@ remove_swap() { cat <<'WARNING' Этот скрипт удалит настройки, службы и данные, созданные проектом lightweight-server. -Он не откатывает обновления Ubuntu и не удаляет общие системные пакеты (OpenSSH, -UFW, curl, мультимедийные библиотеки и другие), потому что они могли существовать -до установки и нужны для доступа к серверу. Их наличие не мешает установке заново. +Он не откатывает обновления Ubuntu и не удаляет обязательные общие пакеты второго +этапа (OpenSSH, UFW и другие), потому что они нужны для доступа к серверу. Пакеты, +которые первый этап зафиксировал как новые, можно удалить отдельным подтверждением. Сначала будет открыт SSH-порт 22. Не закрывайте текущую сессию, пока не проверите новый вход после очистки. @@ -326,6 +356,7 @@ remove_xray remove_panel_and_warp remove_backup_automation restore_locale +remove_step1_programs remove_swap rm -f -- "$SSH_PORT_CONFIG" "$IP_FAMILY_CONFIG" "$MANAGED_PORTS_CONFIG" diff --git a/tests/test-step1-selection.sh b/tests/test-step1-selection.sh new file mode 100644 index 0000000..59bae7c --- /dev/null +++ b/tests/test-step1-selection.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." +source <(awk '/^\[\[ "\$EUID"/{exit} {print}' ochenstarik-server-1.sh) + +declare -a MOCK_APT_PACKAGES=() +MOCK_LOCALE="" +MOCK_UPDATE_LOCALE="" + +ensure_apt_updated() { :; } +apt-cache() { return 0; } +package_is_installed() { return 1; } +record_new_packages() { :; } +save_original_locale() { :; } + +apt-get() { + [[ "${1:-}" == install ]] || return 0 + shift + [[ "${1:-}" != -y ]] || shift + MOCK_APT_PACKAGES=("$@") +} + +locale-gen() { MOCK_LOCALE="$1"; } +update-locale() { MOCK_UPDATE_LOCALE="$*"; } + +contains_package() { + local expected="$1" package_name + for package_name in "${MOCK_APT_PACKAGES[@]}"; do + [[ "$package_name" != "$expected" ]] || return 0 + done + return 1 +} + +choose_and_install_programs <<< '1 3' >/dev/null +contains_package git +contains_package iotop +! contains_package nmap +! contains_package ffmpeg + +MOCK_APT_PACKAGES=() +choose_and_install_programs <<< '' >/dev/null +contains_package nmap +contains_package ffmpeg +contains_package restic +contains_package postgresql-client + +MOCK_APT_PACKAGES=() +choose_and_install_programs <<< '0' >/dev/null +((${#MOCK_APT_PACKAGES[@]} == 0)) + +choose_terminal_language <<< '2' >/dev/null +[[ "$MOCK_LOCALE" == ru_RU.UTF-8 ]] +[[ "$MOCK_UPDATE_LOCALE" == *'LANG=ru_RU.UTF-8'* ]] +[[ "$MOCK_UPDATE_LOCALE" == *'LANGUAGE=ru_RU:ru'* ]] + +printf 'Step 1 selection tests passed.\n' From 78da5ae6e7c21ec49dd9bfb4fc00694f7d743819 Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Wed, 15 Jul 2026 13:49:57 +0700 Subject: [PATCH 04/12] Add quick installation guide (#5) --- README.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/README.md b/README.md index aaade69..699d151 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,50 @@ Набор независимых Bash-скриптов для первоначальной настройки Ubuntu Server. Можно запустить только нужные модули или выполнить их по порядку для полной настройки. +## Быстрая установка + +Скопируйте весь блок в терминал сервера. Он загружает архив через `codeload.github.com`, проверяет ошибки на каждом шаге и запускает единый пошаговый мастер: + +```bash +set -e +cd "$HOME" +sudo apt-get update +sudo apt-get install -y curl ca-certificates tar +archive="$(mktemp)" +trap 'rm -f "$archive"' EXIT +curl -4 -fL \ + --retry 5 \ + --retry-delay 10 \ + --connect-timeout 30 \ + https://codeload.github.com/ochenstarik-ui/lightweight-server/tar.gz/refs/heads/main \ + -o "$archive" +install -d -m 700 lightweight-server +tar -xzf "$archive" -C lightweight-server --strip-components=1 +rm -f "$archive" +trap - EXIT +cd lightweight-server +chmod 700 ./*.sh +sudo ./ochenstarik-server-install.sh +``` + +Мастер показывает этапы 1–7 по порядку. На каждом этапе можно выбрать установку, пропуск или завершение. В первом этапе пустой ввод устанавливает все рекомендуемые наборы программ. Во время изменения SSH-порта не закрывайте текущую сессию — сначала проверьте новый вход во втором терминале. + +Если нужен только мастер без скачивания полного архива: + +```bash +sudo apt-get update +sudo apt-get install -y curl ca-certificates +curl -4 -fLO \ + --retry 5 \ + --retry-delay 10 \ + --connect-timeout 30 \ + https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh +chmod 700 ochenstarik-server-install.sh +sudo ./ochenstarik-server-install.sh +``` + +При запуске одного мастера недостающие этапы будут загружены автоматически из основной ветки и проверены командой `bash -n`. + ## Что входит в проект | Файл | Назначение | Можно запускать отдельно | From a1ef247af179506f276e028abe16cc0203e65fba Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Wed, 15 Jul 2026 14:04:48 +0700 Subject: [PATCH 05/12] Add multilingual dialog to step one (#6) --- README.md | 6 +- ochenstarik-server-1.sh | 418 +++++++++++++++++++++++++++++----- tests/test-step1-selection.sh | 8 + 3 files changed, 376 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 699d151..9301c4e 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ sudo ./ochenstarik-server-install.sh | Файл | Назначение | Можно запускать отдельно | | --- | --- | --- | | `ochenstarik-server-install.sh` | Единый пошаговый мастер: установить, пропустить или завершить на каждом этапе | Да; рекомендуемый способ полной настройки | -| `ochenstarik-server-1.sh` | Предлагает выбрать часовой пояс, язык терминала и наборы программ, создаёт swap-файл 2 ГБ | Да | +| `ochenstarik-server-1.sh` | Предлагает выбрать язык диалога, часовой пояс, язык терминала и наборы программ, создаёт swap-файл 2 ГБ | Да | | `ochenstarik-server-2.sh` | Предлагает будущий SSH-порт и IPv4/IPv6, обновляет систему, ставит обязательные пакеты и настраивает UFW | Да | | `ochenstarik-server-user-3.sh` | Создаёт администратора, переносит SSH на порт из этапа 2, проверяет права и включает fail2ban | После установки необходимых пакетов; проще всего сначала запустить скрипт 2 | | `ochenstarik-server-tg-4.sh` | Отправляет уведомления в Telegram при успешном входе по SSH | После установки OpenSSH и `curl`; обычно после скрипта 2 или 3 | @@ -115,7 +115,9 @@ bash -n ./ИМЯ-СКРИПТА.sh Файл: `ochenstarik-server-1.sh`. -Сначала показывает нумерованное меню популярных часовых поясов, включая `Asia/Novosibirsk`, `Europe/Moscow` и `UTC`. Нужную зону достаточно выбрать по номеру. Для остальных зон предусмотрен полный список с последовательным выбором региона и часового пояса — вводить имя вручную не требуется. +Самый первый запрос выбирает язык диалога установщика: English, Русский, Español, Deutsch, Français, Português, 中文, 日本語, العربية или हिन्दी. Начальное меню выводится на английском, а пустой ввод оставляет English. Этот выбор меняет вопросы и сообщения скрипта, но не системную локаль сервера. + +Затем показывается нумерованное меню популярных часовых поясов, включая `Asia/Novosibirsk`, `Europe/Moscow` и `UTC`. Нужную зону достаточно выбрать по номеру. Для остальных зон предусмотрен полный список с последовательным выбором региона и часового пояса — вводить имя вручную не требуется. После выбора времени скрипт показывает меню языков терминала: оставить текущий, русский, английский, немецкий, французский, испанский, итальянский, португальский, польский, украинский, турецкий, китайский, японский, корейский либо выбрать другую локаль из полного системного списка. Выбранный язык применяется к новым терминальным сессиям. Исходный `/etc/default/locale` сохраняется для последующего восстановления. diff --git a/ochenstarik-server-1.sh b/ochenstarik-server-1.sh index 51fd1ab..d3be6c5 100644 --- a/ochenstarik-server-1.sh +++ b/ochenstarik-server-1.sh @@ -14,28 +14,28 @@ readonly LEGACY_LOCALE_ABSENT_MARKER="${CONFIG_DIR}/locale-before-russian.absent readonly LEGACY_LOCALE_PACKAGES="${CONFIG_DIR}/russian-locale-installed-packages.list" readonly PROGRAM_PACKAGES="${CONFIG_DIR}/step1-installed-packages.list" -readonly -a PROGRAM_GROUP_TITLES=( - "Терминал и повседневная работа" - "Диагностика сети" - "Мониторинг системы и дисков" - "Безопасность и обслуживание Ubuntu" - "Разработка и автоматизация" - "Архивы, таблицы и локальные данные" - "Документы, PDF и OCR" - "Изображения, аудио и видео" - "Резервное копирование и синхронизация" +declare -a PROGRAM_GROUP_TITLES=( + "Terminal and everyday tools" + "Network diagnostics" + "System and disk monitoring" + "Ubuntu security and maintenance" + "Development and automation" + "Archives, tables, and local data" + "Documents, PDF, and OCR" + "Images, audio, and video" + "Backup and synchronization" ) -readonly -a PROGRAM_GROUP_DESCRIPTIONS=( - "git, wget, curl, jq/yq, ripgrep/fd/fzf, lnav, редакторы, mc, tmux, htop/btop, ncdu, rsync" - "DNS/ping/MTR/traceroute, tcpdump, nmap, netcat, HTTPie, whois, ethtool и socat" - "iotop, sysstat, SMART, датчики, atop, nethogs, vnStat и инструменты процессов" - "auditd, AppArmor tools, Lynis, needrestart, автоматические обновления, debsums, ACL/xattr" - "компилятор, ShellCheck, Python pip/venv и pipx" - "7-Zip/ZIP/RAR, SQLite, csvkit, клиенты PostgreSQL и Redis" +declare -a PROGRAM_GROUP_DESCRIPTIONS=( + "git, wget, curl, jq/yq, ripgrep/fd/fzf, lnav, editors, mc, tmux, htop/btop, ncdu, rsync" + "DNS/ping/MTR/traceroute, tcpdump, nmap, netcat, HTTPie, whois, ethtool, socat" + "iotop, sysstat, SMART, sensors, atop, nethogs, vnStat, process tools" + "auditd, AppArmor tools, Lynis, needrestart, automatic updates, debsums, ACL/xattr" + "compiler, ShellCheck, Python pip/venv, pipx" + "7-Zip/ZIP/RAR, SQLite, csvkit, PostgreSQL and Redis clients" "Poppler, qpdf, Ghostscript, OCRmyPDF/Tesseract, LibreOffice, Pandoc, antiword/catdoc" - "ImageMagick, ExifTool, WebP, FFmpeg и MediaInfo" - "restic, BorgBackup и rclone" + "ImageMagick, ExifTool, WebP, FFmpeg, MediaInfo" + "restic, BorgBackup, rclone" ) readonly -a PROGRAM_GROUP_PACKAGES=( @@ -51,6 +51,315 @@ readonly -a PROGRAM_GROUP_PACKAGES=( ) APT_UPDATED=no +UI_LANG=en + +declare -A UI_TEXT=() + +load_ui_text() { + UI_TEXT=( + [input_interrupted]="Input was interrupted" + [invalid_number]="Enter a number from %s to %s" + [no_options]="No options are available for: %s" + [select_option]="Enter the option number: " + [select_region]="Select a region:" + [select_timezone]="Select a timezone in %s:" + [timezone_heading]="Select the server timezone (current: %s):" + [other_timezone]="Other timezone (select by region)" + [unknown_timezone]="Unknown timezone: %s" + [timezone_set]="Timezone set to %s" + [terminal_heading]="Select the language for new terminal sessions (current: %s):" + [terminal_keep]="Keep the current terminal language" + [terminal_other]="Other locale from the full list" + [terminal_prompt]="Language number [1 — keep current]: " + [terminal_unchanged]="Terminal language was not changed" + [terminal_full_list]="Select a locale from the full list:" + [locale_package_missing]="Language package is unavailable and will be skipped: %s" + [locale_selected]="Locale %s selected. It will apply at the next login" + [program_heading]="Select program groups to install:" + [program_none]="Do not install additional programs" + [program_example]="Enter numbers separated by spaces, for example: 1 2 4 9." + [program_prompt]="Selection [Enter — install all groups]: " + [program_skipped]="Installation of additional programs was skipped" + [program_bad_number]="Invalid group number: %s" + [program_out_of_range]="Group number is out of range: %s" + [program_selected]="Selected group: %s" + [package_missing]="Package is unavailable in the configured repositories and will be skipped: %s" + [no_packages]="None of the selected packages were found" + [programs_installed]="Selected programs have been installed" + [apt_update]="Updating the package list" + [root_required]="Run this script as root" + [required_missing]="Required command not found: %s" + [swap_configuring]="Configuring %s swap file" + [setup_complete]="Server memory setup is complete" + [next_script]="Now run ochenstarik-server-2.sh as root." + ) + + case "$UI_LANG" in + ru) UI_TEXT+=( + [input_interrupted]="Ввод прерван" [invalid_number]="Введите номер от %s до %s" + [select_option]="Введите номер варианта: " [select_region]="Выберите регион:" + [select_timezone]="Выберите часовой пояс в регионе %s:" + [timezone_heading]="Выберите часовой пояс сервера (сейчас: %s):" + [other_timezone]="Другой часовой пояс (выбор по региону)" + [unknown_timezone]="Неизвестный часовой пояс: %s" [timezone_set]="Установлен часовой пояс %s" + [terminal_heading]="Выберите язык новых терминальных сессий (сейчас: %s):" + [terminal_keep]="Не менять язык терминала" [terminal_other]="Другая локаль из полного списка" + [terminal_prompt]="Номер языка [1 — не менять]: " [terminal_unchanged]="Язык терминала не изменён" + [terminal_full_list]="Выберите локаль из полного списка:" + [locale_package_missing]="Языковой пакет недоступен и будет пропущен: %s" + [locale_selected]="Выбрана локаль %s. Она применится при следующем входе" + [program_heading]="Выберите наборы программ для установки:" + [program_none]="Не устанавливать дополнительные программы" + [program_example]="Введите номера через пробел, например: 1 2 4 9." + [program_prompt]="Выбор [Enter — установить все наборы]: " + [program_skipped]="Установка дополнительных программ пропущена" + [program_bad_number]="Некорректный номер набора: %s" + [program_out_of_range]="Номер набора вне диапазона: %s" + [program_selected]="Выбран набор: %s" + [package_missing]="Пакет отсутствует в подключённых репозиториях и будет пропущен: %s" + [no_packages]="Ни один выбранный пакет не найден" + [programs_installed]="Выбранные программы установлены" [apt_update]="Обновление списка пакетов" + [root_required]="Запустите этот скрипт от root" + [required_missing]="Не найдена обязательная команда: %s" + [swap_configuring]="Настройка файла подкачки размером %s" + [setup_complete]="Настройка памяти сервера завершена" + [next_script]="Теперь запустите ochenstarik-server-2.sh от root." + ) ;; + es) UI_TEXT+=( + [input_interrupted]="Entrada interrumpida" [invalid_number]="Introduzca un número del %s al %s" + [select_option]="Introduzca el número de opción: " [select_region]="Seleccione una región:" + [select_timezone]="Seleccione una zona horaria en %s:" + [timezone_heading]="Seleccione la zona horaria del servidor (actual: %s):" + [other_timezone]="Otra zona horaria (seleccionar por región)" [timezone_set]="Zona horaria configurada: %s" + [terminal_heading]="Seleccione el idioma para nuevas sesiones de terminal (actual: %s):" + [terminal_keep]="Mantener el idioma actual" [terminal_other]="Otra configuración regional de la lista completa" + [terminal_prompt]="Número de idioma [1 — mantener]: " [terminal_unchanged]="El idioma del terminal no se cambió" + [terminal_full_list]="Seleccione una configuración regional de la lista completa:" + [program_heading]="Seleccione los grupos de programas que desea instalar:" + [program_none]="No instalar programas adicionales" + [program_example]="Introduzca números separados por espacios, por ejemplo: 1 2 4 9." + [program_prompt]="Selección [Enter — instalar todos]: " [program_skipped]="Se omitió la instalación de programas adicionales" + [program_bad_number]="Número de grupo no válido: %s" [program_out_of_range]="Número de grupo fuera de rango: %s" + [program_selected]="Grupo seleccionado: %s" [programs_installed]="Los programas seleccionados se instalaron" + [apt_update]="Actualizando la lista de paquetes" [root_required]="Ejecute este script como root" + [swap_configuring]="Configurando un archivo swap de %s" [setup_complete]="Configuración de memoria completada" + [next_script]="Ahora ejecute ochenstarik-server-2.sh como root." + ) ;; + de) UI_TEXT+=( + [input_interrupted]="Eingabe wurde unterbrochen" [invalid_number]="Geben Sie eine Zahl von %s bis %s ein" + [select_option]="Optionsnummer eingeben: " [select_region]="Region auswählen:" + [select_timezone]="Zeitzone in %s auswählen:" + [timezone_heading]="Server-Zeitzone auswählen (aktuell: %s):" + [other_timezone]="Andere Zeitzone (nach Region auswählen)" [timezone_set]="Zeitzone auf %s gesetzt" + [terminal_heading]="Sprache für neue Terminalsitzungen auswählen (aktuell: %s):" + [terminal_keep]="Aktuelle Terminalsprache beibehalten" [terminal_other]="Andere Locale aus der vollständigen Liste" + [terminal_prompt]="Sprachnummer [1 — beibehalten]: " [terminal_unchanged]="Terminalsprache wurde nicht geändert" + [terminal_full_list]="Locale aus der vollständigen Liste auswählen:" + [program_heading]="Zu installierende Programmgruppen auswählen:" + [program_none]="Keine zusätzlichen Programme installieren" + [program_example]="Nummern durch Leerzeichen getrennt eingeben, zum Beispiel: 1 2 4 9." + [program_prompt]="Auswahl [Enter — alle installieren]: " [program_skipped]="Installation zusätzlicher Programme übersprungen" + [program_bad_number]="Ungültige Gruppennummer: %s" [program_out_of_range]="Gruppennummer außerhalb des Bereichs: %s" + [program_selected]="Ausgewählte Gruppe: %s" [programs_installed]="Ausgewählte Programme wurden installiert" + [apt_update]="Paketliste wird aktualisiert" [root_required]="Führen Sie dieses Skript als root aus" + [swap_configuring]="%s-Swap-Datei wird eingerichtet" [setup_complete]="Speichereinrichtung abgeschlossen" + [next_script]="Führen Sie jetzt ochenstarik-server-2.sh als root aus." + ) ;; + fr) UI_TEXT+=( + [input_interrupted]="Saisie interrompue" [invalid_number]="Saisissez un nombre de %s à %s" + [select_option]="Saisissez le numéro de l’option : " [select_region]="Sélectionnez une région :" + [select_timezone]="Sélectionnez un fuseau horaire dans %s :" + [timezone_heading]="Sélectionnez le fuseau horaire du serveur (actuel : %s) :" + [other_timezone]="Autre fuseau horaire (sélection par région)" [timezone_set]="Fuseau horaire défini sur %s" + [terminal_heading]="Sélectionnez la langue des nouvelles sessions (actuelle : %s) :" + [terminal_keep]="Conserver la langue actuelle" [terminal_other]="Autre locale de la liste complète" + [terminal_prompt]="Numéro de langue [1 — conserver] : " [terminal_unchanged]="La langue du terminal n’a pas été modifiée" + [terminal_full_list]="Sélectionnez une locale dans la liste complète :" + [program_heading]="Sélectionnez les groupes de programmes à installer :" + [program_none]="Ne pas installer de programmes supplémentaires" + [program_example]="Saisissez les numéros séparés par des espaces, par exemple : 1 2 4 9." + [program_prompt]="Sélection [Entrée — tout installer] : " [program_skipped]="Installation des programmes supplémentaires ignorée" + [program_bad_number]="Numéro de groupe incorrect : %s" [program_out_of_range]="Numéro de groupe hors plage : %s" + [program_selected]="Groupe sélectionné : %s" [programs_installed]="Les programmes sélectionnés ont été installés" + [apt_update]="Mise à jour de la liste des paquets" [root_required]="Exécutez ce script en tant que root" + [swap_configuring]="Configuration d’un fichier swap de %s" [setup_complete]="Configuration de la mémoire terminée" + [next_script]="Exécutez maintenant ochenstarik-server-2.sh en tant que root." + ) ;; + pt) UI_TEXT+=( + [input_interrupted]="Entrada interrompida" [invalid_number]="Digite um número de %s a %s" + [select_option]="Digite o número da opção: " [select_region]="Selecione uma região:" + [select_timezone]="Selecione um fuso horário em %s:" + [timezone_heading]="Selecione o fuso horário do servidor (atual: %s):" + [other_timezone]="Outro fuso horário (selecionar por região)" [timezone_set]="Fuso horário definido como %s" + [terminal_heading]="Selecione o idioma das novas sessões de terminal (atual: %s):" + [terminal_keep]="Manter o idioma atual" [terminal_other]="Outra localidade da lista completa" + [terminal_prompt]="Número do idioma [1 — manter]: " [terminal_unchanged]="O idioma do terminal não foi alterado" + [terminal_full_list]="Selecione uma localidade na lista completa:" + [program_heading]="Selecione os grupos de programas para instalar:" + [program_none]="Não instalar programas adicionais" + [program_example]="Digite números separados por espaços, por exemplo: 1 2 4 9." + [program_prompt]="Seleção [Enter — instalar todos]: " [program_skipped]="Instalação de programas adicionais ignorada" + [program_bad_number]="Número de grupo inválido: %s" [program_out_of_range]="Número de grupo fora do intervalo: %s" + [program_selected]="Grupo selecionado: %s" [programs_installed]="Os programas selecionados foram instalados" + [apt_update]="Atualizando a lista de pacotes" [root_required]="Execute este script como root" + [swap_configuring]="Configurando arquivo swap de %s" [setup_complete]="Configuração de memória concluída" + [next_script]="Agora execute ochenstarik-server-2.sh como root." + ) ;; + zh) UI_TEXT+=( + [input_interrupted]="输入已中断" [invalid_number]="请输入 %s 到 %s 之间的数字" + [select_option]="请输入选项编号:" [select_region]="请选择地区:" [select_timezone]="请选择 %s 的时区:" + [timezone_heading]="请选择服务器时区(当前:%s):" [other_timezone]="其他时区(按地区选择)" [timezone_set]="时区已设置为 %s" + [terminal_heading]="请选择新终端会话的语言(当前:%s):" [terminal_keep]="保留当前终端语言" + [terminal_other]="从完整列表选择其他区域设置" [terminal_prompt]="语言编号 [1 — 保留当前设置]:" + [terminal_unchanged]="终端语言未更改" [terminal_full_list]="请从完整列表选择区域设置:" + [program_heading]="请选择要安装的程序组:" [program_none]="不安装其他程序" + [program_example]="请输入用空格分隔的编号,例如:1 2 4 9。" [program_prompt]="选择 [Enter — 全部安装]:" + [program_skipped]="已跳过其他程序的安装" [program_bad_number]="无效的程序组编号:%s" + [program_out_of_range]="程序组编号超出范围:%s" [program_selected]="已选择程序组:%s" + [programs_installed]="已安装所选程序" [apt_update]="正在更新软件包列表" [root_required]="请以 root 身份运行此脚本" + [swap_configuring]="正在配置 %s 交换文件" [setup_complete]="服务器内存设置完成" + [next_script]="现在请以 root 身份运行 ochenstarik-server-2.sh。" + ) ;; + ja) UI_TEXT+=( + [input_interrupted]="入力が中断されました" [invalid_number]="%s から %s の番号を入力してください" + [select_option]="オプション番号を入力してください: " [select_region]="地域を選択してください:" + [select_timezone]="%s のタイムゾーンを選択してください:" + [timezone_heading]="サーバーのタイムゾーンを選択してください(現在: %s):" + [other_timezone]="その他のタイムゾーン(地域から選択)" [timezone_set]="タイムゾーンを %s に設定しました" + [terminal_heading]="新しいターミナルセッションの言語を選択してください(現在: %s):" + [terminal_keep]="現在の言語を維持" [terminal_other]="完全なリストから別のロケールを選択" + [terminal_prompt]="言語番号 [1 — 変更しない]: " [terminal_unchanged]="ターミナルの言語は変更されませんでした" + [terminal_full_list]="完全なリストからロケールを選択してください:" + [program_heading]="インストールするプログラムグループを選択してください:" + [program_none]="追加プログラムをインストールしない" + [program_example]="番号を空白で区切って入力してください。例: 1 2 4 9。" + [program_prompt]="選択 [Enter — すべてインストール]: " [program_skipped]="追加プログラムのインストールをスキップしました" + [program_bad_number]="無効なグループ番号: %s" [program_out_of_range]="グループ番号が範囲外です: %s" + [program_selected]="選択したグループ: %s" [programs_installed]="選択したプログラムをインストールしました" + [apt_update]="パッケージリストを更新しています" [root_required]="このスクリプトを root で実行してください" + [swap_configuring]="%s のスワップファイルを設定しています" [setup_complete]="メモリ設定が完了しました" + [next_script]="次に ochenstarik-server-2.sh を root で実行してください。" + ) ;; + ar) UI_TEXT+=( + [input_interrupted]="تم إيقاف الإدخال" [invalid_number]="أدخل رقماً من %s إلى %s" + [select_option]="أدخل رقم الخيار: " [select_region]="اختر المنطقة:" [select_timezone]="اختر منطقة زمنية في %s:" + [timezone_heading]="اختر المنطقة الزمنية للخادم (الحالية: %s):" [other_timezone]="منطقة زمنية أخرى (حسب المنطقة)" + [timezone_set]="تم ضبط المنطقة الزمنية على %s" [terminal_heading]="اختر لغة جلسات الطرفية الجديدة (الحالية: %s):" + [terminal_keep]="الاحتفاظ باللغة الحالية" [terminal_other]="إعداد محلي آخر من القائمة الكاملة" + [terminal_prompt]="رقم اللغة [1 — الاحتفاظ]: " [terminal_unchanged]="لم تتغير لغة الطرفية" + [terminal_full_list]="اختر إعداداً محلياً من القائمة الكاملة:" + [program_heading]="اختر مجموعات البرامج المراد تثبيتها:" [program_none]="عدم تثبيت برامج إضافية" + [program_example]="أدخل الأرقام مفصولة بمسافات، مثال: 1 2 4 9." [program_prompt]="الاختيار [Enter — تثبيت الكل]: " + [program_skipped]="تم تخطي تثبيت البرامج الإضافية" [program_bad_number]="رقم مجموعة غير صالح: %s" + [program_out_of_range]="رقم المجموعة خارج النطاق: %s" [program_selected]="المجموعة المحددة: %s" + [programs_installed]="تم تثبيت البرامج المحددة" [apt_update]="جارٍ تحديث قائمة الحزم" + [root_required]="شغّل هذا البرنامج النصي بصلاحية root" [swap_configuring]="جارٍ إعداد ملف swap بحجم %s" + [setup_complete]="اكتمل إعداد ذاكرة الخادم" [next_script]="شغّل الآن ochenstarik-server-2.sh بصلاحية root." + ) ;; + hi) UI_TEXT+=( + [input_interrupted]="इनपुट बाधित हुआ" [invalid_number]="%s से %s तक की संख्या दर्ज करें" + [select_option]="विकल्प संख्या दर्ज करें: " [select_region]="क्षेत्र चुनें:" [select_timezone]="%s में समय क्षेत्र चुनें:" + [timezone_heading]="सर्वर का समय क्षेत्र चुनें (वर्तमान: %s):" [other_timezone]="अन्य समय क्षेत्र (क्षेत्र के अनुसार)" + [timezone_set]="समय क्षेत्र %s पर सेट किया गया" [terminal_heading]="नई टर्मिनल सत्रों की भाषा चुनें (वर्तमान: %s):" + [terminal_keep]="वर्तमान भाषा बनाए रखें" [terminal_other]="पूरी सूची से अन्य लोकेल" + [terminal_prompt]="भाषा संख्या [1 — वर्तमान रखें]: " [terminal_unchanged]="टर्मिनल भाषा नहीं बदली गई" + [terminal_full_list]="पूरी सूची से लोकेल चुनें:" + [program_heading]="इंस्टॉल करने के लिए प्रोग्राम समूह चुनें:" [program_none]="अतिरिक्त प्रोग्राम इंस्टॉल न करें" + [program_example]="खाली स्थान से अलग संख्याएँ दर्ज करें, उदाहरण: 1 2 4 9।" [program_prompt]="चयन [Enter — सभी इंस्टॉल करें]: " + [program_skipped]="अतिरिक्त प्रोग्राम की स्थापना छोड़ दी गई" [program_bad_number]="अमान्य समूह संख्या: %s" + [program_out_of_range]="समूह संख्या सीमा से बाहर है: %s" [program_selected]="चयनित समूह: %s" + [programs_installed]="चयनित प्रोग्राम इंस्टॉल हो गए" [apt_update]="पैकेज सूची अपडेट की जा रही है" + [root_required]="इस स्क्रिप्ट को root के रूप में चलाएँ" [swap_configuring]="%s swap फ़ाइल कॉन्फ़िगर की जा रही है" + [setup_complete]="सर्वर मेमोरी सेटअप पूरा हुआ" [next_script]="अब ochenstarik-server-2.sh को root के रूप में चलाएँ।" + ) ;; + esac +} + +msg() { + local key="$1" + shift + printf "${UI_TEXT[$key]}" "$@" +} + +localize_program_catalog() { + case "$UI_LANG" in + ru) PROGRAM_GROUP_TITLES=( + "Терминал и повседневная работа" "Диагностика сети" "Мониторинг системы и дисков" + "Безопасность и обслуживание Ubuntu" "Разработка и автоматизация" + "Архивы, таблицы и локальные данные" "Документы, PDF и OCR" + "Изображения, аудио и видео" "Резервное копирование и синхронизация" + ) ;; + es) PROGRAM_GROUP_TITLES=( + "Terminal y herramientas cotidianas" "Diagnóstico de red" "Supervisión del sistema y discos" + "Seguridad y mantenimiento de Ubuntu" "Desarrollo y automatización" + "Archivos, tablas y datos locales" "Documentos, PDF y OCR" + "Imágenes, audio y vídeo" "Copias de seguridad y sincronización" + ) ;; + de) PROGRAM_GROUP_TITLES=( + "Terminal und Alltagswerkzeuge" "Netzwerkdiagnose" "System- und Datenträgerüberwachung" + "Ubuntu-Sicherheit und Wartung" "Entwicklung und Automatisierung" + "Archive, Tabellen und lokale Daten" "Dokumente, PDF und OCR" + "Bilder, Audio und Video" "Sicherung und Synchronisierung" + ) ;; + fr) PROGRAM_GROUP_TITLES=( + "Terminal et outils quotidiens" "Diagnostic réseau" "Surveillance du système et des disques" + "Sécurité et maintenance d’Ubuntu" "Développement et automatisation" + "Archives, tableaux et données locales" "Documents, PDF et OCR" + "Images, audio et vidéo" "Sauvegarde et synchronisation" + ) ;; + pt) PROGRAM_GROUP_TITLES=( + "Terminal e ferramentas diárias" "Diagnóstico de rede" "Monitoramento do sistema e discos" + "Segurança e manutenção do Ubuntu" "Desenvolvimento e automação" + "Arquivos, tabelas e dados locais" "Documentos, PDF e OCR" + "Imagens, áudio e vídeo" "Backup e sincronização" + ) ;; + zh) PROGRAM_GROUP_TITLES=( + "终端与日常工具" "网络诊断" "系统与磁盘监控" "Ubuntu 安全与维护" "开发与自动化" + "压缩包、表格与本地数据" "文档、PDF 与 OCR" "图像、音频与视频" "备份与同步" + ) ;; + ja) PROGRAM_GROUP_TITLES=( + "ターミナルと日常ツール" "ネットワーク診断" "システムとディスクの監視" + "Ubuntu のセキュリティと保守" "開発と自動化" "アーカイブ、表、ローカルデータ" + "文書、PDF、OCR" "画像、音声、動画" "バックアップと同期" + ) ;; + ar) PROGRAM_GROUP_TITLES=( + "الطرفية والأدوات اليومية" "تشخيص الشبكة" "مراقبة النظام والأقراص" + "أمان Ubuntu وصيانته" "التطوير والأتمتة" "الأرشيفات والجداول والبيانات المحلية" + "المستندات وPDF وOCR" "الصور والصوت والفيديو" "النسخ الاحتياطي والمزامنة" + ) ;; + hi) PROGRAM_GROUP_TITLES=( + "टर्मिनल और दैनिक उपकरण" "नेटवर्क निदान" "सिस्टम और डिस्क निगरानी" + "Ubuntu सुरक्षा और रखरखाव" "विकास और स्वचालन" "अभिलेख, तालिकाएँ और स्थानीय डेटा" + "दस्तावेज़, PDF और OCR" "चित्र, ऑडियो और वीडियो" "बैकअप और सिंक्रोनाइज़ेशन" + ) ;; + esac +} + +choose_ui_language() { + local answer + printf '\nSelect the installer language / Выберите язык установщика:\n' + printf '%s\n' \ + ' 1) English' ' 2) Русский' ' 3) Español' ' 4) Deutsch' \ + ' 5) Français' ' 6) Português' ' 7) 中文' ' 8) 日本語' \ + ' 9) العربية' ' 10) हिन्दी' + while :; do + read -rp 'Language [1 — English]: ' answer || { printf '[x] Input was interrupted\n' >&2; exit 1; } + answer="${answer:-1}" + case "$answer" in + 1) UI_LANG=en ;; 2) UI_LANG=ru ;; 3) UI_LANG=es ;; 4) UI_LANG=de ;; + 5) UI_LANG=fr ;; 6) UI_LANG=pt ;; 7) UI_LANG=zh ;; 8) UI_LANG=ja ;; + 9) UI_LANG=ar ;; 10) UI_LANG=hi ;; + *) printf '[!] Enter a number from 1 to 10.\n' >&2; continue ;; + esac + break + done + load_ui_text + localize_program_catalog +} + +# English is active until the user answers the first prompt. This also keeps +# sourced helper functions usable by the automated tests. +load_ui_text log() { printf '[+] %s\n' "$*"; } warn() { printf '[!] %s\n' "$*" >&2; } @@ -64,14 +373,14 @@ ensure_package_tools() { local command_name for command_name in apt-get apt-cache dpkg-query; do command -v "$command_name" >/dev/null 2>&1 \ - || die "Required command not found: $command_name" + || die "$(msg required_missing "$command_name")" done } ensure_apt_updated() { [[ "$APT_UPDATED" == no ]] || return 0 ensure_package_tools - log "Обновление списка пакетов" + log "$(msg apt_update)" apt-get update APT_UPDATED=yes } @@ -131,7 +440,7 @@ choose_terminal_language() { local answer selected_locale selected_language primary_language package_name index local current_locale="${LANG:-не задана}" local -a titles=( - "Не менять язык терминала" + "$(msg terminal_keep)" "Русский — ru_RU.UTF-8" "English (US) — en_US.UTF-8" "Deutsch — de_DE.UTF-8" @@ -145,7 +454,7 @@ choose_terminal_language() { "简体中文 — zh_CN.UTF-8" "日本語 — ja_JP.UTF-8" "한국어 — ko_KR.UTF-8" - "Другая локаль из полного списка" + "$(msg terminal_other)" ) local -a locales=( "" ru_RU.UTF-8 en_US.UTF-8 de_DE.UTF-8 fr_FR.UTF-8 es_ES.UTF-8 @@ -166,17 +475,17 @@ choose_terminal_language() { newly_installed=() extra_packages=() while :; do - printf '\nВыберите язык новых терминальных сессий (сейчас: %s):\n' "$current_locale" + printf '\n%s\n' "$(msg terminal_heading "$current_locale")" for index in "${!titles[@]}"; do printf ' %d) %s\n' "$((index + 1))" "${titles[index]}" done - read -rp 'Номер языка [1 — не менять]: ' answer || die "Ввод прерван" + read -rp "$(msg terminal_prompt)" answer || die "$(msg input_interrupted)" answer="${answer:-1}" [[ "$answer" =~ ^[0-9]+$ ]] && ((answer >= 1 && answer <= ${#titles[@]})) && break - warn "Введите номер от 1 до ${#titles[@]}" + warn "$(msg invalid_number 1 "${#titles[@]}")" done - ((answer == 1)) && { log "Язык терминала не изменён"; return 0; } + ((answer == 1)) && { log "$(msg terminal_unchanged)"; return 0; } ensure_apt_updated save_original_locale @@ -184,7 +493,7 @@ choose_terminal_language() { package_is_installed locales || newly_installed+=(locales) apt-get install -y locales mapfile -t supported_locales < <(awk '{print $1}' /usr/share/i18n/SUPPORTED | sort -u) - choose_numbered_option "Выберите локаль из полного списка:" selected_locale \ + choose_numbered_option "$(msg terminal_full_list)" selected_locale \ "${supported_locales[@]}" primary_language="${selected_locale%%_*}" selected_language="${selected_locale%%.*}:${primary_language}" @@ -202,7 +511,7 @@ choose_terminal_language() { available_packages+=("$package_name") package_is_installed "$package_name" || newly_installed+=("$package_name") else - warn "Языковой пакет недоступен и будет пропущен: $package_name" + warn "$(msg locale_package_missing "$package_name")" fi done @@ -213,7 +522,7 @@ choose_terminal_language() { LC_MESSAGES="$selected_locale" record_new_packages "$LOCALE_PACKAGES" "${newly_installed[@]}" - log "Выбрана локаль ${selected_locale}. Она применится при следующем входе" + log "$(msg locale_selected "$selected_locale")" } choose_and_install_programs() { @@ -221,14 +530,14 @@ choose_and_install_programs() { local -a selected_groups=() group_packages=() requested=() available=() newly_installed=() local -A selected=() seen=() - printf '\nВыберите наборы программ для установки:\n' + printf '\n%s\n' "$(msg program_heading)" for group_index in "${!PROGRAM_GROUP_TITLES[@]}"; do printf ' %d) %s\n %s\n' "$((group_index + 1))" \ "${PROGRAM_GROUP_TITLES[group_index]}" "${PROGRAM_GROUP_DESCRIPTIONS[group_index]}" done - printf ' 0) Не устанавливать дополнительные программы\n' - printf '\nВведите номера через пробел, например: 1 2 4 9.\n' - read -rp 'Выбор [Enter — установить все наборы]: ' answer || die "Ввод прерван" + printf ' 0) %s\n' "$(msg program_none)" + printf '\n%s\n' "$(msg program_example)" + read -rp "$(msg program_prompt)" answer || die "$(msg input_interrupted)" answer="${answer//,/ }" if [[ -z "${answer//[[:space:]]/}" ]]; then @@ -236,14 +545,14 @@ choose_and_install_programs() { selected_groups+=("$group_index") done elif [[ "$answer" =~ ^[[:space:]]*0[[:space:]]*$ ]]; then - log "Установка дополнительных программ пропущена" + log "$(msg program_skipped)" return 0 else for token in $answer; do [[ "$token" =~ ^[0-9]+$ ]] \ - || die "Некорректный номер набора: $token" + || die "$(msg program_bad_number "$token")" ((token >= 1 && token <= ${#PROGRAM_GROUP_TITLES[@]})) \ - || die "Номер набора вне диапазона: $token" + || die "$(msg program_out_of_range "$token")" group_index="$((token - 1))" [[ -n "${selected[$group_index]:-}" ]] || selected_groups+=("$group_index") selected[$group_index]=yes @@ -252,7 +561,7 @@ choose_and_install_programs() { ensure_apt_updated for group_index in "${selected_groups[@]}"; do - log "Выбран набор: ${PROGRAM_GROUP_TITLES[group_index]}" + log "$(msg program_selected "${PROGRAM_GROUP_TITLES[group_index]}")" read -r -a group_packages <<< "${PROGRAM_GROUP_PACKAGES[group_index]}" for package_name in "${group_packages[@]}"; do [[ -n "${seen[$package_name]:-}" ]] && continue @@ -266,15 +575,15 @@ choose_and_install_programs() { available+=("$package_name") package_is_installed "$package_name" || newly_installed+=("$package_name") else - warn "Пакет отсутствует в подключённых репозиториях и пропущен: $package_name" + warn "$(msg package_missing "$package_name")" fi done - ((${#available[@]} > 0)) || die "Ни один выбранный пакет не найден" + ((${#available[@]} > 0)) || die "$(msg no_packages)" export DEBIAN_FRONTEND=noninteractive apt-get install -y "${available[@]}" record_new_packages "$PROGRAM_PACKAGES" "${newly_installed[@]}" - log "Выбранные программы установлены" + log "$(msg programs_installed)" } choose_numbered_option() { @@ -282,18 +591,18 @@ choose_numbered_option() { shift 2 local -a options=("$@") - ((${#options[@]} > 0)) || die "No options are available for: ${prompt}" + ((${#options[@]} > 0)) || die "$(msg no_options "$prompt")" while :; do printf '\n%s\n' "$prompt" for index in "${!options[@]}"; do printf ' %d) %s\n' "$((index + 1))" "${options[index]}" done - read -rp "Enter the option number: " answer || die "Input was interrupted" + read -rp "$(msg select_option)" answer || die "$(msg input_interrupted)" if [[ "$answer" =~ ^[0-9]+$ ]] && ((answer >= 1 && answer <= ${#options[@]})); then printf -v "$result_variable" '%s' "${options[answer - 1]}" return 0 fi - warn "Enter a number from 1 to ${#options[@]}" + warn "$(msg invalid_number 1 "${#options[@]}")" done } @@ -302,15 +611,15 @@ choose_timezone_from_full_list() { local -a regions region_timezones mapfile -t regions < <(timedatectl list-timezones | awk -F/ 'NF > 1 && !seen[$1]++ { print $1 }') - choose_numbered_option "Select a region:" region "${regions[@]}" + choose_numbered_option "$(msg select_region)" region "${regions[@]}" mapfile -t region_timezones < <(timedatectl list-timezones | awk -F/ -v selected_region="$region" '$1 == selected_region') - choose_numbered_option "Select a timezone in ${region}:" selected_timezone "${region_timezones[@]}" + choose_numbered_option "$(msg select_timezone "$region")" selected_timezone "${region_timezones[@]}" printf -v "$result_variable" '%s' "$selected_timezone" } choose_timezone() { local current_timezone timezone candidate - local full_list_option="Other timezone (select by region)" + local full_list_option="$(msg other_timezone)" local -a timezone_options=() current_timezone="$(timedatectl show --property=Timezone --value 2>/dev/null || true)" @@ -337,26 +646,27 @@ choose_timezone() { done timezone_options+=("$full_list_option") - choose_numbered_option "Select the server timezone (current: ${current_timezone}):" timezone "${timezone_options[@]}" + choose_numbered_option "$(msg timezone_heading "$current_timezone")" timezone "${timezone_options[@]}" if [[ "$timezone" == "$full_list_option" ]]; then choose_timezone_from_full_list timezone fi - timedatectl list-timezones | grep -Fqx -- "$timezone" || die "Unknown timezone: $timezone" + timedatectl list-timezones | grep -Fqx -- "$timezone" || die "$(msg unknown_timezone "$timezone")" timedatectl set-timezone "$timezone" - log "Timezone set to ${timezone}" + log "$(msg timezone_set "$timezone")" } [[ "$EUID" -eq 0 ]] || die "Run this script as root" +choose_ui_language for command_name in awk blkid fallocate grep mkswap swapon sysctl timedatectl; do - command -v "$command_name" >/dev/null 2>&1 || die "Required command not found: $command_name" + command -v "$command_name" >/dev/null 2>&1 || die "$(msg required_missing "$command_name")" done choose_timezone choose_terminal_language choose_and_install_programs -log "Configuring ${SWAPSIZE} swap file" +log "$(msg swap_configuring "$SWAPSIZE")" if [[ ! -e "$SWAPFILE" ]]; then fallocate -l "$SWAPSIZE" "$SWAPFILE" chmod 600 "$SWAPFILE" @@ -385,7 +695,7 @@ EOF chmod 644 "$SYSCTL_FILE" sysctl -p "$SYSCTL_FILE" >/dev/null -log "Server memory setup is complete" +log "$(msg setup_complete)" printf 'Timezone: %s\n' "$(timedatectl show --property=Timezone --value)" swapon --show -printf '\nNow run ochenstarik-server-2.sh as root.\n' +printf '\n%s\n' "$(msg next_script)" diff --git a/tests/test-step1-selection.sh b/tests/test-step1-selection.sh index 59bae7c..fd24e74 100644 --- a/tests/test-step1-selection.sh +++ b/tests/test-step1-selection.sh @@ -32,6 +32,14 @@ contains_package() { return 1 } +declare -a EXPECTED_UI_LANGUAGES=(en ru es de fr pt zh ja ar hi) +for index in "${!EXPECTED_UI_LANGUAGES[@]}"; do + choose_ui_language <<< "$((index + 1))" >/dev/null + [[ "$UI_LANG" == "${EXPECTED_UI_LANGUAGES[index]}" ]] +done +choose_ui_language <<< '' >/dev/null +[[ "$UI_LANG" == en ]] + choose_and_install_programs <<< '1 3' >/dev/null contains_package git contains_package iotop From 97a30aa6017c34b92d38c81df17d198ce58ac247 Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Wed, 15 Jul 2026 14:39:30 +0700 Subject: [PATCH 06/12] Fix silent step three SSH validation failure (#7) --- .github/workflows/bash-syntax.yml | 3 +++ ochenstarik-server-user-3.sh | 18 +++++++++++++++--- tests/test-step3-sshd-parsing.sh | 18 ++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 tests/test-step3-sshd-parsing.sh diff --git a/.github/workflows/bash-syntax.yml b/.github/workflows/bash-syntax.yml index 222bc1b..b1f8133 100644 --- a/.github/workflows/bash-syntax.yml +++ b/.github/workflows/bash-syntax.yml @@ -25,3 +25,6 @@ jobs: - name: Test step 1 selections run: bash tests/test-step1-selection.sh + + - name: Test step 3 SSH configuration parsing + run: bash tests/test-step3-sshd-parsing.sh diff --git a/ochenstarik-server-user-3.sh b/ochenstarik-server-user-3.sh index ef1caa4..614a8f7 100644 --- a/ochenstarik-server-user-3.sh +++ b/ochenstarik-server-user-3.sh @@ -60,6 +60,13 @@ require_command() { command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1" } +read_sshd_setting() { + local setting="$1" config="$2" value + value="$(awk -v setting="$setting" '$1 == setting { print $2; exit }' <<< "$config")" + [[ -n "$value" ]] || die "SSH setting was not found in effective configuration: $setting" + printf '%s' "$value" +} + is_valid_port() { local port="$1" [[ "$port" =~ ^[0-9]{1,5}$ ]] || return 1 @@ -465,9 +472,14 @@ EOF chmod 644 "$SSHD_DROPIN" sshd -t || die "sshd syntax validation failed" -effective_port="$(sshd -T | awk '$1 == "port" { print $2; exit }')" -effective_root="$(sshd -T | awk '$1 == "permitrootlogin" { print $2; exit }')" -effective_password="$(sshd -T | awk '$1 == "passwordauthentication" { print $2; exit }')" +# Capture the complete output once. With pipefail enabled, piping `sshd -T` +# into an awk program that exits after the first match can terminate sshd with +# SIGPIPE (status 141) and abort the script even though the configuration is +# valid. +effective_sshd_config="$(sshd -T)" || die "Could not read effective SSH configuration" +effective_port="$(read_sshd_setting port "$effective_sshd_config")" +effective_root="$(read_sshd_setting permitrootlogin "$effective_sshd_config")" +effective_password="$(read_sshd_setting passwordauthentication "$effective_sshd_config")" [[ "$effective_port" == "$SSH_PORT" ]] || die "Effective SSH port is $effective_port, expected $SSH_PORT" [[ "$effective_root" == no ]] || die "Effective PermitRootLogin is $effective_root, expected no" [[ "$effective_password" == "$PASSWORD_AUTH" ]] || die "Effective PasswordAuthentication is $effective_password, expected $PASSWORD_AUTH" diff --git a/tests/test-step3-sshd-parsing.sh b/tests/test-step3-sshd-parsing.sh new file mode 100644 index 0000000..391ea08 --- /dev/null +++ b/tests/test-step3-sshd-parsing.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." +source <(awk '/^\[\[ "\$EUID"/{exit} {print}' ochenstarik-server-user-3.sh) + +effective_config=$'port 20202\npermitrootlogin no\npasswordauthentication no\npubkeyauthentication yes' + +[[ "$(read_sshd_setting port "$effective_config")" == 20202 ]] +[[ "$(read_sshd_setting permitrootlogin "$effective_config")" == no ]] +[[ "$(read_sshd_setting passwordauthentication "$effective_config")" == no ]] + +if grep -E 'sshd[[:space:]]+-T[[:space:]]*\|' ochenstarik-server-user-3.sh; then + printf 'Unsafe sshd -T pipeline found.\n' >&2 + exit 1 +fi + +printf 'Step 3 SSH configuration parsing tests passed.\n' From 52b8c96a2ff267c025e1ec107bb4f8c1c3387170 Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Wed, 15 Jul 2026 14:55:33 +0700 Subject: [PATCH 07/12] Move language selection to installer master (#8) --- .github/workflows/bash-syntax.yml | 3 + README.md | 10 +- ochenstarik-server-1.sh | 34 ++--- ochenstarik-server-install.sh | 227 +++++++++++++++++++++++++----- tests/test-master-language.sh | 22 +++ tests/test-step1-selection.sh | 8 -- 6 files changed, 231 insertions(+), 73 deletions(-) create mode 100644 tests/test-master-language.sh diff --git a/.github/workflows/bash-syntax.yml b/.github/workflows/bash-syntax.yml index b1f8133..3313963 100644 --- a/.github/workflows/bash-syntax.yml +++ b/.github/workflows/bash-syntax.yml @@ -26,5 +26,8 @@ jobs: - name: Test step 1 selections run: bash tests/test-step1-selection.sh + - name: Test master language selection + run: bash tests/test-master-language.sh + - name: Test step 3 SSH configuration parsing run: bash tests/test-step3-sshd-parsing.sh diff --git a/README.md b/README.md index 9301c4e..6150fb0 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ chmod 700 ./*.sh sudo ./ochenstarik-server-install.sh ``` -Мастер показывает этапы 1–7 по порядку. На каждом этапе можно выбрать установку, пропуск или завершение. В первом этапе пустой ввод устанавливает все рекомендуемые наборы программ. Во время изменения SSH-порта не закрывайте текущую сессию — сначала проверьте новый вход во втором терминале. +Первым запросом мастер предлагает выбрать язык диалога и только затем показывает этапы 1–7 по порядку. На каждом этапе можно выбрать установку, пропуск или завершение. В первом этапе пустой ввод устанавливает все рекомендуемые наборы программ. Во время изменения SSH-порта не закрывайте текущую сессию — сначала проверьте новый вход во втором терминале. Если нужен только мастер без скачивания полного архива: @@ -50,8 +50,8 @@ sudo ./ochenstarik-server-install.sh | Файл | Назначение | Можно запускать отдельно | | --- | --- | --- | -| `ochenstarik-server-install.sh` | Единый пошаговый мастер: установить, пропустить или завершить на каждом этапе | Да; рекомендуемый способ полной настройки | -| `ochenstarik-server-1.sh` | Предлагает выбрать язык диалога, часовой пояс, язык терминала и наборы программ, создаёт swap-файл 2 ГБ | Да | +| `ochenstarik-server-install.sh` | Сначала выбирает язык диалога, затем последовательно предлагает установить, пропустить или завершить каждый этап | Да; рекомендуемый способ полной настройки | +| `ochenstarik-server-1.sh` | Предлагает выбрать часовой пояс, язык терминала и наборы программ, создаёт swap-файл 2 ГБ | Да | | `ochenstarik-server-2.sh` | Предлагает будущий SSH-порт и IPv4/IPv6, обновляет систему, ставит обязательные пакеты и настраивает UFW | Да | | `ochenstarik-server-user-3.sh` | Создаёт администратора, переносит SSH на порт из этапа 2, проверяет права и включает fail2ban | После установки необходимых пакетов; проще всего сначала запустить скрипт 2 | | `ochenstarik-server-tg-4.sh` | Отправляет уведомления в Telegram при успешном входе по SSH | После установки OpenSSH и `curl`; обычно после скрипта 2 или 3 | @@ -115,9 +115,9 @@ bash -n ./ИМЯ-СКРИПТА.sh Файл: `ochenstarik-server-1.sh`. -Самый первый запрос выбирает язык диалога установщика: English, Русский, Español, Deutsch, Français, Português, 中文, 日本語, العربية или हिन्दी. Начальное меню выводится на английском, а пустой ввод оставляет English. Этот выбор меняет вопросы и сообщения скрипта, но не системную локаль сервера. +При запуске единого мастера самый первый запрос, ещё до выбора этапа 1, задаёт язык диалога: English, Русский, Español, Deutsch, Français, Português, 中文, 日本語, العربية или हिन्दी. Начальное меню выводится на английском, а пустой ввод оставляет English. Выбор передаётся первому этапу автоматически и не меняет системную локаль сервера. -Затем показывается нумерованное меню популярных часовых поясов, включая `Asia/Novosibirsk`, `Europe/Moscow` и `UTC`. Нужную зону достаточно выбрать по номеру. Для остальных зон предусмотрен полный список с последовательным выбором региона и часового пояса — вводить имя вручную не требуется. +Первый этап начинается с нумерованного меню популярных часовых поясов, включая `Asia/Novosibirsk`, `Europe/Moscow` и `UTC`. Нужную зону достаточно выбрать по номеру. Для остальных зон предусмотрен полный список с последовательным выбором региона и часового пояса — вводить имя вручную не требуется. После выбора времени скрипт показывает меню языков терминала: оставить текущий, русский, английский, немецкий, французский, испанский, итальянский, португальский, польский, украинский, турецкий, китайский, японский, корейский либо выбрать другую локаль из полного системного списка. Выбранный язык применяется к новым терминальным сессиям. Исходный `/etc/default/locale` сохраняется для последующего восстановления. diff --git a/ochenstarik-server-1.sh b/ochenstarik-server-1.sh index d3be6c5..dbb94aa 100644 --- a/ochenstarik-server-1.sh +++ b/ochenstarik-server-1.sh @@ -51,7 +51,11 @@ readonly -a PROGRAM_GROUP_PACKAGES=( ) APT_UPDATED=no -UI_LANG=en +UI_LANG="${OCHENSTARIK_UI_LANG:-en}" +case "$UI_LANG" in + en|ru|es|de|fr|pt|zh|ja|ar|hi) ;; + *) UI_LANG=en ;; +esac declare -A UI_TEXT=() @@ -335,31 +339,10 @@ localize_program_catalog() { esac } -choose_ui_language() { - local answer - printf '\nSelect the installer language / Выберите язык установщика:\n' - printf '%s\n' \ - ' 1) English' ' 2) Русский' ' 3) Español' ' 4) Deutsch' \ - ' 5) Français' ' 6) Português' ' 7) 中文' ' 8) 日本語' \ - ' 9) العربية' ' 10) हिन्दी' - while :; do - read -rp 'Language [1 — English]: ' answer || { printf '[x] Input was interrupted\n' >&2; exit 1; } - answer="${answer:-1}" - case "$answer" in - 1) UI_LANG=en ;; 2) UI_LANG=ru ;; 3) UI_LANG=es ;; 4) UI_LANG=de ;; - 5) UI_LANG=fr ;; 6) UI_LANG=pt ;; 7) UI_LANG=zh ;; 8) UI_LANG=ja ;; - 9) UI_LANG=ar ;; 10) UI_LANG=hi ;; - *) printf '[!] Enter a number from 1 to 10.\n' >&2; continue ;; - esac - break - done - load_ui_text - localize_program_catalog -} - -# English is active until the user answers the first prompt. This also keeps -# sourced helper functions usable by the automated tests. +# The unified installer passes the selected dialog language through the +# environment. A directly launched step uses English by default. load_ui_text +localize_program_catalog log() { printf '[+] %s\n' "$*"; } warn() { printf '[!] %s\n' "$*" >&2; } @@ -657,7 +640,6 @@ choose_timezone() { } [[ "$EUID" -eq 0 ]] || die "Run this script as root" -choose_ui_language for command_name in awk blkid fallocate grep mkswap swapon sysctl timedatectl; do command -v "$command_name" >/dev/null 2>&1 || die "$(msg required_missing "$command_name")" done diff --git a/ochenstarik-server-install.sh b/ochenstarik-server-install.sh index 9c9240b..c6f7b4b 100644 --- a/ochenstarik-server-install.sh +++ b/ochenstarik-server-install.sh @@ -15,16 +15,180 @@ declare -a STEP_FILES=( ) declare -a STEP_TITLES=( - "Часовой пояс, язык терминала, набор программ и swap" - "Базовые пакеты, будущий SSH-порт, IPv4/IPv6 и UFW" - "Администратор, перенос SSH и fail2ban" - "Telegram-уведомления о входах по SSH" - "Системный VPN через Xray" - "Панель 3x-ui и Cloudflare WARP" - "Первичный снимок и расписания резервного копирования" + "Timezone, terminal language, programs, and swap" + "Base packages, SSH port, IPv4/IPv6, and UFW" + "Administrator, SSH migration, and fail2ban" + "Telegram notifications for SSH logins" + "System VPN through Xray" + "3x-ui panel and Cloudflare WARP" + "Initial snapshot and backup schedules" ) TEMP_FILE="" +UI_LANG=en +declare -A MASTER_TEXT=() + +load_master_text() { + MASTER_TEXT=( + [intro_title]="Unified lightweight-server installer" + [intro_body]="Steps are shown in order. Each step can be installed, skipped, or used to exit the installer. Keep the current SSH session open while changing the SSH port and test the new login in another terminal." + [step]="Step %s of %s: %s" + [install]="Install / run this step" + [skip]="Skip and continue to the next step" + [finish]="Exit the installer" + [action]="Select an action: " + [invalid]="Enter 1, 2, or 3" + [interrupted]="Input was interrupted" + [failed]="The step failed." + [retry]="Run this step again" + [summary]="Installer completed." + [completed]="Steps completed successfully: %s" + [skipped]="Steps skipped: %s" + [rerun]="You can run the installer again; every step supports reconfiguration." + [reset]="For a complete reset, use ochenstarik-server-uninstall.sh." + [launch]="Running step %s: %s" + [success]="Step %s completed successfully" + [error]="Step %s returned an error" + ) + + case "$UI_LANG" in + ru) MASTER_TEXT+=( + [intro_title]="Единый мастер установки lightweight-server" + [intro_body]="Этапы будут показаны по порядку. Каждый можно установить, пропустить или завершить мастер. Не закрывайте текущую SSH-сессию при изменении порта и проверьте новый вход во втором терминале." + [step]="Этап %s из %s: %s" [install]="Установить / запустить этот этап" + [skip]="Пропустить и перейти к следующему" [finish]="Завершить мастер" + [action]="Выберите действие: " [invalid]="Введите 1, 2 или 3" [interrupted]="Ввод прерван" + [failed]="Этап завершился с ошибкой." [retry]="Запустить этот этап повторно" + [summary]="Мастер установки завершён." [completed]="Успешно выполнено этапов: %s" + [skipped]="Пропущено этапов: %s" + [rerun]="Мастер можно запустить повторно: каждый этап поддерживает повторную настройку." + [reset]="Для полного сброса используйте ochenstarik-server-uninstall.sh." + [launch]="Запуск этапа %s: %s" [success]="Этап %s успешно завершён" [error]="Этап %s вернул ошибку" + ) ;; + es) MASTER_TEXT+=( + [intro_title]="Instalador unificado de lightweight-server" + [intro_body]="Los pasos se muestran en orden. Puede instalar, omitir o salir en cada paso. Mantenga abierta la sesión SSH actual al cambiar el puerto y pruebe el nuevo acceso en otro terminal." + [step]="Paso %s de %s: %s" [install]="Instalar / ejecutar este paso" [skip]="Omitir y continuar" + [finish]="Salir del instalador" [action]="Seleccione una acción: " [invalid]="Introduzca 1, 2 o 3" + [interrupted]="Entrada interrumpida" [failed]="El paso terminó con un error." [retry]="Ejecutar este paso de nuevo" + [summary]="Instalación finalizada." [completed]="Pasos completados: %s" [skipped]="Pasos omitidos: %s" + [rerun]="Puede ejecutar de nuevo el instalador; cada paso admite reconfiguración." + [reset]="Para un reinicio completo, use ochenstarik-server-uninstall.sh." + [launch]="Ejecutando el paso %s: %s" [success]="Paso %s completado" [error]="El paso %s devolvió un error" + ) ;; + de) MASTER_TEXT+=( + [intro_title]="Einheitlicher lightweight-server-Installer" + [intro_body]="Die Schritte werden nacheinander angezeigt. Jeder Schritt kann installiert, übersprungen oder zum Beenden verwendet werden. Lassen Sie beim Ändern des SSH-Ports die aktuelle Sitzung geöffnet und testen Sie die neue Anmeldung in einem zweiten Terminal." + [step]="Schritt %s von %s: %s" [install]="Diesen Schritt installieren / ausführen" [skip]="Überspringen und fortfahren" + [finish]="Installer beenden" [action]="Aktion auswählen: " [invalid]="Geben Sie 1, 2 oder 3 ein" + [interrupted]="Eingabe unterbrochen" [failed]="Der Schritt ist fehlgeschlagen." [retry]="Diesen Schritt erneut ausführen" + [summary]="Installation abgeschlossen." [completed]="Erfolgreiche Schritte: %s" [skipped]="Übersprungene Schritte: %s" + [rerun]="Der Installer kann erneut ausgeführt werden; jeder Schritt unterstützt eine Neukonfiguration." + [reset]="Für ein vollständiges Zurücksetzen verwenden Sie ochenstarik-server-uninstall.sh." + [launch]="Schritt %s wird ausgeführt: %s" [success]="Schritt %s erfolgreich abgeschlossen" [error]="Schritt %s meldete einen Fehler" + ) ;; + fr) MASTER_TEXT+=( + [intro_title]="Programme d’installation unifié lightweight-server" + [intro_body]="Les étapes sont présentées dans l’ordre. Chacune peut être installée, ignorée ou utilisée pour quitter. Gardez la session SSH actuelle ouverte pendant le changement de port et testez la nouvelle connexion dans un autre terminal." + [step]="Étape %s sur %s : %s" [install]="Installer / exécuter cette étape" [skip]="Ignorer et continuer" + [finish]="Quitter l’installation" [action]="Sélectionnez une action : " [invalid]="Saisissez 1, 2 ou 3" + [interrupted]="Saisie interrompue" [failed]="L’étape a échoué." [retry]="Relancer cette étape" + [summary]="Installation terminée." [completed]="Étapes réussies : %s" [skipped]="Étapes ignorées : %s" + [rerun]="Vous pouvez relancer l’installation ; chaque étape accepte une reconfiguration." + [reset]="Pour une réinitialisation complète, utilisez ochenstarik-server-uninstall.sh." + [launch]="Exécution de l’étape %s : %s" [success]="Étape %s réussie" [error]="L’étape %s a renvoyé une erreur" + ) ;; + pt) MASTER_TEXT+=( + [intro_title]="Instalador unificado do lightweight-server" + [intro_body]="As etapas são mostradas em ordem. Cada etapa pode ser instalada, ignorada ou usada para sair. Mantenha a sessão SSH atual aberta ao alterar a porta e teste o novo acesso em outro terminal." + [step]="Etapa %s de %s: %s" [install]="Instalar / executar esta etapa" [skip]="Ignorar e continuar" + [finish]="Sair do instalador" [action]="Selecione uma ação: " [invalid]="Digite 1, 2 ou 3" + [interrupted]="Entrada interrompida" [failed]="A etapa falhou." [retry]="Executar esta etapa novamente" + [summary]="Instalação concluída." [completed]="Etapas concluídas: %s" [skipped]="Etapas ignoradas: %s" + [rerun]="O instalador pode ser executado novamente; todas as etapas aceitam reconfiguração." + [reset]="Para redefinir tudo, use ochenstarik-server-uninstall.sh." + [launch]="Executando a etapa %s: %s" [success]="Etapa %s concluída" [error]="A etapa %s retornou um erro" + ) ;; + zh) MASTER_TEXT+=( + [intro_title]="lightweight-server 统一安装程序" [intro_body]="各步骤按顺序显示。每一步都可以安装、跳过或退出。更改 SSH 端口时请保持当前会话,并在另一个终端测试新连接。" + [step]="第 %s/%s 步:%s" [install]="安装 / 运行此步骤" [skip]="跳过并继续" [finish]="退出安装程序" + [action]="请选择操作:" [invalid]="请输入 1、2 或 3" [interrupted]="输入已中断" + [failed]="此步骤执行失败。" [retry]="重新运行此步骤" [summary]="安装程序已完成。" + [completed]="成功完成的步骤:%s" [skipped]="跳过的步骤:%s" [rerun]="可以再次运行安装程序;每个步骤都支持重新配置。" + [reset]="如需完全重置,请使用 ochenstarik-server-uninstall.sh。" [launch]="正在运行步骤 %s:%s" + [success]="步骤 %s 已成功完成" [error]="步骤 %s 返回错误" + ) ;; + ja) MASTER_TEXT+=( + [intro_title]="lightweight-server 統合インストーラー" [intro_body]="手順は順番に表示されます。各手順は実行、スキップ、または終了できます。SSH ポートの変更中は現在のセッションを維持し、別のターミナルで新しい接続を確認してください。" + [step]="ステップ %s/%s: %s" [install]="このステップをインストール / 実行" [skip]="スキップして次へ" + [finish]="インストーラーを終了" [action]="操作を選択してください: " [invalid]="1、2、3 のいずれかを入力してください" + [interrupted]="入力が中断されました" [failed]="ステップが失敗しました。" [retry]="このステップを再実行" + [summary]="インストールが完了しました。" [completed]="成功したステップ: %s" [skipped]="スキップしたステップ: %s" + [rerun]="インストーラーは再実行でき、各ステップを再設定できます。" [reset]="完全にリセットするには ochenstarik-server-uninstall.sh を使用してください。" + [launch]="ステップ %s を実行中: %s" [success]="ステップ %s が完了しました" [error]="ステップ %s でエラーが発生しました" + ) ;; + ar) MASTER_TEXT+=( + [intro_title]="مثبت lightweight-server الموحد" [intro_body]="تظهر المراحل بالترتيب. يمكن تثبيت كل مرحلة أو تخطيها أو الخروج. أبقِ جلسة SSH الحالية مفتوحة عند تغيير المنفذ واختبر الاتصال الجديد في طرفية أخرى." + [step]="المرحلة %s من %s: %s" [install]="تثبيت / تشغيل هذه المرحلة" [skip]="تخطي والمتابعة" + [finish]="إنهاء المثبت" [action]="اختر إجراءً: " [invalid]="أدخل 1 أو 2 أو 3" [interrupted]="تم إيقاف الإدخال" + [failed]="فشلت المرحلة." [retry]="تشغيل هذه المرحلة مرة أخرى" [summary]="اكتمل المثبت." + [completed]="المراحل المكتملة: %s" [skipped]="المراحل المتخطاة: %s" [rerun]="يمكن تشغيل المثبت مرة أخرى؛ كل مرحلة تدعم إعادة الإعداد." + [reset]="لإعادة الضبط الكامل استخدم ochenstarik-server-uninstall.sh." [launch]="تشغيل المرحلة %s: %s" + [success]="اكتملت المرحلة %s" [error]="أعادت المرحلة %s خطأ" + ) ;; + hi) MASTER_TEXT+=( + [intro_title]="एकीकृत lightweight-server इंस्टॉलर" [intro_body]="चरण क्रम से दिखाए जाते हैं। प्रत्येक चरण को चलाया, छोड़ा या इंस्टॉलर बंद किया जा सकता है। SSH पोर्ट बदलते समय वर्तमान सत्र खुला रखें और दूसरे टर्मिनल में नया लॉगिन जाँचें।" + [step]="चरण %s/%s: %s" [install]="यह चरण इंस्टॉल / चलाएँ" [skip]="छोड़ें और आगे बढ़ें" + [finish]="इंस्टॉलर बंद करें" [action]="कार्रवाई चुनें: " [invalid]="1, 2 या 3 दर्ज करें" [interrupted]="इनपुट बाधित हुआ" + [failed]="चरण विफल हुआ।" [retry]="यह चरण फिर चलाएँ" [summary]="इंस्टॉलर पूरा हुआ।" + [completed]="सफल चरण: %s" [skipped]="छोड़े गए चरण: %s" [rerun]="इंस्टॉलर फिर चलाया जा सकता है; हर चरण पुनः कॉन्फ़िगर हो सकता है।" + [reset]="पूर्ण रीसेट के लिए ochenstarik-server-uninstall.sh का उपयोग करें।" [launch]="चरण %s चल रहा है: %s" + [success]="चरण %s सफल रहा" [error]="चरण %s में त्रुटि हुई" + ) ;; + esac +} + +master_msg() { + local key="$1" + shift + printf "${MASTER_TEXT[$key]}" "$@" +} + +localize_step_titles() { + case "$UI_LANG" in + ru) STEP_TITLES=("Часовой пояс, язык терминала, программы и swap" "Базовые пакеты, SSH-порт, IPv4/IPv6 и UFW" "Администратор, перенос SSH и fail2ban" "Telegram-уведомления о входах по SSH" "Системный VPN через Xray" "Панель 3x-ui и Cloudflare WARP" "Первичный снимок и расписания бэкапов") ;; + es) STEP_TITLES=("Zona horaria, idioma del terminal, programas y swap" "Paquetes base, puerto SSH, IPv4/IPv6 y UFW" "Administrador, migración SSH y fail2ban" "Notificaciones de Telegram para accesos SSH" "VPN del sistema mediante Xray" "Panel 3x-ui y Cloudflare WARP" "Instantánea inicial y copias programadas") ;; + de) STEP_TITLES=("Zeitzone, Terminalsprache, Programme und Swap" "Basispakete, SSH-Port, IPv4/IPv6 und UFW" "Administrator, SSH-Umstellung und fail2ban" "Telegram-Benachrichtigungen für SSH-Anmeldungen" "System-VPN über Xray" "3x-ui und Cloudflare WARP" "Erstsicherung und Sicherungspläne") ;; + fr) STEP_TITLES=("Fuseau horaire, langue du terminal, programmes et swap" "Paquets de base, port SSH, IPv4/IPv6 et UFW" "Administrateur, migration SSH et fail2ban" "Notifications Telegram des connexions SSH" "VPN système via Xray" "Panneau 3x-ui et Cloudflare WARP" "Instantané initial et sauvegardes planifiées") ;; + pt) STEP_TITLES=("Fuso horário, idioma do terminal, programas e swap" "Pacotes básicos, porta SSH, IPv4/IPv6 e UFW" "Administrador, migração SSH e fail2ban" "Notificações Telegram de logins SSH" "VPN do sistema via Xray" "Painel 3x-ui e Cloudflare WARP" "Snapshot inicial e backups agendados") ;; + zh) STEP_TITLES=("时区、终端语言、程序和交换文件" "基础软件包、SSH 端口、IPv4/IPv6 和 UFW" "管理员、SSH 迁移和 fail2ban" "SSH 登录的 Telegram 通知" "通过 Xray 的系统 VPN" "3x-ui 面板和 Cloudflare WARP" "初始快照和备份计划") ;; + ja) STEP_TITLES=("タイムゾーン、端末言語、プログラム、スワップ" "基本パッケージ、SSH ポート、IPv4/IPv6、UFW" "管理者、SSH 移行、fail2ban" "SSH ログインの Telegram 通知" "Xray によるシステム VPN" "3x-ui パネルと Cloudflare WARP" "初回スナップショットとバックアップ予定") ;; + ar) STEP_TITLES=("المنطقة الزمنية ولغة الطرفية والبرامج وswap" "الحزم الأساسية ومنفذ SSH وIPv4/IPv6 وUFW" "المسؤول ونقل SSH وfail2ban" "إشعارات Telegram لدخول SSH" "VPN للنظام عبر Xray" "لوحة 3x-ui وCloudflare WARP" "النسخة الأولية وجداول النسخ الاحتياطي") ;; + hi) STEP_TITLES=("समय क्षेत्र, टर्मिनल भाषा, प्रोग्राम और swap" "मूल पैकेज, SSH पोर्ट, IPv4/IPv6 और UFW" "प्रशासक, SSH स्थानांतरण और fail2ban" "SSH लॉगिन की Telegram सूचनाएँ" "Xray द्वारा सिस्टम VPN" "3x-ui पैनल और Cloudflare WARP" "प्रारंभिक स्नैपशॉट और बैकअप अनुसूचियाँ") ;; + esac +} + +choose_dialog_language() { + local answer + printf '\nSelect the installer language / Выберите язык установщика:\n' + printf '%s\n' ' 1) English' ' 2) Русский' ' 3) Español' ' 4) Deutsch' \ + ' 5) Français' ' 6) Português' ' 7) 中文' ' 8) 日本語' ' 9) العربية' ' 10) हिन्दी' + while :; do + read -rp 'Language [1 — English]: ' answer || die "Input was interrupted" + answer="${answer:-1}" + case "$answer" in + 1) UI_LANG=en ;; 2) UI_LANG=ru ;; 3) UI_LANG=es ;; 4) UI_LANG=de ;; 5) UI_LANG=fr ;; + 6) UI_LANG=pt ;; 7) UI_LANG=zh ;; 8) UI_LANG=ja ;; 9) UI_LANG=ar ;; 10) UI_LANG=hi ;; + *) warn "Enter a number from 1 to 10"; continue ;; + esac + break + done + export OCHENSTARIK_UI_LANG="$UI_LANG" + load_master_text + localize_step_titles +} + +load_master_text log() { printf '[+] %s\n' "$*"; } warn() { printf '[!] %s\n' "$*" >&2; } @@ -71,14 +235,14 @@ choose_step_action() { local step_number="$1" title="$2" result_variable="$3" answer while :; do printf '\n============================================================\n' - printf 'Этап %s из %s: %s\n' "$step_number" "${#STEP_FILES[@]}" "$title" - printf ' 1) Установить / запустить этот этап\n' - printf ' 2) Пропустить и перейти к следующему\n' - printf ' 3) Завершить мастер\n' - read -rp 'Выберите действие: ' answer || die "Ввод прерван" + printf '%s\n' "$(master_msg step "$step_number" "${#STEP_FILES[@]}" "$title")" + printf ' 1) %s\n' "$(master_msg install)" + printf ' 2) %s\n' "$(master_msg skip)" + printf ' 3) %s\n' "$(master_msg finish)" + read -rp "$(master_msg action)" answer || die "$(master_msg interrupted)" case "$answer" in 1|2|3) printf -v "$result_variable" '%s' "$answer"; return 0 ;; - *) warn "Введите 1, 2 или 3" ;; + *) warn "$(master_msg invalid)" ;; esac done } @@ -86,14 +250,14 @@ choose_step_action() { choose_after_failure() { local result_variable="$1" answer while :; do - printf '\nЭтап завершился с ошибкой.\n' - printf ' 1) Запустить этот этап повторно\n' - printf ' 2) Пропустить и перейти к следующему\n' - printf ' 3) Завершить мастер\n' - read -rp 'Выберите действие: ' answer || die "Ввод прерван" + printf '\n%s\n' "$(master_msg failed)" + printf ' 1) %s\n' "$(master_msg retry)" + printf ' 2) %s\n' "$(master_msg skip)" + printf ' 3) %s\n' "$(master_msg finish)" + read -rp "$(master_msg action)" answer || die "$(master_msg interrupted)" case "$answer" in 1|2|3) printf -v "$result_variable" '%s' "$answer"; return 0 ;; - *) warn "Введите 1, 2 или 3" ;; + *) warn "$(master_msg invalid)" ;; esac done } @@ -101,24 +265,19 @@ choose_after_failure() { print_summary() { local completed="$1" skipped="$2" printf '\n============================================================\n' - printf 'Мастер установки завершён.\n' - printf 'Успешно выполнено этапов: %s\n' "$completed" - printf 'Пропущено этапов: %s\n' "$skipped" - printf '\nМастер можно запустить повторно: каждый этап поддерживает повторную настройку.\n' - printf 'Для полного сброса используйте ochenstarik-server-uninstall.sh.\n' + printf '%s\n' "$(master_msg summary)" + printf '%s\n' "$(master_msg completed "$completed")" + printf '%s\n' "$(master_msg skipped "$skipped")" + printf '\n%s\n' "$(master_msg rerun)" + printf '%s\n' "$(master_msg reset)" } [[ "$EUID" -eq 0 ]] || die "Запустите мастер от имени root: sudo ./ochenstarik-server-install.sh" ((${#STEP_FILES[@]} == ${#STEP_TITLES[@]})) || die "Некорректное описание этапов" -cat <<'INTRO' +choose_dialog_language -Единый мастер установки lightweight-server - -Этапы будут показаны по порядку. Каждый из них можно выполнить, пропустить -или завершить весь мастер. Не закрывайте текущую SSH-сессию во время изменения -порта SSH и проверяйте новый вход во втором терминале. -INTRO +printf '\n%s\n\n%s\n' "$(master_msg intro_title)" "$(master_msg intro_body)" completed=0 skipped=0 @@ -141,14 +300,14 @@ for index in "${!STEP_FILES[@]}"; do script_path="" ensure_step_script "${STEP_FILES[index]}" script_path while :; do - log "Запуск этапа ${step_number}: ${STEP_TITLES[index]}" + log "$(master_msg launch "$step_number" "${STEP_TITLES[index]}")" if bash -- "$script_path"; then completed="$((completed + 1))" - log "Этап ${step_number} успешно завершён" + log "$(master_msg success "$step_number")" break fi - warn "Этап ${step_number} вернул ошибку" + warn "$(master_msg error "$step_number")" failure_action="" choose_after_failure failure_action case "$failure_action" in diff --git a/tests/test-master-language.sh b/tests/test-master-language.sh new file mode 100644 index 0000000..dc58172 --- /dev/null +++ b/tests/test-master-language.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." +source <(awk '/^\[\[ "\$EUID"/{exit} {print}' ochenstarik-server-install.sh) + +declare -a expected=(en ru es de fr pt zh ja ar hi) +for index in "${!expected[@]}"; do + choose_dialog_language <<< "$((index + 1))" >/dev/null + [[ "$UI_LANG" == "${expected[index]}" ]] + [[ "$OCHENSTARIK_UI_LANG" == "${expected[index]}" ]] +done + +choose_dialog_language <<< '' >/dev/null +[[ "$UI_LANG" == en ]] +[[ "$(master_msg install)" == 'Install / run this step' ]] + +choose_dialog_language <<< '2' >/dev/null +[[ "$(master_msg install)" == 'Установить / запустить этот этап' ]] +[[ "${STEP_TITLES[0]}" == 'Часовой пояс, язык терминала, программы и swap' ]] + +printf 'Master language selection tests passed.\n' diff --git a/tests/test-step1-selection.sh b/tests/test-step1-selection.sh index fd24e74..59bae7c 100644 --- a/tests/test-step1-selection.sh +++ b/tests/test-step1-selection.sh @@ -32,14 +32,6 @@ contains_package() { return 1 } -declare -a EXPECTED_UI_LANGUAGES=(en ru es de fr pt zh ja ar hi) -for index in "${!EXPECTED_UI_LANGUAGES[@]}"; do - choose_ui_language <<< "$((index + 1))" >/dev/null - [[ "$UI_LANG" == "${EXPECTED_UI_LANGUAGES[index]}" ]] -done -choose_ui_language <<< '' >/dev/null -[[ "$UI_LANG" == en ]] - choose_and_install_programs <<< '1 3' >/dev/null contains_package git contains_package iotop From 1f162aa93e8b2b50d0fbbc6650a94922d2b8912d Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Wed, 15 Jul 2026 15:04:07 +0700 Subject: [PATCH 08/12] Add multilingual README and server manager link (#9) --- .github/workflows/bash-syntax.yml | 3 + README.md | 455 ++++++------------------------ docs/readme/README.ar.md | 32 +++ docs/readme/README.de.md | 32 +++ docs/readme/README.es.md | 32 +++ docs/readme/README.fr.md | 32 +++ docs/readme/README.hi.md | 32 +++ docs/readme/README.ja.md | 32 +++ docs/readme/README.pt.md | 32 +++ docs/readme/README.ru.md | 427 ++++++++++++++++++++++++++++ docs/readme/README.zh-CN.md | 32 +++ tests/test-readme-languages.sh | 31 ++ 12 files changed, 807 insertions(+), 365 deletions(-) create mode 100644 docs/readme/README.ar.md create mode 100644 docs/readme/README.de.md create mode 100644 docs/readme/README.es.md create mode 100644 docs/readme/README.fr.md create mode 100644 docs/readme/README.hi.md create mode 100644 docs/readme/README.ja.md create mode 100644 docs/readme/README.pt.md create mode 100644 docs/readme/README.ru.md create mode 100644 docs/readme/README.zh-CN.md create mode 100644 tests/test-readme-languages.sh diff --git a/.github/workflows/bash-syntax.yml b/.github/workflows/bash-syntax.yml index 3313963..c205cf7 100644 --- a/.github/workflows/bash-syntax.yml +++ b/.github/workflows/bash-syntax.yml @@ -29,5 +29,8 @@ jobs: - name: Test master language selection run: bash tests/test-master-language.sh + - name: Test README languages and monitoring links + run: bash tests/test-readme-languages.sh + - name: Test step 3 SSH configuration parsing run: bash tests/test-step3-sshd-parsing.sh diff --git a/README.md b/README.md index 6150fb0..54c2d15 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,14 @@ -# Быстрая настройка Ubuntu Server +# Lightweight Ubuntu Server Setup -Набор независимых Bash-скриптов для первоначальной настройки Ubuntu Server. Можно запустить только нужные модули или выполнить их по порядку для полной настройки. +**English** | [Русский](docs/readme/README.ru.md) | [Español](docs/readme/README.es.md) | [Deutsch](docs/readme/README.de.md) | [Français](docs/readme/README.fr.md) | [Português](docs/readme/README.pt.md) | [中文](docs/readme/README.zh-CN.md) | [日本語](docs/readme/README.ja.md) | [العربية](docs/readme/README.ar.md) | [हिन्दी](docs/readme/README.hi.md) -## Быстрая установка +A modular set of Bash installers for the initial setup, security hardening, administration, VPN, web panel, WARP, and backups on Ubuntu Server. Run the unified wizard for a guided installation or execute only the modules you need. -Скопируйте весь блок в терминал сервера. Он загружает архив через `codeload.github.com`, проверяет ошибки на каждом шаге и запускает единый пошаговый мастер: +> **Manage multiple servers:** use the companion [Server Monitor Manager](https://github.com/ochenstarik-ui/server-monitor-manager) project to add servers, collect reports, and manage several hosts from one application. It is currently in early development and should not yet be treated as production-ready. + +## Quick installation + +Copy the complete block into the server terminal. It downloads the repository archive through `codeload.github.com`, stops on errors, and starts the unified installer: ```bash set -e @@ -28,9 +32,13 @@ chmod 700 ./*.sh sudo ./ochenstarik-server-install.sh ``` -Первым запросом мастер предлагает выбрать язык диалога и только затем показывает этапы 1–7 по порядку. На каждом этапе можно выбрать установку, пропуск или завершение. В первом этапе пустой ввод устанавливает все рекомендуемые наборы программ. Во время изменения SSH-порта не закрывайте текущую сессию — сначала проверьте новый вход во втором терминале. +The first wizard prompt selects the dialog language. English is the default; Russian, Spanish, German, French, Portuguese, Simplified Chinese, Japanese, Arabic, and Hindi are also available. The wizard then presents steps 1–7 in order. Each step can be installed, skipped, or used to exit the wizard. -Если нужен только мастер без скачивания полного архива: +Keep the current SSH session open while changing the SSH port. Test the new login in a second terminal before disconnecting. + +## Short wizard-only installation + +If `raw.githubusercontent.com` is reachable from the server: ```bash sudo apt-get update @@ -44,380 +52,97 @@ chmod 700 ochenstarik-server-install.sh sudo ./ochenstarik-server-install.sh ``` -При запуске одного мастера недостающие этапы будут загружены автоматически из основной ветки и проверены командой `bash -n`. +The wizard downloads any missing module from the `main` branch and validates it with `bash -n` before execution. -## Что входит в проект +## Modules -| Файл | Назначение | Можно запускать отдельно | +| Script | Purpose | Standalone use | | --- | --- | --- | -| `ochenstarik-server-install.sh` | Сначала выбирает язык диалога, затем последовательно предлагает установить, пропустить или завершить каждый этап | Да; рекомендуемый способ полной настройки | -| `ochenstarik-server-1.sh` | Предлагает выбрать часовой пояс, язык терминала и наборы программ, создаёт swap-файл 2 ГБ | Да | -| `ochenstarik-server-2.sh` | Предлагает будущий SSH-порт и IPv4/IPv6, обновляет систему, ставит обязательные пакеты и настраивает UFW | Да | -| `ochenstarik-server-user-3.sh` | Создаёт администратора, переносит SSH на порт из этапа 2, проверяет права и включает fail2ban | После установки необходимых пакетов; проще всего сначала запустить скрипт 2 | -| `ochenstarik-server-tg-4.sh` | Отправляет уведомления в Telegram при успешном входе по SSH | После установки OpenSSH и `curl`; обычно после скрипта 2 или 3 | -| `ochenstarik-server-vpn-5.sh` | Устанавливает Xray и направляет системный трафик через VLESS + REALITY | Да; зависимости устанавливаются автоматически | -| `ochenstarik-server-panel-warp-6.sh` | Устанавливает 3x-ui и Cloudflare WARP, настраивает и проверяет их порты | Ubuntu 22.04/24.04/26.04 или Debian 12/13 | -| `ochenstarik-server-backup-7.sh` | Создаёт неизменяемый первичный снимок и настраивает выбранные расписания резервного копирования | Да; зависимости устанавливаются автоматически | -| `ochenstarik-server-uninstall.sh` | Удаляет настройки, службы и данные проекта для повторной установки с начала | Да; запускать только из сохранённой SSH-сессии | +| `ochenstarik-server-install.sh` | Unified wizard with language selection and sequential module installation | Recommended | +| `ochenstarik-server-1.sh` | Timezone, terminal locale, program groups, and a 2 GiB swap file | Yes | +| `ochenstarik-server-2.sh` | Base packages, SSH port, IPv4/IPv6 mode, UFW, and port management | Yes | +| `ochenstarik-server-user-3.sh` | Administrative user, SSH key permissions, SSH migration, sudo, and fail2ban | Run after step 2 | +| `ochenstarik-server-tg-4.sh` | Telegram notifications for successful SSH logins | Optional | +| `ochenstarik-server-vpn-5.sh` | System VPN through Xray with a selectable subscription port | Optional | +| `ochenstarik-server-panel-warp-6.sh` | 3x-ui panel and local Cloudflare WARP proxy | Optional | +| `ochenstarik-server-backup-7.sh` | Protected initial snapshot and selected daily, weekly, or monthly schedules | Optional | +| `ochenstarik-server-uninstall.sh` | Removes project-managed settings so installation can start again | Use carefully | -Файлы `*.env.example` являются только примерами. Не записывайте настоящие пароли, токены и приватные SSH-ключи в Git. - -## Требования - -- Ubuntu Server или совместимая Debian-система с `systemd`; -- доступ `root` или пользователь с `sudo`; -- подключение к интернету; -- рекомендуется сохранить текущую SSH-сессию открытой до проверки нового входа. - -## Получение проекта - -Рекомендуемый способ — загрузка архива через `codeload.github.com`. Он подходит в том числе для серверов, на которых соединение с основным доменом `github.com` завершается ошибкой `SSL connection timeout`: +Every module can be downloaded and run independently: ```bash -set -e -cd "$HOME" -sudo apt-get update -sudo apt-get install -y curl ca-certificates tar -curl -4 -fL \ - --retry 5 \ - --retry-delay 10 \ - --connect-timeout 30 \ - https://codeload.github.com/ochenstarik-ui/lightweight-server/tar.gz/refs/heads/main \ - -o lightweight-server.tar.gz -mkdir lightweight-server -tar -xzf lightweight-server.tar.gz -C lightweight-server --strip-components=1 -rm lightweight-server.tar.gz -cd lightweight-server -chmod 700 ./*.sh +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/SCRIPT_NAME.sh +chmod 700 SCRIPT_NAME.sh +bash -n SCRIPT_NAME.sh +sudo ./SCRIPT_NAME.sh ``` -Если `github.com` доступен с сервера, проект также можно получить через Git: +Replace `SCRIPT_NAME.sh` with the required filename from the table. + +## Installation flow + +1. Select the wizard dialog language. +2. Configure the timezone, terminal locale, recommended tools, and swap. +3. Select IPv4, IPv6, or dual-stack operation; install base services and select the future SSH port. +4. Create or update the administrative user, install the public SSH key, repair ownership and permissions, and move SSH to the port selected in step 2. +5. Optionally configure Telegram SSH-login notifications. +6. Optionally configure the system VPN and subscription port. +7. Optionally deploy 3x-ui and Cloudflare WARP with selected panel and subscription ports. +8. Optionally create the permanent initial backup and configure rotating schedules. + +## Firewall and SSH notes + +- Step 2 supports IPv4-only, IPv6-only, and dual-stack UFW rules. +- A repeated step 2 run can change the IP-family mode or add ports without reinstalling everything. +- Step 3 reads the SSH port chosen in step 2 and creates an administrator with sudo access. +- The home directory, `.ssh`, and `authorized_keys` ownership and permissions are checked on every run. +- Do not remove temporary port 22 access until a new SSH session succeeds on the selected port. + +## Backups + +Step 7 can create: + +- one initial snapshot that is never rotated automatically; +- daily backups retained for 7 days; +- weekly backups retained for 4 weeks; +- monthly backups retained for 12 months. + +Schedules are selected independently, so any combination can be enabled. Keep an additional copy on another server or storage device; a local backup does not protect against loss of the entire host. + +## Multi-server monitoring and management + +[Server Monitor Manager](https://github.com/ochenstarik-ui/server-monitor-manager) is the companion project for centralized server operations. Its planned architecture includes: + +- a Flutter client for Android and iOS; +- an ASP.NET Core API with accounts, TOTP two-factor authentication, and SignalR; +- an outbound Linux agent for metrics and managed operations; +- support for adding and monitoring multiple servers from one application. + +The manager is under active early development. Review its security and development documentation before connecting real production servers. + +## Requirements + +- Ubuntu Server with systemd and APT; +- root access through `sudo`; +- an active SSH session; +- outbound HTTPS access to GitHub or GitHub codeload; +- a valid public SSH key for the administrator setup. + +## Update + +For a Git clone: ```bash -set -e -cd "$HOME" -sudo apt-get update -sudo apt-get install -y git ca-certificates -git -c http.version=HTTP/1.1 clone --depth 1 https://github.com/ochenstarik-ui/lightweight-server.git -cd lightweight-server -chmod 700 ./*.sh -``` - -Команда `set -e` останавливает последовательность при ошибке загрузки, поэтому команды `cd` и `chmod` не будут выполняться для отсутствующей папки. - -Перед запуском любого выбранного файла можно проверить его синтаксис: - -```bash -bash -n ./ИМЯ-СКРИПТА.sh -``` - -## 1. Часовой пояс, язык, программы и swap - -Файл: `ochenstarik-server-1.sh`. - -При запуске единого мастера самый первый запрос, ещё до выбора этапа 1, задаёт язык диалога: English, Русский, Español, Deutsch, Français, Português, 中文, 日本語, العربية или हिन्दी. Начальное меню выводится на английском, а пустой ввод оставляет English. Выбор передаётся первому этапу автоматически и не меняет системную локаль сервера. - -Первый этап начинается с нумерованного меню популярных часовых поясов, включая `Asia/Novosibirsk`, `Europe/Moscow` и `UTC`. Нужную зону достаточно выбрать по номеру. Для остальных зон предусмотрен полный список с последовательным выбором региона и часового пояса — вводить имя вручную не требуется. - -После выбора времени скрипт показывает меню языков терминала: оставить текущий, русский, английский, немецкий, французский, испанский, итальянский, португальский, польский, украинский, турецкий, китайский, японский, корейский либо выбрать другую локаль из полного системного списка. Выбранный язык применяется к новым терминальным сессиям. Исходный `/etc/default/locale` сохраняется для последующего восстановления. - -Затем можно отметить несколько наборов программ номерами через пробел, например `1 2 4 9`. Пустой ввод устанавливает все наборы, а `0` пропускает дополнительные программы: - -1. Терминал и повседневная работа — Git, wget/curl, jq/yq, ripgrep, fd, fzf, lnav, редакторы, MC, tmux/screen, htop/btop, ncdu, rsync и strace. -2. Диагностика сети — DNS, ping, MTR, traceroute, tcpdump, nmap, netcat, HTTPie, whois, ethtool и socat. -3. Мониторинг системы и дисков — iotop, sysstat, SMART, lm-sensors, atop, nethogs, vnStat и psmisc. -4. Безопасность и обслуживание — auditd, AppArmor tools, Lynis, needrestart, unattended-upgrades, debsums, ACL и extended attributes. -5. Разработка и автоматизация — компилятор, ShellCheck, Python pip/venv и pipx. -6. Архивы и данные — 7-Zip, ZIP/RAR, SQLite, csvkit, клиенты PostgreSQL и Redis. -7. Документы, PDF и OCR — Poppler, qpdf, Ghostscript, OCRmyPDF/Tesseract, LibreOffice, Pandoc, antiword и catdoc. -8. Изображения, аудио и видео — ImageMagick, ExifTool, WebP, FFmpeg и MediaInfo. -9. Резервное копирование — restic, BorgBackup и rclone. - -Скрипт проверяет доступность каждого пакета в подключённых репозиториях. Недоступные для конкретной версии или набора репозиториев пакеты пропускаются с предупреждением. Список программ, которых не было до запуска, сохраняется в `/etc/ochenstarik-server/step1-installed-packages.list` для безопасной очистки. - -Затем скрипт создаёт `/swapfile` размером 2 ГБ, добавляет его в `/etc/fstab` и устанавливает `vm.swappiness=20`. Повторный запуск предусмотрен. - -```bash -curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-1.sh -chmod 700 ochenstarik-server-1.sh -bash -n ochenstarik-server-1.sh -sudo ./ochenstarik-server-1.sh -``` - -Проверка: - -```bash -swapon --show -sysctl vm.swappiness -``` - -## 2. Базовые пакеты, SSH-порт и брандмауэр - -Файл: `ochenstarik-server-2.sh`. - -При каждом запуске скрипт предлагает три действия: полную установку или обновление, изменение режима IP без переустановки пакетов либо добавление открытых портов. При полной установке он запрашивает SSH-порт для этапа 3, обновляет систему и устанавливает только обязательные компоненты: OpenSSH, UFW, fail2ban, `sudo`, TLS-сертификаты и сетевые инструменты. Дополнительные программы выбираются на этапе 1. - -Для входящих подключений можно выбрать: - -- только IPv4 — IPv6 отключается в UFW и системно через `sysctl`; этот режим рекомендуется, если 3x-ui используется только по IPv4; -- только IPv6 — управляемые скриптом порты открываются лишь для IPv6; -- IPv4 + IPv6 — порты открываются для обоих семейств. - -Выбор сохраняется в `/etc/ochenstarik-server/ip-family.conf`, а список управляемых портов — в `/etc/ochenstarik-server/ufw-managed-ports.conf`. При переходе на режим с одним семейством старые правила этих портов пересоздаются. Скрипт не разрешит отключить семейство адресов, через которое работает текущая SSH-сессия. Для режима IPv6 также проверяются глобальный IPv6-адрес и маршрут по умолчанию. - -В режиме «только IPv6» системный IPv4 остаётся доступен для исходящих соединений и работы менеджера пакетов, но входящие управляемые порты по IPv4 закрываются UFW. Полное переносимое отключение IPv4 в Linux без привязки к конкретной сетевой конфигурации не выполняется. - -Открываются TCP-порты: - -- `22` — временный SSH-порт; -- выбранный SSH-порт — будущий порт SSH; -- `80`, `443` — HTTP и HTTPS; -- `2096`, `40000`, `63636` — порты приложений. - -```bash -curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-2.sh -chmod 700 ochenstarik-server-2.sh -bash -n ochenstarik-server-2.sh -sudo ./ochenstarik-server-2.sh -``` - -Проверка: - -```bash -sudo ufw status verbose -cat /etc/ochenstarik-server/ip-family.conf -cat /proc/sys/net/ipv6/conf/all/disable_ipv6 -timedatectl -``` - -## 3. Администратор и защита SSH - -Файл: `ochenstarik-server-user-3.sh`. - -При каждом запуске скрипт предлагает два режима: - -1. новая установка или повторная настройка администратора и SSH; -2. только добавление открытых портов в UFW без изменения пользователя и SSH. - -В режиме новой установки скрипт интерактивно запрашивает имя администратора, SSH-ключ и пароль. SSH-порт автоматически читается из защищённого файла, созданного этапом 2. Если этап 2 не выполнялся, скрипт предложит выбрать порт вручную. Затем он: - -- создаёт или обновляет пользователя; -- добавляет пользователя в группу `sudo`; -- назначает пользователю его домашнюю папку и всё содержимое `.ssh`; -- устанавливает права `700` для `.ssh` и `600` для `authorized_keys` и остальных файлов ключей; -- запрещает вход `root` по SSH; -- переносит SSH на выбранный порт и добавляет для него правило UFW; -- настраивает fail2ban; -- по выбору управляет правилами UFW. - -Правила UFW добавляются с учётом режима IPv4/IPv6, сохранённого скриптом 2. Новые порты также записываются в общий список управляемых правил, поэтому последующее переключение IP-режима пересоздаст их корректно. - -Для отдельного запуска сначала установите зависимости: - -```bash -sudo apt-get update -sudo apt-get install -y sudo ufw fail2ban openssl openssh-server -curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-user-3.sh -chmod 700 ochenstarik-server-user-3.sh -bash -n ochenstarik-server-user-3.sh -sudo ./ochenstarik-server-user-3.sh -``` - -Не закрывая текущую сессию, откройте второй терминал и проверьте вход: - -```bash -ssh -p ВЫБРАННЫЙ_ПОРТ ИМЯ_ПОЛЬЗОВАТЕЛЯ@IP_СЕРВЕРА -``` - -Только после успешной проверки можно удалить временное правило порта 22: - -```bash -sudo ufw delete allow 22/tcp -``` - -Чтобы позднее открыть дополнительные порты, повторно запустите скрипт, выберите пункт `2` и введите правила через пробел или запятую: - -```text -80 443/tcp 53/udp 40000 -``` - -Для записей без протокола используется TCP. Допустимы только порты от `1` до `65535`. Если UFW неактивен, правила будут сохранены, но скрипт не включит брандмауэр автоматически во избежание потери SSH-доступа. - -## 4. Telegram-уведомления о входах по SSH - -Файл: `ochenstarik-server-tg-4.sh`. - -Создайте бота через `@BotFather`, отправьте ему сообщение и узнайте ID чата. Скрипт запросит токен и ID интерактивно, отправит тестовое сообщение и подключит уведомления через PAM. - -Для отдельного запуска: - -```bash -sudo apt-get update -sudo apt-get install -y curl openssh-server logrotate -curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-tg-4.sh -chmod 700 ochenstarik-server-tg-4.sh -bash -n ochenstarik-server-tg-4.sh -sudo ./ochenstarik-server-tg-4.sh -``` - -Журнал уведомлений: - -```bash -sudo tail -f /var/log/ochenstarik-ssh-login-telegram.log -``` - -Токен сохраняется на сервере в `/etc/ochenstarik-server/telegram.conf` с ограниченными правами. Не добавляйте этот файл в репозиторий. - -## 5. Системный VPN через Xray - -Файл: `ochenstarik-server-vpn-5.sh`. - -Скрипт устанавливает Xray и необходимые пакеты, принимает прямую ссылку `vless://` либо HTTPS-ссылку подписки 3x-ui и настраивает маршрутизацию через `nftables`. Если подписка содержит VLESS-ссылки на нескольких портах, скрипт выводит доступные порты и просит выбрать нужный. Поддерживается VLESS с транспортом TCP/RAW и REALITY. - -```bash -curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-vpn-5.sh -chmod 700 ochenstarik-server-vpn-5.sh -bash -n ochenstarik-server-vpn-5.sh -sudo ./ochenstarik-server-vpn-5.sh -``` - -Во время запуска ссылка вводится скрыто. Скрипт сравнивает внешний IP до и после настройки и использует автоматический откат при ошибке. SSH и ответы сервисов на портах `443` и `63636` остаются на прямом маршруте. - -Проверка маршрутизации: - -```bash -sudo /usr/local/sbin/ochenstarik-xray-routing status -curl -4 https://api.ipify.org -``` - -Отключение системного VPN: - -```bash -sudo ./ochenstarik-server-vpn-5.sh --disable -``` - -## 6. Панель 3x-ui и локальный proxy Cloudflare WARP - -Файл: `ochenstarik-server-panel-warp-6.sh`. - -Скрипт предлагает выбрать три разных порта: - -- порт веб-панели 3x-ui, по умолчанию `2053`; -- порт подписок 3x-ui, по умолчанию `2096`; -- локальный SOCKS5/HTTP proxy-порт WARP, по умолчанию `40000`. - -3x-ui устанавливается официальным интерактивным установщиком. После установки выбранные порты записываются в стандартную SQLite-базу с предварительной резервной копией. Скрипт включает сервис подписок, запускает `x-ui` и проверяет оба TCP-слушателя. - -Cloudflare WARP устанавливается из официального APT-репозитория, переводится в Local proxy mode через MASQUE и проверяется запросом к Cloudflare: ответ должен содержать `warp=on`. - -```bash -curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-panel-warp-6.sh -chmod 700 ochenstarik-server-panel-warp-6.sh -bash -n ochenstarik-server-panel-warp-6.sh -sudo ./ochenstarik-server-panel-warp-6.sh -``` - -В UFW открываются и проверяются `80/tcp`, `443/tcp`, выбранный порт панели и выбранный порт подписок. WARP слушает только локально на `127.0.0.1:40000` либо другом выбранном порту. Этот порт намеренно не публикуется через UFW, поскольку локальный proxy не требует аутентификации. - -Публичные порты 3x-ui открываются только для семейств адресов, выбранных на этапе 2. В режиме `ipv4` скрипт не создаёт правил IPv6. - -Поддерживаются официально доступные пакеты WARP для Ubuntu `jammy`, `noble`, `resolute` и Debian `bookworm`, `trixie` на `amd64` и `arm64`. - -Официальные источники: [установка 3x-ui](https://github.com/MHSanaei/3x-ui/wiki/Installation), [пакеты Cloudflare WARP](https://pkg.cloudflareclient.com/) и [режимы WARP](https://developers.cloudflare.com/warp-client/warp-modes/). - -## 7. Резервные копии сервера - -Файл: `ochenstarik-server-backup-7.sh`. - -Скрипт создаёт полный сжатый снимок корневой файловой системы с сохранением владельцев, ACL и расширенных атрибутов. Виртуальные файловые системы, временные каталоги, swap, подключённые диски и сам каталог бэкапов исключаются. Первичный снимок создаётся сразу, повторно не перезаписывается и не участвует в ротации. Если файловая система поддерживает атрибут `immutable`, первичный архив и его контрольная сумма дополнительно защищаются от случайного удаления. - -При установке можно выбрать несколько расписаний одним вводом, например `1 3`: - -- ежедневное — хранится 7 последних архивов; -- еженедельное — хранится 4 последних архива; -- ежемесячное — хранится 12 последних архивов; -- только первичный снимок без автоматического расписания. - -```bash -curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-backup-7.sh -chmod 700 ochenstarik-server-backup-7.sh -bash -n ochenstarik-server-backup-7.sh -sudo ./ochenstarik-server-backup-7.sh -``` - -По умолчанию архивы сохраняются в `/var/backups/ochenstarik-server`, но во время установки можно указать абсолютный путь на другом диске. Незавершённые архивы имеют суффикс `.partial` и не попадают в ротацию. Для каждого готового архива создаётся файл `.sha256`. - -Проверка расписаний и ручной запуск: - -```bash -systemctl list-timers "ochenstarik-backup-*" -sudo /usr/local/sbin/ochenstarik-server-backup daily -sudo journalctl -u ochenstarik-backup@daily.service -``` - -Проверка целостности первичного снимка: - -```bash -cd /var/backups/ochenstarik-server/initial -sha256sum -c ./*.sha256 -``` - -Восстанавливайте архив только из rescue-системы или в отдельный пустой каталог, предварительно проверив контрольную сумму. Пример распаковки в подготовленный каталог `/restore`: - -```bash -mkdir -p /restore -zstd -dc /ПУТЬ/К/АРХИВУ.tar.zst | tar --acls --xattrs --numeric-owner -xpf - -C /restore -``` - -Это локальная резервная копия, а не защита от отказа диска или потери сервера. Регулярно переносите готовые архивы и `.sha256` на отдельный сервер или накопитель. Снимок создаётся с работающей системы; для активно изменяющихся баз данных дополнительно используйте штатный экспорт или согласованный снимок самой базы. - -## Полная установка - -Рекомендуемый способ — единый мастер. Он показывает этапы по порядку и перед каждым предлагает установить его, пропустить либо завершить работу. Если остальные файлы находятся рядом, мастер использует их. Если какой-либо этап отсутствует, мастер безопасно загрузит его из основной ветки, проверит `bash -n` и только потом запустит. - -```bash -curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh -chmod 700 ochenstarik-server-install.sh -bash -n ochenstarik-server-install.sh +cd ~/lightweight-server +git pull sudo ./ochenstarik-server-install.sh ``` -После ошибки этап можно повторить, пропустить или завершить весь мастер. Повторный запуск мастера разрешён: каждый модуль поддерживает повторную настройку. - -Отдельные скрипты сохранены. При необходимости их по-прежнему можно запускать вручную по очереди: +## Reset project-managed configuration ```bash -sudo ./ochenstarik-server-1.sh -sudo ./ochenstarik-server-2.sh -sudo ./ochenstarik-server-user-3.sh -sudo ./ochenstarik-server-tg-4.sh -sudo ./ochenstarik-server-vpn-5.sh -sudo ./ochenstarik-server-panel-warp-6.sh -sudo ./ochenstarik-server-backup-7.sh -``` - -VPN, 3x-ui, WARP, Telegram-уведомления и автоматические расписания резервного копирования являются необязательными этапами. - -## Удаление и повторная установка с начала - -Файл: `ochenstarik-server-uninstall.sh`. - -Скрипт удаляет управляемые правила UFW, настройки IPv4/IPv6, SSH drop-in, fail2ban-конфигурацию, Telegram-hook, Xray, 3x-ui, WARP, расписания резервного копирования, swap и служебные файлы проекта. Перед изменением SSH открывается порт `22/tcp`. Правило порта, через который работает текущая SSH-сессия, сохраняется, чтобы не потерять доступ. - -Удаление администратора и самих архивов резервных копий требует отдельных подтверждений. Обязательные системные пакеты второго этапа и обновления Ubuntu сохраняются, чтобы не потерять доступ к серверу. Языковые пакеты и дополнительные программы удаляются только если первый скрипт зафиксировал, что до его запуска их не было. - -```bash -curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-uninstall.sh -chmod 700 ochenstarik-server-uninstall.sh -bash -n ochenstarik-server-uninstall.sh +cd ~/lightweight-server sudo ./ochenstarik-server-uninstall.sh ``` -Не закрывайте текущую SSH-сессию. После очистки сначала проверьте отдельное подключение через порт 22, затем снова запускайте нужные установочные скрипты. Старое правило текущего SSH-порта можно удалить только после успешной проверки нового входа. - -## Безопасность - -- Всегда проверяйте скачанные скрипты перед запуском от `root`. -- Не публикуйте токены Telegram, пароли, приватные SSH-ключи и ссылки VLESS. -- Не закрывайте действующую SSH-сессию во время переноса SSH на новый порт. -- Перед настройкой на рабочем сервере сделайте резервную копию важных данных. +Read every confirmation carefully. The reset script preserves critical Ubuntu packages and avoids removing access until explicitly confirmed. diff --git a/docs/readme/README.ar.md b/docs/readme/README.ar.md new file mode 100644 index 0000000..044230d --- /dev/null +++ b/docs/readme/README.ar.md @@ -0,0 +1,32 @@ +# الإعداد السريع لخادم Ubuntu + +[English](../../README.md) | [Русский](README.ru.md) | [Español](README.es.md) | [Deutsch](README.de.md) | [Français](README.fr.md) | [Português](README.pt.md) | [中文](README.zh-CN.md) | [日本語](README.ja.md) | **العربية** | [हिन्दी](README.hi.md) + +مجموعة نصوص Bash مستقلة لإعداد Ubuntu Server وتأمينه وإدارة SSH وVPN و3x-ui وWARP والنسخ الاحتياطية. + +> لمراقبة عدة خوادم وإدارتها من تطبيق واحد، استخدم [Server Monitor Manager](https://github.com/ochenstarik-ui/server-monitor-manager). المشروع ما زال في مرحلة تطوير مبكرة. + +## التثبيت السريع + +```bash +sudo apt-get update +sudo apt-get install -y curl ca-certificates +curl -4 -fLO --retry 5 --retry-delay 10 --connect-timeout 30 \ + https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh +chmod 700 ochenstarik-server-install.sh +sudo ./ochenstarik-server-install.sh +``` + +السؤال الأول يحدد لغة المثبت، واللغة الافتراضية هي English. بعد ذلك يمكن تشغيل كل مرحلة من المراحل السبع أو تخطيها أو إنهاء المثبت. + +## المراحل + +1. المنطقة الزمنية ولغة الطرفية والبرامج وملف swap. +2. الحزم الأساسية ومنفذ SSH وIPv4/IPv6 وUFW. +3. المستخدم الإداري ومفتاح SSH والصلاحيات وsudo وfail2ban. +4. إشعارات Telegram عند تسجيل الدخول عبر SSH. +5. VPN للنظام باستخدام Xray. +6. لوحة 3x-ui ووكيل Cloudflare WARP المحلي. +7. النسخة الأولية والنسخ اليومية أو الأسبوعية أو الشهرية. + +لا تغلق جلسة SSH الحالية أثناء تغيير المنفذ. اختبر الاتصال الجديد أولاً في طرفية أخرى. راجع [الوثائق الإنجليزية الرئيسية](../../README.md) لمزيد من التفاصيل. diff --git a/docs/readme/README.de.md b/docs/readme/README.de.md new file mode 100644 index 0000000..34f45d1 --- /dev/null +++ b/docs/readme/README.de.md @@ -0,0 +1,32 @@ +# Schnelle Einrichtung von Ubuntu Server + +[English](../../README.md) | [Русский](README.ru.md) | [Español](README.es.md) | **Deutsch** | [Français](README.fr.md) | [Português](README.pt.md) | [中文](README.zh-CN.md) | [日本語](README.ja.md) | [العربية](README.ar.md) | [हिन्दी](README.hi.md) + +Modulare Bash-Skripte für Ersteinrichtung, Absicherung, SSH, VPN, 3x-ui, WARP und Sicherungen auf Ubuntu Server. + +> Zur Überwachung und gemeinsamen Verwaltung mehrerer Server verwenden Sie [Server Monitor Manager](https://github.com/ochenstarik-ui/server-monitor-manager). Das Projekt befindet sich noch in einer frühen Entwicklungsphase. + +## Schnellinstallation + +```bash +sudo apt-get update +sudo apt-get install -y curl ca-certificates +curl -4 -fLO --retry 5 --retry-delay 10 --connect-timeout 30 \ + https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh +chmod 700 ochenstarik-server-install.sh +sudo ./ochenstarik-server-install.sh +``` + +Die erste Frage wählt die Sprache des Installers; English ist voreingestellt. Danach kann jeder der sieben Schritte ausgeführt, übersprungen oder zum Beenden verwendet werden. + +## Schritte + +1. Zeitzone, Terminalsprache, empfohlene Programme und Swap. +2. Basispakete, SSH-Port, IPv4/IPv6 und UFW. +3. Administrator, SSH-Schlüssel, Rechte, sudo und fail2ban. +4. Telegram-Benachrichtigungen für SSH-Anmeldungen. +5. System-VPN über Xray. +6. 3x-ui und lokaler Cloudflare-WARP-Proxy. +7. Erstsicherung sowie tägliche, wöchentliche oder monatliche Sicherungen. + +Schließen Sie die aktuelle SSH-Sitzung während der Portänderung nicht. Testen Sie zuerst die neue Anmeldung in einem zweiten Terminal. Weitere Hinweise enthält die [englische Hauptdokumentation](../../README.md). diff --git a/docs/readme/README.es.md b/docs/readme/README.es.md new file mode 100644 index 0000000..457868d --- /dev/null +++ b/docs/readme/README.es.md @@ -0,0 +1,32 @@ +# Configuración rápida de Ubuntu Server + +[English](../../README.md) | [Русский](README.ru.md) | **Español** | [Deutsch](README.de.md) | [Français](README.fr.md) | [Português](README.pt.md) | [中文](README.zh-CN.md) | [日本語](README.ja.md) | [العربية](README.ar.md) | [हिन्दी](README.hi.md) + +Conjunto modular de scripts Bash para la configuración inicial, seguridad, SSH, VPN, 3x-ui, WARP y copias de seguridad de Ubuntu Server. + +> Para supervisar y administrar varios servidores desde una aplicación, consulte [Server Monitor Manager](https://github.com/ochenstarik-ui/server-monitor-manager). El proyecto se encuentra en una fase temprana de desarrollo. + +## Instalación rápida + +```bash +sudo apt-get update +sudo apt-get install -y curl ca-certificates +curl -4 -fLO --retry 5 --retry-delay 10 --connect-timeout 30 \ + https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh +chmod 700 ochenstarik-server-install.sh +sudo ./ochenstarik-server-install.sh +``` + +La primera pregunta selecciona el idioma del instalador; English es el valor predeterminado. Después puede instalar, omitir o detener cada una de las siete etapas. + +## Etapas + +1. Zona horaria, idioma del terminal, programas recomendados y swap. +2. Paquetes básicos, puerto SSH, IPv4/IPv6 y UFW. +3. Usuario administrador, clave SSH, permisos, sudo y fail2ban. +4. Notificaciones de Telegram para accesos SSH. +5. VPN del sistema mediante Xray. +6. Panel 3x-ui y proxy local Cloudflare WARP. +7. Instantánea inicial y copias diarias, semanales o mensuales. + +No cierre la sesión SSH actual durante el cambio de puerto. Pruebe primero el nuevo acceso en otro terminal. Consulte la [documentación principal en inglés](../../README.md) para la instalación por archivo, actualización y restablecimiento. diff --git a/docs/readme/README.fr.md b/docs/readme/README.fr.md new file mode 100644 index 0000000..b86c4e0 --- /dev/null +++ b/docs/readme/README.fr.md @@ -0,0 +1,32 @@ +# Configuration rapide d’Ubuntu Server + +[English](../../README.md) | [Русский](README.ru.md) | [Español](README.es.md) | [Deutsch](README.de.md) | **Français** | [Português](README.pt.md) | [中文](README.zh-CN.md) | [日本語](README.ja.md) | [العربية](README.ar.md) | [हिन्दी](README.hi.md) + +Scripts Bash modulaires pour la configuration initiale, la sécurisation, SSH, le VPN, 3x-ui, WARP et les sauvegardes d’Ubuntu Server. + +> Pour surveiller et administrer plusieurs serveurs depuis une seule application, consultez [Server Monitor Manager](https://github.com/ochenstarik-ui/server-monitor-manager). Ce projet est encore au début de son développement. + +## Installation rapide + +```bash +sudo apt-get update +sudo apt-get install -y curl ca-certificates +curl -4 -fLO --retry 5 --retry-delay 10 --connect-timeout 30 \ + https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh +chmod 700 ochenstarik-server-install.sh +sudo ./ochenstarik-server-install.sh +``` + +La première question sélectionne la langue de l’installateur ; English est la valeur par défaut. Chacune des sept étapes peut ensuite être installée, ignorée ou utilisée pour quitter. + +## Étapes + +1. Fuseau horaire, langue du terminal, programmes recommandés et swap. +2. Paquets de base, port SSH, IPv4/IPv6 et UFW. +3. Administrateur, clé SSH, permissions, sudo et fail2ban. +4. Notifications Telegram des connexions SSH. +5. VPN système via Xray. +6. Panneau 3x-ui et proxy local Cloudflare WARP. +7. Instantané initial et sauvegardes quotidiennes, hebdomadaires ou mensuelles. + +Ne fermez pas la session SSH actuelle pendant le changement de port. Testez d’abord la nouvelle connexion dans un autre terminal. Consultez la [documentation principale en anglais](../../README.md) pour plus de détails. diff --git a/docs/readme/README.hi.md b/docs/readme/README.hi.md new file mode 100644 index 0000000..0a69f30 --- /dev/null +++ b/docs/readme/README.hi.md @@ -0,0 +1,32 @@ +# Ubuntu Server का त्वरित सेटअप + +[English](../../README.md) | [Русский](README.ru.md) | [Español](README.es.md) | [Deutsch](README.de.md) | [Français](README.fr.md) | [Português](README.pt.md) | [中文](README.zh-CN.md) | [日本語](README.ja.md) | [العربية](README.ar.md) | **हिन्दी** + +Ubuntu Server की प्रारंभिक स्थापना, सुरक्षा, SSH, VPN, 3x-ui, WARP और बैकअप के लिए मॉड्यूलर Bash स्क्रिप्ट। + +> एक ऐप से कई सर्वरों की निगरानी और संयुक्त प्रबंधन के लिए [Server Monitor Manager](https://github.com/ochenstarik-ui/server-monitor-manager) का उपयोग करें। यह परियोजना अभी शुरुआती विकास चरण में है। + +## त्वरित स्थापना + +```bash +sudo apt-get update +sudo apt-get install -y curl ca-certificates +curl -4 -fLO --retry 5 --retry-delay 10 --connect-timeout 30 \ + https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh +chmod 700 ochenstarik-server-install.sh +sudo ./ochenstarik-server-install.sh +``` + +पहला प्रश्न इंस्टॉलर की भाषा चुनता है; डिफ़ॉल्ट English है। इसके बाद सातों चरणों को चलाया, छोड़ा या इंस्टॉलर बंद किया जा सकता है। + +## चरण + +1. समय क्षेत्र, टर्मिनल भाषा, अनुशंसित प्रोग्राम और swap। +2. मूल पैकेज, SSH पोर्ट, IPv4/IPv6 और UFW। +3. प्रशासक, SSH कुंजी, अनुमतियाँ, sudo और fail2ban। +4. SSH लॉगिन के लिए Telegram सूचनाएँ। +5. Xray के माध्यम से सिस्टम VPN। +6. 3x-ui पैनल और स्थानीय Cloudflare WARP प्रॉक्सी। +7. प्रारंभिक स्नैपशॉट और दैनिक, साप्ताहिक या मासिक बैकअप। + +SSH पोर्ट बदलते समय मौजूदा सत्र बंद न करें। पहले दूसरे टर्मिनल में नया लॉगिन जाँचें। अधिक जानकारी के लिए [मुख्य अंग्रेज़ी दस्तावेज़](../../README.md) देखें। diff --git a/docs/readme/README.ja.md b/docs/readme/README.ja.md new file mode 100644 index 0000000..8e15e1e --- /dev/null +++ b/docs/readme/README.ja.md @@ -0,0 +1,32 @@ +# Ubuntu Server クイックセットアップ + +[English](../../README.md) | [Русский](README.ru.md) | [Español](README.es.md) | [Deutsch](README.de.md) | [Français](README.fr.md) | [Português](README.pt.md) | [中文](README.zh-CN.md) | **日本語** | [العربية](README.ar.md) | [हिन्दी](README.hi.md) + +Ubuntu Server の初期設定、セキュリティ、SSH、VPN、3x-ui、WARP、バックアップを行うモジュール式 Bash スクリプトです。 + +> 複数のサーバーを一つのアプリで監視・管理するには、[Server Monitor Manager](https://github.com/ochenstarik-ui/server-monitor-manager) を使用してください。このプロジェクトは現在、開発の初期段階です。 + +## クイックインストール + +```bash +sudo apt-get update +sudo apt-get install -y curl ca-certificates +curl -4 -fLO --retry 5 --retry-delay 10 --connect-timeout 30 \ + https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh +chmod 700 ochenstarik-server-install.sh +sudo ./ochenstarik-server-install.sh +``` + +最初の質問でインストーラーの言語を選択します。既定値は English です。その後、7 つの各ステップを実行、スキップ、または終了できます。 + +## ステップ + +1. タイムゾーン、端末言語、推奨プログラム、スワップ。 +2. 基本パッケージ、SSH ポート、IPv4/IPv6、UFW。 +3. 管理者、SSH 鍵、アクセス権、sudo、fail2ban。 +4. SSH ログインの Telegram 通知。 +5. Xray によるシステム VPN。 +6. 3x-ui パネルとローカル Cloudflare WARP プロキシ。 +7. 初回スナップショットと日次、週次、月次バックアップ。 + +SSH ポートの変更中は現在のセッションを閉じないでください。別のターミナルで新しい接続を先に確認してください。詳細は[英語のメイン文書](../../README.md)を参照してください。 diff --git a/docs/readme/README.pt.md b/docs/readme/README.pt.md new file mode 100644 index 0000000..0e8d360 --- /dev/null +++ b/docs/readme/README.pt.md @@ -0,0 +1,32 @@ +# Configuração rápida do Ubuntu Server + +[English](../../README.md) | [Русский](README.ru.md) | [Español](README.es.md) | [Deutsch](README.de.md) | [Français](README.fr.md) | **Português** | [中文](README.zh-CN.md) | [日本語](README.ja.md) | [العربية](README.ar.md) | [हिन्दी](README.hi.md) + +Scripts Bash modulares para configuração inicial, segurança, SSH, VPN, 3x-ui, WARP e backups no Ubuntu Server. + +> Para monitorar e administrar vários servidores em um único aplicativo, use o [Server Monitor Manager](https://github.com/ochenstarik-ui/server-monitor-manager). O projeto ainda está em uma fase inicial de desenvolvimento. + +## Instalação rápida + +```bash +sudo apt-get update +sudo apt-get install -y curl ca-certificates +curl -4 -fLO --retry 5 --retry-delay 10 --connect-timeout 30 \ + https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh +chmod 700 ochenstarik-server-install.sh +sudo ./ochenstarik-server-install.sh +``` + +A primeira pergunta seleciona o idioma do instalador; English é o padrão. Depois, cada uma das sete etapas pode ser instalada, ignorada ou usada para sair. + +## Etapas + +1. Fuso horário, idioma do terminal, programas recomendados e swap. +2. Pacotes básicos, porta SSH, IPv4/IPv6 e UFW. +3. Administrador, chave SSH, permissões, sudo e fail2ban. +4. Notificações Telegram para logins SSH. +5. VPN do sistema por Xray. +6. Painel 3x-ui e proxy local Cloudflare WARP. +7. Snapshot inicial e backups diários, semanais ou mensais. + +Não feche a sessão SSH atual durante a alteração da porta. Teste primeiro o novo login em outro terminal. Consulte a [documentação principal em inglês](../../README.md) para obter detalhes adicionais. diff --git a/docs/readme/README.ru.md b/docs/readme/README.ru.md new file mode 100644 index 0000000..2a3a022 --- /dev/null +++ b/docs/readme/README.ru.md @@ -0,0 +1,427 @@ +# Быстрая настройка Ubuntu Server + +[English](../../README.md) | **Русский** | [Español](README.es.md) | [Deutsch](README.de.md) | [Français](README.fr.md) | [Português](README.pt.md) | [中文](README.zh-CN.md) | [日本語](README.ja.md) | [العربية](README.ar.md) | [हिन्दी](README.hi.md) + +> Для мониторинга и объединённого управления несколькими серверами используйте дополнительный проект [Server Monitor Manager](https://github.com/ochenstarik-ui/server-monitor-manager). Проект находится на ранней стадии разработки. + +Набор независимых Bash-скриптов для первоначальной настройки Ubuntu Server. Можно запустить только нужные модули или выполнить их по порядку для полной настройки. + +## Быстрая установка + +Скопируйте весь блок в терминал сервера. Он загружает архив через `codeload.github.com`, проверяет ошибки на каждом шаге и запускает единый пошаговый мастер: + +```bash +set -e +cd "$HOME" +sudo apt-get update +sudo apt-get install -y curl ca-certificates tar +archive="$(mktemp)" +trap 'rm -f "$archive"' EXIT +curl -4 -fL \ + --retry 5 \ + --retry-delay 10 \ + --connect-timeout 30 \ + https://codeload.github.com/ochenstarik-ui/lightweight-server/tar.gz/refs/heads/main \ + -o "$archive" +install -d -m 700 lightweight-server +tar -xzf "$archive" -C lightweight-server --strip-components=1 +rm -f "$archive" +trap - EXIT +cd lightweight-server +chmod 700 ./*.sh +sudo ./ochenstarik-server-install.sh +``` + +Первым запросом мастер предлагает выбрать язык диалога и только затем показывает этапы 1–7 по порядку. На каждом этапе можно выбрать установку, пропуск или завершение. В первом этапе пустой ввод устанавливает все рекомендуемые наборы программ. Во время изменения SSH-порта не закрывайте текущую сессию — сначала проверьте новый вход во втором терминале. + +Если нужен только мастер без скачивания полного архива: + +```bash +sudo apt-get update +sudo apt-get install -y curl ca-certificates +curl -4 -fLO \ + --retry 5 \ + --retry-delay 10 \ + --connect-timeout 30 \ + https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh +chmod 700 ochenstarik-server-install.sh +sudo ./ochenstarik-server-install.sh +``` + +При запуске одного мастера недостающие этапы будут загружены автоматически из основной ветки и проверены командой `bash -n`. + +## Что входит в проект + +| Файл | Назначение | Можно запускать отдельно | +| --- | --- | --- | +| `ochenstarik-server-install.sh` | Сначала выбирает язык диалога, затем последовательно предлагает установить, пропустить или завершить каждый этап | Да; рекомендуемый способ полной настройки | +| `ochenstarik-server-1.sh` | Предлагает выбрать часовой пояс, язык терминала и наборы программ, создаёт swap-файл 2 ГБ | Да | +| `ochenstarik-server-2.sh` | Предлагает будущий SSH-порт и IPv4/IPv6, обновляет систему, ставит обязательные пакеты и настраивает UFW | Да | +| `ochenstarik-server-user-3.sh` | Создаёт администратора, переносит SSH на порт из этапа 2, проверяет права и включает fail2ban | После установки необходимых пакетов; проще всего сначала запустить скрипт 2 | +| `ochenstarik-server-tg-4.sh` | Отправляет уведомления в Telegram при успешном входе по SSH | После установки OpenSSH и `curl`; обычно после скрипта 2 или 3 | +| `ochenstarik-server-vpn-5.sh` | Устанавливает Xray и направляет системный трафик через VLESS + REALITY | Да; зависимости устанавливаются автоматически | +| `ochenstarik-server-panel-warp-6.sh` | Устанавливает 3x-ui и Cloudflare WARP, настраивает и проверяет их порты | Ubuntu 22.04/24.04/26.04 или Debian 12/13 | +| `ochenstarik-server-backup-7.sh` | Создаёт неизменяемый первичный снимок и настраивает выбранные расписания резервного копирования | Да; зависимости устанавливаются автоматически | +| `ochenstarik-server-uninstall.sh` | Удаляет настройки, службы и данные проекта для повторной установки с начала | Да; запускать только из сохранённой SSH-сессии | + +Файлы `*.env.example` являются только примерами. Не записывайте настоящие пароли, токены и приватные SSH-ключи в Git. + +## Требования + +- Ubuntu Server или совместимая Debian-система с `systemd`; +- доступ `root` или пользователь с `sudo`; +- подключение к интернету; +- рекомендуется сохранить текущую SSH-сессию открытой до проверки нового входа. + +## Получение проекта + +Рекомендуемый способ — загрузка архива через `codeload.github.com`. Он подходит в том числе для серверов, на которых соединение с основным доменом `github.com` завершается ошибкой `SSL connection timeout`: + +```bash +set -e +cd "$HOME" +sudo apt-get update +sudo apt-get install -y curl ca-certificates tar +curl -4 -fL \ + --retry 5 \ + --retry-delay 10 \ + --connect-timeout 30 \ + https://codeload.github.com/ochenstarik-ui/lightweight-server/tar.gz/refs/heads/main \ + -o lightweight-server.tar.gz +mkdir lightweight-server +tar -xzf lightweight-server.tar.gz -C lightweight-server --strip-components=1 +rm lightweight-server.tar.gz +cd lightweight-server +chmod 700 ./*.sh +``` + +Если `github.com` доступен с сервера, проект также можно получить через Git: + +```bash +set -e +cd "$HOME" +sudo apt-get update +sudo apt-get install -y git ca-certificates +git -c http.version=HTTP/1.1 clone --depth 1 https://github.com/ochenstarik-ui/lightweight-server.git +cd lightweight-server +chmod 700 ./*.sh +``` + +Команда `set -e` останавливает последовательность при ошибке загрузки, поэтому команды `cd` и `chmod` не будут выполняться для отсутствующей папки. + +Перед запуском любого выбранного файла можно проверить его синтаксис: + +```bash +bash -n ./ИМЯ-СКРИПТА.sh +``` + +## 1. Часовой пояс, язык, программы и swap + +Файл: `ochenstarik-server-1.sh`. + +При запуске единого мастера самый первый запрос, ещё до выбора этапа 1, задаёт язык диалога: English, Русский, Español, Deutsch, Français, Português, 中文, 日本語, العربية или हिन्दी. Начальное меню выводится на английском, а пустой ввод оставляет English. Выбор передаётся первому этапу автоматически и не меняет системную локаль сервера. + +Первый этап начинается с нумерованного меню популярных часовых поясов, включая `Asia/Novosibirsk`, `Europe/Moscow` и `UTC`. Нужную зону достаточно выбрать по номеру. Для остальных зон предусмотрен полный список с последовательным выбором региона и часового пояса — вводить имя вручную не требуется. + +После выбора времени скрипт показывает меню языков терминала: оставить текущий, русский, английский, немецкий, французский, испанский, итальянский, португальский, польский, украинский, турецкий, китайский, японский, корейский либо выбрать другую локаль из полного системного списка. Выбранный язык применяется к новым терминальным сессиям. Исходный `/etc/default/locale` сохраняется для последующего восстановления. + +Затем можно отметить несколько наборов программ номерами через пробел, например `1 2 4 9`. Пустой ввод устанавливает все наборы, а `0` пропускает дополнительные программы: + +1. Терминал и повседневная работа — Git, wget/curl, jq/yq, ripgrep, fd, fzf, lnav, редакторы, MC, tmux/screen, htop/btop, ncdu, rsync и strace. +2. Диагностика сети — DNS, ping, MTR, traceroute, tcpdump, nmap, netcat, HTTPie, whois, ethtool и socat. +3. Мониторинг системы и дисков — iotop, sysstat, SMART, lm-sensors, atop, nethogs, vnStat и psmisc. +4. Безопасность и обслуживание — auditd, AppArmor tools, Lynis, needrestart, unattended-upgrades, debsums, ACL и extended attributes. +5. Разработка и автоматизация — компилятор, ShellCheck, Python pip/venv и pipx. +6. Архивы и данные — 7-Zip, ZIP/RAR, SQLite, csvkit, клиенты PostgreSQL и Redis. +7. Документы, PDF и OCR — Poppler, qpdf, Ghostscript, OCRmyPDF/Tesseract, LibreOffice, Pandoc, antiword и catdoc. +8. Изображения, аудио и видео — ImageMagick, ExifTool, WebP, FFmpeg и MediaInfo. +9. Резервное копирование — restic, BorgBackup и rclone. + +Скрипт проверяет доступность каждого пакета в подключённых репозиториях. Недоступные для конкретной версии или набора репозиториев пакеты пропускаются с предупреждением. Список программ, которых не было до запуска, сохраняется в `/etc/ochenstarik-server/step1-installed-packages.list` для безопасной очистки. + +Затем скрипт создаёт `/swapfile` размером 2 ГБ, добавляет его в `/etc/fstab` и устанавливает `vm.swappiness=20`. Повторный запуск предусмотрен. + +```bash +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-1.sh +chmod 700 ochenstarik-server-1.sh +bash -n ochenstarik-server-1.sh +sudo ./ochenstarik-server-1.sh +``` + +Проверка: + +```bash +swapon --show +sysctl vm.swappiness +``` + +## 2. Базовые пакеты, SSH-порт и брандмауэр + +Файл: `ochenstarik-server-2.sh`. + +При каждом запуске скрипт предлагает три действия: полную установку или обновление, изменение режима IP без переустановки пакетов либо добавление открытых портов. При полной установке он запрашивает SSH-порт для этапа 3, обновляет систему и устанавливает только обязательные компоненты: OpenSSH, UFW, fail2ban, `sudo`, TLS-сертификаты и сетевые инструменты. Дополнительные программы выбираются на этапе 1. + +Для входящих подключений можно выбрать: + +- только IPv4 — IPv6 отключается в UFW и системно через `sysctl`; этот режим рекомендуется, если 3x-ui используется только по IPv4; +- только IPv6 — управляемые скриптом порты открываются лишь для IPv6; +- IPv4 + IPv6 — порты открываются для обоих семейств. + +Выбор сохраняется в `/etc/ochenstarik-server/ip-family.conf`, а список управляемых портов — в `/etc/ochenstarik-server/ufw-managed-ports.conf`. При переходе на режим с одним семейством старые правила этих портов пересоздаются. Скрипт не разрешит отключить семейство адресов, через которое работает текущая SSH-сессия. Для режима IPv6 также проверяются глобальный IPv6-адрес и маршрут по умолчанию. + +В режиме «только IPv6» системный IPv4 остаётся доступен для исходящих соединений и работы менеджера пакетов, но входящие управляемые порты по IPv4 закрываются UFW. Полное переносимое отключение IPv4 в Linux без привязки к конкретной сетевой конфигурации не выполняется. + +Открываются TCP-порты: + +- `22` — временный SSH-порт; +- выбранный SSH-порт — будущий порт SSH; +- `80`, `443` — HTTP и HTTPS; +- `2096`, `40000`, `63636` — порты приложений. + +```bash +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-2.sh +chmod 700 ochenstarik-server-2.sh +bash -n ochenstarik-server-2.sh +sudo ./ochenstarik-server-2.sh +``` + +Проверка: + +```bash +sudo ufw status verbose +cat /etc/ochenstarik-server/ip-family.conf +cat /proc/sys/net/ipv6/conf/all/disable_ipv6 +timedatectl +``` + +## 3. Администратор и защита SSH + +Файл: `ochenstarik-server-user-3.sh`. + +При каждом запуске скрипт предлагает два режима: + +1. новая установка или повторная настройка администратора и SSH; +2. только добавление открытых портов в UFW без изменения пользователя и SSH. + +В режиме новой установки скрипт интерактивно запрашивает имя администратора, SSH-ключ и пароль. SSH-порт автоматически читается из защищённого файла, созданного этапом 2. Если этап 2 не выполнялся, скрипт предложит выбрать порт вручную. Затем он: + +- создаёт или обновляет пользователя; +- добавляет пользователя в группу `sudo`; +- назначает пользователю его домашнюю папку и всё содержимое `.ssh`; +- устанавливает права `700` для `.ssh` и `600` для `authorized_keys` и остальных файлов ключей; +- запрещает вход `root` по SSH; +- переносит SSH на выбранный порт и добавляет для него правило UFW; +- настраивает fail2ban; +- по выбору управляет правилами UFW. + +Правила UFW добавляются с учётом режима IPv4/IPv6, сохранённого скриптом 2. Новые порты также записываются в общий список управляемых правил, поэтому последующее переключение IP-режима пересоздаст их корректно. + +Для отдельного запуска сначала установите зависимости: + +```bash +sudo apt-get update +sudo apt-get install -y sudo ufw fail2ban openssl openssh-server +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-user-3.sh +chmod 700 ochenstarik-server-user-3.sh +bash -n ochenstarik-server-user-3.sh +sudo ./ochenstarik-server-user-3.sh +``` + +Не закрывая текущую сессию, откройте второй терминал и проверьте вход: + +```bash +ssh -p ВЫБРАННЫЙ_ПОРТ ИМЯ_ПОЛЬЗОВАТЕЛЯ@IP_СЕРВЕРА +``` + +Только после успешной проверки можно удалить временное правило порта 22: + +```bash +sudo ufw delete allow 22/tcp +``` + +Чтобы позднее открыть дополнительные порты, повторно запустите скрипт, выберите пункт `2` и введите правила через пробел или запятую: + +```text +80 443/tcp 53/udp 40000 +``` + +Для записей без протокола используется TCP. Допустимы только порты от `1` до `65535`. Если UFW неактивен, правила будут сохранены, но скрипт не включит брандмауэр автоматически во избежание потери SSH-доступа. + +## 4. Telegram-уведомления о входах по SSH + +Файл: `ochenstarik-server-tg-4.sh`. + +Создайте бота через `@BotFather`, отправьте ему сообщение и узнайте ID чата. Скрипт запросит токен и ID интерактивно, отправит тестовое сообщение и подключит уведомления через PAM. + +Для отдельного запуска: + +```bash +sudo apt-get update +sudo apt-get install -y curl openssh-server logrotate +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-tg-4.sh +chmod 700 ochenstarik-server-tg-4.sh +bash -n ochenstarik-server-tg-4.sh +sudo ./ochenstarik-server-tg-4.sh +``` + +Журнал уведомлений: + +```bash +sudo tail -f /var/log/ochenstarik-ssh-login-telegram.log +``` + +Токен сохраняется на сервере в `/etc/ochenstarik-server/telegram.conf` с ограниченными правами. Не добавляйте этот файл в репозиторий. + +## 5. Системный VPN через Xray + +Файл: `ochenstarik-server-vpn-5.sh`. + +Скрипт устанавливает Xray и необходимые пакеты, принимает прямую ссылку `vless://` либо HTTPS-ссылку подписки 3x-ui и настраивает маршрутизацию через `nftables`. Если подписка содержит VLESS-ссылки на нескольких портах, скрипт выводит доступные порты и просит выбрать нужный. Поддерживается VLESS с транспортом TCP/RAW и REALITY. + +```bash +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-vpn-5.sh +chmod 700 ochenstarik-server-vpn-5.sh +bash -n ochenstarik-server-vpn-5.sh +sudo ./ochenstarik-server-vpn-5.sh +``` + +Во время запуска ссылка вводится скрыто. Скрипт сравнивает внешний IP до и после настройки и использует автоматический откат при ошибке. SSH и ответы сервисов на портах `443` и `63636` остаются на прямом маршруте. + +Проверка маршрутизации: + +```bash +sudo /usr/local/sbin/ochenstarik-xray-routing status +curl -4 https://api.ipify.org +``` + +Отключение системного VPN: + +```bash +sudo ./ochenstarik-server-vpn-5.sh --disable +``` + +## 6. Панель 3x-ui и локальный proxy Cloudflare WARP + +Файл: `ochenstarik-server-panel-warp-6.sh`. + +Скрипт предлагает выбрать три разных порта: + +- порт веб-панели 3x-ui, по умолчанию `2053`; +- порт подписок 3x-ui, по умолчанию `2096`; +- локальный SOCKS5/HTTP proxy-порт WARP, по умолчанию `40000`. + +3x-ui устанавливается официальным интерактивным установщиком. После установки выбранные порты записываются в стандартную SQLite-базу с предварительной резервной копией. Скрипт включает сервис подписок, запускает `x-ui` и проверяет оба TCP-слушателя. + +Cloudflare WARP устанавливается из официального APT-репозитория, переводится в Local proxy mode через MASQUE и проверяется запросом к Cloudflare: ответ должен содержать `warp=on`. + +```bash +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-panel-warp-6.sh +chmod 700 ochenstarik-server-panel-warp-6.sh +bash -n ochenstarik-server-panel-warp-6.sh +sudo ./ochenstarik-server-panel-warp-6.sh +``` + +В UFW открываются и проверяются `80/tcp`, `443/tcp`, выбранный порт панели и выбранный порт подписок. WARP слушает только локально на `127.0.0.1:40000` либо другом выбранном порту. Этот порт намеренно не публикуется через UFW, поскольку локальный proxy не требует аутентификации. + +Публичные порты 3x-ui открываются только для семейств адресов, выбранных на этапе 2. В режиме `ipv4` скрипт не создаёт правил IPv6. + +Поддерживаются официально доступные пакеты WARP для Ubuntu `jammy`, `noble`, `resolute` и Debian `bookworm`, `trixie` на `amd64` и `arm64`. + +Официальные источники: [установка 3x-ui](https://github.com/MHSanaei/3x-ui/wiki/Installation), [пакеты Cloudflare WARP](https://pkg.cloudflareclient.com/) и [режимы WARP](https://developers.cloudflare.com/warp-client/warp-modes/). + +## 7. Резервные копии сервера + +Файл: `ochenstarik-server-backup-7.sh`. + +Скрипт создаёт полный сжатый снимок корневой файловой системы с сохранением владельцев, ACL и расширенных атрибутов. Виртуальные файловые системы, временные каталоги, swap, подключённые диски и сам каталог бэкапов исключаются. Первичный снимок создаётся сразу, повторно не перезаписывается и не участвует в ротации. Если файловая система поддерживает атрибут `immutable`, первичный архив и его контрольная сумма дополнительно защищаются от случайного удаления. + +При установке можно выбрать несколько расписаний одним вводом, например `1 3`: + +- ежедневное — хранится 7 последних архивов; +- еженедельное — хранится 4 последних архива; +- ежемесячное — хранится 12 последних архивов; +- только первичный снимок без автоматического расписания. + +```bash +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-backup-7.sh +chmod 700 ochenstarik-server-backup-7.sh +bash -n ochenstarik-server-backup-7.sh +sudo ./ochenstarik-server-backup-7.sh +``` + +По умолчанию архивы сохраняются в `/var/backups/ochenstarik-server`, но во время установки можно указать абсолютный путь на другом диске. Незавершённые архивы имеют суффикс `.partial` и не попадают в ротацию. Для каждого готового архива создаётся файл `.sha256`. + +Проверка расписаний и ручной запуск: + +```bash +systemctl list-timers "ochenstarik-backup-*" +sudo /usr/local/sbin/ochenstarik-server-backup daily +sudo journalctl -u ochenstarik-backup@daily.service +``` + +Проверка целостности первичного снимка: + +```bash +cd /var/backups/ochenstarik-server/initial +sha256sum -c ./*.sha256 +``` + +Восстанавливайте архив только из rescue-системы или в отдельный пустой каталог, предварительно проверив контрольную сумму. Пример распаковки в подготовленный каталог `/restore`: + +```bash +mkdir -p /restore +zstd -dc /ПУТЬ/К/АРХИВУ.tar.zst | tar --acls --xattrs --numeric-owner -xpf - -C /restore +``` + +Это локальная резервная копия, а не защита от отказа диска или потери сервера. Регулярно переносите готовые архивы и `.sha256` на отдельный сервер или накопитель. Снимок создаётся с работающей системы; для активно изменяющихся баз данных дополнительно используйте штатный экспорт или согласованный снимок самой базы. + +## Полная установка + +Рекомендуемый способ — единый мастер. Он показывает этапы по порядку и перед каждым предлагает установить его, пропустить либо завершить работу. Если остальные файлы находятся рядом, мастер использует их. Если какой-либо этап отсутствует, мастер безопасно загрузит его из основной ветки, проверит `bash -n` и только потом запустит. + +```bash +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh +chmod 700 ochenstarik-server-install.sh +bash -n ochenstarik-server-install.sh +sudo ./ochenstarik-server-install.sh +``` + +После ошибки этап можно повторить, пропустить или завершить весь мастер. Повторный запуск мастера разрешён: каждый модуль поддерживает повторную настройку. + +Отдельные скрипты сохранены. При необходимости их по-прежнему можно запускать вручную по очереди: + +```bash +sudo ./ochenstarik-server-1.sh +sudo ./ochenstarik-server-2.sh +sudo ./ochenstarik-server-user-3.sh +sudo ./ochenstarik-server-tg-4.sh +sudo ./ochenstarik-server-vpn-5.sh +sudo ./ochenstarik-server-panel-warp-6.sh +sudo ./ochenstarik-server-backup-7.sh +``` + +VPN, 3x-ui, WARP, Telegram-уведомления и автоматические расписания резервного копирования являются необязательными этапами. + +## Удаление и повторная установка с начала + +Файл: `ochenstarik-server-uninstall.sh`. + +Скрипт удаляет управляемые правила UFW, настройки IPv4/IPv6, SSH drop-in, fail2ban-конфигурацию, Telegram-hook, Xray, 3x-ui, WARP, расписания резервного копирования, swap и служебные файлы проекта. Перед изменением SSH открывается порт `22/tcp`. Правило порта, через который работает текущая SSH-сессия, сохраняется, чтобы не потерять доступ. + +Удаление администратора и самих архивов резервных копий требует отдельных подтверждений. Обязательные системные пакеты второго этапа и обновления Ubuntu сохраняются, чтобы не потерять доступ к серверу. Языковые пакеты и дополнительные программы удаляются только если первый скрипт зафиксировал, что до его запуска их не было. + +```bash +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-uninstall.sh +chmod 700 ochenstarik-server-uninstall.sh +bash -n ochenstarik-server-uninstall.sh +sudo ./ochenstarik-server-uninstall.sh +``` + +Не закрывайте текущую SSH-сессию. После очистки сначала проверьте отдельное подключение через порт 22, затем снова запускайте нужные установочные скрипты. Старое правило текущего SSH-порта можно удалить только после успешной проверки нового входа. + +## Безопасность + +- Всегда проверяйте скачанные скрипты перед запуском от `root`. +- Не публикуйте токены Telegram, пароли, приватные SSH-ключи и ссылки VLESS. +- Не закрывайте действующую SSH-сессию во время переноса SSH на новый порт. +- Перед настройкой на рабочем сервере сделайте резервную копию важных данных. diff --git a/docs/readme/README.zh-CN.md b/docs/readme/README.zh-CN.md new file mode 100644 index 0000000..c6b826c --- /dev/null +++ b/docs/readme/README.zh-CN.md @@ -0,0 +1,32 @@ +# Ubuntu Server 快速配置 + +[English](../../README.md) | [Русский](README.ru.md) | [Español](README.es.md) | [Deutsch](README.de.md) | [Français](README.fr.md) | [Português](README.pt.md) | **中文** | [日本語](README.ja.md) | [العربية](README.ar.md) | [हिन्दी](README.hi.md) + +一组模块化 Bash 脚本,用于 Ubuntu Server 的初始配置、安全加固、SSH、VPN、3x-ui、WARP 和备份。 + +> 如需在一个应用中监控并统一管理多台服务器,请使用 [Server Monitor Manager](https://github.com/ochenstarik-ui/server-monitor-manager)。该项目目前仍处于早期开发阶段。 + +## 快速安装 + +```bash +sudo apt-get update +sudo apt-get install -y curl ca-certificates +curl -4 -fLO --retry 5 --retry-delay 10 --connect-timeout 30 \ + https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-install.sh +chmod 700 ochenstarik-server-install.sh +sudo ./ochenstarik-server-install.sh +``` + +安装程序的第一个问题用于选择界面语言,默认是 English。随后七个步骤都可以运行、跳过或用于退出安装程序。 + +## 安装步骤 + +1. 时区、终端语言、推荐程序和交换文件。 +2. 基础软件包、SSH 端口、IPv4/IPv6 和 UFW。 +3. 管理员、SSH 密钥、权限、sudo 和 fail2ban。 +4. SSH 登录的 Telegram 通知。 +5. 通过 Xray 配置系统 VPN。 +6. 3x-ui 面板和本地 Cloudflare WARP 代理。 +7. 初始快照以及每日、每周或每月备份。 + +更改 SSH 端口时不要关闭当前会话。请先在另一个终端中测试新连接。更多说明请参阅[英文主文档](../../README.md)。 diff --git a/tests/test-readme-languages.sh b/tests/test-readme-languages.sh new file mode 100644 index 0000000..8d56f72 --- /dev/null +++ b/tests/test-readme-languages.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." + +readmes=( + README.md + docs/readme/README.ru.md + docs/readme/README.es.md + docs/readme/README.de.md + docs/readme/README.fr.md + docs/readme/README.pt.md + docs/readme/README.zh-CN.md + docs/readme/README.ja.md + docs/readme/README.ar.md + docs/readme/README.hi.md +) + +[[ "${#readmes[@]}" == 10 ]] +grep -Fqx '# Lightweight Ubuntu Server Setup' README.md + +for readme in "${readmes[@]}"; do + [[ -s "$readme" ]] + grep -Fq 'https://github.com/ochenstarik-ui/server-monitor-manager' "$readme" + grep -Fq 'English' "$readme" + grep -Fq 'Русский' "$readme" + grep -Fq 'Español' "$readme" + grep -Fq 'हिन्दी' "$readme" +done + +printf 'README language and monitoring links passed.\n' From 00434a9819910a22d1b4d38b976d68ccb0b8925c Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Wed, 15 Jul 2026 15:18:39 +0700 Subject: [PATCH 09/12] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=8D=D1=82=D0=B0=D0=BF=20=D1=83=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D0=BD=D0=BE=D0=B2=D0=BA=D0=B8=20=D0=BF=D0=BE=D0=BF=D1=83?= =?UTF-8?q?=D0=BB=D1=8F=D1=80=D0=BD=D1=8B=D1=85=20AI-=D0=B0=D0=B3=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=BE=D0=B2=20(#10)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add optional AI agent installation step * Keep validated agent installers root-owned --- .github/workflows/bash-syntax.yml | 3 + README.md | 10 +- docs/readme/README.ar.md | 3 +- docs/readme/README.de.md | 3 +- docs/readme/README.es.md | 3 +- docs/readme/README.fr.md | 3 +- docs/readme/README.hi.md | 3 +- docs/readme/README.ja.md | 3 +- docs/readme/README.pt.md | 3 +- docs/readme/README.ru.md | 21 ++- docs/readme/README.zh-CN.md | 3 +- ochenstarik-server-ai-agents-8.sh | 231 ++++++++++++++++++++++++++++++ ochenstarik-server-install.sh | 20 +-- tests/test-master-language.sh | 4 + tests/test-step8-agent-catalog.sh | 32 +++++ 15 files changed, 325 insertions(+), 20 deletions(-) create mode 100644 ochenstarik-server-ai-agents-8.sh create mode 100644 tests/test-step8-agent-catalog.sh diff --git a/.github/workflows/bash-syntax.yml b/.github/workflows/bash-syntax.yml index c205cf7..c9e047b 100644 --- a/.github/workflows/bash-syntax.yml +++ b/.github/workflows/bash-syntax.yml @@ -34,3 +34,6 @@ jobs: - name: Test step 3 SSH configuration parsing run: bash tests/test-step3-sshd-parsing.sh + + - name: Test step 8 AI agent catalog + run: bash tests/test-step8-agent-catalog.sh diff --git a/README.md b/README.md index 54c2d15..ec06c91 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ chmod 700 ./*.sh sudo ./ochenstarik-server-install.sh ``` -The first wizard prompt selects the dialog language. English is the default; Russian, Spanish, German, French, Portuguese, Simplified Chinese, Japanese, Arabic, and Hindi are also available. The wizard then presents steps 1–7 in order. Each step can be installed, skipped, or used to exit the wizard. +The first wizard prompt selects the dialog language. English is the default; Russian, Spanish, German, French, Portuguese, Simplified Chinese, Japanese, Arabic, and Hindi are also available. The wizard then presents steps 1–8 in order. Each step can be installed, skipped, or used to exit the wizard. Keep the current SSH session open while changing the SSH port. Test the new login in a second terminal before disconnecting. @@ -66,6 +66,7 @@ The wizard downloads any missing module from the `main` branch and validates it | `ochenstarik-server-vpn-5.sh` | System VPN through Xray with a selectable subscription port | Optional | | `ochenstarik-server-panel-warp-6.sh` | 3x-ui panel and local Cloudflare WARP proxy | Optional | | `ochenstarik-server-backup-7.sh` | Protected initial snapshot and selected daily, weekly, or monthly schedules | Optional | +| `ochenstarik-server-ai-agents-8.sh` | Installs selected AI agents for a regular user: Hermes, OpenClaw, OpenHands, OpenCode, or Aider | Optional | | `ochenstarik-server-uninstall.sh` | Removes project-managed settings so installation can start again | Use carefully | Every module can be downloaded and run independently: @@ -89,6 +90,13 @@ Replace `SCRIPT_NAME.sh` with the required filename from the table. 6. Optionally configure the system VPN and subscription port. 7. Optionally deploy 3x-ui and Cloudflare WARP with selected panel and subscription ports. 8. Optionally create the permanent initial backup and configure rotating schedules. +9. Optionally install one or more AI agents for the administrative user. + +## Optional AI agents + +Step 8 offers a multiple selection of five popular agents: Hermes Agent, OpenClaw, OpenHands, OpenCode, and Aider. Installers are downloaded only from each project's official HTTPS endpoint, checked with `bash -n`, and executed as the selected regular user rather than `root`. + +The script does not request or store model-provider API keys. After installation, log in as the selected user and run the displayed onboarding command. AI agents can execute commands, access files, install plugins, and use network services; review their permissions and sandbox settings before connecting production data or secrets. ## Firewall and SSH notes diff --git a/docs/readme/README.ar.md b/docs/readme/README.ar.md index 044230d..9c54b18 100644 --- a/docs/readme/README.ar.md +++ b/docs/readme/README.ar.md @@ -17,7 +17,7 @@ chmod 700 ochenstarik-server-install.sh sudo ./ochenstarik-server-install.sh ``` -السؤال الأول يحدد لغة المثبت، واللغة الافتراضية هي English. بعد ذلك يمكن تشغيل كل مرحلة من المراحل السبع أو تخطيها أو إنهاء المثبت. +السؤال الأول يحدد لغة المثبت، واللغة الافتراضية هي English. بعد ذلك يمكن تشغيل كل مرحلة من المراحل الثماني أو تخطيها أو إنهاء المثبت. ## المراحل @@ -28,5 +28,6 @@ sudo ./ochenstarik-server-install.sh 5. VPN للنظام باستخدام Xray. 6. لوحة 3x-ui ووكيل Cloudflare WARP المحلي. 7. النسخة الأولية والنسخ اليومية أو الأسبوعية أو الشهرية. +8. وكلاء ذكاء اصطناعي اختياريون: Hermes أو OpenClaw أو OpenHands أو OpenCode أو Aider. لا تغلق جلسة SSH الحالية أثناء تغيير المنفذ. اختبر الاتصال الجديد أولاً في طرفية أخرى. راجع [الوثائق الإنجليزية الرئيسية](../../README.md) لمزيد من التفاصيل. diff --git a/docs/readme/README.de.md b/docs/readme/README.de.md index 34f45d1..39ee3aa 100644 --- a/docs/readme/README.de.md +++ b/docs/readme/README.de.md @@ -17,7 +17,7 @@ chmod 700 ochenstarik-server-install.sh sudo ./ochenstarik-server-install.sh ``` -Die erste Frage wählt die Sprache des Installers; English ist voreingestellt. Danach kann jeder der sieben Schritte ausgeführt, übersprungen oder zum Beenden verwendet werden. +Die erste Frage wählt die Sprache des Installers; English ist voreingestellt. Danach kann jeder der acht Schritte ausgeführt, übersprungen oder zum Beenden verwendet werden. ## Schritte @@ -28,5 +28,6 @@ Die erste Frage wählt die Sprache des Installers; English ist voreingestellt. D 5. System-VPN über Xray. 6. 3x-ui und lokaler Cloudflare-WARP-Proxy. 7. Erstsicherung sowie tägliche, wöchentliche oder monatliche Sicherungen. +8. Optionale KI-Agenten: Hermes, OpenClaw, OpenHands, OpenCode oder Aider. Schließen Sie die aktuelle SSH-Sitzung während der Portänderung nicht. Testen Sie zuerst die neue Anmeldung in einem zweiten Terminal. Weitere Hinweise enthält die [englische Hauptdokumentation](../../README.md). diff --git a/docs/readme/README.es.md b/docs/readme/README.es.md index 457868d..ed4f7ea 100644 --- a/docs/readme/README.es.md +++ b/docs/readme/README.es.md @@ -17,7 +17,7 @@ chmod 700 ochenstarik-server-install.sh sudo ./ochenstarik-server-install.sh ``` -La primera pregunta selecciona el idioma del instalador; English es el valor predeterminado. Después puede instalar, omitir o detener cada una de las siete etapas. +La primera pregunta selecciona el idioma del instalador; English es el valor predeterminado. Después puede instalar, omitir o detener cada una de las ocho etapas. ## Etapas @@ -28,5 +28,6 @@ La primera pregunta selecciona el idioma del instalador; English es el valor pre 5. VPN del sistema mediante Xray. 6. Panel 3x-ui y proxy local Cloudflare WARP. 7. Instantánea inicial y copias diarias, semanales o mensuales. +8. Agentes de IA opcionales: Hermes, OpenClaw, OpenHands, OpenCode o Aider. No cierre la sesión SSH actual durante el cambio de puerto. Pruebe primero el nuevo acceso en otro terminal. Consulte la [documentación principal en inglés](../../README.md) para la instalación por archivo, actualización y restablecimiento. diff --git a/docs/readme/README.fr.md b/docs/readme/README.fr.md index b86c4e0..1fa834d 100644 --- a/docs/readme/README.fr.md +++ b/docs/readme/README.fr.md @@ -17,7 +17,7 @@ chmod 700 ochenstarik-server-install.sh sudo ./ochenstarik-server-install.sh ``` -La première question sélectionne la langue de l’installateur ; English est la valeur par défaut. Chacune des sept étapes peut ensuite être installée, ignorée ou utilisée pour quitter. +La première question sélectionne la langue de l’installateur ; English est la valeur par défaut. Chacune des huit étapes peut ensuite être installée, ignorée ou utilisée pour quitter. ## Étapes @@ -28,5 +28,6 @@ La première question sélectionne la langue de l’installateur ; English est l 5. VPN système via Xray. 6. Panneau 3x-ui et proxy local Cloudflare WARP. 7. Instantané initial et sauvegardes quotidiennes, hebdomadaires ou mensuelles. +8. Agents IA facultatifs : Hermes, OpenClaw, OpenHands, OpenCode ou Aider. Ne fermez pas la session SSH actuelle pendant le changement de port. Testez d’abord la nouvelle connexion dans un autre terminal. Consultez la [documentation principale en anglais](../../README.md) pour plus de détails. diff --git a/docs/readme/README.hi.md b/docs/readme/README.hi.md index 0a69f30..04e3930 100644 --- a/docs/readme/README.hi.md +++ b/docs/readme/README.hi.md @@ -17,7 +17,7 @@ chmod 700 ochenstarik-server-install.sh sudo ./ochenstarik-server-install.sh ``` -पहला प्रश्न इंस्टॉलर की भाषा चुनता है; डिफ़ॉल्ट English है। इसके बाद सातों चरणों को चलाया, छोड़ा या इंस्टॉलर बंद किया जा सकता है। +पहला प्रश्न इंस्टॉलर की भाषा चुनता है; डिफ़ॉल्ट English है। इसके बाद आठों चरणों को चलाया, छोड़ा या इंस्टॉलर बंद किया जा सकता है। ## चरण @@ -28,5 +28,6 @@ sudo ./ochenstarik-server-install.sh 5. Xray के माध्यम से सिस्टम VPN। 6. 3x-ui पैनल और स्थानीय Cloudflare WARP प्रॉक्सी। 7. प्रारंभिक स्नैपशॉट और दैनिक, साप्ताहिक या मासिक बैकअप। +8. वैकल्पिक AI एजेंट: Hermes, OpenClaw, OpenHands, OpenCode या Aider। SSH पोर्ट बदलते समय मौजूदा सत्र बंद न करें। पहले दूसरे टर्मिनल में नया लॉगिन जाँचें। अधिक जानकारी के लिए [मुख्य अंग्रेज़ी दस्तावेज़](../../README.md) देखें। diff --git a/docs/readme/README.ja.md b/docs/readme/README.ja.md index 8e15e1e..aabd977 100644 --- a/docs/readme/README.ja.md +++ b/docs/readme/README.ja.md @@ -17,7 +17,7 @@ chmod 700 ochenstarik-server-install.sh sudo ./ochenstarik-server-install.sh ``` -最初の質問でインストーラーの言語を選択します。既定値は English です。その後、7 つの各ステップを実行、スキップ、または終了できます。 +最初の質問でインストーラーの言語を選択します。既定値は English です。その後、8 つの各ステップを実行、スキップ、または終了できます。 ## ステップ @@ -28,5 +28,6 @@ sudo ./ochenstarik-server-install.sh 5. Xray によるシステム VPN。 6. 3x-ui パネルとローカル Cloudflare WARP プロキシ。 7. 初回スナップショットと日次、週次、月次バックアップ。 +8. オプションの AI エージェント:Hermes、OpenClaw、OpenHands、OpenCode、Aider。 SSH ポートの変更中は現在のセッションを閉じないでください。別のターミナルで新しい接続を先に確認してください。詳細は[英語のメイン文書](../../README.md)を参照してください。 diff --git a/docs/readme/README.pt.md b/docs/readme/README.pt.md index 0e8d360..f1e3b48 100644 --- a/docs/readme/README.pt.md +++ b/docs/readme/README.pt.md @@ -17,7 +17,7 @@ chmod 700 ochenstarik-server-install.sh sudo ./ochenstarik-server-install.sh ``` -A primeira pergunta seleciona o idioma do instalador; English é o padrão. Depois, cada uma das sete etapas pode ser instalada, ignorada ou usada para sair. +A primeira pergunta seleciona o idioma do instalador; English é o padrão. Depois, cada uma das oito etapas pode ser instalada, ignorada ou usada para sair. ## Etapas @@ -28,5 +28,6 @@ A primeira pergunta seleciona o idioma do instalador; English é o padrão. Depo 5. VPN do sistema por Xray. 6. Painel 3x-ui e proxy local Cloudflare WARP. 7. Snapshot inicial e backups diários, semanais ou mensais. +8. Agentes de IA opcionais: Hermes, OpenClaw, OpenHands, OpenCode ou Aider. Não feche a sessão SSH atual durante a alteração da porta. Teste primeiro o novo login em outro terminal. Consulte a [documentação principal em inglês](../../README.md) para obter detalhes adicionais. diff --git a/docs/readme/README.ru.md b/docs/readme/README.ru.md index 2a3a022..5746914 100644 --- a/docs/readme/README.ru.md +++ b/docs/readme/README.ru.md @@ -32,7 +32,7 @@ chmod 700 ./*.sh sudo ./ochenstarik-server-install.sh ``` -Первым запросом мастер предлагает выбрать язык диалога и только затем показывает этапы 1–7 по порядку. На каждом этапе можно выбрать установку, пропуск или завершение. В первом этапе пустой ввод устанавливает все рекомендуемые наборы программ. Во время изменения SSH-порта не закрывайте текущую сессию — сначала проверьте новый вход во втором терминале. +Первым запросом мастер предлагает выбрать язык диалога и только затем показывает этапы 1–8 по порядку. На каждом этапе можно выбрать установку, пропуск или завершение. В первом этапе пустой ввод устанавливает все рекомендуемые наборы программ. Во время изменения SSH-порта не закрывайте текущую сессию — сначала проверьте новый вход во втором терминале. Если нужен только мастер без скачивания полного архива: @@ -62,6 +62,7 @@ sudo ./ochenstarik-server-install.sh | `ochenstarik-server-vpn-5.sh` | Устанавливает Xray и направляет системный трафик через VLESS + REALITY | Да; зависимости устанавливаются автоматически | | `ochenstarik-server-panel-warp-6.sh` | Устанавливает 3x-ui и Cloudflare WARP, настраивает и проверяет их порты | Ubuntu 22.04/24.04/26.04 или Debian 12/13 | | `ochenstarik-server-backup-7.sh` | Создаёт неизменяемый первичный снимок и настраивает выбранные расписания резервного копирования | Да; зависимости устанавливаются автоматически | +| `ochenstarik-server-ai-agents-8.sh` | Устанавливает выбранных AI-агентов для обычного пользователя: Hermes, OpenClaw, OpenHands, OpenCode или Aider | Да; API-ключи настраиваются отдельно | | `ochenstarik-server-uninstall.sh` | Удаляет настройки, службы и данные проекта для повторной установки с начала | Да; запускать только из сохранённой SSH-сессии | Файлы `*.env.example` являются только примерами. Не записывайте настоящие пароли, токены и приватные SSH-ключи в Git. @@ -398,9 +399,25 @@ sudo ./ochenstarik-server-tg-4.sh sudo ./ochenstarik-server-vpn-5.sh sudo ./ochenstarik-server-panel-warp-6.sh sudo ./ochenstarik-server-backup-7.sh +sudo ./ochenstarik-server-ai-agents-8.sh ``` -VPN, 3x-ui, WARP, Telegram-уведомления и автоматические расписания резервного копирования являются необязательными этапами. +## 8. AI-агенты на выбор + +Файл: `ochenstarik-server-ai-agents-8.sh`. + +Скрипт предлагает множественный выбор из Hermes Agent, OpenClaw, OpenHands, OpenCode и Aider. Агенты устанавливаются только для выбранного обычного пользователя, а не для `root`. Каждый официальный установщик сначала загружается по HTTPS во временный файл и проходит проверку `bash -n`. + +API-ключи скрипт не запрашивает и не сохраняет. После установки нужно войти под выбранным пользователем и выполнить показанную команду первоначальной настройки. AI-агенты могут запускать команды, изменять файлы, использовать сеть и устанавливать плагины, поэтому перед подключением рабочих данных необходимо проверить разрешения, песочницу и доступ к секретам. + +```bash +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-ai-agents-8.sh +chmod 700 ochenstarik-server-ai-agents-8.sh +bash -n ochenstarik-server-ai-agents-8.sh +sudo ./ochenstarik-server-ai-agents-8.sh +``` + +VPN, 3x-ui, WARP, Telegram-уведомления, автоматические расписания резервного копирования и AI-агенты являются необязательными этапами. ## Удаление и повторная установка с начала diff --git a/docs/readme/README.zh-CN.md b/docs/readme/README.zh-CN.md index c6b826c..09e2c6e 100644 --- a/docs/readme/README.zh-CN.md +++ b/docs/readme/README.zh-CN.md @@ -17,7 +17,7 @@ chmod 700 ochenstarik-server-install.sh sudo ./ochenstarik-server-install.sh ``` -安装程序的第一个问题用于选择界面语言,默认是 English。随后七个步骤都可以运行、跳过或用于退出安装程序。 +安装程序的第一个问题用于选择界面语言,默认是 English。随后八个步骤都可以运行、跳过或用于退出安装程序。 ## 安装步骤 @@ -28,5 +28,6 @@ sudo ./ochenstarik-server-install.sh 5. 通过 Xray 配置系统 VPN。 6. 3x-ui 面板和本地 Cloudflare WARP 代理。 7. 初始快照以及每日、每周或每月备份。 +8. 可选 AI 代理:Hermes、OpenClaw、OpenHands、OpenCode 或 Aider。 更改 SSH 端口时不要关闭当前会话。请先在另一个终端中测试新连接。更多说明请参阅[英文主文档](../../README.md)。 diff --git a/ochenstarik-server-ai-agents-8.sh b/ochenstarik-server-ai-agents-8.sh new file mode 100644 index 0000000..68cb9b4 --- /dev/null +++ b/ochenstarik-server-ai-agents-8.sh @@ -0,0 +1,231 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +readonly CONFIG_DIR="/etc/ochenstarik-server" +readonly AGENT_CONFIG="${CONFIG_DIR}/ai-agents.conf" +readonly STEP3_ENV="/root/setup-data/env.txt" + +readonly -a AGENT_IDS=(hermes openclaw openhands opencode aider) +readonly -a AGENT_NAMES=( + "Hermes Agent" + "OpenClaw" + "OpenHands" + "OpenCode" + "Aider" +) +readonly -a AGENT_DESCRIPTIONS=( + "Personal self-improving agent with memory, skills, schedules, and messaging gateways" + "Personal assistant with persistent memory and Telegram, WhatsApp, Discord, Slack, and other channels" + "Software-development agent with terminal, headless, web, and IDE modes" + "Terminal coding agent with multiple model providers and parallel sessions" + "Git-oriented coding agent for editing and reviewing an existing repository" +) +readonly -a AGENT_INSTALL_URLS=( + "https://hermes-agent.nousresearch.com/install.sh" + "https://openclaw.ai/install.sh" + "https://install.openhands.dev/install.sh" + "https://opencode.ai/install" + "https://aider.chat/install.sh" +) +readonly -a AGENT_COMMANDS=(hermes openclaw openhands opencode aider) +readonly -a AGENT_SETUP_HINTS=( + "hermes setup" + "openclaw onboard --install-daemon" + "openhands" + "opencode" + "aider" +) + +TEMP_INSTALLER="" + +log() { printf '[+] %s\n' "$*"; } +warn() { printf '[!] %s\n' "$*" >&2; } +die() { printf '[x] %s\n' "$*" >&2; exit 1; } + +cleanup() { + if [[ -n "$TEMP_INSTALLER" && "$TEMP_INSTALLER" == /tmp/ochenstarik-ai-installer.* \ + && -f "$TEMP_INSTALLER" && ! -L "$TEMP_INSTALLER" ]]; then + rm -f -- "$TEMP_INSTALLER" + fi +} +trap cleanup EXIT + +require_command() { + command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1" +} + +default_target_user() { + local username="" + if [[ -f "$STEP3_ENV" && ! -L "$STEP3_ENV" ]]; then + username="$(awk -F= '$1 == "NEW_USERNAME" && !found { sub(/\r$/, "", $2); print $2; found=1 }' "$STEP3_ENV")" + if [[ "$username" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]] && id "$username" >/dev/null 2>&1; then + printf '%s' "$username" + return 0 + fi + fi + getent passwd | awk -F: '$3 >= 1000 && $3 != 65534 && $1 != "nobody" && !found { print $1; found=1 }' +} + +select_target_user() { + local default_user username uid home owner + default_user="$(default_target_user)" + + printf '\nAI agents must be installed for a regular user, never for root.\n' + while :; do + if [[ -n "$default_user" ]]; then + read -rp "Installation user [${default_user}]: " username || die "Input was interrupted" + username="${username:-$default_user}" + else + read -rp 'Installation user: ' username || die "Input was interrupted" + fi + + [[ "$username" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]] || { warn "Invalid username"; continue; } + [[ "$username" != root ]] || { warn "Root cannot be used for AI agents"; continue; } + id "$username" >/dev/null 2>&1 || { warn "User does not exist: $username"; continue; } + uid="$(id -u "$username")" + ((uid >= 1000 && uid != 65534)) || { warn "System accounts are not allowed"; continue; } + home="$(getent passwd "$username" | cut -d: -f6)" + [[ -n "$home" && "$home" == /* && -d "$home" && ! -L "$home" ]] \ + || { warn "A safe home directory was not found for $username"; continue; } + owner="$(stat -c %U "$home")" + [[ "$owner" == "$username" ]] || { warn "$home must be owned by $username"; continue; } + + TARGET_USER="$username" + TARGET_HOME="$home" + TARGET_GROUP="$(id -gn "$username")" + return 0 + done +} + +select_agents() { + local input token index + local -A seen=() + SELECTED_INDEXES=() + + printf '\nSelect one or more AI agents to install:\n' + for index in "${!AGENT_IDS[@]}"; do + printf ' %d) %s\n %s\n Official installer: %s\n' \ + "$((index + 1))" "${AGENT_NAMES[index]}" "${AGENT_DESCRIPTIONS[index]}" \ + "${AGENT_INSTALL_URLS[index]}" + done + printf ' 0) Do not install AI agents\n' + printf '\nEnter numbers separated by spaces, for example: 1 2 4.\n' + + while :; do + read -rp 'Selection [0]: ' input || die "Input was interrupted" + input="${input//,/ }" + input="${input:-0}" + if [[ "$input" =~ ^[[:space:]]*0[[:space:]]*$ ]]; then + return 0 + fi + + SELECTED_INDEXES=() + seen=() + for token in $input; do + if [[ ! "$token" =~ ^[1-5]$ ]]; then + warn "Enter agent numbers from 1 to 5, or 0 to skip" + SELECTED_INDEXES=() + break + fi + index="$((token - 1))" + if [[ -z "${seen[$index]:-}" ]]; then + SELECTED_INDEXES+=("$index") + seen["$index"]=1 + fi + done + ((${#SELECTED_INDEXES[@]} > 0)) && return 0 + done +} + +install_agent() { + local index="$1" name url command_name path_value + name="${AGENT_NAMES[index]}" + url="${AGENT_INSTALL_URLS[index]}" + command_name="${AGENT_COMMANDS[index]}" + path_value="${TARGET_HOME}/.local/bin:${TARGET_HOME}/.opencode/bin:${TARGET_HOME}/.hermes/bin:${TARGET_HOME}/.local/share/pnpm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + + log "Downloading the official ${name} installer" + TEMP_INSTALLER="$(mktemp /tmp/ochenstarik-ai-installer.XXXXXX)" + chmod 600 "$TEMP_INSTALLER" + curl -fL --retry 5 --retry-delay 5 --connect-timeout 30 \ + --proto '=https' --tlsv1.2 "$url" -o "$TEMP_INSTALLER" + [[ -s "$TEMP_INSTALLER" && ! -L "$TEMP_INSTALLER" ]] \ + || die "The downloaded ${name} installer is empty or unsafe" + bash -n "$TEMP_INSTALLER" || die "The official ${name} installer failed Bash syntax validation" + + # Keep the validated file root-owned until execution so the target account + # cannot replace it between validation and launch. + chown root:root "$TEMP_INSTALLER" + chmod 555 "$TEMP_INSTALLER" + log "Installing ${name} for ${TARGET_USER}" + runuser -u "$TARGET_USER" -- env \ + HOME="$TARGET_HOME" USER="$TARGET_USER" LOGNAME="$TARGET_USER" SHELL=/bin/bash PATH="$path_value" \ + bash "$TEMP_INSTALLER" + + rm -f -- "$TEMP_INSTALLER" + TEMP_INSTALLER="" + + if runuser -u "$TARGET_USER" -- env HOME="$TARGET_HOME" PATH="$path_value" \ + bash -lc "command -v '$command_name' >/dev/null 2>&1"; then + log "${name} command is available: ${command_name}" + else + warn "${name} installer completed, but ${command_name} is not visible in a new login shell yet" + warn "Log in again or reload the user's shell configuration before running it" + fi +} + +write_state() { + local ids="" index + install -d -m 700 -o root -g root "$CONFIG_DIR" + [[ ! -L "$AGENT_CONFIG" ]] || die "$AGENT_CONFIG must not be a symbolic link" + for index in "${SELECTED_INDEXES[@]}"; do + ids+="${ids:+ }${AGENT_IDS[index]}" + done + umask 077 + printf 'TARGET_USER=%s\nAGENTS=%s\n' "$TARGET_USER" "$ids" > "$AGENT_CONFIG" + chown root:root "$AGENT_CONFIG" + chmod 600 "$AGENT_CONFIG" +} + +[[ "$EUID" -eq 0 ]] || die "Run this script as root" +require_command awk +require_command apt-get +require_command bash +require_command getent +require_command runuser +require_command stat + +((${#AGENT_IDS[@]} == 5 && ${#AGENT_NAMES[@]} == 5 \ + && ${#AGENT_DESCRIPTIONS[@]} == 5 && ${#AGENT_INSTALL_URLS[@]} == 5 \ + && ${#AGENT_COMMANDS[@]} == 5 && ${#AGENT_SETUP_HINTS[@]} == 5)) \ + || die "Invalid AI agent catalog" + +select_target_user +select_agents +if ((${#SELECTED_INDEXES[@]} == 0)); then + log "AI agent installation was skipped" + exit 0 +fi + +export DEBIAN_FRONTEND=noninteractive +apt-get update +apt-get install -y curl ca-certificates git +require_command curl + +printf '\nThe selected agents can execute commands, access files, and use network services.\n' +printf 'Review permissions, tools, plugins, mounted directories, and API-key access during onboarding.\n' +read -rp "Type INSTALL to continue for user ${TARGET_USER}: " confirmation || die "Input was interrupted" +[[ "$confirmation" == INSTALL ]] || die "AI agent installation was cancelled" + +for selected_index in "${SELECTED_INDEXES[@]}"; do + install_agent "$selected_index" +done +write_state + +printf '\nInstalled AI agents for %s:\n' "$TARGET_USER" +for selected_index in "${SELECTED_INDEXES[@]}"; do + printf ' - %s\n Next step: %s\n' \ + "${AGENT_NAMES[selected_index]}" "${AGENT_SETUP_HINTS[selected_index]}" +done +printf '\nRun the setup commands while logged in as %s, not as root.\n' "$TARGET_USER" +printf 'API keys are managed by each agent and were not requested or stored by this script.\n' diff --git a/ochenstarik-server-install.sh b/ochenstarik-server-install.sh index c6f7b4b..8727c9b 100644 --- a/ochenstarik-server-install.sh +++ b/ochenstarik-server-install.sh @@ -12,6 +12,7 @@ declare -a STEP_FILES=( "ochenstarik-server-vpn-5.sh" "ochenstarik-server-panel-warp-6.sh" "ochenstarik-server-backup-7.sh" + "ochenstarik-server-ai-agents-8.sh" ) declare -a STEP_TITLES=( @@ -22,6 +23,7 @@ declare -a STEP_TITLES=( "System VPN through Xray" "3x-ui panel and Cloudflare WARP" "Initial snapshot and backup schedules" + "Optional AI agents for a regular user" ) TEMP_FILE="" @@ -156,15 +158,15 @@ master_msg() { localize_step_titles() { case "$UI_LANG" in - ru) STEP_TITLES=("Часовой пояс, язык терминала, программы и swap" "Базовые пакеты, SSH-порт, IPv4/IPv6 и UFW" "Администратор, перенос SSH и fail2ban" "Telegram-уведомления о входах по SSH" "Системный VPN через Xray" "Панель 3x-ui и Cloudflare WARP" "Первичный снимок и расписания бэкапов") ;; - es) STEP_TITLES=("Zona horaria, idioma del terminal, programas y swap" "Paquetes base, puerto SSH, IPv4/IPv6 y UFW" "Administrador, migración SSH y fail2ban" "Notificaciones de Telegram para accesos SSH" "VPN del sistema mediante Xray" "Panel 3x-ui y Cloudflare WARP" "Instantánea inicial y copias programadas") ;; - de) STEP_TITLES=("Zeitzone, Terminalsprache, Programme und Swap" "Basispakete, SSH-Port, IPv4/IPv6 und UFW" "Administrator, SSH-Umstellung und fail2ban" "Telegram-Benachrichtigungen für SSH-Anmeldungen" "System-VPN über Xray" "3x-ui und Cloudflare WARP" "Erstsicherung und Sicherungspläne") ;; - fr) STEP_TITLES=("Fuseau horaire, langue du terminal, programmes et swap" "Paquets de base, port SSH, IPv4/IPv6 et UFW" "Administrateur, migration SSH et fail2ban" "Notifications Telegram des connexions SSH" "VPN système via Xray" "Panneau 3x-ui et Cloudflare WARP" "Instantané initial et sauvegardes planifiées") ;; - pt) STEP_TITLES=("Fuso horário, idioma do terminal, programas e swap" "Pacotes básicos, porta SSH, IPv4/IPv6 e UFW" "Administrador, migração SSH e fail2ban" "Notificações Telegram de logins SSH" "VPN do sistema via Xray" "Painel 3x-ui e Cloudflare WARP" "Snapshot inicial e backups agendados") ;; - zh) STEP_TITLES=("时区、终端语言、程序和交换文件" "基础软件包、SSH 端口、IPv4/IPv6 和 UFW" "管理员、SSH 迁移和 fail2ban" "SSH 登录的 Telegram 通知" "通过 Xray 的系统 VPN" "3x-ui 面板和 Cloudflare WARP" "初始快照和备份计划") ;; - ja) STEP_TITLES=("タイムゾーン、端末言語、プログラム、スワップ" "基本パッケージ、SSH ポート、IPv4/IPv6、UFW" "管理者、SSH 移行、fail2ban" "SSH ログインの Telegram 通知" "Xray によるシステム VPN" "3x-ui パネルと Cloudflare WARP" "初回スナップショットとバックアップ予定") ;; - ar) STEP_TITLES=("المنطقة الزمنية ولغة الطرفية والبرامج وswap" "الحزم الأساسية ومنفذ SSH وIPv4/IPv6 وUFW" "المسؤول ونقل SSH وfail2ban" "إشعارات Telegram لدخول SSH" "VPN للنظام عبر Xray" "لوحة 3x-ui وCloudflare WARP" "النسخة الأولية وجداول النسخ الاحتياطي") ;; - hi) STEP_TITLES=("समय क्षेत्र, टर्मिनल भाषा, प्रोग्राम और swap" "मूल पैकेज, SSH पोर्ट, IPv4/IPv6 और UFW" "प्रशासक, SSH स्थानांतरण और fail2ban" "SSH लॉगिन की Telegram सूचनाएँ" "Xray द्वारा सिस्टम VPN" "3x-ui पैनल और Cloudflare WARP" "प्रारंभिक स्नैपशॉट और बैकअप अनुसूचियाँ") ;; + ru) STEP_TITLES=("Часовой пояс, язык терминала, программы и swap" "Базовые пакеты, SSH-порт, IPv4/IPv6 и UFW" "Администратор, перенос SSH и fail2ban" "Telegram-уведомления о входах по SSH" "Системный VPN через Xray" "Панель 3x-ui и Cloudflare WARP" "Первичный снимок и расписания бэкапов" "AI-агенты на выбор для обычного пользователя") ;; + es) STEP_TITLES=("Zona horaria, idioma del terminal, programas y swap" "Paquetes base, puerto SSH, IPv4/IPv6 y UFW" "Administrador, migración SSH y fail2ban" "Notificaciones de Telegram para accesos SSH" "VPN del sistema mediante Xray" "Panel 3x-ui y Cloudflare WARP" "Instantánea inicial y copias programadas" "Agentes de IA opcionales para un usuario normal") ;; + de) STEP_TITLES=("Zeitzone, Terminalsprache, Programme und Swap" "Basispakete, SSH-Port, IPv4/IPv6 und UFW" "Administrator, SSH-Umstellung und fail2ban" "Telegram-Benachrichtigungen für SSH-Anmeldungen" "System-VPN über Xray" "3x-ui und Cloudflare WARP" "Erstsicherung und Sicherungspläne" "Optionale KI-Agenten für einen normalen Benutzer") ;; + fr) STEP_TITLES=("Fuseau horaire, langue du terminal, programmes et swap" "Paquets de base, port SSH, IPv4/IPv6 et UFW" "Administrateur, migration SSH et fail2ban" "Notifications Telegram des connexions SSH" "VPN système via Xray" "Panneau 3x-ui et Cloudflare WARP" "Instantané initial et sauvegardes planifiées" "Agents IA facultatifs pour un utilisateur standard") ;; + pt) STEP_TITLES=("Fuso horário, idioma do terminal, programas e swap" "Pacotes básicos, porta SSH, IPv4/IPv6 e UFW" "Administrador, migração SSH e fail2ban" "Notificações Telegram de logins SSH" "VPN do sistema via Xray" "Painel 3x-ui e Cloudflare WARP" "Snapshot inicial e backups agendados" "Agentes de IA opcionais para um usuário comum") ;; + zh) STEP_TITLES=("时区、终端语言、程序和交换文件" "基础软件包、SSH 端口、IPv4/IPv6 和 UFW" "管理员、SSH 迁移和 fail2ban" "SSH 登录的 Telegram 通知" "通过 Xray 的系统 VPN" "3x-ui 面板和 Cloudflare WARP" "初始快照和备份计划" "为普通用户安装可选 AI 代理") ;; + ja) STEP_TITLES=("タイムゾーン、端末言語、プログラム、スワップ" "基本パッケージ、SSH ポート、IPv4/IPv6、UFW" "管理者、SSH 移行、fail2ban" "SSH ログインの Telegram 通知" "Xray によるシステム VPN" "3x-ui パネルと Cloudflare WARP" "初回スナップショットとバックアップ予定" "一般ユーザー向けのオプション AI エージェント") ;; + ar) STEP_TITLES=("المنطقة الزمنية ولغة الطرفية والبرامج وswap" "الحزم الأساسية ومنفذ SSH وIPv4/IPv6 وUFW" "المسؤول ونقل SSH وfail2ban" "إشعارات Telegram لدخول SSH" "VPN للنظام عبر Xray" "لوحة 3x-ui وCloudflare WARP" "النسخة الأولية وجداول النسخ الاحتياطي" "وكلاء ذكاء اصطناعي اختياريون لمستخدم عادي") ;; + hi) STEP_TITLES=("समय क्षेत्र, टर्मिनल भाषा, प्रोग्राम और swap" "मूल पैकेज, SSH पोर्ट, IPv4/IPv6 और UFW" "प्रशासक, SSH स्थानांतरण और fail2ban" "SSH लॉगिन की Telegram सूचनाएँ" "Xray द्वारा सिस्टम VPN" "3x-ui पैनल और Cloudflare WARP" "प्रारंभिक स्नैपशॉट और बैकअप अनुसूचियाँ" "सामान्य उपयोगकर्ता के लिए वैकल्पिक AI एजेंट") ;; esac } diff --git a/tests/test-master-language.sh b/tests/test-master-language.sh index dc58172..9f4e551 100644 --- a/tests/test-master-language.sh +++ b/tests/test-master-language.sh @@ -18,5 +18,9 @@ choose_dialog_language <<< '' >/dev/null choose_dialog_language <<< '2' >/dev/null [[ "$(master_msg install)" == 'Установить / запустить этот этап' ]] [[ "${STEP_TITLES[0]}" == 'Часовой пояс, язык терминала, программы и swap' ]] +[[ "${#STEP_FILES[@]}" == 8 ]] +[[ "${#STEP_TITLES[@]}" == 8 ]] +[[ "${STEP_FILES[7]}" == 'ochenstarik-server-ai-agents-8.sh' ]] +[[ "${STEP_TITLES[7]}" == 'AI-агенты на выбор для обычного пользователя' ]] printf 'Master language selection tests passed.\n' diff --git a/tests/test-step8-agent-catalog.sh b/tests/test-step8-agent-catalog.sh new file mode 100644 index 0000000..488fa53 --- /dev/null +++ b/tests/test-step8-agent-catalog.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." +source <(awk '/^\[\[ "\$EUID"/{exit} {print}' ochenstarik-server-ai-agents-8.sh) + +[[ "${#AGENT_IDS[@]}" == 5 ]] +[[ "${AGENT_IDS[*]}" == 'hermes openclaw openhands opencode aider' ]] + +expected_urls=( + 'https://hermes-agent.nousresearch.com/install.sh' + 'https://openclaw.ai/install.sh' + 'https://install.openhands.dev/install.sh' + 'https://opencode.ai/install' + 'https://aider.chat/install.sh' +) + +for index in "${!expected_urls[@]}"; do + [[ "${AGENT_INSTALL_URLS[index]}" == "${expected_urls[index]}" ]] +done + +select_agents <<< '1 3 5' >/dev/null +[[ "${SELECTED_INDEXES[*]}" == '0 2 4' ]] +select_agents <<< '' >/dev/null +[[ "${#SELECTED_INDEXES[@]}" == 0 ]] + +if grep -E 'curl[^|]*\|[[:space:]]*(ba)?sh' ochenstarik-server-ai-agents-8.sh; then + printf 'Direct curl-to-shell execution found.\n' >&2 + exit 1 +fi + +printf 'Step 8 AI agent catalog tests passed.\n' From 5844b4a637e5e7bf102e14d38cadfd3adad19da9 Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Wed, 15 Jul 2026 15:50:26 +0700 Subject: [PATCH 10/12] Add AutoGPT and Pi coding agents (#11) --- README.md | 6 ++++-- docs/readme/README.ar.md | 2 +- docs/readme/README.de.md | 2 +- docs/readme/README.es.md | 2 +- docs/readme/README.fr.md | 2 +- docs/readme/README.hi.md | 2 +- docs/readme/README.ja.md | 2 +- docs/readme/README.pt.md | 2 +- docs/readme/README.ru.md | 6 ++++-- docs/readme/README.zh-CN.md | 2 +- ochenstarik-server-ai-agents-8.sh | 24 ++++++++++++++++-------- tests/test-step8-agent-catalog.sh | 10 ++++++---- 12 files changed, 38 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index ec06c91..836439f 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ The wizard downloads any missing module from the `main` branch and validates it | `ochenstarik-server-vpn-5.sh` | System VPN through Xray with a selectable subscription port | Optional | | `ochenstarik-server-panel-warp-6.sh` | 3x-ui panel and local Cloudflare WARP proxy | Optional | | `ochenstarik-server-backup-7.sh` | Protected initial snapshot and selected daily, weekly, or monthly schedules | Optional | -| `ochenstarik-server-ai-agents-8.sh` | Installs selected AI agents for a regular user: Hermes, OpenClaw, OpenHands, OpenCode, or Aider | Optional | +| `ochenstarik-server-ai-agents-8.sh` | Installs selected AI agents: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT, or Pi Coding Agent | Optional | | `ochenstarik-server-uninstall.sh` | Removes project-managed settings so installation can start again | Use carefully | Every module can be downloaded and run independently: @@ -94,7 +94,9 @@ Replace `SCRIPT_NAME.sh` with the required filename from the table. ## Optional AI agents -Step 8 offers a multiple selection of five popular agents: Hermes Agent, OpenClaw, OpenHands, OpenCode, and Aider. Installers are downloaded only from each project's official HTTPS endpoint, checked with `bash -n`, and executed as the selected regular user rather than `root`. +Step 8 offers a multiple selection of seven popular agents: Hermes Agent, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT, and Pi Coding Agent. Installers are downloaded only from each project's official HTTPS endpoint, checked with `bash -n`, and launched as the selected regular user rather than directly as `root`. + +AutoGPT is a larger Docker-based platform. Its official installer can request `sudo`, install Docker-related dependencies, and start local services. Pi is a lightweight terminal coding agent; after installation, start it with `pi` and use `/login` to connect a supported provider. The script does not request or store model-provider API keys. After installation, log in as the selected user and run the displayed onboarding command. AI agents can execute commands, access files, install plugins, and use network services; review their permissions and sandbox settings before connecting production data or secrets. diff --git a/docs/readme/README.ar.md b/docs/readme/README.ar.md index 9c54b18..7b29938 100644 --- a/docs/readme/README.ar.md +++ b/docs/readme/README.ar.md @@ -28,6 +28,6 @@ sudo ./ochenstarik-server-install.sh 5. VPN للنظام باستخدام Xray. 6. لوحة 3x-ui ووكيل Cloudflare WARP المحلي. 7. النسخة الأولية والنسخ اليومية أو الأسبوعية أو الشهرية. -8. وكلاء ذكاء اصطناعي اختياريون: Hermes أو OpenClaw أو OpenHands أو OpenCode أو Aider. +8. وكلاء ذكاء اصطناعي اختياريون: Hermes أو OpenClaw أو OpenHands أو OpenCode أو Aider أو AutoGPT أو Pi Coding Agent. لا تغلق جلسة SSH الحالية أثناء تغيير المنفذ. اختبر الاتصال الجديد أولاً في طرفية أخرى. راجع [الوثائق الإنجليزية الرئيسية](../../README.md) لمزيد من التفاصيل. diff --git a/docs/readme/README.de.md b/docs/readme/README.de.md index 39ee3aa..35b4ba5 100644 --- a/docs/readme/README.de.md +++ b/docs/readme/README.de.md @@ -28,6 +28,6 @@ Die erste Frage wählt die Sprache des Installers; English ist voreingestellt. D 5. System-VPN über Xray. 6. 3x-ui und lokaler Cloudflare-WARP-Proxy. 7. Erstsicherung sowie tägliche, wöchentliche oder monatliche Sicherungen. -8. Optionale KI-Agenten: Hermes, OpenClaw, OpenHands, OpenCode oder Aider. +8. Optionale KI-Agenten: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT oder Pi Coding Agent. Schließen Sie die aktuelle SSH-Sitzung während der Portänderung nicht. Testen Sie zuerst die neue Anmeldung in einem zweiten Terminal. Weitere Hinweise enthält die [englische Hauptdokumentation](../../README.md). diff --git a/docs/readme/README.es.md b/docs/readme/README.es.md index ed4f7ea..75ac21b 100644 --- a/docs/readme/README.es.md +++ b/docs/readme/README.es.md @@ -28,6 +28,6 @@ La primera pregunta selecciona el idioma del instalador; English es el valor pre 5. VPN del sistema mediante Xray. 6. Panel 3x-ui y proxy local Cloudflare WARP. 7. Instantánea inicial y copias diarias, semanales o mensuales. -8. Agentes de IA opcionales: Hermes, OpenClaw, OpenHands, OpenCode o Aider. +8. Agentes de IA opcionales: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT o Pi Coding Agent. No cierre la sesión SSH actual durante el cambio de puerto. Pruebe primero el nuevo acceso en otro terminal. Consulte la [documentación principal en inglés](../../README.md) para la instalación por archivo, actualización y restablecimiento. diff --git a/docs/readme/README.fr.md b/docs/readme/README.fr.md index 1fa834d..802052c 100644 --- a/docs/readme/README.fr.md +++ b/docs/readme/README.fr.md @@ -28,6 +28,6 @@ La première question sélectionne la langue de l’installateur ; English est l 5. VPN système via Xray. 6. Panneau 3x-ui et proxy local Cloudflare WARP. 7. Instantané initial et sauvegardes quotidiennes, hebdomadaires ou mensuelles. -8. Agents IA facultatifs : Hermes, OpenClaw, OpenHands, OpenCode ou Aider. +8. Agents IA facultatifs : Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT ou Pi Coding Agent. Ne fermez pas la session SSH actuelle pendant le changement de port. Testez d’abord la nouvelle connexion dans un autre terminal. Consultez la [documentation principale en anglais](../../README.md) pour plus de détails. diff --git a/docs/readme/README.hi.md b/docs/readme/README.hi.md index 04e3930..794e489 100644 --- a/docs/readme/README.hi.md +++ b/docs/readme/README.hi.md @@ -28,6 +28,6 @@ sudo ./ochenstarik-server-install.sh 5. Xray के माध्यम से सिस्टम VPN। 6. 3x-ui पैनल और स्थानीय Cloudflare WARP प्रॉक्सी। 7. प्रारंभिक स्नैपशॉट और दैनिक, साप्ताहिक या मासिक बैकअप। -8. वैकल्पिक AI एजेंट: Hermes, OpenClaw, OpenHands, OpenCode या Aider। +8. वैकल्पिक AI एजेंट: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT या Pi Coding Agent। SSH पोर्ट बदलते समय मौजूदा सत्र बंद न करें। पहले दूसरे टर्मिनल में नया लॉगिन जाँचें। अधिक जानकारी के लिए [मुख्य अंग्रेज़ी दस्तावेज़](../../README.md) देखें। diff --git a/docs/readme/README.ja.md b/docs/readme/README.ja.md index aabd977..dac8270 100644 --- a/docs/readme/README.ja.md +++ b/docs/readme/README.ja.md @@ -28,6 +28,6 @@ sudo ./ochenstarik-server-install.sh 5. Xray によるシステム VPN。 6. 3x-ui パネルとローカル Cloudflare WARP プロキシ。 7. 初回スナップショットと日次、週次、月次バックアップ。 -8. オプションの AI エージェント:Hermes、OpenClaw、OpenHands、OpenCode、Aider。 +8. オプションの AI エージェント:Hermes、OpenClaw、OpenHands、OpenCode、Aider、AutoGPT、Pi Coding Agent。 SSH ポートの変更中は現在のセッションを閉じないでください。別のターミナルで新しい接続を先に確認してください。詳細は[英語のメイン文書](../../README.md)を参照してください。 diff --git a/docs/readme/README.pt.md b/docs/readme/README.pt.md index f1e3b48..18a7da4 100644 --- a/docs/readme/README.pt.md +++ b/docs/readme/README.pt.md @@ -28,6 +28,6 @@ A primeira pergunta seleciona o idioma do instalador; English é o padrão. Depo 5. VPN do sistema por Xray. 6. Painel 3x-ui e proxy local Cloudflare WARP. 7. Snapshot inicial e backups diários, semanais ou mensais. -8. Agentes de IA opcionais: Hermes, OpenClaw, OpenHands, OpenCode ou Aider. +8. Agentes de IA opcionais: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT ou Pi Coding Agent. Não feche a sessão SSH atual durante a alteração da porta. Teste primeiro o novo login em outro terminal. Consulte a [documentação principal em inglês](../../README.md) para obter detalhes adicionais. diff --git a/docs/readme/README.ru.md b/docs/readme/README.ru.md index 5746914..7e34a6c 100644 --- a/docs/readme/README.ru.md +++ b/docs/readme/README.ru.md @@ -62,7 +62,7 @@ sudo ./ochenstarik-server-install.sh | `ochenstarik-server-vpn-5.sh` | Устанавливает Xray и направляет системный трафик через VLESS + REALITY | Да; зависимости устанавливаются автоматически | | `ochenstarik-server-panel-warp-6.sh` | Устанавливает 3x-ui и Cloudflare WARP, настраивает и проверяет их порты | Ubuntu 22.04/24.04/26.04 или Debian 12/13 | | `ochenstarik-server-backup-7.sh` | Создаёт неизменяемый первичный снимок и настраивает выбранные расписания резервного копирования | Да; зависимости устанавливаются автоматически | -| `ochenstarik-server-ai-agents-8.sh` | Устанавливает выбранных AI-агентов для обычного пользователя: Hermes, OpenClaw, OpenHands, OpenCode или Aider | Да; API-ключи настраиваются отдельно | +| `ochenstarik-server-ai-agents-8.sh` | Устанавливает выбранных AI-агентов: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT или Pi Coding Agent | Да; API-ключи настраиваются отдельно | | `ochenstarik-server-uninstall.sh` | Удаляет настройки, службы и данные проекта для повторной установки с начала | Да; запускать только из сохранённой SSH-сессии | Файлы `*.env.example` являются только примерами. Не записывайте настоящие пароли, токены и приватные SSH-ключи в Git. @@ -406,7 +406,9 @@ sudo ./ochenstarik-server-ai-agents-8.sh Файл: `ochenstarik-server-ai-agents-8.sh`. -Скрипт предлагает множественный выбор из Hermes Agent, OpenClaw, OpenHands, OpenCode и Aider. Агенты устанавливаются только для выбранного обычного пользователя, а не для `root`. Каждый официальный установщик сначала загружается по HTTPS во временный файл и проходит проверку `bash -n`. +Скрипт предлагает множественный выбор из Hermes Agent, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT и Pi Coding Agent. Установщики запускаются от выбранного обычного пользователя, а не напрямую от `root`. Каждый официальный установщик сначала загружается по HTTPS во временный файл и проходит проверку `bash -n`. + +AutoGPT — более тяжёлая Docker-платформа: её официальный установщик может запросить `sudo`, установить зависимости Docker и запустить локальные службы. Pi — лёгкий терминальный агент; после установки его можно запустить командой `pi`, а затем выполнить `/login` для подключения провайдера. API-ключи скрипт не запрашивает и не сохраняет. После установки нужно войти под выбранным пользователем и выполнить показанную команду первоначальной настройки. AI-агенты могут запускать команды, изменять файлы, использовать сеть и устанавливать плагины, поэтому перед подключением рабочих данных необходимо проверить разрешения, песочницу и доступ к секретам. diff --git a/docs/readme/README.zh-CN.md b/docs/readme/README.zh-CN.md index 09e2c6e..c99f436 100644 --- a/docs/readme/README.zh-CN.md +++ b/docs/readme/README.zh-CN.md @@ -28,6 +28,6 @@ sudo ./ochenstarik-server-install.sh 5. 通过 Xray 配置系统 VPN。 6. 3x-ui 面板和本地 Cloudflare WARP 代理。 7. 初始快照以及每日、每周或每月备份。 -8. 可选 AI 代理:Hermes、OpenClaw、OpenHands、OpenCode 或 Aider。 +8. 可选 AI 代理:Hermes、OpenClaw、OpenHands、OpenCode、Aider、AutoGPT 或 Pi Coding Agent。 更改 SSH 端口时不要关闭当前会话。请先在另一个终端中测试新连接。更多说明请参阅[英文主文档](../../README.md)。 diff --git a/ochenstarik-server-ai-agents-8.sh b/ochenstarik-server-ai-agents-8.sh index 68cb9b4..782a105 100644 --- a/ochenstarik-server-ai-agents-8.sh +++ b/ochenstarik-server-ai-agents-8.sh @@ -5,13 +5,15 @@ readonly CONFIG_DIR="/etc/ochenstarik-server" readonly AGENT_CONFIG="${CONFIG_DIR}/ai-agents.conf" readonly STEP3_ENV="/root/setup-data/env.txt" -readonly -a AGENT_IDS=(hermes openclaw openhands opencode aider) +readonly -a AGENT_IDS=(hermes openclaw openhands opencode aider autogpt pi) readonly -a AGENT_NAMES=( "Hermes Agent" "OpenClaw" "OpenHands" "OpenCode" "Aider" + "AutoGPT" + "Pi Coding Agent" ) readonly -a AGENT_DESCRIPTIONS=( "Personal self-improving agent with memory, skills, schedules, and messaging gateways" @@ -19,6 +21,8 @@ readonly -a AGENT_DESCRIPTIONS=( "Software-development agent with terminal, headless, web, and IDE modes" "Terminal coding agent with multiple model providers and parallel sessions" "Git-oriented coding agent for editing and reviewing an existing repository" + "Docker-based platform for building, deploying, and running autonomous agents; may request sudo" + "Minimal and extensible terminal coding agent with skills, extensions, and multiple providers" ) readonly -a AGENT_INSTALL_URLS=( "https://hermes-agent.nousresearch.com/install.sh" @@ -26,14 +30,18 @@ readonly -a AGENT_INSTALL_URLS=( "https://install.openhands.dev/install.sh" "https://opencode.ai/install" "https://aider.chat/install.sh" + "https://setup.agpt.co/install.sh" + "https://pi.dev/install.sh" ) -readonly -a AGENT_COMMANDS=(hermes openclaw openhands opencode aider) +readonly -a AGENT_COMMANDS=(hermes openclaw openhands opencode aider docker pi) readonly -a AGENT_SETUP_HINTS=( "hermes setup" "openclaw onboard --install-daemon" "openhands" "opencode" "aider" + "Open the local AutoGPT address printed by the official installer" + "pi, then use /login to connect a provider" ) TEMP_INSTALLER="" @@ -122,8 +130,8 @@ select_agents() { SELECTED_INDEXES=() seen=() for token in $input; do - if [[ ! "$token" =~ ^[1-5]$ ]]; then - warn "Enter agent numbers from 1 to 5, or 0 to skip" + if [[ ! "$token" =~ ^[1-7]$ ]]; then + warn "Enter agent numbers from 1 to 7, or 0 to skip" SELECTED_INDEXES=() break fi @@ -160,7 +168,7 @@ install_agent() { log "Installing ${name} for ${TARGET_USER}" runuser -u "$TARGET_USER" -- env \ HOME="$TARGET_HOME" USER="$TARGET_USER" LOGNAME="$TARGET_USER" SHELL=/bin/bash PATH="$path_value" \ - bash "$TEMP_INSTALLER" + bash -c 'cd "$HOME" && exec bash "$1"' ochenstarik-agent-installer "$TEMP_INSTALLER" rm -f -- "$TEMP_INSTALLER" TEMP_INSTALLER="" @@ -195,9 +203,9 @@ require_command getent require_command runuser require_command stat -((${#AGENT_IDS[@]} == 5 && ${#AGENT_NAMES[@]} == 5 \ - && ${#AGENT_DESCRIPTIONS[@]} == 5 && ${#AGENT_INSTALL_URLS[@]} == 5 \ - && ${#AGENT_COMMANDS[@]} == 5 && ${#AGENT_SETUP_HINTS[@]} == 5)) \ +((${#AGENT_IDS[@]} == 7 && ${#AGENT_NAMES[@]} == 7 \ + && ${#AGENT_DESCRIPTIONS[@]} == 7 && ${#AGENT_INSTALL_URLS[@]} == 7 \ + && ${#AGENT_COMMANDS[@]} == 7 && ${#AGENT_SETUP_HINTS[@]} == 7)) \ || die "Invalid AI agent catalog" select_target_user diff --git a/tests/test-step8-agent-catalog.sh b/tests/test-step8-agent-catalog.sh index 488fa53..aef0b43 100644 --- a/tests/test-step8-agent-catalog.sh +++ b/tests/test-step8-agent-catalog.sh @@ -4,8 +4,8 @@ set -Eeuo pipefail cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." source <(awk '/^\[\[ "\$EUID"/{exit} {print}' ochenstarik-server-ai-agents-8.sh) -[[ "${#AGENT_IDS[@]}" == 5 ]] -[[ "${AGENT_IDS[*]}" == 'hermes openclaw openhands opencode aider' ]] +[[ "${#AGENT_IDS[@]}" == 7 ]] +[[ "${AGENT_IDS[*]}" == 'hermes openclaw openhands opencode aider autogpt pi' ]] expected_urls=( 'https://hermes-agent.nousresearch.com/install.sh' @@ -13,14 +13,16 @@ expected_urls=( 'https://install.openhands.dev/install.sh' 'https://opencode.ai/install' 'https://aider.chat/install.sh' + 'https://setup.agpt.co/install.sh' + 'https://pi.dev/install.sh' ) for index in "${!expected_urls[@]}"; do [[ "${AGENT_INSTALL_URLS[index]}" == "${expected_urls[index]}" ]] done -select_agents <<< '1 3 5' >/dev/null -[[ "${SELECTED_INDEXES[*]}" == '0 2 4' ]] +select_agents <<< '1 3 5 6 7' >/dev/null +[[ "${SELECTED_INDEXES[*]}" == '0 2 4 5 6' ]] select_agents <<< '' >/dev/null [[ "${#SELECTED_INDEXES[@]}" == 0 ]] From bc0b29a1b1ef257231f81aed7a63513ff4aaea3a Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Wed, 15 Jul 2026 22:52:50 +0700 Subject: [PATCH 11/12] Add VPN reconnect and disable menu (#12) --- .github/workflows/bash-syntax.yml | 3 + README.md | 15 +++- docs/readme/README.ar.md | 2 +- docs/readme/README.de.md | 2 +- docs/readme/README.es.md | 2 +- docs/readme/README.fr.md | 2 +- docs/readme/README.hi.md | 2 +- docs/readme/README.ja.md | 2 +- docs/readme/README.pt.md | 2 +- docs/readme/README.ru.md | 9 ++- docs/readme/README.zh-CN.md | 2 +- ochenstarik-server-vpn-5.sh | 110 +++++++++++++++++++++++++++--- tests/test-step5-actions.sh | 22 ++++++ 13 files changed, 157 insertions(+), 18 deletions(-) create mode 100644 tests/test-step5-actions.sh diff --git a/.github/workflows/bash-syntax.yml b/.github/workflows/bash-syntax.yml index c9e047b..2e27cee 100644 --- a/.github/workflows/bash-syntax.yml +++ b/.github/workflows/bash-syntax.yml @@ -35,5 +35,8 @@ jobs: - name: Test step 3 SSH configuration parsing run: bash tests/test-step3-sshd-parsing.sh + - name: Test step 5 repeated-run actions + run: bash tests/test-step5-actions.sh + - name: Test step 8 AI agent catalog run: bash tests/test-step8-agent-catalog.sh diff --git a/README.md b/README.md index 836439f..8a3fc1e 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ The wizard downloads any missing module from the `main` branch and validates it | `ochenstarik-server-2.sh` | Base packages, SSH port, IPv4/IPv6 mode, UFW, and port management | Yes | | `ochenstarik-server-user-3.sh` | Administrative user, SSH key permissions, SSH migration, sudo, and fail2ban | Run after step 2 | | `ochenstarik-server-tg-4.sh` | Telegram notifications for successful SSH logins | Optional | -| `ochenstarik-server-vpn-5.sh` | System VPN through Xray with a selectable subscription port | Optional | +| `ochenstarik-server-vpn-5.sh` | System VPN through Xray; repeated runs can connect, disconnect, reconfigure, or show status | Optional | | `ochenstarik-server-panel-warp-6.sh` | 3x-ui panel and local Cloudflare WARP proxy | Optional | | `ochenstarik-server-backup-7.sh` | Protected initial snapshot and selected daily, weekly, or monthly schedules | Optional | | `ochenstarik-server-ai-agents-8.sh` | Installs selected AI agents: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT, or Pi Coding Agent | Optional | @@ -92,6 +92,19 @@ Replace `SCRIPT_NAME.sh` with the required filename from the table. 8. Optionally create the permanent initial backup and configure rotating schedules. 9. Optionally install one or more AI agents for the administrative user. +## VPN connection control + +After the first successful setup, running `ochenstarik-server-vpn-5.sh` again opens an action menu: connect using saved settings, disconnect while keeping the configuration, replace the subscription, or show status. The same actions are available directly: + +```bash +sudo ./ochenstarik-server-vpn-5.sh --enable +sudo ./ochenstarik-server-vpn-5.sh --disable +sudo ./ochenstarik-server-vpn-5.sh --reconfigure +sudo ./ochenstarik-server-vpn-5.sh --status +``` + +Disconnecting removes the system routing rules but keeps Xray and its configuration so the VPN can be reconnected without entering the subscription again. + ## Optional AI agents Step 8 offers a multiple selection of seven popular agents: Hermes Agent, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT, and Pi Coding Agent. Installers are downloaded only from each project's official HTTPS endpoint, checked with `bash -n`, and launched as the selected regular user rather than directly as `root`. diff --git a/docs/readme/README.ar.md b/docs/readme/README.ar.md index 7b29938..e7ad69b 100644 --- a/docs/readme/README.ar.md +++ b/docs/readme/README.ar.md @@ -25,7 +25,7 @@ sudo ./ochenstarik-server-install.sh 2. الحزم الأساسية ومنفذ SSH وIPv4/IPv6 وUFW. 3. المستخدم الإداري ومفتاح SSH والصلاحيات وsudo وfail2ban. 4. إشعارات Telegram عند تسجيل الدخول عبر SSH. -5. VPN للنظام باستخدام Xray. +5. VPN للنظام باستخدام Xray مع إمكانية الاتصال أو الفصل عند إعادة التشغيل. 6. لوحة 3x-ui ووكيل Cloudflare WARP المحلي. 7. النسخة الأولية والنسخ اليومية أو الأسبوعية أو الشهرية. 8. وكلاء ذكاء اصطناعي اختياريون: Hermes أو OpenClaw أو OpenHands أو OpenCode أو Aider أو AutoGPT أو Pi Coding Agent. diff --git a/docs/readme/README.de.md b/docs/readme/README.de.md index 35b4ba5..d662b7e 100644 --- a/docs/readme/README.de.md +++ b/docs/readme/README.de.md @@ -25,7 +25,7 @@ Die erste Frage wählt die Sprache des Installers; English ist voreingestellt. D 2. Basispakete, SSH-Port, IPv4/IPv6 und UFW. 3. Administrator, SSH-Schlüssel, Rechte, sudo und fail2ban. 4. Telegram-Benachrichtigungen für SSH-Anmeldungen. -5. System-VPN über Xray. +5. System-VPN über Xray mit Verbinden und Trennen bei späteren Ausführungen. 6. 3x-ui und lokaler Cloudflare-WARP-Proxy. 7. Erstsicherung sowie tägliche, wöchentliche oder monatliche Sicherungen. 8. Optionale KI-Agenten: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT oder Pi Coding Agent. diff --git a/docs/readme/README.es.md b/docs/readme/README.es.md index 75ac21b..81bb8ca 100644 --- a/docs/readme/README.es.md +++ b/docs/readme/README.es.md @@ -25,7 +25,7 @@ La primera pregunta selecciona el idioma del instalador; English es el valor pre 2. Paquetes básicos, puerto SSH, IPv4/IPv6 y UFW. 3. Usuario administrador, clave SSH, permisos, sudo y fail2ban. 4. Notificaciones de Telegram para accesos SSH. -5. VPN del sistema mediante Xray. +5. VPN del sistema mediante Xray, con conexión y desconexión en ejecuciones posteriores. 6. Panel 3x-ui y proxy local Cloudflare WARP. 7. Instantánea inicial y copias diarias, semanales o mensuales. 8. Agentes de IA opcionales: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT o Pi Coding Agent. diff --git a/docs/readme/README.fr.md b/docs/readme/README.fr.md index 802052c..39cf2d5 100644 --- a/docs/readme/README.fr.md +++ b/docs/readme/README.fr.md @@ -25,7 +25,7 @@ La première question sélectionne la langue de l’installateur ; English est l 2. Paquets de base, port SSH, IPv4/IPv6 et UFW. 3. Administrateur, clé SSH, permissions, sudo et fail2ban. 4. Notifications Telegram des connexions SSH. -5. VPN système via Xray. +5. VPN système via Xray, avec connexion et déconnexion lors des exécutions suivantes. 6. Panneau 3x-ui et proxy local Cloudflare WARP. 7. Instantané initial et sauvegardes quotidiennes, hebdomadaires ou mensuelles. 8. Agents IA facultatifs : Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT ou Pi Coding Agent. diff --git a/docs/readme/README.hi.md b/docs/readme/README.hi.md index 794e489..6d40873 100644 --- a/docs/readme/README.hi.md +++ b/docs/readme/README.hi.md @@ -25,7 +25,7 @@ sudo ./ochenstarik-server-install.sh 2. मूल पैकेज, SSH पोर्ट, IPv4/IPv6 और UFW। 3. प्रशासक, SSH कुंजी, अनुमतियाँ, sudo और fail2ban। 4. SSH लॉगिन के लिए Telegram सूचनाएँ। -5. Xray के माध्यम से सिस्टम VPN। +5. Xray के माध्यम से सिस्टम VPN, दोबारा चलाने पर कनेक्ट या डिस्कनेक्ट करने की सुविधा। 6. 3x-ui पैनल और स्थानीय Cloudflare WARP प्रॉक्सी। 7. प्रारंभिक स्नैपशॉट और दैनिक, साप्ताहिक या मासिक बैकअप। 8. वैकल्पिक AI एजेंट: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT या Pi Coding Agent। diff --git a/docs/readme/README.ja.md b/docs/readme/README.ja.md index dac8270..9788e28 100644 --- a/docs/readme/README.ja.md +++ b/docs/readme/README.ja.md @@ -25,7 +25,7 @@ sudo ./ochenstarik-server-install.sh 2. 基本パッケージ、SSH ポート、IPv4/IPv6、UFW。 3. 管理者、SSH 鍵、アクセス権、sudo、fail2ban。 4. SSH ログインの Telegram 通知。 -5. Xray によるシステム VPN。 +5. Xray によるシステム VPN。再実行時に接続または切断できます。 6. 3x-ui パネルとローカル Cloudflare WARP プロキシ。 7. 初回スナップショットと日次、週次、月次バックアップ。 8. オプションの AI エージェント:Hermes、OpenClaw、OpenHands、OpenCode、Aider、AutoGPT、Pi Coding Agent。 diff --git a/docs/readme/README.pt.md b/docs/readme/README.pt.md index 18a7da4..07da52f 100644 --- a/docs/readme/README.pt.md +++ b/docs/readme/README.pt.md @@ -25,7 +25,7 @@ A primeira pergunta seleciona o idioma do instalador; English é o padrão. Depo 2. Pacotes básicos, porta SSH, IPv4/IPv6 e UFW. 3. Administrador, chave SSH, permissões, sudo e fail2ban. 4. Notificações Telegram para logins SSH. -5. VPN do sistema por Xray. +5. VPN do sistema por Xray, com conexão e desconexão nas execuções seguintes. 6. Painel 3x-ui e proxy local Cloudflare WARP. 7. Snapshot inicial e backups diários, semanais ou mensais. 8. Agentes de IA opcionais: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT ou Pi Coding Agent. diff --git a/docs/readme/README.ru.md b/docs/readme/README.ru.md index 7e34a6c..a412b41 100644 --- a/docs/readme/README.ru.md +++ b/docs/readme/README.ru.md @@ -279,6 +279,8 @@ sudo tail -f /var/log/ochenstarik-ssh-login-telegram.log Скрипт устанавливает Xray и необходимые пакеты, принимает прямую ссылку `vless://` либо HTTPS-ссылку подписки 3x-ui и настраивает маршрутизацию через `nftables`. Если подписка содержит VLESS-ссылки на нескольких портах, скрипт выводит доступные порты и просит выбрать нужный. Поддерживается VLESS с транспортом TCP/RAW и REALITY. +При повторном запуске появляется меню: подключить VPN с сохранёнными настройками, отключить его без удаления конфигурации, заменить подписку либо показать состояние. + ```bash curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-vpn-5.sh chmod 700 ochenstarik-server-vpn-5.sh @@ -295,12 +297,17 @@ sudo /usr/local/sbin/ochenstarik-xray-routing status curl -4 https://api.ipify.org ``` -Отключение системного VPN: +Прямые команды управления: ```bash +sudo ./ochenstarik-server-vpn-5.sh --enable sudo ./ochenstarik-server-vpn-5.sh --disable +sudo ./ochenstarik-server-vpn-5.sh --reconfigure +sudo ./ochenstarik-server-vpn-5.sh --status ``` +Отключение удаляет правила системной маршрутизации, но сохраняет Xray и настройки для быстрого повторного подключения. + ## 6. Панель 3x-ui и локальный proxy Cloudflare WARP Файл: `ochenstarik-server-panel-warp-6.sh`. diff --git a/docs/readme/README.zh-CN.md b/docs/readme/README.zh-CN.md index c99f436..af38f14 100644 --- a/docs/readme/README.zh-CN.md +++ b/docs/readme/README.zh-CN.md @@ -25,7 +25,7 @@ sudo ./ochenstarik-server-install.sh 2. 基础软件包、SSH 端口、IPv4/IPv6 和 UFW。 3. 管理员、SSH 密钥、权限、sudo 和 fail2ban。 4. SSH 登录的 Telegram 通知。 -5. 通过 Xray 配置系统 VPN。 +5. 通过 Xray 配置系统 VPN,重复运行时可连接或断开。 6. 3x-ui 面板和本地 Cloudflare WARP 代理。 7. 初始快照以及每日、每周或每月备份。 8. 可选 AI 代理:Hermes、OpenClaw、OpenHands、OpenCode、Aider、AutoGPT 或 Pi Coding Agent。 diff --git a/ochenstarik-server-vpn-5.sh b/ochenstarik-server-vpn-5.sh index 085c42c..91bf9e1 100644 --- a/ochenstarik-server-vpn-5.sh +++ b/ochenstarik-server-vpn-5.sh @@ -44,17 +44,107 @@ stop_rollback_timer() { ochenstarik-xray-rollback.service >/dev/null 2>&1 || true } -[[ "$EUID" -eq 0 ]] || die "Запустите этот скрипт от имени root" +vpn_is_configured() { + [[ -f "$XRAY_CONFIG" && ! -L "$XRAY_CONFIG" \ + && -x "$ROUTE_HELPER" && ! -L "$ROUTE_HELPER" \ + && -f "$ROUTE_SERVICE" && ! -L "$ROUTE_SERVICE" ]] +} -if [[ "${1:-}" == "--disable" ]]; then +select_existing_action() { + local choice + printf '\nОбнаружена существующая конфигурация системного VPN.\n' + printf ' 1) Подключить VPN с сохранёнными настройками\n' + printf ' 2) Отключить VPN, сохранив настройки\n' + printf ' 3) Переустановить VPN или заменить подписку\n' + printf ' 4) Показать состояние VPN\n' + while :; do + read -rp 'Выберите действие [1]: ' choice || die "Ввод прерван" + choice="${choice:-1}" + case "$choice" in + 1) ACTION=enable; return 0 ;; + 2) ACTION=disable; return 0 ;; + 3) ACTION=reconfigure; return 0 ;; + 4) ACTION=status; return 0 ;; + *) warn "Введите номер от 1 до 4" ;; + esac + done +} + +disable_vpn() { + stop_rollback_timer systemctl disable --now ochenstarik-xray-routing.service >/dev/null 2>&1 || true - [[ ! -x "$ROUTE_HELPER" ]] || "$ROUTE_HELPER" stop - log "Системная маршрутизация через Xray отключена" + [[ ! -x "$ROUTE_HELPER" || -L "$ROUTE_HELPER" ]] || "$ROUTE_HELPER" stop + log "Системная маршрутизация через Xray отключена; конфигурация сохранена" if command -v curl >/dev/null 2>&1; then printf 'Текущий внешний IP: %s\n' "$(public_ipv4)" fi - exit 0 -fi +} + +enable_vpn() { + local direct_ip vpn_ip + vpn_is_configured || die "Сохранённая конфигурация VPN не найдена; выполните установку" + command -v xray >/dev/null 2>&1 || die "Xray не установлен" + xray run -test -config "$XRAY_CONFIG" || die "Xray отклонил сохранённую конфигурацию" + systemctl stop ochenstarik-xray-routing.service >/dev/null 2>&1 || true + "$ROUTE_HELPER" stop + direct_ip="$(public_ipv4)" + valid_ipv4 "$direct_ip" || die "Не удалось определить прямой внешний IPv4 перед подключением" + systemctl daemon-reload + systemctl enable --now xray.service + systemctl restart xray.service + systemctl enable --now ochenstarik-xray-routing.service + systemctl restart ochenstarik-xray-routing.service + systemctl is-active --quiet xray.service || die "Служба Xray не запущена" + systemctl is-active --quiet ochenstarik-xray-routing.service \ + || die "Системная маршрутизация через VPN не запущена" + vpn_ip="$(public_ipv4)" + if ! valid_ipv4 "$vpn_ip" || [[ "$vpn_ip" == "$direct_ip" ]]; then + systemctl stop ochenstarik-xray-routing.service >/dev/null 2>&1 || true + "$ROUTE_HELPER" stop + die "Проверка внешнего IP не пройдена; VPN автоматически отключён" + fi + log "Системный VPN подключён с сохранёнными настройками" + printf 'Прямой внешний IP: %s\n' "$direct_ip" + printf 'Внешний IP через VPN: %s\n' "$vpn_ip" +} + +show_vpn_status() { + if systemctl is-active --quiet ochenstarik-xray-routing.service; then + printf 'Системный VPN: подключён\n' + else + printf 'Системный VPN: отключён\n' + fi + printf 'Xray: %s\n' "$(systemctl is-active xray.service 2>/dev/null || true)" + printf 'Маршрутизация: %s\n' \ + "$(systemctl is-active ochenstarik-xray-routing.service 2>/dev/null || true)" + if [[ -x "$ROUTE_HELPER" && ! -L "$ROUTE_HELPER" ]] \ + && systemctl is-active --quiet ochenstarik-xray-routing.service; then + "$ROUTE_HELPER" status || warn "Не удалось полностью прочитать правила маршрутизации" + fi + if command -v curl >/dev/null 2>&1; then + printf 'Текущий внешний IP: %s\n' "$(public_ipv4)" + fi +} + +[[ "$EUID" -eq 0 ]] || die "Запустите этот скрипт от имени root" + +ACTION="" +case "${1:-}" in + "") vpn_is_configured && select_existing_action || ACTION=reconfigure ;; + --enable) ACTION=enable ;; + --disable) ACTION=disable ;; + --reconfigure) ACTION=reconfigure ;; + --status) ACTION=status ;; + *) die "Использование: $0 [--enable|--disable|--reconfigure|--status]" ;; +esac + +case "$ACTION" in + enable) enable_vpn; exit 0 ;; + disable) disable_vpn; exit 0 ;; + status) show_vpn_status; exit 0 ;; + reconfigure) ;; + *) die "Некорректное действие VPN: $ACTION" ;; +esac export DEBIAN_FRONTEND=noninteractive log "Установка системных зависимостей" @@ -359,7 +449,8 @@ printf 'IP через локальный прокси Xray: %s\n' "$PROXY_IP" [[ "$PROXY_IP" != "$BEFORE_IP" ]] \ || die "IP через Xray совпадает с исходным; системная маршрутизация не включена" -SSH_PORT="$(sshd -T 2>/dev/null | awk '$1 == "port" { print $2; exit }' || true)" +SSHD_EFFECTIVE_CONFIG="$(sshd -T 2>/dev/null || true)" +SSH_PORT="$(awk '$1 == "port" { print $2; exit }' <<< "$SSHD_EFFECTIVE_CONFIG")" [[ "$SSH_PORT" =~ ^[0-9]+$ ]] || SSH_PORT="20202" install -d -m 700 -o root -g root "$STATE_DIR" @@ -516,5 +607,8 @@ printf 'IP через Xray: %s\n' "$PROXY_IP" printf 'IP после VPN: %s\n' "$AFTER_IP" printf 'SSH-порт %s и ответы сервисов 443/63636 оставлены напрямую.\n' "$SSH_PORT" printf 'Публичный IPv6 заблокирован, чтобы исключить обход VPN.\n' -printf '\nОтключить системный VPN:\n %s --disable\n' "$0" +printf '\nПодключить VPN повторно:\n %s --enable\n' "$0" +printf 'Отключить VPN с сохранением настроек:\n %s --disable\n' "$0" +printf 'Заменить подписку:\n %s --reconfigure\n' "$0" +printf 'Показать состояние VPN:\n %s --status\n' "$0" printf 'Проверить маршрутизацию:\n %s status\n' "$ROUTE_HELPER" diff --git a/tests/test-step5-actions.sh b/tests/test-step5-actions.sh new file mode 100644 index 0000000..fd54321 --- /dev/null +++ b/tests/test-step5-actions.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." +source <(awk '/^\[\[ "\$EUID"/{exit} {print}' ochenstarik-server-vpn-5.sh) + +ACTION="" +select_existing_action <<< '' >/dev/null +[[ "$ACTION" == enable ]] + +for pair in '1 enable' '2 disable' '3 reconfigure' '4 status'; do + read -r input expected <<< "$pair" + ACTION="" + select_existing_action <<< "$input" >/dev/null + [[ "$ACTION" == "$expected" ]] +done + +for option in --enable --disable --reconfigure --status; do + grep -Fq -- "$option" ochenstarik-server-vpn-5.sh +done + +printf 'Step 5 repeated-run action tests passed.\n' From 3994102cd13646938ffa630f44fb0f6f665f1070 Mon Sep 17 00:00:00 2001 From: ochenstarik-ui Date: Wed, 15 Jul 2026 23:12:47 +0700 Subject: [PATCH 12/12] Keep IPv6 stack enabled in IPv4 firewall mode (#13) --- .github/workflows/bash-syntax.yml | 3 +++ README.md | 2 +- docs/readme/README.ru.md | 4 +-- ochenstarik-server-2.sh | 25 +++++++----------- tests/test-step2-ipv4-firewall.sh | 44 +++++++++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 19 deletions(-) mode change 100644 => 100755 ochenstarik-server-2.sh create mode 100755 tests/test-step2-ipv4-firewall.sh diff --git a/.github/workflows/bash-syntax.yml b/.github/workflows/bash-syntax.yml index 2e27cee..f1d0f45 100644 --- a/.github/workflows/bash-syntax.yml +++ b/.github/workflows/bash-syntax.yml @@ -26,6 +26,9 @@ jobs: - name: Test step 1 selections run: bash tests/test-step1-selection.sh + - name: Test step 2 IPv4 firewall mode + run: bash tests/test-step2-ipv4-firewall.sh + - name: Test master language selection run: bash tests/test-master-language.sh diff --git a/README.md b/README.md index 8a3fc1e..6b719c9 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ The script does not request or store model-provider API keys. After installation ## Firewall and SSH notes -- Step 2 supports IPv4-only, IPv6-only, and dual-stack UFW rules. +- Step 2 supports IPv4-only, IPv6-only, and dual-stack UFW rules. IPv4-only mode blocks public inbound IPv6 through UFW without disabling the kernel IPv6 stack. - A repeated step 2 run can change the IP-family mode or add ports without reinstalling everything. - Step 3 reads the SSH port chosen in step 2 and creates an administrator with sudo access. - The home directory, `.ssh`, and `authorized_keys` ownership and permissions are checked on every run. diff --git a/docs/readme/README.ru.md b/docs/readme/README.ru.md index a412b41..640c4c6 100644 --- a/docs/readme/README.ru.md +++ b/docs/readme/README.ru.md @@ -164,11 +164,11 @@ sysctl vm.swappiness Для входящих подключений можно выбрать: -- только IPv4 — IPv6 отключается в UFW и системно через `sysctl`; этот режим рекомендуется, если 3x-ui используется только по IPv4; +- только IPv4 — публичные входящие IPv6-порты закрываются через UFW, но IPv6-стек ядра не отключается; этот режим рекомендуется, если 3x-ui используется только по IPv4; - только IPv6 — управляемые скриптом порты открываются лишь для IPv6; - IPv4 + IPv6 — порты открываются для обоих семейств. -Выбор сохраняется в `/etc/ochenstarik-server/ip-family.conf`, а список управляемых портов — в `/etc/ochenstarik-server/ufw-managed-ports.conf`. При переходе на режим с одним семейством старые правила этих портов пересоздаются. Скрипт не разрешит отключить семейство адресов, через которое работает текущая SSH-сессия. Для режима IPv6 также проверяются глобальный IPv6-адрес и маршрут по умолчанию. +Выбор сохраняется в `/etc/ochenstarik-server/ip-family.conf`, а список управляемых портов — в `/etc/ochenstarik-server/ufw-managed-ports.conf`. При переходе на режим с одним семейством старые правила этих портов пересоздаются. Скрипт не разрешит отключить семейство адресов, через которое работает текущая SSH-сессия. Для режима IPv4 UFW остаётся с поддержкой IPv6, чтобы политика `deny incoming` продолжала закрывать публичный IPv6. Для режима IPv6 также проверяются глобальный IPv6-адрес и маршрут по умолчанию. В режиме «только IPv6» системный IPv4 остаётся доступен для исходящих соединений и работы менеджера пакетов, но входящие управляемые порты по IPv4 закрываются UFW. Полное переносимое отключение IPv4 в Linux без привязки к конкретной сетевой конфигурации не выполняется. diff --git a/ochenstarik-server-2.sh b/ochenstarik-server-2.sh old mode 100644 new mode 100755 index 2969f69..1c5be54 --- a/ochenstarik-server-2.sh +++ b/ochenstarik-server-2.sh @@ -87,7 +87,7 @@ choose_ip_mode() { esac printf '\nВыберите семейства IP для входящих подключений:\n' - printf ' 1) Только IPv4 — IPv6 отключается системно и в UFW\n' + printf ' 1) Только IPv4 — входящие IPv6-порты закрываются через UFW\n' printf ' 2) Только IPv6 — открытые входящие порты доступны лишь по IPv6\n' printf ' 3) IPv4 + IPv6\n' while :; do @@ -206,19 +206,6 @@ set_ufw_ipv6_setting() { fi } -disable_ipv6_systemwide() { - cat > "$IPV6_SYSCTL_FILE" <<'EOF' -# Managed by ochenstarik-server-2.sh -net.ipv6.conf.all.disable_ipv6 = 1 -net.ipv6.conf.default.disable_ipv6 = 1 -net.ipv6.conf.lo.disable_ipv6 = 1 -EOF - chmod 644 "$IPV6_SYSCTL_FILE" - sysctl -p "$IPV6_SYSCTL_FILE" >/dev/null - [[ "$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null || true)" == 1 ]] \ - || die "Не удалось отключить IPv6" -} - enable_ipv6_systemwide() { local sysctl_path local -a sysctl_paths=(/proc/sys/net/ipv6/conf/*/disable_ipv6) @@ -230,6 +217,11 @@ enable_ipv6_systemwide() { done } +restore_ipv6_stack_if_managed() { + [[ -e "$IPV6_SYSCTL_FILE" ]] || return 0 + enable_ipv6_systemwide +} + check_session_safety() { local client_ip client_port server_ip server_port @@ -284,9 +276,10 @@ apply_ip_mode_and_firewall() { check_session_safety if [[ "$IP_MODE" == ipv4 ]]; then + restore_ipv6_stack_if_managed + set_ufw_ipv6_setting yes + ufw reload >/dev/null 2>&1 || true delete_managed_ufw_rules - set_ufw_ipv6_setting no - disable_ipv6_systemwide else enable_ipv6_systemwide [[ "$IP_MODE" != ipv6 ]] || verify_ipv6_connectivity diff --git a/tests/test-step2-ipv4-firewall.sh b/tests/test-step2-ipv4-firewall.sh new file mode 100755 index 0000000..56f8016 --- /dev/null +++ b/tests/test-step2-ipv4-firewall.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +script_path="${1:-ochenstarik-server-2.sh}" + +[[ -f "$script_path" ]] || { + printf '[x] Missing script: %s\n' "$script_path" >&2 + exit 1 +} + +if grep -q 'disable_ipv6 = 1' "$script_path"; then + printf '[x] Step 2 must not disable the IPv6 kernel stack for IPv4 mode\n' >&2 + exit 1 +fi + +if grep -q 'disable_ipv6_systemwide' "$script_path"; then + printf '[x] Step 2 still references the old system-wide IPv6 disable helper\n' >&2 + exit 1 +fi + +if ! grep -q 'restore_ipv6_stack_if_managed' "$script_path"; then + printf '[x] Step 2 should restore IPv6 when a legacy managed sysctl file exists\n' >&2 + exit 1 +fi + +ipv4_block="$( + awk ' + /if \[\[ "\$IP_MODE" == ipv4 \]\]; then/ { capture=1 } + capture { print } + capture && /^ else$/ { exit } + ' "$script_path" +)" + +if [[ "$ipv4_block" != *'set_ufw_ipv6_setting yes'* ]]; then + printf '[x] IPv4 mode must keep UFW IPv6 support enabled so deny incoming applies to IPv6\n' >&2 + exit 1 +fi + +if [[ "$ipv4_block" != *'delete_managed_ufw_rules'* ]]; then + printf '[x] IPv4 mode must delete old managed IPv6 allow rules before applying IPv4-only rules\n' >&2 + exit 1 +fi + +printf '[+] Step 2 IPv4 firewall behavior looks correct\n'