From 5606fb056331682613bfa539463e57ba9008ee14 Mon Sep 17 00:00:00 2001 From: NW Date: Fri, 7 Aug 2026 14:58:14 +0100 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D1=81=D0=BA=D1=80=D0=B8=D0=BF=D1=82=D1=8B=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B2=D0=B8=D1=87=D0=BD=D0=BE=D0=B9=20=D0=B8=D0=BD?= =?UTF-8?q?=D0=B8=D1=86=D0=B8=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8,=20=D0=BE=D1=87=D0=B8=D1=81=D1=82=D0=BA=D0=B8=20=D0=B8?= =?UTF-8?q?=20=D0=B0=D0=B2=D1=82=D0=BE=D0=B7=D0=B0=D0=BF=D1=83=D1=81=D0=BA?= =?UTF-8?q?=D0=B0=20(BBox)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 44 +++++++++ cleanup_system.sh | 194 ++++++++++++++++++++++++++++++++++++++ init_system.sh | 226 +++++++++++++++++++++++++++++++++++++++++++++ install_scripts.sh | 89 ++++++++++++++++++ setup_vps.sh | 0 5 files changed, 553 insertions(+) create mode 100755 cleanup_system.sh create mode 100755 init_system.sh create mode 100755 install_scripts.sh mode change 100644 => 100755 setup_vps.sh diff --git a/README.md b/README.md index 4c8fc15..932485e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Автоматический скрипт для настройки VPS с базовыми инструментами, Docker-окружением и расширенными возможностями. +> Часть экосистемы **BBox (bbox.wtf)** — «Свободные люди для свободного мира, во имя приватности и безопасности». + ## Что устанавливается - 🛠️ **Системные утилиты**: @@ -15,6 +17,48 @@ - 🔗 **Клонирование репозитория** [opendoor-base-install](https://git.softuniq.eu/OpenDoor/opendoor-base-install) - 📂 **Копирование файлов из папки `tmux`** в `/root/` +## Скрипты репозитория + +| Скрипт | Назначение | +|--------|-----------| +| `setup_vps.sh` | Основная настройка VPS: утилиты, Docker, dry, btop, tmux | +| `init_system.sh` | Первичная инициализация устройства (однократно после развёртывания): обновление, базовые утилиты, Docker, hostname, баннер BBox, сброс идентификаторов | +| `cleanup_system.sh` | Очистка и оптимизация: apt-кэш, логи, tmp, orphaned-пакеты, Docker-мусор; `--full` дополнительно удаляет Matrix/nginx и сбрасывает идентификаторы | +| `install_scripts.sh` | Установка скриптов в `/usr/local/bin` + сервис автозапуска `bbox-firstboot.service` | + +### Первичная инициализация (`init_system.sh`) + +Выполняется один раз на новом устройстве: + +```bash +sudo ./init_system.sh +``` + +Что делает: +1. Обновляет систему и устанавливает базовые утилиты (tmux, mc, git, net-tools, curl, wget, build-essential, btop) +2. Устанавливает Docker + Docker Compose +3. Настраивает hostname (по умолчанию `bbox-node`) +4. Обновляет баннер входа: **BBox v1.2 (bbox.wtf)** с подписью «Created by free people for a free world» +5. Сбрасывает machine-id и SSH host keys (для уникальности клонированных образов) +6. Вызывает `install_scripts.sh` для установки автозапуска + +### Очистка системы (`cleanup_system.sh`) + +```bash +sudo ./cleanup_system.sh # стандартная очистка +sudo ./cleanup_system.sh --dry-run # показать, что будет очищено +sudo ./cleanup_system.sh --full # + удаление Matrix/nginx, сброс идентификаторов +``` + +### Автозапуск (firstboot) + +`install_scripts.sh` создаёт systemd-сервис `bbox-firstboot.service`, который при **первой** загрузке устройства автоматически выполняет инициализацию. Повторный запуск блокируется маркером `/var/lib/bbox-firstboot-done`. + +```bash +sudo ./install_scripts.sh # установить скрипты + автозапуск +systemctl status bbox-firstboot.service # статус автозапуска +``` + ## Особенности - 🔧 **Гибкая установка tmux**: diff --git a/cleanup_system.sh b/cleanup_system.sh new file mode 100755 index 0000000..bd26165 --- /dev/null +++ b/cleanup_system.sh @@ -0,0 +1,194 @@ +#!/bin/bash +# ============================================================================ +# cleanup_system.sh — очистка и оптимизация системы (BBox / OpenDoor base) +# ---------------------------------------------------------------------------- +# Безопасная очистка от мусора и подготовка устройства к клонированию образа: +# * apt-кэш и списки пакетов +# * логи (journald, /var/log, /var/log.hdd) +# * tmp-директории и кэши пользователей +# * orphaned-пакеты (autoremove) +# * тестовые Docker-образы/контейнеры +# * удаление следов Matrix/coturn/nginx (если были установлены) +# * освобождение места в /boot +# ============================================================================ + +set -uo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +DRY_RUN=0 +LOG_FILE="/var/log/bbox-cleanup.log" + +log() { echo -e "${GREEN}[cleanup]${NC} $*" | tee -a "$LOG_FILE"; } +warn() { echo -e "${YELLOW}[cleanup][warn]${NC} $*" | tee -a "$LOG_FILE"; } +fail() { echo -e "${RED}[cleanup][error]${NC} $*" | tee -a "$LOG_FILE"; exit 1; } + +usage() { + cat </dev/null | awk '{print $1}' || echo "0" +} + +report() { + echo + echo "=== Текущее состояние ===" + echo " Диск: $(df -h / | tail -1 | awk '{print $3" занято из "$2", свободно "$4}')" + echo " apt-кэш: $(size_of /var/cache/apt)" + echo " apt-lists: $(size_of /var/lib/apt/lists)" + echo " /var/log: $(size_of /var/log)" + echo " journald: $(journalctl --disk-usage 2>/dev/null | tail -1)" + echo " /tmp: $(size_of /tmp)" +} + +# --------------------------------------------------------------------------- +# 1. Apt-кэш и списки +# --------------------------------------------------------------------------- +clean_apt() { + log "Очистка apt-кэша..." + [ $DRY_RUN -eq 1 ] && { warn "dry-run: apt-get clean/autoclean"; return; } + apt-get clean + apt-get autoclean -y + + log "Удаление кэшей индексов apt (пересоздадутся автоматически)..." + rm -f /var/cache/apt/pkgcache.bin /var/cache/apt/srcpkgcache.bin + rm -rf /var/lib/apt/lists/* +} + +# --------------------------------------------------------------------------- +# 2. Логи +# --------------------------------------------------------------------------- +clean_logs() { + log "Очистка journald (ограничение до 20M)..." + [ $DRY_RUN -eq 1 ] && { warn "dry-run: journalctl --vacuum-size"; return; } + journalctl --vacuum-size=20M 2>/dev/null || warn "journalctl недоступен" + + log "Удаление старых ротированных логов..." + [ $DRY_RUN -eq 1 ] && { warn "dry-run: удаление *.gz/*.old/*.1 в /var/log"; return; } + find /var/log -type f \( -name "*.gz" -o -name "*.1" -o -name "*.old" \) -delete 2>/dev/null + find /var/log.hdd -type f \( -name "*.gz" -o -name "*.1" -o -name "*.old" \) -delete 2>/dev/null + + # Логи sysstat (если пакет удалён — остаются файлы) + rm -rf /var/log/sysstat /var/log.hdd/sysstat +} + +# --------------------------------------------------------------------------- +# 3. TMP и кэши +# --------------------------------------------------------------------------- +clean_tmp() { + log "Очистка временных директорий и кэшей..." + [ $DRY_RUN -eq 1 ] && { warn "dry-run: очистка /tmp, /var/tmp, /root/.cache"; return; } + rm -rf /tmp/* /var/tmp/* 2>/dev/null + find /root/.cache /root/.npm /root/.cargo /root/.local/share/Trash -mindepth 1 -delete 2>/dev/null + find /home -maxdepth 3 -type d -name ".cache" -exec rm -rf {} + 2>/dev/null +} + +# --------------------------------------------------------------------------- +# 4. Orphaned-пакеты +# --------------------------------------------------------------------------- +clean_autoremove() { + log "Удаление неиспользуемых (orphaned) пакетов..." + [ $DRY_RUN -eq 1 ] && { warn "dry-run: apt-get autoremove"; return; } + apt-get autoremove -y --purge 2>/dev/null || warn "autoremove завершился с ошибкой" +} + +# --------------------------------------------------------------------------- +# 5. Docker-мусор (образы/контейнеры/тома, не используемые) +# --------------------------------------------------------------------------- +clean_docker() { + if ! command -v docker &> /dev/null; then + log "Docker не установлен, пропуск." + return + fi + log "Очистка Docker (dangling-образы, остановленные контейнеры, неиспользуемые сети)..." + [ $DRY_RUN -eq 1 ] && { warn "dry-run: docker system prune -f"; return; } + docker system prune -f 2>/dev/null || warn "docker prune завершился с ошибкой" +} + +# --------------------------------------------------------------------------- +# 6. Удаление следов Matrix/coturn/nginx (для чистого образа) +# --------------------------------------------------------------------------- +clean_matrix() { + local pkgs=() + for p in coturn nginx nginx-core nginx-common certbot; do + if dpkg -l "$p" &>/dev/null; then pkgs+=("$p"); fi + done + if [ ${#pkgs[@]} -eq 0 ]; then + log "Следов Matrix/coturn/nginx нет, пропуск." + return + fi + log "Удаление пакетов Matrix/nginx: ${pkgs[*]}..." + [ $DRY_RUN -eq 1 ] && { warn "dry-run: удаление ${pkgs[*]} и их конфигов"; return; } + DEBIAN_FRONTEND=noninteractive apt-get purge -y "${pkgs[@]}" + rm -rf /etc/nginx /etc/letsencrypt /etc/turnserver.conf /etc/coturn /var/www 2>/dev/null + rm -f /etc/cron.d/certbot 2>/dev/null + log "Следы Matrix/nginx удалены." +} + +# --------------------------------------------------------------------------- +# 7. Сброс уникальных идентификаторов (перед клонированием) +# --------------------------------------------------------------------------- +reset_ids() { + log "Сброс machine-id и SSH host keys..." + [ $DRY_RUN -eq 1 ] && { warn "dry-run: сброс machine-id/ssh keys"; return; } + rm -f /etc/machine-id /var/lib/dbus/machine-id + systemd-machine-id-setup 2>/dev/null || warn "machine-id не пересоздан" + rm -f /etc/ssh/ssh_host_* + # Примечание: после этого нужно перезапустить ssh или перезагрузиться, + # чтобы sshd сгенерировал новые ключи. +} + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- +main() { + while [ $# -gt 0 ]; do + case "$1" in + --dry-run) DRY_RUN=1 ;; + --full) FULL=1 ;; + -h|--help) usage; exit 0 ;; + *) fail "Неизвестный аргумент: $1" ;; + esac + shift + done + + if [ "$(id -u)" -ne 0 ]; then + fail "Запустите скрипт от root: sudo ./cleanup_system.sh" + fi + + touch "$LOG_FILE" + log "=== Очистка системы начата $(date) ===" + + report + clean_apt + clean_logs + clean_tmp + clean_autoremove + clean_docker + if [ "${FULL:-0}" -eq 1 ]; then + clean_matrix + reset_ids + fi + + echo + report + log "Очистка завершена. Журнал: $LOG_FILE" + if [ $DRY_RUN -eq 1 ]; then + warn "Это был dry-run — ничего не удалено. Запустите без --dry-run для применения." + fi +} + +main "$@" diff --git a/init_system.sh b/init_system.sh new file mode 100755 index 0000000..1f7da66 --- /dev/null +++ b/init_system.sh @@ -0,0 +1,226 @@ +#!/bin/bash +# ============================================================================ +# init_system.sh — первичная инициализация системы (BBox / OpenDoor base) +# ---------------------------------------------------------------------------- +# Выполняется ОДИН раз после развёртывания образа на новом устройстве: +# * обновление пакетов +# * установка базовых утилит (tmux, mc, git, net-tools, curl, wget, btop) +# * установка Docker + Docker Compose +# * настройка hostname и баннера BBox (bbox.wtf) +# * сброс уникальных идентификаторов (machine-id, SSH host keys) +# * установка вспомогательных скриптов и автозапуска +# +# Безопасно повторять: повторный запуск пропускает уже выполненные шаги. +# ============================================================================ + +set -euo pipefail + +# Цвета +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +BBOX_VERSION="1.2" +BBOX_URL="bbox.wtf" +HOSTNAME_DEFAULT="bbox-node" + +log() { echo -e "${GREEN}[init]${NC} $*"; } +warn() { echo -e "${YELLOW}[init][warn]${NC} $*"; } +fail() { echo -e "${RED}[init][error]${NC} $*"; exit 1; } + +# --------------------------------------------------------------------------- +# 1. Определение дистрибутива и архитектуры +# --------------------------------------------------------------------------- +detect_distro() { + if [ -f /etc/os-release ]; then + . /etc/os-release + DISTRO=$ID + VERSION_CODENAME=$VERSION_CODENAME + else + fail "Не удалось определить дистрибутив." + fi + ARCH=$(uname -m) + log "Дистрибутив: $DISTRO ($VERSION_CODENAME), архитектура: $ARCH" +} + +check_install() { + if command -v "$1" &> /dev/null; then + log "$1 уже установлен." + else + log "Установка $1..." + if ! apt-get install -y "$1" &> /dev/null; then + fail "Ошибка при установке $1." + fi + log "$1 установлен." + fi +} + +# --------------------------------------------------------------------------- +# 2. Обновление системы +# --------------------------------------------------------------------------- +update_system() { + log "Обновление списков пакетов..." + apt-get update || fail "Ошибка apt-get update" + apt-get upgrade -y || warn "apt-get upgrade завершился с предупреждениями" +} + +# --------------------------------------------------------------------------- +# 3. Установка базовых утилит +# --------------------------------------------------------------------------- +install_base_utils() { + log "Установка базовых утилит..." + check_install tmux + check_install mc + check_install git + check_install net-tools + check_install curl + check_install wget + check_install build-essential + check_install btop +} + +# --------------------------------------------------------------------------- +# 4. Установка Docker +# --------------------------------------------------------------------------- +install_docker() { + if command -v docker &> /dev/null; then + log "Docker уже установлен." + return + fi + log "Установка Docker для $DISTRO..." + apt-get install -y apt-transport-https ca-certificates curl gnupg + + mkdir -p /etc/apt/keyrings + curl -fsSL "https://download.docker.com/linux/$DISTRO/gpg" | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + chmod a+r /etc/apt/keyrings/docker.gpg + + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$DISTRO $VERSION_CODENAME stable" \ + > /etc/apt/sources.list.d/docker.list + + apt-get update + if ! apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin; then + warn "Официальный репозиторий не подошёл, пробуем docker.io..." + apt-get install -y docker.io docker-compose-plugin || fail "Не удалось установить Docker." + fi + systemctl enable --now docker + log "Docker установлен: $(docker --version)" +} + +# --------------------------------------------------------------------------- +# 5. Настройка hostname +# --------------------------------------------------------------------------- +setup_hostname() { + local NEW_HOSTNAME="${HOSTNAME:-$HOSTNAME_DEFAULT}" + local CUR_HOSTNAME + CUR_HOSTNAME=$(hostname) + if [ "$CUR_HOSTNAME" != "$NEW_HOSTNAME" ]; then + log "Смена hostname: $CUR_HOSTNAME -> $NEW_HOSTNAME" + hostnamectl set-hostname "$NEW_HOSTNAME" || warn "Не удалось сменить hostname" + echo "$NEW_HOSTNAME" > /etc/hostname + else + log "Hostname уже установлен: $CUR_HOSTNAME" + fi +} + +# --------------------------------------------------------------------------- +# 6. Баннер BBox (MOTD) +# --------------------------------------------------------------------------- +setup_motd() { + local HEADER="/etc/update-motd.d/10-armbian-header" + if [ -f "$HEADER" ] && ! grep -q "BBox" "$HEADER"; then + log "Обновление баннера входа: BBox v${BBOX_VERSION} ($BBOX_URL)" + cp "$HEADER" "${HEADER}.bak" + + # ASCII-баннер + sed -i 's|figlet -f small " $VENDOR"|figlet -f small " BBox"|' "$HEADER" + + # Строка версии + подпись + sed -i 's|running Armbian Linux \\e\[0;92m\${KERNELID\^}\\x1B\[0m|running \\e[0;92m${KERNELID^}\\x1B[0m, built on Armbian Linux|' "$HEADER" + # Вставка строки версии/подписи после строки баннера figlet + if ! grep -q "bbox.wtf v" "$HEADER"; then + sed -i "/figlet -f small \" BBox\"/a echo -e \" \\\\e[0;92mv${BBOX_VERSION}\\\\x1B[0m ${BBOX_URL} on \$BOARD_NAME\"\necho -e \" \\\\e[0;36mCreated by free people for a free world, in the name of privacy and security.\\\\x1B[0m\"" "$HEADER" + fi + log "Баннер обновлён." + else + log "Баннер BBox уже установлен или скрипт не найден." + fi +} + +# --------------------------------------------------------------------------- +# 7. Сброс уникальных идентификаторов (для клонированных образов) +# --------------------------------------------------------------------------- +reset_unique_ids() { + log "Сброс machine-id..." + rm -f /etc/machine-id /var/lib/dbus/machine-id + systemd-machine-id-setup || warn "Не удалось пересоздать machine-id" + + log "Сброс SSH host keys (будут пересозданы при старте sshd)..." + rm -f /etc/ssh/ssh_host_* + systemctl restart ssh || systemctl restart sshd || warn "SSH будет перезапущен при перезагрузке" +} + +# --------------------------------------------------------------------------- +# 8. Установка вспомогательных скриптов и автозапуска +# --------------------------------------------------------------------------- +install_helper_scripts() { + local SRC_DIR + SRC_DIR=$(dirname "$(readlink -f "$0")") + if [ -x "$SRC_DIR/install_scripts.sh" ]; then + log "Установка вспомогательных скриптов..." + "$SRC_DIR/install_scripts.sh" || warn "install_scripts.sh завершился с ошибкой" + else + warn "install_scripts.sh не найден рядом ($SRC_DIR), пропуск." + fi +} + +# --------------------------------------------------------------------------- +# MAIN +# --------------------------------------------------------------------------- +FIRSTBOOT=0 +FIRSTBOOT_MARKER="/var/lib/bbox-firstboot-done" + +main() { + if [ "$(id -u)" -ne 0 ]; then + fail "Запустите скрипт от root: sudo ./init_system.sh" + fi + + # --firstboot: режим автозапуска (из bbox-firstboot.service) + if [ "${1:-}" = "--firstboot" ]; then + FIRSTBOOT=1 + log "Режим firstboot (автозапуск при первой загрузке)" + if [ -f "$FIRSTBOOT_MARKER" ]; then + log "Инициализация уже выполнялась, пропуск." + exit 0 + fi + fi + + log "=== BBox (bbox.wtf) v${BBOX_VERSION} — первичная инициализация ===" + + detect_distro + update_system + install_base_utils + install_docker + setup_hostname + setup_motd + + if [ $FIRSTBOOT -eq 1 ]; then + # При автозапуске не трогаем ssh/machine-id до перезагрузки + warn "Режим firstboot: сброс идентификаторов выполняется при следующем запуске" + touch "$FIRSTBOOT_MARKER" + else + reset_unique_ids + fi + + install_helper_scripts + + log "Инициализация завершена." + if [ $FIRSTBOOT -eq 1 ]; then + log "Сервис автозапуска завершён. Перезагрузка не требуется." + else + echo + echo "Рекомендуется перезагрузить устройство: reboot" + fi +} + +main "$@" diff --git a/install_scripts.sh b/install_scripts.sh new file mode 100755 index 0000000..51546ad --- /dev/null +++ b/install_scripts.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# ============================================================================ +# install_scripts.sh — установка первичных скриптов в систему +# ---------------------------------------------------------------------------- +# Копирует скрипты этого репозитория в /usr/local/bin и настраивает +# автозапуск: +# * /usr/local/bin/init-system -> первичная инициализация (ручной запуск) +# * /usr/local/bin/cleanup-system -> очистка системы (ручной запуск) +# * /etc/systemd/system/bbox-firstboot.service -> автозапуск при первой загрузке +# * /root/firstboot.log -> журнал автозапуска +# ============================================================================ + +set -euo pipefail + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +BIN_DIR="/usr/local/bin" +SERVICE_DIR="/etc/systemd/system" +MARKER="/var/lib/bbox-firstboot-done" + +log() { echo -e "${GREEN}[install-scripts]${NC} $*"; } +warn() { echo -e "${YELLOW}[install-scripts][warn]${NC} $*"; } +fail() { echo -e "${RED}[install-scripts][error]${NC} $*"; exit 1; } + +SCRIPT_SRC_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)" + +install_script() { + local src="$1" dst="$2" + if [ ! -f "$src" ]; then + warn "Не найден: $src" + return 1 + fi + install -m 0755 "$src" "$dst" + log "Установлен: $dst" +} + +install_firstboot_service() { + local unit="$SERVICE_DIR/bbox-firstboot.service" + cat > "$unit" <