Bootstrap

Вторая жизнь Asus RT-AC51U: OpenWrt

Вторая жизнь Asus RT-AC51U: OpenWrt

Вторая жизнь Asus RT-AC51U: OpenWrt, swap на флешке и доступ в домашнюю сеть без белого IP

Как превратить роутер десятилетней давности с 64 МБ памяти в постоянную точку входа в домашнюю сеть: от неработающего opkg update до своего туннельного клиента на Go и десятка практических сценариев.


У меня лежал Asus RT-AC51U, роутер 2015 года: одно ядро MIPS на 580 МГц, 64 МБ оперативной памяти, 16 МБ флеш-памяти. Как основной роутер он давно не годится: порты по 100 Мбит/с, Wi-Fi 5 ГГц только формально. Выбросить жалко, а лежать без дела ему незачем.

В прошлой статье я поставил на него OpenWrt и перенёс корневую систему на USB-флешку (extroot). Здесь продолжение: что с этим роутером делать дальше. Если коротко, получилась постоянно включённая «дверь» в домашнюю сеть. Он потребляет 3–5 Вт, работает за любым NAT без белого IP и проброса портов и пускает к любому устройству дома: к ПК, NAS, камере, роутеру провайдера.

По пути пришлось:

  • разобраться, почему на свежей прошивке не работает opkg update;
  • подключить swap, потому что свободной памяти около 14 МБ;
  • понять, как устроен extroot изнутри и как сбросить настройки, не потеряв загрузку с флешки;
  • написать клиент туннеля на Go, потому что штатный не помещается в 64 МБ;
  • честно проверить, что это железо тянет, а что нет.

TL;DR

ЧтоИтог
opkg update → SSL verify errorу роутера нет часов реального времени. Лечится ntpd -q -n -p pool.ntp.org
Память64 МБ, свободно ~14 МБ. Добавлен swap 512 МБ на флешке, swappiness=10
«Reset to defaults» в LuCIломает загрузку с флешки. Сбрасывать нужно иначе (раздел 5.3)
Туннельсвой клиент на Go: 8 МБ на диске, ~7.5 МБ RAM против ~60 МБ у штатного
ПрименениеSSH во всю домашнюю сеть, Wake-on-LAN, удалённая помощь родственникам, уведомления о пропаже света, гостевой Wi-Fi, MQTT, принт-сервер
Не потянулVPN на всю сеть через WDTT (VK TURN): ~1 Мбит/с и зависания

Содержание

  1. Железо
  2. opkg update не работает: виновато время
  3. Что внутри: смотрим характеристики
  4. Swap на флешке
  5. Extroot изнутри и сброс без потери флешки
  6. Туннель без белого IP: свой клиент на Go
  7. Что делать с таким роутером
  8. Что не взлетело
  9. Безопасность
  10. Итоги

1. Железо

ПараметрЗначение
МодельAsus RT-AC51U
SoCMediaTek MT7620A, MIPS 24KEc, 1 ядро, ~580 МГц, без FPU
Архитектура пакетовmipsel_24kc (little-endian)
RAM64 МБ (ядру доступно ~55 МБ)
Флеш16 МБ SPI NOR
USBодин порт USB 2.0 (занят флешкой с extroot)
Ethernet4 × LAN + WAN, 100 Мбит/с
Wi-Fi2.4 ГГц 802.11n + 5 ГГц 802.11ac
ПрошивкаOpenWrt 24.10.5, extroot на USB-флешке 4 ГБ
Потребление3–5 Вт

Всё дальнейшее применимо почти к любому роутеру на OpenWrt с 64–128 МБ RAM: MT7620/MT7621/MT7628, ath79, IPQ40xx.


2. `opkg update` не работает: виновато время

Первое, что я увидел после прошивки:

☯

Terminal:

⌕

≡

✕
root@OpenWrt:~# opkg update
Downloading https://downloads.openwrt.org/releases/24.10.5/targets/ramips/mt7620/packages/Packages.gz
Failed to send request: Operation not permitted
...
root@OpenWrt:~# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
ping: sendto: Network unreachable
root@OpenWrt:~# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=107 time=53.260 ms
64 bytes from 8.8.8.8: seq=1 ttl=107 time=51.680 ms
root@OpenWrt:~# opkg update
Downloading https://downloads.openwrt.org/releases/24.10.5/targets/ramips/mt7620/packages/Packages.gz
SSL verify error: unknown error
*** Failed to download the package list from https://downloads.openwrt.org/...
Collected errors:
 * opkg_download: Failed to download ..., wget returned 5.

Здесь две разные ошибки.

  1. Operation not permitted и Network unreachable появились сразу после загрузки: WAN ещё не поднялся, маршрута по умолчанию не было. Через несколько секунд ping прошёл, значит, сеть в порядке.
  2. SSL verify error остался, причём на всех адресах. Сеть есть, TLS нет. Классическая причина — неверное системное время: - у MT7620 нет часов реального времени (RTC). После включения часы показывают дату сборки прошивки, пока не сработает NTP; - с неверной датой сертификат сервера выглядит «ещё не действительным» или «просроченным», и проверка падает.

Лечение:

☯

Terminal:

⌕

≡

✕
date                          # проверить время
ntpd -q -n -p pool.ntp.org    # синхронизировать (NTP идёт по UDP, без TLS)
date
opkg update                   # теперь работает

Дальше OpenWrt держит время сам через sysntpd. Но после каждой загрузки есть окно в несколько секунд, пока время неверное. Если какой-то скрипт в автозапуске ходит по HTTPS, пусть сначала дождётся NTP.

Если ошибка не уходит, возможно, провайдер режет downloads.openwrt.org. Тогда можно переключить репозиторий на HTTP. Пакеты всё равно проверяются по подписи usign, так что это безопасно:

