forked from p15670423/monkey
Merge pull request #2188 from guardicore/2165-labda-decoupling
2165 lambda decoupling
This commit is contained in:
commit
82c7782ff0
|
@ -40,6 +40,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- The "/api/netmap/nodeStates" endpoint to "/api/netmap/node-states". #1888
|
- The "/api/netmap/nodeStates" endpoint to "/api/netmap/node-states". #1888
|
||||||
- All "/api/monkey_control" endpoints to "/api/monkey-control". #1888
|
- All "/api/monkey_control" endpoints to "/api/monkey-control". #1888
|
||||||
- All "/api/monkey" endpoints to "/api/agent". #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
|
- Update MongoDB version to 4.4.x. #1924
|
||||||
- Endpoint to get agent binaries from "/api/agent/download/<string:os>" to
|
- Endpoint to get agent binaries from "/api/agent/download/<string:os>" to
|
||||||
"/api/agent-binaries/<string:os>". #1978
|
"/api/agent-binaries/<string:os>". #1978
|
||||||
|
|
|
@ -81,7 +81,6 @@ Monkey in the newly created folder.
|
||||||
|
|
||||||
## Reset the Monkey Island password
|
## Reset the Monkey Island password
|
||||||
|
|
||||||
|
|
||||||
{{% notice warning %}}
|
{{% notice warning %}}
|
||||||
If you reset the credentials, the database will be cleared. Any findings of the Infection Monkey from previous runs will be lost. <br/><br/>
|
If you reset the credentials, the database will be cleared. Any findings of the Infection Monkey from previous runs will be lost. <br/><br/>
|
||||||
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.
|
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.
|
||||||
|
@ -160,8 +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:
|
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. The Infection Monkey agent checks if it has internet access by performing
|
||||||
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.
|
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
|
## Logging and how to find logs
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,11 @@ import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import gevent.hub
|
import gevent.hub
|
||||||
|
import requests
|
||||||
from gevent.pywsgi import WSGIServer
|
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.server_utils.consts import ISLAND_PORT
|
||||||
from monkey_island.cc.setup.config_setup import get_server_config
|
from monkey_island.cc.setup.config_setup import get_server_config
|
||||||
|
|
||||||
|
@ -150,6 +153,7 @@ def _start_island_server(
|
||||||
error_log=logger,
|
error_log=logger,
|
||||||
)
|
)
|
||||||
_log_init_info()
|
_log_init_info()
|
||||||
|
_send_analytics(container)
|
||||||
http_server.serve_forever()
|
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 "
|
"To access the web interface, navigate to one of the the following URLs using your "
|
||||||
f"browser: {web_interface_urls}"
|
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 number to the analytics server: {err}"
|
||||||
|
)
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import logging
|
import logging
|
||||||
from threading import Event, Thread
|
from threading import Event, Thread
|
||||||
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import semantic_version
|
|
||||||
|
|
||||||
from .deployment import Deployment
|
from .deployment import Deployment
|
||||||
|
|
||||||
VERSION_SERVER_URL_PREF = "https://updates.infectionmonkey.com"
|
# TODO get redirects instead of using direct links to AWS
|
||||||
VERSION_SERVER_CHECK_NEW_URL = VERSION_SERVER_URL_PREF + "?deployment=%s&monkey_version=%s"
|
LATEST_VERSION_URL = "https://njf01cuupf.execute-api.us-east-1.amazonaws.com/default?deployment={}"
|
||||||
VERSION_SERVER_DOWNLOAD_URL = VERSION_SERVER_CHECK_NEW_URL + "&is_download=true"
|
|
||||||
|
|
||||||
LATEST_VERSION_TIMEOUT = 7
|
LATEST_VERSION_TIMEOUT = 7
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -55,29 +53,23 @@ class Version:
|
||||||
return self._download_url
|
return self._download_url
|
||||||
|
|
||||||
def _set_version_metadata(self):
|
def _set_version_metadata(self):
|
||||||
self._latest_version = self._get_latest_version()
|
self._latest_version, self._download_url = self._get_version_info()
|
||||||
self._download_url = self._get_download_link()
|
|
||||||
self._initialization_complete.set()
|
self._initialization_complete.set()
|
||||||
|
|
||||||
def _get_latest_version(self) -> str:
|
def _get_version_info(self) -> Tuple[str, Optional[str]]:
|
||||||
url = VERSION_SERVER_CHECK_NEW_URL % (self._deployment.value, self._version_number)
|
url = LATEST_VERSION_URL.format(self._deployment.value)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
reply = requests.get(url, timeout=LATEST_VERSION_TIMEOUT)
|
response = requests.get(url, timeout=LATEST_VERSION_TIMEOUT).json()
|
||||||
except requests.exceptions.RequestException as err:
|
except requests.exceptions.RequestException as err:
|
||||||
logger.warning(f"Failed to connect to {VERSION_SERVER_URL_PREF}: {err}")
|
logger.warning(f"Failed to fetch version information from {url}: {err}")
|
||||||
return self._version_number
|
return self._version_number, 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}")
|
||||||
|
return self._version_number, None
|
||||||
|
|
||||||
if res is False:
|
return latest_version, download_link
|
||||||
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)
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
assert version.latest_version == SUCCESS_VERSION
|
||||||
|
assert version.download_url == SUCCESS_URL
|
Loading…
Reference in New Issue