sraverify 0.1.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.
- sraverify/__init__.py +36 -0
- sraverify/checks/__init__.py +56 -0
- sraverify/checks/accessanalyzer/SRA_IAA_1.py +188 -0
- sraverify/checks/accessanalyzer/SRA_IAA_2.py +162 -0
- sraverify/checks/accessanalyzer/SRA_IAA_3.py +260 -0
- sraverify/checks/accessanalyzer/SRA_IAA_4.py +207 -0
- sraverify/checks/accessanalyzer/__init__.py +3 -0
- sraverify/checks/cloudtrail/SRA-CT-1.py +220 -0
- sraverify/checks/cloudtrail/SRA-CT-10.py +229 -0
- sraverify/checks/cloudtrail/SRA-CT-11.py +242 -0
- sraverify/checks/cloudtrail/SRA-CT-12.py +163 -0
- sraverify/checks/cloudtrail/SRA-CT-13.py +279 -0
- sraverify/checks/cloudtrail/SRA-CT-2.py +218 -0
- sraverify/checks/cloudtrail/SRA-CT-3.py +196 -0
- sraverify/checks/cloudtrail/SRA-CT-4.py +161 -0
- sraverify/checks/cloudtrail/SRA-CT-5.py +200 -0
- sraverify/checks/cloudtrail/SRA-CT-6.py +161 -0
- sraverify/checks/cloudtrail/SRA-CT-7.py +194 -0
- sraverify/checks/cloudtrail/SRA-CT-8.py +226 -0
- sraverify/checks/cloudtrail/SRA-CT-9.py +226 -0
- sraverify/checks/cloudtrail/__init__.py +3 -0
- sraverify/checks/config/SRA-CONFIG-1.py +197 -0
- sraverify/checks/config/__init__.py +3 -0
- sraverify/core/__init__.py +3 -0
- sraverify/core/check.py +227 -0
- sraverify/core/logging.py +37 -0
- sraverify/core/session.py +47 -0
- sraverify/lib/__init__.py +4 -0
- sraverify/lib/audit_info.py +37 -0
- sraverify/lib/banner.py +42 -0
- sraverify/lib/check_loader.py +80 -0
- sraverify/lib/org_mgmt_checker.py +86 -0
- sraverify/lib/outputs.py +46 -0
- sraverify/lib/progress.py +75 -0
- sraverify/lib/regions.py +27 -0
- sraverify/lib/session.py +23 -0
- sraverify/main.py +350 -0
- sraverify/services/__init__.py +3 -0
- sraverify/services/accessanalyzer/__init__.py +15 -0
- sraverify/services/accessanalyzer/base.py +123 -0
- sraverify/services/accessanalyzer/checks/__init__.py +3 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_01.py +82 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_02.py +82 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_03.py +103 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_04.py +139 -0
- sraverify/services/accessanalyzer/client.py +123 -0
- sraverify/services/account/__init__.py +9 -0
- sraverify/services/account/base.py +56 -0
- sraverify/services/account/checks/__init__.py +1 -0
- sraverify/services/account/checks/sra_account_01.py +65 -0
- sraverify/services/account/checks/sra_account_02.py +63 -0
- sraverify/services/account/checks/sra_account_03.py +63 -0
- sraverify/services/account/client.py +51 -0
- sraverify/services/auditmanager/__init__.py +10 -0
- sraverify/services/auditmanager/base.py +72 -0
- sraverify/services/auditmanager/checks/__init__.py +1 -0
- sraverify/services/auditmanager/checks/sra_auditmanager_01.py +58 -0
- sraverify/services/auditmanager/checks/sra_auditmanager_02.py +80 -0
- sraverify/services/auditmanager/client.py +58 -0
- sraverify/services/cloudtrail/__init__.py +33 -0
- sraverify/services/cloudtrail/base.py +167 -0
- sraverify/services/cloudtrail/checks/__init__.py +1 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_01.py +83 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_02.py +99 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_03.py +94 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_04.py +92 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_05.py +106 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_06.py +93 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_07.py +96 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_08.py +145 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_09.py +167 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_10.py +162 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_11.py +178 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_12.py +77 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_13.py +120 -0
- sraverify/services/cloudtrail/client.py +118 -0
- sraverify/services/config/__init__.py +25 -0
- sraverify/services/config/base.py +249 -0
- sraverify/services/config/checks/__init__.py +1 -0
- sraverify/services/config/checks/sra_config_01.py +123 -0
- sraverify/services/config/checks/sra_config_02.py +156 -0
- sraverify/services/config/checks/sra_config_03.py +149 -0
- sraverify/services/config/checks/sra_config_04.py +104 -0
- sraverify/services/config/checks/sra_config_05.py +104 -0
- sraverify/services/config/checks/sra_config_06.py +194 -0
- sraverify/services/config/checks/sra_config_07.py +162 -0
- sraverify/services/config/checks/sra_config_08.py +185 -0
- sraverify/services/config/checks/sra_config_09.py +177 -0
- sraverify/services/config/client.py +264 -0
- sraverify/services/ec2/__init__.py +8 -0
- sraverify/services/ec2/base.py +75 -0
- sraverify/services/ec2/checks/__init__.py +1 -0
- sraverify/services/ec2/checks/sra_ec2_01.py +83 -0
- sraverify/services/ec2/client.py +63 -0
- sraverify/services/firewallmanager/__init__.py +23 -0
- sraverify/services/firewallmanager/base.py +48 -0
- sraverify/services/firewallmanager/checks/__init__.py +1 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_01.py +75 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_02.py +57 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_03.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_04.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_05.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_06.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_07.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_08.py +61 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_09.py +61 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_10.py +71 -0
- sraverify/services/firewallmanager/client.py +40 -0
- sraverify/services/guardduty/__init__.py +58 -0
- sraverify/services/guardduty/base.py +207 -0
- sraverify/services/guardduty/checks/__init__.py +3 -0
- sraverify/services/guardduty/checks/sra_guardduty_01.py +51 -0
- sraverify/services/guardduty/checks/sra_guardduty_02.py +80 -0
- sraverify/services/guardduty/checks/sra_guardduty_03.py +77 -0
- sraverify/services/guardduty/checks/sra_guardduty_04.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_05.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_06.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_07.py +85 -0
- sraverify/services/guardduty/checks/sra_guardduty_08.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_09.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_10.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_11.py +93 -0
- sraverify/services/guardduty/checks/sra_guardduty_12.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_13.py +90 -0
- sraverify/services/guardduty/checks/sra_guardduty_14.py +136 -0
- sraverify/services/guardduty/checks/sra_guardduty_15.py +94 -0
- sraverify/services/guardduty/checks/sra_guardduty_16.py +94 -0
- sraverify/services/guardduty/checks/sra_guardduty_17.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_18.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_19.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_20.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_21.py +112 -0
- sraverify/services/guardduty/checks/sra_guardduty_22.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_23.py +154 -0
- sraverify/services/guardduty/checks/sra_guardduty_24.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_25.py +111 -0
- sraverify/services/guardduty/client.py +107 -0
- sraverify/services/inspector/__init__.py +29 -0
- sraverify/services/inspector/base.py +233 -0
- sraverify/services/inspector/checks/__init__.py +3 -0
- sraverify/services/inspector/checks/sra_inspector_01.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_02.py +68 -0
- sraverify/services/inspector/checks/sra_inspector_03.py +68 -0
- sraverify/services/inspector/checks/sra_inspector_04.py +70 -0
- sraverify/services/inspector/checks/sra_inspector_05.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_06.py +115 -0
- sraverify/services/inspector/checks/sra_inspector_07.py +109 -0
- sraverify/services/inspector/checks/sra_inspector_08.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_09.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_10.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_11.py +69 -0
- sraverify/services/inspector/client.py +99 -0
- sraverify/services/macie/__init__.py +27 -0
- sraverify/services/macie/base.py +271 -0
- sraverify/services/macie/checks/__init__.py +1 -0
- sraverify/services/macie/checks/sra_macie_01.py +100 -0
- sraverify/services/macie/checks/sra_macie_02.py +102 -0
- sraverify/services/macie/checks/sra_macie_03.py +152 -0
- sraverify/services/macie/checks/sra_macie_04.py +120 -0
- sraverify/services/macie/checks/sra_macie_05.py +85 -0
- sraverify/services/macie/checks/sra_macie_06.py +124 -0
- sraverify/services/macie/checks/sra_macie_07.py +138 -0
- sraverify/services/macie/checks/sra_macie_08.py +82 -0
- sraverify/services/macie/checks/sra_macie_09.py +103 -0
- sraverify/services/macie/checks/sra_macie_10.py +81 -0
- sraverify/services/macie/client.py +220 -0
- sraverify/services/s3/__init__.py +16 -0
- sraverify/services/s3/base.py +69 -0
- sraverify/services/s3/checks/__init__.py +1 -0
- sraverify/services/s3/checks/sra_s3_01.py +89 -0
- sraverify/services/s3/checks/sra_s3_02.py +89 -0
- sraverify/services/s3/checks/sra_s3_03.py +88 -0
- sraverify/services/s3/checks/sra_s3_04.py +88 -0
- sraverify/services/s3/client.py +52 -0
- sraverify/services/securityhub/__init__.py +27 -0
- sraverify/services/securityhub/base.py +349 -0
- sraverify/services/securityhub/checks/__init__.py +1 -0
- sraverify/services/securityhub/checks/sra_securityhub_01.py +115 -0
- sraverify/services/securityhub/checks/sra_securityhub_02.py +114 -0
- sraverify/services/securityhub/checks/sra_securityhub_03.py +136 -0
- sraverify/services/securityhub/checks/sra_securityhub_04.py +75 -0
- sraverify/services/securityhub/checks/sra_securityhub_05.py +102 -0
- sraverify/services/securityhub/checks/sra_securityhub_06.py +113 -0
- sraverify/services/securityhub/checks/sra_securityhub_07.py +121 -0
- sraverify/services/securityhub/checks/sra_securityhub_08.py +113 -0
- sraverify/services/securityhub/checks/sra_securityhub_09.py +100 -0
- sraverify/services/securityhub/checks/sra_securityhub_10.py +94 -0
- sraverify/services/securityhub/checks/sra_securityhub_11.py +73 -0
- sraverify/services/securityhub/client.py +249 -0
- sraverify/services/securityincidentresponse/__init__.py +13 -0
- sraverify/services/securityincidentresponse/base.py +95 -0
- sraverify/services/securityincidentresponse/checks/__init__.py +1 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_01.py +77 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_02.py +72 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_03.py +86 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_04.py +117 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_05.py +55 -0
- sraverify/services/securityincidentresponse/client.py +71 -0
- sraverify/services/securitylake/__init__.py +39 -0
- sraverify/services/securitylake/base.py +461 -0
- sraverify/services/securitylake/checks/__init__.py +1 -0
- sraverify/services/securitylake/checks/sra_securitylake_01.py +98 -0
- sraverify/services/securitylake/checks/sra_securitylake_02.py +133 -0
- sraverify/services/securitylake/checks/sra_securitylake_03.py +116 -0
- sraverify/services/securitylake/checks/sra_securitylake_04.py +72 -0
- sraverify/services/securitylake/checks/sra_securitylake_05.py +116 -0
- sraverify/services/securitylake/checks/sra_securitylake_06.py +104 -0
- sraverify/services/securitylake/checks/sra_securitylake_07.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_08.py +107 -0
- sraverify/services/securitylake/checks/sra_securitylake_09.py +107 -0
- sraverify/services/securitylake/checks/sra_securitylake_10.py +106 -0
- sraverify/services/securitylake/checks/sra_securitylake_11.py +109 -0
- sraverify/services/securitylake/checks/sra_securitylake_12.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_13.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_14.py +72 -0
- sraverify/services/securitylake/checks/sra_securitylake_15.py +120 -0
- sraverify/services/securitylake/checks/sra_securitylake_16.py +104 -0
- sraverify/services/securitylake/checks/sra_securitylake_17.py +103 -0
- sraverify/services/securitylake/client.py +247 -0
- sraverify/services/shield/__init__.py +33 -0
- sraverify/services/shield/base.py +199 -0
- sraverify/services/shield/checks/__init__.py +1 -0
- sraverify/services/shield/checks/sra_shield_01.py +68 -0
- sraverify/services/shield/checks/sra_shield_02.py +77 -0
- sraverify/services/shield/checks/sra_shield_03.py +84 -0
- sraverify/services/shield/checks/sra_shield_04.py +84 -0
- sraverify/services/shield/checks/sra_shield_05.py +84 -0
- sraverify/services/shield/checks/sra_shield_06.py +84 -0
- sraverify/services/shield/checks/sra_shield_07.py +84 -0
- sraverify/services/shield/checks/sra_shield_08.py +69 -0
- sraverify/services/shield/checks/sra_shield_09.py +86 -0
- sraverify/services/shield/checks/sra_shield_10.py +100 -0
- sraverify/services/shield/checks/sra_shield_11.py +71 -0
- sraverify/services/shield/checks/sra_shield_12.py +130 -0
- sraverify/services/shield/checks/sra_shield_13.py +112 -0
- sraverify/services/shield/checks/sra_shield_14.py +111 -0
- sraverify/services/shield/client.py +214 -0
- sraverify/services/waf/__init__.py +21 -0
- sraverify/services/waf/base.py +100 -0
- sraverify/services/waf/checks/__init__.py +1 -0
- sraverify/services/waf/checks/sra_waf_01.py +63 -0
- sraverify/services/waf/checks/sra_waf_02.py +82 -0
- sraverify/services/waf/checks/sra_waf_03.py +123 -0
- sraverify/services/waf/checks/sra_waf_04.py +94 -0
- sraverify/services/waf/checks/sra_waf_05.py +94 -0
- sraverify/services/waf/checks/sra_waf_06.py +91 -0
- sraverify/services/waf/checks/sra_waf_07.py +94 -0
- sraverify/services/waf/checks/sra_waf_08.py +66 -0
- sraverify/services/waf/checks/sra_waf_09.py +95 -0
- sraverify/services/waf/client.py +109 -0
- sraverify/utils/__init__.py +3 -0
- sraverify/utils/banner.py +65 -0
- sraverify/utils/outputs.py +57 -0
- sraverify/utils/progress.py +97 -0
- sraverify-0.1.0.dist-info/LICENSE +175 -0
- sraverify-0.1.0.dist-info/METADATA +516 -0
- sraverify-0.1.0.dist-info/NOTICE +1 -0
- sraverify-0.1.0.dist-info/RECORD +261 -0
- sraverify-0.1.0.dist-info/WHEEL +5 -0
- sraverify-0.1.0.dist-info/entry_points.txt +2 -0
- sraverify-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SRA-CONFIG-01: AWS Config Recorder Configured.
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Dict, Any
|
|
5
|
+
from sraverify.services.config.base import ConfigCheck
|
|
6
|
+
from sraverify.core.logging import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SRA_CONFIG_01(ConfigCheck):
|
|
10
|
+
"""Check if AWS Config recorder is configured in each region."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
"""Initialize the check."""
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.check_id = "SRA-CONFIG-01"
|
|
16
|
+
self.check_name = "AWS Config recorder is configured in this region"
|
|
17
|
+
self.account_type = "application"
|
|
18
|
+
self.severity = "HIGH"
|
|
19
|
+
self.description = (
|
|
20
|
+
"This check verifies that a configuration recorder exists in the AWS Region. "
|
|
21
|
+
"AWS Config uses the configuration recorder to detect changes in your resource configurations "
|
|
22
|
+
"and capture these changes as configuration items. You must create a configuration recorder "
|
|
23
|
+
"in every AWS Region for AWS Config can track your resource configurations in the region."
|
|
24
|
+
)
|
|
25
|
+
self.check_logic = (
|
|
26
|
+
"Checks if AWS Config recorder exists in each region using describe-configuration-recorder-status API."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
30
|
+
"""
|
|
31
|
+
Execute the check.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
List of findings
|
|
35
|
+
"""
|
|
36
|
+
findings = []
|
|
37
|
+
|
|
38
|
+
if not self.regions:
|
|
39
|
+
findings.append(
|
|
40
|
+
self.create_finding(
|
|
41
|
+
status="ERROR",
|
|
42
|
+
region="global",
|
|
43
|
+
resource_id="config:global",
|
|
44
|
+
actual_value="No regions specified for check",
|
|
45
|
+
remediation="Specify at least one region when running the check"
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
return findings
|
|
49
|
+
|
|
50
|
+
# Check each region for configuration recorder
|
|
51
|
+
for region in self.regions:
|
|
52
|
+
# Get configuration recorders for the region
|
|
53
|
+
recorders = self.get_configuration_recorders(region)
|
|
54
|
+
|
|
55
|
+
# Get configuration recorder status for the region
|
|
56
|
+
recorder_statuses = self.get_configuration_recorder_status(region)
|
|
57
|
+
|
|
58
|
+
if not recorders:
|
|
59
|
+
# No configuration recorder found in this region
|
|
60
|
+
findings.append(
|
|
61
|
+
self.create_finding(
|
|
62
|
+
status="FAIL",
|
|
63
|
+
region=region,
|
|
64
|
+
resource_id=f"arn:aws:config:{region}:{self.account_id}:configurationRecorder/default",
|
|
65
|
+
actual_value="No configuration recorder found in this region",
|
|
66
|
+
remediation=(
|
|
67
|
+
f"1. Check if the AWS Config service-linked role exists: aws iam get-role --role-name AWSServiceRoleForConfig. "
|
|
68
|
+
f"2. If the role doesn't exist, create it: aws iam create-service-linked-role --aws-service-name config.amazonaws.com. "
|
|
69
|
+
f"3. Create a configuration recorder in {region}: aws configservice put-configuration-recorder --configuration-recorder name=default,roleARN=arn:aws:iam::{self.account_id}:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig --recording-group allSupported=true,includeGlobalResourceTypes=true --region {region}"
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
else:
|
|
74
|
+
# Configuration recorder exists, check if it's enabled
|
|
75
|
+
recorder_name = recorders[0].get('name', 'default')
|
|
76
|
+
recorder_role_arn = recorders[0].get('roleARN', '')
|
|
77
|
+
|
|
78
|
+
# Construct the Config Recorder ARN
|
|
79
|
+
recorder_arn = f"arn:aws:config:{region}:{self.account_id}:configurationRecorder/{recorder_name}"
|
|
80
|
+
|
|
81
|
+
# Find the status for this recorder
|
|
82
|
+
recorder_status = next((status for status in recorder_statuses if status.get('name') == recorder_name), None)
|
|
83
|
+
|
|
84
|
+
if recorder_status and recorder_status.get('recording', False):
|
|
85
|
+
# Configuration recorder exists and is recording
|
|
86
|
+
findings.append(
|
|
87
|
+
self.create_finding(
|
|
88
|
+
status="PASS",
|
|
89
|
+
region=region,
|
|
90
|
+
resource_id=recorder_arn,
|
|
91
|
+
actual_value=f"Configuration recorder '{recorder_name}' exists and is recording",
|
|
92
|
+
remediation="No remediation needed"
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
elif recorder_status:
|
|
96
|
+
# Configuration recorder exists but is not recording
|
|
97
|
+
findings.append(
|
|
98
|
+
self.create_finding(
|
|
99
|
+
status="FAIL",
|
|
100
|
+
region=region,
|
|
101
|
+
resource_id=recorder_arn,
|
|
102
|
+
actual_value=f"Configuration recorder '{recorder_name}' exists but is not recording",
|
|
103
|
+
remediation=(
|
|
104
|
+
f"Start the configuration recorder in {region} using the AWS CLI command: "
|
|
105
|
+
f"aws configservice start-configuration-recorder --configuration-recorder-name {recorder_name} --region {region}"
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
else:
|
|
110
|
+
# Configuration recorder exists but no status found
|
|
111
|
+
findings.append(
|
|
112
|
+
self.create_finding(
|
|
113
|
+
status="FAIL",
|
|
114
|
+
region=region,
|
|
115
|
+
resource_id=recorder_arn,
|
|
116
|
+
actual_value=f"Configuration recorder '{recorder_name}' exists but status could not be determined",
|
|
117
|
+
remediation=(
|
|
118
|
+
f"Check the configuration recorder status in {region} and ensure it's properly configured"
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return findings
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SRA-CONFIG-02: AWS Config Delivery Channel Configured.
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Dict, Any
|
|
5
|
+
from sraverify.services.config.base import ConfigCheck
|
|
6
|
+
from sraverify.core.logging import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SRA_CONFIG_02(ConfigCheck):
|
|
10
|
+
"""Check if AWS Config recorder is running."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
"""Initialize the check."""
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.check_id = "SRA-CONFIG-02"
|
|
16
|
+
self.check_name = "AWS Config recorder is running"
|
|
17
|
+
self.account_type = "application"
|
|
18
|
+
self.severity = "HIGH"
|
|
19
|
+
self.description = (
|
|
20
|
+
"This check verifies that configuration recorder is running. AWS Config configuration "
|
|
21
|
+
"recorder must be started and running to record resource configurations. If you set up "
|
|
22
|
+
"AWS Config by using the console or the AWS CLI, AWS Config automatically creates and "
|
|
23
|
+
"then starts the configuration recorder for you. Users with right permission have the "
|
|
24
|
+
"ability to stop configuration recorder."
|
|
25
|
+
)
|
|
26
|
+
self.check_logic = (
|
|
27
|
+
"Checks if AWS Config recorder is running by verifying the lastStatus is SUCCESS."
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
31
|
+
"""
|
|
32
|
+
Execute the check.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
List of findings
|
|
36
|
+
"""
|
|
37
|
+
findings = []
|
|
38
|
+
|
|
39
|
+
if not self.regions:
|
|
40
|
+
findings.append(
|
|
41
|
+
self.create_finding(
|
|
42
|
+
status="ERROR",
|
|
43
|
+
region="global",
|
|
44
|
+
resource_id="config:global",
|
|
45
|
+
actual_value="No regions specified for check",
|
|
46
|
+
remediation="Specify at least one region when running the check"
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
return findings
|
|
50
|
+
|
|
51
|
+
# Check each region for configuration recorder status
|
|
52
|
+
for region in self.regions:
|
|
53
|
+
# Get configuration recorders for the region
|
|
54
|
+
recorders = self.get_configuration_recorders(region)
|
|
55
|
+
|
|
56
|
+
# Get configuration recorder status for the region
|
|
57
|
+
recorder_statuses = self.get_configuration_recorder_status(region)
|
|
58
|
+
|
|
59
|
+
if not recorders:
|
|
60
|
+
# No configuration recorder found in this region
|
|
61
|
+
findings.append(
|
|
62
|
+
self.create_finding(
|
|
63
|
+
status="FAIL",
|
|
64
|
+
region=region,
|
|
65
|
+
resource_id=f"arn:aws:config:{region}:{self.account_id}:configurationRecorder/default",
|
|
66
|
+
actual_value="No configuration recorder found in this region",
|
|
67
|
+
remediation=(
|
|
68
|
+
f"First create a configuration recorder in {region} using: aws configservice put-configuration-recorder --configuration-recorder name=default,roleARN=arn:aws:iam::{self.account_id}:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig --recording-group allSupported=true,includeGlobalResourceTypes=true --region {region}. "
|
|
69
|
+
f"Then start the recorder with: aws configservice start-configuration-recorder --configuration-recorder-name default --region {region}"
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
# Configuration recorder exists, check if it's running
|
|
76
|
+
recorder_name = recorders[0].get('name', 'default')
|
|
77
|
+
|
|
78
|
+
# Find the status for this recorder
|
|
79
|
+
recorder_status = next((status for status in recorder_statuses if status.get('name') == recorder_name), None)
|
|
80
|
+
|
|
81
|
+
# Get the full ARN from the status if available
|
|
82
|
+
recorder_arn = None
|
|
83
|
+
if recorder_status and 'arn' in recorder_status:
|
|
84
|
+
recorder_arn = recorder_status.get('arn')
|
|
85
|
+
else:
|
|
86
|
+
# Construct the Config Recorder ARN if not available in status
|
|
87
|
+
recorder_arn = f"arn:aws:config:{region}:{self.account_id}:configurationRecorder/{recorder_name}"
|
|
88
|
+
|
|
89
|
+
if not recorder_status:
|
|
90
|
+
# No status found for this recorder
|
|
91
|
+
findings.append(
|
|
92
|
+
self.create_finding(
|
|
93
|
+
status="FAIL",
|
|
94
|
+
region=region,
|
|
95
|
+
resource_id=recorder_arn,
|
|
96
|
+
actual_value=f"Configuration recorder '{recorder_name}' exists but status could not be determined",
|
|
97
|
+
remediation=(
|
|
98
|
+
f"Check the configuration recorder status in {region} and ensure it's properly configured"
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
# Check if the recorder is recording
|
|
105
|
+
is_recording = recorder_status.get('recording', False)
|
|
106
|
+
last_status = recorder_status.get('lastStatus', 'UNKNOWN')
|
|
107
|
+
last_error_code = recorder_status.get('lastErrorCode', '')
|
|
108
|
+
last_error_message = recorder_status.get('lastErrorMessage', '')
|
|
109
|
+
|
|
110
|
+
if is_recording and last_status == "SUCCESS":
|
|
111
|
+
# Configuration recorder is running successfully
|
|
112
|
+
findings.append(
|
|
113
|
+
self.create_finding(
|
|
114
|
+
status="PASS",
|
|
115
|
+
region=region,
|
|
116
|
+
resource_id=recorder_arn,
|
|
117
|
+
actual_value=f"Configuration recorder '{recorder_name}' is running with lastStatus: SUCCESS",
|
|
118
|
+
remediation="No remediation needed"
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
elif is_recording:
|
|
122
|
+
# Configuration recorder is recording but not in SUCCESS state
|
|
123
|
+
error_info = f"lastStatus: {last_status}"
|
|
124
|
+
if last_error_code:
|
|
125
|
+
error_info += f", errorCode: {last_error_code}"
|
|
126
|
+
if last_error_message:
|
|
127
|
+
error_info += f", errorMessage: {last_error_message}"
|
|
128
|
+
|
|
129
|
+
findings.append(
|
|
130
|
+
self.create_finding(
|
|
131
|
+
status="FAIL",
|
|
132
|
+
region=region,
|
|
133
|
+
resource_id=recorder_arn,
|
|
134
|
+
actual_value=f"Configuration recorder '{recorder_name}' is recording but has issues: {error_info}",
|
|
135
|
+
remediation=(
|
|
136
|
+
f"Check the AWS Config logs and permissions in {region}. "
|
|
137
|
+
f"Ensure the Config service role has the necessary permissions to record resources."
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
else:
|
|
142
|
+
# Configuration recorder is not recording
|
|
143
|
+
findings.append(
|
|
144
|
+
self.create_finding(
|
|
145
|
+
status="FAIL",
|
|
146
|
+
region=region,
|
|
147
|
+
resource_id=recorder_arn,
|
|
148
|
+
actual_value=f"Configuration recorder '{recorder_name}' is not recording",
|
|
149
|
+
remediation=(
|
|
150
|
+
f"Start the configuration recorder in {region} using the AWS CLI command: "
|
|
151
|
+
f"aws configservice start-configuration-recorder --configuration-recorder-name {recorder_name} --region {region}"
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
return findings
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SRA-CONFIG-03: AWS Config Recording All Resource Types.
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Dict, Any
|
|
5
|
+
from sraverify.services.config.base import ConfigCheck
|
|
6
|
+
from sraverify.core.logging import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SRA_CONFIG_03(ConfigCheck):
|
|
10
|
+
"""Check if AWS Config latest recording event is processed successfully."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
"""Initialize the check."""
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.check_id = "SRA-CONFIG-03"
|
|
16
|
+
self.check_name = "AWS Config latest recording event is processed successfully"
|
|
17
|
+
self.account_type = "application" # This check applies to all account types
|
|
18
|
+
self.severity = "HIGH"
|
|
19
|
+
self.description = (
|
|
20
|
+
"This check verifies whether the last delivery attempt to the delivery channel was successful "
|
|
21
|
+
"to ensure you receive configuration change notifications. As AWS Config continually records "
|
|
22
|
+
"the changes that occur to your AWS resources, it sends notifications and updated configuration "
|
|
23
|
+
"states through the delivery channel."
|
|
24
|
+
)
|
|
25
|
+
self.check_logic = (
|
|
26
|
+
"Checks if the lastStatus of the delivery channel is SUCCESS."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
30
|
+
"""
|
|
31
|
+
Execute the check.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
List of findings
|
|
35
|
+
"""
|
|
36
|
+
findings = []
|
|
37
|
+
|
|
38
|
+
if not self.regions:
|
|
39
|
+
findings.append(
|
|
40
|
+
self.create_finding(
|
|
41
|
+
status="ERROR",
|
|
42
|
+
region="global",
|
|
43
|
+
resource_id="config:global",
|
|
44
|
+
actual_value="No regions specified for check",
|
|
45
|
+
remediation="Specify at least one region when running the check"
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
return findings
|
|
49
|
+
|
|
50
|
+
# Check each region for delivery channel status
|
|
51
|
+
for region in self.regions:
|
|
52
|
+
# Get delivery channels for the region
|
|
53
|
+
channels = self.get_delivery_channels(region)
|
|
54
|
+
|
|
55
|
+
# Get delivery channel status for the region
|
|
56
|
+
channel_statuses = self.get_delivery_channel_status(region)
|
|
57
|
+
|
|
58
|
+
if not channels:
|
|
59
|
+
# No delivery channel found in this region
|
|
60
|
+
findings.append(
|
|
61
|
+
self.create_finding(
|
|
62
|
+
status="FAIL",
|
|
63
|
+
region=region,
|
|
64
|
+
resource_id=f"arn:aws:config:{region}:{self.account_id}:deliveryChannel/default",
|
|
65
|
+
actual_value="No delivery channel found in this region",
|
|
66
|
+
remediation=(
|
|
67
|
+
f"Create a delivery channel in {region} using: aws configservice put-delivery-channel --delivery-channel name=default,s3BucketName=config-bucket-{self.account_id},snsTopicARN=arn:aws:sns:{region}:{self.account_id}:config-notifications --region {region}. "
|
|
68
|
+
f"Note: You must first create an S3 bucket and SNS topic with appropriate permissions for AWS Config."
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
if not channel_statuses:
|
|
75
|
+
# No delivery channel status found in this region
|
|
76
|
+
findings.append(
|
|
77
|
+
self.create_finding(
|
|
78
|
+
status="FAIL",
|
|
79
|
+
region=region,
|
|
80
|
+
resource_id=f"arn:aws:config:{region}:{self.account_id}:deliveryChannel/default",
|
|
81
|
+
actual_value="No delivery channel status found in this region",
|
|
82
|
+
remediation=(
|
|
83
|
+
f"Check the delivery channel configuration in {region} and ensure it's properly configured. "
|
|
84
|
+
f"Verify S3 bucket permissions and SNS topic permissions are correctly set up for AWS Config."
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
# Check each delivery channel status
|
|
91
|
+
for status in channel_statuses:
|
|
92
|
+
channel_name = status.get('name', 'default')
|
|
93
|
+
resource_id = f"config:deliveryChannel:{channel_name}"
|
|
94
|
+
|
|
95
|
+
# Check configHistoryDeliveryInfo - this is the most important one
|
|
96
|
+
history_info = status.get('configHistoryDeliveryInfo', {})
|
|
97
|
+
history_status = history_info.get('lastStatus', 'UNKNOWN')
|
|
98
|
+
history_attempt_time = history_info.get('lastAttemptTime', 'UNKNOWN')
|
|
99
|
+
history_success_time = history_info.get('lastSuccessfulTime', 'UNKNOWN')
|
|
100
|
+
|
|
101
|
+
# Check configSnapshotDeliveryInfo - may be UNKNOWN if no snapshot has been taken yet
|
|
102
|
+
snapshot_info = status.get('configSnapshotDeliveryInfo', {})
|
|
103
|
+
snapshot_status = snapshot_info.get('lastStatus', 'UNKNOWN')
|
|
104
|
+
|
|
105
|
+
# Check configStreamDeliveryInfo - may be NOT_APPLICABLE if streaming is not configured
|
|
106
|
+
stream_info = status.get('configStreamDeliveryInfo', {})
|
|
107
|
+
stream_status = stream_info.get('lastStatus', 'UNKNOWN')
|
|
108
|
+
|
|
109
|
+
# Determine overall status - focus on history delivery as the primary indicator
|
|
110
|
+
# Stream may be NOT_APPLICABLE and snapshot may be UNKNOWN if not configured/used
|
|
111
|
+
if history_status == "SUCCESS":
|
|
112
|
+
# History delivery is successful, which is the most important
|
|
113
|
+
findings.append(
|
|
114
|
+
self.create_finding(
|
|
115
|
+
status="PASS",
|
|
116
|
+
region=region,
|
|
117
|
+
resource_id=resource_id,
|
|
118
|
+
actual_value=(
|
|
119
|
+
f"Delivery channel '{channel_name}' is processing events successfully: "
|
|
120
|
+
f"Config History: {history_status} (Last success: {history_success_time}), "
|
|
121
|
+
f"Config Snapshot: {snapshot_status}, "
|
|
122
|
+
f"Config Stream: {stream_status}"
|
|
123
|
+
),
|
|
124
|
+
remediation="No remediation needed"
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
else:
|
|
128
|
+
# History delivery is not successful
|
|
129
|
+
findings.append(
|
|
130
|
+
self.create_finding(
|
|
131
|
+
status="FAIL",
|
|
132
|
+
region=region,
|
|
133
|
+
resource_id=resource_id,
|
|
134
|
+
actual_value=(
|
|
135
|
+
f"Delivery channel '{channel_name}' has issues processing events: "
|
|
136
|
+
f"Config History: {history_status} (Last attempt: {history_attempt_time}), "
|
|
137
|
+
f"Config Snapshot: {snapshot_status}, "
|
|
138
|
+
f"Config Stream: {stream_status}"
|
|
139
|
+
),
|
|
140
|
+
remediation=(
|
|
141
|
+
"Check the following: 1. S3 bucket permissions - ensure Config has write access. "
|
|
142
|
+
"2. SNS topic permissions - ensure Config can publish to the topic. "
|
|
143
|
+
"3. IAM role permissions - ensure Config service role has necessary permissions. "
|
|
144
|
+
"4. Check CloudWatch Logs for Config service errors."
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
return findings
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SRA-CONFIG-04: AWS Config Recording Global Resources.
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Dict, Any
|
|
5
|
+
from sraverify.services.config.base import ConfigCheck
|
|
6
|
+
from sraverify.core.logging import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SRA_CONFIG_04(ConfigCheck):
|
|
10
|
+
"""Check if AWS Config has an organization aggregator."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
"""Initialize the check."""
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.check_id = "SRA-CONFIG-04"
|
|
16
|
+
self.check_name = "AWS Config has organization aggregator"
|
|
17
|
+
self.account_type = "audit" # This check applies to audit account
|
|
18
|
+
self.severity = "HIGH"
|
|
19
|
+
self.description = (
|
|
20
|
+
"This check verifies that a AWS Config aggregator exists in the AWS Region that collects "
|
|
21
|
+
"configuration and compliance data from all member accounts of the AWS Organization. "
|
|
22
|
+
"It periodically retrieves configuration snapshots from the source accounts and stores "
|
|
23
|
+
"them in the designated S3 bucket."
|
|
24
|
+
)
|
|
25
|
+
self.check_logic = (
|
|
26
|
+
"Checks if AWS Config aggregator exists using describe-configuration-aggregators API."
|
|
27
|
+
)
|
|
28
|
+
self.resource_type = "AWS::Config::ConfigurationAggregator"
|
|
29
|
+
|
|
30
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
31
|
+
"""
|
|
32
|
+
Execute the check.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
List of findings
|
|
36
|
+
"""
|
|
37
|
+
findings = []
|
|
38
|
+
|
|
39
|
+
if not self.regions:
|
|
40
|
+
findings.append(
|
|
41
|
+
self.create_finding(
|
|
42
|
+
status="ERROR",
|
|
43
|
+
region="global",
|
|
44
|
+
resource_id="config:global",
|
|
45
|
+
checked_value="Configuration aggregator exists",
|
|
46
|
+
actual_value="No regions specified for check",
|
|
47
|
+
remediation="Specify at least one region when running the check"
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
return findings
|
|
51
|
+
|
|
52
|
+
# Check if an organization aggregator exists in any region
|
|
53
|
+
found_org_aggregator = False
|
|
54
|
+
org_aggregator_region = None
|
|
55
|
+
org_aggregator_name = None
|
|
56
|
+
org_aggregator_arn = None
|
|
57
|
+
|
|
58
|
+
for region in self.regions:
|
|
59
|
+
# Get configuration aggregators for the region using the cache
|
|
60
|
+
aggregators = self.get_configuration_aggregators(region)
|
|
61
|
+
|
|
62
|
+
# Check if any of the aggregators is an organization aggregator
|
|
63
|
+
for aggregator in aggregators:
|
|
64
|
+
if 'OrganizationAggregationSource' in aggregator:
|
|
65
|
+
found_org_aggregator = True
|
|
66
|
+
org_aggregator_region = region
|
|
67
|
+
org_aggregator_name = aggregator.get('ConfigurationAggregatorName', 'Unknown')
|
|
68
|
+
org_aggregator_arn = aggregator.get('ConfigurationAggregatorArn',
|
|
69
|
+
f"arn:aws:config:{region}:{self.account_id}:config-aggregator/{org_aggregator_name}")
|
|
70
|
+
break
|
|
71
|
+
|
|
72
|
+
if found_org_aggregator:
|
|
73
|
+
break
|
|
74
|
+
|
|
75
|
+
# Return a single finding based on whether an organization aggregator was found
|
|
76
|
+
if found_org_aggregator:
|
|
77
|
+
findings.append(
|
|
78
|
+
self.create_finding(
|
|
79
|
+
status="PASS",
|
|
80
|
+
region="global",
|
|
81
|
+
resource_id=org_aggregator_arn,
|
|
82
|
+
checked_value="Configuration aggregator exists",
|
|
83
|
+
actual_value=f"Configuration aggregator '{org_aggregator_name}' exists in region {org_aggregator_region} with Source Type \"My Organization\"",
|
|
84
|
+
remediation="No remediation needed"
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
findings.append(
|
|
89
|
+
self.create_finding(
|
|
90
|
+
status="FAIL",
|
|
91
|
+
region="global",
|
|
92
|
+
resource_id=f"arn:aws:config:global:{self.account_id}:config-aggregator/none",
|
|
93
|
+
checked_value="Configuration aggregator exists",
|
|
94
|
+
actual_value="No organization aggregator found in any region",
|
|
95
|
+
remediation=(
|
|
96
|
+
"Create an organization configuration aggregator in at least one region using: aws configservice put-configuration-aggregator "
|
|
97
|
+
"--configuration-aggregator-name organization-aggregator --organization-aggregation-source "
|
|
98
|
+
f"\"EnableAllRegions=true,RoleArn=arn:aws:iam::{self.account_id}:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfigServiceRole\" "
|
|
99
|
+
"--region <region>"
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return findings
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SRA-CONFIG-05: AWS Config Recorder Status.
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Dict, Any
|
|
5
|
+
from sraverify.services.config.base import ConfigCheck
|
|
6
|
+
from sraverify.core.logging import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SRA_CONFIG_05(ConfigCheck):
|
|
10
|
+
"""Check if AWS Config organization aggregator includes all regions."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
"""Initialize the check."""
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.check_id = "SRA-CONFIG-05"
|
|
16
|
+
self.check_name = "AWS Config organization aggregator includes all regions"
|
|
17
|
+
self.account_type = "audit" # This check applies to audit account
|
|
18
|
+
self.severity = "MEDIUM"
|
|
19
|
+
self.description = (
|
|
20
|
+
"This check verifies that the AWS Config organization aggregator is configured to aggregate "
|
|
21
|
+
"config data from all existing and future AWS Regions. This provides you visibility into "
|
|
22
|
+
"activities across all regions even if your business does not operate in the region."
|
|
23
|
+
)
|
|
24
|
+
self.check_logic = (
|
|
25
|
+
"Checks if AWS Config organization aggregator has AllAwsRegions set to true."
|
|
26
|
+
)
|
|
27
|
+
self.resource_type = "AWS::Config::ConfigurationAggregator"
|
|
28
|
+
|
|
29
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
30
|
+
"""
|
|
31
|
+
Execute the check.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
List of findings
|
|
35
|
+
"""
|
|
36
|
+
findings = []
|
|
37
|
+
|
|
38
|
+
if not self.regions:
|
|
39
|
+
findings.append(
|
|
40
|
+
self.create_finding(
|
|
41
|
+
status="ERROR",
|
|
42
|
+
region="global",
|
|
43
|
+
resource_id="config:global",
|
|
44
|
+
checked_value="AllAwsRegions: true",
|
|
45
|
+
actual_value="No regions specified for check",
|
|
46
|
+
remediation="Specify at least one region when running the check"
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
return findings
|
|
50
|
+
|
|
51
|
+
# Check if an organization aggregator with AllAwsRegions=true exists in any region
|
|
52
|
+
found_all_regions_aggregator = False
|
|
53
|
+
all_regions_aggregator_region = None
|
|
54
|
+
all_regions_aggregator_name = None
|
|
55
|
+
all_regions_aggregator_arn = None
|
|
56
|
+
|
|
57
|
+
for region in self.regions:
|
|
58
|
+
# Get configuration aggregators for the region from cache
|
|
59
|
+
aggregators = self.get_configuration_aggregators(region)
|
|
60
|
+
|
|
61
|
+
# Check if any of the aggregators is an organization aggregator with all regions enabled
|
|
62
|
+
for aggregator in aggregators:
|
|
63
|
+
if ('OrganizationAggregationSource' in aggregator and
|
|
64
|
+
aggregator.get('OrganizationAggregationSource', {}).get('AllAwsRegions', False)):
|
|
65
|
+
found_all_regions_aggregator = True
|
|
66
|
+
all_regions_aggregator_region = region
|
|
67
|
+
all_regions_aggregator_name = aggregator.get('ConfigurationAggregatorName', 'Unknown')
|
|
68
|
+
all_regions_aggregator_arn = aggregator.get('ConfigurationAggregatorArn',
|
|
69
|
+
f"arn:aws:config:{region}:{self.account_id}:config-aggregator/{all_regions_aggregator_name}")
|
|
70
|
+
break
|
|
71
|
+
|
|
72
|
+
if found_all_regions_aggregator:
|
|
73
|
+
break
|
|
74
|
+
|
|
75
|
+
# Return a single finding based on whether an organization aggregator with AllAwsRegions=true was found
|
|
76
|
+
if found_all_regions_aggregator:
|
|
77
|
+
findings.append(
|
|
78
|
+
self.create_finding(
|
|
79
|
+
status="PASS",
|
|
80
|
+
region="global",
|
|
81
|
+
resource_id=all_regions_aggregator_arn,
|
|
82
|
+
checked_value="AllAwsRegions: true",
|
|
83
|
+
actual_value=f"Configuration aggregator '{all_regions_aggregator_name}' is configured to aggregate all regions, located in region {all_regions_aggregator_region} with Region selection \"All current and future AWS regions\"",
|
|
84
|
+
remediation="No remediation needed"
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
findings.append(
|
|
89
|
+
self.create_finding(
|
|
90
|
+
status="FAIL",
|
|
91
|
+
region="global",
|
|
92
|
+
resource_id=f"arn:aws:config:global:{self.account_id}:config-aggregator/none",
|
|
93
|
+
checked_value="AllAwsRegions: true",
|
|
94
|
+
actual_value="No organization aggregator with AllAwsRegions=true found in any region",
|
|
95
|
+
remediation=(
|
|
96
|
+
"Create an organization configuration aggregator with AllAwsRegions=true in at least one region using: aws configservice put-configuration-aggregator "
|
|
97
|
+
"--configuration-aggregator-name organization-aggregator --organization-aggregation-source "
|
|
98
|
+
f"\"EnableAllRegions=true,RoleArn=arn:aws:iam::{self.account_id}:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfigServiceRole\" "
|
|
99
|
+
"--region <region>"
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return findings
|