From 02e719f7b24edc0334116fdc8668e554dfe5a8c1 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 11 Aug 2022 15:14:54 +0300 Subject: [PATCH 01/15] Island: Fetch version and download url from new lambda API --- monkey/monkey_island/cc/version.py | 42 +++++++++---------- .../monkey_island/cc/test_version.py | 41 ++++++++++++++++++ 2 files changed, 60 insertions(+), 23 deletions(-) create mode 100644 monkey/tests/unit_tests/monkey_island/cc/test_version.py diff --git a/monkey/monkey_island/cc/version.py b/monkey/monkey_island/cc/version.py index 09c3fefa1..b755b2c84 100644 --- a/monkey/monkey_island/cc/version.py +++ b/monkey/monkey_island/cc/version.py @@ -1,14 +1,13 @@ import logging from threading import Event, Thread +from typing import Optional, Tuple import requests -import semantic_version from .deployment import Deployment -VERSION_SERVER_URL_PREF = "https://updates.infectionmonkey.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" +# TODO get redirects instead of using direct links to AWS +LATEST_VERSION_URL = "https://njf01cuupf.execute-api.us-east-1.amazonaws.com/default?deployment={}" LATEST_VERSION_TIMEOUT = 7 @@ -55,29 +54,26 @@ class Version: return self._download_url def _set_version_metadata(self): - self._latest_version = self._get_latest_version() - self._download_url = self._get_download_link() + self._latest_version, self._download_url = self._get_version_info() self._initialization_complete.set() - def _get_latest_version(self) -> str: - url = VERSION_SERVER_CHECK_NEW_URL % (self._deployment.value, self._version_number) + def _get_version_info(self) -> Tuple[Optional[str], Optional[str]]: + url = LATEST_VERSION_URL.format(self._deployment.value) try: - reply = requests.get(url, timeout=LATEST_VERSION_TIMEOUT) + response = requests.get(url, timeout=LATEST_VERSION_TIMEOUT).json() except requests.exceptions.RequestException as err: - logger.warning(f"Failed to connect to {VERSION_SERVER_URL_PREF}: {err}") - return self._version_number + logger.warning(f"Failed to connect to {url}, Error: {err}") + return None, None - res = reply.json().get("newer_version", None) + try: + download_link = response["download_link"] + latest_version = response["version"] + except KeyError: + logger.error( + f"Failed to fetch version information from {url}, response: {response}." + f"Most likely deployment {self._deployment.value} is not recognized." + ) + return None, None - if res is False: - return self._version_number - - if not semantic_version.validate(res): - logger.warning(f"Recieved invalid version {res} from {VERSION_SERVER_URL_PREF}") - return self._version_number - - return res.strip() - - def _get_download_link(self): - return VERSION_SERVER_DOWNLOAD_URL % (self._deployment.value, self._version_number) + return latest_version, download_link diff --git a/monkey/tests/unit_tests/monkey_island/cc/test_version.py b/monkey/tests/unit_tests/monkey_island/cc/test_version.py new file mode 100644 index 000000000..c7b9e9cad --- /dev/null +++ b/monkey/tests/unit_tests/monkey_island/cc/test_version.py @@ -0,0 +1,41 @@ +from unittest.mock import MagicMock + +import pytest +import requests + +from monkey_island.cc import Version +from monkey_island.cc.deployment import Deployment + +failed_response = MagicMock() +failed_response.return_value.json.return_value = {"message": "Internal server error"} + +successful_response = MagicMock() +SUCCESS_VERSION = "1.1.1" +SUCCESS_URL = "http://be_free.gov" +successful_response.return_value.json.return_value = { + "version": SUCCESS_VERSION, + "download_link": SUCCESS_URL, +} + + +@pytest.mark.parametrize( + "request_mock", + [ + failed_response, + MagicMock(side_effect=requests.exceptions.RequestException("Timeout or something")), + ], +) +def test_version__request_failed(monkeypatch, request_mock): + monkeypatch.setattr("requests.get", request_mock) + version = Version(version_number="1.0.0", deployment=Deployment.DEVELOP) + version._initialization_complete.wait() + assert version.latest_version is None + assert version.download_url is None + + +def test_version__request_successful(monkeypatch): + monkeypatch.setattr("requests.get", successful_response) + version = Version(version_number="1.0.0", deployment=Deployment.DEVELOP) + version._initialization_complete.wait() + assert version.latest_version == SUCCESS_VERSION + assert version.download_url == SUCCESS_URL From 32460bbb3efc3b30ea41cc5856879e538cd10e96 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 11 Aug 2022 17:03:42 +0300 Subject: [PATCH 02/15] Island: Send analytics upon island startup --- monkey/monkey_island/cc/services/analytics.py | 28 +++++++++++++++++++ .../monkey_island/cc/services/initialize.py | 2 ++ monkey/monkey_island/cc/version.py | 1 - 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 monkey/monkey_island/cc/services/analytics.py diff --git a/monkey/monkey_island/cc/services/analytics.py b/monkey/monkey_island/cc/services/analytics.py new file mode 100644 index 000000000..55fc09d0c --- /dev/null +++ b/monkey/monkey_island/cc/services/analytics.py @@ -0,0 +1,28 @@ +import logging + +import requests + +from monkey_island.cc import Version +from monkey_island.cc.deployment import Deployment + +logger = logging.getLogger(__name__) + +ANALYTICS_URL = ( + "https://m15mjynko3.execute-api.us-east-1.amazonaws.com/default?version={" + "version}&deployment={deployment}" +) + + +class Analytics: + def __init__(self, version: Version, deployment: Deployment): + self.version = version.version_number + self.deployment = deployment.value + self._send_analytics() + + def _send_analytics(self): + url = ANALYTICS_URL.format(deployment=self.deployment, version=self.version) + response = requests.get(url).json() + logger.info( + f"Version number and deployment type was sent to analytics server." + f" The response is: {response}" + ) diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py index fc7b6e432..3cfd456c8 100644 --- a/monkey/monkey_island/cc/services/initialize.py +++ b/monkey/monkey_island/cc/services/initialize.py @@ -53,6 +53,7 @@ from monkey_island.cc.services.utils.network_utils import get_ip_addresses from monkey_island.cc.setup.mongo.mongo_setup import MONGO_URL from . import AuthenticationService +from .analytics import Analytics from .reporting.report import ReportService logger = logging.getLogger(__name__) @@ -73,6 +74,7 @@ def initialize_services(data_dir: Path) -> DIContainer: ILockableEncryptor, RepositoryEncryptor(data_dir / REPOSITORY_KEY_FILE_NAME) ) container.register_instance(Version, container.resolve(Version)) + container.register_instance(Analytics, container.resolve(Analytics)) container.register(Publisher, Publisher) container.register_instance(IEventQueue, container.resolve(PyPubSubEventQueue)) diff --git a/monkey/monkey_island/cc/version.py b/monkey/monkey_island/cc/version.py index b755b2c84..bcec8bc6a 100644 --- a/monkey/monkey_island/cc/version.py +++ b/monkey/monkey_island/cc/version.py @@ -8,7 +8,6 @@ from .deployment import Deployment # TODO get redirects instead of using direct links to AWS LATEST_VERSION_URL = "https://njf01cuupf.execute-api.us-east-1.amazonaws.com/default?deployment={}" - LATEST_VERSION_TIMEOUT = 7 logger = logging.getLogger(__name__) From 248cef589a437e30ab6c9169371e206bc1772673 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 11 Aug 2022 17:22:51 +0300 Subject: [PATCH 03/15] Docs: Change performed queried documentation in the FAQ Island now performs 2 queries instead of 1(1 for analytics and 1 for update information) --- docs/content/FAQ/_index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index c3a16e81b..7d2812055 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -81,7 +81,6 @@ Monkey in the newly created folder. ## Reset the Monkey Island password - {{% notice warning %}} If you reset the credentials, the database will be cleared. Any findings of the Infection Monkey from previous runs will be lost.

