feat(firewall): persist firewall snapshots to SQLite (F001/D007)
save_state_report now also writes the address-list + firewall-rule snapshot into the app.db firewall_snapshots tables, making the audit trail independent of git. Removes the 'git-visible data/mikrotik' framing.
This commit is contained in:
1 parent
aef132fe94
commit
4960eef312
1 file changed
+31
-7
+31
-7
@@ -143,9 +143,11 @@ class MikrotikFirewall:
|
|||||||
"""Save current address lists and firewall rules for auditing.
|
"""Save current address lists and firewall rules for auditing.
|
||||||
|
|
||||||
Writes structured snapshots to ``<report_dir>/mikrotik/data/`` and to
|
Writes structured snapshots to ``<report_dir>/mikrotik/data/`` and to
|
||||||
the common ``data/mikrotik/`` directory (overwritten every run so git
|
the common ``data/mikrotik/`` directory (scratch mirror for debugging;
|
||||||
can diff changes to the firewall state over time), plus a timestamped
|
**not** a git audit trail), plus a timestamped JSON report under
|
||||||
JSON report under ``<report_dir>/mikrotik/``.
|
``<report_dir>/mikrotik/``. The authoritative long-term audit trail is
|
||||||
|
the SQLite ``firewall_snapshots`` tables (see ``app.db``), which are
|
||||||
|
appended here regardless of the file writes.
|
||||||
"""
|
"""
|
||||||
state = {
|
state = {
|
||||||
"router": self.router_ip,
|
"router": self.router_ip,
|
||||||
@@ -174,11 +176,31 @@ class MikrotikFirewall:
|
|||||||
os.path.join(data_dir, f"firewall_{version}.json"),
|
os.path.join(data_dir, f"firewall_{version}.json"),
|
||||||
state["firewall_rules"][version])
|
state["firewall_rules"][version])
|
||||||
|
|
||||||
|
# Persist a SQLite snapshot (authoritative audit trail, independent of
|
||||||
|
# git). Best-effort: never breaks a router sync if the DB is down.
|
||||||
|
self._persist_snapshot_to_db(state)
|
||||||
|
|
||||||
timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
||||||
report_path = os.path.join(report_dir, "mikrotik", f"{timestamp}.json")
|
report_path = os.path.join(report_dir, "mikrotik", f"{timestamp}.json")
|
||||||
self._write_json(report_path, state)
|
self._write_json(report_path, state)
|
||||||
return report_path
|
return report_path
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _persist_snapshot_to_db(state: dict) -> None:
|
||||||
|
"""Write the firewall snapshot into the SQLite store (best-effort)."""
|
||||||
|
try:
|
||||||
|
from .db import get_connection
|
||||||
|
from .db import repositories as repo
|
||||||
|
repo.save_firewall_snapshot(
|
||||||
|
get_connection(),
|
||||||
|
address_lists_v4=state["address_lists"]["ipv4"],
|
||||||
|
address_lists_v6=state["address_lists"]["ipv6"],
|
||||||
|
rules_v4=state["firewall_rules"]["ipv4"],
|
||||||
|
rules_v6=state["firewall_rules"]["ipv6"],
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
print(f"WARN: could not persist firewall snapshot to db ({exc})")
|
||||||
|
|
||||||
def sync_blocklist(self, blocked_ipv4: Iterable[str],
|
def sync_blocklist(self, blocked_ipv4: Iterable[str],
|
||||||
blocked_ipv6: Iterable[str],
|
blocked_ipv6: Iterable[str],
|
||||||
dry_run: bool = False,
|
dry_run: bool = False,
|
||||||
@@ -186,10 +208,12 @@ class MikrotikFirewall:
|
|||||||
"""Synchronize the Blocked list cumulatively and audit the result.
|
"""Synchronize the Blocked list cumulatively and audit the result.
|
||||||
|
|
||||||
The blocklist is grow-only: targets are added when present in the
|
The blocklist is grow-only: targets are added when present in the
|
||||||
complete set (all git history of the blocklist files + the current
|
complete cumulative set (git history of the blocklist files + the
|
||||||
working files + this scan's targets) and never removed automatically.
|
current working files + this scan's targets) and never removed
|
||||||
Only router entries absent from the complete set — i.e. never blocked
|
automatically. Only router entries absent from the complete set — i.e.
|
||||||
by the system — are pruned. See ``app.blocklist_history``.
|
never blocked by the system — are pruned. The history now lives in the
|
||||||
|
SQLite blocklist ledger (``app.db`` ``blocklist_entries``); git history
|
||||||
|
remains a read-only fallback. See ``app.blocklist_history``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
desired_ipv4 = set(blocked_ipv4)
|
desired_ipv4 = set(blocked_ipv4)
|
||||||
|
|||||||
Reference in new issue
Block a user