mikrotik-chr-installer/chr-installer.sh
2026-01-31 08:24:40 +03:00

448 lines
No EOL
15 KiB
Bash
Raw 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
set -e
# Цвета для вывода
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_debug() { echo -e "${CYAN}[DEBUG]${NC} $1"; }
# ============================================
# КОНФИГУРАЦИЯ
# ============================================
CHR_VERSION="7.16.1"
CHR_URL="https://download.mikrotik.com/routeros/${CHR_VERSION}/chr-${CHR_VERSION}.img.zip"
CHR_ZIP="chr-${CHR_VERSION}.img.zip"
CHR_IMG="chr-${CHR_VERSION}.img"
WORK_DIR="/tmp/chr-install"
MOUNT_POINT="/mnt/chr"
# Настройки CHR
ADMIN_PASSWORD="PASSWORD"
DNS_SERVERS="8.8.8.8,8.8.4.4"
# Флаги
SKIP_AUTORUN=false
FORCE_DOWNLOAD=false
VERIFY_WRITE=true
AUTO_YES=false
AUTO_REBOOT=false
# ============================================
# ПАРСИНГ АРГУМЕНТОВ
# ============================================
usage() {
echo "Использование: $0 [опции]"
echo ""
echo "Опции:"
echo " --clean Чистая установка без autorun.scr"
echo " --force Принудительно скачать образ заново"
echo " --no-verify Пропустить верификацию записи"
echo " --yes, -y Без подтверждений (автоматический режим)"
echo " --reboot Автоматическая перезагрузка (требует --yes)"
echo " --version VER Версия CHR (по умолчанию: $CHR_VERSION)"
echo " --password PASS Пароль admin (по умолчанию: $ADMIN_PASSWORD)"
echo " -h, --help Показать справку"
echo ""
echo "Примеры:"
echo " $0 --clean # Чистая установка с подтверждением"
echo " $0 --yes --reboot # Полностью автоматическая установка"
echo " $0 -y --clean --reboot # Автоматическая чистая установка"
exit 0
}
while [[ $# -gt 0 ]]; do
case $1 in
--clean)
SKIP_AUTORUN=true
shift
;;
--force)
FORCE_DOWNLOAD=true
shift
;;
--no-verify)
VERIFY_WRITE=false
shift
;;
--yes|-y)
AUTO_YES=true
shift
;;
--reboot)
AUTO_REBOOT=true
shift
;;
--version)
CHR_VERSION="$2"
CHR_URL="https://download.mikrotik.com/routeros/${CHR_VERSION}/chr-${CHR_VERSION}.img.zip"
CHR_ZIP="chr-${CHR_VERSION}.img.zip"
CHR_IMG="chr-${CHR_VERSION}.img"
shift 2
;;
--password)
ADMIN_PASSWORD="$2"
shift 2
;;
-h|--help)
usage
;;
*)
log_error "Неизвестная опция: $1"
usage
;;
esac
done
# ============================================
# ПРОВЕРКА ROOT
# ============================================
if [[ $EUID -ne 0 ]]; then
log_error "Скрипт должен запускаться от root"
exit 1
fi
# ============================================
# ПРОВЕРКА ЗАВИСИМОСТЕЙ
# ============================================
log_info "Проверка зависимостей..."
REQUIRED_TOOLS="wget unzip fdisk dd mount umount file md5sum xxd"
MISSING_TOOLS=""
for tool in $REQUIRED_TOOLS; do
if ! command -v $tool &> /dev/null; then
MISSING_TOOLS="$MISSING_TOOLS $tool"
fi
done
if [[ -n "$MISSING_TOOLS" ]]; then
log_warn "Отсутствуют утилиты:$MISSING_TOOLS"
log_info "Попытка установки..."
if command -v apt-get &> /dev/null; then
apt-get update && apt-get install -y wget unzip fdisk coreutils mount xxd
elif command -v yum &> /dev/null; then
yum install -y wget unzip util-linux coreutils vim-common
elif command -v dnf &> /dev/null; then
dnf install -y wget unzip util-linux coreutils vim-common
else
log_error "Установи вручную:$MISSING_TOOLS"
exit 1
fi
fi
log_info "Все зависимости в порядке"
# ============================================
# ПОДГОТОВКА РАБОЧЕЙ ДИРЕКТОРИИ
# ============================================
mkdir -p "$WORK_DIR"
cd "$WORK_DIR"
log_info "Рабочая директория: $WORK_DIR"
log_debug "Свободное место: $(df -h "$WORK_DIR" | tail -1 | awk '{print $4}')"
# ============================================
# СКАЧИВАНИЕ ОБРАЗА
# ============================================
if [[ "$FORCE_DOWNLOAD" == true ]] || [[ ! -f "$CHR_IMG" ]]; then
rm -f "$CHR_ZIP" "$CHR_IMG" "${CHR_IMG}.modified"
log_info "Скачивание CHR ${CHR_VERSION}..."
wget --progress=bar:force -O "$CHR_ZIP" "$CHR_URL"
# Проверка размера скачанного файла
ACTUAL_SIZE=$(stat -c%s "$CHR_ZIP")
log_debug "Размер скачанного файла: $ACTUAL_SIZE байт"
if [[ $ACTUAL_SIZE -lt 30000000 ]]; then
log_error "Файл слишком маленький, скачивание неполное"
exit 1
fi
# Проверка типа файла
FILE_TYPE=$(file "$CHR_ZIP")
log_debug "Тип файла: $FILE_TYPE"
if echo "$FILE_TYPE" | grep -q "Zip archive"; then
log_info "Распаковка ZIP..."
unzip -o "$CHR_ZIP"
elif echo "$FILE_TYPE" | grep -q "gzip"; then
log_info "Распаковка GZIP..."
gunzip -c "$CHR_ZIP" > "$CHR_IMG"
else
log_error "Неизвестный формат: $FILE_TYPE"
exit 1
fi
rm -f "$CHR_ZIP"
else
log_info "Используется существующий образ: $CHR_IMG"
fi
# ============================================
# ВАЛИДАЦИЯ ОБРАЗА (РАСШИРЕННАЯ)
# ============================================
log_info "Валидация образа..."
if [[ ! -f "$CHR_IMG" ]]; then
log_error "Образ не найден!"
ls -la "$WORK_DIR"
exit 1
fi
IMG_SIZE=$(stat -c%s "$CHR_IMG")
log_debug "Размер образа: $IMG_SIZE байт ($(( IMG_SIZE / 1024 / 1024 )) MB)"
# Проверка MBR сигнатуры (последние 2 байта первого сектора = 55 AA)
MBR_SIG=$(xxd -s 510 -l 2 -p "$CHR_IMG")
if [[ "$MBR_SIG" != "55aa" ]]; then
log_error "Неверная MBR сигнатура: $MBR_SIG (ожидается 55aa)"
exit 1
fi
log_debug "MBR сигнатура: OK (55aa)"
# Сохраняем MD5 оригинального образа
ORIGINAL_MD5=$(md5sum "$CHR_IMG" | awk '{print $1}')
log_info "MD5 оригинального образа: $ORIGINAL_MD5"
# Проверка таблицы разделов
log_debug "Таблица разделов:"
fdisk -l "$CHR_IMG" 2>/dev/null | grep -E "^(Disk|Device|${CHR_IMG})" || true
log_info "Образ прошёл валидацию ✓"
# ============================================
# ОПРЕДЕЛЕНИЕ СЕТЕВЫХ ПАРАМЕТРОВ
# ============================================
log_info "Определение сетевых параметров..."
INTERFACE=$(ip route | grep default | awk '{print $5}' | head -n1)
ADDRESS=$(ip addr show "$INTERFACE" 2>/dev/null | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | head -n1)
GATEWAY=$(ip route | grep default | awk '{print $3}' | head -n1)
if [[ -z "$INTERFACE" || -z "$ADDRESS" || -z "$GATEWAY" ]]; then
log_error "Не удалось определить сетевые параметры"
log_error "INTERFACE=$INTERFACE ADDRESS=$ADDRESS GATEWAY=$GATEWAY"
exit 1
fi
log_info "Интерфейс: $INTERFACE | Адрес: $ADDRESS | Шлюз: $GATEWAY"
# ============================================
# ОПРЕДЕЛЕНИЕ ДИСКА
# ============================================
log_info "Определение целевого диска..."
echo ""
lsblk -d -o NAME,SIZE,MODEL,TYPE | grep -E "(NAME|disk)"
echo ""
DISK_DEVICE=$(lsblk -ndo NAME,TYPE | grep disk | head -n1 | awk '{print "/dev/"$1}')
if [[ -z "$DISK_DEVICE" ]]; then
log_error "Диск не найден"
exit 1
fi
DISK_SIZE=$(lsblk -ndo SIZE "$DISK_DEVICE")
log_warn "Целевой диск: $DISK_DEVICE ($DISK_SIZE)"
# ============================================
# ВЫБОР ОБРАЗА ДЛЯ ЗАПИСИ
# ============================================
FINAL_IMG="$CHR_IMG"
if [[ "$SKIP_AUTORUN" == true ]]; then
log_warn "Режим --clean: autorun.scr НЕ будет создан"
log_warn "Настройка CHR вручную после загрузки"
else
log_info "Создание autorun.scr..."
# Создаём копию образа для модификации
CHR_IMG_MOD="${CHR_IMG}.modified"
cp "$CHR_IMG" "$CHR_IMG_MOD"
# Монтирование
mkdir -p "$MOUNT_POINT"
OFFSET_SECTORS=$(fdisk -l "$CHR_IMG_MOD" 2>/dev/null | grep "${CHR_IMG_MOD}2" | awk '{print $2}')
if [[ -z "$OFFSET_SECTORS" ]]; then
OFFSET_BYTES=33571840
else
OFFSET_BYTES=$((OFFSET_SECTORS * 512))
fi
log_debug "Монтирование с offset: $OFFSET_BYTES"
mount -o loop,offset="$OFFSET_BYTES" "$CHR_IMG_MOD" "$MOUNT_POINT"
# Проверяем структуру
log_debug "Содержимое смонтированного раздела:"
ls -la "$MOUNT_POINT/" || true
if [[ ! -d "$MOUNT_POINT/rw" ]]; then
log_warn "Директория /rw не существует, создаём..."
mkdir -p "$MOUNT_POINT/rw"
fi
# Создание autorun
cat > "$MOUNT_POINT/rw/autorun.scr" <<EOF
/ip dns set servers=${DNS_SERVERS}
/ip service set telnet disabled=yes
/ip service set ftp disabled=yes
/ip service set www disabled=no
/ip service set ssh disabled=no
/ip service set api disabled=yes
/ip service set api-ssl disabled=yes
/ip service set winbox disabled=no
/ip dhcp-client remove [find]
/ip address add address=${ADDRESS} interface=[/interface ethernet find where name=ether1]
/ip route add gateway=${GATEWAY}
/user set 0 name=admin password=${ADMIN_PASSWORD}
EOF
# Синхронизация ФС перед размонтированием
sync
log_debug "autorun.scr создан:"
cat "$MOUNT_POINT/rw/autorun.scr"
# Проверяем что файл записался
if [[ ! -s "$MOUNT_POINT/rw/autorun.scr" ]]; then
log_error "autorun.scr пустой или не создан!"
umount "$MOUNT_POINT"
exit 1
fi
# Размонтирование с sync
sync
umount "$MOUNT_POINT"
sync
# Проверка MD5 после модификации
MODIFIED_MD5=$(md5sum "$CHR_IMG_MOD" | awk '{print $1}')
log_debug "MD5 после модификации: $MODIFIED_MD5"
if [[ "$ORIGINAL_MD5" == "$MODIFIED_MD5" ]]; then
log_warn "MD5 не изменился — возможно autorun.scr не записался"
fi
# Проверка MBR после модификации
MBR_SIG_MOD=$(xxd -s 510 -l 2 -p "$CHR_IMG_MOD")
if [[ "$MBR_SIG_MOD" != "55aa" ]]; then
log_error "MBR повреждён после модификации! Сигнатура: $MBR_SIG_MOD"
log_error "Используем оригинальный образ без autorun"
rm -f "$CHR_IMG_MOD"
SKIP_AUTORUN=true
FINAL_IMG="$CHR_IMG"
else
log_debug "MBR после модификации: OK"
FINAL_IMG="$CHR_IMG_MOD"
log_info "Autorun настроен ✓"
fi
fi
# ============================================
# ФИНАЛЬНОЕ ПОДТВЕРЖДЕНИЕ
# ============================================
echo ""
echo "============================================"
echo -e "${YELLOW} ТОЧКА НЕВОЗВРАТА!${NC}"
echo "============================================"
echo ""
echo "Образ: $FINAL_IMG"
echo "Диск: $DISK_DEVICE ($DISK_SIZE)"
echo "IP: $ADDRESS"
echo "Шлюз: $GATEWAY"
echo "Autorun: $(if [[ "$SKIP_AUTORUN" == true ]]; then echo "ОТКЛЮЧЕН"; else echo "включен"; fi)"
echo ""
echo -e "${RED}ВСЕ ДАННЫЕ НА $DISK_DEVICE БУДУТ УНИЧТОЖЕНЫ!${NC}"
echo ""
if [[ "$AUTO_YES" == true ]]; then
log_warn "Автоматический режим (--yes), продолжаем без подтверждения..."
else
read -p "Введи 'YES' для продолжения: " confirm
if [[ "$confirm" != "YES" ]]; then
log_info "Отменено"
exit 0
fi
fi
# ============================================
# ЗАПИСЬ НА ДИСК
# ============================================
log_info "Запись образа на $DISK_DEVICE..."
# Переводим FS в read-only чтобы избежать race condition
log_info "Перевод файловой системы в read-only..."
sync
echo 1 > /proc/sys/kernel/sysrq
echo u > /proc/sysrq-trigger
sleep 2
dd if="$FINAL_IMG" of="$DISK_DEVICE" bs=4M oflag=direct status=progress
log_info "Запись завершена"
# sync не нужен - FS уже в read-only, dd с oflag=direct пишет напрямую
# ============================================
# ВЕРИФИКАЦИЯ ЗАПИСИ
# ============================================
# После echo u файловая система в read-only, верификация невозможна
# MBR уже проверен до записи, dd с oflag=direct гарантирует запись
log_info "Верификация пропущена (FS в read-only после remount)"
# ============================================
# ЗАВЕРШЕНИЕ
# ============================================
echo ""
log_info "=========================================="
log_info "Установка CHR завершена успешно!"
log_info "=========================================="
echo ""
if [[ "$SKIP_AUTORUN" == true ]]; then
echo "После загрузки настрой вручную:"
echo " /ip address add address=$ADDRESS interface=ether1"
echo " /ip route add gateway=$GATEWAY"
echo ""
fi
echo "CHR будет доступен: ${ADDRESS%/*}"
echo ""
if [[ "$AUTO_YES" == true && "$AUTO_REBOOT" == true ]]; then
log_info "Автоматическая перезагрузка через 3 секунды..."
sleep 3
echo 1 > /proc/sys/kernel/sysrq
echo s > /proc/sysrq-trigger
sleep 1
echo u > /proc/sysrq-trigger
sleep 1
echo b > /proc/sysrq-trigger
elif [[ "$AUTO_YES" == true ]]; then
log_info "Перезагрузи вручную: reboot"
else
read -p "Перезагрузить сейчас? (y/n): " do_reboot
if [[ "$do_reboot" == "y" ]]; then
log_info "Перезагрузка..."
sleep 2
echo 1 > /proc/sys/kernel/sysrq
echo s > /proc/sysrq-trigger
sleep 1
echo u > /proc/sysrq-trigger
sleep 1
echo b > /proc/sysrq-trigger
else
log_info "Перезагрузи вручную: reboot"
fi
fi