初始提交:恶意 IP 特征+行为融合检测系统

- 特征检测(规则/签名)与行为检测(24h 统计)单进程运行
- 融合名单按首次发现时间倒序,展示位置/归属(GeoIP + ASN)
- 钉钉融合推送(加签、24h 节流、去重)
- 页面状态灯、CSV 导出、Docker 镜像构建文件
This commit is contained in:
DeepFal committed 2026-10-18 05:03:05 +08:00
commit 2d76ed6672
42 files changed
+10645

No files matched your search

+161
View File
@@ -0,0 +1,161 @@
"""Configuration resolution.
Precedence (highest wins):
1. command line flags
2. environment variables (GRAYLOG_URL, GRAYLOG_TOKEN, ...)
3. JSON config file (--config, ./graylog-ops.json,
~/.config/graylog-ops/config.json)
File format::
{
"url": "http://edu-master:9000",
"token": "xxxxxxxx",
"insecure": false,
"timeout": 30
}
"""
import json
import os
ENV_KEYS = {
"url": "GRAYLOG_URL",
"token": "GRAYLOG_TOKEN",
"user": "GRAYLOG_USER",
"password": "GRAYLOG_PASSWORD",
"insecure": "GRAYLOG_INSECURE",
"timeout": "GRAYLOG_TIMEOUT",
"retries": "GRAYLOG_RETRIES",
}
DEFAULT_CONFIG_PATHS = (
os.path.expanduser("~/.config/graylog-ops/config.json"),
"graylog-ops.json",
)
class ConfigError(Exception):
"""User facing configuration problem."""
def _truthy(value):
return str(value).strip().lower() in ("1", "true", "yes", "on", "y")
def _read_file(path):
try:
with open(path, "r", encoding="utf-8") as handle:
data = json.load(handle)
except OSError as exc:
raise ConfigError("cannot read config %s: %s" % (path, exc))
except ValueError as exc:
raise ConfigError("config %s is not valid JSON: %s" % (path, exc))
if not isinstance(data, dict):
raise ConfigError("config %s must contain a JSON object" % path)
return data
class Config:
def __init__(self, url, token=None, user=None, password=None,
insecure=False, timeout=30.0, retries=2, source=None):
self.url = url
self.token = token
self.user = user
self.password = password
self.insecure = bool(insecure)
self.timeout = float(timeout)
self.retries = int(retries)
self.source = source
@property
def credentials(self):
"""(username, password) pair for HTTP Basic auth."""
if self.token:
# Graylog token auth: username is the token, password is "token".
return self.token, "token"
return self.user, self.password
@property
def auth_kind(self):
return "token" if self.token else "user/password"
def _pick(values, key, default=None):
value = values.get(key, default)
return value
def load_config(args):
"""Build a Config from CLI args, env vars and the config file."""
values = {}
chosen = None
explicit = getattr(args, "config", None)
if explicit:
if not os.path.exists(explicit):
raise ConfigError("config file not found: %s" % explicit)
values.update(_read_file(explicit))
chosen = explicit
else:
for candidate in DEFAULT_CONFIG_PATHS:
if os.path.exists(candidate):
values.update(_read_file(candidate))
chosen = candidate
break
for key, env_name in ENV_KEYS.items():
env_value = os.environ.get(env_name)
if env_value is not None and env_value.strip() != "":
values[key] = env_value
for key in ("url", "token", "user", "password", "timeout", "retries"):
flag_value = getattr(args, key, None)
if flag_value is not None and flag_value != "":
values[key] = flag_value
if getattr(args, "insecure", False):
values["insecure"] = True
url = values.get("url")
if not url:
raise ConfigError(
"no Graylog URL: pass --url, set GRAYLOG_URL, or add \"url\" to the config file"
)
url = str(url).strip()
if "://" not in url:
url = "http://" + url
token = (values.get("token") or "").strip() or None
user = (values.get("user") or "").strip() or None
password = values.get("password")
if not token and not (user and password):
raise ConfigError(
"no credentials: pass --token or --user/--password "
"(env GRAYLOG_TOKEN / GRAYLOG_USER + GRAYLOG_PASSWORD)"
)
timeout = values.get("timeout", 30)
retries = values.get("retries", 2)
try:
timeout = float(timeout)
except (TypeError, ValueError):
raise ConfigError("timeout must be a number, got %r" % (timeout,))
try:
retries = int(retries)
except (TypeError, ValueError):
raise ConfigError("retries must be an integer, got %r" % (retries,))
insecure = values.get("insecure", False)
if isinstance(insecure, str):
insecure = _truthy(insecure)
return Config(
url=url,
token=token,
user=user,
password=password,
insecure=bool(insecure),
timeout=timeout,
retries=retries,
source=chosen,
)