New log_relay.py replaces raw 'cat' on the rsyslog pipe. Intercepts sshd and bash lines and re-emits them as structured RFC 5424 events: login_success, session_opened, disconnect, connection_closed, command. Parsers updated to accept non-nil PROCID (sshd uses PID).
107 lines
2.8 KiB
Python
107 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
SSH log relay — reads rsyslog output from the named pipe and re-emits
|
|
matched sshd/bash events as proper DECNET RFC 5424 syslog lines to stdout.
|
|
|
|
Matched events:
|
|
- Accepted password (login_success)
|
|
- Connection closed (connection_closed)
|
|
- Disconnected from user (disconnect)
|
|
- Session opened (session_opened)
|
|
- bash CMD (command)
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
from decnet_logging import syslog_line, write_syslog_file, SEVERITY_INFO, SEVERITY_WARNING
|
|
|
|
NODE_NAME = os.environ.get("NODE_NAME", "ssh-decky")
|
|
SERVICE = "ssh"
|
|
|
|
# sshd patterns
|
|
_ACCEPTED_RE = re.compile(
|
|
r"Accepted (\S+) for (\S+) from (\S+) port (\d+)"
|
|
)
|
|
_SESSION_RE = re.compile(
|
|
r"session opened for user (\S+?)(?:\(uid=\d+\))? by"
|
|
)
|
|
_DISCONNECTED_RE = re.compile(
|
|
r"Disconnected from user (\S+) (\S+) port (\d+)"
|
|
)
|
|
_CONN_CLOSED_RE = re.compile(
|
|
r"Connection closed by (\S+) port (\d+)"
|
|
)
|
|
|
|
# bash PROMPT_COMMAND pattern
|
|
_BASH_CMD_RE = re.compile(
|
|
r"CMD\s+uid=(\S+)\s+pwd=(\S+)\s+cmd=(.*)"
|
|
)
|
|
|
|
|
|
def _handle_line(line: str) -> None:
|
|
"""Parse a raw rsyslog line and emit a DECNET syslog line if it matches."""
|
|
|
|
# --- Accepted password ---
|
|
m = _ACCEPTED_RE.search(line)
|
|
if m:
|
|
method, user, src_ip, port = m.groups()
|
|
write_syslog_file(syslog_line(
|
|
SERVICE, NODE_NAME, "login_success", SEVERITY_WARNING,
|
|
src_ip=src_ip, username=user, auth_method=method, src_port=port,
|
|
))
|
|
return
|
|
|
|
# --- Session opened ---
|
|
m = _SESSION_RE.search(line)
|
|
if m:
|
|
user = m.group(1)
|
|
write_syslog_file(syslog_line(
|
|
SERVICE, NODE_NAME, "session_opened", SEVERITY_INFO,
|
|
username=user,
|
|
))
|
|
return
|
|
|
|
# --- Disconnected from user ---
|
|
m = _DISCONNECTED_RE.search(line)
|
|
if m:
|
|
user, src_ip, port = m.groups()
|
|
write_syslog_file(syslog_line(
|
|
SERVICE, NODE_NAME, "disconnect", SEVERITY_INFO,
|
|
src_ip=src_ip, username=user, src_port=port,
|
|
))
|
|
return
|
|
|
|
# --- Connection closed ---
|
|
m = _CONN_CLOSED_RE.search(line)
|
|
if m:
|
|
src_ip, port = m.groups()
|
|
write_syslog_file(syslog_line(
|
|
SERVICE, NODE_NAME, "connection_closed", SEVERITY_INFO,
|
|
src_ip=src_ip, src_port=port,
|
|
))
|
|
return
|
|
|
|
# --- bash CMD ---
|
|
m = _BASH_CMD_RE.search(line)
|
|
if m:
|
|
uid, pwd, cmd = m.groups()
|
|
write_syslog_file(syslog_line(
|
|
SERVICE, NODE_NAME, "command", SEVERITY_INFO,
|
|
uid=uid, pwd=pwd, command=cmd,
|
|
))
|
|
return
|
|
|
|
|
|
def main() -> None:
|
|
pipe_path = "/var/run/decnet-logs"
|
|
while True:
|
|
with open(pipe_path, "r") as pipe:
|
|
for line in pipe:
|
|
_handle_line(line.rstrip("\n"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|