Доброго свободного времени, товарищи! В этой своей первой статье хотел бы вам рассказать как я будучи далёк от сетевых технологий перешел с роутера мыльницы на старый комп из-под дивана.

Зачем?

Как-то раз я заметил что когда начинается большая закачка файла (тогда это как раз был образ freebsd), браузер перестает загружать страницы на всех устройствах в доме. Это раз. Ну и получить опыт таких дел и приобщиться к культуре сисадминов тоже хотелось бы. Это два. Хочется сразу отметить, что это чисто любительский эксперимент. Не рекомендую использовать эту статью как пошаговое руководство, это, скорее, просто моя история из жизни.

Так вот,

Заняло это у меня 4 дня от начала реализации идеи (достал комп из-под дивана) до полноценного пользования инетом.

Про железо:

железо)))))
железо)))))
вот он, уже в другом корпусе.
вот он, уже в другом корпусе.

Это был мой первый комп. Мне он очень нравился. да и сейчас нравится. тут intel core 2 duo, 2 гига ddr2, эзернеты по 100 мегабит. материнка asrock p43twins1600. можно вставить либо 2 плашки ддр3 либо 4 ддр2. Диск вставлен ssd на 64 гига.

про реализацию:

Начну с общих принципов. Чтобы сделать маршрутизатор для локальной домашней сети нужно несколько компонентов:

  1. Межсетевой экран (у меня это PF)

  2. dhcpd, который раздает ip всем хостам локалки

  3. dns (очень важно, об этом позже)

  4. pppoe клиент (у меня это mpd5), получает ip адрес и интернет от провайдера.

  5. Две сетевые карты (у меня вс��роенная + pci)

База: одну из этих карт вы обзываете наружной, а другую внутренней. В наружную втыкается wan кабель от провайдера, во внутреннюю втыкается коммутатор и к нему уже вся ваша локалка (но не сразу!!). Межсетевой экран управляет прохождением трафика между этими двумя картами. Начнем вот с чего: вот вы установили систему, включили комп. ifconfig показывает 2 интерфейса. У меня re0 наружный, rl0 внутренний. Я так решил. Тот, что внутренний, должен иметь статик айпи. Для этого пишем:

sudo ifconfig rl0 inet 192.168.1.1 netmask 255.255.255.0

ОБРАТИТЕ ВНИМАНИЕ: я указал именно такую сеть только потому что в момент конфигурации комп находился в подсети 192.168.0.0/24, потом, в самом конце, когда все программы уже будут написаны, в каждом конфиге придется поменять 1 на 0 ( ну или оставить так, но мне приятнее когда 0, поэтому я поменял), и это может сыграть злую шутку если где-то что-то забыть. Теперь это надо зафиксировать для последующего запуска. Сразу скажу, что все, что я настраивал - я делал с другого компа по ssh из той же локалки. К маршрутизатору испытуемый подключен внешним портом re0. пишем:

sudo sysrc ifconfig_rl0="inet 192.168.1.1 netmask 255.255.255.0"

Ну или можно просто символы после слова sysrc вставить в файл /etc/rc.conf в любое место. Программа sysrc сделает это за вас (и больше почти ничего она не сделает). Теперь при перезагрузке эта конфигурация будет сохраняться. Далее нам нужно разрешить передавать данные между этими двумя интерфейсами. для этого:

sudo sysrc gateway_enable="yes"

Теперь можно попробовать настроить PF. В целом, было интересно разобраться в том, как пишутся правила в его конфиге. Инфа об этом есть и на хабре чуть чуть, и достаточно понятно написано в мане. но я покажу свой конфиг. Это сырой вариант, по мере пользования он корректируется.

udp_blocked = "{1900, 5353, 135, 137:139, 445, 161, 162}"
tcp_blocked = "{25, 135, 137:139, 445, 23}"
ext_if = "ng0"
int_if="rl0"
nonroutaddrs = "{127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 \
                169.254.0.0/16, 192.0.2.0/24, 0.0.0.0/8, 240.0.0.0/4}"
rtp_ports = "{ ssh, 53, irc, ntp, 853 }"
web_ports = "{ ftp, http, https, pop3, smtp, imap, 5222, 3478 }"
web_udp = "3478"
#options
set block-policy drop
set skip on lo0
scrub in all
altq on $ext_if hfsc bandwidth 100Mb queue {def, web, rtq}
queue web bandwidth 50% hfsc (linkshare 50%)
queue rtq bandwidth 5% hfsc (realtime (10% 20 5%))
queue def bandwidth 45% hfsc (default linkshare 45%)
#tables
table <bruteforce> persist
table <scanners> persist
#
#nat
nat on $ext_if inet from $int_if:network -> ($ext_if)
nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"
rdr pass on $int_if proto tcp from any port ftp -> 127.0.0.1 port 8021
#######filter
        block quick from <bruteforce>
        block quick from <scanners>
        pass in log quick on $ext_if proto tcp from any to any port { 23, 25, 135, 137, 138, 139, 445, 3389 } \
        flags S/SA keep state (max-src-conn 1, max-src-conn-rate 3/10, overload <scanners> flush global)
        block all
        pass in quick on $int_if from $int_if:network to any keep state
        pass out quick on $int_if from any to $int_if:network keep state
