CR comments fixed

This commit is contained in:
VakarisZ 2019-10-01 10:42:51 +03:00
parent 72e30bb631
commit 73d434119d
15 changed files with 105 additions and 72 deletions

View File

@ -1,4 +1,5 @@
# Automatic blackbox tests
### Prerequisites
1. Download google sdk: https://cloud.google.com/sdk/docs/
2. Download service account key GCP console -> IAM -> service accounts(you can use the same key used to authenticate terraform scripts)
2. Download service account key for MonkeyZoo project (if you deployed MonkeyZoo via terraform scripts then you already have it).
GCP console -> IAM -> service accounts(you can use the same key used to authenticate terraform scripts)

View File

@ -1,6 +1,7 @@
from time import sleep
import json
import logging
from bson import json_util
from envs.monkey_zoo.blackbox.island_client.monkey_island_requests import MonkeyIslandRequests
@ -30,9 +31,9 @@ class MonkeyIslandClient(object):
def run_monkey_local(self):
response = self.requests.post_json("api/local-monkey", dict_data={"action": "run"})
if MonkeyIslandClient.monkey_ran_successfully(response):
print("Running the monkey.")
logging.info("Running the monkey.")
else:
print("Failed to run the monkey.")
logging.error("Failed to run the monkey.")
assert False
@staticmethod
@ -42,36 +43,35 @@ class MonkeyIslandClient(object):
@avoid_race_condition
def kill_all_monkeys(self):
if self.requests.get("api", {"action": "killall"}).ok:
print("Killing all monkeys after the test.")
logging.info("Killing all monkeys after the test.")
else:
print("Failed to kill all monkeys.")
logging.error("Failed to kill all monkeys.")
assert False
@avoid_race_condition
def reset_env(self):
if self.requests.get("api", {"action": "reset"}).ok:
print("Resetting environment after the test.")
logging.info("Resetting environment after the test.")
else:
print("Failed to reset the environment.")
logging.error("Failed to reset the environment.")
assert False
def find_monkeys_in_db(self, query):
if query is None:
raise TypeError
response = self.requests.get(MONKEY_TEST_ENDPOINT,
MonkeyIslandClient.form_find_query_for_request(query))
try:
return MonkeyIslandClient.get_test_query_results(response)
except Exception:
print("Ran into trouble parsing response for monkey query")
raise
def get_all_monkeys_from_db(self):
response = self.requests.get(MONKEY_TEST_ENDPOINT,
MonkeyIslandClient.form_find_query_for_request(None))
return MonkeyIslandClient.get_test_query_results(response)
def find_log_in_db(self, query):
response = self.requests.get(LOG_TEST_ENDPOINT,
MonkeyIslandClient.form_find_query_for_request(query))
try:
return MonkeyIslandClient.get_test_query_results(response)
except Exception:
print("Ran into trouble parsing response for log query")
raise
@staticmethod
def form_find_query_for_request(query):

View File

@ -1,6 +1,8 @@
import requests
# SHA3-512 of '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()'
import logging
NO_AUTH_CREDS = '55e97c9dcfd22b8079189ddaeea9bce8125887e3237b800c6176c9afa80d2062' \
'8d2c8d0b1538d2208c1444ac66535b764a3d902b35e751df3faec1e477ed3557'
@ -14,7 +16,7 @@ class MonkeyIslandRequests(object):
try:
return self.get_jwt_from_server()
except requests.ConnectionError:
print("Unable to connect to island, aborting!")
logging.error("Unable to connect to island, aborting!")
assert False
def get_jwt_from_server(self):

View File

@ -1,5 +1,6 @@
import os
import logging
from bson import ObjectId
@ -11,7 +12,7 @@ class MonkeyLog(object):
def download_log(self, island_client):
log = island_client.find_log_in_db({'monkey_id': ObjectId(self.monkey['id'])})
if not log:
print("Log for monkey {} not found".format(self.monkey['ip_addresses'][0]))
logging.error("Log for monkey {} not found".format(self.monkey['ip_addresses'][0]))
return False
else:
self.write_log_to_file(log)

