runbooks 0.2.5__py3-none-any.whl → 0.7.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.
- conftest.py +26 -0
- jupyter-agent/.env +2 -0
- jupyter-agent/.env.template +2 -0
- jupyter-agent/.gitattributes +35 -0
- jupyter-agent/.gradio/certificate.pem +31 -0
- jupyter-agent/README.md +16 -0
- jupyter-agent/__main__.log +8 -0
- jupyter-agent/app.py +256 -0
- jupyter-agent/cloudops-agent.png +0 -0
- jupyter-agent/ds-system-prompt.txt +154 -0
- jupyter-agent/jupyter-agent.png +0 -0
- jupyter-agent/llama3_template.jinja +123 -0
- jupyter-agent/requirements.txt +9 -0
- jupyter-agent/tmp/4ojbs8a02ir/jupyter-agent.ipynb +68 -0
- jupyter-agent/tmp/cm5iasgpm3p/jupyter-agent.ipynb +91 -0
- jupyter-agent/tmp/crqbsseag5/jupyter-agent.ipynb +91 -0
- jupyter-agent/tmp/hohanq1u097/jupyter-agent.ipynb +57 -0
- jupyter-agent/tmp/jns1sam29wm/jupyter-agent.ipynb +53 -0
- jupyter-agent/tmp/jupyter-agent.ipynb +27 -0
- jupyter-agent/utils.py +409 -0
- runbooks/__init__.py +71 -3
- runbooks/__main__.py +13 -0
- runbooks/aws/ec2_describe_instances.py +1 -1
- runbooks/aws/ec2_run_instances.py +8 -2
- runbooks/aws/ec2_start_stop_instances.py +17 -4
- runbooks/aws/ec2_unused_volumes.py +5 -1
- runbooks/aws/s3_create_bucket.py +4 -2
- runbooks/aws/s3_list_objects.py +6 -1
- runbooks/aws/tagging_lambda_handler.py +13 -2
- runbooks/aws/tags.json +12 -0
- runbooks/base.py +353 -0
- runbooks/cfat/README.md +49 -0
- runbooks/cfat/__init__.py +74 -0
- runbooks/cfat/app.ts +644 -0
- runbooks/cfat/assessment/__init__.py +40 -0
- runbooks/cfat/assessment/asana-import.csv +39 -0
- runbooks/cfat/assessment/cfat-checks.csv +31 -0
- runbooks/cfat/assessment/cfat.txt +520 -0
- runbooks/cfat/assessment/collectors.py +200 -0
- runbooks/cfat/assessment/jira-import.csv +39 -0
- runbooks/cfat/assessment/runner.py +387 -0
- runbooks/cfat/assessment/validators.py +290 -0
- runbooks/cfat/cli.py +103 -0
- runbooks/cfat/docs/asana-import.csv +24 -0
- runbooks/cfat/docs/cfat-checks.csv +31 -0
- runbooks/cfat/docs/cfat.txt +335 -0
- runbooks/cfat/docs/checks-output.png +0 -0
- runbooks/cfat/docs/cloudshell-console-run.png +0 -0
- runbooks/cfat/docs/cloudshell-download.png +0 -0
- runbooks/cfat/docs/cloudshell-output.png +0 -0
- runbooks/cfat/docs/downloadfile.png +0 -0
- runbooks/cfat/docs/jira-import.csv +24 -0
- runbooks/cfat/docs/open-cloudshell.png +0 -0
- runbooks/cfat/docs/report-header.png +0 -0
- runbooks/cfat/models.py +1026 -0
- runbooks/cfat/package-lock.json +5116 -0
- runbooks/cfat/package.json +38 -0
- runbooks/cfat/report.py +496 -0
- runbooks/cfat/reporting/__init__.py +46 -0
- runbooks/cfat/reporting/exporters.py +337 -0
- runbooks/cfat/reporting/formatters.py +496 -0
- runbooks/cfat/reporting/templates.py +135 -0
- runbooks/cfat/run-assessment.sh +23 -0
- runbooks/cfat/runner.py +69 -0
- runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
- runbooks/cfat/src/actions/check-config-existence.ts +37 -0
- runbooks/cfat/src/actions/check-control-tower.ts +37 -0
- runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
- runbooks/cfat/src/actions/check-iam-users.ts +50 -0
- runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
- runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
- runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
- runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
- runbooks/cfat/src/actions/create-backlog.ts +372 -0
- runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
- runbooks/cfat/src/actions/create-report.ts +616 -0
- runbooks/cfat/src/actions/define-account-type.ts +51 -0
- runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
- runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
- runbooks/cfat/src/actions/get-idc-info.ts +34 -0
- runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
- runbooks/cfat/src/actions/get-org-details.ts +35 -0
- runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
- runbooks/cfat/src/actions/get-org-ous.ts +35 -0
- runbooks/cfat/src/actions/get-regions.ts +22 -0
- runbooks/cfat/src/actions/zip-assessment.ts +27 -0
- runbooks/cfat/src/types/index.d.ts +147 -0
- runbooks/cfat/tests/__init__.py +141 -0
- runbooks/cfat/tests/test_cli.py +340 -0
- runbooks/cfat/tests/test_integration.py +290 -0
- runbooks/cfat/tests/test_models.py +505 -0
- runbooks/cfat/tests/test_reporting.py +354 -0
- runbooks/cfat/tsconfig.json +16 -0
- runbooks/cfat/webpack.config.cjs +27 -0
- runbooks/config.py +260 -0
- runbooks/finops/README.md +337 -0
- runbooks/finops/__init__.py +86 -0
- runbooks/finops/aws_client.py +245 -0
- runbooks/finops/cli.py +151 -0
- runbooks/finops/cost_processor.py +410 -0
- runbooks/finops/dashboard_runner.py +448 -0
- runbooks/finops/helpers.py +355 -0
- runbooks/finops/main.py +14 -0
- runbooks/finops/profile_processor.py +174 -0
- runbooks/finops/types.py +66 -0
- runbooks/finops/visualisations.py +80 -0
- runbooks/inventory/.gitignore +354 -0
- runbooks/inventory/ArgumentsClass.py +261 -0
- runbooks/inventory/FAILED_SCRIPTS_TROUBLESHOOTING.md +619 -0
- runbooks/inventory/Inventory_Modules.py +6130 -0
- runbooks/inventory/LandingZone/delete_lz.py +1075 -0
- runbooks/inventory/PASSED_SCRIPTS_GUIDE.md +738 -0
- runbooks/inventory/README.md +1320 -0
- runbooks/inventory/__init__.py +62 -0
- runbooks/inventory/account_class.py +532 -0
- runbooks/inventory/all_my_instances_wrapper.py +123 -0
- runbooks/inventory/aws_decorators.py +201 -0
- runbooks/inventory/aws_organization.png +0 -0
- runbooks/inventory/cfn_move_stack_instances.py +1526 -0
- runbooks/inventory/check_cloudtrail_compliance.py +614 -0
- runbooks/inventory/check_controltower_readiness.py +1107 -0
- runbooks/inventory/check_landingzone_readiness.py +711 -0
- runbooks/inventory/cloudtrail.md +727 -0
- runbooks/inventory/collectors/__init__.py +20 -0
- runbooks/inventory/collectors/aws_compute.py +518 -0
- runbooks/inventory/collectors/aws_networking.py +275 -0
- runbooks/inventory/collectors/base.py +222 -0
- runbooks/inventory/core/__init__.py +19 -0
- runbooks/inventory/core/collector.py +303 -0
- runbooks/inventory/core/formatter.py +296 -0
- runbooks/inventory/delete_s3_buckets_objects.py +169 -0
- runbooks/inventory/discovery.md +81 -0
- runbooks/inventory/draw_org_structure.py +748 -0
- runbooks/inventory/ec2_vpc_utils.py +341 -0
- runbooks/inventory/find_cfn_drift_detection.py +272 -0
- runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
- runbooks/inventory/find_cfn_stackset_drift.py +733 -0
- runbooks/inventory/find_ec2_security_groups.py +669 -0
- runbooks/inventory/find_landingzone_versions.py +201 -0
- runbooks/inventory/find_vpc_flow_logs.py +1221 -0
- runbooks/inventory/inventory.sh +659 -0
- runbooks/inventory/list_cfn_stacks.py +558 -0
- runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
- runbooks/inventory/list_cfn_stackset_operations.py +734 -0
- runbooks/inventory/list_cfn_stacksets.py +453 -0
- runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
- runbooks/inventory/list_ds_directories.py +354 -0
- runbooks/inventory/list_ec2_availability_zones.py +286 -0
- runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
- runbooks/inventory/list_ec2_instances.py +425 -0
- runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
- runbooks/inventory/list_elbs_load_balancers.py +411 -0
- runbooks/inventory/list_enis_network_interfaces.py +526 -0
- runbooks/inventory/list_guardduty_detectors.py +568 -0
- runbooks/inventory/list_iam_policies.py +404 -0
- runbooks/inventory/list_iam_roles.py +518 -0
- runbooks/inventory/list_iam_saml_providers.py +359 -0
- runbooks/inventory/list_lambda_functions.py +882 -0
- runbooks/inventory/list_org_accounts.py +446 -0
- runbooks/inventory/list_org_accounts_users.py +354 -0
- runbooks/inventory/list_rds_db_instances.py +406 -0
- runbooks/inventory/list_route53_hosted_zones.py +318 -0
- runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
- runbooks/inventory/list_sns_topics.py +360 -0
- runbooks/inventory/list_ssm_parameters.py +402 -0
- runbooks/inventory/list_vpc_subnets.py +433 -0
- runbooks/inventory/list_vpcs.py +422 -0
- runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
- runbooks/inventory/models/__init__.py +24 -0
- runbooks/inventory/models/account.py +192 -0
- runbooks/inventory/models/inventory.py +309 -0
- runbooks/inventory/models/resource.py +247 -0
- runbooks/inventory/recover_cfn_stack_ids.py +205 -0
- runbooks/inventory/requirements.txt +12 -0
- runbooks/inventory/run_on_multi_accounts.py +211 -0
- runbooks/inventory/tests/common_test_data.py +3661 -0
- runbooks/inventory/tests/common_test_functions.py +204 -0
- runbooks/inventory/tests/setup.py +24 -0
- runbooks/inventory/tests/src.py +18 -0
- runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
- runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
- runbooks/inventory/tests/test_inventory_modules.py +55 -0
- runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
- runbooks/inventory/tests/test_moto_integration_example.py +273 -0
- runbooks/inventory/tests/test_org_list_accounts.py +49 -0
- runbooks/inventory/update_aws_actions.py +173 -0
- runbooks/inventory/update_cfn_stacksets.py +1215 -0
- runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
- runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
- runbooks/inventory/update_s3_public_access_block.py +539 -0
- runbooks/inventory/utils/__init__.py +23 -0
- runbooks/inventory/utils/aws_helpers.py +510 -0
- runbooks/inventory/utils/threading_utils.py +493 -0
- runbooks/inventory/utils/validation.py +682 -0
- runbooks/inventory/verify_ec2_security_groups.py +1430 -0
- runbooks/main.py +1004 -0
- runbooks/organizations/__init__.py +12 -0
- runbooks/organizations/manager.py +374 -0
- runbooks/security/README.md +447 -0
- runbooks/security/__init__.py +71 -0
- runbooks/{security_baseline → security}/checklist/alternate_contacts.py +8 -1
- runbooks/{security_baseline → security}/checklist/bucket_public_access.py +4 -1
- runbooks/{security_baseline → security}/checklist/cloudwatch_alarm_configuration.py +9 -2
- runbooks/{security_baseline → security}/checklist/guardduty_enabled.py +9 -2
- runbooks/{security_baseline → security}/checklist/multi_region_instance_usage.py +5 -1
- runbooks/{security_baseline → security}/checklist/root_access_key.py +6 -1
- runbooks/{security_baseline → security}/config-origin.json +1 -1
- runbooks/{security_baseline → security}/config.json +1 -1
- runbooks/{security_baseline → security}/permission.json +1 -1
- runbooks/{security_baseline → security}/report_generator.py +10 -2
- runbooks/{security_baseline → security}/report_template_en.html +7 -7
- runbooks/{security_baseline → security}/report_template_jp.html +7 -7
- runbooks/{security_baseline → security}/report_template_kr.html +12 -12
- runbooks/{security_baseline → security}/report_template_vn.html +7 -7
- runbooks/{security_baseline → security}/run_script.py +8 -2
- runbooks/{security_baseline → security}/security_baseline_tester.py +12 -4
- runbooks/{security_baseline → security}/utils/common.py +5 -1
- runbooks/utils/__init__.py +204 -0
- runbooks-0.7.0.dist-info/METADATA +375 -0
- runbooks-0.7.0.dist-info/RECORD +249 -0
- {runbooks-0.2.5.dist-info → runbooks-0.7.0.dist-info}/WHEEL +1 -1
- runbooks-0.7.0.dist-info/entry_points.txt +7 -0
- runbooks-0.7.0.dist-info/licenses/LICENSE +201 -0
- runbooks-0.7.0.dist-info/top_level.txt +3 -0
- runbooks/python101/calculator.py +0 -34
- runbooks/python101/config.py +0 -1
- runbooks/python101/exceptions.py +0 -16
- runbooks/python101/file_manager.py +0 -218
- runbooks/python101/toolkit.py +0 -153
- runbooks-0.2.5.dist-info/METADATA +0 -439
- runbooks-0.2.5.dist-info/RECORD +0 -61
- runbooks-0.2.5.dist-info/entry_points.txt +0 -3
- runbooks-0.2.5.dist-info/top_level.txt +0 -1
- /runbooks/{security_baseline/__init__.py → inventory/tests/script_test_data.py} +0 -0
- /runbooks/{security_baseline → security}/checklist/__init__.py +0 -0
- /runbooks/{security_baseline → security}/checklist/account_level_bucket_public_access.py +0 -0
- /runbooks/{security_baseline → security}/checklist/direct_attached_policy.py +0 -0
- /runbooks/{security_baseline → security}/checklist/iam_password_policy.py +0 -0
- /runbooks/{security_baseline → security}/checklist/iam_user_mfa.py +0 -0
- /runbooks/{security_baseline → security}/checklist/multi_region_trail.py +0 -0
- /runbooks/{security_baseline → security}/checklist/root_mfa.py +0 -0
- /runbooks/{security_baseline → security}/checklist/root_usage.py +0 -0
- /runbooks/{security_baseline → security}/checklist/trail_enabled.py +0 -0
- /runbooks/{security_baseline → security}/checklist/trusted_advisor.py +0 -0
- /runbooks/{security_baseline → security}/utils/__init__.py +0 -0
- /runbooks/{security_baseline → security}/utils/enums.py +0 -0
- /runbooks/{security_baseline → security}/utils/language.py +0 -0
- /runbooks/{security_baseline → security}/utils/level_const.py +0 -0
- /runbooks/{security_baseline → security}/utils/permission_list.py +0 -0
@@ -24,19 +24,19 @@
|
|
24
24
|
</path>
|
25
25
|
</symbol>
|
26
26
|
<symbol id="bi-x-circle-fill" fill="currentColor" viewBox="0 0 16 16">
|
27
|
-
<path
|
27
|
+
<path
|
28
28
|
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z">
|
29
29
|
</path>
|
30
30
|
</symbol>
|
31
31
|
<symbol id="bi-dash-circle" fill="currentColor" viewBox="0 0 16 16">
|
32
|
-
<path
|
32
|
+
<path
|
33
33
|
d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z">
|
34
34
|
</path>
|
35
|
-
<path
|
35
|
+
<path
|
36
36
|
d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z">
|
37
37
|
</path>
|
38
38
|
</symbol>
|
39
|
-
</svg>
|
39
|
+
</svg>
|
40
40
|
|
41
41
|
<div class="container">
|
42
42
|
<div class="row justify-content-center">
|
@@ -88,15 +88,15 @@
|
|
88
88
|
{% for section in result_sections %}
|
89
89
|
<div class="card-body" id="{{ section.level|lower }}-list">
|
90
90
|
<div class="accordion" id="accordionSection{{ section.level }}">
|
91
|
-
|
91
|
+
|
92
92
|
{% for item in section.result_items %}
|
93
93
|
<div class="accordion-item">
|
94
94
|
<h2 class="accordion-header" id="panelsStayOpen-heading-{{ section.level|lower }}-{{ '%02d'|format(loop.index) }}">
|
95
95
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}" aria-expanded="true" aria-controls="panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}">
|
96
96
|
<span class="badge text-bg-{% if section.level == 'Error' %}dark{% elif section.level == 'Info' %}primary{% else %}{{ section.level|lower }}{% endif %} rounded-pill">{{ section.level }}</span>
|
97
|
-
<div class="ms-2 me-auto">{{ item.title }}</div>
|
97
|
+
<div class="ms-2 me-auto">{{ item.title }}</div>
|
98
98
|
</button>
|
99
|
-
</h2>
|
99
|
+
</h2>
|
100
100
|
<div id="panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}" class="accordion-collapse collapse" aria-labelledby="panelsStayOpen-heading-{{ section.level|lower }}-{{ '%02d'|format(loop.index) }}">
|
101
101
|
<div class="accordion-body">
|
102
102
|
<div class="alert alert-{% if section.level == 'Error' %}dark{% elif section.level == 'Info' %}primary{% else %}{{ section.level|lower }}{% endif %} d-flex align-items-center" role="alert">
|
@@ -24,19 +24,19 @@
|
|
24
24
|
</path>
|
25
25
|
</symbol>
|
26
26
|
<symbol id="bi-x-circle-fill" fill="currentColor" viewBox="0 0 16 16">
|
27
|
-
<path
|
27
|
+
<path
|
28
28
|
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z">
|
29
29
|
</path>
|
30
30
|
</symbol>
|
31
31
|
<symbol id="bi-dash-circle" fill="currentColor" viewBox="0 0 16 16">
|
32
|
-
<path
|
32
|
+
<path
|
33
33
|
d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z">
|
34
34
|
</path>
|
35
|
-
<path
|
35
|
+
<path
|
36
36
|
d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z">
|
37
37
|
</path>
|
38
38
|
</symbol>
|
39
|
-
</svg>
|
39
|
+
</svg>
|
40
40
|
|
41
41
|
<div class="container">
|
42
42
|
<div class="row justify-content-center">
|
@@ -88,15 +88,15 @@
|
|
88
88
|
{% for section in result_sections %}
|
89
89
|
<div class="card-body" id="{{ section.level|lower }}-list">
|
90
90
|
<div class="accordion" id="accordionSection{{ section.level }}">
|
91
|
-
|
91
|
+
|
92
92
|
{% for item in section.result_items %}
|
93
93
|
<div class="accordion-item">
|
94
94
|
<h2 class="accordion-header" id="panelsStayOpen-heading-{{ section.level|lower }}-{{ '%02d'|format(loop.index) }}">
|
95
95
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}" aria-expanded="true" aria-controls="panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}">
|
96
96
|
<span class="badge text-bg-{% if section.level == 'Error' %}dark{% elif section.level == 'Info' %}primary{% else %}{{ section.level|lower }}{% endif %} rounded-pill">{{ section.level }}</span>
|
97
|
-
<div class="ms-2 me-auto">{{ item.title }}</div>
|
97
|
+
<div class="ms-2 me-auto">{{ item.title }}</div>
|
98
98
|
</button>
|
99
|
-
</h2>
|
99
|
+
</h2>
|
100
100
|
<div id="panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}" class="accordion-collapse collapse" aria-labelledby="panelsStayOpen-heading-{{ section.level|lower }}-{{ '%02d'|format(loop.index) }}">
|
101
101
|
<div class="accordion-body">
|
102
102
|
<div class="alert alert-{% if section.level == 'Error' %}dark{% elif section.level == 'Info' %}primary{% else %}{{ section.level|lower }}{% endif %} d-flex align-items-center" role="alert">
|
@@ -24,19 +24,19 @@
|
|
24
24
|
</path>
|
25
25
|
</symbol>
|
26
26
|
<symbol id="bi-x-circle-fill" fill="currentColor" viewBox="0 0 16 16">
|
27
|
-
<path
|
27
|
+
<path
|
28
28
|
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z">
|
29
29
|
</path>
|
30
30
|
</symbol>
|
31
31
|
<symbol id="bi-dash-circle" fill="currentColor" viewBox="0 0 16 16">
|
32
|
-
<path
|
32
|
+
<path
|
33
33
|
d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z">
|
34
34
|
</path>
|
35
|
-
<path
|
35
|
+
<path
|
36
36
|
d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z">
|
37
37
|
</path>
|
38
38
|
</symbol>
|
39
|
-
</svg>
|
39
|
+
</svg>
|
40
40
|
|
41
41
|
<div class="container">
|
42
42
|
<div class="row justify-content-center">
|
@@ -88,15 +88,15 @@
|
|
88
88
|
{% for section in result_sections %}
|
89
89
|
<div class="card-body" id="{{ section.level|lower }}-list">
|
90
90
|
<div class="accordion" id="accordionSection{{ section.level }}">
|
91
|
-
|
91
|
+
|
92
92
|
{% for item in section.result_items %}
|
93
93
|
<div class="accordion-item">
|
94
94
|
<h2 class="accordion-header" id="panelsStayOpen-heading-{{ section.level|lower }}-{{ '%02d'|format(loop.index) }}">
|
95
95
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}" aria-expanded="true" aria-controls="panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}">
|
96
96
|
<span class="badge text-bg-{% if section.level == 'Error' %}dark{% elif section.level == 'Info' %}primary{% else %}{{ section.level|lower }}{% endif %} rounded-pill">{{ section.level }}</span>
|
97
|
-
<div class="ms-2 me-auto">{{ item.title }}</div>
|
97
|
+
<div class="ms-2 me-auto">{{ item.title }}</div>
|
98
98
|
</button>
|
99
|
-
</h2>
|
99
|
+
</h2>
|
100
100
|
<div id="panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}" class="accordion-collapse collapse" aria-labelledby="panelsStayOpen-heading-{{ section.level|lower }}-{{ '%02d'|format(loop.index) }}">
|
101
101
|
<div class="accordion-body">
|
102
102
|
<div class="alert alert-{% if section.level == 'Error' %}dark{% elif section.level == 'Info' %}primary{% else %}{{ section.level|lower }}{% endif %} d-flex align-items-center" role="alert">
|
@@ -141,7 +141,7 @@
|
|
141
141
|
<div class="col-10 col-lg-8">
|
142
142
|
|
143
143
|
<!-- Below this section, you can add content references for each language. -->
|
144
|
-
|
144
|
+
|
145
145
|
<br>
|
146
146
|
<div class="row">
|
147
147
|
<div class="alert alert-primary d-flex align-items-center" role="alert">
|
@@ -156,7 +156,7 @@
|
|
156
156
|
</span>
|
157
157
|
</div>
|
158
158
|
</div>
|
159
|
-
|
159
|
+
|
160
160
|
<div class="row">
|
161
161
|
<div class="alert alert-primary d-flex align-items-center" role="alert">
|
162
162
|
<svg aria-label="Info:" class="bi flex-shrink-0 me-2" width="24" height="24" role="img">
|
@@ -169,21 +169,21 @@
|
|
169
169
|
</span>
|
170
170
|
</div>
|
171
171
|
</div>
|
172
|
-
|
172
|
+
|
173
173
|
<div class="row">
|
174
174
|
<div class="alert alert-primary d-flex align-items-center" role="alert">
|
175
175
|
<svg aria-label="Info:" class="bi flex-shrink-0 me-2" width="24" height="24" role="img">
|
176
176
|
<use xlink:href="#info-fill"></use>
|
177
177
|
</svg>
|
178
178
|
<span>
|
179
|
-
AWS Trusted Advisor 사용 방법에 대한 자세한 내용은
|
179
|
+
AWS Trusted Advisor 사용 방법에 대한 자세한 내용은
|
180
180
|
<a href="https://aws.amazon.com/ko/premiumsupport/knowledge-center/trusted-advisor-intro/" target="_blank" style="overflow:hidden;word-break:break-all;">
|
181
181
|
여기
|
182
182
|
</a>를 눌러 확인해주세요.
|
183
183
|
</span>
|
184
184
|
</div>
|
185
185
|
</div>
|
186
|
-
|
186
|
+
|
187
187
|
<div class="col"></div>
|
188
188
|
</div>
|
189
189
|
</div>
|
@@ -24,19 +24,19 @@
|
|
24
24
|
</path>
|
25
25
|
</symbol>
|
26
26
|
<symbol id="bi-x-circle-fill" fill="currentColor" viewBox="0 0 16 16">
|
27
|
-
<path
|
27
|
+
<path
|
28
28
|
d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z">
|
29
29
|
</path>
|
30
30
|
</symbol>
|
31
31
|
<symbol id="bi-dash-circle" fill="currentColor" viewBox="0 0 16 16">
|
32
|
-
<path
|
32
|
+
<path
|
33
33
|
d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z">
|
34
34
|
</path>
|
35
|
-
<path
|
35
|
+
<path
|
36
36
|
d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z">
|
37
37
|
</path>
|
38
38
|
</symbol>
|
39
|
-
</svg>
|
39
|
+
</svg>
|
40
40
|
|
41
41
|
<div class="container">
|
42
42
|
<div class="row justify-content-center">
|
@@ -88,15 +88,15 @@
|
|
88
88
|
{% for section in result_sections %}
|
89
89
|
<div class="card-body" id="{{ section.level|lower }}-list">
|
90
90
|
<div class="accordion" id="accordionSection{{ section.level }}">
|
91
|
-
|
91
|
+
|
92
92
|
{% for item in section.result_items %}
|
93
93
|
<div class="accordion-item">
|
94
94
|
<h2 class="accordion-header" id="panelsStayOpen-heading-{{ section.level|lower }}-{{ '%02d'|format(loop.index) }}">
|
95
95
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}" aria-expanded="true" aria-controls="panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}">
|
96
96
|
<span class="badge text-bg-{% if section.level == 'Error' %}dark{% elif section.level == 'Info' %}primary{% else %}{{ section.level|lower }}{% endif %} rounded-pill">{{ section.level }}</span>
|
97
|
-
<div class="ms-2 me-auto">{{ item.title }}</div>
|
97
|
+
<div class="ms-2 me-auto">{{ item.title }}</div>
|
98
98
|
</button>
|
99
|
-
</h2>
|
99
|
+
</h2>
|
100
100
|
<div id="panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}" class="accordion-collapse collapse" aria-labelledby="panelsStayOpen-heading-{{ section.level|lower }}-{{ '%02d'|format(loop.index) }}">
|
101
101
|
<div class="accordion-body">
|
102
102
|
<div class="alert alert-{% if section.level == 'Error' %}dark{% elif section.level == 'Info' %}primary{% else %}{{ section.level|lower }}{% endif %} d-flex align-items-center" role="alert">
|
@@ -37,7 +37,9 @@ def parse_arguments():
|
|
37
37
|
description="AWS Security Baseline Tester - Evaluate your AWS account's security configuration."
|
38
38
|
)
|
39
39
|
parser.add_argument(
|
40
|
-
"--profile",
|
40
|
+
"--profile",
|
41
|
+
default="default",
|
42
|
+
help="AWS IAM profile to use for authentication (default: 'default').",
|
41
43
|
)
|
42
44
|
parser.add_argument(
|
43
45
|
"--language",
|
@@ -45,7 +47,11 @@ def parse_arguments():
|
|
45
47
|
default="EN",
|
46
48
|
help="Language for the Security Baseline Report (default: 'EN').",
|
47
49
|
)
|
48
|
-
parser.add_argument(
|
50
|
+
parser.add_argument(
|
51
|
+
"--output",
|
52
|
+
default=None,
|
53
|
+
help="Custom output directory for HTML results (default: ./results).",
|
54
|
+
)
|
49
55
|
return parser.parse_args()
|
50
56
|
|
51
57
|
|
@@ -10,7 +10,7 @@ import boto3
|
|
10
10
|
import botocore
|
11
11
|
|
12
12
|
from . import report_generator
|
13
|
-
from .
|
13
|
+
from . import checklist # noqa: F403
|
14
14
|
from .utils import common, language, level_const
|
15
15
|
|
16
16
|
# from .utils.language import get_translator
|
@@ -60,7 +60,10 @@ class SecurityBaselineTester:
|
|
60
60
|
|
61
61
|
logging.info(self.translator.translate("test_completed"))
|
62
62
|
except Exception as e:
|
63
|
-
logging.error(
|
63
|
+
logging.error(
|
64
|
+
f"An error occurred during the security baseline test: {str(e)}",
|
65
|
+
exc_info=True,
|
66
|
+
)
|
64
67
|
|
65
68
|
def _validate_session(self):
|
66
69
|
if self.session.region_name is None:
|
@@ -105,7 +108,7 @@ class SecurityBaselineTester:
|
|
105
108
|
|
106
109
|
def _run_check(self, check_name, credential_report):
|
107
110
|
# check_module = __import__(f"checklist.{check_name}", fromlist=[check_name])
|
108
|
-
check_module = importlib.import_module(f"runbooks.
|
111
|
+
check_module = importlib.import_module(f"runbooks.security.checklist.{check_name}")
|
109
112
|
check_method = getattr(check_module, self.config["checks"][check_name])
|
110
113
|
translator = language.get_translator(check_name, self.language)
|
111
114
|
|
@@ -122,7 +125,12 @@ class SecurityBaselineTester:
|
|
122
125
|
"iam_password_policy",
|
123
126
|
]:
|
124
127
|
return check_method(self.session, translator)
|
125
|
-
elif check_name in [
|
128
|
+
elif check_name in [
|
129
|
+
"root_mfa",
|
130
|
+
"root_usage",
|
131
|
+
"root_access_key",
|
132
|
+
"iam_user_mfa",
|
133
|
+
]:
|
126
134
|
return check_method(self.session, translator, credential_report)
|
127
135
|
elif check_name == "trusted_advisor":
|
128
136
|
return check_method(translator)
|
@@ -15,7 +15,11 @@ class Ret:
|
|
15
15
|
self.headers = {"Content-Type": "text/html;charset=UTF-8"}
|
16
16
|
|
17
17
|
def to_dict(self) -> dict:
|
18
|
-
return {
|
18
|
+
return {
|
19
|
+
"statusCode": self.status_code,
|
20
|
+
"body": self.body,
|
21
|
+
"headers": self.headers,
|
22
|
+
}
|
19
23
|
|
20
24
|
|
21
25
|
class CheckResult:
|
runbooks/utils/__init__.py
CHANGED
@@ -0,0 +1,204 @@
|
|
1
|
+
"""
|
2
|
+
Utilities package for CloudOps Runbooks.
|
3
|
+
|
4
|
+
This package provides utility modules including logging, configuration,
|
5
|
+
and helper functions used throughout the runbooks package.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import sys
|
9
|
+
from pathlib import Path
|
10
|
+
from typing import Optional
|
11
|
+
|
12
|
+
try:
|
13
|
+
from loguru import logger
|
14
|
+
|
15
|
+
_HAS_LOGURU = True
|
16
|
+
except ImportError:
|
17
|
+
import logging
|
18
|
+
|
19
|
+
logger = logging.getLogger(__name__)
|
20
|
+
_HAS_LOGURU = False
|
21
|
+
|
22
|
+
# Legacy utilities
|
23
|
+
from runbooks.utils.logger import configure_logger
|
24
|
+
|
25
|
+
|
26
|
+
def setup_logging(debug: bool = False, log_file: Optional[str] = None) -> None:
|
27
|
+
"""
|
28
|
+
Configure logging for the application.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
debug: Enable debug logging if True
|
32
|
+
log_file: Optional path to log file
|
33
|
+
"""
|
34
|
+
if _HAS_LOGURU:
|
35
|
+
from loguru import logger as loguru_logger
|
36
|
+
|
37
|
+
# Remove default handler
|
38
|
+
loguru_logger.remove()
|
39
|
+
|
40
|
+
# Console handler with appropriate level
|
41
|
+
log_level = "DEBUG" if debug else "INFO"
|
42
|
+
loguru_logger.add(
|
43
|
+
sys.stderr,
|
44
|
+
level=log_level,
|
45
|
+
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
|
46
|
+
colorize=True,
|
47
|
+
)
|
48
|
+
|
49
|
+
# File handler if specified
|
50
|
+
if log_file:
|
51
|
+
log_path = Path(log_file)
|
52
|
+
log_path.parent.mkdir(parents=True, exist_ok=True)
|
53
|
+
|
54
|
+
loguru_logger.add(
|
55
|
+
log_path,
|
56
|
+
level="DEBUG",
|
57
|
+
format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}",
|
58
|
+
rotation="10 MB",
|
59
|
+
retention="7 days",
|
60
|
+
compression="zip",
|
61
|
+
)
|
62
|
+
|
63
|
+
loguru_logger.info(f"Logging initialized with level: {log_level}")
|
64
|
+
else:
|
65
|
+
# Fallback to standard logging
|
66
|
+
import logging
|
67
|
+
|
68
|
+
log_level = logging.DEBUG if debug else logging.INFO
|
69
|
+
logging.basicConfig(
|
70
|
+
level=log_level,
|
71
|
+
format="%(asctime)s | %(levelname)-8s | %(name)s:%(funcName)s:%(lineno)d - %(message)s",
|
72
|
+
handlers=[logging.StreamHandler(sys.stderr)],
|
73
|
+
)
|
74
|
+
|
75
|
+
if log_file:
|
76
|
+
log_path = Path(log_file)
|
77
|
+
log_path.parent.mkdir(parents=True, exist_ok=True)
|
78
|
+
file_handler = logging.FileHandler(log_path)
|
79
|
+
file_handler.setFormatter(
|
80
|
+
logging.Formatter("%(asctime)s | %(levelname)-8s | %(name)s:%(funcName)s:%(lineno)d - %(message)s")
|
81
|
+
)
|
82
|
+
logging.getLogger().addHandler(file_handler)
|
83
|
+
|
84
|
+
logging.info(f"Logging initialized with level: {log_level}")
|
85
|
+
|
86
|
+
|
87
|
+
def validate_aws_profile(profile: str) -> bool:
|
88
|
+
"""
|
89
|
+
Validate that an AWS profile exists in credentials.
|
90
|
+
|
91
|
+
Args:
|
92
|
+
profile: AWS profile name to validate
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
True if profile exists, False otherwise
|
96
|
+
"""
|
97
|
+
import configparser
|
98
|
+
|
99
|
+
try:
|
100
|
+
# Check ~/.aws/credentials
|
101
|
+
credentials_path = Path.home() / ".aws" / "credentials"
|
102
|
+
if credentials_path.exists():
|
103
|
+
config = configparser.ConfigParser()
|
104
|
+
config.read(credentials_path)
|
105
|
+
if profile in config.sections():
|
106
|
+
return True
|
107
|
+
|
108
|
+
# Check ~/.aws/config
|
109
|
+
config_path = Path.home() / ".aws" / "config"
|
110
|
+
if config_path.exists():
|
111
|
+
config = configparser.ConfigParser()
|
112
|
+
config.read(config_path)
|
113
|
+
profile_section = f"profile {profile}" if profile != "default" else "default"
|
114
|
+
if profile_section in config.sections():
|
115
|
+
return True
|
116
|
+
|
117
|
+
return False
|
118
|
+
except Exception as e:
|
119
|
+
logger.warning(f"Error validating AWS profile '{profile}': {e}")
|
120
|
+
return False
|
121
|
+
|
122
|
+
|
123
|
+
def ensure_directory(path: Path) -> Path:
|
124
|
+
"""
|
125
|
+
Ensure directory exists, creating it if necessary.
|
126
|
+
|
127
|
+
Args:
|
128
|
+
path: Directory path to ensure
|
129
|
+
|
130
|
+
Returns:
|
131
|
+
The directory path
|
132
|
+
"""
|
133
|
+
path.mkdir(parents=True, exist_ok=True)
|
134
|
+
return path
|
135
|
+
|
136
|
+
|
137
|
+
def format_size(size_bytes: int) -> str:
|
138
|
+
"""
|
139
|
+
Format byte size in human readable format.
|
140
|
+
|
141
|
+
Args:
|
142
|
+
size_bytes: Size in bytes
|
143
|
+
|
144
|
+
Returns:
|
145
|
+
Formatted size string (e.g., "1.5 GB")
|
146
|
+
"""
|
147
|
+
if size_bytes == 0:
|
148
|
+
return "0 B"
|
149
|
+
|
150
|
+
size_names = ["B", "KB", "MB", "GB", "TB", "PB"]
|
151
|
+
size_index = 0
|
152
|
+
size = float(size_bytes)
|
153
|
+
|
154
|
+
while size >= 1024 and size_index < len(size_names) - 1:
|
155
|
+
size /= 1024
|
156
|
+
size_index += 1
|
157
|
+
|
158
|
+
return f"{size:.1f} {size_names[size_index]}"
|
159
|
+
|
160
|
+
|
161
|
+
def retry_with_backoff(max_retries: int = 3, backoff_factor: float = 1.0):
|
162
|
+
"""
|
163
|
+
Decorator for retrying operations with exponential backoff.
|
164
|
+
|
165
|
+
Args:
|
166
|
+
max_retries: Maximum number of retry attempts
|
167
|
+
backoff_factor: Factor for exponential backoff
|
168
|
+
"""
|
169
|
+
import time
|
170
|
+
from functools import wraps
|
171
|
+
|
172
|
+
def decorator(func):
|
173
|
+
@wraps(func)
|
174
|
+
def wrapper(*args, **kwargs):
|
175
|
+
last_exception = None
|
176
|
+
|
177
|
+
for attempt in range(max_retries + 1):
|
178
|
+
try:
|
179
|
+
return func(*args, **kwargs)
|
180
|
+
except Exception as e:
|
181
|
+
last_exception = e
|
182
|
+
if attempt == max_retries:
|
183
|
+
logger.error(f"Failed after {max_retries} retries: {e}")
|
184
|
+
raise
|
185
|
+
|
186
|
+
wait_time = backoff_factor * (2**attempt)
|
187
|
+
logger.warning(f"Attempt {attempt + 1} failed: {e}. Retrying in {wait_time}s...")
|
188
|
+
time.sleep(wait_time)
|
189
|
+
|
190
|
+
raise last_exception
|
191
|
+
|
192
|
+
return wrapper
|
193
|
+
|
194
|
+
return decorator
|
195
|
+
|
196
|
+
|
197
|
+
__all__ = [
|
198
|
+
"setup_logging",
|
199
|
+
"validate_aws_profile",
|
200
|
+
"ensure_directory",
|
201
|
+
"format_size",
|
202
|
+
"retry_with_backoff",
|
203
|
+
"configure_logger",
|
204
|
+
]
|