feat(opensearch): Add check logic with respective unit tests and metadata

This commit is contained in:
MarioRgzLpz committed 2024-10-10 19:06:34 +02:00
1 parent eca94e2e3b
commit 0d743fa179
4 files changed
+270

No files matched your search

@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled",
"CheckTitle": "Elasticsearch/Opensearch domains should have at least three data nodes",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "opensearch",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:{region}:{account-id}:domain/{domain-name}",
"Severity": "medium",
"ResourceType": "AwsElasticsearchDomain",
"Description": "This control checks whether Elasticsearch/Opensearch domains are configured with at least three data nodes and zoneAwarenessEnabled is true.",
"Risk": "Without at least three data nodes, the Elasticsearch/Opensearch domain may not be fault-tolerant, leading to a higher risk of data loss or unavailability in case of node failure.",
"RelatedUrl": "https://docs.aws.amazon.com/opensearch-service/latest/developerguide/what-is.html",
"Remediation": {
"Code": {
"CLI": "aws opensearch update-domain-config --domain-name <domain-name> --cluster-config InstanceCount=3,ZoneAwarenessEnabled=true",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/es-controls.html#es-6",
"Terraform": ""
},
"Recommendation": {
"Text": "Modify the Elasticsearch/Opensearch domain to ensure at least three data nodes and enable zone awareness for high availability.",
"Url": "https://docs.aws.amazon.com/opensearch-service/latest/developerguide/managedomains-configuration-changes.html"
}
},
"Categories": [
"redundancy"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,33 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.opensearch.opensearch_client import (
opensearch_client,
)
class opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled(Check):
def execute(self):
findings = []
for domain in opensearch_client.opensearch_domains.values():
report = Check_Report_AWS(self.metadata())
report.region = domain.region
report.resource_id = domain.name
report.resource_arn = domain.arn
report.resource_tags = domain.tags
report.status = "FAIL"
report.status_extended = f"Opensearch domain {domain.name} does not meet the requirements: it has less than 3 data nodes and zone awareness is not enabled."
if domain.instance_count >= 3 and domain.zone_awareness_enabled:
report.status = "PASS"
report.status_extended = f"Opensearch domain {domain.name} has {domain.instance_count} data nodes and zone awareness enabled."
elif domain.instance_count >= 3 and not domain.zone_awareness_enabled:
report.status = "FAIL"
report.status_extended = f"Opensearch domain {domain.name} has {domain.instance_count} data nodes, but zone awareness is not enabled."
elif domain.instance_count < 3 and domain.zone_awareness_enabled:
report.status = "FAIL"
report.status_extended = f"Opensearch domain {domain.name} has zone awareness enabled, but only {domain.instance_count} data nodes."
findings.append(report)
return findings
@@ -0,0 +1,203 @@
from unittest import mock
from boto3 import client
from moto import mock_aws
from tests.providers.aws.utils import (
AWS_ACCOUNT_NUMBER,
AWS_REGION_US_EAST_1,
set_mocked_aws_provider,
)
class Test_opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled:
@mock_aws
def test_no_domains(self):
client("opensearch", region_name=AWS_REGION_US_EAST_1)
from prowler.providers.aws.services.opensearch.opensearch_service import (
OpenSearchService,
)
mocked_aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=mocked_aws_provider,
), mock.patch(
"prowler.providers.aws.services.opensearch.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_client",
new=OpenSearchService(mocked_aws_provider),
):
from prowler.providers.aws.services.opensearch.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled import (
opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled,
)
check = (
opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled()
)
result = check.execute()
assert len(result) == 0
@mock_aws
def test_instance_count_less_than_three_zoneawareness_enabled(self):
opensearch_client = client("opensearch", region_name=AWS_REGION_US_EAST_1)
domain = opensearch_client.create_domain(
DomainName="test-domain",
ClusterConfig={"ZoneAwarenessEnabled": True, "InstanceCount": 2},
)
from prowler.providers.aws.services.opensearch.opensearch_service import (
OpenSearchService,
)
mocked_aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=mocked_aws_provider,
), mock.patch(
"prowler.providers.aws.services.opensearch.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_client",
new=OpenSearchService(mocked_aws_provider),
):
from prowler.providers.aws.services.opensearch.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled import (
opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled,
)
check = (
opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled()
)
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Opensearch domain test-domain has zone awareness enabled, but only 2 data nodes."
)
assert result[0].resource_id == domain["DomainStatus"]["DomainName"]
assert (
result[0].resource_arn
== f"arn:aws:es:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:domain/{domain["DomainStatus"]["DomainName"]}"
)
@mock_aws
def test_instance_count_more_than_three_zoneawareness_not_enabled(self):
opensearch_client = client("opensearch", region_name=AWS_REGION_US_EAST_1)
domain = opensearch_client.create_domain(
DomainName="test-domain",
ClusterConfig={"ZoneAwarenessEnabled": False, "InstanceCount": 3},
)
from prowler.providers.aws.services.opensearch.opensearch_service import (
OpenSearchService,
)
mocked_aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=mocked_aws_provider,
), mock.patch(
"prowler.providers.aws.services.opensearch.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_client",
new=OpenSearchService(mocked_aws_provider),
):
from prowler.providers.aws.services.opensearch.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled import (
opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled,
)
check = (
opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled()
)
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Opensearch domain test-domain has 3 data nodes, but zone awareness is not enabled."
)
assert result[0].resource_id == domain["DomainStatus"]["DomainName"]
assert (
result[0].resource_arn
== f"arn:aws:es:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:domain/{domain["DomainStatus"]["DomainName"]}"
)
@mock_aws
def test_instance_count_less_than_three_zoneawareness_not_enabled(self):
opensearch_client = client("opensearch", region_name=AWS_REGION_US_EAST_1)
domain = opensearch_client.create_domain(
DomainName="test-domain",
ClusterConfig={"ZoneAwarenessEnabled": False, "InstanceCount": 2},
)
from prowler.providers.aws.services.opensearch.opensearch_service import (
OpenSearchService,
)
mocked_aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=mocked_aws_provider,
), mock.patch(
"prowler.providers.aws.services.opensearch.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_client",
new=OpenSearchService(mocked_aws_provider),
):
from prowler.providers.aws.services.opensearch.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled import (
opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled,
)
check = (
opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled()
)
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "Opensearch domain test-domain does not meet the requirements: it has less than 3 data nodes and zone awareness is not enabled."
)
assert result[0].resource_id == domain["DomainStatus"]["DomainName"]
assert (
result[0].resource_arn
== f"arn:aws:es:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:domain/{domain["DomainStatus"]["DomainName"]}"
)
@mock_aws
def test_logging_instance_count_more_than_three_zoneawareness_enabled(self):
opensearch_client = client("opensearch", region_name=AWS_REGION_US_EAST_1)
domain = opensearch_client.create_domain(
DomainName="test-domain",
ClusterConfig={"ZoneAwarenessEnabled": True, "InstanceCount": 3},
)
from prowler.providers.aws.services.opensearch.opensearch_service import (
OpenSearchService,
)
mocked_aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=mocked_aws_provider,
), mock.patch(
"prowler.providers.aws.services.opensearch.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_client",
new=OpenSearchService(mocked_aws_provider),
):
from prowler.providers.aws.services.opensearch.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled.opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled import (
opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled,
)
check = (
opensearch_service_domains_at_least_three_nodes_and_zoneawareness_enabled()
)
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "Opensearch domain test-domain has 3 data nodes and zone awareness enabled."
)
assert result[0].resource_id == domain["DomainStatus"]["DomainName"]
assert (
result[0].resource_arn
== f"arn:aws:es:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:domain/{domain["DomainStatus"]["DomainName"]}"
)