Аварийный удалённый доступ к офисному роутеру (OpenWrt 25.12.x, nftables/firewall4) через WireGuard — независимый от основного туннеля селективной маршрутизации (Podkop / VLESS Reality), работающего на том же устройстве.
- Задача
- Архитектурные решения
- Схема
- 1. Пакеты и ключи
- 2. Интерфейс wg0
- 3. Firewall
- 4. Совместимость с Podkop
- 5. Клиентская сторона
- 6. Управление клиентами
- 7. Troubleshooting
- Стек
- Безопасность
Офисный роутер выполняет селективную маршрутизацию части трафика через Podkop (VLESS+Reality → 3x-ui на VPS). Если прокси-цепочка на роутере сбоит или требует переконфигурации, а физического доступа к устройству нет — нужен независимый канал для входа в LuCI/SSH.
Решение: WireGuard-туннель напрямую на статический публичный IP офиса. Даже при полностью неработающем Podkop административный доступ сохраняется.
WireGuard, а не публичный SSH. Открытый SSH-порт на WAN — постоянная поверхность атаки (брутфорс, сканеры). WireGuard не отвечает на пакеты без валидного ключа (silent drop): сервис извне практически невидим.
Split-tunnel, а не full-tunnel. AllowedIPs ограничен туннельной подсетью и LAN офиса. Остальной интернет-трафик клиента идёт напрямую — без лишней нагрузки на офисный канал и без риска непреднамеренно завернуть личный трафик через рабочую инфраструктуру.
Отдельный preshared key на каждого клиента. PSK — дополнительный симметричный слой поверх асимметричной пары ключей. Общий PSK означал бы, что компрометация одного устройства ослабляет защиту соединений всех остальных.
WireGuard как отдельный интерфейс, а не поверх Podkop. Частая причина обращения к роутеру — сбой самого прокси. Административный канал обязан жить независимо от его состояния.
Клиент (админ) ──WireGuard (UDP/51820)──► Публичный статический IP ──► wg0 (10.10.10.1/24) ──► LAN (192.168.1.0/24)
Внутри роутера WireGuard (wg0) и Podkop/Xray — два независимых контура (интерфейсы + nftables-цепочки), которые не пересекаются.
Адресация:
| Узел | Адрес |
|---|---|
| Туннельная подсеть | 10.10.10.0/24 |
| Роутер (сервер) | 10.10.10.1 |
| Клиенты | 10.10.10.2, 10.10.10.3, … |
| LAN офиса | 192.168.1.0/24 |
На роутере (по SSH из локальной сети):
apk add wireguard-tools luci-proto-wireguard
mkdir -p /etc/wireguard && cd /etc/wireguard
umask 077
# Ключи сервера (роутера)
wg genkey | tee server_private.key | wg pubkey > server_public.key
# Ключи первого клиента
wg genkey | tee client_private.key | wg pubkey > client_public.key
# Pre-shared key (уникальный для этой пары)
wg genpsk > preshared.keyumask 077 гарантирует, что файлы ключей будут читаемы только root.
UCI не выполняет подстановку $(...) внутри конфигов — значения ключей подставляются через shell-переменные в heredoc:
SERVER_PRIV=$(cat /etc/wireguard/server_private.key)
CLIENT_PUB=$(cat /etc/wireguard/client_public.key)
PSK=$(cat /etc/wireguard/preshared.key)
cat >> /etc/config/network << EOF
config interface 'wg0'
option proto 'wireguard'
option private_key '$SERVER_PRIV'
option listen_port '51820'
list addresses '10.10.10.1/24'
config wireguard_wg0
option description 'admin-remote'
option public_key '$CLIENT_PUB'
option preshared_key '$PSK'
list allowed_ips '10.10.10.2/32'
option route_allowed_ips '1'
EOF
/etc/init.d/network restartПроверка:
ip addr show wg0 # интерфейс UP, адрес 10.10.10.1/24
wg show # публичный ключ сервера, порт 51820, peer клиентаВнимание: блок
cat >> ...дописывает в конец файла. Повторный запуск создаст дубликат секций — см. Troubleshooting.
cat >> /etc/config/firewall << 'EOF'
config zone
option name 'wireguard'
option input 'ACCEPT'
option output 'ACCEPT'
option forward 'ACCEPT'
list network 'wg0'
config forwarding
option src 'wireguard'
option dest 'lan'
config forwarding
option src 'lan'
option dest 'wireguard'
config rule
option name 'Allow-WireGuard'
option src 'wan'
option proto 'udp'
option dest_port '51820'
option target 'ACCEPT'
EOF
/etc/init.d/firewall restartПравило Allow-WireGuard обязательно: без него входящие UDP-пакеты на 51820 из WAN отбрасываются и подключение не установится.
Проверка, что правило применилось в nftables:
nft list ruleset | grep -B2 -A2 "51820"Ожидаемая строка:
udp dport 51820 counter packets 0 bytes 0 accept comment "!fw4: Allow-WireGuard"
Счётчик packets 0 до первого подключения клиента — норма.
Podkop создаёт собственную nftables-таблицу PodkopTable и матчит трафик по:
iifname @interfaces— исходящий трафик LAN-клиентов;ip daddrв сетахpodkop_subnets/podkop_discord_subnets/ диапазоне FakeIP198.18.0.0/15.
Входящий WireGuard-трафик на порт 51820 приходит с WAN на сам роутер и под эти условия не попадает — конфликта нет. Проверка перед настройкой:
ip rule show # только local/main/default — ОК
ip route show table all | grep -i podkop # пусто: отдельных таблиц маршрутизации нет
nft list ruleset | grep -i -A5 podkop # нет правил на udp dport 51820[Interface]
PrivateKey = <client_private.key>
Address = 10.10.10.2/24
DNS = 192.168.1.1
[Peer]
PublicKey = <server_public.key>
PresharedKey = <preshared.key>
Endpoint = <публичный_статический_IP>:51820
AllowedIPs = 10.10.10.0/24, 192.168.1.0/24
PersistentKeepalive = 25PersistentKeepalive = 25 поддерживает NAT-биндинг на клиентской стороне — без него туннель «засыпает» и первые пакеты после простоя теряются.
| Платформа | Ссылка |
|---|---|
| iOS | App Store |
| Android | Google Play / прямой APK |
| Windows | wireguard.com/install |
| macOS | App Store |
| Linux | wireguard.com/install (wireguard-tools + wg-quick) |
- Установить официальное приложение WireGuard.
+→ «Создать из QR-кода» (QR генерируется из содержимого.confлюбым генератором, например python-пакетомqrcode), либо+→ «Импорт из файла или архива».- Включить туннель и убедиться, что появился handshake (счётчики sent/received растут).
.conf-файл и QR-код содержат приватный ключ — не хранить в открытом виде дольше, чем нужно для импорта, и не пересылать по незашифрованным каналам.
Каждому клиенту — своя пара ключей и свой preshared key:
cd /etc/wireguard
umask 077
wg genkey | tee client2_private.key | wg pubkey > client2_public.key
wg genpsk > preshared2.key
CLIENT2_PUB=$(cat client2_public.key)
PSK2=$(cat preshared2.key)
uci add network wireguard_wg0
uci set network.@wireguard_wg0[-1].description='client2-remote'
uci set network.@wireguard_wg0[-1].public_key="$CLIENT2_PUB"
uci set network.@wireguard_wg0[-1].preshared_key="$PSK2"
uci add_list network.@wireguard_wg0[-1].allowed_ips='10.10.10.3/32'
uci set network.@wireguard_wg0[-1].route_allowed_ips='1'
uci commit network
/etc/init.d/network restart
wg show # должно быть два peer'аАдреса выдаются по инкременту: следующий клиент — 10.10.10.4/32 и так далее.
Если клиенту не нужна вся локальная сеть — в его конфиге сузить AllowedIPs до 10.10.10.0/24 (без 192.168.1.0/24). Через туннель будет доступен только сам роутер.
uci show network | grep -B1 "client2-remote" # найти индекс peer-записи
uci delete network.@wireguard_wg0[N]
uci commit network
/etc/init.d/network restartЕсли устройство клиента могло быть скомпрометировано — удаления peer'а достаточно: без записи на сервере его ключи бесполезны.
- Firewall-правило на роутере:
nft list ruleset | grep 51820— должна быть строка сaccept. - NAT/файрвол на клиентской стороне и у провайдера — исходящий UDP не должен блокироваться.
- MTU: дефолтные 1420 могут быть велики для сетей с дополнительной инкапсуляцией (PPPoE и т.п.). При фрагментации снизить до 1380–1400:
MTU = 1380в секции[Interface]клиента. - Сверить ключи:
PublicKeyв клиентском конфиге должен совпадать с выводомwg showна роутере,Endpoint— с актуальным публичным IP.
- На роутере:
route_allowed_ips='1'в секцииwireguard_wg0— без него маршруты не прописываются. - Forwarding-правила
wireguard → lanиlan → wireguardв/etc/config/firewall. - В клиентском конфиге
AllowedIPsдолжен включать LAN-подсеть (192.168.1.0/24), а не только туннельную.
Причина: блок config zone 'wireguard' добавлен в /etc/config/firewall дважды (heredoc-команда выполнена повторно). firewall4 генерирует nft-переменную по имени зоны и падает на дубликате.
Диагностика:
uci show firewall | grep -i wireguard
grep -n "wireguard" /etc/config/firewallЕсли видно два одинаковых @zone[N].name='wireguard' — удалить лишний набор (zone + связанные forwarding + rule), начиная с наибольшего индекса, иначе нумерация съедет и удалится не то:
uci delete firewall.@rule[N]
uci delete firewall.@forwarding[N]
uci delete firewall.@forwarding[N-1]
uci delete firewall.@zone[N]
uci commit firewall
/etc/init.d/firewall restart| Компонент | Роль |
|---|---|
| OpenWrt 25.12.x | ОС роутера, nftables / firewall4 |
| WireGuard | административный туннель (wireguard-tools, luci-proto-wireguard) |
| Podkop (podkop.net) | селективная маршрутизация LAN-трафика, независимый контур |
| Xray (VLESS+Reality) | транспорт основного туннеля |
| 3x-ui | панель управления Xray на VPS-стороне |
.conf-файлы, QR-коды и файлы*.keyне хранить в репозиториях и не пересылать по незашифрованным каналам. В.gitignoreэтого репозитория соответствующие маски уже добавлены.- Все ключи, адреса и идентификаторы в этом документе — плейсхолдеры или примеры из приватных диапазонов. Реальные значения подставляются локально и не коммитятся.
- Если приватный ключ или
.confвсё же попали в git-историю запушенного репозитория — удаление файла новым коммитом не помогает: ключи считать скомпрометированными и перегенерировать пары на роутере.
