aws-cis-controls-assessment 1.1.4__py3-none-any.whl → 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aws_cis_assessment/__init__.py +4 -4
- aws_cis_assessment/config/rules/cis_controls_ig1.yaml +365 -2
- aws_cis_assessment/controls/ig1/control_access_analyzer.py +198 -0
- aws_cis_assessment/controls/ig1/control_access_asset_mgmt.py +360 -0
- aws_cis_assessment/controls/ig1/control_access_control.py +323 -0
- aws_cis_assessment/controls/ig1/control_backup_security.py +579 -0
- aws_cis_assessment/controls/ig1/control_cloudfront_logging.py +215 -0
- aws_cis_assessment/controls/ig1/control_configuration_mgmt.py +407 -0
- aws_cis_assessment/controls/ig1/control_data_classification.py +255 -0
- aws_cis_assessment/controls/ig1/control_dynamodb_encryption.py +279 -0
- aws_cis_assessment/controls/ig1/control_ebs_encryption.py +177 -0
- aws_cis_assessment/controls/ig1/control_efs_encryption.py +243 -0
- aws_cis_assessment/controls/ig1/control_elb_logging.py +195 -0
- aws_cis_assessment/controls/ig1/control_guardduty.py +156 -0
- aws_cis_assessment/controls/ig1/control_inspector.py +184 -0
- aws_cis_assessment/controls/ig1/control_inventory.py +511 -0
- aws_cis_assessment/controls/ig1/control_macie.py +165 -0
- aws_cis_assessment/controls/ig1/control_messaging_encryption.py +419 -0
- aws_cis_assessment/controls/ig1/control_mfa.py +485 -0
- aws_cis_assessment/controls/ig1/control_network_security.py +194 -619
- aws_cis_assessment/controls/ig1/control_patch_management.py +626 -0
- aws_cis_assessment/controls/ig1/control_rds_encryption.py +228 -0
- aws_cis_assessment/controls/ig1/control_s3_encryption.py +383 -0
- aws_cis_assessment/controls/ig1/control_tls_ssl.py +556 -0
- aws_cis_assessment/controls/ig1/control_version_mgmt.py +329 -0
- aws_cis_assessment/controls/ig1/control_vpc_flow_logs.py +205 -0
- aws_cis_assessment/controls/ig1/control_waf_logging.py +226 -0
- aws_cis_assessment/core/models.py +20 -1
- aws_cis_assessment/core/scoring_engine.py +98 -1
- aws_cis_assessment/reporters/base_reporter.py +31 -1
- aws_cis_assessment/reporters/html_reporter.py +163 -0
- aws_cis_controls_assessment-1.2.0.dist-info/METADATA +320 -0
- {aws_cis_controls_assessment-1.1.4.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/RECORD +39 -15
- docs/developer-guide.md +204 -5
- docs/user-guide.md +137 -4
- aws_cis_controls_assessment-1.1.4.dist-info/METADATA +0 -404
- {aws_cis_controls_assessment-1.1.4.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/WHEEL +0 -0
- {aws_cis_controls_assessment-1.1.4.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/entry_points.txt +0 -0
- {aws_cis_controls_assessment-1.1.4.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {aws_cis_controls_assessment-1.1.4.dist-info → aws_cis_controls_assessment-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CIS Control 2.2 - Version Management
|
|
3
|
+
Ensures software versions are current and supported.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from typing import List, Dict, Any
|
|
8
|
+
from botocore.exceptions import ClientError
|
|
9
|
+
|
|
10
|
+
from aws_cis_assessment.controls.base_control import BaseConfigRuleAssessment
|
|
11
|
+
from aws_cis_assessment.core.models import ComplianceResult, ComplianceStatus
|
|
12
|
+
from aws_cis_assessment.core.aws_client_factory import AWSClientFactory
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class EC2OSVersionSupportedAssessment(BaseConfigRuleAssessment):
|
|
18
|
+
"""
|
|
19
|
+
CIS Control 2.2 - Ensure Authorized Software is Currently Supported
|
|
20
|
+
AWS Config Rule: ec2-os-version-supported
|
|
21
|
+
|
|
22
|
+
Ensures EC2 instances run supported operating system versions.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
super().__init__(
|
|
27
|
+
rule_name="ec2-os-version-supported",
|
|
28
|
+
control_id="2.2",
|
|
29
|
+
resource_types=["AWS::EC2::Instance"]
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
|
|
33
|
+
"""Get EC2 instances with OS information."""
|
|
34
|
+
if resource_type != "AWS::EC2::Instance":
|
|
35
|
+
return []
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
ssm_client = aws_factory.get_client('ssm', region)
|
|
39
|
+
|
|
40
|
+
# Get instance information from SSM
|
|
41
|
+
response = ssm_client.describe_instance_information()
|
|
42
|
+
instances = []
|
|
43
|
+
|
|
44
|
+
for instance in response.get('InstanceInformationList', []):
|
|
45
|
+
platform_name = instance.get('PlatformName', 'Unknown')
|
|
46
|
+
platform_version = instance.get('PlatformVersion', 'Unknown')
|
|
47
|
+
|
|
48
|
+
# Simple heuristic for supported versions
|
|
49
|
+
is_supported = self._check_version_support(platform_name, platform_version)
|
|
50
|
+
|
|
51
|
+
instances.append({
|
|
52
|
+
'InstanceId': instance.get('InstanceId'),
|
|
53
|
+
'PlatformName': platform_name,
|
|
54
|
+
'PlatformVersion': platform_version,
|
|
55
|
+
'IsSupported': is_supported
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
return instances
|
|
59
|
+
|
|
60
|
+
except ClientError as e:
|
|
61
|
+
logger.error(f"Error retrieving instance information in {region}: {e}")
|
|
62
|
+
return []
|
|
63
|
+
|
|
64
|
+
def _check_version_support(self, platform_name: str, platform_version: str) -> bool:
|
|
65
|
+
"""Check if OS version is supported (simplified logic)."""
|
|
66
|
+
# This is a simplified check - in production, maintain a list of EOL versions
|
|
67
|
+
deprecated_keywords = ['2008', '2012', 'centos 6', 'ubuntu 14', 'ubuntu 16']
|
|
68
|
+
platform_lower = f"{platform_name} {platform_version}".lower()
|
|
69
|
+
|
|
70
|
+
return not any(keyword in platform_lower for keyword in deprecated_keywords)
|
|
71
|
+
|
|
72
|
+
def _evaluate_resource_compliance(
|
|
73
|
+
self,
|
|
74
|
+
resource: Dict[str, Any],
|
|
75
|
+
aws_factory: AWSClientFactory,
|
|
76
|
+
region: str
|
|
77
|
+
) -> ComplianceResult:
|
|
78
|
+
"""Evaluate if EC2 instance runs supported OS version."""
|
|
79
|
+
instance_id = resource.get('InstanceId', 'unknown')
|
|
80
|
+
platform_name = resource.get('PlatformName', 'Unknown')
|
|
81
|
+
platform_version = resource.get('PlatformVersion', 'Unknown')
|
|
82
|
+
is_supported = resource.get('IsSupported', True)
|
|
83
|
+
|
|
84
|
+
if is_supported:
|
|
85
|
+
evaluation_reason = f"Instance {instance_id} runs supported OS: {platform_name} {platform_version}"
|
|
86
|
+
compliance_status = ComplianceStatus.COMPLIANT
|
|
87
|
+
else:
|
|
88
|
+
evaluation_reason = f"Instance {instance_id} runs unsupported OS: {platform_name} {platform_version}"
|
|
89
|
+
compliance_status = ComplianceStatus.NON_COMPLIANT
|
|
90
|
+
|
|
91
|
+
return ComplianceResult(
|
|
92
|
+
resource_id=instance_id,
|
|
93
|
+
resource_type="AWS::EC2::Instance",
|
|
94
|
+
compliance_status=compliance_status,
|
|
95
|
+
evaluation_reason=evaluation_reason,
|
|
96
|
+
config_rule_name=self.rule_name,
|
|
97
|
+
region=region
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def _get_rule_remediation_steps(self) -> List[str]:
|
|
101
|
+
"""Get remediation steps for OS version support."""
|
|
102
|
+
return [
|
|
103
|
+
"1. Identify instances with unsupported OS:",
|
|
104
|
+
" aws ssm describe-instance-information \\",
|
|
105
|
+
" --query 'InstanceInformationList[].{ID:InstanceId,OS:PlatformName,Version:PlatformVersion}'",
|
|
106
|
+
"",
|
|
107
|
+
"2. Plan migration to supported OS versions",
|
|
108
|
+
"3. Create new instances with supported OS",
|
|
109
|
+
"4. Migrate workloads to new instances",
|
|
110
|
+
"5. Decommission old instances",
|
|
111
|
+
"",
|
|
112
|
+
"Priority: HIGH - Security risk with unsupported OS",
|
|
113
|
+
"Effort: High - Requires migration planning",
|
|
114
|
+
"",
|
|
115
|
+
"AWS Documentation:",
|
|
116
|
+
"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html"
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class RDSEngineVersionSupportedAssessment(BaseConfigRuleAssessment):
|
|
121
|
+
"""
|
|
122
|
+
CIS Control 2.2 - Ensure Authorized Software is Currently Supported
|
|
123
|
+
AWS Config Rule: rds-engine-version-supported
|
|
124
|
+
|
|
125
|
+
Ensures RDS instances run supported database engine versions.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __init__(self):
|
|
129
|
+
super().__init__(
|
|
130
|
+
rule_name="rds-engine-version-supported",
|
|
131
|
+
control_id="2.2",
|
|
132
|
+
resource_types=["AWS::RDS::DBInstance"]
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
|
|
136
|
+
"""Get RDS instances with engine version information."""
|
|
137
|
+
if resource_type != "AWS::RDS::DBInstance":
|
|
138
|
+
return []
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
rds_client = aws_factory.get_client('rds', region)
|
|
142
|
+
|
|
143
|
+
response = rds_client.describe_db_instances()
|
|
144
|
+
instances = []
|
|
145
|
+
|
|
146
|
+
for db in response.get('DBInstances', []):
|
|
147
|
+
engine = db.get('Engine')
|
|
148
|
+
engine_version = db.get('EngineVersion')
|
|
149
|
+
|
|
150
|
+
# Check if version is deprecated
|
|
151
|
+
try:
|
|
152
|
+
versions_response = rds_client.describe_db_engine_versions(
|
|
153
|
+
Engine=engine,
|
|
154
|
+
EngineVersion=engine_version
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if versions_response.get('DBEngineVersions'):
|
|
158
|
+
version_info = versions_response['DBEngineVersions'][0]
|
|
159
|
+
is_deprecated = version_info.get('Status') == 'deprecated'
|
|
160
|
+
else:
|
|
161
|
+
is_deprecated = False
|
|
162
|
+
except ClientError:
|
|
163
|
+
is_deprecated = False
|
|
164
|
+
|
|
165
|
+
instances.append({
|
|
166
|
+
'DBInstanceIdentifier': db.get('DBInstanceIdentifier'),
|
|
167
|
+
'Engine': engine,
|
|
168
|
+
'EngineVersion': engine_version,
|
|
169
|
+
'IsDeprecated': is_deprecated
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
return instances
|
|
173
|
+
|
|
174
|
+
except ClientError as e:
|
|
175
|
+
logger.error(f"Error retrieving RDS instances in {region}: {e}")
|
|
176
|
+
return []
|
|
177
|
+
|
|
178
|
+
def _evaluate_resource_compliance(
|
|
179
|
+
self,
|
|
180
|
+
resource: Dict[str, Any],
|
|
181
|
+
aws_factory: AWSClientFactory,
|
|
182
|
+
region: str
|
|
183
|
+
) -> ComplianceResult:
|
|
184
|
+
"""Evaluate if RDS instance runs supported engine version."""
|
|
185
|
+
db_id = resource.get('DBInstanceIdentifier', 'unknown')
|
|
186
|
+
engine = resource.get('Engine', 'unknown')
|
|
187
|
+
engine_version = resource.get('EngineVersion', 'unknown')
|
|
188
|
+
is_deprecated = resource.get('IsDeprecated', False)
|
|
189
|
+
|
|
190
|
+
if not is_deprecated:
|
|
191
|
+
evaluation_reason = f"RDS instance {db_id} runs supported {engine} version {engine_version}"
|
|
192
|
+
compliance_status = ComplianceStatus.COMPLIANT
|
|
193
|
+
else:
|
|
194
|
+
evaluation_reason = f"RDS instance {db_id} runs deprecated {engine} version {engine_version}"
|
|
195
|
+
compliance_status = ComplianceStatus.NON_COMPLIANT
|
|
196
|
+
|
|
197
|
+
return ComplianceResult(
|
|
198
|
+
resource_id=db_id,
|
|
199
|
+
resource_type="AWS::RDS::DBInstance",
|
|
200
|
+
compliance_status=compliance_status,
|
|
201
|
+
evaluation_reason=evaluation_reason,
|
|
202
|
+
config_rule_name=self.rule_name,
|
|
203
|
+
region=region
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
def _get_rule_remediation_steps(self) -> List[str]:
|
|
207
|
+
"""Get remediation steps for RDS version support."""
|
|
208
|
+
return [
|
|
209
|
+
"1. Check for available engine versions:",
|
|
210
|
+
" aws rds describe-db-engine-versions \\",
|
|
211
|
+
" --engine <engine-name> \\",
|
|
212
|
+
" --query 'DBEngineVersions[?Status!=`deprecated`]'",
|
|
213
|
+
"",
|
|
214
|
+
"2. Upgrade RDS instance:",
|
|
215
|
+
" aws rds modify-db-instance \\",
|
|
216
|
+
" --db-instance-identifier <db-id> \\",
|
|
217
|
+
" --engine-version <new-version> \\",
|
|
218
|
+
" --apply-immediately",
|
|
219
|
+
"",
|
|
220
|
+
"3. Test application compatibility before upgrading",
|
|
221
|
+
"",
|
|
222
|
+
"Priority: HIGH - Deprecated versions lack security updates",
|
|
223
|
+
"Effort: Medium - Requires testing and maintenance window",
|
|
224
|
+
"",
|
|
225
|
+
"AWS Documentation:",
|
|
226
|
+
"https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_UpgradeDBInstance.Upgrading.html"
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class LambdaRuntimeSupportedAssessment(BaseConfigRuleAssessment):
|
|
231
|
+
"""
|
|
232
|
+
CIS Control 2.2 - Ensure Authorized Software is Currently Supported
|
|
233
|
+
AWS Config Rule: lambda-runtime-supported
|
|
234
|
+
|
|
235
|
+
Ensures Lambda functions use supported runtimes.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
def __init__(self):
|
|
239
|
+
super().__init__(
|
|
240
|
+
rule_name="lambda-runtime-supported",
|
|
241
|
+
control_id="2.2",
|
|
242
|
+
resource_types=["AWS::Lambda::Function"]
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
|
|
246
|
+
"""Get Lambda functions with runtime information."""
|
|
247
|
+
if resource_type != "AWS::Lambda::Function":
|
|
248
|
+
return []
|
|
249
|
+
|
|
250
|
+
try:
|
|
251
|
+
lambda_client = aws_factory.get_client('lambda', region)
|
|
252
|
+
functions = []
|
|
253
|
+
|
|
254
|
+
# Deprecated runtimes as of 2026
|
|
255
|
+
deprecated_runtimes = [
|
|
256
|
+
'python2.7', 'python3.6', 'python3.7',
|
|
257
|
+
'nodejs10.x', 'nodejs12.x', 'nodejs14.x',
|
|
258
|
+
'ruby2.5', 'ruby2.7',
|
|
259
|
+
'dotnetcore2.1', 'dotnetcore3.1',
|
|
260
|
+
'go1.x'
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
paginator = lambda_client.get_paginator('list_functions')
|
|
264
|
+
for page in paginator.paginate():
|
|
265
|
+
for func in page.get('Functions', []):
|
|
266
|
+
runtime = func.get('Runtime', 'unknown')
|
|
267
|
+
is_deprecated = runtime in deprecated_runtimes
|
|
268
|
+
|
|
269
|
+
functions.append({
|
|
270
|
+
'FunctionName': func.get('FunctionName'),
|
|
271
|
+
'FunctionArn': func.get('FunctionArn'),
|
|
272
|
+
'Runtime': runtime,
|
|
273
|
+
'IsDeprecated': is_deprecated
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
return functions
|
|
277
|
+
|
|
278
|
+
except ClientError as e:
|
|
279
|
+
logger.error(f"Error retrieving Lambda functions in {region}: {e}")
|
|
280
|
+
return []
|
|
281
|
+
|
|
282
|
+
def _evaluate_resource_compliance(
|
|
283
|
+
self,
|
|
284
|
+
resource: Dict[str, Any],
|
|
285
|
+
aws_factory: AWSClientFactory,
|
|
286
|
+
region: str
|
|
287
|
+
) -> ComplianceResult:
|
|
288
|
+
"""Evaluate if Lambda function uses supported runtime."""
|
|
289
|
+
function_name = resource.get('FunctionName', 'unknown')
|
|
290
|
+
runtime = resource.get('Runtime', 'unknown')
|
|
291
|
+
is_deprecated = resource.get('IsDeprecated', False)
|
|
292
|
+
|
|
293
|
+
if not is_deprecated:
|
|
294
|
+
evaluation_reason = f"Lambda function {function_name} uses supported runtime: {runtime}"
|
|
295
|
+
compliance_status = ComplianceStatus.COMPLIANT
|
|
296
|
+
else:
|
|
297
|
+
evaluation_reason = f"Lambda function {function_name} uses deprecated runtime: {runtime}"
|
|
298
|
+
compliance_status = ComplianceStatus.NON_COMPLIANT
|
|
299
|
+
|
|
300
|
+
return ComplianceResult(
|
|
301
|
+
resource_id=resource.get('FunctionArn', function_name),
|
|
302
|
+
resource_type="AWS::Lambda::Function",
|
|
303
|
+
compliance_status=compliance_status,
|
|
304
|
+
evaluation_reason=evaluation_reason,
|
|
305
|
+
config_rule_name=self.rule_name,
|
|
306
|
+
region=region
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
def _get_rule_remediation_steps(self) -> List[str]:
|
|
310
|
+
"""Get remediation steps for Lambda runtime support."""
|
|
311
|
+
return [
|
|
312
|
+
"1. List functions with deprecated runtimes:",
|
|
313
|
+
" aws lambda list-functions \\",
|
|
314
|
+
" --query 'Functions[?Runtime==`python3.7`].FunctionName'",
|
|
315
|
+
"",
|
|
316
|
+
"2. Update function runtime:",
|
|
317
|
+
" aws lambda update-function-configuration \\",
|
|
318
|
+
" --function-name <function-name> \\",
|
|
319
|
+
" --runtime python3.11",
|
|
320
|
+
"",
|
|
321
|
+
"3. Test function after runtime update",
|
|
322
|
+
"4. Update deployment packages if needed",
|
|
323
|
+
"",
|
|
324
|
+
"Priority: HIGH - Deprecated runtimes lose security support",
|
|
325
|
+
"Effort: Medium - Requires testing and code updates",
|
|
326
|
+
"",
|
|
327
|
+
"AWS Documentation:",
|
|
328
|
+
"https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html"
|
|
329
|
+
]
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CIS Control 8.2 - VPC Flow Logs
|
|
3
|
+
Ensures VPC Flow Logs are enabled for network traffic visibility.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from typing import List, Dict, Any
|
|
8
|
+
from botocore.exceptions import ClientError
|
|
9
|
+
|
|
10
|
+
from aws_cis_assessment.controls.base_control import BaseConfigRuleAssessment
|
|
11
|
+
from aws_cis_assessment.core.models import ComplianceResult, ComplianceStatus
|
|
12
|
+
from aws_cis_assessment.core.aws_client_factory import AWSClientFactory
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class VPCFlowLogsEnabledAssessment(BaseConfigRuleAssessment):
|
|
18
|
+
"""
|
|
19
|
+
CIS Control 8.2 - Collect Audit Logs
|
|
20
|
+
AWS Config Rule: vpc-flow-logs-enabled
|
|
21
|
+
|
|
22
|
+
Ensures VPC Flow Logs are enabled for network traffic monitoring.
|
|
23
|
+
Flow Logs capture information about IP traffic going to and from
|
|
24
|
+
network interfaces in VPCs, essential for security analysis and troubleshooting.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
super().__init__(
|
|
29
|
+
rule_name="vpc-flow-logs-enabled",
|
|
30
|
+
control_id="8.2",
|
|
31
|
+
resource_types=["AWS::EC2::VPC"]
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
|
|
35
|
+
"""Get VPCs and their Flow Logs configuration."""
|
|
36
|
+
if resource_type != "AWS::EC2::VPC":
|
|
37
|
+
return []
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
ec2_client = aws_factory.get_client('ec2', region)
|
|
41
|
+
|
|
42
|
+
# Get all VPCs
|
|
43
|
+
vpcs_response = ec2_client.describe_vpcs()
|
|
44
|
+
vpcs = vpcs_response.get('Vpcs', [])
|
|
45
|
+
|
|
46
|
+
if not vpcs:
|
|
47
|
+
return []
|
|
48
|
+
|
|
49
|
+
# Get all Flow Logs
|
|
50
|
+
flow_logs_response = ec2_client.describe_flow_logs()
|
|
51
|
+
flow_logs = flow_logs_response.get('FlowLogs', [])
|
|
52
|
+
|
|
53
|
+
# Create a mapping of VPC ID to Flow Logs
|
|
54
|
+
vpc_flow_logs = {}
|
|
55
|
+
for flow_log in flow_logs:
|
|
56
|
+
resource_id = flow_log.get('ResourceId', '')
|
|
57
|
+
if resource_id.startswith('vpc-'):
|
|
58
|
+
if resource_id not in vpc_flow_logs:
|
|
59
|
+
vpc_flow_logs[resource_id] = []
|
|
60
|
+
vpc_flow_logs[resource_id].append(flow_log)
|
|
61
|
+
|
|
62
|
+
# Build resource list with Flow Log status
|
|
63
|
+
vpc_resources = []
|
|
64
|
+
for vpc in vpcs:
|
|
65
|
+
vpc_id = vpc.get('VpcId', '')
|
|
66
|
+
vpc_flow_log_list = vpc_flow_logs.get(vpc_id, [])
|
|
67
|
+
|
|
68
|
+
# Check if any active Flow Logs exist
|
|
69
|
+
active_flow_logs = [
|
|
70
|
+
fl for fl in vpc_flow_log_list
|
|
71
|
+
if fl.get('FlowLogStatus') == 'ACTIVE'
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
vpc_resources.append({
|
|
75
|
+
'VpcId': vpc_id,
|
|
76
|
+
'IsDefault': vpc.get('IsDefault', False),
|
|
77
|
+
'CidrBlock': vpc.get('CidrBlock', ''),
|
|
78
|
+
'State': vpc.get('State', ''),
|
|
79
|
+
'Region': region,
|
|
80
|
+
'FlowLogs': active_flow_logs,
|
|
81
|
+
'HasActiveFlowLogs': len(active_flow_logs) > 0,
|
|
82
|
+
'Tags': vpc.get('Tags', [])
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
return vpc_resources
|
|
86
|
+
|
|
87
|
+
except ClientError as e:
|
|
88
|
+
error_code = e.response.get('Error', {}).get('Code', '')
|
|
89
|
+
if error_code == 'UnauthorizedOperation':
|
|
90
|
+
logger.warning(f"Access denied to describe VPCs/Flow Logs in {region}")
|
|
91
|
+
else:
|
|
92
|
+
logger.error(f"Error describing VPCs/Flow Logs in {region}: {e}")
|
|
93
|
+
return []
|
|
94
|
+
|
|
95
|
+
def _evaluate_resource_compliance(
|
|
96
|
+
self,
|
|
97
|
+
resource: Dict[str, Any],
|
|
98
|
+
aws_factory: AWSClientFactory,
|
|
99
|
+
region: str
|
|
100
|
+
) -> ComplianceResult:
|
|
101
|
+
"""Evaluate if VPC has Flow Logs enabled."""
|
|
102
|
+
vpc_id = resource.get('VpcId', '')
|
|
103
|
+
has_flow_logs = resource.get('HasActiveFlowLogs', False)
|
|
104
|
+
flow_logs = resource.get('FlowLogs', [])
|
|
105
|
+
is_default = resource.get('IsDefault', False)
|
|
106
|
+
|
|
107
|
+
# Check if VPC has active Flow Logs
|
|
108
|
+
is_compliant = has_flow_logs
|
|
109
|
+
|
|
110
|
+
if is_compliant:
|
|
111
|
+
# Provide details about the Flow Logs
|
|
112
|
+
flow_log_details = []
|
|
113
|
+
for fl in flow_logs:
|
|
114
|
+
destination = fl.get('LogDestinationType', 'unknown')
|
|
115
|
+
traffic_type = fl.get('TrafficType', 'unknown')
|
|
116
|
+
flow_log_details.append(f"{traffic_type} to {destination}")
|
|
117
|
+
|
|
118
|
+
evaluation_reason = (
|
|
119
|
+
f"VPC {vpc_id} has {len(flow_logs)} active Flow Log(s) enabled: "
|
|
120
|
+
f"{', '.join(flow_log_details)}"
|
|
121
|
+
)
|
|
122
|
+
if is_default:
|
|
123
|
+
evaluation_reason += " (Default VPC)"
|
|
124
|
+
|
|
125
|
+
compliance_status = ComplianceStatus.COMPLIANT
|
|
126
|
+
else:
|
|
127
|
+
evaluation_reason = f"VPC {vpc_id} does not have Flow Logs enabled."
|
|
128
|
+
if is_default:
|
|
129
|
+
evaluation_reason += " (Default VPC - should be monitored)"
|
|
130
|
+
compliance_status = ComplianceStatus.NON_COMPLIANT
|
|
131
|
+
|
|
132
|
+
return ComplianceResult(
|
|
133
|
+
resource_id=vpc_id,
|
|
134
|
+
resource_type="AWS::EC2::VPC",
|
|
135
|
+
compliance_status=compliance_status,
|
|
136
|
+
evaluation_reason=evaluation_reason,
|
|
137
|
+
config_rule_name=self.rule_name,
|
|
138
|
+
region=region
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def _get_rule_remediation_steps(self) -> List[str]:
|
|
142
|
+
"""Get remediation steps for enabling VPC Flow Logs."""
|
|
143
|
+
return [
|
|
144
|
+
"1. Enable VPC Flow Logs in the AWS Console:",
|
|
145
|
+
" - Navigate to VPC service",
|
|
146
|
+
" - Select the VPC",
|
|
147
|
+
" - Click 'Actions' > 'Create flow log'",
|
|
148
|
+
" - Configure:",
|
|
149
|
+
" * Filter: ALL (captures accepted and rejected traffic)",
|
|
150
|
+
" * Destination: CloudWatch Logs or S3",
|
|
151
|
+
" * IAM role: Create or select role with permissions",
|
|
152
|
+
" - Click 'Create flow log'",
|
|
153
|
+
"",
|
|
154
|
+
"2. Enable Flow Logs using AWS CLI (CloudWatch Logs):",
|
|
155
|
+
" # Create IAM role first (one-time setup)",
|
|
156
|
+
" aws iam create-role \\",
|
|
157
|
+
" --role-name VPCFlowLogsRole \\",
|
|
158
|
+
" --assume-role-policy-document file://trust-policy.json",
|
|
159
|
+
"",
|
|
160
|
+
" # Create Flow Log",
|
|
161
|
+
" aws ec2 create-flow-logs \\",
|
|
162
|
+
" --resource-type VPC \\",
|
|
163
|
+
" --resource-ids <vpc-id> \\",
|
|
164
|
+
" --traffic-type ALL \\",
|
|
165
|
+
" --log-destination-type cloud-watch-logs \\",
|
|
166
|
+
" --log-group-name /aws/vpc/flowlogs \\",
|
|
167
|
+
" --deliver-logs-permission-arn <role-arn> \\",
|
|
168
|
+
" --region <region>",
|
|
169
|
+
"",
|
|
170
|
+
"3. Enable Flow Logs using AWS CLI (S3):",
|
|
171
|
+
" aws ec2 create-flow-logs \\",
|
|
172
|
+
" --resource-type VPC \\",
|
|
173
|
+
" --resource-ids <vpc-id> \\",
|
|
174
|
+
" --traffic-type ALL \\",
|
|
175
|
+
" --log-destination-type s3 \\",
|
|
176
|
+
" --log-destination arn:aws:s3:::<bucket-name> \\",
|
|
177
|
+
" --region <region>",
|
|
178
|
+
"",
|
|
179
|
+
"4. Best practices:",
|
|
180
|
+
" - Enable for ALL traffic (not just ACCEPT or REJECT)",
|
|
181
|
+
" - Use S3 for cost-effective long-term storage",
|
|
182
|
+
" - Use CloudWatch Logs for real-time analysis and alerting",
|
|
183
|
+
" - Set appropriate log retention (30-90 days minimum)",
|
|
184
|
+
" - Enable for all VPCs, including default VPC",
|
|
185
|
+
"",
|
|
186
|
+
"5. Analyze Flow Logs:",
|
|
187
|
+
" - Use CloudWatch Insights for queries",
|
|
188
|
+
" - Use Athena for S3-based logs",
|
|
189
|
+
" - Look for:",
|
|
190
|
+
" * Rejected traffic (potential attacks)",
|
|
191
|
+
" * Unusual traffic patterns",
|
|
192
|
+
" * Data exfiltration attempts",
|
|
193
|
+
" * Unauthorized access attempts",
|
|
194
|
+
"",
|
|
195
|
+
"6. Cost optimization:",
|
|
196
|
+
" - Flow Logs incur charges for data ingestion and storage",
|
|
197
|
+
" - Use sampling if full capture is too expensive",
|
|
198
|
+
" - Archive old logs to S3 Glacier",
|
|
199
|
+
"",
|
|
200
|
+
"Priority: HIGH - Network visibility is essential for security",
|
|
201
|
+
"Effort: Low - Can be enabled in minutes per VPC",
|
|
202
|
+
"",
|
|
203
|
+
"AWS Documentation:",
|
|
204
|
+
"https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html"
|
|
205
|
+
]
|