mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-04 02:04:06 +00:00
feat(memorydb): add memorydb_cluster_in_transit_encryption_enabled check (#12246)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
`memorydb_cluster_in_transit_encryption_enabled` check for AWS provider, verifying MemoryDB clusters have in-transit encryption (TLS) enabled
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "memorydb_cluster_in_transit_encryption_enabled",
|
||||
"CheckTitle": "MemoryDB cluster has in-transit encryption enabled",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices/Network Security",
|
||||
"Effects/Data Exposure"
|
||||
],
|
||||
"ServiceName": "memorydb",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "Other",
|
||||
"ResourceGroup": "database",
|
||||
"Description": "**MemoryDB clusters** are evaluated for **in-transit encryption (TLS)** on client and inter-node traffic (`TLSEnabled=true`).",
|
||||
"Risk": "Without **in-transit encryption**, client and inter-node traffic travels in cleartext inside the VPC.\n\nAnyone able to observe it can **read cached data** and the **ACL credentials** clients send to authenticate, or **tamper with commands**, enabling **MITM** and replication corruption. This harms **confidentiality** and **integrity**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/memorydb/latest/devguide/in-transit-encryption.html",
|
||||
"https://docs.aws.amazon.com/memorydb/latest/devguide/API_CreateCluster.html",
|
||||
"https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/memorydb_cluster#tls_enabled"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "```yaml\n# Enable in-transit encryption for a MemoryDB cluster\nResources:\n <example_resource_name>:\n Type: AWS::MemoryDB::Cluster\n Properties:\n ClusterName: <example_resource_name>\n ACLName: <example_acl_name>\n NodeType: <example_node_type>\n NumShards: 1\n TLSEnabled: true # CRITICAL: enables TLS in-transit; replaces the cluster if changed\n```",
|
||||
"Other": "1. In the AWS Console, go to MemoryDB > Clusters\n2. Select the cluster <cluster-name> and choose Actions > Take snapshot\n3. Once the snapshot is available, go to Snapshots, select it and choose Actions > Restore\n4. In the restore form, enable \"Encryption in transit\" and create the cluster\n5. Repoint clients at the new cluster endpoint using a TLS-enabled client, then delete the old cluster",
|
||||
"Terraform": "```hcl\nresource \"aws_memorydb_cluster\" \"<example_resource_name>\" {\n name = \"<example_resource_name>\"\n acl_name = \"<example_acl_name>\"\n node_type = \"<example_node_type>\"\n num_shards = 1\n\n tls_enabled = true # CRITICAL: enables TLS in-transit; forces replacement if changed\n}\n```"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Create MemoryDB clusters with `TLSEnabled=true`. Because this flag is **immutable after creation**, an unencrypted cluster must be replaced: snapshot it, restore into a new TLS-enabled cluster, then cut clients over and delete the original.\n\nConfigure clients to validate the server certificate, and pair TLS with **ACL users**, least-privilege security groups, and private subnets for defense in depth.",
|
||||
"Url": "https://hub.prowler.com/check/memorydb_cluster_in_transit_encryption_enabled"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"encryption"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": "MemoryDB enables in-transit encryption by default, and `TLSEnabled` cannot be changed after the cluster is created, so a failing cluster must be recreated from a snapshot rather than updated in place."
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
||||
from prowler.providers.aws.services.memorydb.memorydb_client import memorydb_client
|
||||
|
||||
|
||||
class memorydb_cluster_in_transit_encryption_enabled(Check):
|
||||
"""Verify that every MemoryDB cluster encrypts data in transit.
|
||||
|
||||
A cluster PASSES when ``TLSEnabled`` is set. The flag can only be chosen at
|
||||
creation time, so a failing cluster has to be rebuilt from a snapshot.
|
||||
"""
|
||||
|
||||
def execute(self) -> list[Check_Report_AWS]:
|
||||
"""Execute the MemoryDB in-transit encryption check.
|
||||
|
||||
Returns:
|
||||
A list of reports with each cluster in-transit encryption status.
|
||||
"""
|
||||
findings = []
|
||||
for cluster in memorydb_client.clusters.values():
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=cluster)
|
||||
if cluster.tls_enabled:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Memory DB Cluster {cluster.name} has in-transit encryption enabled."
|
||||
else:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Memory DB Cluster {cluster.name} does not have in-transit encryption enabled."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
from unittest import mock
|
||||
|
||||
import botocore
|
||||
from moto import mock_aws
|
||||
|
||||
from tests.providers.aws.utils import (
|
||||
AWS_ACCOUNT_NUMBER,
|
||||
AWS_REGION_US_EAST_1,
|
||||
set_mocked_aws_provider,
|
||||
)
|
||||
|
||||
CLUSTER_NAME = "db-cluster-1"
|
||||
CLUSTER_ARN = f"arn:aws:memorydb:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:cluster:{CLUSTER_NAME}"
|
||||
|
||||
CHECK_PATH = "prowler.providers.aws.services.memorydb.memorydb_cluster_in_transit_encryption_enabled.memorydb_cluster_in_transit_encryption_enabled.memorydb_client"
|
||||
PROVIDER_PATH = "prowler.providers.common.provider.Provider.get_global_provider"
|
||||
|
||||
make_api_call = botocore.client.BaseClient._make_api_call
|
||||
|
||||
|
||||
def cluster_payload(tls_enabled: bool) -> dict:
|
||||
return {
|
||||
"Name": CLUSTER_NAME,
|
||||
"Status": "available",
|
||||
"NumberOfShards": 2,
|
||||
"AvailabilityMode": "multiaz",
|
||||
"Engine": "valkey",
|
||||
"EngineVersion": "7.2",
|
||||
"EnginePatchVersion": "7.2.4",
|
||||
"SecurityGroups": [
|
||||
{"SecurityGroupId": "sg-0a1434xxxxxc9fae", "Status": "active"}
|
||||
],
|
||||
"TLSEnabled": tls_enabled,
|
||||
"ARN": CLUSTER_ARN,
|
||||
"SnapshotRetentionLimit": 0,
|
||||
"AutoMinorVersionUpgrade": True,
|
||||
}
|
||||
|
||||
|
||||
def mock_describe_clusters(clusters: list):
|
||||
"""Stub DescribeClusters, which moto covers only partially.
|
||||
|
||||
Moto accepts ``Engine`` on CreateCluster but never returns it from
|
||||
DescribeClusters, and the service layer requires that field, so every
|
||||
cluster would be dropped before reaching the check.
|
||||
"""
|
||||
|
||||
def mock_make_api_call(self, operation_name, kwargs):
|
||||
if operation_name == "DescribeClusters":
|
||||
return {"Clusters": clusters}
|
||||
return make_api_call(self, operation_name, kwargs)
|
||||
|
||||
return mock_make_api_call
|
||||
|
||||
|
||||
def build_memorydb_client(clusters: list):
|
||||
from prowler.providers.aws.services.memorydb.memorydb_service import MemoryDB
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
with mock.patch(
|
||||
"botocore.client.BaseClient._make_api_call",
|
||||
new=mock_describe_clusters(clusters),
|
||||
):
|
||||
return aws_provider, MemoryDB(aws_provider)
|
||||
|
||||
|
||||
class Test_memorydb_cluster_in_transit_encryption_enabled:
|
||||
@mock_aws
|
||||
def test_no_clusters(self):
|
||||
aws_provider, memorydb_client = build_memorydb_client([])
|
||||
|
||||
with mock.patch(PROVIDER_PATH, return_value=aws_provider):
|
||||
with mock.patch(CHECK_PATH, new=memorydb_client):
|
||||
from prowler.providers.aws.services.memorydb.memorydb_cluster_in_transit_encryption_enabled.memorydb_cluster_in_transit_encryption_enabled import (
|
||||
memorydb_cluster_in_transit_encryption_enabled,
|
||||
)
|
||||
|
||||
check = memorydb_cluster_in_transit_encryption_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_aws
|
||||
def test_cluster_in_transit_encryption_disabled(self):
|
||||
aws_provider, memorydb_client = build_memorydb_client(
|
||||
[cluster_payload(tls_enabled=False)]
|
||||
)
|
||||
|
||||
with mock.patch(PROVIDER_PATH, return_value=aws_provider):
|
||||
with mock.patch(CHECK_PATH, new=memorydb_client):
|
||||
from prowler.providers.aws.services.memorydb.memorydb_cluster_in_transit_encryption_enabled.memorydb_cluster_in_transit_encryption_enabled import (
|
||||
memorydb_cluster_in_transit_encryption_enabled,
|
||||
)
|
||||
|
||||
check = memorydb_cluster_in_transit_encryption_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Memory DB Cluster {CLUSTER_NAME} does not have in-transit encryption enabled."
|
||||
)
|
||||
assert result[0].resource_id == CLUSTER_NAME
|
||||
assert result[0].resource_arn == CLUSTER_ARN
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert result[0].resource_tags == []
|
||||
|
||||
@mock_aws
|
||||
def test_cluster_in_transit_encryption_enabled(self):
|
||||
aws_provider, memorydb_client = build_memorydb_client(
|
||||
[cluster_payload(tls_enabled=True)]
|
||||
)
|
||||
|
||||
with mock.patch(PROVIDER_PATH, return_value=aws_provider):
|
||||
with mock.patch(CHECK_PATH, new=memorydb_client):
|
||||
from prowler.providers.aws.services.memorydb.memorydb_cluster_in_transit_encryption_enabled.memorydb_cluster_in_transit_encryption_enabled import (
|
||||
memorydb_cluster_in_transit_encryption_enabled,
|
||||
)
|
||||
|
||||
check = memorydb_cluster_in_transit_encryption_enabled()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Memory DB Cluster {CLUSTER_NAME} has in-transit encryption enabled."
|
||||
)
|
||||
assert result[0].resource_id == CLUSTER_NAME
|
||||
assert result[0].resource_arn == CLUSTER_ARN
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert result[0].resource_tags == []
|
||||
Reference in New Issue
Block a user