From 614f464f24f890ec099bf35ebc0501950c83d977 Mon Sep 17 00:00:00 2001 From: itay Date: Mon, 22 Apr 2019 18:07:20 +0300 Subject: [PATCH 1/8] Add deployment and version for environment --- monkey/monkey_island/cc/environment/__init__.py | 15 +++++++++++++++ .../monkey_island/cc/environment/environment.py | 1 + monkey/monkey_island/cc/server_config.json | 4 +++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/environment/__init__.py b/monkey/monkey_island/cc/environment/__init__.py index 62b0e9eed..bc53ced92 100644 --- a/monkey/monkey_island/cc/environment/__init__.py +++ b/monkey/monkey_island/cc/environment/__init__.py @@ -37,6 +37,21 @@ class Environment(object): h.update(secret) return h.hexdigest() + def get_deployment(self): + return self._get_from_config('deployment', 'unknown') + + def is_develop(self): + return self.get_deployment() == 'develop' + + def get_version(self): + return self._get_from_config('monkey_version', 'unknown') + ('-dev' if self.is_develop() else '') + + def _get_from_config(self, key, default_value=None): + val = default_value + if self.config is not None: + val = self.config.get(key, val) + return val + @abc.abstractmethod def get_auth_users(self): return diff --git a/monkey/monkey_island/cc/environment/environment.py b/monkey/monkey_island/cc/environment/environment.py index 3cd6bb587..b27880e07 100644 --- a/monkey/monkey_island/cc/environment/environment.py +++ b/monkey/monkey_island/cc/environment/environment.py @@ -32,6 +32,7 @@ def load_env_from_file(): config_json = load_server_configuration_from_file() return config_json['server_config'] + try: config_json = load_server_configuration_from_file() __env_type = config_json['server_config'] diff --git a/monkey/monkey_island/cc/server_config.json b/monkey/monkey_island/cc/server_config.json index 2d1a5995b..495b966ed 100644 --- a/monkey/monkey_island/cc/server_config.json +++ b/monkey/monkey_island/cc/server_config.json @@ -1,3 +1,5 @@ { - "server_config": "standard" + "server_config": "standard", + "deployment": "develop", + "monkey_version": "1.6.2" } \ No newline at end of file From fe1d9b8f721034fd656de93d142a27370bf2701f Mon Sep 17 00:00:00 2001 From: itay Date: Mon, 22 Apr 2019 18:08:58 +0300 Subject: [PATCH 2/8] Add version update service+resource Add routing to update resource Split app initializing to functions --- monkey/monkey_island/cc/app.py | 28 ++++++--- .../cc/resources/version_update.py | 23 ++++++++ .../cc/services/version_update.py | 57 +++++++++++++++++++ 3 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 monkey/monkey_island/cc/resources/version_update.py create mode 100644 monkey/monkey_island/cc/services/version_update.py diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 4f055fec6..781640b0d 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -28,6 +28,7 @@ from monkey_island.cc.resources.root import Root from monkey_island.cc.resources.telemetry import Telemetry from monkey_island.cc.resources.telemetry_feed import TelemetryFeed from monkey_island.cc.resources.pba_file_download import PBAFileDownload +from monkey_island.cc.resources.version_update import VersionUpdate from monkey_island.cc.services.config import ConfigService from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH from monkey_island.cc.resources.pba_file_upload import FileUpload @@ -82,18 +83,14 @@ def output_json(obj, code, headers=None): return resp -def init_app(mongo_url): - app = Flask(__name__) - - api = flask_restful.Api(app) - api.representations = {'application/json': output_json} - +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() + +def init_app_services(app): init_jwt(app) mongo.init_app(app) @@ -101,9 +98,13 @@ def init_app(mongo_url): database.init() ConfigService.init_config() + +def init_app_url_rules(app): app.add_url_rule('/', 'serve_home', serve_home) app.add_url_rule('/', 'serve_static_file', serve_static_file) + +def init_api_resources(api): api.add_resource(Root, '/api') api.add_resource(Monkey, '/api/monkey', '/api/monkey/', '/api/monkey/') api.add_resource(LocalRun, '/api/local-monkey', '/api/local-monkey/') @@ -126,5 +127,18 @@ def init_app(mongo_url): '/api/fileUpload/?restore=') api.add_resource(RemoteRun, '/api/remote-monkey', '/api/remote-monkey/') api.add_resource(AttackTelem, '/api/attack/') + api.add_resource(VersionUpdate, '/api/version-update', '/api/version-update/') + + +def init_app(mongo_url): + app = Flask(__name__) + + api = flask_restful.Api(app) + api.representations = {'application/json': output_json} + + init_app_config(app, mongo_url) + init_app_services(app) + init_app_url_rules(app) + init_api_resources(api) return app diff --git a/monkey/monkey_island/cc/resources/version_update.py b/monkey/monkey_island/cc/resources/version_update.py new file mode 100644 index 000000000..e9cb8ffb0 --- /dev/null +++ b/monkey/monkey_island/cc/resources/version_update.py @@ -0,0 +1,23 @@ +import flask_restful +import logging + +from monkey_island.cc.environment.environment import env +from monkey_island.cc.auth import jwt_required +from monkey_island.cc.services.version_update import VersionUpdateService + +__author__ = 'itay.mizeretz' + +logger = logging.getLogger(__name__) + + +class VersionUpdate(flask_restful.Resource): + def __init__(self): + super(VersionUpdate, self).__init__() + + @jwt_required() + def get(self): + return { + 'current_version': env.get_version(), + 'newer_version': VersionUpdateService.get_newer_version(), + 'download_link': VersionUpdateService.get_download_link() + } diff --git a/monkey/monkey_island/cc/services/version_update.py b/monkey/monkey_island/cc/services/version_update.py new file mode 100644 index 000000000..127e39f57 --- /dev/null +++ b/monkey/monkey_island/cc/services/version_update.py @@ -0,0 +1,57 @@ +import logging + +import requests + +from monkey_island.cc.environment.environment import env + +__author__ = "itay.mizeretz" + +logger = logging.getLogger(__name__) + + +class VersionUpdateService: + VERSION_SERVER_URL_PREF = 'https://monkey.guardicore.com' + VERSION_SERVER_CHECK_NEW_URL = VERSION_SERVER_URL_PREF + '?deployment=%s&monkey_version=%s' + VERSION_SERVER_DOWNLOAD_URL = VERSION_SERVER_CHECK_NEW_URL + '&is_download=true' + + newer_version = None + + def __init__(self): + pass + + @staticmethod + def get_newer_version(): + """ + Checks for newer version if never checked before. + :return: None if failed checking for newer version, result of '_check_new_version' otherwise + """ + if VersionUpdateService.newer_version is None: + try: + VersionUpdateService.newer_version = VersionUpdateService._check_new_version() + except Exception: + logger.exception('Failed updating version number') + + return VersionUpdateService.newer_version + + @staticmethod + def _check_new_version(): + """ + 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(), env.get_version()) + + reply = requests.get(url, timeout=15) + + res = reply.json().get('newer_version', None) + + if res is False: + return res + + [int(x) for x in res.split('.')] # raises value error if version is invalid format + return res + + @staticmethod + def get_download_link(): + return VersionUpdateService.VERSION_SERVER_DOWNLOAD_URL % (env.get_deployment(), env.get_version()) + From d486471fb225ee6ae31eb2e7e330d44140758bb7 Mon Sep 17 00:00:00 2001 From: itay Date: Mon, 22 Apr 2019 18:09:21 +0300 Subject: [PATCH 3/8] Add version component to UI --- .../cc/ui/src/components/Main.js | 4 +- .../components/side-menu/VersionComponent.js | 47 +++++++++++++++++++ monkey/monkey_island/cc/ui/src/styles/App.css | 10 ++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index da8e59113..8229133e6 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -20,6 +20,7 @@ import 'react-data-components/css/table-twbs.css'; import 'styles/App.css'; import 'react-toggle/style.css'; import 'react-table/react-table.css'; +import VersionComponent from "./side-menu/VersionComponent"; let logoImage = require('../images/monkey-icon.svg'); let infectionMonkeyImage = require('../images/infection-monkey.svg'); @@ -85,7 +86,7 @@ class AppComponent extends AuthComponent { infection_done: false, report_done: false, isLoggedIn: undefined - } + }, }; } @@ -175,6 +176,7 @@ class AppComponent extends AuthComponent {
License
+ ()}/> diff --git a/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js b/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js new file mode 100644 index 000000000..9e2b55edb --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js @@ -0,0 +1,47 @@ +import React from 'react'; +import AuthComponent from "../AuthComponent"; +import {Icon} from 'react-fa'; + +class VersionComponent extends AuthComponent { + constructor(props) { + super(props); + this.state = { + currentVersion: undefined, + newerVersion: undefined, + downloadLink: undefined + } + } + + componentDidMount() { + this.authFetch('/api/version-update') + .then(res => res.json()) + .then(res => { + this.setState({ + currentVersion: res['current_version'], + newerVersion: res['newer_version'], + downloadLink: res['download_link'], + }); + }); + } + + render() { + return ( +
+ Infection Monkey Version: {this.state.currentVersion} + { + this.state.newerVersion ? +
+ Newer version available! +
+ Download here +
+ : + undefined + } +
+ ); + } +} + + +export default VersionComponent; diff --git a/monkey/monkey_island/cc/ui/src/styles/App.css b/monkey/monkey_island/cc/ui/src/styles/App.css index 6155a4dcc..b44fa4562 100644 --- a/monkey/monkey_island/cc/ui/src/styles/App.css +++ b/monkey/monkey_island/cc/ui/src/styles/App.css @@ -515,3 +515,13 @@ body { } } + +.version-text { + font-size: 0.9em; + position: absolute; + bottom: 5px; + left: 0; + right: 0; + margin-left: auto; + margin-right: auto; +} From 85230754943605c2c144f174fb679e3063458d8f Mon Sep 17 00:00:00 2001 From: itay Date: Tue, 23 Apr 2019 11:54:51 +0300 Subject: [PATCH 4/8] Remove authentication from version info resource Add new line at end of server_config.json --- monkey/monkey_island/cc/resources/version_update.py | 3 ++- monkey/monkey_island/cc/server_config.json | 2 +- .../cc/ui/src/components/side-menu/VersionComponent.js | 5 ++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/resources/version_update.py b/monkey/monkey_island/cc/resources/version_update.py index e9cb8ffb0..5b34f4206 100644 --- a/monkey/monkey_island/cc/resources/version_update.py +++ b/monkey/monkey_island/cc/resources/version_update.py @@ -14,7 +14,8 @@ class VersionUpdate(flask_restful.Resource): def __init__(self): super(VersionUpdate, self).__init__() - @jwt_required() + # We don't secure this since it doesn't give out any private info and we want UI to know version + # even when not authenticated def get(self): return { 'current_version': env.get_version(), diff --git a/monkey/monkey_island/cc/server_config.json b/monkey/monkey_island/cc/server_config.json index 495b966ed..a8df79e3e 100644 --- a/monkey/monkey_island/cc/server_config.json +++ b/monkey/monkey_island/cc/server_config.json @@ -2,4 +2,4 @@ "server_config": "standard", "deployment": "develop", "monkey_version": "1.6.2" -} \ No newline at end of file +} diff --git a/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js b/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js index 9e2b55edb..2921db983 100644 --- a/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js +++ b/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js @@ -1,8 +1,7 @@ import React from 'react'; -import AuthComponent from "../AuthComponent"; import {Icon} from 'react-fa'; -class VersionComponent extends AuthComponent { +class VersionComponent extends React.Component { constructor(props) { super(props); this.state = { @@ -13,7 +12,7 @@ class VersionComponent extends AuthComponent { } componentDidMount() { - this.authFetch('/api/version-update') + this.fetch('/api/version-update') // This is not authenticated on purpose .then(res => res.json()) .then(res => { this.setState({ From 06f915397668ed1cd23ae67f43d2677db8f896be Mon Sep 17 00:00:00 2001 From: itay Date: Tue, 23 Apr 2019 12:28:38 +0300 Subject: [PATCH 5/8] fix fetch call --- .../cc/ui/src/components/side-menu/VersionComponent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js b/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js index 2921db983..1246b5b94 100644 --- a/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js +++ b/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js @@ -12,7 +12,7 @@ class VersionComponent extends React.Component { } componentDidMount() { - this.fetch('/api/version-update') // This is not authenticated on purpose + fetch('/api/version-update') // This is not authenticated on purpose .then(res => res.json()) .then(res => { this.setState({ From 362b87f72ab051fe0ce0adb4d896e205c60bd589 Mon Sep 17 00:00:00 2001 From: itay Date: Tue, 23 Apr 2019 14:44:09 +0300 Subject: [PATCH 6/8] Use hardcoded version --- monkey/monkey_island/cc/environment/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/environment/__init__.py b/monkey/monkey_island/cc/environment/__init__.py index bc53ced92..ffa85693c 100644 --- a/monkey/monkey_island/cc/environment/__init__.py +++ b/monkey/monkey_island/cc/environment/__init__.py @@ -13,6 +13,7 @@ class Environment(object): _MONGO_URL = os.environ.get("MONKEY_MONGO_URL", "mongodb://localhost:27017/monkeyisland") _DEBUG_SERVER = False _AUTH_EXPIRATION_TIME = timedelta(hours=1) + _MONKEY_VERSION = "1.6.2" def __init__(self): self.config = None @@ -44,7 +45,7 @@ class Environment(object): return self.get_deployment() == 'develop' def get_version(self): - return self._get_from_config('monkey_version', 'unknown') + ('-dev' if self.is_develop() else '') + return self._MONKEY_VERSION + ('-dev' if self.is_develop() else '') def _get_from_config(self, key, default_value=None): val = default_value From 49d55a4b8e00420ac3ccc647f0250dca27d904a7 Mon Sep 17 00:00:00 2001 From: itay Date: Tue, 23 Apr 2019 14:45:30 +0300 Subject: [PATCH 7/8] remove monkey version from server_config.json --- monkey/monkey_island/cc/server_config.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/server_config.json b/monkey/monkey_island/cc/server_config.json index a8df79e3e..420f1b303 100644 --- a/monkey/monkey_island/cc/server_config.json +++ b/monkey/monkey_island/cc/server_config.json @@ -1,5 +1,4 @@ { "server_config": "standard", - "deployment": "develop", - "monkey_version": "1.6.2" + "deployment": "develop" } From b560f4c58fd862772ce32060c00554cb4cae3044 Mon Sep 17 00:00:00 2001 From: itay Date: Tue, 30 Apr 2019 15:22:30 +0300 Subject: [PATCH 8/8] move bson to beginning of requirements add requests to requirements --- monkey/monkey_island/requirements.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/requirements.txt b/monkey/monkey_island/requirements.txt index 0203a1cea..c01d337e2 100644 --- a/monkey/monkey_island/requirements.txt +++ b/monkey/monkey_island/requirements.txt @@ -1,3 +1,4 @@ +bson python-dateutil tornado==5.1.1 werkzeug @@ -16,8 +17,8 @@ enum34 pycryptodome boto3 awscli -bson cffi PyInstaller virtualenv -wheel \ No newline at end of file +wheel +requests