#ssh
        pass in quick on $ext_if inet proto tcp from any to ($ext_if) port ssh keep state \
         (max-src-conn 8, max-src-conn-rate 3/10, overload <bruteforce> flush global)
        block out quick on $ext_if from any to $nonroutaddrs
        block out quick on $ext_if proto tcp to any port $tcp_blocked
        block out quick on $ext_if proto udp to any port $udp_blocked
        pass out quick on $ext_if proto tcp to any port $rtp_ports keep state queue rtq
        pass out quick on $ext_if proto udp to any port $rtp_ports keep state queue rtq
        pass out quick on $ext_if proto icmp keep state queue rtq
        pass out quick on $ext_if proto tcp to any port $web_ports keep state queue web
        pass out quick on $ext_if proto udp to any port $web_udp keep state queue web
        pass out on $ext_if proto { tcp, udp } to any keep state queue def
###############

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

  1. tcp_blocked и udp_blocked это порты, на которые внутренним устройствам выходить запрещено.

  2. ext_if это интерфейс наружной карты. при испытаниях в локалке там стоял re0, на ng0 я его поменял после запуска pppoe клиента. это важно.

  3. Дальше немаршрутизируемые адреса. Нужно, чтобы пакеты которые предназначены для этих адресов не попадали в инет.

  4. rtp_ports это порты с наивысшим приоритетом. Для них будет применено правило, пропускающее по ним трафик по первому требованию (realtime)

  5. web_ports. конечно, тут не только веб. Но я для себя так обозначил порты которые имеют средний приоритет.

Дальше идут разные опции и фильтры.

  1. block-policy drop: если не принимаем пакет, не шлём никаких ответов. можно поставить return вместо block тогда будет возвращаться ответ типа "в соединении отказано".

  2. skip on l0 - не контролировать пакеты идущие на лупбэк. Логично.

  3. scrub - нормализация, см. man pf.conf

  4. altq. это инструмент для контроля пропускной способности. В данном случае я создал 3 группы по приоритету, дефолтная (низший приоритет), веб(средний) и rtq - realtime queue, самый высокий приоритет. Применяется этот фильтр к наружнему интерфейсу, ширина канала у меня 100 мегабит (такой тариф), поэтому поставил 100 мегабит. После этой строчки описывается сколько какая группа занимает ширины канала в процентах. Можно указывать в битах.

  5. Таблицы. очень интересно. Чуть позже о них.

  6. Правило для nat на наружнем интерфейсе.

  7. ftp-proxy. Нужно для протокола фтп в интернете, см. freebsd handbook

Дальше начинается сам фильтр пакетов. тут и разберемся с таблицами.

Стоит сказать, что пакет проходит последовательно по всем правилам сверху вниз, и если например его сверху разрешили, а снизу запретили, то он никуда не пойдет. Или наоборот, его сначала запретили, а потом разрешили, то пакет полетит куда надо. То есть высший приоритет имеет последнее правило. Поэтому часто пишут block all а потом ниже добавляют разрешающие правила. Исключением является слово quick. Если пакет соответствует правилу, помеченному этим словом, то это правило является для него последним, правила, стоящие ниже прос��о не учитываются.

  1. Блокируем все пакеты от ip адресов входящих в таблицу брутфорсеров. далее будет понятно как айпи адреса туда попадают.

  2. То же для сканеров портов.

  3. Далее идет строка ловушка для ребят которые захотели просканить внешние порты на моем маршрутизаторе. ох и нелегка их участь! Они попадают в таблицу сканеров.... в скобках указано условие попадание в таблицу. Превышено одно соединение от данного айпи адреса на любой из перечисленных портов (просто порты которые часто сканят). Или превышено количество попыток соединения на эти порты (3 раза в 10 секунд). Следующий же пакет от этих ребят упрётся в правило из пункта 2.

  4. Не хотелось бы рассматривать каждую строку, тут в целом все понятно. Перейдем к строке ssh. Тут я открыл порт внешний на свой сервер, но те ребята, которые захотят подключиться чаще чем 3 раза в 10 секунд попадают в таблицу брутфорсеров. Там им и место.

  5. Далее идут несколько блокировок

  6. Потом идут разрешения. И в конце присутствует слово queue и имя приоритета. Например, строка 41 пропускает пакет на любой из указанных в макросе rtp_ports порт в порядке наивысшего приоритета. Мы можем настроить таким образом любой трафик, просто в конце дописываем к какой группе он относится.

