138 lines
4.3 KiB
Python
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())
|