В связи с тем, что форум воспрял из пепла - восстаналиваю мануал (Копия есть в комментах на хабре Решаем проблему блокировок (и YouTube) за 5 минут на роутере Mikrotik через контейнеры и без VPN / Хабр)
На микроте ставим пакет с контейнерами, ребутаемся, включаем контейнеры
/system/device-mode/update container=yes
На железке после этого нужно будет нажать кнопку mode за ограниченное время, удалённо включить контейнеры не выйдет, т.к. потанцевальная дыра в безопасности. Дальше создаём VETH для нашего контейнера, создаём отдельный бридж для контейнеров, навешиваем туда ip адреса(я тут добавляю и ipv6 и ipv4 адрес. На самом деле для схемы с роутингом через WG интерфейс достаточно только v4, но у меня все контейнеры настроены единообразно, так что для общего понимания оставлю)
/interface/veth/add address=172.20.0.2/24,fd08:172:20::2/64 gateway=172.20.0.1 gateway6=fd08:172:20::1 name=xray_eth
/interface/bridge/add name=containers
/interface/bridge/port/add bridge=containers interface=xray_eth
/ip/address/add address=172.20.0.1/24 interface=containers interface=xray_eth
/ip/firewall/nat/add action=masquerade chain=srcnat comment="from containers masq" out-interface-list=WAN src-address=172.20.0.0/24
/ipv6/address/add address=fd08:172:20::1 interface=containers
/ipv6/firewall/nat/
add action=masquerade chain=srcnat comment="from containers masq" out-interface-list=WAN src-address=fd08:172:20::/64
add action=masquerade chain=srcnat comment="to containers masq" out-interface=containers
/ipv6/firewall/filter/add action=accept chain=forward comment="Allow Containers" out-interface-list=WAN src-address=fd08:172:20::/64
Последние два правила если что рождены в попытке заставить работать схему с byeDPI, для xray это тоже не нужно(при условии что вы отправляете траффик на ipv4 адрес. Если у вас ipv6-only VPS - то нужно, всё нужно) Дальше втыкаем в микрот флешку, из интерфейса форматируем её в EXT4(Обязательно! Иначе при распаковке контейнера порушатся разрешения фалов и ничего не взлетит) Конфигурим местный докер
/container/config/set ram-high=384.0MiB registry-url=https://ghcr.io/ tmpdir=usb1/pull
Аттеншн! Тут ram-high=384.0MiB - это конфиг для AX3 с гигом оперативы. У AС3 её всего 256Мб и с пустым конфигом и установленным пакетом wifi-qcom доступно примерно 145Мб(а с подробным конфигом и под нагрузкой - что-то около 70Мб). У AC2(128Мб) при таких же вводных доступно и того меньше - всего примерно 30Мб. Для AC2 вероятно придётся вообще перейти на легаси Wifi драйвер, иначе с ресурсами ну совсем грустно. Соотв. выставляем параметр с запасом, так чтоб и самому роутеру хватило. При превышении лимита процессы в контейнере начнут нещадно троттлиться, т.е. работать будет, но с дикими тормозами. А вот если память у роутера закончится - скорее всего он тупо крашнется. Не доводим и не включаем старт контейнера при загрузке пока не отладим всё. Скачиваем образ
/container/add comment=xray-core remote-image=xtls/xray-core:main interface=veth1 root-dir=usb1/xray/store
После этого можно попробовать его стартануть и убедиться что всё найс и ничего не сыпется. Дальше нужно приготовить конфигу xray (файлик config.json) Либо выдёргиваем его из /usr/local/x-ui/bin/config.json (не с сервера разумеется, а с заранее где-то настроенной клиентской инсталляции x-ui. В самом конфиге придётся поправить ip-шники при этом, очевидно), либо пишем ручками. Должно получиться что-то такое
{
"log": {
"access": "none",
"dnsLog": false,
"error": "./error.log",
"loglevel": "error"
},
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"inboundTag": [
"api"
],
"outboundTag": "api"
},
{
"type": "field",
"ip": [
"geoip:private",
"192.168.88.0/24"
],
"outboundTag": "direct"
},
{
"type": "field",
"ip": [
"geoip:ru"
],
"outboundTag": "warp"
},
{
"type": "field",
"protocol": [
"bittorrent"
],
"outboundTag": "direct"
},
{
"type": "field",
"inboundTag": [
"inbound-172.20.0.2:3125"
],
"outboundTag": "warp"
},
{
"type": "field",
"inboundTag": [
"inbound-172.20.0.2:3126"
],
"outboundTag": "VLESS-VPS"
}
]
},
"dns": null,
"inbounds": [
{
"listen": "172.20.0.2",
"port": 3126,
"protocol": "wireguard",
"settings": {
"mtu": 1420,
"secretKey": "секрет",
"peers": [
{
"privateKey": "секрет",
"publicKey": "секрет",
"preSharedKey": "секрет",
"allowedIPs": [
"0.0.0.0/0",
"::/0"
],
"keepAlive": 0
}
],
"kernelMode": false
},
"streamSettings": null,
"tag": "inbound-172.20.0.2:3126",
"sniffing": {
"enabled": false,
"destOverride": [
"http",
"tls",
"quic",
"fakedns"
],
"metadataOnly": false,
"routeOnly": false
}
},
{
"listen": "172.20.0.2",
"port": 3125,
"protocol": "wireguard",
"settings": {
"mtu": 1420,
"secretKey": "секрет",
"peers": [
{
"privateKey": "секрет",
"publicKey": "секрет",
"allowedIPs": [
"0.0.0.0/0",
"::/0"
],
"keepAlive": 0
}
],
"kernelMode": false
},
"streamSettings": null,
"tag": "inbound-172.20.0.2:3125",
"sniffing": {
"enabled": false,
"destOverride": [
"http",
"tls",
"quic",
"fakedns"
],
"metadataOnly": false,
"routeOnly": false
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "AsIs"
}
},
{
"tag": "blocked",
"protocol": "blackhole",
"settings": {}
},
{
"tag": "VLESS-VPS",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "секрет",
"port": 443,
"users": [
{
"id": "секрет",
"flow": "xtls-rprx-vision",
"encryption": "none"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"publicKey": "секрет",
"fingerprint": "chrome",
"serverName": "популярный.хост.близко.к.VPS",
"shortId": "секрет",
"spiderX": "/"
},
"tcpSettings": {
"header": {
"type": "none"
}
}
}
},
{
"tag": "warp",
"protocol": "wireguard",
"settings": {
"mtu": 1420,
"secretKey": "секрет",
"address": [
"172.16.0.2/32",
"секрет"
],
"workers": 2,
"domainStrategy": "ForceIP",
"reserved": [
166,
231,
19
],
"peers": [
{
"publicKey": "секрет",
"allowedIPs": [
"0.0.0.0/0",
"::/0"
],
"endpoint": "список известных хостов и портов есть в сети. Подберите тот, что будет работать для вас",
"keepAlive": 0
}
],
"kernelMode": false
}
}
],
"transport": null,
"policy": {
"levels": {
"0": {
"statsUserDownlink": true,
"statsUserUplink": true
}
},
"system": {
"statsInboundDownlink": true,
"statsInboundUplink": true,
"statsOutboundDownlink": true,
"statsOutboundUplink": true
}
},
"api": {
"tag": "api",
"services": [
"HandlerService",
"LoggerService",
"StatsService"
]
},
"stats": {},
"reverse": null,
"fakedns": null,
"observatory": null,
"burstObservatory": null
}
Тут у меня конфига на два входа и к ним два выхода - в WARP и в свой VLESS VPN Подкидываем эту конфигу на флешку по пути /usb1/xray/ Добавляем маунт поинт и привязываем его к контейнеру
/container/mounts/add dst=/etc/xray/config.json name=xray_config src=/usb1/xray/config.json
/container/set mounts=xray_config [ /container/find comment=xray-core ]
После чего можно рестартовать контейнер.
В общем случае вам нужны две ключевые пары из публичного и приватного ключа. Генерятся они любым удобным способом, главное чтоб публичная часть ключа соответствовала приватной. По конфигам раскалдываются каким-то таким образом
{
"listen": "172.20.0.2",
"port": 3125,
"protocol": "wireguard",
"settings": {
"mtu": 1420,
"secretKey": "PrivkeyB",
"peers": [
{
"privateKey": "PrivkeyA",
"publicKey": "PubkeyA",
"allowedIPs": [
"0.0.0.0/0",
"::/0"
],
"keepAlive": 0
}
],
"kernelMode": false
},
"streamSettings": null,
"tag": "inbound-172.20.0.2:3125",
"sniffing": {
"enabled": false,
"destOverride": [
"http",
"tls",
"quic",
"fakedns"
],
"metadataOnly": false,
"routeOnly": false
}
}
/interface/wireguard/print
Flags: X - disabled; R - running
4 R name="WARP-WG" mtu=1420 listen-port=13234 private-key="PrivkeyA"
public-key="PubkeyA"
/interface wireguard peers
add allowed-address=0.0.0.0/0,::/0 endpoint-address=172.20.0.2 endpoint-port=3125 interface=WARP-WG name=peer5 public-key=\
"PubkeyB"
При этом на самом деле PrivkeyA в конфиге сервера по большому счёту не должен быть нужен, не знаю зачем он его хранит - может это последствия генерации конфига через x-ui.
Для маршрутизации траффика до контейнера нам вполне достаточно динамических маршрутов, которые автоматически будут созданы в момент создания бриджа.
ip/route/print where gateway="containers"
Flags: D - DYNAMIC; A - ACTIVE; c - CONNECT
Columns: DST-ADDRESS, GATEWAY, DISTANCE
DST-ADDRESS GATEWAY DISTANCE
DAc 172.20.0.0/24 containers 0
ipv6/route/print where gateway="containers"
Flags: D - DYNAMIC; X - DISABLED, I - INACTIVE, A - ACTIVE; c - CONNECT, s - STATIC
Columns: DST-ADDRESS, GATEWAY, DISTANCE
# DST-ADDRESS GATEWAY DISTANCE
DAc fd08:172:20::/64 containers 0
DAc fe80::%containers/64 containers 0
Ну а дальше мы просто создаём WG интерфейсы и пиры для каждого inbound в контейнере
/interface wireguard
add listen-port=13233 mtu=1420 name=VLESS-WG private-key="секрет"
add listen-port=13234 mtu=1420 name=WARP-WG private-key="секрет"
/interface wireguard peers
add allowed-address=0.0.0.0/0,::/0 endpoint-address=172.20.0.2 endpoint-port=3126 interface=VLESS-WG name=peer4 public-key="секрет"
add allowed-address=0.0.0.0/0,::/0 endpoint-address=172.20.0.2 endpoint-port=3125 interface=WARP-WG name=peer5 public-key="секрет"
создаём таблицы маршрутизации
/routing table
add disabled=no fib name=vless
add disabled=no fib name=warp
Добавляем dummy lookup rule (возможно в актуальных версиях ROS уже не нужно, раньше без этого ipv6 маршрутизация не работала)
/routing rule
add action=lookup disabled=no interface=WARP-WG routing-mark=warp table=warp
add action=lookup disabled=no interface=VLESS-WG routing-mark=vless table=vless
Добавляем маскарадинг
/ip firewall nat
add action=masquerade chain=srcnat comment="WARP-WG masq" out-interface=WARP-WG
add action=masquerade chain=srcnat comment="VLESS-WG masq" out-interface=VLESS-WG
/ipv6 firewall nat
add action=masquerade chain=srcnat comment="WARP-WG masq" out-interface=WARP-WG
add action=masquerade chain=srcnat comment="VLESS-WG masq" out-interface=VLESS-WG
Дальше нам нужно повесить на интерфейсы WG какие-нибудь IP адреса, любые, из приватного диапазона(по ним будет идти маршрутизация только до приложения в контейнере). Например так. Опять же, в целом можно без этого, т.к. маршрутизировать мы будет тупо по интерфейсу, но некрасиво.
/ip address
add address=172.19.0.2 interface=WARP-WG network=172.19.0.2
add address=172.19.0.3 interface=VLESS-WG network=172.19.0.3
/ipv6 address
add address=fd08:172:19::3/128 advertise=no interface=VLESS-WG no-dad=yes
add address=fd08:172:19::2/128 advertise=no interface=WARP-WG no-dad=yes
Наконец добавляем правила, по которым траффик будет заворачиваться в соотв. таблицу маршрутизации
/ip firewall mangle
add action=mark-routing chain=prerouting comment=VLESS dst-address-list=vless new-routing-mark=vless passthrough=yes routing-mark=!vless
add action=mark-routing chain=prerouting comment=WARP dst-address-list=warp new-routing-mark=warp passthrough=yes routing-mark=!warp
/ipv6 firewall mangle
add action=mark-routing chain=prerouting comment=VLESS dst-address-list=vless new-routing-mark=vless passthrough=yes routing-mark=!vless
add action=mark-routing chain=prerouting comment=WARP dst-address-list=warp new-routing-mark=warp passthrough=yes routing-mark=!warp
Здесь мы отправляем всё, что будет в соотв. адрес листе в соотв. ему таблицу маршрутизации. Если нужно заворачивать несколько адрес листов в один интерфейс или как-то более гибко рулить всем этим безобразием - можно добавить свои цепочки и делать в них jump. Напр. как-то так:
/ipv6 firewall mangle
add action=mark-connection chain=prerouting_warp comment="WARP_chain" dst-address-list=!vpnexclude new-connection-mark=warp
add action=mark-routing chain=prerouting_warp comment="WARP_chain" dst-address-list=!vpnexclude new-routing-mark=warp passthrough=yes
add action=jump chain=prerouting comment=WARP dst-address-list=youtube jump-target=prerouting_warp routing-mark=!warp
add action=jump chain=prerouting comment=WARP dst-address-list=facebook jump-target=prerouting_warp routing-mark=!warp
В этом примере я создал для ipv6 отдельную цепочку prerouting_warp, в которой заворачиваю траффик в таблицу warp и помечаю соединение(можно собрать любое количество нужных правил). При этом проверяю, что коннект не идёт на адреса из списка, подключение до которых ни в коем случае не должно происходить через VPN - в качестве защиты от своих кривых рук. Ну и дальше двумя правилами закидываю в эту цепочку траффик для адресс листов youtube и facebook. Можно так, можно как в предыдущем примере - в зависимости от подхода и решаемой задачи. Как больше нравится и как удобнее.
Ну и вишенкой на торте - создаём маршруты в дикий интернет для наших новых таблиц маршрутизации
/ip route
add comment=WARP disabled=no distance=1 dst-address=0.0.0.0/0 gateway=WARP-WG routing-table=warp
add comment=VLESS disabled=no distance=1 dst-address=0.0.0.0/0 gateway=VLESS-WG routing-table=vless
/ipv6 route
add comment=WARP disabled=no distance=1 dst-address=2000::/3 gateway=WARP-WG routing-table=warp
add comment=VLESS disabled=no distance=1 dst-address=2000::/3 gateway=VLESS-WG routing-table=vless