"""Erzeugt beim Start des Managers aus /sshpull/hosts.yml: * /var/ossec/etc/sshpull/ssh_config SSH-Client-Config (Alias = Hostname) * /var/ossec/etc/sshpull/keys/* Kopie der Schluessel (Owner wazuh) * /var/ossec/etc/sshpull/commands/*.sh Befehle fuer den Diff-Check pro Host * einen -Block in ossec.conf mit und Laeuft mit dem Wazuh-eigenen Python, bevor wazuh-control startet. """ import grp import os import pwd import shutil import sys from xml.sax.saxutils import escape sys.path.insert(0, "/opt/sshpull") import sshpull_hosts # noqa: E402 HOSTS_FILE = "/sshpull/hosts.yml" SSH_SRC = "/sshpull/ssh" LOG_DIR = "/remote-logs" BASE = "/var/ossec/etc/sshpull" OSSEC_CONF = "/var/ossec/etc/ossec.conf" BEGIN = "" END = "" UID = pwd.getpwnam("wazuh").pw_uid GID = grp.getgrnam("wazuh").gr_gid def write(path, content, mode): with open(path, "w", encoding="utf-8") as fh: fh.write(content) os.chown(path, UID, GID) os.chmod(path, mode) def mkdir(path, mode=0o750): os.makedirs(path, exist_ok=True) os.chown(path, UID, GID) os.chmod(path, mode) def agentless_block(h): host = f"{'use_sudo ' if h['sudo'] else ''}{h['user']}@{h['name']}" out = [] integ = h["integrity"] if integ.get("enabled") and integ.get("paths"): out.append(f""" sshpull_integrity_linux {int(integ['interval'])} {escape(host)} periodic {escape(' '.join(integ['paths']))} """) cmds = h["commands"] if cmds.get("enabled") and cmds.get("run"): out.append(f""" sshpull_generic_diff {int(cmds['interval'])} {escape(host)} periodic_diff {escape(h['name'])} """) return out def localfile_block(h): out = [] for src in h["logs"]: fmt = "syslog" if src["type"] == "journal" else src["format"] out.append(f""" {fmt} {LOG_DIR}/{h['name']}/{src['name']}.log """) return out def main(): if not os.path.exists(HOSTS_FILE): print("sshpull: keine hosts.yml gefunden - ueberspringe") return 0 try: cfg = sshpull_hosts.load(HOSTS_FILE) except (sshpull_hosts.ConfigError, OSError, ValueError) as exc: print(f"sshpull: FEHLER in hosts.yml: {exc}", file=sys.stderr) return 1 mkdir(BASE) keys = os.path.join(BASE, "keys") cmd_dir = os.path.join(BASE, "commands") for d in (keys, cmd_dir): shutil.rmtree(d, ignore_errors=True) mkdir(d, 0o700 if d == keys else 0o750) for key in {h["identity_file"] for h in cfg["hosts"]}: src = os.path.join(SSH_SRC, key) if not os.path.isfile(src): print(f"sshpull: WARNUNG: Schluessel {src} fehlt", file=sys.stderr) continue dst = os.path.join(keys, key) shutil.copyfile(src, dst) os.chown(dst, UID, GID) os.chmod(dst, 0o600) write(os.path.join(BASE, "ssh_config"), sshpull_hosts.render_ssh_config(cfg, keys, f"{SSH_SRC}/known_hosts"), 0o640) blocks = [] for h in cfg["hosts"]: write(os.path.join(cmd_dir, f"{h['name']}.sh"), "\n".join(h["commands"].get("run") or []) + "\n", 0o640) blocks += agentless_block(h) + localfile_block(h) # Verbindungsstatus des Collectors (Zustandswechsel up/down) blocks.append(f""" json {LOG_DIR}/_status/status.log """) with open(OSSEC_CONF, encoding="utf-8") as fh: conf = fh.read() if BEGIN in conf: # alten Block entfernen (falls ossec.conf nicht neu gemountet wurde) conf = conf[:conf.index(BEGIN)] + conf[conf.index(END) + len(END):] conf = conf.rstrip() + "\n\n" + BEGIN + "\n\n" + \ "\n\n".join(blocks) + "\n\n" + END + "\n" with open(OSSEC_CONF, "w", encoding="utf-8") as fh: fh.write(conf) print(f"sshpull: {len(cfg['hosts'])} Host(s) in ossec.conf eingetragen") return 0 if __name__ == "__main__": sys.exit(main())