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 22 commits
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 @@ -33,6 +33,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- 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
- Create/check data directory on Island init. #1170

### Removed
- Relevant dead code as reported by Vulture. #1149
Expand Down
8 changes: 0 additions & 8 deletions appimage/run_appimage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,12 @@
PYTHON_CMD="$APPDIR"/opt/python3.7/bin/python3.7
DOT_MONKEY="$HOME"/.monkey_island/

configure_default_server() {
if [ ! -f "$DOT_MONKEY"/server_config.json ]; then
cp "$APPDIR"/usr/src/monkey_island/cc/server_config.json.standard "$DOT_MONKEY"/server_config.json
fi
}

# shellcheck disable=SC2174
mkdir --mode=0700 --parents "$DOT_MONKEY"

DB_DIR="$DOT_MONKEY"/db
mkdir --parents "$DB_DIR"

configure_default_server

cd "$APPDIR"/usr/src || exit 1
./monkey_island/bin/mongodb/bin/mongod --dbpath "$DB_DIR" &
${PYTHON_CMD} ./monkey_island.py --server-config "$DOT_MONKEY"/server_config.json
11 changes: 9 additions & 2 deletions monkey/monkey_island.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import json # noqa: E402
import os # noqa: E402

import monkey_island.cc.environment.server_config_generator as server_config_generator # noqa: E402
from monkey_island import config_loader # noqa: E402
from monkey_island.cc.environment.data_dir_generator import create_data_dir # noqa: E402
from monkey_island.cc.server_utils.island_logger import setup_logging # noqa: E402

if "__main__" == __name__:
Expand All @@ -16,10 +18,15 @@
# This is here in order to catch EVERYTHING, some functions are being called on
# imports, so the log init needs to be first.
try:
server_config_path = os.path.expanduser(island_args.server_config)
if island_args.server_config:
server_config_path = os.path.expanduser(island_args.server_config)
else:
server_config_path = server_config_generator.create_default_server_config_file()

config = config_loader.load_server_config_from_file(server_config_path)

create_data_dir(config["data_dir"], True)
Copy link
Contributor

Choose a reason for hiding this comment

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

This line only makes sense if island_args.server_config, because otherwise you create the dir in server_config_generator.create_default_server_config_file()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in #1184


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

except OSError as ex:
Expand All @@ -32,4 +39,4 @@

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

main(config["data_dir"], island_args.setup_only, island_args.server_config)
main(config["data_dir"], island_args.setup_only, server_config_path)
7 changes: 1 addition & 6 deletions monkey/monkey_island/cc/arg_parser.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from dataclasses import dataclass

from monkey_island.cc.server_utils.consts import DEFAULT_SERVER_CONFIG_PATH


@dataclass
class IslandArgs:
Expand All @@ -25,10 +23,7 @@ def parse_cli_args() -> IslandArgs:
"compiling/packaging Islands.",
)
parser.add_argument(
"--server-config",
action="store",
help="The path to the server configuration file.",
default=DEFAULT_SERVER_CONFIG_PATH,
"--server-config", action="store", help="The path to the server configuration file."
)
args = parser.parse_args()

Expand Down
14 changes: 14 additions & 0 deletions monkey/monkey_island/cc/environment/data_dir_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import os

from monkey_island.cc.environment.utils import is_windows_os
from monkey_island.cc.environment.windows_permissions import set_full_folder_access


def create_data_dir(data_dir: str, create_parent_dirs: bool) -> None:
if not os.path.isdir(data_dir):
if create_parent_dirs:
os.makedirs(data_dir, mode=0o700)
else:
os.mkdir(data_dir, mode=0o700)
if is_windows_os(): # `mode=0o700` doesn't work on Windows
set_full_folder_access(folder_path=data_dir)
4 changes: 0 additions & 4 deletions monkey/monkey_island/cc/environment/environment_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

import json
import os
from pathlib import Path
from typing import Dict, List

