Migrate from file-based configs to database with per-IP site configuration
Major architectural changes: - Replace YAML config files with database-stored ScanConfig model - Remove CIDR block support in favor of individual IP addresses per site - Each IP now has its own expected_ping, expected_tcp_ports, expected_udp_ports - AlertRule now uses config_id FK instead of config_file string API changes: - POST /api/scans now requires config_id instead of config_file - Alert rules API uses config_id with validation - All config dropdowns fetch from /api/configs dynamically Template updates: - scans.html, dashboard.html, alert_rules.html load configs via API - Display format: Config Title (X sites) in dropdowns - Removed Jinja2 config_files loops Migrations: - 008: Expand CIDRs to individual IPs with per-IP port configs - 009: Remove CIDR-related columns - 010: Add config_id to alert_rules, remove config_file
This commit is contained in:
@@ -261,14 +261,14 @@ class SneakyScanner:
|
||||
"""
|
||||
Load a site definition from the database.
|
||||
|
||||
IPs are pre-expanded in the database, so we just load them directly.
|
||||
|
||||
Args:
|
||||
site_name: Name of the site to load
|
||||
|
||||
Returns:
|
||||
Site definition dict with expanded IPs, or None if not found
|
||||
Site definition dict with IPs, or None if not found
|
||||
"""
|
||||
import ipaddress
|
||||
|
||||
try:
|
||||
# Import database modules
|
||||
import os
|
||||
@@ -281,7 +281,7 @@ class SneakyScanner:
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker, joinedload
|
||||
from web.models import Site, SiteCIDR
|
||||
from web.models import Site
|
||||
|
||||
# Get database URL from environment
|
||||
database_url = os.environ.get('DATABASE_URL', 'sqlite:///./sneakyscanner.db')
|
||||
@@ -291,12 +291,10 @@ class SneakyScanner:
|
||||
Session = sessionmaker(bind=engine)
|
||||
session = Session()
|
||||
|
||||
# Query site with CIDRs and IP overrides
|
||||
# Query site with all IPs (CIDRs are already expanded)
|
||||
site = (
|
||||
session.query(Site)
|
||||
.options(
|
||||
joinedload(Site.cidrs).joinedload(SiteCIDR.ips)
|
||||
)
|
||||
.options(joinedload(Site.ips))
|
||||
.filter(Site.name == site_name)
|
||||
.first()
|
||||
)
|
||||
@@ -305,60 +303,25 @@ class SneakyScanner:
|
||||
session.close()
|
||||
return None
|
||||
|
||||
# Expand CIDRs to IP list
|
||||
# Load all IPs directly from database (already expanded)
|
||||
expanded_ips = []
|
||||
|
||||
for cidr_obj in site.cidrs:
|
||||
cidr = cidr_obj.cidr
|
||||
expected_ping = cidr_obj.expected_ping
|
||||
expected_tcp_ports = json.loads(cidr_obj.expected_tcp_ports) if cidr_obj.expected_tcp_ports else []
|
||||
expected_udp_ports = json.loads(cidr_obj.expected_udp_ports) if cidr_obj.expected_udp_ports else []
|
||||
for ip_obj in site.ips:
|
||||
# Get settings from IP (no need to merge with CIDR defaults)
|
||||
expected_ping = ip_obj.expected_ping if ip_obj.expected_ping is not None else False
|
||||
expected_tcp_ports = json.loads(ip_obj.expected_tcp_ports) if ip_obj.expected_tcp_ports else []
|
||||
expected_udp_ports = json.loads(ip_obj.expected_udp_ports) if ip_obj.expected_udp_ports else []
|
||||
|
||||
# Build IP override map
|
||||
override_map = {}
|
||||
for ip_override in cidr_obj.ips:
|
||||
override_map[ip_override.ip_address] = {
|
||||
'expected_ping': ip_override.expected_ping if ip_override.expected_ping is not None else expected_ping,
|
||||
'expected_tcp_ports': json.loads(ip_override.expected_tcp_ports) if ip_override.expected_tcp_ports else expected_tcp_ports,
|
||||
'expected_udp_ports': json.loads(ip_override.expected_udp_ports) if ip_override.expected_udp_ports else expected_udp_ports
|
||||
ip_config = {
|
||||
'address': ip_obj.ip_address,
|
||||
'expected': {
|
||||
'ping': expected_ping,
|
||||
'tcp_ports': expected_tcp_ports,
|
||||
'udp_ports': expected_udp_ports
|
||||
}
|
||||
}
|
||||
|
||||
# Expand CIDR to IP list
|
||||
try:
|
||||
network = ipaddress.ip_network(cidr, strict=False)
|
||||
ip_list = [str(ip) for ip in network.hosts()]
|
||||
|
||||
if not ip_list:
|
||||
ip_list = [str(network.network_address)]
|
||||
|
||||
for ip_address in ip_list:
|
||||
# Check if this IP has an override
|
||||
if ip_address in override_map:
|
||||
override = override_map[ip_address]
|
||||
ip_config = {
|
||||
'address': ip_address,
|
||||
'expected': {
|
||||
'ping': override['expected_ping'],
|
||||
'tcp_ports': override['expected_tcp_ports'],
|
||||
'udp_ports': override['expected_udp_ports']
|
||||
}
|
||||
}
|
||||
else:
|
||||
# Use CIDR-level defaults
|
||||
ip_config = {
|
||||
'address': ip_address,
|
||||
'expected': {
|
||||
'ping': expected_ping if expected_ping is not None else False,
|
||||
'tcp_ports': expected_tcp_ports,
|
||||
'udp_ports': expected_udp_ports
|
||||
}
|
||||
}
|
||||
|
||||
expanded_ips.append(ip_config)
|
||||
|
||||
except ValueError as e:
|
||||
print(f"WARNING: Invalid CIDR '{cidr}' in site '{site_name}': {e}", file=sys.stderr)
|
||||
continue
|
||||
expanded_ips.append(ip_config)
|
||||
|
||||
session.close()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user