Настройка роутера на OpenWRT и установка sing-box

Третьего дня ко мне таки приехал мой долгожданный роутер с OpenWRT на борту и я моментально приступил к жесточайшему пердолингу. Пердолинг оказался совсем не жесточайший, и все получилось настолько быстро и просто, что я даже немного расстроился.

Итак, первым делом настраиваем наш свеженький роутер как обычно. Ставим там логины, пароли, настраиваем wi-fi, сети и т.д. Дальше ломимся на него по ssh

ssh root@192.168.1.1

и начинаем делать грязь.

Первым делом нужно отключить обход блокировок ютуба если он у вас есть.

# отключим встроенный обход блокирокки ютуба. Мы же ничего не хотим обходить, помните?
service youtubeUnblock disable
service youtubeUnblock stop
rm /usr/share/nftables.d/ruleset-post/537-youtubeUnblock.nft

Затем ставим sing-box

# обновим пакеты и поставим сингбокс
opkg update
opkg install sing-box
# Настроим sing-box от root (необходимый костыль)
uci set sing-box.main.enabled='1'
uci set sing-box.main.user='root'
uci commit sing-box

Ну а дальше правим конфиг sing-box с помощью nano /etc/sing-box/config.json и пишем туда что-нибудь такое

{
  "log": {
    "level": "warn"
  },
  "dns": {
    "servers": [
      {
        "tag": "local-dns",
        "address": "local",
        "detour": "direct-out"
      },
      {
        "tag": "cloudflare-dns",
        "address": "https://1.1.1.1/dns-query",
        "address_resolver": "local-dns",
        "detour": "proxy"
      }
    ],
    "rules": [
      {
        "rule_set": [
          "antizapret",
          "geosite-youtube",
          "geosite-openai",
          "geosite-anthropic",
          "geosite-jetbrains",
          "geosite-jetbrains-ai",
          "geosite-x",
          "geosite-discord"
        ],
        "server": "cloudflare-dns"
      },
      {
        "domain_suffix": [
          "lucid.app",
          "lucid.co",
          "qodana.cloud",
          "habr.com"
        ],
        "server": "cloudflare-dns"
      }
    ]
  },
  "inbounds": [
    {
      "type": "tproxy",
      "tag": "proxy",
      "listen": "::",
      "listen_port": 1602,
      "sniff": true,
      "domain_strategy": "prefer_ipv4"
    }
  ],
  "outbounds": [
    {
      "type": "direct",
      "tag": "direct-out"
    },
    {
      "domain_strategy": "ipv4_only",
      "flow": "",
      "packet_encoding": "",
      "server": "YOUR_SERVER",
      "server_port": 443,
      "tag": "proxy",
      "tls": {
        "enabled": true,
        "reality": {
          "enabled": true,
          "public_key": "YOUR_KEY",
          "short_id": "YOUR_ID"
        },
        "server_name": "YOUR_SERVER_NAME",
        "utls": {
          "enabled": true,
          "fingerprint": "chrome"
        }
      },
      "type": "vless",
      "uuid": "YOUR_UUID"
    }
  ],
  "route": {
    "rules": [
      {
        "ip_cidr": [
          "104.16.0.0/12",
          "13.24.0.0/13",
          "13.32.0.0/12",
          "13.48.0.0/13",
          "13.56.0.0/14",
          "143.204.0.0/16",
          "162.158.0.0/15",
          "172.64.0.0/13",
          "18.128.0.0/9",
          "18.32.0.0/11",
          "18.64.0.0/10",
          "188.114.96.0/22",
          "204.11.56.0/23",
          "23.227.32.0/19",
          "3.128.0.0/9",
          "138.128.136.0/21",
          "34.0.0.0/15",
          "34.2.0.0/16",
          "34.3.0.0/23",
          "34.3.2.0/24",
          "35.192.0.0/12",
          "35.208.0.0/12",
          "35.224.0.0/12",
          "35.240.0.0/13",
          "5.200.14.128/25",
          "66.22.192.0/18"
        ],
        "outbound": "proxy"
      },
      {
        "rule_set": "antizapret",
        "outbound": "proxy"
      },
      {
        "rule_set": [
          "geosite-youtube",
          "geosite-openai",
          "geosite-anthropic",
          "geosite-jetbrains",
          "geosite-jetbrains-ai",
          "geosite-x",
          "geosite-discord",
          "geo-block"
        ],
        "outbound": "proxy"
      },
      {
        "domain_suffix": [
          "lucid.app", 
          "lucid.co", 
          "qodana.cloud", 
          "habr.com", 
          "ntc.party"
        ],
        "outbound": "proxy"
      }
    ],
    "rule_set": [
      {
        "tag": "antizapret",
        "type": "remote",
        "format": "binary",
        "url": "https://github.com/savely-krasovsky/antizapret-sing-box/releases/latest/download/antizapret.srs",
        "download_detour": "proxy"
      },
      {
        "tag": "geosite-youtube",
        "type": "remote",
        "format": "binary",
        "url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-youtube.srs",
        "download_detour": "proxy"
      },
      {
        "tag": "geosite-openai",
        "type": "remote",
        "format": "binary",
        "url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-openai.srs",
        "download_detour": "proxy"
      },
      {
        "tag": "geosite-anthropic",
        "type": "remote",
        "format": "binary",
        "url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-anthropic.srs",
        "download_detour": "proxy"
      },
      {
        "tag": "geosite-jetbrains",
        "type": "remote",
        "format": "binary",
        "url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-jetbrains.srs",
        "download_detour": "proxy"
      },
      {
        "tag": "geosite-jetbrains-ai",
        "type": "remote",
        "format": "binary",
        "url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-jetbrains-ai.srs",
        "download_detour": "proxy"
      },
      {
        "tag": "geosite-x",
        "type": "remote",
        "format": "binary",
        "url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-x.srs",
        "download_detour": "proxy"
      },
      {
        "tag": "geosite-discord",
        "type": "remote",
        "format": "binary",
        "url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-discord.srs",
        "download_detour": "proxy"
      },
      {
        "tag": "geo-block",
        "type": "remote",
        "format": "binary",
        "url": "https://github.com/itdoginfo/allow-domains/releases/latest/download/geoblock.srs",
        "download_detour": "proxy"
      }
    ],
    "auto_detect_interface": true
  },
  "experimental": {
    "cache_file": {
      "enabled": true
    }
  }
}

