Agent: Add should_agent_stop to IslandAPIClient

This commit is contained in:
Kekoa Kaaikala 2022-09-19 17:09:04 +00:00
parent 54ef77698c
commit 92e793c2cd
4 changed files with 97 additions and 18 deletions

View File

@ -1,4 +1,5 @@
import functools import functools
import json
import logging import logging
from typing import List, Sequence from typing import List, Sequence
@ -139,6 +140,33 @@ class HTTPIslandAPIClient(IIslandAPIClient):
except requests.exceptions.Timeout as e: except requests.exceptions.Timeout as e:
raise IslandAPITimeoutError(e) raise IslandAPITimeoutError(e)
def should_agent_stop(self, island_server: str, agent_id: str) -> bool:
try:
url = f"https://{island_server}/api/monkey-control" f"/needs-to-stop/{agent_id}"
response = requests.get( # noqa: DUO123
url,
verify=False,
timeout=SHORT_REQUEST_TIMEOUT,
)
response.raise_for_status()
json_response = json.loads(response.content.decode())
return json_response["stop_agent"]
except (
requests.exceptions.ConnectionError,
requests.exceptions.TooManyRedirects,
) as e:
raise IslandAPIConnectionError(e)
except requests.exceptions.Timeout as e:
raise IslandAPITimeoutError(e)
except requests.exceptions.HTTPError as e:
if e.errno >= 500:
raise IslandAPIRequestFailedError(e)
else:
raise IslandAPIRequestError(e)
except json.JSONDecodeError as e:
raise IslandAPIRequestFailedError(e)
def _serialize_events(self, events: Sequence[AbstractAgentEvent]) -> JSONSerializable: def _serialize_events(self, events: Sequence[AbstractAgentEvent]) -> JSONSerializable:
serialized_events: List[JSONSerializable] = [] serialized_events: List[JSONSerializable] = []

View File

@ -104,3 +104,17 @@ class IIslandAPIClient(ABC):
:raises IslandAPIConnectionError: If the client could not connect to the island :raises IslandAPIConnectionError: If the client could not connect to the island
:raises IslandAPITimeoutError: If the command timed out :raises IslandAPITimeoutError: If the command timed out
""" """
@abstractmethod
def should_agent_stop(self, island_server: str, agent_id: str) -> bool:
"""
Check with the island to see if the agent should stop
:param island_server: The server to query
:param agent_id: The agent identifier for the agent to check
:raises IslandAPIConnectionError: If the client could not connect to the island
:raises IslandAPIRequestError: If there was a problem with the client request
:raises IslandAPIRequestFailedError: If the server experienced an error
:raises IslandAPITimeoutError: If the command timed out
:return: True if the agent should stop, otherwise False
"""

View File

@ -16,6 +16,8 @@ from infection_monkey.i_control_channel import IControlChannel, IslandCommunicat
from infection_monkey.island_api_client import ( from infection_monkey.island_api_client import (
IIslandAPIClient, IIslandAPIClient,
IslandAPIConnectionError, IslandAPIConnectionError,
IslandAPIRequestError,
IslandAPIRequestFailedError,
IslandAPITimeoutError, IslandAPITimeoutError,
) )
from infection_monkey.utils import agent_process from infection_monkey.utils import agent_process
@ -53,25 +55,14 @@ class ControlChannel(IControlChannel):
logger.error("Agent should stop because it can't connect to the C&C server.") logger.error("Agent should stop because it can't connect to the C&C server.")
return True return True
try: try:
url = ( return self._island_api_client.should_agent_stop(
f"https://{self._control_channel_server}/api/monkey-control" self._control_channel_server, self._agent_id
f"/needs-to-stop/{self._agent_id}"
) )
response = requests.get( # noqa: DUO123
url,
verify=False,
timeout=SHORT_REQUEST_TIMEOUT,
)
response.raise_for_status()
json_response = json.loads(response.content.decode())
return json_response["stop_agent"]
except ( except (
json.JSONDecodeError, IslandAPIConnectionError,
requests.exceptions.ConnectionError, IslandAPIRequestError,
requests.exceptions.Timeout, IslandAPIRequestFailedError,
requests.exceptions.TooManyRedirects, IslandAPITimeoutError,
requests.exceptions.HTTPError,
) as e: ) as e:
raise IslandCommunicationError(e) raise IslandCommunicationError(e)

View File

@ -6,10 +6,15 @@ from infection_monkey.i_control_channel import IslandCommunicationError
from infection_monkey.island_api_client import ( from infection_monkey.island_api_client import (
IIslandAPIClient, IIslandAPIClient,
IslandAPIConnectionError, IslandAPIConnectionError,
IslandAPIRequestError,
IslandAPIRequestFailedError,
IslandAPITimeoutError, IslandAPITimeoutError,
) )
from infection_monkey.master.control_channel import ControlChannel from infection_monkey.master.control_channel import ControlChannel
SERVER = "server"
AGENT_ID = "agent"
@pytest.fixture @pytest.fixture
def island_api_client() -> IIslandAPIClient: def island_api_client() -> IIslandAPIClient:
@ -19,7 +24,7 @@ def island_api_client() -> IIslandAPIClient:
@pytest.fixture @pytest.fixture
def control_channel(island_api_client) -> ControlChannel: def control_channel(island_api_client) -> ControlChannel:
return ControlChannel("server", "agent-id", island_api_client) return ControlChannel(SERVER, AGENT_ID, island_api_client)
def test_control_channel__register_agent(control_channel, island_api_client): def test_control_channel__register_agent(control_channel, island_api_client):
@ -43,3 +48,44 @@ def test_control_channel__register_agent_raises_on_timeout_error(
with pytest.raises(IslandCommunicationError): with pytest.raises(IslandCommunicationError):
control_channel.register_agent() control_channel.register_agent()
def test_control_channel__should_agent_stop(control_channel, island_api_client):
control_channel.should_agent_stop()
assert island_api_client.should_agent_stop.called_once()
def test_control_channel__should_agent_stop_raises_on_connection_error(
control_channel, island_api_client
):
island_api_client.should_agent_stop.side_effect = IslandAPIConnectionError()
with pytest.raises(IslandCommunicationError):
control_channel.should_agent_stop()
def test_control_channel__should_agent_stop_raises_on_timeout_error(
control_channel, island_api_client
):
island_api_client.should_agent_stop.side_effect = IslandAPITimeoutError()
with pytest.raises(IslandCommunicationError):
control_channel.should_agent_stop()
def test_control_channel__should_agent_stop_raises_on_request_error(
control_channel, island_api_client
):
island_api_client.should_agent_stop.side_effect = IslandAPIRequestError()
with pytest.raises(IslandCommunicationError):
control_channel.should_agent_stop()
def test_control_channel__should_agent_stop_raises_on_request_failed_error(
control_channel, island_api_client
):
island_api_client.should_agent_stop.side_effect = IslandAPIRequestFailedError()
with pytest.raises(IslandCommunicationError):
control_channel.should_agent_stop()