Finished "no password" environment option

This commit is contained in:
VakarisZ 2020-06-16 13:45:10 +03:00
parent a5b1ac22f9
commit ce5e415788
19 changed files with 129 additions and 45 deletions

View File

@ -7,9 +7,10 @@ from werkzeug.exceptions import NotFound
from monkey_island.cc.resources.auth.auth import init_jwt from monkey_island.cc.resources.auth.auth import init_jwt
from monkey_island.cc.database import mongo, database 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.client_run import ClientRun
from monkey_island.cc.resources.edge import Edge 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.local_run import LocalRun
from monkey_island.cc.resources.log import Log from monkey_island.cc.resources.log import Log
from monkey_island.cc.resources.island_logs import IslandLog 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['MONGO_URI'] = mongo_url
app.config['SECRET_KEY'] = str(uuid.getnode()) app.config['SECRET_KEY'] = str(uuid.getnode())
app.config['JWT_AUTH_URL_RULE'] = '/api/auth' 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): def init_app_services(app):
@ -93,6 +94,7 @@ def init_app_url_rules(app):
def init_api_resources(api): def init_api_resources(api):
api.add_resource(Root, '/api') api.add_resource(Root, '/api')
api.add_resource(Registration, '/api/registration') api.add_resource(Registration, '/api/registration')
api.add_resource(Environment, '/api/environment')
api.add_resource(Monkey, '/api/monkey', '/api/monkey/', '/api/monkey/<string:guid>') api.add_resource(Monkey, '/api/monkey', '/api/monkey/', '/api/monkey/<string:guid>')
api.add_resource(Bootloader, '/api/bootloader/<string:os>') api.add_resource(Bootloader, '/api/bootloader/<string:os>')
api.add_resource(LocalRun, '/api/local-monkey', '/api/local-monkey/') api.add_resource(LocalRun, '/api/local-monkey', '/api/local-monkey/')

View File

@ -23,8 +23,8 @@ class Environment(object, metaclass=ABCMeta):
_testing = False _testing = False
def __init__(self): def __init__(self, config: EnvironmentConfig):
self._config = EnvironmentConfig("", "", UserCreds()) self._config = config
self._testing = False # Assume env is not for unit testing. self._testing = False # Assume env is not for unit testing.
@property @property
@ -72,8 +72,11 @@ class Environment(object, metaclass=ABCMeta):
def testing(self, value): def testing(self, value):
self._testing = value self._testing = value
def set_config(self, config: EnvironmentConfig): def save_config(self):
self._config = config self._config.save_to_file()
def get_config(self) -> EnvironmentConfig:
return self._config
def get_island_port(self): def get_island_port(self):
return self._ISLAND_PORT return self._ISLAND_PORT
@ -93,12 +96,15 @@ class Environment(object, metaclass=ABCMeta):
hash_obj.update(secret.encode('utf-8')) hash_obj.update(secret.encode('utf-8'))
return hash_obj.hexdigest() return hash_obj.hexdigest()
def get_deployment(self): def get_deployment(self) -> str:
deployment = 'unknown' deployment = 'unknown'
if self._config and self._config.deployment: if self._config and self._config.deployment:
deployment = self._config.deployment deployment = self._config.deployment
return deployment return deployment
def set_deployment(self, deployment: str):
self._config.deployment = deployment
@property @property
def mongo_db_name(self): def mongo_db_name(self):
return self._MONGO_DB_NAME return self._MONGO_DB_NAME

View File

@ -9,8 +9,8 @@ class AwsEnvironment(Environment):
_credentials_required = True _credentials_required = True
def __init__(self): def __init__(self, config):
super(AwsEnvironment, self).__init__() super(AwsEnvironment, self).__init__(config)
# Not suppressing error here on purpose. This is critical if we're on AWS env. # Not suppressing error here on purpose. This is critical if we're on AWS env.
self.aws_info = AwsInstance() self.aws_info = AwsInstance()
self._instance_id = self._get_instance_id() self._instance_id = self._get_instance_id()

View File

