FastNetMon

среда, 4 декабря 2097 г.

Решение по обнаружению DDoS атак - FastNetMon



Всем привет! Как многие знают, я являюсь автором системы обнаружения атак FastNetMon.

FastNetMon поволяет обнаружить в Вашей сети узел, на который идет атака и заблокировать либо весь трафик до этого узла (силами BGP Blackhole на стороне роутеров в Вашей сети, а также с помощью сети оператора) либо отсечь только паразитный трафик силами BGP Flow Spec.

FastNetMon предоставляет большое количество динамической информации о Вашей сети и предоставляет удобный интерфейс для доступа к ней:


FastNetMon поддерживает почти все оборудование имеющееся на рынке и реализует следующие протоколы захвата трафика: 
  • sFlow v5
  • Netflow v5, v9, v10
  • IPFIX
  • SPAN/Mirror

С точки зрения платформ FastNetMon поддерживает почти все топовые дистрибутивы Linux, имеется в официальных портах FreeBSD, а также недавно добавлен в проект Debian.

Добро пожаловать на официальный сайт проекта: https://fastnetmon.com

суббота, 26 октября 2019 г.

Пожалуйста, не используйте DNS протокол для вашего приложения!

Отличный заголовок, не правда ли? Я думаю многие знают, что уже почти 3.5 года я работаю исключительно с протоколом DNS и за это время у меня накопилось довольно большое количество способов выстрелить себе в ногу на пустом, казалось бы, месте.

Протокол DNS отлично подходит для задачи, чтобы привести посетителей на Ваш сайт / ресурс, но на этом его задача должна заканчиваться. Если вам требуется раздача какой-то конфигурации (почему бы не сделать это используя TXT запись?), списка доступных узлов для приложения (также известного как service discovery в быту) или что-то еще не связанное напрямую с браузерами конечного клиента (также известного как eyeballs), то, пожалуйста, не используйте DNS.

Итак, проследим процесс разработки клиента для DNS протокола с нуля. Сразу скажу, что количество хороших библиотек для работы с DNS очень небольшое, я могу порекомендовать лишь MiekG DNS, хотя как клиент-библиотека он также не подходит. Почти все реализации DNS клиентов присутствующие в стандартной библиотеке языков программирования излишне упрощены и имеют не особенно удачные интерфейсы, скрывающие множество важных деталей. Я уверен, что мой список рекомендованных библиотек не полон, но это не говорит о том, что их нету.

Мы убедились, что реализация протокола DNS для нашего языка отсутствует и мы начали реализацию собственного клиента. Общеизвестно, что DNS - это один из тех немногих протоколов, который использует UDP протокол и мы уже подготовили UDP клиент, устанавливющий соединение с удаленным узлом (на котором ожидается работающий рекурсивный DNS резолвер) по порту 53.

Следующий шаг, попытаться сказать DNS серверу, что мы конкретно от него хотим, тут перед нашим взглядом предстает картина (любезно взятая с inacon.de):


Выглядит страшно, но мы видим поле Questions и можем догадаться, что именно туда нам нужно добавить наш запрос (естественно, закодированный в особенном формате).

Первая проблема, с которой вы можете столкнуться - это то, что слово Question здесь во множественном числе. Нет, возможности запросить более 1й записи в реально работающих реализация DNS серверов в 99% случаев нету, поэтому задать вы можете сугубо один вопрос.

Но что если Ваше приложение поддерживает dual stack (IPv4 + IPv6 адреса) и вам не хочется делать два последовательных запроса (A и потом AAAA, хотя я бы рекомендовал обратную последовательность). Очевидно, на выручку приходит тип запроса ANY, который вернет все записи (на самом деле - не все, далеко не все). 

Это еще одна ловушка! Многие провайдеры DNS отказываются от типа ANY и существует RFC, стандартизирующий это поведение. Из крупных провайдеров, например, 1.1.1.1 не поддерживает этот тип и вы получите NOTIMP в ответ. Поэтому вам придется делать разрешение имен последовательно.

