Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create data directory on Island initialisation #1170

Merged
merged 39 commits into from
May 26, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
76d82ce
Create `data_dir` if no `--server-config` is passed during Monkey Isl…
shreyamalviya May 12, 2021
8246341
Create data dir if `--server-config` is passed, "data_dir" field exis…
shreyamalviya May 13, 2021
808e86d
Create data_dir before logger is set up
shreyamalviya May 14, 2021
9eedac4
Move `create_data_dir()` from monkey_island.py to monkey_island/cc/se…
shreyamalviya May 17, 2021
805e5e6
Restructure code for creating default data directory and server config
shreyamalviya May 17, 2021
d8927a5
Create constant SERVER_CONFIG_FILENAME
shreyamalviya May 17, 2021
af42c01
Replace missed out function name
shreyamalviya May 17, 2021
ff1e6bd
Remove logic for creating default server config in appimage script
shreyamalviya May 17, 2021
a1beee9
Change data_dir permissions on Windows
shreyamalviya May 18, 2021
3201672
Move `is_windows_os` to data_dir_generator.py and add user write
shreyamalviya May 19, 2021
e7a26aa
Rename `set_data_dir_security_to_read_by_owner()` to
shreyamalviya May 19, 2021
8c575b9
Import Windows specific modules only on Windows
shreyamalviya May 19, 2021
8ce506a
Refactored windows permission handling into a separate file
VakarisZ May 19, 2021
409a3c5
Refactored code duplication by adding a parameter for create_data_dir
VakarisZ May 19, 2021
2fb77fc
Get default directory depending on OS
shreyamalviya May 20, 2021
37b7815
Move `is_windows_os()` to separate file
shreyamalviya May 20, 2021
dc129c0
Fix unit test in test_consts.py
shreyamalviya May 20, 2021
4640a76
Update CHANGELOG (create data dir on island init)
shreyamalviya May 20, 2021
b4708fc
Import Windows specific modules only on Windows systems
shreyamalviya May 20, 2021
9705c73
Remove code in environment_config.py which was calling `write_default…
shreyamalviya May 21, 2021
23b0492
Remove unit test related to code removed in the previous commit
shreyamalviya May 21, 2021
43d919a
Create default server_config.json when running unit tests
shreyamalviya May 21, 2021
70b9a9f
Refactored config_loader.py and server_config_handler.py into a singl…
VakarisZ May 24, 2021
8afe937
Improved monkey_island.py setup readability by extracting workflows o…
VakarisZ May 24, 2021
5f88f6f
Moved a comment to a proper place
VakarisZ May 24, 2021
3098ac1
Fixed a failing environment setup in unit tests
VakarisZ May 24, 2021
d33c5d6
Use `os.path.expandvars()` on server config argument
shreyamalviya May 24, 2021
f500ca8
Merge pull request #1184 from guardicore/data-dir-on-island-refactor
shreyamalviya May 24, 2021
7d1c5dd
Merged develop into data-dir-on-island-init
VakarisZ May 24, 2021
0a7cf1d
Improved readability in the arg_parser.py by extracting defaults to t…
VakarisZ May 24, 2021
2b257f0
Fixed bugs merge bugs where structures are being accessed in an outda…
VakarisZ May 24, 2021
d273e85
Fixed bugs in argument parser passing default server config path even…
VakarisZ May 24, 2021
3a800d9
Fixed a bug where models were getting imported without mongodb connec…
VakarisZ May 24, 2021
9337c68
Removed the infrastructure from starting main.py - it can no longer b…
VakarisZ May 24, 2021
057a579
Rolled back the changes that made default server config on model pack…
VakarisZ May 25, 2021
5b7329b
Fixed stack-overflow that has been happening due to gevent unpatched …
VakarisZ May 25, 2021
17e994c
Fixed UT's for models by mocking an environment singleton with needed…
VakarisZ May 25, 2021
a89a62c
Remove unneeded "noqa" statements
shreyamalviya May 26, 2021
baee74b
Add exception messages during data directory creation
shreyamalviya May 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Authentication mechanism to use bcrypt on server side. #1139
- `server_config.json` puts environment config options in a separate section
named "environment". #1161
- BlackBox tests can now register if they are ran on a fresh installation. #1180
- Improved the structure of unit tests by scoping fixtures only to relevant modules
instead of having a one huge fixture file, improved and renamed the directory
structure of unit tests and unit test infrastructure. #1178
Expand Down
3 changes: 3 additions & 0 deletions appimage/server_config.json.standard
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
"environment": {
"server_config": "password",
"deployment": "standard"
},
"mongodb": {
"start_mongodb": true
}
}
30 changes: 22 additions & 8 deletions envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
LOGGER = logging.getLogger(__name__)


class AuthenticationFailedError(Exception):
pass


# noinspection PyArgumentList
class MonkeyIslandRequests(object):
def __init__(self, server_address):
Expand Down Expand Up @@ -43,6 +47,9 @@ def send_request_by_method(self, url, method=SupportedRequestMethod.GET, data=No
def try_get_jwt_from_server(self):
try:
return self.get_jwt_from_server()
except AuthenticationFailedError:
self.try_set_island_to_no_password()
return self.get_jwt_from_server()
except requests.ConnectionError as err:
LOGGER.error(
"Unable to connect to island, aborting! Error information: {}. Server: {}".format(
Expand All @@ -51,6 +58,21 @@ def try_get_jwt_from_server(self):
)
assert False

def get_jwt_from_server(self):
resp = requests.post( # noqa: DUO123
self.addr + "api/auth",
json={"username": NO_AUTH_CREDS, "password": NO_AUTH_CREDS},
verify=False,
)
if resp.status_code == 401:
raise AuthenticationFailedError
return resp.json()["access_token"]

def try_set_island_to_no_password(self):
requests.patch( # noqa: DUO123
self.addr + "api/environment", json={"server_config": "standard"}, verify=False
)

class _Decorators:
@classmethod
def refresh_jwt_token(cls, request_function):
Expand All @@ -62,14 +84,6 @@ def request_function_wrapper(self, *args, **kwargs):

return request_function_wrapper

def get_jwt_from_server(self):
resp = requests.post( # noqa: DUO123
self.addr + "api/auth",
json={"username": NO_AUTH_CREDS, "password": NO_AUTH_CREDS},
verify=False,
)
return resp.json()["access_token"]

@_Decorators.refresh_jwt_token
def get(self, url, data=None):
return requests.get( # noqa: DUO123
Expand Down
14 changes: 9 additions & 5 deletions envs/monkey_zoo/blackbox/test_blackbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,15 @@ def wait_machine_bootup():

@pytest.fixture(scope="class")
def island_client(island, quick_performance_tests):
island_client_object = MonkeyIslandClient(island)
client_established = False
try:
island_client_object = MonkeyIslandClient(island)
client_established = island_client_object.get_api_status()
except Exception:
logging.exception("Got an exception while trying to establish connection to the Island.")
finally:
if not client_established:
pytest.exit("BB tests couldn't establish communication to the island.")
if not quick_performance_tests:
island_client_object.reset_env()
yield island_client_object
Expand Down Expand Up @@ -158,10 +166,6 @@ def run_performance_test(
def get_log_dir_path():
return os.path.abspath(LOG_DIR_PATH)

def test_server_online(self, island_client):
if not island_client.get_api_status():
pytest.exit("BB tests couldn't reach the Island server, quiting.")

def test_ssh_exploiter(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(island_client, Ssh, "SSH_exploiter_and_keys")

Expand Down
4 changes: 2 additions & 2 deletions monkey/monkey_island.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
else:
config, server_config_path = setup_default_config()

setup_logging(config["data_dir"], config["log_level"])
setup_logging(config.data_dir, config.log_level)

except OSError as ex:
print(f"Error opening server config file: {ex}")
Expand All @@ -32,4 +32,4 @@

from monkey_island.cc.main import main # noqa: E402

main(config["data_dir"], island_args.setup_only, server_config_path)
main(island_args.setup_only, island_args.server_config_path, config)
22 changes: 15 additions & 7 deletions monkey/monkey_island/cc/arg_parser.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
from dataclasses import dataclass
from monkey_island.cc.server_utils.consts import (
DEFAULT_SERVER_CONFIG_PATH,
DEFAULT_SHOULD_SETUP_ONLY,
)


@dataclass
class IslandArgs:
setup_only: bool
server_config: str
class IslandCmdArgs:
setup_only: bool = DEFAULT_SHOULD_SETUP_ONLY
server_config_path: str = DEFAULT_SERVER_CONFIG_PATH

def __init__(self, setup_only: None, server_config_path: None):
if setup_only:
self.setup_only = setup_only
if server_config_path:
self.server_config_path = server_config_path

def parse_cli_args() -> IslandArgs:

def parse_cli_args() -> IslandCmdArgs:
import argparse

parser = argparse.ArgumentParser(
Expand All @@ -27,4 +35,4 @@ def parse_cli_args() -> IslandArgs:
)
args = parser.parse_args()

return IslandArgs(args.setup_only, args.server_config)
return IslandCmdArgs(args.setup_only, args.server_config)
18 changes: 3 additions & 15 deletions monkey/monkey_island/cc/environment/server_config_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
from pathlib import Path

from monkey_island.cc.server_utils.consts import (
DEFAULT_DATA_DIR,
DEFAULT_DEVELOP_SERVER_CONFIG_PATH,
DEFAULT_LOG_LEVEL,
DEFAULT_SERVER_CONFIG_PATH,
)
from monkey_island.setup.island_config_options import IslandConfigOptions


def create_default_server_config_file() -> None:
Expand All @@ -20,20 +19,9 @@ def write_default_server_config_to_file(path: str) -> None:
Path(path).write_text(default_config)


def load_server_config_from_file(server_config_path):
def load_server_config_from_file(server_config_path) -> IslandConfigOptions:
with open(server_config_path, "r") as f:
config_content = f.read()
config = json.loads(config_content)
add_default_values_to_config(config)

return config


def add_default_values_to_config(config):
config["data_dir"] = os.path.abspath(
os.path.expanduser(os.path.expandvars(config.get("data_dir", DEFAULT_DATA_DIR)))
)

config.setdefault("log_level", DEFAULT_LOG_LEVEL)

return config
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we deliberately removing this or is this part of the rebase mess?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit also removes monkey/tests/unit_tests/monkey_island/cc/environment/test_server_config_handler.py as part of the rebase. Depending on this comment, we need to re-add/modify this file too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deliberately. Config is now built in island_config_options.py.

return IslandConfigOptions(config)
26 changes: 12 additions & 14 deletions monkey/monkey_island/cc/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# "monkey_island." work.
from gevent.pywsgi import WSGIServer

from monkey_island.setup.island_config_options import IslandConfigOptions

MONKEY_ISLAND_DIR_BASE_PATH = str(Path(__file__).parent.parent)
if str(MONKEY_ISLAND_DIR_BASE_PATH) not in sys.path:
sys.path.insert(0, MONKEY_ISLAND_DIR_BASE_PATH)
Expand All @@ -22,40 +24,36 @@
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.consts import DEFAULT_SERVER_CONFIG_PATH # 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 import setup # noqa: E402

MINIMUM_MONGO_DB_VERSION_REQUIRED = "4.2.0"


def main(
data_dir,
should_setup_only=False,
server_config_filename=DEFAULT_SERVER_CONFIG_PATH,
):
logger.info("Starting bootloader server")
def main(setup_only: bool, server_config_path: str, config_options: IslandConfigOptions):

env_singleton.initialize_from_file(server_config_filename)
initialize_encryptor(data_dir)
initialize_services(data_dir)
env_singleton.initialize_from_file(server_config_path)
initialize_encryptor(config_options.data_dir)
initialize_services(config_options.data_dir)

mongo_url = os.environ.get("MONGO_URL", env_singleton.env.get_mongo_url())
bootloader_server_thread = Thread(
target=BootloaderHttpServer(mongo_url).serve_forever, daemon=True
)

bootloader_server_thread.start()
start_island_server(should_setup_only)
start_island_server(setup_only, config_options)
bootloader_server_thread.join()


def start_island_server(should_setup_only):
def start_island_server(should_setup_only, config_options: IslandConfigOptions):
if config_options.start_mongodb:
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)
Expand All @@ -66,7 +64,7 @@ def start_island_server(should_setup_only):
crt_path = str(Path(MONKEY_ISLAND_ABS_PATH, "cc", "server.crt"))
key_path = str(Path(MONKEY_ISLAND_ABS_PATH, "cc", "server.key"))

setup()
init_collections()

if should_setup_only:
logger.warning("Setup only flag passed. Exiting.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
logger = logging.getLogger(__name__)


def setup():
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()

Expand Down
3 changes: 3 additions & 0 deletions monkey/monkey_island/cc/server_config.json.develop
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
"environment": {
"server_config": "password",
"deployment": "develop"
},
"mongodb": {
"start_mongodb": true
}
}
6 changes: 4 additions & 2 deletions monkey/monkey_island/cc/server_utils/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ def get_default_data_dir() -> str:

SERVER_CONFIG_FILENAME = "server_config.json"

DEFAULT_LOG_LEVEL = "INFO"

MONKEY_ISLAND_ABS_PATH = os.path.join(os.getcwd(), "monkey_island")

DEFAULT_DATA_DIR = os.path.expandvars(get_default_data_dir())
Expand All @@ -29,3 +27,7 @@ def get_default_data_dir() -> str:
DEFAULT_DEVELOP_SERVER_CONFIG_PATH = os.path.join(
MONKEY_ISLAND_ABS_PATH, "cc", f"{SERVER_CONFIG_FILENAME}.develop"
)

DEFAULT_LOG_LEVEL = "INFO"
DEFAULT_START_MONGO_DB = True
DEFAULT_SHOULD_SETUP_ONLY = False
19 changes: 19 additions & 0 deletions monkey/monkey_island/config_file_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import json
from os.path import isfile

from monkey_island.cc.server_utils.consts import DEFAULT_SERVER_CONFIG_PATH
from monkey_island.setup.island_config_options import IslandConfigOptions


def load_island_config_from_file(server_config_path: str) -> IslandConfigOptions:
config_contents = read_config_file(server_config_path)
return IslandConfigOptions(config_contents)


def read_config_file(server_config_path: str) -> dict:
if not server_config_path or not isfile(server_config_path):
server_config_path = DEFAULT_SERVER_CONFIG_PATH
with open(server_config_path, "r") as f:
config_content = f.read()
config = json.loads(config_content)
return config
Empty file.
6 changes: 4 additions & 2 deletions monkey/monkey_island/setup/config_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
from monkey_island.cc.environment import server_config_handler
from monkey_island.cc.environment.data_dir_generator import create_data_dir # noqa: E402
from monkey_island.cc.server_utils.consts import DEFAULT_DATA_DIR, DEFAULT_SERVER_CONFIG_PATH
from monkey_island.setup.island_config_options import IslandConfigOptions


def setup_config_by_cmd_arg(server_config_path) -> Tuple[dict, str]:
def setup_config_by_cmd_arg(server_config_path) -> Tuple[IslandConfigOptions, str]:
server_config_path = os.path.expandvars(os.path.expanduser(server_config_path))
config = server_config_handler.load_server_config_from_file(server_config_path)

create_data_dir(config["data_dir"], create_parent_dirs=True)
return config, server_config_path


def setup_default_config() -> Tuple[dict, str]:
def setup_default_config() -> Tuple[IslandConfigOptions, str]:
server_config_path = DEFAULT_SERVER_CONFIG_PATH
create_data_dir(DEFAULT_DATA_DIR, create_parent_dirs=False)
server_config_handler.create_default_server_config_file()
Expand Down
18 changes: 18 additions & 0 deletions monkey/monkey_island/setup/island_config_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from __future__ import annotations

from monkey_island.cc.server_utils.consts import (
DEFAULT_DATA_DIR,
DEFAULT_LOG_LEVEL,
DEFAULT_START_MONGO_DB,
)


class IslandConfigOptions:
def __init__(self, config_contents: dict):
self.data_dir = config_contents.get("data_dir", DEFAULT_DATA_DIR)

self.log_level = config_contents.get("log_level", DEFAULT_LOG_LEVEL)

self.start_mongodb = config_contents.get(
"mongodb", {"start_mongodb": DEFAULT_START_MONGO_DB}
).get("start_mongodb", DEFAULT_START_MONGO_DB)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
Original file line number Diff line number Diff line change
@@ -1,27 +0,0 @@
import os

from monkey_island.cc.environment import server_config_handler
from monkey_island.cc.server_utils.consts import DEFAULT_DATA_DIR


def test_load_server_config_from_file(test_server_config, mock_home_env):
config = server_config_handler.load_server_config_from_file(test_server_config)

assert config["data_dir"] == os.path.join(mock_home_env, ".monkey_island")
assert config["log_level"] == "NOTICE"


def test_default_log_level():
test_config = {}
config = server_config_handler.add_default_values_to_config(test_config)

assert "log_level" in config
assert config["log_level"] == "INFO"


def test_default_data_dir(mock_home_env):
test_config = {}
config = server_config_handler.add_default_values_to_config(test_config)

assert "data_dir" in config
assert config["data_dir"] == DEFAULT_DATA_DIR
9 changes: 7 additions & 2 deletions monkey/tests/unit_tests/monkey_island/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,10 @@ def server_configs_dir(data_for_tests_dir):


@pytest.fixture(scope="module")
def test_server_config(server_configs_dir):
return os.path.join(server_configs_dir, "test_server_config.json")
def server_config_init_only(server_configs_dir):
return os.path.join(server_configs_dir, "server_config_init_only.json")


@pytest.fixture(scope="module")
def server_config_empty(server_configs_dir):
return os.path.join(server_configs_dir, "server_config_empty.json")
Loading
You are viewing a condensed version of this merge commit. You can view the full changes here.