Дисклеймер:

Этот метод удаляет все данные на целевой системе без предупреждения. Использовать его можно только на свежесозданных VPS, где потеря информации не критична. Скрипт предназначен для быстрых экспериментов и тестов и не является универсальным решением. Например, некоторые хостеры отдают сетевой интерфейс с маской /32 и onlink шлюзом. Подобные варианты потребуют ручного допиливания через VNC консоль вашего VPS.


Идея

Бывает так, что хостинг не предоставляет возможности загружать VPS с ISO образа, даже при наличии такой возможности, на голом rescue-образе через VNC консоль приходится руками настраивать сеть, скачивать и записывать образ openwrt, а потом в vi редактировать конфиги до посинения - процесс утомительный и небыстрый.

Предлагаемый подход позволяет:

  • Начать с чистого Debian 13, который имеется в списках 99% хостеров

  • Выполнить скрипт.

  • Через ~20 секунд иметь работающий OpenWrt с WAN, LuCI и SSH.

Весь процесс проходит через initramfs, что позволяет безопасно записать образ OpenWrt на диск без загрузки через ISO.


Как это работает

  1. Сбор сетевых данных
    Скрипт определяет активный интерфейс, IP, маску и шлюз.

  2. Установка зависимостей
    Обновляются пакеты и устанавливаются необходимые утилиты.

  3. Загрузка и подготовка образа OpenWrt
    Образ ext4 загружается и монтируется через loop device. Скрипт патчит rootfs:

    • Настраивает dropbear (SSH порт).

    • Переназначает LuCI UI на порт 8880.

    • Настраивает firewall (SSH и LuCI WAN-доступ на нестандартных портах).

    • Настраивает сеть и root-пароль.

  4. Подготовка initramfs takeover

    • Скрипт помещает сжатый образ в initramfs.

    • Создаёт hook, который при загрузке записывает образ прямо на диск и перезагружает систему.

  5. Автозагрузка в 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 сотрудничества на удаленке. Пишите в личку.