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,72 @@
|
|
|
1
|
+
"""Check if Security Lake has a delegated administrator."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Dict, Any
|
|
4
|
+
from sraverify.services.securitylake.base import SecurityLakeCheck
|
|
5
|
+
from sraverify.core.logging import logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SRA_SECURITYLAKE_14(SecurityLakeCheck):
|
|
9
|
+
"""Check if Security Lake has a delegated administrator."""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
"""Initialize check."""
|
|
13
|
+
super().__init__()
|
|
14
|
+
self.account_type = "management" # Delegated admin check runs from management account
|
|
15
|
+
self.check_id = "SRA-SECURITYLAKE-14"
|
|
16
|
+
self.check_name = "Security Lake has delegated administrator"
|
|
17
|
+
self.severity = "CRITICAL"
|
|
18
|
+
self.description = (
|
|
19
|
+
"This check verifies whether Security Lake service administration for "
|
|
20
|
+
"the AWS Organization is delegated out from AWS Organization management "
|
|
21
|
+
"account to a member account."
|
|
22
|
+
)
|
|
23
|
+
self.check_logic = (
|
|
24
|
+
"Checks if Security Lake has a delegated administrator configured. "
|
|
25
|
+
"The check passes if at least one delegated administrator is found for the Security Lake service. "
|
|
26
|
+
"The check fails if no delegated administrator is configured."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
30
|
+
"""Run check."""
|
|
31
|
+
findings = []
|
|
32
|
+
|
|
33
|
+
# This is a global check, so we only need to run it once
|
|
34
|
+
# Use the first region just to make the API call
|
|
35
|
+
region = self.regions[0] if self.regions else "us-east-1"
|
|
36
|
+
resource_id = f"arn:aws:organizations::global:delegatedadministrator/securitylake"
|
|
37
|
+
|
|
38
|
+
# Get delegated administrators using the base class method
|
|
39
|
+
delegated_admin = self.get_delegated_administrators(region)
|
|
40
|
+
|
|
41
|
+
if not delegated_admin:
|
|
42
|
+
findings.append(
|
|
43
|
+
self.create_finding(
|
|
44
|
+
status="FAIL",
|
|
45
|
+
region="global",
|
|
46
|
+
resource_id=resource_id,
|
|
47
|
+
checked_value="Delegated administrator configured",
|
|
48
|
+
actual_value="No delegated administrator configured for Security Lake",
|
|
49
|
+
remediation=(
|
|
50
|
+
"Configure a delegated administrator for Security Lake. In the AWS Organizations console, "
|
|
51
|
+
"navigate to Services > Security Lake and delegate administration to a member account. "
|
|
52
|
+
"Alternatively, use the AWS CLI command: "
|
|
53
|
+
"aws organizations register-delegated-administrator --service-principal securitylake.amazonaws.com "
|
|
54
|
+
"--account-id ACCOUNT_ID"
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
else:
|
|
59
|
+
admin_info = delegated_admin[0] if delegated_admin else {} # Get first admin if exists
|
|
60
|
+
admin_id = admin_info.get('Id', 'Unknown')
|
|
61
|
+
findings.append(
|
|
62
|
+
self.create_finding(
|
|
63
|
+
status="PASS",
|
|
64
|
+
region="global",
|
|
65
|
+
resource_id=resource_id,
|
|
66
|
+
checked_value="Delegated administrator configured",
|
|
67
|
+
actual_value=f"Delegated administrator {admin_id} is configured for Security Lake",
|
|
68
|
+
remediation="No remediation needed"
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
return findings
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Check if Security Lake delegated admin is Log Archive account."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Dict, Any
|
|
4
|
+
from sraverify.services.securitylake.base import SecurityLakeCheck
|
|
5
|
+
from sraverify.core.logging import logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SRA_SECURITYLAKE_15(SecurityLakeCheck):
|
|
9
|
+
"""Check if Security Lake delegated admin is Log Archive account."""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
"""Initialize check."""
|
|
13
|
+
super().__init__()
|
|
14
|
+
self.account_type = "management" # Delegated admin check runs from management account
|
|
15
|
+
self.check_id = "SRA-SECURITYLAKE-15"
|
|
16
|
+
self.check_name = "Security Lake delegated admin is log archive account"
|
|
17
|
+
self.severity = "CRITICAL"
|
|
18
|
+
self.description = (
|
|
19
|
+
"This check verifies whether Security Lake delegated admin account "
|
|
20
|
+
"is the Log Archive account of your AWS organization. The Log Archive "
|
|
21
|
+
"account is dedicated to ingesting and archiving all security-related "
|
|
22
|
+
"logs and backups."
|
|
23
|
+
)
|
|
24
|
+
self.check_logic = (
|
|
25
|
+
"Checks if the Security Lake delegated administrator is the Log Archive account. "
|
|
26
|
+
"The check passes if the delegated administrator account ID matches the Log Archive account ID. "
|
|
27
|
+
"The check fails if there is no delegated administrator or if the delegated administrator "
|
|
28
|
+
"is not the Log Archive account."
|
|
29
|
+
)
|
|
30
|
+
# Initialize log archive account attribute
|
|
31
|
+
self._log_archive_accounts = None
|
|
32
|
+
|
|
33
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
34
|
+
"""
|
|
35
|
+
Execute the check.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
List of findings
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# This is a global check, so we only need to run it once
|
|
42
|
+
# Use the first region just to make the API call
|
|
43
|
+
region = self.regions[0] if self.regions else "us-east-1"
|
|
44
|
+
resource_id = f"arn:aws:organizations::global:delegatedadministrator/securitylake"
|
|
45
|
+
|
|
46
|
+
# Check if Log Archive account ID is provided
|
|
47
|
+
if not hasattr(self, '_log_archive_accounts') or not self._log_archive_accounts:
|
|
48
|
+
self.findings.append(
|
|
49
|
+
self.create_finding(
|
|
50
|
+
status="ERROR",
|
|
51
|
+
region="global",
|
|
52
|
+
resource_id=resource_id,
|
|
53
|
+
checked_value="Delegated administrator is Log Archive account",
|
|
54
|
+
actual_value="Log Archive Account ID not provided",
|
|
55
|
+
remediation="Provide the Log Archive account ID using the --log-archive-account parameter"
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
return self.findings
|
|
59
|
+
|
|
60
|
+
# Use the first log archive account if multiple are provided
|
|
61
|
+
log_archive_account = self._log_archive_accounts[0]
|
|
62
|
+
logger.debug(f"Using Log Archive account: {log_archive_account}")
|
|
63
|
+
|
|
64
|
+
# Get delegated administrators using the base class method
|
|
65
|
+
delegated_admin = self.get_delegated_administrators(region)
|
|
66
|
+
|
|
67
|
+
if not delegated_admin:
|
|
68
|
+
self.findings.append(
|
|
69
|
+
self.create_finding(
|
|
70
|
+
status="FAIL",
|
|
71
|
+
region="global",
|
|
72
|
+
resource_id=resource_id,
|
|
73
|
+
checked_value=f"Delegated administrator is Log Archive account {log_archive_account}",
|
|
74
|
+
actual_value="No delegated administrator configured for Security Lake",
|
|
75
|
+
remediation=(
|
|
76
|
+
f"Configure a delegated administrator for Security Lake and ensure it is the Log Archive account. "
|
|
77
|
+
f"In the AWS Organizations console, navigate to Services > Security Lake and delegate "
|
|
78
|
+
f"administration to the Log Archive account {log_archive_account}."
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
return self.findings
|
|
83
|
+
|
|
84
|
+
# Get the delegated admin account ID
|
|
85
|
+
admin_info = delegated_admin[0] if delegated_admin else {}
|
|
86
|
+
admin_id = admin_info.get('Id', 'Unknown')
|
|
87
|
+
|
|
88
|
+
# Check if the delegated admin is the Log Archive account
|
|
89
|
+
if admin_id == log_archive_account:
|
|
90
|
+
self.findings.append(
|
|
91
|
+
self.create_finding(
|
|
92
|
+
status="PASS",
|
|
93
|
+
region="global",
|
|
94
|
+
resource_id=resource_id,
|
|
95
|
+
checked_value=f"Delegated administrator is Log Archive account {log_archive_account}",
|
|
96
|
+
actual_value=f"Delegated administrator {admin_id} is the Log Archive account",
|
|
97
|
+
remediation="No remediation needed"
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
self.findings.append(
|
|
102
|
+
self.create_finding(
|
|
103
|
+
status="FAIL",
|
|
104
|
+
region="global",
|
|
105
|
+
resource_id=resource_id,
|
|
106
|
+
checked_value=f"Delegated administrator is Log Archive account {log_archive_account}",
|
|
107
|
+
actual_value=f"Delegated administrator {admin_id} is not the Log Archive account {log_archive_account}",
|
|
108
|
+
remediation=(
|
|
109
|
+
f"Update the delegated administrator for Security Lake to be the Log Archive account. "
|
|
110
|
+
f"1. Deregister the current delegated administrator: "
|
|
111
|
+
f"aws organizations deregister-delegated-administrator --service-principal securitylake.amazonaws.com "
|
|
112
|
+
f"--account-id {admin_id} "
|
|
113
|
+
f"2. Register the Log Archive account: "
|
|
114
|
+
f"aws organizations register-delegated-administrator --service-principal securitylake.amazonaws.com "
|
|
115
|
+
f"--account-id {log_archive_account}"
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return self.findings
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Check if Audit account has query access."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Dict, Any
|
|
4
|
+
from sraverify.services.securitylake.base import SecurityLakeCheck
|
|
5
|
+
from sraverify.core.logging import logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SRA_SECURITYLAKE_16(SecurityLakeCheck):
|
|
9
|
+
"""Check if Audit account has query access."""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
"""Initialize check."""
|
|
13
|
+
super().__init__()
|
|
14
|
+
self.check_id = "SRA-SECURITYLAKE-16"
|
|
15
|
+
self.check_name = "Security Lake audit account has query access"
|
|
16
|
+
self.severity = "HIGH"
|
|
17
|
+
self.description = (
|
|
18
|
+
"This check verifies whether the AWS Organization audit "
|
|
19
|
+
"account is set up as query access subscriber. These "
|
|
20
|
+
"subscribers directly query AWS Lake Formation tables in your S3 "
|
|
21
|
+
"bucket with services like Amazon Athena. Separation of log storage "
|
|
22
|
+
"(Log Archive account) and log access (audit account) "
|
|
23
|
+
"helps is separation of duties and helps in least privilege access."
|
|
24
|
+
)
|
|
25
|
+
self.check_logic = (
|
|
26
|
+
"Checks if the audit account is set up as a query access subscriber. "
|
|
27
|
+
"The check passes if there is at least one subscriber with type QUERY_ACCESS and "
|
|
28
|
+
"the account ID matches the audit account ID. "
|
|
29
|
+
"The check fails if no query access subscriber is found for the audit account."
|
|
30
|
+
)
|
|
31
|
+
self._audit_accounts = []
|
|
32
|
+
|
|
33
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
34
|
+
"""Run check."""
|
|
35
|
+
findings = []
|
|
36
|
+
|
|
37
|
+
# Check if audit account ID is provided
|
|
38
|
+
if not self._audit_accounts:
|
|
39
|
+
logger.warning("Audit account ID not provided. Check cannot be completed.")
|
|
40
|
+
findings.append(
|
|
41
|
+
self.create_finding(
|
|
42
|
+
status="ERROR",
|
|
43
|
+
region="global",
|
|
44
|
+
resource_id=f"arn:aws:securitylake::global:subscriber/query-access",
|
|
45
|
+
checked_value="Security tooling account has query access",
|
|
46
|
+
actual_value="Audit account ID not provided",
|
|
47
|
+
remediation="Run sraverify with --audit-account parameter"
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
return findings
|
|
51
|
+
|
|
52
|
+
# Use the first audit account in the list
|
|
53
|
+
audit_account_id = self._audit_accounts[0]
|
|
54
|
+
logger.debug(f"Using audit account ID: {audit_account_id}")
|
|
55
|
+
|
|
56
|
+
# Check each region
|
|
57
|
+
for region in self.regions:
|
|
58
|
+
resource_id = f"arn:aws:securitylake:{region}:{self.account_id}:subscriber/query-access"
|
|
59
|
+
|
|
60
|
+
# Get subscribers using the base class method
|
|
61
|
+
subscribers = self.get_subscribers(region)
|
|
62
|
+
|
|
63
|
+
# Check if any subscriber is the audit account with query access
|
|
64
|
+
audit_subscriber = next(
|
|
65
|
+
(sub for sub in subscribers
|
|
66
|
+
if "LAKEFORMATION" in sub.get("accessTypes", []) and
|
|
67
|
+
sub.get("subscriberIdentity", {}).get("principal") == audit_account_id),
|
|
68
|
+
None
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if not audit_subscriber:
|
|
72
|
+
logger.debug(f"Audit account is not set up as query access subscriber in {region}")
|
|
73
|
+
findings.append(
|
|
74
|
+
self.create_finding(
|
|
75
|
+
status="FAIL",
|
|
76
|
+
region=region,
|
|
77
|
+
resource_id=resource_id,
|
|
78
|
+
checked_value=f"Audit account {audit_account_id} has query access",
|
|
79
|
+
actual_value=f"Audit account {audit_account_id} is not set up as query access subscriber",
|
|
80
|
+
remediation=(
|
|
81
|
+
f"Set up the audit account {audit_account_id} as a query access subscriber in Security Lake. "
|
|
82
|
+
"In the Security Lake console, navigate to Subscribers > Create subscriber and select "
|
|
83
|
+
f"the audit account {audit_account_id} with Query access type."
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
# Get subscriber ID for resource ID
|
|
89
|
+
subscriber_id = audit_subscriber.get("subscriberId", "default")
|
|
90
|
+
resource_id = f"arn:aws:securitylake:{region}:{self.account_id}:subscriber/{subscriber_id}"
|
|
91
|
+
|
|
92
|
+
logger.debug(f"Audit account is set up as query access subscriber in {region}")
|
|
93
|
+
findings.append(
|
|
94
|
+
self.create_finding(
|
|
95
|
+
status="PASS",
|
|
96
|
+
region=region,
|
|
97
|
+
resource_id=resource_id,
|
|
98
|
+
checked_value=f"Audit account {audit_account_id} has query access",
|
|
99
|
+
actual_value=f"Audit account {audit_account_id} is set up as query access subscriber",
|
|
100
|
+
remediation="No remediation needed"
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return findings
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Check if Audit account has data access."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Dict, Any
|
|
4
|
+
from sraverify.services.securitylake.base import SecurityLakeCheck
|
|
5
|
+
from sraverify.core.logging import logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SRA_SECURITYLAKE_17(SecurityLakeCheck):
|
|
9
|
+
"""Check if Audit account has data access."""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
"""Initialize check."""
|
|
13
|
+
super().__init__()
|
|
14
|
+
self.check_id = "SRA-SECURITYLAKE-17"
|
|
15
|
+
self.check_name = "Security Lake audit account has data access"
|
|
16
|
+
self.severity = "HIGH"
|
|
17
|
+
self.description = (
|
|
18
|
+
"This check verifies whether the AWS Organization "
|
|
19
|
+
"Audit account is set up as data access subscriber. These "
|
|
20
|
+
"subscribers can directly access the S3 objects and receive "
|
|
21
|
+
"notifications of new objects through a subscription endpoint or "
|
|
22
|
+
"by polling an Amazon SQS queue."
|
|
23
|
+
)
|
|
24
|
+
self.check_logic = (
|
|
25
|
+
"Checks if the audit account is set up as a data access subscriber. "
|
|
26
|
+
"The check passes if there is at least one subscriber with type DATA_ACCESS and "
|
|
27
|
+
"the account ID matches the audit account ID. "
|
|
28
|
+
"The check fails if no data access subscriber is found for the audit account."
|
|
29
|
+
)
|
|
30
|
+
self._audit_accounts = []
|
|
31
|
+
|
|
32
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
33
|
+
"""Run check."""
|
|
34
|
+
findings = []
|
|
35
|
+
|
|
36
|
+
# Check if audit account ID is provided
|
|
37
|
+
if not self._audit_accounts:
|
|
38
|
+
logger.warning("Audit account ID not provided. Check cannot be completed.")
|
|
39
|
+
findings.append(
|
|
40
|
+
self.create_finding(
|
|
41
|
+
status="ERROR",
|
|
42
|
+
region="global",
|
|
43
|
+
resource_id=f"arn:aws:securitylake::global:subscriber/data-access",
|
|
44
|
+
checked_value="Security tooling account has data access",
|
|
45
|
+
actual_value="Audit account ID not provided",
|
|
46
|
+
remediation="Run sraverify with --audit-account parameter"
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
return findings
|
|
50
|
+
|
|
51
|
+
# Use the first audit account in the list
|
|
52
|
+
audit_account_id = self._audit_accounts[0]
|
|
53
|
+
logger.debug(f"Using audit account ID: {audit_account_id}")
|
|
54
|
+
|
|
55
|
+
# Check each region
|
|
56
|
+
for region in self.regions:
|
|
57
|
+
resource_id = f"arn:aws:securitylake:{region}:{self.account_id}:subscriber/data-access"
|
|
58
|
+
|
|
59
|
+
# Get subscribers using the base class method
|
|
60
|
+
subscribers = self.get_subscribers(region)
|
|
61
|
+
|
|
62
|
+
# Check if any subscriber is the audit account with data access
|
|
63
|
+
audit_subscriber = next(
|
|
64
|
+
(sub for sub in subscribers
|
|
65
|
+
if "S3" in sub.get("accessTypes", []) and
|
|
66
|
+
sub.get("subscriberIdentity", {}).get("principal") == audit_account_id),
|
|
67
|
+
None
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if not audit_subscriber:
|
|
71
|
+
logger.debug(f"Audit account is not set up as data access subscriber in {region}")
|
|
72
|
+
findings.append(
|
|
73
|
+
self.create_finding(
|
|
74
|
+
status="FAIL",
|
|
75
|
+
region=region,
|
|
76
|
+
resource_id=resource_id,
|
|
77
|
+
checked_value=f"Audit account {audit_account_id} has data access",
|
|
78
|
+
actual_value=f"Audit account {audit_account_id} is not set up as data access subscriber",
|
|
79
|
+
remediation=(
|
|
80
|
+
f"Set up the audit account {audit_account_id} as a data access subscriber in Security Lake. "
|
|
81
|
+
"In the Security Lake console, navigate to Subscribers > Create subscriber and select "
|
|
82
|
+
f"the audit account {audit_account_id} with Data access type."
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
else:
|
|
87
|
+
# Get subscriber ID for resource ID
|
|
88
|
+
subscriber_id = audit_subscriber.get("subscriberId", "default")
|
|
89
|
+
resource_id = f"arn:aws:securitylake:{region}:{self.account_id}:subscriber/{subscriber_id}"
|
|
90
|
+
|
|
91
|
+
logger.debug(f"Audit account is set up as data access subscriber in {region}")
|
|
92
|
+
findings.append(
|
|
93
|
+
self.create_finding(
|
|
94
|
+
status="PASS",
|
|
95
|
+
region=region,
|
|
96
|
+
resource_id=resource_id,
|
|
97
|
+
checked_value=f"Audit account {audit_account_id} has data access",
|
|
98
|
+
actual_value=f"Audit account {audit_account_id} is set up as data access subscriber",
|
|
99
|
+
remediation="No remediation needed"
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return findings
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""Security Lake client for interacting with AWS Security Lake service."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, List, Optional, Any
|
|
4
|
+
import boto3
|
|
5
|
+
from botocore.exceptions import ClientError
|
|
6
|
+
from sraverify.core.logging import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SecurityLakeClient:
|
|
10
|
+
"""Client for interacting with AWS Security Lake service."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, region: str, session: Optional[boto3.Session] = None):
|
|
13
|
+
"""
|
|
14
|
+
Initialize Security Lake client.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
region: AWS region name
|
|
18
|
+
session: Optional boto3 session
|
|
19
|
+
"""
|
|
20
|
+
self.region = region
|
|
21
|
+
self.session = session or boto3.Session()
|
|
22
|
+
self.client = self.session.client('securitylake', region_name=region)
|
|
23
|
+
self.org_client = self.session.client('organizations', region_name=region)
|
|
24
|
+
|
|
25
|
+
def is_security_lake_enabled(self):
|
|
26
|
+
"""
|
|
27
|
+
Check if Security Lake is enabled in the region.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
True if enabled, False otherwise
|
|
31
|
+
"""
|
|
32
|
+
try:
|
|
33
|
+
response = self.client.list_data_lakes(regions=[self.region])
|
|
34
|
+
data_lakes = response.get('dataLakes', [])
|
|
35
|
+
return len(data_lakes) > 0
|
|
36
|
+
except ClientError as e:
|
|
37
|
+
logger.debug(f"Error checking Security Lake status in {self.region}: {e}")
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
def get_organization_configuration(self):
|
|
41
|
+
"""
|
|
42
|
+
Get Security Lake organization configuration.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Organization configuration or empty dict if error
|
|
46
|
+
"""
|
|
47
|
+
try:
|
|
48
|
+
response = self.client.get_data_lake_organization_configuration()
|
|
49
|
+
return response
|
|
50
|
+
except self.client.exceptions.ResourceNotFoundException:
|
|
51
|
+
logger.debug(f"No organization configuration found in region {self.region}")
|
|
52
|
+
return {}
|
|
53
|
+
except ClientError as e:
|
|
54
|
+
logger.error(f"Error getting organization configuration in {self.region}: {e}")
|
|
55
|
+
return {}
|
|
56
|
+
|
|
57
|
+
def list_data_lakes(self):
|
|
58
|
+
"""
|
|
59
|
+
List Security Lake data lakes.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
List of data lakes or empty list if error
|
|
63
|
+
"""
|
|
64
|
+
try:
|
|
65
|
+
response = self.client.list_data_lakes(regions=[self.region])
|
|
66
|
+
return response.get("dataLakes", [])
|
|
67
|
+
except self.client.exceptions.ResourceNotFoundException:
|
|
68
|
+
logger.debug(f"No data lakes found in region {self.region}")
|
|
69
|
+
return []
|
|
70
|
+
except ClientError as e:
|
|
71
|
+
logger.error(f"Error listing data lakes in {self.region}: {e}")
|
|
72
|
+
return []
|
|
73
|
+
|
|
74
|
+
def list_log_sources(self, regions=None, accounts=None):
|
|
75
|
+
"""
|
|
76
|
+
List enabled log sources with pagination support.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
regions: List of regions to filter (optional)
|
|
80
|
+
accounts: List of account IDs to filter (optional)
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
List of log sources or empty list if error
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
params = {}
|
|
87
|
+
if regions:
|
|
88
|
+
params['regions'] = regions
|
|
89
|
+
if accounts:
|
|
90
|
+
params['accounts'] = accounts
|
|
91
|
+
|
|
92
|
+
response = self.client.list_log_sources(**params)
|
|
93
|
+
log_sources = response.get("sources", [])
|
|
94
|
+
|
|
95
|
+
# Handle pagination
|
|
96
|
+
while response.get('nextToken'):
|
|
97
|
+
params['nextToken'] = response['nextToken']
|
|
98
|
+
response = self.client.list_log_sources(**params)
|
|
99
|
+
log_sources.extend(response.get("sources", []))
|
|
100
|
+
|
|
101
|
+
return log_sources
|
|
102
|
+
except self.client.exceptions.ResourceNotFoundException:
|
|
103
|
+
logger.debug(f"No log sources found in region {self.region}")
|
|
104
|
+
return []
|
|
105
|
+
except ClientError as e:
|
|
106
|
+
logger.error(f"Error listing log sources in {self.region}: {e}")
|
|
107
|
+
return []
|
|
108
|
+
|
|
109
|
+
def list_subscribers(self):
|
|
110
|
+
"""
|
|
111
|
+
List Security Lake subscribers with pagination support.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
List of subscribers or empty list if error
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
response = self.client.list_subscribers()
|
|
118
|
+
subscribers = response.get("subscribers", [])
|
|
119
|
+
|
|
120
|
+
# Handle pagination
|
|
121
|
+
while response.get('nextToken'):
|
|
122
|
+
response = self.client.list_subscribers(nextToken=response['nextToken'])
|
|
123
|
+
subscribers.extend(response.get("subscribers", []))
|
|
124
|
+
|
|
125
|
+
return subscribers
|
|
126
|
+
except self.client.exceptions.ResourceNotFoundException:
|
|
127
|
+
logger.debug(f"No subscribers found in region {self.region}")
|
|
128
|
+
return []
|
|
129
|
+
except ClientError as e:
|
|
130
|
+
logger.error(f"Error listing subscribers in {self.region}: {e}")
|
|
131
|
+
return []
|
|
132
|
+
|
|
133
|
+
def get_delegated_admin(self):
|
|
134
|
+
"""
|
|
135
|
+
Get Security Lake delegated admin account.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Delegated admin info or None if error
|
|
139
|
+
"""
|
|
140
|
+
try:
|
|
141
|
+
response = self.org_client.list_delegated_administrators(ServicePrincipal="securitylake.amazonaws.com")
|
|
142
|
+
admins = response.get("DelegatedAdministrators", [])
|
|
143
|
+
return admins[0] if admins else None
|
|
144
|
+
except ClientError as e:
|
|
145
|
+
logger.error(f"Error getting delegated admin: {e}")
|
|
146
|
+
return None
|
|
147
|
+
|
|
148
|
+
def list_delegated_administrators(self, service_principal: str = "securitylake.amazonaws.com") -> List[Dict[str, Any]]:
|
|
149
|
+
"""
|
|
150
|
+
List delegated administrators for SecurityLake.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
service_principal: Service principal to check for delegated administrators
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
List of delegated administrators or empty list if error
|
|
157
|
+
"""
|
|
158
|
+
try:
|
|
159
|
+
response = self.org_client.list_delegated_administrators(ServicePrincipal=service_principal)
|
|
160
|
+
delegated_admins = response.get("DelegatedAdministrators", [])
|
|
161
|
+
|
|
162
|
+
logger.debug(f"Found {len(delegated_admins)} delegated administrators for {service_principal}")
|
|
163
|
+
for admin in delegated_admins:
|
|
164
|
+
logger.debug(f"Delegated admin: {admin.get('Id')} - {admin.get('Name')}")
|
|
165
|
+
return delegated_admins
|
|
166
|
+
except ClientError as e:
|
|
167
|
+
logger.error(f"Error listing delegated administrators for {service_principal}: {e}")
|
|
168
|
+
return []
|
|
169
|
+
|
|
170
|
+
def get_sqs_queue_encryption(self, queue_url):
|
|
171
|
+
"""
|
|
172
|
+
Get SQS queue encryption settings.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
queue_url: SQS queue URL
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
KMS key ID or None if error
|
|
179
|
+
"""
|
|
180
|
+
try:
|
|
181
|
+
sqs = self.session.client('sqs', region_name=self.region)
|
|
182
|
+
response = sqs.get_queue_attributes(
|
|
183
|
+
QueueUrl=queue_url,
|
|
184
|
+
AttributeNames=["KmsMasterKeyId"]
|
|
185
|
+
)
|
|
186
|
+
return response.get("Attributes", {}).get("KmsMasterKeyId")
|
|
187
|
+
except ClientError as e:
|
|
188
|
+
logger.error(f"Error getting SQS queue encryption for {queue_url}: {e}")
|
|
189
|
+
return None
|
|
190
|
+
|
|
191
|
+
def list_organization_accounts(self) -> List[Dict[str, Any]]:
|
|
192
|
+
"""
|
|
193
|
+
List all accounts in the organization.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
List of organization accounts or empty list if error
|
|
197
|
+
"""
|
|
198
|
+
try:
|
|
199
|
+
response = self.org_client.list_accounts()
|
|
200
|
+
accounts = response.get('Accounts', [])
|
|
201
|
+
|
|
202
|
+
# Handle pagination
|
|
203
|
+
while response.get('NextToken'):
|
|
204
|
+
response = self.org_client.list_accounts(NextToken=response['NextToken'])
|
|
205
|
+
accounts.extend(response.get('Accounts', []))
|
|
206
|
+
|
|
207
|
+
logger.debug(f"Found {len(accounts)} organization accounts")
|
|
208
|
+
return accounts
|
|
209
|
+
except ClientError as e:
|
|
210
|
+
logger.error(f"Error listing organization accounts: {e}")
|
|
211
|
+
return []
|
|
212
|
+
|
|
213
|
+
def get_data_lake_sources(self, account_id: str = None):
|
|
214
|
+
"""
|
|
215
|
+
Get data lake sources for a specific account.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
account_id: AWS account ID string to check sources for
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
List of data lake sources or empty list if error
|
|
222
|
+
"""
|
|
223
|
+
try:
|
|
224
|
+
request_body = {}
|
|
225
|
+
if account_id:
|
|
226
|
+
# Ensure account_id is a string (extract ID if it's a dict like SecurityHub pattern)
|
|
227
|
+
if isinstance(account_id, dict):
|
|
228
|
+
if 'Id' in account_id:
|
|
229
|
+
account_id = account_id['Id']
|
|
230
|
+
else:
|
|
231
|
+
logger.error(f"Cannot extract account ID from dict: {account_id}")
|
|
232
|
+
return []
|
|
233
|
+
|
|
234
|
+
request_body["accounts"] = [account_id]
|
|
235
|
+
|
|
236
|
+
response = self.client.get_data_lake_sources(**request_body)
|
|
237
|
+
return response.get("dataLakeSources", [])
|
|
238
|
+
except self.client.exceptions.ResourceNotFoundException:
|
|
239
|
+
logger.debug(f"No data lake sources found in region {self.region}")
|
|
240
|
+
return []
|
|
241
|
+
except ClientError as e:
|
|
242
|
+
# Use debug level for UnauthorizedException as it's expected when Security Lake isn't enabled
|
|
243
|
+
if e.response.get('Error', {}).get('Code') == 'UnauthorizedException':
|
|
244
|
+
logger.debug(f"Security Lake not enabled in {self.region}: {e}")
|
|
245
|
+
else:
|
|
246
|
+
logger.error(f"Error getting data lake sources in {self.region}: {e}")
|
|
247
|
+
return []
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Shield service checks."""
|
|
2
|
+
|
|
3
|
+
from sraverify.services.shield.checks.sra_shield_01 import SRA_SHIELD_01
|
|
4
|
+
from sraverify.services.shield.checks.sra_shield_02 import SRA_SHIELD_02
|
|
5
|
+
from sraverify.services.shield.checks.sra_shield_03 import SRA_SHIELD_03
|
|
6
|
+
from sraverify.services.shield.checks.sra_shield_04 import SRA_SHIELD_04
|
|
7
|
+
from sraverify.services.shield.checks.sra_shield_05 import SRA_SHIELD_05
|
|
8
|
+
from sraverify.services.shield.checks.sra_shield_06 import SRA_SHIELD_06
|
|
9
|
+
from sraverify.services.shield.checks.sra_shield_07 import SRA_SHIELD_07
|
|
10
|
+
from sraverify.services.shield.checks.sra_shield_08 import SRA_SHIELD_08
|
|
11
|
+
from sraverify.services.shield.checks.sra_shield_09 import SRA_SHIELD_09
|
|
12
|
+
from sraverify.services.shield.checks.sra_shield_10 import SRA_SHIELD_10
|
|
13
|
+
from sraverify.services.shield.checks.sra_shield_11 import SRA_SHIELD_11
|
|
14
|
+
from sraverify.services.shield.checks.sra_shield_12 import SRA_SHIELD_12
|
|
15
|
+
from sraverify.services.shield.checks.sra_shield_13 import SRA_SHIELD_13
|
|
16
|
+
from sraverify.services.shield.checks.sra_shield_14 import SRA_SHIELD_14
|
|
17
|
+
|
|
18
|
+
CHECKS = {
|
|
19
|
+
"SRA-SHIELD-01": SRA_SHIELD_01,
|
|
20
|
+
"SRA-SHIELD-02": SRA_SHIELD_02,
|
|
21
|
+
"SRA-SHIELD-03": SRA_SHIELD_03,
|
|
22
|
+
"SRA-SHIELD-04": SRA_SHIELD_04,
|
|
23
|
+
"SRA-SHIELD-05": SRA_SHIELD_05,
|
|
24
|
+
"SRA-SHIELD-06": SRA_SHIELD_06,
|
|
25
|
+
"SRA-SHIELD-07": SRA_SHIELD_07,
|
|
26
|
+
"SRA-SHIELD-08": SRA_SHIELD_08,
|
|
27
|
+
"SRA-SHIELD-09": SRA_SHIELD_09,
|
|
28
|
+
"SRA-SHIELD-10": SRA_SHIELD_10,
|
|
29
|
+
"SRA-SHIELD-11": SRA_SHIELD_11,
|
|
30
|
+
"SRA-SHIELD-12": SRA_SHIELD_12,
|
|
31
|
+
"SRA-SHIELD-13": SRA_SHIELD_13,
|
|
32
|
+
"SRA-SHIELD-14": SRA_SHIELD_14,
|
|
33
|
+
}
|