Commit c963038c authored by qiuqunfeng's avatar qiuqunfeng
Browse files

commit

parent 1f48bad2
...@@ -8,11 +8,16 @@ import ( ...@@ -8,11 +8,16 @@ import (
) )
func SetWafRouter(e *gin.Engine, clusterClientManager *utils.ClusterClientManager, db *gorm.DB) { 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) wafController := controller.NewWafController(clusterClientManager, db)
v1.GET("waf/:region_code/:namespace/:gateway_name", wafController.Waf) v1.GET("/:region_code/:namespace/:gateway_name", wafController.Waf)
v1.POST("waf", wafController.CreateWaf) v1.POST("/", wafController.CreateWaf)
v1.PUT("mode", wafController.UpdateMode) 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) 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 ( ...@@ -7,7 +7,9 @@ CREATE TABLE waf_services (
mode VARCHAR(50) NOT NULL, mode VARCHAR(50) NOT NULL,
rule_num INTEGER DEFAULT 0, rule_num INTEGER DEFAULT 0,
attack_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 -- Create waf_rules table
......
...@@ -2,6 +2,7 @@ package controller ...@@ -2,6 +2,7 @@ package controller
import ( import (
"context" "context"
"strconv"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
...@@ -90,6 +91,24 @@ func (c *WafController) UpdateMode(ctx *gin.Context) { ...@@ -90,6 +91,24 @@ func (c *WafController) UpdateMode(ctx *gin.Context) {
utils.AssembleResponse(ctx, nil, nil) 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) { func (c *WafController) SaveRuleCategoryToDB(ctx *gin.Context) {
ctx1, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx1, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
...@@ -101,3 +120,87 @@ func (c *WafController) SaveRuleCategoryToDB(ctx *gin.Context) { ...@@ -101,3 +120,87 @@ func (c *WafController) SaveRuleCategoryToDB(ctx *gin.Context) {
} }
utils.AssembleResponse(ctx, nil, nil) 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 { ...@@ -40,6 +40,12 @@ func (r *RuleCategoryStatusList) Scan(src interface{}) error {
return json.Unmarshal(src.([]byte), r) return json.Unmarshal(src.([]byte), r)
} }
const (
WafStatusEnable = 0
WafStatusDisable = 1
WafStatusUnknown = 2
)
type WafService struct { type WafService struct {
ID uint `gorm:"column:id;primaryKey;autoIncrement"` ID uint `gorm:"column:id;primaryKey;autoIncrement"`
GatewayName string `gorm:"column:gateway_name"` GatewayName string `gorm:"column:gateway_name"`
...@@ -49,6 +55,7 @@ type WafService struct { ...@@ -49,6 +55,7 @@ type WafService struct {
RuleNum int `gorm:"column:rule_num"` RuleNum int `gorm:"column:rule_num"`
AttackNum int `gorm:"column:attack_num"` AttackNum int `gorm:"column:attack_num"`
RuleCategoryStatus *RuleCategoryStatus `gorm:"column:rule_category_status;type:json"` RuleCategoryStatus *RuleCategoryStatus `gorm:"column:rule_category_status;type:json"`
Host HostList `gorm:"column:host"`
} }
func (WafService) TableName() string { func (WafService) TableName() string {
...@@ -107,3 +114,16 @@ func (r *WafRuleCategory) Scan(src interface{}) error { ...@@ -107,3 +114,16 @@ func (r *WafRuleCategory) Scan(src interface{}) error {
func (r WafRuleCategory) Value() (driver.Value, error) { func (r WafRuleCategory) Value() (driver.Value, error) {
return json.Marshal(r) 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 { ...@@ -7,5 +7,10 @@ type Service interface {
GetWaf(ctx context.Context, regionCode, namespace, gatewayName string) (*WafService, error) GetWaf(ctx context.Context, regionCode, namespace, gatewayName string) (*WafService, error)
CreateWaf(ctx context.Context, req *CreateWafReq) (*WafService, error) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafService, error)
UpdateMode(ctx context.Context, req *UpdateModeReq) (*WafService, error) UpdateMode(ctx context.Context, req *UpdateModeReq) (*WafService, error)
UpdateRule(ctx context.Context, req *RuleRequest) error
SaveRuleCategoryToDB(ctx context.Context) 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 { ...@@ -170,15 +170,27 @@ type WafService struct {
AttackNum int `json:"attack_num"` AttackNum int `json:"attack_num"`
} // WAF configuration details } // WAF configuration details
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 { type CreateWafReq struct {
RegionCode string `json:"region_code"` GatewateInfo
Namespace string `json:"namespace"` Port uint32 `json:"port"`
GatewayName string `json:"gateway_name"` Host []string `json:"host"`
Port uint32 `json:"port"` }
Host []string `json:"host"`
type DeleteWafReq struct {
GatewateInfo
Ports []int `json:"ports"`
} }
type RuleRequest struct { type RuleRequest struct {
GatewateInfo
CategoryID []string `json:"category_id"` CategoryID []string `json:"category_id"`
Status int `json:"status"` Status int `json:"status"`
} }
...@@ -198,6 +210,17 @@ type UpdateModeReq struct { ...@@ -198,6 +210,17 @@ type UpdateModeReq struct {
RegionCode string `json:"region_code"` 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 { type WafRule struct {
ID int `json:"id"` ID int `json:"id"`
Level int `json:"level"` Level int `json:"level"`
...@@ -225,3 +248,21 @@ type WafRuleCategory struct { ...@@ -225,3 +248,21 @@ type WafRuleCategory struct {
Description Description `json:"description"` Description Description `json:"description"`
Rules []WafRule `json:"rules"` 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 package service
import ( import (
"bytes"
"context" "context"
"encoding/json"
"fmt" "fmt"
"net/http"
"os" "os"
"slices" "slices"
...@@ -57,6 +60,9 @@ func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafServ ...@@ -57,6 +60,9 @@ func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafServ
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: req.Namespace, Namespace: req.Namespace,
Labels: map[string]string{
"apigateway_name": req.GatewayName,
},
}, },
Spec: v1alpha1.ServiceSpec{ Spec: v1alpha1.ServiceSpec{
HostNames: req.Host, HostNames: req.Host,
...@@ -86,7 +92,7 @@ func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafServ ...@@ -86,7 +92,7 @@ func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafServ
// Get existing WAF service config if any // Get existing WAF service config if any
wafService := &model.WafService{} 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 != nil {
if err == gorm.ErrRecordNotFound { if err == gorm.ErrRecordNotFound {
// Create new WAF service record if not found // Create new WAF service record if not found
...@@ -95,6 +101,7 @@ func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafServ ...@@ -95,6 +101,7 @@ func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafServ
Namespace: req.Namespace, Namespace: req.Namespace,
GatewayName: req.GatewayName, GatewayName: req.GatewayName,
Mode: string(WafModeAlert), Mode: string(WafModeAlert),
Host: model.HostList(req.Host),
} }
if err := s.db.Create(wafService).Error; err != nil { if err := s.db.Create(wafService).Error; err != nil {
return nil, fmt.Errorf("failed to create WAF service: %v", err) 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 ...@@ -146,6 +153,18 @@ func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafServ
return nil, nil 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) { func (s *wafService) UpdateMode(ctx context.Context, req *UpdateModeReq) (*WafService, error) {
// Check if WAF service exists // Check if WAF service exists
wafService := &model.WafService{} wafService := &model.WafService{}
...@@ -244,3 +263,201 @@ func (s *wafService) SaveRuleCategoryToDB(ctx context.Context) error { ...@@ -244,3 +263,201 @@ func (s *wafService) SaveRuleCategoryToDB(ctx context.Context) error {
return nil 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