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,194 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SRA-CONFIG-06: AWS Config Conformance Packs.
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Dict, Any
|
|
5
|
+
import json
|
|
6
|
+
from sraverify.services.config.base import ConfigCheck
|
|
7
|
+
from sraverify.core.logging import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SRA_CONFIG_06(ConfigCheck):
|
|
11
|
+
"""Check if AWS Config delivery channel S3 bucket is centralized in Log Archive account."""
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
"""Initialize the check."""
|
|
15
|
+
super().__init__()
|
|
16
|
+
self.check_id = "SRA-CONFIG-06"
|
|
17
|
+
self.check_name = "AWS Config delivery channel S3 bucket is centralized in Log Archive account"
|
|
18
|
+
self.account_type = "application" # This check applies to management account
|
|
19
|
+
self.severity = "MEDIUM"
|
|
20
|
+
self.description = (
|
|
21
|
+
"This check verifies that the AWS Config delivery channel S3 bucket is centralized in "
|
|
22
|
+
"Log Archive account. Security Tooling provides central visibility and monitoring of AWS "
|
|
23
|
+
"Organization wide resource configuration."
|
|
24
|
+
)
|
|
25
|
+
self.check_logic = (
|
|
26
|
+
"Checks if AWS Config delivery channel S3 bucket is owned by the Log Archive account."
|
|
27
|
+
)
|
|
28
|
+
self.resource_type = "AWS::Config::DeliveryChannel"
|
|
29
|
+
# Initialize log archive account attribute
|
|
30
|
+
self._log_archive_accounts = None
|
|
31
|
+
|
|
32
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
33
|
+
"""
|
|
34
|
+
Execute the check.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
List of findings
|
|
38
|
+
"""
|
|
39
|
+
findings = []
|
|
40
|
+
|
|
41
|
+
# Check if Log Archive account ID is provided
|
|
42
|
+
if not hasattr(self, '_log_archive_accounts') or not self._log_archive_accounts:
|
|
43
|
+
findings.append(
|
|
44
|
+
self.create_finding(
|
|
45
|
+
status="ERROR",
|
|
46
|
+
region="global",
|
|
47
|
+
resource_id="config:global",
|
|
48
|
+
checked_value="S3 bucket owned by Log Archive account",
|
|
49
|
+
actual_value="Log Archive Account ID not provided",
|
|
50
|
+
remediation="Provide the Log Archive account ID using the --log-archive-account parameter"
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
return findings
|
|
54
|
+
|
|
55
|
+
# Use the first log archive account if multiple are provided
|
|
56
|
+
log_archive_account = self._log_archive_accounts[0]
|
|
57
|
+
logger.debug(f"Using Log Archive account: {log_archive_account}")
|
|
58
|
+
|
|
59
|
+
if not self.regions:
|
|
60
|
+
findings.append(
|
|
61
|
+
self.create_finding(
|
|
62
|
+
status="ERROR",
|
|
63
|
+
region="global",
|
|
64
|
+
resource_id="config:global",
|
|
65
|
+
checked_value=f"S3 bucket owned by Log Archive account {log_archive_account}",
|
|
66
|
+
actual_value="No regions specified for check",
|
|
67
|
+
remediation="Specify at least one region when running the check"
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
return findings
|
|
71
|
+
|
|
72
|
+
# Check each region for delivery channels
|
|
73
|
+
for region in self.regions:
|
|
74
|
+
# Get delivery channels for the region
|
|
75
|
+
channels = self.get_delivery_channels(region)
|
|
76
|
+
|
|
77
|
+
if not channels:
|
|
78
|
+
# No delivery channel found in this region
|
|
79
|
+
findings.append(
|
|
80
|
+
self.create_finding(
|
|
81
|
+
status="FAIL",
|
|
82
|
+
region=region,
|
|
83
|
+
resource_id=f"arn:aws:config:{region}:{self.account_id}:deliveryChannel/default",
|
|
84
|
+
checked_value=f"S3 bucket owned by Log Archive account {log_archive_account}",
|
|
85
|
+
actual_value="No delivery channel found in this region",
|
|
86
|
+
remediation=(
|
|
87
|
+
f"Create a delivery channel in {region} using: aws configservice put-delivery-channel "
|
|
88
|
+
f"--delivery-channel name=default,s3BucketName=aws-controltower-logs-{log_archive_account}-{region} "
|
|
89
|
+
f"--region {region}"
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
# Check each delivery channel
|
|
96
|
+
for channel in channels:
|
|
97
|
+
channel_name = channel.get('name', 'default')
|
|
98
|
+
bucket_name = channel.get('s3BucketName', '')
|
|
99
|
+
|
|
100
|
+
if not bucket_name:
|
|
101
|
+
# No S3 bucket configured
|
|
102
|
+
findings.append(
|
|
103
|
+
self.create_finding(
|
|
104
|
+
status="FAIL",
|
|
105
|
+
region=region,
|
|
106
|
+
resource_id=f"arn:aws:config:{region}:{self.account_id}:deliveryChannel/{channel_name}",
|
|
107
|
+
checked_value=f"S3 bucket owned by Log Archive account {log_archive_account}",
|
|
108
|
+
actual_value="No S3 bucket configured for delivery channel",
|
|
109
|
+
remediation=(
|
|
110
|
+
f"Update the delivery channel in {region} to use an S3 bucket in the Log Archive account: "
|
|
111
|
+
f"aws configservice put-delivery-channel --delivery-channel name={channel_name},"
|
|
112
|
+
f"s3BucketName=aws-controltower-logs-{log_archive_account}-{region} --region {region}"
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
# Check if the bucket name contains the Log Archive account ID
|
|
119
|
+
# This is a common pattern for AWS Control Tower and other AWS managed solutions
|
|
120
|
+
bucket_owner_found = False
|
|
121
|
+
|
|
122
|
+
# Check for common bucket naming patterns
|
|
123
|
+
patterns_to_check = [
|
|
124
|
+
f"aws-controltower-logs-{log_archive_account}", # AWS Control Tower pattern
|
|
125
|
+
f"config-bucket-{log_archive_account}", # Common custom pattern
|
|
126
|
+
f"aws-config-{log_archive_account}", # Another common pattern
|
|
127
|
+
f"config-{log_archive_account}", # Simple pattern
|
|
128
|
+
f"-{log_archive_account}-" # Generic pattern (account ID in the middle)
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
for pattern in patterns_to_check:
|
|
132
|
+
if pattern in bucket_name:
|
|
133
|
+
bucket_owner_found = True
|
|
134
|
+
break
|
|
135
|
+
|
|
136
|
+
if bucket_owner_found:
|
|
137
|
+
# Bucket name contains the Log Archive account ID, assume it's owned by the Log Archive account
|
|
138
|
+
findings.append(
|
|
139
|
+
self.create_finding(
|
|
140
|
+
status="PASS",
|
|
141
|
+
region=region,
|
|
142
|
+
resource_id=f"arn:aws:config:{region}:{self.account_id}:deliveryChannel/{channel_name}",
|
|
143
|
+
checked_value=f"S3 bucket owned by Log Archive account {log_archive_account}",
|
|
144
|
+
actual_value=f"Delivery channel S3 bucket '{bucket_name}' is owned by the Log Archive account {log_archive_account}",
|
|
145
|
+
remediation="No remediation needed"
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
else:
|
|
149
|
+
# Try to check if the bucket is in another region but still owned by Log Archive
|
|
150
|
+
# For example, a bucket in us-east-1 might be used by a delivery channel in us-east-2
|
|
151
|
+
cross_region_match = False
|
|
152
|
+
|
|
153
|
+
for other_region in self.regions:
|
|
154
|
+
if other_region != region and f"-{other_region}" in bucket_name:
|
|
155
|
+
# The bucket name contains another region, check if it also contains the Log Archive account ID
|
|
156
|
+
for pattern in patterns_to_check:
|
|
157
|
+
if pattern in bucket_name:
|
|
158
|
+
cross_region_match = True
|
|
159
|
+
break
|
|
160
|
+
|
|
161
|
+
if cross_region_match:
|
|
162
|
+
break
|
|
163
|
+
|
|
164
|
+
if cross_region_match:
|
|
165
|
+
# Bucket is in another region but still owned by Log Archive
|
|
166
|
+
findings.append(
|
|
167
|
+
self.create_finding(
|
|
168
|
+
status="PASS",
|
|
169
|
+
region=region,
|
|
170
|
+
resource_id=f"arn:aws:config:{region}:{self.account_id}:deliveryChannel/{channel_name}",
|
|
171
|
+
checked_value=f"S3 bucket owned by Log Archive account {log_archive_account}",
|
|
172
|
+
actual_value=f"Delivery channel S3 bucket '{bucket_name}' is owned by the Log Archive account {log_archive_account} (cross-region bucket)",
|
|
173
|
+
remediation="No remediation needed"
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
else:
|
|
177
|
+
# Bucket name doesn't contain the Log Archive account ID
|
|
178
|
+
findings.append(
|
|
179
|
+
self.create_finding(
|
|
180
|
+
status="FAIL",
|
|
181
|
+
region=region,
|
|
182
|
+
resource_id=f"arn:aws:config:{region}:{self.account_id}:deliveryChannel/{channel_name}",
|
|
183
|
+
checked_value=f"S3 bucket owned by Log Archive account {log_archive_account}",
|
|
184
|
+
actual_value=f"Delivery channel S3 bucket '{bucket_name}' does not appear to be owned by the Log Archive account {log_archive_account} based on the bucket name",
|
|
185
|
+
remediation=(
|
|
186
|
+
f"1. Create an S3 bucket in the Log Archive account {log_archive_account}. "
|
|
187
|
+
f"2. Update the delivery channel in {region} to use the new S3 bucket: "
|
|
188
|
+
f"aws configservice put-delivery-channel --delivery-channel name={channel_name},"
|
|
189
|
+
f"s3BucketName=aws-controltower-logs-{log_archive_account}-{region} --region {region}"
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
return findings
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SRA-CONFIG-07: AWS Config Aggregator.
|
|
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_07(ConfigCheck):
|
|
10
|
+
"""Check if Config administration for the AWS Organization has a delegated administrator."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
"""Initialize the check."""
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.check_id = "SRA-CONFIG-07"
|
|
16
|
+
self.check_name = "Config administration for the AWS Organization has a delegated administrator"
|
|
17
|
+
self.account_type = "management" # This check applies to management account
|
|
18
|
+
self.severity = "MEDIUM"
|
|
19
|
+
self.description = (
|
|
20
|
+
"This check verifies whether Config service administration for your AWS Organization "
|
|
21
|
+
"is delegated out of the AWS Organization management account."
|
|
22
|
+
)
|
|
23
|
+
self.check_logic = (
|
|
24
|
+
"Checks if a delegated administrator exists for the Config service using the "
|
|
25
|
+
"list-delegated-administrators API with service principals config.amazonaws.com "
|
|
26
|
+
"and config-multiaccountsetup.amazonaws.com."
|
|
27
|
+
)
|
|
28
|
+
self.resource_type = "AWS::Organizations::Account"
|
|
29
|
+
# Initialize parameters as an empty dict
|
|
30
|
+
self.params = {}
|
|
31
|
+
|
|
32
|
+
def initialize(self, session, regions=None, **kwargs):
|
|
33
|
+
"""
|
|
34
|
+
Initialize check with AWS session, regions, and parameters.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
session: AWS session to use for the check
|
|
38
|
+
regions: List of AWS regions to check
|
|
39
|
+
**kwargs: Additional parameters for the check
|
|
40
|
+
"""
|
|
41
|
+
super().initialize(session, regions)
|
|
42
|
+
# Store parameters
|
|
43
|
+
self.params = kwargs
|
|
44
|
+
logger.debug(f"Initialized {self.check_id} with parameters: {self.params}")
|
|
45
|
+
|
|
46
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
47
|
+
"""
|
|
48
|
+
Execute the check.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
List of findings
|
|
52
|
+
"""
|
|
53
|
+
findings = []
|
|
54
|
+
|
|
55
|
+
# Get delegated administrators for both Config service principals
|
|
56
|
+
delegated_admins = self.get_delegated_administrators()
|
|
57
|
+
|
|
58
|
+
if not delegated_admins:
|
|
59
|
+
# No delegated administrator found for either service principal
|
|
60
|
+
findings.append(
|
|
61
|
+
self.create_finding(
|
|
62
|
+
status="FAIL",
|
|
63
|
+
region="global",
|
|
64
|
+
resource_id="delegated-admin/none",
|
|
65
|
+
checked_value="Delegated administrator exists for Config service",
|
|
66
|
+
actual_value="No delegated administrator found for Config service",
|
|
67
|
+
remediation=(
|
|
68
|
+
"Register a delegated administrator for Config using both service principals:\n"
|
|
69
|
+
"1. aws organizations register-delegated-administrator "
|
|
70
|
+
"--service-principal config.amazonaws.com "
|
|
71
|
+
"--account-id <AUDIT_ACCOUNT_ID>\n"
|
|
72
|
+
"2. aws organizations register-delegated-administrator "
|
|
73
|
+
"--service-principal config-multiaccountsetup.amazonaws.com "
|
|
74
|
+
"--account-id <AUDIT_ACCOUNT_ID>"
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
return findings
|
|
79
|
+
|
|
80
|
+
# Group delegated admins by service principal to check coverage
|
|
81
|
+
service_principals_covered = set()
|
|
82
|
+
admin_accounts = {}
|
|
83
|
+
|
|
84
|
+
for admin in delegated_admins:
|
|
85
|
+
admin_id = admin.get('Id', 'Unknown')
|
|
86
|
+
admin_name = admin.get('Name', 'Unknown')
|
|
87
|
+
|
|
88
|
+
# In a real implementation, we would know which service principal this admin is for
|
|
89
|
+
# For now, we'll just track unique admin accounts
|
|
90
|
+
if admin_id not in admin_accounts:
|
|
91
|
+
admin_accounts[admin_id] = {
|
|
92
|
+
'name': admin_name,
|
|
93
|
+
'count': 1
|
|
94
|
+
}
|
|
95
|
+
else:
|
|
96
|
+
admin_accounts[admin_id]['count'] += 1
|
|
97
|
+
|
|
98
|
+
# Check if we have full coverage of service principals
|
|
99
|
+
if len(delegated_admins) >= 2:
|
|
100
|
+
# We have at least one delegated admin for each service principal
|
|
101
|
+
for admin_id, info in admin_accounts.items():
|
|
102
|
+
admin_name = info['name']
|
|
103
|
+
service_count = info['count']
|
|
104
|
+
|
|
105
|
+
if service_count == 2:
|
|
106
|
+
# This account is delegated for both service principals
|
|
107
|
+
findings.append(
|
|
108
|
+
self.create_finding(
|
|
109
|
+
status="PASS",
|
|
110
|
+
region="global",
|
|
111
|
+
resource_id=f"delegated-admin/{admin_id}",
|
|
112
|
+
checked_value="Delegated administrator exists for Config service",
|
|
113
|
+
actual_value=f"Config service has delegated administrator set to account {admin_id} ({admin_name}) for both service principals",
|
|
114
|
+
remediation="No remediation needed"
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
else:
|
|
118
|
+
# This account is delegated for only one service principal
|
|
119
|
+
findings.append(
|
|
120
|
+
self.create_finding(
|
|
121
|
+
status="WARN",
|
|
122
|
+
region="global",
|
|
123
|
+
resource_id=f"delegated-admin/{admin_id}",
|
|
124
|
+
checked_value="Delegated administrator exists for all Config service principals",
|
|
125
|
+
actual_value=f"Config service has delegated administrator set to account {admin_id} ({admin_name}) but not for all required service principals",
|
|
126
|
+
remediation=(
|
|
127
|
+
f"Ensure the same account is registered as a delegated administrator for both Config service principals:\n"
|
|
128
|
+
f"1. aws organizations register-delegated-administrator "
|
|
129
|
+
f"--service-principal config.amazonaws.com "
|
|
130
|
+
f"--account-id {admin_id}\n"
|
|
131
|
+
f"2. aws organizations register-delegated-administrator "
|
|
132
|
+
f"--service-principal config-multiaccountsetup.amazonaws.com "
|
|
133
|
+
f"--account-id {admin_id}"
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
else:
|
|
138
|
+
# We don't have full coverage of service principals
|
|
139
|
+
admin_list = []
|
|
140
|
+
for admin_id, info in admin_accounts.items():
|
|
141
|
+
admin_list.append(f"{admin_id} ({info['name']})")
|
|
142
|
+
|
|
143
|
+
findings.append(
|
|
144
|
+
self.create_finding(
|
|
145
|
+
status="WARN",
|
|
146
|
+
region="global",
|
|
147
|
+
resource_id=f"delegated-admin/{','.join(admin_accounts.keys())}",
|
|
148
|
+
checked_value="Delegated administrator exists for all Config service principals",
|
|
149
|
+
actual_value=f"Config service has delegated administrators ({', '.join(admin_list)}) but not for all required service principals",
|
|
150
|
+
remediation=(
|
|
151
|
+
"Ensure a delegated administrator is registered for both Config service principals:\n"
|
|
152
|
+
"1. aws organizations register-delegated-administrator "
|
|
153
|
+
"--service-principal config.amazonaws.com "
|
|
154
|
+
"--account-id <AUDIT_ACCOUNT_ID>\n"
|
|
155
|
+
"2. aws organizations register-delegated-administrator "
|
|
156
|
+
"--service-principal config-multiaccountsetup.amazonaws.com "
|
|
157
|
+
"--account-id <AUDIT_ACCOUNT_ID>"
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
return findings
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SRA-CONFIG-08: AWS Config Aggregator Authorization.
|
|
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_08(ConfigCheck):
|
|
10
|
+
"""Check if Config delegated admin account is the Security Tooling (Audit) account."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
"""Initialize the check."""
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.check_id = "SRA-CONFIG-08"
|
|
16
|
+
self.check_name = "Config delegated admin account is the Security Tooling (Audit) account"
|
|
17
|
+
self.account_type = "management" # This check applies to management account
|
|
18
|
+
self.severity = "MEDIUM"
|
|
19
|
+
self.description = (
|
|
20
|
+
"This check verifies whether Config delegated admin account is the audit account of your "
|
|
21
|
+
"AWS organization. The audit account is dedicated to operating security services, monitoring "
|
|
22
|
+
"AWS accounts, and automating security alerting and response."
|
|
23
|
+
)
|
|
24
|
+
self.check_logic = (
|
|
25
|
+
"Compares the delegated admin account ID with the provided audit account ID."
|
|
26
|
+
)
|
|
27
|
+
self.resource_type = "AWS::Organizations::Account"
|
|
28
|
+
# Initialize audit account attribute
|
|
29
|
+
self._audit_accounts = []
|
|
30
|
+
|
|
31
|
+
def initialize(self, session, regions=None, **kwargs):
|
|
32
|
+
"""
|
|
33
|
+
Initialize check with AWS session, regions, and parameters.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
session: AWS session to use for the check
|
|
37
|
+
regions: List of AWS regions to check
|
|
38
|
+
**kwargs: Additional parameters for the check
|
|
39
|
+
"""
|
|
40
|
+
super().initialize(session, regions)
|
|
41
|
+
|
|
42
|
+
# Extract audit-account from kwargs
|
|
43
|
+
if 'audit-account' in kwargs:
|
|
44
|
+
# Handle both single value and list
|
|
45
|
+
audit_account = kwargs['audit-account']
|
|
46
|
+
if isinstance(audit_account, list):
|
|
47
|
+
self._audit_accounts = audit_account
|
|
48
|
+
else:
|
|
49
|
+
self._audit_accounts = [audit_account]
|
|
50
|
+
logger.debug(f"Audit account IDs set to: {self._audit_accounts}")
|
|
51
|
+
else:
|
|
52
|
+
logger.debug("No Audit account ID provided in parameters")
|
|
53
|
+
|
|
54
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
55
|
+
"""
|
|
56
|
+
Execute the check.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
List of findings
|
|
60
|
+
"""
|
|
61
|
+
findings = []
|
|
62
|
+
|
|
63
|
+
# Check if Audit account ID is provided
|
|
64
|
+
if not self._audit_accounts:
|
|
65
|
+
findings.append(
|
|
66
|
+
self.create_finding(
|
|
67
|
+
status="ERROR",
|
|
68
|
+
region="global",
|
|
69
|
+
resource_id="delegated-admin/none",
|
|
70
|
+
checked_value="Delegated administrator is audit account",
|
|
71
|
+
actual_value="No audit account ID provided",
|
|
72
|
+
remediation="Provide the audit account ID using the --audit-account parameter"
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
return findings
|
|
76
|
+
|
|
77
|
+
# Get delegated administrators for both Config service principals
|
|
78
|
+
delegated_admins = self.get_delegated_administrators()
|
|
79
|
+
|
|
80
|
+
if not delegated_admins:
|
|
81
|
+
# No delegated administrator found for either service principal
|
|
82
|
+
findings.append(
|
|
83
|
+
self.create_finding(
|
|
84
|
+
status="FAIL",
|
|
85
|
+
region="global",
|
|
86
|
+
resource_id=f"delegated-admin/none",
|
|
87
|
+
checked_value=f"Delegated administrator is audit account {', '.join(self._audit_accounts)}",
|
|
88
|
+
actual_value=f"No delegated administrator found for Config service",
|
|
89
|
+
remediation=(
|
|
90
|
+
f"Register the audit account as a delegated administrator for Config using both service principals:\n"
|
|
91
|
+
f"1. aws organizations register-delegated-administrator "
|
|
92
|
+
f"--service-principal config.amazonaws.com "
|
|
93
|
+
f"--account-id {self._audit_accounts[0]}\n"
|
|
94
|
+
f"2. aws organizations register-delegated-administrator "
|
|
95
|
+
f"--service-principal config-multiaccountsetup.amazonaws.com "
|
|
96
|
+
f"--account-id {self._audit_accounts[0]}"
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
return findings
|
|
101
|
+
|
|
102
|
+
# Group delegated admins by account ID to check if the same account is used for both service principals
|
|
103
|
+
admin_accounts = {}
|
|
104
|
+
for admin in delegated_admins:
|
|
105
|
+
admin_id = admin.get('Id', 'Unknown')
|
|
106
|
+
admin_name = admin.get('Name', 'Unknown')
|
|
107
|
+
|
|
108
|
+
if admin_id not in admin_accounts:
|
|
109
|
+
admin_accounts[admin_id] = {
|
|
110
|
+
'name': admin_name,
|
|
111
|
+
'count': 1
|
|
112
|
+
}
|
|
113
|
+
else:
|
|
114
|
+
admin_accounts[admin_id]['count'] += 1
|
|
115
|
+
|
|
116
|
+
# Check if any of the audit accounts is a delegated administrator
|
|
117
|
+
audit_account_found = False
|
|
118
|
+
for audit_account_id in self._audit_accounts:
|
|
119
|
+
if audit_account_id in admin_accounts:
|
|
120
|
+
admin_info = admin_accounts[audit_account_id]
|
|
121
|
+
admin_name = admin_info['name']
|
|
122
|
+
service_count = admin_info['count']
|
|
123
|
+
|
|
124
|
+
# Check if the audit account is delegated for both service principals
|
|
125
|
+
if service_count == 2:
|
|
126
|
+
audit_account_found = True
|
|
127
|
+
findings.append(
|
|
128
|
+
self.create_finding(
|
|
129
|
+
status="PASS",
|
|
130
|
+
region="global",
|
|
131
|
+
resource_id=f"delegated-admin/{audit_account_id}",
|
|
132
|
+
checked_value=f"Delegated administrator is audit account {audit_account_id}",
|
|
133
|
+
actual_value=f"Config delegated administrator is the audit account {audit_account_id} ({admin_name}) for both service principals",
|
|
134
|
+
remediation="No remediation needed"
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
else:
|
|
138
|
+
audit_account_found = True
|
|
139
|
+
findings.append(
|
|
140
|
+
self.create_finding(
|
|
141
|
+
status="WARN",
|
|
142
|
+
region="global",
|
|
143
|
+
resource_id=f"delegated-admin/{audit_account_id}",
|
|
144
|
+
checked_value=f"Delegated administrator is audit account {audit_account_id} for both service principals",
|
|
145
|
+
actual_value=f"Config delegated administrator is the audit account {audit_account_id} ({admin_name}) but not for all required service principals",
|
|
146
|
+
remediation=(
|
|
147
|
+
f"Ensure the audit account is registered as a delegated administrator for both Config service principals:\n"
|
|
148
|
+
f"1. aws organizations register-delegated-administrator "
|
|
149
|
+
f"--service-principal config.amazonaws.com "
|
|
150
|
+
f"--account-id {audit_account_id}\n"
|
|
151
|
+
f"2. aws organizations register-delegated-administrator "
|
|
152
|
+
f"--service-principal config-multiaccountsetup.amazonaws.com "
|
|
153
|
+
f"--account-id {audit_account_id}"
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# If no audit account is a delegated administrator
|
|
159
|
+
if not audit_account_found:
|
|
160
|
+
# List all accounts that are delegated administrators
|
|
161
|
+
other_admins = []
|
|
162
|
+
for admin_id, info in admin_accounts.items():
|
|
163
|
+
other_admins.append(f"{admin_id} ({info['name']})")
|
|
164
|
+
|
|
165
|
+
findings.append(
|
|
166
|
+
self.create_finding(
|
|
167
|
+
status="FAIL",
|
|
168
|
+
region="global",
|
|
169
|
+
resource_id=f"delegated-admin/none",
|
|
170
|
+
checked_value=f"Delegated administrator is audit account {', '.join(self._audit_accounts)}",
|
|
171
|
+
actual_value=f"Config delegated administrator(s) {', '.join(other_admins)} are not the audit account {', '.join(self._audit_accounts)}",
|
|
172
|
+
remediation=(
|
|
173
|
+
f"1. Deregister the current delegated administrator(s).\n"
|
|
174
|
+
f"2. Register the audit account as a delegated administrator for both Config service principals:\n"
|
|
175
|
+
f" aws organizations register-delegated-administrator "
|
|
176
|
+
f"--service-principal config.amazonaws.com "
|
|
177
|
+
f"--account-id {self._audit_accounts[0]}\n"
|
|
178
|
+
f" aws organizations register-delegated-administrator "
|
|
179
|
+
f"--service-principal config-multiaccountsetup.amazonaws.com "
|
|
180
|
+
f"--account-id {self._audit_accounts[0]}"
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
return findings
|