查找短暂的 TCP 连接所有者进程

查找短暂的 TCP 连接所有者进程

在与 Apache 服务器的本地连接上运行tcpdump,我发现 TCP 连接每 2 秒建立一次并立即关闭一次。如何找到负责这些连接的进程?netstat -ctp没有帮助,连接速度太快,并且 TIME_WAIT 连接不显示进程标识符。

它们原来是 haproxy 探测器,我可以用 来验证strace,但我仍然不知道首先如何精确定位 haproxy。

答案1

您可以使用 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 连接到 news.ycombinator.com 端口 80 将导致如下结果:

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),即 news.ycombinator.com 的 IP 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

来自 IT 安全 Stack Exchange 的 auditd 简介。 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但如果它是一台使用率很高的机器,请谨慎进行大量日志记录。根据需要进行修改!

答案2

事实上,自从提出这个问题以来,很多事情都发生了变化。大多数现代 Linux 系统都具有可用于此目的的高级跟踪功能。

例如:密件抄送(有些发行版称其为bpfcc-tools)有tcp连接实用程序,它就是这么做的。以下是来自官方的一段摘录例子

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具有类似tcp连接工具。

或者你甚至可以使用普通的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。

答案3

您还可以 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

相关内容