feat(config): add config service and checks and check43 (#1441)

Co-authored-by: Pepe Fagoaga <pepe@verica.io>
This commit is contained in:
Sergio Garcia
2022-10-31 14:37:59 +01:00
committed by GitHub
parent adf04ba632
commit 3e749dd652
34 changed files with 563 additions and 424 deletions

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
CHECK_ID_check25="2.5"
CHECK_TITLE_check25="[check25] Ensure AWS Config is enabled in all regions"
CHECK_SCORED_check25="SCORED"
CHECK_CIS_LEVEL_check25="LEVEL1"
CHECK_SEVERITY_check25="Medium"
CHECK_ASFF_TYPE_check25="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ALTERNATE_check205="check25"
CHECK_ASFF_COMPLIANCE_TYPE_check25="ens-op.exp.1.aws.cfg.1"
CHECK_SERVICENAME_check25="config"
CHECK_RISK_check25='The AWS configuration item history captured by AWS Config enables security analysis; resource change tracking; and compliance auditing.'
CHECK_REMEDIATION_check25='It is recommended to enable AWS Config be enabled in all regions.'
CHECK_DOC_check25='https://aws.amazon.com/blogs/mt/aws-config-best-practices/'
CHECK_CAF_EPIC_check25='Logging and Monitoring'
check25(){
# "Ensure AWS Config is enabled in all regions (Scored)"
for regx in $REGIONS; do
CHECK_AWSCONFIG_RECORDING=$($AWSCLI configservice describe-configuration-recorder-status $PROFILE_OPT --region $regx --query 'ConfigurationRecordersStatus[*].recording' --output text 2>&1)
CHECK_AWSCONFIG_STATUS=$($AWSCLI configservice describe-configuration-recorder-status $PROFILE_OPT --region $regx --query 'ConfigurationRecordersStatus[*].lastStatus' --output text 2>&1)
if [[ $(echo "$CHECK_AWSCONFIG_STATUS" | grep AccessDenied) ]]; then
textInfo "$regx: Access Denied trying to describe configuration recorder status" "$regx" "recorder"
continue
fi
if [[ $CHECK_AWSCONFIG_RECORDING == "True" ]]; then
if [[ $CHECK_AWSCONFIG_STATUS == "SUCCESS" ]]; then
textPass "$regx: AWS Config recorder enabled" "$regx" "recorder"
else
textFail "$regx: AWS Config recorder in failure state" "$regx" "recorder"
fi
else
textFail "$regx: AWS Config recorder disabled" "$regx" "recorder"
fi
done
}

View File

@@ -1,55 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#
# Remediation:
#
# https://d1.awsstatic.com/whitepapers/compliance/AWS_CIS_Foundations_Benchmark.pdf
#
# aws logs put-metric-filter \
# --region us-east-1 \
# --log-group-name CloudTrail/CloudWatchLogGroup \
# --filter-name AWSConfigChanges \
# --filter-pattern '{ ($.eventSource = config.amazonaws.com) && (($.eventName = StopConfigurationRecorder)||($.eventName = DeleteDeliveryChannel)||($.eventName = PutDeliveryChannel)||($.eventName = PutConfigurationRecorder)) }' \
# --metric-transformations metricName=ConfigEventCount,metricNamespace=CloudTrailMetrics,metricValue=1
#
# aws cloudwatch put-metric-alarm \
# --region us-east-1 \
# --alarm-name AWSConfigChangesAlarm \
# --alarm-description "Triggered by AWS Config changes." \
# --metric-name ConfigEventCount \
# --namespace CloudTrailMetrics \
# --statistic Sum \
# --comparison-operator GreaterThanOrEqualToThreshold \
# --evaluation-periods 1 \
# --period 300 \
# --threshold 1 \
# --actions-enabled \
# --alarm-actions arn:aws:sns:us-east-1:123456789012:CloudWatchAlarmTopic
CHECK_ID_check39="3.9"
CHECK_TITLE_check39="[check39] Ensure a log metric filter and alarm exist for AWS Config configuration changes"
CHECK_SCORED_check39="SCORED"
CHECK_CIS_LEVEL_check39="LEVEL2"
CHECK_SEVERITY_check39="Medium"
CHECK_ASFF_TYPE_check39="Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check39="AwsCloudTrailTrail"
CHECK_ALTERNATE_check309="check39"
CHECK_SERVICENAME_check39="config"
CHECK_RISK_check39='If not enabled important changes to accounts could go unnoticed or difficult to find.'
CHECK_REMEDIATION_check39='Use this service as a complement to implement detective controls that cannot be prevented. (e.g. a Security Group is modified to open to internet without restrictions or route changed to avoid going thru the network firewall). Ensure AWS Config is enabled in all regions in order to detect any not intended action. On the other hand if sufficient preventive controls to make changes in critical services are in place; the rating on this finding can be lowered or discarded depending on residual risk.'
CHECK_DOC_check39='https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html'
CHECK_CAF_EPIC_check39='Logging and Monitoring'
check39(){
check3x '\$\.eventSource\s*=\s*config.amazonaws.com.+\$\.eventName\s*=\s*StopConfigurationRecorder.+\$\.eventName\s*=\s*DeleteDeliveryChannel.+\$\.eventName\s*=\s*PutDeliveryChannel.+\$\.eventName\s*=\s*PutConfigurationRecorder'
}

View File

@@ -0,0 +1,4 @@
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.config.config_service import Config
config_client = Config(current_audit_info)

View File

@@ -0,0 +1,35 @@
{
"Provider": "aws",
"CheckID": "config_recorder_all_regions_enabled",
"CheckTitle": "Ensure AWS Config is enabled in all regions.",
"CheckType": ["Logging and Monitoring"],
"ServiceName": "config",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:access-recorder:region:account-id:recorder/resource-id",
"Severity": "medium",
"ResourceType": "Other",
"Description": "Ensure AWS Config is enabled in all regions.",
"Risk": "The AWS configuration item history captured by AWS Config enables security analysis, resource change tracking and compliance auditing.",
"RelatedUrl": "https://aws.amazon.com/blogs/mt/aws-config-best-practices/",
"Remediation": {
"Code": {
"CLI": "https://docs.bridgecrew.io/docs/logging_5-enable-aws-config-regions#cli-command",
"NativeIaC": "",
"Other": "https://docs.bridgecrew.io/docs/logging_5-enable-aws-config-regions#aws-console",
"Terraform": "https://docs.bridgecrew.io/docs/logging_5-enable-aws-config-regions#terraform"
},
"Recommendation": {
"Text": "It is recommended to enable AWS Config be enabled in all regions.",
"Url": "https://aws.amazon.com/blogs/mt/aws-config-best-practices/"
}
},
"Categories": [],
"Tags": {
"Tag1Key": "value",
"Tag2Key": "value"
},
"DependsOn": [],
"RelatedTo": [],
"Notes": "",
"Compliance": []
}

View File

@@ -0,0 +1,35 @@
from lib.check.models import Check, Check_Report
from providers.aws.services.config.config_client import config_client
class config_recorder_all_regions_enabled(Check):
def execute(self):
findings = []
for recorder in config_client.recorders:
report = Check_Report(self.metadata)
report.region = recorder.region
report.resource_id = recorder.name
# Check if Config is enabled in region
if not recorder.name:
report.status = "FAIL"
report.status_extended = f"No AWS Config recorders in region."
else:
if recorder.recording:
if recorder.last_status == "Failure":
report.status = "FAIL"
report.status_extended = (
f"AWS Config recorder {recorder.name} in failure state."
)
else:
report.status = "PASS"
report.status_extended = (
f"AWS Config recorder {recorder.name} is enabled."
)
else:
report.status = "FAIL"
report.status_extended = (
f"AWS Config recorder {recorder.name} is disabled."
)
findings.append(report)
return findings

View File

@@ -0,0 +1,107 @@
from unittest import mock
from boto3 import client
from moto import mock_config
AWS_REGION = "us-east-1"
class Test_config_recorder_all_regions_enabled:
@mock_config
def test_config_no_recorders(self):
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.config.config_service import Config
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.config.config_recorder_all_regions_enabled.config_recorder_all_regions_enabled.config_client",
new=Config(current_audit_info),
):
# Test Check
from providers.aws.services.config.config_recorder_all_regions_enabled.config_recorder_all_regions_enabled import (
config_recorder_all_regions_enabled,
)
check = config_recorder_all_regions_enabled()
result = check.execute()
assert (
len(result) == 23
) # One fail result per region, since there are no recorders
assert result[0].status == "FAIL"
@mock_config
def test_config_one_recoder_disabled(self):
# Create Config Mocked Resources
config_client = client("config", region_name=AWS_REGION)
# Create Config Recorder
config_client.put_configuration_recorder(
ConfigurationRecorder={"name": "default", "roleARN": "somearn"}
)
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.config.config_service import Config
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.config.config_recorder_all_regions_enabled.config_recorder_all_regions_enabled.config_client",
new=Config(current_audit_info),
):
# Test Check
from providers.aws.services.config.config_recorder_all_regions_enabled.config_recorder_all_regions_enabled import (
config_recorder_all_regions_enabled,
)
check = config_recorder_all_regions_enabled()
result = check.execute()
assert len(result) == 23
# Search for the recorder just created
for recorder in result:
if recorder.resource_id:
assert recorder.status == "FAIL"
assert (
recorder.status_extended
== f"AWS Config recorder default is disabled."
)
assert recorder.resource_id == "default"
@mock_config
def test_config_one_recoder_enabled(self):
# Create Config Mocked Resources
config_client = client("config", region_name=AWS_REGION)
# Create Config Recorder and start it
config_client.put_configuration_recorder(
ConfigurationRecorder={"name": "default", "roleARN": "somearn"}
)
# Make the delivery channel
config_client.put_delivery_channel(
DeliveryChannel={"name": "testchannel", "s3BucketName": "somebucket"}
)
config_client.start_configuration_recorder(ConfigurationRecorderName="default")
from providers.aws.lib.audit_info.audit_info import current_audit_info
from providers.aws.services.config.config_service import Config
current_audit_info.audited_partition = "aws"
with mock.patch(
"providers.aws.services.config.config_recorder_all_regions_enabled.config_recorder_all_regions_enabled.config_client",
new=Config(current_audit_info),
):
# Test Check
from providers.aws.services.config.config_recorder_all_regions_enabled.config_recorder_all_regions_enabled import (
config_recorder_all_regions_enabled,
)
check = config_recorder_all_regions_enabled()
result = check.execute()
assert len(result) == 23
# Search for the recorder just created
for recorder in result:
if recorder.resource_id:
assert recorder.status == "PASS"
assert (
recorder.status_extended
== f"AWS Config recorder default is enabled."
)
assert recorder.resource_id == "default"

