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

feat(elasticache): add check elasticache_redis_cluster_auth_enabled #4830

Conversation

HugoPBrito
Copy link
Member

@HugoPBrito HugoPBrito commented Aug 22, 2024

Context

Redis AUTH is essential for securing access to Redis clusters by requiring a password for client commands, especially since Role-Based Access Control (RBAC) is not available in versions prior to 6.0. The control will fail if Redis AUTH is not enabled for these earlier versions, helping to enforce best practices for data security in environments where older Redis versions are still in use. For Redis versions 6.0 and later, RBAC is recommended, but this check specifically targets the need for AUTH in versions below 6.0.

Description

I have implemented a new check called elasticache_redis_cluster_auth_enabled to address a security concern in Amazon ElastiCache for Redis. This check ensures that replication groups running Redis versions earlier than 6.0 have Redis AUTH enabled.

Checklist

License

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@HugoPBrito HugoPBrito requested review from a team as code owners August 22, 2024 10:21
@github-actions github-actions bot added the provider/aws Issues/PRs related with the AWS provider label Aug 22, 2024
@HugoPBrito
Copy link
Member Author

I used the describe_cache_clusters method and filtered within Redis instead of describe_replication_groups because Boto3 provides the necessary attributes only through this method.

Copy link

codecov bot commented Aug 22, 2024

Codecov Report

Attention: Patch coverage is 93.54839% with 2 lines in your changes missing coverage. Please review.

Project coverage is 89.12%. Comparing base (9263ade) to head (97440d0).
Report is 31 commits behind head on master.

Files with missing lines Patch % Lines
...rs/aws/services/elasticache/elasticache_service.py 77.77% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4830      +/-   ##
==========================================
+ Coverage   89.00%   89.12%   +0.12%     
==========================================
  Files         965      974       +9     
  Lines       29526    29852     +326     
==========================================
+ Hits        26279    26607     +328     
+ Misses       3247     3245       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "elasticache_redis_cluster_below_v6_auth_enabled",
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"CheckID": "elasticache_redis_cluster_below_v6_auth_enabled",
"CheckID": "elasticache_redis_cluster_auth_enabled",

