Shell脚本实现内网ARP洪泛监控告警系统:原理、实战与优化
1. 项目概述为什么我们需要一个内网ARP洪泛监控告警系统如果你管理过企业或校园网络或者哪怕只是维护一个稍具规模的家庭网络一定遇到过这样的场景网络突然变得异常卡顿网页打不开视频会议断断续续但检查路由器、交换机、带宽占用似乎都正常。这时候一个有经验的网管可能会告诉你去查查是不是有ARP攻击或者ARP洪泛。ARP洪泛简单来说就是网络中有设备在短时间内疯狂发送大量的ARP请求或应答报文试图耗尽交换机的MAC地址表缓存或者进行ARP欺骗攻击的前奏。这种行为轻则导致网络性能急剧下降重则可能引发中间人攻击窃听或篡改网络数据。传统的监控手段比如依赖昂贵的商业安全设备或者复杂的网络分析软件对于很多中小团队或个人来说门槛太高。而基于Shell脚本的方案则提供了一种轻量、灵活、几乎零成本的解决方案。它可以直接运行在Linux服务器、网关设备甚至一台常开的树莓派上通过解析系统底层的网络数据实时发现异常并通过飞书或邮件将告警信息精准推送到你的手上。这个项目就是教你如何从零开始打造这样一个“网络哨兵”。2. 核心原理与工具选型为什么是Shell、ARP和飞书2.1 ARP协议与洪泛攻击原理浅析要监控先得懂原理。ARPAddress Resolution Protocol地址解析协议是局域网通信的基石。它的工作就像在一个大办公室里你想找“张三”目标IP地址传个文件但你只知道他的名字不知道他坐在哪个工位MAC地址。于是你站起来大喊一声“张三的MAC地址是多少”广播ARP请求。办公室里真正的张三听到后会回应“我是张三我的MAC是XX:XX:XX:XX:XX:XX”单播ARP应答。这样你就完成了“名字”到“座位”的映射后续通信就可以直接进行了。ARP洪泛攻击就是有人恶意地、持续地站起来大喊无数个不存在的人名或者冒充很多人回应。这会导致两个问题交换机MAC表泛洪交换机需要记录每个端口对应的MAC地址。当海量虚假的ARP报文涌入交换机的MAC地址表会被迅速填满或频繁刷新导致其退化为一个低效的集线器向所有端口广播数据浪费带宽并引发广播风暴。ARP欺骗中间人攻击攻击者可以伪造ARP应答告诉受害者“网关的MAC地址是我”同时告诉网关“受害者的MAC地址也是我”。这样双方的数据都会流经攻击者的机器他就可以窃听甚至篡改数据。我们的脚本核心任务就是监听网络中的ARP报文统计其频率当单位时间内来自同一源MAC或目标IP的ARP请求/应答数量超过阈值时判定为异常。2.2 为什么选择Shell脚本作为实现载体你可能会问Python、Go不是更强大吗没错但对于这个特定场景Shell脚本有不可替代的优势无处不在任何Linux/Unix系统包括OpenWRT等嵌入式系统都原生支持Shell无需安装额外的运行时环境。贴近系统网络数据抓取tcpdump、报文解析awk,grep、进程管理、定时任务cron等操作Shell调用系统命令是天然而高效的。轻量快速对于这种高频率、低复杂度的数据采集和阈值判断任务Shell脚本的启动和执行开销极小。易于集成可以非常方便地嵌入到现有的运维监控体系如Zabbix Agent自定义监控项或系统初始化脚本中。2.3 告警通道选择飞书机器人 vs. 传统邮件及时告警是关键。我们选择飞书机器人作为首选邮件作为备用。飞书机器人配置简单API直观支持富文本Markdown、卡片消息可以将告警信息结构化地展示并相关责任人。消息送达手机App和PC客户端时效性极强。对于运维团队协作来说体验远胜邮件。邮件作为经典且可靠的备用方案几乎所有的服务器和网络设备都支持发送邮件。当飞书API出现临时故障或网络策略限制时邮件可以作为一个兜底保障。注意在实际生产环境中建议至少配置两种不同原理的告警通道如即时通讯短信/电话避免单一通道失效导致告警遗漏。3. 系统架构与核心脚本设计思路整个系统可以看作一个由数据采集、分析判断、告警执行三个环节组成的管道Pipeline。数据源 (网络接口) - 采集器 (tcpdump) - 分析器 (awk/grep) - 判断器 (阈值逻辑) - 告警器 (飞书/邮件API)3.1 整体工作流程设计启动监控脚本以守护进程或定时任务形式启动在指定网络接口如eth0上开启tcpdump过滤只捕获ARP报文。实时解析tcpdump的输出以流的形式传递给awk实时解析每一行提取出关键字段时间戳、源MAC地址、源IP地址、目标IP地址、操作类型请求/应答。滑动窗口统计我们采用一个“滑动时间窗口”的统计模型。例如统计过去60秒内每个源MAC地址发送的ARP报文数量。这比固定时间间隔的统计更能及时反映瞬时爆发。阈值判断为不同指标设置阈值。例如MAC_FLOOD_THRESHOLD: 单个MAC在窗口内发送ARP报文数 1000个/分钟。IP_FLOOD_THRESHOLD: 对同一目标IP的ARP请求数 500个/分钟可能是ARP扫描或攻击。触发告警一旦有指标突破阈值立即调用告警发送函数。为了避免在持续攻击下告警风暴需要加入“告警冷却”机制例如同一个MAC地址触发的告警30分钟内只发送一次。日志记录所有异常事件和告警发送记录都需要写入本地日志文件/var/log/arp_monitor.log便于事后审计和排查。3.2 关键技术与命令详解tcpdump网络抓包利器。关键参数tcpdump -i eth0 -n -l arp-i eth0: 指定监控网卡。-n: 不解析主机名和端口名显示IP和端口号加快解析速度。-l: 行缓冲模式使输出能立即被管道|后的命令处理。arp: 过滤条件只捕获ARP类型报文。awk文本处理瑞士军刀。用于从tcpdump输出行中提取结构化数据。tcpdump输出ARP报文的一行示例09:23:45.123456 arp who-has 192.168.1.1 tell 192.168.1.100我们可以用awk轻松提取时间、操作类型、目标IP、源IP。关联数组这是Shell脚本实现滑动窗口统计的核心数据结构。我们可以用awk的关联数组或Bash 4.0的关联数组来记录。# 在Bash中声明一个关联数组用于记录每个MAC的计数 declare -A mac_counter mac_counter[aa:bb:cc:dd:ee:ff]$((mac_counter[aa:bb:cc:dd:ee:ff] 1))4. 分步实现与脚本编写实战下面我们开始编写核心脚本arp_flood_monitor.sh。建议在测试环境虚拟机或隔离的网络中操作。4.1 环境准备与参数配置首先创建一个脚本文件并定义可配置的参数。良好的配置能让脚本适应不同环境。#!/bin/bash # arp_flood_monitor.sh - 内网ARP洪泛监控与告警脚本 # 用户配置区域 # 监控配置 INTERFACEeth0 # 监控的网络接口 STATS_WINDOW60 # 统计时间窗口秒 CHECK_INTERVAL10 # 检查阈值间隔秒 # 阈值配置 MAC_FLOOD_THRESHOLD1000 # 单个MAC在窗口内ARP报文数阈值 IP_FLOOD_THRESHOLD500 # 对同一目标IP的ARP请求数阈值 # 飞书机器人告警配置 FEISHU_WEBHOOK_URLhttps://open.feishu.cn/open-apis/bot/v2/hook/your-webhook-token FEISHU_MSG_TITLE【紧急】ARP洪泛攻击告警 FEISHU_AT_MOBILES13800138000 # 需要的成员手机号多个用逗号分隔 # 邮件告警配置备用 MAIL_ENABLEtrue MAIL_TOadminyour-company.com MAIL_SUBJECTARP Flood Alert from $(hostname) SMTP_SERVERsmtp.your-company.com SMTP_PORT587 SMTP_USERalert-senderyour-company.com SMTP_PASSWORDyour-password # 注意建议使用应用专用密码或将密码放在更安全的地方 # 日志配置 LOG_FILE/var/log/arp_monitor.log # 配置结束 实操心得千万不要将密码等敏感信息硬编码在脚本里上述仅为示例。生产环境中建议将SMTP_PASSWORD和FEISHU_WEBHOOK_URL中的token通过环境变量或外部配置文件读取并设置严格的文件权限如chmod 600 config.conf。4.2 核心监控循环与数据采集脚本的主体是一个无限循环负责启动tcpdump、处理数据流并定期检查阈值。# 初始化日志 log_message() { echo [$(date %Y-%m-%d %H:%M:%S)] $1 $LOG_FILE } # 清理函数用于脚本退出时杀死后台进程 cleanup() { log_message 监控脚本停止。 if [[ -n $TCPDUMP_PID ]]; then kill $TCPDUMP_PID 2/dev/null fi exit 0 } trap cleanup SIGINT SIGTERM log_message ARP洪泛监控脚本启动监控网卡: $INTERFACE 时间窗口: ${STATS_WINDOW}s。 # 使用命名管道FIFO来高效处理tcpdump输出 FIFO_PATH$(mktemp -u) mkfifo $FIFO_PATH # 在后台启动tcpdump输出到FIFO tcpdump -i $INTERFACE -n -l -e arp 2/dev/null $FIFO_PATH TCPDUMP_PID$! # 声明关联数组用于统计 (需要Bash 4.0) declare -A mac_count declare -A ip_request_count # 用于记录告警冷却时间 declare -A alarm_cooldown # 主循环从FIFO中读取数据 while read -r line; do # 提取关键信息使用awk提高效率 # -e 参数输出链路层头包含源MAC # 样例行: 09:23:45.123456 aa:bb:cc:dd:ee:ff ff:ff:ff:ff:ff:ff, ARP, Request who-has 192.168.1.1 tell 192.168.1.100, length 46 src_mac$(echo $line | awk {print $2}) # 提取操作类型和目标IP arp_info$(echo $line | grep -o ARP,.*) if echo $arp_info | grep -q Request who-has; then # 这是一个ARP请求 dst_ip$(echo $arp_info | awk {print $4}) # 更新统计 mac_count[$src_mac]$((mac_count[$src_mac] 1)) ip_request_count[$dst_ip]$((ip_request_count[$dst_ip] 1)) fi # 可以类似地处理ARP应答此处省略... done $FIFO_PATH READER_PID$! # 另一个循环定期检查阈值并清理旧数据 while true; do sleep $CHECK_INTERVAL current_time$(date %s) # 这里需要一个机制来清理超出时间窗口的旧计数。 # 由于Bash关联数组的局限性实现精确的滑动窗口较复杂。 # 简化方案定期如每CHECK_INTERVAL秒将计数数组清空相当于一个时间桶。 # 更精确的方案需要记录每个报文的时间戳这里我们采用简化方案。 for mac in ${!mac_count[]}; do if [[ ${mac_count[$mac]} -gt $MAC_FLOOD_THRESHOLD ]]; then # 检查冷却 if [[ -z ${alarm_cooldown[mac:$mac]} ]] || [[ $current_time -gt ${alarm_cooldown[mac:$mac]} ]]; then message检测到MAC地址 ${mac} 在最近${STATS_WINDOW}秒内发送了 ${mac_count[$mac]} 个ARP报文可能发生洪泛攻击 log_message ALERT: $message send_feishu_alert $message [[ $MAIL_ENABLE true ]] send_mail_alert $message # 设置冷却时间例如30分钟1800秒 alarm_cooldown[mac:$mac]$((current_time 1800)) fi fi done # 清空本周期计数开始下一个统计窗口 unset mac_count declare -A mac_count unset ip_request_count declare -A ip_request_count # 清理过期的冷却记录可选防止数组膨胀 for key in ${!alarm_cooldown[]}; do if [[ $current_time -gt ${alarm_cooldown[$key]} ]]; then unset alarm_cooldown[$key] fi done done这个脚本框架展示了核心逻辑但简化了滑动窗口的实现。生产环境需要一个更精确的、能记录每个报文时间戳的机制可能需要用awk或Python辅助处理数据流。4.3 飞书机器人告警功能实现飞书机器人提供了非常简单的Webhook接口我们使用curl命令即可发送告警。send_feishu_alert() { local alert_message$1 local json_payload$(cat EOF { msg_type: text, content: { text: $FEISHU_MSG_TITLE\n时间: $(date %Y-%m-%d %H:%M:%S)\n主机: $(hostname)\n详情: $alert_message\n\nat id\all\所有人/at } } EOF ) # 如果需要特定人可以使用post的user_id或open_id这里用手机号示例需机器人拥有相应权限 # 更复杂的消息格式可以使用“interactive”卡片消息展示效果更好。 curl -s -X POST $FEISHU_WEBHOOK_URL \ -H Content-Type: application/json \ -d $json_payload $LOG_FILE 21 if [[ $? -eq 0 ]]; then log_message 飞书告警发送成功。 else log_message ERROR: 飞书告警发送失败。 fi }注意事项飞书机器人的消息内容有长度限制并且特殊字符需要转义。对于复杂的告警信息建议使用飞书卡片消息它可以构建更美观、结构化的界面包含标题、颜色、按钮等。构建卡片消息的JSON会复杂一些但可读性大大提升。4.4 邮件告警功能实现备用通道使用mailx或sendmail命令发送邮件通常需要配置系统MTA如Postfix。更通用的方法是使用外部SMTP服务器。send_mail_alert() { [[ $MAIL_ENABLE ! true ]] return 0 local alert_message$1 # 使用curl发送SMTP邮件需要SMTP服务器支持 local email_body$(cat EOF From: ARP Monitor $SMTP_USER To: $MAIL_TO Subject: $MAIL_SUBJECT Date: $(date -R) Content-Type: text/plain; charsetutf-8 告警时间: $(date %Y-%m-%d %H:%M:%S) 告警主机: $(hostname) ($(hostname -I | awk {print $1})) 告警详情: $alert_message 请及时登录服务器或网络设备排查。 EOF ) # 使用curl的smtp协议发送注意此方法可能因curl版本和SMTP服务器配置而异 # 更可靠的方法是使用专业的命令行邮件工具如msmtp、sendEmail或者Python的smtplib echo $email_body | \ curl -s --ssl-reqd \ --url smtp://$SMTP_SERVER:$SMTP_PORT \ --user $SMTP_USER:$SMTP_PASSWORD \ --mail-from $SMTP_USER \ --mail-rcpt $MAIL_TO \ --upload-file - $LOG_FILE 21 if [[ $? -eq 0 ]]; then log_message 邮件告警发送成功。 else log_message ERROR: 邮件告警发送失败将尝试使用系统mail命令。 # 备用方案使用系统mail命令前提是已配置好 echo $alert_message | mail -s $MAIL_SUBJECT $MAIL_TO 2 $LOG_FILE fi }踩坑记录邮件发送是脚本中最容易失败的一环。服务器防火墙、SMTP认证方式明文/加密、SPF/DKIM记录等都可能导致发送失败。务必在脚本部署前单独测试邮件发送功能。可以考虑将邮件发送部分剥离成一个独立的、更健壮的脚本或工具。4.5 脚本的部署、运行与维护保存脚本将完整的脚本保存为/usr/local/bin/arp_flood_monitor.sh。赋予执行权限sudo chmod x /usr/local/bin/arp_flood_monitor.sh。测试运行首先在前台运行观察输出和日志是否正常。sudo /usr/local/bin/arp_flood_monitor.sh可以使用arping命令在另一台机器上制造一些ARP流量来测试。# 在另一台机器上对网关进行ARP ping arping -c 100 -I eth0 192.168.1.1配置为系统服务推荐创建Systemd服务文件实现开机自启、故障重启。# /etc/systemd/system/arp-monitor.service [Unit] DescriptionARP Flood Monitor Service Afternetwork.target [Service] Typesimple Userroot ExecStart/usr/local/bin/arp_flood_monitor.sh Restarton-failure RestartSec10 StandardOutputjournal StandardErrorjournal [Install] WantedBymulti-user.target然后执行sudo systemctl daemon-reload sudo systemctl enable arp-monitor.service sudo systemctl start arp-monitor.service sudo systemctl status arp-monitor.service # 查看状态日志轮转配置logrotate防止日志文件无限增大。# /etc/logrotate.d/arp-monitor /var/log/arp_monitor.log { daily missingok rotate 30 compress delaycompress notifempty create 640 root adm postrotate systemctl reload arp-monitor.service /dev/null 21 || true endscript }5. 高级优化与排查技巧实录基础的脚本跑起来后我们还需要考虑一些实际运营中会遇到的问题。5.1 性能优化应对高流量网络在核心交换机镜像端口或非常繁忙的网段上ARP报文量可能巨大。原始的while read循环配合awk可能会成为瓶颈。优化方案一使用更高效的数据处理工具。考虑用tsharkWireshark的命令行版替代tcpdump它解析和输出字段更高效。或者将原始报文捕获到PCAP文件然后由另一个专用分析进程甚至是用Python写的定期分析。优化方案二降低精度提升性能。如果不是为了取证只是为了告警可以增大统计窗口如300秒和检查间隔如30秒并提高阈值。这能显著降低系统负载。优化方案三内核级过滤。在tcpdump过滤表达式上做更严格的限制或者在驱动层面进行采样。5.2 误报排除识别合法的高ARP流量并非所有高ARP流量都是攻击。以下情况可能触发误报网络设备重启或大量新主机上线会广播大量ARP请求来寻找网关和其他主机。某些虚拟化或集群软件如VRRP虚拟路由冗余协议、某些数据库集群的心跳检测会定期发送ARP报文。网络扫描工具合法的安全扫描也可能产生大量ARP请求。应对策略建立白名单将网关、核心服务器、已知虚拟IP的MAC地址加入白名单忽略它们的ARP流量统计。设置基线脚本运行初期如前24小时为学习期只记录不告警用于了解网络正常情况下的ARP流量基线然后基于基线动态调整阈值如设置为基线值的10倍。关联分析单纯的ARP洪泛告警可能不准。可以结合其他信息如同一源IP是否也在进行端口扫描可用tcpdump监控SYN包综合判断攻击可能性。5.3 常见问题与故障排查问题一脚本启动后无任何日志输出tcpdump没抓到包排查首先检查网卡名称INTERFACE是否正确ip addr或ifconfig。其次使用sudo tcpdump -i eth0 arp命令手动测试是否能抓到ARP包。最后检查脚本是否有权限在指定网卡上抓包通常需要root权限。问题二飞书收不到告警消息排查检查curl命令是否执行成功。可以在脚本中curl命令后加-v参数查看详细输出重定向到日志。检查飞书机器人Webhook URL是否正确机器人是否已被添加到需要告警的群聊中。检查网络连通性服务器是否能访问open.feishu.cn。查看飞书群聊的机器人设置确认消息类型是否被允许。问题三邮件发送失败排查最可能的原因是SMTP认证失败。检查用户名、密码、SMTP服务器地址和端口587常用于STARTTLS465用于SSL。检查服务器防火墙是否放行了对SMTP服务器端口的出站连接。查看目标邮箱的垃圾邮件文件夹。使用telnet或openssl s_client手动连接SMTP服务器测试。问题四脚本运行一段时间后CPU或内存占用过高排查很可能是统计数组未及时清理导致膨胀。检查脚本中清理旧数据的逻辑。确保在每次检查周期后或当数组大小超过一定限制时能正确清理过期条目。可以考虑使用更高效的语言如Python重写核心分析模块。5.4 功能扩展思路一个基础的监控告警系统建成后还可以考虑以下扩展方向让它变得更强大可视化仪表盘将脚本收集到的计数如每分钟的ARP报文总数、Top N 活跃MAC输出到/proc或一个文本文件然后使用Prometheus Node Exporter的textfile collector来采集最终在Grafana上展示ARP流量趋势图。攻击源自动阻断当检测到确切的攻击MAC时可以联动网络设备如通过SSH登录到支持管理的交换机或本机防火墙iptables/nftables添加一条规则临时或永久丢弃来自该MAC地址的所有流量。指纹库与威胁情报将可疑MAC地址与公开的OUI厂商标识数据库对比如果发现是虚拟化或特定攻击工具常见的MAC段可以提升告警级别。甚至可以接入威胁情报查询IP或MAC是否在恶意名单内。分布式监控在多台关键位置的服务器或探针上部署此脚本由一个中心服务器汇总告警可以绘制出攻击在内网中的传播路径。这个由Shell脚本驱动的内网ARP洪泛监控系统就像给你的网络安装了一个7x24小时在线的“免疫细胞”。它成本低廉反应迅速定制性强。从最初的几十行代码到融入白名单、基线学习、多通道告警再到考虑性能优化和联动响应整个过程本身就是一次精彩的运维自动化实践。真正有效的监控不在于工具的昂贵而在于对原理的深刻理解和对细节的持续打磨。当你收到第一条由自己编写的脚本发出的告警信息时那种对网络了如指掌的掌控感便是对投入最好的回报。