View File

@@ -0,0 +1,90 @@
import threading
from dataclasses import dataclass
from lib.logger import logger
from providers.aws.aws_provider import generate_regional_clients
################## Config
class Config:
def __init__(self, audit_info):
self.service = "config"
self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account
self.regional_clients = generate_regional_clients(self.service, audit_info)
self.recorders = []
self.__threading_call__(self.__describe_configuration_recorder_status__)
def __get_session__(self):
return self.session
def __threading_call__(self, call):
threads = []
for regional_client in self.regional_clients.values():
threads.append(threading.Thread(target=call, args=(regional_client,)))
for t in threads:
t.start()
for t in threads:
t.join()
def __describe_configuration_recorder_status__(self, regional_client):
logger.info("Config - Listing Recorders...")
try:
recorders = regional_client.describe_configuration_recorder_status()[
"ConfigurationRecordersStatus"
]
if recorders:
for recorder in recorders:
if "lastStatus" in recorder:
self.recorders.append(
Recorder(
recorder["name"],
recorder["recording"],
recorder["lastStatus"],
regional_client.region,
)
)
else:
self.recorders.append(
Recorder(
recorder["name"],
recorder["recording"],
None,
regional_client.region,
)
)
# No config recorders in region
else:
self.recorders.append(
Recorder(
None,
None,
None,
regional_client.region,
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
@dataclass
class Recorder:
name: str
recording: bool
last_status: str
region: str
def __init__(
self,
name,
recording,
last_status,
region,
):
self.name = name
self.recording = recording
self.last_status = last_status
self.region = region

View File

@@ -0,0 +1,89 @@
from boto3 import client, session
from moto import mock_config
from providers.aws.lib.audit_info.models import AWS_Audit_Info
from providers.aws.services.config.config_service import Config
AWS_ACCOUNT_NUMBER = 123456789012
AWS_REGION = "us-east-1"
class Test_Config_Service:
# Mocked Audit Info
def set_mocked_audit_info(self):
audit_info = AWS_Audit_Info(
original_session=None,
audit_session=session.Session(
profile_name=None,
botocore_session=None,
),
audited_account=AWS_ACCOUNT_NUMBER,
audited_user_id=None,
audited_partition="aws",
audited_identity_arn=None,
profile=None,
profile_region=None,
credentials=None,
assumed_role_info=None,
audited_regions=None,
organizations_metadata=None,
)
return audit_info
# Test Config Service
@mock_config
def test_service(self):
# Config client for this test class
audit_info = self.set_mocked_audit_info()
config = Config(audit_info)
assert config.service == "config"
# Test Config Client
@mock_config
def test_client(self):
# Config client for this test class
audit_info = self.set_mocked_audit_info()
config = Config(audit_info)
for client in config.regional_clients.values():
assert client.__class__.__name__ == "ConfigService"
# Test Config Session
@mock_config
def test__get_session__(self):
# Config client for this test class
audit_info = self.set_mocked_audit_info()
config = Config(audit_info)
assert config.session.__class__.__name__ == "Session"
# Test Config Session
@mock_config
def test_audited_account(self):
# Config client for this test class
audit_info = self.set_mocked_audit_info()
config = Config(audit_info)
assert config.audited_account == AWS_ACCOUNT_NUMBER
# Test Config Get Rest APIs
@mock_config
def test__describe_configuration_recorder_status__(self):
# Generate Config Client
config_client = client("config", region_name=AWS_REGION)
# Create Config Recorder and start it
config_client.put_configuration_recorder(
ConfigurationRecorder={"name": "default", "roleARN": "somearn"}
)
# Make the delivery channel
config_client.put_delivery_channel(
DeliveryChannel={"name": "testchannel", "s3BucketName": "somebucket"}
)
config_client.start_configuration_recorder(ConfigurationRecorderName="default")
# Config client for this test class
audit_info = self.set_mocked_audit_info()
config = Config(audit_info)
# One recorder per region
assert len(config.recorders) == 23
# Check the active one
# Search for the recorder just created
for recorder in config.recorders:
if recorder.name == "default":
assert recorder.recording == True