Files
docker-wazuh/manager/generate.py
T
2026-10-07 10:47:30 +02:00

138 lines
4.3 KiB
Python

"""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 <ossec_config>-Block in ossec.conf mit <agentless> und <localfile>
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 = "<!-- SSHPULL BEGIN (automatisch erzeugt) -->"
END = "<!-- SSHPULL 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""" <agentless>
<type>sshpull_integrity_linux</type>
<frequency>{int(integ['interval'])}</frequency>
<host>{escape(host)}</host>
<state>periodic</state>
<arguments>{escape(' '.join(integ['paths']))}</arguments>
</agentless>""")
cmds = h["commands"]
if cmds.get("enabled") and cmds.get("run"):
out.append(f""" <agentless>
<type>sshpull_generic_diff</type>
<frequency>{int(cmds['interval'])}</frequency>
<host>{escape(host)}</host>
<state>periodic_diff</state>
<arguments>{escape(h['name'])}</arguments>
</agentless>""")
return out
def localfile_block(h):
out = []
for src in h["logs"]:
fmt = "syslog" if src["type"] == "journal" else src["format"]
out.append(f""" <localfile>
<log_format>{fmt}</log_format>
<location>{LOG_DIR}/{h['name']}/{src['name']}.log</location>
</localfile>""")
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""" <localfile>
<log_format>json</log_format>
<location>{LOG_DIR}/_status/status.log</location>
</localfile>""")
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<ossec_config>\n" + \
"\n\n".join(blocks) + "\n</ossec_config>\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())