feat(backup): add tags to backup vaults and backup plans (#5194)

This commit is contained in:
Rubén De la Torre Vico
2024-09-25 17:02:53 +02:00
committed by GitHub
parent 8e087196c9
commit bc8f3eba4d
8 changed files with 123 additions and 11 deletions
@@ -12,6 +12,7 @@ class backup_plans_exist(Check):
report.resource_arn = backup_client.backup_plans[0].arn
report.resource_id = backup_client.backup_plans[0].name
report.region = backup_client.backup_plans[0].region
report.resource_tags = backup_client.backup_plans[0].tags
findings.append(report)
elif backup_client.backup_vaults:
report = Check_Report_AWS(self.metadata())
@@ -20,5 +21,6 @@ class backup_plans_exist(Check):
report.resource_arn = backup_client.backup_plan_arn_template
report.resource_id = backup_client.audited_account
report.region = backup_client.region
report.resource_tags = []
findings.append(report)
return findings
@@ -9,7 +9,6 @@ from prowler.lib.scan_filters.scan_filters import is_resource_filtered
from prowler.providers.aws.lib.service.service import AWSService
################## Backup
class Backup(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
@@ -19,8 +18,10 @@ class Backup(AWSService):
self.backup_vault_arn_template = f"arn:{self.audited_partition}:backup:{self.region}:{self.audited_account}:backup-vault"
self.backup_vaults = []
self.__threading_call__(self._list_backup_vaults)
self.__threading_call__(self._list_tags, self.backup_vaults)
self.backup_plans = []
self.__threading_call__(self._list_backup_plans)
self.__threading_call__(self._list_tags, self.backup_plans)
self.backup_report_plans = []
self.__threading_call__(self._list_backup_report_plans)
self.protected_resources = {}
@@ -167,6 +168,17 @@ class Backup(AWSService):
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _list_tags(self, resource):
try:
tags = self.regional_clients[resource.region].list_tags(
ResourceArn=resource.arn
)["Tags"]
resource.tags = [tags] if tags else []
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
class BackupVault(BaseModel):
arn: str
@@ -177,6 +189,7 @@ class BackupVault(BaseModel):
locked: bool
min_retention_days: int = None
max_retention_days: int = None
tags: Optional[list]
class BackupPlan(BaseModel):
@@ -187,6 +200,7 @@ class BackupPlan(BaseModel):
version_id: str
last_execution_date: Optional[datetime]
advanced_settings: list
tags: Optional[list]
class BackupReportPlan(BaseModel):
@@ -16,6 +16,7 @@ class backup_vaults_encrypted(Check):
report.resource_arn = backup_vault.arn
report.resource_id = backup_vault.name
report.region = backup_vault.region
report.resource_tags = backup_vault.tags
# if it is encrypted we only change the status and the status extended
if backup_vault.encryption:
report.status = "PASS"
@@ -12,12 +12,14 @@ class backup_vaults_exist(Check):
report.resource_arn = backup_client.backup_vault_arn_template
report.resource_id = backup_client.audited_account
report.region = backup_client.region
report.resource_tags = []
if backup_client.backup_vaults:
report.status = "PASS"
report.status_extended = f"At least one backup vault exists: {backup_client.backup_vaults[0].name}."
report.resource_arn = backup_client.backup_vaults[0].arn
report.resource_id = backup_client.backup_vaults[0].name
report.region = backup_client.backup_vaults[0].region
report.resource_tags = backup_client.backup_vaults[0].tags
findings.append(report)
return findings
@@ -45,6 +45,7 @@ class Test_backup_plans_exist:
== f"arn:aws:backup:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:backup-plan"
)
assert result[0].region == AWS_REGION
assert result[0].resource_tags == []
def test_no_backup_plans_not_vaults(self):
backup_client = mock.MagicMock
@@ -88,6 +89,7 @@ class Test_backup_plans_exist:
version_id="version_id",
last_execution_date=datetime(2015, 1, 1),
advanced_settings=[],
tags=[],
)
]
with mock.patch(
@@ -117,3 +119,4 @@ class Test_backup_plans_exist:
== f"arn:aws:backup:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:plan:{backup_plan_id}"
)
assert result[0].region == AWS_REGION
assert result[0].resource_tags == []
@@ -2,10 +2,15 @@ from datetime import datetime
from unittest.mock import patch
import botocore
from boto3 import client
from moto import mock_aws
from prowler.providers.aws.services.backup.backup_service import Backup
from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider
from tests.providers.aws.utils import (
AWS_ACCOUNT_NUMBER,
AWS_REGION_EU_WEST_1,
set_mocked_aws_provider,
)
# Mocking Backup Calls
make_api_call = botocore.client.BaseClient._make_api_call
@@ -74,15 +79,14 @@ def mock_generate_regional_clients(provider, service):
return {AWS_REGION_EU_WEST_1: regional_client}
@mock_aws
# Patch every AWS call using Boto3 and generate_regional_clients to have 1 client
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@patch(
"prowler.providers.aws.aws_provider.AwsProvider.generate_regional_clients",
new=mock_generate_regional_clients,
)
class Test_Backup_Service:
class TestBackupService:
# Test Backup Client
@mock_aws
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@patch(
"prowler.providers.aws.aws_provider.AwsProvider.generate_regional_clients",
new=mock_generate_regional_clients,
)
def test_get_client(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
backup = Backup(aws_provider)
@@ -91,18 +95,36 @@ class Test_Backup_Service:
)
# Test Backup Session
@mock_aws
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@patch(
"prowler.providers.aws.aws_provider.AwsProvider.generate_regional_clients",
new=mock_generate_regional_clients,
)
def test__get_session__(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
access_analyzer = Backup(aws_provider)
assert access_analyzer.session.__class__.__name__ == "Session"
# Test Backup Service
# Test Backup Serviceç
@mock_aws
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@patch(
"prowler.providers.aws.aws_provider.AwsProvider.generate_regional_clients",
new=mock_generate_regional_clients,
)
def test__get_service__(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
access_analyzer = Backup(aws_provider)
assert access_analyzer.service == "backup"
# Test Backup List Backup Vaults
@mock_aws
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@patch(
"prowler.providers.aws.aws_provider.AwsProvider.generate_regional_clients",
new=mock_generate_regional_clients,
)
def test_list_backup_vaults(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
backup = Backup(aws_provider)
@@ -117,6 +139,12 @@ class Test_Backup_Service:
assert backup.backup_vaults[0].max_retention_days == 2
# Test Backup List Backup Plans
@mock_aws
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@patch(
"prowler.providers.aws.aws_provider.AwsProvider.generate_regional_clients",
new=mock_generate_regional_clients,
)
def test_list_backup_plans(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
backup = Backup(aws_provider)
@@ -130,6 +158,12 @@ class Test_Backup_Service:
assert backup.backup_plans[0].advanced_settings == []
# Test Backup List Report Plans
@mock_aws
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@patch(
"prowler.providers.aws.aws_provider.AwsProvider.generate_regional_clients",
new=mock_generate_regional_clients,
)
def test_list_backup_report_plans(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
backup = Backup(aws_provider)
@@ -144,6 +178,12 @@ class Test_Backup_Service:
2015, 1, 1
)
@mock_aws
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@patch(
"prowler.providers.aws.aws_provider.AwsProvider.generate_regional_clients",
new=mock_generate_regional_clients,
)
def test_list_protected_resources(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
backup = Backup(aws_provider)
@@ -155,3 +195,46 @@ class Test_Backup_Service:
assert protected_resource.resource_type == "RDS"
assert protected_resource.region == AWS_REGION_EU_WEST_1
assert protected_resource.last_backup_time == datetime(2015, 1, 1)
@mock_aws
def test_list_tags(self):
backup_client = client("backup", region_name=AWS_REGION_EU_WEST_1)
# Create necessary resources and tags
backup_vault = backup_client.create_backup_vault(
BackupVaultName="TestVault",
EncryptionKeyArn=f"arn:aws:kms:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:key/1234abcd-12ab-34cd-56ef-123456789012",
)
tags = {"TestKey": "TestValue"}
backup_client.tag_resource(
ResourceArn=backup_vault["BackupVaultArn"], Tags=tags
)
# Create a backup plan
backup_plan = backup_client.create_backup_plan(
BackupPlan={
"BackupPlanName": "TestPlan",
"Rules": [
{
"RuleName": "TestRule",
"TargetBackupVaultName": "TestVault", # Match the vault name
"ScheduleExpression": "cron(0 12 * * ? *)",
}
],
}
)
backup_client.tag_resource(ResourceArn=backup_plan["BackupPlanArn"], Tags=tags)
# Test list_tags
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
backup = Backup(aws_provider)
assert len(backup.backup_vaults) == 1
assert len(backup.backup_vaults[0].tags) == 1
assert backup.backup_vaults[0].tags[0]["TestKey"] == "TestValue"
assert len(backup.backup_plans) == 1
assert len(backup.backup_plans[0].tags) == 1
assert backup.backup_plans[0].tags[0]["TestKey"] == "TestValue"
@@ -40,6 +40,7 @@ class Test_backup_vaults_encrypted:
locked=True,
min_retention_days=1,
max_retention_days=2,
tags=[],
)
]
@@ -67,6 +68,7 @@ class Test_backup_vaults_encrypted:
assert result[0].resource_id == "MyBackupVault"
assert result[0].resource_arn == backup_vault_arn
assert result[0].region == AWS_REGION
assert result[0].resource_tags == []
def test_one_backup_vault_encrypted(self):
backup_client = mock.MagicMock
@@ -81,6 +83,7 @@ class Test_backup_vaults_encrypted:
locked=True,
min_retention_days=1,
max_retention_days=2,
tags=[],
)
]
@@ -108,3 +111,4 @@ class Test_backup_vaults_encrypted:
assert result[0].resource_id == "MyBackupVault"
assert result[0].resource_arn == backup_vault_arn
assert result[0].region == AWS_REGION
assert result[0].resource_tags == []
@@ -42,6 +42,7 @@ class Test_backup_vaults_exist:
== f"arn:aws:backup:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:backup-vault"
)
assert result[0].region == AWS_REGION
assert result[0].resource_tags == []
def test_one_backup_vault(self):
backup_client = mock.MagicMock
@@ -64,6 +65,7 @@ class Test_backup_vaults_exist:
locked=True,
min_retention_days=1,
max_retention_days=2,
tags=[],
)
]
@@ -91,6 +93,7 @@ class Test_backup_vaults_exist:
assert result[0].resource_id == "MyBackupVault"
assert result[0].resource_arn == backup_vault_arn
assert result[0].region == AWS_REGION
assert result[0].resource_tags == []
def test_access_denied(self):
backup_client = mock.MagicMock