Separate report "sending" into modules (#156)

* moved report output into dispatchers, stdout by default with config option of http(s)

* notes in arg config on how to configure http dispatcher

* removed some debug log visibility indicators

* missing import

* env vars more descriptive: KUBEHUNTER_HTTP_DISPATCH_METHOD and KUBEHUNTER_HTTP_DISPATCH_URL

* optimisation: delayed instantiation of the dispatcher until after selection to avoid instantiating unnecessarily

* refactor: config selection as per reporter selection

* bugfix: fall-back to default required if unknown reporter or dispatcher specified

* swapping urllib3 for requests

* corrected visibility levels for logging

* moving dispatchers into a file in reporters rather than it's own place to fit with theme and support dynamic module loading
This commit is contained in:
Tom Davidson
2019-08-12 13:28:31 +03:00
committed by danielsagi
parent cb90673bcb
commit e3af42cbce
4 changed files with 80 additions and 11 deletions
+21 -6
View File
@@ -15,6 +15,7 @@ parser.add_argument('--remote', nargs='+', metavar="HOST", default=list(), help=
parser.add_argument('--active', action="store_true", help="enables active hunting")
parser.add_argument('--log', type=str, metavar="LOGLEVEL", default='INFO', help="set log level, options are: debug, info, warn, none")
parser.add_argument('--report', type=str, default='plain', help="set report type, options are: plain, yaml, json")
parser.add_argument('--dispatch', type=str, default='stdout', help="where to send the report to, options are: stdout, http (use KUBEHUNTER_HTTP_DISPATCH_URL and KUBEHUNTER_HTTP_DISPATCH_METHOD to configure)")
parser.add_argument('--statistics', action="store_true", help="set hunting statistics")
import plugins
@@ -31,13 +32,27 @@ if config.log.lower() != "none":
from src.modules.report.plain import PlainReporter
from src.modules.report.yaml import YAMLReporter
from src.modules.report.json_reporter import JSONReporter
if config.report.lower() == "yaml":
config.reporter = YAMLReporter()
elif config.report.lower() == "json":
config.reporter = JSONReporter()
reporters = {
'yaml': YAMLReporter,
'json': JSONReporter,
'plain': PlainReporter
}
if config.report.lower() in reporters.keys():
config.reporter = reporters[config.report.lower()]()
else:
config.reporter = PlainReporter()
logging.warning('Unknown reporter selected, using plain')
config.reporter = reporters['plain']()
from src.modules.report.dispatchers import STDOUTDispatcher, HTTPDispatcher
dispatchers = {
'stdout': STDOUTDispatcher,
'http': HTTPDispatcher
}
if config.dispatch.lower() in dispatchers.keys():
config.dispatcher = dispatchers[config.dispatch.lower()]()
else:
logging.warning('Unknown dispatcher selected, using stdout')
config.dispatcher = dispatchers['stdout']()
from src.core.events import handler
from src.core.events.types import HuntFinished, HuntStarted
+4
View File
@@ -145,3 +145,7 @@ class HuntFinished(Event):
class HuntStarted(Event):
pass
class ReportDispatched(Event):
pass
+3 -5
View File
@@ -2,7 +2,7 @@ import logging
from __main__ import config
from src.core.events import handler
from src.core.events.types import Event, Service, Vulnerability, HuntFinished, HuntStarted
from src.core.events.types import Event, Service, Vulnerability, HuntFinished, HuntStarted, ReportDispatched
import threading
@@ -82,10 +82,8 @@ class SendFullReport(object):
def execute(self):
report = config.reporter.get_report()
if config.report == "plain":
logging.info("\n{div}\n{report}".format(div="-" * 10, report=report))
else:
print(report)
config.dispatcher.dispatch(report)
handler.publish_event(ReportDispatched())
handler.publish_event(TablesPrinted())
+52
View File
@@ -0,0 +1,52 @@
import logging
import os
import requests
from __main__ import config
class HTTPDispatcher(object):
def dispatch(self, report):
logging.info('Dispatching report via http')
dispatchMethod = os.environ.get(
'KUBEHUNTER_HTTP_DISPATCH_METHOD',
'POST'
).upper()
dispatchURL = os.environ.get(
'KUBEHUNTER_HTTP_DISPATCH_URL',
'https://localhost/'
)
logging.info(
'Dispatching report via {method} to {url}'.format(
method=dispatchMethod,
url=dispatchURL
)
)
try:
r = requests.request(
dispatchMethod,
dispatchURL,
json=report,
headers={'Content-Type': 'application/json'}
)
r.raise_for_status()
logging.info(
"\tResponse Code: {status}\n\tResponse Data:\n{data}".format(
status=r.status_code,
data=r.text
)
)
except requests.HTTPError as e:
logging.error(
"Dispatcher failed to deliver\n\tResponse Code: {status}\n\tResponse Data:\n{data}".format(
status=r.status_code,
data=r.text
)
)
class STDOUTDispatcher(object):
def dispatch(self, report):
logging.info('Dispatching report via stdout')
if config.report == "plain":
logging.info("\n{div}\n{report}".format(div="-" * 10, report=report))
else:
print(report)