chore(fixer): add more fixers (#3772)

Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
This commit is contained in:
Sergio Garcia
2024-04-18 08:09:03 +02:00
committed by GitHub
parent 465b0f6a16
commit ab714f0fc7
16 changed files with 444 additions and 14 deletions
+27 -2
View File
@@ -101,10 +101,10 @@ For some fixers, you can have configurable parameters depending on your use case
# Fixer configuration file
aws:
# ec2_ebs_default_encryption
# No configuration needed for this check
# No configuration needed for this check
# s3_account_level_public_access_blocks
# No configuration needed for this check
# No configuration needed for this check
# iam_password_policy_* checks:
iam_password_policy:
@@ -117,4 +117,29 @@ aws:
MaxPasswordAge: 90
PasswordReusePrevention: 24
HardExpiry: False
# accessanalyzer_enabled
accessanalyzer_enabled:
AnalyzerName: "DefaultAnalyzer"
AnalyzerType: "ACCOUNT_UNUSED_ACCESS"
# guardduty_is_enabled
# No configuration needed for this check
# securityhub_enabled
securityhub_enabled:
EnableDefaultStandards: True
# cloudtrail_multi_region_enabled
cloudtrail_multi_region_enabled:
TrailName: "DefaultTrail"
S3BucketName: "my-cloudtrail-bucket"
IsMultiRegionTrail: True
EnableLogFileValidation: True
# CloudWatchLogsLogGroupArn: "arn:aws:logs:us-east-1:123456789012:log-group:my-cloudtrail-log-group"
# CloudWatchLogsRoleArn: "arn:aws:iam::123456789012:role/my-cloudtrail-role"
# KmsKeyId: "arn:aws:kms:us-east-1:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab"
# kms_cmk_rotation_enabled
# No configuration needed for this check
```
+27 -2
View File
@@ -1,10 +1,10 @@
# Fixer configuration file
aws:
# ec2_ebs_default_encryption
# No configuration needed for this check
# No configuration needed for this check
# s3_account_level_public_access_blocks
# No configuration needed for this check
# No configuration needed for this check
# iam_password_policy_* checks:
iam_password_policy:
@@ -17,3 +17,28 @@ aws:
MaxPasswordAge: 90
PasswordReusePrevention: 24
HardExpiry: False
# accessanalyzer_enabled
accessanalyzer_enabled:
AnalyzerName: "DefaultAnalyzer"
AnalyzerType: "ACCOUNT_UNUSED_ACCESS"
# guardduty_is_enabled
# No configuration needed for this check
# securityhub_enabled
securityhub_enabled:
EnableDefaultStandards: True
# cloudtrail_multi_region_enabled
cloudtrail_multi_region_enabled:
TrailName: "DefaultTrail"
S3BucketName: "my-cloudtrail-bucket"
IsMultiRegionTrail: True
EnableLogFileValidation: True
# CloudWatchLogsLogGroupArn: "arn:aws:logs:us-east-1:123456789012:log-group:my-cloudtrail-log-group"
# CloudWatchLogsRoleArn: "arn:aws:iam::123456789012:role/my-cloudtrail-role"
# KmsKeyId: "arn:aws:kms:us-east-1:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab"
# kms_cmk_rotation_enabled
# No configuration needed for this check
+22 -4
View File
@@ -500,12 +500,30 @@ def run_fixer(check_findings: list) -> int:
)
for finding in findings:
if finding.status == "FAIL":
# Check if fixer has region as argument to check if it is a region specific fixer
if "region" in fixer.__code__.co_varnames:
# Check what type of fixer is:
# - If it is a fixer for a specific resource and region
# - If it is a fixer for a specific region
# - If it is a fixer for a specific resource
if (
"region" in fixer.__code__.co_varnames
and "resource_id" in fixer.__code__.co_varnames
):
print(
f"\t{orange_color}FIXING{Style.RESET_ALL} {finding.resource_id} in {finding.region}... "
)
if fixer(
resource_id=finding.resource_id,
region=finding.region,
):
fixed_findings += 1
print(f"\t{Fore.GREEN}DONE{Style.RESET_ALL}")
else:
print(f"\t{Fore.RED}ERROR{Style.RESET_ALL}")
elif "region" in fixer.__code__.co_varnames:
print(
f"\t{orange_color}FIXING{Style.RESET_ALL} {finding.region}... "
)
if fixer(finding.region):
if fixer(region=finding.region):
fixed_findings += 1
print(f"\t{Fore.GREEN}DONE{Style.RESET_ALL}")
else:
@@ -514,7 +532,7 @@ def run_fixer(check_findings: list) -> int:
print(
f"\t{orange_color}FIXING{Style.RESET_ALL} Resource {finding.resource_id}... "
)
if fixer(finding.resource_id):
if fixer(resource_id=finding.resource_id):
fixed_findings += 1
print(f"\t\t{Fore.GREEN}DONE{Style.RESET_ALL}")
else:
@@ -0,0 +1,41 @@
from prowler.lib.logger import logger
from prowler.providers.aws.services.accessanalyzer.accessanalyzer_client import (
accessanalyzer_client,
)
def fixer(region):
"""
Enable Access Analyzer in a region. Requires the access-analyzer:CreateAnalyzer permission:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "access-analyzer:CreateAnalyzer",
"Resource": "*"
}
]
}
Args:
region (str): AWS region
Returns:
bool: True if Access Analyzer is enabled, False otherwise
"""
try:
regional_client = accessanalyzer_client.regional_clients[region]
regional_client.create_analyzer(
analyzerName=accessanalyzer_client.fixer_config.get(
"accessanalyzer_enabled", {}
).get("AnalyzerName", "DefaultAnalyzer"),
type=accessanalyzer_client.fixer_config.get(
"accessanalyzer_enabled", {}
).get("AnalyzerType", "ACCOUNT_UNUSED_ACCESS"),
)
except Exception as error:
logger.error(
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return False
else:
return True
@@ -0,0 +1,58 @@
from prowler.lib.logger import logger
from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
cloudtrail_client,
)
def fixer(region):
"""
NOTE: Define the S3 bucket name in the fixer_config.yaml file.
Enable CloudTrail in a region. Requires the cloudtrail:CreateTrail permission:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "cloudtrail:CreateTrail",
"Resource": "*"
}
]
}
Args:
region (str): AWS region
Returns:
bool: True if CloudTrail is enabled, False otherwise
"""
try:
cloudtrail_fixer_config = cloudtrail_client.fixer_config.get(
"cloudtrail_multi_region_enabled", {}
)
regional_client = cloudtrail_client.regional_clients[region]
args = {
"Name": cloudtrail_fixer_config.get("TrailName", "DefaultTrail"),
"S3BucketName": cloudtrail_fixer_config.get("S3BucketName"),
"IsMultiRegionTrail": cloudtrail_fixer_config.get(
"IsMultiRegionTrail", True
),
"EnableLogFileValidation": cloudtrail_fixer_config.get(
"EnableLogFileValidation", True
),
}
if cloudtrail_fixer_config.get("CloudWatchLogsLogGroupArn"):
args["CloudWatchLogsLogGroupArn"] = cloudtrail_fixer_config.get(
"CloudWatchLogsLogGroupArn"
)
if cloudtrail_fixer_config.get("CloudWatchLogsRoleArn"):
args["CloudWatchLogsRoleArn"] = cloudtrail_fixer_config.get(
"CloudWatchLogsRoleArn"
)
if cloudtrail_fixer_config.get("KmsKeyId"):
args["KmsKeyId"] = cloudtrail_fixer_config.get("KmsKeyId")
regional_client.create_trail(**args)
except Exception as error:
logger.error(
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return False
else:
return True
@@ -0,0 +1,32 @@
from prowler.lib.logger import logger
from prowler.providers.aws.services.guardduty.guardduty_client import guardduty_client
def fixer(region):
"""
Enable GuardDuty in a region. Requires the guardduty:CreateDetector permission:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "guardduty:CreateDetector",
"Resource": "*"
}
]
}
Args:
region (str): AWS region
Returns:
bool: True if GuardDuty is enabled, False otherwise
"""
try:
regional_client = guardduty_client.regional_clients[region]
regional_client.create_detector(Enable=True)
except Exception as error:
logger.error(
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return False
else:
return True
@@ -0,0 +1,33 @@
from prowler.lib.logger import logger
from prowler.providers.aws.services.kms.kms_client import kms_client
def fixer(resource_id: str, region: str) -> bool:
"""
Enable CMK rotation. Requires the kms:EnableKeyRotation permission:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "kms:EnableKeyRotation",
"Resource": "*"
}
]
}
Args:
resource_id (str): KMS CMK ID
region (str): AWS region
Returns:
bool: True if CMK rotation is enabled, False otherwise
"""
try:
regional_client = kms_client.regional_clients[region]
regional_client.enable_key_rotation(KeyId=resource_id)
except Exception as error:
logger.error(
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return False
else:
return True
@@ -0,0 +1,38 @@
from prowler.lib.logger import logger
from prowler.providers.aws.services.securityhub.securityhub_client import (
securityhub_client,
)
def fixer(region):
"""
Enable Security Hub in a region. Requires the securityhub:EnableSecurityHub permission:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "securityhub:EnableSecurityHub",
"Resource": "*"
}
]
}
Args:
region (str): AWS region
Returns:
bool: True if Security Hub is enabled, False otherwise
"""
try:
regional_client = securityhub_client.regional_clients[region]
regional_client.enable_security_hub(
EnableDefaultStandards=securityhub_client.fixer_config.get(
"securityhub_enabled", {}
).get("EnableDefaultStandards", True)
)
except Exception as error:
logger.error(
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return False
else:
return True
@@ -0,0 +1,19 @@
from unittest import mock
AWS_REGION = "eu-west-1"
class Test_accessanalyzer_enabled_fixer:
def test_accessanalyzer_enabled_fixer(self):
accessanalyzer_client = mock.MagicMock
accessanalyzer_client.analyzers = []
with mock.patch(
"prowler.providers.aws.services.accessanalyzer.accessanalyzer_service.AccessAnalyzer",
new=accessanalyzer_client,
):
# Test Check
from prowler.providers.aws.services.accessanalyzer.accessanalyzer_enabled.accessanalyzer_enabled_fixer import (
fixer,
)
assert fixer(AWS_REGION)
@@ -0,0 +1,44 @@
from unittest import mock
from boto3 import client
from moto import mock_aws
from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider
class Test_cloudtrail_multi_region_enabled_fixer:
@mock_aws
def test_cloudtrail_multi_region_enabled_fixer(self):
from prowler.providers.aws.services.cloudtrail.cloudtrail_service import (
Cloudtrail,
)
aws_provider = set_mocked_aws_provider(
audited_regions=[AWS_REGION_US_EAST_1],
fixer_config={
"cloudtrail_multi_region_enabled": {
"TrailName": "DefaultTrail",
"S3BucketName": "test-bucket",
"IsMultiRegionTrail": True,
"EnableLogFileValidation": True,
},
},
)
# Create s3 test-bucket
s3 = client("s3")
s3.create_bucket(Bucket="test-bucket")
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=aws_provider,
):
with mock.patch(
"prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled_fixer.cloudtrail_client",
new=Cloudtrail(aws_provider),
):
# Test Check
from prowler.providers.aws.services.cloudtrail.cloudtrail_multi_region_enabled.cloudtrail_multi_region_enabled_fixer import (
fixer,
)
assert fixer(AWS_REGION_US_EAST_1)
@@ -16,7 +16,7 @@ class Test_ec2_ebs_default_encryption_fixer:
"prowler.providers.common.common.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.ec2.ec2_ebs_default_encryption.ec2_ebs_default_encryption.ec2_client",
"prowler.providers.aws.services.ec2.ec2_ebs_default_encryption.ec2_ebs_default_encryption_fixer.ec2_client",
new=EC2(aws_provider),
):
from prowler.providers.aws.services.ec2.ec2_ebs_default_encryption.ec2_ebs_default_encryption_fixer import (
@@ -0,0 +1,31 @@
from unittest import mock
from uuid import uuid4
from moto import mock_aws
from tests.providers.aws.utils import (
AWS_ACCOUNT_ARN,
AWS_ACCOUNT_NUMBER,
AWS_REGION_EU_WEST_1,
)
DETECTOR_ID = str(uuid4())
DETECTOR_ARN = f"arn:aws:guardduty:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:detector/{DETECTOR_ID}"
class Test_guardduty_is_enabled_fixer:
@mock_aws
def test_guardduty_is_enabled_fixer(self):
guardduty_client = mock.MagicMock
guardduty_client.region = AWS_REGION_EU_WEST_1
guardduty_client.detectors = []
guardduty_client.audited_account_arn = AWS_ACCOUNT_ARN
with mock.patch(
"prowler.providers.aws.services.guardduty.guardduty_service.GuardDuty",
guardduty_client,
):
from prowler.providers.aws.services.guardduty.guardduty_is_enabled.guardduty_is_enabled_fixer import (
fixer,
)
assert fixer(AWS_REGION_EU_WEST_1)
@@ -0,0 +1,33 @@
from unittest import mock
from boto3 import client
from moto import mock_aws
from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider
class Test_kms_cmk_rotation_enabled_fixer:
@mock_aws
def test_kms_cmk_rotation_enabled_fixer(self):
# Generate KMS Client
kms_client = client("kms", region_name=AWS_REGION_US_EAST_1)
# Creaty KMS key without rotation
key = kms_client.create_key()["KeyMetadata"]
from prowler.providers.aws.services.kms.kms_service import KMS
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.kms.kms_cmk_rotation_enabled.kms_cmk_rotation_enabled_fixer.kms_client",
new=KMS(aws_provider),
):
# Test Check
from prowler.providers.aws.services.kms.kms_cmk_rotation_enabled.kms_cmk_rotation_enabled_fixer import (
fixer,
)
assert fixer(resource_id=key["KeyId"], region=AWS_REGION_US_EAST_1)
@@ -12,7 +12,7 @@ from tests.providers.aws.utils import (
class Test_s3_account_level_public_access_block_fixer:
@mock_aws
def test_bucket_account_public_block_fixer(self):
from prowler.providers.aws.services.s3.s3_service import S3, S3Control
from prowler.providers.aws.services.s3.s3_service import S3Control
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
@@ -20,10 +20,7 @@ class Test_s3_account_level_public_access_block_fixer:
"prowler.providers.common.common.get_global_provider",
return_value=aws_provider,
), mock.patch(
"prowler.providers.aws.services.s3.s3_account_level_public_access_blocks.s3_account_level_public_access_blocks.s3_client",
new=S3(aws_provider),
), mock.patch(
"prowler.providers.aws.services.s3.s3_account_level_public_access_blocks.s3_account_level_public_access_blocks.s3control_client",
"prowler.providers.aws.services.s3.s3_account_level_public_access_blocks.s3_account_level_public_access_blocks_fixer.s3control_client",
new=S3Control(aws_provider),
):
from prowler.providers.aws.services.s3.s3_account_level_public_access_blocks.s3_account_level_public_access_blocks_fixer import (
@@ -0,0 +1,34 @@
from unittest import mock
from moto import mock_aws
from prowler.providers.aws.services.securityhub.securityhub_service import (
SecurityHubHub,
)
from tests.providers.aws.utils import AWS_REGION_EU_WEST_1
class test_securityhub_enabled_fixer:
@mock_aws
def test_securityhub_enabled_fixer(self):
securityhub_client = mock.MagicMock
securityhub_client.securityhubs = [
SecurityHubHub(
arn="arn:aws:securityhub:us-east-1:0123456789012:hub/default",
id="default",
status="ACTIVE",
standards="cis-aws-foundations-benchmark/v/1.2.0",
integrations="",
region="eu-west-1",
)
]
with mock.patch(
"prowler.providers.aws.services.securityhub.securityhub_service.SecurityHub",
new=securityhub_client,
):
# Test Check
from prowler.providers.aws.services.securityhub.securityhub_enabled.securityhub_enabled_fixer import (
fixer,
)
assert fixer(AWS_REGION_EU_WEST_1)
+2
View File
@@ -62,6 +62,7 @@ def set_mocked_aws_provider(
expected_checks: list[str] = [],
profile_region: str = None,
audit_config: dict = {},
fixer_config: dict = {},
scan_unused_services: bool = True,
audit_session: session.Session = session.Session(
profile_name=None,
@@ -108,6 +109,7 @@ def set_mocked_aws_provider(
provider._organizations_metadata = None
provider._audit_resources = []
provider._audit_config = audit_config
provider._fixer_config = fixer_config
provider.audit_metadata = Audit_Metadata(
services_scanned=0,
expected_checks=expected_checks,