Если применяем настройки конфига по ssh то делаем так:

sudo pfctl -f /etc/pf.conf; sleep 60; sudo pfctl -d

Он включит межсетевой экран на минуту, потом выключит. Если что-то не так настроили и нас выкинет, мы сможем подсоединиться обратно через 1 минуту.

Ещё немного про altq. Пришлось пересобрать ядро. Но это совсем не сложно делается по handbook. И про таблицы: их надо чистить. желательно чтобы из них удалялись ip адреса ребят которые не проявляли активность в течение скольких-то часов. для этого пишем в crontab:

0 0 * * * root expiretable -t 24h bruteforce -t 24h scanners

(надо еще установить expiretable, можно просто из пакета). Теперь надо написать конфиг для dhcpd. Это самое простое.

subnet 192.168.0.0 netmask 255.255.255.0 {
        range 192.168.0.10 192.168.0.200;
        default-lease-time 3600;
        max-lease-time 7200;
        option broadcast-address 192.168.0.255;
        option routers 192.168.0.1;
        option domain-name-servers 192.168.0.1;
}

Все понятно. Опять же, везде стоит 0 потому что это окончательный вариант конфига. При испытании в локалке там стояла 1. Теперь включаем это все дело в /etc/rc.conf для автозапуска.

pf_enable="yes"
pf_rules="/etc/pf.conf"
dhcpd_enable="yes"
dhcpd_ifaces="rl0"

И запускаем серверы командами service <имя сервиса> start (или pfctl -e для PF)

Надо еще настроить dns. И то, как вы можете это сделать - достаточно весомая причина заниматься подобным. Вот почему:

  1. DoT. вы можете настроить шифрование ваших днс запросов и спрятать их от провайдера.

  2. DNSSEC. днс ответы подписываются цифровой подписью, что уменьшает риск атаки dns spoofing. да и в целом круто и прикольно.

  3. Кеширование. это про скорость.

Ну это не всё. Есть много положительных плюшек которые являются следствиями из перечисленного. unbound очень гибок в этом плане. При установке у меня был конфиг на 1400 строк. Можно его почитать, там почти всё - комментарии. А если быть точным, то вообще всё поначалу. Я покажу строки которые я раскоментировал и поясню их. Их можно найти поиском по файлу.

 interface: 192.168.0.1
 interface: 127.0.0.1
 msg-cache-size: 100m # размер кеша сообщений
 rrset-cache-size: 200m # 200 мегабайт кеша записей. можно поставить и больше.
 cache-min-ttl: 3600 # сколько минимум хранится запись в кеше
 cache-max-ttl: 86400 
 access-control: 127.0.0.0/8 allow
 access-control: 192.168.0.0/24 allow 
#иначе не подключимся и он не ответит
 prefetch: yes # обновляет самые популярные запросы заранее
 prefetch-key: yes # обновляет ключ dnssec
 auto-trust-anchor-file: "/usr/local/etc/unbound/root.key" # для DoT
 serve-expired: yes
 tls-cert-bundle: "/etc/ssl/cert.pem" #для DoT

 forward-zone:
        name: "."
        forward-addr: 1.1.1.1@853
        forward-addr: 1.0.0.1@853
        forward-addr: 8.8.8.8@853
        forward-addr: 8.8.4.4@853
        forward-tls-upstream: yes
        forward-no-cache: no

Вот такие дела. Теперь у нас DoT с указанными серверами. Если закоментировать forward zone, то unbound обойдётся без них. Очень круто, что мы можем регулировать размер личного кеша. Если оперативки много, то можно поставить много. Или если сеть большая. Но у меня она маленькая, да и оперативки 2 гига. Для включения днс запускаем его как сервис и пишем в /etc/rc.conf

unbound_enable="yes"

Теперь можно испытывать наш маршрутизатор в локальной сети. Можно подключить к его внутреннему эзернет порту какой-нибудь комп и набрать vim /var/db/dhcpd.leases и посмотреть выдался ли ему адрес. Когда видим что выдался, можно добавить правило в pf.conf для доступа к этому компа с внешней сети. это делается сразу после правила nat с помощью операции rdr. примерно так:

rdr pass on $ext_if proto tcp from any to ($ext_if) port 762 -> 192.168.0.10 port ssh
# дальше тут какие-то правила...
pass in quick on $ext_if proto tcp from any to 192.168.0.10 port ssh keep state
# таким образом мы подключили внешний 762 порт к ssh порту нашего компьютера в подсети.

