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,279 @@
|
|
|
1
|
+
from typing import Dict, List, Any
|
|
2
|
+
from sraverify.checks import SecurityCheck
|
|
3
|
+
from botocore.exceptions import ClientError
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
class SRACT13(SecurityCheck):
|
|
7
|
+
"""SRA-CT-13: Security Tooling Account CloudTrail Delegated Administrator"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, check_type="organization", security_ou_name=None):
|
|
10
|
+
"""Initialize the check with organization type and optional security OU name"""
|
|
11
|
+
super().__init__(check_type=check_type)
|
|
12
|
+
self.check_id = "SRA-CT-13"
|
|
13
|
+
self.check_name = "The Security Tooling Account is the Delegated Administrator set for CloudTrail"
|
|
14
|
+
self.description = ('This check verifies whether CloudTrail delegated admin account is the security tooling account '
|
|
15
|
+
'of your AWS organization. Security Tooling account is dedicated to operating security services, '
|
|
16
|
+
'monitoring AWS accounts, and automating security alerting and response. CloudTrail helps monitor '
|
|
17
|
+
'API activities across all your AWS accounts and regions.')
|
|
18
|
+
self.service = "CloudTrail"
|
|
19
|
+
self.severity = "HIGH"
|
|
20
|
+
self.check_type = check_type
|
|
21
|
+
self.check_logic = ('1. Verify execution from Organization Management Account | '
|
|
22
|
+
'2. List all Organization Units and find specified OU (via flag --security-ou-name) or OU containing "security" | '
|
|
23
|
+
'3. List accounts in Security OU | '
|
|
24
|
+
'4. List CloudTrail delegated administrators | '
|
|
25
|
+
'5. Verify delegated admin account exists in Security OU')
|
|
26
|
+
self.logger = logging.getLogger(self.__class__.__name__)
|
|
27
|
+
self.findings = []
|
|
28
|
+
self.security_ou_name = security_ou_name.lower() if security_ou_name else None
|
|
29
|
+
|
|
30
|
+
def get_findings(self) -> List[Dict[str, Any]]:
|
|
31
|
+
"""Return the findings"""
|
|
32
|
+
return self.findings
|
|
33
|
+
|
|
34
|
+
def find_security_ou(self, org_client, root_id):
|
|
35
|
+
"""Find the security OU based on name parameter or default search"""
|
|
36
|
+
paginator = org_client.get_paginator('list_organizational_units_for_parent')
|
|
37
|
+
search_term = self.security_ou_name if self.security_ou_name else 'security'
|
|
38
|
+
|
|
39
|
+
for page in paginator.paginate(ParentId=root_id):
|
|
40
|
+
for ou in page['OrganizationalUnits']:
|
|
41
|
+
# If security_ou_name is provided, use exact match
|
|
42
|
+
if self.security_ou_name and self.security_ou_name == ou['Name'].lower():
|
|
43
|
+
return ou['Id'], ou['Name'], search_term
|
|
44
|
+
# If no security_ou_name provided, look for 'security' in name
|
|
45
|
+
elif not self.security_ou_name and 'security' in ou['Name'].lower():
|
|
46
|
+
return ou['Id'], ou['Name'], search_term
|
|
47
|
+
|
|
48
|
+
return None, None, search_term
|
|
49
|
+
|
|
50
|
+
def get_accounts_in_ou(self, organizations_client, ou_id: str) -> List[str]:
|
|
51
|
+
"""Get list of account IDs in the specified OU"""
|
|
52
|
+
try:
|
|
53
|
+
account_ids = []
|
|
54
|
+
paginator = organizations_client.get_paginator('list_accounts_for_parent')
|
|
55
|
+
|
|
56
|
+
for page in paginator.paginate(ParentId=ou_id):
|
|
57
|
+
for account in page['Accounts']:
|
|
58
|
+
account_ids.append(account['Id'])
|
|
59
|
+
|
|
60
|
+
return account_ids
|
|
61
|
+
|
|
62
|
+
except ClientError as e:
|
|
63
|
+
self.logger.error(f"Error listing accounts in OU: {str(e)}")
|
|
64
|
+
return []
|
|
65
|
+
|
|
66
|
+
def run(self, session) -> None:
|
|
67
|
+
"""Run the security check"""
|
|
68
|
+
try:
|
|
69
|
+
# Get account information
|
|
70
|
+
sts_client = session.client('sts')
|
|
71
|
+
account_id = sts_client.get_caller_identity()['Account']
|
|
72
|
+
region = session.region_name
|
|
73
|
+
self.logger.debug(f"Running check for account: {account_id} in region: {region}")
|
|
74
|
+
|
|
75
|
+
# Step 1: Verify execution from Organization Management Account
|
|
76
|
+
is_management, error_message = self.org_checker.verify_org_management()
|
|
77
|
+
if not is_management:
|
|
78
|
+
self.findings.append({
|
|
79
|
+
'CheckId': self.check_id,
|
|
80
|
+
'Status': 'ERROR',
|
|
81
|
+
'Region': region,
|
|
82
|
+
"Severity": self.severity,
|
|
83
|
+
'Title': f"{self.check_id} {self.check_name}",
|
|
84
|
+
'Description': self.description,
|
|
85
|
+
'ResourceId': account_id,
|
|
86
|
+
'ResourceType': 'AWS::Organizations::Account',
|
|
87
|
+
'AccountId': account_id,
|
|
88
|
+
'CheckedValue': 'Management Account Access',
|
|
89
|
+
'ActualValue': error_message if error_message else 'Not running from management account',
|
|
90
|
+
'Remediation': 'Run this check from the Organization Management Account',
|
|
91
|
+
'Service': self.service,
|
|
92
|
+
'CheckLogic': self.check_logic,
|
|
93
|
+
'CheckType': self.check_type
|
|
94
|
+
})
|
|
95
|
+
return self.findings
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
# Initialize Organizations client
|
|
99
|
+
organizations_client = session.client('organizations')
|
|
100
|
+
|
|
101
|
+
# Step 2: List all Organization Units and find Security OU
|
|
102
|
+
root_id = organizations_client.list_roots()['Roots'][0]['Id']
|
|
103
|
+
security_ou_id, security_ou_name, search_term = self.find_security_ou(organizations_client, root_id)
|
|
104
|
+
|
|
105
|
+
if not security_ou_id:
|
|
106
|
+
self.findings.append({
|
|
107
|
+
"CheckId": self.check_id,
|
|
108
|
+
"Status": "ERROR",
|
|
109
|
+
"Region": region,
|
|
110
|
+
"Severity": self.severity,
|
|
111
|
+
"Title": f"{self.check_id} {self.check_name}",
|
|
112
|
+
"Description": self.description,
|
|
113
|
+
"ResourceId": "security-ou",
|
|
114
|
+
"ResourceType": "AWS::Organizations::OrganizationalUnit",
|
|
115
|
+
"AccountId": account_id,
|
|
116
|
+
"CheckedValue": "Security OU Existence",
|
|
117
|
+
"ActualValue": f"No OU found matching search term: {search_term}",
|
|
118
|
+
"Remediation": "Verify Security OU name or existence of OU containing 'security'",
|
|
119
|
+
"Service": self.service,
|
|
120
|
+
"CheckLogic": self.check_logic,
|
|
121
|
+
"CheckType": self.check_type
|
|
122
|
+
})
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
# Step 3: List accounts in Security OU
|
|
126
|
+
security_accounts = self.get_accounts_in_ou(organizations_client, security_ou_id)
|
|
127
|
+
if not security_accounts:
|
|
128
|
+
self.findings.append({
|
|
129
|
+
"CheckId": self.check_id,
|
|
130
|
+
"Status": "FAIL",
|
|
131
|
+
"Region": region,
|
|
132
|
+
"Severity": self.severity,
|
|
133
|
+
"Title": f"{self.check_id} {self.check_name}",
|
|
134
|
+
"Description": self.description,
|
|
135
|
+
"ResourceId": security_ou_id,
|
|
136
|
+
"ResourceType": "AWS::Organizations::OrganizationalUnit",
|
|
137
|
+
"AccountId": account_id,
|
|
138
|
+
"CheckedValue": "Security OU Accounts",
|
|
139
|
+
"ActualValue": f"No accounts found in Security OU: {security_ou_name}",
|
|
140
|
+
"Remediation": "Verify Security OU configuration and account assignments",
|
|
141
|
+
"Service": self.service,
|
|
142
|
+
"CheckLogic": self.check_logic,
|
|
143
|
+
"CheckType": self.check_type
|
|
144
|
+
})
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
# Step 4: List CloudTrail delegated administrators
|
|
148
|
+
try:
|
|
149
|
+
delegated_admins = organizations_client.list_delegated_administrators(
|
|
150
|
+
ServicePrincipal='cloudtrail.amazonaws.com'
|
|
151
|
+
)
|
|
152
|
+
admin_accounts = delegated_admins.get('DelegatedAdministrators', [])
|
|
153
|
+
|
|
154
|
+
if not admin_accounts:
|
|
155
|
+
self.findings.append({
|
|
156
|
+
"CheckId": self.check_id,
|
|
157
|
+
"Status": "FAIL",
|
|
158
|
+
"Region": region,
|
|
159
|
+
"Severity": self.severity,
|
|
160
|
+
"Title": f"{self.check_id} {self.check_name}",
|
|
161
|
+
"Description": self.description,
|
|
162
|
+
"ResourceId": "cloudtrail-delegated-admin",
|
|
163
|
+
"ResourceType": "AWS::Organizations::Account",
|
|
164
|
+
"AccountId": account_id,
|
|
165
|
+
"CheckedValue": "Delegated Administrator Configuration",
|
|
166
|
+
"ActualValue": "No CloudTrail delegated administrators configured",
|
|
167
|
+
"Remediation": "Configure a Security Tooling account as CloudTrail delegated administrator",
|
|
168
|
+
"Service": self.service,
|
|
169
|
+
"CheckLogic": self.check_logic,
|
|
170
|
+
"CheckType": self.check_type
|
|
171
|
+
})
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
# Step 5: Verify delegated admin account exists in Security OU
|
|
175
|
+
valid_admin = None
|
|
176
|
+
for admin in admin_accounts:
|
|
177
|
+
if admin['Id'] in security_accounts:
|
|
178
|
+
valid_admin = admin
|
|
179
|
+
break
|
|
180
|
+
|
|
181
|
+
if valid_admin:
|
|
182
|
+
self.findings.append({
|
|
183
|
+
"CheckId": self.check_id,
|
|
184
|
+
"Status": "PASS",
|
|
185
|
+
"Region": region,
|
|
186
|
+
"Severity": self.severity,
|
|
187
|
+
"Title": f"{self.check_id} {self.check_name}",
|
|
188
|
+
"Description": self.description,
|
|
189
|
+
"ResourceId": valid_admin['Id'],
|
|
190
|
+
"ResourceType": "AWS::Organizations::Account",
|
|
191
|
+
"AccountId": account_id,
|
|
192
|
+
"CheckedValue": "CloudTrail Delegated Administrator",
|
|
193
|
+
"ActualValue": f"CloudTrail delegated administrator {valid_admin['Id']} is in Security OU {security_ou_name}",
|
|
194
|
+
"Remediation": "None required",
|
|
195
|
+
"Service": self.service,
|
|
196
|
+
"CheckLogic": self.check_logic,
|
|
197
|
+
"CheckType": self.check_type
|
|
198
|
+
})
|
|
199
|
+
else:
|
|
200
|
+
admin_list = ', '.join([admin['Id'] for admin in admin_accounts])
|
|
201
|
+
self.findings.append({
|
|
202
|
+
"CheckId": self.check_id,
|
|
203
|
+
"Status": "FAIL",
|
|
204
|
+
"Region": region,
|
|
205
|
+
"Severity": self.severity,
|
|
206
|
+
"Title": f"{self.check_id} {self.check_name}",
|
|
207
|
+
"Description": self.description,
|
|
208
|
+
"ResourceId": "cloudtrail-delegated-admin",
|
|
209
|
+
"ResourceType": "AWS::Organizations::Account",
|
|
210
|
+
"AccountId": account_id,
|
|
211
|
+
"CheckedValue": "CloudTrail Delegated Administrator",
|
|
212
|
+
"ActualValue": f"CloudTrail delegated administrators ({admin_list}) are not in Security OU {security_ou_name}",
|
|
213
|
+
"Remediation": f"Configure a Security Tooling account from Security OU {security_ou_name} as CloudTrail delegated administrator",
|
|
214
|
+
"Service": self.service,
|
|
215
|
+
"CheckLogic": self.check_logic,
|
|
216
|
+
"CheckType": self.check_type
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
except ClientError as e:
|
|
220
|
+
if 'AccessDeniedException' in str(e):
|
|
221
|
+
self.findings.append({
|
|
222
|
+
"CheckId": self.check_id,
|
|
223
|
+
"Status": "ERROR",
|
|
224
|
+
"Region": region,
|
|
225
|
+
"Severity": self.severity,
|
|
226
|
+
"Title": f"{self.check_id} {self.check_name}",
|
|
227
|
+
"Description": self.description,
|
|
228
|
+
"ResourceId": "organizations-permissions",
|
|
229
|
+
"ResourceType": "AWS::Organizations::Account",
|
|
230
|
+
"AccountId": account_id,
|
|
231
|
+
"CheckedValue": "Organizations Permissions",
|
|
232
|
+
"ActualValue": "Insufficient permissions to list delegated administrators",
|
|
233
|
+
"Remediation": "Verify Organizations permissions in management account",
|
|
234
|
+
"Service": self.service,
|
|
235
|
+
"CheckLogic": self.check_logic,
|
|
236
|
+
"CheckType": self.check_type
|
|
237
|
+
})
|
|
238
|
+
else:
|
|
239
|
+
raise e
|
|
240
|
+
|
|
241
|
+
except ClientError as e:
|
|
242
|
+
self.logger.error(f"Error accessing Organizations: {str(e)}")
|
|
243
|
+
self.findings.append({
|
|
244
|
+
"CheckId": self.check_id,
|
|
245
|
+
"Status": "ERROR",
|
|
246
|
+
"Region": region,
|
|
247
|
+
"Severity": self.severity,
|
|
248
|
+
"Title": f"{self.check_id} {self.check_name}",
|
|
249
|
+
"Description": self.description,
|
|
250
|
+
"ResourceId": "organizations",
|
|
251
|
+
"ResourceType": "AWS::Organizations::Account",
|
|
252
|
+
"AccountId": account_id,
|
|
253
|
+
"CheckedValue": "Organizations API Access",
|
|
254
|
+
"ActualValue": f"Error accessing Organizations: {str(e)}",
|
|
255
|
+
"Remediation": "Verify Organizations permissions",
|
|
256
|
+
"Service": self.service,
|
|
257
|
+
"CheckLogic": self.check_logic,
|
|
258
|
+
"CheckType": self.check_type
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
except Exception as e:
|
|
262
|
+
self.logger.error(f"Unexpected error in check: {str(e)}")
|
|
263
|
+
self.findings.append({
|
|
264
|
+
"CheckId": self.check_id,
|
|
265
|
+
"Status": "ERROR",
|
|
266
|
+
"Region": region if 'region' in locals() else session.region_name,
|
|
267
|
+
"Severity": self.severity,
|
|
268
|
+
"Title": f"{self.check_id} {self.check_name}",
|
|
269
|
+
"Description": self.description,
|
|
270
|
+
"ResourceId": "check-execution",
|
|
271
|
+
"ResourceType": "AWS::Organizations::Account",
|
|
272
|
+
"AccountId": account_id if 'account_id' in locals() else "unknown",
|
|
273
|
+
"CheckedValue": "Check Execution",
|
|
274
|
+
"ActualValue": f"Unexpected error: {str(e)}",
|
|
275
|
+
"Remediation": "Contact support team",
|
|
276
|
+
"Service": self.service,
|
|
277
|
+
"CheckLogic": self.check_logic,
|
|
278
|
+
"CheckType": self.check_type
|
|
279
|
+
})
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
from typing import Dict, List, Any
|
|
2
|
+
from sraverify.checks import SecurityCheck
|
|
3
|
+
from botocore.exceptions import ClientError
|
|
4
|
+
|
|
5
|
+
class SRACT2(SecurityCheck):
|
|
6
|
+
"""SRA-CT-2: Organization trail is encrypted with KMS"""
|
|
7
|
+
|
|
8
|
+
def __init__(self, check_type="organization"):
|
|
9
|
+
super().__init__(check_type=check_type)
|
|
10
|
+
self.check_id = "SRA-CT-2"
|
|
11
|
+
self.check_name = "Organization trail is encrypted with KMS"
|
|
12
|
+
self.severity = 'HIGH'
|
|
13
|
+
self.description = ('This check verifies that your organization trail is encrypted with a KMS key. '
|
|
14
|
+
'Log files delivered by CloudTrail to your bucket should be encrypted by using SSE-KMS. '
|
|
15
|
+
'This is selected by default in the console but can be altered by users. With SSE-KMS '
|
|
16
|
+
'you create and manage the KMS key yourself with the ability to manage permissions on '
|
|
17
|
+
'who can use the key. For a user to read log files they must have read permissions to '
|
|
18
|
+
'the bucket and have permissions that allows decrypt permission on the key applied by '
|
|
19
|
+
'the KMS key policy.')
|
|
20
|
+
self.check_logic = ('1. Verify execution from Organization Management account | '
|
|
21
|
+
'2. List all CloudTrail trails in the region | '
|
|
22
|
+
'3. Identify organization trail | '
|
|
23
|
+
'4. Check if trail is enabled and logging | '
|
|
24
|
+
'5. Verify KMS encryption is enabled on the trail | '
|
|
25
|
+
'6. Validate KMS key exists and is accessible | '
|
|
26
|
+
'7. Check passes if organization trail uses KMS encryption')
|
|
27
|
+
self.service = 'CloudTrail'
|
|
28
|
+
|
|
29
|
+
def get_findings(self) -> List[Dict[str, Any]]:
|
|
30
|
+
"""Return the findings of the check"""
|
|
31
|
+
return self.findings
|
|
32
|
+
|
|
33
|
+
def run(self, session):
|
|
34
|
+
"""Run the CloudTrail organization trail encryption check"""
|
|
35
|
+
try:
|
|
36
|
+
cloudtrail_client = session.client('cloudtrail')
|
|
37
|
+
account_id = session.client('sts').get_caller_identity()['Account']
|
|
38
|
+
region = session.region_name
|
|
39
|
+
|
|
40
|
+
# Step 1: Verify we're in management account using org_mgmt_checker
|
|
41
|
+
is_management, error_message = self.org_checker.verify_org_management()
|
|
42
|
+
if not is_management:
|
|
43
|
+
finding = {
|
|
44
|
+
'CheckId': self.check_id,
|
|
45
|
+
'Status': 'ERROR',
|
|
46
|
+
'Region': region,
|
|
47
|
+
"Severity": self.severity,
|
|
48
|
+
'Title': f"{self.check_id} {self.check_name}",
|
|
49
|
+
'Description': self.description,
|
|
50
|
+
'ResourceId': account_id,
|
|
51
|
+
'ResourceType': 'AWS::Organizations::Account',
|
|
52
|
+
'AccountId': account_id,
|
|
53
|
+
'CheckedValue': 'Management Account Access',
|
|
54
|
+
'ActualValue': error_message if error_message else 'Not running from management account',
|
|
55
|
+
'Remediation': 'Run this check from the Organization Management Account',
|
|
56
|
+
'Service': self.service,
|
|
57
|
+
'CheckLogic': self.check_logic,
|
|
58
|
+
'CheckType': self.check_type
|
|
59
|
+
}
|
|
60
|
+
self.findings.append(finding)
|
|
61
|
+
return self.findings
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
# Step 2: List all CloudTrail trails in the region
|
|
65
|
+
trails = cloudtrail_client.list_trails()
|
|
66
|
+
|
|
67
|
+
if not trails['Trails']:
|
|
68
|
+
self.findings.append({
|
|
69
|
+
'CheckId': self.check_id,
|
|
70
|
+
'Status': 'FAIL',
|
|
71
|
+
'Region': region,
|
|
72
|
+
"Severity": self.severity,
|
|
73
|
+
'Title': f"{self.check_id} {self.check_name}",
|
|
74
|
+
'Description': self.description,
|
|
75
|
+
'ResourceId': account_id,
|
|
76
|
+
'ResourceType': 'AWS::CloudTrail::Trail',
|
|
77
|
+
'AccountId': account_id,
|
|
78
|
+
'CheckedValue': 'Organization CloudTrail',
|
|
79
|
+
'ActualValue': 'No trails found',
|
|
80
|
+
'Remediation': 'Create an organization-level CloudTrail with KMS encryption enabled',
|
|
81
|
+
'Service': 'CloudTrail',
|
|
82
|
+
'CheckLogic': self.check_logic,
|
|
83
|
+
'CheckType': self.check_type
|
|
84
|
+
})
|
|
85
|
+
return self.findings
|
|
86
|
+
|
|
87
|
+
org_trail_found = False
|
|
88
|
+
|
|
89
|
+
for trail in trails['Trails']:
|
|
90
|
+
trail_name = trail['Name']
|
|
91
|
+
trail_arn = trail['TrailARN']
|
|
92
|
+
|
|
93
|
+
# Step 3: Identify organization trail
|
|
94
|
+
trail_info = cloudtrail_client.get_trail(Name=trail_name)['Trail']
|
|
95
|
+
|
|
96
|
+
if trail_info.get('IsOrganizationTrail'):
|
|
97
|
+
org_trail_found = True
|
|
98
|
+
|
|
99
|
+
# Step 4: Check if trail is enabled and logging
|
|
100
|
+
trail_status = cloudtrail_client.get_trail_status(Name=trail_name)
|
|
101
|
+
if not trail_status.get('IsLogging', False):
|
|
102
|
+
self.findings.append({
|
|
103
|
+
'CheckId': self.check_id,
|
|
104
|
+
'Status': 'FAIL',
|
|
105
|
+
'Region': region,
|
|
106
|
+
"Severity": self.severity,
|
|
107
|
+
'Title': f"{self.check_id} {self.check_name}",
|
|
108
|
+
'Description': self.description,
|
|
109
|
+
'ResourceId': trail_arn,
|
|
110
|
+
'ResourceType': 'AWS::CloudTrail::Trail',
|
|
111
|
+
'AccountId': account_id,
|
|
112
|
+
'CheckedValue': 'Trail Logging Status',
|
|
113
|
+
'ActualValue': 'Organization trail is not logging',
|
|
114
|
+
'Remediation': 'Enable logging for the organization trail',
|
|
115
|
+
'Service': 'CloudTrail',
|
|
116
|
+
'CheckLogic': self.check_logic,
|
|
117
|
+
'CheckType': self.check_type
|
|
118
|
+
})
|
|
119
|
+
continue
|
|
120
|
+
|
|
121
|
+
# Step 5: Verify KMS encryption is enabled on the trail
|
|
122
|
+
# Step 6: Validate KMS key exists and is accessible
|
|
123
|
+
if not trail_info.get('KmsKeyId'):
|
|
124
|
+
self.findings.append({
|
|
125
|
+
'CheckId': self.check_id,
|
|
126
|
+
'Status': 'FAIL',
|
|
127
|
+
'Region': region,
|
|
128
|
+
"Severity": self.severity,
|
|
129
|
+
'Title': f"{self.check_id} {self.check_name}",
|
|
130
|
+
'Description': self.description,
|
|
131
|
+
'ResourceId': trail_arn,
|
|
132
|
+
'ResourceType': 'AWS::CloudTrail::Trail',
|
|
133
|
+
'AccountId': account_id,
|
|
134
|
+
'CheckedValue': 'KMS Encryption',
|
|
135
|
+
'ActualValue': 'Organization trail is not KMS encrypted',
|
|
136
|
+
'Remediation': 'Enable KMS encryption for the organization trail',
|
|
137
|
+
'Service': 'CloudTrail',
|
|
138
|
+
'CheckLogic': self.check_logic,
|
|
139
|
+
'CheckType': self.check_type
|
|
140
|
+
})
|
|
141
|
+
else:
|
|
142
|
+
# Step 7: Check passes if organization trail uses KMS encryption
|
|
143
|
+
self.findings.append({
|
|
144
|
+
'CheckId': self.check_id,
|
|
145
|
+
'Status': 'PASS',
|
|
146
|
+
'Region': region,
|
|
147
|
+
"Severity": self.severity,
|
|
148
|
+
'Title': f"{self.check_id} {self.check_name}",
|
|
149
|
+
'Description': self.description,
|
|
150
|
+
'ResourceId': trail_arn,
|
|
151
|
+
'ResourceType': 'AWS::CloudTrail::Trail',
|
|
152
|
+
'AccountId': account_id,
|
|
153
|
+
'CheckedValue': 'KMS Encryption',
|
|
154
|
+
'ActualValue': f"Organization trail is encrypted with key: {trail_info['KmsKeyId']}",
|
|
155
|
+
'Remediation': 'None required',
|
|
156
|
+
'Service': 'CloudTrail',
|
|
157
|
+
'CheckLogic': self.check_logic,
|
|
158
|
+
'CheckType': self.check_type
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
if not org_trail_found:
|
|
162
|
+
self.findings.append({
|
|
163
|
+
'CheckId': self.check_id,
|
|
164
|
+
'Status': 'FAIL',
|
|
165
|
+
'Region': region,
|
|
166
|
+
"Severity": self.severity,
|
|
167
|
+
'Title': f"{self.check_id} {self.check_name}",
|
|
168
|
+
'Description': self.description,
|
|
169
|
+
'ResourceId': account_id,
|
|
170
|
+
'ResourceType': 'AWS::CloudTrail::Trail',
|
|
171
|
+
'AccountId': account_id,
|
|
172
|
+
'CheckedValue': 'Organization Trail',
|
|
173
|
+
'ActualValue': 'No organization trail found',
|
|
174
|
+
'Remediation': 'Create an organization-level CloudTrail with KMS encryption enabled',
|
|
175
|
+
'Service': 'CloudTrail',
|
|
176
|
+
'CheckLogic': self.check_logic,
|
|
177
|
+
'CheckType': self.check_type
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
except ClientError as e:
|
|
181
|
+
self.findings.append({
|
|
182
|
+
'CheckId': self.check_id,
|
|
183
|
+
'Status': 'ERROR',
|
|
184
|
+
'Region': region,
|
|
185
|
+
"Severity": self.severity,
|
|
186
|
+
'Title': f"{self.check_id} {self.check_name}",
|
|
187
|
+
'Description': self.description,
|
|
188
|
+
'ResourceId': account_id,
|
|
189
|
+
'ResourceType': 'AWS::CloudTrail::Trail',
|
|
190
|
+
'AccountId': account_id,
|
|
191
|
+
'CheckedValue': 'CloudTrail Access',
|
|
192
|
+
'ActualValue': f"Error: {str(e)}",
|
|
193
|
+
'Remediation': 'Check CloudTrail permissions and try again',
|
|
194
|
+
'Service': 'CloudTrail',
|
|
195
|
+
'CheckLogic': self.check_logic,
|
|
196
|
+
'CheckType': self.check_type
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
except Exception as e:
|
|
200
|
+
self.findings.append({
|
|
201
|
+
'CheckId': self.check_id,
|
|
202
|
+
'Status': 'ERROR',
|
|
203
|
+
'Region': region,
|
|
204
|
+
"Severity": self.severity,
|
|
205
|
+
'Title': f"{self.check_id} {self.check_name}",
|
|
206
|
+
'Description': self.description,
|
|
207
|
+
'ResourceId': account_id,
|
|
208
|
+
'ResourceType': 'AWS::CloudTrail::Trail',
|
|
209
|
+
'AccountId': account_id,
|
|
210
|
+
'CheckedValue': 'Check Execution',
|
|
211
|
+
'ActualValue': f"Error: {str(e)}",
|
|
212
|
+
'Remediation': 'Check permissions and try again',
|
|
213
|
+
'Service': 'CloudTrail',
|
|
214
|
+
'CheckLogic': self.check_logic,
|
|
215
|
+
'CheckType': self.check_type
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
return self.findings
|