diff --git a/.github/workflows/bash-syntax.yml b/.github/workflows/bash-syntax.yml new file mode 100644 index 0000000..f1d0f45 --- /dev/null +++ b/.github/workflows/bash-syntax.yml @@ -0,0 +1,45 @@ +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 + while IFS= read -r -d '' script; do + echo "Checking ${script}" + bash -n "${script}" + done < <(find . -type f -name '*.sh' -print0) + + - 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 + + - 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 + + - 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 fc28f8b..a001180 100644 --- a/README.md +++ b/README.md @@ -1,377 +1,202 @@ -# Быстрая настройка 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. -| Файл | Назначение | Можно запускать отдельно | -| --- | --- | --- | -| `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-monitor-manager.sh` | Устанавливает главный WireGuard Hub или подключаемый узел Server Monitor Manager | Да; Hub нужен белый IP, вторичному узлу — только исходящий доступ | +> **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. -Файлы `*.env.example` являются только примерами. Не записывайте настоящие пароли, токены и приватные SSH-ключи в Git. +## Quick installation -## Требования - -- Ubuntu Server или совместимая Debian-система с `systemd`; -- доступ `root` или пользователь с `sudo`; -- подключение к интернету; -- рекомендуется сохранить текущую SSH-сессию открытой до проверки нового входа. - -## Получение проекта - -Рекомендуемый способ — загрузка архива через `codeload.github.com`. Он подходит в том числе для серверов, на которых соединение с основным доменом `github.com` завершается ошибкой `SSL connection timeout`: +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 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 lightweight-server.tar.gz -mkdir lightweight-server -tar -xzf lightweight-server.tar.gz -C lightweight-server --strip-components=1 -rm lightweight-server.tar.gz + -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 ``` -Если `github.com` доступен с сервера, проект также можно получить через Git: +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. + +## Short wizard-only installation + +If `raw.githubusercontent.com` is reachable from the server: ```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 +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 ``` -Команда `set -e` останавливает последовательность при ошибке загрузки, поэтому команды `cd` и `chmod` не будут выполняться для отсутствующей папки. +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` | 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; 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 | +| `ochenstarik-server-monitor-manager.sh` | Installs the Server Monitor Manager SSH endpoint, public WireGuard Hub, or outbound-only Node | Optional; Hub needs a public UDP endpoint | +| `ochenstarik-server-uninstall.sh` | Removes project-managed settings so installation can start again | Use carefully | + +Every module can be downloaded and run independently: ```bash -bash -n ./ИМЯ-СКРИПТА.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 ``` -## Server Monitor Manager: своя сеть без Tailscale +Replace `SCRIPT_NAME.sh` with the required filename from the table. -Файл `ochenstarik-server-monitor-manager.sh` устанавливает лёгкую сеть WireGuard со звездной топологией. Главный сервер с белым IP становится Hub, а домашний и другие серверы подключаются к нему исходящими соединениями. Белый IP на вторичных узлах не нужен. +## Server Monitor Manager Hub and Nodes -На главном сервере откройте входящий UDP-порт WireGuard (по умолчанию `51820`) в панели хостинга и запустите: +The Hub needs a public IPv4 address or domain and an open UDP port (default `51820`). Secondary Nodes establish outbound WireGuard connections and do not need a public IP. ```bash -curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/agent/server-monitor-installer/ochenstarik-server-monitor-manager.sh +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-monitor-manager.sh chmod 700 ochenstarik-server-monitor-manager.sh bash -n ochenstarik-server-monitor-manager.sh sudo ./ochenstarik-server-monitor-manager.sh hub ``` -После установки создайте отдельный секретный код конфигурации для каждого узла: +Create a separate configuration code for each Node on the Hub, then run the same installer with `node` on the matching server: ```bash sudo ochenstarik-smm node-code ai-agent sudo ochenstarik-smm node-code home -sudo ochenstarik-smm node-code server2 -``` - -На соответствующем вторичном сервере скачайте тот же файл и выполните: - -```bash sudo ./ochenstarik-server-monitor-manager.sh node ``` -Установщик попросит публичный SSH-ключ приложения и код, созданный для этого узла. Код содержит приватный ключ WireGuard узла: передавайте его только в защищённый терминал, используйте для одного сервера и создайте новый узел, если код стал известен посторонним. - -Связи направленные и управляются на Hub. Например, разрешить AI-агенту доступ к домашнему серверу и ко второму серверу, а затем отключить только второй маршрут: +Links are directional. The reverse direction requires its own rule: ```bash sudo ochenstarik-smm link-connect ai-agent home -sudo ochenstarik-smm link-connect ai-agent server2 -sudo ochenstarik-smm link-disconnect ai-agent server2 -``` - -Проверка состояния: - -```bash +sudo ochenstarik-smm link-disconnect ai-agent home sudo ochenstarik-smm nodes sudo ochenstarik-smm links -sudo ochenstarik-smm status ``` -Hub маршрутизирует только явно разрешённые пары `источник → цель`; обратное направление включается отдельно. Вторичные серверы не соединяются друг с другом напрямую и не требуют Tailscale или другого внешнего сервиса. +The current configuration code contains the Node private WireGuard key and is intended only for early testing. Do not store it in Git or messages. Local Node key generation and expiring one-time enrollment are tracked before production use. -## 1. Часовой пояс и настройка swap +## Installation flow -Файл: `ochenstarik-server-1.sh`. +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. +9. Optionally install one or more AI agents for the administrative user. -Сначала показывает нумерованное меню популярных часовых поясов, включая `Asia/Novosibirsk`, `Europe/Moscow` и `UTC`. Нужную зону достаточно выбрать по номеру. Для остальных зон предусмотрен полный список с последовательным выбором региона и часового пояса — вводить имя вручную не требуется. Затем скрипт создаёт `/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, инструменты обработки документов, изображений, аудио и видео. - -Для входящих подключений можно выбрать: - -- только 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: +## 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 ``` -## 6. Панель 3x-ui и локальный proxy Cloudflare WARP +Disconnecting removes the system routing rules but keeps Xray and its configuration so the VPN can be reconnected without entering the subscription again. -Файл: `ochenstarik-server-panel-warp-6.sh`. +## 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`. -- порт веб-панели 3x-ui, по умолчанию `2053`; -- порт подписок 3x-ui, по умолчанию `2096`; -- локальный SOCKS5/HTTP proxy-порт WARP, по умолчанию `40000`. +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. -3x-ui устанавливается официальным интерактивным установщиком. После установки выбранные порты записываются в стандартную SQLite-базу с предварительной резервной копией. Скрипт включает сервис подписок, запускает `x-ui` и проверяет оба TCP-слушателя. +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. -Cloudflare WARP устанавливается из официального APT-репозитория, переводится в Local proxy mode через MASQUE и проверяется запросом к Cloudflare: ответ должен содержать `warp=on`. +## Firewall and SSH notes + +- 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. +- 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 -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 +cd ~/lightweight-server +git pull +sudo ./ochenstarik-server-install.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 последних архивов; -- только первичный снимок без автоматического расписания. +## Reset project-managed configuration ```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 +cd ~/lightweight-server +sudo ./ochenstarik-server-uninstall.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 -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-уведомления и автоматические расписания резервного копирования являются необязательными этапами. - -## Безопасность - -- Всегда проверяйте скачанные скрипты перед запуском от `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..e7ad69b --- /dev/null +++ b/docs/readme/README.ar.md @@ -0,0 +1,33 @@ +# الإعداد السريع لخادم 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. النسخة الأولية والنسخ اليومية أو الأسبوعية أو الشهرية. +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 new file mode 100644 index 0000000..d662b7e --- /dev/null +++ b/docs/readme/README.de.md @@ -0,0 +1,33 @@ +# 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 acht 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 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. + +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..81bb8ca --- /dev/null +++ b/docs/readme/README.es.md @@ -0,0 +1,33 @@ +# 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 ocho 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, 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. + +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..39cf2d5 --- /dev/null +++ b/docs/readme/README.fr.md @@ -0,0 +1,33 @@ +# 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 huit é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, 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. + +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..6d40873 --- /dev/null +++ b/docs/readme/README.hi.md @@ -0,0 +1,33 @@ +# 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. प्रारंभिक स्नैपशॉट और दैनिक, साप्ताहिक या मासिक बैकअप। +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 new file mode 100644 index 0000000..9788e28 --- /dev/null +++ b/docs/readme/README.ja.md @@ -0,0 +1,33 @@ +# 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 です。その後、8 つの各ステップを実行、スキップ、または終了できます。 + +## ステップ + +1. タイムゾーン、端末言語、推奨プログラム、スワップ。 +2. 基本パッケージ、SSH ポート、IPv4/IPv6、UFW。 +3. 管理者、SSH 鍵、アクセス権、sudo、fail2ban。 +4. SSH ログインの Telegram 通知。 +5. Xray によるシステム VPN。再実行時に接続または切断できます。 +6. 3x-ui パネルとローカル Cloudflare WARP プロキシ。 +7. 初回スナップショットと日次、週次、月次バックアップ。 +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 new file mode 100644 index 0000000..07da52f --- /dev/null +++ b/docs/readme/README.pt.md @@ -0,0 +1,33 @@ +# 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 oito 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, 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. + +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..c0052d7 --- /dev/null +++ b/docs/readme/README.ru.md @@ -0,0 +1,484 @@ +# Быстрая настройка 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–8 по порядку. На каждом этапе можно выбрать установку, пропуск или завершение. В первом этапе пустой ввод устанавливает все рекомендуемые наборы программ. Во время изменения 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-ai-agents-8.sh` | Устанавливает выбранных AI-агентов: Hermes, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT или Pi Coding Agent | Да; API-ключи настраиваются отдельно | +| `ochenstarik-server-monitor-manager.sh` | Устанавливает SSH endpoint, главный WireGuard Hub или подключаемый Node для Server Monitor Manager | Да; Hub требуется публичный UDP-адрес | +| `ochenstarik-server-uninstall.sh` | Удаляет настройки, службы и данные проекта для повторной установки с начала | Да; запускать только из сохранённой SSH-сессии | + +Файлы `*.env.example` являются только примерами. Не записывайте настоящие пароли, токены и приватные SSH-ключи в Git. + +## Server Monitor Manager: Hub и вторичные серверы + +Главному Hub нужен белый IPv4 или домен и открытый UDP-порт (по умолчанию `51820`). Вторичные Node подключаются исходящим WireGuard-соединением и не требуют белого IP. + +```bash +curl -fLO https://raw.githubusercontent.com/ochenstarik-ui/lightweight-server/main/ochenstarik-server-monitor-manager.sh +chmod 700 ochenstarik-server-monitor-manager.sh +bash -n ochenstarik-server-monitor-manager.sh +sudo ./ochenstarik-server-monitor-manager.sh hub +``` + +На Hub создайте отдельный код для каждого Node, после чего на соответствующем сервере запустите тот же файл в режиме `node`: + +```bash +sudo ochenstarik-smm node-code ai-agent +sudo ochenstarik-smm node-code home +sudo ./ochenstarik-server-monitor-manager.sh node +``` + +Links являются направленными: + +```bash +sudo ochenstarik-smm link-connect ai-agent home +sudo ochenstarik-smm link-disconnect ai-agent home +sudo ochenstarik-smm nodes +sudo ochenstarik-smm links +``` + +Текущий код конфигурации содержит приватный WireGuard-ключ Node и предназначен только для раннего тестирования. Не сохраняйте его в Git или сообщениях. Локальная генерация ключа и одноразовая регистрация с ограниченным сроком будут добавлены до production-использования. + +## Требования + +- 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, но IPv6-стек ядра не отключается; этот режим рекомендуется, если 3x-ui используется только по IPv4; +- только IPv6 — управляемые скриптом порты открываются лишь для IPv6; +- IPv4 + 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 без привязки к конкретной сетевой конфигурации не выполняется. + +Открываются 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. + +При повторном запуске появляется меню: подключить 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 +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 +``` + +Прямые команды управления: + +```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`. + +Скрипт предлагает выбрать три разных порта: + +- порт веб-панели 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 +sudo ./ochenstarik-server-ai-agents-8.sh +``` + +## 8. AI-агенты на выбор + +Файл: `ochenstarik-server-ai-agents-8.sh`. + +Скрипт предлагает множественный выбор из Hermes Agent, OpenClaw, OpenHands, OpenCode, Aider, AutoGPT и Pi Coding Agent. Установщики запускаются от выбранного обычного пользователя, а не напрямую от `root`. Каждый официальный установщик сначала загружается по HTTPS во временный файл и проходит проверку `bash -n`. + +AutoGPT — более тяжёлая Docker-платформа: её официальный установщик может запросить `sudo`, установить зависимости Docker и запустить локальные службы. Pi — лёгкий терминальный агент; после установки его можно запустить командой `pi`, а затем выполнить `/login` для подключения провайдера. + +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-агенты являются необязательными этапами. + +## Удаление и повторная установка с начала + +Файл: `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..af38f14 --- /dev/null +++ b/docs/readme/README.zh-CN.md @@ -0,0 +1,33 @@ +# 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. 初始快照以及每日、每周或每月备份。 +8. 可选 AI 代理:Hermes、OpenClaw、OpenHands、OpenCode、Aider、AutoGPT 或 Pi Coding Agent。 + +更改 SSH 端口时不要关闭当前会话。请先在另一个终端中测试新连接。更多说明请参阅[英文主文档](../../README.md)。 diff --git a/ochenstarik-server-1.sh b/ochenstarik-server-1.sh index 606b72b..dbb94aa 100644 --- a/ochenstarik-server-1.sh +++ b/ochenstarik-server-1.sh @@ -5,28 +5,587 @@ 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-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" + +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" +) + +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" +) + +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 +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=() + +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 +} + +# 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; } die() { printf '[x] %s\n' "$*" >&2; exit 1; } +package_is_installed() { + dpkg-query -W -f='${Status}' "$1" 2>/dev/null | grep -Fqx 'install ok installed' +} + +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 "$(msg required_missing "$command_name")" + done +} + +ensure_apt_updated() { + [[ "$APT_UPDATED" == no ]] || return 0 + ensure_package_tools + log "$(msg apt_update)" + 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" + chmod 600 "$LOCALE_BACKUP" + else + : > "$LOCALE_ABSENT_MARKER" + chmod 600 "$LOCALE_ABSENT_MARKER" + fi + fi +} + +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" + "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" + "$(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 + 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' "$(msg terminal_heading "$current_locale")" + for index in "${!titles[@]}"; do + printf ' %d) %s\n' "$((index + 1))" "${titles[index]}" + done + read -rp "$(msg terminal_prompt)" answer || die "$(msg input_interrupted)" + answer="${answer:-1}" + [[ "$answer" =~ ^[0-9]+$ ]] && ((answer >= 1 && answer <= ${#titles[@]})) && break + warn "$(msg invalid_number 1 "${#titles[@]}")" + done + + ((answer == 1)) && { log "$(msg terminal_unchanged)"; 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 "$(msg terminal_full_list)" 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 + available_packages+=("$package_name") + package_is_installed "$package_name" || newly_installed+=("$package_name") + else + warn "$(msg locale_package_missing "$package_name")" + fi + done + + export DEBIAN_FRONTEND=noninteractive + ((${#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[@]}" + + log "$(msg locale_selected "$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%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) %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 + for group_index in "${!PROGRAM_GROUP_TITLES[@]}"; do + selected_groups+=("$group_index") + done + elif [[ "$answer" =~ ^[[:space:]]*0[[:space:]]*$ ]]; then + log "$(msg program_skipped)" + return 0 + else + for token in $answer; do + [[ "$token" =~ ^[0-9]+$ ]] \ + || die "$(msg program_bad_number "$token")" + ((token >= 1 && token <= ${#PROGRAM_GROUP_TITLES[@]})) \ + || die "$(msg program_out_of_range "$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 "$(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 + 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 "$(msg package_missing "$package_name")" + fi + done + + ((${#available[@]} > 0)) || die "$(msg no_packages)" + export DEBIAN_FRONTEND=noninteractive + apt-get install -y "${available[@]}" + record_new_packages "$PROGRAM_PACKAGES" "${newly_installed[@]}" + log "$(msg programs_installed)" +} + choose_numbered_option() { local prompt="$1" result_variable="$2" answer index 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 } @@ -35,15 +594,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)" @@ -70,24 +629,26 @@ 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" 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" @@ -116,7 +677,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/ochenstarik-server-2.sh b/ochenstarik-server-2.sh old mode 100644 new mode 100755 index 65c138f..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 @@ -326,14 +319,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-ai-agents-8.sh b/ochenstarik-server-ai-agents-8.sh new file mode 100644 index 0000000..782a105 --- /dev/null +++ b/ochenstarik-server-ai-agents-8.sh @@ -0,0 +1,239 @@ +#!/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 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" + "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" + "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" + "https://openclaw.ai/install.sh" + "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 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="" + +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-7]$ ]]; then + warn "Enter agent numbers from 1 to 7, 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 -c 'cd "$HOME" && exec bash "$1"' ochenstarik-agent-installer "$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[@]} == 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 +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 new file mode 100644 index 0000000..8727c9b --- /dev/null +++ b/ochenstarik-server-install.sh @@ -0,0 +1,323 @@ +#!/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" + "ochenstarik-server-ai-agents-8.sh" +) + +declare -a STEP_TITLES=( + "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" + "Optional AI agents for a regular user" +) + +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" "Первичный снимок и расписания бэкапов" "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 +} + +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; } +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\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 "$(master_msg invalid)" ;; + esac + done +} + +choose_after_failure() { + local result_variable="$1" answer + while :; do + 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 "$(master_msg invalid)" ;; + esac + done +} + +print_summary() { + local completed="$1" skipped="$2" + printf '\n============================================================\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 "Некорректное описание этапов" + +choose_dialog_language + +printf '\n%s\n\n%s\n' "$(master_msg intro_title)" "$(master_msg intro_body)" + +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 "$(master_msg launch "$step_number" "${STEP_TITLES[index]}")" + if bash -- "$script_path"; then + completed="$((completed + 1))" + log "$(master_msg success "$step_number")" + break + fi + + warn "$(master_msg error "$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" diff --git a/ochenstarik-server-uninstall.sh b/ochenstarik-server-uninstall.sh new file mode 100644 index 0000000..07e4b72 --- /dev/null +++ b/ochenstarik-server-uninstall.sh @@ -0,0 +1,373 @@ +#!/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-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; } +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 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 + + 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} " ]] \ + && 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" \ + "$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() { + [[ -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 и другие), потому что они нужны для доступа к серверу. Пакеты, +которые первый этап зафиксировал как новые, можно удалить отдельным подтверждением. + +Сначала будет открыт 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_step1_programs +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 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/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-master-language.sh b/tests/test-master-language.sh new file mode 100644 index 0000000..9f4e551 --- /dev/null +++ b/tests/test-master-language.sh @@ -0,0 +1,26 @@ +#!/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' ]] +[[ "${#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-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' 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' 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' 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' 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' diff --git a/tests/test-step8-agent-catalog.sh b/tests/test-step8-agent-catalog.sh new file mode 100644 index 0000000..aef0b43 --- /dev/null +++ b/tests/test-step8-agent-catalog.sh @@ -0,0 +1,34 @@ +#!/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[@]}" == 7 ]] +[[ "${AGENT_IDS[*]}" == 'hermes openclaw openhands opencode aider autogpt pi' ]] + +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' + '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 6 7' >/dev/null +[[ "${SELECTED_INDEXES[*]}" == '0 2 4 5 6' ]] +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'