Shell脚本如何扫描开放端口并告警

wen 实用脚本 2

本文目录导读:

Shell脚本如何扫描开放端口并告警

  1. 方案一:基于nc(netcat)的简单端口扫描脚本
  2. 方案二:使用nmap的高级扫描脚本
  3. 方案三:带Webhook和状态检查的完整监控脚本
  4. 使用说明

我可以为您提供一个完整的Shell脚本,用于扫描开放端口并在发现特定服务时发送告警,以下是几种不同方案的实现:

基于nc(netcat)的简单端口扫描脚本

#!/bin/bash
# 配置参数
TARGET_HOST="192.168.1.1"  # 目标主机IP
PORTS=(22 80 443 3306 8080)  # 要扫描的端口列表
TIMEOUT=2  # 超时时间(秒)
LOG_FILE="/var/log/port_scan.log"
ALERT_EMAIL="admin@example.com"
# 颜色代码
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # 无颜色
# 创建日志目录
mkdir -p "$(dirname "$LOG_FILE")"
echo "========== 端口扫描报告 ==========" > "$LOG_FILE"
echo "扫描时间: $(date)" >> "$LOG_FILE"
echo "目标主机: $TARGET_HOST" >> "$LOG_FILE"
echo "--------------------------------" >> "$LOG_FILE"
echo "开始扫描 $TARGET_HOST 的端口..."
open_ports=""
for port in "${PORTS[@]}"; do
    # 使用nc检查端口
    if nc -z -w "$TIMEOUT" "$TARGET_HOST" "$port" 2>/dev/null; then
        echo -e "${GREEN}[开放]${NC} 端口 $port"
        echo "[开放] 端口 $port" >> "$LOG_FILE"
        # 收集开放端口信息用于告警
        if [ -z "$open_ports" ]; then
            open_ports="$port"
        else
            open_ports="$open_ports, $port"
        fi
    else
        echo -e "${RED}[关闭]${NC} 端口 $port"
        echo "[关闭] 端口 $port" >> "$LOG_FILE"
    fi
done
echo "--------------------------------" >> "$LOG_FILE"
echo "扫描完成: $(date)" >> "$LOG_FILE"
# 如果有开放端口,发送告警
if [ -n "$open_ports" ]; then
    echo "⚠️ 发现开放端口: $open_ports"
    # 发送邮件告警
    if command -v mail &> /dev/null; then
        echo "目标 $TARGET_HOST 发现开放端口: $open_ports" | \
        mail -s "[告警] 端口扫描结果 - $TARGET_HOST" "$ALERT_EMAIL"
    fi
    # 发送系统日志
    logger "端口扫描告警: $TARGET_HOST 开放端口: $open_ports"
fi

使用nmap的高级扫描脚本

#!/bin/bash
# 配置参数
TARGET_SUBNET="192.168.1.0/24"  # 目标子网
SCAN_PORTS="1-1000"  # 扫描端口范围
SCAN_SPEED="T4"  # 扫描速度 T1-T5
LOG_DIR="/var/log/port_scans"
ALERT_THRESHOLD=5  # 开放端口超过此数量则告警
# 依赖检查
if ! command -v nmap &> /dev/null; then
    echo "错误: 未安装nmap,请先安装"
    exit 1
fi
# 创建日志目录
mkdir -p "$LOG_DIR"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
LOG_FILE="$LOG_DIR/scan_$TIMESTAMP.log"
# 执行nmap扫描
echo "开始扫描 $TARGET_SUBNET ..."
nmap -"$SCAN_SPEED" -p "$SCAN_PORTS" --open "$TARGET_SUBNET" \
    -oN "$LOG_FILE" 2>/dev/null
# 解析结果
OPEN_PORT_COUNT=$(grep -c "^[0-9]/tcp" "$LOG_FILE")
echo "发现 $OPEN_PORT_COUNT 个开放端口"
# 告警逻辑
if [ "$OPEN_PORT_COUNT" -gt "$ALERT_THRESHOLD" ]; then
    echo "⚠️ 端口开放数量超过阈值!"
    # 保存告警报告
    REPORT_FILE="$LOG_DIR/alert_$TIMESTAMP.txt"
    {
        echo "时间: $(date)"
        echo "目标: $TARGET_SUBNET"
        echo "开放端口数: $OPEN_PORT_COUNT"
        echo "详情:"
        grep "^[0-9]/tcp" "$LOG_FILE"
    } > "$REPORT_FILE"
    # Slack/Webhook 告警示例
    if [ -n "$SLACK_WEBHOOK_URL" ]; then
        curl -X POST -H "Content-Type: application/json" \
            -d "{\"text\":\"端口扫描告警: $TARGET_SUBNET 发现 $OPEN_PORT_COUNT 个开放端口\"}" \
            "$SLACK_WEBHOOK_URL"
    fi
fi

带Webhook和状态检查的完整监控脚本

#!/bin/bash
# ========== 配置区域 ==========
TARGET_HOST="example.com"
PORTS=(80 443 22 3306 6379 8080)
TIMEOUT=3
CHECK_INTERVAL=300  # 检查间隔(秒)
STATE_DIR="/tmp/port_monitor"
# 告警配置
ENABLE_EMAIL=false
EMAIL_ADDRESS=""
ENABLE_WEBHOOK=true
WEBHOOK_URL="https://hooks.example.com/alert"
ENABLE_LOGGING=true
LOG_FILE="/var/log/port_monitor.log"
# 服务名称映射
declare -A SERVICE_NAMES=(
    [22]="SSH"
    [80]="HTTP"
    [443]="HTTPS"
    [3306]="MySQL"
    [6379]="Redis"
    [8080]="HTTP-Alt"
)
# ========== 函数定义 ==========
log_message() {
    local level="$1"
    local message="$2"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    if [ "$ENABLE_LOGGING" = true ]; then
        echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
    fi
    echo "[$timestamp] [$level] $message"
}
send_alert() {
    local subject="$1"
    local message="$2"
    # Webhook 告警
    if [ "$ENABLE_WEBHOOK" = true ] && [ -n "$WEBHOOK_URL" ]; then
        local payload=$(cat <<EOF
{: "$subject",
    "message": "$message",
    "timestamp": "$(date -Iseconds)"
}
EOF
)
        curl -s -X POST -H "Content-Type: application/json" \
            -d "$payload" "$WEBHOOK_URL" &>/dev/null
    fi
    # 邮件告警
    if [ "$ENABLE_EMAIL" = true ] && [ -n "$EMAIL_ADDRESS" ]; then
        echo "$message" | mail -s "$subject" "$EMAIL_ADDRESS"
    fi
}
check_port() {
    local host="$1"
    local port="$2"
    local service_name="${SERVICE_NAMES[$port]:-$port}"
    # 使用 /dev/tcp 进行端口检查
    timeout "$TIMEOUT" bash -c "echo >/dev/tcp/$host/$port" 2>/dev/null
    return $?
}
get_service_banner() {
    local host="$1"
    local port="$2"
    # 尝试获取服务banner
    exec 3<>/dev/tcp/$host/$port 2>/dev/null
    if [ $? -eq 0 ]; then
        read -t 2 banner <&3
        exec 3>&-
        echo "$banner"
    fi
}
# ========== 主逻辑 ==========
# 创建状态目录
mkdir -p "$STATE_DIR"
echo "========== 端口监控服务启动 =========="
log_message "INFO" "监控目标: $TARGET_HOST"
log_message "INFO" "监控端口: ${PORTS[*]}"
while true; do
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    changes_detected=false
    for port in "${PORTS[@]}"; do
        state_file="$STATE_DIR/${port}_state"
        service_name="${SERVICE_NAMES[$port]:-$port}"
        # 检查端口状态
        if check_port "$TARGET_HOST" "$port"; then
            current_state="open"
            # 获取服务banner
            banner=$(get_service_banner "$TARGET_HOST" "$port")
        else
            current_state="closed"
            banner=""
        fi
        # 读取上次状态
        if [ -f "$state_file" ]; then
            last_state=$(cat "$state_file")
        else
            last_state=""
        fi
        # 状态变化检测
        if [ "$current_state" != "$last_state" ]; then
            changes_detected=true
            echo "$current_state" > "$state_file"
            if [ "$current_state" = "open" ]; then
                alert_msg="${service_name}($port) 端口从关闭变为开放"
                alert_data="服务: $service_name\n端口: $port\n目标: $TARGET_HOST\n时间: $timestamp\nBanner: ${banner:-无}"
            else
                alert_msg="${service_name}($port) 端口从开放变为关闭"
                alert_data="服务: $service_name\n端口: $port\n目标: $TARGET_HOST\n时间: $timestamp"
            fi
            log_message "WARN" "$alert_msg"
            send_alert "[端口监控] $alert_msg" "$alert_data"
        fi
    done
    if [ "$changes_detected" = false ]; then
        log_message "DEBUG" "未检测到端口状态变化"
    fi
    sleep "$CHECK_INTERVAL"
done

使用说明

安装依赖

# 方案一需要
apt-get install netcat-openbsd  # Debian/Ubuntu
yum install nmap-ncat          # CentOS/RHEL
# 方案二需要
apt-get install nmap           # Debian/Ubuntu
yum install nmap               # CentOS/RHEL

运行脚本

# 赋予执行权限
chmod +x port_scanner.sh
# 运行方案一
./port_scanner.sh
# 方案三作为服务运行
nohup ./port_monitor.sh > /dev/null 2>&1 &
# 或者使用systemd创建服务
cat > /etc/systemd/system/port-monitor.service << EOF
[Unit]
Description=Port Monitor Service
After=network.target
[Service]
ExecStart=/path/to/port_monitor.sh
Restart=always
User=root
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable port-monitor
systemctl start port-monitor

告警方式

脚本支持多种告警方式:

  1. 邮件告警 - 使用mail命令
  2. Webhook - 支持Slack、钉钉等
  3. 系统日志 - 通过logger命令
  4. 文件日志 - 记录到指定文件

您可以根据实际需求选择适合的方案并进行配置。

抱歉,评论功能暂时关闭!