However, you can save the Monkey's existing configuration by logging in with your current credentials and clicking on the **Export config** button on the configuration page. @@ -161,7 +160,8 @@ If internet access is available, the Infection Monkey will use the internet for The Monkey performs queries out to the Internet on two separate occasions: 1. The Infection Monkey agent checks if it has internet access by performing requests to pre-configured domains. By default, these domains are `monkey.guardicore.com` and `www.google.com`, which can be changed. The request doesn't include any extra information - it's a GET request with no extra parameters. Since the Infection Monkey is 100% open-source, you can find the domains in the configuration [here](https://github.com/guardicore/monkey/blob/85c70a3e7125217c45c751d89205e95985b279eb/monkey/infection_monkey/config.py#L152) and the code that performs the internet check [here](https://github.com/guardicore/monkey/blob/85c70a3e7125217c45c751d89205e95985b279eb/monkey/infection_monkey/network/info.py#L123). This **IS NOT** used for statistics collection. -1. After installing the Monkey Island, it sends a request to check for updates on `updates.infectionmonkey.com`. The request doesn't include any PII other than the IP address of the request. It also includes the server's deployment type (e.g., Windows Server, AppImage, Docker) and the server's version (e.g., "1.6.3"), so we can check if we have an update available for this type of deployment. Since the Infection Monkey is 100% open-source, you can inspect the code that performs this [here](https://github.com/guardicore/monkey/blob/85c70a3e7125217c45c751d89205e95985b279eb/monkey/monkey_island/cc/services/version_update.py#L37). This **IS** used for statistics collection. However, due to this data's anonymous nature, we use this to get an aggregate assumption of how many deployments we see over a specific time period - it's not used for "personal" tracking. +1. After the Monkey Island launch it sends a GET request with current deployment type to the update server to fetch the latest version and a download link for it. This information is used by the Monkey Island to suggest an update if one is available. No information gets collected during this process. +1. After the Monkey Island launch it sends a GET request to the analytics server with your deployment type and a version number. This information gets collected on the analytics server. It is used to understand which deployment types/versions are no longer used and can be deprecated. ## Logging and how to find logs From 5ffec0eb4f6d29c649660884f7f76573bae0a759 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 11 Aug 2022 17:27:01 +0300 Subject: [PATCH 04/15] Changelog: Add entry about separating version and analytics requests --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f78b06508..5a11f5552 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/). - The "/api/netmap/nodeStates" endpoint to "/api/netmap/node-states". #1888 - All "/api/monkey_control" endpoints to "/api/monkey-control". #1888 - All "/api/monkey" endpoints to "/api/agent". #1888 +- Analytics and version update queries are sent separately instead of just one query. #2165 - Update MongoDB version to 4.4.x. #1924 - Endpoint to get agent binaries from "/api/agent/download/" to "/api/agent-binaries/". #1978 From bec7bef1b323344adb6d19090a10dfd4bf74d6a7 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Thu, 11 Aug 2022 17:57:07 +0300 Subject: [PATCH 05/15] Island: Return current version number instead of None --- monkey/monkey_island/cc/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/version.py b/monkey/monkey_island/cc/version.py index bcec8bc6a..a67fd440d 100644 --- a/monkey/monkey_island/cc/version.py +++ b/monkey/monkey_island/cc/version.py @@ -63,7 +63,7 @@ class Version: response = requests.get(url, timeout=LATEST_VERSION_TIMEOUT).json() except requests.exceptions.RequestException as err: logger.warning(f"Failed to connect to {url}, Error: {err}") - return None, None + return self._version_number, None try: download_link = response["download_link"] @@ -73,6 +73,6 @@ class Version: f"Failed to fetch version information from {url}, response: {response}." f"Most likely deployment {self._deployment.value} is not recognized." ) - return None, None + return self._version_number, None return latest_version, download_link From c61737abc0c029f6dd6106a3d04de476c8887f85 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Fri, 12 Aug 2022 09:32:50 +0300 Subject: [PATCH 06/15] Island: Send analytics on island startup instead of object creation This change makes the logic more evident, because sending the analytics is not done during the initialization of an object --- monkey/monkey_island/cc/server_setup.py | 26 +++++++++++++++++ monkey/monkey_island/cc/services/analytics.py | 28 ------------------- .../monkey_island/cc/services/initialize.py | 2 -- 3 files changed, 26 insertions(+), 30 deletions(-) delete mode 100644 monkey/monkey_island/cc/services/analytics.py diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index eed949a02..c8d9985fe 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -5,8 +5,11 @@ import sys from pathlib import Path import gevent.hub +import requests from gevent.pywsgi import WSGIServer +from monkey_island.cc import Version +from monkey_island.cc.deployment import Deployment from monkey_island.cc.server_utils.consts import ISLAND_PORT from monkey_island.cc.setup.config_setup import get_server_config @@ -150,6 +153,7 @@ def _start_island_server( error_log=logger, ) _log_init_info() + _send_analytics(container) http_server.serve_forever() @@ -173,3 +177,25 @@ def _log_web_interface_access_urls(): "To access the web interface, navigate to one of the the following URLs using your " f"browser: {web_interface_urls}" ) + + +ANALYTICS_URL = ( + "https://m15mjynko3.execute-api.us-east-1.amazonaws.com/default?version={" + "version}&deployment={deployment}" +) + + +def _send_analytics(di_container): + version = di_container.resolve(Version) + deployment = di_container.resolve(Deployment) + url = ANALYTICS_URL.format(deployment=deployment.value, version=version.version_number) + try: + response = requests.get(url).json() + logger.info( + f"Version number and deployment type was sent to analytics server." + f" The response is: {response}" + ) + except requests.exceptions.ConnectionError as err: + logger.info( + f"Failed to send deployment type and version " f"number to the analytics server: {err}" + ) diff --git a/monkey/monkey_island/cc/services/analytics.py b/monkey/monkey_island/cc/services/analytics.py deleted file mode 100644 index 55fc09d0c..000000000 --- a/monkey/monkey_island/cc/services/analytics.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -import requests - -from monkey_island.cc import Version -from monkey_island.cc.deployment import Deployment - -logger = logging.getLogger(__name__) - -ANALYTICS_URL = ( - "https://m15mjynko3.execute-api.us-east-1.amazonaws.com/default?version={" - "version}&deployment={deployment}" -) - - -class Analytics: - def __init__(self, version: Version, deployment: Deployment): - self.version = version.version_number - self.deployment = deployment.value - self._send_analytics() - - def _send_analytics(self): - url = ANALYTICS_URL.format(deployment=self.deployment, version=self.version) - response = requests.get(url).json() - logger.info( - f"Version number and deployment type was sent to analytics server." - f" The response is: {response}" - ) diff --git a/monkey/monkey_island/cc/services/initialize.py b/monkey/monkey_island/cc/services/initialize.py index 3cfd456c8..fc7b6e432 100644 --- a/monkey/monkey_island/cc/services/initialize.py +++ b/monkey/monkey_island/cc/services/initialize.py @@ -53,7 +53,6 @@ from monkey_island.cc.services.utils.network_utils import get_ip_addresses from monkey_island.cc.setup.mongo.mongo_setup import MONGO_URL from . import AuthenticationService -from .analytics import Analytics from .reporting.report import ReportService logger = logging.getLogger(__name__) @@ -74,7 +73,6 @@ def initialize_services(data_dir: Path) -> DIContainer: ILockableEncryptor, RepositoryEncryptor(data_dir / REPOSITORY_KEY_FILE_NAME) ) container.register_instance(Version, container.resolve(Version)) - container.register_instance(Analytics, container.resolve(Analytics)) container.register(Publisher, Publisher) container.register_instance(IEventQueue, container.resolve(PyPubSubEventQueue)) From 737070f95615d23ea0f702f4aff2b03bc23e19d6 Mon Sep 17 00:00:00 2001 From: vakarisz Date: Fri, 12 Aug 2022 09:50:28 +0300 Subject: [PATCH 07/15] Island, UI: Handle the case when version couldn't be fetched --- .../cc/ui/src/components/logo/VersionComponent.js | 5 +++++ monkey/monkey_island/cc/version.py | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/logo/VersionComponent.js b/monkey/monkey_island/cc/ui/src/components/logo/VersionComponent.js index fe93158ec..4fbb3fbb9 100644 --- a/monkey/monkey_island/cc/ui/src/components/logo/VersionComponent.js +++ b/monkey/monkey_island/cc/ui/src/components/logo/VersionComponent.js @@ -28,6 +28,11 @@ class VersionComponent extends AuthComponent { newerVersionAvailable() { const semverGt = require('semver/functions/gt'); + if(this.state.latestVersion === null) { + // Couldn't fetch the latest version. + // Consider showing this state to the user + return false; + } if(this.state.latestVersion !== undefined && this.state.versionNumber !== undefined) { return semverGt(this.state.latestVersion, this.state.versionNumber); } diff --git a/monkey/monkey_island/cc/version.py b/monkey/monkey_island/cc/version.py index a67fd440d..bcec8bc6a 100644 --- a/monkey/monkey_island/cc/version.py +++ b/monkey/monkey_island/cc/version.py @@ -63,7 +63,7 @@ class Version: response = requests.get(url, timeout=LATEST_VERSION_TIMEOUT).json() except requests.exceptions.RequestException as err: logger.warning(f"Failed to connect to {url}, Error: {err}") - return self._version_number, None + return None, None try: download_link = response["download_link"] @@ -73,6 +73,6 @@ class Version: f"Failed to fetch version information from {url}, response: {response}." f"Most likely deployment {self._deployment.value} is not recognized." ) - return self._version_number, None + return None, None return latest_version, download_link From 2e70b87ee9d1d276d40ec2fb8cd825ff5580c01e Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 12 Aug 2022 10:15:44 -0400 Subject: [PATCH 08/15] Revert "Island, UI: Handle the case when version couldn't be fetched" This reverts commit 737070f95615d23ea0f702f4aff2b03bc23e19d6. --- .../cc/ui/src/components/logo/VersionComponent.js | 5 ----- monkey/monkey_island/cc/version.py | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/monkey/monkey_island/cc/ui/src/components/logo/VersionComponent.js b/monkey/monkey_island/cc/ui/src/components/logo/VersionComponent.js index 4fbb3fbb9..fe93158ec 100644 --- a/monkey/monkey_island/cc/ui/src/components/logo/VersionComponent.js +++ b/monkey/monkey_island/cc/ui/src/components/logo/VersionComponent.js @@ -28,11 +28,6 @@ class VersionComponent extends AuthComponent { newerVersionAvailable() { const semverGt = require('semver/functions/gt'); - if(this.state.latestVersion === null) { - // Couldn't fetch the latest version. - // Consider showing this state to the user - return false; - } if(this.state.latestVersion !== undefined && this.state.versionNumber !== undefined) { return semverGt(this.state.latestVersion, this.state.versionNumber); } diff --git a/monkey/monkey_island/cc/version.py b/monkey/monkey_island/cc/version.py index bcec8bc6a..a67fd440d 100644 --- a/monkey/monkey_island/cc/version.py +++ b/monkey/monkey_island/cc/version.py @@ -63,7 +63,7 @@ class Version: response = requests.get(url, timeout=LATEST_VERSION_TIMEOUT).json() except requests.exceptions.RequestException as err: logger.warning(f"Failed to connect to {url}, Error: {err}") - return None, None + return self._version_number, None try: download_link = response["download_link"] @@ -73,6 +73,6 @@ class Version: f"Failed to fetch version information from {url}, response: {response}." f"Most likely deployment {self._deployment.value} is not recognized." ) - return None, None + return self._version_number, None return latest_version, download_link From 6dc29e36e2736ae7a596e85bf1c36d1679316bcf Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 12 Aug 2022 10:16:46 -0400 Subject: [PATCH 09/15] UT: Fix test_version__request_failed() --- monkey/tests/unit_tests/monkey_island/cc/test_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/test_version.py b/monkey/tests/unit_tests/monkey_island/cc/test_version.py index c7b9e9cad..7fc8ca38b 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/test_version.py +++ b/monkey/tests/unit_tests/monkey_island/cc/test_version.py @@ -29,7 +29,7 @@ def test_version__request_failed(monkeypatch, request_mock): monkeypatch.setattr("requests.get", request_mock) version = Version(version_number="1.0.0", deployment=Deployment.DEVELOP) version._initialization_complete.wait() - assert version.latest_version is None + assert version.latest_version == "1.0.0" assert version.download_url is None From 617d101af2e6bce6dcdc6f4a35789aa30542f27b Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 12 Aug 2022 10:20:30 -0400 Subject: [PATCH 10/15] Island: Fix string formatting in _send_analytics() --- monkey/monkey_island/cc/server_setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monkey/monkey_island/cc/server_setup.py b/monkey/monkey_island/cc/server_setup.py index c8d9985fe..418074557 100644 --- a/monkey/monkey_island/cc/server_setup.py +++ b/monkey/monkey_island/cc/server_setup.py @@ -192,10 +192,10 @@ def _send_analytics(di_container): try: response = requests.get(url).json() logger.info( - f"Version number and deployment type was sent to analytics server." - f" The response is: {response}" + f"Version number and deployment type was sent to analytics server. " + f"The response is: {response}" ) except requests.exceptions.ConnectionError as err: logger.info( - f"Failed to send deployment type and version " f"number to the analytics server: {err}" + f"Failed to send deployment type and version number to the analytics server: {err}" ) From df1b9f0f9cba538525940a1976d3dbe920451dca Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 12 Aug 2022 10:22:22 -0400 Subject: [PATCH 11/15] Island: Fix return type hint for Version._get_version_info() --- monkey/monkey_island/cc/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/version.py b/monkey/monkey_island/cc/version.py index a67fd440d..b3896e66a 100644 --- a/monkey/monkey_island/cc/version.py +++ b/monkey/monkey_island/cc/version.py @@ -56,7 +56,7 @@ class Version: self._latest_version, self._download_url = self._get_version_info() self._initialization_complete.set() - def _get_version_info(self) -> Tuple[Optional[str], Optional[str]]: + def _get_version_info(self) -> Tuple[str, Optional[str]]: url = LATEST_VERSION_URL.format(self._deployment.value) try: From 94a25b07b2895983995da0cff6a7b2859d582700 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 12 Aug 2022 10:24:29 -0400 Subject: [PATCH 12/15] Island: Simplify error messages in Version --- monkey/monkey_island/cc/version.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/monkey/monkey_island/cc/version.py b/monkey/monkey_island/cc/version.py index b3896e66a..b7dc82f16 100644 --- a/monkey/monkey_island/cc/version.py +++ b/monkey/monkey_island/cc/version.py @@ -62,17 +62,14 @@ class Version: try: response = requests.get(url, timeout=LATEST_VERSION_TIMEOUT).json() except requests.exceptions.RequestException as err: - logger.warning(f"Failed to connect to {url}, Error: {err}") + logger.warning(f"Failed to connect to {url}: {err}") return self._version_number, None try: download_link = response["download_link"] latest_version = response["version"] except KeyError: - logger.error( - f"Failed to fetch version information from {url}, response: {response}." - f"Most likely deployment {self._deployment.value} is not recognized." - ) + logger.error(f"Failed to fetch version information from {url}: {response}") return self._version_number, None return latest_version, download_link From 658607de25e0c821501baf325bceeef923c9de77 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 12 Aug 2022 10:26:11 -0400 Subject: [PATCH 13/15] UT: Remove unnecessary wait() from test_version Unit tests should not be exposed to the internals of what they are testing. Furthermore, the `latest_version` and `download` properties wait for the event to be set, making the extra `wait()` redundant. --- monkey/tests/unit_tests/monkey_island/cc/test_version.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/monkey/tests/unit_tests/monkey_island/cc/test_version.py b/monkey/tests/unit_tests/monkey_island/cc/test_version.py index 7fc8ca38b..51c5def58 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/test_version.py +++ b/monkey/tests/unit_tests/monkey_island/cc/test_version.py @@ -27,15 +27,17 @@ successful_response.return_value.json.return_value = { ) def test_version__request_failed(monkeypatch, request_mock): monkeypatch.setattr("requests.get", request_mock) + version = Version(version_number="1.0.0", deployment=Deployment.DEVELOP) - version._initialization_complete.wait() + assert version.latest_version == "1.0.0" assert version.download_url is None def test_version__request_successful(monkeypatch): monkeypatch.setattr("requests.get", successful_response) + version = Version(version_number="1.0.0", deployment=Deployment.DEVELOP) - version._initialization_complete.wait() + assert version.latest_version == SUCCESS_VERSION assert version.download_url == SUCCESS_URL From 8788b258dd11b737626f0d3720b57a889f8af129 Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 12 Aug 2022 10:29:42 -0400 Subject: [PATCH 14/15] Docs: Change "launch" to "starts" --- docs/content/FAQ/_index.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/content/FAQ/_index.md b/docs/content/FAQ/_index.md index 7d2812055..5ed4c5c27 100644 --- a/docs/content/FAQ/_index.md +++ b/docs/content/FAQ/_index.md @@ -159,9 +159,25 @@ If internet access is available, the Infection Monkey will use the internet for The Monkey performs queries out to the Internet on two separate occasions: -1. The Infection Monkey agent checks if it has internet access by performing requests to pre-configured domains. By default, these domains are `monkey.guardicore.com` and `www.google.com`, which can be changed. The request doesn't include any extra information - it's a GET request with no extra parameters. Since the Infection Monkey is 100% open-source, you can find the domains in the configuration [here](https://github.com/guardicore/monkey/blob/85c70a3e7125217c45c751d89205e95985b279eb/monkey/infection_monkey/config.py#L152) and the code that performs the internet check [here](https://github.com/guardicore/monkey/blob/85c70a3e7125217c45c751d89205e95985b279eb/monkey/infection_monkey/network/info.py#L123). This **IS NOT** used for statistics collection. -1. After the Monkey Island launch it sends a GET request with current deployment type to the update server to fetch the latest version and a download link for it. This information is used by the Monkey Island to suggest an update if one is available. No information gets collected during this process. -1. After the Monkey Island launch it sends a GET request to the analytics server with your deployment type and a version number. This information gets collected on the analytics server. It is used to understand which deployment types/versions are no longer used and can be deprecated. +1. The Infection Monkey agent checks if it has internet access by performing + requests to pre-configured domains. By default, these domains are + `monkey.guardicore.com` and `www.google.com`, which can be changed. The + request doesn't include any extra information - it's a GET request with no + extra parameters. Since the Infection Monkey is 100% open-source, you can + find the domains in the configuration + [here](https://github.com/guardicore/monkey/blob/85c70a3e7125217c45c751d89205e95985b279eb/monkey/infection_monkey/config.py#L152) + and the code that performs the internet check + [here](https://github.com/guardicore/monkey/blob/85c70a3e7125217c45c751d89205e95985b279eb/monkey/infection_monkey/network/info.py#L123). + This **IS NOT** used for statistics collection. +1. After the Monkey Island starts it sends a GET request with current + deployment type to the update server to fetch the latest version and a + download link for it. This information is used by the Monkey Island to + suggest an update if one is available. No information gets collected during + this process. +1. After the Monkey Island starts it sends a GET request to the analytics + server with your deployment type and a version number. This information gets + collected on the analytics server. It is used to understand which deployment + types/versions are no longer used and can be deprecated. ## Logging and how to find logs From af7eb23befb863cee65f4babe6b1c5fb152e97de Mon Sep 17 00:00:00 2001 From: Mike Salvatore Date: Fri, 12 Aug 2022 10:31:30 -0400 Subject: [PATCH 15/15] Island: Reword warning in Version --- monkey/monkey_island/cc/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monkey/monkey_island/cc/version.py b/monkey/monkey_island/cc/version.py index b7dc82f16..093549b1c 100644 --- a/monkey/monkey_island/cc/version.py +++ b/monkey/monkey_island/cc/version.py @@ -62,7 +62,7 @@ class Version: try: response = requests.get(url, timeout=LATEST_VERSION_TIMEOUT).json() except requests.exceptions.RequestException as err: - logger.warning(f"Failed to connect to {url}: {err}") + logger.warning(f"Failed to fetch version information from {url}: {err}") return self._version_number, None try: