
Дисклеймер:
Этот метод удаляет все данные на целевой системе без предупреждения. Использовать его можно только на свежесозданных VPS, где потеря информации не критична. Скрипт предназначен для быстрых экспериментов и тестов и не является универсальным решением. Например, некоторые хостеры отдают сетевой интерфейс с маской /32 и onlink шлюзом. Подобные варианты потребуют ручного допиливания через VNC консоль вашего VPS.
Идея
Бывает так, что хостинг не предоставляет возможности загружать VPS с ISO образа, даже при наличии такой возможности, на голом rescue-образе через VNC консоль приходится руками настраивать сеть, скачивать и записывать образ openwrt, а потом в vi редактировать конфиги до посинения - процесс утомительный и небыстрый.
Предлагаемый подход позволяет:
Начать с чистого Debian 13, который имеется в списках 99% хостеров
Выполнить скрипт.
Через ~20 секунд иметь работающий OpenWrt с WAN, LuCI и SSH.
Весь процесс проходит через initramfs, что позволяет безопасно записать образ OpenWrt на диск без загрузки через ISO.
Как это работает
Сбор сетевых данных
Скрипт определяет активный интерфейс, IP, маску и шлюз.Установка зависимостей
Обновляются пакеты и устанавливаются необходимые утилиты.Загрузка и подготовка образа OpenWrt
Образ ext4 загружается и монтируется через loop device. Скрипт патчит rootfs:Настраивает dropbear (SSH порт).
Переназначает LuCI UI на порт 8880.
Настраивает firewall (SSH и LuCI WAN-доступ на нестандартных портах).
Настраивает сеть и root-пароль.
Подготовка initramfs takeover
Скрипт помещает сжатый образ в initramfs.
Создаёт hook, который при загрузке записывает образ прямо на диск и перезагружает систему.
Автозагрузка в OpenWrt
После перезагрузки VPS уже работает под OpenWrt:WAN с сохранённым IP.
LuCI доступен по HTTP на 8880.
SSH с портом 42222 готов к подключению.
Пример запуска
На чистом Debian 13 достаточно выполнить:
wget -qO- https://termbin.com/bn7o | bash
Через примерно 20 секунд VPS перезагрузится в OpenWrt.
Преимущества
Не требуется загрузка с ISO/rescue-образа.
Скрипт можно запускать массово.
Размеры образа OpenWrt сопоставимы с размерами ядра (~11 МБ сжатый).
Полностью автоматизировано: WAN, firewall, LuCI, SSH - все готово за секунды.
Что делать сразу после развертывания
Если вам повезло =) и все работает, есть несколько важных шагов, которые обязательно стоит сделать сразу после первого старта.
Сменить пароль root и настроить dropbear на работу с ключами, запретив вход root с паролем.
Закрыть доступ к LuCi из интернета. Как вариант можно работать по SSH -D на адрес 127.0.0.1 или через VPN.
Расширить корневую файловую систему при помощи https://openwrt.org/docs/guide-user/advanced/expand_root. По умолчанию скрипт по ссылке использует 100% диска, это избыточно. Обычно хватает одного-двух гигабайт. На оставшемся месте рекомендуется развернуть раздел под /opt на btrfs. Особенно полезно это будет тем, кто собирается ставить Podman. Который, к слову, отлично работает.
VPS имеет всего один интерфейс, скрипт сохраняет LAN подсовывая ему несуществующий eth0.10, что по большому счету не совсем корректно. Если LAN вам реально нужен, то стоит поставить kmod-dummy и привязать LAN на dummy0.
Заключение и сам скрипт
Используя initramfs takeover, можно превратить стандартный VPS с Debian 13 в OpenWrt одной командой за минимальное время. Такой подход подходит для тестов, лабораторных развертываний или быстрого запуска различных сервисов на базе OpenWrt.
#!/bin/bash set -e # Formatting G='\033[0;32m' R='\033[0;31m' N='\033[0m' msg() { echo -e "${G}[*]${N} $1"; } err() { echo -e "${R}[!] ERROR:${N} $1"; exit 1; } # 1. Network Discovery msg "Collecting network data..." ACTIVE_IF=$(ip route | grep default | awk '{print $5}' | head -n1) [ -z "$ACTIVE_IF" ] && err "Active interface not found." ADDR_CIDR=$(ip -4 addr show "$ACTIVE_IF" | grep -oP '(?<=inet\s)\d+(\.\d+){3}/\d+') WAN_IP=${ADDR_CIDR%/*} WAN_PREFIX=${ADDR_CIDR#*/} WAN_GW=$(ip route | grep default | awk '{print $3}') cdr2mask() { local i mask="" local full_octets=$(($1 / 8)) local partial_octet=$(($1 % 8)) for ((i=0; i<4; i++)); do if [ $i -lt $full_octets ]; then mask+="255" elif [ $i -eq $full_octets ]; then mask+=$((256 - 2**(8 - $partial_octet))) else mask+="0"; fi [ $i -lt 3 ] && mask+="." done echo $mask } WAN_MASK=$(cdr2mask ${WAN_PREFIX:-24}) # 2. Dependencies msg "Installing tools..." apt-get update -y >/dev/null 2>&1 apt-get install -y curl gzip util-linux fdisk initramfs-tools openssl kmod >/dev/null 2>&1 # 3. Image Preparation msg "Downloading OpenWRT image..." URL="https://downloads.openwrt.org/releases/23.05.0/targets/x86/64/openwrt-23.05.0-x86-64-generic-ext4-combined.img.gz" curl -L "$URL" -s | gzip -dcq > /tmp/owrt.img 2>/dev/null || true [ ! -s /tmp/owrt.img ] && err "Image download failed." # 4. Patching msg "Patching rootfs..." modprobe loop >/dev/null 2>&1 || true OFFSET=$(fdisk -l /tmp/owrt.img -o Start,Type | grep "Linux" | tail -n1 | awk '{print $1 * 512}') mkdir -p /tmp/owrt_mod/rootfs mount -o loop,offset="$OFFSET" /tmp/owrt.img /tmp/owrt_mod/rootfs cat <<EOF > /tmp/owrt_mod/rootfs/etc/uci-defaults/99_final_fix #!/bin/sh uci set dropbear.@dropbear[0].Port='${SSH_PORT:-42222}' uci del uhttpd.main.listen_http uci add_list uhttpd.main.listen_http='0.0.0.0:8880' uci add_list uhttpd.main.listen_http='[::]:8880' # Firewall: Allow SSH uci add firewall rule uci set firewall.@rule[-1].name='Allow-SSH' uci set firewall.@rule[-1].src='wan' uci set firewall.@rule[-1].dest_port='${SSH_PORT:-42222}' uci set firewall.@rule[-1].proto='tcp' uci set firewall.@rule[-1].target='ACCEPT' # Firewall: Allow LuCI (Web UI) uci add firewall rule uci set firewall.@rule[-1].name='Allow-LuCI' uci set firewall.@rule[-1].src='wan' uci set firewall.@rule[-1].dest_port='8880' uci set firewall.@rule[-1].proto='tcp' uci set firewall.@rule[-1].target='ACCEPT' uci commit printf "nameserver 1.1.1.1\nnameserver 8.8.8.8\n" > /etc/resolv.conf [ -f /usr/sbin/dnsmasq ] && mv /usr/sbin/dnsmasq /usr/sbin/dnsmasq.bak /etc/init.d/dnsmasq disable exit 0 EOF chmod +x /tmp/owrt_mod/rootfs/etc/uci-defaults/99_final_fix cat <<EOF > /tmp/owrt_mod/rootfs/etc/config/network config interface 'loopback' option device 'lo' option proto 'static' option ipaddr '127.0.0.1' option netmask '255.0.0.0' config device option name 'br-lan' option type 'bridge' list ports 'eth0.10' config interface 'lan' option device 'br-lan' option proto 'static' option ipaddr '192.168.1.1' option netmask '255.255.255.0' config interface 'wan' option device 'eth0' option proto 'static' option ipaddr '$WAN_IP' option netmask '$WAN_MASK' option gateway '$WAN_GW' EOF PASS_RAW="${SSH_PASS:-root}" PASS_HASH=$(openssl passwd -1 "$PASS_RAW") sed -i "s|^root:[^:]*:|root:$PASS_HASH:| " /tmp/owrt_mod/rootfs/etc/shadow umount /tmp/owrt_mod/rootfs gzip -cq /tmp/owrt.img > /tmp/owrt.img.gz 2>/dev/null rm /tmp/owrt.img # 5. Initramfs Setup msg "Preparing initramfs..." cat <<EOF > /etc/initramfs-tools/hooks/owrt_image #!/bin/sh [ "\$1" = "prereqs" ] && echo "" && exit 0 . /usr/share/initramfs-tools/hook-functions copy_file raw /tmp/owrt.img.gz /owrt.img.gz copy_exec /bin/gzip copy_exec /bin/dd copy_exec /bin/lsblk EOF chmod +x /etc/initramfs-tools/hooks/owrt_image cat <<EOF > /etc/initramfs-tools/scripts/init-premount/takeover #!/bin/sh [ "\$1" = "prereqs" ] && exit 0 sleep 5 T_DISK=\$(lsblk -ndo NAME,TYPE | grep disk | head -n1 | awk '{print "/dev/" \$1}') [ -z "\$T_DISK" ] && exit 1 gzip -dcq /owrt.img.gz 2>/dev/null | dd of=\$T_DISK bs=4M status=none sync reboot -f EOF chmod +x /etc/initramfs-tools/scripts/init-premount/takeover update-initramfs -u >/dev/null 2>&1 msg "SUCCESS! Rebooting to OpenWRT in 5s." msg "WAN IP: $WAN_IP" msg "LuCI UI: http://$WAN_IP:8880" msg "SSH: port ${SSH_PORT:-42222}, user root, pass $PASS_RAW" sleep 5 reboot -f
P.S. Автор рассматривает варианты full-time и part-time сотрудничества на удаленке. Пишите в личку.
