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,242 @@
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 SRACT11(SecurityCheck):
7
+ """SRA-CT-11: Organization Trail Log Archive Configuration"""
8
+
9
+ def __init__(self, check_type="organization"):
10
+ """Initialize the check with organization type"""
11
+ super().__init__(check_type=check_type)
12
+ self.check_id = "SRA-CT-11"
13
+ self.check_name = "Organization trail Logs are delivered to a centralized S3 bucket in the Log Archive Account"
14
+ self.description = ('This check verifies whether the corresponding S3 buckets that stores organization trail logs in created in Log Archive account. '
15
+ 'This separates the management and usage of CloudTrail log privileges. The Log Archive account is dedicated to '
16
+ 'ingesting and archiving all security-related logs and backups.')
17
+ self.service = "CloudTrail"
18
+ self.severity = "HIGH"
19
+ self.check_type = check_type
20
+ self.check_logic = ('1. Verify execution from Organization Management Account | '
21
+ '2. Identify Log Archive account in the organization | '
22
+ '3. List CloudTrail trails and identify organization trails | '
23
+ '4. Verify S3 bucket is in Log Archive account | '
24
+ '5. Check passes if organization trail logs are delivered to Log Archive account S3 bucket')
25
+ self.logger = logging.getLogger(self.__class__.__name__)
26
+ self.findings = []
27
+
28
+ def get_findings(self) -> List[Dict[str, Any]]:
29
+ """Return the findings"""
30
+ return self.findings
31
+
32
+ def find_log_archive_account(self, organizations_client) -> str:
33
+ """Find the Log Archive account ID in the organization"""
34
+ try:
35
+ paginator = organizations_client.get_paginator('list_accounts')
36
+ for page in paginator.paginate():
37
+ for account in page['Accounts']:
38
+ # Check account name for "log" pattern (case insensitive)
39
+ if 'log' in account.get('Name', '').lower():
40
+ self.logger.debug(f"Found Log Archive account: {account['Name']} ({account['Id']})")
41
+ return account['Id']
42
+
43
+ # Check account tags as backup method
44
+ try:
45
+ tags = organizations_client.list_tags_for_resource(ResourceId=account['Id'])
46
+ for tag in tags.get('Tags', []):
47
+ if (tag.get('Key') == 'aws-control-tower' and
48
+ 'log' in tag.get('Value', '').lower()):
49
+ self.logger.debug(f"Found Log Archive account by tag: {account['Name']} ({account['Id']})")
50
+ return account['Id']
51
+ except ClientError:
52
+ continue
53
+
54
+ self.logger.error("No account found with 'log' in the name or tags")
55
+ return None
56
+
57
+ except ClientError as e:
58
+ self.logger.error(f"Error listing organization accounts: {str(e)}")
59
+ return None
60
+
61
+ def run(self, session) -> None:
62
+ """Run the security check"""
63
+ try:
64
+ # Get account information
65
+ sts_client = session.client('sts')
66
+ account_id = sts_client.get_caller_identity()['Account']
67
+ region = session.region_name
68
+ self.logger.debug(f"Running check for account: {account_id} in region: {region}")
69
+
70
+ # Step 1: Verify we're in management account using org_mgmt_checker
71
+ is_management, error_message = self.org_checker.verify_org_management()
72
+ if not is_management:
73
+ finding = {
74
+ 'CheckId': self.check_id,
75
+ 'Status': 'ERROR',
76
+ 'Region': region,
77
+ "Severity": self.severity,
78
+ 'Title': f"{self.check_id} {self.check_name}",
79
+ 'Description': self.description,
80
+ 'ResourceId': account_id,
81
+ 'ResourceType': 'AWS::Organizations::Account',
82
+ 'AccountId': account_id,
83
+ 'CheckedValue': 'Management Account Access',
84
+ 'ActualValue': error_message if error_message else 'Not running from management account',
85
+ 'Remediation': 'Run this check from the Organization Management Account',
86
+ 'Service': self.service,
87
+ 'CheckLogic': self.check_logic,
88
+ 'CheckType': self.check_type
89
+ }
90
+ self.findings.append(finding)
91
+ return self.findings
92
+
93
+ # Initialize clients
94
+ organizations_client = session.client('organizations')
95
+ cloudtrail_client = session.client('cloudtrail')
96
+
97
+ try:
98
+ # Find Log Archive account
99
+ log_archive_account = self.find_log_archive_account(organizations_client)
100
+ if not log_archive_account:
101
+ self.findings.append({
102
+ "CheckId": self.check_id,
103
+ "Status": "ERROR",
104
+ "Region": region,
105
+ "Severity": self.severity,
106
+ "Title": f"{self.check_id} {self.check_name}",
107
+ "Description": self.description,
108
+ "ResourceId": "log-archive-account",
109
+ "ResourceType": "AWS::Organizations::Account",
110
+ "AccountId": account_id,
111
+ "CheckedValue": "Log Archive Account Identification",
112
+ "ActualValue": "Could not identify Log Archive account",
113
+ "Remediation": "Verify Control Tower setup and Log Archive account existence",
114
+ "Service": self.service,
115
+ "CheckLogic": self.check_logic,
116
+ "CheckType": self.check_type
117
+ })
118
+ return
119
+
120
+ # List trails and find organization trails
121
+ trails = cloudtrail_client.describe_trails(includeShadowTrails=True)
122
+ org_trails = [t for t in trails['trailList'] if t.get('IsOrganizationTrail')]
123
+ self.logger.debug(f"Found {len(org_trails)} organization trails")
124
+
125
+ if not org_trails:
126
+ self.findings.append({
127
+ "CheckId": self.check_id,
128
+ "Status": "FAIL",
129
+ "Region": region,
130
+ "Severity": self.severity,
131
+ "Title": f"{self.check_id} {self.check_name}",
132
+ "Description": self.description,
133
+ "ResourceId": "organization-trail",
134
+ "ResourceType": "AWS::CloudTrail::Trail",
135
+ "AccountId": account_id,
136
+ "CheckedValue": "Organization Trail Configuration",
137
+ "ActualValue": "No organization trail found",
138
+ "Remediation": "Create an organization trail with S3 bucket in Log Archive account",
139
+ "Service": self.service,
140
+ "CheckLogic": self.check_logic,
141
+ "CheckType": self.check_type
142
+ })
143
+ return
144
+
145
+ # Check each organization trail for S3 bucket location
146
+ valid_trail = None
147
+ for trail in org_trails:
148
+ s3_bucket = trail.get('S3BucketName')
149
+ if not s3_bucket:
150
+ continue
151
+
152
+ # Check if the bucket name contains the Log Archive account ID
153
+ if f"aws-controltower-logs-{log_archive_account}" in s3_bucket:
154
+ valid_trail = trail
155
+ self.logger.debug(f"Found trail with S3 bucket in Log Archive account: {trail['Name']}")
156
+ break
157
+
158
+ # Create finding based on S3 bucket location
159
+ if valid_trail:
160
+ self.findings.append({
161
+ "CheckId": self.check_id,
162
+ "Status": "PASS",
163
+ "Region": region,
164
+ "Severity": self.severity,
165
+ "Title": f"{self.check_id} {self.check_name}",
166
+ "Description": self.description,
167
+ "ResourceId": valid_trail['TrailARN'],
168
+ "ResourceType": "AWS::CloudTrail::Trail",
169
+ "AccountId": account_id,
170
+ "CheckedValue": "S3 Bucket Location",
171
+ "ActualValue": f"Organization trail {valid_trail['Name']} delivers logs to S3 bucket {valid_trail['S3BucketName']} in Log Archive account ({log_archive_account})",
172
+ "Remediation": "None required",
173
+ "Service": self.service,
174
+ "CheckLogic": self.check_logic,
175
+ "CheckType": self.check_type
176
+ })
177
+ else:
178
+ actual_value = f"Trail S3 bucket is not in Log Archive account ({log_archive_account})"
179
+ remediation = "Configure organization trail to deliver logs to S3 bucket in Log Archive account"
180
+ if org_trails:
181
+ trail = org_trails[0]
182
+ if not trail.get('S3BucketName'):
183
+ actual_value = f"Trail {trail['Name']} has no S3 bucket configured"
184
+ remediation = "Configure S3 bucket in Log Archive account for the organization trail"
185
+
186
+ self.findings.append({
187
+ "CheckId": self.check_id,
188
+ "Status": "FAIL",
189
+ "Region": region,
190
+ "Severity": self.severity,
191
+ "Title": f"{self.check_id} {self.check_name}",
192
+ "Description": self.description,
193
+ "ResourceId": (valid_trail or org_trails[0])['TrailARN'],
194
+ "ResourceType": "AWS::CloudTrail::Trail",
195
+ "AccountId": account_id,
196
+ "CheckedValue": "S3 Bucket Location",
197
+ "ActualValue": actual_value,
198
+ "Remediation": remediation,
199
+ "Service": self.service,
200
+ "CheckLogic": self.check_logic,
201
+ "CheckType": self.check_type
202
+ })
203
+
204
+ except ClientError as e:
205
+ self.logger.error(f"Error accessing AWS services: {str(e)}")
206
+ self.findings.append({
207
+ "CheckId": self.check_id,
208
+ "Status": "ERROR",
209
+ "Region": region,
210
+ "Severity": self.severity,
211
+ "Title": f"{self.check_id} {self.check_name}",
212
+ "Description": self.description,
213
+ "ResourceId": "aws-services",
214
+ "ResourceType": "AWS::CloudTrail::Trail",
215
+ "AccountId": account_id,
216
+ "CheckedValue": "AWS Services Access",
217
+ "ActualValue": f"Error accessing AWS services: {str(e)}",
218
+ "Remediation": "Verify required permissions",
219
+ "Service": self.service,
220
+ "CheckLogic": self.check_logic,
221
+ "CheckType": self.check_type
222
+ })
223
+
224
+ except Exception as e:
225
+ self.logger.error(f"Unexpected error in check: {str(e)}")
226
+ self.findings.append({
227
+ "CheckId": self.check_id,
228
+ "Status": "ERROR",
229
+ "Region": region if 'region' in locals() else session.region_name,
230
+ "Severity": self.severity,
231
+ "Title": f"{self.check_id} {self.check_name}",
232
+ "Description": self.description,
233
+ "ResourceId": "check-execution",
234
+ "ResourceType": "AWS::CloudTrail::Trail",
235
+ "AccountId": account_id if 'account_id' in locals() else "unknown",
236
+ "CheckedValue": "Check Execution",
237
+ "ActualValue": f"Unexpected error: {str(e)}",
238
+ "Remediation": "Contact support team",
239
+ "Service": self.service,
240
+ "CheckLogic": self.check_logic,
241
+ "CheckType": self.check_type
242
+ })
@@ -0,0 +1,163 @@
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 SRACT12(SecurityCheck):
7
+ """SRA-CT-12: CloudTrail Delegated Administrator Configuration"""
8
+
9
+ def __init__(self, check_type="organization"):
10
+ """Initialize the check with organization type"""
11
+ super().__init__(check_type=check_type)
12
+ self.check_id = "SRA-CT-12"
13
+ self.check_name = "Delegated Administrator set for CloudTrail"
14
+ self.description = ('This check verifies whether CloudTrail service administration for the AWS Organization '
15
+ 'is delegated out to AWS Organization management account. The delegated administrator has '
16
+ 'permissions to create and manage analyzers with the AWS organization as the zone of trust.')
17
+ self.service = "CloudTrail"
18
+ self.severity = "MEDIUM"
19
+ self.check_type = check_type
20
+ self.check_logic = ('1. Verify execution from Organization Management Account | '
21
+ '2. Get management account ID from Organizations | '
22
+ '3. List CloudTrail delegated administrators | '
23
+ '4. Check passes if at least one delegated administrator is configured')
24
+ self.logger = logging.getLogger(self.__class__.__name__)
25
+ self.findings = []
26
+
27
+ def get_findings(self) -> List[Dict[str, Any]]:
28
+ """Return the findings"""
29
+ return self.findings
30
+
31
+ def run(self, session) -> None:
32
+ """Run the security check"""
33
+ try:
34
+ # Get account information
35
+ sts_client = session.client('sts')
36
+ account_id = sts_client.get_caller_identity()['Account']
37
+ region = session.region_name
38
+ self.logger.debug(f"Running check for account: {account_id} in region: {region}")
39
+
40
+ # Step 1: Verify execution from Organization Management Account
41
+ is_management, error_message = self.org_checker.verify_org_management()
42
+ if not is_management:
43
+ self.findings.append({
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
+ return self.findings
61
+
62
+ try:
63
+ # Step 2: Get management account ID from Organizations
64
+ organizations_client = session.client('organizations')
65
+
66
+ # Step 3: List CloudTrail delegated administrators
67
+ try:
68
+ delegated_admins = organizations_client.list_delegated_administrators(
69
+ ServicePrincipal='cloudtrail.amazonaws.com'
70
+ )
71
+ admin_accounts = delegated_admins.get('DelegatedAdministrators', [])
72
+
73
+ # Step 4: Check if at least one delegated administrator is configured
74
+ if admin_accounts:
75
+ # Format account list for actual value
76
+ admin_list = ', '.join([f"{admin['Id']}" for admin in admin_accounts])
77
+ status = "PASS"
78
+ actual_value = f"CloudTrail delegated administrators configured: {admin_list}"
79
+ remediation = "None required"
80
+ else:
81
+ status = "FAIL"
82
+ actual_value = "No CloudTrail delegated administrators configured"
83
+ remediation = "Configure at least one delegated administrator for CloudTrail using the CloudTrail console or AWS CLI"
84
+
85
+ self.findings.append({
86
+ "CheckId": self.check_id,
87
+ "Status": status,
88
+ "Region": region,
89
+ "Severity": self.severity,
90
+ "Title": f"{self.check_id} {self.check_name}",
91
+ "Description": self.description,
92
+ "ResourceId": "cloudtrail-delegated-admin",
93
+ "ResourceType": "AWS::Organizations::Account",
94
+ "AccountId": account_id,
95
+ "CheckedValue": "Delegated Administrator Configuration",
96
+ "ActualValue": actual_value,
97
+ "Remediation": remediation,
98
+ "Service": self.service,
99
+ "CheckLogic": self.check_logic,
100
+ "CheckType": self.check_type
101
+ })
102
+
103
+ except ClientError as e:
104
+ if 'AccessDeniedException' in str(e):
105
+ self.findings.append({
106
+ "CheckId": self.check_id,
107
+ "Status": "ERROR",
108
+ "Region": region,
109
+ "Severity": self.severity,
110
+ "Title": f"{self.check_id} {self.check_name}",
111
+ "Description": self.description,
112
+ "ResourceId": "organizations-permissions",
113
+ "ResourceType": "AWS::Organizations::Account",
114
+ "AccountId": account_id,
115
+ "CheckedValue": "Organizations Permissions",
116
+ "ActualValue": "Insufficient permissions to list delegated administrators",
117
+ "Remediation": "Verify Organizations permissions in management account",
118
+ "Service": self.service,
119
+ "CheckLogic": self.check_logic,
120
+ "CheckType": self.check_type
121
+ })
122
+ else:
123
+ raise e
124
+
125
+ except ClientError as e:
126
+ self.logger.error(f"Error accessing Organizations: {str(e)}")
127
+ self.findings.append({
128
+ "CheckId": self.check_id,
129
+ "Status": "ERROR",
130
+ "Region": region,
131
+ "Severity": self.severity,
132
+ "Title": f"{self.check_id} {self.check_name}",
133
+ "Description": self.description,
134
+ "ResourceId": "organizations",
135
+ "ResourceType": "AWS::Organizations::Account",
136
+ "AccountId": account_id,
137
+ "CheckedValue": "Organizations API Access",
138
+ "ActualValue": f"Error accessing Organizations: {str(e)}",
139
+ "Remediation": "Verify Organizations permissions",
140
+ "Service": self.service,
141
+ "CheckLogic": self.check_logic,
142
+ "CheckType": self.check_type
143
+ })
144
+
145
+ except Exception as e:
146
+ self.logger.error(f"Unexpected error in check: {str(e)}")
147
+ self.findings.append({
148
+ "CheckId": self.check_id,
149
+ "Status": "ERROR",
150
+ "Region": region if 'region' in locals() else session.region_name,
151
+ "Severity": self.severity,
152
+ "Title": f"{self.check_id} {self.check_name}",
153
+ "Description": self.description,
154
+ "ResourceId": "check-execution",
155
+ "ResourceType": "AWS::CloudTrail::Trail",
156
+ "AccountId": account_id if 'account_id' in locals() else "unknown",
157
+ "CheckedValue": "Check Execution",
158
+ "ActualValue": f"Unexpected error: {str(e)}",
159
+ "Remediation": "Contact support team",
160
+ "Service": self.service,
161
+ "CheckLogic": self.check_logic,
162
+ "CheckType": self.check_type
163
+ })