first commit
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
"""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())
|
||||
Reference in New Issue
Block a user