Skip to content

通过飞书机器人对特定机器远程封禁恶意IP

Notifications You must be signed in to change notification settings

Beatrueman/EasyBanner

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

EasyBanner

功能介绍

通过@机器人来触发飞书机器人对特定机器远程封禁恶意IP,依赖于GitHub - evilsp/xdp_banner: 一个简单的 XDP 小程序,用于 BAN IP

1.当机器人未连接服务器时,提示如下消息。

image-20240827174105360

2.当未发现访问次数超过250次的IP,提示如下消息。

image-20240827174159408

3.当发现访问次数超过250次的恶意IP,罗列出恶意IP以及对应的访问次数,并显示封禁BAN按钮。

image-20240827174315387

点击按钮,可以对远程机器进行封禁恶意IP,然后卡片更新,提示封禁完成。

image-20240827174403213

逻辑介绍

接口

目标机器开放两个接口

  • GET /execute:用于查询日志里当前小时内访问量排名前十的IP以及对应的次数。
  • POST /ban:用于接收需要封禁的IP,然后执行xdp封禁命令。

机器人设置两个接口

  • POST /webhook:用来处理接收消息事件。
  • POST /event:用来处理卡片回调
  • POST /event/alert:用来接收来自Grafana Alerting Webhook的消息体

运行逻辑

用户给机器人发消息,触发接收消息v2.0事件,飞书服务器返回消息体,通过消息体里messageBody.Event.Message.Mentionsmention.ID.UserID是否为空来判断用户发的消息是否为@机器人,不是则忽略。

是则发送卡片消息。这时调用目标机器/execute接口来获得IP以及对应的次数,通过判断 ip 次数,发送不同的模板。

没有返回数据,则发送未连接服务器模板。有返回数据但没有大于250次的IP,返回未检测到恶意IP模板。

有返回数据且有大于250次的IP时,获取所有次数大于250次的IP以及对应的次数动态填充至 JSON 模板,然后发送。

此时用户点击红色按钮BAN,会触发卡片回传交互事件,此时会回传数据。接下来对回传消息体进行一些判断:

  1. 判断event_type是否为card.action.trigger
  2. 判断action.Tag是否为button
  3. 判断action.Value中键action对应的值是否为ban_ip

如果全部满足,则将需要封禁的IP制作成请求体,对目标机器POST /ban进行调用。

API成功调用后,调用飞书更新卡片API,对卡片内容进行更新。

部署

申请机器人

app_idapp_secret获取方法

1.用企业账户,在开发者后台中,创建企业自建应用

image-20230731184331697

2.找到app_id与qpp_secret

image-20230731184507412

3.添加应用能力,选择机器人

image-20230731184549009

4.添加以下权限

im:message,im:message.group_at_msg,im:message.group_at_msg:readonly,im:message.group_msg,im:message.p2p_msg,im:message.p2p_msg:readonly,im:message:readonly,im:chat:readonly,im:chat,im:message:send_as_bot

image-20230731184637236

5.订阅接收消息卡片回传交互

若要使机器人有互动对话功能,需要填写请求配置地址,并添加接收消息v2.0消息已读v2.0事件

卡片交互需要订阅卡片回传交互

image-20240827203202358

image-20240827203244721

裸机部署

首先需要在目标机器上执行EasyBanner/pkgs/data/app_current.py,保持其稳定运行。

最好将其制作成Service,保证后台持久运行。

这里提供get_ip.service文件供参考。

[Unit] 
Description=Get IP Service 
After=network.target 
[Service] 
User=root 
WorkingDirectory=/root/yiiong/get_ip  # app_current.py所在目录
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ExecStart=/bin/bash -c 'source /root/yiiong/get_ip/venv/bin/activate && exec python3 app_current.py'  # 运行Python程序,注意文件路径
Restart=always 
[Install] 
WantedBy=multi-user.target

或者手动运行

python3 ../EasyBanner/pkgs/data/app_current.py

然后下载依赖后,运行main.go

go mod tidy
go run main.go

Docker部署

目标机器的API只能裸机部署

容器启动

