Commit c963038c authored by qiuqunfeng's avatar qiuqunfeng
Browse files

commit

parent 1f48bad2
......@@ -8,11 +8,16 @@ import (
)
func SetWafRouter(e *gin.Engine, clusterClientManager *utils.ClusterClientManager, db *gorm.DB) {
v1 := e.Group("v1/api")
v1 := e.Group("v1/api/waf")
wafController := controller.NewWafController(clusterClientManager, db)
v1.GET("waf/:region_code/:namespace/:gateway_name", wafController.Waf)
v1.POST("waf", wafController.CreateWaf)
v1.GET("/:region_code/:namespace/:gateway_name", wafController.Waf)
v1.POST("/", wafController.CreateWaf)
v1.PUT("mode", wafController.UpdateMode)
v1.PUT("rules", wafController.UpdateRule)
v1.PUT("listener/enable", wafController.EnableListenerWaf)
v1.PUT("gateway/enable", wafController.EnableGatewayWaf)
v1.DELETE("listener/:region_code/:namespace/:gateway_name/:port", wafController.DeleteListenerWaf)
v1.DELETE("gateway/:region_code/:namespace/:gateway_name", wafController.DeleteGatewayWaf)
v1.POST("debug/savecatagory", wafController.SaveRuleCategoryToDB)
}
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: testaaa-lsnr-aaaa
namespace: tensorsec
labels:
apigateway: testaaa
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- "*"
port:
name: http2-9999
number: 9999
protocol: HTTP2
\ No newline at end of file
......@@ -7,7 +7,9 @@ CREATE TABLE waf_services (
mode VARCHAR(50) NOT NULL,
rule_num INTEGER DEFAULT 0,
attack_num INTEGER DEFAULT 0,
rule_category_status JSON
rule_category_status JSON,
status INTEGER NOT NULL,
port INTEGER NOT NULL
);
-- Create waf_rules table
......
......@@ -2,6 +2,7 @@ package controller
import (
"context"
"strconv"
"time"
"github.com/gin-gonic/gin"
......@@ -90,6 +91,24 @@ func (c *WafController) UpdateMode(ctx *gin.Context) {
utils.AssembleResponse(ctx, nil, nil)
}
func (c *WafController) UpdateRule(ctx *gin.Context) {
ctx1, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var req service.RuleRequest
if err := ctx.BindJSON(&req); err != nil {
utils.AssembleResponse(ctx, nil, err)
return
}
err := c.service.UpdateRule(ctx1, &req)
if err != nil {
utils.AssembleResponse(ctx, nil, err)
return
}
utils.AssembleResponse(ctx, nil, nil)
}
func (c *WafController) SaveRuleCategoryToDB(ctx *gin.Context) {
ctx1, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
......@@ -101,3 +120,87 @@ func (c *WafController) SaveRuleCategoryToDB(ctx *gin.Context) {
}
utils.AssembleResponse(ctx, nil, nil)
}
func (c *WafController) EnableListenerWaf(ctx *gin.Context) {
ctx1, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var req service.EnableListenerWafReq
if err := ctx.BindJSON(&req); err != nil {
utils.AssembleResponse(ctx, nil, err)
return
}
err := c.service.EnableListenerWaf(ctx1, &req)
if err != nil {
utils.AssembleResponse(ctx, nil, err)
return
}
utils.AssembleResponse(ctx, nil, nil)
}
func (c *WafController) EnableGatewayWaf(ctx *gin.Context) {
ctx1, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var req service.EnableGatewayWafReq
if err := ctx.BindJSON(&req); err != nil {
utils.AssembleResponse(ctx, nil, err)
return
}
err := c.service.EnableGatewayWaf(ctx1, &req)
if err != nil {
utils.AssembleResponse(ctx, nil, err)
return
}
utils.AssembleResponse(ctx, nil, nil)
}
func (c *WafController) DeleteListenerWaf(ctx *gin.Context) {
ctx1, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
regionCode := ctx.Param("region_code")
namespace := ctx.Param("namespace")
gatewayName := ctx.Param("gateway_name")
port, err := strconv.Atoi(ctx.Param("port"))
if err != nil {
utils.AssembleResponse(ctx, nil, err)
return
}
err = c.service.DeleteListenerWaf(ctx1, &service.DeleteListenerReq{
GatewateInfo: service.GatewateInfo{
RegionCode: regionCode,
Namespace: namespace,
GatewayName: gatewayName,
},
Port: port,
})
if err != nil {
utils.AssembleResponse(ctx, nil, err)
return
}
utils.AssembleResponse(ctx, nil, nil)
}
func (c *WafController) DeleteGatewayWaf(ctx *gin.Context) {
ctx1, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
regionCode := ctx.Param("region_code")
namespace := ctx.Param("namespace")
gatewayName := ctx.Param("gateway_name")
err := c.service.DeleteGatewayWaf(ctx1, &service.GatewateInfo{
RegionCode: regionCode,
Namespace: namespace,
GatewayName: gatewayName,
})
if err != nil {
utils.AssembleResponse(ctx, nil, err)
return
}
utils.AssembleResponse(ctx, nil, nil)
}
......@@ -40,6 +40,12 @@ func (r *RuleCategoryStatusList) Scan(src interface{}) error {
return json.Unmarshal(src.([]byte), r)
}
const (
WafStatusEnable = 0
WafStatusDisable = 1
WafStatusUnknown = 2
)
type WafService struct {
ID uint `gorm:"column:id;primaryKey;autoIncrement"`
GatewayName string `gorm:"column:gateway_name"`
......@@ -49,6 +55,7 @@ type WafService struct {
RuleNum int `gorm:"column:rule_num"`
AttackNum int `gorm:"column:attack_num"`
RuleCategoryStatus *RuleCategoryStatus `gorm:"column:rule_category_status;type:json"`
Host HostList `gorm:"column:host"`
}
func (WafService) TableName() string {
......@@ -107,3 +114,16 @@ func (r *WafRuleCategory) Scan(src interface{}) error {
func (r WafRuleCategory) Value() (driver.Value, error) {
return json.Marshal(r)
}
type GatewayListener struct {
ID int `gorm:"column:id;primaryKey;autoIncrement"`
GatewayName string `gorm:"column:gateway_name"`
Namespace string `gorm:"column:namespace"`
RegionCode string `gorm:"column:region_code"`
Port int `gorm:"column:port"`
Enable bool `gorm:"column:enable"`
}
func (GatewayListener) TableName() string {
return "gateway_listeners"
}
......@@ -7,5 +7,10 @@ type Service interface {
GetWaf(ctx context.Context, regionCode, namespace, gatewayName string) (*WafService, error)
CreateWaf(ctx context.Context, req *CreateWafReq) (*WafService, error)
UpdateMode(ctx context.Context, req *UpdateModeReq) (*WafService, error)
UpdateRule(ctx context.Context, req *RuleRequest) error
SaveRuleCategoryToDB(ctx context.Context) error
EnableListenerWaf(ctx context.Context, req *EnableListenerWafReq) error
EnableGatewayWaf(ctx context.Context, req *EnableGatewayWafReq) error
DeleteGatewayWaf(ctx context.Context, req *GatewateInfo) error
DeleteListenerWaf(ctx context.Context, req *DeleteListenerReq) error
}
......@@ -170,15 +170,27 @@ type WafService struct {
AttackNum int `json:"attack_num"`
} // WAF configuration details
type CreateWafReq struct {
RegionCode string `json:"region_code"`
Namespace string `json:"namespace"`
type GatewateInfo struct {
GatewayName string `json:"gateway_name"`
Namespace string `json:"namespace"`
RegionCode string `json:"region_code"`
ApiGatewayCrn string `json:"gateway_crn"`
Hosts []string `json:"hosts"`
}
type CreateWafReq struct {
GatewateInfo
Port uint32 `json:"port"`
Host []string `json:"host"`
}
type DeleteWafReq struct {
GatewateInfo
Ports []int `json:"ports"`
}
type RuleRequest struct {
GatewateInfo
CategoryID []string `json:"category_id"`
Status int `json:"status"`
}
......@@ -198,6 +210,17 @@ type UpdateModeReq struct {
RegionCode string `json:"region_code"`
}
type EnableListenerWafReq struct {
GatewateInfo
Enable bool `json:"enable"`
Port int `json:"port"`
}
type EnableGatewayWafReq struct {
GatewateInfo
Enable bool `json:"enable"`
}
type WafRule struct {
ID int `json:"id"`
Level int `json:"level"`
......@@ -225,3 +248,21 @@ type WafRuleCategory struct {
Description Description `json:"description"`
Rules []WafRule `json:"rules"`
}
type GatewayListener struct {
GatewayName string `json:"gateway_name"`
Namespace string `json:"namespace"`
RegionCode string `json:"region_code"`
Port int `json:"port"`
Enable bool `json:"enable"`
}
type CreateListenerReq struct {
GatewateInfo
Port int `json:"port"`
}
type DeleteListenerReq struct {
GatewateInfo
Port int `json:"port"`
}
package service
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"slices"
......@@ -57,6 +60,9 @@ func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafServ
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: req.Namespace,
Labels: map[string]string{
"apigateway_name": req.GatewayName,
},
},
Spec: v1alpha1.ServiceSpec{
HostNames: req.Host,
......@@ -86,7 +92,7 @@ func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafServ
// Get existing WAF service config if any
wafService := &model.WafService{}
err := s.db.Model(&model.WafService{}).Where("gateway_name = ?", req.GatewayName).First(wafService).Error
err := s.db.Model(&model.WafService{}).Where("gateway_name = ? AND namespace = ? AND region_code = ?", req.GatewayName, req.Namespace, req.RegionCode).First(wafService).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
// Create new WAF service record if not found
......@@ -95,6 +101,7 @@ func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafServ
Namespace: req.Namespace,
GatewayName: req.GatewayName,
Mode: string(WafModeAlert),
Host: model.HostList(req.Host),
}
if err := s.db.Create(wafService).Error; err != nil {
return nil, fmt.Errorf("failed to create WAF service: %v", err)
......@@ -146,6 +153,18 @@ func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafServ
return nil, nil
}
func (s *wafService) DeleteListenerWaf(ctx context.Context, req *DeleteListenerReq) error {
client := s.clusterClientManager.GetClient(req.RegionCode)
if client == nil {
return fmt.Errorf("failed to get cluster client")
}
name := fmt.Sprintf("%s-%d", req.GatewayName, req.Port)
if err := client.WafV1alpha1().Services(req.Namespace).Delete(ctx, name, metav1.DeleteOptions{}); err != nil {
return fmt.Errorf("failed to delete WAF service: %v", err)
}
return nil
}
func (s *wafService) UpdateMode(ctx context.Context, req *UpdateModeReq) (*WafService, error) {
// Check if WAF service exists
wafService := &model.WafService{}
......@@ -244,3 +263,201 @@ func (s *wafService) SaveRuleCategoryToDB(ctx context.Context) error {
return nil
}
func (s *wafService) CreateListener(ctx context.Context, req *CreateListenerReq) (*GatewayListener, error) {
listener := &model.GatewayListener{}
err := s.db.Model(&model.GatewayListener{}).Where("gateway_name = ? AND namespace = ? AND region_code = ?", req.GatewayName, req.Namespace, req.RegionCode).First(listener).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
listener = &model.GatewayListener{
GatewayName: req.GatewayName,
Namespace: req.Namespace,
RegionCode: req.RegionCode,
Port: req.Port,
Enable: true,
}
err = s.db.Model(&model.GatewayListener{}).Create(listener).Error
if err != nil {
return nil, err
}
} else {
return nil, err
}
}
return &GatewayListener{
GatewayName: listener.GatewayName,
Namespace: listener.Namespace,
RegionCode: listener.RegionCode,
Port: listener.Port,
Enable: listener.Enable,
}, nil
}
func (s *wafService) DeleteListener(ctx context.Context, req *DeleteListenerReq) error {
listener := &model.GatewayListener{}
err := s.db.Model(&model.GatewayListener{}).Where("gateway_name = ? AND namespace = ? AND region_code = ?", req.GatewayName, req.Namespace, req.RegionCode).First(listener).Error
if err != nil {
return err
}
err = s.db.Model(&model.GatewayListener{}).Where("gateway_name = ? AND namespace = ? AND region_code = ?", req.GatewayName, req.Namespace, req.RegionCode).Delete(listener).Error
if err != nil {
return err
}
return nil
}
func (s *wafService) EnableListenerWaf(ctx context.Context, req *EnableListenerWafReq) error {
listener := &model.GatewayListener{}
err := s.db.Model(&model.GatewayListener{}).Where("gateway_name = ? AND namespace = ? AND region_code = ?", req.GatewayName, req.Namespace, req.RegionCode).First(listener).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
listener = &model.GatewayListener{
GatewayName: req.GatewayName,
Namespace: req.Namespace,
RegionCode: req.RegionCode,
Port: int(req.Port),
Enable: req.Enable,
}
err = s.db.Model(&model.GatewayListener{}).Create(listener).Error
if err != nil {
return err
}
} else {
return err
}
}
listener.Enable = req.Enable
err = s.db.Model(&model.GatewayListener{}).Where("gateway_name = ? AND namespace = ? AND region_code = ?", req.GatewayName, req.Namespace, req.RegionCode).Update("enable", req.Enable).Error
if err != nil {
return err
}
wafService := &model.WafService{}
err = s.db.Model(&model.WafService{}).Where("gateway_name = ? AND namespace = ? AND region_code = ?", req.GatewayName, req.Namespace, req.RegionCode).First(wafService).Error
if err != nil && err != gorm.ErrRecordNotFound {
return err
}
if listener.Enable {
s.CreateWaf(ctx, &CreateWafReq{
GatewateInfo: GatewateInfo{
GatewayName: req.GatewayName,
Namespace: req.Namespace,
RegionCode: req.RegionCode,
},
Port: uint32(req.Port),
Host: wafService.Host,
})
} else {
s.DeleteListenerWaf(ctx, &DeleteListenerReq{
GatewateInfo: GatewateInfo{
GatewayName: req.GatewayName,
Namespace: req.Namespace,
RegionCode: req.RegionCode,
},
Port: req.Port,
})
}
return nil
}
func (s *wafService) EnableGatewayWaf(ctx context.Context, req *EnableGatewayWafReq) error {
if req.Enable {
// Get listener list from API
body, err := json.Marshal(map[string]string{
"apigateway_crn": req.ApiGatewayCrn,
"region_code": req.RegionCode,
})
if err != nil {
return fmt.Errorf("failed to marshal request body: %v", err)
}
resp, err := http.Post("https://csm.console.test.tg.unicom.local/apigatewaymng/listener/lf-tst7/list_listeners", "application/json", bytes.NewBuffer(body))
if err != nil {
return fmt.Errorf("failed to get listener list: %v", err)
}
defer resp.Body.Close()
// Parse response
var listeners []struct {
ApigatewayCrn string `json:"apigateway_crn"`
Port uint32 `json:"port"`
Hosts []string `json:"hosts"`
}
if err := json.NewDecoder(resp.Body).Decode(&listeners); err != nil {
return fmt.Errorf("failed to parse listener list: %v", err)
}
// Create WAF for each listener
for _, listener := range listeners {
if _, err := s.CreateWaf(ctx, &CreateWafReq{
GatewateInfo: GatewateInfo{
GatewayName: req.GatewayName,
Namespace: req.Namespace,
RegionCode: req.RegionCode,
},
Port: uint32(listener.Port),
Host: listener.Hosts,
}); err != nil {
return fmt.Errorf("failed to create WAF for listener %d: %v", listener.Port, err)
}
}
} else {
s.DeleteGatewayWaf(ctx, &GatewateInfo{
GatewayName: req.GatewayName,
Namespace: req.Namespace,
RegionCode: req.RegionCode,
})
}
return nil
}
func (s *wafService) DeleteGatewayWaf(ctx context.Context, req *GatewateInfo) error {
client := s.clusterClientManager.GetClient(req.RegionCode)
if client == nil {
return fmt.Errorf("failed to get cluster client")
}
labelSelector := fmt.Sprintf("apigateway_name=%s", req.GatewayName)
if err := client.WafV1alpha1().Services(req.Namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: labelSelector}); err != nil {
return fmt.Errorf("failed to delete WAF service: %v", err)
}
return nil
}
func (s *wafService) UpdateRule(ctx context.Context, req *RuleRequest) error {
wafService := &model.WafService{}
err := s.db.Model(&model.WafService{}).Where("gateway_name = ?", req.GatewayName).First(wafService).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
// Create new WAF service record if not found
wafService = &model.WafService{
RegionCode: req.RegionCode,
Namespace: req.Namespace,
GatewayName: req.GatewayName,
Mode: string(WafModeAlert),
RuleCategoryStatus: &model.RuleCategoryStatus{
CategoryID: req.CategoryID,
Status: req.Status,
},
}
if err := s.db.Create(wafService).Error; err != nil {
return fmt.Errorf("failed to create WAF service: %v", err)
}
} else {
return fmt.Errorf("failed to query WAF service: %v", err)
}
} else {
// Update mode if service exists
if err := s.db.Model(wafService).Update("rule_category_status", model.RuleCategoryStatus{
CategoryID: req.CategoryID,
Status: req.Status,
}).Error; err != nil {
return fmt.Errorf("failed to update WAF service mode: %v", err)
}
}
return nil
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment