mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-04 15:06:37 +08:00
[优化] 添加 WAF IP 组功能,包括 CRUD 接口和前端页面支持
This commit is contained in:
@@ -46,6 +46,7 @@ func registeredModels() []any {
|
||||
&AcmeAccount{},
|
||||
&DnsAccount{},
|
||||
&WAFRuleGroup{},
|
||||
&WAFIPGroup{},
|
||||
&WAFRuleGroupBinding{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,6 +184,41 @@ func TestRegisterShardingAutoMigratesShardTables(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpgradeDatabaseSchemaV15ToV16AddsWAFIPGroups(t *testing.T) {
|
||||
db := openBareTestSQLiteDB(t, "v16.db")
|
||||
if err := registerSharding(db, "sqlite"); err != nil {
|
||||
t.Fatalf("register sharding: %v", err)
|
||||
}
|
||||
if err := autoMigrateSchemaMetadata(db); err != nil {
|
||||
t.Fatalf("auto migrate schema metadata: %v", err)
|
||||
}
|
||||
if err := applyCurrentSchema(db, "sqlite"); err != nil {
|
||||
t.Fatalf("apply current schema: %v", err)
|
||||
}
|
||||
if err := ensureDefaultWAFRuleGroup(db); err != nil {
|
||||
t.Fatalf("ensure default waf rule group: %v", err)
|
||||
}
|
||||
if err := saveDatabaseSchemaVersion(db, 15); err != nil {
|
||||
t.Fatalf("save schema version: %v", err)
|
||||
}
|
||||
if err := upgradeDatabaseSchema(db, "sqlite", 15); err != nil {
|
||||
t.Fatalf("upgrade schema: %v", err)
|
||||
}
|
||||
if !db.Migrator().HasTable(&WAFIPGroup{}) {
|
||||
t.Fatal("expected waf_ip_groups table")
|
||||
}
|
||||
if !db.Migrator().HasColumn(&WAFRuleGroup{}, "ip_whitelist_groups") {
|
||||
t.Fatal("expected waf_rule_groups.ip_whitelist_groups column")
|
||||
}
|
||||
version, ok, err := loadDatabaseSchemaVersion(db)
|
||||
if err != nil {
|
||||
t.Fatalf("load schema version: %v", err)
|
||||
}
|
||||
if !ok || version != currentDatabaseSchemaVersion {
|
||||
t.Fatalf("unexpected schema version: got %d ok=%v want %d", version, ok, currentDatabaseSchemaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateObservabilityLegacyColumnsBackfillsHealthEventMetadata(t *testing.T) {
|
||||
db := openTestSQLiteDB(t, "legacy-health-events.db")
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// v16 升级内容:新增 Tunnel 隧道表,为 nodes 增加 TunnelRelay 相关字段,为 proxy_routes 增加隧道上游支持字段。
|
||||
// 背景说明:引入 Tunnel 组件后,边缘节点可通过隧道中继回源,需要区分节点类型(edge_node/relay_node)、记录中继绑定端口,
|
||||
// 并在代理路由中支持 tunnel 上游类型以替代直连回源,同时需要独立的 tunnels 表管理隧道实例。
|
||||
package migrate
|
||||
|
||||
import (
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type wafIPGroupV17 struct{}
|
||||
|
||||
type wafRuleGroupV17 struct {
|
||||
IPWhitelistGroups string `gorm:"column:ip_whitelist_groups;type:text;not null;default:'[]'"`
|
||||
IPBlacklistGroups string `gorm:"column:ip_blacklist_groups;type:text;not null;default:'[]'"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(V17())
|
||||
}
|
||||
|
||||
func V17() Migration {
|
||||
return Migration{
|
||||
FromVersion: 16,
|
||||
ToVersion: 17,
|
||||
Migrate: migrateV17,
|
||||
Validate: validateV17,
|
||||
}
|
||||
}
|
||||
|
||||
func (wafIPGroupV17) TableName() string {
|
||||
return "waf_ip_groups"
|
||||
}
|
||||
|
||||
func (wafRuleGroupV17) TableName() string {
|
||||
return "waf_rule_groups"
|
||||
}
|
||||
|
||||
func migrateV17(ctx Context, db *gorm.DB, backend string) error {
|
||||
return ctx.ApplyCurrentSchema(db, backend)
|
||||
}
|
||||
|
||||
func validateV17(ctx Context, db *gorm.DB, backend string) error {
|
||||
if err := ctx.ValidateDatabaseSchemaVersion(db, backend, 16); err != nil {
|
||||
return err
|
||||
}
|
||||
if db == nil || !db.Migrator().HasTable(&wafIPGroupV17{}) {
|
||||
return fmt.Errorf("table waf_ip_groups is missing")
|
||||
}
|
||||
if !db.Migrator().HasColumn(&wafRuleGroupV17{}, "ip_whitelist_groups") {
|
||||
return fmt.Errorf("column waf_rule_groups.ip_whitelist_groups is missing")
|
||||
}
|
||||
if !db.Migrator().HasColumn(&wafRuleGroupV17{}, "ip_blacklist_groups") {
|
||||
return fmt.Errorf("column waf_rule_groups.ip_blacklist_groups is missing")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -78,6 +78,8 @@ func (databaseSchemaMigrationContext) ValidateDatabaseSchemaVersion(db *gorm.DB,
|
||||
return validateDatabaseSchemaV15(db, backend)
|
||||
case 16:
|
||||
return validateDatabaseSchemaV16(db, backend)
|
||||
case 17:
|
||||
return validateDatabaseSchemaV17(db, backend)
|
||||
default:
|
||||
return fmt.Errorf("database schema validation for v%d is not defined", version)
|
||||
}
|
||||
@@ -1097,6 +1099,8 @@ func ensureDefaultWAFRuleGroup(db *gorm.DB) error {
|
||||
BlockStatusCode: 418,
|
||||
IPWhitelist: "[]",
|
||||
IPBlacklist: "[]",
|
||||
IPWhitelistGroups: "[]",
|
||||
IPBlacklistGroups: "[]",
|
||||
CountryWhitelist: "[]",
|
||||
CountryBlacklist: "[]",
|
||||
RegionWhitelist: "[]",
|
||||
@@ -1170,6 +1174,22 @@ func validateDatabaseSchemaV16(db *gorm.DB, backend string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDatabaseSchemaV17(db *gorm.DB, backend string) error {
|
||||
if err := validateDatabaseSchemaV16(db, backend); err != nil {
|
||||
return err
|
||||
}
|
||||
if !db.Migrator().HasTable(&WAFIPGroup{}) {
|
||||
return fmt.Errorf("table waf_ip_groups is missing")
|
||||
}
|
||||
if !db.Migrator().HasColumn(&WAFRuleGroup{}, "ip_whitelist_groups") {
|
||||
return fmt.Errorf("column waf_rule_groups.ip_whitelist_groups is missing")
|
||||
}
|
||||
if !db.Migrator().HasColumn(&WAFRuleGroup{}, "ip_blacklist_groups") {
|
||||
return fmt.Errorf("column waf_rule_groups.ip_blacklist_groups is missing")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func databaseSchemaMigrations() []databaseSchemaMigration {
|
||||
ctx := databaseSchemaMigrationContext{}
|
||||
migrations := []databaseSchemaMigration{}
|
||||
|
||||
@@ -11,6 +11,8 @@ type WAFRuleGroup struct {
|
||||
BlockResponseBody string `json:"block_response_body" gorm:"type:text;not null;default:''"`
|
||||
IPWhitelist string `json:"ip_whitelist" gorm:"type:text;not null;default:'[]'"`
|
||||
IPBlacklist string `json:"ip_blacklist" gorm:"type:text;not null;default:'[]'"`
|
||||
IPWhitelistGroups string `json:"ip_whitelist_group_ids" gorm:"type:text;not null;default:'[]'"`
|
||||
IPBlacklistGroups string `json:"ip_blacklist_group_ids" gorm:"type:text;not null;default:'[]'"`
|
||||
CountryWhitelist string `json:"country_whitelist" gorm:"type:text;not null;default:'[]'"`
|
||||
CountryBlacklist string `json:"country_blacklist" gorm:"type:text;not null;default:'[]'"`
|
||||
RegionWhitelist string `json:"region_whitelist" gorm:"type:text;not null;default:'[]'"`
|
||||
@@ -22,6 +24,26 @@ type WAFRuleGroup struct {
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type WAFIPGroup struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
Name string `json:"name" gorm:"size:255;not null"`
|
||||
Type string `json:"type" gorm:"size:32;not null;index"`
|
||||
Enabled bool `json:"enabled" gorm:"not null;default:true"`
|
||||
IPList string `json:"ip_list" gorm:"type:text;not null;default:'[]'"`
|
||||
AutoConfig string `json:"auto_config" gorm:"type:text;not null;default:'{}'"`
|
||||
SubscriptionURL string `json:"subscription_url" gorm:"size:2048;not null;default:''"`
|
||||
SubscriptionFormat string `json:"subscription_format" gorm:"size:32;not null;default:'text'"`
|
||||
SubscriptionMappingRule string `json:"subscription_mapping_rule" gorm:"size:255;not null;default:''"`
|
||||
SyncIntervalMinutes int `json:"sync_interval_minutes" gorm:"not null;default:1440"`
|
||||
LastSyncedAt *time.Time `json:"last_synced_at"`
|
||||
NextSyncAt *time.Time `json:"next_sync_at" gorm:"index"`
|
||||
LastSyncStatus string `json:"last_sync_status" gorm:"size:32;not null;default:''"`
|
||||
LastSyncMessage string `json:"last_sync_message" gorm:"type:text;not null;default:''"`
|
||||
Remark string `json:"remark" gorm:"size:255"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type WAFRuleGroupBinding struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
RuleGroupID uint `json:"rule_group_id" gorm:"not null;uniqueIndex:idx_waf_group_route"`
|
||||
@@ -60,6 +82,8 @@ func (group *WAFRuleGroup) Update() error {
|
||||
"block_response_body": group.BlockResponseBody,
|
||||
"ip_whitelist": group.IPWhitelist,
|
||||
"ip_blacklist": group.IPBlacklist,
|
||||
"ip_whitelist_groups": group.IPWhitelistGroups,
|
||||
"ip_blacklist_groups": group.IPBlacklistGroups,
|
||||
"country_whitelist": group.CountryWhitelist,
|
||||
"country_blacklist": group.CountryBlacklist,
|
||||
"region_whitelist": group.RegionWhitelist,
|
||||
@@ -73,3 +97,69 @@ func (group *WAFRuleGroup) Update() error {
|
||||
func (group *WAFRuleGroup) Delete() error {
|
||||
return DB.Delete(group).Error
|
||||
}
|
||||
|
||||
func ListWAFIPGroups() ([]*WAFIPGroup, error) {
|
||||
var groups []*WAFIPGroup
|
||||
err := DB.Order("type asc").Order("id asc").Find(&groups).Error
|
||||
return groups, err
|
||||
}
|
||||
|
||||
func GetWAFIPGroupByID(id uint) (*WAFIPGroup, error) {
|
||||
group := &WAFIPGroup{}
|
||||
err := DB.First(group, id).Error
|
||||
return group, err
|
||||
}
|
||||
|
||||
func ListWAFIPGroupsByIDs(ids []uint) ([]*WAFIPGroup, error) {
|
||||
if len(ids) == 0 {
|
||||
return []*WAFIPGroup{}, nil
|
||||
}
|
||||
var groups []*WAFIPGroup
|
||||
err := DB.Where("id IN ?", ids).Order("id asc").Find(&groups).Error
|
||||
return groups, err
|
||||
}
|
||||
|
||||
func ListDueSubscriptionWAFIPGroups(now time.Time) ([]*WAFIPGroup, error) {
|
||||
var groups []*WAFIPGroup
|
||||
err := DB.Where("type = ? AND enabled = ? AND subscription_url <> '' AND (next_sync_at IS NULL OR next_sync_at <= ?)", "subscription", true, now).
|
||||
Order("id asc").
|
||||
Find(&groups).Error
|
||||
return groups, err
|
||||
}
|
||||
|
||||
func (group *WAFIPGroup) Insert() error {
|
||||
return DB.Create(group).Error
|
||||
}
|
||||
|
||||
func (group *WAFIPGroup) Update() error {
|
||||
return DB.Model(&WAFIPGroup{}).Where("id = ?", group.ID).Updates(map[string]any{
|
||||
"name": group.Name,
|
||||
"type": group.Type,
|
||||
"enabled": group.Enabled,
|
||||
"ip_list": group.IPList,
|
||||
"auto_config": group.AutoConfig,
|
||||
"subscription_url": group.SubscriptionURL,
|
||||
"subscription_format": group.SubscriptionFormat,
|
||||
"subscription_mapping_rule": group.SubscriptionMappingRule,
|
||||
"sync_interval_minutes": group.SyncIntervalMinutes,
|
||||
"next_sync_at": group.NextSyncAt,
|
||||
"last_sync_status": group.LastSyncStatus,
|
||||
"last_sync_message": group.LastSyncMessage,
|
||||
"remark": group.Remark,
|
||||
}).Error
|
||||
}
|
||||
|
||||
func (group *WAFIPGroup) UpdateSyncResult() error {
|
||||
return DB.Model(&WAFIPGroup{}).Where("id = ?", group.ID).Updates(map[string]any{
|
||||
"ip_list": group.IPList,
|
||||
"last_synced_at": group.LastSyncedAt,
|
||||
"next_sync_at": group.NextSyncAt,
|
||||
"last_sync_status": group.LastSyncStatus,
|
||||
"last_sync_message": group.LastSyncMessage,
|
||||
"subscription_format": group.SubscriptionFormat,
|
||||
}).Error
|
||||
}
|
||||
|
||||
func (group *WAFIPGroup) Delete() error {
|
||||
return DB.Delete(group).Error
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user