Posts tagged with “network”

L2/L3 Сегментация и изоляция домашней сети на роутере Mikrotik

Written by skeirss

Задача: изолировать мобильные и IoT-устройства от компьютера, сервера и других устройств, подключенных кабелем на LAN-порты на роутере Mikrotik с помощью VLAN.

Замечание: подключиться к роутеру лучше через IP, а не через MAC из-за некоторых технических особенностей. И не забыть включить safe-mode.

Зачем нужна L2 и L3-изоляция

IoT - недоверенный, плохо патчащийся и потенциально уже скомпрометированный сегмент, поэтому его лучше держать в отдельном L2-домене с default-deny L3-политикой в сторону LAN, оставляя только явно разрешённые направления (обычно только исходящий интернет + при необходимости точечные "дыры" к серверу автоматизации).

L2-изоляция через VLAN блокирует ARP-spoofing, ARP-poisoning и sniffing трафика внутри одного broadcast-домена - без неё скомпрометированная IoT-лампочка технически может перехватывать/подменять трафик соседних хостов.

Также VLAN ограничевает L2-discovery-протоколы (mDNS, SSDP/UPnP, NetBIOS, LLMNR) от расползания между сегментами - иначе любое IoT-устройство видит все узлы в локальной сети, которые мы не хотим, чтобы их видели.

Главная идея

Отделить wifi от основной сети сразу двумя слоями защиты:

  • VLAN разделяет сети на уровне L2 и не позволяет устройствам видеть друг друга как участников одного Ethernet-сегмента;
  • Firewall на L3 уровне контролирует, какой трафик маршрутизатор может передавать между этими разными сетями.

Краткий план действий

  • Выносим беспроводные порты wlan1 и wlan2 в изолированный VLAN
  • LAN порты ether2-5 оставляем на нативном VLAN'e (PVID 1), то есть отдельно второй VLAN не создаем
  • Создадим отдельную подсеть для wifi-устройств (192.168.99.1/24) и настроим DHCP-сервер
  • Настроим firewall, сделаем исключение для смартфона, чтобы можно было передавать файлы по wifi с помощью kde connect и других подобных приложений (не забыть сделать ip-адрес смартфона статическим на dhcp-сервере)

Приблизительная схема сети

Что делаем по шагам

Шаг 1: Создаем VLAN для Wi-Fi и назначаем IP

Создадим виртуальный интерфейс для VLAN 99 поверх бриджа и выдадим ему адрес:

/interface vlan
add interface=bridge name=vlan99-wifi vlan-id=99

/ip address
add address=192.168.99.1/24 interface=vlan99-wifi

Шаг 2: Настраиваем DHCP-сервер для Wi-Fi

Создадим пул, настроим сеть и поднимем DHCP-сервер на интерфейсе vlan99-wifi:

/ip pool
add name=pool-vlan99 ranges=192.168.99.10-192.168.99.254

/ip dhcp-server
add address-pool=pool-vlan99 interface=vlan99-wifi name=dhcp-vlan99 disabled=no

/ip dhcp-server network
add address=192.168.99.0/24 dns-server=192.168.99.1 gateway=192.168.99.1

Шаг 3: Настраиваем порты Bridge (VLAN Filtering)

Теперь мы назначаем портам wlan нужный PVID. Проводные порты (ether2-5) мы не трогаем, они останутся в PVID 1, что сохранит их работу и статические IP-адреса в сети 192.168.88.0/24.

/interface bridge port
# Мобильные устройства отправляют нетегированный трафик, поэтому admit-only-untagged
set [find interface=wlan1] pvid=99 frame-types=admit-only-untagged-and-priority-tagged
set [find interface=wlan2] pvid=99 frame-types=admit-only-untagged-and-priority-tagged

Шаг 4: Таблица Bridge VLAN и включение фильтрации

Указываем, что процессор роутера (сам bridge) должен иметь доступ к VLAN 99 (тегированный трафик), чтобы работал наш DHCP и шлюз.

/interface bridge vlan
add bridge=bridge tagged=bridge vlan-ids=99

# Включаем фильтрацию VLAN на бридже
/interface bridge set bridge vlan-filtering=yes

Шаг 5: Изоляция L3 (Firewall) и исключение для KDE Connect

Сейчас сети разделены на уровне L2, но маршрутизатор по умолчанию маршрутизирует трафик между всеми своими интерфейсами. Нужно запретить хождение пакетов между подсетями в файрволе.

# Изоляция на уровне подсети: 
/ip firewall filter
add action=drop chain=forward comment="Isolate WiFi from LAN 88" dst-address=192.168.88.0/24 \
    src-address=192.168.99.0/24