☯

Terminal:

⌕

≡

✕
sed -i 's|https://|http://|' /etc/opkg/distfeeds.conf
opkg update

3. Что внутри: смотрим характеристики

☯

Terminal:

⌕

≡

✕
ubus call system board                                   # модель, SoC, прошивка
grep -E "system type|cpu model|BogoMIPS" /proc/cpuinfo   # процессор
free                                                      # RAM (BusyBox выводит в КБ)
df -h                                                     # разделы
cat /proc/mtd                                             # разметка флеша

☯

Terminal:

⌕

≡

✕
        "system": "MediaTek MT7620A ver:2 eco:6",
        "model": "Asus RT-AC51U",
        "release": { "version": "24.10.5", "target": "ramips/mt7620" }
system type             : MediaTek MT7620A ver:2 eco:6
cpu model               : MIPS 24KEc V5.0
BogoMIPS                : 383.38
              total        used        free      shared  buff/cache   available
Mem:          56380       23520       20596        1252       12264       13816
Swap:             0           0           0
Filesystem                Size      Used Available Use% Mounted on
/dev/root                 4.0M      4.0M         0 100% /rom
tmpfs                    27.5M      1.2M     26.3M   4% /tmp
/dev/sda1                 3.7G     22.4M      3.4G   1% /overlay
overlayfs:/overlay        3.7G     22.4M      3.4G   1% /
mtd5: 00d37895 00010000 "rootfs"
mtd6: 00960000 00010000 "rootfs_data"

Две особенности, о которые легко споткнуться:

  • uname -m на MIPS всегда показывает просто mips и не говорит, little-endian это или big-endian. Архитектуру пакетов (и бинарников) смотрите через opkg print-architecture: здесь mipsel_24kc.
  • Узкое место — память. Из 64 МБ реально свободно ~14 МБ (available), swap нет. Любой сервис, которому внезапно понадобится 15–20 МБ, приведёт к OOM-killer.

4. Swap на флешке

Почему swap, и почему такой:

  • Место есть. На флешке extroot свободно 3.4 ГБ.
  • Размер 512 МБ — страховка от OOM. Если система реально начнёт активно гонять сотни мегабайт swap по USB-флешке, роутер будет тормозить, но это лучше, чем внезапно убитый сервис.
  • swappiness=10: ядро уходит в swap только при реальной нехватке памяти, а флешка изнашивается меньше.

☯

Terminal:

⌕

≡

✕
dd if=/dev/zero of=/overlay/swapfile bs=1M count=512
chmod 600 /overlay/swapfile
mkswap /overlay/swapfile
swapon /overlay/swapfile
free

☯

Terminal:

⌕

≡

✕
Setting up swapspace version 1, size = 536866816 bytes
              total        used        free      shared  buff/cache   available
Mem:          56380       21716       19092        1252       15572       13968
Swap:        524284           0      524284

Если mkswap или swapon не найдены, поставьте пакет: opkg install swap-utils. Повторный mkswap по уже включённому файлу выдаст Text file busy, это безвредно.

Автозагрузка. Extroot монтируется рано, поэтому к моменту /etc/rc.local флешка уже доступна:

☯

Terminal:

⌕

≡

✕
grep -q swapfile /etc/rc.local || \
  sed -i '/^exit 0/i [ -f /overlay/swapfile ] && swapon /overlay/swapfile' /etc/rc.local

echo 'vm.swappiness=10' > /etc/sysctl.d/99-swap.conf
sysctl -p /etc/sysctl.d/99-swap.conf

Проверка после reboot:

☯

Terminal:

⌕

≡

✕
free                          # Swap: ~524284
cat /proc/swaps               # /overlay/swapfile  file  524284 ...
cat /proc/sys/vm/swappiness   # 10

5. Extroot изнутри и сброс без потери флешки

Однажды я захотел «начисто» сбросить роутер кнопкой в LuCI. Хорошо, что сначала разобрался, к чему это приведёт.

5.1. Где что лежит

☯

Terminal:

⌕

≡

✕
Встроенный флеш 16 МБ
├─ mtd5 "rootfs"       squashfs, только чтение  → /rom      ← сама прошивка
└─ mtd6 "rootfs_data"  jffs2, ~9.4 МБ                       ← «загрузчик» extroot:
                                                               fstab + block-mount + модули USB/ext4
USB-флешка
└─ /dev/sda1 ext4                                → /overlay
   ├─ upper/      ← ВСЕ изменения: настройки, пакеты, пароль, ключи, сервисы
   ├─ work/       служебный каталог overlayfs
   └─ swapfile    swap 512 МБ

/  =  overlayfs( нижний слой /rom  +  верхний слой /overlay/upper )

Порядок загрузки:

  1. Ядро монтирует /rom.
  2. Preinit монтирует встроенный rootfs_data и читает оттуда /etc/config/fstab.
  3. По этому fstab подключается флешка как /overlay.
  4. / собирается как «прошивка + всё из upper».

Любой файл в /overlay/upper перекрывает одноимённый файл прошивки. Удалите его из upper, и снова будет виден заводской. Посмотреть, что изменено относительно прошивки:

☯

Terminal:

⌕

≡

✕
mount | grep -E ' / | /rom|overlay|sda|mtdblock'
find /overlay/upper -type f | grep -v '^/overlay/upper/usr/lib/opkg' | head -50
grep '^Package:' /overlay/upper/usr/lib/opkg/status | cut -d' ' -f2 | sort   # что ставили сами

5.2. Почему нельзя «Reset to defaults» в LuCI

