Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Onboarding MDE for Linux to LISA #3113

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Linter
  • Loading branch information
Zeeshan Akhter committed Dec 22, 2023
commit bf6866738dce3af545094eede3e1713a286d962e
1 change: 1 addition & 0 deletions lisa/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -180,6 +180,7 @@
"Lsvmbus",
"Make",
"Mdadm",
"MDE",
"Mkdir",
"Mkfs",
"Mkfsext",
44 changes: 25 additions & 19 deletions lisa/tools/mde.py
Original file line number Diff line number Diff line change
@@ -4,9 +4,11 @@
import json
from typing import Any

from lisa.base_tools import Wget
from lisa.executable import Tool

from .chmod import Chmod
from lisa.base_tools import Wget


class MDE(Tool):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used by MDE test suite only, so it can be in the same file with the test suite.

@property
@@ -18,30 +20,33 @@ def can_install(self) -> bool:
return True

def get_mde_installer(self) -> bool:
if not hasattr(self, 'mde_installer'):
if not hasattr(self, "mde_installer"):
wget = self.node.tools[Wget]

download_path = wget.get(
url="https://raw.githubusercontent.com/microsoft/mdatp-xplat/master/linux/installation/mde_installer.sh",
filename="mde_installer.sh",
url="https://raw.githubusercontent.com/microsoft/mdatp-xplat/"
"master/linux/installation/mde_installer.sh",
filename="mde_installer.sh",
)
self.mde_installer = download_path
self._log.info(self.mde_installer)
self.node.tools[Chmod].update_folder(
self.mde_installer, "777", sudo=True)
self.node.tools[Chmod].update_folder(self.mde_installer, "777", sudo=True)
return True

def _install(self) -> bool:
if not self.get_mde_installer():
self._log.error("Unable to download mde_installer.sh script. MDE can't be installed")
self._log.error(
"Unable to download mde_installer.sh script. MDE can't be installed"
)

self._log.info('Installing MDE')
result1 = self.node.execute(f"{self.mde_installer} --install", shell=True, sudo=True)
self._log.info("Installing MDE")
result1 = self.node.execute(
f"{self.mde_installer} --install", shell=True, sudo=True
)
self._log.info(result1)

return self._check_exists()


def onboard(self, onboarding_script_sas_uri: str) -> bool:
if not self._check_exists():
self._log.error("MDE is not installed, onboarding not possible")
@@ -55,18 +60,21 @@ def onboard(self, onboarding_script_sas_uri: str) -> bool:
)

if not self.get_mde_installer():
self._log.error("Unable to download mde_installer.sh script. MDE can't be onboarded")
self._log.error(
"Unable to download mde_installer.sh script. MDE can't be onboarded"
)

self._log.info('Onboarding MDE')
result1 = self.node.execute(f"{self.mde_installer} --onboard {download_path}", shell=True, sudo=True)
self._log.info("Onboarding MDE")
result1 = self.node.execute(
f"{self.mde_installer} --onboard {download_path}", shell=True, sudo=True
)
self._log.info(result1)

output = self.get_result('health --field licensed')
output = self.get_result("health --field licensed")

self._log.info(output)

return bool(output == ['true'])

return bool(output == ["true"])

