mirror of
https://github.com/ghndrx/prowler.git
synced 2026-02-10 06:45:08 +00:00
feat(shield): Service and checks (#1504)
This commit is contained in:
2446
Pipfile.lock
generated
2446
Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -100,9 +100,6 @@ def print_services(service_list: set):
|
|||||||
|
|
||||||
|
|
||||||
def print_checks(provider: str, check_list: set, bulk_checks_metadata: dict):
|
def print_checks(provider: str, check_list: set, bulk_checks_metadata: dict):
|
||||||
print(
|
|
||||||
f"There are {Fore.YELLOW}{len(check_list)}{Style.RESET_ALL} available checks: \n"
|
|
||||||
)
|
|
||||||
for check in check_list:
|
for check in check_list:
|
||||||
try:
|
try:
|
||||||
print(
|
print(
|
||||||
@@ -112,6 +109,9 @@ def print_checks(provider: str, check_list: set, bulk_checks_metadata: dict):
|
|||||||
logger.error(
|
logger.error(
|
||||||
f"Check {error} was not found for the {provider.upper()} provider"
|
f"Check {error} was not found for the {provider.upper()} provider"
|
||||||
)
|
)
|
||||||
|
print(
|
||||||
|
f"\nThere are {Fore.YELLOW}{len(check_list)}{Style.RESET_ALL} available checks.\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# List available groups
|
# List available groups
|
||||||
|
|||||||
@@ -1,211 +1,211 @@
|
|||||||
# from datetime import datetime
|
from unittest import mock
|
||||||
# from unittest import mock
|
|
||||||
|
|
||||||
# from boto3 import session
|
from boto3 import client, session
|
||||||
# from moto.core import DEFAULT_ACCOUNT_ID
|
from mock import patch
|
||||||
|
from moto import mock_cloudtrail, mock_s3
|
||||||
|
from moto.core import DEFAULT_ACCOUNT_ID
|
||||||
|
|
||||||
# from providers.aws.lib.audit_info.audit_info import AWS_Audit_Info
|
from providers.aws.lib.audit_info.audit_info import AWS_Audit_Info
|
||||||
# from providers.aws.services.awslambda.awslambda_service import Function
|
from providers.aws.services.awslambda.awslambda_service import Function
|
||||||
# from providers.aws.services.cloudtrail.cloudtrail_service import Trail
|
|
||||||
|
|
||||||
# AWS_REGION = "us-east-1"
|
AWS_REGION = "us-east-1"
|
||||||
|
|
||||||
|
|
||||||
# class Test_awslambda_function_invoke_api_operations_cloudtrail_logging_enabled:
|
# Mock generate_regional_clients()
|
||||||
# # Mocked Audit Info
|
def mock_generate_regional_clients(service, audit_info):
|
||||||
# def set_mocked_audit_info(self):
|
regional_client = audit_info.audit_session.client(service, region_name=AWS_REGION)
|
||||||
# audit_info = AWS_Audit_Info(
|
regional_client.region = AWS_REGION
|
||||||
# original_session=None,
|
return {AWS_REGION: regional_client}
|
||||||
# audit_session=session.Session(
|
|
||||||
# profile_name=None,
|
|
||||||
# botocore_session=None,
|
|
||||||
# ),
|
|
||||||
# audited_account=None,
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# def test_no_functions(self):
|
|
||||||
# lambda_client = mock.MagicMock
|
|
||||||
# lambda_client.functions = {}
|
|
||||||
# cloudtrail_client = mock.MagicMock
|
|
||||||
# cloudtrail_client.trails = []
|
|
||||||
|
|
||||||
# with mock.patch(
|
# Patch every AWS call using Boto3 and generate_regional_clients to have 1 client
|
||||||
# "providers.aws.services.awslambda.awslambda_service.Lambda",
|
@patch(
|
||||||
# new=lambda_client,
|
"providers.aws.services.accessanalyzer.accessanalyzer_service.generate_regional_clients",
|
||||||
# ), mock.patch(
|
new=mock_generate_regional_clients,
|
||||||
# "providers.aws.lib.audit_info.audit_info.current_audit_info",
|
)
|
||||||
# self.set_mocked_audit_info(),
|
class Test_awslambda_function_invoke_api_operations_cloudtrail_logging_enabled:
|
||||||
# ), mock.patch(
|
# Mocked Audit Info
|
||||||
# "providers.aws.services.cloudtrail.cloudtrail_service.Cloudtrail",
|
def set_mocked_audit_info(self):
|
||||||
# new=cloudtrail_client,
|
audit_info = AWS_Audit_Info(
|
||||||
# ):
|
original_session=None,
|
||||||
# # Test Check
|
audit_session=session.Session(
|
||||||
# from providers.aws.services.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled import (
|
profile_name=None,
|
||||||
# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled,
|
botocore_session=None,
|
||||||
# )
|
),
|
||||||
|
audited_account=None,
|
||||||
|
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
|
||||||
|
|
||||||
# check = (
|
@mock_cloudtrail
|
||||||
# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled()
|
def test_no_functions(self):
|
||||||
# )
|
lambda_client = mock.MagicMock
|
||||||
# result = check.execute()
|
lambda_client.functions = {}
|
||||||
|
|
||||||
# assert len(result) == 0
|
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
|
||||||
|
|
||||||
# def test_lambda_not_recorded_by_cloudtrail(self):
|
with mock.patch(
|
||||||
# # Lambda Client
|
"providers.aws.services.awslambda.awslambda_service.Lambda",
|
||||||
# lambda_client = mock.MagicMock
|
new=lambda_client,
|
||||||
# function_name = "test-lambda"
|
), mock.patch(
|
||||||
# function_runtime = "python3.9"
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
# function_arn = (
|
self.set_mocked_audit_info(),
|
||||||
# f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}"
|
), mock.patch(
|
||||||
# )
|
"providers.aws.services.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.cloudtrail_client",
|
||||||
# lambda_client.functions = {
|
new=Cloudtrail(self.set_mocked_audit_info()),
|
||||||
# function_name: Function(
|
):
|
||||||
# name=function_name,
|
# Test Check
|
||||||
# arn=function_arn,
|
from providers.aws.services.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled import (
|
||||||
# region=AWS_REGION,
|
awslambda_function_invoke_api_operations_cloudtrail_logging_enabled,
|
||||||
# runtime=function_runtime,
|
)
|
||||||
# )
|
|
||||||
# }
|
|
||||||
# # CloudTrail Client
|
|
||||||
# cloudtrail_client = mock.MagicMock
|
|
||||||
# cloudtrail_client.trails = [
|
|
||||||
# Trail(
|
|
||||||
# name="test-trail",
|
|
||||||
# is_multiregion=False,
|
|
||||||
# home_region=AWS_REGION,
|
|
||||||
# arn="",
|
|
||||||
# region=AWS_REGION,
|
|
||||||
# is_logging=True,
|
|
||||||
# log_file_validation_enabled=True,
|
|
||||||
# latest_cloudwatch_delivery_time=datetime(2022, 1, 1),
|
|
||||||
# s3_bucket="",
|
|
||||||
# kms_key="",
|
|
||||||
# log_group_arn="",
|
|
||||||
# data_events=[
|
|
||||||
# {
|
|
||||||
# "ReadWriteType": "All",
|
|
||||||
# "IncludeManagementEvents": True,
|
|
||||||
# "DataResources": [],
|
|
||||||
# "ExcludeManagementEventSources": [],
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# )
|
|
||||||
# ]
|
|
||||||
|
|
||||||
# with mock.patch(
|
check = (
|
||||||
# "providers.aws.services.awslambda.awslambda_service.Lambda",
|
awslambda_function_invoke_api_operations_cloudtrail_logging_enabled()
|
||||||
# new=lambda_client,
|
)
|
||||||
# ), mock.patch(
|
result = check.execute()
|
||||||
# "providers.aws.services.cloudtrail.cloudtrail_service.Cloudtrail",
|
|
||||||
# new=cloudtrail_client,
|
|
||||||
# ):
|
|
||||||
|
|
||||||
# # Test Check
|
assert len(result) == 0
|
||||||
# from providers.aws.services.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled import (
|
|
||||||
# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled,
|
|
||||||
# )
|
|
||||||
|
|
||||||
# check = (
|
@mock_cloudtrail
|
||||||
# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled()
|
@mock_s3
|
||||||
# )
|
def test_lambda_not_recorded_by_cloudtrail(self):
|
||||||
# result = check.execute()
|
# Lambda Client
|
||||||
|
lambda_client = mock.MagicMock
|
||||||
|
function_name = "test-lambda"
|
||||||
|
function_runtime = "python3.9"
|
||||||
|
function_arn = (
|
||||||
|
f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}"
|
||||||
|
)
|
||||||
|
lambda_client.functions = {
|
||||||
|
function_name: Function(
|
||||||
|
name=function_name,
|
||||||
|
arn=function_arn,
|
||||||
|
region=AWS_REGION,
|
||||||
|
runtime=function_runtime,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
# assert len(result) == 1
|
# CloudTrail Client
|
||||||
# assert result[0].region == AWS_REGION
|
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||||
# assert result[0].resource_id == function_name
|
s3_client = client("s3", region_name=AWS_REGION)
|
||||||
# assert result[0].resource_arn == function_arn
|
trail_name = "test-trail"
|
||||||
# assert result[0].status == "FAIL"
|
bucket_name = "test-bucket"
|
||||||
# assert (
|
s3_client.create_bucket(Bucket=bucket_name)
|
||||||
# result[0].status_extended
|
cloudtrail_client.create_trail(
|
||||||
# == f"Lambda function {function_name} is not recorded by CloudTrail"
|
Name=trail_name, S3BucketName=bucket_name, IsMultiRegionTrail=False
|
||||||
# )
|
)
|
||||||
|
|
||||||
# def test_lambda_recorded_by_cloudtrail(self):
|
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
|
||||||
# # Lambda Client
|
|
||||||
# lambda_client = mock.MagicMock
|
|
||||||
# function_name = "test-lambda"
|
|
||||||
# function_runtime = "python3.9"
|
|
||||||
# function_arn = (
|
|
||||||
# f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}"
|
|
||||||
# )
|
|
||||||
# lambda_client.functions = {
|
|
||||||
# function_name: Function(
|
|
||||||
# name=function_name,
|
|
||||||
# arn=function_arn,
|
|
||||||
# region=AWS_REGION,
|
|
||||||
# runtime=function_runtime,
|
|
||||||
# )
|
|
||||||
# }
|
|
||||||
# # CloudTrail Client
|
|
||||||
# cloudtrail_client = mock.MagicMock
|
|
||||||
# trail_name = "test-trail"
|
|
||||||
# cloudtrail_client.trails = [
|
|
||||||
# Trail(
|
|
||||||
# name=trail_name,
|
|
||||||
# is_multiregion=False,
|
|
||||||
# home_region=AWS_REGION,
|
|
||||||
# arn="",
|
|
||||||
# region=AWS_REGION,
|
|
||||||
# is_logging=True,
|
|
||||||
# log_file_validation_enabled=True,
|
|
||||||
# latest_cloudwatch_delivery_time=datetime(2022, 1, 1),
|
|
||||||
# s3_bucket="",
|
|
||||||
# kms_key="",
|
|
||||||
# log_group_arn="",
|
|
||||||
# data_events=[
|
|
||||||
# {
|
|
||||||
# "ReadWriteType": "All",
|
|
||||||
# "IncludeManagementEvents": True,
|
|
||||||
# "DataResources": [
|
|
||||||
# {
|
|
||||||
# "Type": "AWS::Lambda::Function",
|
|
||||||
# "Values": [
|
|
||||||
# function_arn,
|
|
||||||
# ],
|
|
||||||
# },
|
|
||||||
# ],
|
|
||||||
# "ExcludeManagementEventSources": [],
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# )
|
|
||||||
# ]
|
|
||||||
|
|
||||||
# with mock.patch(
|
with mock.patch(
|
||||||
# "providers.aws.services.awslambda.awslambda_service.Lambda",
|
"providers.aws.services.awslambda.awslambda_service.Lambda",
|
||||||
# new=lambda_client,
|
new=lambda_client,
|
||||||
# ), mock.patch(
|
), mock.patch(
|
||||||
# "providers.aws.services.cloudtrail.cloudtrail_service.Cloudtrail",
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
# new=cloudtrail_client,
|
self.set_mocked_audit_info(),
|
||||||
# ):
|
), mock.patch(
|
||||||
|
"providers.aws.services.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.cloudtrail_client",
|
||||||
|
new=Cloudtrail(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
|
||||||
# #
|
# Test Check
|
||||||
# # Test Check
|
from providers.aws.services.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled import (
|
||||||
# from providers.aws.services.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled import (
|
awslambda_function_invoke_api_operations_cloudtrail_logging_enabled,
|
||||||
# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled,
|
)
|
||||||
# )
|
|
||||||
|
|
||||||
# check = (
|
check = (
|
||||||
# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled()
|
awslambda_function_invoke_api_operations_cloudtrail_logging_enabled()
|
||||||
# )
|
)
|
||||||
# result = check.execute()
|
result = check.execute()
|
||||||
|
|
||||||
# assert len(result) == 1
|
assert len(result) == 1
|
||||||
# assert result[0].region == AWS_REGION
|
assert result[0].region == AWS_REGION
|
||||||
# assert result[0].resource_id == function_name
|
assert result[0].resource_id == function_name
|
||||||
# assert result[0].resource_arn == function_arn
|
assert result[0].resource_arn == function_arn
|
||||||
# assert result[0].status == "PASS"
|
assert result[0].status == "FAIL"
|
||||||
# assert (
|
assert (
|
||||||
# result[0].status_extended
|
result[0].status_extended
|
||||||
# == f"Lambda function {function_name} is recorded by CloudTrail {trail_name}"
|
== f"Lambda function {function_name} is not recorded by CloudTrail"
|
||||||
# )
|
)
|
||||||
|
|
||||||
|
@mock_cloudtrail
|
||||||
|
@mock_s3
|
||||||
|
def test_lambda_recorded_by_cloudtrail(self):
|
||||||
|
# Lambda Client
|
||||||
|
lambda_client = mock.MagicMock
|
||||||
|
function_name = "test-lambda"
|
||||||
|
function_runtime = "python3.9"
|
||||||
|
function_arn = (
|
||||||
|
f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}"
|
||||||
|
)
|
||||||
|
lambda_client.functions = {
|
||||||
|
function_name: Function(
|
||||||
|
name=function_name,
|
||||||
|
arn=function_arn,
|
||||||
|
region=AWS_REGION,
|
||||||
|
runtime=function_runtime,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# CloudTrail Client
|
||||||
|
cloudtrail_client = client("cloudtrail", region_name=AWS_REGION)
|
||||||
|
s3_client = client("s3", region_name=AWS_REGION)
|
||||||
|
trail_name = "test-trail"
|
||||||
|
bucket_name = "test-bucket"
|
||||||
|
s3_client.create_bucket(Bucket=bucket_name)
|
||||||
|
cloudtrail_client.create_trail(
|
||||||
|
Name=trail_name, S3BucketName=bucket_name, IsMultiRegionTrail=False
|
||||||
|
)
|
||||||
|
_ = cloudtrail_client.put_event_selectors(
|
||||||
|
TrailName=trail_name,
|
||||||
|
EventSelectors=[
|
||||||
|
{
|
||||||
|
"ReadWriteType": "All",
|
||||||
|
"IncludeManagementEvents": True,
|
||||||
|
"DataResources": [
|
||||||
|
{"Type": "AWS::Lambda::Function", "Values": [function_arn]}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.awslambda.awslambda_service.Lambda",
|
||||||
|
new=lambda_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.cloudtrail_client",
|
||||||
|
new=Cloudtrail(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled import (
|
||||||
|
awslambda_function_invoke_api_operations_cloudtrail_logging_enabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = (
|
||||||
|
awslambda_function_invoke_api_operations_cloudtrail_logging_enabled()
|
||||||
|
)
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0].region == AWS_REGION
|
||||||
|
assert result[0].resource_id == function_name
|
||||||
|
assert result[0].resource_arn == function_arn
|
||||||
|
assert result[0].status == "PASS"
|
||||||
|
assert (
|
||||||
|
result[0].status_extended
|
||||||
|
== f"Lambda function {function_name} is recorded by CloudTrail {trail_name}"
|
||||||
|
)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class EC2:
|
|||||||
def __init__(self, audit_info):
|
def __init__(self, audit_info):
|
||||||
self.service = "ec2"
|
self.service = "ec2"
|
||||||
self.session = audit_info.audit_session
|
self.session = audit_info.audit_session
|
||||||
|
self.audited_partition = audit_info.audited_partition
|
||||||
self.audited_account = audit_info.audited_account
|
self.audited_account = audit_info.audited_account
|
||||||
self.regional_clients = generate_regional_clients(self.service, audit_info)
|
self.regional_clients = generate_regional_clients(self.service, audit_info)
|
||||||
self.instances = []
|
self.instances = []
|
||||||
@@ -264,11 +265,14 @@ class EC2:
|
|||||||
association_id = address["AssociationId"]
|
association_id = address["AssociationId"]
|
||||||
if "AllocationId" in address:
|
if "AllocationId" in address:
|
||||||
allocation_id = address["AllocationId"]
|
allocation_id = address["AllocationId"]
|
||||||
|
elastic_ip_arn = f"arn:{self.audited_partition}:ec2:{regional_client.region}:{self.audited_account}:eip-allocation/{allocation_id}"
|
||||||
|
|
||||||
self.elastic_ips.append(
|
self.elastic_ips.append(
|
||||||
ElasticIP(
|
ElasticIP(
|
||||||
public_ip,
|
public_ip,
|
||||||
association_id,
|
association_id,
|
||||||
allocation_id,
|
allocation_id,
|
||||||
|
elastic_ip_arn,
|
||||||
regional_client.region,
|
regional_client.region,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -403,13 +407,15 @@ class NetworkACL:
|
|||||||
class ElasticIP:
|
class ElasticIP:
|
||||||
public_ip: str
|
public_ip: str
|
||||||
association_id: str
|
association_id: str
|
||||||
|
arn: str
|
||||||
allocation_id: str
|
allocation_id: str
|
||||||
region: str
|
region: str
|
||||||
|
|
||||||
def __init__(self, public_ip, association_id, allocation_id, region):
|
def __init__(self, public_ip, association_id, allocation_id, arn, region):
|
||||||
self.public_ip = public_ip
|
self.public_ip = public_ip
|
||||||
self.association_id = association_id
|
self.association_id = association_id
|
||||||
self.allocation_id = allocation_id
|
self.allocation_id = allocation_id
|
||||||
|
self.arn = arn
|
||||||
self.region = region
|
self.region = region
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -205,8 +205,14 @@ class Test_EC2_Service:
|
|||||||
def test__describe_addresses__(self):
|
def test__describe_addresses__(self):
|
||||||
# Generate EC2 Client
|
# Generate EC2 Client
|
||||||
ec2_client = client("ec2", region_name=AWS_REGION)
|
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||||
ec2_client.allocate_address(Domain="vpc", Address="127.38.43.222")
|
allocation_id = ec2_client.allocate_address(
|
||||||
|
Domain="vpc", Address="127.38.43.222"
|
||||||
|
)["AllocationId"]
|
||||||
# EC2 client for this test class
|
# EC2 client for this test class
|
||||||
audit_info = self.set_mocked_audit_info()
|
audit_info = self.set_mocked_audit_info()
|
||||||
ec2 = EC2(audit_info)
|
ec2 = EC2(audit_info)
|
||||||
assert "127.38.43.222" in str(ec2.elastic_ips)
|
assert "127.38.43.222" in str(ec2.elastic_ips)
|
||||||
|
assert (
|
||||||
|
ec2.elastic_ips[0].arn
|
||||||
|
== f"arn:aws:ec2:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:eip-allocation/{allocation_id}"
|
||||||
|
)
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ class ELB:
|
|||||||
def __init__(self, audit_info):
|
def __init__(self, audit_info):
|
||||||
self.service = "elb"
|
self.service = "elb"
|
||||||
self.session = audit_info.audit_session
|
self.session = audit_info.audit_session
|
||||||
|
self.audited_partition = audit_info.audited_partition
|
||||||
|
self.audited_account = audit_info.audited_account
|
||||||
self.regional_clients = generate_regional_clients(self.service, audit_info)
|
self.regional_clients = generate_regional_clients(self.service, audit_info)
|
||||||
self.loadbalancers = []
|
self.loadbalancers = []
|
||||||
self.__threading_call__(self.__describe_load_balancers__)
|
self.__threading_call__(self.__describe_load_balancers__)
|
||||||
@@ -48,6 +50,7 @@ class ELB:
|
|||||||
self.loadbalancers.append(
|
self.loadbalancers.append(
|
||||||
LoadBalancer(
|
LoadBalancer(
|
||||||
name=elb["LoadBalancerName"],
|
name=elb["LoadBalancerName"],
|
||||||
|
arn=f"arn:{self.audited_partition}:elasticloadbalancing:{regional_client.region}:{self.audited_account}:loadbalancer/{elb['LoadBalancerName']}",
|
||||||
dns=elb["DNSName"],
|
dns=elb["DNSName"],
|
||||||
region=regional_client.region,
|
region=regional_client.region,
|
||||||
scheme=elb["Scheme"],
|
scheme=elb["Scheme"],
|
||||||
@@ -85,6 +88,7 @@ class Listener(BaseModel):
|
|||||||
class LoadBalancer(BaseModel):
|
class LoadBalancer(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
dns: str
|
dns: str
|
||||||
|
arn: str
|
||||||
region: str
|
region: str
|
||||||
scheme: str
|
scheme: str
|
||||||
access_logs: Optional[bool]
|
access_logs: Optional[bool]
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ class Test_ELB_Service:
|
|||||||
assert elb.loadbalancers[0].name == "my-lb"
|
assert elb.loadbalancers[0].name == "my-lb"
|
||||||
assert elb.loadbalancers[0].region == AWS_REGION
|
assert elb.loadbalancers[0].region == AWS_REGION
|
||||||
assert elb.loadbalancers[0].scheme == "internal"
|
assert elb.loadbalancers[0].scheme == "internal"
|
||||||
|
assert (
|
||||||
|
elb.loadbalancers[0].arn
|
||||||
|
== f"arn:aws:elasticloadbalancing:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:loadbalancer/my-lb"
|
||||||
|
)
|
||||||
|
|
||||||
# Test ELB Describe Load Balancers Attributes
|
# Test ELB Describe Load Balancers Attributes
|
||||||
@mock_ec2
|
@mock_ec2
|
||||||
@@ -124,3 +128,7 @@ class Test_ELB_Service:
|
|||||||
assert elb.loadbalancers[0].region == AWS_REGION
|
assert elb.loadbalancers[0].region == AWS_REGION
|
||||||
assert elb.loadbalancers[0].scheme == "internal"
|
assert elb.loadbalancers[0].scheme == "internal"
|
||||||
assert elb.loadbalancers[0].access_logs
|
assert elb.loadbalancers[0].access_logs
|
||||||
|
assert (
|
||||||
|
elb.loadbalancers[0].arn
|
||||||
|
== f"arn:aws:elasticloadbalancing:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:loadbalancer/my-lb"
|
||||||
|
)
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||||
|
from providers.aws.services.globalaccelerator.globalaccelerator_service import (
|
||||||
|
GlobalAccelerator,
|
||||||
|
)
|
||||||
|
|
||||||
|
globalaccelerator_client = GlobalAccelerator(current_audit_info)
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
from lib.logger import logger
|
||||||
|
from providers.aws.aws_provider import get_region_global_service
|
||||||
|
|
||||||
|
|
||||||
|
################### GlobalAccelerator
|
||||||
|
class GlobalAccelerator:
|
||||||
|
def __init__(self, audit_info):
|
||||||
|
self.service = "globalaccelerator"
|
||||||
|
self.session = audit_info.audit_session
|
||||||
|
self.audited_account = audit_info.audited_account
|
||||||
|
self.region = get_region_global_service(audit_info)
|
||||||
|
self.client = self.session.client(self.service, self.region)
|
||||||
|
self.accelerators = {}
|
||||||
|
self.__list_accelerators__()
|
||||||
|
|
||||||
|
def __get_session__(self):
|
||||||
|
return self.session
|
||||||
|
|
||||||
|
def __list_accelerators__(self):
|
||||||
|
logger.info("GlobalAccelerator - Listing Accelerators...")
|
||||||
|
try:
|
||||||
|
list_accelerators_paginator = self.client.get_paginator("list_accelerators")
|
||||||
|
for page in list_accelerators_paginator.paginate():
|
||||||
|
for accelerator in page["Accelerators"]:
|
||||||
|
accelerator_arn = accelerator["AcceleratorArn"]
|
||||||
|
accelerator_name = accelerator["Name"]
|
||||||
|
enabled = accelerator["Enabled"]
|
||||||
|
self.accelerators[accelerator_name] = Accelerator(
|
||||||
|
name=accelerator_name,
|
||||||
|
arn=accelerator_arn,
|
||||||
|
region=self.region,
|
||||||
|
enabled=enabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as error:
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Accelerator(BaseModel):
|
||||||
|
arn: str
|
||||||
|
name: str
|
||||||
|
region: str
|
||||||
|
enabled: bool
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
from providers.aws.lib.audit_info.models import AWS_Audit_Info
|
||||||
|
from providers.aws.services.globalaccelerator.globalaccelerator_service import (
|
||||||
|
GlobalAccelerator,
|
||||||
|
)
|
||||||
|
from mock import patch
|
||||||
|
from moto.core import DEFAULT_ACCOUNT_ID
|
||||||
|
import botocore
|
||||||
|
from boto3 import session
|
||||||
|
|
||||||
|
# Mock Test Region
|
||||||
|
AWS_REGION = "eu-west-1"
|
||||||
|
|
||||||
|
# Mocking Access Analyzer Calls
|
||||||
|
make_api_call = botocore.client.BaseClient._make_api_call
|
||||||
|
|
||||||
|
|
||||||
|
def mock_make_api_call(self, operation_name, kwarg):
|
||||||
|
"""We have to mock every AWS API call using Boto3"""
|
||||||
|
if operation_name == "ListAccelerators":
|
||||||
|
return {
|
||||||
|
"Accelerators": [
|
||||||
|
{
|
||||||
|
"AcceleratorArn": f"arn:aws:globalaccelerator::{DEFAULT_ACCOUNT_ID}:accelerator/5555abcd-abcd-5555-abcd-5555EXAMPLE1",
|
||||||
|
"Name": "TestAccelerator",
|
||||||
|
"IpAddressType": "IPV4",
|
||||||
|
"Enabled": True,
|
||||||
|
"IpSets": [
|
||||||
|
{
|
||||||
|
"IpFamily": "IPv4",
|
||||||
|
"IpAddresses": ["192.0.2.250", "198.51.100.52"],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"DnsName": "5a5a5a5a5a5a5a5a.awsglobalaccelerator.com",
|
||||||
|
"Status": "DEPLOYED",
|
||||||
|
"CreatedTime": 1552424416.0,
|
||||||
|
"LastModifiedTime": 1569375641.0,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if operation_name == "GetSubscriptionState":
|
||||||
|
return {"SubscriptionState": "ACTIVE"}
|
||||||
|
|
||||||
|
return make_api_call(self, operation_name, kwarg)
|
||||||
|
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
class Test_GlobalAccelerator_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=DEFAULT_ACCOUNT_ID,
|
||||||
|
audited_user_id=None,
|
||||||
|
audited_partition="aws",
|
||||||
|
audited_identity_arn=None,
|
||||||
|
profile=None,
|
||||||
|
profile_region=AWS_REGION,
|
||||||
|
credentials=None,
|
||||||
|
assumed_role_info=None,
|
||||||
|
audited_regions=None,
|
||||||
|
organizations_metadata=None,
|
||||||
|
)
|
||||||
|
return audit_info
|
||||||
|
|
||||||
|
# Test GlobalAccelerator Service
|
||||||
|
def test_service(self):
|
||||||
|
# GlobalAccelerator client for this test class
|
||||||
|
audit_info = self.set_mocked_audit_info()
|
||||||
|
globalaccelerator = GlobalAccelerator(audit_info)
|
||||||
|
assert globalaccelerator.service == "globalaccelerator"
|
||||||
|
|
||||||
|
# Test GlobalAccelerator Client
|
||||||
|
def test_client(self):
|
||||||
|
# GlobalAccelerator client for this test class
|
||||||
|
audit_info = self.set_mocked_audit_info()
|
||||||
|
globalaccelerator = GlobalAccelerator(audit_info)
|
||||||
|
assert globalaccelerator.client.__class__.__name__ == "GlobalAccelerator"
|
||||||
|
|
||||||
|
# Test GlobalAccelerator Session
|
||||||
|
def test__get_session__(self):
|
||||||
|
# GlobalAccelerator client for this test class
|
||||||
|
audit_info = self.set_mocked_audit_info()
|
||||||
|
globalaccelerator = GlobalAccelerator(audit_info)
|
||||||
|
assert globalaccelerator.session.__class__.__name__ == "Session"
|
||||||
|
|
||||||
|
def test__list_accelerators__(self):
|
||||||
|
# GlobalAccelerator client for this test class
|
||||||
|
audit_info = self.set_mocked_audit_info()
|
||||||
|
globalaccelerator = GlobalAccelerator(audit_info)
|
||||||
|
|
||||||
|
accelerator_arn = f"arn:aws:globalaccelerator::{DEFAULT_ACCOUNT_ID}:accelerator/5555abcd-abcd-5555-abcd-5555EXAMPLE1"
|
||||||
|
accelerator_name = "TestAccelerator"
|
||||||
|
|
||||||
|
assert globalaccelerator.accelerators
|
||||||
|
assert len(globalaccelerator.accelerators) == 1
|
||||||
|
assert globalaccelerator.accelerators[accelerator_name]
|
||||||
|
assert globalaccelerator.accelerators[accelerator_name].name == accelerator_name
|
||||||
|
assert globalaccelerator.accelerators[accelerator_name].arn == accelerator_arn
|
||||||
|
assert globalaccelerator.accelerators[accelerator_name].region == AWS_REGION
|
||||||
|
assert globalaccelerator.accelerators[accelerator_name].enabled
|
||||||
@@ -22,7 +22,7 @@ class kms_key_not_publicly_accessible(Check):
|
|||||||
report.status_extended = (
|
report.status_extended = (
|
||||||
f"KMS key {key.id} may be publicly accessible!"
|
f"KMS key {key.id} may be publicly accessible!"
|
||||||
)
|
)
|
||||||
else:
|
elif "AWS" in statement["Principal"]:
|
||||||
if type(statement["Principal"]["AWS"]) == str:
|
if type(statement["Principal"]["AWS"]) == str:
|
||||||
principals = [statement["Principal"]["AWS"]]
|
principals = [statement["Principal"]["AWS"]]
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class Test_route53_public_hosted_zones_cloudwatch_logging_enabled:
|
|||||||
route53.hosted_zones = {
|
route53.hosted_zones = {
|
||||||
hosted_zone_name: HostedZone(
|
hosted_zone_name: HostedZone(
|
||||||
name=hosted_zone_name,
|
name=hosted_zone_name,
|
||||||
|
arn=f"arn:aws:route53:::{hosted_zone_id}",
|
||||||
id=hosted_zone_id,
|
id=hosted_zone_id,
|
||||||
private_zone=False,
|
private_zone=False,
|
||||||
region=AWS_REGION,
|
region=AWS_REGION,
|
||||||
@@ -72,6 +73,7 @@ class Test_route53_public_hosted_zones_cloudwatch_logging_enabled:
|
|||||||
route53.hosted_zones = {
|
route53.hosted_zones = {
|
||||||
hosted_zone_name: HostedZone(
|
hosted_zone_name: HostedZone(
|
||||||
name=hosted_zone_name,
|
name=hosted_zone_name,
|
||||||
|
arn=f"arn:aws:route53:::{hosted_zone_id}",
|
||||||
id=hosted_zone_id,
|
id=hosted_zone_id,
|
||||||
private_zone=False,
|
private_zone=False,
|
||||||
region=AWS_REGION,
|
region=AWS_REGION,
|
||||||
@@ -106,6 +108,7 @@ class Test_route53_public_hosted_zones_cloudwatch_logging_enabled:
|
|||||||
route53.hosted_zones = {
|
route53.hosted_zones = {
|
||||||
hosted_zone_name: HostedZone(
|
hosted_zone_name: HostedZone(
|
||||||
name=hosted_zone_name,
|
name=hosted_zone_name,
|
||||||
|
arn=f"arn:aws:route53:::{hosted_zone_id}",
|
||||||
id=hosted_zone_id,
|
id=hosted_zone_id,
|
||||||
private_zone=True,
|
private_zone=True,
|
||||||
region=AWS_REGION,
|
region=AWS_REGION,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class Route53:
|
|||||||
def __init__(self, audit_info):
|
def __init__(self, audit_info):
|
||||||
self.service = "route53"
|
self.service = "route53"
|
||||||
self.session = audit_info.audit_session
|
self.session = audit_info.audit_session
|
||||||
|
self.audited_partition = audit_info.audited_partition
|
||||||
self.client = self.session.client(self.service)
|
self.client = self.session.client(self.service)
|
||||||
self.region = get_region_global_service(audit_info)
|
self.region = get_region_global_service(audit_info)
|
||||||
self.hosted_zones = {}
|
self.hosted_zones = {}
|
||||||
@@ -32,6 +33,7 @@ class Route53:
|
|||||||
id=hosted_zone_id,
|
id=hosted_zone_id,
|
||||||
name=hosted_zone_name,
|
name=hosted_zone_name,
|
||||||
private_zone=private_zone,
|
private_zone=private_zone,
|
||||||
|
arn=f"arn:{self.audited_partition}:route53:::{hosted_zone_id}",
|
||||||
region=self.region,
|
region=self.region,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -69,6 +71,7 @@ class LoggingConfig(BaseModel):
|
|||||||
|
|
||||||
class HostedZone(BaseModel):
|
class HostedZone(BaseModel):
|
||||||
id: str
|
id: str
|
||||||
|
arn: str
|
||||||
name: str
|
name: str
|
||||||
private_zone: bool
|
private_zone: bool
|
||||||
logging_config: LoggingConfig = None
|
logging_config: LoggingConfig = None
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ class Test_Route53_Service:
|
|||||||
assert len(route53.hosted_zones) == 1
|
assert len(route53.hosted_zones) == 1
|
||||||
assert route53.hosted_zones[hosted_zone_id]
|
assert route53.hosted_zones[hosted_zone_id]
|
||||||
assert route53.hosted_zones[hosted_zone_id].id == hosted_zone_id
|
assert route53.hosted_zones[hosted_zone_id].id == hosted_zone_id
|
||||||
|
assert (
|
||||||
|
route53.hosted_zones[hosted_zone_id].arn
|
||||||
|
== f"arn:aws:route53:::{hosted_zone_id}"
|
||||||
|
)
|
||||||
assert route53.hosted_zones[hosted_zone_id].name == hosted_zone_name
|
assert route53.hosted_zones[hosted_zone_id].name == hosted_zone_name
|
||||||
assert route53.hosted_zones[hosted_zone_id].private_zone
|
assert route53.hosted_zones[hosted_zone_id].private_zone
|
||||||
assert route53.hosted_zones[hosted_zone_id].logging_config
|
assert route53.hosted_zones[hosted_zone_id].logging_config
|
||||||
@@ -131,6 +135,10 @@ class Test_Route53_Service:
|
|||||||
assert len(route53.hosted_zones) == 1
|
assert len(route53.hosted_zones) == 1
|
||||||
assert route53.hosted_zones[hosted_zone_id]
|
assert route53.hosted_zones[hosted_zone_id]
|
||||||
assert route53.hosted_zones[hosted_zone_id].id == hosted_zone_id
|
assert route53.hosted_zones[hosted_zone_id].id == hosted_zone_id
|
||||||
|
assert (
|
||||||
|
route53.hosted_zones[hosted_zone_id].arn
|
||||||
|
== f"arn:aws:route53:::{hosted_zone_id}"
|
||||||
|
)
|
||||||
assert route53.hosted_zones[hosted_zone_id].name == hosted_zone_name
|
assert route53.hosted_zones[hosted_zone_id].name == hosted_zone_name
|
||||||
assert not route53.hosted_zones[hosted_zone_id].private_zone
|
assert not route53.hosted_zones[hosted_zone_id].private_zone
|
||||||
assert route53.hosted_zones[hosted_zone_id].logging_config
|
assert route53.hosted_zones[hosted_zone_id].logging_config
|
||||||
@@ -159,6 +167,10 @@ class Test_Route53_Service:
|
|||||||
assert len(route53.hosted_zones) == 1
|
assert len(route53.hosted_zones) == 1
|
||||||
assert route53.hosted_zones[hosted_zone_id]
|
assert route53.hosted_zones[hosted_zone_id]
|
||||||
assert route53.hosted_zones[hosted_zone_id].id == hosted_zone_id
|
assert route53.hosted_zones[hosted_zone_id].id == hosted_zone_id
|
||||||
|
assert (
|
||||||
|
route53.hosted_zones[hosted_zone_id].arn
|
||||||
|
== f"arn:aws:route53:::{hosted_zone_id}"
|
||||||
|
)
|
||||||
assert route53.hosted_zones[hosted_zone_id].name == hosted_zone_name
|
assert route53.hosted_zones[hosted_zone_id].name == hosted_zone_name
|
||||||
assert route53.hosted_zones[hosted_zone_id].private_zone
|
assert route53.hosted_zones[hosted_zone_id].private_zone
|
||||||
assert not route53.hosted_zones[hosted_zone_id].logging_config
|
assert not route53.hosted_zones[hosted_zone_id].logging_config
|
||||||
@@ -183,6 +195,10 @@ class Test_Route53_Service:
|
|||||||
assert len(route53.hosted_zones) == 1
|
assert len(route53.hosted_zones) == 1
|
||||||
assert route53.hosted_zones[hosted_zone_id]
|
assert route53.hosted_zones[hosted_zone_id]
|
||||||
assert route53.hosted_zones[hosted_zone_id].id == hosted_zone_id
|
assert route53.hosted_zones[hosted_zone_id].id == hosted_zone_id
|
||||||
|
assert (
|
||||||
|
route53.hosted_zones[hosted_zone_id].arn
|
||||||
|
== f"arn:aws:route53:::{hosted_zone_id}"
|
||||||
|
)
|
||||||
assert route53.hosted_zones[hosted_zone_id].name == hosted_zone_name
|
assert route53.hosted_zones[hosted_zone_id].name == hosted_zone_name
|
||||||
assert not route53.hosted_zones[hosted_zone_id].private_zone
|
assert not route53.hosted_zones[hosted_zone_id].private_zone
|
||||||
assert not route53.hosted_zones[hosted_zone_id].logging_config
|
assert not route53.hosted_zones[hosted_zone_id].logging_config
|
||||||
|
|||||||
@@ -182,9 +182,9 @@ class S3Control:
|
|||||||
def __init__(self, audit_info):
|
def __init__(self, audit_info):
|
||||||
self.service = "s3control"
|
self.service = "s3control"
|
||||||
self.session = audit_info.audit_session
|
self.session = audit_info.audit_session
|
||||||
self.client = self.session.client(self.service)
|
|
||||||
self.audited_account = audit_info.audited_account
|
self.audited_account = audit_info.audited_account
|
||||||
self.region = get_region_global_service(audit_info)
|
self.region = get_region_global_service(audit_info)
|
||||||
|
self.client = self.session.client(self.service, self.region)
|
||||||
self.account_public_access_block = self.__get_public_access_block__()
|
self.account_public_access_block = self.__get_public_access_block__()
|
||||||
|
|
||||||
def __get_session__(self):
|
def __get_session__(self):
|
||||||
|
|||||||
0
providers/aws/services/shield/__init__.py
Normal file
0
providers/aws/services/shield/__init__.py
Normal file
@@ -1,50 +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_extra7166="7.166"
|
|
||||||
CHECK_TITLE_extra7166="[extra7166] Check if Elastic IP addresses with associations are protected by AWS Shield Advanced"
|
|
||||||
CHECK_SCORED_extra7166="NOT_SCORED"
|
|
||||||
CHECK_CIS_LEVEL_extra7166="EXTRA"
|
|
||||||
CHECK_SEVERITY_extra7166="Medium"
|
|
||||||
CHECK_ASFF_RESOURCE_TYPE_extra7166="AwsEc2Eip"
|
|
||||||
CHECK_ALTERNATE_check7166="extra7166"
|
|
||||||
CHECK_SERVICENAME_extra7166="shield"
|
|
||||||
CHECK_RISK_extra7166='AWS Shield Advanced provides expanded DDoS attack protection for your resources'
|
|
||||||
CHECK_REMEDIATION_extra7166='Add as a protected resource in AWS Shield Advanced.'
|
|
||||||
CHECK_DOC_extra7166='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html'
|
|
||||||
CHECK_CAF_EPIC_extra7166='Infrastructure security'
|
|
||||||
|
|
||||||
extra7166() {
|
|
||||||
if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
|
|
||||||
CALLER_IDENTITY=$($AWSCLI sts get-caller-identity $PROFILE_OPT --query Arn)
|
|
||||||
PARTITION=$(echo $CALLER_IDENTITY | cut -d: -f2)
|
|
||||||
ACCOUNT_ID=$(echo $CALLER_IDENTITY | cut -d: -f5)
|
|
||||||
for regx in $REGIONS; do
|
|
||||||
LIST_OF_ELASTIC_IPS_WITH_ASSOCIATIONS=$($AWSCLI ec2 describe-addresses $PROFILE_OPT --region $regx --query 'Addresses[?AssociationId].AllocationId' --output text)
|
|
||||||
if [[ $LIST_OF_ELASTIC_IPS_WITH_ASSOCIATIONS ]]; then
|
|
||||||
for elastic_ip in $LIST_OF_ELASTIC_IPS_WITH_ASSOCIATIONS; do
|
|
||||||
EIP_ARN="arn:${PARTITION}:ec2:${regx}:${ACCOUNT_ID}:eip-allocation/${elastic_ip}"
|
|
||||||
if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $EIP_ARN >/dev/null 2>&1; then
|
|
||||||
textPass "$regx: EIP $elastic_ip is protected by AWS Shield Advanced" "$regx" "$elastic_ip"
|
|
||||||
else
|
|
||||||
textFail "$regx: EIP $elastic_ip is not protected by AWS Shield Advanced" "$regx" "$elastic_ip"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
textInfo "$regx: no elastic IP addresses with assocations found" "$regx"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
textInfo "No AWS Shield Advanced subscription found. Skipping check."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
@@ -1,46 +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_extra7167="7.167"
|
|
||||||
CHECK_TITLE_extra7167="[extra7167] Check if Cloudfront distributions are protected by AWS Shield Advanced"
|
|
||||||
CHECK_SCORED_extra7167="NOT_SCORED"
|
|
||||||
CHECK_CIS_LEVEL_extra7167="EXTRA"
|
|
||||||
CHECK_SEVERITY_extra7167="Medium"
|
|
||||||
CHECK_ASFF_RESOURCE_TYPE_extra7167="AwsCloudFrontDistribution"
|
|
||||||
CHECK_ALTERNATE_check7167="extra7167"
|
|
||||||
CHECK_SERVICENAME_extra7167="shield"
|
|
||||||
CHECK_RISK_extra7167='AWS Shield Advanced provides expanded DDoS attack protection for your resources'
|
|
||||||
CHECK_REMEDIATION_extra7167='Add as a protected resource in AWS Shield Advanced.'
|
|
||||||
CHECK_DOC_extra7167='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html'
|
|
||||||
CHECK_CAF_EPIC_extra7167='Infrastructure security'
|
|
||||||
|
|
||||||
extra7167() {
|
|
||||||
if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
|
|
||||||
LIST_OF_CLOUDFRONT_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --query 'DistributionList.Items[*].[Id,ARN]' --output text | grep -v None)
|
|
||||||
if [[ $LIST_OF_CLOUDFRONT_DISTRIBUTIONS ]]; then
|
|
||||||
while read -r distribution; do
|
|
||||||
DISTRIBUTION_ID=$(echo $distribution | awk '{ print $1; }')
|
|
||||||
DISTRIBUTION_ARN=$(echo $distribution | awk '{ print $2; }')
|
|
||||||
if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $DISTRIBUTION_ARN >/dev/null 2>&1; then
|
|
||||||
textPass "$REGION: Cloudfront distribution $DISTRIBUTION_ID is protected by AWS Shield Advanced" "$REGION" "$DISTRIBUTION_ID"
|
|
||||||
else
|
|
||||||
textFail "$REGION: Cloudfront distribution $DISTRIBUTION_ID is not protected by AWS Shield Advanced" "$REGION" "$DISTRIBUTION_ID"
|
|
||||||
fi
|
|
||||||
done <<<"$LIST_OF_CLOUDFRONT_DISTRIBUTIONS"
|
|
||||||
else
|
|
||||||
textInfo "$REGION: no Cloudfront distributions found" "$REGION"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
textInfo "No AWS Shield Advanced subscription found. Skipping check."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
@@ -1,49 +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_extra7168="7.168"
|
|
||||||
CHECK_TITLE_extra7168="[extra7168] Check if Route53 hosted zones are protected by AWS Shield Advanced"
|
|
||||||
CHECK_SCORED_extra7168="NOT_SCORED"
|
|
||||||
CHECK_CIS_LEVEL_extra7168="EXTRA"
|
|
||||||
CHECK_SEVERITY_extra7168="Medium"
|
|
||||||
CHECK_ASFF_RESOURCE_TYPE_extra7168="AwsRoute53Domain"
|
|
||||||
CHECK_ALTERNATE_check7168="extra7168"
|
|
||||||
CHECK_SERVICENAME_extra7168="shield"
|
|
||||||
CHECK_RISK_extra7168='AWS Shield Advanced provides expanded DDoS attack protection for your resources'
|
|
||||||
CHECK_REMEDIATION_extra7168='Add as a protected resource in AWS Shield Advanced.'
|
|
||||||
CHECK_DOC_extra7168='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html'
|
|
||||||
CHECK_CAF_EPIC_extra7168='Infrastructure security'
|
|
||||||
|
|
||||||
extra7168() {
|
|
||||||
if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
|
|
||||||
CALLER_IDENTITY=$($AWSCLI sts get-caller-identity $PROFILE_OPT --query Arn)
|
|
||||||
PARTITION=$(echo $CALLER_IDENTITY | cut -d: -f2)
|
|
||||||
LIST_OF_ROUTE53_HOSTED_ZONES=$($AWSCLI route53 list-hosted-zones $PROFILE_OPT --query 'HostedZones[*].[Id,Name]' --output text)
|
|
||||||
if [[ $LIST_OF_ROUTE53_HOSTED_ZONES ]]; then
|
|
||||||
while read -r hosted_zone; do
|
|
||||||
HOSTED_ZONE_ID=$(echo $hosted_zone | awk '{ print $1; }')
|
|
||||||
HOSTED_ZONE_NAME=$(echo $hosted_zone | awk '{ print $2; }')
|
|
||||||
HOSTED_ZONE_ARN="arn:${PARTITION}:route53:::${HOSTED_ZONE_ID:1}"
|
|
||||||
if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $HOSTED_ZONE_ARN >/dev/null 2>&1; then
|
|
||||||
textPass "$REGION: Route53 Hosted Zone $HOSTED_ZONE_NAME is protected by AWS Shield Advanced" "$REGION" "$HOSTED_ZONE_NAME"
|
|
||||||
else
|
|
||||||
textFail "$REGION: Route53 Hosted Zone $HOSTED_ZONE_NAME is not protected by AWS Shield Advanced" "$REGION" "$HOSTED_ZONE_NAME"
|
|
||||||
fi
|
|
||||||
done <<<"$LIST_OF_ROUTE53_HOSTED_ZONES"
|
|
||||||
else
|
|
||||||
textInfo "$REGION: no Route53 hosted zones found" "$REGION"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
textInfo "No AWS Shield Advanced subscription found. Skipping check."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
@@ -1,46 +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_extra7169="7.169"
|
|
||||||
CHECK_TITLE_extra7169="[extra7169] Check if global accelerators are protected by AWS Shield Advanced"
|
|
||||||
CHECK_SCORED_extra7169="NOT_SCORED"
|
|
||||||
CHECK_CIS_LEVEL_extra7169="EXTRA"
|
|
||||||
CHECK_SEVERITY_extra7169="Medium"
|
|
||||||
CHECK_ASFF_RESOURCE_TYPE_extra7169="AwsGlobalAccelerator"
|
|
||||||
CHECK_ALTERNATE_check7169="extra7169"
|
|
||||||
CHECK_SERVICENAME_extra7169="shield"
|
|
||||||
CHECK_RISK_extra7169='AWS Shield Advanced provides expanded DDoS attack protection for your resources'
|
|
||||||
CHECK_REMEDIATION_extra7169='Add as a protected resource in AWS Shield Advanced.'
|
|
||||||
CHECK_DOC_extra7169='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html'
|
|
||||||
CHECK_CAF_EPIC_extra7169='Infrastructure security'
|
|
||||||
|
|
||||||
extra7169() {
|
|
||||||
if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
|
|
||||||
LIST_OF_GLOBAL_ACCELERATORS=$($AWSCLI globalaccelerator list-accelerators --region us-west-2 $PROFILE_OPT --query 'Accelerators[?Enabled].[Name,AcceleratorArn]' --output text)
|
|
||||||
if [[ $LIST_OF_GLOBAL_ACCELERATORS ]]; then
|
|
||||||
while read -r accelerator; do
|
|
||||||
ACCELERATOR_NAME=$(echo $accelerator | awk '{ print $1; }')
|
|
||||||
ACCELERATOR_ARN=$(echo $accelerator | awk '{ print $2; }')
|
|
||||||
if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $ACCELERATOR_ARN >/dev/null 2>&1; then
|
|
||||||
textPass "$REGION: Global Accelerator $ACCELERATOR_NAME is protected by AWS Shield Advanced" "$REGION" "$ACCELERATOR_NAME"
|
|
||||||
else
|
|
||||||
textFail "$REGION: Global Accelerator $ACCELERATOR_NAME is not protected by AWS Shield Advanced" "$REGION" "$ACCELERATOR_NAME"
|
|
||||||
fi
|
|
||||||
done <<<"$LIST_OF_GLOBAL_ACCELERATORS"
|
|
||||||
else
|
|
||||||
textInfo "$REGION: no global accelerators found" "$REGION"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
textInfo "No AWS Shield Advanced subscription found. Skipping check."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
@@ -1,50 +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_extra7171="7.171"
|
|
||||||
CHECK_TITLE_extra7171="[extra7171] Check if classic load balancers are protected by AWS Shield Advanced"
|
|
||||||
CHECK_SCORED_extra7171="NOT_SCORED"
|
|
||||||
CHECK_CIS_LEVEL_extra7171="EXTRA"
|
|
||||||
CHECK_SEVERITY_extra7171="Medium"
|
|
||||||
CHECK_ASFF_RESOURCE_TYPE_extra7171="AwsElasticLoadBalancingLoadBalancer"
|
|
||||||
CHECK_ALTERNATE_check7171="extra7171"
|
|
||||||
CHECK_SERVICENAME_extra7171="shield"
|
|
||||||
CHECK_RISK_extra7171='AWS Shield Advanced provides expanded DDoS attack protection for your resources'
|
|
||||||
CHECK_REMEDIATION_extra7171='Add as a protected resource in AWS Shield Advanced.'
|
|
||||||
CHECK_DOC_extra7171='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html'
|
|
||||||
CHECK_CAF_EPIC_extra7171='Infrastructure security'
|
|
||||||
|
|
||||||
extra7171() {
|
|
||||||
if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
|
|
||||||
CALLER_IDENTITY=$($AWSCLI sts get-caller-identity $PROFILE_OPT --query Arn)
|
|
||||||
PARTITION=$(echo $CALLER_IDENTITY | cut -d: -f2)
|
|
||||||
ACCOUNT_ID=$(echo $CALLER_IDENTITY | cut -d: -f5)
|
|
||||||
for regx in $REGIONS; do
|
|
||||||
LIST_OF_CLASSIC_LOAD_BALANCERS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[?Scheme == `internet-facing`].[LoadBalancerName]' --output text |grep -v '^None$')
|
|
||||||
if [[ $LIST_OF_CLASSIC_LOAD_BALANCERS ]]; then
|
|
||||||
for elb in $LIST_OF_CLASSIC_LOAD_BALANCERS; do
|
|
||||||
ELB_ARN="arn:${PARTITION}:elasticloadbalancing:${regx}:${ACCOUNT_ID}:loadbalancer/${elb}"
|
|
||||||
if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $ELB_ARN >/dev/null 2>&1; then
|
|
||||||
textPass "$regx: ELB $elb is protected by AWS Shield Advanced" "$regx" "$elb"
|
|
||||||
else
|
|
||||||
textFail "$regx: ELB $elb is not protected by AWS Shield Advanced" "$regx" "$elb"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
textInfo "$regx: no classic load balancers found" "$regx"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
textInfo "No AWS Shield Advanced subscription found. Skipping check."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"Provider": "aws",
|
||||||
|
"CheckID": "shield_advanced_protection_in_associated_elastic_ips",
|
||||||
|
"CheckTitle": "Check if Elastic IP addresses with associations are protected by AWS Shield Advanced.",
|
||||||
|
"CheckType": [],
|
||||||
|
"ServiceName": "shield",
|
||||||
|
"SubServiceName": "",
|
||||||
|
"ResourceIdTemplate": "",
|
||||||
|
"Severity": "medium",
|
||||||
|
"ResourceType": "AwsEc2Eip",
|
||||||
|
"Description": "Check if Elastic IP addresses with associations are protected by AWS Shield Advanced.",
|
||||||
|
"Risk": "AWS Shield Advanced provides expanded DDoS attack protection for your resources.",
|
||||||
|
"RelatedUrl": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html",
|
||||||
|
"Remediation": {
|
||||||
|
"Code": {
|
||||||
|
"CLI": "",
|
||||||
|
"NativeIaC": "",
|
||||||
|
"Other": "",
|
||||||
|
"Terraform": ""
|
||||||
|
},
|
||||||
|
"Recommendation": {
|
||||||
|
"Text": "Add as a protected resource in AWS Shield Advanced.",
|
||||||
|
"Url": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Categories": [],
|
||||||
|
"Tags": {
|
||||||
|
"Tag1Key": "value",
|
||||||
|
"Tag2Key": "value"
|
||||||
|
},
|
||||||
|
"DependsOn": [],
|
||||||
|
"RelatedTo": [],
|
||||||
|
"Notes": "",
|
||||||
|
"Compliance": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
from lib.check.models import Check, Check_Report
|
||||||
|
from providers.aws.services.shield.shield_client import shield_client
|
||||||
|
from providers.aws.services.ec2.ec2_client import ec2_client
|
||||||
|
|
||||||
|
|
||||||
|
class shield_advanced_protection_in_associated_elastic_ips(Check):
|
||||||
|
def execute(self):
|
||||||
|
findings = []
|
||||||
|
if shield_client.enabled:
|
||||||
|
for elastic_ip in ec2_client.elastic_ips:
|
||||||
|
report = Check_Report(self.metadata)
|
||||||
|
report.region = shield_client.region
|
||||||
|
report.resource_id = elastic_ip.allocation_id
|
||||||
|
report.resource_arn = elastic_ip.arn
|
||||||
|
report.status = "FAIL"
|
||||||
|
report.status_extended = f"Elastic IP {elastic_ip.allocation_id} is not protected by AWS Shield Advanced"
|
||||||
|
|
||||||
|
for protection in shield_client.protections.values():
|
||||||
|
if elastic_ip.arn == protection.resource_arn:
|
||||||
|
report.status = "PASS"
|
||||||
|
report.status_extended = f"Elastic IP {elastic_ip.allocation_id} is protected by AWS Shield Advanced"
|
||||||
|
break
|
||||||
|
|
||||||
|
findings.append(report)
|
||||||
|
|
||||||
|
return findings
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from boto3 import client, session
|
||||||
|
from mock import patch
|
||||||
|
from moto import mock_ec2
|
||||||
|
from moto.core import DEFAULT_ACCOUNT_ID
|
||||||
|
|
||||||
|
from providers.aws.lib.audit_info.models import AWS_Audit_Info
|
||||||
|
from providers.aws.services.shield.shield_service import Protection
|
||||||
|
|
||||||
|
AWS_REGION = "eu-west-1"
|
||||||
|
|
||||||
|
|
||||||
|
# Mock generate_regional_clients()
|
||||||
|
def mock_generate_regional_clients(service, audit_info):
|
||||||
|
regional_client = audit_info.audit_session.client(service, region_name=AWS_REGION)
|
||||||
|
regional_client.region = AWS_REGION
|
||||||
|
return {AWS_REGION: regional_client}
|
||||||
|
|
||||||
|
|
||||||
|
# Patch every AWS call using Boto3 and generate_regional_clients to have 1 client
|
||||||
|
@patch(
|
||||||
|
"providers.aws.services.accessanalyzer.accessanalyzer_service.generate_regional_clients",
|
||||||
|
new=mock_generate_regional_clients,
|
||||||
|
)
|
||||||
|
class Test_shield_advanced_protection_in_associated_elastic_ips:
|
||||||
|
# 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=DEFAULT_ACCOUNT_ID,
|
||||||
|
audited_user_id=None,
|
||||||
|
audited_partition="aws",
|
||||||
|
audited_identity_arn=None,
|
||||||
|
profile=None,
|
||||||
|
profile_region=AWS_REGION,
|
||||||
|
credentials=None,
|
||||||
|
assumed_role_info=None,
|
||||||
|
audited_regions=None,
|
||||||
|
organizations_metadata=None,
|
||||||
|
)
|
||||||
|
return audit_info
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_no_shield_not_active(self):
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = False
|
||||||
|
|
||||||
|
from providers.aws.services.ec2.ec2_service import EC2
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
new=self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips.ec2_client",
|
||||||
|
new=EC2(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips import (
|
||||||
|
shield_advanced_protection_in_associated_elastic_ips,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_associated_elastic_ips()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_shield_enabled_ip_protected(self):
|
||||||
|
# EC2 Client
|
||||||
|
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||||
|
resp = ec2_client.allocate_address(Domain="vpc", Address="127.38.43.222")
|
||||||
|
allocation_id = resp["AllocationId"]
|
||||||
|
elastic_ip_arn = f"arn:aws:ec2:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:eip-allocation/{allocation_id}"
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = True
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
protection_id = "test-protection"
|
||||||
|
shield_client.protections = {
|
||||||
|
protection_id: Protection(
|
||||||
|
id=protection_id,
|
||||||
|
name="",
|
||||||
|
resource_arn=elastic_ip_arn,
|
||||||
|
protection_arn="",
|
||||||
|
region=AWS_REGION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
from providers.aws.services.ec2.ec2_service import EC2
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
new=self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips.ec2_client",
|
||||||
|
new=EC2(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips import (
|
||||||
|
shield_advanced_protection_in_associated_elastic_ips,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_associated_elastic_ips()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0].region == AWS_REGION
|
||||||
|
assert result[0].resource_id == allocation_id
|
||||||
|
assert result[0].resource_arn == elastic_ip_arn
|
||||||
|
assert result[0].status == "PASS"
|
||||||
|
assert (
|
||||||
|
result[0].status_extended
|
||||||
|
== f"Elastic IP {allocation_id} is protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_shield_enabled_ip_not_protected(self):
|
||||||
|
# EC2 Client
|
||||||
|
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||||
|
resp = ec2_client.allocate_address(Domain="vpc", Address="127.38.43.222")
|
||||||
|
allocation_id = resp["AllocationId"]
|
||||||
|
elastic_ip_arn = f"arn:aws:ec2:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:eip-allocation/{allocation_id}"
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = True
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
shield_client.protections = {}
|
||||||
|
|
||||||
|
from providers.aws.services.ec2.ec2_service import EC2
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
new=self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips.ec2_client",
|
||||||
|
new=EC2(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips import (
|
||||||
|
shield_advanced_protection_in_associated_elastic_ips,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_associated_elastic_ips()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0].region == AWS_REGION
|
||||||
|
assert result[0].resource_id == allocation_id
|
||||||
|
assert result[0].resource_arn == elastic_ip_arn
|
||||||
|
assert result[0].status == "FAIL"
|
||||||
|
assert (
|
||||||
|
result[0].status_extended
|
||||||
|
== f"Elastic IP {allocation_id} is not protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
def test_shield_disabled_ip_not_protected(self):
|
||||||
|
# EC2 Client
|
||||||
|
ec2_client = client("ec2", region_name=AWS_REGION)
|
||||||
|
resp = ec2_client.allocate_address(Domain="vpc", Address="127.38.43.222")
|
||||||
|
allocation_id = resp["AllocationId"]
|
||||||
|
_ = f"arn:aws:ec2:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:eip-allocation/{allocation_id}"
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = False
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
shield_client.protections = {}
|
||||||
|
|
||||||
|
from providers.aws.services.ec2.ec2_service import EC2
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
new=self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips.ec2_client",
|
||||||
|
new=EC2(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips import (
|
||||||
|
shield_advanced_protection_in_associated_elastic_ips,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_associated_elastic_ips()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"Provider": "aws",
|
||||||
|
"CheckID": "shield_advanced_protection_in_associated_elastic_ips",
|
||||||
|
"CheckTitle": "Check if Classic Load Balancers are protected by AWS Shield Advanced.",
|
||||||
|
"CheckType": [],
|
||||||
|
"ServiceName": "shield",
|
||||||
|
"SubServiceName": "",
|
||||||
|
"ResourceIdTemplate": "",
|
||||||
|
"Severity": "medium",
|
||||||
|
"ResourceType": "AwsElasticLoadBalancingLoadBalancer",
|
||||||
|
"Description": "Check if Classic Load Balancers are protected by AWS Shield Advanced.",
|
||||||
|
"Risk": "AWS Shield Advanced provides expanded DDoS attack protection for your resources.",
|
||||||
|
"RelatedUrl": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html",
|
||||||
|
"Remediation": {
|
||||||
|
"Code": {
|
||||||
|
"CLI": "",
|
||||||
|
"NativeIaC": "",
|
||||||
|
"Other": "",
|
||||||
|
"Terraform": ""
|
||||||
|
},
|
||||||
|
"Recommendation": {
|
||||||
|
"Text": "Add as a protected resource in AWS Shield Advanced.",
|
||||||
|
"Url": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Categories": [],
|
||||||
|
"Tags": {
|
||||||
|
"Tag1Key": "value",
|
||||||
|
"Tag2Key": "value"
|
||||||
|
},
|
||||||
|
"DependsOn": [],
|
||||||
|
"RelatedTo": [],
|
||||||
|
"Notes": "",
|
||||||
|
"Compliance": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
from lib.check.models import Check, Check_Report
|
||||||
|
from providers.aws.services.shield.shield_client import shield_client
|
||||||
|
from providers.aws.services.elb.elb_client import elb_client
|
||||||
|
|
||||||
|
|
||||||
|
class shield_advanced_protection_in_classic_load_balancers(Check):
|
||||||
|
def execute(self):
|
||||||
|
findings = []
|
||||||
|
if shield_client.enabled:
|
||||||
|
for elb in elb_client.loadbalancers:
|
||||||
|
report = Check_Report(self.metadata)
|
||||||
|
report.region = shield_client.region
|
||||||
|
report.resource_id = elb.name
|
||||||
|
report.resource_arn = elb.arn
|
||||||
|
report.status = "FAIL"
|
||||||
|
report.status_extended = (
|
||||||
|
f"ELB {elb.name} is not protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
|
||||||
|
for protection in shield_client.protections.values():
|
||||||
|
if elb.arn == protection.resource_arn:
|
||||||
|
report.status = "PASS"
|
||||||
|
report.status_extended = (
|
||||||
|
f"ELB {elb.name} is protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
findings.append(report)
|
||||||
|
|
||||||
|
return findings
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from boto3 import client, resource, session
|
||||||
|
from moto import mock_ec2, mock_elb
|
||||||
|
from moto.core import DEFAULT_ACCOUNT_ID
|
||||||
|
|
||||||
|
from providers.aws.lib.audit_info.models import AWS_Audit_Info
|
||||||
|
from providers.aws.services.shield.shield_service import Protection
|
||||||
|
|
||||||
|
AWS_REGION = "eu-west-1"
|
||||||
|
|
||||||
|
|
||||||
|
class Test_shield_advanced_protection_in_classic_load_balancers:
|
||||||
|
# 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=DEFAULT_ACCOUNT_ID,
|
||||||
|
audited_user_id=None,
|
||||||
|
audited_partition="aws",
|
||||||
|
audited_identity_arn=None,
|
||||||
|
profile=None,
|
||||||
|
profile_region=AWS_REGION,
|
||||||
|
credentials=None,
|
||||||
|
assumed_role_info=None,
|
||||||
|
audited_regions=None,
|
||||||
|
organizations_metadata=None,
|
||||||
|
)
|
||||||
|
return audit_info
|
||||||
|
|
||||||
|
@mock_elb
|
||||||
|
@mock_ec2
|
||||||
|
def test_no_shield_not_active(self):
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = False
|
||||||
|
|
||||||
|
from providers.aws.services.elb.elb_service import ELB
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
new=self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers.elb_client",
|
||||||
|
new=ELB(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers import (
|
||||||
|
shield_advanced_protection_in_classic_load_balancers,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_classic_load_balancers()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
@mock_elb
|
||||||
|
def test_shield_enabled_elb_protected(self):
|
||||||
|
# ELB Client
|
||||||
|
elb = client("elb", region_name=AWS_REGION)
|
||||||
|
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||||
|
|
||||||
|
security_group = ec2.create_security_group(
|
||||||
|
GroupName="sg01", Description="Test security group sg01"
|
||||||
|
)
|
||||||
|
elb_name = "my-lb"
|
||||||
|
elb.create_load_balancer(
|
||||||
|
LoadBalancerName=elb_name,
|
||||||
|
Listeners=[
|
||||||
|
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
|
||||||
|
{"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000},
|
||||||
|
],
|
||||||
|
AvailabilityZones=[f"{AWS_REGION}a"],
|
||||||
|
Scheme="internet-facing",
|
||||||
|
SecurityGroups=[security_group.id],
|
||||||
|
)
|
||||||
|
elb_arn = f"arn:aws:elasticloadbalancing:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:loadbalancer/{elb_name}"
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = True
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
protection_id = "test-protection"
|
||||||
|
shield_client.protections = {
|
||||||
|
protection_id: Protection(
|
||||||
|
id=protection_id,
|
||||||
|
name="",
|
||||||
|
resource_arn=elb_arn,
|
||||||
|
protection_arn="",
|
||||||
|
region=AWS_REGION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
from providers.aws.services.elb.elb_service import ELB
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
new=self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers.elb_client",
|
||||||
|
new=ELB(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers import (
|
||||||
|
shield_advanced_protection_in_classic_load_balancers,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_classic_load_balancers()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0].region == AWS_REGION
|
||||||
|
assert result[0].resource_id == elb_name
|
||||||
|
assert result[0].resource_arn == elb_arn
|
||||||
|
assert result[0].status == "PASS"
|
||||||
|
assert (
|
||||||
|
result[0].status_extended
|
||||||
|
== f"ELB {elb_name} is protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock_elb
|
||||||
|
@mock_ec2
|
||||||
|
def test_shield_enabled_elb_not_protected(self):
|
||||||
|
# ELB Client
|
||||||
|
elb = client("elb", region_name=AWS_REGION)
|
||||||
|
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||||
|
|
||||||
|
security_group = ec2.create_security_group(
|
||||||
|
GroupName="sg01", Description="Test security group sg01"
|
||||||
|
)
|
||||||
|
elb_name = "my-lb"
|
||||||
|
elb.create_load_balancer(
|
||||||
|
LoadBalancerName=elb_name,
|
||||||
|
Listeners=[
|
||||||
|
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
|
||||||
|
{"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000},
|
||||||
|
],
|
||||||
|
AvailabilityZones=[f"{AWS_REGION}a"],
|
||||||
|
Scheme="internet-facing",
|
||||||
|
SecurityGroups=[security_group.id],
|
||||||
|
)
|
||||||
|
elb_arn = f"arn:aws:elasticloadbalancing:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:loadbalancer/{elb_name}"
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = True
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
shield_client.protections = {}
|
||||||
|
|
||||||
|
from providers.aws.services.elb.elb_service import ELB
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
new=self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers.elb_client",
|
||||||
|
new=ELB(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers import (
|
||||||
|
shield_advanced_protection_in_classic_load_balancers,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_classic_load_balancers()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0].region == AWS_REGION
|
||||||
|
assert result[0].resource_id == elb_name
|
||||||
|
assert result[0].resource_arn == elb_arn
|
||||||
|
assert result[0].status == "FAIL"
|
||||||
|
assert (
|
||||||
|
result[0].status_extended
|
||||||
|
== f"ELB {elb_name} is not protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock_elb
|
||||||
|
@mock_ec2
|
||||||
|
def test_shield_disabled_elb_not_protected(self):
|
||||||
|
# ELB Client
|
||||||
|
elb = client("elb", region_name=AWS_REGION)
|
||||||
|
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||||
|
|
||||||
|
security_group = ec2.create_security_group(
|
||||||
|
GroupName="sg01", Description="Test security group sg01"
|
||||||
|
)
|
||||||
|
elb_name = "my-lb"
|
||||||
|
elb.create_load_balancer(
|
||||||
|
LoadBalancerName=elb_name,
|
||||||
|
Listeners=[
|
||||||
|
{"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080},
|
||||||
|
{"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000},
|
||||||
|
],
|
||||||
|
AvailabilityZones=[f"{AWS_REGION}a"],
|
||||||
|
Scheme="internet-facing",
|
||||||
|
SecurityGroups=[security_group.id],
|
||||||
|
)
|
||||||
|
_ = f"arn:aws:elasticloadbalancing:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:loadbalancer/{elb_name}"
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = False
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
shield_client.protections = {}
|
||||||
|
|
||||||
|
from providers.aws.services.elb.elb_service import ELB
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
new=self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers.elb_client",
|
||||||
|
new=ELB(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers import (
|
||||||
|
shield_advanced_protection_in_classic_load_balancers,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_classic_load_balancers()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"Provider": "aws",
|
||||||
|
"CheckID": "shield_advanced_protection_in_cloudfront_distributions",
|
||||||
|
"CheckTitle": "Check if Cloudfront distributions are protected by AWS Shield Advanced.",
|
||||||
|
"CheckType": [],
|
||||||
|
"ServiceName": "shield",
|
||||||
|
"SubServiceName": "",
|
||||||
|
"ResourceIdTemplate": "",
|
||||||
|
"Severity": "medium",
|
||||||
|
"ResourceType": "AwsCloudFrontDistribution",
|
||||||
|
"Description": "Check if Cloudfront distributions are protected by AWS Shield Advanced.",
|
||||||
|
"Risk": "AWS Shield Advanced provides expanded DDoS attack protection for your resources.",
|
||||||
|
"RelatedUrl": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html",
|
||||||
|
"Remediation": {
|
||||||
|
"Code": {
|
||||||
|
"CLI": "",
|
||||||
|
"NativeIaC": "",
|
||||||
|
"Other": "",
|
||||||
|
"Terraform": ""
|
||||||
|
},
|
||||||
|
"Recommendation": {
|
||||||
|
"Text": "Add as a protected resource in AWS Shield Advanced.",
|
||||||
|
"Url": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Categories": [],
|
||||||
|
"Tags": {
|
||||||
|
"Tag1Key": "value",
|
||||||
|
"Tag2Key": "value"
|
||||||
|
},
|
||||||
|
"DependsOn": [],
|
||||||
|
"RelatedTo": [],
|
||||||
|
"Notes": "",
|
||||||
|
"Compliance": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
from lib.check.models import Check, Check_Report
|
||||||
|
from providers.aws.services.shield.shield_client import shield_client
|
||||||
|
from providers.aws.services.cloudfront.cloudfront_client import cloudfront_client
|
||||||
|
|
||||||
|
|
||||||
|
class shield_advanced_protection_in_cloudfront_distributions(Check):
|
||||||
|
def execute(self):
|
||||||
|
findings = []
|
||||||
|
if shield_client.enabled:
|
||||||
|
for distribution in cloudfront_client.distributions.values():
|
||||||
|
report = Check_Report(self.metadata)
|
||||||
|
report.region = shield_client.region
|
||||||
|
report.resource_id = distribution.id
|
||||||
|
report.resource_arn = distribution.arn
|
||||||
|
report.status = "FAIL"
|
||||||
|
report.status_extended = f"CloudFront distribution {distribution.id} is not protected by AWS Shield Advanced"
|
||||||
|
|
||||||
|
for protection in shield_client.protections.values():
|
||||||
|
if distribution.arn == protection.resource_arn:
|
||||||
|
report.status = "PASS"
|
||||||
|
report.status_extended = f"CloudFront distribution {distribution.id} is protected by AWS Shield Advanced"
|
||||||
|
break
|
||||||
|
|
||||||
|
findings.append(report)
|
||||||
|
|
||||||
|
return findings
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from moto.core import DEFAULT_ACCOUNT_ID
|
||||||
|
from providers.aws.services.cloudfront.cloudfront_service import Distribution
|
||||||
|
from providers.aws.services.shield.shield_service import Protection
|
||||||
|
|
||||||
|
AWS_REGION = "eu-west-1"
|
||||||
|
|
||||||
|
|
||||||
|
class Test_shield_advanced_protection_in_cloudfront_distributions:
|
||||||
|
def test_no_shield_not_active(self):
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = False
|
||||||
|
# CloudFront Client
|
||||||
|
cloudfront_client = mock.MagicMock
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.cloudfront.cloudfront_service.CloudFront",
|
||||||
|
new=cloudfront_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_cloudfront_distributions.shield_advanced_protection_in_cloudfront_distributions import (
|
||||||
|
shield_advanced_protection_in_cloudfront_distributions,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_cloudfront_distributions()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|
||||||
|
def test_shield_enabled_cloudfront_protected(self):
|
||||||
|
# CloudFront Client
|
||||||
|
cloudfront_client = mock.MagicMock
|
||||||
|
distribution_id = "EDFDVBD632BHDS5"
|
||||||
|
distribution_arn = (
|
||||||
|
f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{distribution_id}"
|
||||||
|
)
|
||||||
|
cloudfront_client.distributions = {
|
||||||
|
distribution_id: Distribution(
|
||||||
|
arn=distribution_arn, id=distribution_id, region=AWS_REGION, origins=[]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = True
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
protection_id = "test-protection"
|
||||||
|
shield_client.protections = {
|
||||||
|
protection_id: Protection(
|
||||||
|
id=protection_id,
|
||||||
|
name="",
|
||||||
|
resource_arn=distribution_arn,
|
||||||
|
protection_arn="",
|
||||||
|
region=AWS_REGION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.cloudfront.cloudfront_service.CloudFront",
|
||||||
|
new=cloudfront_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_cloudfront_distributions.shield_advanced_protection_in_cloudfront_distributions import (
|
||||||
|
shield_advanced_protection_in_cloudfront_distributions,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_cloudfront_distributions()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0].region == AWS_REGION
|
||||||
|
assert result[0].resource_id == distribution_id
|
||||||
|
assert result[0].resource_arn == distribution_arn
|
||||||
|
assert result[0].status == "PASS"
|
||||||
|
assert (
|
||||||
|
result[0].status_extended
|
||||||
|
== f"CloudFront distribution {distribution_id} is protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_shield_enabled_cloudfront_not_protected(self):
|
||||||
|
# CloudFront Client
|
||||||
|
cloudfront_client = mock.MagicMock
|
||||||
|
distribution_id = "EDFDVBD632BHDS5"
|
||||||
|
distribution_arn = (
|
||||||
|
f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{distribution_id}"
|
||||||
|
)
|
||||||
|
cloudfront_client.distributions = {
|
||||||
|
distribution_id: Distribution(
|
||||||
|
arn=distribution_arn, id=distribution_id, region=AWS_REGION, origins=[]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = True
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
shield_client.protections = {}
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.cloudfront.cloudfront_service.CloudFront",
|
||||||
|
new=cloudfront_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_cloudfront_distributions.shield_advanced_protection_in_cloudfront_distributions import (
|
||||||
|
shield_advanced_protection_in_cloudfront_distributions,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_cloudfront_distributions()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0].region == AWS_REGION
|
||||||
|
assert result[0].resource_id == distribution_id
|
||||||
|
assert result[0].resource_arn == distribution_arn
|
||||||
|
assert result[0].status == "FAIL"
|
||||||
|
assert (
|
||||||
|
result[0].status_extended
|
||||||
|
== f"CloudFront distribution {distribution_id} is not protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_shield_disabled_cloudfront_not_protected(self):
|
||||||
|
# CloudFront Client
|
||||||
|
cloudfront_client = mock.MagicMock
|
||||||
|
distribution_id = "EDFDVBD632BHDS5"
|
||||||
|
distribution_arn = (
|
||||||
|
f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{distribution_id}"
|
||||||
|
)
|
||||||
|
cloudfront_client.distributions = {
|
||||||
|
distribution_id: Distribution(
|
||||||
|
arn=distribution_arn, id=distribution_id, region=AWS_REGION, origins=[]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = False
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
shield_client.protections = {}
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.cloudfront.cloudfront_service.CloudFront",
|
||||||
|
new=cloudfront_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_cloudfront_distributions.shield_advanced_protection_in_cloudfront_distributions import (
|
||||||
|
shield_advanced_protection_in_cloudfront_distributions,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_cloudfront_distributions()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"Provider": "aws",
|
||||||
|
"CheckID": "shield_advanced_protection_in_global_accelerators",
|
||||||
|
"CheckTitle": "Check if Global Accelerators are protected by AWS Shield Advanced.",
|
||||||
|
"CheckType": [],
|
||||||
|
"ServiceName": "shield",
|
||||||
|
"SubServiceName": "",
|
||||||
|
"ResourceIdTemplate": "",
|
||||||
|
"Severity": "medium",
|
||||||
|
"ResourceType": "AwsGlobalAccelerator",
|
||||||
|
"Description": "Check if Global Accelerators are protected by AWS Shield Advanced.",
|
||||||
|
"Risk": "AWS Shield Advanced provides expanded DDoS attack protection for your resources.",
|
||||||
|
"RelatedUrl": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html",
|
||||||
|
"Remediation": {
|
||||||
|
"Code": {
|
||||||
|
"CLI": "",
|
||||||
|
"NativeIaC": "",
|
||||||
|
"Other": "",
|
||||||
|
"Terraform": ""
|
||||||
|
},
|
||||||
|
"Recommendation": {
|
||||||
|
"Text": "Add as a protected resource in AWS Shield Advanced.",
|
||||||
|
"Url": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Categories": [],
|
||||||
|
"Tags": {
|
||||||
|
"Tag1Key": "value",
|
||||||
|
"Tag2Key": "value"
|
||||||
|
},
|
||||||
|
"DependsOn": [],
|
||||||
|
"RelatedTo": [],
|
||||||
|
"Notes": "",
|
||||||
|
"Compliance": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
from lib.check.models import Check, Check_Report
|
||||||
|
from providers.aws.services.shield.shield_client import shield_client
|
||||||
|
from providers.aws.services.globalaccelerator.globalaccelerator_client import (
|
||||||
|
globalaccelerator_client,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class shield_advanced_protection_in_global_accelerators(Check):
|
||||||
|
def execute(self):
|
||||||
|
findings = []
|
||||||
|
if shield_client.enabled:
|
||||||
|
for accelerator in globalaccelerator_client.accelerators.values():
|
||||||
|
report = Check_Report(self.metadata)
|
||||||
|
report.region = shield_client.region
|
||||||
|
report.resource_id = accelerator.name
|
||||||
|
report.resource_arn = accelerator.arn
|
||||||
|
report.status = "FAIL"
|
||||||
|
report.status_extended = f"Global Accelerator {accelerator.name} is not protected by AWS Shield Advanced"
|
||||||
|
|
||||||
|
for protection in shield_client.protections.values():
|
||||||
|
if accelerator.arn == protection.resource_arn:
|
||||||
|
report.status = "PASS"
|
||||||
|
report.status_extended = f"Global Accelerator {accelerator.name} is protected by AWS Shield Advanced"
|
||||||
|
break
|
||||||
|
|
||||||
|
findings.append(report)
|
||||||
|
|
||||||
|
return findings
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from moto.core import DEFAULT_ACCOUNT_ID
|
||||||
|
from providers.aws.services.globalaccelerator.globalaccelerator_service import (
|
||||||
|
Accelerator,
|
||||||
|
)
|
||||||
|
from providers.aws.services.shield.shield_service import Protection
|
||||||
|
|
||||||
|
AWS_REGION = "eu-west-1"
|
||||||
|
|
||||||
|
|
||||||
|
class Test_shield_advanced_protection_in_global_accelerators:
|
||||||
|
def test_no_shield_not_active(self):
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = False
|
||||||
|
# GlobalAccelerator Client
|
||||||
|
globalaccelerator_client = mock.MagicMock
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.globalaccelerator.globalaccelerator_service.GlobalAccelerator",
|
||||||
|
new=globalaccelerator_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_global_accelerators.shield_advanced_protection_in_global_accelerators import (
|
||||||
|
shield_advanced_protection_in_global_accelerators,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_global_accelerators()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|
||||||
|
def test_shield_enabled_globalaccelerator_protected(self):
|
||||||
|
# GlobalAccelerator Client
|
||||||
|
globalaccelerator_client = mock.MagicMock
|
||||||
|
accelerator_name = "1234abcd-abcd-1234-abcd-1234abcdefgh"
|
||||||
|
accelerator_id = "1234abcd-abcd-1234-abcd-1234abcdefgh"
|
||||||
|
accelerator_arn = f"arn:aws:globalaccelerator::{DEFAULT_ACCOUNT_ID}:accelerator/{accelerator_id}"
|
||||||
|
globalaccelerator_client.accelerators = {
|
||||||
|
accelerator_name: Accelerator(
|
||||||
|
arn=accelerator_arn,
|
||||||
|
name=accelerator_name,
|
||||||
|
region=AWS_REGION,
|
||||||
|
enabled=True,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = True
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
protection_id = "test-protection"
|
||||||
|
shield_client.protections = {
|
||||||
|
protection_id: Protection(
|
||||||
|
id=protection_id,
|
||||||
|
name="",
|
||||||
|
resource_arn=accelerator_arn,
|
||||||
|
protection_arn="",
|
||||||
|
region=AWS_REGION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.globalaccelerator.globalaccelerator_service.GlobalAccelerator",
|
||||||
|
new=globalaccelerator_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_global_accelerators.shield_advanced_protection_in_global_accelerators import (
|
||||||
|
shield_advanced_protection_in_global_accelerators,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_global_accelerators()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0].region == AWS_REGION
|
||||||
|
assert result[0].resource_id == accelerator_id
|
||||||
|
assert result[0].resource_arn == accelerator_arn
|
||||||
|
assert result[0].status == "PASS"
|
||||||
|
assert (
|
||||||
|
result[0].status_extended
|
||||||
|
== f"Global Accelerator {accelerator_id} is protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_shield_enabled_globalaccelerator_not_protected(self):
|
||||||
|
# GlobalAccelerator Client
|
||||||
|
globalaccelerator_client = mock.MagicMock
|
||||||
|
accelerator_name = "1234abcd-abcd-1234-abcd-1234abcdefgh"
|
||||||
|
accelerator_id = "1234abcd-abcd-1234-abcd-1234abcdefgh"
|
||||||
|
accelerator_arn = f"arn:aws:globalaccelerator::{DEFAULT_ACCOUNT_ID}:accelerator/{accelerator_id}"
|
||||||
|
globalaccelerator_client.accelerators = {
|
||||||
|
accelerator_name: Accelerator(
|
||||||
|
arn=accelerator_arn,
|
||||||
|
name=accelerator_name,
|
||||||
|
region=AWS_REGION,
|
||||||
|
enabled=True,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = True
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
shield_client.protections = {}
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.globalaccelerator.globalaccelerator_service.GlobalAccelerator",
|
||||||
|
new=globalaccelerator_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_global_accelerators.shield_advanced_protection_in_global_accelerators import (
|
||||||
|
shield_advanced_protection_in_global_accelerators,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_global_accelerators()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0].region == AWS_REGION
|
||||||
|
assert result[0].resource_id == accelerator_id
|
||||||
|
assert result[0].resource_arn == accelerator_arn
|
||||||
|
assert result[0].status == "FAIL"
|
||||||
|
assert (
|
||||||
|
result[0].status_extended
|
||||||
|
== f"Global Accelerator {accelerator_id} is not protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_shield_disabled_globalaccelerator_not_protected(self):
|
||||||
|
# GlobalAccelerator Client
|
||||||
|
globalaccelerator_client = mock.MagicMock
|
||||||
|
accelerator_name = "1234abcd-abcd-1234-abcd-1234abcdefgh"
|
||||||
|
accelerator_id = "1234abcd-abcd-1234-abcd-1234abcdefgh"
|
||||||
|
accelerator_arn = f"arn:aws:globalaccelerator::{DEFAULT_ACCOUNT_ID}:accelerator/{accelerator_id}"
|
||||||
|
globalaccelerator_client.accelerators = {
|
||||||
|
accelerator_name: Accelerator(
|
||||||
|
arn=accelerator_arn,
|
||||||
|
name=accelerator_name,
|
||||||
|
region=AWS_REGION,
|
||||||
|
enabled=True,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = False
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
shield_client.protections = {}
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.globalaccelerator.globalaccelerator_service.GlobalAccelerator",
|
||||||
|
new=globalaccelerator_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_global_accelerators.shield_advanced_protection_in_global_accelerators import (
|
||||||
|
shield_advanced_protection_in_global_accelerators,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_global_accelerators()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"Provider": "aws",
|
||||||
|
"CheckID": "shield_advanced_protection_in_internet_facing_load_balancers",
|
||||||
|
"CheckTitle": "Check if internet-facing Application Load Balancers are protected by AWS Shield Advanced.",
|
||||||
|
"CheckType": [],
|
||||||
|
"ServiceName": "shield",
|
||||||
|
"SubServiceName": "",
|
||||||
|
"ResourceIdTemplate": "",
|
||||||
|
"Severity": "medium",
|
||||||
|
"ResourceType": "AwsElasticLoadBalancingV2LoadBalancer",
|
||||||
|
"Description": "Check if internet-facing Application Load Balancers are protected by AWS Shield Advanced.",
|
||||||
|
"Risk": "AWS Shield Advanced provides expanded DDoS attack protection for your resources.",
|
||||||
|
"RelatedUrl": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html",
|
||||||
|
"Remediation": {
|
||||||
|
"Code": {
|
||||||
|
"CLI": "",
|
||||||
|
"NativeIaC": "",
|
||||||
|
"Other": "",
|
||||||
|
"Terraform": ""
|
||||||
|
},
|
||||||
|
"Recommendation": {
|
||||||
|
"Text": "Add as a protected resource in AWS Shield Advanced.",
|
||||||
|
"Url": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Categories": [],
|
||||||
|
"Tags": {
|
||||||
|
"Tag1Key": "value",
|
||||||
|
"Tag2Key": "value"
|
||||||
|
},
|
||||||
|
"DependsOn": [],
|
||||||
|
"RelatedTo": [],
|
||||||
|
"Notes": "",
|
||||||
|
"Compliance": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
from lib.check.models import Check, Check_Report
|
||||||
|
from providers.aws.services.shield.shield_client import shield_client
|
||||||
|
from providers.aws.services.elbv2.elbv2_client import (
|
||||||
|
elbv2_client,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class shield_advanced_protection_in_internet_facing_load_balancers(Check):
|
||||||
|
def execute(self):
|
||||||
|
findings = []
|
||||||
|
if shield_client.enabled:
|
||||||
|
for elbv2 in elbv2_client.loadbalancersv2:
|
||||||
|
if elbv2.type == "application" and elbv2.scheme == "internet-facing":
|
||||||
|
report = Check_Report(self.metadata)
|
||||||
|
report.region = shield_client.region
|
||||||
|
report.resource_id = elbv2.name
|
||||||
|
report.resource_arn = elbv2.arn
|
||||||
|
report.status = "FAIL"
|
||||||
|
report.status_extended = f"ELBv2 ALB {elbv2.name} is not protected by AWS Shield Advanced"
|
||||||
|
|
||||||
|
for protection in shield_client.protections.values():
|
||||||
|
if elbv2.arn == protection.resource_arn:
|
||||||
|
report.status = "PASS"
|
||||||
|
report.status_extended = f"ELBv2 ALB {elbv2.name} is protected by AWS Shield Advanced"
|
||||||
|
break
|
||||||
|
|
||||||
|
findings.append(report)
|
||||||
|
|
||||||
|
return findings
|
||||||
@@ -0,0 +1,337 @@
|
|||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from boto3 import client, resource, session
|
||||||
|
from mock import patch
|
||||||
|
from moto import mock_ec2, mock_elbv2
|
||||||
|
from moto.core import DEFAULT_ACCOUNT_ID as AWS_ACCOUNT_NUMBER
|
||||||
|
|
||||||
|
from providers.aws.lib.audit_info.models import AWS_Audit_Info
|
||||||
|
from providers.aws.services.shield.shield_service import Protection
|
||||||
|
|
||||||
|
AWS_REGION = "eu-west-1"
|
||||||
|
|
||||||
|
|
||||||
|
# Mock generate_regional_clients()
|
||||||
|
def mock_generate_regional_clients(service, audit_info):
|
||||||
|
regional_client = audit_info.audit_session.client(service, region_name=AWS_REGION)
|
||||||
|
regional_client.region = AWS_REGION
|
||||||
|
return {AWS_REGION: regional_client}
|
||||||
|
|
||||||
|
|
||||||
|
# Patch every AWS call using Boto3 and generate_regional_clients to have 1 client
|
||||||
|
@patch(
|
||||||
|
"providers.aws.services.accessanalyzer.accessanalyzer_service.generate_regional_clients",
|
||||||
|
new=mock_generate_regional_clients,
|
||||||
|
)
|
||||||
|
class Test_shield_advanced_protection_in_internet_facing_load_balancers:
|
||||||
|
# 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=AWS_REGION,
|
||||||
|
credentials=None,
|
||||||
|
assumed_role_info=None,
|
||||||
|
audited_regions=None,
|
||||||
|
organizations_metadata=None,
|
||||||
|
)
|
||||||
|
return audit_info
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
@mock_elbv2
|
||||||
|
def test_no_shield_not_active(self):
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = False
|
||||||
|
|
||||||
|
from providers.aws.services.elbv2.elbv2_service import ELBv2
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
new=self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers.elbv2_client",
|
||||||
|
new=ELBv2(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers import (
|
||||||
|
shield_advanced_protection_in_internet_facing_load_balancers,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_internet_facing_load_balancers()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
@mock_elbv2
|
||||||
|
def test_shield_enabled_elbv2_internet_facing_protected(self):
|
||||||
|
# ELBv2 Client
|
||||||
|
conn = client("elbv2", region_name=AWS_REGION)
|
||||||
|
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||||
|
|
||||||
|
security_group = ec2.create_security_group(
|
||||||
|
GroupName="a-security-group", Description="First One"
|
||||||
|
)
|
||||||
|
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
|
||||||
|
subnet1 = ec2.create_subnet(
|
||||||
|
VpcId=vpc.id,
|
||||||
|
CidrBlock="172.28.7.192/26",
|
||||||
|
AvailabilityZone=f"{AWS_REGION}a",
|
||||||
|
)
|
||||||
|
subnet2 = ec2.create_subnet(
|
||||||
|
VpcId=vpc.id,
|
||||||
|
CidrBlock="172.28.7.0/26",
|
||||||
|
AvailabilityZone=f"{AWS_REGION}b",
|
||||||
|
)
|
||||||
|
lb_name = "my-lb"
|
||||||
|
lb = conn.create_load_balancer(
|
||||||
|
Name=lb_name,
|
||||||
|
Subnets=[subnet1.id, subnet2.id],
|
||||||
|
SecurityGroups=[security_group.id],
|
||||||
|
Scheme="internet-facing",
|
||||||
|
Type="application",
|
||||||
|
)["LoadBalancers"][0]
|
||||||
|
lb_arn = lb["LoadBalancerArn"]
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = True
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
protection_id = "test-protection"
|
||||||
|
shield_client.protections = {
|
||||||
|
protection_id: Protection(
|
||||||
|
id=protection_id,
|
||||||
|
name="",
|
||||||
|
resource_arn=lb_arn,
|
||||||
|
protection_arn="",
|
||||||
|
region=AWS_REGION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
from providers.aws.services.elbv2.elbv2_service import ELBv2
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
new=self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers.elbv2_client",
|
||||||
|
new=ELBv2(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers import (
|
||||||
|
shield_advanced_protection_in_internet_facing_load_balancers,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_internet_facing_load_balancers()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0].region == AWS_REGION
|
||||||
|
assert result[0].resource_id == lb_name
|
||||||
|
assert result[0].resource_arn == lb["LoadBalancerArn"]
|
||||||
|
assert result[0].status == "PASS"
|
||||||
|
assert (
|
||||||
|
result[0].status_extended
|
||||||
|
== f"ELBv2 ALB {lb_name} is protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
@mock_elbv2
|
||||||
|
def test_shield_enabled_elbv2_internal_protected(self):
|
||||||
|
# ELBv2 Client
|
||||||
|
conn = client("elbv2", region_name=AWS_REGION)
|
||||||
|
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||||
|
|
||||||
|
security_group = ec2.create_security_group(
|
||||||
|
GroupName="a-security-group", Description="First One"
|
||||||
|
)
|
||||||
|
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
|
||||||
|
subnet1 = ec2.create_subnet(
|
||||||
|
VpcId=vpc.id,
|
||||||
|
CidrBlock="172.28.7.192/26",
|
||||||
|
AvailabilityZone=f"{AWS_REGION}a",
|
||||||
|
)
|
||||||
|
subnet2 = ec2.create_subnet(
|
||||||
|
VpcId=vpc.id,
|
||||||
|
CidrBlock="172.28.7.0/26",
|
||||||
|
AvailabilityZone=f"{AWS_REGION}b",
|
||||||
|
)
|
||||||
|
lb_name = "my-lb"
|
||||||
|
lb = conn.create_load_balancer(
|
||||||
|
Name=lb_name,
|
||||||
|
Subnets=[subnet1.id, subnet2.id],
|
||||||
|
SecurityGroups=[security_group.id],
|
||||||
|
Scheme="internal",
|
||||||
|
Type="application",
|
||||||
|
)["LoadBalancers"][0]
|
||||||
|
lb_arn = lb["LoadBalancerArn"]
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = True
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
protection_id = "test-protection"
|
||||||
|
shield_client.protections = {
|
||||||
|
protection_id: Protection(
|
||||||
|
id=protection_id,
|
||||||
|
name="",
|
||||||
|
resource_arn=lb_arn,
|
||||||
|
protection_arn="",
|
||||||
|
region=AWS_REGION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
from providers.aws.services.elbv2.elbv2_service import ELBv2
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
new=self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers.elbv2_client",
|
||||||
|
new=ELBv2(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers import (
|
||||||
|
shield_advanced_protection_in_internet_facing_load_balancers,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_internet_facing_load_balancers()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
@mock_elbv2
|
||||||
|
def test_shield_enabled_elbv2_internet_facing_not_protected(self):
|
||||||
|
# ELBv2 Client
|
||||||
|
conn = client("elbv2", region_name=AWS_REGION)
|
||||||
|
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||||
|
|
||||||
|
security_group = ec2.create_security_group(
|
||||||
|
GroupName="a-security-group", Description="First One"
|
||||||
|
)
|
||||||
|
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
|
||||||
|
subnet1 = ec2.create_subnet(
|
||||||
|
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
|
||||||
|
)
|
||||||
|
subnet2 = ec2.create_subnet(
|
||||||
|
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
|
||||||
|
)
|
||||||
|
lb_name = "my-lb"
|
||||||
|
lb = conn.create_load_balancer(
|
||||||
|
Name=lb_name,
|
||||||
|
Subnets=[subnet1.id, subnet2.id],
|
||||||
|
SecurityGroups=[security_group.id],
|
||||||
|
Scheme="internet-facing",
|
||||||
|
Type="application",
|
||||||
|
)["LoadBalancers"][0]
|
||||||
|
lb_arn = lb["LoadBalancerArn"]
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = True
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
shield_client.protections = {}
|
||||||
|
|
||||||
|
from providers.aws.services.elbv2.elbv2_service import ELBv2
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
new=self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers.elbv2_client",
|
||||||
|
new=ELBv2(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers import (
|
||||||
|
shield_advanced_protection_in_internet_facing_load_balancers,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_internet_facing_load_balancers()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0].region == AWS_REGION
|
||||||
|
assert result[0].resource_id == lb_name
|
||||||
|
assert result[0].resource_arn == lb_arn
|
||||||
|
assert result[0].status == "FAIL"
|
||||||
|
assert (
|
||||||
|
result[0].status_extended
|
||||||
|
== f"ELBv2 ALB {lb_name} is not protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock_ec2
|
||||||
|
@mock_elbv2
|
||||||
|
def test_shield_disabled_elbv2_internet_facing_not_protected(self):
|
||||||
|
# ELBv2 Client
|
||||||
|
conn = client("elbv2", region_name=AWS_REGION)
|
||||||
|
ec2 = resource("ec2", region_name=AWS_REGION)
|
||||||
|
|
||||||
|
security_group = ec2.create_security_group(
|
||||||
|
GroupName="a-security-group", Description="First One"
|
||||||
|
)
|
||||||
|
vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default")
|
||||||
|
subnet1 = ec2.create_subnet(
|
||||||
|
VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a"
|
||||||
|
)
|
||||||
|
subnet2 = ec2.create_subnet(
|
||||||
|
VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b"
|
||||||
|
)
|
||||||
|
lb_name = "my-lb"
|
||||||
|
lb = conn.create_load_balancer(
|
||||||
|
Name=lb_name,
|
||||||
|
Subnets=[subnet1.id, subnet2.id],
|
||||||
|
SecurityGroups=[security_group.id],
|
||||||
|
Scheme="internal",
|
||||||
|
Type="application",
|
||||||
|
)["LoadBalancers"][0]
|
||||||
|
_ = lb["LoadBalancerArn"]
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = False
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
shield_client.protections = {}
|
||||||
|
|
||||||
|
from providers.aws.services.elbv2.elbv2_service import ELBv2
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.lib.audit_info.audit_info.current_audit_info",
|
||||||
|
new=self.set_mocked_audit_info(),
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers.elbv2_client",
|
||||||
|
new=ELBv2(self.set_mocked_audit_info()),
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers import (
|
||||||
|
shield_advanced_protection_in_internet_facing_load_balancers,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_internet_facing_load_balancers()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"Provider": "aws",
|
||||||
|
"CheckID": "shield_advanced_protection_in_route53_hosted_zones",
|
||||||
|
"CheckTitle": "Check if Route53 hosted zones are protected by AWS Shield Advanced.",
|
||||||
|
"CheckType": [],
|
||||||
|
"ServiceName": "shield",
|
||||||
|
"SubServiceName": "",
|
||||||
|
"ResourceIdTemplate": "",
|
||||||
|
"Severity": "medium",
|
||||||
|
"ResourceType": "AwsRoute53Domain",
|
||||||
|
"Description": "Check if Route53 hosted zones are protected by AWS Shield Advanced.",
|
||||||
|
"Risk": "AWS Shield Advanced provides expanded DDoS attack protection for your resources.",
|
||||||
|
"RelatedUrl": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html",
|
||||||
|
"Remediation": {
|
||||||
|
"Code": {
|
||||||
|
"CLI": "",
|
||||||
|
"NativeIaC": "",
|
||||||
|
"Other": "",
|
||||||
|
"Terraform": ""
|
||||||
|
},
|
||||||
|
"Recommendation": {
|
||||||
|
"Text": "Add as a protected resource in AWS Shield Advanced.",
|
||||||
|
"Url": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Categories": [],
|
||||||
|
"Tags": {
|
||||||
|
"Tag1Key": "value",
|
||||||
|
"Tag2Key": "value"
|
||||||
|
},
|
||||||
|
"DependsOn": [],
|
||||||
|
"RelatedTo": [],
|
||||||
|
"Notes": "",
|
||||||
|
"Compliance": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
from lib.check.models import Check, Check_Report
|
||||||
|
from providers.aws.services.shield.shield_client import shield_client
|
||||||
|
from providers.aws.services.route53.route53_client import (
|
||||||
|
route53_client,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class shield_advanced_protection_in_route53_hosted_zones(Check):
|
||||||
|
def execute(self):
|
||||||
|
findings = []
|
||||||
|
if shield_client.enabled:
|
||||||
|
for hosted_zone in route53_client.hosted_zones.values():
|
||||||
|
report = Check_Report(self.metadata)
|
||||||
|
report.region = shield_client.region
|
||||||
|
report.resource_id = hosted_zone.id
|
||||||
|
report.resource_arn = hosted_zone.arn
|
||||||
|
report.status = "FAIL"
|
||||||
|
report.status_extended = f"Route53 Hosted Zone {hosted_zone.id} is not protected by AWS Shield Advanced"
|
||||||
|
|
||||||
|
for protection in shield_client.protections.values():
|
||||||
|
if hosted_zone.arn == protection.resource_arn:
|
||||||
|
report.status = "PASS"
|
||||||
|
report.status_extended = f"Route53 Hosted Zone {hosted_zone.id} is protected by AWS Shield Advanced"
|
||||||
|
break
|
||||||
|
|
||||||
|
findings.append(report)
|
||||||
|
|
||||||
|
return findings
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
from providers.aws.services.route53.route53_service import (
|
||||||
|
HostedZone,
|
||||||
|
)
|
||||||
|
from providers.aws.services.shield.shield_service import Protection
|
||||||
|
|
||||||
|
AWS_REGION = "eu-west-1"
|
||||||
|
|
||||||
|
|
||||||
|
class Test_shield_advanced_protection_in_route53_hosted_zones:
|
||||||
|
def test_no_shield_not_active(self):
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = False
|
||||||
|
# Route53 Client
|
||||||
|
route53_client = mock.MagicMock
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.route53.route53_service.Route53",
|
||||||
|
new=route53_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_route53_hosted_zones.shield_advanced_protection_in_route53_hosted_zones import (
|
||||||
|
shield_advanced_protection_in_route53_hosted_zones,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_route53_hosted_zones()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
|
|
||||||
|
def test_shield_enabled_route53_hosted_zone_protected(self):
|
||||||
|
# Route53 Client
|
||||||
|
route53_client = mock.MagicMock
|
||||||
|
hosted_zone_id = "ABCDEF12345678"
|
||||||
|
hosted_zone_arn = f"arn:aws:route53:::hostedzone/{hosted_zone_id}"
|
||||||
|
hosted_zone_name = "test-hosted-zone"
|
||||||
|
|
||||||
|
route53_client.hosted_zones = {
|
||||||
|
hosted_zone_id: HostedZone(
|
||||||
|
id=hosted_zone_id,
|
||||||
|
arn=hosted_zone_arn,
|
||||||
|
name=hosted_zone_name,
|
||||||
|
hosted_zone_name=hosted_zone_name,
|
||||||
|
private_zone=False,
|
||||||
|
region=AWS_REGION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = True
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
protection_id = "test-protection"
|
||||||
|
shield_client.protections = {
|
||||||
|
protection_id: Protection(
|
||||||
|
id=protection_id,
|
||||||
|
name="",
|
||||||
|
resource_arn=hosted_zone_arn,
|
||||||
|
protection_arn="",
|
||||||
|
region=AWS_REGION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.route53.route53_service.Route53",
|
||||||
|
new=route53_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_route53_hosted_zones.shield_advanced_protection_in_route53_hosted_zones import (
|
||||||
|
shield_advanced_protection_in_route53_hosted_zones,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_route53_hosted_zones()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0].region == AWS_REGION
|
||||||
|
assert result[0].resource_id == hosted_zone_id
|
||||||
|
assert result[0].resource_arn == hosted_zone_arn
|
||||||
|
assert result[0].status == "PASS"
|
||||||
|
assert (
|
||||||
|
result[0].status_extended
|
||||||
|
== f"Route53 Hosted Zone {hosted_zone_id} is protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_shield_enabled_route53_hosted_zone_not_protected(self):
|
||||||
|
# Route53 Client
|
||||||
|
route53_client = mock.MagicMock
|
||||||
|
hosted_zone_id = "ABCDEF12345678"
|
||||||
|
hosted_zone_arn = f"arn:aws:route53:::hostedzone/{hosted_zone_id}"
|
||||||
|
hosted_zone_name = "test-hosted-zone"
|
||||||
|
|
||||||
|
route53_client.hosted_zones = {
|
||||||
|
hosted_zone_id: HostedZone(
|
||||||
|
id=hosted_zone_id,
|
||||||
|
arn=hosted_zone_arn,
|
||||||
|
name=hosted_zone_name,
|
||||||
|
hosted_zone_name=hosted_zone_name,
|
||||||
|
private_zone=False,
|
||||||
|
region=AWS_REGION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = True
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
shield_client.protections = {}
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.route53.route53_service.Route53",
|
||||||
|
new=route53_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_route53_hosted_zones.shield_advanced_protection_in_route53_hosted_zones import (
|
||||||
|
shield_advanced_protection_in_route53_hosted_zones,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_route53_hosted_zones()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 1
|
||||||
|
assert result[0].region == AWS_REGION
|
||||||
|
assert result[0].resource_id == hosted_zone_id
|
||||||
|
assert result[0].resource_arn == hosted_zone_arn
|
||||||
|
assert result[0].status == "FAIL"
|
||||||
|
assert (
|
||||||
|
result[0].status_extended
|
||||||
|
== f"Route53 Hosted Zone {hosted_zone_id} is not protected by AWS Shield Advanced"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_shield_disabled_route53_hosted_zone_not_protected(self):
|
||||||
|
# Route53 Client
|
||||||
|
route53_client = mock.MagicMock
|
||||||
|
hosted_zone_id = "ABCDEF12345678"
|
||||||
|
hosted_zone_arn = f"arn:aws:route53:::hostedzone/{hosted_zone_id}"
|
||||||
|
hosted_zone_name = "test-hosted-zone"
|
||||||
|
|
||||||
|
route53_client.hosted_zones = {
|
||||||
|
hosted_zone_id: HostedZone(
|
||||||
|
id=hosted_zone_id,
|
||||||
|
arn=hosted_zone_arn,
|
||||||
|
name=hosted_zone_name,
|
||||||
|
hosted_zone_name=hosted_zone_name,
|
||||||
|
private_zone=False,
|
||||||
|
region=AWS_REGION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Shield Client
|
||||||
|
shield_client = mock.MagicMock
|
||||||
|
shield_client.enabled = False
|
||||||
|
shield_client.region = AWS_REGION
|
||||||
|
shield_client.protections = {}
|
||||||
|
|
||||||
|
with mock.patch(
|
||||||
|
"providers.aws.services.shield.shield_service.Shield",
|
||||||
|
new=shield_client,
|
||||||
|
), mock.patch(
|
||||||
|
"providers.aws.services.route53.route53_service.Route53",
|
||||||
|
new=route53_client,
|
||||||
|
):
|
||||||
|
# Test Check
|
||||||
|
from providers.aws.services.shield.shield_advanced_protection_in_route53_hosted_zones.shield_advanced_protection_in_route53_hosted_zones import (
|
||||||
|
shield_advanced_protection_in_route53_hosted_zones,
|
||||||
|
)
|
||||||
|
|
||||||
|
check = shield_advanced_protection_in_route53_hosted_zones()
|
||||||
|
result = check.execute()
|
||||||
|
|
||||||
|
assert len(result) == 0
|
||||||
4
providers/aws/services/shield/shield_client.py
Normal file
4
providers/aws/services/shield/shield_client.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from providers.aws.lib.audit_info.audit_info import current_audit_info
|
||||||
|
from providers.aws.services.shield.shield_service import Shield
|
||||||
|
|
||||||
|
shield_client = Shield(current_audit_info)
|
||||||
64
providers/aws/services/shield/shield_service.py
Normal file
64
providers/aws/services/shield/shield_service.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
from lib.logger import logger
|
||||||
|
from providers.aws.aws_provider import get_region_global_service
|
||||||
|
|
||||||
|
|
||||||
|
################### Shield
|
||||||
|
class Shield:
|
||||||
|
def __init__(self, audit_info):
|
||||||
|
self.service = "shield"
|
||||||
|
self.session = audit_info.audit_session
|
||||||
|
self.audited_account = audit_info.audited_account
|
||||||
|
self.client = self.session.client(self.service)
|
||||||
|
self.region = get_region_global_service(audit_info)
|
||||||
|
self.enabled = self.__get_subscription_state__()
|
||||||
|
self.protections = {}
|
||||||
|
self.__list_protections__()
|
||||||
|
|
||||||
|
def __get_session__(self):
|
||||||
|
return self.session
|
||||||
|
|
||||||
|
def __get_subscription_state__(self):
|
||||||
|
logger.info("Shield - Getting Subscription State...")
|
||||||
|
try:
|
||||||
|
return (
|
||||||
|
True
|
||||||
|
if self.client.get_subscription_state()["SubscriptionState"] == "ACTIVE"
|
||||||
|
else False
|
||||||
|
)
|
||||||
|
except Exception as error:
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def __list_protections__(self):
|
||||||
|
logger.info("Shield - Listing Protections...")
|
||||||
|
try:
|
||||||
|
list_protections_paginator = self.client.get_paginator("list_protections")
|
||||||
|
for page in list_protections_paginator.paginate():
|
||||||
|
for protection in page["Protections"]:
|
||||||
|
protection_arn = protection.get("ProtectionArn")
|
||||||
|
protection_id = protection.get("Id")
|
||||||
|
protection_name = protection.get("Name")
|
||||||
|
resource_arn = protection.get("ResourceArn")
|
||||||
|
|
||||||
|
self.protections[protection_id] = Protection(
|
||||||
|
id=protection_id,
|
||||||
|
name=protection_name,
|
||||||
|
resource_arn=resource_arn,
|
||||||
|
protection_arn=protection_arn,
|
||||||
|
region=self.region,
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as error:
|
||||||
|
logger.error(
|
||||||
|
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Protection(BaseModel):
|
||||||
|
id: str
|
||||||
|
name: str
|
||||||
|
resource_arn: str
|
||||||
|
protection_arn: str = None
|
||||||
|
region: str
|
||||||
101
providers/aws/services/shield/shield_service_test.py
Normal file
101
providers/aws/services/shield/shield_service_test.py
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
from providers.aws.lib.audit_info.models import AWS_Audit_Info
|
||||||
|
from providers.aws.services.shield.shield_service import Shield
|
||||||
|
from mock import patch
|
||||||
|
from moto.core import DEFAULT_ACCOUNT_ID
|
||||||
|
import botocore
|
||||||
|
from boto3 import session
|
||||||
|
|
||||||
|
# Mock Test Region
|
||||||
|
AWS_REGION = "eu-west-1"
|
||||||
|
|
||||||
|
# Mocking Access Analyzer Calls
|
||||||
|
make_api_call = botocore.client.BaseClient._make_api_call
|
||||||
|
|
||||||
|
|
||||||
|
def mock_make_api_call(self, operation_name, kwarg):
|
||||||
|
"""We have to mock every AWS API call using Boto3"""
|
||||||
|
if operation_name == "ListProtections":
|
||||||
|
return {
|
||||||
|
"Protections": [
|
||||||
|
{
|
||||||
|
"Id": "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111",
|
||||||
|
"Name": "Protection for CloudFront distribution",
|
||||||
|
"ResourceArn": f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/E198WC25FXOWY8",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if operation_name == "GetSubscriptionState":
|
||||||
|
return {"SubscriptionState": "ACTIVE"}
|
||||||
|
|
||||||
|
return make_api_call(self, operation_name, kwarg)
|
||||||
|
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
class Test_Shield_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=DEFAULT_ACCOUNT_ID,
|
||||||
|
audited_user_id=None,
|
||||||
|
audited_partition="aws",
|
||||||
|
audited_identity_arn=None,
|
||||||
|
profile=None,
|
||||||
|
profile_region=AWS_REGION,
|
||||||
|
credentials=None,
|
||||||
|
assumed_role_info=None,
|
||||||
|
audited_regions=None,
|
||||||
|
organizations_metadata=None,
|
||||||
|
)
|
||||||
|
return audit_info
|
||||||
|
|
||||||
|
# Test Shield Service
|
||||||
|
def test_service(self):
|
||||||
|
# Shield client for this test class
|
||||||
|
audit_info = self.set_mocked_audit_info()
|
||||||
|
shield = Shield(audit_info)
|
||||||
|
assert shield.service == "shield"
|
||||||
|
|
||||||
|
# Test Shield Client
|
||||||
|
def test_client(self):
|
||||||
|
# Shield client for this test class
|
||||||
|
audit_info = self.set_mocked_audit_info()
|
||||||
|
shield = Shield(audit_info)
|
||||||
|
assert shield.client.__class__.__name__ == "Shield"
|
||||||
|
|
||||||
|
# Test Shield Session
|
||||||
|
def test__get_session__(self):
|
||||||
|
# Shield client for this test class
|
||||||
|
audit_info = self.set_mocked_audit_info()
|
||||||
|
shield = Shield(audit_info)
|
||||||
|
assert shield.session.__class__.__name__ == "Session"
|
||||||
|
|
||||||
|
def test__get_subscription_state__(self):
|
||||||
|
# Shield client for this test class
|
||||||
|
audit_info = self.set_mocked_audit_info()
|
||||||
|
shield = Shield(audit_info)
|
||||||
|
assert shield.enabled
|
||||||
|
|
||||||
|
def test__list_protections__(self):
|
||||||
|
# Shield client for this test class
|
||||||
|
audit_info = self.set_mocked_audit_info()
|
||||||
|
shield = Shield(audit_info)
|
||||||
|
protection_id = "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"
|
||||||
|
protection_name = "Protection for CloudFront distribution"
|
||||||
|
cloudfront_distribution_id = "E198WC25FXOWY8"
|
||||||
|
resource_arn = (
|
||||||
|
f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{cloudfront_distribution_id}",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert shield.protections
|
||||||
|
assert len(shield.protections) == 1
|
||||||
|
assert shield.protections[protection_id]
|
||||||
|
assert shield.protections[protection_id].id == protection_id
|
||||||
|
assert shield.protections[protection_id].name == protection_name
|
||||||
|
assert not shield.protections[protection_id].protection_arn
|
||||||
|
assert not shield.protections[protection_id].resource_arn == resource_arn
|
||||||
Reference in New Issue
Block a user