Теперь самое волнительное. Надо настроить mpd5. С горем пополам я его настроил. Там есть дефолтный конфиг, можно его просто подредактировать. Не уверен что в моем файле все строки точно нужны, но в инете просто мало инфы о том как его настроить для провайдера, ну или я просто плохо искал.

startup:
  set user admin admin admin
  set console self 127.0.0.1 5005
  set console open
  log +auth +ipcp +link +phys +lcp
default: 
  load pppoe_client
pppoe_client: 
 create bundle static B1
 set iface route default
 set ipcp ranges 0.0.0.0/0 0.0.0.0/0
 set iface enable tcpmssfix
 set iface idle 0
 set iface enable on-demand
 set bundle disable compression
 set ipcp no vjcomp # без этой строчки я не мог получить ip

 create link static L1 pppoe
 set link disable acfcomp
 set link action bundle B1
 set auth authname MyLogin
 set auth password MyPass
 set link max-redial 0
 set link mtu 1460
 set link keep-alive 10 60
 set pppoe iface re0
 set pppoe service ""
 open

Надо еще прикрутить логи. файл /etc/newsyslog.conf:

/var/log/mpd.log 600 7 1000 * JC

Файл /etc/syslog.conf:

!mpd
*.* /var/log/mpd.log

sudo touch /var/log/mpd.log

У меня сработало примерно так.... после того как настроили, идем ногами к будущему маршрутизатору с монитором и клавой, подключаемся, меняем во всех конфигах 192.168.1.0/24 на 192.168.0.0/24, если хотим. если не хотим можем оставить. Я забыл поменять в одном месте в конфиге днс, там где access control, и все устройства дома говорили что нет инета, а пинг шел. И имена не разолвились. Зашел в конфиг, поправил. надо быть внимательным.

Пробуем пустить mpd5 и смотрим в ifconfig какой новый интерфейс появился. У меня это был ng0, поэтому идем в конфиг pf и меняем там re0 на ng0 (физический внешний интерфейс на новый виртуальный). Втыкаем провод wan, и бьем в бубен 10 раз. После этого запускаем ping, если не идет, останавливаем, смотрим логи.

Теперь у маршрутизатора в вашей квартире нет веб интерфейса. смотреть всё нужно по ssh, прокидывать порты через pf.conf, смотреть список клиентов через pftop или /var/db/dhcpd.leases. В целом удобно, если вам нравится ощущение полного контроля над своей сетью. Да, шумит он сильнее роутера. а еще ему нужен полноценный ибп. Локальная сеть подключена через коммутатор, так что вся эта мишура занимает 2 розетки. Можно это исправить, отрезать один из sata разъемов и к нему припаять разъем питания для коммутатора. Все же не зря там столько проводов из блока торчит. Но я пока не стал.

Доволен ли я? Честно сказать, я доволен, но многим это может показаться странным. Это мой первый опыт с freebsd, до этого я пользовался ubuntu c 2020-го года, А до этого абсолютно ничем. freebsd оказалась очень приятной в администрировании после линукса. Все в порядке, все на своих местах. Мне очень понравилось. Документация просто супер, handbook выручает. Своё исчадие я ещё не проверял никакими тестами на скорость, но многое стало грузиться чуть пободрее. Как только перешел с роутера на этот вариант, перестали грузиться фотки в вк. Но я погуглил и нашел какие порты использует вк, добавил недостающие в группу web и фотки полетели лучше чем раньше. Еще пару слов об altq. В режимах, представленных в моем конфиге, если например торрент качает большую раздачу, а браузер закрыт, то он может занять 100% канала, то есть скорость скачивания будет достигать 100 мегабит. Но если браузеру понадобилось свободное место, торрент отодвигается. Если браузеру понадобилось очень много, торренту все равно будет обеспечено 45%, а вот браузер в этом случае дальше чем 50% не расширится. Если же нет торрента, то ему ничего не препятствует. Таким образом, мы получили очень хороший контроль пропускной способности пакетов. Стоит ли вам этим заниматься? Это зависит от того, чего вы хотите от жизни. Можно просто поставить прошивку openwrt на свой роутер. Но там будет меньше оперативки и слабее процессор. Хотя для домашней сети это не важно. В целом функционал роутеров мыльниц может вполне устраивать. Если сетевое администрирование не является самоцелью, то скорее всего, это окажется для вас пустой тратой времени. Ну а я чувствую себя хорошо из-за того что я точно знаю какой размер кеша у моего DNS сервера, как реализована защита от разных видов атак, могу смотреть логи абсолютно всех программ, настраивать межсетевой экран настолько точно, насколько мне надо. И даже если у меня есть дырка в PF, я сам в этом виноват и если что-то случится, я точно знаю как её закрыть.