Skip to content

Instantly share code, notes, and snippets.

@gjyoung1974
Last active July 22, 2022 14:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gjyoung1974/a68020c7a4e92b5d595ff382e1e19c20 to your computer and use it in GitHub Desktop.
Save gjyoung1974/a68020c7a4e92b5d595ff382e1e19c20 to your computer and use it in GitHub Desktop.
#!/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