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.
Files changed (261) hide show
  1. sraverify/__init__.py +36 -0
  2. sraverify/checks/__init__.py +56 -0
  3. sraverify/checks/accessanalyzer/SRA_IAA_1.py +188 -0
  4. sraverify/checks/accessanalyzer/SRA_IAA_2.py +162 -0
  5. sraverify/checks/accessanalyzer/SRA_IAA_3.py +260 -0
  6. sraverify/checks/accessanalyzer/SRA_IAA_4.py +207 -0
  7. sraverify/checks/accessanalyzer/__init__.py +3 -0
  8. sraverify/checks/cloudtrail/SRA-CT-1.py +220 -0
  9. sraverify/checks/cloudtrail/SRA-CT-10.py +229 -0
  10. sraverify/checks/cloudtrail/SRA-CT-11.py +242 -0
  11. sraverify/checks/cloudtrail/SRA-CT-12.py +163 -0
  12. sraverify/checks/cloudtrail/SRA-CT-13.py +279 -0
  13. sraverify/checks/cloudtrail/SRA-CT-2.py +218 -0
  14. sraverify/checks/cloudtrail/SRA-CT-3.py +196 -0
  15. sraverify/checks/cloudtrail/SRA-CT-4.py +161 -0
  16. sraverify/checks/cloudtrail/SRA-CT-5.py +200 -0
  17. sraverify/checks/cloudtrail/SRA-CT-6.py +161 -0
  18. sraverify/checks/cloudtrail/SRA-CT-7.py +194 -0
  19. sraverify/checks/cloudtrail/SRA-CT-8.py +226 -0
  20. sraverify/checks/cloudtrail/SRA-CT-9.py +226 -0
  21. sraverify/checks/cloudtrail/__init__.py +3 -0
  22. sraverify/checks/config/SRA-CONFIG-1.py +197 -0
  23. sraverify/checks/config/__init__.py +3 -0
  24. sraverify/core/__init__.py +3 -0
  25. sraverify/core/check.py +227 -0
  26. sraverify/core/logging.py +37 -0
  27. sraverify/core/session.py +47 -0
  28. sraverify/lib/__init__.py +4 -0
  29. sraverify/lib/audit_info.py +37 -0
  30. sraverify/lib/banner.py +42 -0
  31. sraverify/lib/check_loader.py +80 -0
  32. sraverify/lib/org_mgmt_checker.py +86 -0
  33. sraverify/lib/outputs.py +46 -0
  34. sraverify/lib/progress.py +75 -0
  35. sraverify/lib/regions.py +27 -0
  36. sraverify/lib/session.py +23 -0
  37. sraverify/main.py +350 -0
  38. sraverify/services/__init__.py +3 -0
  39. sraverify/services/accessanalyzer/__init__.py +15 -0
  40. sraverify/services/accessanalyzer/base.py +123 -0
  41. sraverify/services/accessanalyzer/checks/__init__.py +3 -0
  42. sraverify/services/accessanalyzer/checks/sra_accessanalyzer_01.py +82 -0
  43. sraverify/services/accessanalyzer/checks/sra_accessanalyzer_02.py +82 -0
  44. sraverify/services/accessanalyzer/checks/sra_accessanalyzer_03.py +103 -0
  45. sraverify/services/accessanalyzer/checks/sra_accessanalyzer_04.py +139 -0
  46. sraverify/services/accessanalyzer/client.py +123 -0
  47. sraverify/services/account/__init__.py +9 -0
  48. sraverify/services/account/base.py +56 -0
  49. sraverify/services/account/checks/__init__.py +1 -0
  50. sraverify/services/account/checks/sra_account_01.py +65 -0
  51. sraverify/services/account/checks/sra_account_02.py +63 -0
  52. sraverify/services/account/checks/sra_account_03.py +63 -0
  53. sraverify/services/account/client.py +51 -0
  54. sraverify/services/auditmanager/__init__.py +10 -0
  55. sraverify/services/auditmanager/base.py +72 -0
  56. sraverify/services/auditmanager/checks/__init__.py +1 -0
  57. sraverify/services/auditmanager/checks/sra_auditmanager_01.py +58 -0
  58. sraverify/services/auditmanager/checks/sra_auditmanager_02.py +80 -0
  59. sraverify/services/auditmanager/client.py +58 -0
  60. sraverify/services/cloudtrail/__init__.py +33 -0
  61. sraverify/services/cloudtrail/base.py +167 -0
  62. sraverify/services/cloudtrail/checks/__init__.py +1 -0
  63. sraverify/services/cloudtrail/checks/sra_cloudtrail_01.py +83 -0
  64. sraverify/services/cloudtrail/checks/sra_cloudtrail_02.py +99 -0
  65. sraverify/services/cloudtrail/checks/sra_cloudtrail_03.py +94 -0
  66. sraverify/services/cloudtrail/checks/sra_cloudtrail_04.py +92 -0
  67. sraverify/services/cloudtrail/checks/sra_cloudtrail_05.py +106 -0
  68. sraverify/services/cloudtrail/checks/sra_cloudtrail_06.py +93 -0
  69. sraverify/services/cloudtrail/checks/sra_cloudtrail_07.py +96 -0
  70. sraverify/services/cloudtrail/checks/sra_cloudtrail_08.py +145 -0
  71. sraverify/services/cloudtrail/checks/sra_cloudtrail_09.py +167 -0
  72. sraverify/services/cloudtrail/checks/sra_cloudtrail_10.py +162 -0
  73. sraverify/services/cloudtrail/checks/sra_cloudtrail_11.py +178 -0
  74. sraverify/services/cloudtrail/checks/sra_cloudtrail_12.py +77 -0
  75. sraverify/services/cloudtrail/checks/sra_cloudtrail_13.py +120 -0
  76. sraverify/services/cloudtrail/client.py +118 -0
  77. sraverify/services/config/__init__.py +25 -0
  78. sraverify/services/config/base.py +249 -0
  79. sraverify/services/config/checks/__init__.py +1 -0
  80. sraverify/services/config/checks/sra_config_01.py +123 -0
  81. sraverify/services/config/checks/sra_config_02.py +156 -0
  82. sraverify/services/config/checks/sra_config_03.py +149 -0
  83. sraverify/services/config/checks/sra_config_04.py +104 -0
  84. sraverify/services/config/checks/sra_config_05.py +104 -0
  85. sraverify/services/config/checks/sra_config_06.py +194 -0
  86. sraverify/services/config/checks/sra_config_07.py +162 -0
  87. sraverify/services/config/checks/sra_config_08.py +185 -0
  88. sraverify/services/config/checks/sra_config_09.py +177 -0
  89. sraverify/services/config/client.py +264 -0
  90. sraverify/services/ec2/__init__.py +8 -0
  91. sraverify/services/ec2/base.py +75 -0
  92. sraverify/services/ec2/checks/__init__.py +1 -0
  93. sraverify/services/ec2/checks/sra_ec2_01.py +83 -0
  94. sraverify/services/ec2/client.py +63 -0
  95. sraverify/services/firewallmanager/__init__.py +23 -0
  96. sraverify/services/firewallmanager/base.py +48 -0
  97. sraverify/services/firewallmanager/checks/__init__.py +1 -0
  98. sraverify/services/firewallmanager/checks/sra_firewallmanager_01.py +75 -0
  99. sraverify/services/firewallmanager/checks/sra_firewallmanager_02.py +57 -0
  100. sraverify/services/firewallmanager/checks/sra_firewallmanager_03.py +51 -0
  101. sraverify/services/firewallmanager/checks/sra_firewallmanager_04.py +51 -0
  102. sraverify/services/firewallmanager/checks/sra_firewallmanager_05.py +51 -0
  103. sraverify/services/firewallmanager/checks/sra_firewallmanager_06.py +51 -0
  104. sraverify/services/firewallmanager/checks/sra_firewallmanager_07.py +51 -0
  105. sraverify/services/firewallmanager/checks/sra_firewallmanager_08.py +61 -0
  106. sraverify/services/firewallmanager/checks/sra_firewallmanager_09.py +61 -0
  107. sraverify/services/firewallmanager/checks/sra_firewallmanager_10.py +71 -0
  108. sraverify/services/firewallmanager/client.py +40 -0
  109. sraverify/services/guardduty/__init__.py +58 -0
  110. sraverify/services/guardduty/base.py +207 -0
  111. sraverify/services/guardduty/checks/__init__.py +3 -0
  112. sraverify/services/guardduty/checks/sra_guardduty_01.py +51 -0
  113. sraverify/services/guardduty/checks/sra_guardduty_02.py +80 -0
  114. sraverify/services/guardduty/checks/sra_guardduty_03.py +77 -0
  115. sraverify/services/guardduty/checks/sra_guardduty_04.py +84 -0
  116. sraverify/services/guardduty/checks/sra_guardduty_05.py +84 -0
  117. sraverify/services/guardduty/checks/sra_guardduty_06.py +84 -0
  118. sraverify/services/guardduty/checks/sra_guardduty_07.py +85 -0
  119. sraverify/services/guardduty/checks/sra_guardduty_08.py +83 -0
  120. sraverify/services/guardduty/checks/sra_guardduty_09.py +84 -0
  121. sraverify/services/guardduty/checks/sra_guardduty_10.py +83 -0
  122. sraverify/services/guardduty/checks/sra_guardduty_11.py +93 -0
  123. sraverify/services/guardduty/checks/sra_guardduty_12.py +83 -0
  124. sraverify/services/guardduty/checks/sra_guardduty_13.py +90 -0
  125. sraverify/services/guardduty/checks/sra_guardduty_14.py +136 -0
  126. sraverify/services/guardduty/checks/sra_guardduty_15.py +94 -0
  127. sraverify/services/guardduty/checks/sra_guardduty_16.py +94 -0
  128. sraverify/services/guardduty/checks/sra_guardduty_17.py +91 -0
  129. sraverify/services/guardduty/checks/sra_guardduty_18.py +91 -0
  130. sraverify/services/guardduty/checks/sra_guardduty_19.py +91 -0
  131. sraverify/services/guardduty/checks/sra_guardduty_20.py +111 -0
  132. sraverify/services/guardduty/checks/sra_guardduty_21.py +112 -0
  133. sraverify/services/guardduty/checks/sra_guardduty_22.py +111 -0
  134. sraverify/services/guardduty/checks/sra_guardduty_23.py +154 -0
  135. sraverify/services/guardduty/checks/sra_guardduty_24.py +111 -0
  136. sraverify/services/guardduty/checks/sra_guardduty_25.py +111 -0
  137. sraverify/services/guardduty/client.py +107 -0
  138. sraverify/services/inspector/__init__.py +29 -0
  139. sraverify/services/inspector/base.py +233 -0
  140. sraverify/services/inspector/checks/__init__.py +3 -0
  141. sraverify/services/inspector/checks/sra_inspector_01.py +69 -0
  142. sraverify/services/inspector/checks/sra_inspector_02.py +68 -0
  143. sraverify/services/inspector/checks/sra_inspector_03.py +68 -0
  144. sraverify/services/inspector/checks/sra_inspector_04.py +70 -0
  145. sraverify/services/inspector/checks/sra_inspector_05.py +69 -0
  146. sraverify/services/inspector/checks/sra_inspector_06.py +115 -0
  147. sraverify/services/inspector/checks/sra_inspector_07.py +109 -0
  148. sraverify/services/inspector/checks/sra_inspector_08.py +69 -0
  149. sraverify/services/inspector/checks/sra_inspector_09.py +69 -0
  150. sraverify/services/inspector/checks/sra_inspector_10.py +69 -0
  151. sraverify/services/inspector/checks/sra_inspector_11.py +69 -0
  152. sraverify/services/inspector/client.py +99 -0
  153. sraverify/services/macie/__init__.py +27 -0
  154. sraverify/services/macie/base.py +271 -0
  155. sraverify/services/macie/checks/__init__.py +1 -0
  156. sraverify/services/macie/checks/sra_macie_01.py +100 -0
  157. sraverify/services/macie/checks/sra_macie_02.py +102 -0
  158. sraverify/services/macie/checks/sra_macie_03.py +152 -0
  159. sraverify/services/macie/checks/sra_macie_04.py +120 -0
  160. sraverify/services/macie/checks/sra_macie_05.py +85 -0
  161. sraverify/services/macie/checks/sra_macie_06.py +124 -0
  162. sraverify/services/macie/checks/sra_macie_07.py +138 -0
  163. sraverify/services/macie/checks/sra_macie_08.py +82 -0
  164. sraverify/services/macie/checks/sra_macie_09.py +103 -0
  165. sraverify/services/macie/checks/sra_macie_10.py +81 -0
  166. sraverify/services/macie/client.py +220 -0
  167. sraverify/services/s3/__init__.py +16 -0
  168. sraverify/services/s3/base.py +69 -0
  169. sraverify/services/s3/checks/__init__.py +1 -0
  170. sraverify/services/s3/checks/sra_s3_01.py +89 -0
  171. sraverify/services/s3/checks/sra_s3_02.py +89 -0
  172. sraverify/services/s3/checks/sra_s3_03.py +88 -0
  173. sraverify/services/s3/checks/sra_s3_04.py +88 -0
  174. sraverify/services/s3/client.py +52 -0
  175. sraverify/services/securityhub/__init__.py +27 -0
  176. sraverify/services/securityhub/base.py +349 -0
  177. sraverify/services/securityhub/checks/__init__.py +1 -0
  178. sraverify/services/securityhub/checks/sra_securityhub_01.py +115 -0
  179. sraverify/services/securityhub/checks/sra_securityhub_02.py +114 -0
  180. sraverify/services/securityhub/checks/sra_securityhub_03.py +136 -0
  181. sraverify/services/securityhub/checks/sra_securityhub_04.py +75 -0
  182. sraverify/services/securityhub/checks/sra_securityhub_05.py +102 -0
  183. sraverify/services/securityhub/checks/sra_securityhub_06.py +113 -0
  184. sraverify/services/securityhub/checks/sra_securityhub_07.py +121 -0
  185. sraverify/services/securityhub/checks/sra_securityhub_08.py +113 -0
  186. sraverify/services/securityhub/checks/sra_securityhub_09.py +100 -0
  187. sraverify/services/securityhub/checks/sra_securityhub_10.py +94 -0
  188. sraverify/services/securityhub/checks/sra_securityhub_11.py +73 -0
  189. sraverify/services/securityhub/client.py +249 -0
  190. sraverify/services/securityincidentresponse/__init__.py +13 -0
  191. sraverify/services/securityincidentresponse/base.py +95 -0
  192. sraverify/services/securityincidentresponse/checks/__init__.py +1 -0
  193. sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_01.py +77 -0
  194. sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_02.py +72 -0
  195. sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_03.py +86 -0
  196. sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_04.py +117 -0
  197. sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_05.py +55 -0
  198. sraverify/services/securityincidentresponse/client.py +71 -0
  199. sraverify/services/securitylake/__init__.py +39 -0
  200. sraverify/services/securitylake/base.py +461 -0
  201. sraverify/services/securitylake/checks/__init__.py +1 -0
  202. sraverify/services/securitylake/checks/sra_securitylake_01.py +98 -0
  203. sraverify/services/securitylake/checks/sra_securitylake_02.py +133 -0
  204. sraverify/services/securitylake/checks/sra_securitylake_03.py +116 -0
  205. sraverify/services/securitylake/checks/sra_securitylake_04.py +72 -0
  206. sraverify/services/securitylake/checks/sra_securitylake_05.py +116 -0
  207. sraverify/services/securitylake/checks/sra_securitylake_06.py +104 -0
  208. sraverify/services/securitylake/checks/sra_securitylake_07.py +108 -0
  209. sraverify/services/securitylake/checks/sra_securitylake_08.py +107 -0
  210. sraverify/services/securitylake/checks/sra_securitylake_09.py +107 -0
  211. sraverify/services/securitylake/checks/sra_securitylake_10.py +106 -0
  212. sraverify/services/securitylake/checks/sra_securitylake_11.py +109 -0
  213. sraverify/services/securitylake/checks/sra_securitylake_12.py +108 -0
  214. sraverify/services/securitylake/checks/sra_securitylake_13.py +108 -0
  215. sraverify/services/securitylake/checks/sra_securitylake_14.py +72 -0
  216. sraverify/services/securitylake/checks/sra_securitylake_15.py +120 -0
  217. sraverify/services/securitylake/checks/sra_securitylake_16.py +104 -0
  218. sraverify/services/securitylake/checks/sra_securitylake_17.py +103 -0
  219. sraverify/services/securitylake/client.py +247 -0
  220. sraverify/services/shield/__init__.py +33 -0
  221. sraverify/services/shield/base.py +199 -0
  222. sraverify/services/shield/checks/__init__.py +1 -0
  223. sraverify/services/shield/checks/sra_shield_01.py +68 -0
  224. sraverify/services/shield/checks/sra_shield_02.py +77 -0
  225. sraverify/services/shield/checks/sra_shield_03.py +84 -0
  226. sraverify/services/shield/checks/sra_shield_04.py +84 -0
  227. sraverify/services/shield/checks/sra_shield_05.py +84 -0
  228. sraverify/services/shield/checks/sra_shield_06.py +84 -0
  229. sraverify/services/shield/checks/sra_shield_07.py +84 -0
  230. sraverify/services/shield/checks/sra_shield_08.py +69 -0
  231. sraverify/services/shield/checks/sra_shield_09.py +86 -0
  232. sraverify/services/shield/checks/sra_shield_10.py +100 -0
  233. sraverify/services/shield/checks/sra_shield_11.py +71 -0
  234. sraverify/services/shield/checks/sra_shield_12.py +130 -0
  235. sraverify/services/shield/checks/sra_shield_13.py +112 -0
  236. sraverify/services/shield/checks/sra_shield_14.py +111 -0
  237. sraverify/services/shield/client.py +214 -0
  238. sraverify/services/waf/__init__.py +21 -0
  239. sraverify/services/waf/base.py +100 -0
  240. sraverify/services/waf/checks/__init__.py +1 -0
  241. sraverify/services/waf/checks/sra_waf_01.py +63 -0
  242. sraverify/services/waf/checks/sra_waf_02.py +82 -0
  243. sraverify/services/waf/checks/sra_waf_03.py +123 -0
  244. sraverify/services/waf/checks/sra_waf_04.py +94 -0
  245. sraverify/services/waf/checks/sra_waf_05.py +94 -0
  246. sraverify/services/waf/checks/sra_waf_06.py +91 -0
  247. sraverify/services/waf/checks/sra_waf_07.py +94 -0
  248. sraverify/services/waf/checks/sra_waf_08.py +66 -0
  249. sraverify/services/waf/checks/sra_waf_09.py +95 -0
  250. sraverify/services/waf/client.py +109 -0
  251. sraverify/utils/__init__.py +3 -0
  252. sraverify/utils/banner.py +65 -0
  253. sraverify/utils/outputs.py +57 -0
  254. sraverify/utils/progress.py +97 -0
  255. sraverify-0.1.0.dist-info/LICENSE +175 -0
  256. sraverify-0.1.0.dist-info/METADATA +516 -0
  257. sraverify-0.1.0.dist-info/NOTICE +1 -0
  258. sraverify-0.1.0.dist-info/RECORD +261 -0
  259. sraverify-0.1.0.dist-info/WHEEL +5 -0
  260. sraverify-0.1.0.dist-info/entry_points.txt +2 -0
  261. 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