Кнопка в LuCI и firstboot -y стирают встроенный rootfs_data. Вместе с ним уходят fstab с указанием на флешку и пакеты block-mount, kmod-usb-storage, kmod-fs-ext4.

Роутер не превратится в кирпич: он загрузится с заводскими настройками со встроенной памяти. Но флешку он больше не подключит. Данные на флешке не стираются, они просто не используются.

Если уже нажали:

☯

Terminal:

⌕

≡

✕
ntpd -q -n -p pool.ntp.org
opkg update
opkg install block-mount kmod-usb-storage kmod-fs-ext4
block info                       # UUID у /dev/sda1

cat > /etc/config/fstab <<'FSTAB'
config 'global'
        option anon_swap  '0'
        option anon_mount '0'
        option auto_swap  '1'
        option auto_mount '1'
        option delay_root '5'
        option check_fs   '0'

config 'mount'
        option target    '/overlay'
        option uuid      ''
        option fstype    'ext4'
        option options   'rw,noatime'
        option enabled   '1'
        option is_rootfs '1'
FSTAB

/etc/init.d/fstab enable
reboot

Если extroot не подхватился (df -h показывает /dev/mtdblock6 на /overlay, а в logread есть UUID mismatch), удалите метку на флешке и перезагрузитесь:

☯

Terminal:

⌕

≡

✕
mount /dev/sda1 /mnt && rm -f /mnt/etc/.extroot-uuid && umount /mnt
reboot

Флешку не форматировать! Совет «пересоздать разделы в gparted» относится только к первичной настройке.

Совет на будущее: сохраните UUID, пока всё работает.

☯

Terminal:

⌕

≡

✕
cp /etc/config/fstab /overlay/fstab.extroot.bak
block info > /overlay/block-info.txt

5.3. Как сбросить правильно

Чистим только upper на флешке. Встроенный rootfs_data с fstab не трогаем, поэтому роутер снова загрузится с флешки, но в заводском состоянии.

1. Бэкап на компьютер:

☯

Terminal:

⌕

≡

✕
ssh root@192.168.1.1 'sysupgrade -b -' > rt-ac51u-config.tar.gz
ssh root@192.168.1.1 'tar -czf - -C /overlay/upper .' > rt-ac51u-upper.tar.gz

2. Отложить нужное в /overlay/keep. Этот каталог вне upper, поэтому не сотрётся. /tmp не подходит: он в RAM.

☯

Terminal:

⌕

≡

✕
mkdir -p /overlay/keep
cp -a /etc/dropbear/authorized_keys /overlay/keep/ 2>/dev/null
cp -a /etc/shadow /overlay/keep/shadow

3. Стереть верхний слой и сразу перезагрузиться. Именно reboot -f: система уже работает без своих файлов, и штатная перезагрузка может споткнуться.

☯

Terminal:

⌕

≡