@ -1,8 +1,8 @@
import json
import logging import logging
env = None 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 standard, EnvironmentConfig
from monkey_island.cc.environment import testing from monkey_island.cc.environment import testing
from monkey_island.cc.environment import aws from monkey_island.cc.environment import aws
@ -24,11 +24,28 @@ ENV_DICT = {
TESTING: testing.TestingEnvironment 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: try:
config = EnvironmentConfig.get_from_file() config = EnvironmentConfig.get_from_file()
__env_type = config.server_config __env_type = config.server_config
env = ENV_DICT[__env_type]() set_env(__env_type, config)
env.set_config(config) # noinspection PyUnresolvedReferences
logger.info('Monkey\'s env is: {0}'.format(env.__class__.__name__)) logger.info('Monkey\'s env is: {0}'.format(env.__class__.__name__))
except Exception: except Exception:
logger.error('Failed initializing environment', exc_info=True) logger.error('Failed initializing environment', exc_info=True)

View File

@ -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.resources.auth.auth_user import User
from monkey_island.cc.testing.IslandTestCase import IslandTestCase from monkey_island.cc.testing.IslandTestCase import IslandTestCase
from monkey_island.cc.environment.aws import AwsEnvironment from monkey_island.cc.environment.aws import AwsEnvironment
@ -7,7 +8,7 @@ import hashlib
class TestAwsEnvironment(IslandTestCase): class TestAwsEnvironment(IslandTestCase):
def test_get_auth_users(self): 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 # 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). # (might not be an AWS machine at all).
# Perhaps it would have been more elegant to create a Mock, but not worth it for # Perhaps it would have been more elegant to create a Mock, but not worth it for

View File

@ -1,4 +1,4 @@
from monkey_island.cc.environment import Environment from monkey_island.cc.environment import Environment, EnvironmentConfig
class TestingEnvironment(Environment): class TestingEnvironment(Environment):
@ -9,8 +9,8 @@ class TestingEnvironment(Environment):
_credentials_required = True _credentials_required = True
def __init__(self): def __init__(self, config: EnvironmentConfig):
super(TestingEnvironment, self).__init__() super(TestingEnvironment, self).__init__(config)
self.testing = True self.testing = True
def get_auth_users(self): def get_auth_users(self):

View File

@ -23,7 +23,7 @@ logger = logging.getLogger(__name__)
from monkey_island.cc.app import init_app from monkey_island.cc.app import init_app
from monkey_island.cc.services.reporting.exporter_init import populate_exporter_list 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.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.database import is_db_server_up, get_db_version
from monkey_island.cc.resources.monkey_download import MonkeyDownload from monkey_island.cc.resources.monkey_download import MonkeyDownload
from common.version import get_version from common.version import get_version
@ -33,7 +33,7 @@ from monkey_island.cc.setup import setup
def main(should_setup_only=False): def main(should_setup_only=False):
logger.info("Starting bootloader server") 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 = Thread(target=BootloaderHttpServer(mongo_url).serve_forever, daemon=True)
bootloader_server_thread.start() bootloader_server_thread.start()
@ -46,7 +46,7 @@ def start_island_server(should_setup_only):
from tornado.httpserver import HTTPServer from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop 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) wait_for_mongo_db_server(mongo_url)
assert_mongo_db_version(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.") logger.warning("Setup only flag passed. Exiting.")
return 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)) app.run(host='0.0.0.0', debug=True, ssl_context=(crt_path, key_path))
else: else:
http_server = HTTPServer(WSGIContainer(app), http_server = HTTPServer(WSGIContainer(app),
ssl_options={'certfile': os.environ.get('SERVER_CRT', crt_path), ssl_options={'certfile': os.environ.get('SERVER_CRT', crt_path),
'keyfile': os.environ.get('SERVER_KEY', key_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() log_init_info()
IOLoop.instance().start() IOLoop.instance().start()
@ -77,7 +77,7 @@ def log_init_info():
logger.info('Monkey Island Server is running!') logger.info('Monkey Island Server is running!')
logger.info(f"version: {get_version()}") logger.info(f"version: {get_version()}")
logger.info('Listening on the following URLs: {}'.format( 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() MonkeyDownload.log_executable_hashes()

View File

@ -1,15 +1,15 @@
from mongoengine import connect 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. # This section sets up the DB connection according to the environment.
# If testing, use mongomock which only emulates mongo. for more information, see # If testing, use mongomock which only emulates mongo. for more information, see
# http://docs.mongoengine.org/guide/mongomock.html . # http://docs.mongoengine.org/guide/mongomock.html .
# Otherwise, use an actual mongod instance with connection parameters supplied by env. # 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') connect('mongoenginetest', host='mongomock://localhost')
else: 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. # Order of importing matters here, for registering the embedded and referenced documents before using them.
from .config import Config # noqa: F401 from .config import Config # noqa: F401

View File

@ -4,23 +4,23 @@ from flask import current_app, abort
from flask_jwt import JWT, _jwt_required, JWTError from flask_jwt import JWT, _jwt_required, JWTError
from werkzeug.security import safe_str_cmp from werkzeug.security import safe_str_cmp
from monkey_island.cc.environment.environment_singleton import env import monkey_island.cc.environment.environment_singleton as env_singleton
from monkey_island.cc.resources.auth.user_store import UserStore import monkey_island.cc.resources.auth.user_store as user_store
__author__ = 'itay.mizeretz' __author__ = 'itay.mizeretz'
def init_jwt(app): 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): 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')): if user and safe_str_cmp(user.secret.encode('utf-8'), secret.encode('utf-8')):
return user return user
def identity(payload): def identity(payload):
user_id = payload['identity'] 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) JWT(app, authenticate, identity)

View File

@ -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 {}

View File

@ -6,7 +6,7 @@ import sys
from flask import request, jsonify, make_response from flask import request, jsonify, make_response
import flask_restful 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.models import Monkey
from monkey_island.cc.resources.monkey_download import get_monkey_executable from monkey_island.cc.resources.monkey_download import get_monkey_executable
from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.node import NodeService
@ -43,7 +43,7 @@ def run_local_monkey():
# run the monkey # run the monkey
try: 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": if sys.platform == "win32":
args = "".join(args) args = "".join(args)
pid = subprocess.Popen(args, shell=True).pid pid = subprocess.Popen(args, shell=True).pid

View File

@ -2,18 +2,18 @@ import flask_restful
from flask import request, make_response from flask import request, make_response
from common.utils.exceptions import InvalidRegistrationCredentials 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 from monkey_island.cc.environment.user_creds import UserCreds
class Registration(flask_restful.Resource): class Registration(flask_restful.Resource):
def get(self): def get(self):
return {'needs_registration': env.needs_registration()} return {'needs_registration': env_singleton.env.needs_registration()}
def post(self): def post(self):
credentials = UserCreds.get_from_json(request.data) credentials = UserCreds.get_from_json(request.data)
try: try:
env.try_add_user(credentials) env_singleton.env.try_add_user(credentials)
return make_response({"error": ""}, 200) return make_response({"error": ""}, 200)
except InvalidRegistrationCredentials as e: except InvalidRegistrationCredentials as e:
return make_response({"error": str(e)}, 400) return make_response({"error": str(e)}, 400)

View File

@ -0,0 +1,4 @@
{
"server_config": "standard",
"deployment": "develop"
}

View File

@ -6,7 +6,7 @@ from jsonschema import Draft4Validator, validators
import monkey_island.cc.services.post_breach_files import monkey_island.cc.services.post_breach_files
from monkey_island.cc.database import mongo 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 monkey_island.cc.network_utils import local_ip_addresses
from .config_schema import SCHEMA from .config_schema import SCHEMA
from monkey_island.cc.encryptor import encryptor from monkey_island.cc.encryptor import encryptor
@ -216,8 +216,8 @@ class ConfigService:
@staticmethod @staticmethod
def set_server_ips_in_config(config): def set_server_ips_in_config(config):
ips = local_ip_addresses() ips = local_ip_addresses()
config["cnc"]["servers"]["command_servers"] = ["%s:%d" % (ip, env.get_island_port()) for ip in ips] 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.get_island_port()) config["cnc"]["servers"]["current_server"] = "%s:%d" % (ips[0], env_singleton.env.get_island_port())
@staticmethod @staticmethod
def save_initial_config_if_needed(): def save_initial_config_if_needed():

View File

@ -3,7 +3,7 @@ import logging
from monkey_island.cc.services.reporting.report_exporter_manager import ReportExporterManager 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.reporting.aws_exporter import AWSExporter
from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService 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__) logger = logging.getLogger(__name__)
@ -21,7 +21,7 @@ def try_add_aws_exporter_to_manager(manager):
# noinspection PyBroadException # noinspection PyBroadException
try: try:
RemoteRunAwsService.init() 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) manager.add_exporter_to_list(AWSExporter)
except Exception: except Exception:
logger.error("Failed adding aws exporter to manager. Exception info:", exc_info=True) logger.error("Failed adding aws exporter to manager. Exception info:", exc_info=True)

View File

@ -3,7 +3,7 @@ import logging
import requests import requests
from common.version import get_version 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" __author__ = "itay.mizeretz"
@ -40,7 +40,7 @@ class VersionUpdateService:
Checks if newer monkey version is available Checks if newer monkey version is available
:return: False if not, version in string format ('1.6.2') otherwise :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) reply = requests.get(url, timeout=15)
@ -54,4 +54,4 @@ class VersionUpdateService:
@staticmethod @staticmethod
def get_download_link(): 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())

