diff --git a/chaos_monkey/control.py b/chaos_monkey/control.py index c2939ec95..d02306bb7 100644 --- a/chaos_monkey/control.py +++ b/chaos_monkey/control.py @@ -120,7 +120,7 @@ class ControlClient(object): return try: - WormConfiguration.from_dict(reply.json().get('config')) + WormConfiguration.from_dict(reply.json()["objects"][0].get('config')) LOG.info("New configuration was loaded from server: %r" % (WormConfiguration.as_dict(),)) except Exception, exc: # we don't continue with default conf here because it might be dangerous @@ -128,6 +128,11 @@ class ControlClient(object): WormConfiguration.current_server, reply._content, exc) raise Exception("Couldn't load from from server's configuration, aborting. %s" % exc) + @staticmethod + def check_for_stop(): + ControlClient.load_control_config() + return not WormConfiguration.alive + @staticmethod def download_monkey_exe(host): if not WormConfiguration.current_server: diff --git a/chaos_monkey/monkey.py b/chaos_monkey/monkey.py index c4f2a6f09..365198fee 100644 --- a/chaos_monkey/monkey.py +++ b/chaos_monkey/monkey.py @@ -46,7 +46,7 @@ class ChaosMonkey(object): arg_parser.add_argument('-s', '--server') arg_parser.add_argument('-d', '--depth') opts, self._args = arg_parser.parse_known_args(self._args) - + self._parent = opts.parent self._default_tunnel = opts.tunnel self._default_server = opts.server @@ -69,10 +69,18 @@ class ChaosMonkey(object): if firewall.is_enabled(): firewall.add_firewall_rule() ControlClient.wakeup(parent=self._parent, default_tunnel=self._default_tunnel) + ControlClient.load_control_config() + + if not WormConfiguration.alive: + LOG.info("Marked not alive from configuration") + return + monkey_tunnel = ControlClient.create_control_tunnel() if monkey_tunnel: monkey_tunnel.start() + ControlClient.send_telemetry("state", {'done': False}) + self._default_server = WormConfiguration.current_server LOG.debug("default server: %s" % self._default_server) ControlClient.send_telemetry("tunnel", ControlClient.proxies.get('https')) @@ -103,12 +111,16 @@ class ChaosMonkey(object): break machines = self._network.get_victim_machines(WormConfiguration.scanner_class, - max_find=WormConfiguration.victims_max_find) + max_find=WormConfiguration.victims_max_find, + stop_callback=ControlClient.check_for_stop) is_empty = True for machine in machines: + if ControlClient.check_for_stop(): + break + is_empty = False for finger in self._fingerprint: - LOG.info("Trying to get OS fingerprint from %r with module %s", + LOG.info("Trying to get OS fingerprint from %r with module %s", machine, finger.__class__.__name__) finger.get_host_fingerprint(machine) @@ -156,8 +168,8 @@ class ChaosMonkey(object): if successful_exploiter: self._exploited_machines.add(machine) - ControlClient.send_telemetry('exploit', {'machine': machine.__dict__, - 'exploiter': successful_exploiter.__class__.__name__}) + ControlClient.send_telemetry('exploit', {'machine': machine.__dict__, + 'exploiter': successful_exploiter.__class__.__name__}) LOG.info("Successfully propagated to %s using %s", machine, successful_exploiter.__class__.__name__) @@ -202,13 +214,14 @@ class ChaosMonkey(object): from _subprocess import SW_HIDE, STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags = CREATE_NEW_CONSOLE | STARTF_USESHOWWINDOW - startupinfo.wShowWindow = SW_HIDE + startupinfo.wShowWindow = SW_HIDE subprocess.Popen(DELAY_DELETE_CMD % {'file_path': sys.executable}, - stdin=None, stdout=None, stderr=None, + stdin=None, stdout=None, stderr=None, close_fds=True, startupinfo=startupinfo) else: os.remove(sys.executable) except Exception, exc: LOG.error("Exception in self delete: %s", exc) - LOG.info("Monkey is shutting down") + ControlClient.send_telemetry("state", {'done': True}) + LOG.info("Monkey is shutting down") \ No newline at end of file diff --git a/chaos_monkey/network/network_scanner.py b/chaos_monkey/network/network_scanner.py index bef094429..430c4f68a 100644 --- a/chaos_monkey/network/network_scanner.py +++ b/chaos_monkey/network/network_scanner.py @@ -33,7 +33,7 @@ class NetworkScanner(object): for ip_address in self._ip_addresses] LOG.info("Base local networks to scan are: %r", self._ranges) - def get_victim_machines(self, scan_type, max_find=5): + def get_victim_machines(self, scan_type, max_find=5, stop_callback=None): assert issubclass(scan_type, HostScanner) scanner = scan_type() @@ -42,6 +42,10 @@ class NetworkScanner(object): for range in self._ranges: LOG.debug("Scanning for potential victims in the network %r", range) for victim in range: + if stop_callback and stop_callback(): + LOG.debug("Got stop signal") + break + # skip self IP address if victim.ip_addr in self._ip_addresses: continue diff --git a/monkey_island/cc/admin/ui/index.html b/monkey_island/cc/admin/ui/index.html index 5b1be9e53..aadd2c871 100644 --- a/monkey_island/cc/admin/ui/index.html +++ b/monkey_island/cc/admin/ui/index.html @@ -123,6 +123,34 @@
+
+
+
+ Monkey Config +
+
+ + + + + + + +
+
@@ -132,7 +160,7 @@
@@ -146,23 +174,11 @@
-
- - - - -
- + diff --git a/monkey_island/cc/admin/ui/js/monkeys-admin.js b/monkey_island/cc/admin/ui/js/monkeys-admin.js index baf3c81ed..c7f8a395f 100644 --- a/monkey_island/cc/admin/ui/js/monkeys-admin.js +++ b/monkey_island/cc/admin/ui/js/monkeys-admin.js @@ -108,6 +108,8 @@ function initAdmin() { "ordering": false, }); + loadNewMonkeysConfig(); + window.setTimeout(updateMonkeys, 10000); addEventsListeners(); @@ -134,7 +136,15 @@ function updateMonkeys() { createEdges(); createTunnels(); + // keep old selection + var selNode = network.getSelectedNodes(); network.setData({nodes: nodes, edges: edges}); + if (selNode.length) { + var monkey = getMonkey(selNode[0]); + if (monkey) { // The selection might be no longer valid if the monkey was deleted + selectNode(monkey.hostname, false); + } + } } createScanned(); window.setTimeout(updateMonkeys, 10000); @@ -269,8 +279,16 @@ function buildMonkeyDescription(monkey) { var html = " " + monkey.hostname + "
" + " " + monkey.description + "
" + - " " + monkey.internet_access + "
" + - "
" + " " + monkey.internet_access + "
"; + if (monkey.dead) { + html += " Dead
"; + } + if (!monkey.config.alive) { + html += " Marked to be dead
"; + } + html += + " " + monkey.keepalive + "
" + + "
"; for (var i = 0; i < monkey.ip_addresses.length; i++) { html += monkey.ip_addresses[i] + "
" @@ -352,7 +370,7 @@ function onSelect(properties) { var content = "No selection" $("#selectionInfo").html(content); $('#monkey-config').hide() - $('#btnConfigLoad, #btnConfigUpdate').hide(); + $('#btnConfigLoad, #btnConfigUpdate, #btnKillMonkey, #btnReviveMonkey').hide(); telemTable.clear(); telemTable.draw(); } @@ -382,6 +400,17 @@ function onNodeSelect(nodeId) { $('#monkey-config').show() $('#btnConfigLoad, #btnConfigUpdate').show(); + loadMonkeyConfig(); + + if (monkey.config.alive) { + $('#btnKillMonkey').show(); + $('#btnReviveMonkey').hide(); + } + else { + $('#btnKillMonkey').hide(); + $('#btnReviveMonkey').show(); + } + $.getJSON('/api/telemetry/' + monkey.guid, function(json) { telemTable.clear(); @@ -394,7 +423,6 @@ function onNodeSelect(nodeId) { telemTable.draw(); }); - network.selectNodes([nodeId]); } @@ -406,6 +434,19 @@ function onEdgeSelect(edge) { } +function killMonkey() { + var curr_config = monkeyCfg.getValue(); + curr_config.alive = false; + monkeyCfg.setValue(curr_config); + updateMonkeyConfig(); +} + +function reviveMonkey() { + var curr_config = monkeyCfg.getValue(); + curr_config.alive = true; + monkeyCfg.setValue(curr_config); + updateMonkeyConfig(); +} function toggleFocusOnNode() { if (focusedOnNode) { @@ -413,7 +454,7 @@ function toggleFocusOnNode() { focusedOnNode = false; } else { - selectNode(undefined, zoom=true); + selectNode(undefined, true); } } @@ -460,26 +501,26 @@ function updateNewMonkeysConfig() { } function loadMonkeyConfig() { - nodes = network.getSelectedNodes(); + var node = network.getSelectedNodes(); - if(nodes.length != 1) { + if(node.length != 1) { return; } - var monkey = getMonkey(nodes[0]); + var monkey = getMonkey(node[0]); monkeyCfg.setValue(monkey.config); } function updateMonkeyConfig() { - nodes = network.getSelectedNodes(); - if(nodes.length != 1) { + var node = network.getSelectedNodes(); + if(node.length != 1) { return; } - var monkey = getMonkey(nodes[0]); + var monkey = getMonkey(node[0]); - var curr_config = monkeyCfg.getValue() + var curr_config = monkeyCfg.getValue(); $.ajax({ headers : { @@ -491,7 +532,8 @@ function updateMonkeyConfig() { data : JSON.stringify({config: curr_config}), success : function(response, textStatus, jqXhr) { monkey.config = curr_config; - console.log("Monkey config successfully updated!"); + console.log("Monkey config successfully updated! (" + monkey.hostname + ")"); + selectNode(monkey.hostname, false); }, error : function(jqXHR, textStatus, errorThrown) { // log the error to the console diff --git a/monkey_island/cc/main.py b/monkey_island/cc/main.py index 1bcb5b693..c3715c018 100644 --- a/monkey_island/cc/main.py +++ b/monkey_island/cc/main.py @@ -167,7 +167,15 @@ class Telemetry(restful.Resource): mongo.db.monkey.update({"guid": telemetry_json['monkey_guid']}, {'$unset': {'tunnel_guid':''}}, upsert=True) - + elif telemetry_json.get('telem_type') == 'state': + if telemetry_json['data']['done']: + mongo.db.monkey.update({"guid": telemetry_json['monkey_guid']}, + {'$set': {'dead': True}}, + upsert=True) + else: + mongo.db.monkey.update({"guid": telemetry_json['monkey_guid']}, + {'$set': {'dead': False}}, + upsert=True) except: pass