✕
rm -rf /overlay/upper/* /overlay/upper/.[!.]* /overlay/work/*
sync
reboot -f

4. После загрузки роутер будет на 192.168.1.1, Wi-Fi выключен, а df -h по-прежнему показывает /dev/sda1 → /overlay:

☯

Terminal:

⌕

≡

✕
cp -a /overlay/keep/authorized_keys /etc/dropbear/
cp -a /overlay/keep/shadow /etc/shadow
ntpd -q -n -p pool.ntp.org
opkg update && opkg install block-mount kmod-usb-storage kmod-fs-ext4
# swap: сам файл /overlay/swapfile остался, вернуть автозапуск из раздела 4

Точечно, без полного сброса: rm /overlay/upper/<путь к файлу> и перезагрузка возвращают один файл к заводскому. opkg remove --autoremove <пакет> удаляет лишний пакет.

5.4. Из практики: чистка удалённого роутера

У меня роутер стоял удалённо, доступ был только через туннель (о нём ниже), а на нём накопились AmneziaWG, PBR, zapret и dnsmasq-full. Нужно было удалить всё лишнее и не потерять ни загрузку с флешки, ни доступ к роутеру.

Сначала я проверил, что загрузка не зависит от upper. Встроенный раздел примонтировал только на чтение:

☯

Terminal:

⌕

≡

✕
mkdir -p /tmp/int && mount -t jffs2 -o ro /dev/mtdblock6 /tmp/int
cat /tmp/int/upper/etc/config/fstab        # enabled '1', uuid флешки, is_rootfs '1'
ls /tmp/int/upper/sbin/block               # block-mount на месте
umount /tmp/int

Потом написал скрипт, который сохраняет «белый список» файлов (ключи, пароль, туннель, Wi-Fi, сеть, swap), стирает upper и возвращает список обратно:

☯

Terminal:

⌕

≡

✕
#!/bin/sh
set -e
U=/overlay/upper; K=/overlay/keep
mkdir -p $K; cd $U
LIST="etc/dropbear etc/config/dropbear etc/shadow etc/passwd etc/shadow- etc/passwd-
etc/config/dixsu etc/init.d/dixsu etc/rc.d/S99dixsu etc/rc.d/K10dixsu usr/bin/device_client
etc/rc.local etc/sysctl.d/99-swap.conf
etc/config/wireless etc/config/network etc/config/system"
EXIST=""
for p in $LIST; do [ -e "$p" ] || [ -L "$p" ] && EXIST="$EXIST $p"; done
tar -cf $K/keep.tar $EXIST
cd /
rm -rf $U/* $U/.[!.]* /overlay/work/* 2>/dev/null || true
tar -xf $K/keep.tar -C $U
sync
reboot -f

Запускал через setsid, чтобы обрыв SSH не прервал скрипт на полпути:

☯

Terminal:

⌕

≡

✕
setsid sh /overlay/backup/reset-upper.sh > /overlay/backup/reset.log 2>&1 < /dev/null &

Через ~3 минуты роутер вернулся сам: туннель поднялся, ключи, пароль, swap и Wi-Fi на месте, лишнего больше нет.

5.5. Чего не делать

ДействиеЧто будет
«Reset to defaults» в LuCI, firstboot -yextroot отваливается (5.2)
Форматировать флешку, пересоздавать разделытеряются все настройки, пакеты и swap
rm -rf /overlay/*сносит swap, keep/ и метку extroot. Чистить только upper/ и work/

6. Туннель без белого IP: свой клиент на Go

6.1. Задача

Роутер стоит за NAT провайдера (а у родственников ещё и за их роутером). Белого IP нет, пробрасывать порты негде. Нужен постоянный доступ к нему, а через него ко всей сети, из любого места.

Для этого я использую dix.su: устройство само устанавливает исходящее соединение с сервером, и через него доступны:

КаналКуда ведётКак пользоваться
https://.dix.suлокальный HTTP-порт устройства (у меня 80, LuCI)веб-интерфейс роутера в браузере
.dix.su:2222 (TCP)SSH роутера (dropbear)консоль и проход дальше в сеть (раздел 7)

6.2. Почему свой клиент

Штатный клиент dix.su написан на Elixir и собран в один файл через Burrito. На роутере он просто не помещается:

Штатный клиент (Elixir)Клиент на Go
Размер15 МБ + 57 МБ распакованного рантайма7.4–8.9 МБ, один статический файл
RAM~60 МБ (beam.smp)~7.5 МБ (замер на RT-AC51U), куча ограничена 24 МБ
Архитектурыx86_64, aarch64mipsel, mips, armv5, armv7, aarch64, x86_64
Зависимостинет (свой рантайм)нет (статический, без libc)

Протокол тот же (WebSocket + канал Phoenix proxy:), поэтому серверу всё равно, какой клиент подключён. Что умеет клиент:

  • WSS до proxy.dix.su:443, heartbeat каждые 25 с, переподключение через 5 с при любом обрыве;
  • HTTP-прокси на localhost: с заголовками Host, X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-For;
  • автопереход на HTTPS. Если локальный сервис отвечает редиректом на https:// того же хоста (так делает LuCI с redirect_https, многие NAS, Proxmox, принтеры), клиент сам переключается на https://localhost:443. На устройстве ничего менять не нужно, иначе получается бесконечный цикл редиректов;
  • большие загрузки (например, прошивка через LuCI) идут в локальный сервис потоком: в памяти лежит один чанк до 256 КБ;
  • TCP-туннели (SSH, VNC) с ожиданием медленного локального сервиса;
  • встроенные корневые сертификаты на случай, если в системе нет ca-bundle;
  • сквозное шифрование (E2E, раздел 6.8);
  • на устройствах с RAM ≤ 256 МБ куча автоматически ограничивается 24 МБ.

6.3. Для каких устройств подходит

Нужно ~10 МБ свободного места и ~15 МБ свободной RAM:

Устройство / SoCopkg print-architectureСборка
MediaTek MT7620/MT7621/MT7628/MT7688: Asus RT-AC51U, RT-N14U, Xiaomi 4A/4C/3G, TP-Link Archer C6 v3 / C50, Netgear R6220mipsel_24kcGOARCH=mipsle GOMIPS=softfloat
Atheros/QCA ath79: TP-Link Archer C7 / WDR3600 / WR841N, GL.iNet AR150/AR300Mmips_24kcGOARCH=mips GOMIPS=softfloat
Qualcomm IPQ40xx, Marvell mvebu: GL.iNet B1300, Linksys WRT, Asus RT-AC58Uarm_cortex-a7, arm_cortex-a9GOARCH=arm GOARM=7
Старые ARMv5 (kirkwood): Pogoplug, старые NASarm_xscale, arm_arm926ej-sGOARCH=arm GOARM=5
MediaTek Filogic, GL.iNet MT3000/Flint 2, NanoPi R4S/R5S, Raspberry Piaarch64_*GOARCH=arm64
x86 мини-ПК, виртуалкиx86_64GOARCH=amd64

Не подходит: устройства с 4–8 МБ флеша без USB (бинарник не поместится) и устройства с 32 МБ RAM (запустится, но впритык).

6.4. Сборка

☯

Terminal:

⌕

≡

✕
# Go без sudo
curl -sSL -o /tmp/go.tgz https://go.dev/dl/go1.27.2.linux-amd64.tar.gz
mkdir -p ~/go-sdk && tar -C ~/go-sdk --strip-components=1 -xzf /tmp/go.tgz
export PATH=$HOME/go-sdk/bin:$PATH

cd device_client_go
go test -race ./...           # интеграционный тест с фейковым Phoenix-сервером
VERSION=1.1.0-go ./build.sh   # → dist/

☯

Terminal:

⌕

≡

✕
8.8M  dist/device_client_linux_mipsle   GOARCH=mipsle GOMIPS=softfloat
8.9M  dist/device_client_linux_mips     GOARCH=mips   GOMIPS=softfloat
7.9M  dist/device_client_linux_armv7    GOARCH=arm    GOARM=7
7.9M  dist/device_client_linux_armv5    GOARCH=arm    GOARM=5
7.4M  dist/device_client_linux_arm64    GOARCH=arm64
8.0M  dist/device_client_linux_x86_64   GOARCH=amd64

Бинарники статические: CGO_ENABLED=0, -ldflags="-s -w", -trimpath. GOMIPS=softfloat обязателен: у роутерных MIPS нет FPU, и бинарник с hardfloat упадёт или будет крайне медленным.

6.5. Установка и ручная проверка

☯

Terminal:

⌕

≡

✕
# с компьютера; -O обязателен: на OpenWrt нет sftp-server, а новый OpenSSH без него не копирует
scp -O dist/device_client_linux_mipsle root@192.168.1.1:/tmp/device_client

☯

Terminal:

⌕

≡

✕
# на роутере
mv /tmp/device_client /usr/bin/device_client
chmod +x /usr/bin/device_client
device_client --version

LOG_LEVEL=debug device_client   80

☯

Terminal:

⌕

≡

✕
[info] device_client 1.1.0-go slug= local_port=80 server=proxy.dix.su:443 meta=true mem_limit=24MiB
[info] WebSocket ready, joining channel...
[info] Joined channel proxy:
[debug] -> GET / (Er5G5V8arso)
[info] local service redirects to HTTPS, switching to https://localhost:443
[debug] <- 200 Er5G5V8arso

Открываем https://.dix.su, видим форму входа LuCI. Работает. Ctrl+C, и переходим к службе.

> Грабли. Третий аргумент — это HTTP-порт. Я сначала указал 22 и получил malformed HTTP response "SSH-2.0-dropbear": HTTP-запросы ушли в SSH. SSH идёт отдельным TCP-туннелем, его порт задаёт сервер.

6.6. Служба автозапуска (procd)

Главное правило: init-скрипт универсальный, его не редактируют. Slug, токен и порт задаются только в /etc/config/dixsu. Если вписать значения прямо в config_get (config_get slug "$cfg" myslug), скрипт прочитает пустую строку и молча пропустит туннель. Я на это наступил.

☯

Terminal:

⌕

≡

✕
cat > /etc/init.d/dixsu <<'EOF'
#!/bin/sh /etc/rc.common
# dix.su device_client (Go) — procd init script for OpenWrt
# Config: /etc/config/dixsu (UCI, one "tunnel" section per slug)

START=99
STOP=10
USE_PROCD=1

NAME=dixsu
DEFAULT_BIN=/usr/bin/device_client

start_tunnel() {
        local cfg="$1"
        local enabled slug token port no_meta server_host server_port log_level bin e2e_token e2e_data_dir

        config_get_bool enabled "$cfg" enabled 0
        [ "$enabled" = "1" ] || return 0

        config_get slug        "$cfg" slug
        config_get token       "$cfg" token
        config_get port        "$cfg" port 80
        config_get_bool no_meta "$cfg" no_meta 0
        config_get server_host "$cfg" server_host proxy.dix.su
        config_get server_port "$cfg" server_port 443
        config_get log_level   "$cfg" log_level info
        config_get bin         "$cfg" binary "$DEFAULT_BIN"
        config_get e2e_token   "$cfg" e2e_token
        config_get e2e_data_dir "$cfg" e2e_data_dir "/etc/dixsu/e2e_$slug"

        if [ -z "$slug" ] || [ -z "$token" ]; then
                logger -t "$NAME" "section '$cfg': slug/token not set, skipped"
                return 0
        fi
        if [ ! -x "$bin" ]; then
                logger -t "$NAME" "section '$cfg': binary $bin not found or not executable"
                return 0
        fi

        procd_open_instance "$slug"
        # токен — через окружение, чтобы он не светился в ps
        procd_set_param command "$bin" "$slug" "$port"
        [ "$no_meta" = "1" ] && procd_append_param command --no-meta
        procd_set_param env DIXSU_TOKEN="$token" SERVER_HOST="$server_host" SERVER_PORT="$server_port" LOG_LEVEL="$log_level"
        if [ -n "$e2e_token" ]; then
                mkdir -p "$e2e_data_dir" && chmod 700 "$e2e_data_dir"
                procd_append_param env E2E_TOKEN="$e2e_token" E2E_DATA_DIR="$e2e_data_dir"
        fi
        # перезапуск бесконечно: порог 3600 с, задержка 5 с, 0 = без лимита попыток
        procd_set_param respawn 3600 5 0
        procd_set_param stdout 1
        procd_set_param stderr 1
        procd_close_instance
}

start_service() {
        config_load "$NAME"
        config_foreach start_tunnel tunnel
}

service_triggers() {
        procd_add_reload_trigger "$NAME"
}
EOF
chmod +x /etc/init.d/dixsu

Конфиг:

☯

Terminal:

⌕

≡

✕
cat > /etc/config/dixsu <<'EOF'
config tunnel 'main'
        option enabled '1'
        option slug ''
        option token ''
        # локальный HTTP-порт, куда идут запросы с поддомена (80 = LuCI)
        option port '80'
        option server_host 'proxy.dix.su'
        option server_port '443'
        # option log_level 'info'      # debug — каждый HTTP-запрос, warn — только проблемы
        # option e2e_token ''          # E2E, раздел 6.8
EOF
chmod 600 /etc/config/dixsu

/etc/init.d/dixsu enable
/etc/init.d/dixsu restart
logread -e device_client | tail -20

Токен передаётся через переменную окружения DIXSU_TOKEN, а не аргументом, иначе он виден любому, кто запустит ps. Несколько туннелей (разные slug или порты) — несколько секций config tunnel '<имя>'. После изменения конфига: /etc/init.d/dixsu reload.

Где что лежит в итоге:

ПутьЧто это
/usr/bin/device_clientбинарник
/etc/init.d/dixsuinit-скрипт procd
/etc/config/dixsuнастройки, содержит токен
/etc/rc.d/S99dixsu, K10dixsuссылки автозапуска (enable)
/overlay/upper/...физически все три файла лежат на флешке, поэтому переживают перезагрузку, но пропадают при сбросе upper

6.7. Сколько памяти он ест на самом деле

Открываю top и пугаюсь:

☯

Terminal:

⌕

≡

✕
 4778     1 root     S     559m1013%   0% /usr/bin/device_client ...

1013% от 55 МБ физически невозможно. BusyBox top показывает VSZ: рантайм Go заранее резервирует сотни мегабайт виртуальных адресов под кучу и стеки, но физическую память под них не выделяет. Реальное потребление нужно смотреть в /proc:

☯

Terminal:

⌕

≡

✕
root@OpenWrt:~# grep -E 'VmRSS|VmSwap|VmSize' /proc/$(pidof device_client)/status
VmSize:   572556 kB     ← виртуальная память, то же, что в top
VmRSS:      7664 kB     ← реально в RAM: ~7.5 МБ
VmSwap:        4 kB     ← в swap почти ничего

С запущенным туннелем на роутере остаётся ~13 МБ available.

6.8. Сквозное шифрование (E2E)

В обычном режиме TLS до браузера терминирует сервер dix.su. В режиме E2E сервер видит только SNI и пересылает зашифрованные байты, а TLS терминирует сам клиент на роутере своим сертификатом Let's Encrypt для e2e-.dix.su. Ключ не покидает устройство.

☯

Terminal:

⌕

≡

✕
uci set dixsu.main.e2e_token=''
uci commit dixsu
/etc/init.d/dixsu restart
logread -e device_client | grep E2E

☯

Terminal:

⌕

≡

✕
[info] E2E: enabled for e2e-.dix.su, data dir /etc/dixsu/e2e_
[info] E2E: certificate for e2e-.dix.su issued, valid until ...
[info] E2E: listening 127.0.0.1:8443 for e2e-.dix.su -> localhost:80

Первый выпуск сертификата занимает 10–60 с, дальше он лежит на диске и продлевается сам. Через E2E работает и WebSocket (ttyd, Home Assistant, noVNC), которого нет в обычном HTTP-туннеле.

6.9. Частые проблемы

СимптомПричинаРешение
SSL verify error, x509: certificate ... not yet validневерное время (нет RTC)ntpd -q -n -p pool.ntp.org
scp: ... sftp-server: not foundновый OpenSSH копирует через SFTPscp -O
upgrade failed: HTTP 403неверный slug или токенпроверить в кабинете dix.su
slug/token not set, skippedзначения вписаны в init-скрипт, а не в конфигзначения только в /etc/config/dixsu
malformed HTTP response "SSH-2.0-dropbear"HTTP-порт = 22HTTP-порт — 80, SSH идёт отдельно
«Слишком много перенаправлений»старая версия клиента + redirect_https в uhttpdклиент ≥ 1.0.2-go
top: 500+ МБ и >1000%это VSZ, а не реальная памятьVmRSS в /proc//status
exec format error / Illegal instructionне та архитектураopkg print-architecture: mipsel ≠ mips

6.10. Ограничения

  • WebSocket через обычный HTTP-туннель не работает: протокол передаёт только запрос и ответ. ttyd, Home Assistant и noVNC через поддомен не заработают (или заработают частично). Выход — E2E или ssh -L (раздел 7).
  • Нет управления потоком для TCP-туннелей: медленный локальный сервис притормаживает все сессии устройства. На практике один длинный поток (scp большого файла) через туннель у меня обрывался примерно на 2 МБ, а куски по 1 МБ проходили. Большие файлы лучше копировать частями.
  • В режиме E2E реальный IP клиента локальному сервису не передаётся.

7. Что делать с таким роутером

Главная мысль: слабый роутер с туннелем — это постоянно включённая точка входа в сеть, где он стоит. Вычислять и шифровать он не умеет, зато дотягивается до всего, что за ним.

Сначала ресурсы. После установки туннеля:

ЗанятоСвободно
RAM~25 МБ, из них туннель ~7.5 МБ~10–14 МБ + swap 512 МБ
CPUload ~0.05почти весь
Флешка~560 МБ~2.9 ГБ

Отсюда правило: любой сервис должен занимать единицы мегабайт RAM и не шифровать трафик в userspace на скоростях выше нескольких Мбит/с.

7.1. SSH во всю домашнюю сеть

Это основа всего остального. В ~/.ssh/config на своём компьютере:

☯

Terminal:

⌕

≡

✕
Host home-router
    HostName .dix.su
    Port 2222
    User root
    ProxyCommand bash -c '{ echo DIXSU:; cat; } | nc %h %p'
    ServerAliveInterval 30

Host home-pc
    HostName 192.168.1.50          # адрес ПК в сети роутера
    User user
    ProxyJump home-router

☯

Terminal:

⌕

≡

✕
ssh home-router                    # консоль роутера
ssh home-pc                        # сразу на домашний ПК, через роутер
scp -O файл home-router:/tmp/

Веб-интерфейсы устройств (NAS, камера, принтер, роутер провайдера):

☯

Terminal:

⌕

≡

✕
ssh -N -L 8080:192.168.1.20:80   home-router    # → http://localhost:8080
ssh -N -L 8443:192.168.0.1:443   home-router    # роутер провайдера → https://localhost:8443
ssh -N -L 3389:192.168.1.50:3389 home-router    # RDP на ПК → localhost:3389

«Быть дома» из браузера. Команда ssh -N -D 1080 home-router поднимает SOCKS5-прокси. В отдельном профиле браузера укажите 127.0.0.1:1080 и «DNS через прокси»: все устройства домашней сети откроются по их адресам, а в интернет вы выйдете с домашнего IP.

Проброс портов в dropbear разрешён по умолчанию. Скорость ограничена туннелем и CPU роутера: это для управления, не для видео.

7.2. Wake-on-LAN: разбудить домашний ПК

☯

Terminal:

⌕

≡

✕
opkg install etherwake
cat > /usr/bin/wake-pc <<'EOF'
#!/bin/sh
etherwake -i br-lan AA:BB:CC:DD:EE:FF && echo "magic packet sent"
EOF
chmod 755 /usr/bin/wake-pc

С ноутбука в кафе: ssh home-router wake-pc, через минуту ssh home-pc или RDP через ssh -L. На ПК нужно включить WoL в BIOS/UEFI и в свойствах сетевой карты. Работает по кабелю, по Wi-Fi обычно нет.

7.3. Удалённая помощь родственникам

На мой взгляд, самый полезный сценарий. Роутер подключается WAN-портом к их роутеру (именно так он у меня и стоит, за чужим 192.168.0.1), и вы получаете постоянный доступ в их сеть:

  • зайти в их основной роутер (ssh -L 8443:192.168.0.1:443 home-router), поправить Wi-Fi, DNS, перезагрузить;
  • посмотреть, какие устройства в сети и что с ними;
  • проверить «интернет не работает» из их сети: ping, mtr, nslookup;
  • дойти до их ПК или телевизора.

Им ничего делать не нужно: ни TeamViewer, ни «продиктуй цифры с экрана». Перед отправкой включите SSH только по ключу, задайте сильный пароль root и проверьте автозапуск туннеля перезагрузкой.

7.4. Узнать, что дома пропал свет или интернет

Самый простой индикатор: если ssh home-router не подключается или устройство на dix.su ушло в offline, дома нет питания или интернета. После их появления туннель поднимается сам за 1–3 минуты.

Уведомления в Telegram, если состояние интернета или устройств изменилось (бот создаётся у @BotFather):

☯

Terminal:

⌕

≡

✕
mkdir -p /etc/watchdog
cat > /etc/watchdog/tg.conf <<'EOF'
TOKEN='123456:ABC...'
CHAT='123456789'
HOSTS='192.168.1.50 192.168.1.20'
EOF
chmod 600 /etc/watchdog/tg.conf

cat > /usr/bin/netwatch <<'EOF'
#!/bin/sh
. /etc/watchdog/tg.conf
S=/tmp/netwatch; mkdir -p $S
send() { wget -qO /dev/null --post-data "chat_id=$CHAT&text=$1" "https://api.telegram.org/bot$TOKEN/sendMessage"; }
check() {
  if sh -c "$2" >/dev/null 2>&1; then new=up; else new=down; fi
  old=$(cat $S/$1 2>/dev/null)
  [ "$new" != "$old" ] && echo $new > $S/$1 && [ -n "$old" ] && send "$(uci get system.@system[0].hostname): $1 $new"
}
check internet "ping -c 2 -W 3 77.88.8.8"
for h in $HOSTS; do check "$h" "ping -c 2 -W 2 $h"; done
EOF
chmod 755 /usr/bin/netwatch
echo '*/5 * * * * /usr/bin/netwatch' >> /etc/crontabs/root
/etc/init.d/cron enable; /etc/init.d/cron restart