Итак, мы добрались до момента, когда мы создали DNS запрос, требующий у сервера A запись о домене stableit.ru. Как же ответит нам сервер и ответит ли? Начнем с того, что он может не ответить, но точная установка лимита, как долго ждать ответа и сколько раз повторять его - очень сложная задача. В дикой среде (простите, не могу перевести in the wild) Интернета эта цифра может варьироваться от сотен миллисекунд до нескольких тысяч миллисекунд. Поэтому будьте очень аккуратны с лимитом.

Вуаля! Удача! Сервер ответил! Возвращаемся к картинке упомянутой ранее, там есть поля Answer и очевидно ответ будет там! Как же понять, что поиск запрошенного имени был успешен? Смотрим внимательнее и видим поле rCode. Что делает квалифицированный программист? Он идет на сайт IANA и смотрит документированные значения для этого поля.

Еще пару секунд поиска и о чудо - мы нашли NoError! Очевидно, таким образом сервер сообщает, что поиск имени был успешный! Ура! 

Теперь снова смотрим картину со структурой пакета и пытаемся найти, где именно искать ответ. В чем разница между answer, authority и additional? Совершенно не ясно, но методом тыка мы можем обнаружить, что ответ нам пришел в answer. 

Всегда ли это будет так? Нет! В протоколе DNS совершенно нормальным (также известным как NODATA) считается вернуть rCode установленный в NoError и пустую секцию Answer! При этом в секции authority будет совершенно не нужная нам запись SOA.

Итак, мы приходим к выводу, что определить успешность DNS запроса можно двумя проверками: rCode == NoError и секция anwer не пуста.

Ура! Мы почти близки к работающему стабильно DNS клиенту и радостно едем с офиса домой, где решаем продолжить работу. И вот незадача - клиент перестал работать и падает с фатальной ошибкой, потому что rCode == NoError, секция Answer не пуста, но в ней нету A записи! В ней какая-то "CNAME", указывающая на другой сервер и никаких следов IP адреса! Вот тут-то мы и приближаемся к развязке и причинам моего предостережения от использования DNS для приложений кроме случаев, где иных вариантов не существует.

Да, возврат CNAME совершенно легитимен и используется различными реализациями рекурсивных серверов, кроме одного CNAME также может быть возвращена цепочка CNAME или даже CNAME и А запись. Каждый из этих случаев должен быть обработан и время от времени требуется еще следующий (рекурсивный) запрос, чтобы получить IP адрес целевого сервера из CNAME. 

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

Полгода спустя вы начинаете замечать, что свеже добавленные 15-ть серверов к 15ти существуюшим (которые ваше приложение-клиент получает силами DNS запроса) не получают трафика вообще! В чем дело? После часов-дней поиска, вы обнаружите, что DNS сервер возвращает 15 (зависит от случая и длины зоны, но порядок числа остается). Итак, знакомьтесь мы уперлись в лимит длины DNS ответа - 512 байт. Что в этом случае делать? Расчехлять навыки работы с протоколом TCP и реализовывать логику, чтобы в случае, когда по протоколу UDP сервер вернул ответ с флагом TC, то запрос должен быть повторен по TCP, который не имеет размеров на число записей.

Мои поздравления, мы учли почти все довольно очевидные проблемы в реализации надежного и стабильно работающего со всем многообразии авторитативных и рекурсивных DNS серверов клиента!  

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

Ах да, еще. Почти невозможно отличить временный сбой DNS сервера от постоянного, так как в обоих случаях Вы будете видеть SERVFAIL.

Надеюсь, после всего этого страха Вы не будете использовать протокол DNS там, где он не нужен и будете раздавать конфигурацию вашим мобильным (или серверным) приложениям в формате JSON используя хорошо зарекомендовавший себя протокол HTTP.

Спасибо за внимание!  





суббота, 22 июня 2019 г.

PHP FPM dynamic, ondemand or static?

Часто встает вопрос, в каком режиме использовать PHP FPM? Вот есть отличная статья, дающая ответ.

