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.
|
||||
|
||||
Writes structured snapshots to ``<report_dir>/mikrotik/data/`` and to
|
||||
the common ``data/mikrotik/`` directory (overwritten every run so git
|
||||
can diff changes to the firewall state over time), plus a timestamped
|
||||
JSON report under ``<report_dir>/mikrotik/``.
|
||||
the common ``data/mikrotik/`` directory (scratch mirror for debugging;
|
||||
**not** a git audit trail), plus a timestamped JSON report under
|
||||
``<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 = {
|
||||
"router": self.router_ip,
|
||||
@@ -174,11 +176,31 @@ class MikrotikFirewall:
|
||||
os.path.join(data_dir, f"firewall_{version}.json"),
|
||||
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")
|
||||
report_path = os.path.join(report_dir, "mikrotik", f"{timestamp}.json")
|
||||
self._write_json(report_path, state)
|
||||
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],
|
||||
blocked_ipv6: Iterable[str],
|
||||
dry_run: bool = False,
|
||||
@@ -186,10 +208,12 @@ class MikrotikFirewall:
|
||||
"""Synchronize the Blocked list cumulatively and audit the result.
|
||||
|
||||
The blocklist is grow-only: targets are added when present in the
|
||||
complete set (all git history of the blocklist files + the current
|
||||
working files + this scan's targets) and never removed automatically.
|
||||
Only router entries absent from the complete set — i.e. never blocked
|
||||
by the system — are pruned. See ``app.blocklist_history``.
|
||||
complete cumulative set (git history of the blocklist files + the
|
||||
current working files + this scan's targets) and never removed
|
||||
automatically. Only router entries absent from the complete set — i.e.
|
||||
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)
|
||||
|
||||
Reference in new issue
Block a user