Данный конфиг поднимем sing-box в режиме прокси, теперь надо зароутить туда трафик, для этого добавляем “сервис” который будет настраивать форвардинг трафика.

touch /etc/init.d/proxyroute
chmod +x /etc/init.d/proxyroute

запихиваем в файл /etc/init.d/proxyroute скрипт ниже

#!/bin/sh /etc/rc.common

START=99
STOP=15

USE_PROCD=1

NAME="proxyroute"
NFT_TABLE_NAME="ProxyRouteTable"
RT_TABLE_NAME="proxyroute"
RT_TABLE_NUM="106"
FWMARK="0x105"
TPROXY_PORT="1602"

SRC_INTERFACE="br-lan"  # Ваш LAN-интерфейс
YOUR_LAN_SUBNET="192.168.1.0/24" # Ваша LAN-подсеть

start_service() {
    logger "Starting $NAME service"

    # 1. Настройка таблицы роутинга и правила ip rule

    # Добавляем запись в /etc/iproute2/rt_tables, если ее нет
    if ! grep -q "$RT_TABLE_NUM $RT_TABLE_NAME" /etc/iproute2/rt_tables; then
        echo "$RT_TABLE_NUM $RT_TABLE_NAME" >> /etc/iproute2/rt_tables
        logger "Added route table entry to /etc/iproute2/rt_tables"
    fi

    # Создаем таблицу маршрутизации, если ее нет
    if ! ip route show table "$RT_TABLE_NAME" >/dev/null 2>&1; then
        ip route add local 0.0.0.0/0 dev lo table "$RT_TABLE_NAME"
        logger "Created routing table: $RT_TABLE_NAME"
    fi

    # Добавляем локальный маршрут, если его нет
    if ! ip route list table "$RT_TABLE_NAME" | grep -q "local default dev lo"; then
        ip route add local 0.0.0.0/0 dev lo table "$RT_TABLE_NAME"
        logger "Added local route to table: $RT_TABLE_NAME"
    fi

    # Добавляем правило ip rule, если его нет
    if ! ip rule list | grep -q "fwmark $FWMARK lookup $RT_TABLE_NAME"; then
        ip -4 rule add fwmark "$FWMARK" table "$RT_TABLE_NAME" priority "$RT_TABLE_NUM"
        logger "Added ip rule fwmark $FWMARK lookup $RT_TABLE_NAME"
    fi

    # 2. Настройка nftables

    # Создать таблицу, если не существует
    nft list table inet "$NFT_TABLE_NAME" >/dev/null 2>&1 || {
        nft add table inet "$NFT_TABLE_NAME"
        logger "Created nftables table: $NFT_TABLE_NAME"
    }

    # Создать цепочки, если не существуют
    nft list chain inet "$NFT_TABLE_NAME" mangle >/dev/null 2>&1 || {
        nft add chain inet "$NFT_TABLE_NAME" mangle { type filter hook prerouting priority -150 \; policy accept \; }
        logger "Created nftables chain: $NFT_TABLE_NAME mangle"
    }

    nft list chain inet "$NFT_TABLE_NAME" proxy >/dev/null 2>&1 || {
        nft add chain inet "$NFT_TABLE_NAME" proxy { type filter hook prerouting priority -100 \; policy accept \; }
        logger "Created nftables chain: $NFT_TABLE_NAME proxy"
    }

    # Создать и заполнить сет localv4 для исключений
    if ! nft list set inet "$NFT_TABLE_NAME" localv4 >/dev/null 2>&1; then
        nft add set inet "$NFT_TABLE_NAME" localv4 { type ipv4_addr\; flags interval\; }
        nft add element inet "$NFT_TABLE_NAME" localv4 { \
            0.0.0.0/8, \
            10.0.0.0/8, \
            127.0.0.0/8, \
            169.254.0.0/16, \
            172.16.0.0/12, \
            192.0.0.0/24, \
            192.0.2.0/24, \
            192.88.99.0/24, \
            192.168.0.0/16, \
            198.51.100.0/24, \
            203.0.113.0/24, \
            224.0.0.0/4, \
            240.0.0.0-255.255.255.255 }
        logger "Created and populated nftables set: $NFT_TABLE_NAME localv4"
    fi

    # Правила маркировки в цепочке mangle
    # ПРАВИЛО 1: Исключить локальный трафик (ВАЖНО: должно быть до общего правила маркировки)
    if ! nft -a list chain inet "$NFT_TABLE_NAME" mangle | grep -q "ip saddr $YOUR_LAN_SUBNET ip daddr @localv4 return"; then
        nft insert rule inet "$NFT_TABLE_NAME" mangle position 0 iifname "$SRC_INTERFACE" ip saddr "$YOUR_LAN_SUBNET" ip daddr @localv4 return comment "Exclude_local_traffic"
        logger "Added nftables rule to mangle chain: Exclude_local_traffic"
    fi

    # ПРАВИЛО 2: Маркировка ВСЕГО TCP/UDP трафика, идущего из LAN
    if ! nft -a list chain inet "$NFT_TABLE_NAME" mangle | grep -q "ip saddr $YOUR_LAN_SUBNET meta l4proto { tcp, udp } meta mark set $FWMARK"; then
        nft add rule inet "$NFT_TABLE_NAME" mangle iifname "$SRC_INTERFACE" ip saddr "$YOUR_LAN_SUBNET" meta l4proto { tcp, udp } meta mark set "$FWMARK" counter comment "Mark_ALL_LAN_Traffic"
        logger "Added nftables rule to mangle chain: Mark_ALL_LAN_Traffic"
    fi

    # Правила TProxy в цепочке proxy
    if ! nft -a list chain inet "$NFT_TABLE_NAME" proxy | grep -q "meta mark $FWMARK meta l4proto tcp tproxy ip to :$TPROXY_PORT"; then
        nft add rule inet "$NFT_TABLE_NAME" proxy meta mark "$FWMARK" meta l4proto tcp tproxy ip to ":$TPROXY_PORT" counter comment "TPROXY_TCP_to_$TPROXY_PORT"
        logger "Added nftables rule to proxy chain: TPROXY_TCP_to_$TPROXY_PORT"
    fi

    if ! nft -a list chain inet "$NFT_TABLE_NAME" proxy | grep -q "meta mark $FWMARK meta l4proto udp tproxy ip to :$TPROXY_PORT"; then
        nft add rule inet "$NFT_TABLE_NAME" proxy meta mark "$FWMARK" meta l4proto udp tproxy ip to ":$TPROXY_PORT" counter comment "TPROXY_UDP_to_$TPROXY_PORT"
        logger "Added nftables rule to proxy chain: TPROXY_UDP_to_$TPROXY_PORT"
    fi
}

