基于iptables和nginx的deny.conf封禁ip的实践

54 阅读1分钟

我上早八!!!
爬爬爬,你爬个🥚!!!
都TM5202年了, 还TM index.php, index.jsp
除了一堆杂乱无用的log之外还能咋

代码如下:

package main

import (
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/hpcloud/tail"
)

const (
	logPath           = "/var/log/nginx/access.log"
	denyConfPath      = "/var/nginx/conf.d/deny.conf"
	dailyTaskHour     = 3 // 每天凌晨 3 点
	dailyTaskMinute   = 0
	checkIntervalSecs = 30 // 检查时间的粒度
)

var (
	rePHPJSP = regexp.MustCompile(`\.php|\.jsp`)
	blocked  = make(map[string]bool)
	mu       sync.Mutex // 保护 blocked map
)

func main() {
	fmt.Println("🚀 nginx watcher started")
	go watchLog()
	go dailyTaskScheduler()
	select {}
}

func watchLog() {
	t, err := tail.TailFile(logPath, tail.Config{
		Follow:    true,
		ReOpen:    true,
		MustExist: true,
		Poll:      true,
	})
	if err != nil {
		fmt.Println("❌ 无法监听日志:", err)
		return
	}

	fmt.Println("📄 开始监听 nginx 日志 …")

	for line := range t.Lines {
		processLine(line.Text)
	}
}

func processLine(line string) {
	fields := strings.Fields(line)
	if len(fields) < 7 {
		return
	}

	ip := fields[0]
	url := fields[6]

	if rePHPJSP.MatchString(url) {
		mu.Lock()
		alreadyBlocked := blocked[ip]
		mu.Unlock()

		if alreadyBlocked {
			return
		}

		fmt.Printf("🚨 [恶意请求] IP=%s URL=%s\n", ip, url)

		if err := blockIP(ip); err != nil {
			fmt.Println("❌ 封禁失败:", err)
			return
		}

		if err := appendToDenyConf(ip); err != nil {
			fmt.Println("❌ 写入 deny.conf 失败:", err)
			return
		}

		mu.Lock()
		blocked[ip] = true
		mu.Unlock()

		fmt.Println("✅ 封禁完成:", ip)
	}
}

func blockIP(ip string) error {
	cmd := exec.Command("iptables", "-I", "INPUT", "-s", ip, "-j", "DROP")
	output, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("%v: %s", err, output)
	}
	return nil
}

func appendToDenyConf(ip string) error {
	f, err := os.OpenFile(denyConfPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return err
	}
	defer f.Close()

	line := fmt.Sprintf("deny %s;\n", ip)
	if _, err := f.WriteString(line); err != nil {
		return err
	}
	return nil
}

func dailyTaskScheduler() {
	for {
		now := time.Now()
		if now.Hour() == dailyTaskHour && now.Minute() == dailyTaskMinute {
			runDailyTask()
			time.Sleep(time.Minute + time.Second)
		} else {
			time.Sleep(time.Duration(checkIntervalSecs) * time.Second)
		}
	}
}

func runDailyTask() {
	fmt.Printf("🌙 [%s] 执行每日维护任务 …\n", time.Now().Format(time.RFC3339))

	fmt.Println("🔄 重启 nginx …")
	if err := exec.Command("systemctl", "restart", "nginx").Run(); err != nil {
		fmt.Println("❌ 重启 nginx 失败:", err)
	} else {
		fmt.Println("✅ nginx 已重启")
	}

	fmt.Println("🧹 清空 iptables …")
	if err := exec.Command("iptables", "-F").Run(); err != nil {
		fmt.Println("❌ 清空 iptables 失败:", err)
	} else {
		fmt.Println("✅ iptables 已清空")
	}

	mu.Lock()
	blocked = make(map[string]bool)
	mu.Unlock()
	fmt.Println("📝 已清空封禁记录")
}