aws-cis-controls-assessment 1.0.3__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.
- aws_cis_assessment/__init__.py +11 -0
- aws_cis_assessment/cli/__init__.py +3 -0
- aws_cis_assessment/cli/examples.py +274 -0
- aws_cis_assessment/cli/main.py +1259 -0
- aws_cis_assessment/cli/utils.py +356 -0
- aws_cis_assessment/config/__init__.py +1 -0
- aws_cis_assessment/config/config_loader.py +328 -0
- aws_cis_assessment/config/rules/cis_controls_ig1.yaml +590 -0
- aws_cis_assessment/config/rules/cis_controls_ig2.yaml +412 -0
- aws_cis_assessment/config/rules/cis_controls_ig3.yaml +100 -0
- aws_cis_assessment/controls/__init__.py +1 -0
- aws_cis_assessment/controls/base_control.py +400 -0
- aws_cis_assessment/controls/ig1/__init__.py +239 -0
- aws_cis_assessment/controls/ig1/control_1_1.py +586 -0
- aws_cis_assessment/controls/ig1/control_2_2.py +231 -0
- aws_cis_assessment/controls/ig1/control_3_3.py +718 -0
- aws_cis_assessment/controls/ig1/control_3_4.py +235 -0
- aws_cis_assessment/controls/ig1/control_4_1.py +461 -0
- aws_cis_assessment/controls/ig1/control_access_keys.py +310 -0
- aws_cis_assessment/controls/ig1/control_advanced_security.py +512 -0
- aws_cis_assessment/controls/ig1/control_backup_recovery.py +510 -0
- aws_cis_assessment/controls/ig1/control_cloudtrail_logging.py +197 -0
- aws_cis_assessment/controls/ig1/control_critical_security.py +422 -0
- aws_cis_assessment/controls/ig1/control_data_protection.py +898 -0
- aws_cis_assessment/controls/ig1/control_iam_advanced.py +573 -0
- aws_cis_assessment/controls/ig1/control_iam_governance.py +493 -0
- aws_cis_assessment/controls/ig1/control_iam_policies.py +383 -0
- aws_cis_assessment/controls/ig1/control_instance_optimization.py +100 -0
- aws_cis_assessment/controls/ig1/control_network_enhancements.py +203 -0
- aws_cis_assessment/controls/ig1/control_network_security.py +672 -0
- aws_cis_assessment/controls/ig1/control_s3_enhancements.py +173 -0
- aws_cis_assessment/controls/ig1/control_s3_security.py +422 -0
- aws_cis_assessment/controls/ig1/control_vpc_security.py +235 -0
- aws_cis_assessment/controls/ig2/__init__.py +172 -0
- aws_cis_assessment/controls/ig2/control_3_10.py +698 -0
- aws_cis_assessment/controls/ig2/control_3_11.py +1330 -0
- aws_cis_assessment/controls/ig2/control_5_2.py +393 -0
- aws_cis_assessment/controls/ig2/control_advanced_encryption.py +355 -0
- aws_cis_assessment/controls/ig2/control_codebuild_security.py +263 -0
- aws_cis_assessment/controls/ig2/control_encryption_rest.py +382 -0
- aws_cis_assessment/controls/ig2/control_encryption_transit.py +382 -0
- aws_cis_assessment/controls/ig2/control_network_ha.py +467 -0
- aws_cis_assessment/controls/ig2/control_remaining_encryption.py +426 -0
- aws_cis_assessment/controls/ig2/control_remaining_rules.py +363 -0
- aws_cis_assessment/controls/ig2/control_service_logging.py +402 -0
- aws_cis_assessment/controls/ig3/__init__.py +49 -0
- aws_cis_assessment/controls/ig3/control_12_8.py +395 -0
- aws_cis_assessment/controls/ig3/control_13_1.py +467 -0
- aws_cis_assessment/controls/ig3/control_3_14.py +523 -0
- aws_cis_assessment/controls/ig3/control_7_1.py +359 -0
- aws_cis_assessment/core/__init__.py +1 -0
- aws_cis_assessment/core/accuracy_validator.py +425 -0
- aws_cis_assessment/core/assessment_engine.py +1266 -0
- aws_cis_assessment/core/audit_trail.py +491 -0
- aws_cis_assessment/core/aws_client_factory.py +313 -0
- aws_cis_assessment/core/error_handler.py +607 -0
- aws_cis_assessment/core/models.py +166 -0
- aws_cis_assessment/core/scoring_engine.py +459 -0
- aws_cis_assessment/reporters/__init__.py +8 -0
- aws_cis_assessment/reporters/base_reporter.py +454 -0
- aws_cis_assessment/reporters/csv_reporter.py +835 -0
- aws_cis_assessment/reporters/html_reporter.py +2162 -0
- aws_cis_assessment/reporters/json_reporter.py +561 -0
- aws_cis_controls_assessment-1.0.3.dist-info/METADATA +248 -0
- aws_cis_controls_assessment-1.0.3.dist-info/RECORD +77 -0
- aws_cis_controls_assessment-1.0.3.dist-info/WHEEL +5 -0
- aws_cis_controls_assessment-1.0.3.dist-info/entry_points.txt +2 -0
- aws_cis_controls_assessment-1.0.3.dist-info/licenses/LICENSE +21 -0
- aws_cis_controls_assessment-1.0.3.dist-info/top_level.txt +2 -0
- docs/README.md +94 -0
- docs/assessment-logic.md +766 -0
- docs/cli-reference.md +698 -0
- docs/config-rule-mappings.md +393 -0
- docs/developer-guide.md +858 -0
- docs/installation.md +299 -0
- docs/troubleshooting.md +634 -0
- docs/user-guide.md +487 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Control 2.2: Ensure Authorized Software is Currently Supported assessments."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, List, Any
|
|
4
|
+
import logging
|
|
5
|
+
from botocore.exceptions import ClientError
|
|
6
|
+
|
|
7
|
+
from aws_cis_assessment.controls.base_control import BaseConfigRuleAssessment
|
|
8
|
+
from aws_cis_assessment.core.models import ComplianceResult, ComplianceStatus
|
|
9
|
+
from aws_cis_assessment.core.aws_client_factory import AWSClientFactory
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ElasticBeanstalkManagedUpdatesEnabledAssessment(BaseConfigRuleAssessment):
|
|
15
|
+
"""Assessment for elastic-beanstalk-managed-updates-enabled Config rule."""
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
"""Initialize Elastic Beanstalk managed updates assessment."""
|
|
19
|
+
super().__init__(
|
|
20
|
+
rule_name="elastic-beanstalk-managed-updates-enabled",
|
|
21
|
+
control_id="2.2",
|
|
22
|
+
resource_types=["AWS::ElasticBeanstalk::Environment"]
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
|
|
26
|
+
"""Get all Elastic Beanstalk environments in the region."""
|
|
27
|
+
if resource_type != "AWS::ElasticBeanstalk::Environment":
|
|
28
|
+
return []
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
eb_client = aws_factory.get_client('elasticbeanstalk', region)
|
|
32
|
+
|
|
33
|
+
response = aws_factory.aws_api_call_with_retry(
|
|
34
|
+
lambda: eb_client.describe_environments()
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
environments = []
|
|
38
|
+
for env in response.get('Environments', []):
|
|
39
|
+
if env.get('Status') not in ['Terminated', 'Terminating']:
|
|
40
|
+
environments.append({
|
|
41
|
+
'EnvironmentId': env.get('EnvironmentId'),
|
|
42
|
+
'EnvironmentName': env.get('EnvironmentName'),
|
|
43
|
+
'ApplicationName': env.get('ApplicationName'),
|
|
44
|
+
'Status': env.get('Status'),
|
|
45
|
+
'PlatformArn': env.get('PlatformArn')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
logger.debug(f"Found {len(environments)} Elastic Beanstalk environments in region {region}")
|
|
49
|
+
return environments
|
|
50
|
+
|
|
51
|
+
except ClientError as e:
|
|
52
|
+
logger.error(f"Error retrieving Elastic Beanstalk environments in region {region}: {e}")
|
|
53
|
+
raise
|
|
54
|
+
except Exception as e:
|
|
55
|
+
logger.error(f"Unexpected error retrieving Elastic Beanstalk environments in region {region}: {e}")
|
|
56
|
+
raise
|
|
57
|
+
|
|
58
|
+
def _evaluate_resource_compliance(self, resource: Dict[str, Any], aws_factory: AWSClientFactory, region: str) -> ComplianceResult:
|
|
59
|
+
"""Evaluate if Elastic Beanstalk environment has managed updates enabled."""
|
|
60
|
+
env_id = resource.get('EnvironmentId', 'unknown')
|
|
61
|
+
env_name = resource.get('EnvironmentName', 'unknown')
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
eb_client = aws_factory.get_client('elasticbeanstalk', region)
|
|
65
|
+
|
|
66
|
+
# Get configuration settings for the environment
|
|
67
|
+
response = aws_factory.aws_api_call_with_retry(
|
|
68
|
+
lambda: eb_client.describe_configuration_settings(
|
|
69
|
+
ApplicationName=resource.get('ApplicationName'),
|
|
70
|
+
EnvironmentName=env_name
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
managed_updates_enabled = False
|
|
75
|
+
update_level = None
|
|
76
|
+
|
|
77
|
+
for config in response.get('ConfigurationSettings', []):
|
|
78
|
+
for option in config.get('OptionSettings', []):
|
|
79
|
+
if (option.get('Namespace') == 'aws:elasticbeanstalk:managedactions' and
|
|
80
|
+
option.get('OptionName') == 'ManagedActionsEnabled'):
|
|
81
|
+
managed_updates_enabled = option.get('Value', '').lower() == 'true'
|
|
82
|
+
elif (option.get('Namespace') == 'aws:elasticbeanstalk:managedactions:platformupdate' and
|
|
83
|
+
option.get('OptionName') == 'UpdateLevel'):
|
|
84
|
+
update_level = option.get('Value')
|
|
85
|
+
|
|
86
|
+
if managed_updates_enabled:
|
|
87
|
+
compliance_status = ComplianceStatus.COMPLIANT
|
|
88
|
+
evaluation_reason = f"Environment {env_name} has managed platform updates enabled"
|
|
89
|
+
if update_level:
|
|
90
|
+
evaluation_reason += f" (Update level: {update_level})"
|
|
91
|
+
else:
|
|
92
|
+
compliance_status = ComplianceStatus.NON_COMPLIANT
|
|
93
|
+
evaluation_reason = f"Environment {env_name} does not have managed platform updates enabled"
|
|
94
|
+
|
|
95
|
+
except ClientError as e:
|
|
96
|
+
error_code = e.response.get('Error', {}).get('Code', '')
|
|
97
|
+
if error_code in ['AccessDenied', 'UnauthorizedOperation']:
|
|
98
|
+
compliance_status = ComplianceStatus.ERROR
|
|
99
|
+
evaluation_reason = f"Insufficient permissions to check managed updates for environment {env_name}"
|
|
100
|
+
else:
|
|
101
|
+
compliance_status = ComplianceStatus.ERROR
|
|
102
|
+
evaluation_reason = f"Error checking managed updates for environment {env_name}: {str(e)}"
|
|
103
|
+
except Exception as e:
|
|
104
|
+
compliance_status = ComplianceStatus.ERROR
|
|
105
|
+
evaluation_reason = f"Unexpected error checking managed updates for environment {env_name}: {str(e)}"
|
|
106
|
+
|
|
107
|
+
return ComplianceResult(
|
|
108
|
+
resource_id=env_id,
|
|
109
|
+
resource_type="AWS::ElasticBeanstalk::Environment",
|
|
110
|
+
compliance_status=compliance_status,
|
|
111
|
+
evaluation_reason=evaluation_reason,
|
|
112
|
+
config_rule_name=self.rule_name,
|
|
113
|
+
region=region
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def _get_rule_remediation_steps(self) -> List[str]:
|
|
117
|
+
"""Get specific remediation steps for Elastic Beanstalk managed updates."""
|
|
118
|
+
return [
|
|
119
|
+
"Identify Elastic Beanstalk environments without managed platform updates enabled",
|
|
120
|
+
"For each environment, enable managed platform updates:",
|
|
121
|
+
" 1. Go to the Elastic Beanstalk console",
|
|
122
|
+
" 2. Select the environment",
|
|
123
|
+
" 3. Go to Configuration > Managed platform updates",
|
|
124
|
+
" 4. Enable managed platform updates",
|
|
125
|
+
" 5. Configure update level (patch or minor)",
|
|
126
|
+
" 6. Set maintenance window preferences",
|
|
127
|
+
"Use AWS CLI: aws elasticbeanstalk put-configuration-template with managed actions enabled",
|
|
128
|
+
"Monitor platform update notifications and apply updates during maintenance windows"
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class ECSFargateLatestPlatformVersionAssessment(BaseConfigRuleAssessment):
|
|
133
|
+
"""Assessment for ecs-fargate-latest-platform-version Config rule."""
|
|
134
|
+
|
|
135
|
+
def __init__(self):
|
|
136
|
+
"""Initialize ECS Fargate platform version assessment."""
|
|
137
|
+
super().__init__(
|
|
138
|
+
rule_name="ecs-fargate-latest-platform-version",
|
|
139
|
+
control_id="2.2",
|
|
140
|
+
resource_types=["AWS::ECS::Service"]
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def _get_resources(self, aws_factory: AWSClientFactory, resource_type: str, region: str) -> List[Dict[str, Any]]:
|
|
144
|
+
"""Get all ECS services using Fargate in the region."""
|
|
145
|
+
if resource_type != "AWS::ECS::Service":
|
|
146
|
+
return []
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
ecs_client = aws_factory.get_client('ecs', region)
|
|
150
|
+
|
|
151
|
+
# Get all clusters
|
|
152
|
+
clusters_response = aws_factory.aws_api_call_with_retry(
|
|
153
|
+
lambda: ecs_client.list_clusters()
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
services = []
|
|
157
|
+
for cluster_arn in clusters_response.get('clusterArns', []):
|
|
158
|
+
# Get services in each cluster
|
|
159
|
+
services_response = aws_factory.aws_api_call_with_retry(
|
|
160
|
+
lambda: ecs_client.list_services(cluster=cluster_arn)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if services_response.get('serviceArns'):
|
|
164
|
+
# Get detailed service information
|
|
165
|
+
services_detail = aws_factory.aws_api_call_with_retry(
|
|
166
|
+
lambda: ecs_client.describe_services(
|
|
167
|
+
cluster=cluster_arn,
|
|
168
|
+
services=services_response['serviceArns']
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
for service in services_detail.get('services', []):
|
|
173
|
+
# Only include Fargate services
|
|
174
|
+
if service.get('launchType') == 'FARGATE' or 'FARGATE' in service.get('capacityProviderStrategy', []):
|
|
175
|
+
services.append({
|
|
176
|
+
'ServiceArn': service.get('serviceArn'),
|
|
177
|
+
'ServiceName': service.get('serviceName'),
|
|
178
|
+
'ClusterArn': cluster_arn,
|
|
179
|
+
'LaunchType': service.get('launchType'),
|
|
180
|
+
'PlatformVersion': service.get('platformVersion'),
|
|
181
|
+
'TaskDefinition': service.get('taskDefinition'),
|
|
182
|
+
'Status': service.get('status')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
logger.debug(f"Found {len(services)} ECS Fargate services in region {region}")
|
|
186
|
+
return services
|
|
187
|
+
|
|
188
|
+
except ClientError as e:
|
|
189
|
+
logger.error(f"Error retrieving ECS services in region {region}: {e}")
|
|
190
|
+
raise
|
|
191
|
+
except Exception as e:
|
|
192
|
+
logger.error(f"Unexpected error retrieving ECS services in region {region}: {e}")
|
|
193
|
+
raise
|
|
194
|
+
|
|
195
|
+
def _evaluate_resource_compliance(self, resource: Dict[str, Any], aws_factory: AWSClientFactory, region: str) -> ComplianceResult:
|
|
196
|
+
"""Evaluate if ECS Fargate service uses the latest platform version."""
|
|
197
|
+
service_arn = resource.get('ServiceArn', 'unknown')
|
|
198
|
+
service_name = resource.get('ServiceName', 'unknown')
|
|
199
|
+
platform_version = resource.get('PlatformVersion', 'LATEST')
|
|
200
|
+
|
|
201
|
+
# LATEST is the recommended setting as it automatically uses the most recent platform version
|
|
202
|
+
if platform_version == 'LATEST':
|
|
203
|
+
compliance_status = ComplianceStatus.COMPLIANT
|
|
204
|
+
evaluation_reason = f"ECS service {service_name} uses LATEST platform version"
|
|
205
|
+
else:
|
|
206
|
+
compliance_status = ComplianceStatus.NON_COMPLIANT
|
|
207
|
+
evaluation_reason = f"ECS service {service_name} uses specific platform version {platform_version} instead of LATEST"
|
|
208
|
+
|
|
209
|
+
return ComplianceResult(
|
|
210
|
+
resource_id=service_arn,
|
|
211
|
+
resource_type="AWS::ECS::Service",
|
|
212
|
+
compliance_status=compliance_status,
|
|
213
|
+
evaluation_reason=evaluation_reason,
|
|
214
|
+
config_rule_name=self.rule_name,
|
|
215
|
+
region=region
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
def _get_rule_remediation_steps(self) -> List[str]:
|
|
219
|
+
"""Get specific remediation steps for ECS Fargate platform versions."""
|
|
220
|
+
return [
|
|
221
|
+
"Identify ECS Fargate services not using the LATEST platform version",
|
|
222
|
+
"For each service, update to use LATEST platform version:",
|
|
223
|
+
" 1. Go to the ECS console",
|
|
224
|
+
" 2. Select the cluster and service",
|
|
225
|
+
" 3. Update the service configuration",
|
|
226
|
+
" 4. Set Platform version to LATEST",
|
|
227
|
+
" 5. Deploy the updated service",
|
|
228
|
+
"Use AWS CLI: aws ecs update-service --cluster <cluster> --service <service> --platform-version LATEST",
|
|
229
|
+
"Monitor service deployments to ensure successful updates",
|
|
230
|
+
"Set up automated deployment pipelines to use LATEST platform version by default"
|
|
231
|
+
]
|