Установка Salt на Ubuntu 18.04

Довольно долгое время раньше я использовал Puppet, но он был крайне проблемен с точки зрения производительности, а также с точки зрения сложности конфигурации. В этом плане Salt намного лучше и понятнее, что я и постараюсь показать.

Как обычно, официальные инструкции можно найти тут и тут

Для работы нам потребуется по меньшей мере две машины - master (сервер) и minion (клиент).

Как для мастера, так и для клиента требуется добавить официальный репозиторий:
wget -O - https://repo.saltstack.com/apt/ubuntu/18.04/amd64/latest/SALTSTACK-GPG-KEY.pub | sudo apt-key add -
echo "deb [arch=amd64] http://repo.saltstack.com/apt/ubuntu/18.04/amd64/latest bionic main" | sudo tee /etc/apt/sources.list.d/saltstack.list
sudo apt-get update
Итак, установим мастер:
sudo apt-get install salt-master
 Так как мастер будет слушать на двух портах, я настоятельно рекомендую сразу же закрыть его фаерволлом от всех машин кроме клиента.

Для этого можно использовать пакет iptables-persistent, ставим его:
sudo apt-get install -y iptables-persistent
 Открываем файл конфигурации
sudo vim /etc/iptables/rules.v4
Создаем конфигурацию (xx замените на адрес клиента, если их неколько, добавьте несколько записей):
 # Generated by iptables-save v1.6.1 on Sat Jun 22 13:34:44 2019
*filter
:INPUT ACCEPT [278:38814]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [231:33098]
# Allow access from clients
-A INPUT -s xx/32 -p tcp -m tcp --dport 4505 -j ACCEPT
-A INPUT -s xx/32 -p tcp -m tcp --dport 4506 -j ACCEPT
# And from localhost
-A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 4505 -j ACCEPT
-A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 4506 -j ACCEPT

# Block all traffic to external hosts to Salt Master
-A INPUT -p tcp -m tcp --dport 4505 -j DROP
-A INPUT -p udp -m udp --dport 4505 -j DROP
-A INPUT -p tcp -m tcp --dport 4506 -j DROP
-A INPUT -p udp -m udp --dport 4506 -j DROP
COMMIT
# Completed on Sat Jun 22 13:34:44 2019
После этого применяем изменения и убеждаемся: что правила добавлены в фаерволл iptables:
sudo systemctl restart netfilter-persistent
Теперь установим клиента:
sudo apt-get install salt-minion
После этого, нам нужно указать адрес мастер сервера в его конфигурации:
sudo vim /etc/salt/minion
И задаем адрес мастера в следующем параметре:
master: xx.aa.zz.bb
Кроме этого, нам потребуется ключ мастер, который нужно получить на мастер сервере следующим образом:
salt-key -F master
And copy string from "master.pub" from Local Key section and insert into /etc/salt/minion in following place:
master_finger: "xx.aa.xx.ggg"
Этот ключ позволит клиенту убедиться, что мастер именно тот, который нам требуется.

После этого применяем конфигурацию:
sudo systemctl restart salt-minion.service
После этого клиент (minion) запросит авторизацию у мастер сервера, илем на мастер сервер и запрашиваем список активных запрсов на авторизацию:
sudo salt-key -L
В ответ вы увидите что-то вида:
Accepted Keys:
Denied Keys:
Unaccepted Keys:
client_hostname
Rejected Keys: 
Одобряем запрос авторизации:
sudo salt-key --accept=client_hostname
Все, после этого с мастер сервера вы можете посылать любые командый конфигурации:
 salt '*' test.version
Но это лишь половина дела, наша задача заключается в автоматическом конфигурировании сервисов на клиенте и это также очень просто!

Создаем основную конфигурацию:
sudo vim /srv/salt/top.sls
Где указываем следующее:
base:
  'client_hostname':
    - php_fpm_setup
Этот файл представляет собой точку входу для конфигурации Salt и здесь можно управлять тем, какие конфигурации должны быть применены на какого клиента.

Итак, создадим саму конфигурацию:
sudo vim /srv/salt/php_fpm_setup.sls
Вот такого вида:
nginx_pkg:
  pkg.installed:
    - name: nginx
nginx_service:
  service.running:
    - name: nginx
    - enable: True
    - require:
      - pkg: nginx_pkg
fpm_pkg:
  pkg.installed:
     - name: php7.2-fpm
fpm_service:
  service.running:
    - name: php7.2-fpm
    - enable: True
    - require:
      - pkg: fpm_pkg
Данный state файл установит на сервер пакеты nginx и php-fpm и настроит их автозапуск.

После этого, данный state можно применить как со стороны клиента:
salt-call state.apply

Так и удаленно, со стороны сервера:
salt '*' state.apply
Выдача в обоих случаях будет аналогичная:
 ---------
          ID: nginx_pkg
    Function: pkg.installed
        Name: nginx
      Result: True
     Comment: All specified packages are already installed
     Started: 15:44:59.405480
    Duration: 57.533 ms
     Changes: 
----------
          ID: nginx_service
    Function: service.running
        Name: nginx
      Result: True
     Comment: The service nginx is already running
     Started: 15:44:59.463924
    Duration: 54.275 ms
     Changes: 
----------
          ID: fpm_pkg
    Function: pkg.installed
        Name: php7.2-fpm
      Result: True
     Comment: All specified packages are already installed
     Started: 15:44:59.518411
    Duration: 18.16 ms
     Changes:   
Все! Таким образом, у вас все конфигурации всех серверов будут находится в одном месте, сразу же рекомендую сделать /srv/salt git репозиторием.

суббота, 18 мая 2019 г.

Ubuntu 14.0 and Stopping system V runlevel compatibility

Recently I hit this issue after removing all X-related packages from server.

Root cause was some missing packages from lightdm.

NB! Please use this suggestions ONLY if you need only console interface on your server.

You can fix it by removing all configuration files which belong to lightdm:
sudo apt-get purge lightdm

воскресенье, 16 декабря 2018 г.

parted: Warning: The resulting partition is not properly aligned for best performance.

Эта ошибка, уверен, достает каждого! Даже в самом простом случае, когда нужен 1 раздел на весь диск - почти невозможно подобрать значения под конкретный диск, которые бы устроили parted.

Идеальный вариант такой.

Сначала создаем таблицу gpt на диске (все данные будут уничтожены):
parted /dev/sda
mklabel gpt

После этого создаем один раздел на весь диск:
parted /dev/sda
unit s
mkpart main ext4 0% 100%
Проверяем, что parted правда удовлетворен выравниванием вновь созданного раздела:
parted /dev/sda align-check optimal 1
Ответ должен быть "aligned".

За идею спасибо: https://blog.hqcodeshop.fi/archives/273-GNU-Parted-Solving-the-dreaded-The-resulting-partition-is-not-properly-aligned-for-best-performance.html 

суббота, 3 ноября 2018 г.

Installing 4.19 on Ubuntu 14.04

I'm playing with AF_XDP and this feature requires very fresh kernel. I decided to use 4.19 kernel from Ubuntu Kernel PPA.

But unfortunately, these packaged depend of very fresh version of linux-base package and it availabe only in Ubuntu 16.04 or 18.04. And I will help with this :)

First of all, install linux-base:
sudo apt-get install -y  linux-base
And then we will repack deb package to remove dependency on very fresh linux-base. I used guide from ServerFault.

Download original packages:
cd
mkdir -p deb_4_19_kernel_hacked_package
cd deb_4_19_kernel_hacked_package
wget -c http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.19/linux-image-unsigned-4.19.0-041900-generic_4.19.0-041900.201810221809_amd64.deb
Unpack deb:
ar x linux-image-unsigned-4.19.0-041900-generic_4.19.0-041900.201810221809_amd64.deb
tar -xf control.tar.xz
rm control.tar.xz
Remove nasty dependency:
sed -i 's/linux-base (>= 4.5ubuntu1~16.04.1)/linux-base/' control
Pack control file again:
tar --ignore-failed-read -cvJf control.tar.xz {post,pre}{inst,rm} md5sums control

