Несколько недель назад у меня была проблема, где я изменил адреса DNS в большой сети приблизительно 300 узлов. После этого некоторые узлы все еще продолжали спрашивать старые серверы DNS, хотя resolv.conf был в порядке, и host/nslookup запрашивали новые серверы DNS.
Смотря tcpdump и пытающийся записать запросы с входом iptables, я подтвердил, что действительно некоторые хосты все еще отправляли запросы на старые серверы имен.
Я вынул один из хостов из производства и начал закрывать сервисы / stracing процессы в попытке узнать преступника.
В конце - это был lldpd демон, который, очевидно, кэшировал серверы имен при запуске и даже не заметил изменений в resolv.conf.
Так, мой вопрос - там более интеллектуальный способ узнать, какой PId генерирует определенный вид трафика? Я попробовал auditctl, но без большого успеха. CentOS 6 рассматриваем, но если бы существует решение для какого-либо дистрибутива Linux, я ценил бы его.
В netstat
есть много опций, которые показывают комбинации прослушивающих / открытых сокетов через tcp / udp / both. Что-то вроде:
$> sudo netstat -pan
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Addr Foreign Addr State PID/Program name
...
tcp 0 1 192.168.66.1:39219 192.168.66.139:2003 SYN_SENT 2045/logstash-forwa
... дало бы вам много выходных данных, но включало источник, место назначения, номера портов и PID процесса, владеющего этими портами.
Что не так с auditctl?
Вы бы сделали это так
1) Определите правило аудита для аудита системных вызовов sendmsg и sendto. Эти системные вызовы используются при разрешении имен.
auditctl -a exit,always -F arch=b64 -S sendmsg -S sendto -k send
2) Теперь найдите свои записи аудита. Вы можете использовать команду grep на основе IP-адреса удаленного DNS здесь
ausearch -k send -i|grep -A2 "serv:53"
. В приведенном ниже примере вы можете увидеть, что приложение, которое отвечало за системный вызов, называется dig
ausearch -k send -i|grep -A2 "serv:53"
type=SOCKADDR msg=audit(10/31/2016 15:24:56.264:176998) : saddr=inet host:172.16.0.23 serv:53
type=SYSCALL msg=audit(10/31/2016 15:24:56.264:176998) : arch=x86_64 syscall=sendmsg success=yes exit=29 a0=14 a1=7fa1919f9ac0 a2=0 a3=7fa1919f9780 items=0 ppid=31729 pid=32047 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts5 ses=52 comm=dig exe=/usr/bin/dig subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=send
comm=dig exe=/usr/bin/dig
И способ определить, на какой удаленный DNS-запрос отправлен, находится здесь. Так что вам просто нужно будет найти конкретный хост DNS с помощью grep.
saddr=inet host:172.16.0.23 serv:53
Или даже лучше - посмотрите, какие хосты DNS используются (в этом примере у меня только один)
ausearch -k send -i|grep "serv:53"|awk '{print $6}'|sort|uniq -c
3 host:172.16.0.23
А затем сузьте, какие приложения используют эти конкретные хосты.
Правка 1: На самом деле я просто выполнил несколько простых запросов на хост. Похоже, sendmsg не всегда используется. Вот что я вижу
socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("172.16.0.23")}, 16) = 0
gettimeofday({1477929832, 712018}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}])
sendto(4, "\3\326\1\0\0\1\0\0\0\0\0\0\tvkontakte\2ru\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30
poll([{fd=4, events=POLLIN}], 1, 5000) = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [62]) = 0
recvfrom(4, "\3\326\201\200\0\1\0\2\0\0\0\0\tvkontakte\2ru\0\0\1\0\1\300\f"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("172.16.0.23")}, [16]) = 62
close(4) = 0
Мой предыдущий пример был основан на приложении dig, которое использует несколько иной маршрут с точки зрения системных вызовов.
Таким образом, похоже, что в большинстве случаев это правило
auditctl -a exit,always -F arch=b64 -S connect -k connect
, за которым следует ausearch
ausearch -k connect -i|grep saddr|grep "serv:53"|awk '{print $6}'|sort|uniq -c
+1 за ответ Дмитрия выше; это прекрасно сработало для меня:
auditctl -a exit,always -F arch=b64 -F a0=2 -S socket -k SOCKET
Чтобы увидеть результирующие записи, я grep файл журнала для этой строки "-k"
grep SOCKET /var/log/audit/audit.log
Чтобы получить только интересные поля,
grep SOCKET /var/log/audit/audit.log | \
cut -d' ' -f 4- | \
sed "s|^|@\n|g;s| |\n|g" | \
grep -E "^((exe|uid|comm)=|@)" | \
tr '\n@' ' \n' |\
sort -u
(объяснение: cut -d ' '-f 4- -> разделить строку на поля, используя пробел (-d' ') в качестве разделителя, показать поля с четвертого до последнего (4-))
(объяснение: sed "s | ^ | @ \ n | g; s | | \ n | g " -> отредактируйте строку, добавьте '@' char-plus-newline к началу строки, замените пробелы на новые строки)
(объяснение: grep -E "^ ((uid | comm | exe) = | @)" -> поскольку каждое поле исходной строки теперь находится на отдельной строке, выберите интересующие поля: идентификатор пользователя, команда, исполняемый файл - и начало строки '@' char.)
(объяснение: tr '\ n @' '\ n' -> теперь имея только нужные поля, превратите символы новой строки обратно в пробелы , а добавленный "@" обратно в новую строку (который объединяет поля в одну строку)
(объяснение: sort -u -> сортировать строки, показывать только уникальные строки)
дает мне:
uid=0 comm="atop" exe="/usr/bin/atop"
uid=0 comm="http" exe="/usr/lib/apt/methods/http"
uid=0 comm="links" exe="/usr/bin/links"
uid=0 comm="ntpdate" exe="/usr/sbin/ntpdate"
uid=0 comm="ufdbguardd" exe="/usr/local/ufdbguard/bin/ufdbguardd"
uid=1000 comm=536F636B657420546872656164 exe="/usr/lib/firefox/firefox"
uid=1000 comm="clock-applet" exe="/usr/lib/mate-panel/clock-applet"
uid=1000 comm="pool" exe="/usr/lib/mate-panel/clock-applet"
uid=105 comm="http" exe="/usr/lib/apt/methods/http"
uid=105 comm="https" exe="/usr/lib/apt/methods/https"
uid=135 comm="unbound" exe="/usr/sbin/unbound"
uid=13 comm="squid" exe="/usr/src/squid-4-master/src/squid"
uid=1 comm="debsecan" exe="/usr/bin/python2.7"
Команды, содержащие пробелы, кодируются в простом метод ascii в шестнадцатеричный (см. audit_logging.c ). Для декодирования замените «FF» на «ÿ» и перекодируйте это из html в ascii:
grep SOCKET /var/log/audit/audit.log | \
cut -d' ' -f 4- | sed "s|^|@\n|g;s| |\n|g" | \
grep -E "^((exe|uid|comm)=|@)" | tr '\n@' ' \n' | \
sort -u | sed "s|^[^=]*=||g;s| [^ ]*=| |g" | \
while read U C E ; do \
echo "$C" | grep -q '"' || \
{ C=\"`echo $C | sed "s|\(..\)|\&#x\1;|g" | recode h4..u8`\" ; } ; \
echo "uid=$U comm=$C exe=$E" ;
done
(объяснение: sed "s | ^ [^ =] = || g; s | [^] = | | g " -> удалите часть строки 'xxx =' - сначала: начало строки (^), за которым следует any-char-except - '=' заменяется на пробел, затем пробел, за которым следует any-char-except- '' заменен пробелом)
(объяснение: при чтении UCE; do ... done -> цикл по каждой строке, считывание каждого из трех битов данных в U, C, E (идентификатор пользователя, команда, исполняемый файл))
(объяснение: echo "$ C" | grep -q '"' || -> проверьте поле команды, чтобы увидеть если он содержит двойные кавычки - если нет ('||'), выполните следующие действия:)
(объяснение: {C = \ " echo $ C | sed" s | \ (.. \ ) | \ & # x \ 1; | g "| recode h4..ascii
\";} -> распечатайте командную строку, измените каждую пару символов 'FF' на ',', затем пройти через gnu 'recode', чтобы преобразовать их из HTML-сущностей в символы ascii.)
(объяснение: echo "uid = $ U comm = $ C exe = $ E " -> распечатать измененную строку)
Это дает мне вывод (просто показывая декодированную строку):
uid=1000 comm="Socket Thread" exe="/usr/lib/firefox/firefox
/ j
Несколько дней назад я боролся с той же проблемой и придумал очень простой метод. Это основано на том факте, что отправляющий процесс будет ждать ответа DNS, на том же порту, на котором он отправил запрос от :
iptables -j LOG
lsof -i UDP: <порт_источника>
, чтобы узнать, какой процесс ожидает ответа на этом порту. Конечно, когда ответ приходит в миллисекунды, вы не можете сделать это вручную; более того, даже в автоматическом режиме нет гарантии, что вы сможете запросить систему до того, как придет ответ DNS и процесс отправки завершится. Вот почему, прежде чем даже выполнить вышеупомянутые шаги, я также настраиваю Контроллер трафика ядра на задержку исходящих пакетов, направленных на определенный IP / порт (с помощью модуля tc
netem
). Это позволяет мне контролировать временное окно, в котором я должен запрашивать систему о том, какой PID ожидает ответа DNS, на исходном UDP-порту, полученном на шаге 1.
Я автоматизировал вышеуказанные шаги, включая tc
задержка в небольшом скрипте, называемом ptrap (который является более общим решением, не ограниченным запросами DNS, таким образом, подходящим для обнаружения процессов, использующих любой протокол на основе TCP / UDP). С его помощью я узнал, что в моем случае служба, связывающаяся со старым DNS-сервером, была sendmail.
На вершине находится . Существует модуль ядра (
netatop
) и демон, который заставит поверх
отслеживать использование сети процессом.
Сначала вы должны установить поверх
Вот как вы установить модуль ядра. Это действительно, когда сообщение было написано, но оно может устареть:
sudo apt install linux-headers-$(uname -r) make zlib1g-dev
wget https://www.atoptool.nl/download/netatop-2.0.tar.gz
tar xvf netatop-2.0.tar.gz
cd netatop-2.0
make
sudo make install
sudo modprobe -v netatop
Если у вас есть systemd, создайте служебный файл netatopd.service
файл в / etc / systemd / system /
. Он будет содержать:
[Unit]
Description=NetAtop Daemon
[Service]
Type=forking
ExecStart=/usr/sbin/netatopd
[Install]
WantedBy=multi-user.target
Теперь вы можете включить демон:
sudo systemctl enable netatopd
Чтобы увидеть использование сети в реальном времени для каждого процесса:
sudo atop -n
Чтобы увидеть 3 самых интенсивных в сети за день:
atopsar -N
man atopsar
для получения дополнительной информации варианты.
lsof
будет подходящим инструментом для мониторинга определенного порта и определения PID, который генерирует на нем трафик. Например, здесь я отслеживаю на сервере TCP-порт 53 DNS / домена, чтобы определить, какой PID вызывает поиск DNS:
$ lsof -PniTCP:53 -r 1 | grep :53
Теперь, если бы я отправил некоторый трафик curl
на DNS-сервер:
$ curl -v telnet://192.168.3.182:53
* About to connect() to 192.168.3.182 port 53 (#0)
* Trying 192.168.3.182...
* Connected to 192.168.3.182 (192.168.3.182) port 53 (#0)
мы увидим следующий тип вывода из приведенной выше команды:
curl 4953 root 3u IPv4 197807920 0t0 TCP 192.168.23.48:50688->192.168.3.182:53 (ESTABLISHED)
curl 4953 root 3u IPv4 197807920 0t0 TCP 192.168.23.48:50688->192.168.3.182:53 (ESTABLISHED)
curl 4953 root 3u IPv4 197807920 0t0 TCP 192.168.23.48:50688->192.168.3.182:53 (ESTABLISHED)
Вышеупомянутая команда, которая отслеживает трафик порта 53, работает, помещая lsof
в повторяющийся цикл, который выполняется каждую 1 секунду, -r 1
. Затем мы говорим lsof
сообщать только о трафике, который использует порт 53, iTCP: 53
. -Pn
инструктирует lsof
отображать имена хостов и порты в виде чисел, а не фактических имен.
Затем мы используем grep
для чтения вывода, поступающего из lsof
и отфильтруйте вывод так, чтобы мы видели только трафик порта : 53
.
PID процесса, отправляющего трафик, находится в выводе, отображаемом lsof
тоже. Во 2-м столбце показан PID, 4953.