From 8015e84d2b8940c2b2df227b16d8166f73fb1396 Mon Sep 17 00:00:00 2001 From: Meador Inge Date: Thu, 9 Jul 2020 12:46:36 -0500 Subject: [PATCH 1/2] [Scan] Look for Reshift clusters with SSL disabled This commit adds support for identifying Redshift clusters that do *not* have SSL enabled. The analysis is smart enough to adjust the severity depending on whether the parameter group in question is used or not. Examples: * INFO * Redshift cluster parameter groups with SSL disabled (redshift.ssl) { "parameter_group_count": 2, "no_ssl_count": 1, "instances": { "us-east-1": [ { "group_name": "default.redshift-1.0", "in_use": false } ] } } * MEDIUM * Redshift cluster parameter groups with SSL disabled (redshift.ssl) { "parameter_group_count": 2, "no_ssl_count": 2, "instances": { "us-east-1": [ { "group_name": "bad-group", "in_use": true }, { "group_name": "default.redshift-1.0", "in_use": false } ] } } --- scanamabob/scans/redshift.py | 52 +++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/scanamabob/scans/redshift.py b/scanamabob/scans/redshift.py index 1415541..5d859c0 100644 --- a/scanamabob/scans/redshift.py +++ b/scanamabob/scans/redshift.py @@ -60,5 +60,55 @@ def run(self, context): return findings +class SSLEnabledScan(Scan): + title = 'Verifying Redshift clusters are using SSL' + permissions = [''] + + def run(self, context): + findings = [] + parameter_group_count = 0 + no_ssl_count = 0 + groups_without_ssl = {} + instances = {} + + # Search for parameter groups wit SSL disabled. + for region in context.regions: + redshift = client(context, region_name=region) + for page in redshift.get_paginator('describe_cluster_parameter_groups').paginate(): + for parameter_group in page['ParameterGroups']: + parameter_group_count += 1 + group_name = parameter_group['ParameterGroupName'] + for parameter in redshift.describe_cluster_parameters(ParameterGroupName=group_name)['Parameters']: + if parameter['ParameterName'] == 'require_ssl' and parameter['ParameterValue'] == 'false': + no_ssl_count += 1 + if region not in groups_without_ssl: + groups_without_ssl[region] = [] + groups_without_ssl[region].append({'group_name': group_name, 'in_use': False}) + + # Next see if those parameter groups are actually used. + severity = 'INFO' + instances = {} + for region in context.regions: + redshift = client(context, region_name=region) + for page in redshift.get_paginator('describe_clusters').paginate(): + for cluster in page['Clusters']: + for parameter_group in cluster['ClusterParameterGroups']: + group_name = parameter_group['ParameterGroupName'] + for other_group in groups_without_ssl[region]: + if other_group['group_name'] == group_name: + other_group['in_use'] = True + severity = 'MEDIUM' + + if no_ssl_count: + findings.append(Finding(context.state, + 'Redshift cluster parameter groups with SSL disabled', + severity, + parameter_group_count=parameter_group_count, + no_ssl_count=no_ssl_count, + instances=groups_without_ssl)) + return findings + + scans = ScanSuite('Redshift Scans', - {'public': PubliclyAccessibleScan()}) + {'public': PubliclyAccessibleScan(), + 'ssl': SSLEnabledScan()}) From 751bb226fdaeb7b8b2b0591236f2ec1d5f3c63cd Mon Sep 17 00:00:00 2001 From: Meador Inge Date: Thu, 9 Jul 2020 16:55:45 -0500 Subject: [PATCH 2/2] [Scan] Look for Reshift clusters without logging This commit builds on the previous Redshift SSL scan functionality to look for lack of logging. Both the SSL and logging scans involves looking at parameter groups. So, this commit refactors things into a `ParameterGroupScan` that can be used by both the SSL and logging scans. It also provides to eliminating flagging the default parameter group if it isn't used. This is because the default group can't actually be edited. --- scanamabob/scans/redshift.py | 69 +++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/scanamabob/scans/redshift.py b/scanamabob/scans/redshift.py index 5d859c0..e5935aa 100644 --- a/scanamabob/scans/redshift.py +++ b/scanamabob/scans/redshift.py @@ -60,18 +60,20 @@ def run(self, context): return findings -class SSLEnabledScan(Scan): - title = 'Verifying Redshift clusters are using SSL' - permissions = [''] +class ParameterGroupScan(Scan): + def __init__(self, name, value, title): + self.name = name + self.value = value + self.title = title def run(self, context): findings = [] parameter_group_count = 0 - no_ssl_count = 0 - groups_without_ssl = {} + flagged_parameter_group_count = 0 + flagged = {} instances = {} - # Search for parameter groups wit SSL disabled. + # Search for parameter groups with the properties we are looking to flag. for region in context.regions: redshift = client(context, region_name=region) for page in redshift.get_paginator('describe_cluster_parameter_groups').paginate(): @@ -79,11 +81,14 @@ def run(self, context): parameter_group_count += 1 group_name = parameter_group['ParameterGroupName'] for parameter in redshift.describe_cluster_parameters(ParameterGroupName=group_name)['Parameters']: - if parameter['ParameterName'] == 'require_ssl' and parameter['ParameterValue'] == 'false': - no_ssl_count += 1 - if region not in groups_without_ssl: - groups_without_ssl[region] = [] - groups_without_ssl[region].append({'group_name': group_name, 'in_use': False}) + if parameter['ParameterName'] == self.name and parameter['ParameterValue'] == self.value: + flagged_parameter_group_count += 1 + if region not in flagged: + flagged[region] = [] + flagged[region].append({'group_name': group_name, + 'parameter_name': self.name, + 'parameter_value': self.value, + 'in_use': False}) # Next see if those parameter groups are actually used. severity = 'INFO' @@ -94,21 +99,53 @@ def run(self, context): for cluster in page['Clusters']: for parameter_group in cluster['ClusterParameterGroups']: group_name = parameter_group['ParameterGroupName'] - for other_group in groups_without_ssl[region]: + for other_group in flagged[region]: if other_group['group_name'] == group_name: other_group['in_use'] = True severity = 'MEDIUM' - if no_ssl_count: + # If the default parameter group isn't used, then unflag it. + for region in flagged: + default_group = None + for group in flagged[region]: + if group['group_name'] == 'default.redshift-1.0' and not group['in_use']: + default_group = group + break + if default_group: + flagged_parameter_group_count -= 1 + flagged[region].remove(default_group) + + if flagged_parameter_group_count: findings.append(Finding(context.state, - 'Redshift cluster parameter groups with SSL disabled', + self.title, severity, parameter_group_count=parameter_group_count, - no_ssl_count=no_ssl_count, - instances=groups_without_ssl)) + flagged_parameter_group_count=flagged_parameter_group_count, + instances=flagged)) return findings +class SSLEnabledScan(ParameterGroupScan): + title = 'Verifying Redshift clusters are using SSL' + permissions = [''] + + def __init__(self): + super().__init__('require_ssl', + 'false', + 'Redshift cluster parameter groups with SSL disabled') + + +class LoggingEnabledScan(ParameterGroupScan): + title = 'Verifying Redshift clusters are using activity logging' + permissions = [''] + + def __init__(self): + super().__init__('enable_user_activity_logging', + 'false', + 'Redshift cluster parameter groups with activity logging disabled') + + scans = ScanSuite('Redshift Scans', {'public': PubliclyAccessibleScan(), + 'logging': LoggingEnabledScan(), 'ssl': SSLEnabledScan()})