Build deb package again:
ar rcs linux-image-unsigned-4.19.0-041900-generic_4.19.0-041900.201810221809_amd64_fixed_dependencies.deb debian-binary control.tar.xz data.tar.xz
And install it:
wget -c http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.19/linux-modules-4.19.0-041900-generic_4.19.0-041900.201810221809_amd64.deb
sudo dpkg -i linux-modules-4.19.0-041900-generic_4.19.0-041900.201810221809_amd64.deb
sudo dpkg -i linux-image-unsigned-4.19.0-041900-generic_4.19.0-041900.201810221809_amd64_fixed_dependencies.deb
Finally, rebuild grub configuration:
sudo update-grub
And confirm that Grub added new kernel to configuration:
cat /boot/grub/grub.cfg 
After that, prepare keyboard, monitor, IPKVM and start reboot process :) If you're lucky guy then you can get following:
uname -a
Linux flab1 4.19.0-041900-generic #201810221809 SMP Mon Oct 22 22:11:45 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux


воскресенье, 8 апреля 2018 г.

Обновление Ubuntu 16.04 на Ubuntu 18.04 LTS (beta)

Сразу оговорюсь, что крайне не рекомендую этого делать для продакшена. Только сугубо для целей тестирования.

Для начала, нужно обновиться до упора в рамках текущей версии Ubuntu 16.04:
sudo apt update; sudo apt dist-upgrade; sudo apt autoremove
Устанавлим менеджер обновлений:
sudo apt-get install update-manager-core
Убеждаемся, что у нас стоят настройки для только LTS дистрибутивов:
sudo cat /etc/update-manager/release-upgrades
Выдача должна быть следующая:
Prompt=lts
Запускаем процесс:
sudo do-release-upgrade 

Если будет выдано, что не обнаружено релиз версий (скорее всего так и будет на момент публикации пока LTS еще не вышел), то нужно добавить флаг -d:
sudo do-release-upgrade -d
В процессе обновления будет задано множество вопросов, самые сложные, когда меняется конфигурация какого-либо демона.

Например, будьте аккуратны с ssh, если работает от root, так как при обновлений на новый конфиг будет удалена опция PermitRootLogin yes и её нужно вернуть вручную в файле /etc/ssh/sshd_config



воскресенье, 25 февраля 2018 г.

воскресенье, 28 января 2018 г.

Как применить grep на выдачу tpcdump?

Есть магическая опция, которая меняет поведенеи буферизации grep начинает работать по мере того, как tcpdump выводит данные:
sudo tcpdump -i eth0 port 80 -n |grep some_value_filtering --line-buffered

понедельник, 1 января 2018 г.

С Новым 2018м Годом! :)

Поздравляю с Новым Годом! :) Пусть новый год будет полон свершений, которые будет не стыдно добавить в резюме! :)

С Праздником! :)

среда, 22 ноября 2017 г.

Сборка актуальной версии trafgen из пакета netsniff-ng на Ubuntu 14.04

cd /usr/src
git clone git://github.com/netsniff-ng/netsniff-ng.git
cd netsniff-ng
apt-get install -y libcli-dev libnet1-dev libnacl-dev  libpcap-dev liburcu-dev ccache libgeoip-dev libnetfilter-conntrack-dev  libncurses5-dev libnl-3-dev flex bison libnl-genl-3-dev libnl-3-dev libnl1 zlib1g-dev libnl-route-3-dev
./configure  --prefix=/opt/netsniff-ng
make
make install

Потом берем шаблон флуда: https://raw.githubusercontent.com/netoptimizer/network-testing/master/trafgen/udp_example01.trafgen


Кроме этого обязательно нужно обновить ядро до версии 4.4 и выше из пакета hardware support! Ибо используются модные AF_PACKET и чем новее ядро - тем будет быстрее.

