From ea4754812e7c068be85ab351dd6ecbaa9375620a Mon Sep 17 00:00:00 2001 From: Konstantin Antselovich Date: Thu, 2 Apr 2015 17:49:15 -0700 Subject: [PATCH 1/2] TWISTD Run as twistd application, fixes #31 --- hacklog/server.py | 30 +++++++++++++++++++++++------- hacklog/services.py | 2 +- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/hacklog/server.py b/hacklog/server.py index 2f48acc..bd35d00 100755 --- a/hacklog/server.py +++ b/hacklog/server.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python import sys import time import thread @@ -8,6 +9,7 @@ from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor, defer +from twisted.application import service, internet from optparse import OptionParser from ConfigParser import ConfigParser @@ -29,7 +31,7 @@ def __init__(self): self.config_file = '../conf/server.conf' self.loglevel = logging.DEBUG self.running = True - self.usage = "usage: %prog -c config_file" + self.usage = "usage: %prog -c config_file sssss" self.testEnabled = False self.emailTest = False self.successPattern = None @@ -93,19 +95,20 @@ def cleanupThread(self): threadPool.stop() def run(self): - signal.signal(signal.SIGINT, self.interrupt) - reactor.callInThread(self.messageParcer) reactor.listenUDP(self.port, SyslogReader()) reactor.run() + def init(self): + signal.signal(signal.SIGINT, self.interrupt) + reactor.callInThread(self.messageParcer) + def start(self): - self.readCmdArgs() + #self.readCmdArgs() self.parceConfig(self.config_file) self.setLogging() algorithm.setServices(MailConf(self.emailTest)) create_db_engine(self) create_tables() - self.run() def stop(self): reactor.stop() @@ -117,10 +120,23 @@ def datagramReceived(self, data, (host, port)): syslogMsg = SyslogMsg(data, host, port) queue.put(syslogMsg) +def getApplicationService(): + return internet.UDPServer(server.port, SyslogReader()) + def main(): + server.run() - server = SyslogServer() - server.start() +# default context +print "default contx" +server = SyslogServer() +server.start() +server.init() if __name__ == '__main__': + print __name__ main() +else: + print __name__ + application = service.Application("hacklog") + service = getApplicationService() + service.setServiceParent(application) diff --git a/hacklog/services.py b/hacklog/services.py index 629fc20..82c2d70 100644 --- a/hacklog/services.py +++ b/hacklog/services.py @@ -2,7 +2,7 @@ from datetime import datetime import smtplib from entities import * -import server +#import server from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText From bb22f51b9b9fed9e5de58c64d8b3b888dcb1bc61 Mon Sep 17 00:00:00 2001 From: Konstantin Antselovich Date: Tue, 12 May 2015 16:02:58 -0700 Subject: [PATCH 2/2] Run hacklog as twistd application plugin --- hacklog.spec | 3 ++ hacklog/server.py | 51 +++++++++++++++++-------------- hacklog/services.py | 1 - twisted/plugins/hacklog_plugin.py | 39 +++++++++++++++++++++++ 4 files changed, 70 insertions(+), 24 deletions(-) create mode 100644 twisted/plugins/hacklog_plugin.py diff --git a/hacklog.spec b/hacklog.spec index 22e55a5..87da394 100644 --- a/hacklog.spec +++ b/hacklog.spec @@ -88,6 +88,7 @@ mkdir -p $RPM_BUILD_ROOT%{_initrddir} install -p -m 0640 conf/server.conf $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/server.conf install -p -m 0755 bin/%{name} $RPM_BUILD_ROOT%{_bindir}/%{name} install -p -m 0755 scripts/%{name}.init.d $RPM_BUILD_ROOT%{_initrddir}/%{name} +install -p -m 0755 twisted/plugins/%{name}_plugin.py $RPM_BUILD_ROOT%{python_sitelib}/twisted/plugins/%{name}_plugin.py %if ((0%{?rhel} >= 6 || 0%{?fedora} > 12) && 0%{?include_tests}) %check @@ -105,6 +106,7 @@ rm -rf $RPM_BUILD_ROOT %doc $RPM_BUILD_DIR/%{name}-%{version}/%{name}-%{version}/LICENSE %{python_sitelib}/%{name} %{python_sitelib}/%{name}-%{version}-py?.?.egg-info +%{python_sitelib}/twisted/plugins/%{name}_plugin.py %files -n hacklog %defattr(-,root,root) @@ -120,6 +122,7 @@ rm -rf $RPM_BUILD_ROOT %doc $RPM_BUILD_DIR/%{name}-%{version}/%{name}-%{version}/LICENSE %{python_sitelib}/%{name} %{python_sitelib}/%{name}-%{version}-py?.?.egg-info +%{python_sitelib}/twisted/plugins/%{name}_plugin.py # less than RHEL 8 / Fedora 16 # not sure if RHEL 7 will use systemd yet diff --git a/hacklog/server.py b/hacklog/server.py index bd35d00..aafec7a 100755 --- a/hacklog/server.py +++ b/hacklog/server.py @@ -24,19 +24,21 @@ class SyslogServer(): """ Syslog server based on twisted library """ - def __init__(self): + def __init__(self, config_file='../conf/server.conf'): self.dbFile = 'hacklog.db' self.port = 10514 self.bind_address = '127.0.0.1' - self.config_file = '../conf/server.conf' + self.config_file = config_file self.loglevel = logging.DEBUG self.running = True - self.usage = "usage: %prog -c config_file sssss" + self.started = False + self.parser_thread = False + self.usage = "usage: %prog -c config_file" self.testEnabled = False self.emailTest = False self.successPattern = None self.failurePattern = None - + def parceConfig(self, config_file): config = ConfigParser() config.read(config_file) @@ -75,40 +77,43 @@ def interrupt(self, signum, stackframe): self.stop() def messageParcer(self): - logging.debug("messageParcer in thread " + str(thread.get_ident())) + self.parser_thread = thread.get_ident() + # output will be logged when app works under twistd plugin + print("messageParcer in thread " + str(self.parser_thread)) + logging.info("messageParcer in thread " + str(self.parser_thread)) + parser = None # get parsing patterns from config file when in testing mode if self.testEnabled: parser = Parser(self.successPattern, self.failurePattern, self.testEnabled) else: parser = Parser() - + while self.running: msg = queue.get() eventLog = parser.parseLogLine(msg) if eventLog: algorithm.processEventLog(eventLog) logging.debug("messages in queue " + str(queue.qsize()) + ", received %r from %s:%d" % (msg.data, msg.host, msg.port)) - - def cleanupThread(self): - threadPool = reactor.getThreadPool() - threadPool.stop() def run(self): reactor.listenUDP(self.port, SyslogReader()) reactor.run() def init(self): - signal.signal(signal.SIGINT, self.interrupt) - reactor.callInThread(self.messageParcer) + if not self.parser_thread: + print('Staring parser thread') + signal.signal(signal.SIGINT, self.interrupt) + reactor.callInThread(self.messageParcer) def start(self): - #self.readCmdArgs() - self.parceConfig(self.config_file) - self.setLogging() - algorithm.setServices(MailConf(self.emailTest)) - create_db_engine(self) - create_tables() + while not self.started: + self.parceConfig(self.config_file) + self.setLogging() + algorithm.setServices(MailConf(self.emailTest)) + create_db_engine(self) + create_tables() + self.started = True def stop(self): reactor.stop() @@ -124,19 +129,19 @@ def getApplicationService(): return internet.UDPServer(server.port, SyslogReader()) def main(): + server.readCmdArgs() + server.start() + server.init() server.run() # default context -print "default contx" server = SyslogServer() -server.start() -server.init() if __name__ == '__main__': - print __name__ main() else: - print __name__ + logging.info('starting as twisted plugin or application: {0}'.format(__name__)) + application = service.Application("hacklog") service = getApplicationService() service.setServiceParent(application) diff --git a/hacklog/services.py b/hacklog/services.py index 82c2d70..ea3a2e6 100644 --- a/hacklog/services.py +++ b/hacklog/services.py @@ -2,7 +2,6 @@ from datetime import datetime import smtplib from entities import * -#import server from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText diff --git a/twisted/plugins/hacklog_plugin.py b/twisted/plugins/hacklog_plugin.py new file mode 100644 index 0000000..0372b83 --- /dev/null +++ b/twisted/plugins/hacklog_plugin.py @@ -0,0 +1,39 @@ +from zope.interface import implements + +from twisted.python import usage +from twisted.plugin import IPlugin +from twisted.application.service import IServiceMaker +from twisted.application import internet + +from hacklog.server import SyslogServer, SyslogReader + + +class Options(usage.Options): + optParameters = [["config", "c", 'conf/server.conf', "path to configuration file."]] + + +class MyServiceMaker(object): + implements(IServiceMaker, IPlugin) + tapname = "hacklog" + description = "Syslog server for detection of compromised user accounts" + options = Options + + server = SyslogServer() + + def makeService(self, options): + """ + Construct a UDP Server + """ + self.server.config_file = options['config'] + + self.server.start() + self.server.init() + + return internet.UDPServer(self.server.port, SyslogReader()) + + +# Now construct an object which *provides* the relevant interfaces +# The name of this variable is irrelevant, as long as there is *some* +# name bound to a provider of IPlugin and IServiceMaker. + +serviceMaker = MyServiceMaker()