diff --git a/monkey/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py index df015863b..820630210 100644 --- a/monkey/monkey_island/cc/main.py +++ b/monkey/monkey_island/cc/main.py @@ -24,13 +24,16 @@ from common.version import get_version # noqa: E402 from monkey_island.cc.app import init_app # noqa: E402 from monkey_island.cc.database import get_db_version # noqa: E402 from monkey_island.cc.database import is_db_server_up # noqa: E402 -from monkey_island.cc.mongo_setup import init_collections, launch_mongodb # noqa: E402 from monkey_island.cc.resources.monkey_download import MonkeyDownload # noqa: E402 from monkey_island.cc.server_utils.bootloader_server import BootloaderHttpServer # noqa: E402 from monkey_island.cc.server_utils.encryptor import initialize_encryptor # noqa: E402 from monkey_island.cc.services.initialize import initialize_services # noqa: E402 from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list # noqa: E402 from monkey_island.cc.services.utils.network_utils import local_ip_addresses # noqa: E402 +from monkey_island.cc.setup.mongo_process_runner import ( # noqa: E402 + MongoDbRunner, + init_collections, +) MINIMUM_MONGO_DB_VERSION_REQUIRED = "4.2.0" @@ -51,7 +54,9 @@ def main(setup_only: bool, config_options: IslandConfigOptions): def start_island_server(should_setup_only, config_options: IslandConfigOptions): if config_options.start_mongodb: - launch_mongodb() + MongoDbRunner( + db_dir_parent_path=config_options.data_dir, logging_dir_path=config_options.data_dir + ).launch_mongodb() mongo_url = os.environ.get("MONGO_URL", env_singleton.env.get_mongo_url()) wait_for_mongo_db_server(mongo_url) assert_mongo_db_version(mongo_url) diff --git a/monkey/monkey_island/cc/server_utils/consts.py b/monkey/monkey_island/cc/server_utils/consts.py index bc99b4394..9bc1e7059 100644 --- a/monkey/monkey_island/cc/server_utils/consts.py +++ b/monkey/monkey_island/cc/server_utils/consts.py @@ -23,6 +23,10 @@ DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS = 60 * 5 DEFAULT_SERVER_CONFIG_PATH = os.path.expandvars( os.path.join(DEFAULT_DATA_DIR, SERVER_CONFIG_FILENAME) ) +_MONGO_EXECUTABLE_PATH = os.path.join(MONKEY_ISLAND_ABS_PATH, "bin", "mongodb") +MONGO_EXECUTABLE_PATH_WIN = os.path.join(_MONGO_EXECUTABLE_PATH, "mongod.exe") +MONGO_EXECUTABLE_PATH_LINUX = os.path.join(_MONGO_EXECUTABLE_PATH, "bin", "mongod") + DEFAULT_DEVELOP_SERVER_CONFIG_PATH = os.path.join( MONKEY_ISLAND_ABS_PATH, "cc", f"{SERVER_CONFIG_FILENAME}.develop" diff --git a/monkey/monkey_island/cc/setup/__init__.py b/monkey/monkey_island/cc/setup/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/mongo_setup.py b/monkey/monkey_island/cc/setup/database_initializer.py similarity index 88% rename from monkey/monkey_island/cc/mongo_setup.py rename to monkey/monkey_island/cc/setup/database_initializer.py index 74cb29fc2..34914c7ce 100644 --- a/monkey/monkey_island/cc/mongo_setup.py +++ b/monkey/monkey_island/cc/setup/database_initializer.py @@ -9,17 +9,12 @@ from monkey_island.cc.services.attack.mitre_api_interface import MitreApiInterfa logger = logging.getLogger(__name__) -def launch_mongodb(): - # TODO: Implement the launch of mongodb process - pass - - def init_collections(): logger.info("Setting up the Monkey Island, this might take a while...") - try_store_mitigations_on_mongo() + _try_store_mitigations_on_mongo() -def try_store_mitigations_on_mongo(): +def _try_store_mitigations_on_mongo(): mitigation_collection_name = AttackMitigations.COLLECTION_NAME try: mongo.db.validate_collection(mitigation_collection_name) @@ -33,10 +28,10 @@ def try_store_mitigations_on_mongo(): except errors.CollectionInvalid: pass finally: - store_mitigations_on_mongo() + _store_mitigations_on_mongo() -def store_mitigations_on_mongo(): +def _store_mitigations_on_mongo(): stix2_mitigations = MitreApiInterface.get_all_mitigations() mongo_mitigations = AttackMitigations.dict_from_stix2_attack_patterns( MitreApiInterface.get_all_attack_techniques() diff --git a/monkey/monkey_island/cc/setup/mongo_process_runner.py b/monkey/monkey_island/cc/setup/mongo_process_runner.py new file mode 100644 index 000000000..bc429479e --- /dev/null +++ b/monkey/monkey_island/cc/setup/mongo_process_runner.py @@ -0,0 +1,63 @@ +import logging +import os +import subprocess +from typing import List + +from monkey_island.cc.server_utils.common_methods import WINDOWS, get_runtime_os +from monkey_island.cc.server_utils.consts import ( + MONGO_EXECUTABLE_PATH_LINUX, + MONGO_EXECUTABLE_PATH_WIN, +) + +logger = logging.getLogger(__name__) + +DB_DIR_NAME = "db" +DB_DIR_PARAM = "--dbpath" +MONGO_LOG_FILENAME = "mongo_log.txt" + + +class MongoDbRunner: + def __init__(self, db_dir_parent_path: str, logging_dir_path: str): + """ + @param db_dir_parent_path: Path where a folder for database contents will be created + @param logging_dir_path: Path to a folder where mongodb logs will be created + """ + self.db_dir_parent_path = db_dir_parent_path + self.logging_dir_path = logging_dir_path + + def launch_mongodb(self): + db_path = self._create_db_dir() + self._start_mongodb_process(db_path) + + def _create_db_dir(self) -> str: + db_path = os.path.join(self.db_dir_parent_path, DB_DIR_NAME) + logger.info(f"Database content directory: {db_path}.") + if not os.path.isdir(db_path): + logger.info("Database content directory not found, creating one.") + os.mkdir(os.path.join(self.db_dir_parent_path, DB_DIR_NAME)) + return db_path + + def _start_mongodb_process(self, db_dir_path: str): + logger.info("Starting MongoDb process.") + mongo_exec = MongoDbRunner._get_path_of_mongo_exec() + + mongo_run_cmd = MongoDbRunner._build_mongo_launch_cmd(mongo_exec, db_dir_path) + logger.info(f"Mongodb will be launched with command: f{' '.join(mongo_run_cmd)}.") + + mongo_log_path = os.path.join(self.logging_dir_path, MONGO_LOG_FILENAME) + logger.info(f"Mongodb log will be available at f{mongo_log_path}.") + + with open(mongo_log_path, "w") as log: + subprocess.Popen(mongo_run_cmd, stderr=subprocess.STDOUT, stdout=log) + logger.info("MongoDb launched successfully!") + + @staticmethod + def _get_path_of_mongo_exec(): + if get_runtime_os() == WINDOWS: + return MONGO_EXECUTABLE_PATH_WIN + else: + return MONGO_EXECUTABLE_PATH_LINUX + + @staticmethod + def _build_mongo_launch_cmd(exec_path: str, db_path: str) -> List[str]: + return [exec_path, DB_DIR_PARAM, db_path] diff --git a/monkey/tests/unit_tests/monkey_island/cc/setup/test_mongo_setup.py b/monkey/tests/unit_tests/monkey_island/cc/setup/test_mongo_setup.py new file mode 100644 index 000000000..388e1a6c0 --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/setup/test_mongo_setup.py @@ -0,0 +1,24 @@ +import os + +from monkey_island.cc.setup.mongo_setup import _create_db_dir + + +def test_create_db_dir(monkeypatch, tmpdir): + test_dir_name = "test_dir" + monkeypatch.setattr("monkey_island.cc.setup.mongo_setup.DB_DIR_NAME", test_dir_name) + expected_path = os.path.join(tmpdir, test_dir_name) + + db_path = _create_db_dir(tmpdir) + assert os.path.isdir(expected_path) + assert db_path == expected_path + + +def test_create_db_dir_already_created(monkeypatch, tmpdir): + test_dir_name = "test_dir" + monkeypatch.setattr("monkey_island.cc.setup.mongo_setup.DB_DIR_NAME", test_dir_name) + expected_path = os.path.join(tmpdir, test_dir_name) + os.mkdir(expected_path) + + db_path = _create_db_dir(tmpdir) + assert os.path.isdir(expected_path) + assert db_path == expected_path