Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > GO实现基于命令行的简单IPS

GO实现基于命令行的简单IPS程序代码

作者:星星猫

本文介绍了入侵防御系统IPS的工作原理和实现,IPS通过网络流量监控和实时响应,防止网络攻击,通过使用Go语言实现一个简单的IPS示例程序,展示了如何获取本地IP地址和探测网络中其他设备的IP地址,包括如何定义和加载规则文件,以及如何检测IP对相应端口的访问是否达到规定阈值

入侵防御系统

IPS(Intrusion Prevention System)即入侵防御系统,主要用于实时检查和阻止网络入侵。与入侵检测系统(IDS)不同,IPS通常部署在网络的关键路径上,实时监控网络流量,并在发现攻击时立即采取响应措施,如丢弃恶意数据包、封锁攻击源IP地址等。

IPS的工作原理

IPS通过直接嵌入到网络流量中实现主动防御。它通过一个网络端口接收来自外部系统的流量,经过检查确认其中不包含异常活动或可疑内容后,再通过另一个端口传送到内部系统中。有问题的数据包以及所有来自同一数据流的后续数据包都会在IPS设备中被清除掉。

GO实现IPS

下面我们用 Go 语言编写,一个可以在命令行中获取并打印本地 IP 地址(基于常见网络接口)的示例程序,这里主要是获取 IPv4 地址进行展示,示例代码使用了标准库中的 net 包:

获取本地网络接口上绑定的 IP 

package main

import (
    "fmt"
    "net"
)

func main() {
    addrs, err := net.InterfaceAddrs()
    if err!= nil {
        fmt.Printf("获取网络接口地址失败: %v\n", err)
        return
    }

    for _, addr := range addrs {
        ipNet, ok := addr.(*net.IPNet)
        if ok &&!ipNet.IP.IsLoopback() && ipNet.IP.To4()!= nil {
            fmt.Println("IP地址:", ipNet.IP.String())
        }
    }
}

运行

  1. 确保已经安装了 Go 环境(Go 1.10 及以上版本)。
  2. 将上述代码保存到一个以 .go 为后缀的文件中,比如 ips.go。
  3. 打开命令行,切换到该文件所在的目录。
  4. 运行命令 go run ips.go,程序就会执行并输出找到的本地非回环的 IPv4 地址信息。

也可编译后运行

go build ips.go

然后运行生成的可执行文件(在 Windows 上是 .exe 后缀的文件,在 Linux、macOS 等系统上就是普通的二进制文件)也能看到相应的 IP 地址输出。

请注意:这里只是简单获取本地网络接口上绑定的 IP 示例

 对于IPS而言,他要检测的是网络中的流量(比如探测网络中其他设备 IP 等)这需要进一步扩展代码,例如利用 net.Dial 等去做网络连接测试、IP 扫描等操作。

探测网络中其他设备 IP

这里只是通过简单的 ICMP 即 ping 操作概念类似的简单探测

package main

import (
    "fmt"
    "net"
    "os"
    "sync"
    "time"
)

func PingIP(ip string, wg *sync.WaitGroup, result chan<- string) {
    defer wg.Done()
    conn, err := net.DialTimeout("ip4:icmp", ip, time.Second*2)
    if err == nil {
        conn.Close()
        result <- ip
    }
}

func main() {
    if len(os.Args)!= 2 {
        fmt.Println("请输入要扫描的网段,例如 192.168.1.0/24")
        return
    }
    _, ipnet, err := net.ParseCIDR(os.Args[1])
    if err!= nil {
        fmt.Printf("解析网段失败: %v\n", err)
        return
    }

    var wg sync.WaitGroup
    result := make(chan string)

    for ip := ipnet.IP.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
        wg.Add(1)
        go PingIP(ip.String(), &wg, result)
    }

    go func() {
        wg.Wait()
        close(result)
    }()

    for aliveIP := range result {
        fmt.Println("存活IP:", aliveIP)
    }
}

func inc(ip net.IP) {
    for j := len(ip) - 1; j >= 0; j-- {
        ip[j]++
        if ip[j] > 0 {
            break
        }
    }
}

在命令行中使用这个扩展版本时,可以像这样运行(假设编译后的可执行文件名为 ips):

./ips 192.168.1.0/24

上述命令会尝试去探测指定网段 192.168.1.0/24 内的 IP 地址哪些是存活的(可以响应 ICMP 请求的,类似 ping 通的概念),并打印出存活的 IP 地址。

增加规则文件(rules.json)

首先我们设定一个规则文件,用json的方式来保存规则

[
    {
        "id": "1001",
        "description": "SSH暴力破解检测",
        "protocol": "tcp",
        "dst_port": 22,
        "action": "block",
        "threshold": 5,
        "time_window": 60
    },
    {
        "id": "1002",
        "description": "RDP暴力破解检测",
        "protocol": "tcp",
        "dst_port": 3389,
        "action": "block",
        "threshold": 5,
        "time_window": 60
    },
    {
        "id": "1003",
        "description": "MySQL暴力破解检测",
        "protocol": "tcp",
        "dst_port": 3306,
        "action": "block",
        "threshold": 5,
        "time_window": 60
    }
]