View File

@ -1,7 +1,7 @@
import re
class LogParser(object):
class MonkeyLogParser(object):
def __init__(self, log_path):
self.log_path = log_path
@ -13,7 +13,7 @@ class LogParser(object):
def print_errors(self):
print("Errors:")
for error_line in LogParser.get_errors(self.log_contents):
for error_line in MonkeyLogParser.get_errors(self.log_contents):
print(error_line)
@staticmethod
@ -23,7 +23,7 @@ class LogParser(object):
def print_warnings(self):
print("Warnings:")
for warning_line in LogParser.get_warnings(self.log_contents):
for warning_line in MonkeyLogParser.get_warnings(self.log_contents):
print(warning_line)
@staticmethod

View File

@ -1,7 +1,9 @@
import logging
from envs.monkey_zoo.blackbox.log_handlers.monkey_log import MonkeyLog
class LogsDownloader(object):
class MonkeyLogsDownloader(object):
def __init__(self, island_client, log_dir_path):
self.island_client = island_client
@ -9,8 +11,8 @@ class LogsDownloader(object):
self.monkey_log_paths = []
def download_monkey_logs(self):
print("Downloading each monkey log.")
all_monkeys = self.island_client.find_monkeys_in_db(None)
logging.info("Downloading each monkey log.")
all_monkeys = self.island_client.get_all_monkeys_from_db()
for monkey in all_monkeys:
downloaded_log_path = self._download_monkey_log(monkey)
if downloaded_log_path:

View File

@ -1,28 +1,31 @@
import os
import shutil
from envs.monkey_zoo.blackbox.log_handlers.log_parser import LogParser
from envs.monkey_zoo.blackbox.log_handlers.logs_downloader import LogsDownloader
import logging
from envs.monkey_zoo.blackbox.log_handlers.monkey_log_parser import MonkeyLogParser
from envs.monkey_zoo.blackbox.log_handlers.monkey_logs_downloader import MonkeyLogsDownloader
LOG_DIR_NAME = 'logs'
class TestLogsHandler(object):
def __init__(self, test_name, island_client):
def __init__(self, test_name, island_client, log_dir_path):
self.test_name = test_name
self.island_client = island_client
self.log_dir_path = os.path.join(TestLogsHandler.get_log_dir_path(), self.test_name)
self.log_dir_path = os.path.join(log_dir_path, self.test_name)
def parse_test_logs(self):
log_paths = self.download_logs()
if not log_paths:
print("No logs were downloaded, maybe no monkeys were ran?")
logging.error("No logs were downloaded. Maybe no monkeys were ran "
"or early exception prevented log download?")
return
TestLogsHandler.parse_logs(log_paths)
def download_logs(self):
self.try_create_log_dir_for_test()
downloader = LogsDownloader(self.island_client, self.log_dir_path)
downloader = MonkeyLogsDownloader(self.island_client, self.log_dir_path)
downloader.download_monkey_logs()
return downloader.monkey_log_paths
@ -30,21 +33,17 @@ class TestLogsHandler(object):
try:
os.mkdir(self.log_dir_path)
except Exception as e:
print("Can't create a dir for test logs: {}".format(e))
logging.error("Can't create a dir for test logs: {}".format(e))
@staticmethod
def get_log_dir_path():
return os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), LOG_DIR_NAME)
@staticmethod
def delete_log_folder_contents():
shutil.rmtree(TestLogsHandler.get_log_dir_path(), ignore_errors=True)
os.mkdir(TestLogsHandler.get_log_dir_path())
def delete_log_folder_contents(log_dir_path):
shutil.rmtree(log_dir_path, ignore_errors=True)
os.mkdir(log_dir_path)
@staticmethod
def parse_logs(log_paths):
for log_path in log_paths:
print("Info from log at {}".format(log_path))
log_parser = LogParser(log_path)
logging.info("Info from log at {}".format(log_path))
log_parser = MonkeyLogParser(log_path)
log_parser.print_errors()
log_parser.print_warnings()

