Commit 7a0e8ca0 authored by qiuqunfeng's avatar qiuqunfeng
Browse files

Add listener history endpoint and implement history logging

- Introduce a new GET endpoint for listing listener history in the WAF router.
- Implement ListListenerHistory method in the WafController to handle requests and return paginated results based on query parameters.
- Enhance the WAF service to log listener creation and deletion events, capturing relevant details for auditing purposes.
- Update the WafListenerHistory model and service methods to support querying and storing listener history data effectively.
parent 6f3e1a26
......@@ -23,6 +23,7 @@ func SetWafRouter(e *gin.Engine, clusterClientManager *utils.ClusterClientManage
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.GET("listener/history", wafController.ListListenerHistory)
v2 := e.Group("api/v2/containerSec/waf")
v2.GET("attack/log/list", wafController.ListAttackLogs)
......@@ -34,4 +35,5 @@ func SetWafRouter(e *gin.Engine, clusterClientManager *utils.ClusterClientManage
v2.DELETE("blackwhitelist/:id", wafController.DeleteBlackWhiteList)
v2.GET("blackwhitelists", wafController.GetBlackWhiteLists)
v2.GET("services", wafController.ListWafs)
}
......@@ -496,3 +496,59 @@ func (c *WafController) GetBlackWhiteLists(ctx *gin.Context) {
}
utils.AssembleResponse(ctx, respData, nil)
}
func (c *WafController) ListListenerHistory(ctx *gin.Context) {
ctx1, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
limit, offset, err := getLimitAndOffset(ctx)
if err != nil {
utils.AssembleResponse(ctx, nil, err)
return
}
name := ctx.Query("name")
gatewayName := ctx.Query("gateway_name")
listenerName := ctx.Query("listener_name")
regionCode := ctx.Query("region_code")
operation := ctx.Query("operation")
status := ctx.Query("status")
query := service.WafListenerHistoryQuery()
if name != "" {
query.WithFuzzyName(name)
}
if gatewayName != "" {
query.WithFuzzyGatewayName(gatewayName)
}
if listenerName != "" {
query.WithFuzzyListenerName(listenerName)
}
if regionCode != "" {
query.WithFuzzyRegionCode(regionCode)
}
if operation != "" {
query.WithOperation(operation)
}
if status != "" {
statusInt, err := strconv.Atoi(status)
if err != nil {
utils.AssembleResponse(ctx, nil, err)
return
}
query.WithStatus(int32(statusInt))
}
lists, total, err := c.service.ListListenerHistory(ctx1, query, limit, offset)
if err != nil {
utils.AssembleResponse(ctx, nil, err)
return
}
respData := utils.ListRespData{
Items: lists,
TotalItems: total,
ItemsPerPage: limit,
}
utils.AssembleResponse(ctx, respData, nil)
}
......@@ -4,6 +4,7 @@ import (
"database/sql/driver"
"encoding/json"
"errors"
"time"
"github.com/rs/zerolog/log"
)
......@@ -159,3 +160,39 @@ type MatcherExpr struct {
func (MatcherExpr) TableName() string {
return "waf_blackwhitelists"
}
type Model struct {
ID uint `gorm:"primarykey"`
CreatedAt time.Time `gorm:"column:created_at"`
UpdatedAt time.Time `gorm:"column:updated_at"`
}
type Status int
const (
StatusSuccess Status = iota
StatusFailure
)
type Operation string
const (
OperationCreate Operation = "CREATE"
OperationDelete Operation = "DELETE"
)
type WafListenerHistory struct {
Model
Name string `gorm:"column:name"`
GatewayName string `gorm:"column:gateway_name"`
ListenerName string `gorm:"column:listener_name"`
Namespace string `gorm:"column:namespace"`
RegionCode string `gorm:"column:region_code"`
Description string `gorm:"column:description"`
Status Status `gorm:"column:status"`
Operation Operation `gorm:"column:operation"`
}
func (WafListenerHistory) TableName() string {
return "waf_listener_histories"
}
package service
import "context"
import (
"context"
"gitlab.com/tensorsecurity-rd/waf-console/internal/model"
)
type Service interface {
// QueryIP(ip string) (*model.IPInfo, error)
GetWaf(ctx context.Context, regionCode, namespace, gatewayName string) (*WafService, error)
ListWafs(ctx context.Context) ([]WafService, error)
GetWafGatewayInfo(ctx context.Context, req *GetWafGatewayInfoReq) (*WafService, error)
......@@ -23,4 +26,5 @@ type Service interface {
EnableBlackWhiteList(ctx context.Context, req *MatcherExpr) error
DeleteBlackWhiteList(ctx context.Context, ID uint32) error
GetBlackWhiteLists(ctx context.Context, query *MatchExprQueryOption, limit int, offset int) ([]MatcherExpr, int, error)
ListListenerHistory(ctx context.Context, query *WafListenerHistoryOption, limit, offset int) ([]model.WafListenerHistory, int, error)
}
......@@ -299,7 +299,8 @@ type CreateListenerReq struct {
type DeleteListenerReq struct {
GatewateInfo
Port int `json:"port"`
Port int `json:"port"`
ListenerName string `json:"listener_name"`
}
type GatewayRespListenerData struct {
......@@ -405,3 +406,57 @@ func (q *MatchExprQueryOption) WithInConditionCustom(column string, value interf
q.whereInCondition[column] = value
return q
}
type WafListenerHistory struct {
Name string `json:"name"`
GatewayName string `json:"gateway_name"`
ListenerName string `json:"listener_name"`
Namespace string `json:"namespace"`
RegionCode string `json:"region_code"`
Description string `json:"description"`
Status int `json:"status"`
Operation string `json:"operation"`
}
type WafListenerHistoryOption struct {
WhereLikeCondition map[string]string
WhereEqCondition map[string]interface{}
WhereInCondition map[string]interface{}
}
func WafListenerHistoryQuery() *WafListenerHistoryOption {
return &WafListenerHistoryOption{
WhereLikeCondition: make(map[string]string, 3),
WhereEqCondition: make(map[string]interface{}, 3),
WhereInCondition: make(map[string]interface{}, 3),
}
}
func (n *WafListenerHistoryOption) WithFuzzyName(name string) *WafListenerHistoryOption {
n.WhereLikeCondition["name"] = name
return n
}
func (n *WafListenerHistoryOption) WithFuzzyListenerName(listenerName string) *WafListenerHistoryOption {
n.WhereLikeCondition["listener_name"] = listenerName
return n
}
func (n *WafListenerHistoryOption) WithFuzzyGatewayName(gatewayName string) *WafListenerHistoryOption {
n.WhereLikeCondition["gateway_name"] = gatewayName
return n
}
func (n *WafListenerHistoryOption) WithStatus(status int32) *WafListenerHistoryOption {
n.WhereEqCondition["status"] = status
return n
}
func (n *WafListenerHistoryOption) WithOperation(operation string) *WafListenerHistoryOption {
n.WhereEqCondition["operation"] = operation
return n
}
func (n *WafListenerHistoryOption) WithFuzzyRegionCode(regionCode string) *WafListenerHistoryOption {
n.WhereLikeCondition["region_code"] = regionCode
return n
}
......@@ -203,8 +203,16 @@ func (s *wafService) getRulesForService(req *CreateWafReq) ([]v1alpha1.Rule, err
}
func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafService, error) {
// Create the WAF service resource
var errMsg string
var status int = 1 // Success by default
name := fmt.Sprintf("%s-%d", req.GatewayName, req.Port)
defer func() {
_ = s.addListenerHistory(ctx, name, req.ListenerName, req.GatewayName, req.Namespace, req.RegionCode, errMsg, status, "CREATE")
}()
// Create the WAF service resource
service := &v1alpha1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: name,
......@@ -237,21 +245,29 @@ func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafServ
rules, err := s.getRulesForService(req)
if err != nil {
return nil, fmt.Errorf("failed to get rules for service: %v", err)
status = 0 // Failure
errMsg = fmt.Sprintf("failed to get rules for service: %v", err)
return nil, fmt.Errorf("%s", errMsg)
}
service.Spec.Rules = rules
if len(service.Spec.Rules) == 0 {
return nil, fmt.Errorf("cannot create WAF service with no rules")
status = 0 // Failure
errMsg = "cannot create WAF service with no rules"
return nil, fmt.Errorf("%s", errMsg)
}
// Create the WAF service in Kubernetes
client := s.clusterClientManager.GetClient(req.RegionCode)
if client == nil {
return nil, fmt.Errorf("failed to get cluster client for region %s", req.RegionCode)
status = 0 // Failure
errMsg = fmt.Sprintf("failed to get cluster client for region %s", req.RegionCode)
return nil, fmt.Errorf("%s", errMsg)
}
if _, err := client.Versioned.WafV1alpha1().Services(req.Namespace).Create(ctx, service, metav1.CreateOptions{}); err != nil {
return nil, fmt.Errorf("failed to create WAF service: %v", err)
status = 0 // Failure
errMsg = fmt.Sprintf("failed to create WAF service: %v", err)
return nil, fmt.Errorf("%s", errMsg)
}
return &WafService{
......@@ -263,14 +279,28 @@ func (s *wafService) CreateWaf(ctx context.Context, req *CreateWafReq) (*WafServ
}
func (s *wafService) DeleteListenerWaf(ctx context.Context, req *DeleteListenerReq) error {
var errMsg string
var status int = 1 // Success by default
name := fmt.Sprintf("%s-%d", req.GatewayName, req.Port)
defer func() {
_ = s.addListenerHistory(ctx, name, req.ListenerName, req.GatewayName, req.Namespace, req.RegionCode, errMsg, status, "DELETE")
}()
client := s.clusterClientManager.GetClient(req.RegionCode)
if client == nil {
return fmt.Errorf("failed to get cluster client for region %s", req.RegionCode)
status = 0 // Failure
errMsg = fmt.Sprintf("failed to get cluster client for region %s", req.RegionCode)
return fmt.Errorf("%s", errMsg)
}
name := fmt.Sprintf("%s-%d", req.GatewayName, req.Port)
if err := client.Versioned.WafV1alpha1().Services(req.Namespace).Delete(ctx, name, metav1.DeleteOptions{}); err != nil {
return fmt.Errorf("failed to delete WAF service: %v", err)
status = 0 // Failure
errMsg = fmt.Sprintf("failed to delete WAF service: %v", err)
return fmt.Errorf("%s", errMsg)
}
return nil
}
......@@ -470,7 +500,8 @@ func (s *wafService) EnableListenerWaf(ctx context.Context, req *EnableListenerW
Namespace: req.Namespace,
RegionCode: req.RegionCode,
},
Port: req.Port,
Port: req.Port,
ListenerName: req.ListenerName,
})
if err != nil {
return err
......@@ -1253,3 +1284,43 @@ func (s *wafService) GetBlackWhiteLists(ctx context.Context, query *MatchExprQue
}
return exprsResp, int(total), nil
}
func (s *wafService) ListListenerHistory(ctx context.Context, query *WafListenerHistoryOption, limit int, offset int) ([]model.WafListenerHistory, int, error) {
listenerHistories := []model.WafListenerHistory{}
db := s.db.WithContext(ctx).Model(&model.WafListenerHistory{})
if len(query.WhereEqCondition) > 0 {
db = db.Where(query.WhereEqCondition)
}
for column, val := range query.WhereLikeCondition {
db = db.Where(fmt.Sprintf("%s LIKE ?", column), GetLikeExpr(val))
}
if limit > 0 && offset >= 0 {
db = db.Offset(offset).Limit(limit)
}
err := db.Order("created_at DESC").Find(&listenerHistories).Error
if err != nil {
return nil, 0, err
}
var total int64
err = db.Count(&total).Error
if err != nil {
return nil, 0, err
}
return listenerHistories, int(total), nil
}
func (s *wafService) addListenerHistory(ctx context.Context, name, listenerName, gatewayName, namespace, regionCode, description string, status int, operation string) error {
listenerHistory := model.WafListenerHistory{
Name: name,
GatewayName: gatewayName,
ListenerName: listenerName,
Namespace: namespace,
RegionCode: regionCode,
Description: description,
}
err := s.db.WithContext(ctx).Create(&listenerHistory).Error
if err != nil {
return 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