然后我们写一个go文件,来检测 IP 对相应端口的访问是否达到规则中定义的暴力破解阈值情况

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net"
    "os"
    "path/filepath"
    "sync"
    "time"
)

// Rule结构体表示一条安全规则
type Rule struct {
    ID          string  `json:"id"`
    Description string  `json:"description"`
    Protocol    string  `json:"protocol"`
    DstPort     int     `json:"dst_port"`
    Action      string  `json:"action"`
    Threshold   int     `json:"threshold"`
    TimeWindow  int     `json:"time_window"`
}

// LoadRules从文件加载安全规则
func LoadRules(filePath string) ([]*Rule, error) {
    content, err := ioutil.ReadFile(filePath)
    if err!= nil {
        return nil, fmt.Errorf("读取规则文件失败: %v", err)
    }
    var rules []*Rule
    err = json.Unmarshal(content, &rules)
    if err!= nil {
        return nil, fmt.Errorf("解析规则文件失败: %v", err)
    }
    return rules, nil
}

// ConnectionRecord记录IP的连接记录(简化示例,仅记录次数)
type ConnectionRecord struct {
    mu         sync.Mutex
    connections map[string]int
}

func NewConnectionRecord() *ConnectionRecord {
    return &ConnectionRecord{
        connections: make(map[string]int),
    }
}

func (cr *ConnectionRecord) Record(ip net.IP, port int) {
    cr.mu.Lock()
    key := fmt.Sprintf("%s:%d", ip.String(), port)
    cr.connections[key]++
    cr.mu.Unlock()
}

func (cr *ConnectionRecord) CheckViolation(rules []*Rule) []*Rule {
    var violatedRules []*Rule
    cr.mu.Lock()
    now := time.Now()
    for _, rule := range rules {
        for key, count := range cr.connections {
            parts := strings.Split(key, ":")
            ip := net.ParseIP(parts[0])
            port := atoi(parts[1])
            if port == rule.DstPort && count >= rule.Threshold {
                // 这里简单判断次数达到阈值就算违规,实际可能需考虑时间窗口等更复杂逻辑细化
                violatedRules = append(violatedRules, rule)
            }
        }
    }
    cr.mu.Unlock()
    return violatedRules
}

func atoi(s string) int {
    n, _ := fmt.Sscanf(s, "%d", &n)
    return n
}

func main() {
    // 获取当前目录下的rules.json作为配置文件路径(可根据实际调整)
    currentDir, err := os.Getwd()
    if err!= nil {
        fmt.Printf("获取当前目录失败: %v\n", err)
        return
    }
    configFilePath := filepath.Join(currentDir, "rules.json")

    rules, err := LoadRules(configFilePath)
    if err!= nil {
        fmt.Printf("加载规则失败: %v\n", err)
        return
    }

    targetIPNet := &net.IPNet{}
    _, targetIPNet, err = net.ParseCIDR("192.168.1.0/24")
    if err!= nil {
        fmt.Printf("解析目标IP网段失败: %v\n", err)
        return
    }

    record := NewConnectionRecord()

    // 模拟IP对内网端口的连接访问(这里简单循环增加连接次数示例)
    for i := 0; i < 10; i++ {
        for ip := targetIPNet.IP.Mask(targetIPNet.Mask); targetIPNet.Contains(ip); inc(ip) {
            // 简单模拟多次访问各规则关注的端口,实际场景会根据真实网络连接情况记录
            for _, rule := range rules {
                record.Record(ip, rule.DstPort)
            }
        }
    }

    violatedRules := record.CheckViolation(rules)
    for _, violatedRule := range violatedRules {
        fmt.Printf("IP段内存在疑似违反规则 %s 的情况,规则描述:%s\n", violatedRule.ID, violatedRule.Description)
    }
}

func inc(ip net.IP) {
    for j := len(ip) - 1; j >= 0; j-- {
        ip[j]++
        if ip[j] > 0 {
            break
        }
    }
}
  1. 定义规则结构体和相关操作函数:
  1. 定义连接记录结构体及相关方法:
  1. 主函数逻辑:

注意

这里的代码仅是抛砖引玉,其中的规则检查逻辑,尤其是针对时间窗口的处理比较简化,在实际应用场景中,你可能需要精确地记录每个连接的时间戳,并按照时间窗口准确判断是否在指定时间段内达到了阈值等情况,以实现更符合实际需求的检测逻辑。

模拟的 IP 访问场景也非常简单,只是为了演示功能而进行的简单循环增加访问次数,实际需要对接真实的网络流量监控等相关机制来准确记录 IP 的访问情况。

到此这篇关于GO实现基于命令行的简单IPS程序代码的文章就介绍到这了,更多相关GO实现基于命令行的简单IPS内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

阅读全文