Web 管理脚本

作者:Dave Taylor

在过去一个月左右的时间里,我还一直在处理针对我的服务器的恶意 DDOS(即“分布式拒绝服务”)攻击,正如您可能预料到的那样,这非常令人头疼。奇怪的是,在同一服务器上的多个域名中,似乎是我不太受欢迎的网站成为了攻击目标。

因此,这就是本文脚本的出发点:分析日志文件以了解正在发生的事情以及原因。

首先,一个方便的检查是查看正在运行的进程数量,因为我的 DDOS 的特点是触发了大量的评论和搜索脚本——每分钟数百个。如何检查?

ps 命令在任何给定时间提供正在运行的进程列表,但对于许多版本,您只能看到 Web 服务器“httpd”,而没有任何进一步的详细信息。-C cmd 标志将输出范围缩小到仅限这些进程,如下所示


: ps -C httpd
  PID TTY          TIME CMD
20225 ?        00:13:21 httpd
28162 ?        00:00:01 httpd
...
5681 ?        00:00:00 httpd
5683 ?        00:00:00 httpd <defunct>
]]>

(请注意即将消失的“defunct”进程。)

因此,一个简单的测试是查看正在运行多少个 httpd 进程


$ ps -C httpd | wc -l
108

这看起来很多,但此服务器托管了多个站点,包括超级繁忙的 AskDaveTaylor.com 技术支持站点,该站点每天的访问量超过 10 万次。那么,这随时间变化如何呢?嗯...仍在命令行上工作


$ while /bin/true
> do
>   ps -C httpd | wc -l
>   sleep 5
> done
108
107
103
99
94
91
87
84
91
121
120
116

因此,最大值为 121,最小值为 87。但是,如果我实际上想分析它并获得更长时间段内的最小值、最大值和平均值呢?这是我的解决方案


#!/bin/sh
# Calculates the number of processes running that matches
#   a set pattern over time, producing min, max and average.
min=999; max=0; average=0; tally=0; sumtotal=0
pattern="httpd"  # ps -C pattern
while /bin/true
do
  count=$(ps -C $pattern | wc -l)
  tally=$(( $tally + 1 ))
  if [ $count -gt $max ] ; then
    max=$count
  fi
  if [ $count -lt $min ] ; then
    min=$count
  fi
  sumtotal=$(( $sumtotal + $count ))
  average=$(( $sumtotal / $tally ))
  echo "Current ps count=$count: min=$min, max=$max, tally=$tally 
  ↪and average=$average"
  sleep 5 # seconds
done
exit 0

请注意,在脚本中,我没有陷入通过使用运行平均值并将最新值作为递减的加法因子来计算平均值的陷阱,而是使用了一个 sumtotal 变量,该变量不断地添加最新的处理器计数。这个值除以 tally 始终是平均值,尽管在某个时候这可能会大于 MAXINT (2**32) 并开始产生错误的结果。然而,在现代计算机上,这应该需要一段时间。(量子,即迭代之间的时间段,也可以调整。对于将要运行数小时甚至数天的进程来说,五秒可能太精细了。)

以下是输出的前几行。请注意 minmax 如何随着不同值的计算而变化


sh processes.sh
Current ps count=132: min=132, max=132, tally=1 and average=132
Current ps count=128: min=128, max=132, tally=2 and average=130
Current ps count=124: min=124, max=132, tally=3 and average=128
Current ps count=123: min=123, max=132, tally=4 and average=126

如果我让脚本运行更长时间,这些值会变得更加多样化


Current ps count=90: min=76, max=150, tally=70 and average=107

在我运行脚本的 15 分钟左右的时间里,平均有 107 个“httpd”进程在运行,最小值为 76,最大值为 150。

掌握了这些信息,另一个脚本可以通过 cron 作业来监视情况,如下所示


#!/bin/sh
# DDOS - keep an eye on process count to 
# detect a blossoming DDOS attack
pattern="httpd"
max=200   # avoid false positives
admin="d1taylor@gmail.com"
count="$(ps -C $pattern | wc -l)"
if [ $count -gt $max ] ; then
  echo "Warning: DDOS in process? Current httpd count = 
  ↪$count" | sendmail $admin
fi
exit 0

然而,这是一个肤浅的解决方案,它有两个问题:1) 我真正想要的是能够根据处理器计数来识别潜在的 DDOS,并观察它是否在脚本的后续几次调用中持续存在,以及 2) 一旦它被触发,如果它是一个 DDOS,除了其他一切之外,我还会开始被来自此脚本的电子邮件淹没,这些电子邮件每次都基本上说同样的事情。不好。

该脚本需要上下文记忆,以便它可以区分流量的突然峰值和持续的 DDOS 攻击。在前一种情况下,脚本可能会触发肯定响应,然后下次运行时,一切又都在可接受的限制范围内。在后一种情况下,一旦攻击开始,它可能会加速。

但这与电子邮件非重复条件相反,因为在后一种情况下,我想知道电子邮件已经发送,并且在例如 60 分钟的窗口内不再发送它。

我将在下次深入探讨这两种情况。现在,我需要回到我的服务器并继续逐个程序地恢复运行,以尽量避免任何问题。敬请期待!

Dave Taylor 在 UNIX 和 Linux 系统上破解 shell 脚本已经很长时间了。他是 Learning Unix for Mac OS XWicked Cool Shell Scripts 的作者。您可以在 Twitter 上找到他 @DaveTaylor,您可以通过他的技术问答网站联系他:Ask Dave Taylor

加载 Disqus 评论