Детальные конфигурации для продвинутых сценариев: Xray inbound, sing-box routing, DNS, WireGuard, мониторинг и оптимизация.
Во всех примерах используются плейсхолдеры:
SERVER_IP— публичный IP-адрес VPS;ROUTER_IP— IP-адрес роутера в локальной сети (по умолчанию192.168.1.1);UUID— идентификатор клиента VLESS;PRIVATE_KEY/PUBLIC_KEY— ключи Reality;SHORT_ID— short ID Reality;SNI_DOMAIN— домен прикрытия (например,www.amazon.com).
- Xray Inbound (VLESS + Reality + Vision)
- Sing-box Config для Podkop (selective routing)
- Bypass Rules (исключение трафика из туннеля)
- WireGuard (удалённый доступ к роутеру)
- AdGuard Home + Xray
- Списки маршрутизации (selective mode)
- Тестирование и отладка
- Мониторинг и логирование
- Оптимизация производительности
- Управление 3x-ui через API
Серверный inbound. На стороне сервера в realitySettings хранится только приватный ключ; публичный ключ передаётся клиенту отдельно (в ссылке vless:// или в клиентском конфиге).
{
"inbounds": [
{
"port": 443,
"protocol": "vless",
"tag": "vless-reality",
"settings": {
"clients": [
{
"id": "UUID",
"email": "client-1",
"flow": "xtls-rprx-vision"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"show": false,
"dest": "SNI_DOMAIN:443",
"xver": 0,
"serverNames": [
"SNI_DOMAIN"
],
"privateKey": "PRIVATE_KEY",
"maxTimeDiff": 70000,
"shortIds": [
"SHORT_ID"
]
}
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
]
}Способ 1 — через панель 3x-ui:
- Inbound List → выбрать inbound → Edit.
- В секции Security нажать Get New Cert.
- Панель сгенерирует пару ключей (приватный остаётся на сервере, публичный нужен клиенту).
- Скопировать значения.
Способ 2 — через CLI (X25519, формат для Reality):
# На VPS xray x25519 # Вывод: # Private key: ... → privateKey в серверный конфиг # Public key: ... → publicKey в клиентский конфиг
Reality использует ключи X25519, а не ECDSA prime256v1. Команда
openssl ecparamдля генерации ключей Reality не подходит — используйтеxray x25519.
Генерация short ID:
openssl rand -hex 8
{
"log": {
"level": "info",
"timestamp": true
},
"inbounds": [
{
"type": "tun",
"tag": "tun-in",
"inet4_address": "198.18.0.1/15",
"auto_route": false,
"strict_route": false,
"sniff": true,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv4"
},
{
"type": "dns",
"tag": "dns-in",
"listen": "127.0.0.1",
"listen_port": 7874
}
],
"outbounds": [
{
"type": "vless",
"tag": "proxy",
"server": "SERVER_IP",
"server_port": 443,
"uuid": "UUID",
"flow": "xtls-rprx-vision",
"tls": {
"enabled": true,
"server_name": "SNI_DOMAIN",
"utls": {
"enabled": true,
"fingerprint": "chrome"
},
"reality": {
"enabled": true,
"public_key": "PUBLIC_KEY",
"short_id": "SHORT_ID"
}
}
},
{
"type": "direct",
"tag": "direct"
},
{
"type": "dns",
"tag": "dns-out"
}
],
"route": {
"rules": [
{
"protocol": "dns",
"outbound": "dns-out"
},
{
"ip_is_private": true,
"outbound": "direct"
},
{
"rule_set": "blocked-domains",
"outbound": "proxy"
},
{
"outbound": "direct"
}
],
"auto_detect_interface": true,
"override_android_vpn": true
},
"dns": {
"servers": [
{
"tag": "proxydns",
"address": "tcp+local://8.8.8.8:53",
"detour": "proxy"
},
{
"tag": "localdns",
"address": "local"
}
],
"rules": [
{
"rule_set": "blocked-domains",
"server": "proxydns"
},
{
"server": "localdns"
}
]
}
}В правилах route и dns теги серверов (
proxydns,localdns) и rule_set должны совпадать буквально. Частая ошибка — несоответствие тегов (например, сервер объявлен какproxydns, а в правиле указанproxyns), из-за чего конфиг не загружается.
Точный путь зависит от версии Podkop. Проверить кандидатов:
cat /etc/podkop/config.json cat /usr/share/podkop/config.json cat /etc/sing-box/config.json
Редактирование и применение:
vi /etc/podkop/config.json /etc/init.d/podkop restart
Применяется для трафика, который должен идти напрямую: внутренние сервисы, PPTP/GRE-туннели, CDN.
{
"route": {
"rules": [
{
"ip_cidr": [
"203.0.113.45/32",
"10.0.0.0/8"
],
"outbound": "direct"
}
]
}
}Адреса в примере — документационные (RFC 5737
203.0.113.0/24и RFC 191810.0.0.0/8). Замените на фактические IP, которые требуется исключить.
ssh root@ROUTER_IP
cat > /etc/firewall.user << 'EOF' #!/bin/sh # Исключение трафика к указанному IP из туннеля sing-box. # Помечает пакеты меткой 0x100; политика маршрутизации # направляет их в таблицу с прямым шлюзом. DIRECT_IP="203.0.113.45" GATEWAY="192.168.1.1" MARK="0x100" TABLE="100" ip rule add fwmark "$MARK" lookup "$TABLE" 2>/dev/null ip route replace default via "$GATEWAY" table "$TABLE" iptables -t mangle -A PREROUTING -d "$DIRECT_IP" -j MARK --set-mark "$MARK" EOF chmod +x /etc/firewall.user /etc/init.d/firewall restart
Способ 3.1 (через sing-box) предпочтительнее: он не зависит от структуры firewall и не конфликтует с правилами Podkop. Способ 3.2 — резервный, для случаев когда перехват происходит вне sing-box.
Безопасная альтернатива публичному SSH.
# На роутере (OpenWRT) 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.key
UCI-конфиг не выполняет подстановку $(...). Значения ключей нужно вписать явно или подставить через скрипт.
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 'remote-client' 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 wg show
[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 = SERVER_PUBLIC_IP:51820 AllowedIPs = 10.10.10.0/24, 192.168.1.0/24 PersistentKeepalive = 25
Endpointуказывает на публичный IP роутера. Для доступа к ресурсам локальной сети роутера вAllowedIPsвключается её подсеть (192.168.1.0/24).
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 будут отброшены, и подключение не установится.
# Актуальную версию см. на https://github.com/AdguardTeam/AdGuardHome/releases VERSION="v0.107.77" wget "https://github.com/AdguardTeam/AdGuardHome/releases/download/${VERSION}/AdGuardHome_linux_amd64.tar.gz" tar -xzf AdGuardHome_linux_amd64.tar.gz
# Запуск на loopback (не на публичном интерфейсе)
./AdGuardHome/AdGuardHome -h 127.0.0.1Веб-интерфейс первичной настройки доступен на http://127.0.0.1:3000. После настройки:
- порт DNS-сервера AdGuard Home —
53(или иной, заданный в мастере); - Upstream DNS —
8.8.8.8, резервный1.1.1.1; - в 3x-ui (Settings → DNS Settings) указать AdGuard Home как upstream:
127.0.0.1:53.
Web-порт (
3000) и DNS-порт (53) — разные порты. Xray направляется на DNS-порт, а не на web-порт.
AdGuard Home поддерживает установку в качестве сервиса собственными средствами — это надёжнее ручного unit-файла:
./AdGuardHome/AdGuardHome -s install systemctl status AdGuardHome
Управление:
./AdGuardHome/AdGuardHome -s start ./AdGuardHome/AdGuardHome -s stop ./AdGuardHome/AdGuardHome -s uninstall
Современные версии Podkop используют форматы community-листов (geosite, rule-set, текстовые списки доменов/подсетей). Точные пути и форматы зависят от версии — ориентироваться на интерфейс LuCI.
# Домены, маршрутизируемые через туннель (proxy):
example-blocked-1.com
example-blocked-2.org
# Домены, идущие напрямую (direct):
*.lan
internal.example.local
# Через туннель (proxy):
198.51.100.0/24
# Напрямую (direct):
192.168.0.0/16
10.0.0.0/8
172.16.0.0/12
Локальные диапазоны RFC 1918 (
192.168.0.0/16,10.0.0.0/8,172.16.0.0/12) всегда должны идти напрямую, иначе сломается доступ к локальной сети.
# 1. TLS handshake с доменом прикрытия — должен вернуть # валидный сертификат целевого сайта (SNI_DOMAIN) openssl s_client -connect SERVER_IP:443 -servername SNI_DOMAIN </dev/null # 2. TLS fingerprint — сравнить с эталоном браузера curl -v --http2 https://tls.peet.ws/api/all # Эталон: https://browserleaks.com/tls # 3. Проверка DNS curl 'https://dns.google/resolve?name=example.com' # 4. Проверка IP выхода — должен совпадать с SERVER_IP curl https://ifconfig.me
# Уровень логирования в конфиге sing-box: # "log": { "level": "debug", "timestamp": true } # (debug — только на время диагностики, не для постоянной работы) /etc/init.d/podkop restart logread -e podkop -f
# 1. Правила перехвата в nftables nft list ruleset | grep -i tun # 2. DNS-ответ должен содержать FakeIP из диапазона 198.18.0.0/15 dig @127.0.0.1 -p 7874 example.com # 3. Записи conntrack для FakeIP во время активного трафика cat /proc/net/nf_conntrack | grep '198.18' | head -5
{
"log": {
"access": "/var/log/xray/access.log",
"error": "/var/log/xray/error.log",
"loglevel": "warning"
}
}
warningдостаточно для продакшена.infoиdebugувеличивают объём логов и нагрузку.
cat > /etc/logrotate.d/xray << 'EOF' /var/log/xray/*.log { daily rotate 7 compress delaycompress notifempty missingok create 0640 root root sharedscripts postrotate systemctl reload 3x-ui > /dev/null 2>&1 || true endscript } EOF
cat >> /etc/crontabs/root << 'EOF' 0 * * * * echo "$(date '+\%F \%T'): $(free -m | awk '/Mem/{print 3ドル"/"2ドル" MB"}')" >> /tmp/mem.log EOF /etc/init.d/cron restart
Для постоянного мониторинга предпочтительнее
luci-app-statistics(collectd) — он даёт графики и не нагружает flash частыми записями.
-
Отключить неиспользуемые сервисы:
/etc/init.d/dnsmasq stop # только если DNS полностью обрабатывает sing-boxuhttpd(LuCI) отключать не рекомендуется — теряется веб-управление. Достаточно ограничить его доступность по WAN через firewall. -
Использовать урезанные geo-базы (geoip-lite/geosite-lite) или текстовые списки доменов вместо полной базы — это основной потребитель RAM.
-
Включить SQM для управления буфером (bufferbloat):
apk add sqm-scripts luci-app-sqm # Настройка: LuCI → Network → SQM QoS
-
Увеличить TCP-буферы:
cat > /etc/sysctl.d/99-network.conf << 'EOF' net.core.rmem_max=67108864 net.core.wmem_max=67108864 net.ipv4.tcp_rmem=4096 87380 33554432 net.ipv4.tcp_wmem=4096 65536 33554432 EOF sysctl -p /etc/sysctl.d/99-network.conf
-
Включить BBR (если модуль доступен в сборке):
modprobe tcp_bbr 2>/dev/null echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.d/99-network.conf sysctl -p /etc/sysctl.d/99-network.conf sysctl net.ipv4.tcp_congestion_control # проверка
BBR влияет на исходящие соединения самого роутера. На транзитный трафик, проходящий через туннель, основное влияние оказывает congestion control на VPS — BBR имеет смысл включать и там.
Базовый клиент на Python. Учётные данные и адрес панели передаются как параметры, не хранятся в коде.
import requests class XUIClient: def __init__(self, base_url, username, password, verify_tls=True): self.base_url = base_url.rstrip("/") self.session = requests.Session() self.session.verify = verify_tls self._login(username, password) def _login(self, username, password): r = self.session.post( f"{self.base_url}/login", json={"username": username, "password": password}, timeout=10, ) r.raise_for_status() return r.json() def add_client(self, inbound_id, client_data): r = self.session.post( f"{self.base_url}/panel/api/inbounds/addClient", json={"id": inbound_id, "settings": client_data}, timeout=10, ) r.raise_for_status() return r.json() def subscription_url(self, sub_id): return f"{self.base_url}/sub/{sub_id}" # Пример использования client = XUIClient( base_url="https://SERVER_IP:2053", username="admin", password="CHANGE_ME", verify_tls=True, # False только при self-signed сертификате )