def get_result(
self,
@@ -75,7 +83,7 @@ def get_result(
sudo: bool = False,
) -> Any:
if json_out:
arg += ' --output json'
arg += " --output json"
result = self.run(
arg,
sudo=sudo,
@@ -87,5 +95,3 @@ def get_result(
if json_out:
return json.loads(result.stdout)
return result.stdout.split()


99 changes: 57 additions & 42 deletions microsoft/testsuites/vm_extensions/mde.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import time

from typing import Any
from pathlib import PurePath

from assertpy import assert_that

@@ -15,20 +13,24 @@
)
from lisa.operating_system import BSD
from lisa.testsuite import TestResult
from lisa.tools import Curl, MDE as mdatp
from lisa.tools import MDE, Curl
from lisa.util import LisaException, SkippedException


@TestSuiteMetadata(
area="vm_extension",
category="functional",
description="""
Verify MDE installation
Microsoft Defender for Endpoint(MDE) for Linux includes antimalware and endpoint detection and response (EDR) capabilities.
Microsoft Defender for Endpoint(MDE) for Linux includes
antimalware and endpoint detection and response (EDR) capabilities.

This test suites validates if MDE can be installed, onboarded and detect an EICAR file.
This test suites validates if MDE can be installed, onboarded
and detect an EICAR file.

The test requires the onboarding script to be kept in Azure Storage Account and provide the SAS url for downloading
under the secret variable `onboarding_script_sas_uri`.
The test requires the onboarding script to be kept in Azure Storage Account
and provide the SAS url for downloading under the
secret variable `onboarding_script_sas_uri`.

The suite runs the following tests:
1. Installation test
@@ -37,8 +39,7 @@
4. EICAR detection test
""",
)
class MDE(TestSuite):

class MDETest(TestSuite):
def before_case(self, log: Logger, **kwargs: Any) -> None:
variables = kwargs["variables"]
self.onboarding_script_sas_uri = variables.get("onboarding_script_sas_uri", "")
@@ -50,21 +51,20 @@ def before_case(self, log: Logger, **kwargs: Any) -> None:
Verify MDE installation, onboarding, health and EICAR detection.
""",
priority=1,
requirement=simple_requirement(min_core_count=2,
min_memory_mb=1024,
unsupported_os=[BSD])
requirement=simple_requirement(
min_core_count=2, min_memory_mb=1024, unsupported_os=[BSD]
),
)
def verify_mde(self, node: Node, log: Logger, result: TestResult) -> None:

#Invoking tools first time, intalls the tool.
# Invoking tools first time, intalls the tool.
try:
node.tools[mdatp]
node.tools[MDE]
output = True
except LisaException as e:
log.error(e)
output = False

assert_that(output).described_as('Unable to install MDE').is_equal_to(True)
assert_that(output).described_as("Unable to install MDE").is_equal_to(True)

self.verify_onboard(node, log, result)

@@ -73,45 +73,60 @@ def verify_mde(self, node: Node, log: Logger, result: TestResult) -> None:
self.verify_eicar_detection(node, log, result)

def verify_onboard(self, node: Node, log: Logger, result: TestResult) -> None:
onboarding_result = node.tools[MDE].onboard(self.onboarding_script_sas_uri)

onboarding_result = node.tools[mdatp].onboard(self.onboarding_script_sas_uri)

assert_that(onboarding_result).described_as('Unable to onboard MDE').is_equal_to(True)
assert_that(onboarding_result).described_as(
"Unable to onboard MDE"
).is_equal_to(True)

output = node.tools[mdatp].get_result('health --field licensed')
output = node.tools[MDE].get_result("health --field licensed")

assert_that(output).described_as('MDE is not licensed').is_equal_to(['true'])
assert_that(output).described_as("MDE is not licensed").is_equal_to(["true"])

def verify_health(self, node: Node, log: Logger, result: TestResult) -> None:
output = node.tools[mdatp].get_result('health', json_out=True)
output = node.tools[MDE].get_result("health", json_out=True)

log.info(output)

assert_that(output['healthy']).described_as('MDE is not healthy').is_equal_to(True)

def verify_eicar_detection(self, node: Node, log: Logger, result: TestResult) -> None:
log.info('Running EICAR test')

output = node.tools[mdatp].get_result('health --field real_time_protection_enabled')
if output == ['false']:
output = node.tools[mdatp].get_result('config real-time-protection --value enabled', sudo=True)
assert_that(' '.join(output)).described_as('Unable to enable RTP for MDE').is_equal_to('Configuration property updated.')

current_threat_list= node.tools[mdatp].get_result('threat list')
assert_that(output["healthy"]).described_as("MDE is not healthy").is_equal_to(
True
)

def verify_eicar_detection(
self, node: Node, log: Logger, result: TestResult
) -> None:
log.info("Running EICAR test")

output = node.tools[MDE].get_result(
"health --field real_time_protection_enabled"
)
if output == ["false"]:
output = node.tools[MDE].get_result(
"config real-time-protection --value enabled", sudo=True
)
assert_that(" ".join(output)).described_as(
"Unable to enable RTP for MDE"
).is_equal_to("Configuration property updated.")

current_threat_list = node.tools[MDE].get_result("threat list")
log.info(current_threat_list)

node.tools[Curl].fetch(arg="-o /tmp/eicar.com.txt",
execute_arg="",
url="https://secure.eicar.org/eicar.com.txt")
node.tools[Curl].fetch(
arg="-o /tmp/eicar.com.txt",
execute_arg="",
url="https://secure.eicar.org/eicar.com.txt",
)

time.sleep(5) #Wait for remediation
time.sleep(5) # Wait for remediation

new_threat_list = node.tools[mdatp].get_result('threat list')
new_threat_list = node.tools[MDE].get_result("threat list")
log.info(new_threat_list)

eicar_detect = ' '.join(new_threat_list).replace(' '.join(current_threat_list), '')
eicar_detect = " ".join(new_threat_list).replace(
" ".join(current_threat_list), ""
)

log.info(eicar_detect)
assert_that('Name: Virus:DOS/EICAR_Test_File' in eicar_detect).described_as('MDE is not able to detect EICAR file').is_equal_to(True)


assert_that("Name: Virus:DOS/EICAR_Test_File" in eicar_detect).described_as(
"MDE is not able to detect EICAR file"
).is_equal_to(True)