diff --git a/ns8_backup_monitor/smtp_config.py b/ns8_backup_monitor/smtp_config.py deleted file mode 100644 index 45a1128..0000000 --- a/ns8_backup_monitor/smtp_config.py +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env python3 -""" -smtp_config.py - Reads the SMTP relay configuration from NS8 cluster Redis. - -NS8 stores the mail relay settings (configured via the 'Mail relay' panel or -the `mail` module) in Redis under: - - cluster/mail_settings (hash) - fields: relay_host, relay_port, relay_tls, - relay_username, relay_password, - mail_domain, mail_from - -This module reads those values and returns a dict compatible with the -`smtp` and `mail` sections expected by notifier.py, so that ns8-backup-monitor -always uses the same relay that NS8 itself uses for system notifications. - -Fallback chain: - 1. NS8 Redis SMTP config (cluster/mail_settings) - 2. config.yml [smtp] section - 3. localhost:25 unauthenticated -""" - -import logging -import subprocess -from typing import Optional - -log = logging.getLogger(__name__) - -# NS8 Redis key for global mail relay settings -NS8_MAIL_SETTINGS_KEY = "cluster/mail_settings" - -# Alternative key used by some NS8 versions / mail module -NS8_MAIL_SETTINGS_ALT = "module/mail1/settings" - - -def _redis_hgetall(socket: str, key: str) -> dict: - """Read a Redis hash as a dict via redis-cli.""" - cmd = ["redis-cli", "-s", socket, "HGETALL", key] - try: - result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) - lines = [l for l in result.stdout.strip().splitlines() if l] - return dict(zip(lines[::2], lines[1::2])) - except Exception as e: - log.warning(f"redis-cli HGETALL {key} failed: {e}") - return {} - - -def _try_keys(socket: str) -> dict: - """Try the known NS8 Redis keys for mail settings, return first non-empty.""" - for key in (NS8_MAIL_SETTINGS_KEY, NS8_MAIL_SETTINGS_ALT): - fields = _redis_hgetall(socket, key) - if fields: - log.info(f"Found NS8 SMTP config at Redis key: {key}") - return fields - return {} - - -def load_ns8_smtp(config: dict) -> Optional[dict]: - """ - Read NS8 SMTP relay config from Redis. - - Returns a dict with keys: - smtp.host, smtp.port, smtp.use_tls, smtp.use_starttls, - smtp.username, smtp.password, - mail.from (system sender address) - - Returns None if not found or Redis is unreachable. - """ - socket = config.get("redis", {}).get( - "socket", "/var/lib/nethserver/cluster/state/redis.sock" - ) - fields = _try_keys(socket) - - if not fields: - log.debug("No NS8 mail settings found in Redis") - return None - - # NS8 field names (may vary by version — we handle both snake_case and camelCase) - host = ( - fields.get("relay_host") - or fields.get("relayHost") - or fields.get("smarthost") - or "" - ).strip() - - if not host: - log.debug("NS8 mail settings found in Redis but relay_host is empty (direct delivery)") - return None - - try: - port = int( - fields.get("relay_port") - or fields.get("relayPort") - or fields.get("smarthost_port") - or 587 - ) - except (ValueError, TypeError): - port = 587 - - # TLS flags - NS8 stores as string 'true'/'false' or '1'/'0' - def _bool(val) -> bool: - return str(val).lower() in ("true", "1", "yes") - - use_tls = _bool( - fields.get("relay_tls") or fields.get("tls") or fields.get("relayTls") or False - ) - # STARTTLS is the default for port 587; detect from port if not explicit - use_starttls = _bool( - fields.get("relay_starttls") - or fields.get("starttls") - or fields.get("relayStarttls") - or (port == 587 and not use_tls) - ) - - username = ( - fields.get("relay_username") - or fields.get("relayUsername") - or fields.get("username") - or "" - ).strip() - password = ( - fields.get("relay_password") - or fields.get("relayPassword") - or fields.get("password") - or "" - ).strip() - - mail_from = ( - fields.get("mail_from") - or fields.get("mailFrom") - or fields.get("sender") - or f"ns8-backup-monitor@{fields.get('mail_domain', 'localhost')}" - ).strip() - - result = { - "smtp": { - "host": host, - "port": port, - "use_tls": use_tls, - "use_starttls": use_starttls, - "username": username, - "password": password, - }, - "mail_from": mail_from, - } - - log.info( - f"NS8 SMTP relay: {host}:{port} " - f"tls={use_tls} starttls={use_starttls} " - f"auth={'yes' if username else 'no'} " - f"from={mail_from}" - ) - return result - - -def resolve_smtp_config(config: dict) -> tuple: - """ - Resolve the effective SMTP configuration using the fallback chain: - 1. NS8 Redis cluster/mail_settings - 2. config.yml [smtp] + [mail] sections - 3. localhost:25 unauthenticated (last resort) - - Returns (smtp_cfg: dict, mail_from: str). - smtp_cfg keys: host, port, use_tls, use_starttls, username, password - """ - # Try NS8 Redis first, unless explicitly disabled in config - use_ns8_smtp = config.get("smtp", {}).get("use_ns8_relay", True) - - if use_ns8_smtp: - ns8 = load_ns8_smtp(config) - if ns8: - # Merge: NS8 provides smtp + mail_from, but mail.to still comes from config.yml - smtp_cfg = ns8["smtp"] - mail_from = config.get("mail", {}).get("from") or ns8["mail_from"] - return smtp_cfg, mail_from - else: - log.info("NS8 SMTP config not available, falling back to config.yml smtp section") - else: - log.info("NS8 relay lookup disabled (smtp.use_ns8_relay: false), using config.yml") - - # Fallback: config.yml - smtp_cfg = config.get("smtp", {}) - mail_from = config.get("mail", {}).get("from", "ns8-backup-monitor@localhost") - - if not smtp_cfg.get("host"): - log.warning("No SMTP host configured in config.yml, falling back to localhost:25") - smtp_cfg = {"host": "localhost", "port": 25, "use_tls": False, - "use_starttls": False, "username": "", "password": ""} - - return smtp_cfg, mail_from