View File

@ -1,2 +1,3 @@
pytest
unittest
pytest-random-order

View File

@ -1,3 +1,6 @@
import os
import logging
import pytest
from time import sleep
@ -13,6 +16,7 @@ MACHINE_BOOTUP_WAIT_SECONDS = 30
GCP_TEST_MACHINE_LIST = ['sshkeys-11', 'sshkeys-12', 'elastic-4', 'elastic-5', 'haddop-2-v3', 'hadoop-3', 'mssql-16',
'mimikatz-14', 'mimikatz-15', 'final-test-struts2-23', 'final-test-struts2-24',
'tunneling-9', 'tunneling-10', 'tunneling-11', 'weblogic-18', 'weblogic-19', 'shellshock-8']
LOG_DIR_PATH = "./logs"
@pytest.fixture(autouse=True, scope='session')
@ -29,8 +33,8 @@ def GCPHandler(request):
@pytest.fixture(autouse=True, scope='session')
def delete_logs():
print("Deleting monkey logs before new tests.")
TestLogsHandler.delete_log_folder_contents()
logging.info("Deleting monkey logs before new tests.")
TestLogsHandler.delete_log_folder_contents(TestMonkeyBlackbox.get_log_dir_path())
def wait_machine_bootup():
@ -52,11 +56,17 @@ class TestMonkeyBlackbox(object):
def run_basic_test(island_client, conf_filename, test_name, timeout_in_seconds=DEFAULT_TIMEOUT_SECONDS):
config_parser = IslandConfigParser(conf_filename)
analyzer = CommunicationAnalyzer(island_client, config_parser.get_ips_of_targets())
log_handler = TestLogsHandler(test_name, island_client, TestMonkeyBlackbox.get_log_dir_path())
BasicTest(test_name,
island_client,
config_parser,
[analyzer],
timeout_in_seconds).run()
timeout_in_seconds,
log_handler).run()
@staticmethod
def get_log_dir_path():
return os.path.abspath(LOG_DIR_PATH)
def test_server_online(self, island_client):
assert island_client.get_api_status() is not None

View File

