#!/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 "$@"