docker run -d -p 8080:8080 \
-e APP_ID=your_AppID \ # 填写飞书应用 AppID
-e APP_SECRET=your_AppSecret \ # 填写飞书应用 AppSecret
-e URL=your_url \  # 填写检测主机地址
-e GIN_MODE=release beatrueman/easybanner:v1.0.7

Kubernetes部署

先创建Secret设置环境变量

kubectl create secret generic easybanner-secret \
--from-literal=App_ID=your_AppID \
--from-literal=App_Secret=your_AppSecret \
--from-literal=URL=your_url \
--from-literal=GIN_MODE=release \ # 设置gin为生产模式
--namespace=your_namespace

然后apply../deply目录下的deployment.yamlservice.yaml

所有注意修改命名空间。

还有要注意部署的机器人与目标机器接口的通信问题

kubectl apply deployment.yaml
kuectl apply service.yaml

# 如果有域名需求,还可以添加ingressroute或者ingress,注意修改host
# 这里使用ingressroute
kubectl apply -f ingressroute.yaml

缺陷

  • 机器人可能会重复发送消息。
  • 因为会对大日志文件利用bash进行查询,所以执行GET /execute速度会比较慢,测试大概会消耗20s左右。
  • BAN操作比较慢,需要等待一会才能更新卡片。

更新日志

2024/9/15

添加了用于打包和推送镜像的Github Action

使用时在Settings >> Secrets and varibles >> Actions中添加secrets

REGISTRY_USERNAMEREGISTRY_PASSWORD

image-20240915002948963

如果要推送到类似Harbor的自建仓库,请添加varibles

IMAGE_REGISTRY_SERVICE:默认为docker.io

IMAGE_REPOSITORY:默认为beatrueman/eaesybanner

推送时请指定tag,格式为v1.0.0,用于指定镜像版本

git tag v1.0.0
git push origin v1.0.0

或者手动指定tag

2024/11/23

为了实现机器人在无人值守的情况下也能够自动封禁IP,现在增加了自动封禁IP功能。

结合了Grafana Alerting,机器人能够做到Grafana在alert后,自动在飞书群中发送恶意IP列表,并且自动执行封禁IP。

新增了模拟Grafana Alerting发送报警的过程

EasyBanner/utils/mock中新增mock.go,用于发送templates/alert.json,其中alert.json中包含了Grafana Alerting Webhook的消息体,详细内容可在仓库或配置用于警报的 Webhook 通知程序 | Grafana 文档 - Grafana 可观测平台查看。方便了开发过程中的对EasyBanner的测试。

新增了一些获取基本信息的函数

EasyBanner/utils/base下新增info.goInitConfig.go

  • info.go

    func GetTenantAccessToken() string {}                      // 获取机器人的tenant_access_token
    
    func GetChatID() string {}                                 // 获取机器人所在群聊 chat_id
    
    func GetLatestMessageID(AppID string) (string, error) {}   // 获取群组来自EB的最新一个卡片的message_id
    
    
  • InitConfig.go:将原initConfig()封装为包

新增根据Grafana Alerting直接推送恶意IP列表并直接封禁的逻辑

EasyBanner/pkgs/grafana/receive.go:用于接收来自Grafana联络点的消息体,提取Alerts.labels.alertname。其中alertname有预设的两种:

  • aviation telecom带宽持续满载
  • 集群内部流量

alertname不为空时,会发送无按键的交互卡片消息,等待一段时间后,机器人会调用远程机器的封禁/ban接口,然后对IP封禁,最后更新卡片,响应封禁是否成功。

新增的函数

// 用于不需要按键交互的生成模板
func GenerateNoButtonTemplate(ipDataList []model.IPData, showButton bool, resultText string) (string, error) {}

// 获取所有次数大于250次的IP以及对应的次数填充至 JSON 模板,无按键版
func GetNeedBanIPNoButton() (string, error) {}

// 获取alertname,输出报警类型
func GetAlertName(body *model.Body) (string, error) {}

// 无按键版卡片交互
func HandleAlert(c *gin.Context) {}

About

通过飞书机器人对特定机器远程封禁恶意IP

Resources

Stars

Watchers

Forks

Packages

No packages published