View File

@ -1,12 +1,12 @@
import unittest 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 import Monkey
from monkey_island.cc.models.zero_trust.finding import Finding from monkey_island.cc.models.zero_trust.finding import Finding
class IslandTestCase(unittest.TestCase): class IslandTestCase(unittest.TestCase):
def fail_if_not_testing_env(self): 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 @staticmethod
def clean_monkey_db(): def clean_monkey_db():

View File

@ -9,6 +9,8 @@ import monkeyDetective from '../../images/detective-monkey.svg';
class RegisterPageComponent extends React.Component { class RegisterPageComponent extends React.Component {
NO_AUTH_API_ENDPOINT = '/api/environment';
register = () => { register = () => {
this.auth.register(this.username, this.password).then(res => { this.auth.register(this.username, this.password).then(res => {
this.setState({failed: false, error: ''}); 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) => { updateUsername = (evt) => {
this.username = evt.target.value; this.username = evt.target.value;
}; };
@ -72,6 +97,9 @@ class RegisterPageComponent extends React.Component {
}}> }}>
Lets Go! Lets Go!
</Button> </Button>
<a href='#' onClick={this.setNoAuth} className={'no-auth-link'}>
I want anyone to access the island
</a>
{ {
this.state.failed ? this.state.failed ?
<div className='alert alert-danger' role='alert'>{this.state.error}</div> <div className='alert alert-danger' role='alert'>{this.state.error}</div>

View File

@ -60,3 +60,13 @@
.registration-block h3 { .registration-block h3 {
margin-bottom: 20px; margin-bottom: 20px;
} }
.no-auth-link {
float: right;
color: #6a5b00;
}
.no-auth-link:hover {
float: right;
color: #796900;
}