Сообщение о пропаже интернета придёт, когда он вернётся (пока его нет, отправить нечего). Если api.telegram.org недоступен, подойдут ntfy.sh, e-mail через msmtp или webhook на свой сервер.

7.5. Диагностика сети на месте

☯

Terminal:

⌕

≡

✕
opkg install iperf3 mtr-nojson tcpdump-mini
mtr -rwc 20 ya.ru                 # где теряются пакеты
iperf3 -s                         # замер Wi-Fi/LAN с ноутбука в этой сети
tcpdump -ni br-lan port 53        # кто и что спрашивает у DNS
cat /tmp/dhcp.leases              # кто подключён

Статистика трафика по дням и месяцам почти без нагрузки: vnstat2 + luci-app-vnstat2.

7.6. Гостевая сеть и сеть для умных устройств

Два диапазона позволяют сделать гостевую сеть, изолированную от основной, и отдельную сеть на 2.4 ГГц для ламп, розеток и камер, которым незачем видеть ваш ПК. В LuCI: Network → Wireless → Add → новый интерфейс и своя зона firewall с forwarding только в wan. Нагрузки почти никакой.

7.7. Блокировка рекламы через DNS

adblock-lean — это shell-скрипт без своего демона: грузит список в dnsmasq. Каждые ~10 тыс. доменов добавляют dnsmasq примерно 1 МБ. На 64 МБ держите список в пределах ~50–100 тыс. доменов (например, hagezi «light»). AdGuard Home (~50+ МБ) сюда не влезет.

