自动化防火墙日志扫描

作者:Leo Liberti

防火墙是专门用于过滤两个网络之间特定类型网络流量的计算机。它们通常用于保护局域网免受互联网其余部分的侵害。保护局域网上的每台计算机比部署、管理和监控单个防火墙成本更高且更耗时。对于永久连接到互联网的机构而言,防火墙尤为重要。根据网络配置,可以将路由器设置为数据包过滤器;但是,通常更方便的做法是设置一台专用计算机充当防火墙。由于 Linux 计算机可以做得非常安全且成本较低,因此可以成为非常有效的防火墙。

在 Linux 内核 2.2.x 上部署防火墙是使用 ipchains 完成的,而在新的 2.4.x 内核上则使用 iptables。如何设置实际的防火墙超出了本文的范围;我们建议读者参考针对 2.2.x 内核的 ipchains HOWTO 以及 Paul “Rusty” Russell 的 Packet-Filtering HOWTO(针对 2.4.x 内核)。这两者都可以在互联网上通过任何搜索引擎找到。但是,仅仅构建实际的防火墙是不够的;为了提供严密的安全保障,需要对防火墙进行监控。在本文中,我们将解释如何构建和使用一个名为 inside-control 的基于 Web 的 ipchains 监控系统。

防火墙监控系统有两个主要用途:检查是否有恶意破解者试图破坏内部局域网,以及检查局域网内的用户是否滥用互联网服务。

防火墙设置示例

这是一个非常简单的防火墙设置,我们将在本文后面的部分中将其作为工作示例进行参考。

例如,假设内部网络是 10.0.1.0/255.255.255.0,Linux 网关/防火墙在连接到内部局域网的接口上的地址为 10.0.1.1,在连接到互联网的接口上的地址为 10.200.200.1(实际上这两个 IP 地址都是不可路由的,所以这只是一个虚构的例子)。设置防火墙的第一步是启用网络接口之间的网关功能

echo 1 > /proc/sys/net/ipv4/ip_forward

然后我们继续使用 ipchains 构建一个日志防火墙。首先,我们清除所有之前的规则,并允许环回接口上的数据包和所有 ICMP 数据包

ipchains -F
ipchains -A input -i lo -j ACCEPT
ipchains -A input -p ICMP -j ACCEPT
现在我们阻止并记录从互联网到内部局域网的 Telnet 协议
ipchains -A input -p TCP -s 0.0.0.0/0 -d 10.0.1.0/24 23 -l -j DENY
但是我们允许并记录从内部局域网到互联网的 HTTP 协议
ipchains -A input -p TCP -s 10.0.1.0/24 -d 0.0.0.0/0 80 -l -j ACCEPT
最后,我们设置宽松的策略
ipchains -P input ACCEPT
这个防火墙阻止并记录所有传入的 Telnet 连接,它允许并记录所有传出的 HTTP 连接,并允许其他所有内容(参见图 1)。这样的设置对于严肃的保护来说过于宽松,但它将很好地说明自动化日志扫描脚本可以做什么。
Automating Firewall Log Scanning

图 1. 示例防火墙的设置

防火墙输出日志的文件通常是 /var/log/syslog 或 /var/log/messages。为了找出是哪一个,您可以执行

grep -q "Packet log" /var/log/syslog && echo yes

如果输出“yes”,则它是 /var/log/syslog,如果什么都不输出,则很可能是 /var/log/messages。您可以使用以下命令确认

grep -q "Packet log" /var/log/messages && echo yes
如果两个命令都没有输出,则防火墙处于非活动状态,或者没有通过防火墙的日志流量(在我们的示例中,Telnet 和 HTTP)。
2.4.x 内核和 iptables

关于 2.4.x 内核和 iptables,情况稍微复杂一些。首先,您必须记住编译内核时要包含所有数据包过滤选项,包括 LOG 目标。其次,将 ipchains 更改为 iptables。然后将链的名称更改为大写(例如,input 变为 INPUT)。接下来,更改目标的名称(DENY 变为 DROP)。最后,以不同的方式指定端口号。清单 1 是与上面给出的 2.2.x 命令序列等效的 2.4.x 命令序列。

清单 1. 2.4.x iptable 命令序列

ipchains 日志格式

现在让我们检查一下我们防火墙的 /var/log/syslog 中的一个示例日志条目