stop_service() {
    logger "Stopping $NAME service and uninstalling rules"

    # 1. Удаляем nftables правила и таблицы
    nft delete table inet "$NFT_TABLE_NAME" &>/dev/null
    logger "Deleted nftables table: $NFT_TABLE_NAME"

    # 2. Удаляем ip rule
    ip -4 rule del fwmark "$FWMARK" table "$RT_TABLE_NAME" priority "$RT_TABLE_NUM" &>/dev/null
    logger "Deleted ip rule fwmark $FWMARK lookup $RT_TABLE_NAME"

    # 3.  Удаляем таблицу маршрутизации и запись в /etc/iproute2/rt_tables
    ip route flush table "$RT_TABLE_NAME" &>/dev/null
    ip route del local 0.0.0.0/0 dev lo table "$RT_TABLE_NAME"  &>/dev/null
    logger "Deleted routing table: $RT_TABLE_NAME"

    sed -i "/$RT_TABLE_NUM $RT_TABLE_NAME/d" /etc/iproute2/rt_tables
    logger "Removed route table entry from /etc/iproute2/rt_tables"
}
remove() {
    stop
    rm -f /etc/init.d/$NAME
    logger "Removed service init file"
}

включаем sing-box и наш серис по роутингу

service sing-box enable
service sing-box start
service proxyroute enable
service proxyroute start