Назад | Перейти на главную страницу

Поиск процесса владельца короткоживущих TCP-соединений

Бег tcpdump при локальных подключениях к серверу apache я обнаружил, что TCP-соединения устанавливаются и закрываются немедленно каждые 2 секунды. Как мне узнать, какой процесс за это отвечает? netstat -ctp не помогло, соединения были слишком быстрыми и идентификатор процесса не отображается для TIME_WAIT.

Оказалось, что это были прокси-зонды, которые я мог проверить с помощью strace, но я до сих пор не знаю, как определить haproxy.

Для такого рода вещей вы можете использовать структуру auditd. Они не очень "удобны для пользователя" или интуитивно понятны, поэтому с вашей стороны нужно немного покопаться.

Сначала убедитесь, что у вас установлен, запущен auditd и что ваше ядро ​​поддерживает его.
Для Ubuntu вы можете установить его с помощью apt-get install auditd например.

Затем вы добавляете политику аудита, чтобы контролировать все connect такие системные вызовы:

auditctl -a exit,always -F arch=b64 -S connect -k MYCONNECT

Если вы используете 32-битную установку Linux, вам необходимо изменить b64 на b32.

Эта команда вставит политику в структуру аудита, и все системные вызовы connect () теперь будут регистрироваться в ваших файлах журнала аудита (обычно /var/log/audit/audit.log), чтобы вы могли посмотреть.

Например, подключение netcat к порту 80 news.ycombinator.com приведет к примерно следующему:

type=SYSCALL msg=audit(1326872512.453:12752): arch=c000003e syscall=42 success=no exit=-115 a0=3 a1=24e8fa0 a2=10 a3=7fff07a44cd0 items=0 ppid=5675 pid=7270 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts4 ses=4294967295 comm="nc" exe="/bin/nc.openbsd" key="MYCONNECT"
type=SOCKADDR msg=audit(1326872512.453:12752): saddr=02000050AE84E16A0000000000000000

Здесь вы можете видеть, что приложение /bin/nc.openbsd инициировало вызов connect (), если вы получаете много вызовов connect и хотите только grep получить определенный IP-адрес или порт, вам нужно выполнить некоторое преобразование. Строка SOCKADDR содержит аргумент saddr, он начинается с 0200, за которым следует шестнадцатеричный номер порта (0050), что означает 80, а затем шестнадцатеричный IP-адрес (AE84E16A), который является IP-адресом news.ycombinator.com 174.132.225.106.

Структура аудита может генерировать много журналов, поэтому не забудьте отключить его, когда выполнили свою миссию. Чтобы отключить указанную выше политику, просто замените -a на -d как таковую:

auditctl -d exit,always -F arch=b64 -S connect -k MYCONNECT

Хорошая документация по фреймворку auditd:
http://doc.opensuse.org/products/draft/SLES/SLES-security_sd_draft/part.audit.html

Преобразование IP-адресов в / из шестнадцатеричного, десятичного, двоичного и т. Д. По адресу:
http://www.kloth.net/services/iplocate.php

Общий шестнадцатеричный / десятичный преобразователь:
http://www.statman.info/conversions/hexadecimal.html

Краткое введение в auditd от IT Security Stack Exchange. http://security.blogoverflow.com/2013/01/a-brief-introduction-to-auditd/

Редактировать 1:
Еще один быстрый (шведский: fulhack) способ сделать это - создать быстрый цикл, который выдает вам данные подключения, например:

while true;do
  ss -ntap -o state established '( dport = :80 )'
  sleep 1
done

Эта команда использует ss команда (статистика сокетов) для сброса текущих установленных соединений на порт 80, включая то, какой процесс их инициировал. Если данных много, вы можете добавить | tee /tmp/output после того, как это сделано, чтобы показать результат на экране, а также записать его в / tmp / output для последующей обработки / копания. Если он не улавливает быстрое соединение haproxy, попробуйте удалить sleep 1 но будьте осторожны с обширными лесозаготовками, если это сильно загруженная машина. При необходимости измените!

