From ce5e4157883b78fb8129ff07dcc7e62922a935cd Mon Sep 17 00:00:00 2001 From: VakarisZ Date: Tue, 16 Jun 2020 13:45:10 +0300 Subject: [PATCH] Finished "no password" environment option --- monkey/monkey_island/cc/app.py | 6 ++-- .../monkey_island/cc/environment/__init__.py | 16 +++++++---- monkey/monkey_island/cc/environment/aws.py | 4 +-- .../cc/environment/environment_singleton.py | 23 +++++++++++++-- .../monkey_island/cc/environment/test_aws.py | 3 +- .../monkey_island/cc/environment/testing.py | 6 ++-- monkey/monkey_island/cc/main.py | 12 ++++---- monkey/monkey_island/cc/models/__init__.py | 6 ++-- .../monkey_island/cc/resources/auth/auth.py | 10 +++---- .../monkey_island/cc/resources/environment.py | 16 +++++++++++ .../monkey_island/cc/resources/local_run.py | 4 +-- .../cc/resources/registration.py | 6 ++-- monkey/monkey_island/cc/server_config.json | 4 +++ monkey/monkey_island/cc/services/config.py | 6 ++-- .../cc/services/reporting/exporter_init.py | 4 +-- .../cc/services/version_update.py | 6 ++-- .../cc/testing/IslandTestCase.py | 4 +-- .../ui/src/components/pages/RegisterPage.js | 28 +++++++++++++++++++ .../cc/ui/src/styles/RegisterPage.scss | 10 +++++++ 19 files changed, 129 insertions(+), 45 deletions(-) create mode 100644 monkey/monkey_island/cc/resources/environment.py create mode 100644 monkey/monkey_island/cc/server_config.json diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index e480d2cd5..3ba07af8f 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -7,9 +7,10 @@ from werkzeug.exceptions import NotFound from monkey_island.cc.resources.auth.auth import init_jwt from monkey_island.cc.database import mongo, database -from monkey_island.cc.environment.environment_singleton import env +import monkey_island.cc.environment.environment_singleton as env_singleton from monkey_island.cc.resources.client_run import ClientRun from monkey_island.cc.resources.edge import Edge +from monkey_island.cc.resources.environment import Environment from monkey_island.cc.resources.local_run import LocalRun from monkey_island.cc.resources.log import Log from monkey_island.cc.resources.island_logs import IslandLog @@ -70,7 +71,7 @@ def init_app_config(app, mongo_url): app.config['MONGO_URI'] = mongo_url app.config['SECRET_KEY'] = str(uuid.getnode()) app.config['JWT_AUTH_URL_RULE'] = '/api/auth' - app.config['JWT_EXPIRATION_DELTA'] = env.get_auth_expiration_time() + app.config['JWT_EXPIRATION_DELTA'] = env_singleton.env.get_auth_expiration_time() def init_app_services(app): @@ -93,6 +94,7 @@ def init_app_url_rules(app): def init_api_resources(api): api.add_resource(Root, '/api') api.add_resource(Registration, '/api/registration') + api.add_resource(Environment, '/api/environment') api.add_resource(Monkey, '/api/monkey', '/api/monkey/', '/api/monkey/') api.add_resource(Bootloader, '/api/bootloader/') api.add_resource(LocalRun, '/api/local-monkey', '/api/local-monkey/') diff --git a/monkey/monkey_island/cc/environment/__init__.py b/monkey/monkey_island/cc/environment/__init__.py index adcb8aed0..0e05c4923 100644 --- a/monkey/monkey_island/cc/environment/__init__.py +++ b/monkey/monkey_island/cc/environment/__init__.py @@ -23,8 +23,8 @@ class Environment(object, metaclass=ABCMeta): _testing = False - def __init__(self): - self._config = EnvironmentConfig("", "", UserCreds()) + def __init__(self, config: EnvironmentConfig): + self._config = config self._testing = False # Assume env is not for unit testing. @property @@ -72,8 +72,11 @@ class Environment(object, metaclass=ABCMeta): def testing(self, value): self._testing = value - def set_config(self, config: EnvironmentConfig): - self._config = config + def save_config(self): + self._config.save_to_file() + + def get_config(self) -> EnvironmentConfig: + return self._config def get_island_port(self): return self._ISLAND_PORT @@ -93,12 +96,15 @@ class Environment(object, metaclass=ABCMeta): hash_obj.update(secret.encode('utf-8')) return hash_obj.hexdigest() - def get_deployment(self): + def get_deployment(self) -> str: deployment = 'unknown' if self._config and self._config.deployment: deployment = self._config.deployment return deployment + def set_deployment(self, deployment: str): + self._config.deployment = deployment + @property def mongo_db_name(self): return self._MONGO_DB_NAME diff --git a/monkey/monkey_island/cc/environment/aws.py b/monkey/monkey_island/cc/environment/aws.py index b42fa910e..b8f684289 100644 --- a/monkey/monkey_island/cc/environment/aws.py +++ b/monkey/monkey_island/cc/environment/aws.py @@ -9,8 +9,8 @@ class AwsEnvironment(Environment): _credentials_required = True - def __init__(self): - super(AwsEnvironment, self).__init__() + def __init__(self, config): + super(AwsEnvironment, self).__init__(config) # Not suppressing error here on purpose. This is critical if we're on AWS env. self.aws_info = AwsInstance() self._instance_id = self._get_instance_id() diff --git a/monkey/monkey_island/cc/environment/environment_singleton.py b/monkey/monkey_island/cc/environment/environment_singleton.py index 2b3159248..0fcfa8eb0 100644 --- a/monkey/monkey_island/cc/environment/environment_singleton.py +++ b/monkey/monkey_island/cc/environment/environment_singleton.py @@ -1,8 +1,8 @@ -import json import logging env = None +import monkey_island.cc.resources.auth.user_store as user_store from monkey_island.cc.environment import standard, EnvironmentConfig from monkey_island.cc.environment import testing from monkey_island.cc.environment import aws @@ -24,11 +24,28 @@ ENV_DICT = { TESTING: testing.TestingEnvironment } + +def set_env(env_type: str, env_config: EnvironmentConfig): + global env + if env_type in ENV_DICT: + env = ENV_DICT[env_type](env_config) + + +def set_to_standard(): + global env + if env: + env_config = env.get_config() + env_config.server_config = 'standard' + set_env('standard', env_config) + env.save_config() + user_store.UserStore.set_users(env.get_auth_users()) + + try: config = EnvironmentConfig.get_from_file() __env_type = config.server_config - env = ENV_DICT[__env_type]() - env.set_config(config) + set_env(__env_type, config) + # noinspection PyUnresolvedReferences logger.info('Monkey\'s env is: {0}'.format(env.__class__.__name__)) except Exception: logger.error('Failed initializing environment', exc_info=True) diff --git a/monkey/monkey_island/cc/environment/test_aws.py b/monkey/monkey_island/cc/environment/test_aws.py index 65f8c3e58..7c5bc26d2 100644 --- a/monkey/monkey_island/cc/environment/test_aws.py +++ b/monkey/monkey_island/cc/environment/test_aws.py @@ -1,3 +1,4 @@ +from monkey_island.cc.environment import EnvironmentConfig, UserCreds from monkey_island.cc.resources.auth.auth_user import User from monkey_island.cc.testing.IslandTestCase import IslandTestCase from monkey_island.cc.environment.aws import AwsEnvironment @@ -7,7 +8,7 @@ import hashlib class TestAwsEnvironment(IslandTestCase): def test_get_auth_users(self): - env = AwsEnvironment() + env = AwsEnvironment(EnvironmentConfig("", "", UserCreds())) # This is "injecting" the instance id to the env. This is the UTs aren't always executed on the same AWS machine # (might not be an AWS machine at all). # Perhaps it would have been more elegant to create a Mock, but not worth it for diff --git a/monkey/monkey_island/cc/environment/testing.py b/monkey/monkey_island/cc/environment/testing.py index a86ec6353..2dd34a920 100644 --- a/monkey/monkey_island/cc/environment/testing.py +++ b/monkey/monkey_island/cc/environment/testing.py @@ -1,4 +1,4 @@ -from monkey_island.cc.environment import Environment +from monkey_island.cc.environment import Environment, EnvironmentConfig class TestingEnvironment(Environment): @@ -9,8 +9,8 @@ class TestingEnvironment(Environment): _credentials_required = True - def __init__(self): - super(TestingEnvironment, self).__init__() + def __init__(self, config: EnvironmentConfig): + super(TestingEnvironment, self).__init__(config) self.testing = True def get_auth_users(self): diff --git a/monkey/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py index 0b19789e1..546f70c12 100644 --- a/monkey/monkey_island/cc/main.py +++ b/monkey/monkey_island/cc/main.py @@ -23,7 +23,7 @@ logger = logging.getLogger(__name__) from monkey_island.cc.app import init_app from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list from monkey_island.cc.network_utils import local_ip_addresses -from monkey_island.cc.environment.environment_singleton import env +import monkey_island.cc.environment.environment_singleton as env_singleton from monkey_island.cc.database import is_db_server_up, get_db_version from monkey_island.cc.resources.monkey_download import MonkeyDownload from common.version import get_version @@ -33,7 +33,7 @@ from monkey_island.cc.setup import setup def main(should_setup_only=False): logger.info("Starting bootloader server") - mongo_url = os.environ.get('MONGO_URL', env.get_mongo_url()) + 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() @@ -46,7 +46,7 @@ def start_island_server(should_setup_only): from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop - mongo_url = os.environ.get('MONGO_URL', env.get_mongo_url()) + 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) @@ -62,13 +62,13 @@ def start_island_server(should_setup_only): logger.warning("Setup only flag passed. Exiting.") return - if env.is_debug(): + if env_singleton.env.is_debug(): app.run(host='0.0.0.0', debug=True, ssl_context=(crt_path, key_path)) else: http_server = HTTPServer(WSGIContainer(app), ssl_options={'certfile': os.environ.get('SERVER_CRT', crt_path), 'keyfile': os.environ.get('SERVER_KEY', key_path)}) - http_server.listen(env.get_island_port()) + http_server.listen(env_singleton.env.get_island_port()) log_init_info() IOLoop.instance().start() @@ -77,7 +77,7 @@ def log_init_info(): logger.info('Monkey Island Server is running!') logger.info(f"version: {get_version()}") logger.info('Listening on the following URLs: {}'.format( - ", ".join(["https://{}:{}".format(x, env.get_island_port()) for x in local_ip_addresses()]) + ", ".join(["https://{}:{}".format(x, env_singleton.env.get_island_port()) for x in local_ip_addresses()]) ) ) MonkeyDownload.log_executable_hashes() diff --git a/monkey/monkey_island/cc/models/__init__.py b/monkey/monkey_island/cc/models/__init__.py index 1af3a8ad4..d58b64c00 100644 --- a/monkey/monkey_island/cc/models/__init__.py +++ b/monkey/monkey_island/cc/models/__init__.py @@ -1,15 +1,15 @@ from mongoengine import connect -from monkey_island.cc.environment.environment_singleton import env +import monkey_island.cc.environment.environment_singleton as env_singleton # This section sets up the DB connection according to the environment. # If testing, use mongomock which only emulates mongo. for more information, see # http://docs.mongoengine.org/guide/mongomock.html . # Otherwise, use an actual mongod instance with connection parameters supplied by env. -if env.testing: # See monkey_island.cc.environment.testing +if env_singleton.env.testing: # See monkey_island.cc.environment.testing connect('mongoenginetest', host='mongomock://localhost') else: - connect(db=env.mongo_db_name, host=env.mongo_db_host, port=env.mongo_db_port) + connect(db=env_singleton.env.mongo_db_name, host=env_singleton.env.mongo_db_host, port=env_singleton.env.mongo_db_port) # Order of importing matters here, for registering the embedded and referenced documents before using them. from .config import Config # noqa: F401 diff --git a/monkey/monkey_island/cc/resources/auth/auth.py b/monkey/monkey_island/cc/resources/auth/auth.py index c582f8706..6e9ec650c 100644 --- a/monkey/monkey_island/cc/resources/auth/auth.py +++ b/monkey/monkey_island/cc/resources/auth/auth.py @@ -4,23 +4,23 @@ from flask import current_app, abort from flask_jwt import JWT, _jwt_required, JWTError from werkzeug.security import safe_str_cmp -from monkey_island.cc.environment.environment_singleton import env -from monkey_island.cc.resources.auth.user_store import UserStore +import monkey_island.cc.environment.environment_singleton as env_singleton +import monkey_island.cc.resources.auth.user_store as user_store __author__ = 'itay.mizeretz' def init_jwt(app): - UserStore.set_users(env.get_auth_users()) + user_store.UserStore.set_users(env_singleton.env.get_auth_users()) def authenticate(username, secret): - user = UserStore.username_table.get(username, None) + user = user_store.UserStore.username_table.get(username, None) if user and safe_str_cmp(user.secret.encode('utf-8'), secret.encode('utf-8')): return user def identity(payload): user_id = payload['identity'] - return UserStore.userid_table.get(user_id, None) + return user_store.UserStore.userid_table.get(user_id, None) JWT(app, authenticate, identity) diff --git a/monkey/monkey_island/cc/resources/environment.py b/monkey/monkey_island/cc/resources/environment.py new file mode 100644 index 000000000..5a768133c --- /dev/null +++ b/monkey/monkey_island/cc/resources/environment.py @@ -0,0 +1,16 @@ +import json + +from flask import request +import flask_restful + +import monkey_island.cc.environment.environment_singleton as env_singleton + + +class Environment(flask_restful.Resource): + + def patch(self): + env_data = json.loads(request.data) + if env_data['server_config'] == "standard": + if env_singleton.env.needs_registration(): + env_singleton.set_to_standard() + return {} diff --git a/monkey/monkey_island/cc/resources/local_run.py b/monkey/monkey_island/cc/resources/local_run.py index 773b49eca..d4e83cc25 100644 --- a/monkey/monkey_island/cc/resources/local_run.py +++ b/monkey/monkey_island/cc/resources/local_run.py @@ -6,7 +6,7 @@ import sys from flask import request, jsonify, make_response import flask_restful -from monkey_island.cc.environment.environment_singleton import env +import monkey_island.cc.environment.environment_singleton as env_singleton from monkey_island.cc.models import Monkey from monkey_island.cc.resources.monkey_download import get_monkey_executable from monkey_island.cc.services.node import NodeService @@ -43,7 +43,7 @@ def run_local_monkey(): # run the monkey try: - args = ['"%s" m0nk3y -s %s:%s' % (target_path, local_ip_addresses()[0], env.get_island_port())] + args = ['"%s" m0nk3y -s %s:%s' % (target_path, local_ip_addresses()[0], env_singleton.env.get_island_port())] if sys.platform == "win32": args = "".join(args) pid = subprocess.Popen(args, shell=True).pid diff --git a/monkey/monkey_island/cc/resources/registration.py b/monkey/monkey_island/cc/resources/registration.py index 237972ee6..45351ccd2 100644 --- a/monkey/monkey_island/cc/resources/registration.py +++ b/monkey/monkey_island/cc/resources/registration.py @@ -2,18 +2,18 @@ import flask_restful from flask import request, make_response from common.utils.exceptions import InvalidRegistrationCredentials -from monkey_island.cc.environment.environment_singleton import env +import monkey_island.cc.environment.environment_singleton as env_singleton from monkey_island.cc.environment.user_creds import UserCreds class Registration(flask_restful.Resource): def get(self): - return {'needs_registration': env.needs_registration()} + return {'needs_registration': env_singleton.env.needs_registration()} def post(self): credentials = UserCreds.get_from_json(request.data) try: - env.try_add_user(credentials) + env_singleton.env.try_add_user(credentials) return make_response({"error": ""}, 200) except InvalidRegistrationCredentials as e: return make_response({"error": str(e)}, 400) diff --git a/monkey/monkey_island/cc/server_config.json b/monkey/monkey_island/cc/server_config.json new file mode 100644 index 000000000..dc06aac82 --- /dev/null +++ b/monkey/monkey_island/cc/server_config.json @@ -0,0 +1,4 @@ +{ + "server_config": "standard", + "deployment": "develop" +} \ No newline at end of file diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index 4aa2c909f..b35793113 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -6,7 +6,7 @@ from jsonschema import Draft4Validator, validators import monkey_island.cc.services.post_breach_files from monkey_island.cc.database import mongo -from monkey_island.cc.environment.environment_singleton import env +import monkey_island.cc.environment.environment_singleton as env_singleton from monkey_island.cc.network_utils import local_ip_addresses from .config_schema import SCHEMA from monkey_island.cc.encryptor import encryptor @@ -216,8 +216,8 @@ class ConfigService: @staticmethod def set_server_ips_in_config(config): ips = local_ip_addresses() - config["cnc"]["servers"]["command_servers"] = ["%s:%d" % (ip, env.get_island_port()) for ip in ips] - config["cnc"]["servers"]["current_server"] = "%s:%d" % (ips[0], env.get_island_port()) + config["cnc"]["servers"]["command_servers"] = ["%s:%d" % (ip, env_singleton.env.get_island_port()) for ip in ips] + config["cnc"]["servers"]["current_server"] = "%s:%d" % (ips[0], env_singleton.env.get_island_port()) @staticmethod def save_initial_config_if_needed(): diff --git a/monkey/monkey_island/cc/services/reporting/exporter_init.py b/monkey/monkey_island/cc/services/reporting/exporter_init.py index 6a3411bf8..2846bccb5 100644 --- a/monkey/monkey_island/cc/services/reporting/exporter_init.py +++ b/monkey/monkey_island/cc/services/reporting/exporter_init.py @@ -3,7 +3,7 @@ import logging from monkey_island.cc.services.reporting.report_exporter_manager import ReportExporterManager from monkey_island.cc.services.reporting.aws_exporter import AWSExporter from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService -from monkey_island.cc.environment.environment_singleton import env +import monkey_island.cc.environment.environment_singleton as env_singleton logger = logging.getLogger(__name__) @@ -21,7 +21,7 @@ def try_add_aws_exporter_to_manager(manager): # noinspection PyBroadException try: RemoteRunAwsService.init() - if RemoteRunAwsService.is_running_on_aws() and ('aws' == env.get_deployment()): + if RemoteRunAwsService.is_running_on_aws() and ('aws' == env_singleton.env.get_deployment()): manager.add_exporter_to_list(AWSExporter) except Exception: logger.error("Failed adding aws exporter to manager. Exception info:", exc_info=True) diff --git a/monkey/monkey_island/cc/services/version_update.py b/monkey/monkey_island/cc/services/version_update.py index 5ec4419a0..7c2855f21 100644 --- a/monkey/monkey_island/cc/services/version_update.py +++ b/monkey/monkey_island/cc/services/version_update.py @@ -3,7 +3,7 @@ import logging import requests from common.version import get_version -from monkey_island.cc.environment.environment_singleton import env +import monkey_island.cc.environment.environment_singleton as env_singleton __author__ = "itay.mizeretz" @@ -40,7 +40,7 @@ class VersionUpdateService: Checks if newer monkey version is available :return: False if not, version in string format ('1.6.2') otherwise """ - url = VersionUpdateService.VERSION_SERVER_CHECK_NEW_URL % (env.get_deployment(), get_version()) + url = VersionUpdateService.VERSION_SERVER_CHECK_NEW_URL % (env_singleton.env.get_deployment(), get_version()) reply = requests.get(url, timeout=15) @@ -54,4 +54,4 @@ class VersionUpdateService: @staticmethod def get_download_link(): - return VersionUpdateService.VERSION_SERVER_DOWNLOAD_URL % (env.get_deployment(), get_version()) + return VersionUpdateService.VERSION_SERVER_DOWNLOAD_URL % (env_singleton.env.get_deployment(), get_version()) diff --git a/monkey/monkey_island/cc/testing/IslandTestCase.py b/monkey/monkey_island/cc/testing/IslandTestCase.py index 998155b2d..855bc928c 100644 --- a/monkey/monkey_island/cc/testing/IslandTestCase.py +++ b/monkey/monkey_island/cc/testing/IslandTestCase.py @@ -1,12 +1,12 @@ import unittest -from monkey_island.cc.environment.environment_singleton import env +import monkey_island.cc.environment.environment_singleton as env_singleton from monkey_island.cc.models import Monkey from monkey_island.cc.models.zero_trust.finding import Finding class IslandTestCase(unittest.TestCase): def fail_if_not_testing_env(self): - self.assertFalse(not env.testing, "Change server_config.json to testing environment.") + self.assertFalse(not env_singleton.env.testing, "Change server_config.json to testing environment.") @staticmethod def clean_monkey_db(): diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js index c27d558ed..b74846081 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RegisterPage.js @@ -9,6 +9,8 @@ import monkeyDetective from '../../images/detective-monkey.svg'; class RegisterPageComponent extends React.Component { + NO_AUTH_API_ENDPOINT = '/api/environment'; + register = () => { this.auth.register(this.username, this.password).then(res => { this.setState({failed: false, error: ''}); @@ -23,6 +25,29 @@ class RegisterPageComponent extends React.Component { }); }; + setNoAuth = () => { + let options = {} + options['headers'] = { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }; + options['method'] = 'PATCH' + options['body'] = JSON.stringify({'server_config': 'standard'}) + + return fetch(this.NO_AUTH_API_ENDPOINT, options) + .then(res => { + if (res.status === 200) { + this.auth.attemptNoAuthLogin().then(() => { + this.redirectToHome(); + }); + } + this.setState({ + failed: true, + error: res['error'] + }); + }) + } + updateUsername = (evt) => { this.username = evt.target.value; }; @@ -72,6 +97,9 @@ class RegisterPageComponent extends React.Component { }}> Lets Go! + + I want anyone to access the island + { this.state.failed ?
{this.state.error}
diff --git a/monkey/monkey_island/cc/ui/src/styles/RegisterPage.scss b/monkey/monkey_island/cc/ui/src/styles/RegisterPage.scss index 269ddda3d..a29f99437 100644 --- a/monkey/monkey_island/cc/ui/src/styles/RegisterPage.scss +++ b/monkey/monkey_island/cc/ui/src/styles/RegisterPage.scss @@ -60,3 +60,13 @@ .registration-block h3 { margin-bottom: 20px; } + +.no-auth-link { + float: right; + color: #6a5b00; +} + +.no-auth-link:hover { + float: right; + color: #796900; +}