7.8. MQTT для самодельных датчиков

☯

Terminal:

⌕

≡

✕
opkg install mosquitto-nossl mosquitto-client-nossl    # брокер ~1–2 МБ RAM
/etc/init.d/mosquitto enable; /etc/init.d/mosquitto start
mosquitto_sub -h 127.0.0.1 -t 'home/#' -v

Датчики на ESP8266/ESP32 шлют сюда температуру, влажность, протечку. Снаружи брокер доступен через ssh -L 1883:127.0.0.1:1883 home-router. Простые автоматизации вроде «протечка → Telegram» пишутся shell-скриптом. Home Assistant здесь не запустится.

7.9. Принтер и общая папка

⚠ Единственный USB-порт занят флешкой с системой, поэтому принтер или диск подключаются только через USB-хаб с внешним питанием.

  • Принт-сервер: opkg install p910nd kmod-usb-printer luci-app-p910nd. На ПК принтер добавляется как «TCP/IP, RAW, порт 9100».
  • Общая папка: opkg install ksmbd-server luci-app-ksmbd (SMB в ядре, легче Samba), папка /overlay/share. Скорость несколько МБ/с: для документов да, для фильмов нет.

7.10. Обход блокировок: что тянет это железо

ВариантНагрузкаИтог
zapret (обход DPI локально, без VPS)низкая✅ лучший вариант для такого железа
youtubeUnblock (DPI только для YouTube)низкая✅ если нужен только YouTube
AmneziaWG до своего VPSшифрование в ядре⚠ ожидаемо 10–20 Мбит/с, нормально для 1–2 устройств
WDTT (через TURN-серверы VK)очень высокая❌ проверено, см. раздел 8