add action=drop chain=forward comment="Isolate WiFi from LAN 77" dst-address=192.168.77.0/24 \
    src-address=192.168.99.0/24
add action=drop chain=forward comment="Isolate LAN 88 from WiFi" dst-address=192.168.99.0/24 \
    src-address=192.168.88.0/24

# Исключения для смартфона с адресом 192.168.99.254
add action=accept chain=forward comment="Allow Phone to PC (KDE Connect)" dst-address=192.168.88.34 \
    src-address=192.168.99.254
add action=accept chain=forward comment="Allow PC to Phone (KDE Connect)" dst-address=192.168.99.254 \
    src-address=192.168.88.34

Важный нюанс про KDE Connect. KDE Connect ищет устройства в сети с помощью широковещательных UDP-пакетов (UDP Broadcast). Широковещательные пакеты никогда не пересекают границы VLAN из-за этого телефон и компьютер не увидят друг друга автоматически. Чтобы это исправить нужно добавить устройство по IP вручную в приложении KDE Connect.

Базовая настройка завершена. Проверяем, что всё работает. Скорее всего интернет через wifi-сегмент работать не будет из-за дефолтного firewall-правила на микротике:

/ip firewall filter add action=drop chain=input comment="defconf: drop all not coming from LAN" in-interface-list=!LAN

Проблему можно решить просто добавив интерфейс VLAN для wifi в LAN-лист, но это не очень безопасно, так как wifi-сети таким образом дается то же доверие, что и проводной LAN - то есть телефон (и вообще любой, кто подключится к wifi) сможет ломиться в Winbox/SSH/API роутера. Это прямо противоречит цели изоляции.

Правильнее - не добавлять vlan99-wifi в LAN, а точечно разрешить только то, что реально нужно (DHCP + DNS), до финального drop-правила:

/ip firewall filter
add chain=input action=accept in-interface=vlan99-wifi protocol=udp dst-port=67-68 comment="WiFi: allow DHCP to router"
add chain=input action=accept in-interface=vlan99-wifi protocol=udp dst-port=53 comment="WiFi: allow DNS to router"
add chain=input action=accept in-interface=vlan99-wifi protocol=tcp dst-port=53 comment="WiFi: allow DNS to router"

На этом настрйка завершена. Если наблюдаются какие-то проблемы, то велика вероятность в неправильной расстановке firewall-правил.

Docker container traffic routing through proxy

Written by skeirss

So today last.fm stopped working for Russia:

❯ curl -I https://www.last.fm/ | head -n1
HTTP/2 403

So here's the problem: now my dockerized Navidrome server won't scrobble tracks to last.fm, and since Navidrome doesn't support proxy configuration, I have to route all network traffic from the container through a proxy server.

Sometimes I feel like I'm between a rock and a hard place, because the restrictions are coming from both sides... But something good always comes out of every bad situation, and from this one I'll learn a bit more about proxying docker containers. Anyways.

What's my goal: make Navidrome scrobble tracks to last.fm again by routing its traffic though my host machine's Xray proxy. What's my steps:

  • Install and configure xray on my server
  • Update docker compose, add proxy variables

So first I need to update my Xray configuration. Basically I need to tell Xray to listen on all network interfaces (0.0.0.0) so the Docker container can reach it:

  "inbounds": [
    {
      "listen": "0.0.0.0",
      "port": 10808,
      "protocol": "socks"
    },
    {
      "listen": "0.0.0.0",
      "port": 10809,
      "protocol": "http"
    }
  ],

And since my server is local and working behind a router and cg-nat, it is a safe approach.

Now I need to update my docker compose file, so Navidrome can find the host and use the proxy vars:

services:
  navidrome:
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      - HTTP_PROXY=http://host.docker.internal:10809
      - HTTPS_PROXY=http://host.docker.internal:10809
      - ALL_PROXY=socks5://host.docker.internal:10808
      - NO_PROXY=localhost,127.0.0.1

Why extra_hosts? It creates a "map" so the container knows host.docker.internal = your host machine's IP. Checking that variables described in the docker file are read within the container:

 root@thinkcentre:~# docker exec navidrome-navidrome-1 env | grep -i proxy
no_proxy=localhost,127.0.0.1
http_proxy=http://host.docker.internal:10809
https_proxy=http://host.docker.internal:10809
all_proxy=socks5://host.docker.internal:10809

Looks like it's fine. Too bad we can't test connection inside the container using curl, because there's no such utility in the container. Let's just play a song and see if it is scrobbling or not: Yes, it is working as intended, yay!

Only what he finds conducive is to his taste; his pleasure, his enjoyment stops when the mark of what is conducive is overstepped. He guesses correctly what will heal harm, he exploits strokes of bad luck to his advantage; what does not kill him makes him stronger.