sraverify 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sraverify/__init__.py +36 -0
- sraverify/checks/__init__.py +56 -0
- sraverify/checks/accessanalyzer/SRA_IAA_1.py +188 -0
- sraverify/checks/accessanalyzer/SRA_IAA_2.py +162 -0
- sraverify/checks/accessanalyzer/SRA_IAA_3.py +260 -0
- sraverify/checks/accessanalyzer/SRA_IAA_4.py +207 -0
- sraverify/checks/accessanalyzer/__init__.py +3 -0
- sraverify/checks/cloudtrail/SRA-CT-1.py +220 -0
- sraverify/checks/cloudtrail/SRA-CT-10.py +229 -0
- sraverify/checks/cloudtrail/SRA-CT-11.py +242 -0
- sraverify/checks/cloudtrail/SRA-CT-12.py +163 -0
- sraverify/checks/cloudtrail/SRA-CT-13.py +279 -0
- sraverify/checks/cloudtrail/SRA-CT-2.py +218 -0
- sraverify/checks/cloudtrail/SRA-CT-3.py +196 -0
- sraverify/checks/cloudtrail/SRA-CT-4.py +161 -0
- sraverify/checks/cloudtrail/SRA-CT-5.py +200 -0
- sraverify/checks/cloudtrail/SRA-CT-6.py +161 -0
- sraverify/checks/cloudtrail/SRA-CT-7.py +194 -0
- sraverify/checks/cloudtrail/SRA-CT-8.py +226 -0
- sraverify/checks/cloudtrail/SRA-CT-9.py +226 -0
- sraverify/checks/cloudtrail/__init__.py +3 -0
- sraverify/checks/config/SRA-CONFIG-1.py +197 -0
- sraverify/checks/config/__init__.py +3 -0
- sraverify/core/__init__.py +3 -0
- sraverify/core/check.py +227 -0
- sraverify/core/logging.py +37 -0
- sraverify/core/session.py +47 -0
- sraverify/lib/__init__.py +4 -0
- sraverify/lib/audit_info.py +37 -0
- sraverify/lib/banner.py +42 -0
- sraverify/lib/check_loader.py +80 -0
- sraverify/lib/org_mgmt_checker.py +86 -0
- sraverify/lib/outputs.py +46 -0
- sraverify/lib/progress.py +75 -0
- sraverify/lib/regions.py +27 -0
- sraverify/lib/session.py +23 -0
- sraverify/main.py +350 -0
- sraverify/services/__init__.py +3 -0
- sraverify/services/accessanalyzer/__init__.py +15 -0
- sraverify/services/accessanalyzer/base.py +123 -0
- sraverify/services/accessanalyzer/checks/__init__.py +3 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_01.py +82 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_02.py +82 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_03.py +103 -0
- sraverify/services/accessanalyzer/checks/sra_accessanalyzer_04.py +139 -0
- sraverify/services/accessanalyzer/client.py +123 -0
- sraverify/services/account/__init__.py +9 -0
- sraverify/services/account/base.py +56 -0
- sraverify/services/account/checks/__init__.py +1 -0
- sraverify/services/account/checks/sra_account_01.py +65 -0
- sraverify/services/account/checks/sra_account_02.py +63 -0
- sraverify/services/account/checks/sra_account_03.py +63 -0
- sraverify/services/account/client.py +51 -0
- sraverify/services/auditmanager/__init__.py +10 -0
- sraverify/services/auditmanager/base.py +72 -0
- sraverify/services/auditmanager/checks/__init__.py +1 -0
- sraverify/services/auditmanager/checks/sra_auditmanager_01.py +58 -0
- sraverify/services/auditmanager/checks/sra_auditmanager_02.py +80 -0
- sraverify/services/auditmanager/client.py +58 -0
- sraverify/services/cloudtrail/__init__.py +33 -0
- sraverify/services/cloudtrail/base.py +167 -0
- sraverify/services/cloudtrail/checks/__init__.py +1 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_01.py +83 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_02.py +99 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_03.py +94 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_04.py +92 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_05.py +106 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_06.py +93 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_07.py +96 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_08.py +145 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_09.py +167 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_10.py +162 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_11.py +178 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_12.py +77 -0
- sraverify/services/cloudtrail/checks/sra_cloudtrail_13.py +120 -0
- sraverify/services/cloudtrail/client.py +118 -0
- sraverify/services/config/__init__.py +25 -0
- sraverify/services/config/base.py +249 -0
- sraverify/services/config/checks/__init__.py +1 -0
- sraverify/services/config/checks/sra_config_01.py +123 -0
- sraverify/services/config/checks/sra_config_02.py +156 -0
- sraverify/services/config/checks/sra_config_03.py +149 -0
- sraverify/services/config/checks/sra_config_04.py +104 -0
- sraverify/services/config/checks/sra_config_05.py +104 -0
- sraverify/services/config/checks/sra_config_06.py +194 -0
- sraverify/services/config/checks/sra_config_07.py +162 -0
- sraverify/services/config/checks/sra_config_08.py +185 -0
- sraverify/services/config/checks/sra_config_09.py +177 -0
- sraverify/services/config/client.py +264 -0
- sraverify/services/ec2/__init__.py +8 -0
- sraverify/services/ec2/base.py +75 -0
- sraverify/services/ec2/checks/__init__.py +1 -0
- sraverify/services/ec2/checks/sra_ec2_01.py +83 -0
- sraverify/services/ec2/client.py +63 -0
- sraverify/services/firewallmanager/__init__.py +23 -0
- sraverify/services/firewallmanager/base.py +48 -0
- sraverify/services/firewallmanager/checks/__init__.py +1 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_01.py +75 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_02.py +57 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_03.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_04.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_05.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_06.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_07.py +51 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_08.py +61 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_09.py +61 -0
- sraverify/services/firewallmanager/checks/sra_firewallmanager_10.py +71 -0
- sraverify/services/firewallmanager/client.py +40 -0
- sraverify/services/guardduty/__init__.py +58 -0
- sraverify/services/guardduty/base.py +207 -0
- sraverify/services/guardduty/checks/__init__.py +3 -0
- sraverify/services/guardduty/checks/sra_guardduty_01.py +51 -0
- sraverify/services/guardduty/checks/sra_guardduty_02.py +80 -0
- sraverify/services/guardduty/checks/sra_guardduty_03.py +77 -0
- sraverify/services/guardduty/checks/sra_guardduty_04.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_05.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_06.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_07.py +85 -0
- sraverify/services/guardduty/checks/sra_guardduty_08.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_09.py +84 -0
- sraverify/services/guardduty/checks/sra_guardduty_10.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_11.py +93 -0
- sraverify/services/guardduty/checks/sra_guardduty_12.py +83 -0
- sraverify/services/guardduty/checks/sra_guardduty_13.py +90 -0
- sraverify/services/guardduty/checks/sra_guardduty_14.py +136 -0
- sraverify/services/guardduty/checks/sra_guardduty_15.py +94 -0
- sraverify/services/guardduty/checks/sra_guardduty_16.py +94 -0
- sraverify/services/guardduty/checks/sra_guardduty_17.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_18.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_19.py +91 -0
- sraverify/services/guardduty/checks/sra_guardduty_20.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_21.py +112 -0
- sraverify/services/guardduty/checks/sra_guardduty_22.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_23.py +154 -0
- sraverify/services/guardduty/checks/sra_guardduty_24.py +111 -0
- sraverify/services/guardduty/checks/sra_guardduty_25.py +111 -0
- sraverify/services/guardduty/client.py +107 -0
- sraverify/services/inspector/__init__.py +29 -0
- sraverify/services/inspector/base.py +233 -0
- sraverify/services/inspector/checks/__init__.py +3 -0
- sraverify/services/inspector/checks/sra_inspector_01.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_02.py +68 -0
- sraverify/services/inspector/checks/sra_inspector_03.py +68 -0
- sraverify/services/inspector/checks/sra_inspector_04.py +70 -0
- sraverify/services/inspector/checks/sra_inspector_05.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_06.py +115 -0
- sraverify/services/inspector/checks/sra_inspector_07.py +109 -0
- sraverify/services/inspector/checks/sra_inspector_08.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_09.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_10.py +69 -0
- sraverify/services/inspector/checks/sra_inspector_11.py +69 -0
- sraverify/services/inspector/client.py +99 -0
- sraverify/services/macie/__init__.py +27 -0
- sraverify/services/macie/base.py +271 -0
- sraverify/services/macie/checks/__init__.py +1 -0
- sraverify/services/macie/checks/sra_macie_01.py +100 -0
- sraverify/services/macie/checks/sra_macie_02.py +102 -0
- sraverify/services/macie/checks/sra_macie_03.py +152 -0
- sraverify/services/macie/checks/sra_macie_04.py +120 -0
- sraverify/services/macie/checks/sra_macie_05.py +85 -0
- sraverify/services/macie/checks/sra_macie_06.py +124 -0
- sraverify/services/macie/checks/sra_macie_07.py +138 -0
- sraverify/services/macie/checks/sra_macie_08.py +82 -0
- sraverify/services/macie/checks/sra_macie_09.py +103 -0
- sraverify/services/macie/checks/sra_macie_10.py +81 -0
- sraverify/services/macie/client.py +220 -0
- sraverify/services/s3/__init__.py +16 -0
- sraverify/services/s3/base.py +69 -0
- sraverify/services/s3/checks/__init__.py +1 -0
- sraverify/services/s3/checks/sra_s3_01.py +89 -0
- sraverify/services/s3/checks/sra_s3_02.py +89 -0
- sraverify/services/s3/checks/sra_s3_03.py +88 -0
- sraverify/services/s3/checks/sra_s3_04.py +88 -0
- sraverify/services/s3/client.py +52 -0
- sraverify/services/securityhub/__init__.py +27 -0
- sraverify/services/securityhub/base.py +349 -0
- sraverify/services/securityhub/checks/__init__.py +1 -0
- sraverify/services/securityhub/checks/sra_securityhub_01.py +115 -0
- sraverify/services/securityhub/checks/sra_securityhub_02.py +114 -0
- sraverify/services/securityhub/checks/sra_securityhub_03.py +136 -0
- sraverify/services/securityhub/checks/sra_securityhub_04.py +75 -0
- sraverify/services/securityhub/checks/sra_securityhub_05.py +102 -0
- sraverify/services/securityhub/checks/sra_securityhub_06.py +113 -0
- sraverify/services/securityhub/checks/sra_securityhub_07.py +121 -0
- sraverify/services/securityhub/checks/sra_securityhub_08.py +113 -0
- sraverify/services/securityhub/checks/sra_securityhub_09.py +100 -0
- sraverify/services/securityhub/checks/sra_securityhub_10.py +94 -0
- sraverify/services/securityhub/checks/sra_securityhub_11.py +73 -0
- sraverify/services/securityhub/client.py +249 -0
- sraverify/services/securityincidentresponse/__init__.py +13 -0
- sraverify/services/securityincidentresponse/base.py +95 -0
- sraverify/services/securityincidentresponse/checks/__init__.py +1 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_01.py +77 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_02.py +72 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_03.py +86 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_04.py +117 -0
- sraverify/services/securityincidentresponse/checks/sra_securityincidentresponse_05.py +55 -0
- sraverify/services/securityincidentresponse/client.py +71 -0
- sraverify/services/securitylake/__init__.py +39 -0
- sraverify/services/securitylake/base.py +461 -0
- sraverify/services/securitylake/checks/__init__.py +1 -0
- sraverify/services/securitylake/checks/sra_securitylake_01.py +98 -0
- sraverify/services/securitylake/checks/sra_securitylake_02.py +133 -0
- sraverify/services/securitylake/checks/sra_securitylake_03.py +116 -0
- sraverify/services/securitylake/checks/sra_securitylake_04.py +72 -0
- sraverify/services/securitylake/checks/sra_securitylake_05.py +116 -0
- sraverify/services/securitylake/checks/sra_securitylake_06.py +104 -0
- sraverify/services/securitylake/checks/sra_securitylake_07.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_08.py +107 -0
- sraverify/services/securitylake/checks/sra_securitylake_09.py +107 -0
- sraverify/services/securitylake/checks/sra_securitylake_10.py +106 -0
- sraverify/services/securitylake/checks/sra_securitylake_11.py +109 -0
- sraverify/services/securitylake/checks/sra_securitylake_12.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_13.py +108 -0
- sraverify/services/securitylake/checks/sra_securitylake_14.py +72 -0
- sraverify/services/securitylake/checks/sra_securitylake_15.py +120 -0
- sraverify/services/securitylake/checks/sra_securitylake_16.py +104 -0
- sraverify/services/securitylake/checks/sra_securitylake_17.py +103 -0
- sraverify/services/securitylake/client.py +247 -0
- sraverify/services/shield/__init__.py +33 -0
- sraverify/services/shield/base.py +199 -0
- sraverify/services/shield/checks/__init__.py +1 -0
- sraverify/services/shield/checks/sra_shield_01.py +68 -0
- sraverify/services/shield/checks/sra_shield_02.py +77 -0
- sraverify/services/shield/checks/sra_shield_03.py +84 -0
- sraverify/services/shield/checks/sra_shield_04.py +84 -0
- sraverify/services/shield/checks/sra_shield_05.py +84 -0
- sraverify/services/shield/checks/sra_shield_06.py +84 -0
- sraverify/services/shield/checks/sra_shield_07.py +84 -0
- sraverify/services/shield/checks/sra_shield_08.py +69 -0
- sraverify/services/shield/checks/sra_shield_09.py +86 -0
- sraverify/services/shield/checks/sra_shield_10.py +100 -0
- sraverify/services/shield/checks/sra_shield_11.py +71 -0
- sraverify/services/shield/checks/sra_shield_12.py +130 -0
- sraverify/services/shield/checks/sra_shield_13.py +112 -0
- sraverify/services/shield/checks/sra_shield_14.py +111 -0
- sraverify/services/shield/client.py +214 -0
- sraverify/services/waf/__init__.py +21 -0
- sraverify/services/waf/base.py +100 -0
- sraverify/services/waf/checks/__init__.py +1 -0
- sraverify/services/waf/checks/sra_waf_01.py +63 -0
- sraverify/services/waf/checks/sra_waf_02.py +82 -0
- sraverify/services/waf/checks/sra_waf_03.py +123 -0
- sraverify/services/waf/checks/sra_waf_04.py +94 -0
- sraverify/services/waf/checks/sra_waf_05.py +94 -0
- sraverify/services/waf/checks/sra_waf_06.py +91 -0
- sraverify/services/waf/checks/sra_waf_07.py +94 -0
- sraverify/services/waf/checks/sra_waf_08.py +66 -0
- sraverify/services/waf/checks/sra_waf_09.py +95 -0
- sraverify/services/waf/client.py +109 -0
- sraverify/utils/__init__.py +3 -0
- sraverify/utils/banner.py +65 -0
- sraverify/utils/outputs.py +57 -0
- sraverify/utils/progress.py +97 -0
- sraverify-0.1.0.dist-info/LICENSE +175 -0
- sraverify-0.1.0.dist-info/METADATA +516 -0
- sraverify-0.1.0.dist-info/NOTICE +1 -0
- sraverify-0.1.0.dist-info/RECORD +261 -0
- sraverify-0.1.0.dist-info/WHEEL +5 -0
- sraverify-0.1.0.dist-info/entry_points.txt +2 -0
- sraverify-0.1.0.dist-info/top_level.txt +1 -0
sraverify/main.py
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SRA Verify - Security Reference Architecture Verification Tool
|
|
3
|
+
|
|
4
|
+
This module provides both the library interface and CLI functionality.
|
|
5
|
+
The SRAVerify class implements the core functionality that can be used
|
|
6
|
+
as a library, while the CLI functions use this class to provide the
|
|
7
|
+
command-line interface.
|
|
8
|
+
"""
|
|
9
|
+
import argparse
|
|
10
|
+
import datetime
|
|
11
|
+
from boto3 import Session
|
|
12
|
+
from typing import Dict, List, Any, Optional
|
|
13
|
+
|
|
14
|
+
from sraverify.core.session import get_session
|
|
15
|
+
from sraverify.core.logging import logger, configure_logging
|
|
16
|
+
from sraverify.utils.outputs import write_csv_output
|
|
17
|
+
from sraverify.utils.progress import ScanProgress
|
|
18
|
+
from sraverify.utils.banner import print_banner
|
|
19
|
+
from sraverify.services.guardduty import CHECKS as guardduty_checks
|
|
20
|
+
from sraverify.services.cloudtrail import CHECKS as cloudtrail_checks
|
|
21
|
+
from sraverify.services.accessanalyzer import CHECKS as accessanalyzer_checks
|
|
22
|
+
from sraverify.services.config import CHECKS as config_checks
|
|
23
|
+
from sraverify.services.securityhub import CHECKS as securityhub_checks
|
|
24
|
+
from sraverify.services.s3 import CHECKS as s3_checks
|
|
25
|
+
from sraverify.services.inspector import CHECKS as inspector_checks
|
|
26
|
+
from sraverify.services.ec2 import CHECKS as ec2_checks
|
|
27
|
+
from sraverify.services.macie import CHECKS as macie_checks
|
|
28
|
+
from sraverify.services.shield import CHECKS as shield_checks
|
|
29
|
+
from sraverify.services.waf import CHECKS as waf_checks
|
|
30
|
+
from sraverify.services.account import CHECKS as account_checks
|
|
31
|
+
from sraverify.services.auditmanager import CHECKS as auditmanager_checks
|
|
32
|
+
from sraverify.services.firewallmanager import CHECKS as firewallmanager_checks
|
|
33
|
+
from sraverify.services.securitylake import CHECKS as securitylake_checks
|
|
34
|
+
from sraverify.services.securityincidentresponse import CHECKS as securityincidentresponse_checks
|
|
35
|
+
|
|
36
|
+
# Collect all checks from different services
|
|
37
|
+
ALL_CHECKS = {
|
|
38
|
+
**guardduty_checks,
|
|
39
|
+
**cloudtrail_checks,
|
|
40
|
+
**accessanalyzer_checks,
|
|
41
|
+
**config_checks,
|
|
42
|
+
**securityhub_checks,
|
|
43
|
+
**s3_checks,
|
|
44
|
+
**inspector_checks,
|
|
45
|
+
**ec2_checks,
|
|
46
|
+
**macie_checks,
|
|
47
|
+
**shield_checks,
|
|
48
|
+
**waf_checks,
|
|
49
|
+
**account_checks,
|
|
50
|
+
**auditmanager_checks,
|
|
51
|
+
**firewallmanager_checks,
|
|
52
|
+
**securitylake_checks,
|
|
53
|
+
**securityincidentresponse_checks
|
|
54
|
+
# Add more service checks here as they're implemented
|
|
55
|
+
# **config_checks,
|
|
56
|
+
# etc.
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
class SRAVerify:
|
|
60
|
+
"""Main class for SRA Verify functionality."""
|
|
61
|
+
|
|
62
|
+
def __init__(self, profile: Optional[str] = None, role_arn: Optional[str] = None,
|
|
63
|
+
regions: Optional[List[str]] = None, session: Optional[Session] = None,
|
|
64
|
+
debug: bool = False):
|
|
65
|
+
"""
|
|
66
|
+
Initialize SRA Verify.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
profile: AWS profile to use
|
|
70
|
+
role_arn: ARN of IAM role to assume
|
|
71
|
+
regions: List of AWS regions to check
|
|
72
|
+
session: Existing AWS session to use (if provided)
|
|
73
|
+
debug: Enable debug logging
|
|
74
|
+
"""
|
|
75
|
+
configure_logging(debug)
|
|
76
|
+
self.regions = regions
|
|
77
|
+
self.session = session if session else get_session(profile=profile, role_arn=role_arn)
|
|
78
|
+
self.progress = None
|
|
79
|
+
|
|
80
|
+
def get_available_checks(self, account_type: str = 'all') -> Dict[str, Dict[str, str]]:
|
|
81
|
+
"""
|
|
82
|
+
Get all available checks, optionally filtered by account type.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
account_type: Type of accounts to list checks for ('application', 'audit', 'log-archive', 'management', or 'all')
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Dictionary mapping check IDs to check information
|
|
89
|
+
"""
|
|
90
|
+
checks = {}
|
|
91
|
+
for check_id, check_class in sorted(ALL_CHECKS.items()):
|
|
92
|
+
check = check_class()
|
|
93
|
+
if account_type == 'all' or check.account_type == account_type:
|
|
94
|
+
checks[check_id] = {
|
|
95
|
+
'name': check.check_name,
|
|
96
|
+
'service': check.service,
|
|
97
|
+
'account_type': check.account_type,
|
|
98
|
+
'description': check.description,
|
|
99
|
+
'severity': check.severity
|
|
100
|
+
}
|
|
101
|
+
return checks
|
|
102
|
+
|
|
103
|
+
def get_available_services(self) -> List[str]:
|
|
104
|
+
"""
|
|
105
|
+
Get all available services.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
List of service names
|
|
109
|
+
"""
|
|
110
|
+
services = set()
|
|
111
|
+
for check_class in ALL_CHECKS.values():
|
|
112
|
+
check = check_class()
|
|
113
|
+
services.add(check.service)
|
|
114
|
+
return sorted(list(services))
|
|
115
|
+
|
|
116
|
+
def run_checks(self, account_type: str = 'all', service: Optional[str] = None,
|
|
117
|
+
check_id: Optional[str] = None, audit_accounts: Optional[List[str]] = None,
|
|
118
|
+
log_archive_accounts: Optional[List[str]] = None,
|
|
119
|
+
show_progress: bool = False) -> List[Dict[str, Any]]:
|
|
120
|
+
"""
|
|
121
|
+
Run security checks.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
account_type: Type of accounts to check ('application', 'audit', 'log-archive', 'management', or 'all')
|
|
125
|
+
service: Run checks for a specific service
|
|
126
|
+
check_id: Run a specific check
|
|
127
|
+
audit_accounts: List of AWS accounts used for Audit/Security Tooling
|
|
128
|
+
log_archive_accounts: List of AWS accounts used for Logging
|
|
129
|
+
show_progress: Whether to show progress bar
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
List of findings
|
|
133
|
+
"""
|
|
134
|
+
# Start with all checks or filtered by account type
|
|
135
|
+
if account_type == 'all':
|
|
136
|
+
checks_to_run = ALL_CHECKS.copy()
|
|
137
|
+
else:
|
|
138
|
+
logger.debug(f"Filtering checks by account type: {account_type}")
|
|
139
|
+
checks_to_run = {
|
|
140
|
+
check_id: check_class for check_id, check_class in ALL_CHECKS.items()
|
|
141
|
+
if check_class().account_type == account_type
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Filter by specific check if provided
|
|
145
|
+
if check_id:
|
|
146
|
+
logger.debug(f"Filtering for specific check: {check_id}")
|
|
147
|
+
if check_id not in ALL_CHECKS:
|
|
148
|
+
logger.error(f"Check {check_id} not found")
|
|
149
|
+
return []
|
|
150
|
+
|
|
151
|
+
check = ALL_CHECKS[check_id]()
|
|
152
|
+
if account_type != 'all' and check.account_type != account_type:
|
|
153
|
+
logger.error(f"Check {check_id} is for {check.account_type} accounts, but account_type is set to {account_type}")
|
|
154
|
+
return []
|
|
155
|
+
|
|
156
|
+
checks_to_run = {check_id: ALL_CHECKS[check_id]}
|
|
157
|
+
|
|
158
|
+
# Filter by service if provided
|
|
159
|
+
if service:
|
|
160
|
+
logger.debug(f"Filtering checks by service: {service}")
|
|
161
|
+
service_checks = {}
|
|
162
|
+
for check_id, check_class in checks_to_run.items():
|
|
163
|
+
check = check_class()
|
|
164
|
+
if check.service.lower() == service.lower():
|
|
165
|
+
service_checks[check_id] = check_class
|
|
166
|
+
|
|
167
|
+
if not service_checks:
|
|
168
|
+
logger.error(f"No {account_type} checks found for service {service}")
|
|
169
|
+
return []
|
|
170
|
+
|
|
171
|
+
checks_to_run = service_checks
|
|
172
|
+
|
|
173
|
+
# Check if there are any checks after filtering
|
|
174
|
+
if not checks_to_run:
|
|
175
|
+
logger.error("No checks found with selected filters")
|
|
176
|
+
return []
|
|
177
|
+
|
|
178
|
+
all_findings = []
|
|
179
|
+
|
|
180
|
+
# Group checks by service for better organization
|
|
181
|
+
service_checks = {}
|
|
182
|
+
for check_id, check_class in checks_to_run.items():
|
|
183
|
+
check = check_class()
|
|
184
|
+
if check.service not in service_checks:
|
|
185
|
+
service_checks[check.service] = []
|
|
186
|
+
service_checks[check.service].append((check_id, check_class))
|
|
187
|
+
|
|
188
|
+
# Set up progress tracking if requested
|
|
189
|
+
if show_progress:
|
|
190
|
+
self.progress = ScanProgress(len(checks_to_run))
|
|
191
|
+
|
|
192
|
+
# Run checks by service
|
|
193
|
+
for service_name, checks in service_checks.items():
|
|
194
|
+
if self.progress:
|
|
195
|
+
self.progress.update(service_name)
|
|
196
|
+
logger.debug(f"Running {len(checks)} checks for service {service_name}")
|
|
197
|
+
|
|
198
|
+
for check_id, check_class in checks:
|
|
199
|
+
logger.debug(f"Initializing check {check_id}")
|
|
200
|
+
check = check_class()
|
|
201
|
+
check.initialize(self.session, regions=self.regions)
|
|
202
|
+
|
|
203
|
+
# Pass audit and log archive accounts to the check if it needs them
|
|
204
|
+
if audit_accounts:
|
|
205
|
+
check._audit_accounts = audit_accounts
|
|
206
|
+
if log_archive_accounts:
|
|
207
|
+
check._log_archive_accounts = log_archive_accounts
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
logger.debug(f"Executing check {check_id}: {check.check_name}")
|
|
211
|
+
findings = check.execute()
|
|
212
|
+
all_findings.extend(findings)
|
|
213
|
+
logger.debug(f"Check {check_id} completed with {len(findings)} findings")
|
|
214
|
+
except Exception as e:
|
|
215
|
+
logger.error(f"Error running check {check_id}: {e}", exc_info=True)
|
|
216
|
+
# Add a failure finding
|
|
217
|
+
all_findings.append({
|
|
218
|
+
"CheckId": check_id,
|
|
219
|
+
"Status": "ERROR",
|
|
220
|
+
"Region": "global",
|
|
221
|
+
"Severity": "UNKNOWN",
|
|
222
|
+
"Title": f"Error running {check_id}",
|
|
223
|
+
"Description": f"An error occurred while running check {check_id}",
|
|
224
|
+
"ResourceId": None,
|
|
225
|
+
"ResourceType": None,
|
|
226
|
+
"AccountId": None,
|
|
227
|
+
"CheckedValue": None,
|
|
228
|
+
"ActualValue": str(e),
|
|
229
|
+
"Remediation": "Check the error message and try again",
|
|
230
|
+
"Service": service_name,
|
|
231
|
+
"CheckLogic": None,
|
|
232
|
+
"AccountType": check.account_type
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
if self.progress:
|
|
236
|
+
self.progress.increment()
|
|
237
|
+
|
|
238
|
+
if self.progress:
|
|
239
|
+
self.progress.finish()
|
|
240
|
+
|
|
241
|
+
return all_findings
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def parse_args():
|
|
245
|
+
"""Parse command line arguments."""
|
|
246
|
+
parser = argparse.ArgumentParser(description='SRA Verify - Security Rule Assessment Verification Tool')
|
|
247
|
+
parser.add_argument('--profile', type=str, help='AWS profile to use')
|
|
248
|
+
parser.add_argument('--role', type=str, help='ARN of IAM role to assume')
|
|
249
|
+
parser.add_argument('--regions', type=str, help='Comma-separated list of AWS regions to check')
|
|
250
|
+
parser.add_argument('--output', type=str, default='sraverify_findings.csv',
|
|
251
|
+
help='Output file name (default: sraverify_findings.csv)')
|
|
252
|
+
parser.add_argument('--check', type=str, help='Run a specific check (e.g., SRA-GD-1)')
|
|
253
|
+
parser.add_argument('--service', type=str, help='Run checks for a specific service (e.g., GuardDuty)')
|
|
254
|
+
parser.add_argument('--account-type', type=str,
|
|
255
|
+
choices=['application', 'audit', 'log-archive', 'management', 'all'],
|
|
256
|
+
default='all',
|
|
257
|
+
help='Type of accounts to run checks against: application, audit, log-archive, management, or all (default: all)')
|
|
258
|
+
parser.add_argument('--audit-account', type=str, metavar='ACCOUNTID1,ACCOUNTID2',
|
|
259
|
+
help='AWS accounts used for Audit/Security Tooling, use comma separated values')
|
|
260
|
+
parser.add_argument('--log-archive-account', type=str, metavar='ACCOUNTID1,ACCOUNTID2',
|
|
261
|
+
help='AWS accounts used for Logging, use comma separated values')
|
|
262
|
+
parser.add_argument('--list-checks', action='store_true', help='List available checks')
|
|
263
|
+
parser.add_argument('--list-services', action='store_true', help='List available services')
|
|
264
|
+
parser.add_argument('--debug', action='store_true', help='Enable debug logging')
|
|
265
|
+
|
|
266
|
+
return parser.parse_args()
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def main():
|
|
270
|
+
"""Main entry point."""
|
|
271
|
+
args = parse_args()
|
|
272
|
+
|
|
273
|
+
# Create SRAVerify instance
|
|
274
|
+
regions = [r.strip() for r in args.regions.split(',')] if args.regions else None
|
|
275
|
+
sra = SRAVerify(profile=args.profile, role_arn=args.role, regions=regions, debug=args.debug)
|
|
276
|
+
|
|
277
|
+
if args.list_checks:
|
|
278
|
+
checks = sra.get_available_checks(args.account_type)
|
|
279
|
+
print("Available checks:")
|
|
280
|
+
for check_id, info in checks.items():
|
|
281
|
+
print(f" {check_id}: {info['name']} ({info['service']}) [{info['account_type']}]")
|
|
282
|
+
return
|
|
283
|
+
|
|
284
|
+
if args.list_services:
|
|
285
|
+
services = sra.get_available_services()
|
|
286
|
+
print("Available services:")
|
|
287
|
+
for service in services:
|
|
288
|
+
print(f" {service}")
|
|
289
|
+
return
|
|
290
|
+
|
|
291
|
+
# Parse audit accounts if provided
|
|
292
|
+
audit_accounts = None
|
|
293
|
+
if args.audit_account:
|
|
294
|
+
audit_accounts = [a.strip() for a in args.audit_account.split(',')]
|
|
295
|
+
logger.debug(f"Using audit accounts: {', '.join(audit_accounts)}")
|
|
296
|
+
|
|
297
|
+
# Parse log archive accounts if provided
|
|
298
|
+
log_archive_accounts = None
|
|
299
|
+
if args.log_archive_account:
|
|
300
|
+
log_archive_accounts = [a.strip() for a in args.log_archive_account.split(',')]
|
|
301
|
+
logger.debug(f"Using log archive accounts: {', '.join(log_archive_accounts)}")
|
|
302
|
+
|
|
303
|
+
# Generate output filename with timestamp if not specified
|
|
304
|
+
output_file = args.output
|
|
305
|
+
if output_file == 'sraverify_findings.csv':
|
|
306
|
+
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
|
|
307
|
+
output_file = f"sraverify_findings_{timestamp}.csv"
|
|
308
|
+
|
|
309
|
+
# Display banner with session information
|
|
310
|
+
print_banner(
|
|
311
|
+
profile=args.profile or 'default',
|
|
312
|
+
region=sra.session.region_name,
|
|
313
|
+
session=sra.session,
|
|
314
|
+
regions=regions,
|
|
315
|
+
account_type=args.account_type,
|
|
316
|
+
checks_count=len(sra.get_available_checks(args.account_type)),
|
|
317
|
+
output_file=output_file,
|
|
318
|
+
role=args.role
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
# Run checks
|
|
322
|
+
findings = sra.run_checks(
|
|
323
|
+
account_type=args.account_type,
|
|
324
|
+
service=args.service,
|
|
325
|
+
check_id=args.check,
|
|
326
|
+
audit_accounts=audit_accounts,
|
|
327
|
+
log_archive_accounts=log_archive_accounts,
|
|
328
|
+
show_progress=True
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
# Write output
|
|
332
|
+
logger.debug(f"Writing findings to {output_file}")
|
|
333
|
+
write_csv_output(findings, output_file)
|
|
334
|
+
|
|
335
|
+
# Print summary
|
|
336
|
+
pass_count = sum(1 for f in findings if f.get('Status') == 'PASS')
|
|
337
|
+
fail_count = sum(1 for f in findings if f.get('Status') == 'FAIL')
|
|
338
|
+
error_count = sum(1 for f in findings if f.get('Status') == 'ERROR')
|
|
339
|
+
|
|
340
|
+
logger.debug("Scan complete")
|
|
341
|
+
print("\n-> Scan complete!")
|
|
342
|
+
print(f" · Total findings: {len(findings)}")
|
|
343
|
+
print(f" · Pass: {pass_count}")
|
|
344
|
+
print(f" · Fail: {fail_count}")
|
|
345
|
+
print(f" · Error: {error_count}")
|
|
346
|
+
print(f" · Output: {output_file}")
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
if __name__ == "__main__":
|
|
350
|
+
main()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Accessanalyzer security checks.
|
|
3
|
+
"""
|
|
4
|
+
from sraverify.services.accessanalyzer.checks.sra_accessanalyzer_01 import SRA_ACCESSANALYZER_01
|
|
5
|
+
from sraverify.services.accessanalyzer.checks.sra_accessanalyzer_02 import SRA_ACCESSANALYZER_02
|
|
6
|
+
from sraverify.services.accessanalyzer.checks.sra_accessanalyzer_03 import SRA_ACCESSANALYZER_03
|
|
7
|
+
from sraverify.services.accessanalyzer.checks.sra_accessanalyzer_04 import SRA_ACCESSANALYZER_04
|
|
8
|
+
|
|
9
|
+
# Register checks
|
|
10
|
+
CHECKS = {
|
|
11
|
+
"SRA-ACCESSANALYZER-01": SRA_ACCESSANALYZER_01,
|
|
12
|
+
"SRA-ACCESSANALYZER-02": SRA_ACCESSANALYZER_02,
|
|
13
|
+
"SRA-ACCESSANALYZER-03": SRA_ACCESSANALYZER_03,
|
|
14
|
+
"SRA-ACCESSANALYZER-04": SRA_ACCESSANALYZER_04,
|
|
15
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base class for IAM Access Analyzer security checks.
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Optional, Dict, Any
|
|
5
|
+
import boto3
|
|
6
|
+
from sraverify.core.check import SecurityCheck
|
|
7
|
+
from sraverify.services.accessanalyzer.client import AccessAnalyzerClient
|
|
8
|
+
from sraverify.core.logging import logger
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AccessAnalyzerCheck(SecurityCheck):
|
|
12
|
+
"""Base class for all IAM Access Analyzer security checks."""
|
|
13
|
+
|
|
14
|
+
# Class-level caches shared across all instances
|
|
15
|
+
_delegated_admin_cache = {}
|
|
16
|
+
_analyzer_cache = {}
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
"""Initialize IAM Access Analyzer base check."""
|
|
20
|
+
super().__init__(
|
|
21
|
+
account_type="application",
|
|
22
|
+
service="IAM Access Analyzer",
|
|
23
|
+
resource_type="AWS::AccessAnalyzer::Analyzer"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def _setup_clients(self):
|
|
27
|
+
"""Set up AccessAnalyzer clients for enabled regions."""
|
|
28
|
+
# Clear existing clients
|
|
29
|
+
self._clients.clear()
|
|
30
|
+
|
|
31
|
+
if not hasattr(self, 'session') or not self.session:
|
|
32
|
+
logger.debug("No session available, skipping client setup")
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
# For organization checks, we need to check all specified regions
|
|
36
|
+
for region in self.regions:
|
|
37
|
+
try:
|
|
38
|
+
client = AccessAnalyzerClient(region, self.session)
|
|
39
|
+
if client.is_access_analyzer_available():
|
|
40
|
+
self._clients[region] = client
|
|
41
|
+
logger.debug(f"Access Analyzer client set up for region {region}")
|
|
42
|
+
else:
|
|
43
|
+
logger.debug(f"Access Analyzer not available in region {region}")
|
|
44
|
+
except Exception as e:
|
|
45
|
+
# Skip regions where client creation fails
|
|
46
|
+
logger.warning(f"Failed to create Access Analyzer client for region {region}: {e}")
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
def get_client(self, region: str) -> Optional[AccessAnalyzerClient]:
|
|
50
|
+
"""
|
|
51
|
+
Get Access Analyzer client for a region.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
region: AWS region name
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
AccessAnalyzerClient for the region or None if not available
|
|
58
|
+
"""
|
|
59
|
+
return self._clients.get(region)
|
|
60
|
+
|
|
61
|
+
def get_analyzers(self, region: str) -> List[Dict[str, Any]]:
|
|
62
|
+
"""
|
|
63
|
+
Get analyzers for a specific region with caching.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
region: AWS region name
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
List of analyzers in the region
|
|
70
|
+
"""
|
|
71
|
+
# Check class-level cache
|
|
72
|
+
cache_key = f"{self.session.region_name}:{region}"
|
|
73
|
+
if cache_key in AccessAnalyzerCheck._analyzer_cache:
|
|
74
|
+
logger.debug(f"Using cached analyzers for {region}")
|
|
75
|
+
return AccessAnalyzerCheck._analyzer_cache[cache_key]
|
|
76
|
+
|
|
77
|
+
# Get client
|
|
78
|
+
client = self.get_client(region)
|
|
79
|
+
if not client:
|
|
80
|
+
logger.warning(f"No Access Analyzer client available for region {region}")
|
|
81
|
+
return []
|
|
82
|
+
|
|
83
|
+
# Get analyzers
|
|
84
|
+
logger.debug(f"Fetching analyzers for {region}")
|
|
85
|
+
analyzers = client.list_analyzers()
|
|
86
|
+
|
|
87
|
+
# Cache the analyzers
|
|
88
|
+
AccessAnalyzerCheck._analyzer_cache[cache_key] = analyzers
|
|
89
|
+
logger.debug(f"Cached {len(analyzers)} analyzers for {region}")
|
|
90
|
+
|
|
91
|
+
return analyzers
|
|
92
|
+
|
|
93
|
+
def get_delegated_admin(self) -> Dict[str, Any]:
|
|
94
|
+
"""
|
|
95
|
+
Get the delegated administrator for IAM Access Analyzer with caching.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Dictionary containing delegated administrator details or empty dict if none
|
|
99
|
+
"""
|
|
100
|
+
account_id = self.account_id
|
|
101
|
+
|
|
102
|
+
# Check class-level cache
|
|
103
|
+
if account_id in AccessAnalyzerCheck._delegated_admin_cache:
|
|
104
|
+
logger.debug(f"Using cached delegated admin for account {account_id}")
|
|
105
|
+
return AccessAnalyzerCheck._delegated_admin_cache[account_id]
|
|
106
|
+
|
|
107
|
+
# If not in cache, get it from the client
|
|
108
|
+
# Use the first available region to make the API call
|
|
109
|
+
if not self._clients:
|
|
110
|
+
logger.warning("No Access Analyzer clients available")
|
|
111
|
+
return {}
|
|
112
|
+
|
|
113
|
+
# Use the first available region's client
|
|
114
|
+
region = next(iter(self._clients))
|
|
115
|
+
client = self._clients[region]
|
|
116
|
+
|
|
117
|
+
# Get delegated admin from client
|
|
118
|
+
delegated_admin = client.get_delegated_admin()
|
|
119
|
+
|
|
120
|
+
# Cache the result
|
|
121
|
+
AccessAnalyzerCheck._delegated_admin_cache[account_id] = delegated_admin
|
|
122
|
+
|
|
123
|
+
return delegated_admin
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Check if IAM Access Analyzer external access analyzer is configured with account zone of trust.
|
|
3
|
+
"""
|
|
4
|
+
from typing import Dict, List, Any
|
|
5
|
+
from sraverify.services.accessanalyzer.base import AccessAnalyzerCheck
|
|
6
|
+
from sraverify.core.logging import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SRA_ACCESSANALYZER_01(AccessAnalyzerCheck):
|
|
10
|
+
"""Check if IAM Access Analyzer external access analyzer is configured with account zone of trust."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
"""Initialize IAM Access Analyzer check."""
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.check_id = "SRA-ACCESSANALYZER-01"
|
|
16
|
+
self.check_name = "IAM Access Analyzer Account Zone of trust"
|
|
17
|
+
self.description = ("This check verifies whether IAA external access analyzer is configured with a zone of "
|
|
18
|
+
"trust of AWS account. IAM Access Analyzer generates a finding for each instance of a "
|
|
19
|
+
"resource-based policy that grants access to a resource within your zone of trust to a "
|
|
20
|
+
"principal that is not within your zone of trust. When you configure an AWS account as "
|
|
21
|
+
"the zone of trust for an analyzer- IAA generates findings or each instance of a "
|
|
22
|
+
"resource-based policy that grants access to a resource within your AWS account whether "
|
|
23
|
+
"the analyzer exists to a principal that is not within your AWS account.")
|
|
24
|
+
self.severity = "HIGH"
|
|
25
|
+
self.check_logic = "List analyzers in each Region. Check if analyzer exists and is configured with account zone of trust."
|
|
26
|
+
|
|
27
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
28
|
+
"""Execute the check for each region."""
|
|
29
|
+
findings = []
|
|
30
|
+
logger.debug(f"Executing {self.check_id} check for account {self.account_id}")
|
|
31
|
+
|
|
32
|
+
# If no regions have Access Analyzer available, return a single failure
|
|
33
|
+
if not self._clients:
|
|
34
|
+
logger.warning("No regions with Access Analyzer available")
|
|
35
|
+
findings.append(
|
|
36
|
+
self.create_finding(
|
|
37
|
+
status="FAIL",
|
|
38
|
+
region="global",
|
|
39
|
+
resource_id="accessanalyzer:global", # Generic format for global failure
|
|
40
|
+
actual_value="IAM Access Analyzer not available in any region",
|
|
41
|
+
remediation="Enable IAM Access Analyzer in at least one region and configure "
|
|
42
|
+
"with account zone of trust"
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
return findings
|
|
46
|
+
|
|
47
|
+
# Check each region where Access Analyzer is available
|
|
48
|
+
for region, client in self._clients.items():
|
|
49
|
+
logger.debug(f"Checking region {region} for account-level analyzers")
|
|
50
|
+
analyzers = self.get_analyzers(region)
|
|
51
|
+
|
|
52
|
+
# Check if any analyzer exists with account-level zone of trust
|
|
53
|
+
account_analyzer = None
|
|
54
|
+
for analyzer in analyzers:
|
|
55
|
+
if analyzer.get('type') == 'ACCOUNT':
|
|
56
|
+
account_analyzer = analyzer
|
|
57
|
+
logger.debug(f"Found account analyzer in {region}: {analyzer.get('name')}")
|
|
58
|
+
break
|
|
59
|
+
|
|
60
|
+
if account_analyzer:
|
|
61
|
+
findings.append(
|
|
62
|
+
self.create_finding(
|
|
63
|
+
status="PASS",
|
|
64
|
+
region=region,
|
|
65
|
+
resource_id=account_analyzer['arn'], # Use the actual analyzer ARN for PASS
|
|
66
|
+
actual_value="IAM Access Analyzer configured with account zone of trust",
|
|
67
|
+
remediation="No remediation needed"
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
else:
|
|
71
|
+
logger.debug(f"No account analyzer found in {region}")
|
|
72
|
+
findings.append(
|
|
73
|
+
self.create_finding(
|
|
74
|
+
status="FAIL",
|
|
75
|
+
region=region,
|
|
76
|
+
resource_id=f"accessanalyzer:{region}", # Keep generic format for FAIL
|
|
77
|
+
actual_value="No IAM Access Analyzer configured with account zone of trust",
|
|
78
|
+
remediation="Create an IAM Access Analyzer with account zone of trust in this region"
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
return findings
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Check if IAM Access Analyzer has a delegated administrator for the organization.
|
|
3
|
+
"""
|
|
4
|
+
from typing import Dict, List, Any
|
|
5
|
+
from sraverify.services.accessanalyzer.base import AccessAnalyzerCheck
|
|
6
|
+
from sraverify.core.logging import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SRA_ACCESSANALYZER_02(AccessAnalyzerCheck):
|
|
10
|
+
"""Check if IAM Access Analyzer has a delegated administrator for the organization."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
"""Initialize IAM Access Analyzer check."""
|
|
14
|
+
super().__init__()
|
|
15
|
+
self.check_id = "SRA-ACCESSANALYZER-02"
|
|
16
|
+
self.check_name = "IAM Access Analyzer Organization Delegated Administrator"
|
|
17
|
+
self.description = ("This check verifies whether IAA service administration for your AWS "
|
|
18
|
+
"Organization is delegated out of your AWS Organization management account. "
|
|
19
|
+
"The delegated administrator has permissions to create and manage analyzers "
|
|
20
|
+
"with the AWS organization as the zone of trust.")
|
|
21
|
+
self.severity = "HIGH"
|
|
22
|
+
self.account_type = "management"
|
|
23
|
+
self.check_logic = ("Check if a delegated administrator is configured for IAM Access Analyzer in the organization")
|
|
24
|
+
|
|
25
|
+
def execute(self) -> List[Dict[str, Any]]:
|
|
26
|
+
"""Execute the check."""
|
|
27
|
+
findings = []
|
|
28
|
+
logger.debug(f"Executing {self.check_id} check for account {self.account_id}")
|
|
29
|
+
|
|
30
|
+
# Check for delegated administrator
|
|
31
|
+
try:
|
|
32
|
+
logger.debug("Checking for IAM Access Analyzer delegated administrator")
|
|
33
|
+
org_client = self.session.client('organizations')
|
|
34
|
+
response = org_client.list_delegated_administrators(
|
|
35
|
+
ServicePrincipal='access-analyzer.amazonaws.com'
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Store in class-level cache
|
|
39
|
+
if response['DelegatedAdministrators']:
|
|
40
|
+
delegated_admin = response['DelegatedAdministrators'][0]
|
|
41
|
+
self.__class__._delegated_admin_cache[self.account_id] = delegated_admin
|
|
42
|
+
logger.debug(f"Found delegated administrator: {delegated_admin['Id']}")
|
|
43
|
+
|
|
44
|
+
findings.append(
|
|
45
|
+
self.create_finding(
|
|
46
|
+
status="PASS",
|
|
47
|
+
region="global",
|
|
48
|
+
resource_id=delegated_admin['Id'],
|
|
49
|
+
actual_value=f"IAM Access Analyzer delegated administrator configured: "
|
|
50
|
+
f"Account {delegated_admin['Id']}",
|
|
51
|
+
remediation="No remediation needed"
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
logger.debug("No delegated administrator found for IAM Access Analyzer")
|
|
56
|
+
self.__class__._delegated_admin_cache[self.account_id] = {}
|
|
57
|
+
findings.append(
|
|
58
|
+
self.create_finding(
|
|
59
|
+
status="FAIL",
|
|
60
|
+
region="global",
|
|
61
|
+
resource_id=f"organization/{self.account_id}",
|
|
62
|
+
actual_value="No delegated administrator configured for IAM Access Analyzer",
|
|
63
|
+
remediation="Configure a delegated administrator for IAM Access Analyzer using "
|
|
64
|
+
"AWS Organizations"
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
except Exception as e:
|
|
69
|
+
logger.error(f"Error checking delegated administrator: {e}")
|
|
70
|
+
self.__class__._delegated_admin_cache[self.account_id] = {}
|
|
71
|
+
findings.append(
|
|
72
|
+
self.create_finding(
|
|
73
|
+
status="FAIL",
|
|
74
|
+
region="global",
|
|
75
|
+
resource_id=f"organization/{self.account_id}",
|
|
76
|
+
actual_value=f"Error checking delegated administrator: {str(e)}",
|
|
77
|
+
remediation="Ensure proper permissions to check delegated administrators "
|
|
78
|
+
"and that Organizations is enabled"
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
return findings
|