7.11. Для чего не годится

ЗадачаПочему
VPN на всю сеть (WDTT, OpenVPN, тяжёлые sing-box/Xray)одно слабое ядро, шифрование в userspace
Медиасервер, торрентыCPU, RAM, USB 2.0, износ флешки
Docker, Home Assistant, AdGuard Homeнужны сотни МБ RAM
Основной роутер для гигабитного тарифапорты 100 Мбит/с

8. Что не взлетело

Ради интереса я попробовал WDTT — WireGuard, завёрнутый в DTLS и пущенный через TURN-серверы VK, так что трафик выглядит как аудиозвонок. Клиент для OpenWrt существует, но рассчитан на aarch64 с 256–512 МБ RAM.

Что получилось:

  • под MIPS демон пришлось пересобрать и исправить: на 32-битных платформах он падал с panic: unaligned 64-bit atomic operation. Помогла перестановка полей в одной структуре;
  • только туннель работал: подключение за ~25 с, 8–12 МБ RAM, но скорость ~1.2 Мбит/с и ping до 1.7 с;
  • выборочная маршрутизация (~1200 заблокированных доменов через dnsmasq-full и nftables) тоже заработала, но под нагрузкой роутер трижды намертво завис и перезагрузился, load average доходил до 8.5. Instagram с телефона всё равно тормозил.

