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