import monkey_island.cc.environment.server_config_generator as server_config_generator
from monkey_island.cc.environment.user_creds import UserCreds
from monkey_island.cc.resources.auth.auth_user import User
from monkey_island.cc.resources.auth.user_store import UserStore
Expand All @@ -24,8 +22,6 @@ def __init__(self, file_path):
def _load_from_file(self, file_path):
file_path = os.path.expanduser(file_path)

if not Path(file_path).is_file():
server_config_generator.create_default_config_file(file_path)
with open(file_path, "r") as f:
config_content = f.read()

Expand Down
17 changes: 15 additions & 2 deletions monkey/monkey_island/cc/environment/server_config_generator.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
import os
from pathlib import Path

from monkey_island.cc.server_utils.consts import DEFAULT_DEVELOP_SERVER_CONFIG_PATH
from monkey_island.cc.environment.data_dir_generator import create_data_dir
from monkey_island.cc.server_utils.consts import (
DEFAULT_DATA_DIR,
DEFAULT_DEVELOP_SERVER_CONFIG_PATH,
DEFAULT_SERVER_CONFIG_PATH,
)


def create_default_config_file(path):
def create_default_server_config_file() -> str:
if not os.path.isfile(DEFAULT_SERVER_CONFIG_PATH):
create_data_dir(DEFAULT_DATA_DIR, False)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm sorry, but I still don't understand what's wrong with using os.makedirs here. The explanation of $HOME might not be resolved doesn't make sense. It's the responsibility of DEFAULT_DATA_DIR to point to a resolvable path. And if DEFAULT_DATA_DIR can't be resolved, how will not making parent directories help us?

Copy link
Contributor Author

@shreyamalviya shreyamalviya May 24, 2021

Choose a reason for hiding this comment

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

For example, on Linux, if the $HOME env variable doesn't exist, the value of DEFAULT_DATA_DIR will remain "$HOME/.monkey_island" (we're using os.path.expandvars() to resolve the path but in case nothing is resolved, it will simply return the same string, it won't throw any kind of error). Using os.makedirs will create a directory named "$HOME" and the ".monkey_island" directory under that. Creating a directory named "$HOME" is not the expected behaviour. If the $HOME variable can't be resolved, we expect the user to specify the path for the data directory.

Copy link
Contributor

Choose a reason for hiding this comment

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

If the $HOME variable can't be resolved, we expect the user to specify the path for the data directory.

How is this expressed in the code and where? Users and developers are expected to get os.mkdir error and know that os.mkdir failed because it doesn't create parent folders, so it means that the parent folder of data_dir is not present?
What prevents the user from making the same mistake via cmd arguments?
Wouldn't it be better to

if not os.path.isdir(data_dir_parent):
    logger.error(f"Can't create a data directory because path {data_dir_parent} doesn't exist. Specify a reachable directory with {data_dir_flag}")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See baee74b7

write_default_server_config_to_file(DEFAULT_SERVER_CONFIG_PATH)
return DEFAULT_SERVER_CONFIG_PATH


def write_default_server_config_to_file(path: str) -> None:
default_config = Path(DEFAULT_DEVELOP_SERVER_CONFIG_PATH).read_text()
Path(path).write_text(default_config)
5 changes: 5 additions & 0 deletions monkey/monkey_island/cc/environment/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import sys


def is_windows_os() -> bool:
return sys.platform.startswith("win")
34 changes: 34 additions & 0 deletions monkey/monkey_island/cc/environment/windows_permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from monkey_island.cc.environment.utils import is_windows_os

if is_windows_os():
import ntsecuritycon
import win32api
import win32con
import win32security


def set_full_folder_access(folder_path: str) -> None:
user = get_user_pySID_object()

security_descriptor = win32security.GetFileSecurity(
folder_path, win32security.DACL_SECURITY_INFORMATION
)
dacl = win32security.ACL()
dacl.AddAccessAllowedAce(
win32security.ACL_REVISION,
ntsecuritycon.FILE_ALL_ACCESS,
user,
)
security_descriptor.SetSecurityDescriptorDacl(1, dacl, 0)
win32security.SetFileSecurity(
folder_path, win32security.DACL_SECURITY_INFORMATION, security_descriptor
)


