Ajustes gerais
This commit is contained in:
153
agent/main.py
153
agent/main.py
@@ -3,17 +3,17 @@ import sys
|
||||
import time
|
||||
import json
|
||||
import uuid
|
||||
import socket
|
||||
import pwd
|
||||
import grp
|
||||
import requests
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
# Configuration
|
||||
CONFIG_FILE = '/opt/dnsblock/config.json'
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
# Configuration
|
||||
CONFIG_FILE = '/opt/dnsblock/config.json'
|
||||
STATE_FILE = '/opt/dnsblock/rpz_state.json'
|
||||
LOG_DIR = '/opt/dnsblock/logs'
|
||||
LOG_FILE = os.path.join(LOG_DIR, 'agent.log')
|
||||
RPZ_FILE_DEFAULT = '/opt/dnsblock/rpz.dnsblock.zone'
|
||||
@@ -66,32 +66,142 @@ def load_config():
|
||||
logging.error(f"Error loading config: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
def generate_rpz(domains, output_file):
|
||||
"""Generates the RPZ zone file."""
|
||||
def get_system_domain():
|
||||
"""Detects the system domain from FQDN or hostname."""
|
||||
try:
|
||||
fqdn = socket.getfqdn()
|
||||
if fqdn and '.' in fqdn:
|
||||
# Extract domain portion (e.g., server.gtecnet.com.br -> gtecnet.com.br)
|
||||
parts = fqdn.split('.')
|
||||
if len(parts) >= 2:
|
||||
domain = '.'.join(parts[1:])
|
||||
logging.info(f"Detected system domain: {domain}")
|
||||
return domain
|
||||
|
||||
# Try hostname as fallback
|
||||
hostname = socket.gethostname()
|
||||
if hostname and '.' in hostname:
|
||||
parts = hostname.split('.')
|
||||
if len(parts) >= 2:
|
||||
domain = '.'.join(parts[1:])
|
||||
logging.info(f"Detected system domain from hostname: {domain}")
|
||||
return domain
|
||||
|
||||
logging.info("Could not detect system domain, using localhost")
|
||||
return "localhost"
|
||||
except Exception as e:
|
||||
logging.warning(f"Error detecting system domain: {e}, using localhost")
|
||||
return "localhost"
|
||||
|
||||
def load_rpz_state():
|
||||
"""Loads RPZ state from JSON file."""
|
||||
if not os.path.exists(STATE_FILE):
|
||||
return {'last_serial': None, 'last_date': None}
|
||||
|
||||
try:
|
||||
with open(STATE_FILE, 'r') as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
logging.warning(f"Error loading RPZ state: {e}")
|
||||
return {'last_serial': None, 'last_date': None}
|
||||
|
||||
def save_rpz_state(state):
|
||||
"""Saves RPZ state to JSON file."""
|
||||
try:
|
||||
with open(STATE_FILE, 'w') as f:
|
||||
json.dump(state, f)
|
||||
except Exception as e:
|
||||
logging.error(f"Error saving RPZ state: {e}")
|
||||
|
||||
def generate_serial():
|
||||
"""Generates serial in YYYYMMDDNN format."""
|
||||
today = datetime.now().strftime('%Y%m%d')
|
||||
state = load_rpz_state()
|
||||
|
||||
last_serial = state.get('last_serial')
|
||||
last_date = state.get('last_date')
|
||||
|
||||
if last_date == today and last_serial:
|
||||
# Same day, increment sequence
|
||||
sequence = int(str(last_serial)[-2:]) + 1
|
||||
if sequence > 99:
|
||||
sequence = 99 # Cap at 99
|
||||
else:
|
||||
# New day, reset sequence
|
||||
sequence = 0
|
||||
|
||||
new_serial = int(f"{today}{sequence:02d}")
|
||||
|
||||
# Save state
|
||||
save_rpz_state({'last_serial': new_serial, 'last_date': today})
|
||||
|
||||
return new_serial
|
||||
|
||||
def apply_file_ownership(path, file_owner):
|
||||
"""Apply user:group ownership to a file or directory."""
|
||||
if not file_owner:
|
||||
return True
|
||||
|
||||
try:
|
||||
parts = file_owner.split(':')
|
||||
user = parts[0]
|
||||
group = parts[1] if len(parts) > 1 else user
|
||||
|
||||
uid = pwd.getpwnam(user).pw_uid
|
||||
gid = grp.getgrnam(group).gr_gid
|
||||
|
||||
os.chown(path, uid, gid)
|
||||
logging.debug(f"Applied ownership {file_owner} to {path}")
|
||||
return True
|
||||
except KeyError as e:
|
||||
logging.warning(f"User or group not found for file_owner '{file_owner}': {e}")
|
||||
return False
|
||||
except PermissionError as e:
|
||||
logging.warning(f"Permission denied setting ownership on {path}: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logging.error(f"Error applying ownership to {path}: {e}")
|
||||
return False
|
||||
|
||||
def generate_rpz(domains, output_file, file_owner=None):
|
||||
"""Generates the RPZ zone file with proper SOA header."""
|
||||
try:
|
||||
# Ensure directory exists
|
||||
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
||||
output_dir = os.path.dirname(output_file)
|
||||
if output_dir:
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
# Apply ownership to directory
|
||||
if file_owner:
|
||||
apply_file_ownership(output_dir, file_owner)
|
||||
|
||||
# Get system domain and generate serial
|
||||
system_domain = get_system_domain()
|
||||
serial = generate_serial()
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
f.write("$TTL 30\n")
|
||||
f.write("@ IN SOA rpz.dnsblock.zone. root.rpz.dnsblock.zone. (\n")
|
||||
f.write(f" {int(time.time())} ; Serial\n")
|
||||
f.write(" 30 ; Refresh\n")
|
||||
f.write(" 15 ; Retry\n")
|
||||
f.write(" 604800 ; Expire\n")
|
||||
f.write(" 30 ) ; Negative Cache TTL\n")
|
||||
f.write(";\n")
|
||||
f.write("@ IN NS localhost.\n")
|
||||
f.write("@ IN A 127.0.0.1\n")
|
||||
f.write(";\n")
|
||||
f.write("; Blocked Domains\n")
|
||||
f.write("$TTL 1H\n")
|
||||
f.write(f"@ IN SOA localhost. {system_domain}. (\n")
|
||||
f.write(f" {serial} ; Serial\n")
|
||||
f.write(" 1h ; Refresh\n")
|
||||
f.write(" 15m ; Retry\n")
|
||||
f.write(" 30d ; Expire\n")
|
||||
f.write(" 2h ; Negative Cache TTL\n")
|
||||
f.write(" )\n")
|
||||
f.write(f" NS {system_domain}.\n")
|
||||
f.write("\n")
|
||||
f.write("; RPZ block hosts\n")
|
||||
f.write("\n")
|
||||
|
||||
for domain in domains:
|
||||
# CNAME to . (NXDOMAIN) or specific sinkhole
|
||||
f.write(f"{domain} CNAME .\n")
|
||||
f.write(f"*.{domain} CNAME .\n")
|
||||
|
||||
logging.info(f"RPZ file generated at {output_file} with {len(domains)} domains.")
|
||||
# Apply ownership to file
|
||||
if file_owner:
|
||||
apply_file_ownership(output_file, file_owner)
|
||||
|
||||
logging.info(f"RPZ file generated at {output_file} with {len(domains)} domains (serial: {serial}).")
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error(f"Error generating RPZ: {e}")
|
||||
@@ -106,6 +216,7 @@ def main():
|
||||
api_url = config.get('api_url')
|
||||
rpz_file = config.get('rpz_file', RPZ_FILE_DEFAULT)
|
||||
reload_command = config.get('reload_command')
|
||||
file_owner = config.get('file_owner') # Ex: 'unbound:unbound'
|
||||
|
||||
if not serial_key or not api_url:
|
||||
logging.error("Missing serial_key or api_url in config.")
|
||||
@@ -140,7 +251,7 @@ def main():
|
||||
if current_checksum != last_checksum:
|
||||
logging.info("Change detected in domain list. Updating RPZ...")
|
||||
domains = data.get('domains', [])
|
||||
if generate_rpz(domains, rpz_file):
|
||||
if generate_rpz(domains, rpz_file, file_owner):
|
||||
last_checksum = current_checksum
|
||||
|
||||
if reload_command:
|
||||
|
||||
Reference in New Issue
Block a user