Add log sending logic to monkey

Add log processing logic to monkey island backend
This commit is contained in:
Itay Mizeretz 2018-02-14 15:50:53 +02:00
parent 0e9206728f
commit dbe7a6a378
9 changed files with 144 additions and 11 deletions

View File

@ -1,3 +1,4 @@
import base64
import json import json
import logging import logging
import platform import platform
@ -111,6 +112,21 @@ class ControlClient(object):
LOG.warn("Error connecting to control server %s: %s", LOG.warn("Error connecting to control server %s: %s",
WormConfiguration.current_server, exc) WormConfiguration.current_server, exc)
@staticmethod
def send_log(log):
if not WormConfiguration.current_server:
return
try:
telemetry = {'monkey_guid': GUID, 'log': base64.b64encode(log)}
reply = requests.post("https://%s/api/log" % (WormConfiguration.current_server,),
data=json.dumps(telemetry),
headers={'content-type': 'application/json'},
verify=False,
proxies=ControlClient.proxies)
except Exception as exc:
LOG.warn("Error connecting to control server %s: %s",
WormConfiguration.current_server, exc)
@staticmethod @staticmethod
def load_control_config(): def load_control_config():
if not WormConfiguration.current_server: if not WormConfiguration.current_server:

View File

@ -12,6 +12,7 @@ from config import WormConfiguration, EXTERNAL_CONFIG_FILE
from dropper import MonkeyDrops from dropper import MonkeyDrops
from model import MONKEY_ARG, DROPPER_ARG from model import MONKEY_ARG, DROPPER_ARG
from monkey import ChaosMonkey from monkey import ChaosMonkey
import utils
if __name__ == "__main__": if __name__ == "__main__":
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
@ -78,12 +79,10 @@ def main():
try: try:
if MONKEY_ARG == monkey_mode: if MONKEY_ARG == monkey_mode:
log_path = os.path.expandvars( log_path = utils.get_monkey_log_path()
WormConfiguration.monkey_log_path_windows) if sys.platform == "win32" else WormConfiguration.monkey_log_path_linux
monkey_cls = ChaosMonkey monkey_cls = ChaosMonkey
elif DROPPER_ARG == monkey_mode: elif DROPPER_ARG == monkey_mode:
log_path = os.path.expandvars( log_path = utils.get_dropper_log_path()
WormConfiguration.dropper_log_path_windows) if sys.platform == "win32" else WormConfiguration.dropper_log_path_linux
monkey_cls = MonkeyDrops monkey_cls = MonkeyDrops
else: else:
return True return True
@ -91,6 +90,8 @@ def main():
return True return True
if WormConfiguration.use_file_logging: if WormConfiguration.use_file_logging:
if os.path.exists(log_path):
os.remove(log_path)
LOG_CONFIG['handlers']['file']['filename'] = log_path LOG_CONFIG['handlers']['file']['filename'] = log_path
LOG_CONFIG['root']['handlers'].append('file') LOG_CONFIG['root']['handlers'].append('file')
else: else:

View File

@ -6,6 +6,7 @@ import sys
import time import time
import tunnel import tunnel
import utils
from config import WormConfiguration from config import WormConfiguration
from control import ControlClient from control import ControlClient
from model import DELAY_DELETE_CMD from model import DELAY_DELETE_CMD
@ -226,6 +227,8 @@ class ChaosMonkey(object):
firewall.close() firewall.close()
self.send_log()
self._singleton.unlock() self._singleton.unlock()
if WormConfiguration.self_delete_in_cleanup and -1 == sys.executable.find('python'): if WormConfiguration.self_delete_in_cleanup and -1 == sys.executable.find('python'):
@ -244,3 +247,13 @@ class ChaosMonkey(object):
LOG.error("Exception in self delete: %s", exc) LOG.error("Exception in self delete: %s", exc)
LOG.info("Monkey is shutting down") LOG.info("Monkey is shutting down")
def send_log(self):
monkey_log_path = utils.get_monkey_log_path()
if os.path.exists(monkey_log_path):
with open(monkey_log_path, 'r') as f:
log = f.read()
else:
log = ''
ControlClient.send_log(log)

14
chaos_monkey/utils.py Normal file
View File

@ -0,0 +1,14 @@
import os
import sys
from config import WormConfiguration
def get_monkey_log_path():
return os.path.expandvars(WormConfiguration.monkey_log_path_windows) if sys.platform == "win32" \
else WormConfiguration.monkey_log_path_linux
def get_dropper_log_path():
return os.path.expandvars(WormConfiguration.dropper_log_path_windows) if sys.platform == "win32" \
else WormConfiguration.dropper_log_path_linux

View File