def get_user_pySID_object():
# get current user's name
username = win32api.GetUserNameEx(win32con.NameSamCompatible)
# pySID object for the current user
user, _, _ = win32security.LookupAccountName("", username)

return user
22 changes: 15 additions & 7 deletions monkey/monkey_island/cc/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
from mongoengine import connect

import monkey_island.cc.environment.environment_singleton as env_singleton
# Needed so that a server_config.json file exists at the default path,
# otherwise, unit tests will error while importing `env_singleton` below.
from monkey_island.cc.environment.server_config_generator import ( # noqa: E402
create_default_server_config_file,
)

create_default_server_config_file()

import monkey_island.cc.environment.environment_singleton as env_singleton # noqa: E402

from .command_control_channel import CommandControlChannel # noqa: F401
from .command_control_channel import CommandControlChannel # noqa: F401, E402

# Order of importing matters here, for registering the embedded and referenced documents before
# using them.
from .config import Config # noqa: F401
from .creds import Creds # noqa: F401
from .monkey import Monkey # noqa: F401
from .monkey_ttl import MonkeyTtl # noqa: F401
from .pba_results import PbaResults # noqa: F401
from .config import Config # noqa: F401, E402
from .creds import Creds # noqa: F401, E402
from .monkey import Monkey # noqa: F401, E402
from .monkey_ttl import MonkeyTtl # noqa: F401, E402
from .pba_results import PbaResults # noqa: F401, E402

connect(
db=env_singleton.env.mongo_db_name,
Expand Down
23 changes: 19 additions & 4 deletions monkey/monkey_island/cc/server_utils/consts.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import os

from monkey_island.cc.environment.utils import is_windows_os

__author__ = "itay.mizeretz"


def get_default_data_dir() -> str:
if is_windows_os():
return r"%AppData%\monkey_island"
else:
return r"$HOME/.monkey_island"


SERVER_CONFIG_FILENAME = "server_config.json"

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

DEFAULT_DATA_DIR = os.path.expandvars(get_default_data_dir())

DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS = 60 * 5

DEFAULT_SERVER_CONFIG_PATH = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc", "server_config.json")
DEFAULT_SERVER_CONFIG_PATH = os.path.expandvars(
os.path.join(DEFAULT_DATA_DIR, SERVER_CONFIG_FILENAME)
)

DEFAULT_DEVELOP_SERVER_CONFIG_PATH = os.path.join(
MONKEY_ISLAND_ABS_PATH, "cc", "server_config.json.develop"
MONKEY_ISLAND_ABS_PATH, "cc", f"{SERVER_CONFIG_FILENAME}.develop"
)

DEFAULT_DATA_DIR = os.path.join(MONKEY_ISLAND_ABS_PATH, "cc")
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,3 @@ def test_get_users(standard_with_credentials):
assert users[0].id == 1
assert users[0].username == "test"
assert users[0].secret == "abcdef"


def test_generate_default_file(config_file):
environment_config = EnvironmentConfig(config_file)

assert os.path.isfile(config_file)

assert environment_config.server_config == "password"
assert environment_config.deployment == "develop"
assert environment_config.user_creds.username == ""
assert environment_config.user_creds.password_hash == ""
assert environment_config.aws is None
4 changes: 2 additions & 2 deletions monkey/tests/unit_tests/monkey_island/cc/test_consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

def test_default_server_config_file_path():
if platform.system() == "Windows":
server_file_path = consts.MONKEY_ISLAND_ABS_PATH + r"\cc\server_config.json"
server_file_path = f"{consts.DEFAULT_DATA_DIR}\\{consts.SERVER_CONFIG_FILENAME}"
else:
server_file_path = consts.MONKEY_ISLAND_ABS_PATH + "/cc/server_config.json"
server_file_path = f"{consts.DEFAULT_DATA_DIR}/{consts.SERVER_CONFIG_FILENAME}"

assert consts.DEFAULT_SERVER_CONFIG_PATH == server_file_path