mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-05 23:26:38 +08:00
feat(waf): complete composable rule orchestration
Add the React Flow rule editor, ordered graph APIs and runtime DAG execution.\n\nPublish rules only on OpenResty reload and reconcile checksum-driven IP group snapshots in bounded shared memory.
This commit is contained in:
@@ -24,6 +24,7 @@ const (
|
||||
defaultRuntimeConfigDirRelativePath = "etc/openflare"
|
||||
defaultPagesDirRelativePath = "var/lib/openflare/pages"
|
||||
defaultMMDBRelativePath = "etc/openflare/GeoLite2-Country.mmdb"
|
||||
defaultCityMMDBRelativePath = "etc/openflare/GeoLite2-City.mmdb"
|
||||
defaultAccessLogRelativePath = "var/log/openflare/access.log"
|
||||
defaultStateRelativePath = "var/lib/openflare/agent-state.json"
|
||||
defaultObservabilityBufferRelativePath = "var/lib/openflare/observability-buffer.json"
|
||||
@@ -31,6 +32,7 @@ const (
|
||||
defaultObservabilityReplayMinutes = 15
|
||||
defaultMMDBUpdateInterval = 24 * time.Hour
|
||||
defaultMMDBDownloadURL = "https://raw.githubusercontent.com/Loyalsoldier/geoip/release/GeoLite2-Country.mmdb"
|
||||
defaultCityMMDBDownloadURL = "https://raw.githubusercontent.com/Loyalsoldier/geoip/release/GeoLite2-City.mmdb"
|
||||
defaultHeartbeatInterval = 10 * time.Second
|
||||
defaultRequestTimeout = 10 * time.Second
|
||||
configFilePerm = 0o600
|
||||
@@ -58,8 +60,10 @@ type Config struct {
|
||||
RuntimeConfigDir string `json:"runtime_config_dir"`
|
||||
PagesDir string `json:"pages_dir"`
|
||||
MMDBPath string `json:"mmdb_path"`
|
||||
CityMMDBPath string `json:"city_mmdb_path"`
|
||||
MMDBUpdateInterval MillisecondDuration `json:"mmdb_update_interval"`
|
||||
MMDBDownloadURL string `json:"mmdb_download_url"`
|
||||
CityMMDBDownloadURL string `json:"city_mmdb_download_url"`
|
||||
OpenrestyObservabilityPort int `json:"openresty_observability_port"`
|
||||
ObservabilityBufferPath string `json:"observability_buffer_path"`
|
||||
ObservabilityReplayMinutes int `json:"observability_replay_minutes"`
|
||||
@@ -89,8 +93,10 @@ type configFile struct {
|
||||
RuntimeConfigDir string `json:"runtime_config_dir"`
|
||||
PagesDir string `json:"pages_dir"`
|
||||
MMDBPath string `json:"mmdb_path"`
|
||||
CityMMDBPath string `json:"city_mmdb_path"`
|
||||
MMDBUpdateInterval MillisecondDuration `json:"mmdb_update_interval"`
|
||||
MMDBDownloadURL string `json:"mmdb_download_url"`
|
||||
CityMMDBDownloadURL string `json:"city_mmdb_download_url"`
|
||||
OpenrestyObservabilityPort int `json:"openresty_observability_port"`
|
||||
ObservabilityBufferPath string `json:"observability_buffer_path"`
|
||||
ObservabilityReplayMinutes int `json:"observability_replay_minutes"`
|
||||
@@ -178,6 +184,7 @@ func applyAgentPathDefaults(cfg *Config, baseDir string) {
|
||||
{&cfg.RuntimeConfigDir, defaultRuntimeConfigDirRelativePath},
|
||||
{&cfg.PagesDir, defaultPagesDirRelativePath},
|
||||
{&cfg.MMDBPath, defaultMMDBRelativePath},
|
||||
{&cfg.CityMMDBPath, defaultCityMMDBRelativePath},
|
||||
{&cfg.ObservabilityBufferPath, defaultObservabilityBufferRelativePath},
|
||||
}
|
||||
for _, item := range pathDefaults {
|
||||
@@ -200,6 +207,9 @@ func applyAgentTimingDefaults(cfg *Config) {
|
||||
if cfg.MMDBDownloadURL == "" {
|
||||
cfg.MMDBDownloadURL = defaultMMDBDownloadURL
|
||||
}
|
||||
if cfg.CityMMDBDownloadURL == "" {
|
||||
cfg.CityMMDBDownloadURL = defaultCityMMDBDownloadURL
|
||||
}
|
||||
if cfg.OpenrestyObservabilityPort <= 0 {
|
||||
cfg.OpenrestyObservabilityPort = defaultOpenRestyObservabilityPort
|
||||
}
|
||||
@@ -232,6 +242,7 @@ func normalizeManagedPaths(cfg *Config) {
|
||||
&cfg.StatePath,
|
||||
&cfg.ObservabilityBufferPath,
|
||||
&cfg.MMDBPath,
|
||||
&cfg.CityMMDBPath,
|
||||
}
|
||||
for _, p := range paths {
|
||||
if usesSlashPath(*p) {
|
||||
@@ -256,6 +267,8 @@ func hasEnvConfig() bool {
|
||||
"OPENFLARE_MMDB_PATH",
|
||||
"OPENFLARE_MMDB_UPDATE_INTERVAL",
|
||||
"OPENFLARE_MMDB_DOWNLOAD_URL",
|
||||
"OPENFLARE_CITY_MMDB_PATH",
|
||||
"OPENFLARE_CITY_MMDB_DOWNLOAD_URL",
|
||||
} {
|
||||
if strings.TrimSpace(os.Getenv(key)) != "" {
|
||||
return true
|
||||
@@ -283,6 +296,8 @@ func applyEnvOverrides(cfg *Config) {
|
||||
overrideString("OPENFLARE_PAGES_DIR", &cfg.PagesDir)
|
||||
overrideString("OPENFLARE_MMDB_PATH", &cfg.MMDBPath)
|
||||
overrideString("OPENFLARE_MMDB_DOWNLOAD_URL", &cfg.MMDBDownloadURL)
|
||||
overrideString("OPENFLARE_CITY_MMDB_PATH", &cfg.CityMMDBPath)
|
||||
overrideString("OPENFLARE_CITY_MMDB_DOWNLOAD_URL", &cfg.CityMMDBDownloadURL)
|
||||
if value := strings.TrimSpace(os.Getenv("OPENFLARE_HEARTBEAT_INTERVAL")); value != "" {
|
||||
if duration, err := parseDurationValue(value); err == nil {
|
||||
cfg.HeartbeatInterval = duration
|
||||
|
||||
@@ -61,6 +61,12 @@ func TestLoadDefaultsToManagedBinaryPaths(t *testing.T) {
|
||||
if cfg.RuntimeConfigDir != filepath.Join(dir, "data", defaultRuntimeConfigDirRelativePath) {
|
||||
t.Fatalf("unexpected runtime config dir: %s", cfg.RuntimeConfigDir)
|
||||
}
|
||||
if cfg.CityMMDBPath != filepath.Join(dir, "data", defaultCityMMDBRelativePath) {
|
||||
t.Fatalf("unexpected city mmdb path: %s", cfg.CityMMDBPath)
|
||||
}
|
||||
if cfg.CityMMDBDownloadURL != defaultCityMMDBDownloadURL {
|
||||
t.Fatalf("unexpected city mmdb download URL: %s", cfg.CityMMDBDownloadURL)
|
||||
}
|
||||
if cfg.OpenrestyCertDir != cfg.CertDir {
|
||||
t.Fatalf("unexpected openresty cert dir: %s", cfg.OpenrestyCertDir)
|
||||
}
|
||||
@@ -333,6 +339,8 @@ func TestLoadEnvOverridesConfigFile(t *testing.T) {
|
||||
t.Setenv("OPENFLARE_SERVER_URL", "http://new:3000")
|
||||
t.Setenv("OPENFLARE_AGENT_TOKEN", "new-token")
|
||||
t.Setenv("OPENFLARE_OPENRESTY_PATH", "/new/openresty")
|
||||
t.Setenv("OPENFLARE_CITY_MMDB_PATH", "/new/GeoLite2-City.mmdb")
|
||||
t.Setenv("OPENFLARE_CITY_MMDB_DOWNLOAD_URL", "https://geo.example/GeoLite2-City.mmdb")
|
||||
|
||||
cfg, err := Load(configPath)
|
||||
if err != nil {
|
||||
@@ -347,6 +355,25 @@ func TestLoadEnvOverridesConfigFile(t *testing.T) {
|
||||
if cfg.OpenrestyPath != "/new/openresty" {
|
||||
t.Fatalf("expected openresty path from env, got %s", cfg.OpenrestyPath)
|
||||
}
|
||||
if cfg.CityMMDBPath != "/new/GeoLite2-City.mmdb" || cfg.CityMMDBDownloadURL != "https://geo.example/GeoLite2-City.mmdb" {
|
||||
t.Fatalf("unexpected City MMDB env overrides: %s / %s", cfg.CityMMDBPath, cfg.CityMMDBDownloadURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadKeepsExplicitCityMMDBConfig(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
configPath := filepath.Join(dir, "agent.json")
|
||||
payload := `{"server_url":"http://127.0.0.1:3000","agent_token":"token","node_name":"edge-01","node_ip":"10.0.0.8","city_mmdb_path":"/custom/GeoLite2-City.mmdb","city_mmdb_download_url":"https://custom.example/GeoLite2-City.mmdb"}`
|
||||
if err := os.WriteFile(configPath, []byte(payload), 0o644); err != nil {
|
||||
t.Fatalf("failed to write config: %v", err)
|
||||
}
|
||||
cfg, err := Load(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Load failed: %v", err)
|
||||
}
|
||||
if cfg.CityMMDBPath != "/custom/GeoLite2-City.mmdb" || cfg.CityMMDBDownloadURL != "https://custom.example/GeoLite2-City.mmdb" {
|
||||
t.Fatalf("explicit City MMDB config changed: %s / %s", cfg.CityMMDBPath, cfg.CityMMDBDownloadURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadUsesMillisecondsForIntervals(t *testing.T) {
|
||||
@@ -431,6 +458,9 @@ func TestSavePersistsMillisecondsAndOmitsRuntimeVersions(t *testing.T) {
|
||||
if decoded["observability_replay_minutes"] != float64(defaultObservabilityReplayMinutes) {
|
||||
t.Fatalf("unexpected observability replay minutes: %#v", decoded["observability_replay_minutes"])
|
||||
}
|
||||
if decoded["city_mmdb_path"] != cfg.CityMMDBPath || decoded["city_mmdb_download_url"] != cfg.CityMMDBDownloadURL {
|
||||
t.Fatalf("City MMDB config was not persisted: %#v", decoded)
|
||||
}
|
||||
if _, ok := decoded["nginx_path"]; ok {
|
||||
t.Fatal("legacy nginx_path should not be persisted")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user