Files
opendoor-base-install/init_system.sh

333 lines
14 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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"
DEFAULT_TIMEOUT="${BBOX_TIMEOUT:-5}" # секунд на подтверждение (переопределяется env BBOX_TIMEOUT)
log() { echo -e "${GREEN}[init]${NC} $*"; }
warn() { echo -e "${YELLOW}[init][warn]${NC} $*"; }
fail() { echo -e "${RED}[init][error]${NC} $*"; exit 1; }
# ---------------------------------------------------------------------------
# Подтверждение с таймером: если пользователь не ответил за TIMEOUT секунд,
# принимается DEFAULT (автовыбор). Возвращает 0 = yes, 1 = no.
# usage: confirm_default "Вопрос" y|n [timeout]
# ---------------------------------------------------------------------------
confirm_default() {
local msg="$1" default="$2" timeout="${3:-$DEFAULT_TIMEOUT}"
local ans
case "$default" in
y|Y|yes) default="y"; prompt="[Y/n]";;
*) default="n"; prompt="[y/N]";;
esac
printf " %s %s (авто: %s через %ss): " "$msg" "$prompt" "$default" "$timeout"
if ! read -r -t "$timeout" ans 2>/dev/null; then
echo ""
echo " → таймаут, принято значение по умолчанию: $default"
ans="$default"
fi
case "${ans,,}" in
y|yes) return 0 ;;
n|no) return 1 ;;
*) [ "$default" = "y" ] ;;
esac
}
# ---------------------------------------------------------------------------
# 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) — шаблон из репозитория
# ---------------------------------------------------------------------------
# Шаблон: motd/10-armbian-header.tpl с плейсхолдерами __BBOX_VERSION__ / __BBOX_URL__.
# При каждом запуске файл целиком перезаписывается из шаблона, поэтому смена
# версии в репозитории автоматически обновляет баннер на устройстве.
# ---------------------------------------------------------------------------
MOTD_HEADER="/etc/update-motd.d/10-armbian-header"
MOTD_TPL_REPO="motd/10-armbian-header.tpl"
render_motd() {
local tpl="$1"
sed -e "s|__BBOX_VERSION__|${BBOX_VERSION}|g" \
-e "s|__BBOX_URL__|${BBOX_URL}|g" "$tpl"
}
install_motd_template() {
local tpl_src=""
local script_dir
script_dir="$(dirname "$(readlink -f "$0")")"
# Источники шаблона:
# 1) рядом со скриптом (репозиторий / временная загрузка)
# 2) в общем хранилище (после install_scripts.sh -> /usr/local/share/bbox/motd)
for candidate in \
"$script_dir/$MOTD_TPL_REPO" \
"$script_dir/../$MOTD_TPL_REPO" \
"$script_dir/motd/10-armbian-header.tpl" \
"/usr/local/share/bbox/motd/$(basename "$MOTD_TPL_REPO")"
do
if [ -f "$candidate" ]; then
tpl_src="$candidate"
break
fi
done
if [ -z "$tpl_src" ]; then
warn "Шаблон баннера не найден ($MOTD_TPL_REPO), баннер не изменён."
return 1
fi
# Бэкап дефолтного баннера только один раз
if [ ! -f "${MOTD_HEADER}.bak" ]; then
cp -f "$MOTD_HEADER" "${MOTD_HEADER}.bak" 2>/dev/null || true
fi
# Запись шаблона с подстановкой версии/URL
render_motd "$tpl_src" > "$MOTD_HEADER"
chmod 755 "$MOTD_HEADER"
log "Баннер BBox установлен из шаблона: v${BBOX_VERSION} (${BBOX_URL})"
}
setup_motd() {
if [ -f "$MOTD_HEADER" ]; then
install_motd_template
else
warn "Баннер входа не найден ($MOTD_HEADER), пропуск."
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
# Парсинг аргументов
for arg in "$@"; do
case "$arg" in
--firstboot) FIRSTBOOT=1 ;;
--yes|-y) ALL_YES=1 ;;
--timeout=*) DEFAULT_TIMEOUT="${arg#*=}" ;;
esac
done
if [ $FIRSTBOOT -eq 1 ]; then
log "Режим firstboot (автозапуск при первой загрузке)"
if [ -f "$FIRSTBOOT_MARKER" ]; then
log "Инициализация уже выполнялась, пропуск."
exit 0
fi
fi
log "=== BBox (bbox.wtf) v${BBOX_VERSION} — первичная инициализация ==="
echo
echo " Будет выполнено:"
echo " 1. Обновление системы"
echo " 2. Установка базовых утилит (tmux, mc, git, net-tools, curl, wget, build-essential, btop)"
echo " 3. Установка Docker + Docker Compose"
echo " 4. Настройка hostname"
echo " 5. Установка баннера BBox (bbox.wtf)"
echo " 6. Установка вспомогательных скриптов и автозапуска"
echo
# Подтверждение запуска (автовыбор YES через 5 сек)
if [ "${ALL_YES:-0}" -eq 1 ] || confirm_default "Начать инициализацию?" "y"; then
log "Запуск инициализации..."
else
fail "Отменено пользователем."
fi
detect_distro
update_system
install_base_utils
install_docker
# Выбор hostname (автовыбор bbox-node через 5 сек)
if [ "${ALL_YES:-0}" -eq 1 ] || confirm_default "Сменить hostname на '${HOSTNAME:-bbox-node}'?" "y"; then
setup_hostname
else
log "Hostname оставлен прежним: $(hostname)"
fi
# Баннер BBox (автовыбор YES через 5 сек)
if [ "${ALL_YES:-0}" -eq 1 ] || confirm_default "Установить баннер BBox (bbox.wtf)?" "y"; then
setup_motd
else
warn "Баннер не установлен."
fi
if [ $FIRSTBOOT -eq 1 ]; then
# При автозапуске не трогаем ssh/machine-id до перезагрузки
warn "Режим firstboot: сброс идентификаторов выполняется при следующем запуске"
touch "$FIRSTBOOT_MARKER"
else
# Сброс идентификаторов (автовыбор YES — иначе клоны будут одинаковыми)
if [ "${ALL_YES:-0}" -eq 1 ] || confirm_default "Сбросить machine-id и SSH host keys (для клонирования)?" "y"; then
reset_unique_ids
else
warn "Идентификаторы не сброшены."
fi
fi
# Установка вспомогательных скриптов (автовыбор YES через 5 сек)
if [ "${ALL_YES:-0}" -eq 1 ] || confirm_default "Установить вспомогательные скрипты и автозапуск?" "y"; then
install_helper_scripts
else
warn "Скрипты не установлены."
fi
log "Инициализация завершена."
if [ $FIRSTBOOT -eq 1 ]; then
log "Сервис автозапуска завершён. Перезагрузка не требуется."
else
echo
echo "Рекомендуется перезагрузить устройство: reboot"
fi
}
main "$@"