Last active
July 22, 2022 14:02
-
-
Save gjyoung1974/a68020c7a4e92b5d595ff382e1e19c20 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
""" | |
Implements an RFC 5424 compliant SysLog agent/client on MSFT Windows Server. | |
We live in a Internet world, Windows needs Syslog for WinEVT... | |
""" | |
import socket | |
import json | |
import win32con | |
import win32event | |
import win32evtlog | |
import xmltodict | |
import win32serviceutil | |
import win32service | |
import servicemanager | |
# Windows Event Log Queries are in Xpath format | |
# https://blog.backslasher.net/filtering-windows-event-log-using-xpath.html | |
query_text = '*' | |
h = win32event.CreateEvent(None, 0, 0, None) | |
s = win32evtlog.EvtSubscribe('System', win32evtlog.EvtSubscribeStartAtOldestRecord, SignalEvent=h, Query=query_text) | |
# RFC syslog facility types: | |
FACILITY = { | |
'kern': 0, 'user': 1, 'mail': 2, 'daemon': 3, | |
'auth': 4, 'syslog': 5, 'lpr': 6, 'news': 7, | |
'uucp': 8, 'cron': 9, 'authpriv': 10, 'ftp': 11, | |
'local0': 16, 'local1': 17, 'local2': 18, 'local3': 19, | |
'local4': 20, 'local5': 21, 'local6': 22, 'local7': 23, | |
} | |
# RFC log levels for syslog: | |
LEVEL = { | |
'emerg': 0, 'alert': 1, 'crit': 2, 'err': 3, | |
'warning': 4, 'notice': 5, 'info': 6, 'debug': 7 | |
} | |
def syslog(host, port, win_evt, level=LEVEL['debug'], facility=FACILITY['syslog']): | |
""" | |
Send syslog TCP packet to given host and port. TCP is important for security logs vs UDP. | |
It's low enough volume and we want to receive the ACK | |
""" | |
# Open a TCP socket to the remote syslog host | |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
""" | |
Enable SO_REUSEADDR because Windows doesn't close sockets as fasts as we open them | |
""" | |
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
s.connect((host, port)) | |
""" | |
Format a RFC compliant syslog packet | |
Syslog messages must be in RFC 5424-compliant form or they are dropped. | |
Messages over 64KB in length are truncated. | |
<%pri%>%protocol-version% %app-name% %procid% %msgid% %msg% | |
""" | |
data = '<%d>%s %s %s %s %s' % (level + facility * 8, '1', ' win-evt', '0', '0', win_evt) | |
print(data) | |
s.send(data.encode()) # encode the tuple as bytes for TCP packet | |
# Be sure to close the socket! | |
s.close() | |
class AppServerSvc(win32serviceutil.ServiceFramework): | |
_svc_name_ = "SyslogAgentSVC" | |
_svc_display_name_ = "Syslog Agent Service" | |
def __init__(self, args): | |
win32serviceutil.ServiceFramework.__init__(self, args) | |
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) | |
socket.setdefaulttimeout(60) | |
def SvcStop(self): | |
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) | |
win32event.SetEvent(self.hWaitStop) | |
def SvcDoRun(self): | |
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, | |
servicemanager.PYS_SERVICE_STARTED, | |
(self._svc_name_, '')) | |
self.main() | |
def main(): | |
syslog_host = '172.17.0.1' | |
syslog_port = 514 | |
while True: | |
while True: | |
events = win32evtlog.EvtNext(s, 10) | |
if len(events) == 0: | |
break | |
for event in events: | |
try: | |
syslog(syslog_host, syslog_port, | |
json.dumps(xmltodict.parse(win32evtlog.EvtRender(event, win32evtlog.EvtRenderEventXml))), | |
level=LEVEL['debug'], facility=FACILITY['syslog']) | |
except Exception as e: | |
print(e) | |
pass | |
pass | |
while 1: | |
print('polling.for.events..') | |
w = win32event.WaitForSingleObjectEx(h, 2000, True) | |
if w == win32con.WAIT_OBJECT_0: | |
break | |
if __name__ == '__main__': | |
win32serviceutil.HandleCommandLine(AppServerSvc) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment