feat(dms): add new check dms_endpoint_redis_tls_enabled (#5583)

Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
This commit is contained in:
Daniel Barranquero
2024-11-06 17:03:13 +01:00
committed by GitHub
parent 43efabef6c
commit 0ae26bddfc
7 changed files with 357 additions and 0 deletions
@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "dms_endpoint_redis_in_transit_encryption_enabled",
"CheckTitle": "Check if DMS endpoints for Redis OSS are encrypted in transit.",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "dms",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:dms:region:account-id:endpoint/endpoint-id",
"Severity": "medium",
"ResourceType": "AwsDmsEndpoint",
"Description": "This control checks whether an AWS DMS endpoint for Redis OSS is configured with a TLS connection. The control fails if the endpoint doesn't have TLS enabled.",
"Risk": "Without TLS, data transmitted between databases may be vulnerable to interception or eavesdropping, increasing the risk of data breaches and other security incidents.",
"RelatedUrl": "https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Source.Redis.html",
"Remediation": {
"Code": {
"CLI": "aws dms modify-endpoint --endpoint-arn <endpoint-arn> --redis-settings '{'SslSecurityProtocol': 'ssl-encryption'}'",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/dms-controls.html#dms-12",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable TLS for DMS endpoints for Redis OSS to ensure encrypted communication during data migration.",
"Url": "https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Target.Redis.html#CHAP_Target.Redis.EndpointSettings"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,42 @@
from typing import List
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.dms.dms_client import dms_client
class dms_endpoint_redis_in_transit_encryption_enabled(Check):
"""
Check if AWS DMS Endpoints for Redis OSS have TLS enabled.
This class verifies whether each AWS DMS Endpoint configured for Redis OSS is encrypted in transit
by checking the `TlsEnabled` property in the endpoint's configuration. The check ensures that
TLS is enabled to secure data in transit, preventing unauthorized access and ensuring data integrity.
"""
def execute(self) -> List[Check_Report_AWS]:
"""
Execute the DMS Redis TLS enabled check.
Iterates over all DMS Endpoints and generates a report indicating whether
each Redis OSS endpoint is encrypted in transit.
Returns:
List[Check_Report_AWS]: A list of report objects with the results of the check.
"""
findings = []
for endpoint_arn, endpoint in dms_client.endpoints.items():
if endpoint.engine_name == "redis":
report = Check_Report_AWS(self.metadata())
report.resource_id = endpoint.id
report.resource_arn = endpoint_arn
report.region = endpoint.region
report.resource_tags = endpoint.tags
report.status = "FAIL"
report.status_extended = f"DMS Endpoint {endpoint.id} for Redis OSS is not encrypted in transit."
if endpoint.redis_ssl_protocol == "ssl-encryption":
report.status = "PASS"
report.status_extended = f"DMS Endpoint {endpoint.id} for Redis OSS is encrypted in transit."
findings.append(report)
return findings
@@ -71,6 +71,9 @@ class DMS(AWSService):
id=endpoint["EndpointIdentifier"],
region=regional_client.region,
ssl_mode=endpoint.get("SslMode", False),
redis_ssl_protocol=endpoint.get("RedisSettings", {}).get(
"SslSecurityProtocol", "plaintext"
),
mongodb_auth_type=endpoint.get("MongoDbSettings", {}).get(
"AuthType", "no"
),
@@ -101,6 +104,7 @@ class Endpoint(BaseModel):
region: str
ssl_mode: str
tags: Optional[list]
redis_ssl_protocol: str
mongodb_auth_type: str
neptune_iam_auth_enabled: bool = False
engine_name: str
@@ -0,0 +1,271 @@
from unittest import mock
import botocore
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,
)
make_api_call = botocore.client.BaseClient._make_api_call
DMS_ENDPOINT_NAME = "dms-endpoint"
DMS_ENDPOINT_ARN = f"arn:aws:dms:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:endpoint:{DMS_ENDPOINT_NAME}"
DMS_INSTANCE_NAME = "rep-instance"
DMS_INSTANCE_ARN = (
f"arn:aws:dms:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:rep:{DMS_INSTANCE_NAME}"
)
def mock_make_api_call_enabled_not_redis(self, operation_name, kwarg):
if operation_name == "DescribeEndpoints":
return {
"Endpoints": [
{
"EndpointIdentifier": DMS_ENDPOINT_NAME,
"EndpointArn": DMS_ENDPOINT_ARN,
"SslMode": "require",
"RedisSettings": {
"SslSecurityProtocol": "ssl-encryption",
},
"EngineName": "oracle",
}
]
}
elif operation_name == "ListTagsForResource":
if kwarg["ResourceArn"] == DMS_INSTANCE_ARN:
return {
"TagList": [
{"Key": "Name", "Value": "rep-instance"},
{"Key": "Owner", "Value": "admin"},
]
}
elif kwarg["ResourceArn"] == DMS_ENDPOINT_ARN:
return {
"TagList": [
{"Key": "Name", "Value": "dms-endpoint"},
{"Key": "Owner", "Value": "admin"},
]
}
return make_api_call(self, operation_name, kwarg)
def mock_make_api_call_enabled(self, operation_name, kwarg):
if operation_name == "DescribeEndpoints":
return {
"Endpoints": [
{
"EndpointIdentifier": DMS_ENDPOINT_NAME,
"EndpointArn": DMS_ENDPOINT_ARN,
"SslMode": "require",
"RedisSettings": {
"SslSecurityProtocol": "ssl-encryption",
},
"EngineName": "redis",
}
]
}
elif operation_name == "ListTagsForResource":
if kwarg["ResourceArn"] == DMS_INSTANCE_ARN:
return {
"TagList": [
{"Key": "Name", "Value": "rep-instance"},
{"Key": "Owner", "Value": "admin"},
]
}
elif kwarg["ResourceArn"] == DMS_ENDPOINT_ARN:
return {
"TagList": [
{"Key": "Name", "Value": "dms-endpoint"},
{"Key": "Owner", "Value": "admin"},
]
}
return make_api_call(self, operation_name, kwarg)
def mock_make_api_call_not_enabled(self, operation_name, kwarg):
if operation_name == "DescribeEndpoints":
return {
"Endpoints": [
{
"EndpointIdentifier": DMS_ENDPOINT_NAME,
"EndpointArn": DMS_ENDPOINT_ARN,
"SslMode": "require",
"RedisSettings": {
"SslSecurityProtocol": "plaintext",
},
"EngineName": "redis",
}
]
}
elif operation_name == "ListTagsForResource":
if kwarg["ResourceArn"] == DMS_INSTANCE_ARN:
return {
"TagList": [
{"Key": "Name", "Value": "rep-instance"},
{"Key": "Owner", "Value": "admin"},
]
}
elif kwarg["ResourceArn"] == DMS_ENDPOINT_ARN:
return {
"TagList": [
{"Key": "Name", "Value": "dms-endpoint"},
{"Key": "Owner", "Value": "admin"},
]
}
return make_api_call(self, operation_name, kwarg)
class Test_dms_endpoint_redis_in_transit_encryption_enabled:
@mock_aws
def test_no_dms_endpoints(self):
dms_client = client("dms", region_name=AWS_REGION_US_EAST_1)
dms_client.endpoints = {}
from prowler.providers.aws.services.dms.dms_service import DMS
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.dms.dms_endpoint_redis_in_transit_encryption_enabled.dms_endpoint_redis_in_transit_encryption_enabled.dms_client",
new=DMS(aws_provider),
):
# Test Check
from prowler.providers.aws.services.dms.dms_endpoint_redis_in_transit_encryption_enabled.dms_endpoint_redis_in_transit_encryption_enabled import (
dms_endpoint_redis_in_transit_encryption_enabled,
)
check = dms_endpoint_redis_in_transit_encryption_enabled()
result = check.execute()
assert len(result) == 0
@mock_aws
def test_dms_not_mongodb_auth_mecanism_enabled(self):
with mock.patch(
"botocore.client.BaseClient._make_api_call",
new=mock_make_api_call_enabled_not_redis,
):
from prowler.providers.aws.services.dms.dms_service import DMS
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.dms.dms_endpoint_redis_in_transit_encryption_enabled.dms_endpoint_redis_in_transit_encryption_enabled.dms_client",
new=DMS(aws_provider),
):
# Test Check
from prowler.providers.aws.services.dms.dms_endpoint_redis_in_transit_encryption_enabled.dms_endpoint_redis_in_transit_encryption_enabled import (
dms_endpoint_redis_in_transit_encryption_enabled,
)
check = dms_endpoint_redis_in_transit_encryption_enabled()
result = check.execute()
assert len(result) == 0
@mock_aws
def test_dms_mongodb_auth_mecanism_not_enabled(self):
with mock.patch(
"botocore.client.BaseClient._make_api_call",
new=mock_make_api_call_not_enabled,
):
from prowler.providers.aws.services.dms.dms_service import DMS
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.dms.dms_endpoint_redis_in_transit_encryption_enabled.dms_endpoint_redis_in_transit_encryption_enabled.dms_client",
new=DMS(aws_provider),
):
# Test Check
from prowler.providers.aws.services.dms.dms_endpoint_redis_in_transit_encryption_enabled.dms_endpoint_redis_in_transit_encryption_enabled import (
dms_endpoint_redis_in_transit_encryption_enabled,
)
check = dms_endpoint_redis_in_transit_encryption_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].status_extended == (
"DMS Endpoint dms-endpoint for Redis OSS is not encrypted in transit."
)
assert result[0].resource_id == "dms-endpoint"
assert (
result[0].resource_arn
== "arn:aws:dms:us-east-1:123456789012:endpoint:dms-endpoint"
)
assert result[0].resource_tags == [
{
"Key": "Name",
"Value": "dms-endpoint",
},
{
"Key": "Owner",
"Value": "admin",
},
]
assert result[0].region == "us-east-1"
@mock_aws
def test_dms_mongodb_auth_mecanism_enabled(self):
with mock.patch(
"botocore.client.BaseClient._make_api_call",
new=mock_make_api_call_enabled,
):
from prowler.providers.aws.services.dms.dms_service import DMS
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.dms.dms_endpoint_redis_in_transit_encryption_enabled.dms_endpoint_redis_in_transit_encryption_enabled.dms_client",
new=DMS(aws_provider),
):
# Test Check
from prowler.providers.aws.services.dms.dms_endpoint_redis_in_transit_encryption_enabled.dms_endpoint_redis_in_transit_encryption_enabled import (
dms_endpoint_redis_in_transit_encryption_enabled,
)
check = dms_endpoint_redis_in_transit_encryption_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].status_extended == (
"DMS Endpoint dms-endpoint for Redis OSS is encrypted in transit."
)
assert result[0].resource_id == "dms-endpoint"
assert (
result[0].resource_arn
== "arn:aws:dms:us-east-1:123456789012:endpoint:dms-endpoint"
)
assert result[0].resource_tags == [
{
"Key": "Name",
"Value": "dms-endpoint",
},
{
"Key": "Owner",
"Value": "admin",
},
]
assert result[0].region == "us-east-1"
@@ -33,6 +33,7 @@ class Test_dms_endpoint_ssl_enabled:
id="test-endpoint-no-ssl",
mongodb_auth_type="no",
engine_name="test-engine",
redis_ssl_protocol="plaintext",
region=AWS_REGION_US_EAST_1,
ssl_mode="none",
tags=[{"Key": "Name", "Value": "test-endpoint-no-ssl"}],
@@ -81,6 +82,7 @@ class Test_dms_endpoint_ssl_enabled:
id="test-endpoint-ssl-require",
mongodb_auth_type="no",
engine_name="test-engine",
redis_ssl_protocol="plaintext",
region=AWS_REGION_US_EAST_1,
ssl_mode="require",
tags=[{"Key": "Name", "Value": "test-endpoint-ssl-require"}],
@@ -126,6 +128,7 @@ class Test_dms_endpoint_ssl_enabled:
id="test-endpoint-ssl-verify-ca",
engine_name="test-engine",
mongodb_auth_type="no",
redis_ssl_protocol="plaintext",
region=AWS_REGION_US_EAST_1,
ssl_mode="verify-ca",
tags=[{"Key": "Name", "Value": "test-endpoint-ssl-verify-ca"}],
@@ -171,6 +174,7 @@ class Test_dms_endpoint_ssl_enabled:
id="test-endpoint-ssl-verify-full",
mongodb_auth_type="no",
engine_name="test-engine",
redis_ssl_protocol="plaintext",
region=AWS_REGION_US_EAST_1,
ssl_mode="verify-full",
tags=[{"Key": "Name", "Value": "test-endpoint-ssl-verify-full"}],
@@ -44,6 +44,9 @@ def mock_make_api_call(self, operation_name, kwargs):
"EndpointIdentifier": DMS_ENDPOINT_NAME,
"EndpointArn": DMS_ENDPOINT_ARN,
"SslMode": "require",
"RedisSettings": {
"SslSecurityProtocol": "ssl-encryption",
},
"MongoDbSettings": {
"AuthType": "password",
},
@@ -128,6 +131,7 @@ class Test_DMS_Service:
assert len(dms.endpoints) == 1
assert dms.endpoints[DMS_ENDPOINT_ARN].id == DMS_ENDPOINT_NAME
assert dms.endpoints[DMS_ENDPOINT_ARN].ssl_mode == "require"
assert dms.endpoints[DMS_ENDPOINT_ARN].redis_ssl_protocol == "ssl-encryption"
assert dms.endpoints[DMS_ENDPOINT_ARN].mongodb_auth_type == "password"
assert dms.endpoints[DMS_ENDPOINT_ARN].neptune_iam_auth_enabled
assert dms.endpoints[DMS_ENDPOINT_ARN].engine_name == "neptune"