LuCI-интерфейс для sing-box на OpenWrt —
настройка формами, без правки JSON руками, без bash-обвязки, без fw3.
Бэкенд генерирует config.json напрямую из секций UCI.
⚠️ Статус: ранняя разработка (0.1.x). Проект сырой — что-то может ломаться, схема UCI и API ещё устаканиваются. Предложения, хотелки и баг-репорты — в issues или PR, буду рад.
LuCI web UI for sing-box on OpenWrt.
Configure inbounds/outbounds with forms — no hand-edited JSON, no bash glue,
no fw3. The backend (written in ucode) generates sing-box config.json
directly from UCI sections. The full README below is in Russian; the technical
reference in docs/ is mostly English.
⚠️ Status: early development (0.1.x). Rough edges expected; the UCI schema and RPC API are still settling.
Features
- Inbounds:
tproxy,mixed(socks/http),direct, plus server-sidevless/trojan/hysteria2/shadowsocks. - Proxy outbounds:
vless,trojan,hysteria2,shadowsocks,direct, with shared TLS (uTLS / ALPN / Reality / ECH), multiplex, transports (ws / grpc / http / httpupgrade / xhttp) and dial blocks. - Share-link import (
vless://,ss://,trojan://,hy2://), per-section JSON import/export (export masks secrets), subscriptions and rule-sets with.srs/.jsonauto-detect, and live monitoring via the Clash API. - Russian translation bundled. After install the page appears under Services → Singbox-UI.
Install (OpenWrt 25.12+, apk). The project ships as four noarch packages (plus an optional AWG/WARP plugin) —
singbox-ui (backend; bundles the pure-ucode bbolt cache.db reader),
luci-app-singbox-ui (LuCI UI) and luci-i18n-singbox-ui-ru (translation).
You install the UI; apk resolves the backend as a dependency. The easiest path
is the installer, which detects the arch via
apk --print-arch, adds the signed GitHub Pages apk feed (repo + signing
key) and runs apk add:
wget -O- https://raw.githubusercontent.com/janeblower/luci-singbox/main/install.sh | shAdd the signed apk feed manually (what the installer automates;
auto-updates with apk upgrade):
ARCH=$(apk --print-arch)
wget -O /etc/apk/keys/luci-singbox.pem https://janeblower.github.io/luci-singbox/luci-singbox.pem
echo "https://janeblower.github.io/luci-singbox/25.12/$ARCH/luci-singbox/packages.adb" > /etc/apk/repositories.d/luci-singbox.list
apk update && apk add luci-app-singbox-ui luci-i18n-singbox-ui-ruOr install the unsigned release assets manually — grab the four noarch packages (plus singbox-ui-plugin-awg_warp.apk if you want the AWG/WARP plugin)
from the latest release and let apk resolve the order:
apk add --allow-untrusted \
./singbox-ui.apk \
./luci-app-singbox-ui.apk ./luci-i18n-singbox-ui-ru.apk
⚠️ Conflicts withfirewall(fw3). Thesingbox-uibackend drives nftables directly and is meant to replace fw3 — its package declares an apk conflict withfirewalland prints a pre-install warning. apk will offer to removefirewallon install, but confirm nothing else depends on fw3 first; if it lingers it will clobber the singbox-ui nftables ruleset on its next reload, so stop and remove it yourself if unsure:/etc/init.d/firewall stop && /etc/init.d/firewall disable apk del firewall # only if nothing else needs fw3Only
apkis supported; there is noopkg/.ipkbuild.
⚠️ TPROXY requires anip ruleyou must add yourself. The ruleset marks packets withfwmark(default0x40000000/0x40000000, UCI optionssingbox-ui.@global[0].fwmark/fwmark_mask), but the package does not install the policy route that delivers them to the local TPROXY socket. Add, for both families:ip -4 rule add fwmark 0x40000000/0x40000000 lookup 100 ip -4 route add local default dev lo table 100 ip -6 rule add fwmark 0x40000000/0x40000000 lookup 100 ip -6 route add local default dev lo table 100After applying a ruleset the service logs a warning if no matching
ip rule fwmark…is found — checklogread -e singbox-ui. See the Russian section «fwmark иip ruleдля TPROXY» below for details and how to use a different mark bit.Per-inbound override: if a tproxy inbound has UCI
option fwmarkset, that value overrides the global mark for that inbound's ruleset — theip rulemust then use the per-inbound value, not the global one.
- Inbound:
tproxy,mixed(socks/http),direct, а также серверныеvless/trojan/hysteria2/shadowsocks. - Outbound (прокси):
vless,trojan,hysteria2,shadowsocks,direct. Маршрутизация, DNS и rule-set’ы — отдельными вкладками. - Общие блоки протоколов: TLS (uTLS / ALPN / Reality / ECH), multiplex, транспорты (ws / grpc / http / httpupgrade / xhttp), dial-опции.
- Импорт share-link:
vless://,ss://,trojan://,hy2://(с поддержкой IPv6-литералов и секретов с двоеточием). - Импорт/экспорт JSON по секциям; экспорт скрывает секреты.
- Подписки: параллельный фетч подписок и rule-set’ов с авто-детектом
.srs/.json, лимитом размера тела и кэшем «последнего рабочего»; корректный User-Agent для провайдеров за DDoS-Guard. - TProxy на nftables — правила собираются одной атомарной транзакцией
nft -f. Пакет конфликтует сfirewall(fw3). - Мониторинг живых соединений через Clash API.
- Русский перевод в комплекте.
После установки страница появляется в Services → Singbox-UI.
- ucode вместо bash. Генератор конфига, фетчер подписок и эмиттер
nftables написаны на нативном для OpenWrt
ucode, а не на shell. - Полный контроль без ручного JSON. Через UCI доступны основные поля inbound/outbound sing-box (multiplex, транспорты, uTLS, ALPN, masquerade и т.д.) — JSON руками писать не нужно. Если очень хочется — есть кнопка импорта JSON.
- Прямой nftables, без fw3. Маркировка трафика собирается атомарной
nft-транзакцией; пакет специально конфликтует с
firewall. - Всё через UCI/LuCI. Один файл
/etc/config/singbox-ui— источник правды; LuCI-страница это форма поверх него с импортом share-link’ов и JSON-узлов. - Подписки + Clash API. Авто-детект формата rule-set’ов и кэш рабочей версии; вкладка мониторинга показывает соединения в реальном времени.
Проект ставится четырьмя noarch-пакетами (OpenWrt 25.12+, apk):
singbox-ui (бэкенд; включает bbolt-ридер cache.db на чистом ucode),
luci-app-singbox-ui (LuCI-UI) и luci-i18n-singbox-ui-ru (перевод).
Ставишь UI — apk сам подтягивает бэкенд как зависимость.
Проще всего через установщик: он определяет арку (apk --print-arch),
подключает подписанный apk-feed (репозиторий + ключ) и зовёт apk add:
wget -O- https://raw.githubusercontent.com/janeblower/luci-singbox/main/install.sh | shПодключить подписанный apk-feed вручную (то, что делает установщик;
обновляется через apk upgrade):
ARCH=$(apk --print-arch)
wget -O /etc/apk/keys/luci-singbox.pem https://janeblower.github.io/luci-singbox/luci-singbox.pem
echo "https://janeblower.github.io/luci-singbox/25.12/$ARCH/luci-singbox/packages.adb" > /etc/apk/repositories.d/luci-singbox.list
apk update && apk add luci-app-singbox-ui luci-i18n-singbox-ui-ruИли поставить неподписанные ассеты релиза вручную — скачай четыре noarch-пакета из последнего релиза и дай apk разрулить порядок:
apk add --allow-untrusted \
./singbox-ui.apk \
./luci-app-singbox-ui.apk ./luci-i18n-singbox-ui-ru.apkБэкенд singbox-ui конфликтует с firewall (fw3), потому что управляет
nftables напрямую и задуман как замена fw3 — пакет объявляет apk-конфликт с
firewall и печатает предупреждение перед установкой. apk предложит удалить
firewall при установке, но сперва убедитесь, что от fw3 ничего не зависит;
если он останется, на очередном reload затрёт nftables-правила singbox-ui —
тогда остановите и удалите его вручную:
/etc/init.d/firewall stop && /etc/init.d/firewall disable
apk del firewall # только если fw3 больше никому не нуженСборка из исходников — scripts/build-apk.sh <version> поверх OpenWrt SDK.
opkg/.ipk. Толькоapk. Старые ветки OpenWrt с opkg — мимо.fw3/ iptables. Пакет намеренно конфликтует сfirewallи совмещаться с ним не будет.
Ruleset помечает пакеты, которые должен перехватить TPROXY-сокет sing-box.
Значение метки берётся из UCI-опций singbox-ui.@global[0].fwmark /
fwmark_mask (по умолчанию 0x40000000 / 0x40000000).
Чтобы помеченные пакеты дошли до локального сокета, ядру нужны ip rule и
таблица маршрутизации:
ip -4 rule add fwmark 0x40000000/0x40000000 lookup 100
ip -4 route add local default dev lo table 100
ip -6 rule add fwmark 0x40000000/0x40000000 lookup 100
ip -6 route add local default dev lo table 100Пакет эти правила не ставит — это состояние оператора (обычно приходит
из network-конфига UCI, стартового скрипта или пакетов вроде
mwan3 / vpn-policy-routing). После применения ruleset’а сервис пишет в
syslog предупреждение, если подходящего ip rule fwmark… нет — проверяйте
logread -e singbox-ui после включения tproxy-inbound.
Переопределение на уровне inbound: если у tproxy-inbound выставлена UCI-опция
option fwmark, она переопределяет глобальную метку для этого inbound-а — в
этом случае ip rule должен использовать именно это значение, а не глобальное.
Другой бит метки (например, если 0xff00 занят mwan3):
uci set singbox-ui.@global[0].fwmark='0x10000'
uci set singbox-ui.@global[0].fwmark_mask='0x10000'
uci commit singbox-ui
/etc/init.d/singbox-ui restartПосле этого поправьте ip rule. Инвариант, который проверяет валидатор:
(fwmark & fwmark_mask) == fwmark; при нарушении — откат к 0x40000000 / 0x40000000
с записью в лог.
По умолчанию перехватывается только трафик из LAN (цепочка prerouting).
Чтобы пропускать через TPROXY ещё и трафик процессов самого роутера
(health-check’и, OpenVPN-клиенты и т.п.):
uci set singbox-ui.@global[0].redirect_router_traffic='1'
uci commit singbox-ui
/etc/init.d/singbox-ui restartДобавляется цепочка output (priority mangle), зеркалящая логику
prerouting. По умолчанию выключено — процессы роутера обычно проксировать
не нужно.
sh tests/run-vm.shПоднимает реальный OpenWrt 25.12.3 под QEMU/KVM из заранее собранного
Docker-образа (ghcr.io/<owner>/luci-singbox/openwrt-test) и гоняет
backend- и parity-тесты (bun) внутри гостя. На хосте нужны docker
и доступный на запись /dev/kvm (на GitHub Actions есть из коробки).
Переопределить образ (например, локальный dev-тег из tests/docker/):
SINGBOX_TEST_IMAGE=singbox-test:dev sh tests/run-vm.shТехнический справочник — в каталоге docs/:
uci-schema.md (схема UCI), protocol-coverage.md и
protocol-descriptors.md (протоколы и дескрипторы), plugins.md
(плагины), release.md (релизный процесс).
- Допил до полной юзабельности — оставшиеся поля протоколов, UX импорта/экспорта, валидация, тесты.
- Easy-mode в духе podkop: поверх «полного» интерфейса — упрощённый режим с пресетами и импортом подписки одним полем. «Полный» режим при этом остаётся.
GPL-2.0-or-later.