На самом деле многое изменилось с тех пор, как был задан этот вопрос. Большинство современных Linux-систем имеют расширенные возможности трассировки, которые можно использовать для этой цели.

Например: скрытая копия (некоторые дистрибутивы называют это bpfcc-tools) иметь tcpconnect утилита, которая делает именно это. Вот отрывок из официального пример:

TIME(s)  PID    COMM         IP SADDR            DADDR            DPORT
31.871   2482   local_agent  4  10.103.219.236   10.251.148.38    7001
31.874   2482   local_agent  4  10.103.219.236   10.101.3.132     7001
31.878   2482   local_agent  4  10.103.219.236   10.171.133.98    7101
90.917   2482   local_agent  4  10.103.219.236   10.251.148.38    7001
90.928   2482   local_agent  4  10.103.219.236   10.102.64.230    7001
90.938   2482   local_agent  4  10.103.219.236   10.115.167.169   7101

Другая возможность - это bpftrace утилиты, у которых есть похожие tcpconnect инструмент.

Или вы даже можете использовать простой ftrace (но в этом случае вам нужно будет написать скрипт, который будет декодировать структуру sockaddr, или делать это вручную). Например:

# Enable probe
echo 'p:tcp/connect tcp_connect sock=+0(%di):x8[32] prog=$comm' > /sys/kernel/debug/tracing/kprobe_events
echo 1 > /sys/kernel/debug/tracing/events/tcp/connect/enable

# Wait time till connect would be called by apps and check trace buffer
cat /sys/kernel/debug/tracing/trace # note sockaddr data will be encoded here
# disable tracepoint when it's done
echo 0 > /sys/kernel/debug/tracing/events/tcp/connect/enable
echo '-:tcp/connect' >  /sys/kernel/debug/tracing/kprobe_events

Заметка: в некоторых случаях вам может потребоваться смонтировать debugfs / tracefs.

Вы также можете использовать grep для огромных журналов, которые вы получаете от "ausearch -i", чтобы увидеть только те сокеты, которые успешно подключились к другому хосту в Интернете. Я написал упрощенный скрипт для получения каждого процесса и команды, которые создали сокет для подключения к хосту в Интернете, вместе с адресом подключения этого целевого хоста и текущим временем, когда сокет был «создан». Вот:

#!/bin/bash

if [[ $EUID -ne 0 ]]; then

    echo "You must run this script as root boy!"
    exit 1  

fi

> proccessConnections.dat

connections=`ausearch -i | grep host: | awk -F "msg=audit" '{print $2}' | awk -F ": saddr" '{print $1}'`

connectionsNumber=`echo "$connections" | wc -l`

echo "Number of connections: $connectionsNumber"

echo "$connections" > conTemp.dat

let counter=1
while read connectInfo; do

    success=`ausearch -i | grep "$connectInfo" | grep "type=SYSCALL" | grep success=yes`    
    addressInfo=`ausearch -i | grep "$connectInfo" | grep type=SOCKADDR | awk -F ': ' '{print $2}'`
    processInfo=`ausearch -i | grep "$connectInfo" | grep "type=SYSCALL" | awk -F 'comm=' '{print $2}' | awk -F 'key' '{print $1}'` 

    if [[ $success != "" ]]
    then    
        echo "[$counter - $connectionsNumber] (success)     comm=$processInfo - $addressInfo - $connectInfo"
        echo "[$counter - $connectionsNumber] (success)     comm=$processInfo - $addressInfo - $connectInfo" >> proccessConnections.dat
    else
        echo "[$counter - $connectionsNumber] (no success)  comm=$processInfo - $addressInfo - $connectInfo"
        echo "[$counter - $connectionsNumber] (no success)  comm=$processInfo - $addressInfo - $connectInfo" >> proccessConnections.dat
    fi

    let counter++


done < conTemp.dat