{
"Provider": "aws",
"CheckID": "elasticache_redis_cluster_below_v6_auth_enabled",
"CheckTitle": "Ensure Elasticache Elasticache Redis replication groups under version 6.0 have Redis OSS AUTH Enabled.",
Copy link
Member

Choose a reason for hiding this comment

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

Please, don't hardcore versions in the metadata, it could change in the future.

Suggested change
"CheckTitle": "Ensure Elasticache Elasticache Redis replication groups under version 6.0 have Redis OSS AUTH Enabled.",
"CheckTitle": "Ensure Elasticache Elasticache Redis replication groups of earlier versions should have Redis OSS AUTH enabled.",

Comment on lines 5 to 7
"CheckType": [
"AWS Foundational Security Best Practices v1.0.0"
],
Copy link
Member

Choose a reason for hiding this comment

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

for cluster in elasticache_client.clusters.values():
if (
cluster.engine == "redis" and int(cluster.engine_version[0]) < 6
): # major.minor.patch
Copy link
Member

Choose a reason for hiding this comment

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

What is the comment for?

Copy link
Member Author

Choose a reason for hiding this comment

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

It was thought to clarify the string format of the version stored in engine_version. If it is unclear or unnecessary we can remove it.

@@ -41,6 +41,10 @@ def _describe_cache_clusters(self, regional_client):
auto_minor_version_upgrade=cache_cluster.get(
"AutoMinorVersionUpgrade", False
),
engine_version=cache_cluster.get("EngineVersion", None),
Copy link
Member

Choose a reason for hiding this comment

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

The None value would raise an error in the check.

Copy link
Member

Choose a reason for hiding this comment

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

Please, add another MANUAL finding for clusters of later versions saying to implement Redis ACL(Role bases access control) in the cluster.

class elasticache_redis_cluster_below_v6_auth_enabled(Check):
def execute(self):
findings = []
for cluster in elasticache_client.clusters.values():
Copy link
Member

Choose a reason for hiding this comment

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

Please, use the replication_groups since we have to check it at that level and update the metadata.

@puchy22 puchy22 changed the title feat(elasticache): Ensure Redis clusters below v6.0 have AUTH enabled feat(elasticache): add check elasticache_redis_cluster_auth_enabled Aug 28, 2024
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AWSElastiCacheClusters",
Copy link
Member

Choose a reason for hiding this comment

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

I think this ResourceType is not supported by AWS, please refer to the docs

@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "elasticache_redis_cluster_auth_enabled",
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand why names redis_cluster if you are giving findings about replication groups

Copy link
Member Author

@HugoPBrito HugoPBrito Aug 29, 2024

Choose a reason for hiding this comment

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

I handled it this way because all replication group checks were done that way. Making these corrections should imply renaming the checkID and outputs of all other replication group checks as well.

report.resource_tags = repl_group.tags

cluster = repl_group.member_clusters[0]
report.status = "PASS"
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
report.status = "PASS"
report.status = "MANUAL"

report.resource_arn = repl_group.arn
report.resource_tags = repl_group.tags

cluster = repl_group.member_clusters[0]
Copy link
Member

Choose a reason for hiding this comment

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

Is there no other way to get the version of the replication cluster? It's weird to get it from the first cluster, it's a bit arbitrary.

Copy link
Member Author

Choose a reason for hiding this comment

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

I´ll update this in a better way. I thought MemberClusters returned primary node in first position, but I think that´s incorrect. I´ll take the primary node now (whose version is replicated in replica nodes) to make this non arbitrary at all.

@@ -41,6 +41,10 @@ def _describe_cache_clusters(self, regional_client):
auto_minor_version_upgrade=cache_cluster.get(
"AutoMinorVersionUpgrade", False
),
engine_version=cache_cluster.get("EngineVersion", "None"),
Copy link
Member

Choose a reason for hiding this comment

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

2 things here:

  1. If the default value "None" reach to the check it will be: int("None") which is a problem,
  2. Why not store this values as a floating point instead a str?

@@ -90,6 +94,16 @@ def _describe_replication_groups(self, regional_client):
if not self.audit_resources or (
is_resource_filtered(replication_arn, self.audit_resources)
):
member_clusters = repl_group.get("MemberClusters", [])
cluster_list = []
if member_clusters:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if member_clusters:

primary_id = primary_node["CacheClusterId"]
break

except Exception as error:
Copy link
Member

Choose a reason for hiding this comment

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

Please, could you manage the specific Exception instead just generic one

…ion-groups-of-earlier-redis-versions-should-have-redis-auth-enabled
Comment on lines 33 to 35
version = cache_cluster.get("EngineVersion", "0.0")
version = float(version[:3])

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
version = cache_cluster.get("EngineVersion", "0.0")
version = float(version[:3])
version = version.parse(cache_cluster.get("EngineVersion", "0.0"))

Please, use version function from the packaging library.

Copy link
Member

Choose a reason for hiding this comment

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

from packaging import version

report.status = "MANUAL"
report.status_extended = f"Elasticache Redis replication group {repl_group.id}(v{repl_group.engine_version}) does not have to use AUTH, but it should have Redis ACL configured."

if repl_group.engine_version < 6.0:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if repl_group.engine_version < 6.0:
if repl_group.engine_version < version.parse("6.0"):

Comment on lines 100 to 122
# Get primary cluster
try:
for node_group in repl_group["NodeGroups"][0]:
primary_node = next(
(
node
for node in node_group["NodeGroupMembers"]
if node["CurrentRole"] == "primary"
),
None,
)
if primary_node:
primary_id = primary_node["CacheClusterId"]
break

except Exception as error:
primary_node = repl_group["NodeGroups"][0][
"NodeGroupMembers"
][0]
primary_id = primary_node["CacheClusterId"]
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
Copy link
Member

Choose a reason for hiding this comment

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

Please, get the nodes from MemberClusters, and verify within the check if those members have the compliant configuration.

Comment on lines 146 to 148
engine_version=version,
auth_token_enabled=repl_group.get(
"AuthTokenEnabled", False
Copy link
Member

Choose a reason for hiding this comment

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

Please, only store the member_clusters attribute.

report.resource_arn = repl_group.arn
report.resource_tags = repl_group.tags

for cluster in repl_group.member_clusters:
Copy link
Member

Choose a reason for hiding this comment

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

Please, can you show in the status extended the member clusters that are not compliant (if any).


else:
report.status = "PASS"
report.status_extended = f"Elasticache Redis replication group {repl_group.id}(v{cluster.engine_version}) does have AUTH enabled."
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
report.status_extended = f"Elasticache Redis replication group {repl_group.id}(v{cluster.engine_version}) does have AUTH enabled."
report.status_extended = f"Elasticache Redis replication group {repl_group.id}(v{cluster.engine_version}) has all member clusters with AUTH enabled."

report.status = "PASS"
report.status_extended = f"Elasticache Redis replication group {repl_group.id}(v{cluster.engine_version}) does have AUTH enabled."
else:
report.status = "MANUAL"
Copy link
Member

Choose a reason for hiding this comment

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

Can member cluster have different versions?

Copy link
Member

Choose a reason for hiding this comment

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

If not, leave it as it is.

Copy link
Member Author

Choose a reason for hiding this comment

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

As far as I'm concerned, every node must have the exact same version inside a replication group.

# Get first cluster version as they all have the same unless an upgrade is being made
member_clusters = repl_group.get("MemberClusters", [])
if member_clusters:
cluster_arn = f"arn:aws:s3:::{member_clusters[0]}"
Copy link
Member

Choose a reason for hiding this comment

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

Please, verify this ARN and add a test for this case.

Copy link
Member Author

@HugoPBrito HugoPBrito Sep 20, 2024

Choose a reason for hiding this comment

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

Done!

@sergargar sergargar self-requested a review September 20, 2024 16:12
@sergargar sergargar merged commit c6daa60 into master Sep 20, 2024
11 checks passed
@sergargar sergargar deleted the PRWLR-4509-elasti-cache-redis-oss-replication-groups-of-earlier-redis-versions-should-have-redis-auth-enabled branch September 20, 2024 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
provider/aws Issues/PRs related with the AWS provider
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants