mirror of
https://github.com/napnap75/multiarch-docker-images.git
synced 2025-12-15 03:04:19 +01:00
Added the geolocation filter and improved the config file format
This commit is contained in:
@@ -1,27 +1,24 @@
|
||||
{
|
||||
"log-fetchers": [
|
||||
{
|
||||
"name": "loki-fileserver",
|
||||
"log-fetchers": {
|
||||
"loki-fileserver": {
|
||||
"type": "loki",
|
||||
"config": {
|
||||
"url": "http://fileserver.home:3100"
|
||||
"url-from-env": "{LOKI_URL}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"alert-managers":[
|
||||
{
|
||||
"name": "gotify-paris",
|
||||
},
|
||||
"alert-managers": {
|
||||
"gotify-paris": {
|
||||
"type": "gotify",
|
||||
"config": {
|
||||
"url": "https://paris.nappez.com/gotify/message",
|
||||
"url-from-env": "{GOTIFY_URL}",
|
||||
"token-from-env": "{GOTIFY_TOKEN}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"log-alerts": [
|
||||
{
|
||||
"name": "SSH outside connection",
|
||||
"check-interval": 60,
|
||||
},
|
||||
"alerting-rules": {
|
||||
"ssh-outside": {
|
||||
"check-interval": 30,
|
||||
"log-fetcher": {
|
||||
"name": "loki-fileserver",
|
||||
"filters": {
|
||||
@@ -37,16 +34,21 @@
|
||||
"config": {
|
||||
"match": "Accepted (?P<method>\\w+) for (?P<username>\\w+) from (?P<ip>[^\\s]+)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "geolocation",
|
||||
"config": {
|
||||
"source-field": "ip"
|
||||
}
|
||||
}
|
||||
],
|
||||
"alert-manager": {
|
||||
"name": "gotify-paris",
|
||||
"title": "Outside SSH login",
|
||||
"message": "New SSH login for {username} on {instance} from ip {ip} (method: {method})"
|
||||
"message": "New SSH login for {username} on {instance} from ip {ip} (country: {country}, provider: {isp}, method: {method})"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "SSH local connection",
|
||||
"ssh-local": {
|
||||
"check-interval": 30,
|
||||
"log-fetcher": {
|
||||
"name": "loki-fileserver",
|
||||
@@ -71,5 +73,5 @@
|
||||
"message": "New SSH login for {username} on {instance} from ip {ip} (method: {method})"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -2,114 +2,66 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Log Alert configuration schema",
|
||||
"type": "object",
|
||||
"required": ["log-fetchers", "alert-managers", "log-alerts"],
|
||||
"required": ["log-fetchers", "alert-managers", "alerting-rules"],
|
||||
"properties": {
|
||||
"log-fetchers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name", "type", "config"],
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": ["type", "config"],
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"type": { "type": "string", "enum": ["loki"] },
|
||||
"config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": { "type": "string", "format": "uri" }
|
||||
},
|
||||
"required": ["url"],
|
||||
"additionalProperties": true
|
||||
"config": { "type": "object" }
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"alert-managers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name", "type", "config"],
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": ["type", "config"],
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"type": { "type": "string", "enum": ["gotify"] },
|
||||
"config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": { "type": "string", "format": "uri" },
|
||||
"token": { "type": "string" },
|
||||
"token-from-env": { "type": "string" }
|
||||
},
|
||||
"required": ["url"],
|
||||
"anyOf": [
|
||||
{ "required": ["token"] },
|
||||
{ "required": ["token-from-env"] }
|
||||
],
|
||||
"additionalProperties": true
|
||||
"config": { "type": "object" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"log-alerts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"alerting-rules": {
|
||||
"type": "object",
|
||||
"required": ["name", "log-fetcher", "filters", "alert-manager"],
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": ["check-interval", "log-fetcher", "filters", "alert-manager"],
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"check-interval": { "type": "number", "minimum": 0 },
|
||||
"log-fetcher": {
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"filters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": "string" }
|
||||
},
|
||||
"text": { "type": "string" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
"name": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["type", "config"],
|
||||
"properties": {
|
||||
"type": { "type": "string", "enum": ["regexp"] },
|
||||
"type": { "type": "string", "enum": ["regexp", "geolocation"] },
|
||||
"config": {
|
||||
"type": "object",
|
||||
"required": ["match"],
|
||||
"properties": {
|
||||
"match": { "type": "string" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"alert-manager": {
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"title": { "type": "string" },
|
||||
"message": { "type": "string" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
"name": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -103,6 +103,31 @@ class RegexpFilter(Filter):
|
||||
logger.debug(f"Regex did not match for pattern '{self.match}' in log: {log.get('log')}")
|
||||
return None
|
||||
|
||||
# Geolocation Filter
|
||||
class GeolocationFilter(Filter):
|
||||
"""Concrete implementation for Geolocation filter."""
|
||||
|
||||
def __init__(self, config: Dict[str, Any]):
|
||||
self.source_field = config["source-field"]
|
||||
|
||||
def filter(self, log: Dict[str, Any]) -> Dict[str, Any]:
|
||||
ip_address = log.get("labels", {}).get(self.source_field)
|
||||
if not ip_address:
|
||||
logger.warning("No IP address found in log labels for geolocation")
|
||||
else:
|
||||
try:
|
||||
response = requests.get(f"http://ip-api.com/json/{ip_address}").json()
|
||||
if response["status"] == "success":
|
||||
logger.debug(f"Found info {response} for IP {ip_address}")
|
||||
del response["status"]
|
||||
del response["query"]
|
||||
log.setdefault("labels", {}).update(response)
|
||||
else:
|
||||
logger.warning("No info found for IP {ip_address}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Error fetching geolocation for IP {ip_address}: {e}")
|
||||
return log
|
||||
|
||||
# Gotify Alert Manager
|
||||
class GotifyAlertManager(AlertManager):
|
||||
"""Concrete implementation for Gotify alert manager."""
|
||||
@@ -129,7 +154,6 @@ class AlertRule:
|
||||
"""Represents an alert rule with filters and alert template."""
|
||||
|
||||
def __init__(self, log_fetchers: LogFetcher, alert_managers: AlertManager, config: Dict[str, Any]):
|
||||
self.name = config["name"]
|
||||
self.log_fetcher = log_fetchers[config["log-fetcher"]["name"]]
|
||||
self.fetcher_filters = config["log-fetcher"].get("filters", {})
|
||||
self.check_interval = config.get("check-interval", 60)
|
||||
@@ -137,6 +161,8 @@ class AlertRule:
|
||||
for filter in config.get("filters", []):
|
||||
if filter["type"] == "regexp":
|
||||
self.filters.append(RegexpFilter(filter["config"]))
|
||||
elif filter["type"] == "geolocation":
|
||||
self.filters.append(GeolocationFilter(filter["config"]))
|
||||
else:
|
||||
raise ValueError(f"Unsupported filter type: {filter['type']}")
|
||||
self.alert_manager = alert_managers[config["alert-manager"]["name"]]
|
||||
@@ -146,7 +172,6 @@ class AlertRule:
|
||||
self.next_run = time.time()
|
||||
|
||||
def run(self) -> None:
|
||||
logger.debug(f"Processing rule: {self.name}")
|
||||
logs = self.log_fetcher.fetch_logs(self.fetcher_filters, self.last_run, self.next_run)
|
||||
for log_entry in logs:
|
||||
logger.debug(f"Checking log: {log_entry['log']}")
|
||||
@@ -170,12 +195,14 @@ class LogAlertApp:
|
||||
self.config = self._load_config(config_path)
|
||||
logger.debug(f"Configuration loaded: {self.config}")
|
||||
self.log_fetchers = {}
|
||||
for fetcher in self.config["log-fetchers"]:
|
||||
self.log_fetchers[fetcher["name"]] = self._init_log_fetcher(fetcher)
|
||||
for key, fetcher in self.config["log-fetchers"].items():
|
||||
self.log_fetchers[key] = self._init_log_fetcher(fetcher)
|
||||
self.alert_managers = {}
|
||||
for manager in self.config["alert-managers"]:
|
||||
self.alert_managers[manager["name"]] = self._init_alert_manager(manager)
|
||||
self.alert_rules = [AlertRule(self.log_fetchers, self.alert_managers, rule) for rule in self.config["log-alerts"]]
|
||||
for key, manager in self.config["alert-managers"].items():
|
||||
self.alert_managers[key] = self._init_alert_manager(manager)
|
||||
self.alert_rules = {}
|
||||
for key, rule in self.config["alerting-rules"].items():
|
||||
self.alert_rules[key] = AlertRule(self.log_fetchers, self.alert_managers, rule)
|
||||
|
||||
def _load_config(self, config_path: str) -> Dict[str, Any]:
|
||||
"""Load the configuration from a JSON file and validate it with JSON Schema."""
|
||||
@@ -243,8 +270,9 @@ class LogAlertApp:
|
||||
def run(self) -> None:
|
||||
"""Fetch logs, check for matches, and send alerts."""
|
||||
while True:
|
||||
for rule in self.alert_rules:
|
||||
for name, rule in self.alert_rules.items():
|
||||
if time.time() >= rule.next_run:
|
||||
logger.debug(f"Processing rule: {name}")
|
||||
rule.run()
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user