Два урока, которые пригодятся при любой удалённой настройке:

  1. Перед рискованным шагом (firewall reload, network reload, замена dnsmasq) ставьте «мёртвую руку»: таймер, который сам откатит изменение, если связь пропадёт. В BusyBox нет nohup, зато есть start-stop-daemon: ``sh cat > /tmp/rollback.sh <<'EOF' #!/bin/sh sleep 600 # здесь команды отката EOF chmod 755 /tmp/rollback.sh start-stop-daemon -S -b -m -p /tmp/rollback.pid -x /tmp/rollback.sh # всё хорошо — отменить: kill $(ps w | awk '/[s]leep 600/{print $1}') ` Таймер в /tmp` перезагрузку не переживёт, поэтому для сервисов с автозапуском нужен ещё и файл-предохранитель на флешке, который запрещает автостарт после перезагрузки.
  2. Пишите лог на флешку, иначе после зависания причину не найти: ``sh mkdir -p /overlay/log uci set system.@system[0].log_file='/overlay/log/messages' uci set system.@system[0].log_size='512' uci commit system; /etc/init.d/log restart ` При extroot служба лога после загрузки сама в файл не пишет, поэтому добавьте /etc/init.d/log restart в /etc/rc.local перед exit 0`.

Вывод: для VPN на всю сеть нужен другой роутер или клиент прямо на телефоне. RT-AC51U остался тем, что у него получается хорошо: дверью в сеть.


9. Безопасность

Роутер с туннелем — это вход во всю сеть за ним. Отнеситесь к этому серьёзно:

  • LuCI виден из интернета. При port 80 страница входа доступна любому, кто знает адрес https://.dix.su. Нужен сильный пароль root. Если LuCI снаружи не нужен, ходите в него через ssh -L 8080:127.0.0.1:80 home-router.
  • SSH только по ключу. Сначала проверьте, что вход по ключу работает, потом: ``sh uci set dropbear.@dropbear[0].PasswordAuth='off' uci set dropbear.@dropbear[0].RootPasswordAuth='off' uci commit dropbear; /etc/init.d/dropbear restart ``
  • Токен туннеля лежит в /etc/config/dixsu (сделайте chmod 600) и попадает в бэкапы sysupgrade -b. Не выкладывайте бэкапы и не вставляйте токены в чаты и скриншоты. Если токен засветился, перевыпустите его.
  • Не ставьте лишнего, обновляйте пакеты (opkg list-upgradable). Для роутера у родственников используйте отдельный slug и отдельный ключ.

10. Итоги

За вечер роутер, который пылился в шкафу, стал полезным: 3–5 Вт, доступ ко всей домашней сети из любого места, без белого IP, без проброса портов, без VPN на основном роутере. Свободной памяти хватает ещё на несколько лёгких сервисов.

С чего начать, по пользе и простоте:

  1. Время и opkg update (раздел 2), swap (раздел 4).
  2. Туннель и служба (раздел 6).
  3. ~/.ssh/config с ProxyJump (раздел 7.1): пять минут, и вся домашняя сеть под рукой.
  4. Wake-on-LAN (7.2) и уведомления о пропаже света и интернета (7.4).
  5. Гостевая и IoT-сеть (7.6).
  6. Если нужен обход блокировок: zapret, а не VPN (7.10).

И главное: не жмите «Reset to defaults» на роутере с extroot.


Проверено на OpenWrt 24.10.5, Asus RT-AC51U. Предыдущая часть: Руководство по установке OpenWrt на ASUS RT-AC51U с extroot.

Roman Sakhno

Копирование материалов разрешается только с указанием автора Роман Сахно и индексируемой прямой ссылкой на сайт (http://itdid.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/sahroman.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/sahroman.

Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

  1. Кнопка:

    Она выглядит вот так: Как настроить свой компьютер

  2. Текстовая ссылка:

    Она выглядит вот так: Как настроить свой компьютер

  3. BB-код ссылки для форумов (например, можете поставить её в подписи):

Комментарии (0):

Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.