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