@ -1,22 +1,24 @@
from datetime import datetime from datetime import datetime
import bson import bson
from bson.json_util import dumps
from flask import Flask, send_from_directory, redirect, make_response
import flask_restful import flask_restful
from bson.json_util import dumps
from flask import Flask, send_from_directory, make_response
from werkzeug.exceptions import NotFound from werkzeug.exceptions import NotFound
from cc.database import mongo from cc.database import mongo
from cc.resources.client_run import ClientRun from cc.resources.client_run import ClientRun
from cc.resources.monkey import Monkey from cc.resources.edge import Edge
from cc.resources.local_run import LocalRun from cc.resources.local_run import LocalRun
from cc.resources.telemetry import Telemetry from cc.resources.log import Log
from cc.resources.monkey import Monkey
from cc.resources.monkey_configuration import MonkeyConfiguration from cc.resources.monkey_configuration import MonkeyConfiguration
from cc.resources.monkey_download import MonkeyDownload from cc.resources.monkey_download import MonkeyDownload
from cc.resources.netmap import NetMap from cc.resources.netmap import NetMap
from cc.resources.edge import Edge
from cc.resources.node import Node from cc.resources.node import Node
from cc.resources.report import Report from cc.resources.report import Report
from cc.resources.root import Root from cc.resources.root import Root
from cc.resources.telemetry import Telemetry
from cc.resources.telemetry_feed import TelemetryFeed from cc.resources.telemetry_feed import TelemetryFeed
from cc.services.config import ConfigService from cc.services.config import ConfigService
@ -91,5 +93,6 @@ def init_app(mongo_url):
api.add_resource(Node, '/api/netmap/node', '/api/netmap/node/') api.add_resource(Node, '/api/netmap/node', '/api/netmap/node/')
api.add_resource(Report, '/api/report', '/api/report/') api.add_resource(Report, '/api/report', '/api/report/')
api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/') api.add_resource(TelemetryFeed, '/api/telemetry-feed', '/api/telemetry-feed/')
api.add_resource(Log, '/api/log', '/api/log/')
return app return app

View File

@ -0,0 +1,29 @@
import json
import flask_restful
from bson import ObjectId
from flask import request
from cc.database import mongo
from cc.services.log import LogService
from cc.services.node import NodeService
__author__ = "itay.mizeretz"
class Log(flask_restful.Resource):
def get(self):
monkey_id = request.args.get('id')
exists_monkey_id = request.args.get('exists')
if monkey_id:
return LogService.get_log_by_monkey_id(ObjectId(monkey_id))
else:
return LogService.log_exists(ObjectId(exists_monkey_id))
def post(self):
telemetry_json = json.loads(request.data)
monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid'])['_id']
log_id = LogService.add_log(monkey_id, telemetry_json['log'])
return mongo.db.log.find_one_or_404({"_id": log_id})

View File

@ -33,7 +33,8 @@ class Root(flask_restful.Resource):
@staticmethod @staticmethod
def reset_db(): def reset_db():
[mongo.db[x].drop() for x in ['config', 'monkey', 'telemetry', 'node', 'edge', 'report']] [mongo.db[x].drop() for x in
['config', 'monkey', 'telemetry', 'node', 'edge', 'report', 'log', 'fs.chunks', 'fs.files']]
ConfigService.init_config() ConfigService.init_config()
return jsonify(status='OK') return jsonify(status='OK')

View File

@ -0,0 +1,52 @@
from datetime import datetime
import gridfs
import cc.services.node
from cc.database import mongo
__author__ = "itay.mizeretz"
class LogService:
def __init__(self):
pass
@staticmethod
def get_log_by_monkey_id(monkey_id):
log = mongo.db.log.find_one({'monkey_id': monkey_id})
if log:
fs = gridfs.GridFS(mongo.db)
log_file = fs.get(log['file_id'])
monkey_label = cc.services.node.NodeService.get_monkey_label(
cc.services.node.NodeService.get_monkey_by_id(log['monkey_id']))
return \
{
'monkey_label': monkey_label,
'log': log_file.read(),
'timestamp': log['timestamp']
}
@staticmethod
def remove_logs_by_monkey_id(monkey_id):
fs = gridfs.GridFS(mongo.db)
for log in mongo.db.log.find({'monkey_id': monkey_id}):
fs.delete(log['file_id'])
mongo.db.log.delete_many({'monkey_id': monkey_id})
@staticmethod
def add_log(monkey_id, log_data, timestamp=datetime.now()):
LogService.remove_logs_by_monkey_id(monkey_id)
fs = gridfs.GridFS(mongo.db)
file_id = fs.put(log_data)
return mongo.db.log.insert(
{
'monkey_id': monkey_id,
'file_id': file_id,
'timestamp': timestamp
}
)
@staticmethod
def log_exists(monkey_id):
return mongo.db.log.find_one({'monkey_id': monkey_id}) is not None

View File

@ -1,9 +1,12 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from bson import ObjectId from bson import ObjectId
import cc.services.log
from cc.database import mongo from cc.database import mongo
from cc.services.edge import EdgeService from cc.services.edge import EdgeService
from cc.utils import local_ip_addresses from cc.utils import local_ip_addresses
__author__ = "itay.mizeretz" __author__ = "itay.mizeretz"
@ -54,6 +57,7 @@ class NodeService:
else: else:
new_node["services"] = [] new_node["services"] = []
new_node['has_log'] = cc.services.log.LogService.log_exists(ObjectId(node_id))
return new_node return new_node
@staticmethod @staticmethod
@ -241,7 +245,7 @@ class NodeService:
@staticmethod @staticmethod
def get_monkey_island_pseudo_net_node(): def get_monkey_island_pseudo_net_node():
return\ return \
{ {
"id": NodeService.get_monkey_island_pseudo_id(), "id": NodeService.get_monkey_island_pseudo_id(),
"label": "MonkeyIsland", "label": "MonkeyIsland",