feat(memorydb): add memorydb_cluster_in_transit_encryption_enabled check (#12246)

This commit is contained in:
Utkarsh Batham
2026-08-28 14:03:10 +02:00
committed by GitHub
parent 2cae2058e9
commit e21946874f
5 changed files with 205 additions and 0 deletions
@@ -0,0 +1 @@
`memorydb_cluster_in_transit_encryption_enabled` check for AWS provider, verifying MemoryDB clusters have in-transit encryption (TLS) enabled
@@ -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."
}
@@ -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
@@ -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 == []