Jun 12 16:15:54 myfirewall kernel: Packet log: input DENY eth1 PROTO=6 212.65.214.2:34251 10.0.1.2:23 L=52 S=0x10 I=24016 F=0x4000 T=53 SYN (#38)

这意味着在 6 月 12 日下午四点一刻,防火墙(相当乏味地称为 myfirewall)拒绝并记录了一个数据包,该数据包进入其网络接口 eth1(互联网上的外部接口),使用 TCP 协议,来自 212.65.214.2(来自端口 34251),定向到 10.0.1.2(在端口 23 上,即 Telnet 端口),长度为 52 字节。我们将跳过大多数其他细节,除了一个:“SYN”表示该数据包是连接的第一个数据包。实际上,此信息在区分属于预先存在的连接(可能从内部局域网发起)的数据包和试图从互联网向内部局域网建立连接的数据包时非常有用。通常,允许“回复”数据包(未设置“SYN”标志),但拒绝“SYN”数据包,因为这意味着有人试图从外部连接到内部局域网中的计算机。

当然,可以通过检查日志文件中的所有相关条目来检查防火墙的状态,但这在仅记录少量看起来很奇怪的数据包时是可行的。例如,在我设置的一些防火墙上,我决定记录所有从互联网到内部局域网计算机上的端口 31337 的数据包,因为 31337 是 BackOrifice 使用的默认端口。当有人有兴趣从防火墙获取一些统计信息时,日志文件的大小很可能每天超过 5MB。在这种情况下,手动检查日志文件不再是一种选择。这时,自动化日志扫描就派上用场了。

在分析 2.4.x 内核防火墙日志时,格式是不同的

Jun 12 16:15:54 myfirewall kernel: Packet log: IN=eth1 OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:08:00 SRC=212.65.214.2 DST=10.0.1.2 LEN=52 TOS=0x10 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=34251 DPT=23 WINDOW=11592 RES=0x00 SYN URGP=0

我们感兴趣的字段是 SRC(源 IP 地址)、DST(目标 IP 地址)、SPT(源端口)、DPT(目标端口)以及 SYN 标志的存在与否。

inside-control 脚本结构

我将使用 Perl 来构建日志扫描器。这不是唯一的选择,事实上,为了获得最佳性能,应该使用编译型语言。当我在 C++ 中重新编写这个脚本时,我观察到执行速度提高了 100%。

inside-control 脚本由一个主解析循环和一个 HTML 数据显示循环组成。由于该脚本是一个 CGI,因此它需要驻留在配置为运行 CGI 程序的 Web 服务器上。

请注意,如下所述的代码牺牲了功能和有用的细节(如错误检查)以求清晰。例如,在实际读取文件之前,没有检查“打开文件”是否成功。另请注意,下面的代码是为分析内核 2.2.x 的数据包日志格式而定制的。根据上面描述的示例数据包日志,更改为内核 2.4.x 的日志格式应该很简单。

主解析循环

首先,我们打开日志文件并初始化一些变量(使用 Red Hat 的用户应该使用 /var/log/messages 而不是 /var/log/syslog)

#!/usr/bin/perl
open(LOGFILE, "/var/log/syslog");
$firstdate = "";
$date = "";
$total_traffic = 0;

现在我们循环遍历日志文件中的每一行

while ( <LOGFILE> ) {
跳过所有不属于防火墙的日志条目
next unless /Packet log/;
我们还解析该行(警告:在 Perl 脚本中,将此块中的最后一行写成一个长行,不带反斜杠)
chomp;
@log = split;
($month,$day,$time,$policy,$proto,$ipsource,$ipdest, \
$tot_len) = @log[0,1,2,8,10,11,12,13];
然后我们计算日期并存储日志中的第一个日期。随着我们的进行,我们将当前日期存储为最后一个日期,以便在最后一步之后,变量 lastdate 将包含日志中的最后一个日期
$date = $day . " " . $month . " " . $time;
if (length($firstdate) == 0) {
  $firstdate = $date;
}
$lastdate = $date;
读取协议类型、源 IP 地址、源端口、目标 IP 地址、目标端口和数据包长度
$proto = substr($proto, -1);
($ips, $ports) = split ":", $ipsource;
($ipd, $portd) = split ":", $ipdest;
($flush, $packetlen) = split "=", $tot_len;
现在将目标 IP 地址记录在一个字符串中,并将该字符串与源 IP 地址关联起来,以便在数据显示循环中,我们将能够循环遍历源 IP 地址并检索它们连接到的主机
unless ( $sourcedest{$ips} =~ /$ipd/ ) {
  $sourcedest{$ips} = $sourcedest{$ips} . $ipd . " ";
}
我们计算源 IP 地址的日志条目数
++$source{$ips};
并汇总总流量
$total_traffic += $packetlen;
最后,我们汇总每个主机的流量
$traffichost{$ips} += $packetlen;
}
请注意,并非所有收集的信息都已使用(例如,没有端口的讨论),因此这里有很大的扩展空间。
数据显示循环

首先,我们显示一个美观的网页标题,如清单 2 所示。

清单 2. 网页标题

循环遍历已排序的源 IP 地址,并打印源 IP 地址、来自该 IP 的数据包数量以及从该 IP 生成的流量(以字节为单位)

for (sort keys %source) {
  print "<TR><TD>$_</TD> ";
  print "<TD>$source{$_} </TD>\n";
  print "<TD>$traffichost{$_} bytes</TD>\n";

现在我们能够打印包含当前源 IP 地址联系的目标 IP 地址的字符串

$tmp1 = $sourcedest{$_};
if (length($tmp1) gt 0) {
  print "<TD>\n";
  @lt1 = split " ", $tmp1;
  for(sort @lt1) {
    printf "$_ <br>\n";
  }
  print " </TD>\n";
}
print " </TR>\n";
}
最后,我们打印 HTML 尾部
print "</TABLE>\n";
print "</center>\n";
print "</BODY></HTML>\n";
可下载的 inside-control 脚本

我实际实现的 inside-control 版本比这里介绍的版本功能更丰富。您可以从 www.iris-tech.net/hdsl-fw 下载该脚本。一些主要新增功能包括能够在“源 IP”列中显示任意名称而不是 IP 地址。这是通过一个非常简单的文本数据库完成的,该数据库将 IP 号码映射到名称。格式与 /etc/hosts 文件相同,如果该文件为您的内部局域网进行了有意义的配置,则可以使用该文件。可以通过更改脚本开头的相关变量 ($useripdb) 来指定“IP 到名称”数据库文件的确切位置。

还有一个搜索工具,允许用户在日志中查找特定的源 IP 地址(或在“IP 到名称”数据库中找到的相应名称)。当从浏览器调用 CGI 时不带参数时,将显示搜索表单。参数传递是通过 GET 方法完成的。

此外,主循环还包括一些数据验证(内核并非总能正确记录日志,尤其是在低 RAM 或低配置 CPU 上)和一些端口相关信息的存储。

最后,也可以在没有 Web 界面的情况下调用该脚本。只需将任何参数传递给 inside-control,所有 HTML 输出都将被抑制,并会提供一些普通输出作为替代。可以通过 -t 选项将源 IP 地址(或在“IP 到名称”数据库中找到的相应名称)的搜索字符串传递给程序。

注意事项和警告

本文的目的是解释一些设计原则并提供一些提示,而不是为日志扫描问题提供一个预先打包的解决方案。inside-control 脚本在许多方面可以做得更好,例如性能和安全性。以下是一些关于 inside-control 的注意事项,主要与安全问题有关。

为了使 CGI 能够读取计算机日志文件 /var/log/syslog 或 /var/log/messages,必须使这些文件对所有人可读。这可以通过命令 chmod +r /var/log/syslog 完成。然而,这不是很安全,因为它允许系统上的任何人读取计算机日志文件。更好的做法是让 Web 服务器以特定的组权限运行 inside-control,然后使日志文件属于该组。

阅读本文后,人们可能会得出结论,防火墙还必须运行 Web 服务器,因为 inside-control 需要读取防火墙日志文件。事实上,在防火墙上放置 Web 服务器是非常不安全的:理想情况下,防火墙应该不运行任何守护进程服务,所有维护都应该在控制台上完成。当需要远程管理时,防火墙上唯一可以安装的服务是 ssh,即安全外壳。通过在内部网络中设置一个单独的 Web 服务器(该服务器也充当防火墙的 syslog 服务器),仍然可以运行 inside-control。

防火墙日志可以很快填满分区。为了避免防火墙上的硬盘被堵塞(这可能会导致互联网连接故障),根据您要记录的流量大小,您必须留出大量的日志文件空间。对于高数据量服务(通常是 HTTP、FTP、SMTP、NetBIOS、LPD 和数据库服务),我建议设置第二个至少 20GB 大小的硬盘,只有一个分区挂载在 /var/log 上。还要记住,该脚本需要在关键步骤(如打开文件)上添加一些错误检查代码。

最后,脚本的每个地方,尤其是主循环,都有很大的改进空间。可以使用比这里讨论的更多的来自每条日志行的数据。但是,最好始终不要显示太多细节;否则,自动化日志扫描器的全部意义就丧失了。如果您显示所有可用的细节,最终您将不得不在大量无法管理的流量日志中寻找可疑条目。

Automating Firewall Log Scanning
Leo Liberti 是意大利科莫 IrisTech 公司的技术总监,该公司为其客户提供基于 Web 的应用程序和各种电子服务。他的空闲时间都用来尽可能多地在餐厅吃饭。
加载 Disqus 评论