入侵防御系统
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()) } } }
运行
- 确保已经安装了 go 环境(go 1.10 及以上版本)。
- 将上述代码保存到一个以 .go 为后缀的文件中,比如 ips.go。
- 打开命令行,切换到该文件所在的目录。
- 运行命令 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 } } }
- 定义规则结构体和相关操作函数:
- 首先定义了 rule 结构体,其字段与 json 格式的规则文件中的各个属性相对应,用于表示一条安全规则。
- loadrules 函数用于读取指定路径的 json 规则文件,并将其解析为 rule 结构体的切片,若读取或解析过程出现错误则返回相应的错误信息。
- 定义连接记录结构体及相关方法:
- connectionrecord 结构体用于记录 ip 地址对不同端口的连接情况,内部使用一个互斥锁(sync.mutex)来保证并发安全,通过 record 方法记录每个 ip 和端口的连接次数,使用 checkviolation 方法来检查当前的连接记录是否违反了给定的规则(这里只是简单根据连接次数是否达到阈值判断,实际可进一步完善考虑时间窗口等更多细节)。
- 主函数逻辑:
- 先获取当前目录下规则配置文件(rules.json)的路径(可按需修改为其他路径),调用 loadrules 函数加载规则列表。
- 接着解析要检测的目标 ip 网段(示例中为 192.168.1.0/24,可以根据实际情况更改),创建一个 connectionrecord 实例来记录连接情况。
- 然后通过循环模拟 ip 对内网各端口的访问(这里只是简单循环增加访问次数示意,实际需要结合真实的网络连接情况来记录),并调用 connectionrecord 的 record 方法记录每次的访问情况。
- 最后调用 connectionrecord 的 checkviolation 方法检查是否有违反规则的情况,若有则打印出相应的违规规则信息。
注意
这里的代码仅是抛砖引玉,其中的规则检查逻辑,尤其是针对时间窗口的处理比较简化,在实际应用场景中,你可能需要精确地记录每个连接的时间戳,并按照时间窗口准确判断是否在指定时间段内达到了阈值等情况,以实现更符合实际需求的检测逻辑。
模拟的 ip 访问场景也非常简单,只是为了演示功能而进行的简单循环增加访问次数,实际需要对接真实的网络流量监控等相关机制来准确记录 ip 的访问情况。
到此这篇关于go实现基于命令行的简单ips程序代码的文章就介绍到这了,更多相关go实现基于命令行的简单ips内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论