Потом открываем файл udp_example01.trafgen и корректируем там MAC адрес целевой машины. А также программируем порты и прочее.

После этого запускаем:
/opt/netsniff-ng/sbin/trafgen --cpp --dev eth1 --rand --num 0 -i udp_example01.trafgen --cpp --ring-size 500KiB  --cpu 8
В случае ошибки "Cannot allocate TX_RING" рекомендую убрать --ring-size вовсе.

 Итого, на Intel(R) Core(TM) i7 CPU         950  @ 3.07GHz удалось выжать 3.5 MPPS ценой полной загрузки всех ядер машины:

  1  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]     5  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
  2  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]     6  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
  3  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]     7  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
  4  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]     8  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||100.0%]
  Mem[|||                                                   190/48400MB]     Tasks: 32, 65 thr; 9 running
  Swp[|                                                        3/8190MB]     Load average: 0.64 1.37 4.28
                                                                             Uptime: 01:23:24

  PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
 3728 root       20   0 13616  1760  1364 R 100.  0.0  0:09.22 /opt/trafgen --cpp --dev eth1 --rand --num 0 -i udp_example01.trafgen --cpp --ring-size
 3732 root       20   0 13616  1780  1384 R 100.  0.0  0:09.22 /opt/trafgen --cpp --dev eth1 --rand --num 0 -i udp_example01.trafgen --cpp --ring-size
 3721 root       20   0 13620  1780  1384 R 100.  0.0  0:09.22 /opt/trafgen --cpp --dev eth1 --rand --num 0 -i udp_example01.trafgen --cpp --ring-size
 3722 root       20   0 13616  1764  1368 R 99.0  0.0  0:09.20 /opt/trafgen --cpp --dev eth1 --rand --num 0 -i udp_example01.trafgen --cpp --ring-size
 3724 root       20   0 13616  1780  1384 R 99.0  0.0  0:09.21 /opt/trafgen --cpp --dev eth1 --rand --num 0 -i udp_example01.trafgen --cpp --ring-size
 3723 root       20   0 13616  1768  1372 R 99.0  0.0  0:09.21 /opt/trafgen --cpp --dev eth1 --rand --num 0 -i udp_example01.trafgen --cpp --ring-size
 3725 root       20   0 13616  1780  1384 R 99.0  0.0  0:09.20 /opt/trafgen --cpp --dev eth1 --rand --num 0 -i udp_example01.trafgen --cpp --ring-size
 3730 root       20   0 13616  1768  1372 R 98.0  0.0  0:09.12 /opt/trafgen --cpp --dev eth1 --rand --num 0 -i udp_example01.trafgen --cpp --ring-size


пятница, 10 ноября 2017 г.

Отличные инженерные вакансии компании Cloudflare Inc

Всем привет! :)

Знаю, что меня читает много отличных инженеров! Поэтому предлагаю рассмотреть замечательную компанию, в которой очень много прекрасных инженерных позиций - Cloudflare. Полный список вакансий тут: https://www.cloudflare.com/careers/departments/

Если Вам нравится Linux, C, Go или Вам более близок фроентед - милости просим :) 

вторник, 7 ноября 2017 г.

Как сделать из Mac OS NAT сервер чтобы дать доступ в интернет через машину на Mac?

Я все буду делать в macOS High Sierra.

Включаем форвардинг в ядре:
sudo sysctl -w net.inet.ip.forwarding=1
На внутреннем ифейсе задаем IP адрес (без этого не создаст, но потом почему-то поменяет на новый, очень странно он ведет себя):
sudo ifconfig en6 10.10.10.1
Если внешний интерфейс (с интернетом) это en0, а внутренний, которому требуется дать интернет - en6, то сделать это так:
echo "nat on en0 from en6:network to any -> (en0)" > rules
sudo pfctl -f ./rules -e
Очистка правил так: 
sudo pfctl -d
Источник: https://roelant.net/2015/share-your-vpn-mac-el-capitan.html