@ -1,21 +1,25 @@
from time import sleep
import logging
from envs.monkey_zoo.blackbox.utils.test_timer import TestTimer
from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHandler
MAX_TIME_FOR_MONKEYS_TO_DIE = 5*60
WAIT_TIME_BETWEEN_REQUESTS = 10
TIME_FOR_MONKEY_PROCESS_TO_FINISH = 40
DELAY_BETWEEN_ANALYSIS = 3
class BasicTest(object):
def __init__(self, name, island_client, config_parser, analyzers, timeout):
def __init__(self, name, island_client, config_parser, analyzers, timeout, log_handler):
self.name = name
self.island_client = island_client
self.config_parser = config_parser
self.analyzers = analyzers
self.timeout = timeout
self.log_handler = log_handler
def run(self):
self.island_client.import_config(self.config_parser.config_raw)
@ -31,28 +35,28 @@ class BasicTest(object):
self.island_client.reset_env()
def print_test_starting_info(self):
print("Started {} test".format(self.name))
print("Machines participating in test:")
for target_ip in self.config_parser.get_ips_of_targets():
print(" "+target_ip)
print("")
logging.info("Started {} test".format(self.name))
logging.info("Machines participating in test:")
logging.info(" ".join(self.config_parser.get_ips_of_targets()))
logging.info("")
def test_until_timeout(self):
timer = TestTimer(self.timeout)
while not timer.timed_out():
while not timer.is_timed_out():
if self.all_analyzers_pass():
self.log_success(timer)
return
sleep(DELAY_BETWEEN_ANALYSIS)
self.log_failure(timer)
assert False
def log_success(self, timer):
print(self.get_analyzer_logs())
print("{} test passed, time taken: {:.1f} seconds.".format(self.name, timer.get_time_taken()))
logging.info(self.get_analyzer_logs())
logging.info("{} test passed, time taken: {:.1f} seconds.".format(self.name, timer.get_time_taken()))
def log_failure(self, timer):
print(self.get_analyzer_logs())
print("{} test failed because of timeout. Time taken: {:.1f} seconds.".format(self.name,
logging.info(self.get_analyzer_logs())
logging.error("{} test failed because of timeout. Time taken: {:.1f} seconds.".format(self.name,
timer.get_time_taken()))
def all_analyzers_pass(self):
@ -73,13 +77,18 @@ class BasicTest(object):
sleep(WAIT_TIME_BETWEEN_REQUESTS)
time_passed += WAIT_TIME_BETWEEN_REQUESTS
if time_passed > MAX_TIME_FOR_MONKEYS_TO_DIE:
print("Some monkeys didn't die after the test, passing")
logging.error("Some monkeys didn't die after the test, failing")
assert False
def parse_logs(self):
print("\nParsing test logs:")
TestLogsHandler(self.name, self.island_client).parse_test_logs()
logging.info("\nParsing test logs:")
self.log_handler.parse_test_logs()
@staticmethod
def wait_for_monkey_process_to_finish():
"""
There is a time period when monkey is set to dead, but the process is still closing.
If we try to launch monkey during that time window monkey will fail to start, that's
why test needs to wait a bit even after all monkeys are dead.
"""
sleep(TIME_FOR_MONKEY_PROCESS_TO_FINISH)

View File

@ -1,5 +1,7 @@
import subprocess
import logging
class GCPHandler(object):
@ -13,28 +15,28 @@ class GCPHandler(object):
try:
# pass the key file to gcp
subprocess.call(GCPHandler.get_auth_command(key_path), shell=True)
print("GCP Handler passed key")
logging.info("GCP Handler passed key")
# set project
subprocess.call(GCPHandler.get_set_project_command(project_id), shell=True)
print("GCP Handler set project")
print("GCP Handler initialized successfully")
logging.info("GCP Handler set project")
logging.info("GCP Handler initialized successfully")
except Exception as e:
print("GCP Handler failed to initialize: %s." % e)
logging.error("GCP Handler failed to initialize: %s." % e)
def start_machines(self, machine_list):
print("Setting up all GCP machines...")
logging.info("Setting up all GCP machines...")
try:
subprocess.call((GCPHandler.MACHINE_STARTING_COMMAND % (machine_list, self.zone)), shell=True)
print("GCP machines successfully started.")
logging.info("GCP machines successfully started.")
except Exception as e:
print("GCP Handler failed to start GCP machines: %s" % e)
logging.error("GCP Handler failed to start GCP machines: %s" % e)
def stop_machines(self, machine_list):
try:
subprocess.call((GCPHandler.MACHINE_STOPPING_COMMAND % (machine_list, self.zone)), shell=True)
print("GCP machines stopped successfully.")
logging.info("GCP machines stopped successfully.")
except Exception as e:
print("GCP Handler failed to stop network machines: %s" % e)
logging.error("GCP Handler failed to stop network machines: %s" % e)
@staticmethod
def get_auth_command(key_path):

View File

@ -6,7 +6,7 @@ class TestTimer(object):
self.timeout_time = TestTimer.get_timeout_time(timeout)
self.start_time = time()
def timed_out(self):
def is_timed_out(self):
return time() > self.timeout_time
def get_time_taken(self):

View File

@ -1 +1 @@
pyinstaller -F --log-level=DEBUG --clean --upx-dir=.\bin monkey.spec
C:\Programos\Python27\Scripts\pyinstaller.exe -F --log-level=DEBUG --clean --upx-dir=.\bin monkey.spec

View File

@ -90,6 +90,8 @@ class InfectionMonkey(object):
return
self.set_default_port()
fbts
# Create a dir for monkey files if there isn't one
utils.create_monkey_dir()

View File

@ -0,0 +1,4 @@
"""
This package contains resources used by blackbox tests
to analize test results, download logs and so on.
"""